From b5846adb7e130a2aaa5bf508f782a351e7f4a098 Mon Sep 17 00:00:00 2001 From: Khafid Prayoga Date: Mon, 8 Dec 2025 11:32:18 +0700 Subject: [PATCH] composables: cherry-pick --- app/composables/usePaginatedList.ts | 36 ++++++++++---- app/composables/useQueryCRUD.ts | 74 +++++++++++++++++++++-------- 2 files changed, 83 insertions(+), 27 deletions(-) diff --git a/app/composables/usePaginatedList.ts b/app/composables/usePaginatedList.ts index 71dd99b3..ce5dbb40 100644 --- a/app/composables/usePaginatedList.ts +++ b/app/composables/usePaginatedList.ts @@ -2,7 +2,6 @@ import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type' import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type' import { refDebounced, useUrlSearchParams } from '@vueuse/core' import * as z from 'zod' -import { is } from "date-fns/locale" // Default query schema yang bisa digunakan semua list export const defaultQuerySchema = z.object({ @@ -38,10 +37,23 @@ interface UsePaginatedListOptions { }> // Nama endpoint untuk logging error entityName: string + /** + * Apakah state harus disinkronkan ke URL Browser? + * Set `false` jika digunakan di dalam Modal, Drawer, atau Nested Component + * agar tidak menimpa URL halaman induk. + * @default true + */ + syncToUrl?: boolean } export function usePaginatedList(options: UsePaginatedListOptions) { - const { querySchema = defaultQuerySchema, defaultQuery = defaultQueryParams, fetchFn, entityName } = options + const { + querySchema = defaultQuerySchema, + defaultQuery = defaultQueryParams, + fetchFn, + entityName, + syncToUrl = true, // Default true agar behavior lama tetap jalan + } = options // State management const data = ref([]) @@ -49,11 +61,19 @@ export function usePaginatedList(options: UsePaginatedListOptions) { isTableLoading: false, }) - // URL state management - const queryParams = useUrlSearchParams('history', { - initialValue: defaultQuery, - removeFalsyValues: true, - }) + let queryParams: any + + if (syncToUrl) { + // Mode Halaman Utama: Sync ke URL + queryParams = useUrlSearchParams('history', { + initialValue: defaultQuery, + removeFalsyValues: true, + write: false, + }) + } else { + // Mode Nested/Modal: Local Reactive State + queryParams = reactive({ ...defaultQuery }) + } const params = computed(() => { const result = querySchema.safeParse(queryParams) @@ -168,7 +188,7 @@ export function usePaginatedList(options: UsePaginatedListOptions) { } } -export function transform(endpoint: string ,params: any): string { +export function transform(endpoint: string, params: any): string { const urlParams = new URLSearchParams() Object.entries(params).forEach(([key, value]) => { diff --git a/app/composables/useQueryCRUD.ts b/app/composables/useQueryCRUD.ts index b62eadfe..481142aa 100644 --- a/app/composables/useQueryCRUD.ts +++ b/app/composables/useQueryCRUD.ts @@ -3,18 +3,18 @@ import { useRoute, useRouter } from 'vue-router' export function useQueryCRUD(modeKey: string = 'mode', recordIdKey: string = 'record-id') { type params = { - mode: string, + mode: string recordId: any } const route = useRoute() const router = useRouter() - const crudQueryParams = computed ({ + const crudQueryParams = computed({ get: () => { return { mode: route.query[modeKey] && route.query[modeKey] === 'entry' ? 'entry' : 'list', - recordId: route.query[recordIdKey] + recordId: route.query[recordIdKey], } }, set: (val) => { @@ -30,17 +30,15 @@ export function useQueryCRUD(modeKey: string = 'mode', recordIdKey: string = 're }) const goToEntry = (myRecord_id?: any) => { - if(myRecord_id) { - crudQueryParams.value = { mode: 'entry', recordId: myRecord_id } + if (myRecord_id) { + crudQueryParams.value.mode = 'entry' + crudQueryParams.value.recordId = myRecord_id } else { - crudQueryParams.value = { mode: 'entry', recordId: undefined } + crudQueryParams.value.mode = 'entry' + crudQueryParams.value.recordId = undefined } } - const goToDetail = (myRecord_id: string|number) => { - crudQueryParams.value = { mode: 'list', recordId: String(myRecord_id) } - } - const backToList = () => { delete route.query[recordIdKey] router.push({ @@ -52,15 +50,22 @@ export function useQueryCRUD(modeKey: string = 'mode', recordIdKey: string = 're }) } - return { crudQueryParams, goToEntry, goToDetail, backToList } + return { crudQueryParams, goToEntry, backToList } } export function useQueryCRUDMode(key: string = 'mode') { const route = useRoute() const router = useRouter() - const mode = computed<'list' | 'entry'>({ - get: () => (route.query[key] && route.query[key] === 'entry' ? 'entry' : 'list'), + const mode = computed<'list' | 'entry' | 'view'>({ + get: () => { + const q = route.query[key] + + if (q === 'entry') return 'entry' + if (q === 'view') return 'view' + + return 'list' + }, set: (val) => { router.push({ path: route.path, @@ -72,9 +77,22 @@ export function useQueryCRUDMode(key: string = 'mode') { }, }) - const goToEntry = (myRecord_id?: any) => { - mode.value = 'entry' - if(myRecord_id) { + const fromView = computed(() => route.query['from'] === 'view') + + const goToEntry = (options?: { fromView?: boolean }) => { + router.push({ + path: route.path, + query: { + ...route.query, + [key]: 'entry', + from: options?.fromView ? 'view' : undefined, + }, + }) + } + + const goToView = (myRecord_id?: any) => { + mode.value = 'view' + if (myRecord_id) { myRecord_id.value = myRecord_id } } @@ -85,13 +103,32 @@ export function useQueryCRUDMode(key: string = 'mode') { query: { ...route.query, mode: 'list', - // HAPUS record-id recordIdKey: undefined, + from: undefined, }, }) } - return { mode, goToEntry, backToList } + const backToView = () => { + router.push({ + path: route.path, + query: { + ...route.query, + mode: 'view', + from: undefined, + }, + }) + } + + const goBack = () => { + if (fromView.value) { + backToView() + } else { + backToList() + } + } + + return { mode, fromView, goToEntry, goToView, backToList, backToView, goBack } } export function useQueryCRUDRecordId(key: string = 'record-id') { @@ -114,4 +151,3 @@ export function useQueryCRUDRecordId(key: string = 'record-id') { return { recordId } } -