diff --git a/src/app/production/uniformity/add/page.tsx b/src/app/production/uniformity/add/page.tsx new file mode 100644 index 00000000..136aab5d --- /dev/null +++ b/src/app/production/uniformity/add/page.tsx @@ -0,0 +1,7 @@ +import UniformityForm from '@/components/pages/production/uniformity/form/UniformityForm'; + +const AddUniformity = () => { + return ; +}; + +export default AddUniformity; diff --git a/src/app/production/uniformity/detail/page.tsx b/src/app/production/uniformity/detail/page.tsx new file mode 100644 index 00000000..bf1458ef --- /dev/null +++ b/src/app/production/uniformity/detail/page.tsx @@ -0,0 +1,49 @@ +'use client'; + +import UniformityDetail from '@/components/pages/production/uniformity/detail/UniformityDetail'; +import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; +import { UniformityApi } from '@/services/api/uniformity'; +import { useRouter, useSearchParams } from 'next/navigation'; +import useSWR from 'swr'; + +const UniformityDetailPage = () => { + const router = useRouter(); + const searchParams = useSearchParams(); + + const uniformityId = searchParams.get('uniformityId'); + + const { data: uniformity, isLoading: isLoadingUniformity } = useSWR( + uniformityId, + (id: string) => UniformityApi.getUniformityDetail(parseInt(id)) + ); + + if (!uniformityId) { + router.back(); + + return ( +
+ +
+ ); + } + + if (!isLoadingUniformity && (!uniformity || isResponseError(uniformity))) { + router.replace('/404'); + return; + } + + return ( +
+ {isLoadingUniformity && ( +
+ +
+ )} + {isResponseSuccess(uniformity) && ( + + )} +
+ ); +}; + +export default UniformityDetailPage; diff --git a/src/app/production/uniformity/layout.tsx b/src/app/production/uniformity/layout.tsx new file mode 100644 index 00000000..511aa0a1 --- /dev/null +++ b/src/app/production/uniformity/layout.tsx @@ -0,0 +1,10 @@ +import { ReactNode } from 'react'; +import UniformityPageWrapper from '@/components/pages/production/uniformity/UniformityPageWrapper'; + +export default function UniformityLayout({ + children, +}: { + children: ReactNode; +}) { + return {children}; +} diff --git a/src/app/production/uniformity/page.tsx b/src/app/production/uniformity/page.tsx new file mode 100644 index 00000000..841a7507 --- /dev/null +++ b/src/app/production/uniformity/page.tsx @@ -0,0 +1,7 @@ +import UniformityTable from '@/components/pages/production/uniformity/UniformityTable'; + +const Uniformity = () => { + return ; +}; + +export default Uniformity; diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 5dc5022d..821aae42 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -3,29 +3,25 @@ import { HTMLAttributes, ReactNode } from 'react'; import { cn } from '@/lib/helper'; +import type { Color, Variant, Size } from '@/types/theme'; export interface BadgeProps extends Omit, 'className'> { children?: ReactNode; className?: { badge?: string; + status?: string; }; - variant?: 'default' | 'outline' | 'ghost' | 'soft' | 'dash'; - color?: - | 'neutral' - | 'primary' - | 'secondary' - | 'accent' - | 'info' - | 'success' - | 'warning' - | 'error'; - size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + statusIndicator?: boolean; + variant?: Variant; + color?: Color; + size?: Size; } const Badge = ({ children, className, + statusIndicator = false, variant = 'default', color, size = 'md', @@ -34,7 +30,7 @@ const Badge = ({ const getBadgeClasses = () => { const baseClasses = 'badge'; - const variantClasses = { + const variantClasses: Record = { default: '', outline: 'badge-outline', ghost: 'badge-ghost', @@ -42,7 +38,7 @@ const Badge = ({ dash: 'badge-dash', }; - const colorClasses = { + const colorClasses: Record = { neutral: 'badge-neutral', primary: 'badge-primary', secondary: 'badge-secondary', @@ -51,9 +47,10 @@ const Badge = ({ success: 'badge-success', warning: 'badge-warning', error: 'badge-error', + none: '', }; - const sizeClasses = { + const sizeClasses: Record = { xs: 'badge-xs', sm: 'badge-sm', md: 'badge-md', @@ -70,8 +67,31 @@ const Badge = ({ ); }; + const getStatusClasses = () => { + if (!statusIndicator) return ''; + + const statusIndicatorClasses: Record = { + neutral: 'bg-neutral', + primary: 'bg-primary', + secondary: 'bg-secondary', + accent: 'bg-accent', + info: 'bg-info', + success: 'bg-success', + warning: 'bg-warning', + error: 'bg-error', + none: '', + }; + + return cn( + 'w-2.5 h-2.5 rounded-full', + color && statusIndicatorClasses[color], + className?.status + ); + }; + return ( + {statusIndicator && } {children} ); diff --git a/src/components/Drawer.tsx b/src/components/Drawer.tsx index 4cb59cdb..7b5e2374 100644 --- a/src/components/Drawer.tsx +++ b/src/components/Drawer.tsx @@ -15,6 +15,8 @@ interface DrawerProps { className?: DrawerClassName; onBackdropClick?: () => void; closeOnBackdropClick?: boolean; + expandedContent?: ReactNode; + expandedWidth?: string; } type DrawerClassName = { @@ -36,6 +38,8 @@ const Drawer = ({ className, onBackdropClick, closeOnBackdropClick = true, + expandedContent, + expandedWidth = 'w-[400px]', }: DrawerProps) => { const getDrawerClassNames = (): DrawerClassName => { const baseClassNames = { @@ -46,12 +50,21 @@ const Drawer = ({ drawerSidebarContent: 'min-h-full bg-base-100', }; + const getSidebarWidth = () => { + if (variant === 'sidebar') { + return expandedContent + ? 'w-full lg:min-w-[600px] lg:max-w-[600px]' + : 'w-full max-w-[300px] lg:w-[300px]'; + } + return 'w-full sm:min-w-120 sm:w-fit'; + }; + if (variant === 'sidebar') { return { ...baseClassNames, drawerSidebarContent: cn( baseClassNames.drawerSidebarContent, - 'w-full max-w-[300px] lg:w-[300px]' + getSidebarWidth() ), }; } else if (variant === 'right') { @@ -60,11 +73,11 @@ const Drawer = ({ 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' + 'border-l border-solid border-gray-200 sm:drawer-side w-screen top-0 right-0 fixed z-21' ), drawerSidebarContent: cn( baseClassNames.drawerSidebarContent, - 'w-full sm:min-w-120 sm:w-fit' + getSidebarWidth() ), }; } else if (variant === 'left') { @@ -76,7 +89,7 @@ const Drawer = ({ ), drawerSidebarContent: cn( baseClassNames.drawerSidebarContent, - 'w-full sm:min-w-120 sm:w-fit' + getSidebarWidth() ), }; } @@ -138,14 +151,37 @@ const Drawer = ({ onClick={closeDrawer} /> - {/* Sidebar Content */} + {/* Sidebar Content - Full height container */}
- {sidebarContent} + {/* Primary Sidebar Content */} +
+ {sidebarContent} +
+ + {/* Expanded Drawer (Right side, side-by-side) */} + {expandedContent && ( +
+
+ {expandedContent} +
+
+ )}
diff --git a/src/components/modal/ConfirmationModal.tsx b/src/components/modal/ConfirmationModal.tsx index 00b63c86..9cf17008 100644 --- a/src/components/modal/ConfirmationModal.tsx +++ b/src/components/modal/ConfirmationModal.tsx @@ -8,10 +8,13 @@ import Button, { ButtonProps } from '@/components/Button'; import { cn } from '@/lib/helper'; +export type IconPosition = 'left' | 'center' | 'right'; + export interface ConfirmationModalProps { ref: RefObject; type?: 'info' | 'success' | 'error'; text?: string; + subtitleText?: string; closeOnBackdrop?: boolean; primaryButton?: ButtonProps & { text?: string; @@ -24,17 +27,84 @@ export interface ConfirmationModalProps { modalBox?: string; }; children?: React.ReactNode; + iconSize?: number; + iconPosition?: IconPosition; } +const iconConfig = { + info: { + icon: 'material-symbols:info-outline-rounded', + iconClassName: 'text-info-content', + bgClassName: 'bg-info', + outerRingClassName: 'bg-info/20', + borderClassName: 'border-info', + }, + success: { + icon: 'heroicons:check', + iconClassName: 'text-white', + bgClassName: 'bg-[#00D390]', + outerRingClassName: 'bg-[#00D3901F]', + borderClassName: 'border-[#CCF7EB]', + }, + error: { + icon: 'solar:danger-triangle-linear', + iconClassName: 'text-error-content', + bgClassName: 'bg-[#f03338]', + outerRingClassName: 'bg-[#f3cdcd]', + borderClassName: 'border-[#fff0ef]', + }, +} as const; + +const ConfirmationModalIcon = ({ + type, + size = 24, +}: { + type: 'info' | 'success' | 'error'; + size?: number; +}) => { + const config = iconConfig[type]; + + return ( +
+
+
+
+ +
+
+
+
+ ); +}; + const ConfirmationModal = ({ ref, type = 'info', text, + subtitleText, closeOnBackdrop, primaryButton, secondaryButton, className, children, + iconSize = 32, + iconPosition = 'center', }: ConfirmationModalProps) => { const [isPrimaryButtonLoading, setIsPrimaryButtonLoading] = useState(false); @@ -55,47 +125,44 @@ const ConfirmationModal = ({ return (
-
- {type === 'info' && ( - - )} + {iconPosition === 'center' ? ( + <> +
+ +
- {type === 'success' && ( - - )} +

+ {text ?? 'Apakah anda yakin ingin melakukan hal ini?'} +

- {type === 'error' && ( - - )} -
+ {subtitleText && ( +

+ {subtitleText} +

+ )} + + ) : ( +
+
+ +
-

- {text ?? 'Apakah anda yakin ingin melakukan hal ini?'} -

+
+

+ {text ?? 'Apakah anda yakin ingin melakukan hal ini?'} +

+ + {subtitleText && ( +

{subtitleText}

+ )} +
+
+ )} {children &&
{children}
} @@ -103,7 +170,7 @@ const ConfirmationModal = ({ {secondaryButton && secondaryButton.text && (
-
+
+
+ { + const files = Array.from(e.target.files || []); + formik.setFieldValue('travel_documents', files); + }} + onBlur={formik.handleBlur} + bottomLabel={ + formik.values.travel_documents && + formik.values.travel_documents.length > 0 + ? `${formik.values.travel_documents.length} file(s) dipilih` + : undefined + } + isError={ + formik.touched.travel_documents && + Boolean(formik.errors.travel_documents) + } + errorMessage={formik.errors.travel_documents as string} + /> +
+ {/* Action buttons */}
diff --git a/src/components/pages/purchase/form/order/PurchaseOrderForm.schema.ts b/src/components/pages/purchase/form/order/PurchaseOrderForm.schema.ts index c7da956d..bb70053f 100644 --- a/src/components/pages/purchase/form/order/PurchaseOrderForm.schema.ts +++ b/src/components/pages/purchase/form/order/PurchaseOrderForm.schema.ts @@ -48,6 +48,7 @@ type PurchaseRequestAcceptApprovalFormSchemaType = { received_qty: number | string; transport_per_item: number | string; }[]; + travel_documents: File[]; }; export type PurchaseStaffApprovalItemSchema = { @@ -379,6 +380,11 @@ export const PurchaseRequestAcceptApprovalFormSchema: Yup.ObjectSchema().required()) + .required('Dokumen surat jalan wajib diupload!') + .min(1, 'Minimal upload 1 dokumen surat jalan!') + .typeError('Dokumen surat jalan wajib diupload!'), }); export const PurchaseRequestAcceptApprovalFormInitialValues: PurchaseRequestAcceptApprovalFormSchemaType = @@ -397,6 +403,7 @@ export const PurchaseRequestAcceptApprovalFormInitialValues: PurchaseRequestAcce transport_per_item: '', }, ], + travel_documents: [], }; export const PurchaseRequestAcceptApprovalFormDefaultValues = ( @@ -428,6 +435,7 @@ export const PurchaseRequestAcceptApprovalFormDefaultValues = ( transport_per_item: '', }, ], + travel_documents: [], }; }; diff --git a/src/components/pages/purchase/order/PurchaseOrderDetail.tsx b/src/components/pages/purchase/order/PurchaseOrderDetail.tsx index de07ee52..a5d6474b 100644 --- a/src/components/pages/purchase/order/PurchaseOrderDetail.tsx +++ b/src/components/pages/purchase/order/PurchaseOrderDetail.tsx @@ -583,7 +583,7 @@ const PurchaseOrderDetail = ({ { header: 'Ekspedisi', accessorKey: 'expedition_name', - cell: (props) => '-', + cell: (props) => props.row.original.expedition_vendor.name || '-', }, { header: 'Transport /Item', diff --git a/src/config/constant.ts b/src/config/constant.ts index c44fa1cb..839bcd83 100644 --- a/src/config/constant.ts +++ b/src/config/constant.ts @@ -29,6 +29,11 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [ text: 'Transfer to Laying', link: '/production/transfer-to-laying', }, + { + text: 'Uniformity', + link: '/production/uniformity', + permission: ['lti.production.uniformity.list'], + }, ], }, { diff --git a/src/config/route-permission.ts b/src/config/route-permission.ts index f1bc7f70..0a17175d 100644 --- a/src/config/route-permission.ts +++ b/src/config/route-permission.ts @@ -45,6 +45,12 @@ export const ROUTE_PERMISSIONS: Record = { 'lti.production.transfer_to_laying.update', ], + // Production - Uniformity + '/production/uniformity/': ['lti.production.uniformity.list'], + '/production/uniformity/add/': ['lti.production.uniformity.create'], + '/production/uniformity/detail/': ['lti.production.uniformity.detail'], + '/production/uniformity/detail/edit/': ['lti.production.uniformity.update'], + // Purchase '/purchase/': ['lti.purchase.list'], '/purchase/add/': ['lti.purchase.create'], diff --git a/src/services/api/purchase.ts b/src/services/api/purchase.ts index d0438e88..38ace6be 100644 --- a/src/services/api/purchase.ts +++ b/src/services/api/purchase.ts @@ -72,11 +72,29 @@ export const PurchaseApi = { purchaseRequestId: number, payload: CreateAcceptApprovalRequestPayload ): Promise | undefined> => { + const formData = new FormData(); + + formData.append('action', payload.action); + + if (payload.notes) { + formData.append('notes', payload.notes); + } + + if (payload.items) { + formData.append('items', JSON.stringify(payload.items)); + } + + if (payload.travel_documents) { + payload.travel_documents.forEach((file) => { + formData.append('travel_documents', file); + }); + } + return await basePurchaseApi.customRequest< BaseApiResponse<{ message: string }> >(`${purchaseRequestId}/receipts`, { method: 'POST', - payload, + payload: formData as unknown as Record, }); }, }, diff --git a/src/services/api/uniformity.ts b/src/services/api/uniformity.ts new file mode 100644 index 00000000..7ed8098e --- /dev/null +++ b/src/services/api/uniformity.ts @@ -0,0 +1,138 @@ +import { BaseApiService } from '@/services/api/base'; +import { BaseApiResponse } from '@/types/api/api-general'; +import { + Uniformity, + UniformityDetail, + VerifyUniformityPayload, + VerifyUniformityResponse, + CreateUniformityPayload, +} from '@/types/api/production/uniformity'; + +export class UniformityApiService extends BaseApiService< + Uniformity, + CreateUniformityPayload, + VerifyUniformityPayload +> { + constructor(basePath: string) { + super(basePath); + } + + async getUniformity( + project_flock_kandang_id?: number, + start_date?: string, + end_date?: string, + page?: number, + limit?: number + ): Promise | undefined> { + return await this.customRequest>('', { + method: 'GET', + params: { + project_flock_kandang_id: project_flock_kandang_id, + start_date: start_date, + end_date: end_date, + page: page, + limit: limit, + }, + }); + } + + async getUniformityDetail( + id: number, + with_details = false + ): Promise | undefined> { + return await this.customRequest>( + `/${id}`, + { + method: 'GET', + params: { + with_details: with_details, + }, + } + ); + } + + async createUniformity( + payload: CreateUniformityPayload + ): Promise | undefined> { + const formData = new FormData(); + formData.append('date', payload.date); + formData.append('week', payload.week.toString()); + formData.append( + 'project_flock_kandang_id', + payload.project_flock_kandang_id.toString() + ); + + if (payload.document) { + formData.append('document', payload.document); + } + + return await this.customRequest>('', { + method: 'POST', + payload: formData as unknown as Record, + }); + } + + async verifyUniformity( + payload: VerifyUniformityPayload + ): Promise | undefined> { + const formData = new FormData(); + if (payload.document) { + formData.append('document', payload.document); + } + + return await this.customRequest>( + '/verify', + { + method: 'POST', + payload: formData as unknown as Record, + } + ); + } + + async approve( + idOrIds: number | number[], + notes?: string + ): Promise | undefined> { + const approvable_ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds]; + return await this.customRequest>( + 'approvals', + { + method: 'POST', + payload: { + action: 'APPROVED', + approvable_ids, + notes, + }, + } + ); + } + + async reject( + idOrIds: number | number[], + notes: string = '' + ): Promise | undefined> { + const approvable_ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds]; + return await this.customRequest>( + 'approvals', + { + method: 'POST', + payload: { + action: 'REJECTED', + approvable_ids, + notes, + }, + } + ); + } + + async delete(id: number): Promise | undefined> { + return await this.customRequest>(`/${id}`, { + method: 'DELETE', + }); + } +} + +export const UniformityApi = new UniformityApiService( + 'production/uniformities' + // 'http://localhost:4010/api/production/uniformities' +); diff --git a/src/stores/ui/slices/drawer.slice.ts b/src/stores/ui/slices/drawer.slice.ts index b92b60c3..382eaff2 100644 --- a/src/stores/ui/slices/drawer.slice.ts +++ b/src/stores/ui/slices/drawer.slice.ts @@ -1,5 +1,6 @@ import { DrawerUISlice } from '@/types/stores'; import { StateCreator } from 'zustand'; +import { ReactNode } from 'react'; export const createDrawerUISlice: StateCreator< DrawerUISlice, @@ -37,4 +38,14 @@ export const createDrawerUISlice: StateCreator< callback(Boolean(state.isValid)); }); }, + + expandedDrawerOpen: false, + setExpandedDrawerOpen: (open: boolean) => set({ expandedDrawerOpen: open }), + + expandedDrawerContent: null as ReactNode | null, + setExpandedDrawerContent: (content: ReactNode) => + set({ expandedDrawerContent: content }), + + isNextStep: false, + setIsNextStep: (isNextStep: boolean) => set({ isNextStep }), }); diff --git a/src/stores/uniformity/slices/uniformity.slice.ts b/src/stores/uniformity/slices/uniformity.slice.ts new file mode 100644 index 00000000..d95b6388 --- /dev/null +++ b/src/stores/uniformity/slices/uniformity.slice.ts @@ -0,0 +1,37 @@ +import { UniformitySlice } from '@/types/stores'; +import { StateCreator } from 'zustand'; + +export const createUniformitySlice: StateCreator< + UniformitySlice, + [], + [], + UniformitySlice +> = (set) => ({ + // Initial state + uniformityStep: 'preview', + verifyUniformityResult: null, + uniformityFormData: null, + isSuccess: false, + createdUniformity: null, + + // Actions + setUniformityStep: (step) => set({ uniformityStep: step }), + + setVerifyUniformityResult: (result) => + set({ verifyUniformityResult: result }), + + setUniformityFormData: (data) => set({ uniformityFormData: data }), + + setIsSuccess: (success) => set({ isSuccess: success }), + + setCreatedUniformity: (data) => set({ createdUniformity: data }), + + resetUniformity: () => + set({ + uniformityStep: 'preview', + verifyUniformityResult: null, + uniformityFormData: null, + isSuccess: false, + createdUniformity: null, + }), +}); diff --git a/src/stores/uniformity/uniformity.store.ts b/src/stores/uniformity/uniformity.store.ts new file mode 100644 index 00000000..c8d759d6 --- /dev/null +++ b/src/stores/uniformity/uniformity.store.ts @@ -0,0 +1,19 @@ +'use client'; + +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { createUniformitySlice } from '@/stores/uniformity/slices/uniformity.slice'; +import { UniformitySlice } from '@/types/stores'; + +export type UniformityStore = UniformitySlice; + +export const useUniformityStore = create()( + devtools( + (...args) => ({ + ...createUniformitySlice(...args), + }), + { + name: 'UniformityStore', + } + ) +); diff --git a/src/types/api/production/project-flock.d.ts b/src/types/api/production/project-flock.d.ts index b6d43fdb..1aeb2005 100644 --- a/src/types/api/production/project-flock.d.ts +++ b/src/types/api/production/project-flock.d.ts @@ -71,6 +71,8 @@ export type ProjectFlockKandangLookup = { kandang: Kandang; project_flock: ProjectFlock; quantity: number; + available_quantity?: number; + population: number; }; export type ProjectFlockAvailableQuantity = { diff --git a/src/types/api/production/uniformity.d.ts b/src/types/api/production/uniformity.d.ts new file mode 100644 index 00000000..0ebc7ea9 --- /dev/null +++ b/src/types/api/production/uniformity.d.ts @@ -0,0 +1,103 @@ +import { BaseMetadata } from '@/types/api/api-general'; +import { Location } from '@/types/api/location/location'; +import { ProjectFlock } from '@/types/api/project-flock/project-flock'; +import { Kandang } from '@/types/api/kandang/kandang'; +import { BaseApproval } from '@/types/api/approval/approval'; + +// ==================== GET ALL RESPONSE ==================== +export type Uniformity = BaseMetadata & { + id: number; + project_flock_kandang_id: number; + location_name: string; + flock_name: string; + kandang_name: string; + applied_at: string; + week: number; + status: string; + uniformity: number; + cv: number; + chick_qty_of_weight: number; + uniform_qty: number; + mean_up: number; + mean_down: number; + standard_mean_weight: number | null; + standard_uniformity: number | null; + 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 UniformityStandard = { + mean_weight: number; + uniformity: number; +} | null; + +export type UniformityDetail = BaseMetadata & { + id: number; + info_umum: UniformityInfoUmum; + sampling: UniformitySampling; + result: UniformityResult; + standard: UniformityStandard; + uniformity_details?: UniformityDetailItem[]; + latest_approval?: BaseApproval; +}; + +// ==================== VERIFY RESPONSE ==================== +export type VerifyUniformityResponse = { + sampling: UniformitySampling; + result: UniformityResult; + uniformity_details: UniformityDetailItem[]; +}; + +// ==================== PAYLOADS ==================== +export type CreateUniformityPayload = { + date: string; + project_flock_kandang_id: number; + document: File; + week: number; +}; + +export type VerifyUniformityPayload = { + document: File; +}; + +// ==================== OTHER TYPES ==================== +export type BodyWeightData = { + id: string; + number: number; + weight: number; + status?: 'ideal' | 'outside'; +}; + +export type DetailOptionType = OptionType & { + id: string; +}; diff --git a/src/types/api/purchase/purchase.d.ts b/src/types/api/purchase/purchase.d.ts index e4de565b..7f493d08 100644 --- a/src/types/api/purchase/purchase.d.ts +++ b/src/types/api/purchase/purchase.d.ts @@ -42,6 +42,12 @@ export type PurchaseItem = { expedition_vendor_name?: string | null; received_qty?: number | null; transport_per_item?: number | null; + expedition_vendor: { + id: number; + name: string; + alias: string; + category: string; + }; }; export type BasePurchase = { @@ -118,6 +124,7 @@ export type CreateAcceptApprovalRequestPayload = { received_qty: number; transport_per_item: number; }[]; + travel_documents?: File[]; }; export type DeletePurchaseRequestItemPayload = { diff --git a/src/types/stores.d.ts b/src/types/stores.d.ts index 96b681c0..48873805 100644 --- a/src/types/stores.d.ts +++ b/src/types/stores.d.ts @@ -1,4 +1,9 @@ import type { ProductionStandardRepeaterFormSchemaValues } from '@/components/pages/master-data/production-standard/form/ProductionStandardForm.schema'; +import type { + UniformityFormData, + UniformityDetail, + VerifyUniformityResponse, +} from '@/types/api/production/uniformity'; type MainUiSlice = { mainDrawerOpen: boolean; @@ -8,10 +13,16 @@ type MainUiSlice = { type DrawerUISlice = { triggerValidate: boolean; toggleValidate: () => void; - subscribeValidate: (callback: () => void) => void; + subscribeValidate: (callback: () => void) => () => void; isValid: boolean; setIsValid: (v: boolean) => void; subscribeIsValid: (callback: (isValid: boolean) => void) => () => void; + expandedDrawerOpen: boolean; + setExpandedDrawerOpen: (open: boolean) => void; + expandedDrawerContent: ReactNode | null; + setExpandedDrawerContent: (content: ReactNode) => void; + isNextStep: boolean; + setIsNextStep: (v: boolean) => void; }; export type UIStore = MainUiSlice & DrawerUISlice; @@ -40,3 +51,22 @@ type ProductionStandardFormSlice = { }; export type FormStore = ProductionStandardFormSlice; + +export type UniformityStep = 'preview' | 'result'; + +export type UniformitySlice = { + // State + uniformityStep: UniformityStep; + verifyUniformityResult: VerifyUniformityResponse | null; + uniformityFormData: UniformityFormData | null; + isSuccess: boolean; + createdUniformity: UniformityDetail | null; + + // Actions + setUniformityStep: (step: UniformityStep) => void; + setVerifyUniformityResult: (result: VerifyUniformityResponse | null) => void; + setUniformityFormData: (data: UniformityFormData | null) => void; + setIsSuccess: (success: boolean) => void; + setCreatedUniformity: (data: UniformityDetail | null) => void; + resetUniformity: () => void; +}; diff --git a/src/types/theme.d.ts b/src/types/theme.d.ts index dcd9e13f..64413d8d 100644 --- a/src/types/theme.d.ts +++ b/src/types/theme.d.ts @@ -9,4 +9,6 @@ export type Color = | 'error' | 'none'; +export type Variant = 'default' | 'outline' | 'ghost' | 'soft' | 'dash'; + export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';