refactor(services): drop the groups and segments tabs
Anything that belongs to a service is defined on the service itself, so the two tabs that managed selection groups and appointment segments come off the service page. Only the UI goes. SegmentTemplate is what makes a service occupy a room and a device at the same time — it is the input to AppointmentPlanBuilder and the reason the resource timeline has anything to draw — and a service without a template already books through singleSegment(). Removing the model would change booking; removing the tabs does not, which tests/Appointment confirms at 314 green. The active tab moved into the query string on the way past. That is what makes the old ?tab=segments link land on the info tab instead of rendering nothing, and it lets back and refresh return to the same tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -75,12 +75,12 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
// صفحه uuid را از useParams میگیرد، پس به یک Route واقعی نیاز دارد.
|
||||
const render = () =>
|
||||
const render = (route = '/admin/clinic-services/it1') =>
|
||||
renderWithProviders(
|
||||
<Routes>
|
||||
<Route path="/admin/clinic-services/:uuid" element={<ServiceDetailPage />} />
|
||||
</Routes>,
|
||||
{ route: '/admin/clinic-services/it1' },
|
||||
{ route },
|
||||
);
|
||||
|
||||
describe('ServiceDetailPage (جزئیات سرویس)', () => {
|
||||
@@ -202,4 +202,31 @@ describe('ServiceDetailPage (جزئیات سرویس)', () => {
|
||||
expect(await screen.findByText('غیرفعال')).toBeInTheDocument();
|
||||
expect(screen.getByText('فعالکردن')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
/**
|
||||
* گروههای انتخاب و بخشهای نوبت از صفحهٔ سرویس برداشته شدند: هر چیزی که به یک سرویس
|
||||
* مربوط است در خودِ سرویس تعریف میشود. مدل و موتور سر جایشاناند — تستهای
|
||||
* `tests/Appointment` همان رفتار قبلی را قفل میکنند.
|
||||
*/
|
||||
it('دیگر تب گروهها و بخشهای نوبت ندارد', async () => {
|
||||
render();
|
||||
await screen.findByRole('heading', { name: 'سرم ۵۰۰cc' });
|
||||
|
||||
expect(screen.queryByText('گروهها و آیتمها')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('بخشهای نوبت')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
/** لینک قدیمی به تبِ حذفشده نباید صفحهٔ خالی بدهد. */
|
||||
it('تبِ ناشناخته در URL به اطلاعات برمیگردد', async () => {
|
||||
render('/admin/clinic-services/it1?tab=segments');
|
||||
|
||||
expect(await screen.findByText('تاریخ ایجاد')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
/** تب در URL مینشیند تا «بازگشت» و رفرش همان نما را بدهند. */
|
||||
it('تب انتخابشده از URL خوانده میشود', async () => {
|
||||
render('/admin/clinic-services/it1?tab=tariffs');
|
||||
|
||||
expect(await screen.findByText('سال جاری')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,11 +15,10 @@ import PageHeader from '../components/ui/PageHeader';
|
||||
import FeatureGate from '../components/ui/FeatureGate';
|
||||
import ConfirmDialog from '../components/ui/ConfirmDialog';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { useUrlState } from '../hooks/useUrlState';
|
||||
import ServiceTariffModal from '../components/ServiceTariffModal';
|
||||
import ServiceInsuranceModal from '../components/ServiceInsuranceModal';
|
||||
import ServiceItemFormModal from '../components/ServiceItemFormModal';
|
||||
import ServiceGroupsTab from '../components/ServiceGroupsTab';
|
||||
import ServiceSegmentsTab from '../components/ServiceSegmentsTab';
|
||||
import ServiceCategoryTab from '../components/ServiceCategoryTab';
|
||||
|
||||
interface Tariff {
|
||||
@@ -54,8 +53,6 @@ const TABS = [
|
||||
{ id: 'info', label: 'اطلاعات سرویس' },
|
||||
{ id: 'tariffs', label: 'تعرفهها' },
|
||||
{ id: 'insurance', label: 'بیمهها' },
|
||||
{ id: 'groups', label: 'گروهها و آیتمها' },
|
||||
{ id: 'segments', label: 'بخشهای نوبت' },
|
||||
{ id: 'categories',label: 'دستهبندیها' },
|
||||
{ id: 'goods', label: 'کالاهای مرتبط' },
|
||||
{ id: 'history', label: 'لاگ تغییرات' },
|
||||
@@ -458,7 +455,10 @@ function ServiceDetailPageInner() {
|
||||
const { can } = usePermissions();
|
||||
const canUpdate = can('services', 'update');
|
||||
|
||||
const [tab, setTab] = useState<TabId>('info');
|
||||
const [urlState, setUrlState] = useUrlState({ tab: 'info' });
|
||||
// تبِ ناشناخته (لینک قدیمی به گروهها/بخشها) به اطلاعات برمیگردد، نه صفحهٔ خالی.
|
||||
const tab = (TABS.some((t) => t.id === urlState.tab) ? urlState.tab : 'info') as TabId;
|
||||
const setTab = (id: TabId) => setUrlState({ tab: id });
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [tariffOpen, setTariffOpen] = useState(false);
|
||||
const [insuranceOpen, setInsuranceOpen] = useState(false);
|
||||
@@ -543,8 +543,6 @@ function ServiceDetailPageInner() {
|
||||
{tab === 'info' && <InfoTab item={item} />}
|
||||
{tab === 'tariffs' && <TariffsTab item={item} onManage={() => setTariffOpen(true)} canUpdate={canUpdate} />}
|
||||
{tab === 'insurance' && <InsuranceTab item={item} onManage={() => setInsuranceOpen(true)} canUpdate={canUpdate} />}
|
||||
{tab === 'groups' && <ServiceGroupsTab serviceUuid={item.uuid} canEdit={canUpdate} />}
|
||||
{tab === 'segments' && <ServiceSegmentsTab serviceUuid={item.uuid} canEdit={canUpdate} />}
|
||||
{tab === 'categories' && (
|
||||
<ServiceCategoryTab
|
||||
serviceUuid={item.uuid}
|
||||
|
||||
Reference in New Issue
Block a user