mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
fix: persist table filter state in master data
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -20,8 +20,6 @@ import { Area } from '@/types/api/master-data/area';
|
|||||||
import { AreaApi } from '@/services/api/master-data';
|
import { AreaApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
@@ -103,9 +101,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AreasTable = () => {
|
const AreasTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -114,12 +109,14 @@ const AreasTable = () => {
|
|||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter({
|
||||||
initial: {
|
initial: {
|
||||||
search: searchValue,
|
search: '',
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'areas-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
@@ -137,17 +134,8 @@ const AreasTable = () => {
|
|||||||
const [selectedArea, setSelectedArea] = useState<Area | undefined>(undefined);
|
const [selectedArea, setSelectedArea] = useState<Area | undefined>(undefined);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('areas-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -20,8 +20,6 @@ import { Bank } from '@/types/api/master-data/bank';
|
|||||||
import { BankApi } from '@/services/api/master-data';
|
import { BankApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
@@ -103,9 +101,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const BanksTable = () => {
|
const BanksTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -114,12 +109,14 @@ const BanksTable = () => {
|
|||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter({
|
||||||
initial: {
|
initial: {
|
||||||
search: searchValue,
|
search: '',
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'banks-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
@@ -137,17 +134,8 @@ const BanksTable = () => {
|
|||||||
const [selectedBank, setSelectedBank] = useState<Bank | undefined>(undefined);
|
const [selectedBank, setSelectedBank] = useState<Bank | undefined>(undefined);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('banks-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -20,8 +20,6 @@ import { Customer } from '@/types/api/master-data/customer';
|
|||||||
import { CustomerApi } from '@/services/api/master-data';
|
import { CustomerApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
@@ -103,9 +101,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CustomersTable = () => {
|
const CustomersTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -114,12 +109,14 @@ const CustomersTable = () => {
|
|||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter({
|
||||||
initial: {
|
initial: {
|
||||||
search: searchValue,
|
search: '',
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'customers-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
@@ -139,17 +136,8 @@ const CustomersTable = () => {
|
|||||||
>(undefined);
|
>(undefined);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('customers-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -20,8 +20,6 @@ import { Flock } from '@/types/api/master-data/flock';
|
|||||||
import { FlockApi } from '@/services/api/master-data';
|
import { FlockApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
@@ -103,9 +101,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FlockTable = () => {
|
const FlockTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -114,12 +109,14 @@ const FlockTable = () => {
|
|||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter({
|
||||||
initial: {
|
initial: {
|
||||||
search: searchValue,
|
search: '',
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'flock-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
@@ -139,17 +136,8 @@ const FlockTable = () => {
|
|||||||
);
|
);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('flocks-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
ChangeEventHandler,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -35,7 +28,6 @@ import { User } from '@/types/api/api-general';
|
|||||||
import { formatNumber } from '@/lib/helper';
|
import { formatNumber } from '@/lib/helper';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import {
|
import {
|
||||||
KandangFilterSchema,
|
KandangFilterSchema,
|
||||||
KandangFilterType,
|
KandangFilterType,
|
||||||
@@ -122,20 +114,21 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const KandangsTable = () => {
|
const KandangsTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<{
|
||||||
|
search: string;
|
||||||
|
locationFilter?: OptionType<string>;
|
||||||
|
picFilter?: OptionType<string>;
|
||||||
|
}>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
locationFilter: '',
|
locationFilter: undefined,
|
||||||
picFilter: '',
|
picFilter: undefined,
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
@@ -143,6 +136,8 @@ const KandangsTable = () => {
|
|||||||
locationFilter: 'location_id',
|
locationFilter: 'location_id',
|
||||||
picFilter: 'pic_id',
|
picFilter: 'pic_id',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'kandangs-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FILTER MODAL STATE =====
|
// ===== FILTER MODAL STATE =====
|
||||||
@@ -151,22 +146,34 @@ const KandangsTable = () => {
|
|||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<KandangFilterType>({
|
const formik = useFormik<KandangFilterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
location_id: null,
|
location: tableFilterState.locationFilter,
|
||||||
pic_id: null,
|
pic: tableFilterState.picFilter,
|
||||||
},
|
},
|
||||||
validationSchema: KandangFilterSchema,
|
validationSchema: KandangFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('locationFilter', values.location_id || '');
|
updateFilter('locationFilter', values.location || undefined, true);
|
||||||
updateFilter('picFilter', values.pic_id || '');
|
updateFilter('picFilter', values.pic || undefined, true);
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
});
|
||||||
updateFilter('locationFilter', '');
|
|
||||||
updateFilter('picFilter', '');
|
const formikResetHandler = () => {
|
||||||
|
updateFilter('locationFilter', undefined, true);
|
||||||
|
updateFilter('picFilter', undefined, true);
|
||||||
|
|
||||||
|
formik.resetForm({
|
||||||
|
values: {
|
||||||
|
location: undefined,
|
||||||
|
pic: undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
filterModal.closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const { setFieldValue } = formik;
|
||||||
|
|
||||||
// ===== LOCATION OPTIONS =====
|
// ===== LOCATION OPTIONS =====
|
||||||
const {
|
const {
|
||||||
setInputValue: setLocationInputValue,
|
setInputValue: setLocationInputValue,
|
||||||
@@ -194,43 +201,15 @@ const KandangsTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ===== FILTER HANDLERS =====
|
// ===== FILTER HANDLERS =====
|
||||||
const handleFilterLocationChange = useCallback(
|
const handleFilterLocationChange = (
|
||||||
(val: OptionType | OptionType[] | null) => {
|
val: OptionType | OptionType[] | null
|
||||||
const location = val as OptionType | null;
|
) => {
|
||||||
const locationId = location?.value ? String(location.value) : null;
|
setFieldValue('location', val);
|
||||||
|
};
|
||||||
|
|
||||||
formik.setFieldValue('location_id', locationId);
|
const handleFilterPicChange = (val: OptionType | OptionType[] | null) => {
|
||||||
},
|
setFieldValue('pic', val);
|
||||||
[formik]
|
};
|
||||||
);
|
|
||||||
|
|
||||||
const handleFilterPicChange = useCallback(
|
|
||||||
(val: OptionType | OptionType[] | null) => {
|
|
||||||
const pic = val as OptionType | null;
|
|
||||||
const picId = pic?.value ? String(pic.value) : null;
|
|
||||||
|
|
||||||
formik.setFieldValue('pic_id', picId);
|
|
||||||
},
|
|
||||||
[formik]
|
|
||||||
);
|
|
||||||
|
|
||||||
// ===== FILTER HELPERS =====
|
|
||||||
const locationIdValue = useMemo(() => {
|
|
||||||
if (!formik.values.location_id) return null;
|
|
||||||
return (
|
|
||||||
locationOptions.find(
|
|
||||||
(opt) => String(opt.value) === formik.values.location_id
|
|
||||||
) || null
|
|
||||||
);
|
|
||||||
}, [formik.values.location_id, locationOptions]);
|
|
||||||
|
|
||||||
const picIdValue = useMemo(() => {
|
|
||||||
if (!formik.values.pic_id) return null;
|
|
||||||
return (
|
|
||||||
picOptions.find((opt) => String(opt.value) === formik.values.pic_id) ||
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}, [formik.values.pic_id, picOptions]);
|
|
||||||
|
|
||||||
// ===== HANDLE FILTER MODAL OPEN =====
|
// ===== HANDLE FILTER MODAL OPEN =====
|
||||||
const handleFilterModalOpen = () => {
|
const handleFilterModalOpen = () => {
|
||||||
@@ -255,17 +234,8 @@ const KandangsTable = () => {
|
|||||||
);
|
);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('kandangs-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
@@ -475,13 +445,13 @@ const KandangsTable = () => {
|
|||||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
<form onSubmit={formik.handleSubmit} onReset={formikResetHandler}>
|
||||||
<div className='p-4 flex flex-col gap-1.5'>
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Lokasi'
|
label='Lokasi'
|
||||||
placeholder='Pilih Lokasi'
|
placeholder='Pilih Lokasi'
|
||||||
options={locationOptions}
|
options={locationOptions}
|
||||||
value={locationIdValue}
|
value={formik.values.location}
|
||||||
onChange={handleFilterLocationChange}
|
onChange={handleFilterLocationChange}
|
||||||
onInputChange={setLocationInputValue}
|
onInputChange={setLocationInputValue}
|
||||||
isLoading={isLoadingLocationOptions}
|
isLoading={isLoadingLocationOptions}
|
||||||
@@ -494,7 +464,7 @@ const KandangsTable = () => {
|
|||||||
label='PIC'
|
label='PIC'
|
||||||
placeholder='Pilih PIC'
|
placeholder='Pilih PIC'
|
||||||
options={picOptions}
|
options={picOptions}
|
||||||
value={picIdValue}
|
value={formik.values.pic}
|
||||||
onChange={handleFilterPicChange}
|
onChange={handleFilterPicChange}
|
||||||
onInputChange={setPicInputValue}
|
onInputChange={setPicInputValue}
|
||||||
isLoading={isLoadingPicOptions}
|
isLoading={isLoadingPicOptions}
|
||||||
@@ -510,17 +480,14 @@ const KandangsTable = () => {
|
|||||||
type='button'
|
type='button'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
onClick={() => {
|
onClick={formikResetHandler}
|
||||||
formik.resetForm();
|
|
||||||
filterModal.closeModal();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type='submit'
|
type='submit'
|
||||||
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
|
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
|
||||||
disabled={!formik.isValid || formik.isSubmitting}
|
disabled={!formik.isValid}
|
||||||
>
|
>
|
||||||
Apply Filter
|
Apply Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { string, object } from 'yup';
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
export const KandangFilterSchema = object().shape({
|
export const KandangFilterSchema = Yup.object().shape({
|
||||||
location_id: string().nullable(),
|
location: Yup.object({
|
||||||
pic_id: string().nullable(),
|
value: Yup.string().nullable(),
|
||||||
|
label: Yup.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
|
|
||||||
|
pic: Yup.object({
|
||||||
|
value: Yup.string().nullable(),
|
||||||
|
label: Yup.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type KandangFilterType = {
|
export type KandangFilterType = {
|
||||||
location_id: string | null;
|
location?: OptionType<string>;
|
||||||
pic_id: string | null;
|
pic?: OptionType<string>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
ChangeEventHandler,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -32,7 +25,6 @@ import { Area } from '@/types/api/master-data/area';
|
|||||||
import { LocationApi, AreaApi } from '@/services/api/master-data';
|
import { LocationApi, AreaApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import {
|
import {
|
||||||
LocationFilterSchema,
|
LocationFilterSchema,
|
||||||
LocationFilterType,
|
LocationFilterType,
|
||||||
@@ -118,25 +110,27 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const LocationsTable = () => {
|
const LocationsTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<{
|
||||||
|
search: string;
|
||||||
|
areaFilter?: OptionType<string>;
|
||||||
|
}>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
areaFilter: '',
|
areaFilter: undefined,
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
areaFilter: 'area_id',
|
areaFilter: 'area_id',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'locations-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FILTER MODAL STATE =====
|
// ===== FILTER MODAL STATE =====
|
||||||
@@ -145,19 +139,28 @@ const LocationsTable = () => {
|
|||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<LocationFilterType>({
|
const formik = useFormik<LocationFilterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
area_id: null,
|
area: tableFilterState.areaFilter,
|
||||||
},
|
},
|
||||||
validationSchema: LocationFilterSchema,
|
validationSchema: LocationFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('areaFilter', values.area_id || '');
|
updateFilter('areaFilter', values.area || undefined, true);
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
});
|
||||||
updateFilter('areaFilter', '');
|
|
||||||
|
const formikResetHandler = () => {
|
||||||
|
updateFilter('areaFilter', undefined, true);
|
||||||
|
|
||||||
|
formik.resetForm({
|
||||||
|
values: {
|
||||||
|
area: undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
filterModal.closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
// ===== AREA OPTIONS =====
|
// ===== AREA OPTIONS =====
|
||||||
const {
|
const {
|
||||||
setInputValue: setAreaInputValue,
|
setInputValue: setAreaInputValue,
|
||||||
@@ -172,24 +175,9 @@ const LocationsTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ===== FILTER HANDLERS =====
|
// ===== FILTER HANDLERS =====
|
||||||
const handleFilterAreaChange = useCallback(
|
const handleFilterAreaChange = (val: OptionType | OptionType[] | null) => {
|
||||||
(val: OptionType | OptionType[] | null) => {
|
formik.setFieldValue('area', val);
|
||||||
const area = val as OptionType | null;
|
};
|
||||||
const areaId = area?.value ? String(area.value) : null;
|
|
||||||
|
|
||||||
formik.setFieldValue('area_id', areaId);
|
|
||||||
},
|
|
||||||
[formik]
|
|
||||||
);
|
|
||||||
|
|
||||||
// ===== FILTER HELPERS =====
|
|
||||||
const areaIdValue = useMemo(() => {
|
|
||||||
if (!formik.values.area_id) return null;
|
|
||||||
return (
|
|
||||||
areaOptions.find((opt) => String(opt.value) === formik.values.area_id) ||
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}, [formik.values.area_id, areaOptions]);
|
|
||||||
|
|
||||||
// ===== HANDLE FILTER MODAL OPEN =====
|
// ===== HANDLE FILTER MODAL OPEN =====
|
||||||
const handleFilterModalOpen = () => {
|
const handleFilterModalOpen = () => {
|
||||||
@@ -212,19 +200,10 @@ const LocationsTable = () => {
|
|||||||
>(undefined);
|
>(undefined);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('locations-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
@@ -425,13 +404,13 @@ const LocationsTable = () => {
|
|||||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
<form onSubmit={formik.handleSubmit} onReset={formikResetHandler}>
|
||||||
<div className='p-4 flex flex-col gap-1.5'>
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Area'
|
label='Area'
|
||||||
placeholder='Pilih Area'
|
placeholder='Pilih Area'
|
||||||
options={areaOptions}
|
options={areaOptions}
|
||||||
value={areaIdValue}
|
value={formik.values.area}
|
||||||
onChange={handleFilterAreaChange}
|
onChange={handleFilterAreaChange}
|
||||||
onInputChange={setAreaInputValue}
|
onInputChange={setAreaInputValue}
|
||||||
isLoading={isLoadingAreaOptions}
|
isLoading={isLoadingAreaOptions}
|
||||||
@@ -447,10 +426,7 @@ const LocationsTable = () => {
|
|||||||
type='button'
|
type='button'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
onClick={() => {
|
onClick={formikResetHandler}
|
||||||
formik.resetForm();
|
|
||||||
filterModal.closeModal();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { string, object } from 'yup';
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
export const LocationFilterSchema = object().shape({
|
export const LocationFilterSchema = Yup.object().shape({
|
||||||
area_id: string().nullable(),
|
area: Yup.object({
|
||||||
|
value: Yup.string().nullable(),
|
||||||
|
label: Yup.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type LocationFilterType = {
|
export type LocationFilterType = {
|
||||||
area_id: string | null;
|
area?: OptionType<string>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ import { Nonstock } from '@/types/api/master-data/nonstock';
|
|||||||
import { NonstockApi } from '@/services/api/master-data';
|
import { NonstockApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
@@ -103,9 +101,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NonstocksTable = () => {
|
const NonstocksTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -114,22 +109,16 @@ const NonstocksTable = () => {
|
|||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter({
|
||||||
initial: {
|
initial: {
|
||||||
search: searchValue,
|
search: '',
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'nonstock-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('nonstocks-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -148,8 +137,7 @@ const NonstocksTable = () => {
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -21,7 +20,6 @@ import { ProductCategory } from '@/types/api/master-data/product-category';
|
|||||||
import { ProductCategoryApi } from '@/services/api/master-data';
|
import { ProductCategoryApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
@@ -103,9 +101,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ProductCategoryTable = () => {
|
const ProductCategoryTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -120,12 +115,10 @@ const ProductCategoryTable = () => {
|
|||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'product-category-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -144,8 +137,7 @@ const ProductCategoryTable = () => {
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
@@ -214,10 +206,6 @@ const ProductCategoryTable = () => {
|
|||||||
[tableFilterState.pageSize, tableFilterState.page, deleteModal]
|
[tableFilterState.pageSize, tableFilterState.page, deleteModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('product-category-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
ChangeEventHandler,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -33,7 +26,6 @@ import { ProductApi, ProductCategoryApi } from '@/services/api/master-data';
|
|||||||
import { formatCurrency } from '@/lib/helper';
|
import { formatCurrency } from '@/lib/helper';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import {
|
import {
|
||||||
ProductFilterSchema,
|
ProductFilterSchema,
|
||||||
ProductFilterType,
|
ProductFilterType,
|
||||||
@@ -119,25 +111,27 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ProductsTable = () => {
|
const ProductsTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<{
|
||||||
|
search: string;
|
||||||
|
productCategoryFilter?: OptionType<string>;
|
||||||
|
}>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
productCategoryFilter: '',
|
productCategoryFilter: undefined,
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
productCategoryFilter: 'product_category_id',
|
productCategoryFilter: 'product_category_id',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'product-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FILTER MODAL STATE =====
|
// ===== FILTER MODAL STATE =====
|
||||||
@@ -146,19 +140,32 @@ const ProductsTable = () => {
|
|||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<ProductFilterType>({
|
const formik = useFormik<ProductFilterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
product_category_id: null,
|
product_category: tableFilterState.productCategoryFilter,
|
||||||
},
|
},
|
||||||
validationSchema: ProductFilterSchema,
|
validationSchema: ProductFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('productCategoryFilter', values.product_category_id || '');
|
updateFilter(
|
||||||
|
'productCategoryFilter',
|
||||||
|
values.product_category || undefined,
|
||||||
|
true
|
||||||
|
);
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
});
|
||||||
updateFilter('productCategoryFilter', '');
|
|
||||||
|
const formikResetHandler = () => {
|
||||||
|
updateFilter('productCategoryFilter', undefined, true);
|
||||||
|
|
||||||
|
formik.resetForm({
|
||||||
|
values: {
|
||||||
|
product_category: undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
filterModal.closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
// ===== PRODUCT CATEGORY OPTIONS =====
|
// ===== PRODUCT CATEGORY OPTIONS =====
|
||||||
const {
|
const {
|
||||||
setInputValue: setProductCategoryInputValue,
|
setInputValue: setProductCategoryInputValue,
|
||||||
@@ -173,25 +180,11 @@ const ProductsTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ===== FILTER HANDLERS =====
|
// ===== FILTER HANDLERS =====
|
||||||
const handleFilterProductCategoryChange = useCallback(
|
const handleFilterProductCategoryChange = (
|
||||||
(val: OptionType | OptionType[] | null) => {
|
val: OptionType | OptionType[] | null
|
||||||
const category = val as OptionType | null;
|
) => {
|
||||||
const categoryId = category?.value ? String(category.value) : null;
|
formik.setFieldValue('product_category', val);
|
||||||
|
};
|
||||||
formik.setFieldValue('product_category_id', categoryId);
|
|
||||||
},
|
|
||||||
[formik]
|
|
||||||
);
|
|
||||||
|
|
||||||
// ===== FILTER HELPERS =====
|
|
||||||
const productCategoryIdValue = useMemo(() => {
|
|
||||||
if (!formik.values.product_category_id) return null;
|
|
||||||
return (
|
|
||||||
productCategoryOptions.find(
|
|
||||||
(opt) => String(opt.value) === formik.values.product_category_id
|
|
||||||
) || null
|
|
||||||
);
|
|
||||||
}, [formik.values.product_category_id, productCategoryOptions]);
|
|
||||||
|
|
||||||
// ===== HANDLE FILTER MODAL OPEN =====
|
// ===== HANDLE FILTER MODAL OPEN =====
|
||||||
const handleFilterModalOpen = () => {
|
const handleFilterModalOpen = () => {
|
||||||
@@ -199,10 +192,6 @@ const ProductsTable = () => {
|
|||||||
formik.validateForm();
|
formik.validateForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -220,13 +209,8 @@ const ProductsTable = () => {
|
|||||||
);
|
);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('product-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
@@ -477,13 +461,13 @@ const ProductsTable = () => {
|
|||||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
<form onSubmit={formik.handleSubmit} onReset={formikResetHandler}>
|
||||||
<div className='p-4 flex flex-col gap-1.5'>
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Kategori Produk'
|
label='Kategori Produk'
|
||||||
placeholder='Pilih Kategori Produk'
|
placeholder='Pilih Kategori Produk'
|
||||||
options={productCategoryOptions}
|
options={productCategoryOptions}
|
||||||
value={productCategoryIdValue}
|
value={formik.values.product_category}
|
||||||
onChange={handleFilterProductCategoryChange}
|
onChange={handleFilterProductCategoryChange}
|
||||||
onInputChange={setProductCategoryInputValue}
|
onInputChange={setProductCategoryInputValue}
|
||||||
isLoading={isLoadingProductCategoryOptions}
|
isLoading={isLoadingProductCategoryOptions}
|
||||||
@@ -499,10 +483,7 @@ const ProductsTable = () => {
|
|||||||
type='button'
|
type='button'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
onClick={() => {
|
onClick={formikResetHandler}
|
||||||
formik.resetForm();
|
|
||||||
filterModal.closeModal();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { string, object } from 'yup';
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
export const ProductFilterSchema = object().shape({
|
export const ProductFilterSchema = Yup.object().shape({
|
||||||
product_category_id: string().nullable(),
|
product_category: Yup.object({
|
||||||
|
value: Yup.string().nullable(),
|
||||||
|
label: Yup.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ProductFilterType = {
|
export type ProductFilterType = {
|
||||||
product_category_id: string | null;
|
product_category?: OptionType<string>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -128,27 +128,44 @@ const ProductionStandardTable = () => {
|
|||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
projectCategoryFilter: 'project_category',
|
projectCategoryFilter: 'project_category',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'production-standard-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FILTER MODAL STATE =====
|
// ===== FILTER MODAL STATE =====
|
||||||
const filterModal = useModal();
|
const filterModal = useModal();
|
||||||
|
|
||||||
|
// ===== FILTER INITIAL VALUES (derived from persisted state) =====
|
||||||
|
const filterInitialValues = useMemo<ProductionStandardFilterType>(
|
||||||
|
() => ({
|
||||||
|
project_category: tableFilterState.projectCategoryFilter || null,
|
||||||
|
}),
|
||||||
|
[tableFilterState.projectCategoryFilter]
|
||||||
|
);
|
||||||
|
|
||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<ProductionStandardFilterType>({
|
const formik = useFormik<ProductionStandardFilterType>({
|
||||||
initialValues: {
|
initialValues: filterInitialValues,
|
||||||
project_category: null,
|
|
||||||
},
|
|
||||||
validationSchema: ProductionStandardFilterSchema,
|
validationSchema: ProductionStandardFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('projectCategoryFilter', values.project_category || '');
|
updateFilter('projectCategoryFilter', values.project_category || '');
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
});
|
||||||
updateFilter('projectCategoryFilter', '');
|
|
||||||
|
const formikResetHandler = () => {
|
||||||
|
updateFilter('projectCategoryFilter', '', true);
|
||||||
|
|
||||||
|
formik.resetForm({
|
||||||
|
values: {
|
||||||
|
project_category: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
filterModal.closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
// ===== PROJECT CATEGORY OPTIONS (GROWING or LAYING) =====
|
// ===== PROJECT CATEGORY OPTIONS (GROWING or LAYING) =====
|
||||||
const projectCategoryOptions = useMemo(
|
const projectCategoryOptions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
@@ -381,7 +398,7 @@ const ProductionStandardTable = () => {
|
|||||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
<form onSubmit={formik.handleSubmit} onReset={formikResetHandler}>
|
||||||
<div className='p-4 flex flex-col gap-1.5'>
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
<SelectInputRadio
|
<SelectInputRadio
|
||||||
label='Kategori'
|
label='Kategori'
|
||||||
@@ -397,13 +414,9 @@ const ProductionStandardTable = () => {
|
|||||||
{/* Modal Footer */}
|
{/* Modal Footer */}
|
||||||
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
||||||
<Button
|
<Button
|
||||||
type='button'
|
type='reset'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
onClick={() => {
|
|
||||||
formik.resetForm();
|
|
||||||
filterModal.closeModal();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -30,7 +29,7 @@ import { Supplier } from '@/types/api/master-data/supplier';
|
|||||||
import { SupplierApi } from '@/services/api/master-data';
|
import { SupplierApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import {
|
import {
|
||||||
SupplierFilterSchema,
|
SupplierFilterSchema,
|
||||||
SupplierFilterType,
|
SupplierFilterType,
|
||||||
@@ -117,20 +116,21 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SuppliersTable = () => {
|
const SuppliersTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<{
|
||||||
|
search: string;
|
||||||
|
categoryFilter?: OptionType<string>;
|
||||||
|
flagFilter?: string;
|
||||||
|
}>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
categoryFilter: '',
|
categoryFilter: undefined,
|
||||||
flagFilter: '',
|
flagFilter: undefined,
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
@@ -138,6 +138,8 @@ const SuppliersTable = () => {
|
|||||||
categoryFilter: 'category_id',
|
categoryFilter: 'category_id',
|
||||||
flagFilter: 'flag',
|
flagFilter: 'flag',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'supplier-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FILTER MODAL STATE =====
|
// ===== FILTER MODAL STATE =====
|
||||||
@@ -146,26 +148,33 @@ const SuppliersTable = () => {
|
|||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<SupplierFilterType>({
|
const formik = useFormik<SupplierFilterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
category_id: null,
|
category: tableFilterState.categoryFilter,
|
||||||
flag: false,
|
flag: tableFilterState.flagFilter === 'EKSPEDISI',
|
||||||
},
|
},
|
||||||
validationSchema: SupplierFilterSchema,
|
validationSchema: SupplierFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('categoryFilter', values.category_id || '');
|
updateFilter('categoryFilter', values.category || undefined, true);
|
||||||
updateFilter(
|
updateFilter('flagFilter', values.flag === true ? 'EKSPEDISI' : '', true);
|
||||||
'flagFilter',
|
|
||||||
values.flag === true ? 'EKSPEDISI' : values.flag === false ? '' : ''
|
|
||||||
);
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
});
|
||||||
updateFilter('categoryFilter', '');
|
|
||||||
updateFilter('flagFilter', '');
|
const formikResetHandler = () => {
|
||||||
formik.setFieldValue('flag', false);
|
updateFilter('categoryFilter', undefined, true);
|
||||||
|
updateFilter('flagFilter', '', true);
|
||||||
|
|
||||||
|
formik.resetForm({
|
||||||
|
values: {
|
||||||
|
category: undefined,
|
||||||
|
flag: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
filterModal.closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
const { setFieldValue } = formik;
|
const { setFieldValue } = formik;
|
||||||
|
|
||||||
// ===== CATEGORY OPTIONS (SAPRONAK or BOP) =====
|
// ===== CATEGORY OPTIONS (SAPRONAK or BOP) =====
|
||||||
@@ -187,15 +196,11 @@ const SuppliersTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ===== FILTER HANDLERS =====
|
// ===== FILTER HANDLERS =====
|
||||||
const handleFilterCategoryChange = useCallback(
|
const handleFilterCategoryChange = (
|
||||||
(val: OptionType | OptionType[] | null) => {
|
val: OptionType | OptionType[] | null
|
||||||
const option = val as OptionType | null;
|
) => {
|
||||||
const categoryId = option?.value ? String(option.value) : null;
|
setFieldValue('category', val);
|
||||||
|
};
|
||||||
setFieldValue('category_id', categoryId);
|
|
||||||
},
|
|
||||||
[setFieldValue]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFilterFlagChange = useCallback(
|
const handleFilterFlagChange = useCallback(
|
||||||
(val: OptionType | OptionType[] | null) => {
|
(val: OptionType | OptionType[] | null) => {
|
||||||
@@ -213,13 +218,13 @@ const SuppliersTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ===== FILTER HELPERS =====
|
// ===== FILTER HELPERS =====
|
||||||
const categoryIdValue = useMemo(() => {
|
// const categoryIdValue = useMemo(() => {
|
||||||
if (!formik.values.category_id) return null;
|
// if (!formik.values.category_id) return null;
|
||||||
return (
|
// return (
|
||||||
categoryOptions.find((opt) => opt.value === formik.values.category_id) ||
|
// categoryOptions.find((opt) => opt.value === formik.values.category_id) ||
|
||||||
null
|
// null
|
||||||
);
|
// );
|
||||||
}, [formik.values.category_id, categoryOptions]);
|
// }, [formik.values.category_id, categoryOptions]);
|
||||||
|
|
||||||
const flagValue = useMemo(() => {
|
const flagValue = useMemo(() => {
|
||||||
if (formik.values.flag === null) return null;
|
if (formik.values.flag === null) return null;
|
||||||
@@ -243,14 +248,6 @@ const SuppliersTable = () => {
|
|||||||
}
|
}
|
||||||
}, [filterModal.open, tableFilterState.flagFilter, setFieldValue]);
|
}, [filterModal.open, tableFilterState.flagFilter, setFieldValue]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('suppliers-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -269,8 +266,7 @@ const SuppliersTable = () => {
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
@@ -491,13 +487,13 @@ const SuppliersTable = () => {
|
|||||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
<form onSubmit={formik.handleSubmit} onReset={formikResetHandler}>
|
||||||
<div className='p-4 flex flex-col gap-1.5'>
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
<SelectInputRadio
|
<SelectInputRadio
|
||||||
label='Kategori'
|
label='Kategori'
|
||||||
placeholder='Pilih Kategori'
|
placeholder='Pilih Kategori'
|
||||||
options={categoryOptions}
|
options={categoryOptions}
|
||||||
value={categoryIdValue}
|
value={formik.values.category}
|
||||||
onChange={handleFilterCategoryChange}
|
onChange={handleFilterCategoryChange}
|
||||||
isClearable
|
isClearable
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
@@ -517,13 +513,9 @@ const SuppliersTable = () => {
|
|||||||
{/* Modal Footer */}
|
{/* Modal Footer */}
|
||||||
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
||||||
<Button
|
<Button
|
||||||
type='button'
|
type='reset'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
onClick={() => {
|
|
||||||
formik.resetForm();
|
|
||||||
filterModal.closeModal();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { string, boolean, object } from 'yup';
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
export const SupplierFilterSchema = object().shape({
|
export const SupplierFilterSchema = Yup.object().shape({
|
||||||
category_id: string().nullable(),
|
category: Yup.object({
|
||||||
flag: boolean().nullable(),
|
value: Yup.string().required(),
|
||||||
|
label: Yup.string().required(),
|
||||||
|
}).nullable(),
|
||||||
|
|
||||||
|
flag: Yup.boolean().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SupplierFilterType = {
|
export type SupplierFilterType = {
|
||||||
category_id: string | null;
|
category?: OptionType<string>;
|
||||||
flag: boolean | null;
|
flag: boolean | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -20,8 +20,6 @@ import { Uom } from '@/types/api/master-data/uom';
|
|||||||
import { UomApi } from '@/services/api/master-data';
|
import { UomApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
@@ -103,9 +101,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const UomsTable = () => {
|
const UomsTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -114,22 +109,16 @@ const UomsTable = () => {
|
|||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter({
|
||||||
initial: {
|
initial: {
|
||||||
search: searchValue,
|
search: '',
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'uom-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('uoms-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -146,8 +135,7 @@ const UomsTable = () => {
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
|
||||||
ChangeEventHandler,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -31,7 +24,6 @@ import { Warehouse } from '@/types/api/master-data/warehouse';
|
|||||||
import { WarehouseApi, AreaApi } from '@/services/api/master-data';
|
import { WarehouseApi, AreaApi } from '@/services/api/master-data';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import {
|
import {
|
||||||
WarehouseFilterSchema,
|
WarehouseFilterSchema,
|
||||||
WarehouseFilterType,
|
WarehouseFilterType,
|
||||||
@@ -120,9 +112,6 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const WarehousesTable = () => {
|
const WarehousesTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -141,6 +130,8 @@ const WarehousesTable = () => {
|
|||||||
areaFilter: 'area_id',
|
areaFilter: 'area_id',
|
||||||
activeProjectFlockFilter: 'active_project_flock',
|
activeProjectFlockFilter: 'active_project_flock',
|
||||||
},
|
},
|
||||||
|
persist: true,
|
||||||
|
storeName: 'warehouses-table',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FILTER MODAL STATE =====
|
// ===== FILTER MODAL STATE =====
|
||||||
@@ -149,27 +140,36 @@ const WarehousesTable = () => {
|
|||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<WarehouseFilterType>({
|
const formik = useFormik<WarehouseFilterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
area_id: null,
|
area_id: tableFilterState.areaFilter || null,
|
||||||
active_project_flock: false,
|
active_project_flock:
|
||||||
|
tableFilterState.activeProjectFlockFilter === 'true',
|
||||||
},
|
},
|
||||||
validationSchema: WarehouseFilterSchema,
|
validationSchema: WarehouseFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('areaFilter', values.area_id || '');
|
updateFilter('areaFilter', values.area_id || '', true);
|
||||||
updateFilter(
|
updateFilter(
|
||||||
'activeProjectFlockFilter',
|
'activeProjectFlockFilter',
|
||||||
values.active_project_flock === true ? 'true' : ''
|
values.active_project_flock === true ? 'true' : '',
|
||||||
|
true
|
||||||
);
|
);
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
});
|
||||||
updateFilter('areaFilter', '');
|
|
||||||
updateFilter('activeProjectFlockFilter', '');
|
const formikResetHandler = () => {
|
||||||
formik.setFieldValue('active_project_flock', false);
|
updateFilter('areaFilter', '', true);
|
||||||
|
updateFilter('activeProjectFlockFilter', '', true);
|
||||||
|
|
||||||
|
formik.resetForm({
|
||||||
|
values: {
|
||||||
|
area_id: null,
|
||||||
|
active_project_flock: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { setFieldValue } = formik;
|
filterModal.closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
// ===== AREA OPTIONS =====
|
// ===== AREA OPTIONS =====
|
||||||
const {
|
const {
|
||||||
@@ -243,26 +243,6 @@ const WarehousesTable = () => {
|
|||||||
formik.validateForm();
|
formik.validateForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (filterModal.open) {
|
|
||||||
const activeProjectFlockValue =
|
|
||||||
tableFilterState.activeProjectFlockFilter === 'true' ? true : false; // Default ke false (Semua Kandang)
|
|
||||||
setFieldValue('active_project_flock', activeProjectFlockValue);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
filterModal.open,
|
|
||||||
tableFilterState.activeProjectFlockFilter,
|
|
||||||
setFieldValue,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('warehouses-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -281,8 +261,7 @@ const WarehousesTable = () => {
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
@@ -507,7 +486,7 @@ const WarehousesTable = () => {
|
|||||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
<form onSubmit={formik.handleSubmit} onReset={formikResetHandler}>
|
||||||
<div className='p-4 flex flex-col gap-1.5'>
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Area'
|
label='Area'
|
||||||
@@ -538,10 +517,7 @@ const WarehousesTable = () => {
|
|||||||
type='button'
|
type='button'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
onClick={() => {
|
onClick={formikResetHandler}
|
||||||
formik.resetForm();
|
|
||||||
filterModal.closeModal();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user