diff --git a/src/app/globals.css b/src/app/globals.css
index 0eb04a09..96339bf2 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -30,14 +30,14 @@
--color-base-100: oklch(100% 0 0); /* #ffffff */
--color-base-200: oklch(97.2% 0 0); /* #f2f2f2 */
--color-base-300: oklch(93.1% 0.002 249.7); /* #e5e6e6 */
- --color-base-content: oklch(18.6% 0.024 257.7); /* #1f2937 */
+ --color-base-content: #18181b;
/* Status/Utility Colors */
--color-info: oklch(67.4% 0.176 238.9);
--color-info-content: oklch(0% 0 0); /* #000000 */
--color-success: #00d390;
--color-success-content: oklch(100% 0 0); /* #ffffff */
- --color-warning: oklch(82.2% 0.165 91.9);
+ --color-warning: #fcb700;
--color-warning-content: oklch(0% 0 0); /* #000000 */
--color-error: #ff3a3a;
--color-error-content: oklch(100% 0 0); /* #fffffff */
diff --git a/src/app/production/transfer-to-laying/add/page.tsx b/src/app/production/transfer-to-laying/add/page.tsx
deleted file mode 100644
index 6f29085f..00000000
--- a/src/app/production/transfer-to-laying/add/page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import TransferToLayingForm from '@/components/pages/production/transfer-to-laying/form/TransferToLayingForm';
-
-const AddTransferToLaying = () => {
- return (
-
-
-
- );
-};
-
-export default AddTransferToLaying;
diff --git a/src/app/production/transfer-to-laying/detail/edit/page.tsx b/src/app/production/transfer-to-laying/detail/edit/page.tsx
deleted file mode 100644
index d5498e08..00000000
--- a/src/app/production/transfer-to-laying/detail/edit/page.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-'use client';
-
-import { useRouter, useSearchParams } from 'next/navigation';
-import useSWR from 'swr';
-
-import TransferToLayingForm from '@/components/pages/production/transfer-to-laying/form/TransferToLayingForm';
-
-import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-
-const TransferToLayingEdit = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
-
- const transferToLayingId = searchParams.get('transferToLayingId');
-
- const { data: transferToLaying, isLoading: isLoadingTransferToLaying } =
- useSWR(transferToLayingId, (id: number) =>
- TransferToLayingApi.getSingle(id)
- );
-
- if (!transferToLayingId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (
- !isLoadingTransferToLaying &&
- (!transferToLaying || isResponseError(transferToLaying))
- ) {
- router.replace('/404');
- return;
- }
-
- if (
- isResponseSuccess(transferToLaying) &&
- transferToLaying.data.approval.step_number === 2
- ) {
- router.replace('/production/transfer-to-laying');
- return;
- }
-
- return (
-
- {isLoadingTransferToLaying && (
-
- )}
- {!isLoadingTransferToLaying && isResponseSuccess(transferToLaying) && (
-
- )}
-
- );
-};
-
-export default TransferToLayingEdit;
diff --git a/src/app/production/transfer-to-laying/detail/layout.tsx b/src/app/production/transfer-to-laying/detail/layout.tsx
deleted file mode 100644
index 7220dfa1..00000000
--- a/src/app/production/transfer-to-laying/detail/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SuspenseHelper from '@/components/helper/SuspenseHelper';
-
-const Layout = ({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) => {
- return {children};
-};
-
-export default Layout;
diff --git a/src/app/production/transfer-to-laying/detail/page.tsx b/src/app/production/transfer-to-laying/detail/page.tsx
deleted file mode 100644
index 9ff6ed5e..00000000
--- a/src/app/production/transfer-to-laying/detail/page.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-'use client';
-
-import { useRouter, useSearchParams } from 'next/navigation';
-import useSWR from 'swr';
-
-import TransferToLayingForm from '@/components/pages/production/transfer-to-laying/form/TransferToLayingForm';
-
-import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-
-const TransferToLayingDetail = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
-
- const transferToLayingId = searchParams.get('transferToLayingId');
-
- const { data: transferToLaying, isLoading: isLoadingTransferToLaying } =
- useSWR(transferToLayingId, (id: number) =>
- TransferToLayingApi.getSingle(id)
- );
-
- if (!transferToLayingId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (
- !isLoadingTransferToLaying &&
- (!transferToLaying || isResponseError(transferToLaying))
- ) {
- router.replace('/404');
- return;
- }
-
- return (
-
- {isLoadingTransferToLaying && (
-
- )}
-
- {!isLoadingTransferToLaying && isResponseSuccess(transferToLaying) && (
-
- )}
-
- );
-};
-
-export default TransferToLayingDetail;
diff --git a/src/app/production/transfer-to-laying/page.tsx b/src/app/production/transfer-to-laying/page.tsx
index 048ae005..5d790345 100644
--- a/src/app/production/transfer-to-laying/page.tsx
+++ b/src/app/production/transfer-to-laying/page.tsx
@@ -1,5 +1,6 @@
import TransferToLayingsTable from '@/components/pages/production/transfer-to-laying/TransferToLayingsTable';
import TransferToLayingFormModal from '@/components/pages/production/transfer-to-laying/TransferToLayingFormModal';
+import TransferToLayingDetailModal from '@/components/pages/production/transfer-to-laying/TransferToLayingDetailModal';
const TransferToLaying = () => {
return (
@@ -7,6 +8,8 @@ const TransferToLaying = () => {
+
+
);
};
diff --git a/src/components/helper/ApprovalStepsV2.tsx b/src/components/helper/ApprovalStepsV2.tsx
new file mode 100644
index 00000000..6b731a72
--- /dev/null
+++ b/src/components/helper/ApprovalStepsV2.tsx
@@ -0,0 +1,201 @@
+'use client';
+
+import { useEffect, useMemo, useState } from 'react';
+
+import { Icon } from '@iconify/react';
+import { BaseApproval } from '@/types/api/api-general';
+import Button from '@/components/Button';
+
+import { cn, formatDate } from '@/lib/helper';
+
+interface ApprovalStepsV2Props {
+ approvals?: BaseApproval[];
+ steps: {
+ step_number: number;
+ step_name: string;
+ }[];
+ maxVisibleSteps?: number;
+ className?: {
+ wrapper?: string;
+ stepsWrapper?: string;
+ stepsContainer?: string;
+ };
+}
+
+const ApprovalStepsV2 = ({
+ approvals,
+ steps,
+ maxVisibleSteps = 2,
+ className,
+}: ApprovalStepsV2Props) => {
+ const [isSeeAll, setIsSeeAll] = useState(false);
+ const [formattedApprovals, setFormattedApprovals] = useState<
+ (BaseApproval & { isActive: boolean })[]
+ >([]);
+
+ const latestApprovalStepNumber =
+ approvals?.[approvals.length - 1].step_number ?? 0;
+
+ const lastStepNumber = steps[steps.length - 1].step_number;
+
+ const isLatestApprovalStepNumberLessThanLastStepNumber =
+ latestApprovalStepNumber < lastStepNumber;
+
+ const slicedFormattedApprovals = useMemo(() => {
+ return formattedApprovals.slice(0, isSeeAll ? undefined : maxVisibleSteps);
+ }, [formattedApprovals, isSeeAll]);
+
+ const seeMoreClickHandler = () => {
+ setIsSeeAll((prevVal) => !prevVal);
+ };
+
+ useEffect(() => {
+ if (approvals) {
+ const tempFormattedApprovals: (BaseApproval & { isActive: boolean })[] =
+ [];
+
+ approvals.forEach((approval) => {
+ tempFormattedApprovals.push({
+ ...approval,
+ isActive: true,
+ });
+ });
+
+ if (isLatestApprovalStepNumberLessThanLastStepNumber) {
+ const latestApprovalStepNumberIndexInSteps = steps.findIndex(
+ (step) => step.step_number === latestApprovalStepNumber
+ );
+
+ const slicedSteps = steps.slice(
+ latestApprovalStepNumberIndexInSteps + 1
+ );
+
+ slicedSteps.forEach((step) => {
+ tempFormattedApprovals.push({
+ action: 'APPROVED',
+ action_at: new Date().toISOString(),
+ action_by: {
+ id: 0,
+ id_user: 0,
+ email: '',
+ name: '',
+ },
+ step_name: step.step_name,
+ step_number: step.step_number,
+ isActive: false,
+ });
+ });
+ }
+
+ setFormattedApprovals(tempFormattedApprovals);
+ }
+ }, [approvals]);
+
+ return (
+
+
+ Progress Details
+
+
+
+ {slicedFormattedApprovals.map((approval, idx) => {
+ const isApprovalActionCreated = approval.action === 'CREATED';
+ const isApprovalActionUpdated = approval.action === 'UPDATED';
+ const isApprovalActionRejected = approval.action === 'REJECTED';
+ const isApprovalActionApproved = approval.action === 'APPROVED';
+
+ const approvalIcon =
+ isApprovalActionCreated || isApprovalActionUpdated
+ ? 'heroicons:clock-solid'
+ : isApprovalActionRejected
+ ? 'heroicons:x-circle-solid'
+ : isApprovalActionApproved
+ ? 'heroicons:check-badge-solid'
+ : 'heroicons:check-badge-solid';
+
+ return (
+
+
+
+
+
+ {idx < formattedApprovals.length - 1 && (
+
+ )}
+
+
+
+
+
+ {approval.step_name}
+
+ {(isApprovalActionCreated || isApprovalActionUpdated) &&
+ 'Diajukan oleh '}
+ {isApprovalActionRejected && 'Ditolak oleh '}
+ {isApprovalActionApproved && 'Disetujui oleh '}
+ {approval.isActive ? approval.action_by.name : '...'}
+
+
+
+ {approval.isActive && (
+
+ Created at :{' '}
+ {formatDate(approval.action_at, 'DD-MM-YYYY, HH:mm')}
+
+ Notes : {approval.notes ?? '-'}
+
+ )}
+
+
+ );
+ })}
+
+
+
+
+ );
+};
+
+export default ApprovalStepsV2;
diff --git a/src/components/input/DateInput.tsx b/src/components/input/DateInput.tsx
index 558779c7..da1a4d81 100644
--- a/src/components/input/DateInput.tsx
+++ b/src/components/input/DateInput.tsx
@@ -226,7 +226,7 @@ const DateInput = ({
@@ -257,8 +260,8 @@ const DateInput = ({
)}
handleClick(e as unknown as React.MouseEvent)
diff --git a/src/components/input/SelectInput.tsx b/src/components/input/SelectInput.tsx
index 1eb3ccd8..419ed314 100644
--- a/src/components/input/SelectInput.tsx
+++ b/src/components/input/SelectInput.tsx
@@ -42,6 +42,7 @@ interface SelectInputBaseProps {
optionComponent?: OptionComponent;
components?: Partial;
isDisabled?: boolean;
+ readOnly?: boolean;
isLoading?: boolean;
isClearable?: boolean;
isRtl?: boolean;
@@ -156,6 +157,7 @@ const SelectInput = (props: SelectInputProps) => {
closeMenuOnSelect,
hideSelectedOptions,
onMenuScrollToBottom,
+ readOnly,
} = props;
const [internalInputValue, setInternalInputValue] = useState('');
@@ -235,7 +237,7 @@ const SelectInput = (props: SelectInputProps) => {
onInputChange={internalInputChangeHandler}
onMenuClose={() => setInternalInputValue('')}
isMulti={isMulti}
- isDisabled={isDisabled}
+ isDisabled={isDisabled || readOnly}
isLoading={isLoading}
isClearable={isClearable}
isRtl={isRtl}
@@ -247,30 +249,37 @@ const SelectInput = (props: SelectInputProps) => {
classNames={{
...(!startAdornment && {
control: ({ isFocused, isDisabled }) =>
- cn(
- 'w-full min-h-12! rounded-lg! border bg-white transition-shadow cursor-pointer!',
- {
- 'border-red-500! ring-2 ring-red-200': isError,
- 'border-indigo-500 ring-2 ring-indigo-200': isFocused,
- 'border-base-content/10!': !isError && !isFocused,
- 'bg-gray-100 text-gray-400 cursor-not-allowed': isDisabled,
- }
- ),
- valueContainer: () => cn('flex-1 p-3! py-2! gap-1'),
+ cn('w-full rounded-lg! border bg-white transition-shadow', {
+ 'cursor-pointer!': !readOnly && !isDisabled,
+ 'border-red-500! ring-2 ring-red-200': isError,
+ 'border-indigo-500 ring-2 ring-indigo-200': isFocused,
+ 'border-base-content/10!': !isError && !isFocused,
+ 'bg-gray-100 text-gray-400 cursor-not-allowed':
+ isDisabled && !readOnly,
+ 'bg-transparent! cursor-not-allowed!': readOnly,
+ }),
+ valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'),
}),
placeholder: () =>
- cn({ 'text-gray-400': !isError, 'text-red-300!': isError }),
+ cn({
+ 'text-gray-400 text-sm leading-tight': !isError,
+ 'text-red-300!': isError,
+ }),
singleValue: () =>
- cn({ 'text-gray-900': !isError, 'text-error!': isError }),
- input: () => cn('text-gray-900 m-0! p-0!'),
- indicatorsContainer: () => cn('flex items-center gap-1 pr-2'),
+ cn({
+ 'm-0! text-gray-900 text-sm leading-tight': !isError,
+ 'text-error!': isError,
+ 'text-gray-900!': readOnly,
+ }),
+ input: () => cn('text-gray-900 m-0! p-0! text-sm leading-tight'),
+ indicatorsContainer: () => cn('flex items-center gap-1 pr-3 py-2'),
dropdownIndicator: ({ isFocused }) =>
- cn('p-1! rounded hover:bg-gray-100', {
+ cn('p-0! rounded hover:bg-gray-100', {
'text-gray-900': isFocused,
'text-gray-500': !isFocused,
'text-error!': isError,
}),
- clearIndicator: () => cn('p-1! rounded hover:bg-gray-100'),
+ clearIndicator: () => cn('p-0! rounded hover:bg-gray-100'),
menu: () =>
cn(
'border border-base-content/5 rounded-xl! bg-base-100 shadow-lg! my-1.5!'
diff --git a/src/components/input/TextArea.tsx b/src/components/input/TextArea.tsx
index 65bd57d3..b3dd2663 100644
--- a/src/components/input/TextArea.tsx
+++ b/src/components/input/TextArea.tsx
@@ -83,7 +83,7 @@ const TextArea = ({