feat(specialist-position): implement crud operations for specialist positions

add handler, service, schema and update components for specialist position management
update list configuration and form to handle specialist relations
This commit is contained in:
Khafid Prayoga
2025-10-31 10:55:45 +07:00
parent bf441ff714
commit c340de3114
6 changed files with 143 additions and 53 deletions
@@ -3,16 +3,17 @@
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
import AppSpecialistList from '~/components/app/specialist/list.vue'
import AppSpecialistEntryForm from '~/components/app/specialist/entry-form.vue'
import AppSpecialistPositionList from '~/components/app/specialist-position/list.vue'
import AppSpecialistPositionEntryForm from '~/components/app/specialist-position/entry-form.vue'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
import { toast } from '~/components/pub/ui/toast'
import { config } from '~/components/app/specialist-position/list.cfg'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { SpecialistSchema, type SpecialistFormData } from '~/schemas/specialist.schema'
import { type SpecialistPositionFormData, SpecialistPositionSchema } from '~/schemas/specialist-position.schema'
// Handlers
import {
@@ -28,13 +29,15 @@ import {
handleActionEdit,
handleActionRemove,
handleCancelForm,
} from '~/handlers/specialist.handler'
} from '~/handlers/specialist-position.handler'
// Services
import { getList, getDetail } from '~/services/specialist.service'
import { getValueLabelList as getUnitList } from '~/services/unit.service'
import { getList, getDetail } from '~/services/specialist-position.service'
import { getValueLabelList as getValueLabelSpecialistList } from '~/services/specialist.service'
import { getValueLabelList as getEmployeeLabelList } from '~/services/employee.service'
const units = ref<{ value: string | number; label: string }[]>([])
const specialists = ref<{ value: string | number; label: string }[]>([])
const employees = ref<{ value: string | number; label: string }[]>([])
const title = ref('')
const {
@@ -52,11 +55,11 @@ const {
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
includes: 'unit',
includes: 'specialist,Employee.Person',
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'specialist',
entityName: 'specialist-position',
})
const headerPrep: HeaderPrep = {
@@ -98,18 +101,17 @@ const getCurrentSpecialistDetail = async (id: number | string) => {
isFormEntryDialogOpen.value = true
}
}
// Watch for row actions when recId or recAction changes
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
getCurrentSpecialistDetail(recId.value)
title.value = 'Detail Spesialis'
title.value = 'Detail Spesialis Posisi'
isReadonly.value = true
break
case ActionEvents.showEdit:
getCurrentSpecialistDetail(recId.value)
title.value = 'Edit Spesialis'
title.value = 'Edit Spesialis Posisi'
isReadonly.value = false
break
case ActionEvents.showConfirmDelete:
@@ -119,8 +121,18 @@ watch([recId, recAction], () => {
})
onMounted(async () => {
units.value = await getUnitList()
await getSpecialistList()
try {
specialists.value = await getValueLabelSpecialistList({ sort: 'createdAt:asc', 'page-size': 100 })
employees.value = await getEmployeeLabelList({ sort: 'createdAt:asc', 'page-size': 100, includes: 'person' })
} catch (err) {
console.log(err)
// show toast
toast({
title: 'Terjadi Kesalahan',
description: 'Terjadi kesalahan saat memuat data',
variant: 'destructive',
})
}
})
</script>
@@ -131,13 +143,11 @@ onMounted(async () => {
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
/>
<AppSpecialistPositionList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Spesialis'"
@@ -151,13 +161,14 @@ onMounted(async () => {
"
>
<AppSpecialistPositionEntryForm
:schema="SpecialistSchema"
:units="units"
:schema="SpecialistPositionSchema"
:specialists="specialists"
:employees="employees"
:values="recItem"
:is-loading="isProcessing"
:is-readonly="isReadonly"
@submit="
(values: SpecialistFormData | Record<string, any>, resetForm: () => void) => {
(values: SpecialistPositionFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) {
handleActionEdit(recId, values, getSpecialistList, resetForm, toast)
return
@@ -168,7 +179,6 @@ onMounted(async () => {
@cancel="handleCancelForm"
/>
</Dialog>
<!-- Record Confirmation Modal -->
<RecordConfirmation
v-model:open="isRecordConfirmationOpen"
@@ -178,18 +188,14 @@ onMounted(async () => {
@cancel=""
>
<template #default="{ record }">
<div class="text-sm">
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
<div class="space-y-1 text-sm">
<p
v-for="field in config.delKeyNames"
:key="field.key"
:v-if="record?.[field.key]"
>
<span class="font-semibold">{{ field.label }}:</span>
{{ record[field.key] }}
</p>
</div>
</template>