import { computed, type ComputedRef, ref, type Ref } from 'vue' type DialogMode = 'create' | 'edit' | 'view' interface UseMaintenanceCrudFlowOptions { records: ComputedRef editingId: Ref dialogMode: Ref dialogVisible: Ref isLoading: Ref isSaving: Ref isDirty: Readonly> | ComputedRef clearAllErrors: () => void resetForm: () => void openEditDialog: (record: T) => void openViewDialog: (record: T) => void removeRecord: (id: number) => void describeRecord: (record: T) => string onCloseReset?: () => void onAfterDelete?: (deletedId: number) => void } export interface UseMaintenanceCrudFlowResult { confirmCloseVisible: Ref confirmSaveVisible: Ref confirmDeleteVisible: Ref confirmSwitchVisible: Ref confirmNavigateVisible: Ref pendingDelete: Ref pendingDeleteLabel: ComputedRef currentRecordIndex: ComputedRef currentEditingRecord: ComputedRef hasPrevRecord: ComputedRef hasNextRecord: ComputedRef isEditMode: ComputedRef isViewMode: ComputedRef openAdjacentRecord: (direction: 'prev' | 'next') => void openEdgeRecord: (position: 'first' | 'last') => void switchToEditMode: () => void switchToViewMode: () => void confirmSwitch: () => void confirmNavigate: () => void requestDeleteConfirmation: (record: T) => void requestDeleteCurrent: () => void confirmDelete: () => void requestCloseDialog: () => void confirmClose: () => void closeDialog: () => void handleDialogVisibility: (nextValue: boolean) => void } export function useMaintenanceCrudFlow( options: UseMaintenanceCrudFlowOptions ): UseMaintenanceCrudFlowResult { const confirmCloseVisible = ref(false) const confirmSaveVisible = ref(false) const confirmDeleteVisible = ref(false) const confirmSwitchVisible = ref(false) const confirmNavigateVisible = ref(false) const pendingDelete = ref(null) as Ref const pendingSwitchTarget = ref(null) const pendingNavigateTarget = ref(null) const isEditMode = computed(() => options.dialogMode.value === 'edit') const isViewMode = computed(() => options.dialogMode.value === 'view') const currentRecordIndex = computed(() => options.records.value.findIndex((item) => item.id === options.editingId.value) ) const currentEditingRecord = computed( () => options.records.value.find((item) => item.id === options.editingId.value) || null ) const hasPrevRecord = computed(() => currentRecordIndex.value > 0) const hasNextRecord = computed( () => currentRecordIndex.value >= 0 && currentRecordIndex.value < options.records.value.length - 1 ) const pendingDeleteLabel = computed(() => { if (!pendingDelete.value) return '這筆資料' return options.describeRecord(pendingDelete.value) }) function openAdjacentRecord(direction: 'prev' | 'next') { if (!isViewMode.value && !isEditMode.value) return const index = currentRecordIndex.value if (index < 0) return const targetIndex = direction === 'prev' ? index - 1 : index + 1 const target = options.records.value[targetIndex] if (!target) return if (isEditMode.value) { if (options.isDirty.value) { pendingNavigateTarget.value = target confirmNavigateVisible.value = true return } options.openEditDialog(target) return } options.openViewDialog(target) } function openEdgeRecord(position: 'first' | 'last') { if (!isViewMode.value && !isEditMode.value) return if (options.records.value.length === 0) return const target = position === 'first' ? options.records.value[0] : options.records.value.at(-1) if (!target) return if (isEditMode.value) { if (options.isDirty.value) { pendingNavigateTarget.value = target confirmNavigateVisible.value = true return } options.openEditDialog(target) return } options.openViewDialog(target) } function switchToEditMode() { if (!isViewMode.value) return const current = currentEditingRecord.value if (!current) return options.openEditDialog(current) } function switchToViewMode() { if (!isEditMode.value) return const current = currentEditingRecord.value if (!current) return if (options.isDirty.value) { pendingSwitchTarget.value = current confirmSwitchVisible.value = true return } options.openViewDialog(current) } function confirmSwitch() { const target = pendingSwitchTarget.value pendingSwitchTarget.value = null confirmSwitchVisible.value = false if (!target) return options.openViewDialog(target) } function confirmNavigate() { const target = pendingNavigateTarget.value pendingNavigateTarget.value = null confirmNavigateVisible.value = false if (!target) return options.openEditDialog(target) } function requestDeleteConfirmation(record: T) { pendingDelete.value = record confirmDeleteVisible.value = true } function requestDeleteCurrent() { const current = currentEditingRecord.value if (!current) return requestDeleteConfirmation(current) } function closeDialog() { options.dialogVisible.value = false options.isLoading.value = false options.isSaving.value = false confirmCloseVisible.value = false confirmSaveVisible.value = false confirmDeleteVisible.value = false confirmSwitchVisible.value = false confirmNavigateVisible.value = false pendingDelete.value = null pendingSwitchTarget.value = null pendingNavigateTarget.value = null options.dialogMode.value = 'create' options.editingId.value = null options.clearAllErrors() options.resetForm() options.onCloseReset?.() } function confirmDelete() { if (!pendingDelete.value) return const deletedId = pendingDelete.value.id options.removeRecord(deletedId) pendingDelete.value = null confirmDeleteVisible.value = false options.onAfterDelete?.(deletedId) if (options.editingId.value === deletedId) { closeDialog() } } function requestCloseDialog() { if (options.isDirty.value && !options.isSaving.value) { confirmCloseVisible.value = true return } closeDialog() } function confirmClose() { confirmCloseVisible.value = false closeDialog() } function handleDialogVisibility(nextValue: boolean) { if (nextValue) { options.dialogVisible.value = true return } requestCloseDialog() } return { confirmCloseVisible, confirmSaveVisible, confirmDeleteVisible, confirmSwitchVisible, confirmNavigateVisible, pendingDelete, pendingDeleteLabel, currentRecordIndex, currentEditingRecord, hasPrevRecord, hasNextRecord, isEditMode, isViewMode, openAdjacentRecord, openEdgeRecord, switchToEditMode, switchToViewMode, confirmSwitch, confirmNavigate, requestDeleteConfirmation, requestDeleteCurrent, confirmDelete, requestCloseDialog, confirmClose, closeDialog, handleDialogVisibility, } }