dev: hotfix

+ data table
+ genCrudHandler
+ crud-base
This commit is contained in:
2025-10-04 06:55:12 +07:00
parent d9cedf8d42
commit ce785f2092
3 changed files with 188 additions and 7 deletions
@@ -10,9 +10,9 @@ const props = defineProps<{
cols: Col[]
header: Th[][]
keys: string[]
funcParsed: RecStrFuncUnknown
funcHtml: RecStrFuncUnknown
funcComponent: RecStrFuncComponent
funcParsed?: RecStrFuncUnknown
funcHtml?: RecStrFuncUnknown
funcComponent?: RecStrFuncComponent
selectMode?: 'single' | 'multiple'
modelValue?: any[] | any
}>()
@@ -118,16 +118,16 @@ function handleActionCellClick(event: Event, _cellRef: string) {
<TableCell v-for="(key, cellIndex) in keys" :key="`cell-${rowIndex}-${cellIndex}`" class="border">
<!-- existing cell renderer -->
<component
:is="funcComponent[key]?.(row, rowIndex).component"
v-if="funcComponent[key]"
:is="funcComponent?.[key]?.(row, rowIndex).component"
v-if="funcComponent?.[key]"
:rec="row"
:idx="rowIndex"
v-bind="funcComponent[key]?.(row, rowIndex).props"
/>
<template v-else>
<div v-if="funcHtml[key]" v-html="funcHtml[key]?.(row, rowIndex)"></div>
<div v-if="funcHtml?.[key]" v-html="funcHtml?.[key]?.(row, rowIndex)"></div>
<template v-else>
{{ funcParsed[key]?.(row, rowIndex) ?? (row as any)[key] }}
{{ funcParsed?.[key]?.(row, rowIndex) ?? (row as any)[key] }}
</template>
</template>
</TableCell>
+104
View File
@@ -104,6 +104,110 @@ export function createCrudHandler<T = any>(crud: {
}
}
// Factory for CRUD handler state and actions
export function genCrudHandler<T = any>(crud: {
create: (...args: any[]) => Promise<any>
update: (...args: any[]) => Promise<any>
remove: (...args: any[]) => Promise<any>
}) {
const recId = ref<number>(0)
const recAction = ref<string>('')
const recItem = ref<T | null>(null)
const isReadonly = ref(false)
const isProcessing = ref(false)
const isFormEntryDialogOpen = ref(false)
const isRecordConfirmationOpen = ref(false)
function onResetState() {
recId.value = 0
recAction.value = ''
recItem.value = null
}
async function handleActionSave(values: any, refresh: () => void, reset: () => void, toast: ToastFn) {
isProcessing.value = true
await handleAsyncAction<[any], any>({
action: crud.create,
args: [values],
toast,
successMessage: 'Data berhasil disimpan',
errorMessage: 'Gagal menyimpan data',
onSuccess: () => {
isFormEntryDialogOpen.value = false
if (refresh) refresh()
},
onFinally: (isSuccess: boolean) => {
if (isSuccess) setTimeout(reset, 500)
isProcessing.value = false
},
})
}
async function handleActionEdit(
id: number | string,
values: any,
refresh: () => void,
reset: () => void,
toast: ToastFn,
) {
isProcessing.value = true
await handleAsyncAction<[number | string, any], any>({
action: crud.update,
args: [id, values],
toast,
successMessage: 'Data berhasil diubah',
errorMessage: 'Gagal mengubah data',
onSuccess: () => {
isFormEntryDialogOpen.value = false
if (refresh) refresh()
},
onFinally: (isSuccess: boolean) => {
if (isSuccess) setTimeout(reset, 500)
isProcessing.value = false
},
})
}
async function handleActionRemove(id: number | string, refresh: () => void, toast: ToastFn) {
isProcessing.value = true
await handleAsyncAction<[number | string], any>({
action: crud.remove,
args: [id],
toast,
successMessage: 'Data berhasil dihapus',
errorMessage: 'Gagal menghapus data',
onSuccess: () => {
isRecordConfirmationOpen.value = false
if (refresh) refresh()
},
onFinally: () => {
isProcessing.value = false
},
})
}
function handleCancelForm(reset: () => void) {
isFormEntryDialogOpen.value = false
isReadonly.value = false
setTimeout(reset, 300)
}
return {
recId,
recAction,
recItem,
isReadonly,
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
handleCancelForm,
}
}
// Reusable async handler for CRUD actions with toast and state management
export type ToastFn = (params: { title: string; description: string; variant: 'default' | 'destructive' }) => void
+77
View File
@@ -0,0 +1,77 @@
import { xfetch } from '~/composables/useXfetch'
export async function create(path: string, data: any) {
try {
const resp = await xfetch(path, 'POST', data)
const result: any = {}
result.success = resp.success
result.body = (resp.body as Record<string, any>) || {}
return result
} catch (error) {
console.error('Error posting division:', error)
throw new Error('Failed to post division')
}
}
export async function getList(path: string, params: any = null) {
try {
let url = path
if (params && typeof params === 'object' && Object.keys(params).length > 0) {
const searchParams = new URLSearchParams()
for (const key in params) {
if (params[key] !== null && params[key] !== undefined && params[key] !== '') {
searchParams.append(key, params[key])
}
}
const queryString = searchParams.toString()
if (queryString) url += `?${queryString}`
}
const resp = await xfetch(path, 'GET')
const result: any = {}
result.success = resp.success
result.body = (resp.body as Record<string, any>) || {}
return result
} catch (error) {
console.error('Error fetching divisions:', error)
throw new Error('Failed to fetch divisions')
}
}
export async function getDetail(path: string, id: number | string) {
try {
const resp = await xfetch(`${path}/${id}`, 'GET')
const result: any = {}
result.success = resp.success
result.body = (resp.body as Record<string, any>) || {}
return result
} catch (error) {
console.error('Error fetching division detail:', error)
throw new Error('Failed to get division detail')
}
}
export async function update(path: string, id: number | string, data: any) {
try {
const resp = await xfetch(`${path}/${id}`, 'PATCH', data)
const result: any = {}
result.success = resp.success
result.body = (resp.body as Record<string, any>) || {}
return result
} catch (error) {
console.error('Error putting division:', error)
throw new Error('Failed to put division')
}
}
export async function remove(path: string, id: number | string) {
try {
const resp = await xfetch(`${path}/${id}`, 'DELETE')
const result: any = {}
result.success = resp.success
result.body = (resp.body as Record<string, any>) || {}
return result
} catch (error) {
console.error('Error deleting data:', error)
throw new Error('Failed to delete division')
}
}