mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
Merge branch 'feat/share-daily-checklist-to-wa' into 'development'
[FEAT/FE] Adjust Share Daily Checklist to Whatsapp See merge request mbugroup/lti-web-client!429
This commit is contained in:
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
#npm run format
|
npm run format
|
||||||
#npm run lint
|
npm run lint
|
||||||
#npm run typecheck
|
npm run typecheck
|
||||||
#git add .
|
git add .
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { RefObject, useMemo } from 'react';
|
import { RefObject, useCallback, useMemo } from 'react';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
@@ -17,22 +17,31 @@ import {
|
|||||||
import { MarketingFilter } from '@/types/api/marketing/marketing';
|
import { MarketingFilter } from '@/types/api/marketing/marketing';
|
||||||
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||||||
import { MarketingApi } from '@/services/api/marketing/marketing';
|
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 { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { BaseMarketing, BaseSalesOrder } from '@/types/api/marketing/marketing';
|
import { BaseMarketing, BaseSalesOrder } from '@/types/api/marketing/marketing';
|
||||||
import { ProjectFlockApi } from '@/services/api/production';
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||||
|
import { Product } from '@/types/api/master-data/product';
|
||||||
|
|
||||||
interface MarketingFilterModal {
|
interface MarketingFilterModal {
|
||||||
ref: RefObject<HTMLDialogElement | null>;
|
ref: RefObject<HTMLDialogElement | null>;
|
||||||
onSubmit?: (values: MarketingFilter) => void;
|
onSubmit?: (values: MarketingFilter) => void;
|
||||||
onReset?: () => void;
|
onReset?: () => void;
|
||||||
|
initialValues?: {
|
||||||
|
product_ids: OptionType<number>[];
|
||||||
|
status: OptionType<string> | null;
|
||||||
|
customer: OptionType<number> | null;
|
||||||
|
project_flock: OptionType<number> | null;
|
||||||
|
project_flock_kandang: OptionType<number> | null;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const MarketingFilterModal = ({
|
const MarketingFilterModal = ({
|
||||||
ref,
|
ref,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onReset,
|
onReset,
|
||||||
|
initialValues,
|
||||||
}: MarketingFilterModal) => {
|
}: MarketingFilterModal) => {
|
||||||
const closeModalHandler = () => {
|
const closeModalHandler = () => {
|
||||||
ref.current?.close();
|
ref.current?.close();
|
||||||
@@ -40,36 +49,13 @@ const MarketingFilterModal = ({
|
|||||||
|
|
||||||
// ===== OPTIONS =====
|
// ===== OPTIONS =====
|
||||||
const {
|
const {
|
||||||
rawData: productsRawData,
|
options: productsOptions,
|
||||||
isLoadingOptions: isLoadingProductsOptions,
|
isLoadingOptions: isLoadingProductsOptions,
|
||||||
setInputValue: setProductsInputValue,
|
setInputValue: setProductsInputValue,
|
||||||
loadMore: loadMoreProducts,
|
loadMore: loadMoreProducts,
|
||||||
} = useSelect<BaseMarketing>(
|
} = useSelect<Product>(ProductApi.basePath, 'id', 'name', 'search', {
|
||||||
MarketingApi.basePath,
|
include_all: 'true',
|
||||||
'id',
|
|
||||||
'so_number',
|
|
||||||
'search'
|
|
||||||
);
|
|
||||||
|
|
||||||
const productsOptions = useMemo(() => {
|
|
||||||
if (!productsRawData || !isResponseSuccess(productsRawData)) return [];
|
|
||||||
|
|
||||||
const productsMap = new Map<number, { value: number; label: string }>();
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
options: customersOptions,
|
options: customersOptions,
|
||||||
@@ -102,7 +88,7 @@ const MarketingFilterModal = ({
|
|||||||
];
|
];
|
||||||
|
|
||||||
const formik = useFormik<MarketingFilterFormValues>({
|
const formik = useFormik<MarketingFilterFormValues>({
|
||||||
initialValues: {
|
initialValues: initialValues || {
|
||||||
product_ids: [],
|
product_ids: [],
|
||||||
status: null,
|
status: null,
|
||||||
customer: null,
|
customer: null,
|
||||||
@@ -114,11 +100,17 @@ const MarketingFilterModal = ({
|
|||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
const formattedValues: MarketingFilter = {
|
const formattedValues: MarketingFilter = {
|
||||||
product_ids: values.product_ids.map((item) => Number(item.value)),
|
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: values.status?.value.toString() || '',
|
||||||
|
status_name: values.status?.label || '-',
|
||||||
customer_id: Number(values.customer?.value),
|
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:
|
project_flock_kandang_id:
|
||||||
Number(values.project_flock_kandang?.value) || undefined,
|
Number(values.project_flock_kandang?.value) || undefined,
|
||||||
|
project_flock_kandang_name:
|
||||||
|
values.project_flock_kandang?.label || undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmit?.(formattedValues);
|
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) => {
|
const productChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
formik.setFieldValue('product_ids', val as OptionType[]);
|
formik.setFieldValue('product_ids', val as OptionType[]);
|
||||||
};
|
};
|
||||||
@@ -176,7 +184,7 @@ const MarketingFilterModal = ({
|
|||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={formik.handleSubmit}
|
||||||
onReset={formik.handleReset}
|
onReset={formikResetHandler}
|
||||||
className='w-full flex flex-col'
|
className='w-full flex flex-col'
|
||||||
>
|
>
|
||||||
{/* Modal Header */}
|
{/* Modal Header */}
|
||||||
|
|||||||
@@ -189,10 +189,15 @@ const MarketingTable = () => {
|
|||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
product_ids: '',
|
product_ids: '',
|
||||||
|
product_names: '',
|
||||||
status: '',
|
status: '',
|
||||||
|
status_name: '',
|
||||||
customer_id: '',
|
customer_id: '',
|
||||||
|
customer_name: '',
|
||||||
project_flock_id: '',
|
project_flock_id: '',
|
||||||
|
project_flock_name: '',
|
||||||
project_flock_kandang_id: '',
|
project_flock_kandang_id: '',
|
||||||
|
project_flock_kandang_name: '',
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
@@ -203,6 +208,13 @@ const MarketingTable = () => {
|
|||||||
project_flock_id: 'project_flock_id',
|
project_flock_id: 'project_flock_id',
|
||||||
project_flock_kandang_id: 'project_flock_kandang_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,
|
persist: true,
|
||||||
storeName: 'marketing-table',
|
storeName: 'marketing-table',
|
||||||
@@ -225,17 +237,21 @@ const MarketingTable = () => {
|
|||||||
values.product_ids?.map((item) => item.toString()).join(','),
|
values.product_ids?.map((item) => item.toString()).join(','),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
updateFilter('product_names', values.product_names?.join(','));
|
||||||
updateFilter('status', values.status ? values.status.toString() : '', true);
|
updateFilter('status', values.status ? values.status.toString() : '', true);
|
||||||
|
updateFilter('status_name', values.status_name, true);
|
||||||
updateFilter(
|
updateFilter(
|
||||||
'customer_id',
|
'customer_id',
|
||||||
values.customer_id ? values.customer_id.toString() : '',
|
values.customer_id ? values.customer_id.toString() : '',
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
updateFilter('customer_name', values.customer_name, true);
|
||||||
updateFilter(
|
updateFilter(
|
||||||
'project_flock_id',
|
'project_flock_id',
|
||||||
values.project_flock_id ? values.project_flock_id.toString() : '',
|
values.project_flock_id ? values.project_flock_id.toString() : '',
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
updateFilter('project_flock_name', values.project_flock_name ?? '', true);
|
||||||
updateFilter(
|
updateFilter(
|
||||||
'project_flock_kandang_id',
|
'project_flock_kandang_id',
|
||||||
values.project_flock_kandang_id
|
values.project_flock_kandang_id
|
||||||
@@ -243,6 +259,11 @@ const MarketingTable = () => {
|
|||||||
: '',
|
: '',
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
updateFilter(
|
||||||
|
'project_flock_kandang_name',
|
||||||
|
values.project_flock_kandang_name ?? '',
|
||||||
|
true
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] =
|
const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] =
|
||||||
@@ -250,10 +271,15 @@ const MarketingTable = () => {
|
|||||||
|
|
||||||
const filterResetHandler = () => {
|
const filterResetHandler = () => {
|
||||||
updateFilter('product_ids', '', true);
|
updateFilter('product_ids', '', true);
|
||||||
|
updateFilter('product_names', '', true);
|
||||||
updateFilter('status', '', true);
|
updateFilter('status', '', true);
|
||||||
|
updateFilter('status_name', '', true);
|
||||||
updateFilter('customer_id', '', true);
|
updateFilter('customer_id', '', true);
|
||||||
|
updateFilter('customer_name', '', true);
|
||||||
updateFilter('project_flock_id', '', true);
|
updateFilter('project_flock_id', '', true);
|
||||||
|
updateFilter('project_flock_name', '', true);
|
||||||
updateFilter('project_flock_kandang_id', '', true);
|
updateFilter('project_flock_kandang_id', '', true);
|
||||||
|
updateFilter('project_flock_kandang_name', '', true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const approveClickHandler = () => {
|
const approveClickHandler = () => {
|
||||||
@@ -333,6 +359,56 @@ const MarketingTable = () => {
|
|||||||
? 'DELIVERY_ORDER'
|
? 'DELIVERY_ORDER'
|
||||||
: null;
|
: 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) => {
|
const approveMarketingHandler = async (notes: string) => {
|
||||||
if (idsToProcess.length === 0) {
|
if (idsToProcess.length === 0) {
|
||||||
toast.error(`Tidak ada data yang valid untuk di ${approveAction}.`);
|
toast.error(`Tidak ada data yang valid untuk di ${approveAction}.`);
|
||||||
@@ -774,7 +850,16 @@ const MarketingTable = () => {
|
|||||||
<div className='flex flex-row gap-3'>
|
<div className='flex flex-row gap-3'>
|
||||||
<ButtonFilter
|
<ButtonFilter
|
||||||
values={tableFilterState}
|
values={tableFilterState}
|
||||||
excludeFields={['page', 'pageSize', 'search']}
|
excludeFields={[
|
||||||
|
'page',
|
||||||
|
'pageSize',
|
||||||
|
'search',
|
||||||
|
'product_names',
|
||||||
|
'status_name',
|
||||||
|
'customer_name',
|
||||||
|
'project_flock_name',
|
||||||
|
'project_flock_kandang_name',
|
||||||
|
]}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
filterModal.openModal();
|
filterModal.openModal();
|
||||||
}}
|
}}
|
||||||
@@ -1146,6 +1231,7 @@ const MarketingTable = () => {
|
|||||||
ref={filterModal.ref}
|
ref={filterModal.ref}
|
||||||
onSubmit={filterSubmitHandler}
|
onSubmit={filterSubmitHandler}
|
||||||
onReset={filterResetHandler}
|
onReset={filterResetHandler}
|
||||||
|
initialValues={marketingFilterInitialValues}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -643,6 +643,12 @@ const PurchaseTable = () => {
|
|||||||
'search',
|
'search',
|
||||||
'filter_by',
|
'filter_by',
|
||||||
'sort_by',
|
'sort_by',
|
||||||
|
'product_category_name',
|
||||||
|
'supplier_name',
|
||||||
|
'area_name',
|
||||||
|
'location_name',
|
||||||
|
'project_flock_name',
|
||||||
|
'project_flock_kandang_name',
|
||||||
]}
|
]}
|
||||||
fieldGroups={[['startDate', 'endDate']]}
|
fieldGroups={[['startDate', 'endDate']]}
|
||||||
onClick={filterModal.openModal}
|
onClick={filterModal.openModal}
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ import { useRouter, useSearchParams, usePathname } from 'next/navigation';
|
|||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { DailyChecklistKandangApi } from '@/services/api/daily-checklist/kandang';
|
import { DailyChecklistKandangApi } from '@/services/api/daily-checklist/kandang';
|
||||||
|
|
||||||
// Static categories
|
|
||||||
const CATEGORIES = [
|
const CATEGORIES = [
|
||||||
{ value: 'pullet_open', label: 'Pullet Open' },
|
{ value: 'pullet_open', label: 'Pullet Open' },
|
||||||
{ value: 'pullet_close', label: 'Pullet Close' },
|
{ value: 'pullet_close', label: 'Pullet Close' },
|
||||||
@@ -62,6 +61,14 @@ const CATEGORIES = [
|
|||||||
{ value: 'empty_kandang', label: 'Kandang Kosong' },
|
{ 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_ORDER = ['Umum', 'Pagi', 'Siang', 'Sore', 'Malam'];
|
||||||
const TIME_TYPE_LABELS: { [key: string]: string } = {
|
const TIME_TYPE_LABELS: { [key: string]: string } = {
|
||||||
Umum: 'Umum',
|
Umum: 'Umum',
|
||||||
@@ -246,7 +253,6 @@ export function DailyChecklistContent() {
|
|||||||
}
|
}
|
||||||
}, [selectedCategory]);
|
}, [selectedCategory]);
|
||||||
|
|
||||||
// Format date for display
|
|
||||||
const formatDateForDisplay = (dateStr: string) => {
|
const formatDateForDisplay = (dateStr: string) => {
|
||||||
if (!dateStr) return 'Pilih tanggal';
|
if (!dateStr) return 'Pilih tanggal';
|
||||||
const [year, month, day] = dateStr.split('-');
|
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
|
// Fetch master data on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInitialLoading(false);
|
setInitialLoading(false);
|
||||||
@@ -842,7 +878,43 @@ export function DailyChecklistContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setChecklistStatus('SUBMITTED');
|
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: (
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/daily-checklist/list-daily-checklist/detail/?checklistId=${dailyChecklistId}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className='text-blue-600 hover:text-blue-800 underline font-medium'
|
||||||
|
>
|
||||||
|
Lihat Detail
|
||||||
|
</button>
|
||||||
|
),
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error submitting:', error);
|
console.error('Error submitting:', error);
|
||||||
toast.error('Terjadi kesalahan');
|
toast.error('Terjadi kesalahan');
|
||||||
|
|||||||
+95
-19
@@ -556,9 +556,43 @@ export function DetailDailyChecklistContent() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const shareHandler = async () => {
|
const isMobileDevice = () => {
|
||||||
setIsGeneratingImage(true);
|
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 htmlBlob = await htmlToImage.toBlob(document.body);
|
||||||
const imgFile = new File(
|
const imgFile = new File(
|
||||||
[htmlBlob!],
|
[htmlBlob!],
|
||||||
@@ -568,14 +602,21 @@ export function DetailDailyChecklistContent() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
setIsGeneratingImage(false);
|
shareData = {
|
||||||
|
|
||||||
const shareData = {
|
|
||||||
files: [imgFile],
|
files: [imgFile],
|
||||||
title: `Daily Checklist - ${formatDate(header?.date || '')} - ${header?.kandang_name} - ${header?.category}`,
|
title: baseTitle,
|
||||||
text: `Daily Checklist - ${formatDate(header?.date || '')} - ${header?.kandang_name} - ${header?.category}`,
|
text: fullMessage,
|
||||||
url: window.location.href,
|
url: window.location.href,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
shareData = {
|
||||||
|
title: baseTitle,
|
||||||
|
text: fullMessage,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsGeneratingImage(false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!navigator.canShare(shareData)) {
|
if (!navigator.canShare(shareData)) {
|
||||||
@@ -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) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className='min-h-screen'>
|
<div className='min-h-screen'>
|
||||||
@@ -618,8 +678,8 @@ export function DetailDailyChecklistContent() {
|
|||||||
return (
|
return (
|
||||||
<div className='min-h-screen'>
|
<div className='min-h-screen'>
|
||||||
<div className='p-6'>
|
<div className='p-6'>
|
||||||
{/* Page Title with Back Button */}
|
{/* Action Buttons */}
|
||||||
<div className='mb-6 flex items-center gap-4'>
|
<div className='mb-6 flex items-start sm:items-center justify-between gap-4 flex-wrap'>
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
size='sm'
|
size='sm'
|
||||||
@@ -630,18 +690,10 @@ export function DetailDailyChecklistContent() {
|
|||||||
Kembali
|
Kembali
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className='flex-1'>
|
<div className='flex items-center gap-2 flex-wrap'>
|
||||||
<h1 className='text-2xl font-semibold text-gray-900'>
|
|
||||||
Detail Daily Checklist
|
|
||||||
</h1>
|
|
||||||
<p className='text-sm text-gray-600 mt-1'>
|
|
||||||
Lihat detail checklist harian
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{header.status === 'SUBMITTED' && (
|
{header.status === 'SUBMITTED' && (
|
||||||
<RequirePermission permissions='lti.daily_checklist.create'>
|
<RequirePermission permissions='lti.daily_checklist.create'>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2 flex-wrap'>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleApprove}
|
onClick={handleApprove}
|
||||||
disabled={actionLoading}
|
disabled={actionLoading}
|
||||||
@@ -675,6 +727,30 @@ export function DetailDailyChecklistContent() {
|
|||||||
|
|
||||||
{isGeneratingImage && 'Memuat...'}
|
{isGeneratingImage && 'Memuat...'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
size='sm'
|
||||||
|
onClick={shareToWhatsAppHandler}
|
||||||
|
disabled={isGeneratingImage}
|
||||||
|
className='border-gray-200'
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:whatsapp' className='w-4 h-4 mr-1' />
|
||||||
|
{!isGeneratingImage && 'Bagikan via WhatsApp'}
|
||||||
|
|
||||||
|
{isGeneratingImage && 'Memuat...'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Page Title */}
|
||||||
|
<div className='mb-6'>
|
||||||
|
<h1 className='text-2xl font-semibold text-gray-900'>
|
||||||
|
Detail Daily Checklist
|
||||||
|
</h1>
|
||||||
|
<p className='text-sm text-gray-600 mt-1'>
|
||||||
|
Lihat detail checklist harian
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Header Info Card */}
|
{/* Header Info Card */}
|
||||||
|
|||||||
+5
@@ -95,10 +95,15 @@ export type Marketing = BaseMetadata & BaseMarketing;
|
|||||||
*/
|
*/
|
||||||
export type MarketingFilter = {
|
export type MarketingFilter = {
|
||||||
product_ids: number[];
|
product_ids: number[];
|
||||||
|
product_names: string[];
|
||||||
status: string;
|
status: string;
|
||||||
|
status_name: string;
|
||||||
customer_id: number;
|
customer_id: number;
|
||||||
|
customer_name: string;
|
||||||
project_flock_id?: number;
|
project_flock_id?: number;
|
||||||
|
project_flock_name?: string;
|
||||||
project_flock_kandang_id?: number;
|
project_flock_kandang_id?: number;
|
||||||
|
project_flock_kandang_name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user