refactor(resource): drop the branch domain from resources

Resources never needed a branch: devices and rooms belong to the clinic
itself, and the picker always had exactly one option — a mandatory click
that decided nothing.

- `address_uuid` is now optional on resource and pool creation; when it is
  missing the environment's own address is used. Clients still sending it
  keep working.
- The panel no longer asks for or displays a branch anywhere: resource
  form, list column and filter, pool form and column, detail row, and the
  resource-first booking page.
- Availability no longer gates on `doctor_addresses.active`. That gate shut
  down every device of a clinic whose address row happened to be inactive,
  with a message no page in the panel could act on — no endpoint writes
  that column at all.

`address_id` stays on the resource: the timezone and the tenant pair are
derived from it. It is simply no longer the user's decision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-03 14:57:47 +03:30
co-authored by Claude Opus 5
parent 05bb4a4ade
commit 581a553516
19 changed files with 246 additions and 117 deletions
@@ -78,7 +78,7 @@ describe('ResourceDetailPage', () => {
mockApi();
renderPage();
await waitFor(() => expect(screen.getByText('شعبهٔ مرکزی')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('ظرفیت هم‌زمان')).toBeInTheDocument());
expect(screen.getByText('ظرفیت هم‌زمان')).toBeInTheDocument();
expect(screen.getByText('کار با لیزر · 4')).toBeInTheDocument();
});
@@ -193,7 +193,7 @@ describe('ResourceDetailPage', () => {
const user = userEvent.setup();
renderPage();
await waitFor(() => expect(screen.getByText('شعبهٔ مرکزی')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('ظرفیت هم‌زمان')).toBeInTheDocument());
await user.click(screen.getByRole('button', { name: 'دسته‌بندی‌ها' }));
await waitFor(() => expect(screen.getByText(/فقط انتخاب می‌شود/)).toBeInTheDocument());
@@ -221,7 +221,7 @@ describe('ResourceDetailPage', () => {
const user = userEvent.setup();
renderPage();
await waitFor(() => expect(screen.getByText('شعبهٔ مرکزی')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('ظرفیت هم‌زمان')).toBeInTheDocument());
await user.click(screen.getByRole('button', { name: 'غیرفعال‌سازی موقت' }));
expect(await screen.findByText(/یک بازهٔ مشخص را می‌بندد/)).toBeInTheDocument();
@@ -238,7 +238,7 @@ describe('ResourceDetailPage', () => {
mockApi();
renderPage();
await waitFor(() => expect(screen.getByText('شعبهٔ مرکزی')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('ظرفیت هم‌زمان')).toBeInTheDocument());
// منبعِ نمونه فعال است، پس اقدام «غیرفعال کردن» پیشنهاد می‌شود.
expect(screen.getByRole('button', { name: 'غیرفعال کردن' })).toBeInTheDocument();
@@ -252,7 +252,7 @@ describe('ResourceDetailPage', () => {
const user = userEvent.setup();
renderPage();
await waitFor(() => expect(screen.getByText('شعبهٔ مرکزی')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('ظرفیت هم‌زمان')).toBeInTheDocument());
await user.click(screen.getByRole('button', { name: 'غیرفعال کردن' }));
expect(await screen.findByText(/در جستجوی وقت و رزرو نوبت ظاهر نمی‌شود/)).toBeInTheDocument();