mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 23:35:45 +00:00
feat(FE): add state filter dashboard with store
This commit is contained in:
@@ -15,7 +15,6 @@ import { useFormik } from 'formik';
|
|||||||
import { ProjectFlockApi } from '@/services/api/production';
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
import { KandangApi, LocationApi } from '@/services/api/master-data';
|
import { KandangApi, LocationApi } from '@/services/api/master-data';
|
||||||
import { generateDashboardPDF } from '@/components/pages/dashboard/export/DashboardPDF';
|
import { generateDashboardPDF } from '@/components/pages/dashboard/export/DashboardPDF';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DashboardFilterType,
|
DashboardFilterType,
|
||||||
getDashboardFilterSchema,
|
getDashboardFilterSchema,
|
||||||
@@ -38,6 +37,7 @@ import ButtonFilter from '@/components/helper/ButtonFilter';
|
|||||||
import Dropdown from '@/components/Dropdown';
|
import Dropdown from '@/components/Dropdown';
|
||||||
import Menu from '@/components/menu/Menu';
|
import Menu from '@/components/menu/Menu';
|
||||||
import MenuItem from '@/components/menu/MenuItem';
|
import MenuItem from '@/components/menu/MenuItem';
|
||||||
|
import { useDashboardStore } from '@/stores/dashboard';
|
||||||
|
|
||||||
// Helper function to normalize values to array
|
// Helper function to normalize values to array
|
||||||
const normalizeToArray = (
|
const normalizeToArray = (
|
||||||
@@ -52,11 +52,18 @@ const normalizeToArray = (
|
|||||||
|
|
||||||
const DashboardProduction = () => {
|
const DashboardProduction = () => {
|
||||||
const filterModal = useModal();
|
const filterModal = useModal();
|
||||||
|
|
||||||
|
// ===== DASHBOARD STORE =====
|
||||||
|
const { filterValues, setFilterValues, resetFilterValues } =
|
||||||
|
useDashboardStore();
|
||||||
|
|
||||||
const [analysisMode, setAnalysisMode] = useState<'OVERVIEW' | 'COMPARISON'>(
|
const [analysisMode, setAnalysisMode] = useState<'OVERVIEW' | 'COMPARISON'>(
|
||||||
'OVERVIEW'
|
(filterValues.analysisMode as 'OVERVIEW' | 'COMPARISON') || 'OVERVIEW'
|
||||||
);
|
);
|
||||||
const [endpointUrl, setEndpointUrl] = useState('/dashboards');
|
const [endpointUrl, setEndpointUrl] = useState('/dashboards');
|
||||||
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>([]);
|
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>(
|
||||||
|
normalizeToArray(filterValues.location)
|
||||||
|
);
|
||||||
const [exporting, setExporting] = useState(false);
|
const [exporting, setExporting] = useState(false);
|
||||||
const statsRef = useRef<HTMLDivElement>(null);
|
const statsRef = useRef<HTMLDivElement>(null);
|
||||||
const chartRef = useRef<HTMLDivElement>(null);
|
const chartRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -111,19 +118,22 @@ const DashboardProduction = () => {
|
|||||||
// ===== FORMIK =====
|
// ===== FORMIK =====
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
startDate: '',
|
startDate: filterValues.startDate || '',
|
||||||
endDate: '',
|
endDate: filterValues.endDate || '',
|
||||||
flock: [] as OptionType[],
|
flock: filterValues.flock || ([] as OptionType[]),
|
||||||
location: [] as OptionType[],
|
location: filterValues.location || ([] as OptionType[]),
|
||||||
kandang: [] as OptionType[],
|
kandang: filterValues.kandang || ([] as OptionType[]),
|
||||||
analysisMode: analysisMode,
|
analysisMode: filterValues.analysisMode || analysisMode,
|
||||||
comparisonType: '',
|
comparisonType: filterValues.comparisonType || '',
|
||||||
lokasiIds: [],
|
locationIds: filterValues.locationIds || [],
|
||||||
flockIds: [],
|
flockIds: filterValues.flockIds || [],
|
||||||
kandangIds: [],
|
kandangIds: filterValues.kandangIds || [],
|
||||||
} as DashboardFilterType,
|
} as DashboardFilterType,
|
||||||
validationSchema: getDashboardFilterSchema(analysisMode),
|
validationSchema: getDashboardFilterSchema(analysisMode),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
|
// Save filter values to store
|
||||||
|
setFilterValues(values);
|
||||||
|
|
||||||
handleApplyFilter({
|
handleApplyFilter({
|
||||||
start_date: values.startDate || '',
|
start_date: values.startDate || '',
|
||||||
end_date: values.endDate || '',
|
end_date: values.endDate || '',
|
||||||
@@ -138,8 +148,10 @@ const DashboardProduction = () => {
|
|||||||
|
|
||||||
const handleResetFilter = () => {
|
const handleResetFilter = () => {
|
||||||
formik.resetForm();
|
formik.resetForm();
|
||||||
|
resetFilterValues(); // Clear stored filter values
|
||||||
setAnalysisMode('OVERVIEW');
|
setAnalysisMode('OVERVIEW');
|
||||||
setEndpointUrl('/dashboards');
|
setEndpointUrl('/dashboards');
|
||||||
|
setSelectedLocationIds([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleApplyFilter = (values: DashboardFilter) => {
|
const handleApplyFilter = (values: DashboardFilter) => {
|
||||||
@@ -162,6 +174,20 @@ const DashboardProduction = () => {
|
|||||||
refreshDashboardProductionData();
|
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 =====
|
// ===== Formik Error List =====
|
||||||
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
|
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
|
||||||
|
|
||||||
@@ -512,7 +538,6 @@ const DashboardProduction = () => {
|
|||||||
type='reset'
|
type='reset'
|
||||||
variant='soft'
|
variant='soft'
|
||||||
className='ms-4 min-w-36 rounded-lg'
|
className='ms-4 min-w-36 rounded-lg'
|
||||||
onClick={handleResetFilter}
|
|
||||||
>
|
>
|
||||||
Reset Filter
|
Reset Filter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ export type DashboardAllChartsRef = {
|
|||||||
|
|
||||||
// Type guard to check if charts is DashboardOverviewCharts
|
// Type guard to check if charts is DashboardOverviewCharts
|
||||||
function isOverviewCharts(
|
function isOverviewCharts(
|
||||||
charts: DashboardOverviewCharts | DashboardComparisonCharts
|
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
|
||||||
): charts is DashboardOverviewCharts {
|
): charts is DashboardOverviewCharts {
|
||||||
|
if (!charts) return false;
|
||||||
return (
|
return (
|
||||||
'deplesi' in charts ||
|
'deplesi' in charts ||
|
||||||
'body_weight' in charts ||
|
'body_weight' in charts ||
|
||||||
@@ -45,8 +46,9 @@ function isOverviewCharts(
|
|||||||
|
|
||||||
// Type guard to check if charts is DashboardComparisonCharts
|
// Type guard to check if charts is DashboardComparisonCharts
|
||||||
function isComparisonCharts(
|
function isComparisonCharts(
|
||||||
charts: DashboardOverviewCharts | DashboardComparisonCharts
|
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
|
||||||
): charts is DashboardComparisonCharts {
|
): charts is DashboardComparisonCharts {
|
||||||
|
if (!charts) return false;
|
||||||
return 'farm' in charts || 'flock' in charts || 'kandang' in charts;
|
return 'farm' in charts || 'flock' in charts || 'kandang' in charts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ type DashboardLineChartProps = {
|
|||||||
|
|
||||||
// Type guard to check if charts is DashboardOverviewCharts
|
// Type guard to check if charts is DashboardOverviewCharts
|
||||||
function isOverviewCharts(
|
function isOverviewCharts(
|
||||||
charts: DashboardOverviewCharts | DashboardComparisonCharts
|
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
|
||||||
): charts is DashboardOverviewCharts {
|
): charts is DashboardOverviewCharts {
|
||||||
|
if (!charts) return false;
|
||||||
return (
|
return (
|
||||||
'deplesi' in charts ||
|
'deplesi' in charts ||
|
||||||
'body_weight' in charts ||
|
'body_weight' in charts ||
|
||||||
@@ -45,8 +46,9 @@ function isOverviewCharts(
|
|||||||
|
|
||||||
// Type guard to check if charts is DashboardComparisonCharts
|
// Type guard to check if charts is DashboardComparisonCharts
|
||||||
function isComparisonCharts(
|
function isComparisonCharts(
|
||||||
charts: DashboardOverviewCharts | DashboardComparisonCharts
|
charts: DashboardOverviewCharts | DashboardComparisonCharts | undefined
|
||||||
): charts is DashboardComparisonCharts {
|
): charts is DashboardComparisonCharts {
|
||||||
|
if (!charts) return false;
|
||||||
return 'farm' in charts || 'flock' in charts || 'kandang' in charts;
|
return 'farm' in charts || 'flock' in charts || 'kandang' in charts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export type DashboardFilterType = {
|
|||||||
analysisMode: string;
|
analysisMode: string;
|
||||||
comparisonType: string | undefined;
|
comparisonType: string | undefined;
|
||||||
location: OptionType | OptionType[];
|
location: OptionType | OptionType[];
|
||||||
lokasiIds: number[] | undefined;
|
locationIds: number[] | undefined;
|
||||||
flock: OptionType | OptionType[] | undefined;
|
flock: OptionType | OptionType[] | undefined;
|
||||||
flockIds: number[] | undefined;
|
flockIds: number[] | undefined;
|
||||||
kandang: OptionType | OptionType[] | undefined;
|
kandang: OptionType | OptionType[] | undefined;
|
||||||
@@ -25,7 +25,7 @@ export const DashboardFilterOverviewSchema: yup.ObjectSchema<DashboardFilterType
|
|||||||
then: (schema) => schema.required('Compared by is required'),
|
then: (schema) => schema.required('Compared by is required'),
|
||||||
otherwise: (schema) => schema.optional(),
|
otherwise: (schema) => schema.optional(),
|
||||||
}),
|
}),
|
||||||
lokasiIds: yup.array().optional(),
|
locationIds: yup.array().optional(),
|
||||||
flockIds: yup.array().optional(),
|
flockIds: yup.array().optional(),
|
||||||
kandangIds: yup.array().optional(),
|
kandangIds: yup.array().optional(),
|
||||||
location: yup
|
location: yup
|
||||||
@@ -68,7 +68,7 @@ export const DashboardFilterComparisonSchema: yup.ObjectSchema<DashboardFilterTy
|
|||||||
then: (schema) => schema.required('Compared by is required'),
|
then: (schema) => schema.required('Compared by is required'),
|
||||||
otherwise: (schema) => schema.optional(),
|
otherwise: (schema) => schema.optional(),
|
||||||
}),
|
}),
|
||||||
lokasiIds: yup.array().optional(),
|
locationIds: yup.array().optional(),
|
||||||
flockIds: yup.array().optional(),
|
flockIds: yup.array().optional(),
|
||||||
kandangIds: yup.array().optional(),
|
kandangIds: yup.array().optional(),
|
||||||
location: yup
|
location: yup
|
||||||
|
|||||||
@@ -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',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
@@ -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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
Vendored
+11
@@ -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 { ProductionStandardRepeaterFormSchemaValues } from '@/components/pages/master-data/production-standard/form/ProductionStandardForm.schema';
|
||||||
import type {
|
import type {
|
||||||
UniformityFormData,
|
UniformityFormData,
|
||||||
@@ -70,3 +71,13 @@ export type UniformitySlice = {
|
|||||||
setCreatedUniformity: (data: UniformityDetail | null) => void;
|
setCreatedUniformity: (data: UniformityDetail | null) => void;
|
||||||
resetUniformity: () => void;
|
resetUniformity: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Dashboard Filter Slice
|
||||||
|
export type DashboardFilterSlice = {
|
||||||
|
// State
|
||||||
|
filterValues: DashboardFilterType;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
setFilterValues: (values: DashboardFilterType) => void;
|
||||||
|
resetFilterValues: () => void;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user