mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE): change project flock form, detail and chickin view using drawer
This commit is contained in:
@@ -12,8 +12,6 @@ const DetailInventoryAdjustment = () => {
|
||||
|
||||
// Ambil data dari router state
|
||||
useEffect(() => {
|
||||
console.log('Router State');
|
||||
console.log(window.history.state);
|
||||
const state = window.history.state?.usr as
|
||||
| { inventoryAdjustment?: InventoryAdjustment }
|
||||
| undefined;
|
||||
@@ -26,9 +24,6 @@ const DetailInventoryAdjustment = () => {
|
||||
|
||||
const finalData = inventoryAdjustment;
|
||||
|
||||
console.log('Final Data');
|
||||
console.log(finalData);
|
||||
|
||||
if (!finalData) {
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
|
||||
import React, { useImperativeHandle } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const AddProjectFlock = () => {
|
||||
// useImperativeHandle(ref, () => ({
|
||||
// validate() {
|
||||
// toast.success('Validating');
|
||||
// return false;
|
||||
// },
|
||||
// }));
|
||||
return (
|
||||
<section className='w-full p-4 flex flex-row justify-center'>
|
||||
<ProjectFlockForm formType='add' />
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import Drawer from '@/components/Drawer';
|
||||
import React, { ReactNode } from 'react';
|
||||
import ProjectFlockTable from '@/components/pages/production/project-flock/ProjectFlockTable';
|
||||
|
||||
export default function ProjectFlockLayout({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
const isAdd = pathname.endsWith('/add');
|
||||
const isEdit = pathname.includes('/detail/edit');
|
||||
const isDetail = pathname.includes('/detail');
|
||||
const isChickin = pathname.includes('/chickin/add/kandang');
|
||||
|
||||
const isOpen = isAdd || isEdit || isDetail || isChickin;
|
||||
|
||||
// const childRef = useRef<ProjectFlockFormRef>(null);
|
||||
|
||||
const handleBackdropClick = () => {
|
||||
// const isValid = childRef.current?.validate(); // 🔥 trigger validation child
|
||||
|
||||
// if (!isValid) {
|
||||
// toast.error('Form belum valid, Drawer tidak bisa close');
|
||||
// return;
|
||||
// }
|
||||
router.push('/production/project-flock');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* List page always rendered */}
|
||||
<div>
|
||||
<ProjectFlockTable />
|
||||
</div>
|
||||
|
||||
{/* Render Drawer only on /add */}
|
||||
<Drawer
|
||||
open={isOpen}
|
||||
setOpen={(v) => {
|
||||
if (!v) router.push('/production/project-flock');
|
||||
}}
|
||||
closeOnBackdropClick={false}
|
||||
onBackdropClick={handleBackdropClick}
|
||||
variant='right'
|
||||
sidebarContent={isOpen && <div className='p-4'>{children}</div>}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import ProjectFlockTable from '@/components/pages/production/project-flock/Proje
|
||||
|
||||
const ProjectFlock = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<section className='size-full p-4'>
|
||||
<ProjectFlockTable />
|
||||
</section>
|
||||
);
|
||||
|
||||
+101
-6
@@ -10,28 +10,102 @@ interface DrawerProps {
|
||||
open: boolean;
|
||||
setOpen: (newOpenState: boolean) => void;
|
||||
openOnLarge?: boolean;
|
||||
variant?: 'sidebar' | 'left' | 'right';
|
||||
zIndex?: string;
|
||||
className?: DrawerClassName;
|
||||
onBackdropClick?: () => void;
|
||||
closeOnBackdropClick?: boolean;
|
||||
}
|
||||
|
||||
type DrawerClassName = {
|
||||
drawer?: string;
|
||||
drawerContent?: string;
|
||||
drawerSide?: string;
|
||||
drawerOverlay?: string;
|
||||
drawerSidebarContent?: string;
|
||||
};
|
||||
|
||||
const Drawer = ({
|
||||
children,
|
||||
sidebarContent,
|
||||
open,
|
||||
setOpen,
|
||||
openOnLarge,
|
||||
variant = 'sidebar',
|
||||
zIndex = '20',
|
||||
className,
|
||||
onBackdropClick,
|
||||
closeOnBackdropClick = true,
|
||||
}: DrawerProps) => {
|
||||
const getDrawerClassNames = (): DrawerClassName => {
|
||||
const baseClassNames = {
|
||||
drawer: 'drawer',
|
||||
drawerContent: 'drawer-content',
|
||||
drawerSide: 'drawer-side',
|
||||
drawerOverlay: 'drawer-overlay',
|
||||
drawerSidebarContent: 'min-h-full bg-base-100',
|
||||
};
|
||||
|
||||
if (variant === 'sidebar') {
|
||||
return {
|
||||
...baseClassNames,
|
||||
drawerSidebarContent: cn(
|
||||
baseClassNames.drawerSidebarContent,
|
||||
'w-full max-w-[300px] lg:w-[300px]'
|
||||
),
|
||||
};
|
||||
} else if (variant === 'right') {
|
||||
return {
|
||||
...baseClassNames,
|
||||
drawer: cn(baseClassNames.drawer, 'drawer-end'),
|
||||
drawerSide: cn(
|
||||
baseClassNames.drawerSide,
|
||||
'border-l border-solid border-gray-200 drawer-side w-screen top-0 right-0 fixed z-21'
|
||||
),
|
||||
drawerSidebarContent: cn(
|
||||
baseClassNames.drawerSidebarContent,
|
||||
'w-full min-w-120 sm:w-fit'
|
||||
),
|
||||
};
|
||||
} else if (variant === 'left') {
|
||||
return {
|
||||
...baseClassNames,
|
||||
drawerSide: cn(
|
||||
baseClassNames.drawerSide,
|
||||
'border-l border-solid border-gray-200 drawer-side w-screen top-0 right-0 fixed z-21'
|
||||
),
|
||||
drawerSidebarContent: cn(
|
||||
baseClassNames.drawerSidebarContent,
|
||||
'w-full min-w-120 sm:w-fit'
|
||||
),
|
||||
};
|
||||
}
|
||||
return baseClassNames; // Fallback for default or unknown variant
|
||||
};
|
||||
|
||||
const varianClassName = getDrawerClassNames();
|
||||
|
||||
const toggleDrawer = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
const closeDrawer = () => {
|
||||
if (closeOnBackdropClick) {
|
||||
setOpen(false);
|
||||
}
|
||||
onBackdropClick && onBackdropClick();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('drawer', {
|
||||
className={cn(
|
||||
'drawer',
|
||||
{
|
||||
'lg:drawer-open': openOnLarge,
|
||||
})}
|
||||
},
|
||||
varianClassName?.drawer,
|
||||
className?.drawer
|
||||
)}
|
||||
>
|
||||
<input
|
||||
type='checkbox'
|
||||
@@ -40,16 +114,37 @@ const Drawer = ({
|
||||
className='drawer-toggle'
|
||||
/>
|
||||
|
||||
<div className='drawer-content'>{children}</div>
|
||||
{/* Drawer Content */}
|
||||
<div
|
||||
className={cn(varianClassName?.drawerContent, className?.drawerContent)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<div className='drawer-side border-r border-solid border-gray-200 z-20'>
|
||||
{/* Drawer Side */}
|
||||
<div
|
||||
className={cn(
|
||||
varianClassName?.drawerSide,
|
||||
className?.drawerSide,
|
||||
zIndex
|
||||
)}
|
||||
>
|
||||
<label
|
||||
aria-label='close sidebar'
|
||||
className='drawer-overlay'
|
||||
className={cn(
|
||||
varianClassName?.drawerOverlay,
|
||||
className?.drawerOverlay
|
||||
)}
|
||||
onClick={closeDrawer}
|
||||
/>
|
||||
|
||||
<div className='min-h-full w-full max-w-[300px] lg:w-[300px] bg-base-100'>
|
||||
{/* Sidebar Content */}
|
||||
<div
|
||||
className={cn(
|
||||
varianClassName?.drawerSidebarContent,
|
||||
className?.drawerContent
|
||||
)}
|
||||
>
|
||||
{sidebarContent}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -156,8 +156,6 @@ export const recalculate = (
|
||||
field: string,
|
||||
values: ProductCalculationFields
|
||||
) => {
|
||||
console.log('Values');
|
||||
console.log(values);
|
||||
const { qty, unit_price, total_price, avg_weight, total_weight } = values;
|
||||
const result: Partial<ProductCalculationFields> = {};
|
||||
if (field == 'unit_price' || field == 'total_price' || field == 'qty') {
|
||||
@@ -174,8 +172,6 @@ export const recalculate = (
|
||||
result.avg_weight = Number(total_weight) / Number(qty);
|
||||
}
|
||||
}
|
||||
console.log('Result');
|
||||
console.log(result);
|
||||
return result;
|
||||
};
|
||||
export const getSubmitField = (values: ProductCalculationFields) => {
|
||||
@@ -327,8 +323,6 @@ const MarketingForm = ({
|
||||
})
|
||||
.filter((item) => Boolean(item)),
|
||||
} as UpdateDeliveryOrderPayload);
|
||||
console.log('PAYLOAD');
|
||||
console.log(payload);
|
||||
switch (formType) {
|
||||
case 'add':
|
||||
await createMarketingHandler(payload as CreateSalesOrderPayload);
|
||||
@@ -352,7 +346,6 @@ const MarketingForm = ({
|
||||
// ================== FORM REPEATER HANDLER ==================
|
||||
const createMarketingHandler = async (values: CreateSalesOrderPayload) => {
|
||||
setIsLoading(true);
|
||||
console.log(values);
|
||||
const createMarketingRes = await SalesOrderApi.create(values);
|
||||
if (isResponseSuccess(createMarketingRes)) {
|
||||
toast.success(createMarketingRes?.message as string);
|
||||
@@ -365,7 +358,6 @@ const MarketingForm = ({
|
||||
};
|
||||
const updateMarketingHandler = async (values: UpdateSalesOrderPayload) => {
|
||||
setIsLoading(true);
|
||||
console.log(values);
|
||||
const updateMarketingRes = await SalesOrderApi.update(
|
||||
initialValues?.id as number,
|
||||
values
|
||||
@@ -381,10 +373,8 @@ const MarketingForm = ({
|
||||
};
|
||||
const createDeliveryHandler = async (values: CreateDeliveryOrderPayload) => {
|
||||
setIsLoading(true);
|
||||
console.log(initialValues?.id);
|
||||
const createDeliveryRes = await DeliveryOrderApi.create(values);
|
||||
if (isResponseSuccess(createDeliveryRes)) {
|
||||
console.log(createDeliveryRes);
|
||||
toast.success(createDeliveryRes?.message as string);
|
||||
setDeliveryOrderValues(
|
||||
createDeliveryRes.data?.delivery_order?.flatMap((delivery) =>
|
||||
@@ -397,20 +387,17 @@ const MarketingForm = ({
|
||||
router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
|
||||
}
|
||||
if (isResponseError(createDeliveryRes)) {
|
||||
console.log(createDeliveryRes);
|
||||
toast.error(createDeliveryRes?.message as string);
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
const updateDeliveryHandler = async (values: UpdateDeliveryOrderPayload) => {
|
||||
setIsLoading(true);
|
||||
console.log(initialValues?.id);
|
||||
const updateDeliveryRes = await DeliveryOrderApi.update(
|
||||
initialValues?.id as number,
|
||||
values
|
||||
);
|
||||
if (isResponseSuccess(updateDeliveryRes)) {
|
||||
console.log(updateDeliveryRes);
|
||||
toast.success(updateDeliveryRes?.message as string);
|
||||
setDeliveryOrderValues(
|
||||
mergeSOwithDO(
|
||||
@@ -426,7 +413,6 @@ const MarketingForm = ({
|
||||
router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
|
||||
}
|
||||
if (isResponseError(updateDeliveryRes)) {
|
||||
console.log(updateDeliveryRes);
|
||||
toast.error(updateDeliveryRes?.message as string);
|
||||
}
|
||||
setIsLoading(false);
|
||||
@@ -435,16 +421,13 @@ const MarketingForm = ({
|
||||
// ================== MARKETING HANDLER ==================
|
||||
const deleteMarketingHandler = async () => {
|
||||
setIsLoading(true);
|
||||
console.log(initialValues?.id);
|
||||
const deleteMarketingRes = await MarketingApi.delete(
|
||||
initialValues?.id as number
|
||||
);
|
||||
if (isResponseSuccess(deleteMarketingRes)) {
|
||||
console.log(deleteMarketingRes);
|
||||
toast.success(deleteMarketingRes?.message as string);
|
||||
}
|
||||
if (isResponseError(deleteMarketingRes)) {
|
||||
console.log(deleteMarketingRes);
|
||||
toast.error(deleteMarketingRes?.message as string);
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -306,7 +306,6 @@ const SupplierForm = ({
|
||||
label='Hatchery'
|
||||
value={hatcheryOptionsValues}
|
||||
onChange={(val) => {
|
||||
console.log(val); // pastikan val = array of { value, label }
|
||||
setHatcheryOptionValues(val as OptionType[]);
|
||||
}}
|
||||
isError={
|
||||
|
||||
@@ -16,15 +16,11 @@ import { cn } from '@/lib/helper';
|
||||
import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import {
|
||||
ProjectFlockApprovalPayload,
|
||||
ProjectFlock,
|
||||
} from '@/types/api/production/project-flock';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { CellContext, SortingState } from '@tanstack/react-table';
|
||||
import { ChangeEventHandler, useState } from 'react';
|
||||
import { ChangeEventHandler, useRef, useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import useSWR from 'swr';
|
||||
|
||||
@@ -266,10 +262,10 @@ const ProjectFlockTable = () => {
|
||||
<div className='w-full flex flex-col justify-between items-end gap-2'>
|
||||
<div className='flex flex-col sm:flex-row gap-3 w-full'>
|
||||
<Button
|
||||
href='/production/project-flock/add'
|
||||
variant='outline'
|
||||
color='primary'
|
||||
className='w-full sm:w-fit'
|
||||
href='/production/project-flock/add'
|
||||
>
|
||||
<Icon icon='ic:round-plus' width={24} height={24} />
|
||||
Tambah
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
UpdateProjectFlockFormSchema,
|
||||
} from '@/components/pages/production/project-flock/form/ProjectFlockForm.schema';
|
||||
import {
|
||||
ProjectFlockApprovalPayload,
|
||||
CreateProjectFlockPayload,
|
||||
ProjectFlock,
|
||||
} from '@/types/api/production/project-flock';
|
||||
@@ -43,6 +42,7 @@ import ApprovalSteps, {
|
||||
import { PROJECT_FLOCK_APPROVAL_LINE } from '@/config/approval-line';
|
||||
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
||||
import NumberInput from '@/components/input/NumberInput';
|
||||
import Card from '@/components/Card';
|
||||
|
||||
interface ProjectFlockFormProps {
|
||||
formType?: 'add' | 'edit' | 'detail';
|
||||
@@ -259,6 +259,7 @@ const ProjectFlockForm = ({
|
||||
|
||||
if (isResponseSuccess(createProjectFlockRes)) {
|
||||
toast.success(createProjectFlockRes?.message as string);
|
||||
handleReset();
|
||||
router.push('/production/project-flock');
|
||||
}
|
||||
if (isResponseError(createProjectFlockRes)) {
|
||||
@@ -276,6 +277,7 @@ const ProjectFlockForm = ({
|
||||
|
||||
if (isResponseSuccess(updateProjectFlockRes)) {
|
||||
toast.success(updateProjectFlockRes?.message as string);
|
||||
handleReset();
|
||||
router.push('/production/project-flock');
|
||||
}
|
||||
if (isResponseError(updateProjectFlockRes)) {
|
||||
@@ -283,6 +285,15 @@ const ProjectFlockForm = ({
|
||||
toast.error(updateProjectFlockRes?.message as string);
|
||||
}
|
||||
};
|
||||
const handleReset = () => {
|
||||
formik.resetForm();
|
||||
setSelectedArea('');
|
||||
setSelectedLocation('');
|
||||
setDisabledLocation(true);
|
||||
setOpenSelectKandangs(false);
|
||||
setOptionsKandang([]);
|
||||
formikSetValues(formikInitialValues);
|
||||
};
|
||||
|
||||
// Formik InitialValue
|
||||
const formikInitialValues = useMemo<ProjectFlockFormValues>(() => {
|
||||
@@ -557,6 +568,16 @@ const ProjectFlockForm = ({
|
||||
const inputPeriod =
|
||||
(initialValues?.period ?? selectedPeriod == 0) ? 1 : selectedPeriod;
|
||||
|
||||
// expose method validate() ke parent
|
||||
// TODO: Buat Store untuk kirim props formik.isValid ke parent (layout.tsx)
|
||||
// useImperativeHandle(ref, () => ({
|
||||
// validate() {
|
||||
// formik.validateForm();
|
||||
// const isValid = formik.isValid;
|
||||
// return isValid;
|
||||
// },
|
||||
// }));
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className='w-full'>
|
||||
@@ -653,9 +674,13 @@ const ProjectFlockForm = ({
|
||||
onSubmit={formik.handleSubmit}
|
||||
onReset={formik.handleReset}
|
||||
>
|
||||
<div className='card bg-base-100 shadow w-full mb-6'>
|
||||
<div className='card-body'>
|
||||
<div className='card-title mb-4'>Informasi Umum</div>
|
||||
<Card
|
||||
title='Informasi Umum'
|
||||
variant='bordered'
|
||||
className={{
|
||||
wrapper: 'w-full mb-4',
|
||||
}}
|
||||
>
|
||||
<div className='grid sm:grid-cols-2 gap-4'>
|
||||
<SelectInput
|
||||
required
|
||||
@@ -695,8 +720,7 @@ const ProjectFlockForm = ({
|
||||
options={optionsFlock}
|
||||
isLoading={isLoadingFlocks}
|
||||
isError={
|
||||
formik.touched.flock_name &&
|
||||
Boolean(formik.errors.flock_name)
|
||||
formik.touched.flock_name && Boolean(formik.errors.flock_name)
|
||||
}
|
||||
errorMessage={formik.errors.flock_name as string}
|
||||
isClearable
|
||||
@@ -730,9 +754,7 @@ const ProjectFlockForm = ({
|
||||
}}
|
||||
options={optionsFcr}
|
||||
isLoading={isLoadingFcrs}
|
||||
isError={
|
||||
formik.touched.fcr_id && Boolean(formik.errors.fcr_id)
|
||||
}
|
||||
isError={formik.touched.fcr_id && Boolean(formik.errors.fcr_id)}
|
||||
errorMessage={formik.errors.fcr_id as string}
|
||||
isClearable
|
||||
isDisabled={formType === 'detail'}
|
||||
@@ -759,34 +781,16 @@ const ProjectFlockForm = ({
|
||||
value={selectedLocation ? inputPeriod : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='card bg-base-100 shadow w-full'>
|
||||
<div className='card-body'>
|
||||
<Collapse
|
||||
title={
|
||||
<div className='card-actions justify-between w-full'>
|
||||
<div className='card-title'>Pilih Kandang</div>
|
||||
<Button
|
||||
variant='link'
|
||||
className={`text-primary rotate-${
|
||||
openSelectKandangs ? '180' : '0'
|
||||
} transition-transform hover:text-inherit me-3`}
|
||||
</Card>
|
||||
<Card
|
||||
collapsible
|
||||
title='Pilih Kandang'
|
||||
variant='bordered'
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon='material-symbols:keyboard-arrow-down'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
className='sm:w-full'
|
||||
titleClassName='w-full p-0!'
|
||||
onOpenChange={setOpenSelectKandangs}
|
||||
open={openSelectKandangs}
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<div className='overflow-x-auto duration-300 ease-in-out'>
|
||||
{isLoadingKandang && (
|
||||
<span className='loading loading-dots loading-xl'></span>
|
||||
)}
|
||||
@@ -802,9 +806,7 @@ const ProjectFlockForm = ({
|
||||
initialValues={initialValues}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className='flex flex-row justify-center gap-2 flex-wrap my-6'>
|
||||
{formType !== 'detail' && (
|
||||
@@ -912,4 +914,6 @@ const ProjectFlockForm = ({
|
||||
);
|
||||
};
|
||||
|
||||
ProjectFlockForm.displayName = 'ProjectFlockForm';
|
||||
|
||||
export default ProjectFlockForm;
|
||||
|
||||
@@ -144,8 +144,6 @@ const ProjectFlockKandangTable = ({
|
||||
accessorFn: (row) => row.location?.name,
|
||||
header: 'Periode',
|
||||
cell: (props) => {
|
||||
console.log('listPeriods');
|
||||
console.log(listPeriods);
|
||||
const period =
|
||||
listPeriods.length > 0
|
||||
? listPeriods.find((p) => p.id == props.row.original.id)
|
||||
|
||||
@@ -793,8 +793,6 @@ export class ExpenseApiService extends BaseApiService<
|
||||
sentPayload.set(pair[0], pair[1]);
|
||||
}
|
||||
|
||||
console.log({ sentPayload });
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
@@ -815,8 +813,6 @@ export class ExpenseApiService extends BaseApiService<
|
||||
sentPayload.set(pair[0], pair[1]);
|
||||
}
|
||||
|
||||
console.log({ sentPayload });
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
|
||||
Reference in New Issue
Block a user