diff --git a/src/app/us-284/page.tsx b/src/app/us-284/page.tsx
deleted file mode 100644
index 49764069..00000000
--- a/src/app/us-284/page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SapronakCalculationTable from '@/components/pages/us-284/SapronakCalculationTable';
-
-const PerhitunganSapronak = () => {
- return (
-
-
-
- );
-};
-
-export default PerhitunganSapronak;
diff --git a/src/components/helper/RequireAuth.tsx b/src/components/helper/RequireAuth.tsx
index dbd4b6bc..119d74cb 100644
--- a/src/components/helper/RequireAuth.tsx
+++ b/src/components/helper/RequireAuth.tsx
@@ -6,147 +6,9 @@ import useSWRImmutable from 'swr/immutable';
import { useAuth } from '@/services/hooks/useAuth';
import { httpClientFetcher, SWRHttpKey } from '@/services/http/client';
-import { isResponseSuccess } from '@/lib/api-helper';
-import { GetMeResponse } from '@/types/api/api-general';
-
-// TODO: delete this later, DONT HARDCODE USER DATA
-const DUMMY_USER = {
- id: 1,
- email: 'admin@mbugroup.id',
- npk: '0001',
- name: 'Super Admin',
- image: null,
- created_at: '2025-09-30T03:24:20.899229Z',
- updated_at: '2025-09-30T03:24:20.899229Z',
- roles: [
- {
- id: 1,
- key: 'mbu.super_admin',
- name: 'MBU Administrator',
- client: {
- id: 1,
- name: 'PT Mitra Berlian Unggas',
- alias: 'MBU',
- },
- permissions: [
- {
- id: 1,
- name: 'mbu:purchase:read',
- action: 'read',
- client: {
- id: 1,
- name: 'PT Mitra Berlian Unggas',
- alias: 'MBU',
- },
- },
- {
- id: 2,
- name: 'mbu:purchase:create',
- action: 'create',
- client: {
- id: 1,
- name: 'PT Mitra Berlian Unggas',
- alias: 'MBU',
- },
- },
- {
- id: 3,
- name: 'mbu:purchase:approve',
- action: 'approve',
- client: {
- id: 1,
- name: 'PT Mitra Berlian Unggas',
- alias: 'MBU',
- },
- },
- ],
- },
- {
- id: 2,
- key: 'lti.super_admin',
- name: 'LTI Administrator',
- client: {
- id: 2,
- name: 'PT Lumbung Telur Indonesia',
- alias: 'LTI',
- },
- permissions: [
- {
- id: 4,
- name: 'lti:purchase:read',
- action: 'read',
- client: {
- id: 2,
- name: 'PT Lumbung Telur Indonesia',
- alias: 'LTI',
- },
- },
- {
- id: 5,
- name: 'lti:purchase:create',
- action: 'create',
- client: {
- id: 2,
- name: 'PT Lumbung Telur Indonesia',
- alias: 'LTI',
- },
- },
- {
- id: 6,
- name: 'lti:purchase:approve',
- action: 'approve',
- client: {
- id: 2,
- name: 'PT Lumbung Telur Indonesia',
- alias: 'LTI',
- },
- },
- ],
- },
- {
- id: 3,
- key: 'manbu.super_admin',
- name: 'MANBU Administrator',
- client: {
- id: 3,
- name: 'PT Mandiri Berlian Unggas',
- alias: 'MANBU',
- },
- permissions: [
- {
- id: 7,
- name: 'manbu:purchase:read',
- action: 'read',
- client: {
- id: 3,
- name: 'PT Mandiri Berlian Unggas',
- alias: 'MANBU',
- },
- },
- {
- id: 8,
- name: 'manbu:purchase:create',
- action: 'create',
- client: {
- id: 3,
- name: 'PT Mandiri Berlian Unggas',
- alias: 'MANBU',
- },
- },
- {
- id: 9,
- name: 'manbu:purchase:approve',
- action: 'approve',
- client: {
- id: 3,
- name: 'PT Mandiri Berlian Unggas',
- alias: 'MANBU',
- },
- },
- ],
- },
- ],
-};
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { BaseApiResponse, GetMeResponse } from '@/types/api/api-general';
+import { AxiosError } from 'axios';
interface RequireAuthProps {
children?: ReactNode;
@@ -156,17 +18,20 @@ const RequireAuth = ({ children }: RequireAuthProps) => {
const router = useRouter();
const { setUser, setIsLoadingUser } = useAuth();
- const { data: userResponse, isLoading: isLoadingUserResponse } =
- useSWRImmutable(
- '/auth/sso/userinfo',
- httpClientFetcher,
- {
- shouldRetryOnError: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshInterval: 0,
- }
- );
+ const {
+ data: userResponse,
+ isLoading: isLoadingUserResponse,
+ error: userErrorResponse,
+ } = useSWRImmutable<
+ GetMeResponse & { ok?: boolean },
+ AxiosError,
+ SWRHttpKey
+ >('/sso/userinfo', httpClientFetcher, {
+ shouldRetryOnError: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshInterval: 0,
+ });
useEffect(() => {
setIsLoadingUser(isLoadingUserResponse);
@@ -175,23 +40,25 @@ const RequireAuth = ({ children }: RequireAuthProps) => {
useEffect(() => {
if (isResponseSuccess(userResponse)) {
setUser(userResponse.data);
- } else {
- // router.replace(process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string);
- // TODO: remove this later, DONT HARDCODE USER DATA
- setUser(DUMMY_USER);
+ } else if (
+ isResponseError(userErrorResponse?.response?.data) &&
+ typeof window !== 'undefined'
+ ) {
+ router.replace(
+ `${process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string}?redirect_url=${window.location.href}`
+ );
}
- }, [userResponse, setIsLoadingUser, setUser]);
+ }, [userResponse, userErrorResponse, setIsLoadingUser, setUser]);
- // TODO: uncomment this later
- // if (isLoadingUserResponse && !userResponse) {
- // return (
- //
- //
- //
- // );
- // }
+ if (isLoadingUserResponse && !userResponse && !userErrorResponse) {
+ return (
+
+
+
+ );
+ }
- return <>{children}>;
+ return <>{isResponseSuccess(userResponse) && children}>;
};
export default RequireAuth;
diff --git a/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx b/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx
index 7d5f1f9b..bcfb7795 100644
--- a/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx
+++ b/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx
@@ -1,16 +1,15 @@
'use client';
+
import Button from '@/components/Button';
import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
import Table from '@/components/Table';
import Badge from '@/components/Badge';
import { cn, formatDate, formatNumber, formatTitleCase } from '@/lib/helper';
-import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
import { ProjectFlock } from '@/types/api/production/project-flock';
import {
ClosingExpense,
ProjectFlockKandang,
} from '@/types/api/production/project-flock-kandang';
-import { Purchase } from '@/types/api/purchase/purchase';
import { Icon } from '@iconify/react';
import useSWR from 'swr';
import { ProjectFlockKandangApi } from '@/services/api/production/project-flock-kandang';
@@ -19,6 +18,8 @@ import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import { useMemo, useState } from 'react';
import toast from 'react-hot-toast';
+import { useRouter } from 'next/navigation';
+import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
const ProjectFlockClosingForm = ({
projectFlock,
@@ -27,8 +28,9 @@ const ProjectFlockClosingForm = ({
projectFlock: ProjectFlock;
projectFlockKandang: ProjectFlockKandang;
}) => {
+ const router = useRouter();
const closeModal = useModal();
- const isCanClose = projectFlock.approval?.step_number <= 1;
+ const isCanClose = projectFlock.approval?.step_number <= 2;
const [isClosingLoading, setIsClosingLoading] = useState(false);
const { data: closingData, isLoading } = useSWR(
@@ -48,6 +50,7 @@ const ProjectFlockClosingForm = ({
if (isResponseSuccess(deleteProjectFlockRes)) {
toast.success(deleteProjectFlockRes?.message as string);
+ router.push(`/production/project-flock`);
}
if (isResponseError(deleteProjectFlockRes)) {
toast.error(deleteProjectFlockRes?.message as string);
@@ -280,7 +283,7 @@ const ProjectFlockClosingForm = ({
ref={closeModal.ref}
type='error'
text={
- projectFlock.approval?.step_number <= 1
+ isCanClose
? 'Apakah kamu yakin ingin mengakhiri project ini ? *Pastikan persediaan produk di gudang terkait sudah kosong, dan BOP sudah selesai'
: 'Apakah kamu yakin ingin membuka kembali project ini ? *Project ini akan kembali ke status aktif'
}
diff --git a/src/components/pages/us-284/DummyDataSapronakCalculation.ts b/src/components/pages/us-284/DummyDataSapronakCalculation.ts
deleted file mode 100644
index 74a6313e..00000000
--- a/src/components/pages/us-284/DummyDataSapronakCalculation.ts
+++ /dev/null
@@ -1,298 +0,0 @@
-import { BaseApiService } from '@/services/api/base';
-import { BaseApiResponse } from '@/types/api/api-general';
-
-export type RowSapronakCalculation = {
- id: number;
- tanggal: string;
- no_referensi: string;
- qty_masuk: number;
- qty_keluar: number;
- qty_pakai: number;
- uraian: string;
- kategori_produk: string;
- harga_beli_per_qty: number;
- total_harga: number;
- keterangan: string;
-};
-
-export type TotalSapronakCalculation = {
- label: string;
- qty_masuk: number;
- qty_keluar: number;
- qty_pakai: number;
- harga_beli_per_qty: number;
- total_harga: number;
-};
-
-export type SapronakCalculationItem = {
- rows: RowSapronakCalculation[];
- total: TotalSapronakCalculation;
-};
-
-export type SapronakCalculation = {
- doc_broiler: SapronakCalculationItem;
- ovk: SapronakCalculationItem;
- pakan: SapronakCalculationItem;
-};
-
-// Dummy data
-const DUMMY_SAPRONAK_CALCULATION: SapronakCalculation = {
- doc_broiler: {
- rows: [
- {
- id: 1,
- tanggal: '11-Sep-2025',
- no_referensi: 'PO-PULLET-388',
- qty_masuk: 32800,
- qty_keluar: 0,
- qty_pakai: 32800,
- uraian: 'PULLET LOHMANN (16 MINGGU)',
- kategori_produk: 'PULLET LAYER',
- harga_beli_per_qty: 60136,
- total_harga: 1972556800,
- keterangan: '-',
- },
- {
- id: 2,
- tanggal: '24-Sep-2025',
- no_referensi: 'PO-PULLET-410',
- qty_masuk: 14758,
- qty_keluar: 0,
- qty_pakai: 14758,
- uraian: 'PULLET HY-LINE (17 MINGGU)',
- kategori_produk: 'PULLET LAYER',
- harga_beli_per_qty: 65421,
- total_harga: 965908998,
- keterangan: '-',
- },
- {
- id: 3,
- tanggal: '29-Sep-2025',
- no_referensi: 'PO-PULLET-196',
- qty_masuk: 35439,
- qty_keluar: 0,
- qty_pakai: 35439,
- uraian: 'PULLET ISA BROWN (15 MINGGU)',
- kategori_produk: 'PULLET LAYER',
- harga_beli_per_qty: 55909,
- total_harga: 1981297351,
- keterangan: '-',
- },
- ],
- total: {
- label: 'TOTAL PULLET',
- qty_masuk: 82997,
- qty_keluar: 0,
- qty_pakai: 82997,
- harga_beli_per_qty: 59274.65,
- total_harga: 4919963149,
- },
- },
- ovk: {
- rows: [
- {
- id: 1,
- tanggal: '28-Sep-2025',
- no_referensi: 'PO-OVK-276',
- qty_masuk: 52,
- qty_keluar: 0,
- qty_pakai: 52,
- uraian: 'ND-IB VACCINE',
- kategori_produk: 'OVK VAKSIN',
- harga_beli_per_qty: 204652,
- total_harga: 10641904,
- keterangan: 'Program kesehatan & biosecurity',
- },
- {
- id: 2,
- tanggal: '26-Sep-2025',
- no_referensi: 'PO-OVK-811',
- qty_masuk: 43,
- qty_keluar: 0,
- qty_pakai: 43,
- uraian: 'GUMBORO VACCINE',
- kategori_produk: 'OVK VAKSIN',
- harga_beli_per_qty: 298379,
- total_harga: 12830297,
- keterangan: 'Program kesehatan & biosecurity',
- },
- {
- id: 3,
- tanggal: '28-Sep-2025',
- no_referensi: 'PO-OVK-879',
- qty_masuk: 21,
- qty_keluar: 0,
- qty_pakai: 21,
- uraian: 'AMOXITIN SOLUBLE',
- kategori_produk: 'OVK OBAT',
- harga_beli_per_qty: 145952,
- total_harga: 3064992,
- keterangan: 'Program kesehatan & biosecurity',
- },
- {
- id: 4,
- tanggal: '11-Okt-2025',
- no_referensi: 'PO-OVK-340',
- qty_masuk: 38,
- qty_keluar: 0,
- qty_pakai: 38,
- uraian: 'TILOXIN SOLUBLE',
- kategori_produk: 'OVK OBAT',
- harga_beli_per_qty: 200424,
- total_harga: 7616112,
- keterangan: 'Program kesehatan & biosecurity',
- },
- {
- id: 5,
- tanggal: '27-Sep-2025',
- no_referensi: 'PO-OVK-364',
- qty_masuk: 7,
- qty_keluar: 0,
- qty_pakai: 7,
- uraian: 'EGG STIMULANT',
- kategori_produk: 'OVK VITAMIN',
- harga_beli_per_qty: 115024,
- total_harga: 805168,
- keterangan: 'Program kesehatan & biosecurity',
- },
- {
- id: 6,
- tanggal: '16-Sep-2025',
- no_referensi: 'PO-OVK-982',
- qty_masuk: 57,
- qty_keluar: 0,
- qty_pakai: 57,
- uraian: 'MULTIVIT-AMINO',
- kategori_produk: 'OVK VITAMIN',
- harga_beli_per_qty: 65123,
- total_harga: 3712011,
- keterangan: 'Program kesehatan & biosecurity',
- },
- {
- id: 7,
- tanggal: '04-Okt-2025',
- no_referensi: 'PO-OVK-876',
- qty_masuk: 4,
- qty_keluar: 0,
- qty_pakai: 4,
- uraian: 'BKC DESINFEKTAN',
- kategori_produk: 'OVK KIMIA',
- harga_beli_per_qty: 105677,
- total_harga: 422708,
- keterangan: 'Program kesehatan & biosecurity',
- },
- ],
- total: {
- label: 'TOTAL OVK',
- qty_masuk: 222,
- qty_keluar: 0,
- qty_pakai: 222,
- harga_beli_per_qty: 172965.92,
- total_harga: 38481094,
- },
- },
- pakan: {
- rows: [
- {
- id: 1,
- tanggal: '13-Ags-2025',
- no_referensi: 'PO-FEED-730',
- qty_masuk: 4833,
- qty_keluar: 0,
- qty_pakai: 4833,
- uraian: 'FEED PRE-LAY',
- kategori_produk: 'PAKAN PRE-LAY',
- harga_beli_per_qty: 7578,
- total_harga: 36625874,
- keterangan: 'Konsumsi pakan kandang layer',
- },
- {
- id: 2,
- tanggal: '28-Jul-2025',
- no_referensi: 'PO-FEED-555',
- qty_masuk: 6500,
- qty_keluar: 0,
- qty_pakai: 6500,
- uraian: 'FEED LAYER PHASE 1',
- kategori_produk: 'PAKAN LAYER',
- harga_beli_per_qty: 8116,
- total_harga: 52754000,
- keterangan: 'Konsumsi pakan kandang layer',
- },
- {
- id: 3,
- tanggal: '24-Agu-2025',
- no_referensi: 'PO-FEED-683',
- qty_masuk: 8802,
- qty_keluar: 0,
- qty_pakai: 8802,
- uraian: 'FEED LAYER PHASE 2',
- kategori_produk: 'PAKAN LAYER',
- harga_beli_per_qty: 8801,
- total_harga: 77465402,
- keterangan: 'Konsumsi pakan kandang layer',
- },
- {
- id: 4,
- tanggal: '02-Sep-2025',
- no_referensi: 'PO-FEED-448',
- qty_masuk: 2185,
- qty_keluar: 0,
- qty_pakai: 2185,
- uraian: 'JAGUNG GILING',
- kategori_produk: 'PAKAN MIX',
- harga_beli_per_qty: 5573,
- total_harga: 12187705,
- keterangan: 'Konsumsi pakan kandang layer',
- },
- ],
- total: {
- label: 'TOTAL PAKAN',
- qty_masuk: 22320,
- qty_keluar: 0,
- qty_pakai: 22320,
- harga_beli_per_qty: 8092.39,
- total_harga: 179032981,
- },
- },
-};
-
-export class ClosingService extends BaseApiService {
- constructor(basePath: string = '') {
- super(basePath);
- }
-
- async getPerhitunganSapronak(
- projectFlockId: number
- ): Promise | undefined> {
- // Dummy implementation - simulate API call with delay
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve({
- code: 200,
- status: 'success',
- message: 'Retrieved sapronak calculation successfully',
- data: DUMMY_SAPRONAK_CALCULATION,
- });
- }, 500); // Simulate 500ms network delay
- });
-
- /*
- // Real API implementation - uncomment when backend is ready
- try {
- const path = `${this.basePath}/project-flock/${projectFlockId}/sapronak-calculation`;
-
- return await httpClient>(path, {
- method: 'GET',
- });
- } catch (error: unknown) {
- if (axios.isAxiosError>(error)) {
- return error.response?.data;
- }
- return undefined;
- }
- */
- }
-}
-
-export const ClosingApi = new ClosingService(`/closing`);
diff --git a/src/components/pages/us-284/SapronakCalculationTable.tsx b/src/components/pages/us-284/SapronakCalculationTable.tsx
deleted file mode 100644
index dea01068..00000000
--- a/src/components/pages/us-284/SapronakCalculationTable.tsx
+++ /dev/null
@@ -1,204 +0,0 @@
-'use client';
-
-import Card from '@/components/Card';
-import {
- ClosingApi,
- RowSapronakCalculation,
-} from '@/components/pages/us-284/DummyDataSapronakCalculation';
-import Table from '@/components/Table';
-import { isResponseSuccess } from '@/lib/api-helper';
-import { cn, formatCurrency, formatNumber } from '@/lib/helper';
-import { ColumnDef } from '@tanstack/react-table';
-import { useMemo } from 'react';
-import useSWR from 'swr';
-
-const SapronakCalculationTable = ({
- projectFlockId,
-}: {
- projectFlockId: number;
-}) => {
- const { data: sapronakCalculation, isLoading } = useSWR(
- `/sapronak-calculation`,
- () => ClosingApi.getPerhitunganSapronak(projectFlockId)
- );
-
- const columns: ColumnDef[] = useMemo(
- () => [
- {
- header: 'Tanggal',
- accessorKey: 'tanggal',
- cell: (props) => {
- const value = props.getValue() as string;
- // Data already in DD-MMM-YYYY format, just display it
- return value || '-';
- },
- },
- {
- header: 'No. Referensi',
- accessorKey: 'no_referensi',
- },
- {
- header: 'QTY Masuk',
- accessorKey: 'qty_masuk',
- cell: (props) => {
- const value = props.getValue() as number;
- return formatNumber(value);
- },
- },
- {
- header: 'QTY Keluar',
- accessorKey: 'qty_keluar',
- cell: (props) => {
- const value = props.getValue() as number;
- return formatNumber(value);
- },
- },
- {
- header: 'QTY Pakai',
- accessorKey: 'qty_pakai',
- cell: (props) => {
- const value = props.getValue() as number;
- return formatNumber(value);
- },
- },
- {
- header: 'Uraian',
- accessorKey: 'uraian',
- },
- {
- header: 'Kategori Produk',
- accessorKey: 'kategori_produk',
- },
- {
- header: 'Harga Beli/Qty (Rp)',
- accessorKey: 'harga_beli_per_qty',
- cell: (props) => {
- const value = props.getValue() as number;
- return formatCurrency(value);
- },
- },
- {
- header: 'Total Harga (Rp)',
- accessorKey: 'total_harga',
- cell: (props) => {
- const value = props.getValue() as number;
- return formatCurrency(value);
- },
- },
- {
- header: 'Keterangan',
- accessorKey: 'keterangan',
- cell: (props) => {
- const value = props.getValue() as string;
- return value || '-';
- },
- },
- ],
- []
- );
-
- return (
-
- {isLoading && (
-
-
-
- )}
- {isResponseSuccess(sapronakCalculation) && (
- <>
-
-
- data={sapronakCalculation.data.doc_broiler.rows}
- columns={columns}
- className={{
- containerClassName: cn({
- 'mb-20':
- isResponseSuccess(sapronakCalculation) &&
- sapronakCalculation?.data.doc_broiler.rows.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName: 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- }}
- />
-
-
-
-
- data={sapronakCalculation.data.ovk.rows}
- columns={columns}
- className={{
- containerClassName: cn({
- 'mb-20':
- isResponseSuccess(sapronakCalculation) &&
- sapronakCalculation?.data.ovk.rows.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName: 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- }}
- />
-
-
-
-
- data={sapronakCalculation.data.pakan.rows}
- columns={columns}
- className={{
- containerClassName: cn({
- 'mb-20':
- isResponseSuccess(sapronakCalculation) &&
- sapronakCalculation?.data.pakan.rows.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName: 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- }}
- />
-
- >
- )}
-
- );
-};
-
-export default SapronakCalculationTable;