feat(FE): slicing ui dashboard, API integration with dummy data and form validation

This commit is contained in:
randy-ar
2026-01-10 08:09:29 +07:00
parent 777b06c690
commit 126346dc52
7 changed files with 574 additions and 387 deletions
@@ -20,13 +20,19 @@ import Alert from '@/components/Alert';
import { import {
DashboardFilterSchema, DashboardFilterSchema,
DashboardFilterType, DashboardFilterType,
getDashboardFilterSchema,
} from '@/components/pages/dashboard/filter/DashboardProductionFilter.schema'; } from '@/components/pages/dashboard/filter/DashboardProductionFilter.schema';
import DashboardLineChart from '@/components/pages/dashboard/chart/DashboardLineChart'; import DashboardLineChart from '@/components/pages/dashboard/chart/DashboardLineChart';
import DashboardLineChartSkeleton from '@/components/pages/dashboard/skeleton/DashboardLineChartSkeleton'; import DashboardLineChartSkeleton from '@/components/pages/dashboard/skeleton/DashboardLineChartSkeleton';
import { RadioGroup, RadioGroupItem } from '@/components/input/RadioInput'; import { RadioGroup, RadioGroupItem } from '@/components/input/RadioInput';
import { DashboardFilter } from '@/types/api/dashboard/dashboard'; import {
DashboardFilter,
DashboardMeta,
} from '@/types/api/dashboard/dashboard';
import DashboardStats from '@/components/pages/dashboard/chart/DashboardStats'; import DashboardStats from '@/components/pages/dashboard/chart/DashboardStats';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { getUniqueFormikErrors } from '@/lib/formik-helper';
// Helper function to normalize values to array // Helper function to normalize values to array
const normalizeToArray = ( const normalizeToArray = (
@@ -46,6 +52,7 @@ const DashboardProduction = () => {
); );
const [endpointUrl, setEndpointUrl] = useState('/dashboard'); const [endpointUrl, setEndpointUrl] = useState('/dashboard');
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>([]); const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>([]);
const [formErrorList, setFormErrorList] = useState<string[]>([]);
// ===== FETCH DATA ===== // ===== FETCH DATA =====
const { const {
@@ -78,7 +85,7 @@ const DashboardProduction = () => {
location_id: selectedLocationIds ? selectedLocationIds.toString() : '', location_id: selectedLocationIds ? selectedLocationIds.toString() : '',
}); });
const comparedByOptions = [ const comparedByOptions = [
{ value: 'LOCATION', label: 'Location' }, { value: 'LOCATION', label: 'Farm' },
{ value: 'FLOCK', label: 'Flock' }, { value: 'FLOCK', label: 'Flock' },
{ value: 'KANDANG', label: 'Kandang' }, { value: 'KANDANG', label: 'Kandang' },
]; ];
@@ -97,7 +104,7 @@ const DashboardProduction = () => {
flockIds: [], flockIds: [],
kandangIds: [], kandangIds: [],
} as DashboardFilterType, } as DashboardFilterType,
validationSchema: DashboardFilterSchema, validationSchema: getDashboardFilterSchema(analysisMode),
onSubmit: (values) => { onSubmit: (values) => {
console.log(values); console.log(values);
@@ -117,6 +124,7 @@ const DashboardProduction = () => {
setAnalysisMode('OVERVIEW'); setAnalysisMode('OVERVIEW');
setEndpointUrl('/dashboard'); setEndpointUrl('/dashboard');
}; };
const handleApplyFilter = (values: DashboardFilter) => { const handleApplyFilter = (values: DashboardFilter) => {
console.log(values); console.log(values);
@@ -140,6 +148,23 @@ const DashboardProduction = () => {
formik.resetForm(); formik.resetForm();
}; };
const handleValidateForm = async () => {
const errors = await formik.validateForm();
if (Object.keys(errors).length > 0) {
// Parse and display errors
const errorMessages = getUniqueFormikErrors(errors);
setFormErrorList(errorMessages);
return; // Stop submission
}
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
handleValidateForm();
formik.handleSubmit();
};
if (isLoadingDashboardProductionData) { if (isLoadingDashboardProductionData) {
return ( return (
<div className='w-full min-h-screen flex items-center justify-center'> <div className='w-full min-h-screen flex items-center justify-center'>
@@ -155,11 +180,53 @@ const DashboardProduction = () => {
<div className='flex flex-row justify-end gap-2'> <div className='flex flex-row justify-end gap-2'>
<Button <Button
variant='outline' variant='outline'
className='min-w-28 rounded-lg' className={`min-w-28 rounded-lg ${
isResponseSuccess(dashboardProductionResponse) &&
(dashboardProductionResponse.meta as unknown as DashboardMeta)
.filters
? 'bg-gradient-to-r from-blue-50 to-blue-100 border-blue-500 text-blue-600 hover:from-blue-100 hover:to-blue-200'
: ''
}`}
onClick={() => filterModal.openModal()} onClick={() => filterModal.openModal()}
> >
<Icon icon='heroicons:funnel' width={20} height={20} /> <Icon
icon='heroicons:funnel'
width={20}
height={20}
className={
isResponseSuccess(dashboardProductionResponse) &&
(dashboardProductionResponse.meta as unknown as DashboardMeta)
.filters
? 'text-blue-600'
: ''
}
/>
Filter Filter
{isResponseSuccess(dashboardProductionResponse) &&
dashboardProductionResponse.meta &&
(dashboardProductionResponse.meta as unknown as DashboardMeta)
.filters && (
<span className='w-6 h-6 text-white bg-red-500 rounded-lg flex items-center justify-center text-xs'>
{(() => {
const meta =
dashboardProductionResponse.meta as unknown as DashboardMeta;
if (!meta.filters) return 0;
const count =
(meta.filters.location_ids.length > 1
? meta.filters.location_ids.length
: 0) +
(meta.filters.flock_ids.length > 1
? meta.filters.flock_ids.length
: 0) +
(meta.filters.kandang_ids.length > 1
? meta.filters.kandang_ids.length
: 0);
return meta.filters.analysis_mode === 'OVERVIEW'
? 1
: count;
})()}
</span>
)}
</Button> </Button>
<Button <Button
variant='outline' variant='outline'
@@ -183,7 +250,15 @@ const DashboardProduction = () => {
dashboardProductionData.charts && dashboardProductionData.charts &&
Object.keys(dashboardProductionData.charts).length > 0 ? ( Object.keys(dashboardProductionData.charts).length > 0 ? (
<DashboardLineChart <DashboardLineChart
analysisMode={analysisMode} analysisMode={
isResponseSuccess(dashboardProductionResponse)
? dashboardProductionResponse.meta
? (
dashboardProductionResponse.meta as unknown as DashboardMeta
).filters?.analysis_mode
: analysisMode
: analysisMode
}
data={dashboardProductionData} data={dashboardProductionData}
/> />
) : ( ) : (
@@ -214,7 +289,11 @@ const DashboardProduction = () => {
</Button> </Button>
</div> </div>
<form className='space-y-4' onSubmit={formik.handleSubmit}> <form
className='space-y-4'
onSubmit={handleFormSubmit}
onReset={handleResetFilter}
>
{/* Rentang Waktu */} {/* Rentang Waktu */}
<div className='px-4'> <div className='px-4'>
<label className='flex items-center gap-2 mb-3'>Tanggal</label> <label className='flex items-center gap-2 mb-3'>Tanggal</label>
@@ -259,6 +338,7 @@ const DashboardProduction = () => {
value={formik.values.analysisMode} value={formik.values.analysisMode}
onChange={(e) => { onChange={(e) => {
formik.handleChange(e); formik.handleChange(e);
setAnalysisMode(e.target.value as 'OVERVIEW' | 'COMPARISON');
// Reset all dependent fields when analysis mode changes // Reset all dependent fields when analysis mode changes
formik.setFieldValue('location', []); formik.setFieldValue('location', []);
formik.setFieldValue('flock', []); formik.setFieldValue('flock', []);
@@ -395,6 +475,14 @@ const DashboardProduction = () => {
</div> </div>
)} )}
{/* Error List Alert */}
{formErrorList.length > 0 && (
<AlertErrorList
formErrorList={formErrorList}
onClose={() => setFormErrorList([])}
/>
)}
{/* Action Buttons */} {/* Action Buttons */}
<div className='flex justify-between gap-4 py-4 mt-8 border-t border-gray-300 bg-gray-100'> <div className='flex justify-between gap-4 py-4 mt-8 border-t border-gray-300 bg-gray-100'>
<Button <Button
@@ -522,7 +522,13 @@ const DashboardLineChart = ({
? false ? false
: { : {
r: 3, r: 3,
fill: getLineColor(series.id, index, analysisMode), fill: '#fff',
stroke: getLineColor(
series.id,
index,
analysisMode
),
strokeWidth: 2,
} }
} }
activeDot={isStandard ? undefined : { r: 5 }} activeDot={isStandard ? undefined : { r: 5 }}
@@ -1,5 +1,6 @@
import Alert from '@/components/Alert'; import Alert from '@/components/Alert';
import Card from '@/components/Card'; import Card from '@/components/Card';
import { formatNumber } from '@/lib/helper';
import { DashboardStatisticsData } from '@/types/api/dashboard/dashboard'; import { DashboardStatisticsData } from '@/types/api/dashboard/dashboard';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
@@ -7,7 +8,7 @@ interface DashboardStatsProps {
data: DashboardStatisticsData[]; data: DashboardStatisticsData[];
} }
// Configuration for each card's static properties // Konfigurasi untuk setiap kartu
const CARD_CONFIG = [ const CARD_CONFIG = [
{ {
key: 'HPP Global', key: 'HPP Global',
@@ -57,7 +58,7 @@ const DashboardStats = ({ data }: DashboardStatsProps) => {
return ( return (
<> <>
{prefix} {prefix}
{value.toLocaleString('id-ID')} {formatNumber(value)}
{suffix && ( {suffix && (
<span className='text-sm font-normal text-neutral-500'>{suffix}</span> <span className='text-sm font-normal text-neutral-500'>{suffix}</span>
)} )}
@@ -2,11 +2,11 @@ import { OptionType } from '@/components/input/SelectInput';
import * as yup from 'yup'; import * as yup from 'yup';
export type DashboardFilterType = { export type DashboardFilterType = {
startDate: string | undefined; startDate: string;
endDate: string | undefined; endDate: string;
analysisMode: string | undefined; analysisMode: string;
comparedBy: string | undefined; comparedBy: string | undefined;
location: OptionType | OptionType[] | undefined; location: OptionType | OptionType[];
lokasiIds: number[] | undefined; lokasiIds: number[] | undefined;
flock: OptionType | OptionType[] | undefined; flock: OptionType | OptionType[] | undefined;
flockIds: number[] | undefined; flockIds: number[] | undefined;
@@ -14,18 +14,104 @@ export type DashboardFilterType = {
kandangIds: number[] | undefined; kandangIds: number[] | undefined;
}; };
export const DashboardFilterSchema: yup.ObjectSchema<DashboardFilterType> = // Schema untuk mode OVERVIEW - semua field required
export const DashboardFilterOverviewSchema: yup.ObjectSchema<DashboardFilterType> =
yup.object({ yup.object({
startDate: yup.string().optional(), startDate: yup.string().required('Start date is required'),
endDate: yup.string().optional(), endDate: yup.string().required('End date is required'),
analysisMode: yup.string().optional(), analysisMode: yup.string().required('Analysis mode is required'),
comparedBy: yup.string().optional(), comparedBy: yup.string().when('analysisMode', {
is: 'COMPARISON',
then: (schema) => schema.required('Compared by is required'),
otherwise: (schema) => schema.optional(),
}),
lokasiIds: yup.array().optional(), lokasiIds: yup.array().optional(),
flockIds: yup.array().optional(), flockIds: yup.array().optional(),
kandangIds: yup.array().optional(), kandangIds: yup.array().optional(),
location: yup.mixed<OptionType | OptionType[]>().optional(), location: yup
flock: yup.mixed<OptionType | OptionType[]>().optional(), .mixed<OptionType | OptionType[]>()
kandang: yup.mixed<OptionType | OptionType[]>().optional(), .required('Farm is required')
.test('is-not-empty', 'Farm is required', (value) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return !!value;
}),
flock: yup
.mixed<OptionType | OptionType[]>()
.required('Flock is required')
.test('is-not-empty', 'Flock is required', (value) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return !!value;
}),
kandang: yup
.mixed<OptionType | OptionType[]>()
.required('Kandang is required')
.test('is-not-empty', 'Kandang is required', (value) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return !!value;
}),
}); });
// Schema untuk mode COMPARISON - conditional validation
export const DashboardFilterComparisonSchema: yup.ObjectSchema<DashboardFilterType> =
yup.object({
startDate: yup.string().required('Start date is required'),
endDate: yup.string().required('End date is required'),
analysisMode: yup.string().required('Analysis mode is required'),
comparedBy: yup.string().when('analysisMode', {
is: 'COMPARISON',
then: (schema) => schema.required('Compared by is required'),
otherwise: (schema) => schema.optional(),
}),
lokasiIds: yup.array().optional(),
flockIds: yup.array().optional(),
kandangIds: yup.array().optional(),
location: yup
.mixed<OptionType | OptionType[]>()
.required('Farm is required')
.test('is-not-empty', 'Farm is required', (value) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return !!value;
}),
flock: yup.mixed<OptionType | OptionType[]>().when('comparedBy', {
is: (value: string) => value === 'FLOCK' || value === 'KANDANG',
then: (schema) =>
schema.test('is-required', 'Flock is required', (value) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return !!value;
}),
otherwise: (schema) => schema.optional(),
}),
kandang: yup.mixed<OptionType | OptionType[]>().when('comparedBy', {
is: 'KANDANG',
then: (schema) =>
schema.test('is-required', 'Kandang is required', (value) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return !!value;
}),
otherwise: (schema) => schema.optional(),
}),
});
// Helper function untuk mendapatkan schema yang sesuai berdasarkan analysis mode
export const getDashboardFilterSchema = (analysisMode?: string) => {
return analysisMode === 'OVERVIEW'
? DashboardFilterOverviewSchema
: DashboardFilterComparisonSchema;
};
// Default schema
export const DashboardFilterSchema = DashboardFilterComparisonSchema;
export type DashboardFilterValues = yup.InferType<typeof DashboardFilterSchema>; export type DashboardFilterValues = yup.InferType<typeof DashboardFilterSchema>;
@@ -1,366 +1,347 @@
{ {
"code": 200, "statistics_data": [
"status": "success", {
"message": "Get dashboard performance kandang comparison successfully", "label": "HPP Global",
"meta": { "value": 16200,
"page": 1, "percent_last_month": 15.5
"limit": 10, },
"total_pages": 1, {
"total_results": 1, "label": "Avg. Selling Price",
"filters": { "value": 28300,
"start_date": "2025-12-01", "percent_last_month": -50
"end_date": "2025-12-31", },
"analysis_mode": "COMPARASION", {
"lokasi_ids": [1], "label": "FCR",
"flock_ids": [1], "value": 24.02,
"kandang_ids": [1, 2, 3] "percent_last_month": 15.5
},
{
"label": "Mortality",
"value": 5,
"percent_last_month": -15.5
} }
}, ],
"data": { "charts": {
"statistics_data": [ "kandang": {
{ "series": [
"label": "HPP Global", {
"value": 16200, "id": 1,
"percent_last_month": 15.5 "label": "Kandang Dago",
}, "unit": "%"
{ },
"label": "Avg. Selling Price", {
"value": 28300, "id": 2,
"percent_last_month": -50 "label": "Kandang Sulanjana",
}, "unit": "%"
{ },
"label": "FCR", {
"value": 24.02, "id": 3,
"percent_last_month": 15.5 "label": "Kandang Garut 2",
}, "unit": "%"
{ }
"label": "Mortality", ],
"value": 5, "dataset": [
"percent_last_month": -15.5 {
} "week": 1,
], "1": 21.2,
"charts": { "2": 19.5,
"kandang": { "3": 20.1
"series": [ },
{ {
"id": 1, "week": 2,
"label": "Kandang Dago", "1": 22.5,
"unit": "%" "2": 19.8,
}, "3": 20.4
{ },
"id": 2, {
"label": "Kandang Sulanjana", "week": 3,
"unit": "%" "1": 23.1,
}, "2": 20.2,
{ "3": 21.0
"id": 3, },
"label": "Kandang Garut 2", {
"unit": "%" "week": 4,
} "1": 24.5,
], "2": 21.5,
"dataset": [ "3": 22.1
{ },
"week": 1, {
"1": 21.2, "week": 5,
"2": 19.5, "1": 25.8,
"3": 20.1 "2": 22.4,
}, "3": 23.5
{ },
"week": 2, {
"1": 22.5, "week": 6,
"2": 19.8, "1": 26.2,
"3": 20.4 "2": 23.1,
}, "3": 24.8
{ },
"week": 3, {
"1": 23.1, "week": 7,
"2": 20.2, "1": 27.5,
"3": 21.0 "2": 24.5,
}, "3": 26.2
{ },
"week": 4, {
"1": 24.5, "week": 8,
"2": 21.5, "1": 28.1,
"3": 22.1 "2": 25.8,
}, "3": 27.5
{ },
"week": 5, {
"1": 25.8, "week": 9,
"2": 22.4, "1": 28.8,
"3": 23.5 "2": 26.2,
}, "3": 28.4
{ },
"week": 6, {
"1": 26.2, "week": 10,
"2": 23.1, "1": 29.1,
"3": 24.8 "2": 27.5,
}, "3": 28.1
{ },
"week": 7, {
"1": 27.5, "week": 11,
"2": 24.5, "1": 28.5,
"3": 26.2 "2": 28.1,
}, "3": 27.4
{ },
"week": 8, {
"1": 28.1, "week": 12,
"2": 25.8, "1": 27.2,
"3": 27.5 "2": 29.1,
}, "3": 26.5
{ },
"week": 9, {
"1": 28.8, "week": 13,
"2": 26.2, "1": 26.1,
"3": 28.4 "2": 28.5,
}, "3": 25.8
{ },
"week": 10, {
"1": 29.1, "week": 14,
"2": 27.5, "1": 25.8,
"3": 28.1 "2": 27.2,
}, "3": 24.2
{ },
"week": 11, {
"1": 28.5, "week": 15,
"2": 28.1, "1": 24.5,
"3": 27.4 "2": 26.1,
}, "3": 23.1
{ },
"week": 12, {
"1": 27.2, "week": 16,
"2": 29.1, "1": 23.2,
"3": 26.5 "2": 25.8,
}, "3": 22.5
{ },
"week": 13, {
"1": 26.1, "week": 17,
"2": 28.5, "1": 22.8,
"3": 25.8 "2": 24.5,
}, "3": 21.9
{ },
"week": 14, {
"1": 25.8, "week": 18,
"2": 27.2, "1": 21.9,
"3": 24.2 "2": 23.2,
}, "3": 21.0
{ },
"week": 15, {
"1": 24.5, "week": 19,
"2": 26.1, "1": 21.2,
"3": 23.1 "2": 22.8,
}, "3": 20.5
{ },
"week": 16, {
"1": 23.2, "week": 20,
"2": 25.8, "1": 20.5,
"3": 22.5 "2": 21.9,
}, "3": 19.8
{ },
"week": 17, {
"1": 22.8, "week": 21,
"2": 24.5, "1": 19.8,
"3": 21.9 "2": 21.2,
}, "3": 19.2
{ },
"week": 18, {
"1": 21.9, "week": 22,
"2": 23.2, "1": 20.4,
"3": 21.0 "2": 20.5,
}, "3": 18.5
{ },
"week": 19, {
"1": 21.2, "week": 23,
"2": 22.8, "1": 21.0,
"3": 20.5 "2": 19.8,
}, "3": 18.1
{ },
"week": 20, {
"1": 20.5, "week": 24,
"2": 21.9, "1": 22.1,
"3": 19.8 "2": 20.4,
}, "3": 17.8
{ },
"week": 21, {
"1": 19.8, "week": 25,
"2": 21.2, "1": 23.5,
"3": 19.2 "2": 21.0,
}, "3": 18.5
{ },
"week": 22, {
"1": 20.4, "week": 26,
"2": 20.5, "1": 24.8,
"3": 18.5 "2": 22.1,
}, "3": 19.2
{ },
"week": 23, {
"1": 21.0, "week": 27,
"2": 19.8, "1": 26.2,
"3": 18.1 "2": 23.5,
}, "3": 20.1
{ },
"week": 24, {
"1": 22.1, "week": 28,
"2": 20.4, "1": 27.5,
"3": 17.8 "2": 24.8,
}, "3": 21.5
{ },
"week": 25, {
"1": 23.5, "week": 29,
"2": 21.0, "1": 28.4,
"3": 18.5 "2": 26.2,
}, "3": 22.8
{ },
"week": 26, {
"1": 24.8, "week": 30,
"2": 22.1, "1": 28.1,
"3": 19.2 "2": 27.5,
}, "3": 24.2
{ },
"week": 27, {
"1": 26.2, "week": 31,
"2": 23.5, "1": 27.4,
"3": 20.1 "2": 28.4,
}, "3": 25.8
{ },
"week": 28, {
"1": 27.5, "week": 32,
"2": 24.8, "1": 26.5,
"3": 21.5 "2": 28.1,
}, "3": 26.5
{ },
"week": 29, {
"1": 28.4, "week": 33,
"2": 26.2, "1": 25.8,
"3": 22.8 "2": 27.4,
}, "3": 27.2
{ },
"week": 30, {
"1": 28.1, "week": 34,
"2": 27.5, "1": 24.2,
"3": 24.2 "2": 26.5,
}, "3": 28.1
{ },
"week": 31, {
"1": 27.4, "week": 35,
"2": 28.4, "1": 23.1,
"3": 25.8 "2": 25.8,
}, "3": 28.5
{ },
"week": 32, {
"1": 26.5, "week": 36,
"2": 28.1, "1": 22.5,
"3": 26.5 "2": 24.2,
}, "3": 29.1
{ },
"week": 33, {
"1": 25.8, "week": 37,
"2": 27.4, "1": 21.9,
"3": 27.2 "2": 23.1,
}, "3": 28.8
{ },
"week": 34, {
"1": 24.2, "week": 38,
"2": 26.5, "1": 21.0,
"3": 28.1 "2": 22.5,
}, "3": 28.1
{ },
"week": 35, {
"1": 23.1, "week": 39,
"2": 25.8, "1": 20.5,
"3": 28.5 "2": 21.9,
}, "3": 27.4
{ },
"week": 36, {
"1": 22.5, "week": 40,
"2": 24.2, "1": 19.8,
"3": 29.1 "2": 21.0,
}, "3": 26.5
{ },
"week": 37, {
"1": 21.9, "week": 41,
"2": 23.1, "1": 19.2,
"3": 28.8 "2": 20.5,
}, "3": 25.8
{ },
"week": 38, {
"1": 21.0, "week": 42,
"2": 22.5, "1": 18.5,
"3": 28.1 "2": 19.8,
}, "3": 24.2
{ },
"week": 39, {
"1": 20.5, "week": 43,
"2": 21.9, "1": 18.1,
"3": 27.4 "2": 19.2,
}, "3": 23.1
{ },
"week": 40, {
"1": 19.8, "week": 44,
"2": 21.0, "1": 17.8,
"3": 26.5 "2": 18.5,
}, "3": 22.5
{ },
"week": 41, {
"1": 19.2, "week": 45,
"2": 20.5, "1": 18.5,
"3": 25.8 "2": 18.1,
}, "3": 21.9
{ },
"week": 42, {
"1": 18.5, "week": 46,
"2": 19.8, "1": 19.2,
"3": 24.2 "2": 17.8,
}, "3": 21.0
{ },
"week": 43, {
"1": 18.1, "week": 47,
"2": 19.2, "1": 20.1,
"3": 23.1 "2": 18.5,
}, "3": 20.5
{ },
"week": 44, {
"1": 17.8, "week": 48,
"2": 18.5, "1": 21.5,
"3": 22.5 "2": 19.2,
}, "3": 19.8
{ },
"week": 45, {
"1": 18.5, "week": 49,
"2": 18.1, "1": 22.8,
"3": 21.9 "2": 20.1,
}, "3": 19.2
{ },
"week": 46, {
"1": 19.2, "week": 50,
"2": 17.8, "1": 24.2,
"3": 21.0 "2": 21.5,
}, "3": 18.5
{ }
"week": 47, ]
"1": 20.1,
"2": 18.5,
"3": 20.5
},
{
"week": 48,
"1": 21.5,
"2": 19.2,
"3": 19.8
},
{
"week": 49,
"1": 22.8,
"2": 20.1,
"3": 19.2
},
{
"week": 50,
"1": 24.2,
"2": 21.5,
"3": 18.5
}
]
}
} }
} }
} }
@@ -5,10 +5,13 @@
* This file is auto-generated. Do not edit manually. * This file is auto-generated. Do not edit manually.
*/ */
import { Dashboard } from '../../types/api/dashboard/dashboard'; import { Dashboard, DashboardMeta } from '../../types/api/dashboard/dashboard';
import { BaseApiResponse } from '@/types/api/api-general'; import { BaseApiResponse } from '@/types/api/api-general';
import dummyData from './dashboard.default.json'; import dummyData from './dashboard.overview.dummy.json';
import dummyData2 from './dashboard.comparasion.location.dummy.json';
import dummyData3 from './dashboard.comparasion.flock.dummy.json';
import dummyData4 from './dashboard.comparasion.kandang.dummy.json';
import dummyData5 from './dashboard.default.json';
/** /**
* Get dummy DashboardProduction data * Get dummy DashboardProduction data
* @returns Promise with BaseApiResponse containing DashboardProduction * @returns Promise with BaseApiResponse containing DashboardProduction
@@ -20,6 +23,18 @@ export async function getDummySingle(): Promise<BaseApiResponse<Dashboard>> {
code: 200, code: 200,
status: 'success', status: 'success',
message: 'Data retrieved successfully', message: 'Data retrieved successfully',
meta: {
page: 1,
limit: 1,
total_pages: 1,
total_results: 1,
filters: {
analysis_mode: 'OVERVIEW',
location_ids: [1],
flock_ids: [1],
kandang_ids: [1],
},
} as DashboardMeta,
data: dummyData as unknown as Dashboard, data: dummyData as unknown as Dashboard,
}); });
}); });
+10
View File
@@ -1,3 +1,5 @@
import { SuccessApiResponse } from '@/types/api/api-general';
export interface Dashboard { export interface Dashboard {
statistics_data: DashboardStatisticsData[]; statistics_data: DashboardStatisticsData[];
charts: DashboardComparisonCharts | DashboardOverviewCharts; charts: DashboardComparisonCharts | DashboardOverviewCharts;
@@ -48,3 +50,11 @@ export interface DashboardFilter {
flock_ids: number[]; flock_ids: number[];
kandang_ids: number[]; kandang_ids: number[];
} }
export interface DashboardMeta {
page: number;
limit: number;
total_pages: number;
total_results: number;
filters: DashboardFilter;
}