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:
@@ -9,6 +9,8 @@ import { useUrlState } from '../hooks/useUrlState';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { useSkills } from '../hooks/useResources';
|
||||
import type { Skill } from '../types';
|
||||
import SettingsLayout from '../components/layout/SettingsLayout';
|
||||
import ResourcesSubNav from '../components/resources/ResourcesSubNav';
|
||||
|
||||
/**
|
||||
* مهارتها — «کدام اپراتور مجاز است با کدام دستگاه کار کند» یک اطلاعات است، نه یک
|
||||
@@ -39,73 +41,77 @@ export default function SkillsPage() {
|
||||
];
|
||||
|
||||
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={() => setEditing({ open: true, skill: null })}>
|
||||
<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={() => setEditing({ open: true, skill: null })}>
|
||||
<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
|
||||
? (s) => (
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<button type="button" className="btn secondary sm" onClick={() => setEditing({ open: true, skill: s })}>
|
||||
ویرایش
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary sm"
|
||||
disabled={(s.resources_count ?? 0) > 0}
|
||||
title={(s.resources_count ?? 0) > 0 ? 'اول از منابع برداشته شود' : undefined}
|
||||
onClick={() => setToDelete(s)}
|
||||
>
|
||||
حذف
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<ResourcesSubNav />
|
||||
|
||||
<SkillModal
|
||||
open={editing.open}
|
||||
skill={editing.skill}
|
||||
saving={create.isPending || update.isPending}
|
||||
onClose={() => setEditing({ open: false, skill: null })}
|
||||
onSave={({ name, active }) => {
|
||||
const opts = { onSuccess: () => setEditing({ open: false, skill: null }) };
|
||||
if (editing.skill) update.mutate({ uuid: editing.skill.uuid, d: { name, active } }, opts);
|
||||
else create.mutate({ name }, opts);
|
||||
}}
|
||||
/>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
loading={loading}
|
||||
searchValue={urlState.search}
|
||||
onSearchChange={(v) => setUrlState({ search: v })}
|
||||
searchPlaceholder="جستجو در مهارتها..."
|
||||
emptyMessage="هنوز مهارتی تعریف نشده است"
|
||||
actions={
|
||||
canUpdate
|
||||
? (s) => (
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<button type="button" className="btn secondary sm" onClick={() => setEditing({ open: true, skill: s })}>
|
||||
ویرایش
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary sm"
|
||||
disabled={(s.resources_count ?? 0) > 0}
|
||||
title={(s.resources_count ?? 0) > 0 ? 'اول از منابع برداشته شود' : undefined}
|
||||
onClick={() => setToDelete(s)}
|
||||
>
|
||||
حذف
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
open={!!toDelete}
|
||||
title="حذف مهارت"
|
||||
message={`آیا از حذف «${toDelete?.name}» مطمئن هستید؟`}
|
||||
confirmLabel="حذف"
|
||||
loading={remove.isPending}
|
||||
onConfirm={() => toDelete && remove.mutate(toDelete.uuid, { onSuccess: () => setToDelete(null) })}
|
||||
onCancel={() => setToDelete(null)}
|
||||
/>
|
||||
</div>
|
||||
<SkillModal
|
||||
open={editing.open}
|
||||
skill={editing.skill}
|
||||
saving={create.isPending || update.isPending}
|
||||
onClose={() => setEditing({ open: false, skill: null })}
|
||||
onSave={({ name, active }) => {
|
||||
const opts = { onSuccess: () => setEditing({ open: false, skill: null }) };
|
||||
if (editing.skill) update.mutate({ uuid: editing.skill.uuid, d: { name, active } }, opts);
|
||||
else create.mutate({ name }, opts);
|
||||
}}
|
||||
/>
|
||||
|
||||
<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