From 8c8179cda0c4b2a389e645eadfd53fb6c40fddbc Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Tue, 16 Jun 2026 10:41:00 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-Admin:=20ch=E1=BB=8Dn=20"Ph=C3=B2?= =?UTF-8?q?ng=20cha"=20trong=20qu=E1=BA=A3n=20l=C3=BD=20ph=C3=B2ng=20ban?= =?UTF-8?q?=20=E2=80=94=20d=E1=BB=B1ng=20c=C3=A2y=20t=E1=BB=95=20ch?= =?UTF-8?q?=E1=BB=A9c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anh chốt self-service: admin gán phòng cha để dựng sơ đồ org cho trang Hồ sơ Nhân sự. DepartmentsPage: +Select "Phòng cha (Thuộc khối/phòng)" (option "— Không có (cấp gốc) —" = null) + query departments-all (pageSize 200) + cột "Thuộc" hiện tên phòng cha + gửi parentId trong Create/Update + pre-select khi sửa + loại-trừ-chính-nó khỏi dropdown (cycle sâu hơn = BE 409 ConflictException -> toast). +parentId vào type Department. Build PASS fe-admin (0 TS error). Mirror fe-user defer (quản lý phòng ban = admin). Co-Authored-By: Claude Opus 4.8 (1M context) --- fe-admin/src/pages/master/DepartmentsPage.tsx | 36 ++++++++++++++++--- fe-admin/src/types/master.ts | 1 + 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/fe-admin/src/pages/master/DepartmentsPage.tsx b/fe-admin/src/pages/master/DepartmentsPage.tsx index ffdf7ef..657525c 100644 --- a/fe-admin/src/pages/master/DepartmentsPage.tsx +++ b/fe-admin/src/pages/master/DepartmentsPage.tsx @@ -8,6 +8,7 @@ import { PermissionGuard } from '@/components/PermissionGuard' import { Button } from '@/components/ui/Button' import { Input } from '@/components/ui/Input' import { Label } from '@/components/ui/Label' +import { Select } from '@/components/ui/Select' import { Textarea } from '@/components/ui/Textarea' import { Dialog } from '@/components/ui/Dialog' import { api } from '@/lib/api' @@ -15,8 +16,8 @@ import { getErrorMessage } from '@/lib/apiError' import { MenuKeys } from '@/lib/menuKeys' import type { Department, Paged } from '@/types/master' -type FormState = { id?: string; code: string; name: string; note: string } -const emptyForm: FormState = { code: '', name: '', note: '' } +type FormState = { id?: string; code: string; name: string; parentId: string; note: string } +const emptyForm: FormState = { code: '', name: '', parentId: '', note: '' } export function DepartmentsPage() { const qc = useQueryClient() @@ -38,9 +39,24 @@ export function DepartmentsPage() { }, }) + // Toàn bộ phòng ban (không phân trang) để chọn "Phòng cha" + tra tên phòng cha cho cột bảng. + const allDepts = useQuery({ + queryKey: ['departments-all'], + queryFn: async () => + (await api.get>('/departments', { params: { page: 1, pageSize: 200 } })).data.items, + }) + const deptNameById = new Map((allDepts.data ?? []).map(d => [d.id, `${d.code} — ${d.name}`])) + const mutate = useMutation({ mutationFn: async (d: FormState) => { - const payload = { id: d.id, code: d.code, name: d.name, managerUserId: null, note: d.note || null } + const payload = { + id: d.id, + code: d.code, + name: d.name, + parentId: d.parentId || null, + managerUserId: null, + note: d.note || null, + } if (d.id) await api.put(`/departments/${d.id}`, payload) else await api.post('/departments', payload) }, @@ -65,6 +81,7 @@ export function DepartmentsPage() { const columns: Column[] = [ { key: 'code', header: 'Mã', sortable: true, render: d => {d.code}, width: 'w-32' }, { key: 'name', header: 'Tên phòng ban', sortable: true, render: d => d.name }, + { key: 'parentId', header: 'Thuộc', render: d => (d.parentId ? deptNameById.get(d.parentId) ?? '—' : '—') }, { key: 'note', header: 'Ghi chú', render: d => d.note ?? '—' }, { key: 'actions', @@ -75,7 +92,7 @@ export function DepartmentsPage() {
+
+ + +