mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-316): Update uniformity payload/fields and file handling
This commit is contained in:
@@ -269,23 +269,23 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
{
|
{
|
||||||
accessorKey: 'location.name',
|
accessorKey: 'location.name',
|
||||||
header: 'Lokasi',
|
header: 'Lokasi',
|
||||||
cell: (props) => props.row.original.location.name || '-',
|
cell: (props) => props.row.original.location_name || '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'project_flock_kandang_id',
|
accessorKey: 'flock_name',
|
||||||
header: 'Flock',
|
header: 'Flock',
|
||||||
cell: (props) => `Flock ${props.row.original.project_flock_kandang_id}`,
|
cell: (props) => props.row.original.flock_name || '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'kandang.name',
|
accessorKey: 'kandang_name',
|
||||||
header: 'Kandang',
|
header: 'Kandang',
|
||||||
cell: (props) => props.row.original.kandang.name || '-',
|
cell: (props) => props.row.original.kandang_name || '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'week',
|
accessorKey: 'week',
|
||||||
header: 'Tanggal (Week)',
|
header: 'Tanggal (Week)',
|
||||||
cell: (props) =>
|
cell: (props) =>
|
||||||
`${formatDate(props.row.original.date, 'DD MMM YYYY')} (${props.row.original.week})`,
|
`${formatDate(props.row.original.applied_at, 'DD MMM YYYY')} (${props.row.original.week})`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'status',
|
accessorKey: 'status',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Uniformity } from '@/types/api/uniformity/uniformity';
|
|||||||
|
|
||||||
type UniformityFormSchemaType = {
|
type UniformityFormSchemaType = {
|
||||||
date: string;
|
date: string;
|
||||||
|
week: number;
|
||||||
location?: {
|
location?: {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -19,7 +20,7 @@ type UniformityFormSchemaType = {
|
|||||||
label: string;
|
label: string;
|
||||||
} | null;
|
} | null;
|
||||||
kandang_id: number;
|
kandang_id: number;
|
||||||
files: File | undefined;
|
file: File | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FileSchema = Yup.mixed<File>()
|
const FileSchema = Yup.mixed<File>()
|
||||||
@@ -44,6 +45,10 @@ const FileSchema = Yup.mixed<File>()
|
|||||||
export const UniformityFormSchema: Yup.ObjectSchema<UniformityFormSchemaType> =
|
export const UniformityFormSchema: Yup.ObjectSchema<UniformityFormSchemaType> =
|
||||||
Yup.object({
|
Yup.object({
|
||||||
date: Yup.string().required('Tanggal wajib diisi!'),
|
date: Yup.string().required('Tanggal wajib diisi!'),
|
||||||
|
week: Yup.number()
|
||||||
|
.min(1, 'Minggu ke wajib diisi!')
|
||||||
|
.required('Minggu ke wajib diisi!')
|
||||||
|
.typeError('Minggu ke wajib diisi!'),
|
||||||
location: Yup.object({
|
location: Yup.object({
|
||||||
value: Yup.number().min(1).required(),
|
value: Yup.number().min(1).required(),
|
||||||
label: Yup.string().required(),
|
label: Yup.string().required(),
|
||||||
@@ -69,7 +74,7 @@ export const UniformityFormSchema: Yup.ObjectSchema<UniformityFormSchemaType> =
|
|||||||
.min(1, 'Kandang wajib diisi!')
|
.min(1, 'Kandang wajib diisi!')
|
||||||
.required('Kandang wajib diisi!')
|
.required('Kandang wajib diisi!')
|
||||||
.typeError('Kandang wajib diisi!'),
|
.typeError('Kandang wajib diisi!'),
|
||||||
files: FileSchema.required('File wajib diisi!'),
|
file: FileSchema.required('File wajib diisi!'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UniformityFormValues = Yup.InferType<typeof UniformityFormSchema>;
|
export type UniformityFormValues = Yup.InferType<typeof UniformityFormSchema>;
|
||||||
@@ -79,6 +84,7 @@ export const getUniformityFormInitialValues = (
|
|||||||
): UniformityFormValues => {
|
): UniformityFormValues => {
|
||||||
return {
|
return {
|
||||||
date: initialValues?.week ? '' : '',
|
date: initialValues?.week ? '' : '',
|
||||||
|
week: initialValues?.week ?? 0,
|
||||||
location: initialValues?.location
|
location: initialValues?.location
|
||||||
? {
|
? {
|
||||||
value: initialValues.location.id,
|
value: initialValues.location.id,
|
||||||
@@ -101,6 +107,6 @@ export const getUniformityFormInitialValues = (
|
|||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
kandang_id: initialValues?.kandang?.id ?? 0,
|
kandang_id: initialValues?.kandang?.id ?? 0,
|
||||||
files: undefined,
|
file: undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useFormik } from 'formik';
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
|
import moment from 'moment';
|
||||||
import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
|
import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
import { useUiStore } from '@/stores/ui/ui.store';
|
||||||
import { useUniformityStore } from '@/stores/uniformity/uniformity.store';
|
import { useUniformityStore } from '@/stores/uniformity/uniformity.store';
|
||||||
@@ -240,14 +241,17 @@ const UniformityForm = ({
|
|||||||
|
|
||||||
setUniformityFormData({
|
setUniformityFormData({
|
||||||
date: values.date,
|
date: values.date,
|
||||||
|
week: values.week,
|
||||||
project_flock_kandang_id: projectFlockKandangId,
|
project_flock_kandang_id: projectFlockKandangId,
|
||||||
files: values.files as File,
|
file: values.file as File,
|
||||||
fileName: (values.files as File).name,
|
fileName: (values.file as File).name,
|
||||||
});
|
});
|
||||||
|
|
||||||
const payload: VerifyUniformityPayload = {
|
const payload: VerifyUniformityPayload = {
|
||||||
|
date: values.date,
|
||||||
|
week: values.week,
|
||||||
project_flock_kandang_id: projectFlockKandangId,
|
project_flock_kandang_id: projectFlockKandangId,
|
||||||
files: values.files as File,
|
file: values.file as File,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await UniformityApi.verifyUniformity(payload);
|
const res = await UniformityApi.verifyUniformity(payload);
|
||||||
@@ -323,10 +327,10 @@ const UniformityForm = ({
|
|||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
|
|
||||||
formik.setFieldTouched('files', true);
|
formik.setFieldTouched('file', true);
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
formik.setFieldValue('files', undefined);
|
formik.setFieldValue('file', undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,7 +350,7 @@ const UniformityForm = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
formik.setFieldValue('files', file);
|
formik.setFieldValue('file', file);
|
||||||
},
|
},
|
||||||
[formik]
|
[formik]
|
||||||
);
|
);
|
||||||
@@ -363,6 +367,19 @@ const UniformityForm = ({
|
|||||||
}, [formik]);
|
}, [formik]);
|
||||||
|
|
||||||
// ===== SIDE EFFECTS =====
|
// ===== SIDE EFFECTS =====
|
||||||
|
useEffect(() => {
|
||||||
|
// Calculate week from date whenever date changes (week of the month)
|
||||||
|
if (formik.values.date) {
|
||||||
|
const date = moment(formik.values.date);
|
||||||
|
const weekNumber = date.week() - moment(date).startOf('month').week() + 1;
|
||||||
|
|
||||||
|
// Handle edge case for end of year
|
||||||
|
const adjustedWeekNumber = weekNumber <= 0 ? weekNumber + 52 : weekNumber;
|
||||||
|
|
||||||
|
formik.setFieldValue('week', adjustedWeekNumber);
|
||||||
|
}
|
||||||
|
}, [formik.values.date]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub = subscribeValidate(() => {
|
const unsub = subscribeValidate(() => {
|
||||||
setIsValid(true);
|
setIsValid(true);
|
||||||
@@ -486,7 +503,7 @@ const UniformityForm = ({
|
|||||||
htmlFor='file-upload-input'
|
htmlFor='file-upload-input'
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full text-sm font-normal leading-5 after:content-['*'] after:ml-0.5 after:text-red-500",
|
"w-full text-sm font-normal leading-5 after:content-['*'] after:ml-0.5 after:text-red-500",
|
||||||
formik.touched.files && formik.errors.files && 'text-red-500'
|
formik.touched.file && formik.errors.file && 'text-red-500'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Upload File
|
Upload File
|
||||||
@@ -495,7 +512,7 @@ const UniformityForm = ({
|
|||||||
<section
|
<section
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-full w-full border rounded-2xl border-dashed cursor-pointer mt-2',
|
'h-full w-full border rounded-2xl border-dashed cursor-pointer mt-2',
|
||||||
formik.touched.files && formik.errors.files
|
formik.touched.file && formik.errors.file
|
||||||
? 'border-red-500'
|
? 'border-red-500'
|
||||||
: 'border-gray-300'
|
: 'border-gray-300'
|
||||||
)}
|
)}
|
||||||
@@ -503,7 +520,7 @@ const UniformityForm = ({
|
|||||||
document.getElementById('file-upload-input')?.click()
|
document.getElementById('file-upload-input')?.click()
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{formik.values.files ? (
|
{formik.values.file ? (
|
||||||
<div className='flex flex-col items-center justify-center gap-2 my-10'>
|
<div className='flex flex-col items-center justify-center gap-2 my-10'>
|
||||||
<div className='border border-[#18181B]/25 rounded-2xl p-1 flex items-center justify-center'>
|
<div className='border border-[#18181B]/25 rounded-2xl p-1 flex items-center justify-center'>
|
||||||
<Button
|
<Button
|
||||||
@@ -521,7 +538,7 @@ const UniformityForm = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<span className='text-md font-semibold text-black line-clamp-2 text-center max-w-xs break-all'>
|
<span className='text-md font-semibold text-black line-clamp-2 text-center max-w-xs break-all'>
|
||||||
{formik.values.files.name}
|
{formik.values.file.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -585,15 +602,15 @@ const UniformityForm = ({
|
|||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type='file'
|
type='file'
|
||||||
id='file-upload-input'
|
id='file-upload-input'
|
||||||
name='files'
|
name='file'
|
||||||
accept='application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv'
|
accept='application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv'
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
className='hidden'
|
className='hidden'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{formik.touched.files && formik.errors.files && (
|
{formik.touched.file && formik.errors.file && (
|
||||||
<p className='w-full text-sm text-red-500 mt-2'>
|
<p className='w-full text-sm text-red-500 mt-2'>
|
||||||
{formik.errors.files as string}
|
{formik.errors.file as string}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ const UniformityPreviewForm = () => {
|
|||||||
const tableData = useMemo(() => {
|
const tableData = useMemo(() => {
|
||||||
if (!verifyUniformityResult) return [];
|
if (!verifyUniformityResult) return [];
|
||||||
|
|
||||||
return verifyUniformityResult.body_weights.map((weight, index) => ({
|
return verifyUniformityResult.uniformity_details.map((detail, index) => ({
|
||||||
id: `weight-${index}`,
|
id: `weight-${index}`,
|
||||||
number: index + 1,
|
number: index + 1,
|
||||||
weight: weight,
|
weight: detail.weight,
|
||||||
}));
|
}));
|
||||||
}, [verifyUniformityResult]);
|
}, [verifyUniformityResult]);
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const UniformityResultForm = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (!uniformityFormData || !uniformityFormData.files) {
|
if (!uniformityFormData || !uniformityFormData.file) {
|
||||||
toast.error('Form data is missing. Please try again.');
|
toast.error('Form data is missing. Please try again.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -100,8 +100,9 @@ const UniformityResultForm = () => {
|
|||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
date: uniformityFormData.date,
|
date: uniformityFormData.date,
|
||||||
|
week: uniformityFormData.week,
|
||||||
project_flock_kandang_id: uniformityFormData.project_flock_kandang_id,
|
project_flock_kandang_id: uniformityFormData.project_flock_kandang_id,
|
||||||
files: uniformityFormData.files,
|
file: uniformityFormData.file,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await UniformityApi.createUniformity(payload);
|
const res = await UniformityApi.createUniformity(payload);
|
||||||
@@ -206,10 +207,11 @@ const UniformityResultForm = () => {
|
|||||||
const tableData = useMemo(() => {
|
const tableData = useMemo(() => {
|
||||||
if (!verifyUniformityResult) return [];
|
if (!verifyUniformityResult) return [];
|
||||||
|
|
||||||
return verifyUniformityResult.body_weights.map((weight, index) => ({
|
return verifyUniformityResult.uniformity_details.map((detail, index) => ({
|
||||||
id: `weight-${index}`,
|
id: `body-weight-${index + 1}`,
|
||||||
number: index + 1,
|
number: index + 1,
|
||||||
weight: weight,
|
weight: detail.weight,
|
||||||
|
status: detail.range.toLowerCase() as 'ideal' | 'outside',
|
||||||
}));
|
}));
|
||||||
}, [verifyUniformityResult]);
|
}, [verifyUniformityResult]);
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ export class UniformityApiService extends BaseApiService<
|
|||||||
): Promise<BaseApiResponse<Uniformity> | undefined> {
|
): Promise<BaseApiResponse<Uniformity> | undefined> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('date', payload.date);
|
formData.append('date', payload.date);
|
||||||
|
formData.append('week', payload.week.toString());
|
||||||
formData.append(
|
formData.append(
|
||||||
'project_flock_kandang_id',
|
'project_flock_kandang_id',
|
||||||
payload.project_flock_kandang_id.toString()
|
payload.project_flock_kandang_id.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (payload.files) {
|
if (payload.file) {
|
||||||
formData.append('file', payload.files);
|
formData.append('file', payload.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.create(formData as unknown as CreateUniformityPayload);
|
return await this.create(formData as unknown as CreateUniformityPayload);
|
||||||
@@ -41,13 +42,15 @@ export class UniformityApiService extends BaseApiService<
|
|||||||
payload: VerifyUniformityPayload
|
payload: VerifyUniformityPayload
|
||||||
): Promise<BaseApiResponse<VerifyUniformityResponse> | undefined> {
|
): Promise<BaseApiResponse<VerifyUniformityResponse> | undefined> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
formData.append('date', payload.date);
|
||||||
|
formData.append('week', payload.week.toString());
|
||||||
formData.append(
|
formData.append(
|
||||||
'project_flock_kandang_id',
|
'project_flock_kandang_id',
|
||||||
payload.project_flock_kandang_id.toString()
|
payload.project_flock_kandang_id.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (payload.files) {
|
if (payload.file) {
|
||||||
formData.append('file', payload.files);
|
formData.append('file', payload.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.customRequest<BaseApiResponse<VerifyUniformityResponse>>(
|
return await this.customRequest<BaseApiResponse<VerifyUniformityResponse>>(
|
||||||
@@ -61,5 +64,5 @@ export class UniformityApiService extends BaseApiService<
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const UniformityApi = new UniformityApiService(
|
export const UniformityApi = new UniformityApiService(
|
||||||
'http://localhost:4010/api/uniformity'
|
'http://localhost:4010/api/production/uniformities'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ export type UniformityStep = 'preview' | 'result';
|
|||||||
|
|
||||||
export type UniformityFormData = {
|
export type UniformityFormData = {
|
||||||
date: string;
|
date: string;
|
||||||
|
week: number;
|
||||||
project_flock_kandang_id: number;
|
project_flock_kandang_id: number;
|
||||||
files: File | null;
|
file: File | null;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+68
-9
@@ -1,29 +1,88 @@
|
|||||||
|
import { BaseMetadata } from '@/types/api/api-general';
|
||||||
import { Location } from '@/types/api/location/location';
|
import { Location } from '@/types/api/location/location';
|
||||||
|
import { ProjectFlock } from '@/types/api/project-flock/project-flock';
|
||||||
import { Kandang } from '@/types/api/kandang/kandang';
|
import { Kandang } from '@/types/api/kandang/kandang';
|
||||||
import { BaseMetadata } from '@/types/common/base-metadata';
|
import { BaseApproval } from '@/types/api/approval/approval';
|
||||||
|
|
||||||
|
// ==================== GET ALL RESPONSE ====================
|
||||||
export type Uniformity = BaseMetadata & {
|
export type Uniformity = BaseMetadata & {
|
||||||
id: number;
|
id: number;
|
||||||
location: Location;
|
|
||||||
project_flock_kandang_id: number;
|
project_flock_kandang_id: number;
|
||||||
|
location: Location;
|
||||||
|
project_flock: ProjectFlock;
|
||||||
|
location_name: string;
|
||||||
|
flock_name: string;
|
||||||
kandang: Kandang;
|
kandang: Kandang;
|
||||||
|
kandang_name: string;
|
||||||
|
applied_at: string;
|
||||||
week: number;
|
week: number;
|
||||||
status: 'CREATED' | 'APPROVED' | 'REJECTED';
|
status: 'CREATED' | 'APPROVED' | 'REJECTED';
|
||||||
uniformity: number;
|
uniformity: number;
|
||||||
date?: string;
|
cv: number;
|
||||||
|
chick_qty_of_weight: number;
|
||||||
|
uniform_qty: number;
|
||||||
|
mean_up: number;
|
||||||
|
mean_down: number;
|
||||||
|
created_at: string;
|
||||||
|
created_by: number;
|
||||||
|
latest_approval?: BaseApproval;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== GET ONE RESPONSE ====================
|
||||||
|
export type UniformityInfoUmum = {
|
||||||
|
tanggal: string;
|
||||||
|
lokasi_farm: string;
|
||||||
|
project_flock: string;
|
||||||
|
kandang: string;
|
||||||
|
file_name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UniformitySampling = {
|
||||||
|
chick_qty_of_weight: number;
|
||||||
|
mean_weight: number;
|
||||||
|
mean_down: number;
|
||||||
|
mean_up: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UniformityResult = {
|
||||||
|
uniform_qty: number;
|
||||||
|
outside_qty: number;
|
||||||
|
uniformity: number;
|
||||||
|
cv: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UniformityDetailItem = {
|
||||||
|
id: number;
|
||||||
|
weight: number;
|
||||||
|
range: 'Ideal' | 'Outside';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UniformityDetail = BaseMetadata & {
|
||||||
|
id: number;
|
||||||
|
info_umum: UniformityInfoUmum;
|
||||||
|
sampling: UniformitySampling;
|
||||||
|
result: UniformityResult;
|
||||||
|
uniformity_details: UniformityDetailItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== VERIFY RESPONSE ====================
|
||||||
|
export type VerifyUniformityResponse = {
|
||||||
|
sampling: UniformitySampling;
|
||||||
|
result: UniformityResult;
|
||||||
|
uniformity_details: UniformityDetailItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== PAYLOADS ====================
|
||||||
export type CreateUniformityPayload = {
|
export type CreateUniformityPayload = {
|
||||||
date: string;
|
date: string;
|
||||||
project_flock_kandang_id: number;
|
project_flock_kandang_id: number;
|
||||||
files: File;
|
file: File;
|
||||||
|
week: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VerifyUniformityPayload = {
|
export type VerifyUniformityPayload = {
|
||||||
|
date: string;
|
||||||
project_flock_kandang_id: number;
|
project_flock_kandang_id: number;
|
||||||
files: File;
|
file: File;
|
||||||
};
|
week: number;
|
||||||
|
|
||||||
export type VerifyUniformityResponse = {
|
|
||||||
body_weights: number[];
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user