mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'fix/project-flock' into 'development'
[FIX/FE] Project Flock See merge request mbugroup/lti-web-client!412
This commit is contained in:
@@ -523,7 +523,7 @@ const useSelect = <T,>(
|
||||
|
||||
const qs = new URLSearchParams({
|
||||
...(params ?? {}),
|
||||
[searchKey]: inputValue ?? '',
|
||||
[searchKey ? searchKey : 'search']: inputValue ?? '',
|
||||
[pageKey]: String(pageIndex + 1),
|
||||
[limitKey]: String(limit),
|
||||
}).toString();
|
||||
|
||||
@@ -23,7 +23,6 @@ import { Icon } from '@iconify/react';
|
||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||
import { useRouter, usePathname } from 'next/navigation';
|
||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
||||
import { useUiStore } from '@/stores/ui/ui.store';
|
||||
import toast from 'react-hot-toast';
|
||||
import useSWR from 'swr';
|
||||
import { useFormik } from 'formik';
|
||||
@@ -148,7 +147,6 @@ const RowOptionsMenu = ({
|
||||
};
|
||||
|
||||
const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
||||
const pathname = usePathname();
|
||||
|
||||
const isSuccess = useProjectFlockStore((s) => s.isSuccess);
|
||||
@@ -185,7 +183,11 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
category: 'category',
|
||||
period: 'period',
|
||||
},
|
||||
|
||||
persist: true,
|
||||
storeName: 'project-flock-table',
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// ===== State =====
|
||||
@@ -425,18 +427,11 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
setIsDeleteLoading(false);
|
||||
setRowSelection({});
|
||||
};
|
||||
useEffect(() => {
|
||||
updateFilter('search', searchValue);
|
||||
}, [searchValue, updateFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
setTableState('project-flock-table', pathname);
|
||||
}, [pathname, setTableState]);
|
||||
|
||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
setSearchValue(e.target.value);
|
||||
updateFilter('search', e.target.value);
|
||||
};
|
||||
|
||||
const confirmApprovalHandler = async (
|
||||
notes: string,
|
||||
approvalAction: 'APPROVED' | 'REJECTED'
|
||||
|
||||
@@ -261,7 +261,7 @@ const ProjectFlockForm = ({
|
||||
isLoadingOptions: isLoadingFlocks,
|
||||
options: optionsFlock,
|
||||
loadMore: loadMoreFlock,
|
||||
} = useSelect(FlockApi.basePath, 'id', 'name', '', {
|
||||
} = useSelect(FlockApi.basePath, 'id', 'name', 'search', {
|
||||
project_category: selectedCategory,
|
||||
location_id: selectedLocation,
|
||||
area_id: selectedArea,
|
||||
@@ -279,7 +279,7 @@ const ProjectFlockForm = ({
|
||||
isLoadingOptions: isLoadingLocations,
|
||||
setInputValue: setInputValueLocation,
|
||||
loadMore: loadMoreLocation,
|
||||
} = useSelect(LocationApi.basePath, 'id', 'name', '', {
|
||||
} = useSelect(LocationApi.basePath, 'id', 'name', 'search', {
|
||||
area_id:
|
||||
selectedArea != ''
|
||||
? selectedArea
|
||||
@@ -291,7 +291,7 @@ const ProjectFlockForm = ({
|
||||
isLoadingOptions: isLoadingProductionStandards,
|
||||
setInputValue: setInputValueProductionStandard,
|
||||
loadMore: loadMoreProductionStandard,
|
||||
} = useSelect(ProductionStandardApi.basePath, 'id', 'name', '', {
|
||||
} = useSelect(ProductionStandardApi.basePath, 'id', 'name', 'search', {
|
||||
project_category: selectedCategory,
|
||||
});
|
||||
|
||||
@@ -307,7 +307,7 @@ const ProjectFlockForm = ({
|
||||
} = useSWR(kandangUrl, KandangApi.getAllFetcher);
|
||||
|
||||
const { data: periodFlocks, mutate: refreshPeriodFlocks } = useSWR(
|
||||
`${selectedFlock?.toString()}/periods`,
|
||||
selectedFlock ? `${selectedFlock?.toString()}/periods` : undefined,
|
||||
() => ProjectFlockApi.getNextPeriod(parseInt(selectedLocation as string))
|
||||
);
|
||||
|
||||
@@ -793,6 +793,7 @@ const ProjectFlockForm = ({
|
||||
formik.values.kandang_ids?.includes(kandang.id)
|
||||
)?.period
|
||||
: undefined;
|
||||
|
||||
const inputPeriod =
|
||||
(initialValues?.period ?? selectedPeriod == 0) ? 1 : selectedPeriod;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useMemo, useReducer } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useReducer } from 'react';
|
||||
import { useTableFilterStore } from '@/stores/table/table-filter.store';
|
||||
|
||||
/** Core filter shape (page + pageSize) extended by your custom fields */
|
||||
export type TableFilterState<TExtra extends Record<string, unknown>> = {
|
||||
@@ -30,6 +31,9 @@ export type UseTableFilterOptions<TExtra extends Record<string, unknown>> = {
|
||||
paramMap?: Partial<Record<keyof TableFilterState<TExtra>, string>>;
|
||||
/** If true, `toSearchParams`/`toQueryString` will omit values equal to defaults */
|
||||
omitDefaultsInUrl?: boolean;
|
||||
|
||||
persist?: boolean;
|
||||
storeName?: string;
|
||||
};
|
||||
|
||||
function clampToInt(n: number, min = 1) {
|
||||
@@ -90,9 +94,37 @@ function shallowEqual<T extends Record<string, unknown>>(
|
||||
export function useTableFilter<TExtra extends Record<string, unknown>>(
|
||||
options?: UseTableFilterOptions<TExtra>
|
||||
) {
|
||||
const defaults = useMemo(
|
||||
() => createInitialState<TExtra>(options),
|
||||
[options]
|
||||
if (options?.persist && !options?.storeName) {
|
||||
throw new Error(
|
||||
'storeName is required if persist is true in useTableFilter!'
|
||||
);
|
||||
}
|
||||
|
||||
const storeName = options?.storeName ?? '';
|
||||
const persistedState = useTableFilterStore(
|
||||
useCallback(
|
||||
(storeState) =>
|
||||
storeName
|
||||
? (storeState.data[storeName] as Partial<TableFilterState<TExtra>>)
|
||||
: undefined,
|
||||
[storeName]
|
||||
)
|
||||
);
|
||||
const setTableData = useTableFilterStore(
|
||||
(storeState) => storeState.setTableData
|
||||
);
|
||||
|
||||
const defaults = useMemo(() => {
|
||||
return createInitialState<TExtra>(options);
|
||||
}, [options]);
|
||||
|
||||
const initialState = useMemo(
|
||||
() =>
|
||||
({
|
||||
...defaults,
|
||||
...(persistedState as object),
|
||||
}) as TableFilterState<TExtra>,
|
||||
[defaults, persistedState]
|
||||
);
|
||||
|
||||
const [state, dispatch] = useReducer(
|
||||
@@ -106,15 +138,22 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
|
||||
case 'SET_PAGE_SIZE': {
|
||||
const pageSize = clampToInt(a.pageSize);
|
||||
const page = a.resetPage ? 1 : s.page;
|
||||
|
||||
return { ...s, pageSize, page };
|
||||
}
|
||||
case 'SET_FILTERS': {
|
||||
const page = a.resetPage ? 1 : s.page;
|
||||
|
||||
return { ...s, ...a.filters, page };
|
||||
}
|
||||
case 'UPDATE_FILTER': {
|
||||
const page = a.resetPage ? 1 : s.page;
|
||||
return { ...s, [a.key]: a.value, page } as TableFilterState<TExtra>;
|
||||
|
||||
return {
|
||||
...s,
|
||||
[a.key]: a.value,
|
||||
page,
|
||||
} as TableFilterState<TExtra>;
|
||||
}
|
||||
case 'REPLACE_ALL':
|
||||
return {
|
||||
@@ -128,12 +167,19 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
|
||||
return s;
|
||||
}
|
||||
},
|
||||
defaults
|
||||
initialState
|
||||
);
|
||||
|
||||
// Notify consumer on change (stable ref)
|
||||
useEffect(() => {
|
||||
if (!options?.persist || !storeName) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTableData(storeName, state);
|
||||
}, [options?.persist, setTableData, state, storeName]);
|
||||
|
||||
const onChange = options?.onChange;
|
||||
useMemo(() => {
|
||||
useEffect(() => {
|
||||
if (onChange) onChange(state);
|
||||
}, [state, onChange]);
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { create } from 'zustand';
|
||||
import { createJSONStorage, devtools, persist } from 'zustand/middleware';
|
||||
import { TableFilterStore } from '@/types/stores';
|
||||
|
||||
type TableFilterStoreState = TableFilterStore<
|
||||
Record<string, Record<string, unknown>>
|
||||
>;
|
||||
|
||||
export const useTableFilterStore = create<TableFilterStoreState>()(
|
||||
devtools(
|
||||
persist(
|
||||
(set) => ({
|
||||
data: {},
|
||||
|
||||
setData: (newData) => {
|
||||
set({ data: newData });
|
||||
},
|
||||
|
||||
setTableData: (key, tableData) => {
|
||||
set((state) => ({
|
||||
data: {
|
||||
...state.data,
|
||||
[key]: tableData,
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
setTableDataField: (key, field, value) => {
|
||||
set((state) => ({
|
||||
data: {
|
||||
...state.data,
|
||||
[key]: {
|
||||
...state.data[key],
|
||||
[field]: value,
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
setSearchValue: (key, searchValue) => {
|
||||
set((state) => ({
|
||||
data: {
|
||||
...state.data,
|
||||
[key]: {
|
||||
...state.data[key],
|
||||
|
||||
// search key
|
||||
search: searchValue,
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'table-filter-store',
|
||||
storage: createJSONStorage(() => sessionStorage),
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { createJSONStorage, devtools, persist } from 'zustand/middleware';
|
||||
|
||||
import { UIStore } from '@/types/stores';
|
||||
import { createMainUiSlice } from '@/stores/ui/slices/main.slice';
|
||||
@@ -20,6 +20,7 @@ export const useUiStore = create<UIStore>()(
|
||||
}),
|
||||
{
|
||||
name: 'search-store',
|
||||
storage: createJSONStorage(() => sessionStorage),
|
||||
partialize: (state) => ({
|
||||
key: state.key,
|
||||
path: state.path,
|
||||
|
||||
Vendored
+8
@@ -117,3 +117,11 @@ export type ProjectFlockSlice = {
|
||||
setCreatedProjectFlock: (data: ProjectFlock | null) => void;
|
||||
resetProjectFlock: () => void;
|
||||
};
|
||||
|
||||
export type TableFilterStore<T = Record<string, Record<string, unknown>>> = {
|
||||
data: T;
|
||||
setData: (newData: T) => void;
|
||||
setTableData: (key: string, tableData: Record<string, unknown>) => void;
|
||||
setTableDataField: (key: string, field: string, value: unknown) => void;
|
||||
setSearchValue: (key: string, searchValue: string) => void;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user