mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'hotfix/bug-reported-by-user' into 'production'
[HOTFIX/FE] Adjustment See merge request mbugroup/lti-web-client!365
This commit is contained in:
@@ -35,7 +35,9 @@ const NumberInput = ({
|
||||
| undefined;
|
||||
|
||||
if (newChangeEvent) {
|
||||
newChangeEvent.target.value = numberFormatValues.value;
|
||||
newChangeEvent.target.value = parseFloat(
|
||||
numberFormatValues.value
|
||||
) as unknown as string;
|
||||
|
||||
onChange?.(newChangeEvent);
|
||||
}
|
||||
|
||||
@@ -287,8 +287,8 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
PT LUMBUNG TELUR INDONESIA
|
||||
</Text>
|
||||
<Text style={ExpensePDFStyle.companyAddress}>
|
||||
SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
|
||||
Cipedes, Kec. Sukajadi, Kota Bandung 40162
|
||||
Setra Duta Raya No.L3 No.7, Ciwaruga, Kec. Parongpong, Kabupaten
|
||||
Bandung Barat, Jawa Barat 40514
|
||||
</Text>
|
||||
|
||||
<View style={ExpensePDFStyle.doubleDivider} />
|
||||
|
||||
@@ -101,8 +101,8 @@ const PDFDocument = ({
|
||||
<View style={pdfStyles.header}>
|
||||
<Text style={pdfStyles.companyInfo}>PT LUMBUNG TELUR INDONESIA</Text>
|
||||
<Text style={pdfStyles.address}>
|
||||
SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
|
||||
Cipedes, Kec. Sukajadi, Kota Bandung 40162
|
||||
Setra Duta Raya No.L3 No.7, Ciwaruga, Kec. Parongpong, Kabupaten
|
||||
Bandung Barat, Jawa Barat 40514
|
||||
</Text>
|
||||
<View style={pdfStyles.divider} />
|
||||
</View>
|
||||
|
||||
@@ -87,8 +87,8 @@ const PDFDocument = ({ data }: { data: Marketing }) => {
|
||||
<View style={pdfStyles.header}>
|
||||
<Text style={pdfStyles.companyInfo}>PT LUMBUNG TELUR INDONESIA</Text>
|
||||
<Text style={pdfStyles.address}>
|
||||
SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
|
||||
Cipedes, Kec. Sukajadi, Kota Bandung 40162
|
||||
Setra Duta Raya No.L3 No.7, Ciwaruga, Kec. Parongpong, Kabupaten
|
||||
Bandung Barat, Jawa Barat 40514
|
||||
</Text>
|
||||
<View style={pdfStyles.divider} />
|
||||
</View>
|
||||
|
||||
@@ -154,17 +154,17 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
||||
sku: values.sku,
|
||||
uom_id: values.uom_id,
|
||||
product_category_id: values.product_category_id,
|
||||
product_price: parseInt(values.product_price.toString()) || 0,
|
||||
product_price: parseFloat(values.product_price.toString()) || 0,
|
||||
selling_price: values.selling_price
|
||||
? parseInt(values.selling_price.toString()) || 0
|
||||
? parseFloat(values.selling_price.toString()) || 0
|
||||
: undefined,
|
||||
tax: values.tax ? parseInt(values.tax.toString()) || 0 : undefined,
|
||||
tax: values.tax ? parseFloat(values.tax.toString()) || 0 : undefined,
|
||||
expiry_period: values.expiry_period
|
||||
? parseInt(values.expiry_period.toString()) || 0
|
||||
? parseFloat(values.expiry_period.toString()) || 0
|
||||
: undefined,
|
||||
suppliers: values.suppliers.map((s) => ({
|
||||
supplier_id: s.supplier?.value as number,
|
||||
price: parseInt(s.price.toString()) || 0,
|
||||
price: parseFloat(s.price.toString()) || 0,
|
||||
})),
|
||||
flag: values.flag,
|
||||
sub_flags: values.sub_flags,
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
'use client';
|
||||
|
||||
import { RefObject, useState, useEffect } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import { Icon } from '@iconify/react';
|
||||
import Modal from '@/components/Modal';
|
||||
import Button from '@/components/Button';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||||
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import { PurchaseFilter } from '@/types/api/purchase/purchase';
|
||||
import { ProductCategory } from '@/types/api/master-data/product-category';
|
||||
import { ProductCategoryApi } from '@/services/api/master-data';
|
||||
import { PURCHASE_ORDER_APPROVAL_LINE } from '@/config/approval-line';
|
||||
|
||||
interface PurchaseFilterModalProps {
|
||||
ref: RefObject<HTMLDialogElement | null>;
|
||||
onSubmit?: (values: PurchaseFilter) => void;
|
||||
onReset?: () => void;
|
||||
}
|
||||
|
||||
const PurchaseFilterModal = ({
|
||||
ref,
|
||||
onSubmit,
|
||||
onReset,
|
||||
}: PurchaseFilterModalProps) => {
|
||||
const closeModalHandler = () => {
|
||||
ref.current?.close();
|
||||
};
|
||||
|
||||
// ===== DATE ERROR STATE =====
|
||||
const [dateErrorShown, setDateErrorShown] = useState(false);
|
||||
const [hasDateError, setHasDateError] = useState(false);
|
||||
|
||||
// ===== CLEANUP TOAST ON UNMOUNT =====
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (dateErrorShown) {
|
||||
toast.dismiss();
|
||||
}
|
||||
};
|
||||
}, [dateErrorShown]);
|
||||
|
||||
// ===== CLEANUP TOAST WHEN MODAL CLOSES =====
|
||||
useEffect(() => {
|
||||
const dialogElement = ref.current;
|
||||
const handleModalClose = () => {
|
||||
if (dateErrorShown) {
|
||||
toast.dismiss();
|
||||
setDateErrorShown(false);
|
||||
}
|
||||
};
|
||||
|
||||
dialogElement?.addEventListener('close', handleModalClose);
|
||||
|
||||
return () => {
|
||||
dialogElement?.removeEventListener('close', handleModalClose);
|
||||
};
|
||||
}, [ref, dateErrorShown]);
|
||||
|
||||
const {
|
||||
setInputValue: setProductCategoryInputValue,
|
||||
options: productCategoryOptions,
|
||||
isLoadingOptions: isLoadingProductCategoryOptions,
|
||||
loadMore: loadMoreProductCategory,
|
||||
} = useSelect<ProductCategory>(
|
||||
ProductCategoryApi.basePath,
|
||||
'id',
|
||||
'name',
|
||||
'search'
|
||||
);
|
||||
|
||||
const formik = useFormik<{
|
||||
poDate: string;
|
||||
category: { label: string; value: number }[];
|
||||
status: { label: string; value: string }[];
|
||||
}>({
|
||||
initialValues: {
|
||||
poDate: '',
|
||||
category: [],
|
||||
status: [],
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
const formattedValues = {
|
||||
...values,
|
||||
category: values.category.map((item) => String(item.value)),
|
||||
status: values.status.map((item) => String(item.value)),
|
||||
};
|
||||
|
||||
onSubmit?.(formattedValues);
|
||||
closeModalHandler();
|
||||
},
|
||||
onReset: () => {
|
||||
onReset?.();
|
||||
closeModalHandler();
|
||||
},
|
||||
});
|
||||
|
||||
const productCategoryChangeHandler = (
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
formik.setFieldValue('category', val);
|
||||
};
|
||||
|
||||
const statusChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('status', val);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
ref={ref}
|
||||
className={{
|
||||
modalBox: 'p-0 rounded-xl',
|
||||
}}
|
||||
>
|
||||
<form
|
||||
onSubmit={formik.handleSubmit}
|
||||
onReset={formik.handleReset}
|
||||
className='w-full flex flex-col'
|
||||
>
|
||||
{/* Modal Header */}
|
||||
<div className='p-4 flex items-center justify-between gap-2 border-b border-gray-300'>
|
||||
<div className='flex items-center gap-2 text-primary'>
|
||||
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||
<h3 className='text-sm font-medium'>Filter Data</h3>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
color='none'
|
||||
onClick={closeModalHandler}
|
||||
className='p-0 text-base-content/50 hover:text-base-content'
|
||||
>
|
||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Modal Body */}
|
||||
<div className='p-4 flex flex-col gap-1.5'>
|
||||
<div className='flex flex-col'>
|
||||
<DateInput
|
||||
label='PO Date'
|
||||
name='poDate'
|
||||
placeholder='Pilih Tanggal'
|
||||
value={formik.values.poDate}
|
||||
onChange={formik.handleChange}
|
||||
isNestedModal
|
||||
/>
|
||||
|
||||
<SelectInputCheckbox
|
||||
label='Kategori'
|
||||
placeholder='Pilih Kategori'
|
||||
value={formik.values.category}
|
||||
onChange={productCategoryChangeHandler}
|
||||
options={productCategoryOptions}
|
||||
isLoading={isLoadingProductCategoryOptions}
|
||||
onInputChange={setProductCategoryInputValue}
|
||||
onMenuScrollToBottom={loadMoreProductCategory}
|
||||
/>
|
||||
|
||||
<SelectInputCheckbox
|
||||
label='Status'
|
||||
placeholder='Status'
|
||||
value={formik.values.status}
|
||||
onChange={statusChangeHandler}
|
||||
options={PURCHASE_ORDER_APPROVAL_LINE.map((item) => ({
|
||||
label: item.step_name,
|
||||
value: item.step_name,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
<div className='p-4 flex justify-between gap-4 border-t border-gray-300 bg-gray-100'>
|
||||
<Button
|
||||
type='reset'
|
||||
variant='ghost'
|
||||
color='none'
|
||||
className='p-3 rounded-lg text-base-content/65'
|
||||
>
|
||||
Reset Filter
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type='submit'
|
||||
className='p-3 rounded-lg w-fit sm:w-full max-w-40 text-base-100 text-sm'
|
||||
>
|
||||
Apply Filter
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default PurchaseFilterModal;
|
||||
@@ -14,6 +14,7 @@ import useSWRInfinite from 'swr/infinite';
|
||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { Icon } from '@iconify/react';
|
||||
import Table from '@/components/Table';
|
||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||
@@ -25,18 +26,19 @@ import PopoverContent from '@/components/popover/PopoverContent';
|
||||
import RequirePermission from '@/components/helper/RequirePermission';
|
||||
import StatusBadge from '@/components/helper/StatusBadge';
|
||||
import PurchaseTableSkeleton from '@/components/pages/purchase/skeleton/PurchaseTableSkeleton';
|
||||
import ButtonFilter from '@/components/helper/ButtonFilter';
|
||||
import PurchaseFilterModal from '@/components/pages/purchase/PurchaseFilterModal';
|
||||
|
||||
import { cn, formatDate } from '@/lib/helper';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { Purchase } from '@/types/api/purchase/purchase';
|
||||
import { Purchase, PurchaseFilter } from '@/types/api/purchase/purchase';
|
||||
import { PurchaseApi } from '@/services/api/purchase';
|
||||
import { ExpenseApi } from '@/services/api/expense';
|
||||
import { Expense } from '@/types/api/expense';
|
||||
import { Color } from '@/types/theme';
|
||||
import Link from 'next/link';
|
||||
|
||||
// ===== STATUS BADGE UTILITIES =====
|
||||
const statusTextMap: Record<string, string> = {
|
||||
@@ -165,14 +167,21 @@ const PurchaseTable = () => {
|
||||
} = useTableFilter({
|
||||
initial: {
|
||||
search: '',
|
||||
po_date: '',
|
||||
approval_status: '',
|
||||
product_category_id: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
pageSize: 'limit',
|
||||
po_date: 'po_date',
|
||||
approval_status: 'approval_status',
|
||||
product_category_id: 'product_category_id',
|
||||
},
|
||||
});
|
||||
|
||||
// ===== MODAL HOOKS =====
|
||||
const filterModal = useModal();
|
||||
const deleteModal = useModal();
|
||||
|
||||
// ===== API DATA FETCHING =====
|
||||
@@ -410,13 +419,17 @@ const PurchaseTable = () => {
|
||||
[updateFilter, setSearchValue]
|
||||
);
|
||||
|
||||
// const pageSizeChangeHandler = useCallback(
|
||||
// (val: OptionType | OptionType[] | null) => {
|
||||
// const newVal = val as OptionType;
|
||||
// setPageSize(newVal.value as number);
|
||||
// },
|
||||
// [setPageSize]
|
||||
// );
|
||||
const filterSubmitHandler = (values: PurchaseFilter) => {
|
||||
updateFilter('po_date', values.poDate);
|
||||
updateFilter('product_category_id', values.category.join(','));
|
||||
updateFilter('approval_status', values.status.join(','));
|
||||
};
|
||||
|
||||
const filterResetHandler = () => {
|
||||
updateFilter('po_date', '');
|
||||
updateFilter('product_category_id', '');
|
||||
updateFilter('approval_status', '');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -455,6 +468,19 @@ const PurchaseTable = () => {
|
||||
'placeholder:font-semibold placeholder:text-base-content/50',
|
||||
}}
|
||||
/>
|
||||
|
||||
<ButtonFilter
|
||||
values={tableFilterState}
|
||||
excludeFields={[
|
||||
'page',
|
||||
'pageSize',
|
||||
'search',
|
||||
'filter_by',
|
||||
'sort_by',
|
||||
]}
|
||||
onClick={filterModal.openModal}
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -513,6 +539,12 @@ const PurchaseTable = () => {
|
||||
</div>
|
||||
|
||||
{/* ===== MODAL COMPONENTS ===== */}
|
||||
<PurchaseFilterModal
|
||||
ref={filterModal.ref}
|
||||
onSubmit={filterSubmitHandler}
|
||||
onReset={filterResetHandler}
|
||||
/>
|
||||
|
||||
<ConfirmationModal
|
||||
ref={deleteModal.ref}
|
||||
type='error'
|
||||
|
||||
@@ -34,7 +34,7 @@ const pdfStyles = StyleSheet.create({
|
||||
marginBottom: 20,
|
||||
},
|
||||
logo: {
|
||||
width: 120,
|
||||
width: 30,
|
||||
height: 30,
|
||||
marginBottom: 8,
|
||||
},
|
||||
@@ -265,7 +265,7 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => {
|
||||
<View style={pdfStyles.header}>
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||
<Image
|
||||
src={'https://placehold.co/120x30/png'}
|
||||
src='/assets/img/lti-logo.png'
|
||||
style={pdfStyles.logo}
|
||||
id={'mbu-logo'}
|
||||
/>
|
||||
@@ -273,8 +273,8 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => {
|
||||
PT LUMBUNG TELUR INDONESIA
|
||||
</Text>
|
||||
<Text style={pdfStyles.address}>
|
||||
SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
|
||||
Cipedes, Kec. Sukajadi, Kota Bandung 40162
|
||||
Setra Duta Raya No.L3 No.7, Ciwaruga, Kec. Parongpong, Kabupaten
|
||||
Bandung Barat, Jawa Barat 40514
|
||||
</Text>
|
||||
<View style={pdfStyles.divider} />
|
||||
</View>
|
||||
|
||||
@@ -47,7 +47,7 @@ export const generateReportExpensePDF = async (
|
||||
doc.setFontSize(7);
|
||||
doc.setTextColor(102, 102, 102);
|
||||
doc.text(
|
||||
'SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel. Cipedes, Kec. Sukajadi, Kota Bandung 40162',
|
||||
'Setra Duta Raya No.L3 No.7, Ciwaruga, Kec. Parongpong, Kabupaten Bandung Barat, Jawa Barat 40514',
|
||||
marginX,
|
||||
25
|
||||
);
|
||||
|
||||
Vendored
+6
@@ -144,3 +144,9 @@ export type DeletePurchaseRequestItemPayload = {
|
||||
};
|
||||
|
||||
export type UpdatePurchaseRequestPayload = CreatePurchaseRequestPayload;
|
||||
|
||||
export type PurchaseFilter = {
|
||||
poDate: string;
|
||||
category: string[];
|
||||
status: string[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user