From e81c0a3baf271aac5a9af294734f7f43c89dfdf9 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Mon, 29 Dec 2025 19:34:17 +0700 Subject: [PATCH] feat(FE-316): Add uniformity Excel template generator --- .../uniformity/export/UniformityTemplate.tsx | 59 ++++++++++++++++ .../pages/uniformity/form/UniformityForm.tsx | 70 +++++++++++-------- 2 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 src/components/pages/uniformity/export/UniformityTemplate.tsx diff --git a/src/components/pages/uniformity/export/UniformityTemplate.tsx b/src/components/pages/uniformity/export/UniformityTemplate.tsx new file mode 100644 index 00000000..27e3b7e5 --- /dev/null +++ b/src/components/pages/uniformity/export/UniformityTemplate.tsx @@ -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.'); + } +}; diff --git a/src/components/pages/uniformity/form/UniformityForm.tsx b/src/components/pages/uniformity/form/UniformityForm.tsx index 3da378d6..a57cc9d4 100644 --- a/src/components/pages/uniformity/form/UniformityForm.tsx +++ b/src/components/pages/uniformity/form/UniformityForm.tsx @@ -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) => { 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 = ({ {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)'} -
-
- - Templates - -
-
+ {projectFlockKandangLookup?.available_quantity && ( + <> +
+
+ + Templates + +
+
-
- -
+
+ +
+ + )} )}