mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
Merge branch 'staging' into 'production'
Staging See merge request mbugroup/lti-web-client!261
This commit is contained in:
+5
-1
@@ -25,5 +25,9 @@ export default function Home() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>Loading...</>;
|
return (
|
||||||
|
<main className='w-full h-full min-h-screen flex flex-row justify-center items-center'>
|
||||||
|
<span className='loading loading-spinner loading-lg'></span>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-10
@@ -1,11 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ReactNode, useCallback, useEffect, useState } from 'react';
|
import { Fragment, ReactNode, useCallback, useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
flexRender,
|
flexRender,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
getFilteredRowModel,
|
getFilteredRowModel,
|
||||||
getPaginationRowModel,
|
getPaginationRowModel,
|
||||||
|
getExpandedRowModel,
|
||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
TableOptions,
|
TableOptions,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
OnChangeFn,
|
OnChangeFn,
|
||||||
Row,
|
Row,
|
||||||
HeaderContext,
|
HeaderContext,
|
||||||
|
ExpandedState,
|
||||||
} from '@tanstack/react-table';
|
} from '@tanstack/react-table';
|
||||||
import { rankItem } from '@tanstack/match-sorter-utils';
|
import { rankItem } from '@tanstack/match-sorter-utils';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
@@ -33,6 +35,9 @@ interface TableClassNames {
|
|||||||
bodyRowClassName?: string;
|
bodyRowClassName?: string;
|
||||||
selectedBodyRowClassName?: string;
|
selectedBodyRowClassName?: string;
|
||||||
bodyColumnClassName?: string;
|
bodyColumnClassName?: string;
|
||||||
|
bodySubRowClassName?: (depth: number) => string;
|
||||||
|
selectedBodySubRowClassName?: (depth: number) => string;
|
||||||
|
bodySubRowColumnClassName?: (depth: number) => string;
|
||||||
tableFooterClassName?: string;
|
tableFooterClassName?: string;
|
||||||
footerRowClassName?: string;
|
footerRowClassName?: string;
|
||||||
footerColumnClassName?: string;
|
footerColumnClassName?: string;
|
||||||
@@ -60,6 +65,7 @@ export interface TableProps<TData extends object> {
|
|||||||
enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
|
enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
|
||||||
renderFooter?: boolean;
|
renderFooter?: boolean;
|
||||||
withCheckbox?: boolean;
|
withCheckbox?: boolean;
|
||||||
|
withPagination?: boolean;
|
||||||
rowOptions?: number[];
|
rowOptions?: number[];
|
||||||
/**
|
/**
|
||||||
* Custom row renderer. Should return a complete <tr> element or null.
|
* Custom row renderer. Should return a complete <tr> element or null.
|
||||||
@@ -67,6 +73,10 @@ export interface TableProps<TData extends object> {
|
|||||||
* Return null to render the default row.
|
* Return null to render the default row.
|
||||||
*/
|
*/
|
||||||
renderCustomRow?: (row: Row<TData>) => ReactNode | null;
|
renderCustomRow?: (row: Row<TData>) => ReactNode | null;
|
||||||
|
getRowCanExpand?: (row: Row<TData>) => boolean;
|
||||||
|
renderSubComponent?: (props: { row: Row<TData> }) => React.ReactElement;
|
||||||
|
expanded?: ExpandedState;
|
||||||
|
getSubRows?: (originalRow: TData, index: number) => TData[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DUMMY_SKELETON_DATA = [{}, {}, {}, {}, {}];
|
const DUMMY_SKELETON_DATA = [{}, {}, {}, {}, {}];
|
||||||
@@ -92,7 +102,12 @@ export const TABLE_DEFAULT_STYLING = {
|
|||||||
bodyRowClassName:
|
bodyRowClassName:
|
||||||
'transition-all duration-200 border-t border-base-content/10 bg-transparent',
|
'transition-all duration-200 border-t border-base-content/10 bg-transparent',
|
||||||
selectedBodyRowClassName: 'bg-primary/5',
|
selectedBodyRowClassName: 'bg-primary/5',
|
||||||
bodyColumnClassName: 'px-4 py-3 text-base-content',
|
bodyColumnClassName: 'px-4 py-3 text-base-content font-medium',
|
||||||
|
bodySubRowClassName: (depth: number) =>
|
||||||
|
'transition-all duration-200 border-t border-base-content/10 bg-transparent',
|
||||||
|
selectedBodySubRowClassName: (depth: number) => 'bg-primary/5',
|
||||||
|
bodySubRowColumnClassName: (depth: number) =>
|
||||||
|
'px-4 py-3 text-base-content font-medium',
|
||||||
paginationClassName: 'px-3',
|
paginationClassName: 'px-3',
|
||||||
tableFooterClassName: 'font-semibold border-base-content/10',
|
tableFooterClassName: 'font-semibold border-base-content/10',
|
||||||
footerRowClassName: 'bg-base-200 border-t-2 border-base-content/10',
|
footerRowClassName: 'bg-base-200 border-t-2 border-base-content/10',
|
||||||
@@ -120,8 +135,13 @@ const Table = <TData extends object>({
|
|||||||
enableRowSelection,
|
enableRowSelection,
|
||||||
renderFooter = false,
|
renderFooter = false,
|
||||||
withCheckbox = false,
|
withCheckbox = false,
|
||||||
|
withPagination = true,
|
||||||
rowOptions = [10, 20, 50, 100],
|
rowOptions = [10, 20, 50, 100],
|
||||||
renderCustomRow,
|
renderCustomRow,
|
||||||
|
getRowCanExpand,
|
||||||
|
renderSubComponent,
|
||||||
|
expanded = {},
|
||||||
|
getSubRows,
|
||||||
}: TableProps<TData>) => {
|
}: TableProps<TData>) => {
|
||||||
const isServerSideTable =
|
const isServerSideTable =
|
||||||
totalItems !== undefined &&
|
totalItems !== undefined &&
|
||||||
@@ -154,10 +174,14 @@ const Table = <TData extends object>({
|
|||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
onPaginationChange: setPagination,
|
onPaginationChange: setPagination,
|
||||||
|
getExpandedRowModel: getExpandedRowModel(),
|
||||||
|
getRowCanExpand: getRowCanExpand ?? (getSubRows ? undefined : () => false),
|
||||||
|
getSubRows,
|
||||||
manualSorting,
|
manualSorting,
|
||||||
state: {
|
state: {
|
||||||
pagination,
|
pagination,
|
||||||
globalFilter: fuzzySearchValue,
|
globalFilter: fuzzySearchValue,
|
||||||
|
expanded,
|
||||||
},
|
},
|
||||||
filterFns: {
|
filterFns: {
|
||||||
fuzzy: fuzzyFilter,
|
fuzzy: fuzzyFilter,
|
||||||
@@ -228,7 +252,10 @@ const Table = <TData extends object>({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
TABLE_DEFAULT_STYLING.containerClassName,
|
TABLE_DEFAULT_STYLING.containerClassName,
|
||||||
tableClassNames.containerClassName
|
tableClassNames.containerClassName,
|
||||||
|
{
|
||||||
|
'mb-0': !withPagination,
|
||||||
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -352,14 +379,19 @@ const Table = <TData extends object>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Fragment key={row.id}>
|
||||||
<tr
|
<tr
|
||||||
key={row.id}
|
data-depth={row.depth}
|
||||||
className={cn(
|
className={cn(
|
||||||
TABLE_DEFAULT_STYLING.bodyRowClassName,
|
row.depth > 0
|
||||||
tableClassNames.bodyRowClassName,
|
? tableClassNames.bodySubRowClassName(row.depth)
|
||||||
|
: tableClassNames.bodyRowClassName,
|
||||||
{
|
{
|
||||||
[tableClassNames.selectedBodyRowClassName]:
|
[tableClassNames.selectedBodyRowClassName!]:
|
||||||
row.getIsSelected(),
|
row.getIsSelected() && row.depth === 0,
|
||||||
|
[tableClassNames.selectedBodySubRowClassName(
|
||||||
|
row.depth
|
||||||
|
)!]: row.getIsSelected() && row.depth > 0,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -369,7 +401,11 @@ const Table = <TData extends object>({
|
|||||||
className={cn(
|
className={cn(
|
||||||
{ 'first:w-9 first:pr-0': withCheckbox },
|
{ 'first:w-9 first:pr-0': withCheckbox },
|
||||||
TABLE_DEFAULT_STYLING.bodyColumnClassName,
|
TABLE_DEFAULT_STYLING.bodyColumnClassName,
|
||||||
tableClassNames.bodyColumnClassName
|
row.depth > 0
|
||||||
|
? tableClassNames.bodySubRowColumnClassName(
|
||||||
|
row.depth
|
||||||
|
)
|
||||||
|
: tableClassNames.bodyColumnClassName
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
@@ -382,6 +418,28 @@ const Table = <TData extends object>({
|
|||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
{row.getIsExpanded() && (
|
||||||
|
<>
|
||||||
|
{renderSubComponent && (
|
||||||
|
<tr
|
||||||
|
className={cn(
|
||||||
|
TABLE_DEFAULT_STYLING.bodySubRowClassName(1),
|
||||||
|
tableClassNames.bodySubRowClassName(1),
|
||||||
|
{
|
||||||
|
[tableClassNames.selectedBodySubRowClassName(1)]:
|
||||||
|
row.getIsSelected(),
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<td colSpan={row.getVisibleCells().length}>
|
||||||
|
{renderSubComponent({ row })}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -425,7 +483,10 @@ const Table = <TData extends object>({
|
|||||||
!isLoading &&
|
!isLoading &&
|
||||||
emptyContent}
|
emptyContent}
|
||||||
|
|
||||||
{data.length > 0 && table.getRowModel().rows.length > 0 && !isLoading && (
|
{data.length > 0 &&
|
||||||
|
table.getRowModel().rows.length > 0 &&
|
||||||
|
!isLoading &&
|
||||||
|
withPagination && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'mt-5',
|
'mt-5',
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const StatusBadge = ({
|
|||||||
xmlns='http://www.w3.org/2000/svg'
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
className={cn({
|
className={cn({
|
||||||
'text-base-content/10': color === 'neutral',
|
'text-base-content/10': color === 'neutral',
|
||||||
'text-success': color === 'success',
|
'text-[#008000]': color === 'success',
|
||||||
'text-error': color === 'error',
|
'text-error': color === 'error',
|
||||||
'text-primary': color === 'info',
|
'text-primary': color === 'info',
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -167,7 +167,15 @@ const ConfirmationModal = ({
|
|||||||
|
|
||||||
{children && <div className='w-full'>{children}</div>}
|
{children && <div className='w-full'>{children}</div>}
|
||||||
|
|
||||||
<div className='w-full grid grid-cols-2 gap-3'>
|
{(secondaryButton || primaryButton) && (
|
||||||
|
<div
|
||||||
|
className={cn('w-full grid gap-3', {
|
||||||
|
'grid-cols-2': secondaryButton && primaryButton,
|
||||||
|
'grid-cols-1':
|
||||||
|
(secondaryButton && !primaryButton) ||
|
||||||
|
(!secondaryButton && primaryButton),
|
||||||
|
})}
|
||||||
|
>
|
||||||
{secondaryButton && secondaryButton.text && (
|
{secondaryButton && secondaryButton.text && (
|
||||||
<Button
|
<Button
|
||||||
{...secondaryButton}
|
{...secondaryButton}
|
||||||
@@ -179,7 +187,13 @@ const ConfirmationModal = ({
|
|||||||
? secondaryButton?.isLoading
|
? secondaryButton?.isLoading
|
||||||
: isPrimaryButtonLoading
|
: isPrimaryButtonLoading
|
||||||
}
|
}
|
||||||
onClick={closeModalHandler}
|
onClick={(e) => {
|
||||||
|
if (secondaryButton?.onClick) {
|
||||||
|
secondaryButton.onClick(e);
|
||||||
|
} else {
|
||||||
|
closeModalHandler();
|
||||||
|
}
|
||||||
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
'p-2 rounded-xl text-sm',
|
'p-2 rounded-xl text-sm',
|
||||||
secondaryButton?.className
|
secondaryButton?.className
|
||||||
@@ -204,12 +218,16 @@ const ConfirmationModal = ({
|
|||||||
? primaryButton?.isLoading
|
? primaryButton?.isLoading
|
||||||
: isPrimaryButtonLoading
|
: isPrimaryButtonLoading
|
||||||
}
|
}
|
||||||
className={cn('p-2 rounded-xl text-sm', primaryButton?.className)}
|
className={cn(
|
||||||
|
'p-2 rounded-xl text-sm',
|
||||||
|
primaryButton?.className
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{primaryButton?.text ?? 'Ya'}
|
{primaryButton?.text ?? 'Ya'}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Jenis Transaksi',
|
label: 'Jenis Transaksi',
|
||||||
value: finance.transaction_type,
|
value: formatTitleCase(finance.transaction_type.split('_').join(' ')),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Pihak',
|
label: 'Pihak',
|
||||||
@@ -56,7 +56,9 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Nomor Rekening',
|
label: 'Nomor Rekening',
|
||||||
value: `${finance.bank?.alias} - ${finance.bank?.account_number} - ${finance.bank?.owner}`,
|
value: finance.bank?.alias
|
||||||
|
? `${finance.bank?.alias} - ${finance.bank?.account_number} - ${finance.bank?.owner}`
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: `Rekening ${formatTitleCase(finance.party?.type)}`,
|
label: `Rekening ${formatTitleCase(finance.party?.type)}`,
|
||||||
@@ -64,7 +66,11 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Nominal',
|
label: 'Nominal',
|
||||||
value: formatCurrency(Math.abs(finance.nominal)),
|
value: formatCurrency(
|
||||||
|
finance.transaction_type === 'INJECTION'
|
||||||
|
? finance.nominal
|
||||||
|
: Math.abs(finance.nominal)
|
||||||
|
),
|
||||||
},
|
},
|
||||||
].filter((item) => {
|
].filter((item) => {
|
||||||
// Hide party account number row if transaction type is INJECTION
|
// Hide party account number row if transaction type is INJECTION
|
||||||
|
|||||||
@@ -416,7 +416,8 @@ const FinanceTable = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Pemasukan (Rp)',
|
header: 'Pemasukan (Rp)',
|
||||||
accessorFn: (finance: Finance) => formatCurrency(finance.income_amount),
|
accessorFn: (finance: Finance) =>
|
||||||
|
formatCurrency(Math.abs(finance.income_amount)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Aksi',
|
header: 'Aksi',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import * as Yup from 'yup';
|
|||||||
export type InjectionFormValues = {
|
export type InjectionFormValues = {
|
||||||
bank_id_option: OptionType | null;
|
bank_id_option: OptionType | null;
|
||||||
adjustment_date: string;
|
adjustment_date: string;
|
||||||
|
injection_type?: OptionType | null | undefined;
|
||||||
nominal: string;
|
nominal: string;
|
||||||
note: string;
|
note: string;
|
||||||
};
|
};
|
||||||
@@ -18,6 +19,7 @@ export const InjectionFormSchema = Yup.object<InjectionFormValues>({
|
|||||||
(value) => value !== null && value !== undefined
|
(value) => value !== null && value !== undefined
|
||||||
),
|
),
|
||||||
adjustment_date: Yup.string().required('Tanggal penyesuaian wajib diisi'),
|
adjustment_date: Yup.string().required('Tanggal penyesuaian wajib diisi'),
|
||||||
|
injection_type: Yup.mixed().nullable().required('Tipe injeksi wajib diisi'),
|
||||||
nominal: Yup.string().required('Nominal wajib diisi'),
|
nominal: Yup.string().required('Nominal wajib diisi'),
|
||||||
note: Yup.string().required('Catatan wajib diisi'),
|
note: Yup.string().required('Catatan wajib diisi'),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ import { useCallback, useMemo, useState } from 'react';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import Alert from '@/components/Alert';
|
import Alert from '@/components/Alert';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
|
import {
|
||||||
|
FINANCE_INJECTION_STATUS,
|
||||||
|
FINANCE_INJECTION_TYPE_OPTIONS,
|
||||||
|
} from '@/config/constant';
|
||||||
|
|
||||||
interface FormFinanceInjectionProps {
|
interface FormFinanceInjectionProps {
|
||||||
type?: 'add' | 'edit';
|
type?: 'add' | 'edit';
|
||||||
@@ -51,6 +55,11 @@ const FormFinanceInjection = ({
|
|||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
adjustment_date: initialValues?.payment_date || '',
|
adjustment_date: initialValues?.payment_date || '',
|
||||||
|
injection_type: initialValues?.nominal
|
||||||
|
? initialValues.nominal < 0
|
||||||
|
? FINANCE_INJECTION_TYPE_OPTIONS[1]
|
||||||
|
: FINANCE_INJECTION_TYPE_OPTIONS[0]
|
||||||
|
: FINANCE_INJECTION_TYPE_OPTIONS[0],
|
||||||
nominal: initialValues?.nominal?.toString() || '',
|
nominal: initialValues?.nominal?.toString() || '',
|
||||||
note: initialValues?.notes || '',
|
note: initialValues?.notes || '',
|
||||||
};
|
};
|
||||||
@@ -94,7 +103,10 @@ const FormFinanceInjection = ({
|
|||||||
return {
|
return {
|
||||||
bank_id: Number(values.bank_id_option?.value) || 0,
|
bank_id: Number(values.bank_id_option?.value) || 0,
|
||||||
adjustment_date: formatDate(values.adjustment_date, 'YYYY-MM-DD'),
|
adjustment_date: formatDate(values.adjustment_date, 'YYYY-MM-DD'),
|
||||||
nominal: Number(values.nominal.replace(/\D/g, '')) || 0,
|
nominal:
|
||||||
|
values.injection_type?.value == 'POSITIVE'
|
||||||
|
? Math.abs(Number(values.nominal))
|
||||||
|
: -Math.abs(Number(values.nominal)),
|
||||||
notes: values.note,
|
notes: values.note,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -203,6 +215,24 @@ const FormFinanceInjection = ({
|
|||||||
}
|
}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<SelectInput
|
||||||
|
label='Tipe Injeksi'
|
||||||
|
placeholder='Pilih tipe injeksi'
|
||||||
|
options={FINANCE_INJECTION_TYPE_OPTIONS}
|
||||||
|
value={formik.values.injection_type}
|
||||||
|
onChange={(value) => {
|
||||||
|
formik.setFieldValue('injection_type', value);
|
||||||
|
}}
|
||||||
|
isError={Boolean(
|
||||||
|
formik.touched.injection_type && formik.errors.injection_type
|
||||||
|
)}
|
||||||
|
errorMessage={
|
||||||
|
formik.touched.injection_type && formik.errors.injection_type
|
||||||
|
? formik.errors.injection_type
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label='Nominal'
|
label='Nominal'
|
||||||
placeholder='Masukkan nominal'
|
placeholder='Masukkan nominal'
|
||||||
@@ -216,8 +246,17 @@ const FormFinanceInjection = ({
|
|||||||
? formik.errors.nominal
|
? formik.errors.nominal
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
allowNegative={true}
|
allowNegative={false}
|
||||||
required
|
required
|
||||||
|
startAdornment={
|
||||||
|
formik.values.injection_type?.value === 'POSITIVE' ? (
|
||||||
|
<Icon icon='mdi:plus' />
|
||||||
|
) : formik.values.injection_type?.value === 'NEGATIVE' ? (
|
||||||
|
<Icon icon='mdi:minus' />
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<TextArea
|
<TextArea
|
||||||
label='Catatan'
|
label='Catatan'
|
||||||
|
|||||||
@@ -1369,9 +1369,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
}
|
}
|
||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
className={{
|
|
||||||
input: 'bg-base-200',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label='Lokasi'
|
label='Lokasi'
|
||||||
@@ -1382,9 +1379,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
}
|
}
|
||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
className={{
|
|
||||||
input: 'bg-base-200',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1433,9 +1427,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
}
|
}
|
||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
className={{
|
|
||||||
input: 'bg-base-200',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label='Lokasi'
|
label='Lokasi'
|
||||||
@@ -1446,9 +1437,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
}
|
}
|
||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
className={{
|
|
||||||
input: 'bg-base-200',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -164,6 +164,9 @@ export const RecordingLayingFormSchema: Yup.ObjectSchema<RecordingLayingFormSche
|
|||||||
|
|
||||||
export const UpdateRecordingGrowingFormSchema =
|
export const UpdateRecordingGrowingFormSchema =
|
||||||
RecordingGrowingFormSchema.shape({
|
RecordingGrowingFormSchema.shape({
|
||||||
|
location_id: Yup.number().nullable().optional(),
|
||||||
|
project_flock_id: Yup.number().nullable().optional(),
|
||||||
|
kandang_id: Yup.number().nullable().optional(),
|
||||||
project_flock_kandang_id: Yup.number()
|
project_flock_kandang_id: Yup.number()
|
||||||
.default(0)
|
.default(0)
|
||||||
.typeError('Project Flock Kandang wajib diisi!')
|
.typeError('Project Flock Kandang wajib diisi!')
|
||||||
@@ -176,6 +179,9 @@ export const UpdateRecordingGrowingFormSchema =
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const UpdateRecordingLayingFormSchema = RecordingLayingFormSchema.shape({
|
export const UpdateRecordingLayingFormSchema = RecordingLayingFormSchema.shape({
|
||||||
|
location_id: Yup.number().nullable().optional(),
|
||||||
|
project_flock_id: Yup.number().nullable().optional(),
|
||||||
|
kandang_id: Yup.number().nullable().optional(),
|
||||||
project_flock_kandang_id: Yup.number()
|
project_flock_kandang_id: Yup.number()
|
||||||
.default(0)
|
.default(0)
|
||||||
.typeError('Project Flock Kandang wajib diisi!')
|
.typeError('Project Flock Kandang wajib diisi!')
|
||||||
@@ -217,13 +223,25 @@ export const getRecordingGrowingFormInitialValues = (
|
|||||||
project_flock_id: 0,
|
project_flock_id: 0,
|
||||||
kandang: null,
|
kandang: null,
|
||||||
kandang_id: 0,
|
kandang_id: 0,
|
||||||
project_flock_kandang: initialValues?.project_flock_kandang_id
|
project_flock_kandang:
|
||||||
|
(initialValues?.project_flock_kandang_id ??
|
||||||
|
initialValues?.project_flock?.project_flock_kandang_id)
|
||||||
? {
|
? {
|
||||||
value: initialValues.project_flock_kandang_id,
|
value:
|
||||||
label: `Project Flock #${initialValues.project_flock_kandang_id}`,
|
initialValues?.project_flock_kandang_id ??
|
||||||
|
initialValues?.project_flock?.project_flock_kandang_id ??
|
||||||
|
0,
|
||||||
|
label: `Project Flock #${
|
||||||
|
initialValues?.project_flock_kandang_id ??
|
||||||
|
initialValues?.project_flock?.project_flock_kandang_id ??
|
||||||
|
0
|
||||||
|
}`,
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
project_flock_kandang_id: initialValues?.project_flock_kandang_id ?? 0,
|
project_flock_kandang_id:
|
||||||
|
initialValues?.project_flock_kandang_id ??
|
||||||
|
initialValues?.project_flock?.project_flock_kandang_id ??
|
||||||
|
0,
|
||||||
stocks: initialValues?.stocks?.map((stock) => ({
|
stocks: initialValues?.stocks?.map((stock) => ({
|
||||||
product_warehouse_id: stock.product_warehouse_id,
|
product_warehouse_id: stock.product_warehouse_id,
|
||||||
qty:
|
qty:
|
||||||
|
|||||||
@@ -554,15 +554,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
const nextDayRecordingUrl = useMemo(() => {
|
const nextDayRecordingUrl = useMemo(() => {
|
||||||
if (!projectFlockKandangLookup) return null;
|
if (!projectFlockKandangLookup) return null;
|
||||||
const projectFlockKandangId = projectFlockKandangLookup.id;
|
const projectFlockKandangId = projectFlockKandangLookup.id;
|
||||||
return `${RecordingApi.basePath}/next-day?project_flock_kandang_id=${projectFlockKandangId}`;
|
return `${RecordingApi.basePath}/next-day?project_flock_kandang_id=${projectFlockKandangId}&record_date=${selectedRecordDate}`;
|
||||||
}, [projectFlockKandangLookup]);
|
}, [projectFlockKandangLookup, selectedRecordDate]);
|
||||||
|
|
||||||
const { data: nextDayRecordingData } = useSWR(
|
const { data: nextDayRecordingData } = useSWR(
|
||||||
nextDayRecordingUrl,
|
nextDayRecordingUrl,
|
||||||
nextDayRecordingUrl
|
nextDayRecordingUrl
|
||||||
? () => {
|
? () => {
|
||||||
const projectFlockKandangId = projectFlockKandangLookup!.id;
|
const projectFlockKandangId = projectFlockKandangLookup!.id;
|
||||||
return RecordingApi.nextDayRecording(projectFlockKandangId);
|
return RecordingApi.nextDayRecording(
|
||||||
|
projectFlockKandangId,
|
||||||
|
selectedRecordDate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
@@ -1075,14 +1078,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
if ((type as 'add' | 'edit' | 'detail') === 'detail') return null;
|
if ((type as 'add' | 'edit' | 'detail') === 'detail') return null;
|
||||||
const stock = formik.values.stocks?.[stockIdx];
|
const stock = formik.values.stocks?.[stockIdx];
|
||||||
if (!stock || !stock.product_warehouse_id) return null;
|
if (!stock || !stock.product_warehouse_id) return null;
|
||||||
const availableStock = getAvailableStock(stock.product_warehouse_id);
|
|
||||||
const requestedUsage = Number(stock.qty) || 0;
|
|
||||||
if (requestedUsage > availableStock) {
|
|
||||||
return `Jumlah pakai melebihi stok tersedia! Maksimal: ${formatNumber(availableStock)}`;
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
[formik.values.stocks, getAvailableStock, type]
|
[formik.values.stocks, type]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getStockUsageAdornment = useCallback(
|
const getStockUsageAdornment = useCallback(
|
||||||
@@ -1096,7 +1094,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
if (requestedUsage > 0) {
|
if (requestedUsage > 0) {
|
||||||
return (
|
return (
|
||||||
<span className='text-sm text-gray-600 whitespace-nowrap'>
|
<span className='text-sm text-gray-600 whitespace-nowrap'>
|
||||||
(sisa: {formatNumber(remainingStock)})
|
(tersedia: {formatNumber(availableStock)} | pakai:{' '}
|
||||||
|
{formatNumber(requestedUsage)} | sisa:{' '}
|
||||||
|
{formatNumber(Math.max(remainingStock, 0))} | dipinjam:{' '}
|
||||||
|
{formatNumber(Math.max(-remainingStock, 0))})
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2443,7 +2444,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
options={unifiedStockProducts}
|
options={unifiedStockProducts}
|
||||||
placeholder='Pilih Produk'
|
placeholder={
|
||||||
|
!formik.values.project_flock_kandang_id
|
||||||
|
? 'Pilih kandang terlebih dahulu'
|
||||||
|
: 'Pilih Produk'
|
||||||
|
}
|
||||||
isLoading={isLoadingStockProducts}
|
isLoading={isLoadingStockProducts}
|
||||||
onMenuScrollToBottom={loadMoreStockProducts}
|
onMenuScrollToBottom={loadMoreStockProducts}
|
||||||
isError={
|
isError={
|
||||||
@@ -2464,7 +2469,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
wrapper: 'w-full min-w-48',
|
wrapper: 'w-full min-w-48',
|
||||||
}}
|
}}
|
||||||
isSearchable
|
isSearchable
|
||||||
isDisabled={type === 'detail'}
|
isDisabled={
|
||||||
|
type === 'detail' ||
|
||||||
|
!formik.values.project_flock_kandang_id
|
||||||
|
}
|
||||||
isClearable={type !== 'detail'}
|
isClearable={type !== 'detail'}
|
||||||
startAdornment={
|
startAdornment={
|
||||||
stock.product_warehouse_id
|
stock.product_warehouse_id
|
||||||
|
|||||||
+294
@@ -0,0 +1,294 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ChangeEventHandler, RefObject, useId, useState } from 'react';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
import ConfirmationModal, {
|
||||||
|
ConfirmationModalProps,
|
||||||
|
} from '@/components/modal/ConfirmationModal';
|
||||||
|
import Table from '@/components/Table';
|
||||||
|
import TextArea from '@/components/input/TextArea';
|
||||||
|
|
||||||
|
import { ColumnDef } from '@tanstack/react-table';
|
||||||
|
import { cn, formatDate, formatNumber } from '@/lib/helper';
|
||||||
|
import { TransferToLayingFormValues } from '@/components/pages/production/transfer-to-laying/form/TransferToLayingForm.schema';
|
||||||
|
import { Color } from '@/types/theme';
|
||||||
|
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
|
||||||
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
|
|
||||||
|
interface TransferToLayingConfirmationModalProps
|
||||||
|
extends Omit<ConfirmationModalProps, 'children' | 'primaryButton'> {
|
||||||
|
ref: RefObject<HTMLDialogElement | null>;
|
||||||
|
type?: 'info' | 'success' | 'error';
|
||||||
|
transferToLayingIds?: number[];
|
||||||
|
transferToLayingForm?: TransferToLayingFormValues;
|
||||||
|
onClose?: () => void;
|
||||||
|
|
||||||
|
withNote?: boolean;
|
||||||
|
noteLabel?: string;
|
||||||
|
rows?: number;
|
||||||
|
placeholder?: string;
|
||||||
|
|
||||||
|
primaryButton?: {
|
||||||
|
text?: string;
|
||||||
|
color?: Color;
|
||||||
|
isLoading?: boolean;
|
||||||
|
onClick?: (notes: string) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransferToLayingConfirmationTableDataType {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
subRows?: TransferToLayingConfirmationTableDataType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransferToLayingConfirmationModalTable = ({
|
||||||
|
transferToLayingForm,
|
||||||
|
transferToLayingId,
|
||||||
|
}: {
|
||||||
|
transferToLayingForm?: TransferToLayingFormValues;
|
||||||
|
transferToLayingId?: number;
|
||||||
|
}) => {
|
||||||
|
const { data: transferToLaying, isLoading: isLoadingTransferToLaying } =
|
||||||
|
useSWR(
|
||||||
|
transferToLayingId
|
||||||
|
? ['detail-transfer-to-laying', String(transferToLayingId)]
|
||||||
|
: undefined,
|
||||||
|
([_, id]) => TransferToLayingApi.getSingle(Number(id))
|
||||||
|
);
|
||||||
|
|
||||||
|
const confirmationTableColumns: ColumnDef<TransferToLayingConfirmationTableDataType>[] =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
header: 'Label',
|
||||||
|
accessorKey: 'label',
|
||||||
|
enableSorting: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const isSubRow = row.depth > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!isSubRow && row.original.label}
|
||||||
|
|
||||||
|
{isSubRow && (
|
||||||
|
<div
|
||||||
|
className={cn('w-full min-h-full flex items-stretch gap-0')}
|
||||||
|
>
|
||||||
|
<div className='w-px mx-4 bg-base-content/10' />
|
||||||
|
<span className='p-3'>{row.original.label}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Value',
|
||||||
|
accessorKey: 'value',
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const confirmationTableData: TransferToLayingConfirmationTableDataType[] = [
|
||||||
|
{
|
||||||
|
label: 'Tanggal',
|
||||||
|
value: formatDate(
|
||||||
|
transferToLayingId && isResponseSuccess(transferToLaying)
|
||||||
|
? transferToLaying.data.transfer_date
|
||||||
|
: transferToLayingForm?.transfer_date,
|
||||||
|
'DD MMMM YYYY'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Flock Asal',
|
||||||
|
value:
|
||||||
|
transferToLayingId && isResponseSuccess(transferToLaying)
|
||||||
|
? transferToLaying.data.from_project_flock.flock_name
|
||||||
|
: (transferToLayingForm?.flockSource?.label ?? '-'),
|
||||||
|
subRows:
|
||||||
|
transferToLayingId && isResponseSuccess(transferToLaying)
|
||||||
|
? (transferToLaying.data.sources?.map(
|
||||||
|
(sourceProjectFlockKandang) => ({
|
||||||
|
label:
|
||||||
|
sourceProjectFlockKandang.source_project_flock_kandang.kandang
|
||||||
|
.name,
|
||||||
|
value: formatNumber(
|
||||||
|
Number(sourceProjectFlockKandang.qty),
|
||||||
|
'en-US'
|
||||||
|
),
|
||||||
|
})
|
||||||
|
) ?? [])
|
||||||
|
: (transferToLayingForm?.flockSourceKandangs?.map((kandang) => ({
|
||||||
|
label: kandang.kandang.label,
|
||||||
|
value: formatNumber(Number(kandang.quantity), 'en-US'),
|
||||||
|
})) ?? []),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Flock Tujuan',
|
||||||
|
value:
|
||||||
|
transferToLayingId && isResponseSuccess(transferToLaying)
|
||||||
|
? transferToLaying.data.to_project_flock.flock_name
|
||||||
|
: (transferToLayingForm?.flockDestination?.label ?? '-'),
|
||||||
|
subRows:
|
||||||
|
transferToLayingId && isResponseSuccess(transferToLaying)
|
||||||
|
? (transferToLaying.data.targets?.map(
|
||||||
|
(targetProjectFlockKandang) => ({
|
||||||
|
label:
|
||||||
|
targetProjectFlockKandang.target_project_flock_kandang.kandang
|
||||||
|
.name,
|
||||||
|
value: formatNumber(
|
||||||
|
Number(targetProjectFlockKandang.qty),
|
||||||
|
'en-US'
|
||||||
|
),
|
||||||
|
})
|
||||||
|
) ?? [])
|
||||||
|
: (transferToLayingForm?.flockDestinationKandangs?.map((kandang) => ({
|
||||||
|
label: kandang.kandang.label,
|
||||||
|
value: formatNumber(Number(kandang.quantity), 'en-US'),
|
||||||
|
})) ?? []),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Jumlah Transfer',
|
||||||
|
value: formatNumber(
|
||||||
|
transferToLayingId && isResponseSuccess(transferToLaying)
|
||||||
|
? Number(
|
||||||
|
transferToLaying.data.sources.reduce(
|
||||||
|
(total, source) => total + Number(source.qty),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: Number(transferToLayingForm?.totalQuantity),
|
||||||
|
'en-US'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Notes',
|
||||||
|
value:
|
||||||
|
transferToLayingId && isResponseSuccess(transferToLaying)
|
||||||
|
? (transferToLaying.data.notes ?? '-')
|
||||||
|
: (transferToLayingForm?.reason ?? '-'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table<TransferToLayingConfirmationTableDataType>
|
||||||
|
columns={confirmationTableColumns}
|
||||||
|
data={confirmationTableData}
|
||||||
|
withPagination={false}
|
||||||
|
pageSize={10000}
|
||||||
|
expanded={true}
|
||||||
|
getSubRows={(row) => row.subRows}
|
||||||
|
className={{
|
||||||
|
headerRowClassName: 'border-b border-base-content/10',
|
||||||
|
bodyRowClassName: 'border-none',
|
||||||
|
bodySubRowClassName: () => 'border-none',
|
||||||
|
bodySubRowColumnClassName: () => 'first:p-0',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TransferToLayingConfirmationModal = ({
|
||||||
|
ref,
|
||||||
|
type = 'success',
|
||||||
|
transferToLayingForm,
|
||||||
|
transferToLayingIds,
|
||||||
|
onClose,
|
||||||
|
withNote,
|
||||||
|
rows = 4,
|
||||||
|
noteLabel,
|
||||||
|
placeholder = 'Alasan Transfer',
|
||||||
|
primaryButton,
|
||||||
|
secondaryButton,
|
||||||
|
...props
|
||||||
|
}: TransferToLayingConfirmationModalProps) => {
|
||||||
|
const randomId = useId();
|
||||||
|
|
||||||
|
const [notes, setNotes] = useState('');
|
||||||
|
|
||||||
|
const notesChangeHandler: ChangeEventHandler<HTMLTextAreaElement> = (e) => {
|
||||||
|
setNotes(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModalHandler = () => {
|
||||||
|
onClose?.();
|
||||||
|
ref.current?.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationModal
|
||||||
|
ref={ref}
|
||||||
|
iconPosition='left'
|
||||||
|
type={type}
|
||||||
|
primaryButton={{
|
||||||
|
...primaryButton,
|
||||||
|
text: primaryButton?.text ?? 'Oke',
|
||||||
|
color: primaryButton?.color ?? 'primary',
|
||||||
|
className: 'rounded-lg',
|
||||||
|
onClick: (e) => {
|
||||||
|
if (withNote) {
|
||||||
|
primaryButton?.onClick?.(notes);
|
||||||
|
} else if (primaryButton && primaryButton?.onClick) {
|
||||||
|
primaryButton?.onClick?.('');
|
||||||
|
} else {
|
||||||
|
closeModalHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
setNotes('');
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
secondaryButton={
|
||||||
|
secondaryButton
|
||||||
|
? {
|
||||||
|
text: secondaryButton?.text ?? 'Cancel',
|
||||||
|
color: secondaryButton?.color ?? 'none',
|
||||||
|
onClick: (e) => {
|
||||||
|
if (secondaryButton && secondaryButton?.onClick) {
|
||||||
|
secondaryButton.onClick?.(e);
|
||||||
|
} else {
|
||||||
|
closeModalHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
setNotes('');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
className={{
|
||||||
|
modalBox: 'max-h-full',
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col gap-4'>
|
||||||
|
{!transferToLayingIds && transferToLayingForm && (
|
||||||
|
<TransferToLayingConfirmationModalTable
|
||||||
|
transferToLayingForm={transferToLayingForm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{transferToLayingIds &&
|
||||||
|
!transferToLayingForm &&
|
||||||
|
transferToLayingIds.map((transferToLayingId, idx) => (
|
||||||
|
<TransferToLayingConfirmationModalTable
|
||||||
|
key={idx}
|
||||||
|
transferToLayingId={transferToLayingId}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{withNote && (
|
||||||
|
<TextArea
|
||||||
|
name={randomId}
|
||||||
|
label={noteLabel}
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={notes}
|
||||||
|
onChange={notesChangeHandler}
|
||||||
|
rows={rows}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ConfirmationModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransferToLayingConfirmationModal;
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
import useSWR, { useSWRConfig } from 'swr';
|
import useSWR, { useSWRConfig } from 'swr';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
@@ -19,8 +20,8 @@ import { OptionType, useSelect } from '@/components/input/SelectInput';
|
|||||||
import NumberInput from '@/components/input/NumberInput';
|
import NumberInput from '@/components/input/NumberInput';
|
||||||
import TextArea from '@/components/input/TextArea';
|
import TextArea from '@/components/input/TextArea';
|
||||||
import AlertErrorList from '@/components/helper/form/FormErrors';
|
import AlertErrorList from '@/components/helper/form/FormErrors';
|
||||||
|
import TransferToLayingConfirmationModal from '@/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal';
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
|
||||||
import { ProjectFlockApi } from '@/services/api/production';
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
import { getIn, useFormik } from 'formik';
|
import { getIn, useFormik } from 'formik';
|
||||||
import {
|
import {
|
||||||
@@ -58,8 +59,11 @@ const TransferToLayingFormModal = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { data: transferToLaying, isLoading: isLoadingTransferToLaying } =
|
const { data: transferToLaying, isLoading: isLoadingTransferToLaying } =
|
||||||
useSWR(transferToLayingId ? transferToLayingId : undefined, (id: number) =>
|
useSWR(
|
||||||
TransferToLayingApi.getSingle(id)
|
transferToLayingId
|
||||||
|
? ['detail-transfer-to-laying', transferToLayingId]
|
||||||
|
: undefined,
|
||||||
|
([, id]) => TransferToLayingApi.getSingle(Number(id))
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +75,11 @@ const TransferToLayingFormModal = () => {
|
|||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
|
|
||||||
const formModal = useModal();
|
const formModal = useModal();
|
||||||
|
const successModal = useModal();
|
||||||
|
|
||||||
|
const [formikLastValues, setFormikLastValues] = useState<
|
||||||
|
TransferToLayingFormValues | undefined
|
||||||
|
>(undefined);
|
||||||
const [formErrorMessage, setFormErrorMessage] = useState<string | null>(null);
|
const [formErrorMessage, setFormErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
// Flock Source
|
// Flock Source
|
||||||
@@ -133,6 +141,7 @@ const TransferToLayingFormModal = () => {
|
|||||||
toast.success(createTransferToLayingRes?.message as string);
|
toast.success(createTransferToLayingRes?.message as string);
|
||||||
router.push('/production/transfer-to-laying');
|
router.push('/production/transfer-to-laying');
|
||||||
closeModalHandler(false);
|
closeModalHandler(false);
|
||||||
|
successModal.openModal();
|
||||||
},
|
},
|
||||||
[router]
|
[router]
|
||||||
);
|
);
|
||||||
@@ -156,6 +165,7 @@ const TransferToLayingFormModal = () => {
|
|||||||
toast.success(updateKandangRes?.message as string);
|
toast.success(updateKandangRes?.message as string);
|
||||||
router.push('/production/transfer-to-laying');
|
router.push('/production/transfer-to-laying');
|
||||||
closeModalHandler(false);
|
closeModalHandler(false);
|
||||||
|
successModal.openModal();
|
||||||
},
|
},
|
||||||
[router]
|
[router]
|
||||||
);
|
);
|
||||||
@@ -187,6 +197,8 @@ const TransferToLayingFormModal = () => {
|
|||||||
reason: values.reason as string,
|
reason: values.reason as string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setFormikLastValues(values);
|
||||||
|
|
||||||
switch (modalAction) {
|
switch (modalAction) {
|
||||||
case 'add':
|
case 'add':
|
||||||
await createTransferToLayingHandler(transferToLayingPayload);
|
await createTransferToLayingHandler(transferToLayingPayload);
|
||||||
@@ -244,7 +256,7 @@ const TransferToLayingFormModal = () => {
|
|||||||
? selectedFlockSourceRawData.kandangs.map((kandang) => {
|
? selectedFlockSourceRawData.kandangs.map((kandang) => {
|
||||||
const availability =
|
const availability =
|
||||||
flockSourceKandangsAvailability[kandang.project_flock_kandang_id]
|
flockSourceKandangsAvailability[kandang.project_flock_kandang_id]
|
||||||
.available_qty;
|
?.available_qty;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kandang_name: kandang.name,
|
kandang_name: kandang.name,
|
||||||
@@ -375,6 +387,12 @@ const TransferToLayingFormModal = () => {
|
|||||||
formik.setValues(filledInitialValues);
|
formik.setValues(filledInitialValues);
|
||||||
setStep(3);
|
setStep(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isResponseError(transferToLaying)) {
|
||||||
|
router.push('/production/transfer-to-laying');
|
||||||
|
closeModalHandler();
|
||||||
|
toast.error(transferToLaying.message);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFlockSourceData = async () => {
|
const getFlockSourceData = async () => {
|
||||||
@@ -437,7 +455,7 @@ const TransferToLayingFormModal = () => {
|
|||||||
<div className='w-px border-none bg-base-content/10' />
|
<div className='w-px border-none bg-base-content/10' />
|
||||||
|
|
||||||
<h4 className='text-sm font-medium text-base-content/50'>
|
<h4 className='text-sm font-medium text-base-content/50'>
|
||||||
Add Transfer to Laying
|
{modalAction === 'add' ? 'Add' : 'Edit'} Transfer to Laying
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -771,7 +789,12 @@ const TransferToLayingFormModal = () => {
|
|||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
{formik.values.flockSourceKandangs.map((item, index) => {
|
{formik.values.flockSourceKandangs.map((item, index) => {
|
||||||
const isInvalid =
|
const isInvalid =
|
||||||
item.quantity === ''
|
!Boolean(
|
||||||
|
getIn(
|
||||||
|
formik.touched,
|
||||||
|
`flockSourceKandangs[${index}].quantity`
|
||||||
|
)
|
||||||
|
) && item.quantity === ''
|
||||||
? false
|
? false
|
||||||
: Boolean(
|
: Boolean(
|
||||||
getIn(
|
getIn(
|
||||||
@@ -833,7 +856,8 @@ const TransferToLayingFormModal = () => {
|
|||||||
: 'neutral'
|
: 'neutral'
|
||||||
}
|
}
|
||||||
text={`Sisa transfer: ${formatNumber(
|
text={`Sisa transfer: ${formatNumber(
|
||||||
totalAvailableChickenForTransfer
|
totalAvailableChickenForTransfer,
|
||||||
|
'en-US'
|
||||||
)} ekor`}
|
)} ekor`}
|
||||||
className={{
|
className={{
|
||||||
badge: 'text-nowrap',
|
badge: 'text-nowrap',
|
||||||
@@ -852,7 +876,12 @@ const TransferToLayingFormModal = () => {
|
|||||||
{formik.values.flockDestinationKandangs.map(
|
{formik.values.flockDestinationKandangs.map(
|
||||||
(item, index) => {
|
(item, index) => {
|
||||||
const isInvalid =
|
const isInvalid =
|
||||||
item.quantity === ''
|
!Boolean(
|
||||||
|
getIn(
|
||||||
|
formik.touched,
|
||||||
|
`flockDestinationKandangs.${index}.quantity`
|
||||||
|
)
|
||||||
|
) && item.quantity === ''
|
||||||
? false
|
? false
|
||||||
: Boolean(
|
: Boolean(
|
||||||
getIn(
|
getIn(
|
||||||
@@ -968,6 +997,16 @@ const TransferToLayingFormModal = () => {
|
|||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<TransferToLayingConfirmationModal
|
||||||
|
ref={successModal.ref}
|
||||||
|
type='success'
|
||||||
|
text='Data Berhasil Ditambahkan'
|
||||||
|
subtitleText='Data transfer to laying telah berhasil disimpan.'
|
||||||
|
transferToLayingForm={formikLastValues}
|
||||||
|
onClose={() => setFormikLastValues(undefined)}
|
||||||
|
secondaryButton={undefined}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ import { Icon } from '@iconify/react';
|
|||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||||
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
import PopoverButton from '@/components/popover/PopoverButton';
|
import PopoverButton from '@/components/popover/PopoverButton';
|
||||||
import Badge from '@/components/Badge';
|
import Badge from '@/components/Badge';
|
||||||
@@ -24,13 +22,14 @@ import PopoverContent from '@/components/popover/PopoverContent';
|
|||||||
import Dropdown from '@/components/Dropdown';
|
import Dropdown from '@/components/Dropdown';
|
||||||
import StatusBadge from '@/components/helper/StatusBadge';
|
import StatusBadge from '@/components/helper/StatusBadge';
|
||||||
import TransferToLayingFilterModal from '@/components/pages/production/transfer-to-laying/TransferToLayingFilterModal';
|
import TransferToLayingFilterModal from '@/components/pages/production/transfer-to-laying/TransferToLayingFilterModal';
|
||||||
|
import TransferToLayingConfirmationModal from '@/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TransferToLaying,
|
TransferToLaying,
|
||||||
TransferToLayingFilter,
|
TransferToLayingFilter,
|
||||||
} from '@/types/api/production/transfer-to-laying';
|
} from '@/types/api/production/transfer-to-laying';
|
||||||
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
|
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
|
||||||
import { cn, formatDate } from '@/lib/helper';
|
import { cn, formatDate, formatNumber } from '@/lib/helper';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { Color } from '@/types/theme';
|
import { Color } from '@/types/theme';
|
||||||
@@ -38,14 +37,11 @@ import { Color } from '@/types/theme';
|
|||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
props,
|
props,
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
approveClickHandler,
|
|
||||||
rejectClickHandler,
|
|
||||||
deleteClickHandler,
|
deleteClickHandler,
|
||||||
}: {
|
}: {
|
||||||
props: CellContext<TransferToLaying, unknown>;
|
props: CellContext<TransferToLaying, unknown>;
|
||||||
popoverPosition: 'bottom' | 'top';
|
popoverPosition: 'bottom' | 'top';
|
||||||
approveClickHandler: () => void;
|
|
||||||
rejectClickHandler: () => void;
|
|
||||||
deleteClickHandler: () => void;
|
deleteClickHandler: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const showEditButton =
|
const showEditButton =
|
||||||
@@ -54,9 +50,6 @@ const RowOptionsMenu = ({
|
|||||||
|
|
||||||
const showDeleteButton = showEditButton;
|
const showDeleteButton = showEditButton;
|
||||||
|
|
||||||
// const showApproveButton = showEditButton;
|
|
||||||
// const showRejectButton = showEditButton;
|
|
||||||
|
|
||||||
const popoverId = `transferToLaying#${props.row.original.id}`;
|
const popoverId = `transferToLaying#${props.row.original.id}`;
|
||||||
const popoverAnchorName = `--anchor-transferToLaying#${props.row.original.id}`;
|
const popoverAnchorName = `--anchor-transferToLaying#${props.row.original.id}`;
|
||||||
|
|
||||||
@@ -260,7 +253,14 @@ const TransferToLayingsTable = () => {
|
|||||||
{
|
{
|
||||||
accessorKey: 'usage_qty',
|
accessorKey: 'usage_qty',
|
||||||
header: 'Kuantitas',
|
header: 'Kuantitas',
|
||||||
cell: (props) => props.getValue() ?? props.row.original.pending_usage_qty,
|
cell: (props) => {
|
||||||
|
const totalQuantity = props.row.original.targets.reduce(
|
||||||
|
(total, target) => total + target.qty,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return formatNumber(totalQuantity, 'en-US');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'notes',
|
accessorKey: 'notes',
|
||||||
@@ -304,38 +304,20 @@ const TransferToLayingsTable = () => {
|
|||||||
|
|
||||||
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2;
|
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2;
|
||||||
|
|
||||||
const approveClickHandler = () => {
|
|
||||||
setSelectedTransferToLaying(props.row.original);
|
|
||||||
|
|
||||||
// Set row selection
|
|
||||||
setRowSelection({
|
|
||||||
[String(props.row.original.id)]: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
approveModal.openModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const rejectClickHandler = () => {
|
|
||||||
setSelectedTransferToLaying(props.row.original);
|
|
||||||
|
|
||||||
// Set row selection
|
|
||||||
setRowSelection({
|
|
||||||
[String(props.row.original.id)]: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
rejectModal.openModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteClickHandler = () => {
|
const deleteClickHandler = () => {
|
||||||
setSelectedTransferToLaying(props.row.original);
|
setSelectedTransferToLaying(props.row.original);
|
||||||
|
|
||||||
|
// Set row selection
|
||||||
|
setRowSelection({
|
||||||
|
[String(props.row.original.id)]: true,
|
||||||
|
});
|
||||||
|
|
||||||
deleteModal.openModal();
|
deleteModal.openModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RowOptionsMenu
|
<RowOptionsMenu
|
||||||
props={props}
|
props={props}
|
||||||
approveClickHandler={approveClickHandler}
|
|
||||||
rejectClickHandler={rejectClickHandler}
|
|
||||||
deleteClickHandler={deleteClickHandler}
|
deleteClickHandler={deleteClickHandler}
|
||||||
popoverPosition={isLast2Rows ? 'top' : 'bottom'}
|
popoverPosition={isLast2Rows ? 'top' : 'bottom'}
|
||||||
/>
|
/>
|
||||||
@@ -377,6 +359,8 @@ const TransferToLayingsTable = () => {
|
|||||||
|
|
||||||
refreshTransferToLayings();
|
refreshTransferToLayings();
|
||||||
|
|
||||||
|
setRowSelection({});
|
||||||
|
setSelectedTransferToLaying(undefined);
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
toast.success('Berhasil menghapus data transfer ke laying!');
|
toast.success('Berhasil menghapus data transfer ke laying!');
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
@@ -646,53 +630,70 @@ const TransferToLayingsTable = () => {
|
|||||||
onReset={filterResetHandler}
|
onReset={filterResetHandler}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmationModal
|
<TransferToLayingConfirmationModal
|
||||||
ref={deleteModal.ref}
|
ref={deleteModal.ref}
|
||||||
iconPosition='left'
|
|
||||||
type='error'
|
type='error'
|
||||||
text='Delete This Data?'
|
text='Delete This Data?'
|
||||||
subtitleText='Are you sure you want to delete this data? '
|
subtitleText='Are you sure you want to delete this data? '
|
||||||
|
transferToLayingIds={selectedRowIds}
|
||||||
|
primaryButton={{
|
||||||
|
isLoading: isDeleteLoading,
|
||||||
|
color: 'error',
|
||||||
|
onClick: confirmationModalDeleteClickHandler,
|
||||||
|
}}
|
||||||
secondaryButton={{
|
secondaryButton={{
|
||||||
text: 'Cancel',
|
text: 'Cancel',
|
||||||
}}
|
color: 'none',
|
||||||
primaryButton={{
|
onClick: () => {
|
||||||
text: 'Delete',
|
setRowSelection({});
|
||||||
color: 'error',
|
deleteModal.closeModal();
|
||||||
isLoading: isDeleteLoading,
|
},
|
||||||
onClick: confirmationModalDeleteClickHandler,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmationModalWithNotes
|
{/* Approve Modal */}
|
||||||
|
<TransferToLayingConfirmationModal
|
||||||
ref={approveModal.ref}
|
ref={approveModal.ref}
|
||||||
type='success'
|
|
||||||
iconPosition='left'
|
|
||||||
text='Approve This Submission?'
|
text='Approve This Submission?'
|
||||||
subtitleText='Are you sure you want to approve this submission?'
|
subtitleText='Are you sure you want to approve this submission?'
|
||||||
secondaryButton={{
|
type='success'
|
||||||
text: 'Cancel',
|
transferToLayingIds={selectedRowIds}
|
||||||
}}
|
withNote
|
||||||
|
noteLabel='Notes Approval'
|
||||||
primaryButton={{
|
primaryButton={{
|
||||||
text: 'Approve',
|
|
||||||
color: 'success',
|
|
||||||
isLoading: isApproveLoading,
|
isLoading: isApproveLoading,
|
||||||
onClick: confirmationModalApproveClickHandler,
|
onClick: confirmationModalApproveClickHandler,
|
||||||
}}
|
}}
|
||||||
/>
|
|
||||||
|
|
||||||
<ConfirmationModalWithNotes
|
|
||||||
ref={rejectModal.ref}
|
|
||||||
type='error'
|
|
||||||
iconPosition='left'
|
|
||||||
text='Reject This Submission?'
|
|
||||||
subtitleText='Are you sure you want to reject this submission?'
|
|
||||||
secondaryButton={{
|
secondaryButton={{
|
||||||
text: 'Cancel',
|
text: 'Cancel',
|
||||||
|
color: 'none',
|
||||||
|
onClick: () => {
|
||||||
|
setRowSelection({});
|
||||||
|
approveModal.closeModal();
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Reject Modal */}
|
||||||
|
<TransferToLayingConfirmationModal
|
||||||
|
ref={rejectModal.ref}
|
||||||
|
type='error'
|
||||||
|
text='Reject This Submission?'
|
||||||
|
subtitleText='Are you sure you want to reject this submission?'
|
||||||
|
transferToLayingIds={selectedRowIds}
|
||||||
|
withNote
|
||||||
|
noteLabel='Notes Reject'
|
||||||
|
secondaryButton={{
|
||||||
|
text: 'Cancel',
|
||||||
|
color: 'none',
|
||||||
|
onClick: () => {
|
||||||
|
setRowSelection({});
|
||||||
|
rejectModal.closeModal();
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
primaryButton={{
|
primaryButton={{
|
||||||
text: 'Reject',
|
|
||||||
color: 'error',
|
|
||||||
isLoading: isRejectLoading,
|
isLoading: isRejectLoading,
|
||||||
|
color: 'error',
|
||||||
onClick: confirmationModalRejectClickHandler,
|
onClick: confirmationModalRejectClickHandler,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -399,23 +399,58 @@ const UniformityForm = ({
|
|||||||
// ===== SIDE EFFECTS =====
|
// ===== SIDE EFFECTS =====
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
|
projectFlockKandangLookup?.chick_in_date &&
|
||||||
projectFlockKandangLookup?.project_flock_kandang_id &&
|
projectFlockKandangLookup?.project_flock_kandang_id &&
|
||||||
isResponseSuccess(recordingsData) &&
|
isResponseSuccess(recordingsData) &&
|
||||||
recordingsData.data
|
recordingsData.data
|
||||||
) {
|
) {
|
||||||
const matchingRecording = recordingsData.data.find(
|
const matchingRecordings = recordingsData.data.filter(
|
||||||
(recording: Recording) =>
|
(recording: Recording) =>
|
||||||
recording.project_flock?.project_flock_kandang_id ===
|
recording.project_flock?.project_flock_kandang_id ===
|
||||||
projectFlockKandangLookup.project_flock_kandang_id
|
projectFlockKandangLookup.project_flock_kandang_id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (matchingRecording?.project_flock?.production_standart?.week) {
|
matchingRecordings.sort(
|
||||||
const weekValue =
|
(a: Recording, b: Recording) =>
|
||||||
matchingRecording.project_flock.production_standart.week;
|
new Date(a.record_datetime).getTime() -
|
||||||
formik.setFieldValue('week', weekValue);
|
new Date(b.record_datetime).getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
const earliestRecording = matchingRecordings[0];
|
||||||
|
|
||||||
|
if (earliestRecording) {
|
||||||
|
const chickInDate = new Date(projectFlockKandangLookup.chick_in_date);
|
||||||
|
chickInDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const earliestRecordDate = new Date(earliestRecording.record_datetime);
|
||||||
|
earliestRecordDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const initialWeek =
|
||||||
|
earliestRecording.project_flock?.production_standart?.week || 18;
|
||||||
|
|
||||||
|
if (formik.values.date) {
|
||||||
|
const selectedDate = new Date(formik.values.date);
|
||||||
|
selectedDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const daysDiff = Math.floor(
|
||||||
|
(selectedDate.getTime() - chickInDate.getTime()) /
|
||||||
|
(1000 * 60 * 60 * 24)
|
||||||
|
);
|
||||||
|
|
||||||
|
const weeksDiff = Math.floor(daysDiff / 7);
|
||||||
|
|
||||||
|
formik.setFieldValue('week', initialWeek + weeksDiff);
|
||||||
|
} else {
|
||||||
|
formik.setFieldValue('week', initialWeek);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [projectFlockKandangLookup?.project_flock_kandang_id, recordingsData]);
|
}
|
||||||
|
}, [
|
||||||
|
projectFlockKandangLookup?.chick_in_date,
|
||||||
|
projectFlockKandangLookup?.project_flock_kandang_id,
|
||||||
|
recordingsData,
|
||||||
|
formik.values.date,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub = subscribeValidate(() => {
|
const unsub = subscribeValidate(() => {
|
||||||
|
|||||||
@@ -389,6 +389,11 @@ export const FINANCE_INITIAL_BALANCE_TYPE_OPTIONS = [
|
|||||||
{ label: 'Saldo Awal Negatif', value: 'NEGATIVE' },
|
{ label: 'Saldo Awal Negatif', value: 'NEGATIVE' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const FINANCE_INJECTION_TYPE_OPTIONS = [
|
||||||
|
{ label: 'Saldo Injection Positif', value: 'POSITIVE' },
|
||||||
|
{ label: 'Saldo Injection Negatif', value: 'NEGATIVE' },
|
||||||
|
];
|
||||||
|
|
||||||
export const FINANCE_TRANSACTION_TYPE_OPTIONS = [
|
export const FINANCE_TRANSACTION_TYPE_OPTIONS = [
|
||||||
{ label: 'Pembelian', value: 'PEMBELIAN' },
|
{ label: 'Pembelian', value: 'PEMBELIAN' },
|
||||||
{ label: 'Penjualan', value: 'PENJUALAN' },
|
{ label: 'Penjualan', value: 'PENJUALAN' },
|
||||||
|
|||||||
@@ -312,6 +312,7 @@ export function DailyChecklistContent() {
|
|||||||
try {
|
try {
|
||||||
const activitiesRes = await PhaseActivityApi.getAll({
|
const activitiesRes = await PhaseActivityApi.getAll({
|
||||||
phase_ids: selectedPhaseIds.join(','),
|
phase_ids: selectedPhaseIds.join(','),
|
||||||
|
limit: '100',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isResponseError(activitiesRes)) {
|
if (isResponseError(activitiesRes)) {
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ export class RecordingService extends BaseApiService<
|
|||||||
}
|
}
|
||||||
|
|
||||||
async nextDayRecording(
|
async nextDayRecording(
|
||||||
projectFlockId: number
|
projectFlockId: number,
|
||||||
|
recordDate?: string
|
||||||
): Promise<BaseApiResponse<NextDayRecording> | undefined> {
|
): Promise<BaseApiResponse<NextDayRecording> | undefined> {
|
||||||
return await this.customRequest<BaseApiResponse<NextDayRecording>>(
|
return await this.customRequest<BaseApiResponse<NextDayRecording>>(
|
||||||
`next-day`,
|
`next-day`,
|
||||||
@@ -82,6 +83,7 @@ export class RecordingService extends BaseApiService<
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
project_flock_kandang_id: projectFlockId,
|
project_flock_kandang_id: projectFlockId,
|
||||||
|
record_date: recordDate,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
+1
@@ -76,6 +76,7 @@ export type ProjectFlockKandangLookup = {
|
|||||||
quantity: number;
|
quantity: number;
|
||||||
available_quantity?: number;
|
available_quantity?: number;
|
||||||
population: number;
|
population: number;
|
||||||
|
chick_in_date: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProjectFlockAvailableQuantity = {
|
export type ProjectFlockAvailableQuantity = {
|
||||||
|
|||||||
Reference in New Issue
Block a user