diff --git a/src/app/uniformity/add/page.tsx b/src/app/uniformity/add/page.tsx
index 9931eebe..7c12cc72 100644
--- a/src/app/uniformity/add/page.tsx
+++ b/src/app/uniformity/add/page.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import UniformityForm from '@/components/pages/uniformity/form/UniformityForm';
const AddUniformity = () => {
diff --git a/src/app/uniformity/detail/page.tsx b/src/app/uniformity/detail/page.tsx
new file mode 100644
index 00000000..a2e9402e
--- /dev/null
+++ b/src/app/uniformity/detail/page.tsx
@@ -0,0 +1,47 @@
+'use client';
+
+import UniformityDetail from '@/components/pages/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/uniformity/page.tsx b/src/app/uniformity/page.tsx
index dd0e4466..24a31482 100644
--- a/src/app/uniformity/page.tsx
+++ b/src/app/uniformity/page.tsx
@@ -1,5 +1,7 @@
+import UniformityTable from '@/components/pages/uniformity/UniformityTable';
+
const Uniformity = () => {
- return <>>;
+ return ;
};
export default Uniformity;
diff --git a/src/components/pages/uniformity/UniformityTable.tsx b/src/components/pages/uniformity/UniformityTable.tsx
index 27365e40..efa8dd66 100644
--- a/src/components/pages/uniformity/UniformityTable.tsx
+++ b/src/components/pages/uniformity/UniformityTable.tsx
@@ -1,6 +1,7 @@
'use client';
import React, { useCallback, useState, useEffect, useMemo } from 'react';
+import { useRouter } from 'next/navigation';
import useSWR from 'swr';
import { Icon } from '@iconify/react';
import { ColumnDef, SortingState } from '@tanstack/react-table';
@@ -59,6 +60,7 @@ const isUniformityLocked = (uniformity: Uniformity): boolean => {
};
const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
+ const router = useRouter();
const isSuccess = useUniformityStore((s) => s.isSuccess);
const setIsSuccess = useUniformityStore((s) => s.setIsSuccess);
@@ -200,6 +202,14 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
setRowSelection({});
}, []);
+ const handleViewDetail = useCallback(
+ (uniformity: Uniformity) => {
+ router.push(`/uniformity/detail?uniformityId=${uniformity.id}`);
+ setRowSelection({});
+ },
+ [router]
+ );
+
const handleBulkApprove = useCallback(() => {
bulkApproveModal.openModal();
}, [bulkApproveModal]);
@@ -980,6 +990,19 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
{/* Floating Actions Button */}
= ({
+ initialValues,
+}) => {
+ const router = useRouter();
+
+ const handleClose = () => {
+ router.back();
+ };
+
+ const infoUmumTableData: DetailOptionType[] = useMemo(() => {
+ if (!initialValues) return [];
+
+ return [
+ {
+ id: 'tanggal',
+ value: 'tanggal',
+ label: 'Tanggal',
+ },
+ {
+ id: 'lokasi-farm',
+ value: 'lokasi-farm',
+ label: 'Lokasi Farm',
+ },
+ {
+ id: 'project-flock',
+ value: 'project-flock',
+ label: 'Project Flock',
+ },
+ {
+ id: 'kandang',
+ value: 'kandang',
+ label: 'Kandang',
+ },
+ {
+ id: 'file-name',
+ value: 'file-name',
+ label: 'File Name',
+ },
+ ];
+ }, [initialValues]);
+
+ const columnsInfoUmum: ColumnDef[] = useMemo(
+ () => [
+ {
+ accessorKey: 'label',
+ header: 'Label',
+ cell: (props) => props.row.original.label,
+ },
+ {
+ accessorKey: 'value',
+ header: 'Value',
+ cell: (props) => {
+ const id = props.row.original.id;
+ const { info_umum } = initialValues!;
+
+ const valueMap: Record = {
+ tanggal: info_umum.tanggal,
+ 'lokasi-farm': info_umum.lokasi_farm,
+ 'project-flock': info_umum.project_flock,
+ kandang: info_umum.kandang,
+ 'file-name': info_umum.file_name,
+ };
+
+ return {valueMap[id] || '-'};
+ },
+ },
+ ],
+ [initialValues]
+ );
+
+ const samplingTableData: DetailOptionType[] = useMemo(() => {
+ if (!initialValues) return [];
+
+ return [
+ {
+ id: 'sampling-size',
+ value: 'sampling-size',
+ label: 'Sampling size',
+ },
+ {
+ id: 'mean-weight',
+ value: 'mean-weight',
+ label: 'Mean Weight',
+ },
+ {
+ id: 'min-limit',
+ value: 'min-limit',
+ label: 'Min Limit (-10%)',
+ },
+ {
+ id: 'max-limit',
+ value: 'max-limit',
+ label: 'Max Limit (+10%)',
+ },
+ ];
+ }, [initialValues]);
+
+ const columnsSampling: ColumnDef[] = useMemo(
+ () => [
+ {
+ accessorKey: 'label',
+ header: 'Label',
+ cell: (props) => props.row.original.label,
+ },
+ {
+ accessorKey: 'value',
+ header: 'Value',
+ cell: (props) => {
+ const id = props.row.original.id;
+ const { sampling } = initialValues!;
+
+ const valueMap: Record = {
+ 'sampling-size': `${formatNumber(sampling.chick_qty_of_weight)} of Birds`,
+ 'mean-weight': `${formatNumber(sampling.mean_weight)} g`,
+ 'min-limit': `${formatNumber(sampling.mean_down)} g`,
+ 'max-limit': `${formatNumber(sampling.mean_up)} g`,
+ };
+
+ return {valueMap[id] || '-'};
+ },
+ },
+ ],
+ [initialValues]
+ );
+
+ const resultTableData: DetailOptionType[] = useMemo(() => {
+ if (!initialValues) return [];
+
+ return [
+ {
+ id: 'ideal-birds',
+ value: 'ideal-birds',
+ label: 'Ideal Birds',
+ },
+ {
+ id: 'outside-range',
+ value: 'outside-range',
+ label: 'Outside Range',
+ },
+ {
+ id: 'uniformity',
+ value: 'uniformity',
+ label: 'Uniformity',
+ },
+ {
+ id: 'cv',
+ value: 'cv',
+ label: 'CV',
+ },
+ ];
+ }, [initialValues]);
+
+ const columnsResult: ColumnDef[] = useMemo(
+ () => [
+ {
+ accessorKey: 'label',
+ header: 'Label',
+ cell: (props) => props.row.original.label,
+ },
+ {
+ accessorKey: 'value',
+ header: 'Value',
+ cell: (props) => {
+ const id = props.row.original.id;
+ const { result } = initialValues!;
+
+ const valueMap: Record = {
+ 'ideal-birds': `${formatNumber(result.uniform_qty)} of Birds`,
+ 'outside-range': `${formatNumber(result.outside_qty)} of Birds`,
+ uniformity: `${result.uniformity} %`,
+ cv: `${result.cv}`,
+ };
+
+ return {valueMap[id] || '-'};
+ },
+ },
+ ],
+ [initialValues]
+ );
+
+ return (
+
+ {/* Header */}
+
+
+
+
+ {/* Form Section */}
+
+
+ {initialValues ? (
+
+ {/* Info Umum */}
+
+
Informasi Umum
+
+ data={infoUmumTableData}
+ columns={columnsInfoUmum}
+ pageSize={5}
+ className={{
+ containerClassName: 'mb-0',
+ paginationClassName: 'hidden',
+ }}
+ />
+
+
+ {/* Sampling */}
+
+
Sampling and Range
+
+ data={samplingTableData}
+ columns={columnsSampling}
+ pageSize={4}
+ className={{
+ containerClassName: 'mb-0',
+ paginationClassName: 'hidden',
+ }}
+ />
+
+
+ {/* Result */}
+
+
Result
+
+ data={resultTableData}
+ columns={columnsResult}
+ pageSize={4}
+ className={{
+ containerClassName: 'mb-0',
+ paginationClassName: 'hidden',
+ }}
+ />
+
+
+ ) : (
+
+
+
No data available
+
Uniformity detail not found
+
+ )}
+
+
+ );
+};
+
+export default UniformityDetail;
diff --git a/src/services/api/uniformity.ts b/src/services/api/uniformity.ts
index 4728e3e7..892192b6 100644
--- a/src/services/api/uniformity.ts
+++ b/src/services/api/uniformity.ts
@@ -1,9 +1,10 @@
import { BaseApiService } from '@/services/api/base';
import { BaseApiResponse } from '@/types/api/api-general';
import {
+ Uniformity,
+ UniformityDetail,
VerifyUniformityPayload,
VerifyUniformityResponse,
- Uniformity,
CreateUniformityPayload,
} from '@/types/api/uniformity/uniformity';
@@ -20,6 +21,14 @@ export class UniformityApiService extends BaseApiService<
return await this.customRequest>('');
}
+ async getUniformityDetail(
+ id: number
+ ): Promise | undefined> {
+ return await this.customRequest>(
+ `/${id}`
+ );
+ }
+
async createUniformity(
payload: CreateUniformityPayload
): Promise | undefined> {