fix(FE): fixing classname using dynamic value

This commit is contained in:
randy-ar
2026-01-29 14:50:16 +07:00
parent dce913815e
commit 6c2baca807
6 changed files with 350 additions and 339 deletions
@@ -211,7 +211,7 @@ const DashboardProduction = () => {
useEffect(() => { useEffect(() => {
setNavbarActions( setNavbarActions(
<div className='hidden sm:flex flex-row justify-end gap-[12px] '> <div className='hidden sm:flex flex-row justify-end gap-3 '>
<ButtonFilter <ButtonFilter
values={{ values={{
...formik.values, ...formik.values,
@@ -226,24 +226,26 @@ const DashboardProduction = () => {
variant='outline' variant='outline'
color='none' color='none'
className={cn( className={cn(
'padding-[12px] rounded-[8px] max-h-[40px] font-semibold text-[14px] gap-[6px]', 'p-2 rounded-lg font-semibold text-sm gap-1.5',
'text-sm text-base-content/50 border border-base-content/10 shadow-button-soft' 'text-sm text-base-content/50 border border-base-content/10 shadow-button-soft'
)} )}
> >
<Icon width={20} height={20} icon='heroicons:cloud-arrow-down' /> <Icon width={20} height={20} icon='heroicons:cloud-arrow-down' />
Export Export
<div className='w-[26px] h-[20px] flex items-center justify-center border-l border-base-content/10'> <div className='w-6.5 h-5 flex items-center justify-center border-l border-base-content/10'>
<Icon width={14} height={14} icon='heroicons:chevron-down' /> <Icon width={14} height={14} icon='heroicons:chevron-down' />
</div> </div>
</Button> </Button>
} }
className={{ className={{
content: 'w-full', content: 'w-full mt-1 p-0',
}} }}
> >
<Menu className={exporting ? 'hidden' : ''}> <Menu
className={`p-0 w-full shadow-button-soft border border-base-content/10 rounded-lg ${exporting ? 'hidden' : ''}`}
>
<MenuItem <MenuItem
className='text-sm padding-[12px]' className='text-sm p-3'
title='PDF' title='PDF'
onClick={handleExportPDF} onClick={handleExportPDF}
/> />
@@ -270,8 +272,8 @@ const DashboardProduction = () => {
return ( return (
<> <>
<section className='w-full p-[12px] space-y-[12px]'> <section className='w-full p-3 space-y-3'>
<div className='flex sm:hidden flex-row justify-end gap-[12px] '> <div className='flex sm:hidden flex-row justify-end gap-3 '>
<ButtonFilter <ButtonFilter
values={{ values={{
...formik.values, ...formik.values,
@@ -286,7 +288,7 @@ const DashboardProduction = () => {
variant='outline' variant='outline'
color='none' color='none'
className={cn( className={cn(
'padding-[12px] rounded-[8px] max-h-[40px] font-semibold text-[14px] gap-[6px]', 'p-2 rounded-lg font-semibold text-sm gap-1.5',
'text-sm text-base-content/50 border border-base-content/10 shadow-button-soft' 'text-sm text-base-content/50 border border-base-content/10 shadow-button-soft'
)} )}
> >
@@ -296,18 +298,21 @@ const DashboardProduction = () => {
icon='heroicons:cloud-arrow-down' icon='heroicons:cloud-arrow-down'
/> />
Export Export
<div className='w-[26px] h-[20px] flex items-center justify-center border-l border-base-content/10'> <div className='w-6.5 h-5 flex items-center justify-center border-l border-base-content/10'>
<Icon width={14} height={14} icon='heroicons:chevron-down' /> <Icon width={14} height={14} icon='heroicons:chevron-down' />
</div> </div>
</Button> </Button>
} }
className={{ className={{
content: 'w-full', content:
'w-full mt-1 p-0 shadow-button-soft border border-base-content/10 rounded-lg',
}} }}
> >
<Menu className={exporting ? 'hidden' : ''}> <Menu
className={`p-0 w-full shadow-button-soft border border-base-content/10 rounded-lg ${exporting ? 'hidden' : ''}`}
>
<MenuItem <MenuItem
className='text-sm padding-[12px]' className='text-sm p-3'
title='PDF' title='PDF'
onClick={handleExportPDF} onClick={handleExportPDF}
/> />
@@ -405,107 +410,106 @@ const DashboardProduction = () => {
ref={filterModal.ref} ref={filterModal.ref}
className={{ className={{
modal: 'p-0', modal: 'p-0',
modalBox: 'p-0 rounded-xl', modalBox: 'p-0 rounded-[14px]',
}} }}
> >
<div className='space-y-[16px]'> <div className='flex flex-col'>
{/* Modal Header */} {/* Modal Header */}
<div className='flex items-center justify-between gap-[8px] py-[16px] border-b border-[#E5E7EB]'> <div className='flex items-center justify-between p-4 border-b border-base-content/10'>
<div className='flex items-center gap-[8px] ms-[16px] text-[#0069E0]'> <div className='flex items-center gap-2 text-primary'>
<Icon icon='heroicons:funnel' width={20} height={20} /> <Icon icon='heroicons:funnel' width={20} height={20} />
<h3 className='font-semibold'>Filter Data</h3> <h3 className='font-medium text-sm'>Filter Data</h3>
</div> </div>
<Button <Button
variant='link' variant='link'
onClick={() => filterModal.closeModal()} onClick={() => filterModal.closeModal()}
className='text-gray-500 hover:text-gray-700 me-[16px]' className='text-gray-500 hover:text-gray-700'
> >
<Icon icon='heroicons:x-mark' width={20} height={20} /> <Icon icon='heroicons:x-mark' width={20} height={20} />
</Button> </Button>
</div> </div>
<form <form
className='space-y-[8px]' className='flex flex-col'
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
onReset={handleResetFilter} onReset={handleResetFilter}
> >
{/* Rentang Waktu */} <div className='flex flex-col p-4 gap-1.5'>
<div className='px-[16px]'> {/* Rentang Waktu */}
<label className='flex text-xs items-center gap-2 mb-[8px] font-semibold'> <div>
Tanggal <label className='flex text-xs items-center gap-2 py-2 font-semibold'>
</label> Tanggal
<div className='flex items-start gap-2'> </label>
<DateInput <div className='flex items-center gap-2'>
name='startDate' <DateInput
placeholder='Tanggal Mulai' name='startDate'
value={formik.values.startDate} placeholder='Tanggal Mulai'
errorMessage={formik.errors.startDate} value={formik.values.startDate}
onChange={formik.handleChange} errorMessage={formik.errors.startDate}
isError={ onChange={formik.handleChange}
Boolean(formik.errors.startDate) && isError={
Boolean(formik.touched.startDate) Boolean(formik.errors.startDate) &&
} Boolean(formik.touched.startDate)
/> }
<div className='hidden md:block mt-3 text-center text-[#18181B]/10 '> />
<hr className='w-full max-w-3 h-px border-base-content/10'></hr>
<DateInput
name='endDate'
placeholder='Tanggal Akhir'
value={formik.values.endDate}
errorMessage={formik.errors.endDate}
onChange={formik.handleChange}
isError={
Boolean(formik.errors.endDate) &&
Boolean(formik.touched.endDate)
}
/>
</div> </div>
<DateInput
name='endDate'
placeholder='Tanggal Akhir'
value={formik.values.endDate}
errorMessage={formik.errors.endDate}
onChange={formik.handleChange}
className={{
inputWrapper:
'rounded-[8px] px-[12px] py-[10px] text-sm border-[#18181B]/10',
}}
isError={
Boolean(formik.errors.endDate) &&
Boolean(formik.touched.endDate)
}
/>
</div> </div>
</div>
{/* Analysis Mode */} {/* Analysis Mode */}
<div className='px-[16px]'> <div>
<label className='block mb-[8px] text-xs font-semibold'> <label className='block py-2 text-xs font-semibold'>
Analysis Mode Analysis Mode
</label> </label>
<RadioGroup <RadioGroup
name='analysisMode' name='analysisMode'
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'); setAnalysisMode(
// Reset all dependent fields when analysis mode changes e.target.value as 'OVERVIEW' | 'COMPARISON'
formik.setFieldValue('location', []); );
formik.setFieldValue('flock', []); // Reset all dependent fields when analysis mode changes
formik.setFieldValue('kandang', []); formik.setFieldValue('location', []);
formik.setFieldValue('comparisonType', ''); formik.setFieldValue('flock', []);
setSelectedLocationIds([]); formik.setFieldValue('kandang', []);
}} formik.setFieldValue('comparisonType', '');
color='primary' setSelectedLocationIds([]);
className={{ }}
wrapper:
'w-full flex flex-row items-center min-h-[45px] font-medium text-[#18181B]/50',
}}
>
<RadioGroupItem
color='primary' color='primary'
value='OVERVIEW' className={{
label='Performance Overview' wrapper:
/> 'w-full flex flex-row items-center font-medium text-base-content/50',
<RadioGroupItem radioWrapper: 'w-full grid grid-cols-2 gap-0 p-0',
color='primary' }}
value='COMPARISON' >
label='Performance Comparison' <RadioGroupItem
/> color='primary'
</RadioGroup> value='OVERVIEW'
</div> label='Performance Overview'
className='w-full p-3'
/>
<RadioGroupItem
color='primary'
value='COMPARISON'
label='Performance Comparison'
className='w-full p-3'
/>
</RadioGroup>
</div>
{formik.values.analysisMode === 'COMPARISON' && ( {formik.values.analysisMode === 'COMPARISON' && (
<div className='px-[16px]'>
<SelectInputRadio <SelectInputRadio
label='Compared By' label='Compared By'
value={comparisonTypeOptions.find( value={comparisonTypeOptions.find(
@@ -525,15 +529,12 @@ const DashboardProduction = () => {
Boolean(formik.touched.comparisonType) Boolean(formik.touched.comparisonType)
} }
className={{ className={{
label: 'mb-[8px] text-xs font-semibold', select: 'rounded-lg text-sm border-base-content/10',
select: 'rounded-[8px] text-sm border-[#18181B]/10',
}} }}
/> />
</div> )}
)}
{/* Location */} {/* Location */}
<div className='px-[16px]'>
{comparisonTypeOptions.find( {comparisonTypeOptions.find(
(option) => option.value === formik.values.comparisonType (option) => option.value === formik.values.comparisonType
)?.value === 'FARM' ? ( )?.value === 'FARM' ? (
@@ -564,7 +565,7 @@ const DashboardProduction = () => {
Boolean(formik.touched.location) Boolean(formik.touched.location)
} }
className={{ className={{
select: 'rounded-[8px] text-sm border-[#18181B]/10', select: 'rounded-lg text-sm border-base-content/10',
}} }}
/> />
) : ( ) : (
@@ -595,166 +596,164 @@ const DashboardProduction = () => {
Boolean(formik.touched.location) Boolean(formik.touched.location)
} }
className={{ className={{
label: 'mb-[8px] text-xs font-semibold', select: 'rounded-lg text-sm border-base-content/10',
select: 'rounded-[8px] text-sm border-[#18181B]/10',
}} }}
/> />
)} )}
{/* Flock */}
{!(
formik.values.analysisMode === 'COMPARISON' &&
!(
formik.values.comparisonType === 'FLOCK' ||
formik.values.comparisonType === 'KANDANG'
)
) && (
<>
{comparisonTypeOptions.find(
(option) => option.value === formik.values.comparisonType
)?.value === 'FLOCK' ? (
<SelectInputCheckbox
label='Flock'
value={
formik.values.flock as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('flock', selected)
}
errorMessage={formik.errors.flock as string}
onInputChange={setInputValueFlock}
onMenuScrollToBottom={loadMoreFlock}
options={flockOptions}
isLoading={isLoadingFlockOptions}
isError={
Boolean(formik.errors.flock) &&
Boolean(formik.touched.flock)
}
className={{
select: 'rounded-lg text-sm border-base-content/10',
}}
/>
) : (
<SelectInputRadio
label='Flock'
value={
formik.values.flock as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('flock', selected)
}
errorMessage={formik.errors.flock as string}
onInputChange={setInputValueFlock}
onMenuScrollToBottom={loadMoreFlock}
options={flockOptions}
isLoading={isLoadingFlockOptions}
isError={
Boolean(formik.errors.flock) &&
Boolean(formik.touched.flock)
}
className={{
select: 'rounded-lg text-sm border-base-content/10',
}}
/>
)}
</>
)}
{/* Kandang */}
{!(
formik.values.analysisMode === 'COMPARISON' &&
!(formik.values.comparisonType === 'KANDANG')
) && (
<>
{comparisonTypeOptions.find(
(option) => option.value === formik.values.comparisonType
)?.value === 'KANDANG' ? (
<SelectInputCheckbox
label='Kandang'
value={
formik.values.kandang as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('kandang', selected)
}
errorMessage={formik.errors.kandang as string}
onInputChange={setInputValueKandang}
onMenuScrollToBottom={loadMoreKandang}
options={kandangOptions}
isLoading={isLoadingKandangOptions}
isError={
Boolean(formik.errors.kandang) &&
Boolean(formik.touched.kandang)
}
className={{
select: 'rounded-lg text-sm border-base-content/10',
}}
/>
) : (
<SelectInputRadio
label='Kandang'
value={
formik.values.kandang as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('kandang', selected)
}
errorMessage={formik.errors.kandang as string}
onInputChange={setInputValueKandang}
onMenuScrollToBottom={loadMoreKandang}
options={kandangOptions}
isLoading={isLoadingKandangOptions}
isError={
Boolean(formik.errors.kandang) &&
Boolean(formik.touched.kandang)
}
className={{
select: 'rounded-lg text-sm border-base-content/10',
}}
/>
)}
</>
)}
{formErrorList.length > 0 && (
<div className='w-full'>
<AlertErrorList
formErrorList={formErrorList}
onClose={close}
/>
</div>
)}
</div> </div>
{/* Flock */}
{!(
formik.values.analysisMode === 'COMPARISON' &&
!(
formik.values.comparisonType === 'FLOCK' ||
formik.values.comparisonType === 'KANDANG'
)
) && (
<div className='px-[16px]'>
{comparisonTypeOptions.find(
(option) => option.value === formik.values.comparisonType
)?.value === 'FLOCK' ? (
<SelectInputCheckbox
label='Flock'
value={
formik.values.flock as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('flock', selected)
}
errorMessage={formik.errors.flock as string}
onInputChange={setInputValueFlock}
onMenuScrollToBottom={loadMoreFlock}
options={flockOptions}
isLoading={isLoadingFlockOptions}
isError={
Boolean(formik.errors.flock) &&
Boolean(formik.touched.flock)
}
className={{
label: 'mb-[8px] text-xs font-semibold',
select: 'rounded-[8px] text-sm border-[#18181B]/10',
}}
/>
) : (
<SelectInputRadio
label='Flock'
value={
formik.values.flock as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('flock', selected)
}
errorMessage={formik.errors.flock as string}
onInputChange={setInputValueFlock}
onMenuScrollToBottom={loadMoreFlock}
options={flockOptions}
isLoading={isLoadingFlockOptions}
isError={
Boolean(formik.errors.flock) &&
Boolean(formik.touched.flock)
}
className={{
label: 'mb-[8px] text-xs font-semibold',
select: 'rounded-[8px] text-sm border-[#18181B]/10',
}}
/>
)}
</div>
)}
{/* Kandang */}
{!(
formik.values.analysisMode === 'COMPARISON' &&
!(formik.values.comparisonType === 'KANDANG')
) && (
<div className='px-[16px]'>
{comparisonTypeOptions.find(
(option) => option.value === formik.values.comparisonType
)?.value === 'KANDANG' ? (
<SelectInputCheckbox
label='Kandang'
value={
formik.values.kandang as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('kandang', selected)
}
errorMessage={formik.errors.kandang as string}
onInputChange={setInputValueKandang}
onMenuScrollToBottom={loadMoreKandang}
options={kandangOptions}
isLoading={isLoadingKandangOptions}
isError={
Boolean(formik.errors.kandang) &&
Boolean(formik.touched.kandang)
}
className={{
label: 'mb-[8px] text-xs font-semibold',
select: 'rounded-[8px] text-sm border-[#18181B]/10',
}}
/>
) : (
<SelectInputRadio
label='Kandang'
value={
formik.values.kandang as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined
}
onChange={(selected) =>
formik.setFieldValue('kandang', selected)
}
errorMessage={formik.errors.kandang as string}
onInputChange={setInputValueKandang}
onMenuScrollToBottom={loadMoreKandang}
options={kandangOptions}
isLoading={isLoadingKandangOptions}
isError={
Boolean(formik.errors.kandang) &&
Boolean(formik.touched.kandang)
}
className={{
label: 'mb-[8px] text-xs font-semibold',
select: 'rounded-[8px] text-sm border-[#18181B]/10',
}}
/>
)}
</div>
)}
{formErrorList.length > 0 && (
<div className='w-full p-4'>
<AlertErrorList formErrorList={formErrorList} onClose={close} />
</div>
)}
{/* Action Buttons */} {/* Action Buttons */}
<div className='flex justify-between gap-4 py-4 mt-8 border-t border-gray-300 bg-[#F9FAFB]'> <div className='flex justify-between gap-4 p-4 border-t border-base-content/10 bg-[#F9FAFB]'>
<Button <Button
type='reset' type='reset'
variant='soft' variant='soft'
className='ms-4 px-[16px] rounded-[8px] bg-[#F9FAFB] border-[#F9FAFB] text-[#4A5565] hover:bg-[#4A5565]/10' className='rounded-lg p-3 bg-[#F9FAFB] border-[#F9FAFB] text-base-content/65 hover:bg-base-content/10'
> >
Reset Filter Reset Filter
</Button> </Button>
<Button <Button
type='submit' type='submit'
className='me-4 min-w-[160px] px-[16px] rounded-[8px]' className='min-w-40 text-sm p-3 text-white rounded-lg'
> >
Apply Filter Apply Filter
</Button> </Button>
@@ -147,11 +147,12 @@ const DashboardLineChart = ({
return ( return (
<Card <Card
className={{ className={{
wrapper: 'w-full rounded-lg', wrapper: 'w-full rounded-lg p-0',
body: 'p-4',
}} }}
variant='bordered' variant='bordered'
> >
<div className='flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-[12px]'> <div className='flex flex-col sm:flex-row justify-between items-start gap-4 mb-3'>
<div className='text-lg font-semibold'> <div className='text-lg font-semibold'>
Performance{' '} Performance{' '}
<Icon <Icon
@@ -165,28 +166,28 @@ const DashboardLineChart = ({
<Dropdown <Dropdown
align='end' align='end'
direction='bottom' direction='bottom'
className={{
content: 'mt-1 min-w-full',
}}
trigger={ trigger={
<Button <Button
variant='outline' variant='outline'
color='none' color='none'
className='py-[10px] pl-[12px] pr-[4px] text-[#18181B]/50 rounded-[8px] text-[14px] border-[#18181B]/10 shadow-button-soft' className='py-2.5 pl-3 pr-2 text-base-content/50 rounded-lg text-sm border-base-content/10 shadow-button-soft'
onClick={() => setOpen(!open)} onClick={() => setOpen(!open)}
> >
{chartTypeLabels[chartData]}{' '} {chartTypeLabels[chartData]}{' '}
<div className='w-[26px] h-[20px] flex items-center justify-center border-l border-[#18181B]/10'> <div className='w-6 h-5 flex items-center justify-center border-l border-base-content/10'>
<Icon icon='heroicons:chevron-down' width={14} height={14} /> <Icon icon='heroicons:chevron-down' width={14} height={14} />
</div> </div>
</Button> </Button>
} }
className={{
content: '',
}}
controlled={open} controlled={open}
> >
<Menu> <Menu className='p-0 w-full shadow-button-soft border border-base-content/10 rounded-lg'>
<MenuItem <MenuItem
title='Body weight' title='Body weight'
className='text-sm padding-[12px] whitespace-nowrap' className='text-sm padding-3 whitespace-nowrap'
onClick={() => { onClick={() => {
setChartData('body_weight'); setChartData('body_weight');
setOpen(!open); setOpen(!open);
@@ -194,7 +195,7 @@ const DashboardLineChart = ({
/> />
<MenuItem <MenuItem
title='Performance' title='Performance'
className='text-sm padding-[12px] whitespace-nowrap' className='text-sm padding-3 whitespace-nowrap'
onClick={() => { onClick={() => {
setChartData('performance'); setChartData('performance');
setOpen(!open); setOpen(!open);
@@ -202,7 +203,7 @@ const DashboardLineChart = ({
/> />
<MenuItem <MenuItem
title='FCR' title='FCR'
className='text-sm padding-[12px] whitespace-nowrap' className='text-sm padding-3 whitespace-nowrap'
onClick={() => { onClick={() => {
setChartData('fcr'); setChartData('fcr');
setOpen(!open); setOpen(!open);
@@ -210,7 +211,7 @@ const DashboardLineChart = ({
/> />
<MenuItem <MenuItem
title='Quality Control' title='Quality Control'
className='text-sm padding-[12px] whitespace-nowrap' className='text-sm padding-3 whitespace-nowrap'
onClick={() => { onClick={() => {
setChartData('quality_control'); setChartData('quality_control');
setOpen(!open); setOpen(!open);
@@ -218,7 +219,7 @@ const DashboardLineChart = ({
/> />
<MenuItem <MenuItem
title='Deplesi' title='Deplesi'
className='text-sm padding-[12px] whitespace-nowrap' className='text-sm padding-3 whitespace-nowrap'
onClick={() => { onClick={() => {
setChartData('deplesi'); setChartData('deplesi');
setOpen(!open); setOpen(!open);
@@ -267,14 +268,14 @@ const DashboardLineChart = ({
}} }}
variant='outline' variant='outline'
color='none' color='none'
className={`flex items-center gap-[8px] px-[12px] py-[10px] rounded-[8px] border transition-colors ${ className={`flex items-center gap-2 p-3 rounded-lg border transition-colors ${
isVisible isVisible
? 'border-[#18181B]/10 hover:bg-[#18181B]/4' ? 'border-base-content/10 hover:bg-base-content/4'
: 'border-[#18181B]/10 bg-[#18181B]/4' : 'border-base-content/10 bg-base-content/4'
}`} }`}
> >
<div <div
className={`w-[20px] h-[2px] ${ className={`w-5 h-0.5 ${
isStandard ? 'border-t-2 border-dashed' : '' isStandard ? 'border-t-2 border-dashed' : ''
} ${!isVisible ? 'opacity-30' : ''}`} } ${!isVisible ? 'opacity-30' : ''}`}
style={{ style={{
@@ -287,7 +288,7 @@ const DashboardLineChart = ({
}} }}
></div> ></div>
<span <span
className={`font-semibold text-[14px] ${isVisible ? 'text-[#18181B]/50' : 'text-[#18181B]/50'}`} className={`font-semibold text-sm ${isVisible ? 'text-base-content/50' : 'text-base-content/50'}`}
> >
{series.label} {series.label}
</span> </span>
@@ -295,7 +296,7 @@ const DashboardLineChart = ({
icon='heroicons:eye' icon='heroicons:eye'
width={16} width={16}
height={16} height={16}
className='text-[#18181B]/40' className='text-base-content/40'
/> />
</Button> </Button>
); );
@@ -371,7 +372,8 @@ const DashboardLineChart = ({
fontWeight: 600, fontWeight: 600,
}} }}
label={ label={
chartData === 'body_weight' || chartData === 'performance' (chartData === 'body_weight' || chartData === 'performance') &&
analysisMode === 'OVERVIEW'
? { ? {
value: value:
chartData === 'body_weight' chartData === 'body_weight'
@@ -379,7 +381,7 @@ const DashboardLineChart = ({
: 'Percentage', : 'Percentage',
position: 'insideLeft', position: 'insideLeft',
angle: -90, angle: -90,
offset: 25, offset: 5,
style: { style: {
fontSize: 12, fontSize: 12,
fill: '#18181B', fill: '#18181B',
@@ -387,7 +389,20 @@ const DashboardLineChart = ({
fontWeight: 600, fontWeight: 600,
}, },
} }
: undefined : analysisMode === 'COMPARISON'
? {
value: 'Percentage',
position: 'insideLeft',
angle: -90,
offset: 5,
style: {
fontSize: 12,
fill: '#18181B',
opacity: 0.2,
fontWeight: 600,
},
}
: undefined
} }
tickLine={false} tickLine={false}
axisLine={{ stroke: '#C1C1C180', opacity: 0.5 }} axisLine={{ stroke: '#C1C1C180', opacity: 0.5 }}
@@ -508,6 +523,7 @@ const DashboardLineChart = ({
return Array.from(tickSet).sort((a, b) => a - b); return Array.from(tickSet).sort((a, b) => a - b);
})()} })()}
tickFormatter={(value) => formatNumber(Number(value))}
/> />
<Tooltip <Tooltip
contentStyle={{ contentStyle={{
@@ -522,8 +538,8 @@ const DashboardLineChart = ({
labelFormatter={(value) => `Week ${value}`} labelFormatter={(value) => `Week ${value}`}
content={(props) => { content={(props) => {
return ( return (
<div className='flex flex-col gap-[6px] rounded-lg bg-neutral-950 p-4 text-white'> <div className='flex flex-col gap-1.5 rounded-lg bg-neutral-950 p-4 text-white'>
<p className='text-neutral-300 text-[12px] font-semibold text-start'> <p className='text-white/50 text-xs font-semibold text-start'>
{analysisMode === 'OVERVIEW' {analysisMode === 'OVERVIEW'
? selectedKandang ? selectedKandang
? selectedKandang.label || 'Overview Performance' ? selectedKandang.label || 'Overview Performance'
@@ -563,11 +579,11 @@ const DashboardLineChart = ({
return ( return (
<li <li
key={`${item.name}-${index}`} key={`${item.name}-${index}`}
className='flex w-full justify-between items-center flex-row gap-y-[6px] gap-x-[12px] p-0' className='flex w-full justify-between items-center flex-row gap-y-1.5 gap-x-3 p-0'
> >
<span className='flex flex-row gap-1 items-center'> <span className='flex flex-row gap-1.5 items-center'>
<div <div
className='h-[20px] w-[20px] m-0 rounded-[4px]' className='h-5 w-5 m-0 rounded'
style={{ style={{
backgroundColor: color, backgroundColor: color,
}} }}
@@ -582,7 +598,7 @@ const DashboardLineChart = ({
); );
})} })}
</ul> </ul>
<p className='text-neutral-300 text-[12px] text-start'> <p className='text-white/50 text-xs text-start'>
Week {props.label} Week {props.label}
</p> </p>
</div> </div>
@@ -705,8 +721,8 @@ const DashboardLineChart = ({
return ( return (
<div className='absolute inset-x-0 inset-y-15 z-10 flex flex-col items-center justify-center rounded-lg'> <div className='absolute inset-x-0 inset-y-15 z-10 flex flex-col items-center justify-center rounded-lg'>
{/* Chart icon */} {/* Chart icon */}
<div className='w-[50px] h-[50px] bg-[var(--main-color-base-100,#FFFFFF)] border-[1px] border-[#18181B]/10 rounded-[14px] border border-[#18181B] shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center mb-4'> <div className='w-12.5 h-12.5 bg-[var(--main-color-base-100,#FFFFFF)] border border-base-content/10 rounded-[14px] border border-base-content shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center mb-2'>
<div className='w-[38px] h-[38px] bg-[#0069E0] rounded-[8px] border-[1px] border-[#0069E0] flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'> <div className='w-9.5 h-9.5 bg-primary rounded-[8px] border border-primary flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'>
<Icon <Icon
icon='heroicons:chart-bar' icon='heroicons:chart-bar'
className='text-white' className='text-white'
@@ -717,10 +733,10 @@ const DashboardLineChart = ({
</div> </div>
{/* Empty state text */} {/* Empty state text */}
<h3 className='text-gray-900 font-semibold text-base mb-2'> <h3 className='text-base-content/50 font-semibold text-sm mb-1'>
Data Not Yet Available Data Not Yet Available
</h3> </h3>
<p className='text-gray-500 text-sm text-center max-w-xs'> <p className='text-base-content/50 text-xs text-center max-w-xs'>
Please change your filters to get the data. Please change your filters to get the data.
</p> </p>
</div> </div>
@@ -21,7 +21,7 @@ const CARD_CONFIG = [
key: 'Avg. Selling Price', key: 'Avg. Selling Price',
icon: 'heroicons:document-currency-dollar', icon: 'heroicons:document-currency-dollar',
alertColor: 'success' as const, alertColor: 'success' as const,
suffix: ' /Kg', suffix: ' /Kg Telur',
prefix: '', prefix: '',
}, },
{ {
@@ -60,7 +60,7 @@ const DashboardStats = ({ data }: DashboardStatsProps) => {
{prefix} {prefix}
{formatNumber(value)} {formatNumber(value)}
{suffix && ( {suffix && (
<span className='text-[14px] font-normal text-[#18181B]/50'> <span className='text-sm font-normal text-base-content/50'>
{suffix} {suffix}
</span> </span>
)} )}
@@ -69,7 +69,7 @@ const DashboardStats = ({ data }: DashboardStatsProps) => {
}; };
return ( return (
<div className='grid sm:grid-cols-2 xl:grid-cols-4 gap-[12px]'> <div className='grid sm:grid-cols-2 xl:grid-cols-4 gap-3'>
{CARD_CONFIG.map((config) => { {CARD_CONFIG.map((config) => {
// Find matching data from API // Find matching data from API
const cardData = data.find((item) => item.label === config.key); const cardData = data.find((item) => item.label === config.key);
@@ -80,42 +80,41 @@ const DashboardStats = ({ data }: DashboardStatsProps) => {
<Card <Card
key={config.key} key={config.key}
className={{ className={{
wrapper: wrapper: 'w-full rounded-xl border border-base-content/10',
'w-full rounded-[12px] border-[1px] border-[#18181B]/10',
body: 'p-0', body: 'p-0',
wrapperContent: wrapperContent:
'h-full flex flex-col items-between justify-between', 'h-full flex flex-col items-between justify-between',
footer: '!mt-0', footer: 'mt-0!',
}} }}
variant='bordered' variant='bordered'
footer={ footer={
<div className='flex flex-row justify-between px-[16px] pb-[16px] max-h-[48px]'> <div className='flex flex-row justify-between px-4 pb-4 max-h-12'>
<div className='text-[#18181B]/50 font-semibold text-[12px]'> <div className='text-base-content/50 font-semibold text-xs'>
From last month From last month
</div> </div>
<div className='text-[#18181B]/50 font-semibold text-[12px]'> <div className='text-base-content/50 font-semibold text-xs'>
Filter Required Filter Required
</div> </div>
</div> </div>
} }
> >
<div className='flex flex-row items-center gap-4 px-4 py-4'> <div className='flex flex-row items-center gap-3 px-4 py-4'>
<Alert <Alert
variant='soft' variant='soft'
className={`rounded-[8px] p-0 w-[50px] h-[50px] bg-[${config.alertColor}]/12 flex items-center justify-center`} className={`rounded-lg p-0 w-12.5 h-12.5 bg-[${config.alertColor}]/12 flex items-center justify-center`}
> >
<Icon <Icon
icon={config.icon} icon={config.icon}
width={24} width={24}
height={24} height={24}
className='text-[#18181B]/50' className='text-base-content/50'
/> />
</Alert> </Alert>
<div> <div>
<h3 className='text-[#18181B]/50 font-semibold text-[14px]'> <h3 className='text-base-content/50 font-semibold text-sm'>
{config.key} {config.key}
</h3> </h3>
<p className='text-[20px] font-semibold text-[#18181B]/50'> <p className='text-xl font-semibold text-base-content/50'>
******** ********
</p> </p>
</div> </div>
@@ -134,7 +133,7 @@ const DashboardStats = ({ data }: DashboardStatsProps) => {
body: 'p-0', body: 'p-0',
wrapperContent: wrapperContent:
'h-full flex flex-col items-between justify-between', 'h-full flex flex-col items-between justify-between',
footer: '!mt-0', footer: 'mt-0!',
}} }}
variant='bordered' variant='bordered'
footer={ footer={
@@ -189,7 +189,8 @@ const DashboardExportCharts = forwardRef<
> >
<Card <Card
className={{ className={{
wrapper: 'w-full rounded-lg', wrapper: 'w-full rounded-lg p-0',
body: 'p-4',
}} }}
variant='bordered' variant='bordered'
> >
@@ -72,7 +72,7 @@ const DashboardExportStats = forwardRef<
{prefix} {prefix}
{formatNumber(value)} {formatNumber(value)}
{suffix && ( {suffix && (
<span className='text-[14px] font-normal text-[#18181B]/50'> <span className='text-sm font-normal text-base-content/50'>
{suffix} {suffix}
</span> </span>
)} )}
@@ -87,7 +87,7 @@ const DashboardExportStats = forwardRef<
})); }));
return ( return (
<div ref={containerRef} className='grid grid-cols-4 gap-[12px]'> <div ref={containerRef} className='grid grid-cols-4 gap-3'>
{CARD_CONFIG.map((config) => { {CARD_CONFIG.map((config) => {
// Find matching data from API // Find matching data from API
const cardData = data.find((item) => item.label === config.key); const cardData = data.find((item) => item.label === config.key);
@@ -102,37 +102,37 @@ const DashboardExportStats = forwardRef<
body: 'p-0', body: 'p-0',
wrapperContent: wrapperContent:
'h-full flex flex-col items-between justify-between', 'h-full flex flex-col items-between justify-between',
footer: '!mt-0', footer: 'mt-0!',
}} }}
variant='bordered' variant='bordered'
footer={ footer={
<div className='flex flex-row justify-between px-[16px] pb-[16px]'> <div className='flex flex-row justify-between px-4 pb-4 max-h-12'>
<div className='text-[#18181B]/50 font-semibold text-[12px]'> <div className='text-base-content/50 font-semibold text-xs'>
From last month From last month
</div> </div>
<div className='text-[#18181B]/50 font-semibold text-[12px]'> <div className='text-base-content/50 font-semibold text-xs'>
Filter Required Filter Required
</div> </div>
</div> </div>
} }
> >
<div className='flex flex-row items-center gap-4 px-4 pt-4'> <div className='flex flex-row items-center gap-3 px-4 pt-4'>
<Alert <Alert
variant='soft' variant='soft'
className={`rounded-[8px] p-0 w-[50px] h-[50px] bg-[${config.alertColor}]/12 flex items-center justify-center`} className={`rounded-lg p-0 w-12.5 h-12.5 bg-[${config.alertColor}]/12 flex items-center justify-center`}
> >
<Icon <Icon
icon={config.icon} icon={config.icon}
width={24} width={24}
height={24} height={24}
className='text-[#18181B]/50' className='text-base-content/50'
/> />
</Alert> </Alert>
<div> <div>
<h3 className='text-[#18181B]/50 font-semibold text-[14px]'> <h3 className='text-base-content/50 font-semibold text-sm'>
{config.key} {config.key}
</h3> </h3>
<p className='text-[20px] font-semibold text-[#18181B]/50'> <p className='text-xl font-semibold text-base-content/50'>
******** ********
</p> </p>
</div> </div>
@@ -147,21 +147,20 @@ const DashboardExportStats = forwardRef<
<Card <Card
key={config.key} key={config.key}
className={{ className={{
wrapper: wrapper: 'w-full rounded-lg border border-base-content/10',
'w-full rounded-[12px] min-h-[132px] border-[1px] border-[#18181B]/10',
body: 'p-0', body: 'p-0',
wrapperContent: wrapperContent:
'h-full flex flex-col items-between justify-between', 'h-full flex flex-col items-between justify-between',
footer: '!mt-0', footer: 'mt-0!',
}} }}
variant='bordered' variant='bordered'
footer={ footer={
<div className='flex flex-row justify-between px-[16px] pb-[16px]'> <div className='flex flex-row justify-between px-4 pb-4 max-h-12'>
<div className='text-[#18181B]/50 font-semibold text-[12px]'> <div className='text-base-content/50 font-semibold text-xs'>
From last month From last month
</div> </div>
<div <div
className={`${trend.color} font-semibold flex flex-row items-center gap-[8px] text-[12px]`} className={`${trend.color} font-semibold flex flex-row items-center gap-2 text-xs`}
> >
<Icon icon={trend.icon} width={16} height={16} /> <Icon icon={trend.icon} width={16} height={16} />
{trend.value}% {trend.value}%
@@ -173,15 +172,15 @@ const DashboardExportStats = forwardRef<
<Alert <Alert
variant='soft' variant='soft'
color={config.alertColor} color={config.alertColor}
className={`rounded-[8px] p-0 w-[50px] h-[50px] bg-[${config.alertColor}]/12 flex items-center justify-center`} className={`rounded-lg p-0 w-12.5 h-12.5 bg-[${config.alertColor}]/12 flex items-center justify-center`}
> >
<Icon icon={config.icon} width={24} height={24} /> <Icon icon={config.icon} width={24} height={24} />
</Alert> </Alert>
<div> <div>
<h3 className='text-[#18181B]/50 font-semibold text-[14px]'> <h3 className='text-base-content/50 font-semibold text-sm'>
{cardData.label} {cardData.label}
</h3> </h3>
<p className='text-[20px] font-semibold'> <p className='text-xl font-semibold'>
{formatValue(cardData.value, config.prefix, config.suffix)} {formatValue(cardData.value, config.prefix, config.suffix)}
</p> </p>
</div> </div>
@@ -3,15 +3,15 @@ import { DashboardMeta } from '@/types/api/dashboard/dashboard';
const DashboardLineChartSkeleton = ({ meta }: { meta?: DashboardMeta }) => { const DashboardLineChartSkeleton = ({ meta }: { meta?: DashboardMeta }) => {
return ( return (
<div className='w-full bg-white rounded-[12px] border border-[#18181B]/10 p-[16px] relative'> <div className='w-full bg-white rounded-xl border border-base-content/10 p-4 relative'>
{/* Header with title skeleton */} {/* Header with title skeleton */}
<div className='text-[16px] font-semibold'> <div className='text-base font-semibold'>
Performance{' '} Performance{' '}
<Icon <Icon
icon='heroicons:information-circle' icon='heroicons:information-circle'
width={20} width={20}
height={20} height={20}
className='inline text-[#18181B]/50' className='inline text-base-content/50'
/> />
</div> </div>
@@ -24,9 +24,9 @@ const DashboardLineChartSkeleton = ({ meta }: { meta?: DashboardMeta }) => {
{!meta?.filters && ( {!meta?.filters && (
<> <>
{/* Filter icon */} {/* Filter icon */}
<div className='mb-[9px]'> <div className='mb-2'>
<div className='w-[50px] h-[50px] bg-[var(--main-color-base-100,#FFFFFF)] border-[1px] border-[#18181B]/10 rounded-[14px] border border-[#18181B] shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center'> <div className='w-12.5 h-12.5 bg-[var(--main-color-base-100,#FFFFFF)] border border-base-content/10 rounded-[14px] border border-base-content shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center'>
<div className='w-[38px] h-[38px] bg-[#0069E0] rounded-[8px] border-[1px] border-[#0069E0] flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'> <div className='w-9.5 h-9.5 bg-primary rounded-lg border border-primary flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'>
<Icon <Icon
icon='heroicons:funnel' icon='heroicons:funnel'
className='text-white' className='text-white'
@@ -38,10 +38,10 @@ const DashboardLineChartSkeleton = ({ meta }: { meta?: DashboardMeta }) => {
</div> </div>
{/* Empty state text */} {/* Empty state text */}
<h3 className='text-[#18181B]/50 font-semibold text-[14px] mb-[4px]'> <h3 className='text-base-content/50 font-semibold text-sm mb-1'>
No Filters Selected No Filters Selected
</h3> </h3>
<p className='text-[#18181B]/50 text-[12px] text-center max-w-xs'> <p className='text-base-content/50 text-xs text-center max-w-xs'>
Please choose filters to narrow down your results and make Please choose filters to narrow down your results and make
your search easier. your search easier.
</p> </p>
@@ -50,8 +50,8 @@ const DashboardLineChartSkeleton = ({ meta }: { meta?: DashboardMeta }) => {
{meta?.filters && ( {meta?.filters && (
<> <>
{/* Filter icon */} {/* Filter icon */}
<div className='w-[50px] h-[50px] bg-[var(--main-color-base-100,#FFFFFF)] border-[1px] border-[#18181B]/10 rounded-[14px] border border-[#18181B] shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center mb-4'> <div className='w-12.5 h-12.5 bg-[var(--main-color-base-100,#FFFFFF)] border border-base-content/10 rounded-[14px] border border-base-content shadow-[0px_25px_50px_-12px_#00000040] flex items-center justify-center mb-2'>
<div className='w-[38px] h-[38px] bg-[#0069E0] rounded-[8px] border-[1px] border-[#0069E0] flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'> <div className='w-[38px] h-[38px] bg-primary rounded-lg border border-primary flex items-center justify-center shadow-[inset_0px_4px_4px_0px_#FFFFFF80,inset_0px_2px_0px_0px_#FFFFFF80]'>
<Icon <Icon
icon='heroicons:chart-bar' icon='heroicons:chart-bar'
className='text-white' className='text-white'
@@ -62,44 +62,41 @@ const DashboardLineChartSkeleton = ({ meta }: { meta?: DashboardMeta }) => {
</div> </div>
{/* Empty state text */} {/* Empty state text */}
<h3 className='text-gray-900 font-semibold text-base mb-2'> <h3 className='text-base-content/50 font-semibold text-sm mb-1'>
Data Not Yet Available Data Not Yet Available
</h3> </h3>
<p className='text-gray-500 text-sm text-center max-w-xs'> <p className='text-base-content/50 text-xs text-center max-w-xs'>
Please change your filters to get the data. Please change your filters to get the data.
</p> </p>
</> </>
)} )}
</div> </div>
<div className='flex flex-row w-full items-center gap-[16px]'> <div className='flex flex-row w-full items-center gap-4'>
<div className='flex-1 h-full min-w-[16px]'> <div className='flex-1 h-full min-w-4'>
<div className='h-[114px] w-[16x] bg-[#18181B]/4 rounded-[4px]'></div> <div className='h-28.5 w-4 bg-base-content/4 rounded'></div>
</div> </div>
<div className='w-full grid grid-cols-1 gap-y-[53px] mb-[8px]'> <div className='w-full grid grid-cols-1 gap-y-13.25 mb-2'>
{[1, 2, 3, 4].map((item) => ( {[1, 2, 3, 4].map((item) => (
<div <div key={item} className='flex items-center w-full h-4 gap-4'>
key={item} <div className='h-4 w-6 bg-base-content/4 rounded'></div>
className='flex items-center w-full h-[16px] gap-[16px]' <div className='h-0.25 w-full bg-base-content/4 rounded'></div>
>
<div className='h-[16px] w-[24px] bg-[#18181B]/4 rounded-[4px]'></div>
<div className='h-[1px] w-full bg-[#18181B]/4 rounded-[4px]'></div>
</div> </div>
))} ))}
</div> </div>
</div> </div>
{/* X-axis skeleton (bottom) */} {/* X-axis skeleton (bottom) */}
<div className='grid grid-cols-10 gap-[60px] mt-[16px] ps-[52px] sm:ps-[104px] overflow-x-hidden'> <div className='grid grid-cols-10 gap-15 mt-4 ps-13 sm:ps-26 overflow-x-hidden'>
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((item) => ( {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((item) => (
<div <div
key={item} key={item}
className='h-[16px] w-[37px] bg-[#18181B]/4 rounded-[4px]' className='h-4 w-9.5 bg-base-content/4 rounded'
></div> ></div>
))} ))}
</div> </div>
<div className='flex justify-center pt-[16px] ps-[52px] sm:ps-[104px]'> <div className='flex justify-center pt-4 ps-13 sm:ps-26'>
<div className='h-[16px] w-[114px] bg-[#18181B]/4 rounded-[4px]'></div> <div className='h-4 w-28.5 bg-base-content/4 rounded'></div>
</div> </div>
</div> </div>
</div> </div>