feat(resource): remove ResourceCategoriesPanel and related functionality from ResourceDetailPage
This commit is contained in:
@@ -1,94 +0,0 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import SearchableSelect from '../ui/SearchableSelect';
|
||||
import { useCatalogCategories } from '../../hooks/useCatalogCategories';
|
||||
import type { CatalogCategory, ClinicResource } from '../../types';
|
||||
|
||||
type Flat = { uuid: string; name: string; depth: number };
|
||||
|
||||
function flatten(nodes: CatalogCategory[], depth = 0): Flat[] {
|
||||
return nodes.flatMap((n) => [
|
||||
{ uuid: n.uuid, name: n.name, depth },
|
||||
...flatten(n.children ?? [], depth + 1),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* دستهبندی یک منبع — فقط **انتخاب** از کاتالوگ سراسری.
|
||||
*
|
||||
* ساختن دسته اینجا عمداً ممکن نیست؛ تنها جای ساخت «تنظیمات ← دستهبندیها» است، وگرنه
|
||||
* هر کاربر نسخهٔ خودش از «تمام بدن» را میسازد.
|
||||
*/
|
||||
export default function ResourceCategoriesPanel({ resource, canUpdate, saving, onSave }: {
|
||||
resource: ClinicResource | null;
|
||||
canUpdate: boolean;
|
||||
saving: boolean;
|
||||
onSave: (categoryUuids: string[]) => void;
|
||||
}) {
|
||||
const { tree, loading } = useCatalogCategories();
|
||||
const [chosen, setChosen] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setChosen((resource?.categories ?? []).map((c) => c.uuid));
|
||||
}, [resource]);
|
||||
|
||||
const all = useMemo(() => flatten(tree), [tree]);
|
||||
const picked = new Set(chosen);
|
||||
const available = all.filter((c) => !picked.has(c.uuid));
|
||||
const nameOf = (uuid: string) => all.find((c) => c.uuid === uuid)?.name ?? uuid;
|
||||
|
||||
return (
|
||||
<div className="card card-pad" style={{ display: 'grid', gap: 14 }}>
|
||||
<p style={{ margin: 0, fontSize: 12.5, color: 'var(--text-3)', lineHeight: 1.9 }}>
|
||||
دستهبندی سراسری است و اینجا فقط انتخاب میشود. برای ساخت یا ویرایش به{' '}
|
||||
<Link to="/admin/service-categories" style={{ color: 'var(--primary)' }}>تنظیمات ← دستهبندیها</Link>{' '}
|
||||
بروید.
|
||||
</p>
|
||||
|
||||
{loading ? (
|
||||
<span style={{ fontSize: 13, color: 'var(--text-3)' }}>در حال بارگذاری...</span>
|
||||
) : chosen.length === 0 ? (
|
||||
<p style={{ fontSize: 13, color: 'var(--text-3)', margin: 0 }}>این منبع هیچ دستهبندیای ندارد.</p>
|
||||
) : (
|
||||
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
||||
{chosen.map((uuid) => (
|
||||
<span key={uuid} className="badge blue" style={{ fontSize: 12, display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
||||
{nameOf(uuid)}
|
||||
{canUpdate && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setChosen((c) => c.filter((x) => x !== uuid))}
|
||||
aria-label={`حذف ${nameOf(uuid)}`}
|
||||
style={{ background: 'none', border: 0, cursor: 'pointer', color: 'inherit', padding: 0, lineHeight: 1 }}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{canUpdate && available.length > 0 && (
|
||||
<div style={{ display: 'grid', gap: 6, maxWidth: 380 }}>
|
||||
<label style={{ fontSize: 12, color: 'var(--text-2)' }}>افزودن دستهبندی</label>
|
||||
<SearchableSelect
|
||||
options={available.map((c) => ({ value: c.uuid, label: '— '.repeat(c.depth) + c.name }))}
|
||||
value={null}
|
||||
onChange={(v) => v && setChosen((c) => [...c, String(v)])}
|
||||
placeholder="یک دستهبندی انتخاب کنید"
|
||||
height={38}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{canUpdate && (
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<button type="button" className="btn primary" disabled={saving} onClick={() => onSave(chosen)}>
|
||||
{saving ? 'در حال ذخیره...' : 'ذخیره'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user