From a4f8e1a64f7e5f75535748b6be48fb26dd66e55d Mon Sep 17 00:00:00 2001 From: Khafid Prayoga Date: Fri, 31 Oct 2025 11:18:00 +0700 Subject: [PATCH] feat(specialist): add specialist detail page with position management - Add specialist detail page with unit relation - Implement position management including CRUD operations - Update specialist model to include unit relation - Add list and detail components for specialist positions - Create entry form for specialist positions - Implement pagination and search for positions - Add confirmation dialogs for delete operations --- .../app/specialist-position/entry-detail.vue | 192 ++++++++++++++ .../app/specialist/detail/index.vue | 35 +++ .../app/specialist/detail/list.cfg.ts | 61 +++++ app/components/app/specialist/detail/list.vue | 45 ++++ app/components/app/specialist/list-cfg.ts | 15 +- app/components/content/specialist/detail.vue | 234 ++++++++++++++++++ app/components/content/specialist/entry.ts | 95 ------- app/components/content/specialist/list.vue | 20 +- app/models/specialist.ts | 7 +- .../org-src/specialist/[id]/index.vue | 42 ++++ 10 files changed, 633 insertions(+), 113 deletions(-) create mode 100644 app/components/app/specialist-position/entry-detail.vue create mode 100644 app/components/app/specialist/detail/index.vue create mode 100644 app/components/app/specialist/detail/list.cfg.ts create mode 100644 app/components/app/specialist/detail/list.vue create mode 100644 app/components/content/specialist/detail.vue delete mode 100644 app/components/content/specialist/entry.ts create mode 100644 app/pages/(features)/org-src/specialist/[id]/index.vue diff --git a/app/components/app/specialist-position/entry-detail.vue b/app/components/app/specialist-position/entry-detail.vue new file mode 100644 index 00000000..f21ff65f --- /dev/null +++ b/app/components/app/specialist-position/entry-detail.vue @@ -0,0 +1,192 @@ + + + diff --git a/app/components/app/specialist/detail/index.vue b/app/components/app/specialist/detail/index.vue new file mode 100644 index 00000000..faa5234c --- /dev/null +++ b/app/components/app/specialist/detail/index.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/app/components/app/specialist/detail/list.cfg.ts b/app/components/app/specialist/detail/list.cfg.ts new file mode 100644 index 00000000..8faf9e61 --- /dev/null +++ b/app/components/app/specialist/detail/list.cfg.ts @@ -0,0 +1,61 @@ +import type { Config, RecComponent } from '~/components/pub/my-ui/data-table' +import { defineAsyncComponent } from 'vue' +import type { UnitPosition } from '~/models/unit-position' + +type SmallDetailDto = any + +const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue')) + +export const config: Config = { + cols: [{}, {}, {}, {}, {}, { width: 50 }], + + headers: [ + [ + { label: '#' }, + { label: 'Kode Posisi' }, + { label: 'Nama Posisi' }, + { label: 'Karyawan' }, + { label: 'Status Kepala' }, + { label: '' }, + ], + ], + + keys: ['index', 'code', 'name', 'employee', 'head', 'action'], + + delKeyNames: [ + { key: 'code', label: 'Kode' }, + { key: 'name', label: 'Nama' }, + ], + + parses: { + employee: (rec: unknown): unknown => { + const recX = rec as UnitPosition + const fullName = [recX.employee?.person.frontTitle, recX.employee?.person.name, recX.employee?.person.endTitle] + .filter(Boolean) + .join(' ') + .trim() + + return fullName || '-' + }, + head: (rec: unknown): unknown => { + const recX = rec as SmallDetailDto + return recX.headStatus ? 'Ya' : 'Tidak' + }, + }, + + components: { + action(rec, idx) { + const res: RecComponent = { + idx, + rec: rec as object, + component: action, + props: { + size: 'sm', + }, + } + return res + }, + }, + + htmls: {}, +} diff --git a/app/components/app/specialist/detail/list.vue b/app/components/app/specialist/detail/list.vue new file mode 100644 index 00000000..da83e7ca --- /dev/null +++ b/app/components/app/specialist/detail/list.vue @@ -0,0 +1,45 @@ + + + diff --git a/app/components/app/specialist/list-cfg.ts b/app/components/app/specialist/list-cfg.ts index 8ed75f28..010358c4 100644 --- a/app/components/app/specialist/list-cfg.ts +++ b/app/components/app/specialist/list-cfg.ts @@ -8,16 +8,9 @@ const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dr export const config: Config = { cols: [{}, {}, {}, { width: 50 }], - headers: [ - [ - { label: 'Kode' }, - { label: 'Nama' }, - { label: 'Unit' }, - { label: '' }, - ], - ], + headers: [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Nama Unit' }, { label: '' }]], - keys: ['code', 'name', 'unit', 'action'], + keys: ['code', 'name', 'unit.name', 'action'], delKeyNames: [ { key: 'code', label: 'Kode' }, @@ -29,10 +22,6 @@ export const config: Config = { const recX = rec as SmallDetailDto return `${recX.name}`.trim() }, - unit: (rec: unknown): unknown => { - const recX = rec as SmallDetailDto - return recX.unit_id || '-' - }, }, components: { diff --git a/app/components/content/specialist/detail.vue b/app/components/content/specialist/detail.vue new file mode 100644 index 00000000..7387900d --- /dev/null +++ b/app/components/content/specialist/detail.vue @@ -0,0 +1,234 @@ + + + diff --git a/app/components/content/specialist/entry.ts b/app/components/content/specialist/entry.ts deleted file mode 100644 index c89302c2..00000000 --- a/app/components/content/specialist/entry.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as z from 'zod' - -export const schemaConf = z.object({ - name: z - .string({ - required_error: 'Nama spesialisasi harus diisi', - }) - .min(3, 'Nama spesialisasi minimal 3 karakter'), - - code: z - .string({ - required_error: 'Kode spesialisasi harus diisi', - }) - .min(3, 'Kode spesialisasi minimal 3 karakter'), - - installationId: z - .string({ - required_error: 'Instalasi harus dipilih', - }) - .min(1, 'Instalasi harus dipilih'), - - unitId: z - .string({ - required_error: 'Unit harus dipilih', - }) - .min(1, 'Unit harus dipilih'), -}) - -// Unit mapping berdasarkan installation -export const installationUnitMapping: Record = { - '1': ['1', '3', '5'], - '2': ['2', '4', '6'], - '3': ['7', '8', '9', '10', '11'], -} - -export const unitConf = { - msg: { - placeholder: '---pilih unit', - search: 'kode, nama unit', - empty: 'unit tidak ditemukan', - }, - items: [ - { value: '1', label: 'Instalasi Medis', code: 'MED' }, - { value: '2', label: 'Instalasi Keperawatan', code: 'NUR' }, - { value: '3', label: 'Instalasi Administrasi', code: 'ADM' }, - { value: '4', label: 'Instalasi Penunjang Non-Medis', code: 'SUP' }, - { value: '5', label: 'Instalasi Pendidikan & Pelatihan', code: 'EDU' }, - { value: '6', label: 'Instalasi Farmasi', code: 'PHA' }, - { value: '7', label: 'Instalasi Radiologi', code: 'RAD' }, - { value: '8', label: 'Instalasi Laboratorium', code: 'LAB' }, - { value: '9', label: 'Instalasi Keuangan', code: 'FIN' }, - { value: '10', label: 'Instalasi SDM', code: 'HR' }, - { value: '11', label: 'Instalasi Teknologi Informasi', code: 'ITS' }, - { value: '12', label: 'Instalasi Pemeliharaan & Sarana', code: 'MNT' }, - { value: '13', label: 'Instalasi Gizi / Catering', code: 'CAT' }, - { value: '14', label: 'Instalasi Keamanan', code: 'SEC' }, - { value: '15', label: 'Instalasi Gawat Darurat', code: 'EMR' }, - { value: '16', label: 'Instalasi Bedah Sentral', code: 'SUR' }, - { value: '17', label: 'Instalasi Rawat Jalan', code: 'OUT' }, - { value: '18', label: 'Instalasi Rawat Inap', code: 'INP' }, - { value: '19', label: 'Instalasi Rehabilitasi Medik', code: 'REB' }, - { value: '20', label: 'Instalasi Penelitian & Pengembangan', code: 'RSH' }, - ], -} - -export const installationConf = { - msg: { - placeholder: '---pilih instalasi', - search: 'kode, nama instalasi', - empty: 'instalasi tidak ditemukan', - }, - items: [ - { value: '1', label: 'Ambulatory', code: 'AMB' }, - { value: '2', label: 'Inpatient', code: 'IMP' }, - { value: '3', label: 'Emergency', code: 'EMER' }, - ], -} - -// Helper function untuk filter unit berdasarkan installation -export function getFilteredUnits(installationId: string) { - if (!installationId || !installationUnitMapping[installationId]) { - return [] - } - - const allowedUnitIds = installationUnitMapping[installationId] - return unitConf.items.filter((unit) => allowedUnitIds.includes(unit.value)) -} - -// Helper function untuk membuat unit config yang ter-filter -export function createFilteredUnitConf(installationId: string) { - return { - ...unitConf, - items: getFilteredUnits(installationId), - } -} diff --git a/app/components/content/specialist/list.vue b/app/components/content/specialist/list.vue index 9bbf755f..ca293026 100644 --- a/app/components/content/specialist/list.vue +++ b/app/components/content/specialist/list.vue @@ -103,9 +103,23 @@ const getCurrentSpecialistDetail = async (id: number | string) => { watch([recId, recAction], () => { switch (recAction.value) { case ActionEvents.showDetail: - getCurrentSpecialistDetail(recId.value) - title.value = 'Detail Spesialis' - isReadonly.value = true + if (Number(recId.value) > 0) { + const id = Number(recId.value) + + recAction.value = '' + recItem.value = null + recId.value = 0 + isFormEntryDialogOpen.value = false + isReadonly.value = false + + navigateTo({ + name: 'org-src-specialist-id', + params: { + id, + }, + }) + } + break case ActionEvents.showEdit: getCurrentSpecialistDetail(recId.value) diff --git a/app/models/specialist.ts b/app/models/specialist.ts index b4fe1115..64017679 100644 --- a/app/models/specialist.ts +++ b/app/models/specialist.ts @@ -1,9 +1,12 @@ -import { type Base, genBase } from "./_base" +import { type Base, genBase } from './_base' +import type { Unit } from './unit' export interface Specialist extends Base { code: string name: string unit_id?: number | string | null + + unit?: Unit | null } export function genSpecialist(): Specialist { @@ -11,6 +14,6 @@ export function genSpecialist(): Specialist { ...genBase(), code: '', name: '', - unit_id: 0 + unit_id: 0, } } diff --git a/app/pages/(features)/org-src/specialist/[id]/index.vue b/app/pages/(features)/org-src/specialist/[id]/index.vue new file mode 100644 index 00000000..c5554a71 --- /dev/null +++ b/app/pages/(features)/org-src/specialist/[id]/index.vue @@ -0,0 +1,42 @@ + + +