16a4fc5d7f
Add try-catch block to handle potential errors during API calls for division details and employee list. Show toast notification when errors occur to improve user feedback.
235 lines
5.8 KiB
Vue
235 lines
5.8 KiB
Vue
<script setup lang="ts">
|
|
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
|
|
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
|
|
|
// Components
|
|
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
|
|
|
// Service
|
|
import type { Division } from '~/models/division'
|
|
import { getDetail as getDetailDivision } from '~/services/division.service'
|
|
|
|
// #region division positions
|
|
import { config } from '~/components/app/division/detail/list-cfg'
|
|
|
|
// Helpers
|
|
import { usePaginatedList } from '~/composables/usePaginatedList'
|
|
import { toast } from '~/components/pub/ui/toast'
|
|
|
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
|
// Types
|
|
import { DivisionPositionSchema, type DivisionPositionFormData } from '~/schemas/division-position.schema'
|
|
|
|
// Handlers
|
|
import {
|
|
recId,
|
|
recAction,
|
|
recItem,
|
|
isReadonly,
|
|
isProcessing,
|
|
isFormEntryDialogOpen,
|
|
isRecordConfirmationOpen,
|
|
onResetState,
|
|
handleActionSave,
|
|
handleActionEdit,
|
|
handleActionRemove,
|
|
handleCancelForm,
|
|
} from '~/handlers/division-position.handler'
|
|
|
|
// Services
|
|
import { getList, getDetail as getDetailDivisionPosition } from '~/services/division-position.service'
|
|
import { getValueLabelList as getEmployeeLabelList } from '~/services/employee.service'
|
|
|
|
const employees = ref<{ value: string | number; label: string }[]>([])
|
|
|
|
const title = ref('')
|
|
// #endregion
|
|
|
|
// #region Props & Emits
|
|
const props = defineProps<{
|
|
divisionId: number
|
|
}>()
|
|
const division = ref<Division>({} as Division)
|
|
// #endregion
|
|
|
|
// #region State & Computed
|
|
const {
|
|
data,
|
|
isLoading,
|
|
paginationMeta,
|
|
searchInput,
|
|
handlePageChange,
|
|
handleSearch,
|
|
fetchData: getDivisionPositionList,
|
|
} = usePaginatedList({
|
|
fetchFn: async (params: any) => {
|
|
const result = await getList({
|
|
'division-id': props.divisionId,
|
|
includes: 'Employee.Person',
|
|
search: params.search,
|
|
sort: 'createdAt:asc',
|
|
'page-number': params['page-number'] || 0,
|
|
'page-size': params['page-size'] || 10,
|
|
'page-no-limit': true,
|
|
})
|
|
return { success: result.success || false, body: result.body || {} }
|
|
},
|
|
entityName: 'division-position',
|
|
})
|
|
|
|
const dataMap = computed(() => {
|
|
return data.value.map((v, i) => {
|
|
return {
|
|
...v,
|
|
index: i + 1,
|
|
}
|
|
})
|
|
})
|
|
const headerPrep: HeaderPrep = {
|
|
title: 'Detail Divisi',
|
|
icon: 'i-lucide-user',
|
|
refSearchNav: {
|
|
placeholder: 'Cari (min. 3 karakter)...',
|
|
minLength: 3,
|
|
debounceMs: 500,
|
|
showValidationFeedback: true,
|
|
onInput: (value: string) => {
|
|
searchInput.value = value
|
|
},
|
|
onClick: () => {},
|
|
onClear: () => {},
|
|
},
|
|
addNav: {
|
|
label: 'Tambah Jabatan',
|
|
icon: 'i-lucide-plus',
|
|
onClick: () => {
|
|
recItem.value = null
|
|
recId.value = 0
|
|
isFormEntryDialogOpen.value = true
|
|
isReadonly.value = false
|
|
},
|
|
},
|
|
}
|
|
|
|
// #endregion
|
|
|
|
// #region Lifecycle Hooks
|
|
onMounted(async () => {
|
|
try {
|
|
const result = await getDetailDivision(props.divisionId)
|
|
if (result.success) {
|
|
division.value = result.body.data || {}
|
|
}
|
|
|
|
const res = await getEmployeeLabelList({ sort: 'createdAt:asc', 'page-size': 100, includes: 'person' })
|
|
employees.value = res
|
|
} catch (err) {
|
|
// show toast
|
|
toast({
|
|
title: 'Terjadi Kesalahan',
|
|
description: 'Terjadi kesalahan saat memuat data',
|
|
variant: 'destructive',
|
|
})
|
|
}
|
|
})
|
|
// #endregion
|
|
|
|
// #region Functions
|
|
// #endregion region
|
|
|
|
// #region Utilities & event handlers
|
|
// #endregion
|
|
|
|
// #region Watchers
|
|
// #endregion
|
|
provide('rec_id', recId)
|
|
provide('rec_action', recAction)
|
|
provide('rec_item', recItem)
|
|
provide('table_data_loader', isLoading)
|
|
|
|
// Watch for row actions when recId or recAction changes
|
|
watch([recId, recAction], () => {
|
|
console.log(recId, recAction)
|
|
switch (recAction.value) {
|
|
case ActionEvents.showEdit:
|
|
getDetailDivisionPosition(recId.value)
|
|
title.value = 'Edit Jabatan'
|
|
isReadonly.value = false
|
|
isFormEntryDialogOpen.value = true
|
|
break
|
|
case ActionEvents.showConfirmDelete:
|
|
isRecordConfirmationOpen.value = true
|
|
break
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Header
|
|
:prep="headerPrep"
|
|
:ref-search-nav="headerPrep.refSearchNav"
|
|
/>
|
|
|
|
<AppDivisionDetail :division="division" />
|
|
<div class="h-6"></div>
|
|
|
|
<LazyAppDivisionDetailList
|
|
:data="dataMap"
|
|
:pagination-meta="paginationMeta"
|
|
@page-change="handlePageChange"
|
|
/>
|
|
|
|
<Dialog
|
|
v-model:open="isFormEntryDialogOpen"
|
|
:title="!!recItem ? title : 'Tambah Jabatan'"
|
|
size="lg"
|
|
prevent-outside
|
|
@update:open="
|
|
(value: any) => {
|
|
onResetState()
|
|
isFormEntryDialogOpen = value
|
|
}
|
|
"
|
|
>
|
|
<AppDivisionPositionEntry
|
|
:schema="DivisionPositionSchema"
|
|
:division-id="divisionId"
|
|
:employees="employees"
|
|
:values="recItem"
|
|
:is-loading="isProcessing"
|
|
:is-readonly="isReadonly"
|
|
@submit="
|
|
(values: DivisionPositionFormData | Record<string, any>, resetForm: () => void) => {
|
|
console.log(values)
|
|
if (recId > 0) {
|
|
handleActionEdit(recId, values, getDivisionPositionList, onResetState, toast)
|
|
return
|
|
}
|
|
handleActionSave(values, getDivisionPositionList, onResetState, toast)
|
|
}
|
|
"
|
|
@cancel="handleCancelForm"
|
|
/>
|
|
</Dialog>
|
|
<RecordConfirmation
|
|
v-model:open="isRecordConfirmationOpen"
|
|
action="delete"
|
|
:record="recItem"
|
|
@confirm="() => handleActionRemove(recId, getDivisionPositionList, toast)"
|
|
@cancel=""
|
|
>
|
|
<template #default="{ record }">
|
|
<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>
|
|
</RecordConfirmation>
|
|
</template>
|