fix(resolve): fix resolve MR

This commit is contained in:
rstubryan
2025-11-21 09:25:16 +07:00
17 changed files with 5196 additions and 1874 deletions
@@ -0,0 +1,11 @@
import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
return <SuspenseHelper>{children}</SuspenseHelper>;
};
export default Layout;
@@ -0,0 +1,49 @@
'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from 'swr';
import GradingForm from '@/components/pages/production/recording/grading/form/GradingForm';
import { RecordingApi } from '@/services/api/production';
import { isResponseSuccess } from '@/lib/api-helper';
const AddGrading = () => {
const router = useRouter();
const searchParams = useSearchParams();
const recordingId = searchParams.get('recording_id');
const { data: recording, isLoading: isLoadingRecording } = useSWR(
recordingId && recordingId !== 'new' ? [recordingId] : null,
([id]) => RecordingApi.getSingle(parseInt(id))
);
if (
recordingId &&
recordingId !== 'new' &&
!isLoadingRecording &&
(!recording || !isResponseSuccess(recording))
) {
router.replace('/404');
return;
}
return (
<div className='w-full p-4 flex flex-row justify-center'>
{recordingId && recordingId !== 'new' && isLoadingRecording && (
<span className='loading loading-spinner loading-xl' />
)}
{(!recordingId ||
recordingId === 'new' ||
(!isLoadingRecording && recording && isResponseSuccess(recording))) && (
<GradingForm
type='add'
initialValues={
isResponseSuccess(recording) ? recording.data?.eggs?.[0] : undefined
}
/>
)}
</div>
);
};
export default AddGrading;
@@ -0,0 +1,53 @@
'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from 'swr';
import GradingForm from '@/components/pages/production/recording/grading/form/GradingForm';
import { RecordingApi } from '@/services/api/production';
import { isResponseSuccess } from '@/lib/api-helper';
const EditGrading = () => {
const router = useRouter();
const searchParams = useSearchParams();
const recordingId = searchParams.get('recordingId');
const gradingId = searchParams.get('gradingId');
const { data: recording, isLoading: isLoadingRecording } = useSWR(
recordingId ? [recordingId] : null,
([id]) => RecordingApi.getSingle(parseInt(id))
);
if (!recordingId) {
router.back();
return (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
);
}
if (!isLoadingRecording && (!recording || !isResponseSuccess(recording))) {
router.replace('/404');
return;
}
return (
<div className='w-full p-4 flex flex-row justify-center'>
{isLoadingRecording && (
<span className='loading loading-spinner loading-xl' />
)}
{!isLoadingRecording && recording && isResponseSuccess(recording) && (
<GradingForm
type='edit'
initialValues={recording.data.eggs?.find(
(egg) => egg.id === parseInt(gradingId || '0')
)}
/>
)}
</div>
);
};
export default EditGrading;
@@ -0,0 +1,52 @@
'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from 'swr';
import GradingForm from '@/components/pages/production/recording/grading/form/GradingForm';
import { RecordingApi } from '@/services/api/production';
import { isResponseSuccess } from '@/lib/api-helper';
const DetailGrading = () => {
const router = useRouter();
const searchParams = useSearchParams();
const gradingId = searchParams.get('gradingId');
const { data: grading, isLoading: isLoadingGrading } = useSWR(
gradingId ? [gradingId] : null,
([id]) => RecordingApi.getSingle(parseInt(id))
);
if (!gradingId) {
router.back();
return (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
);
}
if (!isLoadingGrading && (!grading || !isResponseSuccess(grading))) {
router.replace('/404');
return;
}
return (
<div className='w-full p-4 flex flex-row justify-center'>
{isLoadingGrading && (
<span className='loading loading-spinner loading-xl' />
)}
{!isLoadingGrading && grading && isResponseSuccess(grading) && (
<GradingForm
type='detail'
initialValues={grading.data.eggs?.find(
(egg) => egg.id === parseInt(gradingId)
)}
/>
)}
</div>
);
};
export default DetailGrading;
@@ -0,0 +1,11 @@
import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
return <SuspenseHelper>{children}</SuspenseHelper>;
};
export default Layout;
File diff suppressed because it is too large Load Diff
@@ -1,212 +1,320 @@
import * as Yup from 'yup';
import { RECORDING_FLAG_OPTIONS } from '@/config/constant';
import { Recording } from '@/types/api/production/recording';
import {
Recording,
CreateGrowingRecordingPayload,
CreateLayingRecordingPayload,
CreateEggPayload,
CreateGradingPayload,
} from '@/types/api/production/recording';
export const RecordingFormSchema = Yup.object({
flock: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
flock_id: Yup.number()
.default(0)
.typeError('Flock wajib diisi!')
.test(
'is-valid-flock',
'Flock wajib diisi!',
(value) => value !== undefined && value !== null && value > 0
)
.required('Flock wajib diisi!'),
location: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
location_id: Yup.number()
.default(0)
.typeError('Lokasi wajib diisi!')
.test(
'is-valid-location',
'Lokasi wajib diisi!',
(value) => value !== undefined && value !== null && value > 0
)
.required('Lokasi wajib diisi!'),
coop: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
coop_id: Yup.number()
.default(0)
.typeError('Kandang wajib diisi!')
.test(
'is-valid-coop',
'Kandang wajib diisi!',
(value) => value !== undefined && value !== null && value > 0
)
.required('Kandang wajib diisi!'),
recording_date: Yup.date()
.required('Tanggal recording wajib diisi')
.typeError('Format tanggal tidak valid'),
feed_data: Yup.array()
.of(
Yup.object({
feed_id: Yup.string().required('Nama pakan wajib diisi!'),
feed_qty: Yup.mixed<number | ''>().notRequired(),
feed_stock: Yup.number()
.required('Jumlah pakan yang digunakan wajib diisi!')
.min(1, 'Jumlah pakan minimal 1!')
.typeError('Jumlah pakan yang digunakan harus berupa angka!')
.test(
'is-not-exceed-qty',
'Jumlah pakan yang digunakan tidak boleh melebihi stok tersedia!',
function (value) {
const { feed_qty } = this.parent;
if (value === undefined) return true;
if (
feed_qty === undefined ||
feed_qty === '' ||
typeof feed_qty !== 'number'
)
return true;
return value <= feed_qty;
}
),
})
)
.min(1, 'Minimal harus ada 1 data pakan!')
.required('Data pakan wajib diisi!'),
body_weight: Yup.array()
.of(
Yup.object({
chicken_weight: Yup.number()
.required('Berat ayam wajib diisi!')
.min(1, 'Berat ayam minimal 1 gram!')
.typeError('Berat ayam harus berupa angka!'),
chicken_count: Yup.number()
.required('Jumlah ayam wajib diisi!')
.min(1, 'Jumlah ayam minimal 1 ekor!')
.typeError('Jumlah ayam harus berupa angka!'),
average_chicken_weight: Yup.number()
.required('Rata-rata berat ayam wajib diisi!')
.min(1, 'Rata-rata berat ayam minimal 1 gram!')
.typeError('Rata-rata berat ayam harus berupa angka!'),
})
)
.min(1, 'Minimal harus ada 1 data bobot badan!')
.required('Data bobot badan wajib diisi!'),
vaccination: Yup.array()
.of(
Yup.object({
vaccine_id: Yup.string().required('Nama vaksin wajib diisi!'),
total_stock: Yup.mixed<number | ''>().notRequired(),
used_stock: Yup.number()
.required('Jumlah vaksin yang digunakan wajib diisi!')
.min(1, 'Jumlah vaksin minimal 1!')
.typeError('Jumlah vaksin yang digunakan harus berupa angka!')
.test(
'is-not-exceed-total',
'Jumlah vaksin yang digunakan tidak boleh melebihi stok tersedia!',
function (value) {
const { total_stock } = this.parent;
if (value === undefined) return true;
if (
total_stock === undefined ||
total_stock === '' ||
typeof total_stock !== 'number'
)
return true;
return value <= total_stock;
}
),
})
)
.min(1, 'Minimal harus ada 1 data vaksinasi!')
.required('Data vaksinasi wajib diisi!'),
mortality: Yup.array()
.of(
Yup.object({
condition: Yup.mixed<string>()
.oneOf(
RECORDING_FLAG_OPTIONS.map((opt) => opt.value),
'Kondisi tidak valid!'
)
.required('Kondisi wajib diisi!'),
count: Yup.number()
.required('Jumlah mortalitas wajib diisi!')
.min(1, 'Jumlah mortalitas minimal 1 ekor!')
.typeError('Jumlah mortalitas harus berupa angka!'),
})
)
.min(1, 'Minimal harus ada 1 data mortalitas!')
.required('Data mortalitas wajib diisi!'),
type RecordingGrowingFormSchemaType = {
project_flock_kandang: {
value: number;
label: string;
} | null;
project_flock_kandang_id: number;
body_weights: {
weight: number | string;
avg_weight: number | string;
qty: number | string;
}[];
stocks: {
product_warehouse_id: number;
qty: number | string;
}[];
depletions: {
product_warehouse_id: number;
qty: number | string;
}[];
};
type RecordingLayingFormSchemaType = RecordingGrowingFormSchemaType & {
eggs: {
product_warehouse_id: number;
qty: number | string;
}[];
};
type RecordingGradingFormSchemaType = {
eggs_grading: {
recording_egg_id: number;
grade: string;
qty: number | string;
}[];
};
export type BodyWeightSchema = {
weight: number | string;
avg_weight: number | string;
qty: number | string;
};
export type StockSchema = {
product_warehouse_id: number;
qty: number | string;
};
export type DepletionSchema = {
product_warehouse_id: number;
qty: number | string;
};
export type EggSchema = {
product_warehouse_id: number;
qty: number | string;
};
const BodyWeightObjectSchema: Yup.ObjectSchema<BodyWeightSchema> = Yup.object({
weight: Yup.number()
.required('Berat ayam total wajib diisi!')
.min(1, 'Berat ayam total minimal 1 gram!')
.typeError('Berat ayam total harus berupa angka!'),
avg_weight: Yup.number()
.required('Berat ayam rata-rata wajib diisi!')
.typeError('Berat ayam rata-rata harus berupa angka!'),
qty: Yup.number()
.required('Jumlah ayam wajib diisi!')
.min(1, 'Jumlah ayam minimal 1 ekor!')
.typeError('Jumlah ayam harus berupa angka!'),
});
export const UpdateRecordingFormSchema = RecordingFormSchema;
const StockObjectSchema: Yup.ObjectSchema<StockSchema> = Yup.object({
product_warehouse_id: Yup.number()
.required('Produk wajib diisi!')
.min(1, 'Produk wajib diisi!')
.typeError('Produk harus berupa angka!'),
qty: Yup.number()
.required('Jumlah penggunaan wajib diisi!')
.min(1, 'Jumlah penggunaan tidak boleh 0!')
.typeError('Jumlah penggunaan harus berupa angka!'),
});
export type RecordingFormValues = Yup.InferType<typeof RecordingFormSchema>;
const DepletionObjectSchema: Yup.ObjectSchema<DepletionSchema> = Yup.object({
product_warehouse_id: Yup.number()
.required('Produk depletions wajib diisi!')
.min(1, 'Produk depletions wajib diisi!')
.typeError('Produk depletions harus berupa angka!'),
qty: Yup.number()
.required('Jumlah depletions wajib diisi!')
.min(1, 'Jumlah depletions minimal 1!')
.typeError('Jumlah depletions harus berupa angka!'),
});
export const getRecordingFormInitialValues = (
initialValues?: Recording
): RecordingFormValues => ({
flock: initialValues?.flock
const EggObjectSchema: Yup.ObjectSchema<EggSchema> = Yup.object({
product_warehouse_id: Yup.number()
.required('Kondisi telur wajib diisi!')
.min(1, 'Kondisi telur wajib diisi!')
.typeError('Kondisi telur harus berupa angka!'),
qty: Yup.number()
.required('Jumlah telur wajib diisi!')
.min(1, 'Jumlah telur tidak boleh 0!')
.typeError('Jumlah telur harus berupa angka!'),
});
export const RecordingGrowingFormSchema: Yup.ObjectSchema<RecordingGrowingFormSchemaType> =
Yup.object({
project_flock_kandang: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
project_flock_kandang_id: Yup.number()
.default(0)
.typeError('Project Flock Kandang wajib diisi!')
.test(
'is-valid-project-flock-kandang',
'Project Flock Kandang wajib diisi!',
(value) => value !== undefined && value !== null && value > 0
)
.required('Project Flock Kandang wajib diisi!')
.test(
'not-already-recorded',
'Project Flock ini sudah direcord hari ini!',
function (value) {
const recordedProjectFlockIds = this.options.context
?.recordedProjectFlockIds as Set<number>;
const formType = this.options.context?.type as
| 'add'
| 'edit'
| 'detail';
if (formType !== 'add') return true;
if (value && recordedProjectFlockIds?.has(value)) {
return false;
}
return true;
}
),
body_weights: Yup.array()
.of(BodyWeightObjectSchema)
.min(1, 'Minimal harus ada 1 data bobot badan!')
.required('Data bobot badan wajib diisi!'),
stocks: Yup.array()
.of(StockObjectSchema)
.min(1, 'Minimal harus ada 1 data stok!')
.required('Data stok wajib diisi!'),
depletions: Yup.array()
.of(DepletionObjectSchema)
.min(1, 'Minimal harus ada 1 data depletions!')
.required('Data depletions wajib diisi!'),
});
export const RecordingLayingFormSchema: Yup.ObjectSchema<RecordingLayingFormSchemaType> =
RecordingGrowingFormSchema.shape({
eggs: Yup.array()
.of(EggObjectSchema)
.min(1, 'Minimal harus ada 1 data telur!')
.required('Data telur wajib diisi!'),
});
export const UpdateRecordingGrowingFormSchema =
RecordingGrowingFormSchema.shape({
project_flock_kandang_id: Yup.number()
.default(0)
.typeError('Project Flock Kandang wajib diisi!')
.test(
'is-valid-project-flock-kandang',
'Project Flock Kandang wajib diisi!',
(value) => value !== undefined && value !== null && value > 0
)
.required('Project Flock Kandang wajib diisi!'),
});
export const UpdateRecordingLayingFormSchema = RecordingLayingFormSchema.shape({
project_flock_kandang_id: Yup.number()
.default(0)
.typeError('Project Flock Kandang wajib diisi!')
.test(
'is-valid-project-flock-kandang',
'Project Flock Kandang wajib diisi!',
(value) => value !== undefined && value !== null && value > 0
)
.required('Project Flock Kandang wajib diisi!'),
});
export const RecordingGradingFormSchema: Yup.ObjectSchema<RecordingGradingFormSchemaType> =
Yup.object({
eggs_grading: Yup.array()
.of(
Yup.object({
recording_egg_id: Yup.number()
.required('Recording Egg ID wajib diisi!')
.min(1, 'Recording Egg ID minimal 1!')
.typeError('Recording Egg ID harus berupa angka!'),
grade: Yup.string()
.required('Grade telur wajib diisi!')
.typeError('Grade telur harus berupa string!'),
qty: Yup.number()
.required('Jumlah telur wajib diisi!')
.min(1, 'Jumlah telur minimal 1!')
.typeError('Jumlah telur harus berupa angka!'),
})
)
.min(1, 'Minimal harus ada 1 data grading telur!')
.required('Data grading telur wajib diisi!'),
});
export const UpdateRecordingGradingFormSchema = RecordingGradingFormSchema;
export type RecordingGrowingFormValues = Yup.InferType<
typeof RecordingGrowingFormSchema
>;
export type RecordingLayingFormValues = Yup.InferType<
typeof RecordingLayingFormSchema
>;
export type RecordingGradingFormValues = Yup.InferType<
typeof RecordingGradingFormSchema
>;
type RecordingFormData = Partial<Recording> & {
body_weights?: CreateGrowingRecordingPayload['body_weights'];
stocks?: CreateGrowingRecordingPayload['stocks'] | Recording['stocks'];
depletions?:
| CreateGrowingRecordingPayload['depletions']
| Recording['depletions'];
eggs?: CreateLayingRecordingPayload['eggs'] | Recording['eggs'];
project_flock_kandang_id?: number;
project_flock_category?: string;
};
export const getRecordingGrowingFormInitialValues = (
initialValues?: RecordingFormData
): RecordingGrowingFormValues => ({
project_flock_kandang: initialValues?.project_flock_kandang_id
? {
value: initialValues.flock.id,
label: initialValues.flock.name,
value: initialValues.project_flock_kandang_id,
label: `Project Flock #${initialValues.project_flock_kandang_id}`,
}
: null,
flock_id: initialValues?.flock?.id ?? 0,
location: initialValues?.location
? {
value: initialValues.location.id,
label: initialValues.location.name,
}
: null,
location_id: initialValues?.location?.id ?? 0,
coop: initialValues?.coop
? {
value: initialValues.coop.id,
label: initialValues.coop.name,
}
: null,
coop_id: initialValues?.coop?.id ?? 0,
recording_date: initialValues?.recording_date
? new Date(initialValues.recording_date)
: new Date(),
feed_data: initialValues?.feed_data
? initialValues.feed_data.map((feed) => ({
feed_id: feed.feed_name,
feed_qty: feed.feed_qty,
feed_stock: feed.feed_stock,
}))
: [
{
feed_id: '',
feed_qty: '',
feed_stock: 0,
},
],
body_weight: initialValues?.body_weight ?? [
project_flock_kandang_id: initialValues?.project_flock_kandang_id ?? 0,
body_weights: initialValues?.body_weights?.map(
(bw: NonNullable<CreateGrowingRecordingPayload['body_weights']>[0]) => ({
weight: bw.avg_weight * bw.qty,
avg_weight: bw.avg_weight,
qty: bw.qty,
})
) ?? [
{
chicken_weight: 0,
chicken_count: 0,
average_chicken_weight: 0,
weight: '',
avg_weight: '',
qty: '',
},
],
vaccination: initialValues?.vaccination
? initialValues.vaccination.map((vaccine) => ({
vaccine_id: vaccine.vaccine_name,
total_stock: vaccine.total_stock,
used_stock: vaccine.used_stock,
}))
: [
{
vaccine_id: '',
total_stock: '',
used_stock: 0,
},
],
mortality: initialValues?.mortality ?? [
stocks: initialValues?.stocks?.map((stock) => ({
product_warehouse_id: stock.product_warehouse_id,
qty:
(stock as { qty?: number; usage_amount?: number }).qty ||
(stock as { qty?: number; usage_amount?: number }).usage_amount ||
'',
})) ?? [
{
condition: '',
count: 0,
product_warehouse_id: 0,
qty: '',
},
],
depletions: initialValues?.depletions?.map(
(
depletion: NonNullable<CreateGrowingRecordingPayload['depletions']>[0]
) => ({
product_warehouse_id: depletion.product_warehouse_id,
qty: depletion.qty,
})
) ?? [
{
product_warehouse_id: 0,
qty: '',
},
],
});
export const getRecordingLayingFormInitialValues = (
initialValues?: RecordingFormData
): RecordingLayingFormValues => ({
...getRecordingGrowingFormInitialValues(initialValues),
eggs: initialValues?.eggs?.map((egg: CreateEggPayload) => ({
product_warehouse_id: egg.product_warehouse_id,
qty: egg.qty,
})) ?? [
{
product_warehouse_id: 0,
qty: '',
},
],
});
export const getRecordingGradingFormInitialValues = (
initialValues?: Partial<CreateGradingPayload> & { recording_egg_id?: number }
): RecordingGradingFormValues => ({
eggs_grading: initialValues?.eggs_grading?.map((grading) => ({
recording_egg_id: grading.recording_egg_id,
grade: grading.grade,
qty: grading.qty,
})) ?? [
{
recording_egg_id: initialValues?.recording_egg_id ?? 0,
grade: '',
qty: '',
},
],
});
File diff suppressed because it is too large Load Diff
@@ -1,70 +0,0 @@
import { useCallback, useState } from 'react';
import { useRouter } from 'next/navigation';
import { toast } from 'react-hot-toast';
import { useModal } from '@/components/Modal';
import { RecordingApi } from '@/services/api/production';
import {
CreateRecordingPayload,
UpdateRecordingPayload,
} from '@/types/api/production/recording';
import { isResponseError } from '@/lib/api-helper';
export const useRecordingFormHandlers = (initialValuesId?: number) => {
const router = useRouter();
const deleteModal = useModal();
const [recordingFormErrorMessage, setRecordingFormErrorMessage] =
useState('');
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const createRecordingHandler = useCallback(
async (payload: CreateRecordingPayload) => {
const res = await RecordingApi.create(payload);
if (isResponseError(res)) {
setRecordingFormErrorMessage(res.message);
return;
}
toast.success(res?.message as string);
router.push('/flock/recording');
},
[router]
);
const updateRecordingHandler = useCallback(
async (recordingId: number, payload: UpdateRecordingPayload) => {
const res = await RecordingApi.update(recordingId, payload);
if (res?.status === 'error') {
setRecordingFormErrorMessage(res.message);
return;
}
toast.success(res?.message as string);
router.refresh();
router.push('/flock/recording');
},
[router]
);
const deleteRecordingClickHandler = useCallback(() => {
deleteModal.openModal();
}, [deleteModal]);
const confirmationModalDeleteClickHandler = useCallback(async () => {
if (!initialValuesId) return;
setIsDeleteLoading(true);
await RecordingApi.delete(initialValuesId);
deleteModal.closeModal();
toast.success('Successfully delete Recording!');
setIsDeleteLoading(false);
router.push('/flock/recording');
}, [deleteModal, initialValuesId, router]);
return {
deleteModal,
recordingFormErrorMessage,
isDeleteLoading,
createRecordingHandler,
updateRecordingHandler,
deleteRecordingClickHandler,
confirmationModalDeleteClickHandler,
};
};
File diff suppressed because it is too large Load Diff
+45
View File
@@ -33,6 +33,51 @@ export const TRANSFER_TO_LAYING_APPROVAL_LINE: ApprovalLine = [
},
] as const;
export const RECORDING_APPROVAL_LINE: ApprovalLine = [
{
step_number: 1,
step_name: 'Grading-Telur',
},
{
step_number: 2,
step_name: 'Pengajuan',
},
{
step_number: 3,
step_name: 'Disetujui',
},
] as const;
export const GROWING_RECORDING_APPROVAL_LINE: ApprovalLine = [
{
step_number: 1,
step_name: 'Grading-Telur',
},
{
step_number: 2,
step_name: 'Pengajuan',
},
{
step_number: 3,
step_name: 'Disetujui',
},
] as const;
export const LAYING_RECORDING_APPROVAL_LINE: ApprovalLine = [
{
step_number: 1,
step_name: 'Grading-Telur',
},
{
step_number: 2,
step_name: 'Pengajuan',
},
{
step_number: 3,
step_name: 'Disetujui',
},
] as const;
export const PURCHASE_ORDER_APPROVAL_LINE: ApprovalLine = [
{
step_number: 1,
+33
View File
@@ -244,6 +244,39 @@ export const RECORDING_FLAG_OPTIONS = [
{ label: 'Ayam Mati', value: 'Ayam Mati' },
];
export const APPROVAL_WORKFLOWS = [
{
key: 'PROJECT_FLOCKS',
steps: [
{
step_number: 1,
step_name: 'Pengajuan',
},
{
step_number: 2,
step_name: 'Aktif',
},
],
},
{
key: 'RECORDINGS',
steps: [
{
step_number: 1,
step_name: 'Grading-Telur',
},
{
step_number: 2,
step_name: 'Pengajuan',
},
{
step_number: 3,
step_name: 'Disetujui',
},
],
},
];
export const ACCEPTED_FILE_TYPE = {
PDF: {
'application/pdf': ['.pdf'],
+6
View File
@@ -0,0 +1,6 @@
import { BaseApiService } from '@/services/api/base';
import { BaseApproval } from '@/types/api/api-general';
export const ApprovalApi = new BaseApiService<BaseApproval, unknown, unknown>(
'/approvals'
);
+99 -2
View File
@@ -1,8 +1,17 @@
import { BaseApiService } from './base';
import { BaseApiResponse } from '@/types/api/api-general';
import {
CreateProjectFlockPayload,
ProjectFlock,
UpdateProjectFlockPayload,
} from '@/types/api/production/project-flock';
import {
CreateRecordingPayload,
Recording,
UpdateRecordingPayload,
CreateGradingPayload,
UpdateGradingPayload,
NextDayRecording,
} from '@/types/api/production/recording';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
@@ -11,8 +20,96 @@ export const ProjectFlockKandangApi = new BaseApiService<
unknown,
unknown
>('/production/project-flock-kandangs');
export const RecordingApi = new BaseApiService<
export const ProjectFlockApi = new BaseApiService<
ProjectFlock,
CreateProjectFlockPayload,
UpdateProjectFlockPayload
>('/production/project-flocks');
export class RecordingService extends BaseApiService<
Recording,
CreateRecordingPayload,
UpdateRecordingPayload
>('/production/recordings');
> {
constructor(basePath: string = '') {
super(basePath);
}
async approve(
idOrIds: number | number[],
notes?: string
): Promise<BaseApiResponse<Recording[]> | undefined> {
const approvable_ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
return await this.customRequest<BaseApiResponse<Recording[]>>('approvals', {
method: 'POST',
payload: {
action: 'APPROVED',
approvable_ids,
notes,
},
});
}
async reject(
idOrIds: number | number[],
notes: string = ''
): Promise<BaseApiResponse<Recording[]> | undefined> {
const approvable_ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
return await this.customRequest<BaseApiResponse<Recording[]>>('approvals', {
method: 'POST',
payload: {
action: 'REJECTED',
approvable_ids,
notes,
},
});
}
async createGrading(
payload: CreateGradingPayload
): Promise<BaseApiResponse<unknown> | undefined> {
return await this.customRequest<BaseApiResponse<unknown>>('gradings', {
method: 'POST',
payload,
});
}
async updateGrading(
gradingId: number,
payload: UpdateGradingPayload
): Promise<BaseApiResponse<unknown> | undefined> {
return await this.customRequest<BaseApiResponse<unknown>>(
`gradings/${gradingId}`,
{
method: 'PUT',
payload,
}
);
}
async deleteGrading(
gradingId: number
): Promise<BaseApiResponse<unknown> | undefined> {
return await this.customRequest<BaseApiResponse<unknown>>(
`gradings/${gradingId}`,
{
method: 'DELETE',
}
);
}
async nextDayRecording(
projectFlockId: number
): Promise<BaseApiResponse<NextDayRecording> | undefined> {
return await this.customRequest<BaseApiResponse<NextDayRecording>>(
`next-day`,
{
method: 'GET',
params: {
project_flock_kandang_id: projectFlockId,
},
}
);
}
}
export const RecordingApi = new RecordingService('/production/recordings');
@@ -0,0 +1,11 @@
import { BaseApiService } from '@/services/api/base';
import {
BaseProjectFlockKandang,
ProjectFlockKandang,
} from '@/types/api/production/project-flock-kandang';
export const ProjectFlockKandangApi = new BaseApiService<
BaseProjectFlockKandang,
ProjectFlockKandang,
unknown
>('project-flock-kandang');
+12 -2
View File
@@ -7,8 +7,8 @@ import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
export type BaseProjectFlock = {
id: number;
name: string;
flock_name: string;
name?: string;
flock_name?: string;
status: string;
flock?: Flock;
flock_i?: number;
@@ -52,6 +52,16 @@ export type ProjectFlockApprovalPayload = {
approvable_ids: number[];
};
export type ProjectFlockKandangLookup = {
id: number;
project_flock_kandang_id: number;
project_flock_id: number;
kandang_id: number;
kandang: Kandang;
project_flock: ProjectFlock;
quantity: number;
};
export type ProjectFlockAvailableQuantity = {
project_flock_id: number;
flock_name: string;
+134 -48
View File
@@ -1,61 +1,147 @@
import { BaseMetadata } from '@/types/api/api-general';
import { Location } from '@/types/api/master-data/location';
import { Kandang } from '@/types/api/master-data/kandang';
import { Flock } from '@/types/api/master-data/flock';
import { BaseApproval, BaseMetadata, User } from '@/types/api/api-general';
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
export type ProductionMetrics = {
total_depletion_qty: number;
cum_depletion_rate: number;
daily_gain: number;
avg_daily_gain: number;
cum_intake: number;
fcr_value: number;
total_chick_qty: number;
daily_depletion_rate?: number;
cum_depletion?: number;
};
export type BaseRecording = {
id: number;
flock: Flock;
recording_date: string;
location: Location;
coop: Kandang;
feed_data: {
feed_name: string;
feed_qty: number;
feed_stock: number;
}[];
body_weight: {
chicken_weight: number;
chicken_count: number;
average_chicken_weight: number;
}[];
vaccination: {
vaccine_name: string;
total_stock: number;
used_stock: number;
}[];
mortality: {
condition: string;
count: number;
project_flock_kandang_id: number;
record_datetime: string;
day: number;
created_by: User;
} & ProductionMetrics;
export type RecordingBW = {
id: number;
recording_id: number;
avg_weight: number;
qty: number;
total_weight: number;
};
export type RecordingDepletion = {
id: number;
recording_id: number;
product_warehouse_id: number;
qty: number;
product_warehouse: ProductWarehouse;
};
export type RecordingStock = {
id: number;
recording_id: number;
product_warehouse_id: number;
usage_amount?: number;
usage_qty: number;
qty: number;
pending_qty: number;
product_warehouse: ProductWarehouse;
};
export type RecordingEgg = {
id: number;
recording_id: number;
product_warehouse_id: number;
qty: number;
created_by: User;
product_warehouse: ProductWarehouse;
gradings?: {
grade: string;
qty: number;
}[];
};
export type Recording = BaseMetadata & BaseRecording;
export type GradingEgg = {
id: number;
recording_egg_id: number;
qty: number;
grade: string;
created_by: User;
};
export type CreateRecordingPayload = {
flock_id: number;
recording_date: string;
location_id: number;
coop_id: number;
feed_data: {
feed_id: string;
feed_qty: number;
feed_stock: number;
export type Recording = BaseMetadata &
BaseRecording & {
project_flock_category?: 'GROWING' | 'LAYING';
approval?: BaseApproval;
egg_grading_status?: string | null;
egg_grading_pending_qty?: number | null;
egg_grading_completed_qty?: number | null;
body_weights?: RecordingBW[];
depletions?: RecordingDepletion[];
stocks?: RecordingStock[];
eggs?: RecordingEgg[];
recording_bws?: RecordingBW[];
recording_depletions?: RecordingDepletion[];
recording_stocks?: RecordingStock[];
recording_eggs?: RecordingEgg[];
grading_eggs?: GradingEgg[];
};
export type NextDayRecording = {
project_flock_kandang_id: number;
next_day: number;
};
export type CreateGrowingRecordingPayload = {
project_flock_kandang_id: number;
body_weights: {
avg_weight: number;
qty: number;
}[];
body_weight: {
chicken_weight: number;
chicken_count: number;
average_chicken_weight: number;
stocks?: {
product_warehouse_id: number;
qty: number;
}[];
vaccination: {
vaccine_id: string;
total_stock: number;
used_stock: number;
}[];
mortality: {
condition: string;
count: number;
depletions?: {
product_warehouse_id: number;
qty: number;
}[];
};
export type CreateGradingPayload = {
eggs_grading: {
recording_egg_id: number;
grade: string;
qty: number;
}[];
};
export type UpdateGradingPayload = CreateGradingPayload;
export type CreateGradingRecordingPayload = {
eggs_grading: {
recording_egg_id: number;
grade: string;
qty: number;
}[];
};
export type CreateEggPayload = {
product_warehouse_id: number;
qty: number;
};
export type CreateLayingRecordingPayload = CreateGrowingRecordingPayload & {
eggs?: CreateEggPayload[];
};
export type CreateRecordingPayload =
| CreateGrowingRecordingPayload
| CreateLayingRecordingPayload
| CreateGradingRecordingPayload;
export type UpdateGrowingRecordingPayload = CreateGrowingRecordingPayload;
export type UpdateLayingRecordingPayload = CreateLayingRecordingPayload;
export type UpdateGradingRecordingPayload = CreateGradingRecordingPayload;
export type UpdateRecordingPayload = CreateRecordingPayload;