From 3d910f78db57c674252b0dda4dab3cff7fe3abb6 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Fri, 24 Apr 2026 16:22:15 +0700 Subject: [PATCH 1/6] fix: set initial value to MarketingFilter --- .../pages/marketing/MarketingFilter.tsx | 72 ++++++++------- .../pages/marketing/MarketingTable.tsx | 88 ++++++++++++++++++- 2 files changed, 127 insertions(+), 33 deletions(-) diff --git a/src/components/pages/marketing/MarketingFilter.tsx b/src/components/pages/marketing/MarketingFilter.tsx index 86298598..8e1ab8c0 100644 --- a/src/components/pages/marketing/MarketingFilter.tsx +++ b/src/components/pages/marketing/MarketingFilter.tsx @@ -1,6 +1,6 @@ 'use client'; -import { RefObject, useMemo } from 'react'; +import { RefObject, useCallback, useMemo } from 'react'; import { useFormik } from 'formik'; import { Icon } from '@iconify/react'; import Modal from '@/components/Modal'; @@ -17,22 +17,31 @@ import { import { MarketingFilter } from '@/types/api/marketing/marketing'; import SelectInputCheckbox from '@/components/input/SelectInputCheckbox'; import { MarketingApi } from '@/services/api/marketing/marketing'; -import { CustomerApi } from '@/services/api/master-data'; +import { CustomerApi, ProductApi } from '@/services/api/master-data'; import { isResponseSuccess } from '@/lib/api-helper'; import { BaseMarketing, BaseSalesOrder } from '@/types/api/marketing/marketing'; import { ProjectFlockApi } from '@/services/api/production'; import { ProjectFlock } from '@/types/api/production/project-flock'; +import { Product } from '@/types/api/master-data/product'; interface MarketingFilterModal { ref: RefObject; onSubmit?: (values: MarketingFilter) => void; onReset?: () => void; + initialValues?: { + product_ids: OptionType[]; + status: OptionType | null; + customer: OptionType | null; + project_flock: OptionType | null; + project_flock_kandang: OptionType | null; + }; } const MarketingFilterModal = ({ ref, onSubmit, onReset, + initialValues, }: MarketingFilterModal) => { const closeModalHandler = () => { ref.current?.close(); @@ -40,36 +49,13 @@ const MarketingFilterModal = ({ // ===== OPTIONS ===== const { - rawData: productsRawData, + options: productsOptions, isLoadingOptions: isLoadingProductsOptions, setInputValue: setProductsInputValue, loadMore: loadMoreProducts, - } = useSelect( - MarketingApi.basePath, - 'id', - 'so_number', - 'search' - ); - - const productsOptions = useMemo(() => { - if (!productsRawData || !isResponseSuccess(productsRawData)) return []; - - const productsMap = new Map(); - - productsRawData.data.forEach((deliveryOrder: BaseMarketing) => { - deliveryOrder.sales_order?.forEach((so: BaseSalesOrder) => { - const product = so.product_warehouse?.product; - if (product?.id && product?.name) { - productsMap.set(product.id, { - value: product.id, - label: product.name, - }); - } - }); - }); - - return Array.from(productsMap.values()); - }, [productsRawData]); + } = useSelect(ProductApi.basePath, 'id', 'name', 'search', { + include_all: 'true', + }); const { options: customersOptions, @@ -102,7 +88,7 @@ const MarketingFilterModal = ({ ]; const formik = useFormik({ - initialValues: { + initialValues: initialValues || { product_ids: [], status: null, customer: null, @@ -114,11 +100,17 @@ const MarketingFilterModal = ({ onSubmit: async (values) => { const formattedValues: MarketingFilter = { product_ids: values.product_ids.map((item) => Number(item.value)), + product_names: values.product_ids.map((item) => item.label), status: values.status?.value.toString() || '', + status_name: values.status?.label || '-', customer_id: Number(values.customer?.value), - project_flock_id: Number(values.project_flock?.value) || undefined, + customer_name: values.customer?.label || '-', + project_flock_id: values.project_flock?.value || undefined, + project_flock_name: values.project_flock?.label, project_flock_kandang_id: Number(values.project_flock_kandang?.value) || undefined, + project_flock_kandang_name: + values.project_flock_kandang?.label || undefined, }; onSubmit?.(formattedValues); @@ -131,6 +123,22 @@ const MarketingFilterModal = ({ }, }); + const { resetForm } = formik; + + const formikResetHandler = useCallback(() => { + resetForm({ + values: { + product_ids: [], + status: null, + customer: null, + project_flock: null, + project_flock_kandang: null, + }, + }); + onReset?.(); + closeModalHandler(); + }, [resetForm, onReset, closeModalHandler]); + const productChangeHandler = (val: OptionType | OptionType[] | null) => { formik.setFieldValue('product_ids', val as OptionType[]); }; @@ -176,7 +184,7 @@ const MarketingFilterModal = ({ >
{/* Modal Header */} diff --git a/src/components/pages/marketing/MarketingTable.tsx b/src/components/pages/marketing/MarketingTable.tsx index 4c7edce1..06949171 100644 --- a/src/components/pages/marketing/MarketingTable.tsx +++ b/src/components/pages/marketing/MarketingTable.tsx @@ -189,10 +189,15 @@ const MarketingTable = () => { initial: { search: '', product_ids: '', + product_names: '', status: '', + status_name: '', customer_id: '', + customer_name: '', project_flock_id: '', + project_flock_name: '', project_flock_kandang_id: '', + project_flock_kandang_name: '', }, paramMap: { page: 'page', @@ -203,6 +208,13 @@ const MarketingTable = () => { project_flock_id: 'project_flock_id', project_flock_kandang_id: 'project_flock_kandang_id', }, + excludeKeysFromUrl: [ + 'product_names', + 'status_name', + 'customer_name', + 'project_flock_name', + 'project_flock_kandang_name', + ], persist: true, storeName: 'marketing-table', @@ -225,17 +237,21 @@ const MarketingTable = () => { values.product_ids?.map((item) => item.toString()).join(','), true ); + updateFilter('product_names', values.product_names?.join(',')); updateFilter('status', values.status ? values.status.toString() : '', true); + updateFilter('status_name', values.status_name, true); updateFilter( 'customer_id', values.customer_id ? values.customer_id.toString() : '', true ); + updateFilter('customer_name', values.customer_name, true); updateFilter( 'project_flock_id', values.project_flock_id ? values.project_flock_id.toString() : '', true ); + updateFilter('project_flock_name', values.project_flock_name ?? '', true); updateFilter( 'project_flock_kandang_id', values.project_flock_kandang_id @@ -243,6 +259,11 @@ const MarketingTable = () => { : '', true ); + updateFilter( + 'project_flock_kandang_name', + values.project_flock_kandang_name ?? '', + true + ); }; const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] = @@ -250,10 +271,15 @@ const MarketingTable = () => { const filterResetHandler = () => { updateFilter('product_ids', '', true); + updateFilter('product_names', '', true); updateFilter('status', '', true); + updateFilter('status_name', '', true); updateFilter('customer_id', '', true); + updateFilter('customer_name', '', true); updateFilter('project_flock_id', '', true); + updateFilter('project_flock_name', '', true); updateFilter('project_flock_kandang_id', '', true); + updateFilter('project_flock_kandang_name', '', true); }; const approveClickHandler = () => { @@ -333,6 +359,56 @@ const MarketingTable = () => { ? 'DELIVERY_ORDER' : null; + const marketingFilterInitialValues = useMemo(() => { + const productIds = tableFilterState.product_ids + ? tableFilterState.product_ids + .split(',') + .map((item) => item.trim()) + .filter(Boolean) + : []; + + const productLabels = tableFilterState.product_names + ? tableFilterState.product_names + .split(',') + .map((item) => item.trim()) + .filter(Boolean) + : []; + + return { + product_ids: productIds.map((value, idx) => ({ + value: Number(value), + label: productLabels[idx] || '-', + })), + status: tableFilterState.status + ? { + value: tableFilterState.status, + label: tableFilterState.status_name, + } + : null, + + customer: tableFilterState.customer_id + ? { + value: Number(tableFilterState.customer_id), + label: tableFilterState.customer_name, + } + : null, + + project_flock: tableFilterState.project_flock_id + ? { + value: Number(tableFilterState.project_flock_id), + label: tableFilterState.project_flock_name, + } + : null, + + project_flock_kandang: tableFilterState.project_flock_kandang_id + ? { + value: Number(tableFilterState.project_flock_kandang_id), + label: tableFilterState.project_flock_kandang_name, + } + : null, + }; + }, [tableFilterState]); + const approveMarketingHandler = async (notes: string) => { if (idsToProcess.length === 0) { toast.error(`Tidak ada data yang valid untuk di ${approveAction}.`); @@ -774,7 +850,16 @@ const MarketingTable = () => {
{ filterModal.openModal(); }} @@ -1146,6 +1231,7 @@ const MarketingTable = () => { ref={filterModal.ref} onSubmit={filterSubmitHandler} onReset={filterResetHandler} + initialValues={marketingFilterInitialValues} /> ); From 7dfa5233f36b3e028745015ce17322bc3b66454a Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Fri, 24 Apr 2026 16:22:23 +0700 Subject: [PATCH 2/6] fix: adjust MarketingFilter type --- src/types/api/marketing/marketing.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/types/api/marketing/marketing.d.ts b/src/types/api/marketing/marketing.d.ts index e9967a2b..ee3a95d3 100644 --- a/src/types/api/marketing/marketing.d.ts +++ b/src/types/api/marketing/marketing.d.ts @@ -95,10 +95,15 @@ export type Marketing = BaseMetadata & BaseMarketing; */ export type MarketingFilter = { product_ids: number[]; + product_names: string[]; status: string; + status_name: string; customer_id: number; + customer_name: string; project_flock_id?: number; + project_flock_name?: string; project_flock_kandang_id?: number; + project_flock_kandang_name?: string; }; /** From d4d77bb13aa54412504db933fd0ce0a1c5528e19 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Fri, 24 Apr 2026 16:22:46 +0700 Subject: [PATCH 3/6] fix: add excluded fields in ButtonFilter --- src/components/pages/purchase/PurchaseTable.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/pages/purchase/PurchaseTable.tsx b/src/components/pages/purchase/PurchaseTable.tsx index ada8a62e..610d0565 100644 --- a/src/components/pages/purchase/PurchaseTable.tsx +++ b/src/components/pages/purchase/PurchaseTable.tsx @@ -643,6 +643,12 @@ const PurchaseTable = () => { 'search', 'filter_by', 'sort_by', + 'product_category_name', + 'supplier_name', + 'area_name', + 'location_name', + 'project_flock_name', + 'project_flock_kandang_name', ]} fieldGroups={[['startDate', 'endDate']]} onClick={filterModal.openModal} From b2dfb8fec630bddbda803db053fad5577f089d13 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Fri, 24 Apr 2026 17:10:11 +0700 Subject: [PATCH 4/6] fix: adjust share to whatsapp message --- .../detail/DetailDailyChecklistContent.tsx | 200 ++++++++++++------ 1 file changed, 138 insertions(+), 62 deletions(-) diff --git a/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx b/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx index d927d312..9af93ee8 100644 --- a/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx +++ b/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx @@ -556,27 +556,68 @@ export function DetailDailyChecklistContent() { }); }; - const shareHandler = async () => { - setIsGeneratingImage(true); - - const htmlBlob = await htmlToImage.toBlob(document.body); - const imgFile = new File( - [htmlBlob!], - `daily-checklist-${header?.date}-${header?.kandang_name}-${header?.category}.png`, - { - type: 'image/png', - } + const isMobileDevice = () => { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent ); + }; + + const getStatusMessage = () => { + switch (header?.status) { + case 'DRAFT': + return 'Checklist harian perlu disubmit'; + case 'SUBMITTED': + return 'Checklist harian menunggu persetujuan'; + case 'APPROVED': + return 'Checklist harian telah disetujui'; + case 'REJECTED': + return 'Checklist harian telah ditolak'; + default: + return ''; + } + }; + + const shareHandler = async () => { + const isMobile = isMobileDevice(); + + if (isMobile) { + setIsGeneratingImage(true); + } + + const baseTitle = `Daily Checklist - ${formatDate(header?.date || '')} - ${header?.kandang_name} - ${header?.category}`; + const statusMsg = getStatusMessage(); + const statusInfo = `\nStatus: ${header?.status}${statusMsg ? ` - ${statusMsg}` : ''}`; + const urlMessage = `\n\nView full checklist: ${window.location.href}`; + const fullMessage = baseTitle + statusInfo + urlMessage; + + let shareData: ShareData; + + if (isMobile) { + const htmlBlob = await htmlToImage.toBlob(document.body); + const imgFile = new File( + [htmlBlob!], + `daily-checklist-${header?.date}-${header?.kandang_name}-${header?.category}.png`, + { + type: 'image/png', + } + ); + + shareData = { + files: [imgFile], + title: baseTitle, + text: fullMessage, + url: window.location.href, + }; + } else { + shareData = { + title: baseTitle, + text: fullMessage, + url: window.location.href, + }; + } setIsGeneratingImage(false); - const shareData = { - files: [imgFile], - title: `Daily Checklist - ${formatDate(header?.date || '')} - ${header?.kandang_name} - ${header?.category}`, - text: `Daily Checklist - ${formatDate(header?.date || '')} - ${header?.kandang_name} - ${header?.category}`, - url: window.location.href, - }; - try { if (!navigator.canShare(shareData)) { toast.error( @@ -592,6 +633,25 @@ export function DetailDailyChecklistContent() { } }; + const shareToWhatsAppHandler = async () => { + const isMobile = isMobileDevice(); + setIsGeneratingImage(true); + + const statusMsg = getStatusMessage(); + const category = header?.category || ''; + const message = encodeURIComponent( + `Daily Checklist\n\nTanggal: ${formatDate(header?.date || '')}\nKandang: ${header?.kandang_name}\nKategori: ${CATEGORY_LABELS[category] || category}\nProgress: ${header?.progress_percent}%\nStatus: ${header?.status}${statusMsg ? ` - ${statusMsg}` : ''}\n\nLihat detail lengkap: ${window.location.href}` + ); + + setIsGeneratingImage(false); + + const whatsappUrl = isMobile + ? `https://wa.me/?text=${message}` + : `https://web.whatsapp.com/send?text=${message}`; + + window.open(whatsappUrl, '_blank'); + }; + if (loading) { return (
@@ -618,8 +678,8 @@ export function DetailDailyChecklistContent() { return (
- {/* Page Title with Back Button */} -
+ {/* Action Buttons */} +
+ +
+ + )} + + + +
+
- {header.status === 'SUBMITTED' && ( - -
- - -
-
- )} - - + {/* Page Title */} +
+

+ Detail Daily Checklist +

+

+ Lihat detail checklist harian +

{/* Header Info Card */} From b3198a44e98d1c1ca302663bca451c80295afbee Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Fri, 24 Apr 2026 17:10:38 +0700 Subject: [PATCH 5/6] uncomment pre-commit --- .husky/pre-commit | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 0836a1c5..de6b606b 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ -#npm run format -#npm run lint -#npm run typecheck -#git add . +npm run format +npm run lint +npm run typecheck +git add . From a369386922ada4e96ab268494343c0d40e0433ec Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Fri, 24 Apr 2026 17:22:20 +0700 Subject: [PATCH 6/6] feat: add share to whatsapp after submitting daily checklist --- .../daily-checklist/DailyChecklistContent.tsx | 78 ++++++++++++++++++- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx b/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx index 4024b581..b83c7b6a 100644 --- a/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx +++ b/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx @@ -53,7 +53,6 @@ import { useRouter, useSearchParams, usePathname } from 'next/navigation'; import { Icon } from '@iconify/react'; import { DailyChecklistKandangApi } from '@/services/api/daily-checklist/kandang'; -// Static categories const CATEGORIES = [ { value: 'pullet_open', label: 'Pullet Open' }, { value: 'pullet_close', label: 'Pullet Close' }, @@ -62,6 +61,14 @@ const CATEGORIES = [ { value: 'empty_kandang', label: 'Kandang Kosong' }, ]; +const CATEGORY_LABELS: { [key: string]: string } = { + pullet_open: 'Pullet Open', + pullet_close: 'Pullet Close', + produksi_open: 'Produksi Open', + produksi_close: 'Produksi Close', + empty_kandang: 'Kandang Kosong', +}; + const TIME_TYPE_ORDER = ['Umum', 'Pagi', 'Siang', 'Sore', 'Malam']; const TIME_TYPE_LABELS: { [key: string]: string } = { Umum: 'Umum', @@ -246,7 +253,6 @@ export function DailyChecklistContent() { } }, [selectedCategory]); - // Format date for display const formatDateForDisplay = (dateStr: string) => { if (!dateStr) return 'Pilih tanggal'; const [year, month, day] = dateStr.split('-'); @@ -259,6 +265,36 @@ export function DailyChecklistContent() { }); }; + const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString('id-ID', { + day: '2-digit', + month: 'long', + year: 'numeric', + }); + }; + + const isMobileDevice = () => { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent + ); + }; + + const getStatusMessage = () => { + switch (checklistStatus) { + case 'DRAFT': + return 'Checklist harian perlu disubmit'; + case 'SUBMITTED': + return 'Checklist harian menunggu persetujuan'; + case 'APPROVED': + return 'Checklist harian telah disetujui'; + case 'REJECTED': + return 'Checklist harian telah ditolak'; + default: + return ''; + } + }; + // Fetch master data on mount useEffect(() => { setInitialLoading(false); @@ -842,7 +878,43 @@ export function DailyChecklistContent() { } setChecklistStatus('SUBMITTED'); - toast.success('Checklist berhasil disubmit untuk approval'); + + const shareToWhatsApp = () => { + const kandangName = kandangOptions.find( + (k) => String(k.value) === kandangId + )?.label || kandangId; + const statusMsg = getStatusMessage(); + const category = selectedCategory || ''; + const message = encodeURIComponent( + `Daily Checklist\n\nTanggal: ${formatDate(date)}\nKandang: ${kandangName}\nKategori: ${CATEGORY_LABELS[category] || category}\nStatus: SUBMITTED${statusMsg ? ` - ${statusMsg}` : ''}\n\nLihat detail lengkap: ${window.location.href}` + ); + + const isMobile = isMobileDevice(); + const whatsappUrl = isMobile + ? `https://wa.me/?text=${message}` + : `https://web.whatsapp.com/send?text=${message}`; + + window.open(whatsappUrl, '_blank'); + }; + + toast.success('Checklist berhasil disubmit untuk approval', { + action: { + label: 'Bagikan ke WhatsApp', + onClick: shareToWhatsApp, + }, + description: ( + + ), + }); } catch (error) { console.error('Error submitting:', error); toast.error('Terjadi kesalahan');