fix(settings): every settings page renders the settings shell
Resources, branches, price lists, holidays and the new categories page sat in the settings menu but rendered bare, so clicking one made the settings sidebar disappear — the subscription page was the only one that kept it. Eleven pages now wrap in SettingsLayout with the key of the menu entry they belong to, and the four resource pages (list, types, skills, pools) share one menu entry plus a sub-nav between them, rather than four entries that would make the menu a third longer without making anything clearer. .seg accepts `a` as well as `button`, and treats `active` as an alias of `on`. Both were needed: cross-page tabs must be real links, and the pages already using `active` (service detail, clinic appointment settings) had no visible highlight at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,8 @@ import { usePermissions } from '../hooks/usePermissions';
|
||||
import { useBranches } from '../hooks/useBranches';
|
||||
import { useResourcePools, useResources, useResourceTypes } from '../hooks/useResources';
|
||||
import type { ResourcePool } from '../types';
|
||||
import SettingsLayout from '../components/layout/SettingsLayout';
|
||||
import ResourcesSubNav from '../components/resources/ResourcesSubNav';
|
||||
|
||||
/**
|
||||
* استخر منابع — گروهی از منابع که جایگزین کامل یکدیگرند.
|
||||
@@ -65,81 +67,85 @@ export default function ResourcePoolsPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="fade-in">
|
||||
<PageHeader
|
||||
title="استخر منابع"
|
||||
description="منابعی که جایگزین کامل یکدیگرند — مثل «لیزرهای آلکساندرایت». همهٔ اعضا باید در یک شعبه و از یک نوع باشند."
|
||||
backTo="/admin/resources"
|
||||
breadcrumbs={[{ label: 'منابع', to: '/admin/resources' }, { label: 'استخر منابع' }]}
|
||||
action={
|
||||
canUpdate ? (
|
||||
<button type="button" className="btn primary" onClick={() => setCreating(true)}>
|
||||
<PlusIcon style={{ width: 16 }} /> افزودن استخر
|
||||
</button>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
<SettingsLayout active="resources">
|
||||
<div className="fade-in">
|
||||
<PageHeader
|
||||
title="استخر منابع"
|
||||
description="منابعی که جایگزین کامل یکدیگرند — مثل «لیزرهای آلکساندرایت». همهٔ اعضا باید در یک شعبه و از یک نوع باشند."
|
||||
backTo="/admin/resources"
|
||||
breadcrumbs={[{ label: 'منابع', to: '/admin/resources' }, { label: 'استخر منابع' }]}
|
||||
action={
|
||||
canUpdate ? (
|
||||
<button type="button" className="btn primary" onClick={() => setCreating(true)}>
|
||||
<PlusIcon style={{ width: 16 }} /> افزودن استخر
|
||||
</button>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
loading={loading}
|
||||
searchValue={urlState.search}
|
||||
onSearchChange={(v) => setUrlState({ search: v })}
|
||||
searchPlaceholder="جستجو در استخرها..."
|
||||
emptyMessage="هنوز استخری تعریف نشده است"
|
||||
actions={
|
||||
canUpdate
|
||||
? (p) => (
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<button type="button" className="btn secondary sm" onClick={() => setMembersFor(p)}>
|
||||
اعضا
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary sm"
|
||||
onClick={() => update.mutate({ uuid: p.uuid, d: { active: !p.active } })}
|
||||
>
|
||||
{p.active ? 'غیرفعال کردن' : 'فعال کردن'}
|
||||
</button>
|
||||
<button type="button" className="btn secondary sm" onClick={() => setToDelete(p)}>
|
||||
حذف
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<ResourcesSubNav />
|
||||
|
||||
<CreatePoolModal
|
||||
open={creating}
|
||||
branches={branches}
|
||||
types={types}
|
||||
saving={create.isPending}
|
||||
onClose={() => setCreating(false)}
|
||||
onSave={(payload) => create.mutate(payload, { onSuccess: () => setCreating(false) })}
|
||||
/>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
loading={loading}
|
||||
searchValue={urlState.search}
|
||||
onSearchChange={(v) => setUrlState({ search: v })}
|
||||
searchPlaceholder="جستجو در استخرها..."
|
||||
emptyMessage="هنوز استخری تعریف نشده است"
|
||||
actions={
|
||||
canUpdate
|
||||
? (p) => (
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<button type="button" className="btn secondary sm" onClick={() => setMembersFor(p)}>
|
||||
اعضا
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary sm"
|
||||
onClick={() => update.mutate({ uuid: p.uuid, d: { active: !p.active } })}
|
||||
>
|
||||
{p.active ? 'غیرفعال کردن' : 'فعال کردن'}
|
||||
</button>
|
||||
<button type="button" className="btn secondary sm" onClick={() => setToDelete(p)}>
|
||||
حذف
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
||||
<PoolMembersModal
|
||||
pool={membersFor}
|
||||
saving={setMembers.isPending}
|
||||
onClose={() => setMembersFor(null)}
|
||||
onSave={(members) =>
|
||||
membersFor &&
|
||||
setMembers.mutate({ uuid: membersFor.uuid, members }, { onSuccess: () => setMembersFor(null) })
|
||||
}
|
||||
/>
|
||||
<CreatePoolModal
|
||||
open={creating}
|
||||
branches={branches}
|
||||
types={types}
|
||||
saving={create.isPending}
|
||||
onClose={() => setCreating(false)}
|
||||
onSave={(payload) => create.mutate(payload, { onSuccess: () => setCreating(false) })}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
open={!!toDelete}
|
||||
title="حذف استخر"
|
||||
message={`آیا از حذف «${toDelete?.name}» مطمئن هستید؟ منابع عضو حذف نمیشوند.`}
|
||||
confirmLabel="حذف"
|
||||
loading={remove.isPending}
|
||||
onConfirm={() => toDelete && remove.mutate(toDelete.uuid, { onSuccess: () => setToDelete(null) })}
|
||||
onCancel={() => setToDelete(null)}
|
||||
/>
|
||||
</div>
|
||||
<PoolMembersModal
|
||||
pool={membersFor}
|
||||
saving={setMembers.isPending}
|
||||
onClose={() => setMembersFor(null)}
|
||||
onSave={(members) =>
|
||||
membersFor &&
|
||||
setMembers.mutate({ uuid: membersFor.uuid, members }, { onSuccess: () => setMembersFor(null) })
|
||||
}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
open={!!toDelete}
|
||||
title="حذف استخر"
|
||||
message={`آیا از حذف «${toDelete?.name}» مطمئن هستید؟ منابع عضو حذف نمیشوند.`}
|
||||
confirmLabel="حذف"
|
||||
loading={remove.isPending}
|
||||
onConfirm={() => toDelete && remove.mutate(toDelete.uuid, { onSuccess: () => setToDelete(null) })}
|
||||
onCancel={() => setToDelete(null)}
|
||||
/>
|
||||
</div>
|
||||
</SettingsLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user