feat(FE): add state filter dashboard with store

This commit is contained in:
randy-ar
2026-01-18 19:30:54 +07:00
parent a9c22d778b
commit 55b50d4184
8 changed files with 132 additions and 21 deletions
@@ -15,7 +15,6 @@ import { useFormik } from 'formik';
import { ProjectFlockApi } from '@/services/api/production';
import { KandangApi, LocationApi } from '@/services/api/master-data';
import { generateDashboardPDF } from '@/components/pages/dashboard/export/DashboardPDF';
import {
DashboardFilterType,
getDashboardFilterSchema,
@@ -38,6 +37,7 @@ import ButtonFilter from '@/components/helper/ButtonFilter';
import Dropdown from '@/components/Dropdown';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
import { useDashboardStore } from '@/stores/dashboard';
// Helper function to normalize values to array
const normalizeToArray = (
@@ -52,11 +52,18 @@ const normalizeToArray = (
const DashboardProduction = () => {
const filterModal = useModal();
// ===== DASHBOARD STORE =====
const { filterValues, setFilterValues, resetFilterValues } =
useDashboardStore();
const [analysisMode, setAnalysisMode] = useState<'OVERVIEW' | 'COMPARISON'>(
'OVERVIEW'
(filterValues.analysisMode as 'OVERVIEW' | 'COMPARISON') || 'OVERVIEW'
);
const [endpointUrl, setEndpointUrl] = useState('/dashboards');
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>([]);
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>(
normalizeToArray(filterValues.location)
);
const [exporting, setExporting] = useState(false);
const statsRef = useRef<HTMLDivElement>(null);
const chartRef = useRef<HTMLDivElement>(null);
@@ -111,19 +118,22 @@ const DashboardProduction = () => {
// ===== FORMIK =====
const formik = useFormik({
initialValues: {
startDate: '',
endDate: '',
flock: [] as OptionType[],
location: [] as OptionType[],
kandang: [] as OptionType[],
analysisMode: analysisMode,
comparisonType: '',
lokasiIds: [],
flockIds: [],
kandangIds: [],
startDate: filterValues.startDate || '',
endDate: filterValues.endDate || '',
flock: filterValues.flock || ([] as OptionType[]),
location: filterValues.location || ([] as OptionType[]),
kandang: filterValues.kandang || ([] as OptionType[]),
analysisMode: filterValues.analysisMode || analysisMode,
comparisonType: filterValues.comparisonType || '',
locationIds: filterValues.locationIds || [],
flockIds: filterValues.flockIds || [],
kandangIds: filterValues.kandangIds || [],
} as DashboardFilterType,
validationSchema: getDashboardFilterSchema(analysisMode),
onSubmit: (values) => {
// Save filter values to store
setFilterValues(values);
handleApplyFilter({
start_date: values.startDate || '',
end_date: values.endDate || '',
@@ -138,8 +148,10 @@ const DashboardProduction = () => {
const handleResetFilter = () => {
formik.resetForm();
resetFilterValues(); // Clear stored filter values
setAnalysisMode('OVERVIEW');
setEndpointUrl('/dashboards');
setSelectedLocationIds([]);
};
const handleApplyFilter = (values: DashboardFilter) => {
@@ -162,6 +174,20 @@ const DashboardProduction = () => {
refreshDashboardProductionData();
};
// ===== Load filter from store on mount =====
useEffect(() => {
if (!filterValues) return;
handleApplyFilter({
start_date: filterValues.startDate,
end_date: filterValues.endDate,
analysis_mode: filterValues.analysisMode as 'OVERVIEW' | 'COMPARISON',
location_ids: normalizeToArray(filterValues.location),
flock_ids: normalizeToArray(filterValues.flock),
kandang_ids: normalizeToArray(filterValues.kandang),
comparison_type: filterValues.comparisonType,
});
}, [filterValues]);
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
@@ -512,7 +538,6 @@ const DashboardProduction = () => {
type='reset'
variant='soft'
className='ms-4 min-w-36 rounded-lg'
onClick={handleResetFilter}
>
Reset Filter
</Button>
@@ -32,8 +32,9 @@ export type DashboardAllChartsRef = {
// Type guard to check if charts is DashboardOverviewCharts
function isOverviewCharts(
charts: DashboardOverviewCharts | DashboardComparisonCharts
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
): charts is DashboardOverviewCharts {
if (!charts) return false;
return (
'deplesi' in charts ||
'body_weight' in charts ||
@@ -45,8 +46,9 @@ function isOverviewCharts(
// Type guard to check if charts is DashboardComparisonCharts
function isComparisonCharts(
charts: DashboardOverviewCharts | DashboardComparisonCharts
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
): charts is DashboardComparisonCharts {
if (!charts) return false;
return 'farm' in charts || 'flock' in charts || 'kandang' in charts;
}
@@ -32,8 +32,9 @@ type DashboardLineChartProps = {
// Type guard to check if charts is DashboardOverviewCharts
function isOverviewCharts(
charts: DashboardOverviewCharts | DashboardComparisonCharts
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
): charts is DashboardOverviewCharts {
if (!charts) return false;
return (
'deplesi' in charts ||
'body_weight' in charts ||
@@ -45,8 +46,9 @@ function isOverviewCharts(
// Type guard to check if charts is DashboardComparisonCharts
function isComparisonCharts(
charts: DashboardOverviewCharts | DashboardComparisonCharts
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
): charts is DashboardComparisonCharts {
if (!charts) return false;
return 'farm' in charts || 'flock' in charts || 'kandang' in charts;
}
@@ -7,7 +7,7 @@ export type DashboardFilterType = {
analysisMode: string;
comparisonType: string | undefined;
location: OptionType | OptionType[];
lokasiIds: number[] | undefined;
locationIds: number[] | undefined;
flock: OptionType | OptionType[] | undefined;
flockIds: number[] | undefined;
kandang: OptionType | OptionType[] | undefined;
@@ -25,7 +25,7 @@ export const DashboardFilterOverviewSchema: yup.ObjectSchema<DashboardFilterType
then: (schema) => schema.required('Compared by is required'),
otherwise: (schema) => schema.optional(),
}),
lokasiIds: yup.array().optional(),
locationIds: yup.array().optional(),
flockIds: yup.array().optional(),
kandangIds: yup.array().optional(),
location: yup
@@ -68,7 +68,7 @@ export const DashboardFilterComparisonSchema: yup.ObjectSchema<DashboardFilterTy
then: (schema) => schema.required('Compared by is required'),
otherwise: (schema) => schema.optional(),
}),
lokasiIds: yup.array().optional(),
locationIds: yup.array().optional(),
flockIds: yup.array().optional(),
kandangIds: yup.array().optional(),
location: yup
+24
View File
@@ -0,0 +1,24 @@
'use client';
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { createDashboardFilterSlice } from '@/stores/dashboard/slices/dashboard-filter.slice';
import { DashboardFilterSlice } from '@/types/stores';
export type DashboardStore = DashboardFilterSlice;
export const useDashboardStore = create<DashboardStore>()(
devtools(
persist(
(...args) => ({
...createDashboardFilterSlice(...args),
}),
{
name: 'dashboard-filter-cache',
}
),
{
name: 'DashboardStore',
}
)
);
+2
View File
@@ -0,0 +1,2 @@
export { useDashboardStore } from './dashboard.store';
export type { DashboardStore } from './dashboard.store';
@@ -0,0 +1,45 @@
import { DashboardFilterSlice } from '@/types/stores';
import { StateCreator } from 'zustand';
export const createDashboardFilterSlice: StateCreator<
DashboardFilterSlice,
[],
[],
DashboardFilterSlice
> = (set) => ({
// Initial state
filterValues: {
startDate: '',
endDate: '',
analysisMode: 'OVERVIEW',
comparisonType: undefined,
location: [],
locationIds: undefined,
flock: undefined,
flockIds: undefined,
kandang: undefined,
kandangIds: undefined,
},
// Actions
setFilterValues: (values) => set({ filterValues: values }),
resetFilterValues: () => {
alert('reset filter values');
return set({
filterValues: {
startDate: '',
endDate: '',
analysisMode: 'OVERVIEW',
comparisonType: undefined,
location: [],
locationIds: undefined,
flock: undefined,
flockIds: undefined,
kandang: undefined,
kandangIds: undefined,
},
});
},
});
+11
View File
@@ -1,3 +1,4 @@
import { DashboardFilterType } from '@/components/pages/dashboard/filter/DashboardProductionFilter.schema';
import type { ProductionStandardRepeaterFormSchemaValues } from '@/components/pages/master-data/production-standard/form/ProductionStandardForm.schema';
import type {
UniformityFormData,
@@ -70,3 +71,13 @@ export type UniformitySlice = {
setCreatedUniformity: (data: UniformityDetail | null) => void;
resetUniformity: () => void;
};
// Dashboard Filter Slice
export type DashboardFilterSlice = {
// State
filterValues: DashboardFilterType;
// Actions
setFilterValues: (values: DashboardFilterType) => void;
resetFilterValues: () => void;
};