feat(FE-316): Add uniformity Excel template generator

This commit is contained in:
rstubryan
2025-12-29 19:34:17 +07:00
parent be0bdcd299
commit e81c0a3baf
2 changed files with 101 additions and 28 deletions
@@ -0,0 +1,59 @@
import { formatNumber, formatDate } from '@/lib/helper';
import { toast } from 'react-hot-toast';
import * as XLSX from 'xlsx';
import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock';
export const generateUniformityTemplate = (
availableQuantity: number,
projectFlockKandangLookup: ProjectFlockKandangLookup
) => {
try {
// Calculate 2% sample from total population
const sampleSize = Math.round(availableQuantity * 0.02);
const data = Array.from({ length: sampleSize }, (_, index) => ({
NO: index + 1,
BW: '',
}));
const worksheet = XLSX.utils.json_to_sheet(data, {
header: ['NO', 'BW'],
});
worksheet['!cols'] = [{ wch: 10 }, { wch: 15 }];
const workbook = XLSX.utils.book_new();
const kandangName = projectFlockKandangLookup.kandang?.name || 'Kandang';
const flockName = projectFlockKandangLookup.project_flock?.flock_name || '';
const flockPeriod = projectFlockKandangLookup.project_flock?.period || 1;
const sheetName =
kandangName.length > 31 ? kandangName.substring(0, 31) : kandangName;
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
const sanitizedFlockName = flockName
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
const sanitizedKandangName = kandangName
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
const filename = `${formatDate(
new Date(),
'YYYY-MM-DD'
)}-${sanitizedFlockName}-${sanitizedKandangName}-periode-${flockPeriod}-${sampleSize}-data.xlsx`;
XLSX.writeFile(workbook, filename);
toast.success(
`Template berhasil dibuat dengan ${formatNumber(sampleSize)} baris data (2% dari ${formatNumber(availableQuantity)} populasi).`
);
} catch (error) {
console.error('Error generating uniformity template:', error);
toast.error('Gagal membuat template Excel. Silakan coba lagi.');
}
};
@@ -40,8 +40,9 @@ import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock'
import { Kandang } from '@/types/api/master-data/kandang';
import UniformityPreviewForm from '@/components/pages/uniformity/form/UniformityPreviewForm';
import UniformityResultForm from '@/components/pages/uniformity/form/UniformityResultForm';
import { generateUniformityTemplate } from '@/components/pages/uniformity/export/UniformityTemplate';
import useSWR from 'swr';
import { cn } from '@/lib/helper';
import { cn, formatNumber } from '@/lib/helper';
interface UniformityFormProps {
formType?: 'add' | 'edit';
@@ -352,20 +353,31 @@ const UniformityForm = ({
formik.setFieldValue('file', file);
},
[formik]
[]
);
const handleDateChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
formik.setFieldValue('date', e.target.value);
},
[formik]
[]
);
const handleRemoveFile = useCallback(() => {
formik.setFieldValue('files', undefined);
}, [formik]);
const handleDownloadTemplate = useCallback(() => {
const availableQuantity = projectFlockKandangLookup?.available_quantity;
if (!availableQuantity || !projectFlockKandangLookup) {
toast.error('Silakan pilih Project Flock dan Kandang terlebih dahulu.');
return;
}
generateUniformityTemplate(availableQuantity, projectFlockKandangLookup);
}, [projectFlockKandangLookup]);
// ===== SIDE EFFECTS =====
useEffect(() => {
if (formik.values.date) {
@@ -576,36 +588,38 @@ const UniformityForm = ({
</span>
<span className='text-xs font-light text-[#18181B80] text-center max-w-xs px-4'>
{projectFlockKandangLookup?.available_quantity
? `Jumlah data yang dibutuhkan: ${projectFlockKandangLookup.available_quantity.toLocaleString('id-ID')} (2% dari total populasi).`
? `Jumlah data yang dibutuhkan: ${formatNumber(Math.round(projectFlockKandangLookup.available_quantity * 0.02))} (2% dari ${formatNumber(projectFlockKandangLookup.available_quantity)} populasi).`
: 'Upload data file (*.xlsx)'}
</span>
</div>
<div className='flex items-center justify-center gap-2 py-4'>
<div className='h-px bg-[#18181B33] w-8'></div>
<span className='text-[#18181B33] text-xs'>
Templates
</span>
<div className='h-px bg-[#18181B33] w-8'></div>
</div>
{projectFlockKandangLookup?.available_quantity && (
<>
<div className='flex items-center justify-center gap-2 py-4'>
<div className='h-px bg-[#18181B33] w-8'></div>
<span className='text-[#18181B33] text-xs'>
Templates
</span>
<div className='h-px bg-[#18181B33] w-8'></div>
</div>
<div className='flex items-center justify-center mb-10'>
<Button
type='button'
variant='outline'
className='btn-sm rounded-2xl shadow-md border border-base-300'
href='https://www.timestored.com/data/sample/chickweight.csv'
target='_blank'
onClick={(e) => e.stopPropagation()}
>
<Icon
icon='heroicons:arrow-down-tray'
width={18}
height={18}
/>
Template XLSX
</Button>
</div>
<div className='flex items-center justify-center mb-10'>
<Button
type='button'
variant='outline'
className='btn-sm rounded-2xl shadow-md border border-base-300'
onClick={handleDownloadTemplate}
>
<Icon
icon='heroicons:arrow-down-tray'
width={18}
height={18}
/>
Template XLSX
</Button>
</div>
</>
)}
</>
)}
</section>