diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e80a7e02..afcc082d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,7 +15,7 @@ default: # ========================================================== .build_template: &build_template stage: build - image: node:20-alpine + image: public.ecr.aws/docker/library/node:20-alpine cache: key: npm-cache paths: @@ -56,7 +56,7 @@ default: .deploy_template: &deploy_template stage: deploy image: - name: amazon/aws-cli:latest + name: public.ecr.aws/aws-cli/aws-cli:latest entrypoint: ['/bin/sh', '-c'] script: - set -e @@ -183,3 +183,31 @@ deploy:staging: environment: name: staging url: https://stg-lti-erp.mbugroup.id + +# ========================================================== +# ====== STAGING (Branch production) ====== +# ========================================================== +build:production: + <<: *build_template + rules: + - if: '$CI_COMMIT_BRANCH == "production"' + environment: + name: staging + variables: + NEXT_PUBLIC_LTI_URL: 'https://lti-erp.mbugroup.id' + NEXT_PUBLIC_SSO_LOGIN_URL: 'https://auth-erp.mbugroup.id' + NEXT_PUBLIC_API_BASE_URL: 'https://api-lti.mbugroup.id/api' + NEXT_PUBLIC_CLIENT_ID: 'Lumbung-Telur-Indonesia' + +deploy:production: + <<: *deploy_template + needs: ['build:production'] + rules: + - if: '$CI_COMMIT_BRANCH == "production"' + variables: + S3_BUCKET: 'production-lti-erp.mbugroup.id' + AWS_REGION: 'ap-southeast-3' + CLOUDFRONT_DISTRIBUTION_ID: 'E1SSLXKYYITASJ' + environment: + name: staging + url: https://lti-erp.mbugroup.id diff --git a/Dockerfile b/Dockerfile index a3a2e197..ff968b26 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine +FROM public.ecr.aws/docker/library/node:20-alpine RUN apk add --no-cache git bash build-base curl @@ -22,4 +22,4 @@ RUN mkdir -p .next/server/app/_next && \ EXPOSE 3000 -CMD ["npx", "serve", ".next/server/app", "-l", "3000"] \ No newline at end of file +CMD ["npx", "serve", ".next/server/app", "-l", "3000"] diff --git a/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx b/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx index 0c8b89d0..ff710329 100644 --- a/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx +++ b/src/components/pages/inventory/adjustment/form/InventoryAdjustmentForm.tsx @@ -185,7 +185,9 @@ const InventoryAdjustmentForm = ({ isLoadingOptions: isLoadingProductOptions, loadMore: loadMoreProducts, rawData: products, - } = useSelect(ProductApi.basePath, 'id', 'name', 'search'); + } = useSelect(ProductApi.basePath, 'id', 'name', 'search', { + include_all: 'true', + }); const { setInputValue: setDepletionProductInputValue, diff --git a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx index bdffda33..acb8c18b 100644 --- a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx +++ b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx @@ -23,7 +23,7 @@ const ChickinLogsView = ({ rawDataApprovals: BaseApproval[]; }) => { const [chickinErrorMessage, setChickinErrorMessage] = useState(''); - const { openChickinApproveModal } = useChickinStore(); + const { openChickinApproveModal, openChickinDeleteModal } = useChickinStore(); const handleClickApprove = () => { openChickinApproveModal(initialValues, async (notes?: string) => { @@ -44,6 +44,21 @@ const ChickinLogsView = ({ }); }; + const handleDeleteChickin = (chickinId: number) => { + openChickinDeleteModal(chickinId, async () => { + const deleteRes = await ChickinApi.delete(chickinId); + + if (isResponseSuccess(deleteRes)) { + toast.success(deleteRes?.message || 'Chickin berhasil dihapus'); + afterSubmit && afterSubmit(); + } + + if (isResponseError(deleteRes)) { + toast.error(deleteRes?.message || 'Gagal menghapus chickin'); + } + }); + }; + return ( <>
@@ -86,14 +101,30 @@ const ChickinLogsView = ({
Chick In #{index + 1} - {latestApproval?.step_number}
- +
+ + + {isApproved && ( + + )} +
{/* Tanggal Chick In */} diff --git a/src/components/pages/production/project-flock/ProjectFlockTable.tsx b/src/components/pages/production/project-flock/ProjectFlockTable.tsx index 1ae56fa2..bdc271a6 100644 --- a/src/components/pages/production/project-flock/ProjectFlockTable.tsx +++ b/src/components/pages/production/project-flock/ProjectFlockTable.tsx @@ -200,6 +200,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const confirmModal = useModal(); const successModal = useModal(); const chickinApproveModal = useModal(); + const chickinDeleteModal = useModal(); const closingModal = useModal(); const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>( 'APPROVED' @@ -214,6 +215,11 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { chickinApproveCallback, closeChickinApproveModal, setChickinApproveLoading, + isChickinDeleteModalOpen, + isChickinDeleteLoading, + chickinDeleteCallback, + closeChickinDeleteModal, + setChickinDeleteLoading, } = useChickinStore(); const { @@ -478,6 +484,14 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { } }, [isChickinApproveModalOpen, chickinApproveModal]); + useEffect(() => { + if (isChickinDeleteModalOpen) { + chickinDeleteModal.openModal(); + } else { + chickinDeleteModal.closeModal(); + } + }, [isChickinDeleteModalOpen, chickinDeleteModal]); + useEffect(() => { if (isClosingModalOpen) { closingModal.openModal(); @@ -1208,6 +1222,38 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { }} /> + {/* Chickin Delete Modal */} + { + closeChickinDeleteModal(); + }, + }} + className={{ + modal: 'z-9999', + }} + primaryButton={{ + text: 'Ya', + color: 'error', + isLoading: isChickinDeleteLoading, + onClick: async () => { + if (chickinDeleteCallback) { + setChickinDeleteLoading(true); + try { + await chickinDeleteCallback(); + } finally { + setChickinDeleteLoading(false); + closeChickinDeleteModal(); + } + } + }, + }} + /> + {/* Filter Modal */} { + if ( + recording.executed_at && + recording.project_flock?.project_flock_category === 'GROWING' + ) { + return false; + } + return true; + }; + const isApproved = isRecordingApproved(props.row.original); const isRejected = isRecordingRejected(props.row.original); + const isEditable = isRecordingEditable(props.row.original); return (
@@ -138,18 +149,20 @@ const RowOptionsMenu = ({ View Details - - - + {isEditable && ( + + + + )} {!isApproved && !isRejected && ( - + {isEditable && ( + + + + )}
diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index fd3a818d..af4ab78b 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -272,6 +272,16 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { return recording?.approval?.action === 'REJECTED'; }, []); + const isRecordingEditable = useCallback((recording?: Recording) => { + if ( + recording?.executed_at && + recording?.project_flock?.project_flock_category === 'GROWING' + ) { + return false; + } + return true; + }, []); + // ===== PAYLOAD CREATION HELPERS ===== const createGrowingPayload = useCallback( (values: RecordingGrowingFormValues) => { @@ -2990,42 +3000,46 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
{/* Left side - Detail & Edit actions */}
- {type === 'detail' && deleteRecordingClickHandler && ( - - - - )} - {type === 'detail' && initialValues && ( - - - - )} + {type === 'detail' && + deleteRecordingClickHandler && + isRecordingEditable(initialValues) && ( + + + + )} + {type === 'detail' && + initialValues && + isRecordingEditable(initialValues) && ( + + + + )}
{/* Right side actions */}
diff --git a/src/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal.tsx b/src/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal.tsx index 2975a865..555c1667 100644 --- a/src/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal.tsx +++ b/src/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal.tsx @@ -50,12 +50,18 @@ const TransferToLayingConfirmationModalTable = ({ transferToLayingForm?: TransferToLayingFormValues; transferToLayingId?: number; }) => { + const isValidId = + transferToLayingId !== undefined && + transferToLayingId !== null && + !isNaN(transferToLayingId) && + transferToLayingId > 0; + const { data: transferToLaying, isLoading: isLoadingTransferToLaying } = useSWR( - transferToLayingId + isValidId ? ['detail-transfer-to-laying', String(transferToLayingId)] : undefined, - ([id]) => TransferToLayingApi.getSingle(Number(id)) + ([, id]) => TransferToLayingApi.getSingle(Number(id)) ); const confirmationTableColumns: ColumnDef[] = @@ -273,12 +279,16 @@ const TransferToLayingConfirmationModal = ({ {transferToLayingIds && !transferToLayingForm && - transferToLayingIds.map((transferToLayingId, idx) => ( - - ))} + transferToLayingIds + .filter( + (id) => id !== undefined && id !== null && !isNaN(id) && id > 0 + ) + .map((transferToLayingId, idx) => ( + + ))} {withNote && (