mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-21 22:05:45 +00:00
Merge branch 'fix/purchase-filter' into 'development'
[FIX/FE] Purchase Filter See merge request mbugroup/lti-web-client!489
This commit is contained in:
@@ -29,7 +29,7 @@ import {
|
||||
FINANCE_TRANSACTION_TYPE_OPTIONS,
|
||||
} from '@/config/constant';
|
||||
import { FinanceApi } from '@/services/api/finance';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { getErrorMessage, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { BankApi, CustomerApi, SupplierApi } from '@/services/api/master-data';
|
||||
import { Bank } from '@/types/api/master-data/bank';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
@@ -39,6 +39,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import toast from 'react-hot-toast';
|
||||
import RequirePermission from '@/components/helper/RequirePermission';
|
||||
import ButtonFilter from '@/components/helper/ButtonFilter';
|
||||
import Dropdown from '@/components/dropdown/Dropdown';
|
||||
import {
|
||||
FinanceTableFilterSchema,
|
||||
FinanceTableFilterValues,
|
||||
@@ -233,6 +234,7 @@ const FinanceTable = () => {
|
||||
const [selectedSortBy, setSelectedSortBy] = useState<OptionType | null>(null);
|
||||
const [selectedFinance, setSelectedFinance] = useState<Finance | null>(null);
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
const [isExportLoading, setIsExportLoading] = useState(false);
|
||||
const [dateErrorShown, setDateErrorShown] = useState(false);
|
||||
const [hasDateError, setHasDateError] = useState(false);
|
||||
|
||||
@@ -552,6 +554,20 @@ const FinanceTable = () => {
|
||||
filterModal.openModal();
|
||||
};
|
||||
|
||||
const exportToExcel = async () => {
|
||||
setIsExportLoading(true);
|
||||
try {
|
||||
await FinanceApi.exportToExcel(getTableFilterQueryString());
|
||||
toast.success('Excel berhasil dibuat dan diunduh.');
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
await getErrorMessage(error, 'Gagal mengekspor data finance.')
|
||||
);
|
||||
} finally {
|
||||
setIsExportLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmationModalDeleteClickHandler = async () => {
|
||||
setIsDeleteLoading(true);
|
||||
|
||||
@@ -759,6 +775,51 @@ const FinanceTable = () => {
|
||||
onClick={handleFilterModalOpen}
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
align='end'
|
||||
direction='bottom'
|
||||
className={{
|
||||
content:
|
||||
'mt-1 rounded-xl border border-base-content/5 shadow-sm overflow-hidden',
|
||||
}}
|
||||
trigger={
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
||||
>
|
||||
<div className='flex flex-row items-center gap-1.5'>
|
||||
<Icon
|
||||
icon='heroicons:cloud-arrow-down'
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
|
||||
<span>Ekspor</span>
|
||||
|
||||
<div className='w-px self-stretch bg-base-content/10' />
|
||||
|
||||
<Icon
|
||||
icon='heroicons:chevron-down'
|
||||
width={14}
|
||||
height={14}
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant='ghost'
|
||||
color='none'
|
||||
onClick={exportToExcel}
|
||||
isLoading={isExportLoading}
|
||||
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap'
|
||||
>
|
||||
<Icon icon='heroicons:table-cells' width={20} height={20} />
|
||||
Ekspor ke Excel
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import Button from '@/components/Button';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||||
import SelectInput from '@/components/input/SelectInput';
|
||||
import SelectInputRadio from '@/components/input/SelectInputRadio';
|
||||
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import { PurchaseFilter } from '@/types/api/purchase/purchase';
|
||||
@@ -24,10 +25,20 @@ import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const filterByOptions: OptionType<string>[] = [
|
||||
{ value: 'po_date', label: 'Tanggal PO' },
|
||||
{ value: 'received_date', label: 'Tanggal Terima' },
|
||||
{ value: 'due_date', label: 'Tanggal Jatuh Tempo' },
|
||||
{ value: 'created_at', label: 'Tanggal Dibuat' },
|
||||
];
|
||||
|
||||
interface PurchaseFilterModalProps {
|
||||
ref: RefObject<HTMLDialogElement | null>;
|
||||
initialValues?: {
|
||||
poDate: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
filterBy: OptionType<string> | undefined;
|
||||
category: OptionType<number>[];
|
||||
status: OptionType<string>[];
|
||||
supplier: OptionType<number> | null;
|
||||
@@ -51,6 +62,7 @@ const PurchaseFilterModal = ({
|
||||
}, [ref]);
|
||||
|
||||
// ===== DATE ERROR STATE =====
|
||||
const [hasDateError, setHasDateError] = useState(false);
|
||||
const [dateErrorShown, setDateErrorShown] = useState(false);
|
||||
|
||||
// ===== CLEANUP TOAST ON UNMOUNT =====
|
||||
@@ -139,6 +151,9 @@ const PurchaseFilterModal = ({
|
||||
|
||||
const formik = useFormik<{
|
||||
poDate: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
filterBy: OptionType<string> | undefined;
|
||||
category: { label: string; value: number }[];
|
||||
status: { label: string; value: string }[];
|
||||
supplier: OptionType<number> | null;
|
||||
@@ -150,6 +165,9 @@ const PurchaseFilterModal = ({
|
||||
// enableReinitialize: true,
|
||||
initialValues: initialValues || {
|
||||
poDate: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
filterBy: undefined,
|
||||
category: [],
|
||||
status: [],
|
||||
supplier: null,
|
||||
@@ -230,9 +248,17 @@ const PurchaseFilterModal = ({
|
||||
};
|
||||
|
||||
const formikResetHandler = useCallback(() => {
|
||||
setHasDateError(false);
|
||||
if (dateErrorShown) {
|
||||
toast.dismiss();
|
||||
setDateErrorShown(false);
|
||||
}
|
||||
resetForm({
|
||||
values: {
|
||||
poDate: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
filterBy: undefined,
|
||||
category: [],
|
||||
status: [],
|
||||
supplier: null,
|
||||
@@ -246,7 +272,56 @@ const PurchaseFilterModal = ({
|
||||
setSelectedLocationId('');
|
||||
onReset?.();
|
||||
closeModalHandler();
|
||||
}, [resetForm, onReset, closeModalHandler]);
|
||||
}, [resetForm, onReset, closeModalHandler, dateErrorShown]);
|
||||
|
||||
const handleStartDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
formik.setFieldValue('start_date', value);
|
||||
|
||||
if (value && formik.values.end_date) {
|
||||
if (new Date(formik.values.end_date) < new Date(value)) {
|
||||
setHasDateError(true);
|
||||
if (!dateErrorShown) {
|
||||
toast.error('Tanggal akhir tidak boleh sebelum tanggal mulai', {
|
||||
duration: Infinity,
|
||||
});
|
||||
setDateErrorShown(true);
|
||||
}
|
||||
} else {
|
||||
setHasDateError(false);
|
||||
if (dateErrorShown) {
|
||||
toast.dismiss();
|
||||
setDateErrorShown(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setHasDateError(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEndDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
formik.setFieldValue('end_date', value);
|
||||
|
||||
if (value && formik.values.start_date) {
|
||||
if (new Date(value) < new Date(formik.values.start_date)) {
|
||||
setHasDateError(true);
|
||||
if (!dateErrorShown) {
|
||||
toast.error('Tanggal akhir tidak boleh sebelum tanggal mulai', {
|
||||
duration: Infinity,
|
||||
});
|
||||
setDateErrorShown(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setHasDateError(false);
|
||||
if (dateErrorShown) {
|
||||
toast.dismiss();
|
||||
setDateErrorShown(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formikSubmitHandler = useCallback(async () => {
|
||||
await submitForm();
|
||||
@@ -287,6 +362,44 @@ const PurchaseFilterModal = ({
|
||||
{/* Modal Body */}
|
||||
<div className='p-4 flex flex-col gap-1.5'>
|
||||
<div className='flex flex-col'>
|
||||
<div>
|
||||
<label className='block text-xs font-semibold text-base-content py-2'>
|
||||
Tanggal
|
||||
</label>
|
||||
<div className='flex flex-row gap-1.5 items-center justify-between'>
|
||||
<DateInput
|
||||
name='start_date'
|
||||
value={formik.values.start_date}
|
||||
onChange={handleStartDateChange}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
isNestedModal
|
||||
/>
|
||||
<hr className='w-full max-w-3 h-px border-base-content/10' />
|
||||
<DateInput
|
||||
name='end_date'
|
||||
value={formik.values.end_date}
|
||||
onChange={handleEndDateChange}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
isNestedModal
|
||||
isError={hasDateError}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SelectInputRadio
|
||||
label='Filter Berdasarkan'
|
||||
placeholder='Pilih Filter Berdasarkan'
|
||||
options={filterByOptions}
|
||||
value={formik.values.filterBy ?? null}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue(
|
||||
'filterBy',
|
||||
!Array.isArray(val) ? (val ?? undefined) : undefined
|
||||
)
|
||||
}
|
||||
isClearable
|
||||
/>
|
||||
|
||||
<DateInput
|
||||
label='PO Date'
|
||||
name='poDate'
|
||||
@@ -436,6 +549,7 @@ const PurchaseFilterModal = ({
|
||||
<Button
|
||||
type='button'
|
||||
onClick={formikSubmitHandler}
|
||||
disabled={hasDateError}
|
||||
className='p-3 rounded-lg w-fit sm:w-full max-w-40 text-base-100 text-sm'
|
||||
>
|
||||
Apply Filter
|
||||
|
||||
@@ -28,7 +28,7 @@ import PurchaseFilterModal from '@/components/pages/purchase/PurchaseFilterModal
|
||||
import Dropdown from '@/components/dropdown/Dropdown';
|
||||
import { OptionType } from '@/components/input/SelectInput';
|
||||
|
||||
import { cn, formatDate } from '@/lib/helper';
|
||||
import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
|
||||
import { getErrorMessage, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
@@ -41,6 +41,9 @@ type PurchaseTableFilters = {
|
||||
search: string;
|
||||
sort_by: string;
|
||||
order_by: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
filter_by: string;
|
||||
po_date: string;
|
||||
approval_status: string;
|
||||
product_category_id: string;
|
||||
@@ -177,6 +180,9 @@ const PurchaseTable = () => {
|
||||
search: '',
|
||||
sort_by: '',
|
||||
order_by: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
filter_by: '',
|
||||
po_date: '',
|
||||
approval_status: '',
|
||||
product_category_id: '',
|
||||
@@ -197,6 +203,9 @@ const PurchaseTable = () => {
|
||||
pageSize: 'limit',
|
||||
sort_by: 'sort_by',
|
||||
order_by: 'sort_order',
|
||||
start_date: 'start_date',
|
||||
end_date: 'end_date',
|
||||
filter_by: 'filter_by',
|
||||
po_date: 'po_date',
|
||||
approval_status: 'approval_status',
|
||||
product_category_id: 'product_category_id',
|
||||
@@ -297,36 +306,11 @@ const PurchaseTable = () => {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'supplier',
|
||||
header: 'Vendor',
|
||||
cell: (props) => props.row.original.supplier.name,
|
||||
},
|
||||
{
|
||||
accessorKey: 'requester_name',
|
||||
header: 'Nama Pengaju',
|
||||
cell: (props) => props.row.original.requester_name || '-',
|
||||
},
|
||||
{
|
||||
accessorKey: 'products',
|
||||
header: 'Produk',
|
||||
cell: (props) => {
|
||||
const products = props.row.original.products;
|
||||
if (!products || products.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{products.map((product, index) => (
|
||||
<li key={index}>{product.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'location',
|
||||
header: 'Lokasi',
|
||||
cell: (props) => props.row.original.location?.name || '-',
|
||||
},
|
||||
{
|
||||
accessorKey: 'po_date',
|
||||
header: 'Tgl. PO',
|
||||
@@ -364,6 +348,202 @@ const PurchaseTable = () => {
|
||||
return `${diffDays} hari`;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'supplier',
|
||||
header: 'Vendor',
|
||||
cell: (props) => props.row.original.supplier.name,
|
||||
},
|
||||
{
|
||||
accessorKey: 'location',
|
||||
header: 'Lokasi',
|
||||
cell: (props) => props.row.original.location?.name || '-',
|
||||
},
|
||||
{
|
||||
accessorKey: 'warehouse',
|
||||
header: 'Gudang',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>{item.warehouse?.name ?? '-'}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'products',
|
||||
header: 'Produk',
|
||||
cell: (props) => {
|
||||
const products = props.row.original.products;
|
||||
if (!products || products.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{products.map((product, index) => (
|
||||
<li key={index}>{product.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'total_qty',
|
||||
header: 'Kuantitas',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>{formatNumber(item.total_qty ?? 0)}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'uom',
|
||||
header: 'Satuan',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>{item.product?.uom?.name ?? '-'}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'price',
|
||||
header: 'Harga',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>{formatCurrency(item.price ?? 0)}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'total_price',
|
||||
header: 'Total Harga',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>{formatCurrency(item.total_price ?? 0)}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'products_total',
|
||||
header: 'Total Harga Produk',
|
||||
cell: (props) => formatCurrency(props.row.original.products_total ?? 0),
|
||||
},
|
||||
{
|
||||
accessorKey: 'expedition_vendor',
|
||||
header: 'Vendor Ekspedisi',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>{item.expedition_vendor?.name ?? '-'}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'expedition_qty',
|
||||
header: 'Qty Ekspedisi',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>
|
||||
{item.expedition_qty != null
|
||||
? formatNumber(item.expedition_qty)
|
||||
: '-'}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'transport_per_item',
|
||||
header: 'Harga Ekspedisi',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>
|
||||
{item.transport_per_item != null
|
||||
? formatCurrency(item.transport_per_item)
|
||||
: '-'}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'item_expedition_total',
|
||||
header: 'Total Ekspedisi',
|
||||
enableSorting: false,
|
||||
cell: (props) => {
|
||||
const items = props.row.original.items;
|
||||
if (!items || items.length === 0) return '-';
|
||||
return (
|
||||
<ul className='list-disc pl-4'>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>
|
||||
{item.expedition_total != null
|
||||
? formatCurrency(item.expedition_total)
|
||||
: '-'}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'expedition_total',
|
||||
header: 'Total Ekspedisi Semua Produk',
|
||||
cell: (props) => formatCurrency(props.row.original.expedition_total ?? 0),
|
||||
},
|
||||
{
|
||||
accessorKey: 'grand_total_all',
|
||||
header: 'Grand Total All',
|
||||
cell: (props) => formatCurrency(props.row.original.grand_total_all ?? 0),
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: 'Status Approval',
|
||||
@@ -410,6 +590,11 @@ const PurchaseTable = () => {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'notes',
|
||||
header: 'Notes',
|
||||
cell: (props) => props.row.original.notes || '-',
|
||||
},
|
||||
{
|
||||
accessorKey: 'created_at',
|
||||
header: 'Tanggal Dibuat',
|
||||
@@ -476,6 +661,9 @@ const PurchaseTable = () => {
|
||||
|
||||
const filterSubmitHandler = (values: PurchaseFilter) => {
|
||||
setFilters({
|
||||
start_date: values.start_date || '',
|
||||
end_date: values.end_date || '',
|
||||
filter_by: values.filterBy?.value || '',
|
||||
po_date: values.poDate,
|
||||
product_category_id: values.category.join(','),
|
||||
product_category_name:
|
||||
@@ -500,6 +688,9 @@ const PurchaseTable = () => {
|
||||
|
||||
const filterResetHandler = () => {
|
||||
setFilters({
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
filter_by: '',
|
||||
po_date: '',
|
||||
product_category_id: '',
|
||||
product_category_name: '',
|
||||
@@ -518,6 +709,13 @@ const PurchaseTable = () => {
|
||||
};
|
||||
|
||||
const purchaseFilterInitialValues = useMemo(() => {
|
||||
const filterByLabelMap: Record<string, string> = {
|
||||
po_date: 'Tanggal PO',
|
||||
received_date: 'Tanggal Terima',
|
||||
due_date: 'Tanggal Jatuh Tempo',
|
||||
created_at: 'Tanggal Dibuat',
|
||||
};
|
||||
|
||||
const categoryIds = tableFilterState.product_category_id
|
||||
? tableFilterState.product_category_id
|
||||
.split(',')
|
||||
@@ -539,6 +737,16 @@ const PurchaseTable = () => {
|
||||
|
||||
return {
|
||||
poDate: tableFilterState.po_date,
|
||||
start_date: tableFilterState.start_date,
|
||||
end_date: tableFilterState.end_date,
|
||||
filterBy: tableFilterState.filter_by
|
||||
? {
|
||||
value: tableFilterState.filter_by,
|
||||
label:
|
||||
filterByLabelMap[tableFilterState.filter_by] ||
|
||||
tableFilterState.filter_by,
|
||||
}
|
||||
: undefined,
|
||||
category: categoryIds.map((value, index) => ({
|
||||
value: Number(value),
|
||||
label: categoryLabels[index] || value,
|
||||
@@ -706,7 +914,7 @@ const PurchaseTable = () => {
|
||||
'project_flock_name',
|
||||
'project_flock_kandang_name',
|
||||
]}
|
||||
fieldGroups={[['startDate', 'endDate']]}
|
||||
fieldGroups={[['start_date', 'end_date']]}
|
||||
onClick={filterModal.openModal}
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
|
||||
@@ -2,6 +2,7 @@ import axios from 'axios';
|
||||
import { BaseApiService } from '@/services/api/base';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||
import { formatDate } from '@/lib/helper';
|
||||
import {
|
||||
CreateFinancePayment,
|
||||
CreateInitialBalance,
|
||||
@@ -174,6 +175,30 @@ export class FinanceApiService extends BaseApiService<
|
||||
}
|
||||
}
|
||||
|
||||
async exportToExcel(initialQueryString: string) {
|
||||
const params = new URLSearchParams(initialQueryString);
|
||||
|
||||
params.set('export', 'excel');
|
||||
params.set('page', '1');
|
||||
params.set('limit', '99999999999');
|
||||
|
||||
const res = await httpClient<Blob>(
|
||||
`${this.basePath}/transactions?${params.toString()}`,
|
||||
{ method: 'GET', responseType: 'blob' }
|
||||
);
|
||||
|
||||
const url = window.URL.createObjectURL(new Blob([res]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute(
|
||||
'download',
|
||||
`finance-${formatDate(Date.now(), 'DD-MM-YYYY')}.xlsx`
|
||||
);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
}
|
||||
|
||||
async delete(id: number) {
|
||||
try {
|
||||
const deletePath = `${this.basePath}/transactions/${id}`;
|
||||
|
||||
Vendored
+8
@@ -57,6 +57,8 @@ export type PurchaseItem = {
|
||||
alias?: string;
|
||||
category?: string;
|
||||
} | null;
|
||||
expedition_qty?: number;
|
||||
expedition_total?: number;
|
||||
};
|
||||
|
||||
export type BasePurchase = {
|
||||
@@ -81,6 +83,9 @@ export type BasePurchase = {
|
||||
po_expedition?: { id: number; refrence: string }[];
|
||||
created_user?: CreatedUser;
|
||||
products?: PurchaseItemProduct[];
|
||||
products_total?: number;
|
||||
expedition_total?: number;
|
||||
grand_total_all?: number;
|
||||
};
|
||||
|
||||
export type Purchase = BaseMetadata & BasePurchase;
|
||||
@@ -149,6 +154,9 @@ export type UpdatePurchaseRequestPayload = CreatePurchaseRequestPayload;
|
||||
|
||||
export type PurchaseFilter = {
|
||||
poDate: string;
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
filterBy?: { label: string; value: string };
|
||||
category: string[];
|
||||
category_labels?: { label: string; value: number }[];
|
||||
status: string[];
|
||||
|
||||
Reference in New Issue
Block a user