mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 15:25:46 +00:00
Merge branch 'feat/FE/US-81/production-result-report' into 'development'
[FEAT/FE][US#81] Production Result Report PDF See merge request mbugroup/lti-web-client!184
This commit is contained in:
@@ -21,10 +21,18 @@ import {
|
|||||||
ProjectFlockApi,
|
ProjectFlockApi,
|
||||||
ProjectFlockKandangApi,
|
ProjectFlockKandangApi,
|
||||||
} from '@/services/api/production';
|
} from '@/services/api/production';
|
||||||
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
|
import {
|
||||||
import { isResponseError } from '@/lib/api-helper';
|
BaseProjectFlockKandang,
|
||||||
|
ProjectFlockKandang,
|
||||||
|
} from '@/types/api/production/project-flock-kandang';
|
||||||
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import Pagination from '@/components/Pagination';
|
import Pagination from '@/components/Pagination';
|
||||||
import { ProductionResultReportApi } from '@/services/api/report/production-result';
|
import { ProductionResultReportApi } from '@/services/api/report/production-result';
|
||||||
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
|
import { httpClient } from '@/services/http/client';
|
||||||
|
import { ProductionResult } from '@/types/api/report/production-result';
|
||||||
|
import ProductionResultReportPDF from './ProductionResultReportPDF';
|
||||||
|
import { pdf } from '@react-pdf/renderer';
|
||||||
|
|
||||||
const ProductionResultContent = () => {
|
const ProductionResultContent = () => {
|
||||||
const [projectFlockKandangs, setProjectFlockKandangs] = useState<
|
const [projectFlockKandangs, setProjectFlockKandangs] = useState<
|
||||||
@@ -49,6 +57,8 @@ const ProductionResultContent = () => {
|
|||||||
const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] =
|
const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
|
const [isLoadingExportingToPdf, setIsLoadingExportingToPdf] = useState(false);
|
||||||
|
|
||||||
const [selectedArea, setSelectedArea] = useState<OptionType | null>(null);
|
const [selectedArea, setSelectedArea] = useState<OptionType | null>(null);
|
||||||
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
||||||
null
|
null
|
||||||
@@ -158,6 +168,87 @@ const ProductionResultContent = () => {
|
|||||||
setIsLoadingExportingToExcel(false);
|
setIsLoadingExportingToExcel(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const exportToPdfHandler = async () => {
|
||||||
|
setIsLoadingExportingToPdf(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let projectFlockKandangsData: BaseProjectFlockKandang[] = [];
|
||||||
|
|
||||||
|
if (selectedProjectFlockKandang) {
|
||||||
|
const projectFlockKandangResponse =
|
||||||
|
await ProjectFlockKandangApi.getSingle(
|
||||||
|
selectedProjectFlockKandang?.value as number
|
||||||
|
);
|
||||||
|
|
||||||
|
projectFlockKandangsData = isResponseSuccess(
|
||||||
|
projectFlockKandangResponse
|
||||||
|
)
|
||||||
|
? [projectFlockKandangResponse.data]
|
||||||
|
: [];
|
||||||
|
} else {
|
||||||
|
const projectFlockKandangsResponse =
|
||||||
|
await ProjectFlockKandangApi.getAll({
|
||||||
|
area_id: selectedArea?.value,
|
||||||
|
project_flock_id: selectedProjectFlock?.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
projectFlockKandangsData = isResponseSuccess(
|
||||||
|
projectFlockKandangsResponse
|
||||||
|
)
|
||||||
|
? projectFlockKandangsResponse.data
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappedProductionResults: {
|
||||||
|
projectFlockKandang: BaseProjectFlockKandang;
|
||||||
|
productionResult: ProductionResult[] | null;
|
||||||
|
}[] = await Promise.all(
|
||||||
|
projectFlockKandangsData.map(async (projectFlockKandang) => {
|
||||||
|
const getProductionResultPath = `${ProductionResultReportApi.basePath}/${projectFlockKandang.id}?page=1&limit=100`;
|
||||||
|
const getProductionResultRes = await httpClient<
|
||||||
|
BaseApiResponse<ProductionResult[]>
|
||||||
|
>(getProductionResultPath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
projectFlockKandang,
|
||||||
|
productionResult: isResponseSuccess(getProductionResultRes)
|
||||||
|
? getProductionResultRes.data
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mappedProductionResults.length === 0) {
|
||||||
|
toast.error('Tidak ada data untuk diexport.');
|
||||||
|
setIsLoadingExportingToPdf(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const openPdf = async () => {
|
||||||
|
const productionResultPdfBlob = await pdf(
|
||||||
|
<ProductionResultReportPDF
|
||||||
|
mappedProductionResults={mappedProductionResults}
|
||||||
|
/>
|
||||||
|
).toBlob();
|
||||||
|
|
||||||
|
const productionResultReportPdfUrl = URL.createObjectURL(
|
||||||
|
productionResultPdfBlob
|
||||||
|
);
|
||||||
|
window.open(productionResultReportPdfUrl, '_blank');
|
||||||
|
};
|
||||||
|
|
||||||
|
await openPdf();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error('Gagal melakukan export laporan hasil produksi! Coba lagi.');
|
||||||
|
}
|
||||||
|
// await ProductionResultReportApi.exportProductionResultToPdf(
|
||||||
|
// projectFlockKandangs
|
||||||
|
// );
|
||||||
|
|
||||||
|
setIsLoadingExportingToPdf(false);
|
||||||
|
};
|
||||||
|
|
||||||
const searchHandler = async () => {
|
const searchHandler = async () => {
|
||||||
setProjectFlockKandangs(null);
|
setProjectFlockKandangs(null);
|
||||||
setIsLoadingSearch(true);
|
setIsLoadingSearch(true);
|
||||||
@@ -355,6 +446,13 @@ const ProductionResultContent = () => {
|
|||||||
onClick={exportToExcelHandler}
|
onClick={exportToExcelHandler}
|
||||||
className='text-nowrap'
|
className='text-nowrap'
|
||||||
/>
|
/>
|
||||||
|
<MenuItem
|
||||||
|
title='Export to PDF'
|
||||||
|
icon='icon-park-outline:file-pdf-one'
|
||||||
|
isLoading={isLoadingExportingToPdf}
|
||||||
|
onClick={exportToPdfHandler}
|
||||||
|
className='text-nowrap'
|
||||||
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,388 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Document,
|
||||||
|
Page,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
Image,
|
||||||
|
} from '@react-pdf/renderer';
|
||||||
|
|
||||||
|
import { formatDate, formatNumber } from '@/lib/helper';
|
||||||
|
import { BaseProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
|
||||||
|
import { ProductionResult } from '@/types/api/report/production-result';
|
||||||
|
|
||||||
|
type MappedProductionResultsItem = {
|
||||||
|
projectFlockKandang: BaseProjectFlockKandang;
|
||||||
|
productionResult: ProductionResult[] | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ProductionResultReportPDFProps {
|
||||||
|
mappedProductionResults?: MappedProductionResultsItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
page: {
|
||||||
|
paddingTop: 24,
|
||||||
|
paddingBottom: 52,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
companyInfoHeader: {
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
companyLogo: {
|
||||||
|
width: 64,
|
||||||
|
height: 'auto',
|
||||||
|
},
|
||||||
|
companyInfoHeaderDate: {
|
||||||
|
paddingTop: 8,
|
||||||
|
fontSize: 10,
|
||||||
|
},
|
||||||
|
companyName: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
companyAddress: {
|
||||||
|
fontSize: 8,
|
||||||
|
maxWidth: 420,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
doubleDivider: {
|
||||||
|
width: '100%',
|
||||||
|
height: 6,
|
||||||
|
borderTopWidth: 2,
|
||||||
|
borderTopColor: '#000',
|
||||||
|
borderBottomWidth: 2,
|
||||||
|
borderBottomColor: '#000',
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
marginTop: 14,
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: '150%',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontFamily: 'Times-Roman',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
|
||||||
|
footer: {
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
position: 'absolute',
|
||||||
|
fontSize: 8,
|
||||||
|
bottom: 22,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
textAlign: 'center',
|
||||||
|
color: 'grey',
|
||||||
|
},
|
||||||
|
|
||||||
|
section: {
|
||||||
|
marginTop: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#000',
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
sectionHeader: {
|
||||||
|
marginBottom: 6,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'baseline',
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
sectionSubtitle: {
|
||||||
|
fontSize: 8,
|
||||||
|
color: '#444',
|
||||||
|
},
|
||||||
|
|
||||||
|
// Simple grid table (label/value pairs)
|
||||||
|
grid: {
|
||||||
|
width: '100%',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#000',
|
||||||
|
},
|
||||||
|
gridRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#000',
|
||||||
|
},
|
||||||
|
gridRowLast: {
|
||||||
|
borderBottomWidth: 0,
|
||||||
|
},
|
||||||
|
gridCellLabel: {
|
||||||
|
width: '40%',
|
||||||
|
paddingVertical: 3,
|
||||||
|
paddingHorizontal: 6,
|
||||||
|
fontSize: 8,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
gridCellValue: {
|
||||||
|
width: '60%',
|
||||||
|
paddingVertical: 3,
|
||||||
|
paddingHorizontal: 6,
|
||||||
|
fontSize: 8,
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
|
||||||
|
// Subsection headings
|
||||||
|
groupTitle: {
|
||||||
|
marginTop: 8,
|
||||||
|
marginBottom: 4,
|
||||||
|
fontSize: 9,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
|
||||||
|
emptyText: {
|
||||||
|
fontSize: 8,
|
||||||
|
color: '#666',
|
||||||
|
fontStyle: 'italic',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function safeNum(v: unknown): number {
|
||||||
|
const n = typeof v === 'number' ? v : Number(v);
|
||||||
|
return Number.isFinite(n) ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function valueText(v: unknown) {
|
||||||
|
if (v === null || v === undefined) return '-';
|
||||||
|
if (typeof v === 'number') return formatNumber(v);
|
||||||
|
return String(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render label/value table for one ProductionResult.
|
||||||
|
* Uses a compact grid to keep page readable.
|
||||||
|
*/
|
||||||
|
function ProductionResultGrid({ pr }: { pr: ProductionResult }) {
|
||||||
|
const rows: Array<[string, string]> = [
|
||||||
|
['WOA', valueText(pr.woa)],
|
||||||
|
|
||||||
|
// BW
|
||||||
|
['BW', valueText(pr.bw)],
|
||||||
|
['Std BW', valueText(pr.std_bw)],
|
||||||
|
['Uniformity', valueText(pr.uniformity)],
|
||||||
|
['Std Uniformity', valueText(pr.std_uniformity)],
|
||||||
|
|
||||||
|
// Dep
|
||||||
|
['Dep Kum', valueText(pr.dep_kum)],
|
||||||
|
['Dep Std', valueText(pr.dep_std)],
|
||||||
|
|
||||||
|
// Butiran
|
||||||
|
['Butiran Utuh', valueText(pr.butiran_utuh)],
|
||||||
|
['Butiran Putih', valueText(pr.butiran_putih)],
|
||||||
|
['Butiran Retak', valueText(pr.butiran_retak)],
|
||||||
|
['Butiran Pecah', valueText(pr.butiran_pecah)],
|
||||||
|
['Butiran Jumlah', valueText(pr.butiran_jumlah)],
|
||||||
|
['Total Butir', valueText(pr.total_butir)],
|
||||||
|
|
||||||
|
// Kg
|
||||||
|
['Kg Utuh', valueText(pr.kg_utuh)],
|
||||||
|
['Kg Putih', valueText(pr.kg_putih)],
|
||||||
|
['Kg Retak', valueText(pr.kg_retak)],
|
||||||
|
['Kg Pecah', valueText(pr.kg_pecah)],
|
||||||
|
['Kg Jumlah', valueText(pr.kg_jumlah)],
|
||||||
|
['Total Kg', valueText(pr.total_kg)],
|
||||||
|
|
||||||
|
// %
|
||||||
|
['% Utuh', valueText(pr.persen_utuh)],
|
||||||
|
['% Putih', valueText(pr.persen_putih)],
|
||||||
|
['% Retak', valueText(pr.persen_retak)],
|
||||||
|
['% Pecah', valueText(pr.persen_pecah)],
|
||||||
|
|
||||||
|
// Produksi
|
||||||
|
['HD', valueText(pr.hd)],
|
||||||
|
['HD Std', valueText(pr.hd_std)],
|
||||||
|
['FI', valueText(pr.fi)],
|
||||||
|
['FI Std', valueText(pr.fi_std)],
|
||||||
|
['EM', valueText(pr.em)],
|
||||||
|
['EM Std', valueText(pr.em_std)],
|
||||||
|
['EW', valueText(pr.ew)],
|
||||||
|
['EW Std', valueText(pr.ew_std)],
|
||||||
|
['FCR', valueText(pr.fcr)],
|
||||||
|
['FCR Std', valueText(pr.fcr_std)],
|
||||||
|
['HH', valueText(pr.hh)],
|
||||||
|
['HH Std', valueText(pr.hh_std)],
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.grid}>
|
||||||
|
{rows.map(([label, value], idx) => {
|
||||||
|
const isLast = idx === rows.length - 1;
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={label}
|
||||||
|
style={[styles.gridRow, ...(isLast ? [styles.gridRowLast] : [])]}
|
||||||
|
>
|
||||||
|
<Text style={styles.gridCellLabel}>{label}</Text>
|
||||||
|
<Text style={styles.gridCellValue}>{value}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there are multiple ProductionResult entries for a kandang,
|
||||||
|
* we show them sequentially with a small header per result.
|
||||||
|
*
|
||||||
|
* You can later change this to render only the latest WOA, or group by week.
|
||||||
|
*/
|
||||||
|
function ProductionResultList({
|
||||||
|
productionResults,
|
||||||
|
}: {
|
||||||
|
productionResults: ProductionResult[];
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{productionResults.map((pr, idx) => {
|
||||||
|
const kandangName =
|
||||||
|
pr.project_flock?.kandang?.name ||
|
||||||
|
pr.project_flock?.kandang?.id?.toString() ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
// Optional: show a compact subheader
|
||||||
|
const headerLeft = `Data #${idx + 1}`;
|
||||||
|
const headerRight =
|
||||||
|
kandangName && pr.woa !== undefined
|
||||||
|
? `${kandangName} • WOA ${safeNum(pr.woa)}`
|
||||||
|
: pr.woa !== undefined
|
||||||
|
? `WOA ${safeNum(pr.woa)}`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={`${pr.project_flock?.id ?? 'pf'}-${idx}`}
|
||||||
|
style={{ marginTop: idx === 0 ? 0 : 10 }}
|
||||||
|
wrap={false}
|
||||||
|
>
|
||||||
|
<View style={styles.sectionHeader}>
|
||||||
|
<Text style={styles.sectionTitle}>{headerLeft}</Text>
|
||||||
|
<Text style={styles.sectionSubtitle}>{headerRight}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<ProductionResultGrid pr={pr} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ✅ Main PDF Component
|
||||||
|
*/
|
||||||
|
const ProductionResultReportPDF = ({
|
||||||
|
mappedProductionResults = [],
|
||||||
|
}: ProductionResultReportPDFProps) => {
|
||||||
|
return (
|
||||||
|
<Document>
|
||||||
|
<Page style={styles.page} size='A4'>
|
||||||
|
{/* Header */}
|
||||||
|
<View>
|
||||||
|
<View style={styles.companyInfoHeader}>
|
||||||
|
<Image style={styles.companyLogo} src='/assets/img/lti-logo.png' />
|
||||||
|
<Text style={styles.companyInfoHeaderDate}>
|
||||||
|
{formatDate(Date.now(), 'DD MMMM YYYY')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<Text style={styles.companyName}>PT LUMBUNG TELUR INDONESIA</Text>
|
||||||
|
<Text style={styles.companyAddress}>
|
||||||
|
SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
|
||||||
|
Cipedes, Kec. Sukajadi, Kota Bandung 40162
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={styles.doubleDivider} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text style={styles.title}>Laporan Production Result</Text>
|
||||||
|
|
||||||
|
{/* Sections per ProjectFlockKandang */}
|
||||||
|
{mappedProductionResults.length === 0 ? (
|
||||||
|
<View style={{ marginTop: 16 }}>
|
||||||
|
<Text style={styles.emptyText}>Tidak ada data.</Text>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
mappedProductionResults.map((item, idx) => {
|
||||||
|
const pfk = item.projectFlockKandang;
|
||||||
|
|
||||||
|
// Try to display meaningful identifiers.
|
||||||
|
// Adjust these fields based on your real BaseProjectFlockKandang structure.
|
||||||
|
const kandangName =
|
||||||
|
pfk?.kandang?.name ?? `Kandang #${pfk?.kandang_id ?? idx + 1}`;
|
||||||
|
|
||||||
|
const projectName = pfk?.project_flock?.name ?? '';
|
||||||
|
|
||||||
|
const locationName = pfk?.project_flock?.location?.name ?? '';
|
||||||
|
|
||||||
|
const areaName = pfk?.project_flock?.area?.name ?? '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={`pfk-${pfk?.id ?? idx}`}
|
||||||
|
style={styles.section}
|
||||||
|
break={idx > 0} // each kandang starts on a new page for clarity
|
||||||
|
>
|
||||||
|
<View style={styles.sectionHeader}>
|
||||||
|
<Text style={styles.sectionTitle}>
|
||||||
|
{projectName
|
||||||
|
? `${projectName} • ${kandangName}`
|
||||||
|
: kandangName}
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.sectionSubtitle}>
|
||||||
|
{[areaName, locationName].filter(Boolean).join(' • ')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{item.productionResult && item.productionResult.length > 0 ? (
|
||||||
|
<ProductionResultList
|
||||||
|
productionResults={item.productionResult}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.emptyText}>
|
||||||
|
Tidak ada production result untuk kandang ini.
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<View style={styles.footer} fixed>
|
||||||
|
<Text
|
||||||
|
render={({ pageNumber, totalPages }) =>
|
||||||
|
`${pageNumber} / ${totalPages}`
|
||||||
|
}
|
||||||
|
fixed
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</Page>
|
||||||
|
</Document>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductionResultReportPDF;
|
||||||
+22
-18
@@ -10,61 +10,65 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [
|
|||||||
text: 'Daily Checklist',
|
text: 'Daily Checklist',
|
||||||
link: '/daily-checklist',
|
link: '/daily-checklist',
|
||||||
icon: 'heroicons-outline:clipboard-check',
|
icon: 'heroicons-outline:clipboard-check',
|
||||||
// TODO: add permission
|
permission: [
|
||||||
// permission: ['lti.daily_checklist.list'],
|
'lti.daily_checklist.dashboard.list',
|
||||||
|
'lti.daily_checklist.create',
|
||||||
|
'lti.daily_checklist.list',
|
||||||
|
'lti.daily_checklist.detail',
|
||||||
|
'lti.daily_checklist.reports',
|
||||||
|
'lti.daily_checklist.master_data.employee',
|
||||||
|
'lti.daily_checklist.master_data.activity',
|
||||||
|
'lti.daily_checklist.master_data.configuration',
|
||||||
|
],
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
text: 'Dashboard',
|
text: 'Dashboard',
|
||||||
link: '/daily-checklist/dashboard',
|
link: '/daily-checklist/dashboard',
|
||||||
icon: 'lucide:layout-dashboard',
|
icon: 'lucide:layout-dashboard',
|
||||||
// TODO: add permission
|
permission: ['lti.daily_checklist.dashboard.list'],
|
||||||
// permission: ['lti.daily_checklist.list'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Daily Checklist',
|
text: 'Daily Checklist',
|
||||||
link: '/daily-checklist/daily-checklist',
|
link: '/daily-checklist/daily-checklist',
|
||||||
icon: 'lucide:clipboard-check',
|
icon: 'lucide:clipboard-check',
|
||||||
// TODO: add permission
|
permission: ['lti.daily_checklist.create'],
|
||||||
// permission: ['lti.daily_checklist.list'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Daftar Daily Checklist',
|
text: 'Daftar Daily Checklist',
|
||||||
link: '/daily-checklist/list-daily-checklist',
|
link: '/daily-checklist/list-daily-checklist',
|
||||||
icon: 'lucide:circle-check',
|
icon: 'lucide:circle-check',
|
||||||
// TODO: add permission
|
permission: ['lti.daily_checklist.list'],
|
||||||
// permission: ['lti.daily_checklist.list'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Laporan',
|
text: 'Laporan',
|
||||||
link: '/daily-checklist/reports',
|
link: '/daily-checklist/reports',
|
||||||
icon: 'lucide:file-text',
|
icon: 'lucide:file-text',
|
||||||
// TODO: add permission
|
permission: ['lti.daily_checklist.reports'],
|
||||||
// permission: ['lti.daily_checklist.list'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Master Data',
|
text: 'Master Data',
|
||||||
link: '/daily-checklist/master-data',
|
link: '/daily-checklist/master-data',
|
||||||
icon: 'lucide:database',
|
icon: 'lucide:database',
|
||||||
// TODO: add permission
|
permission: [
|
||||||
// permission: ['lti.daily_checklist.list'],
|
'lti.daily_checklist.master_data.employee',
|
||||||
|
'lti.daily_checklist.master_data.activity',
|
||||||
|
'lti.daily_checklist.master_data.configuration',
|
||||||
|
],
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
text: 'Employee (ABK)',
|
text: 'Employee (ABK)',
|
||||||
link: '/daily-checklist/master-data/employee',
|
link: '/daily-checklist/master-data/employee',
|
||||||
// TODO: add permission
|
permission: ['lti.daily_checklist.master_data.employee'],
|
||||||
// permission: ['lti.daily_checklist.list'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Aktivitas',
|
text: 'Aktivitas',
|
||||||
link: '/daily-checklist/master-data/activity',
|
link: '/daily-checklist/master-data/activity',
|
||||||
// TODO: add permission
|
permission: ['lti.daily_checklist.master_data.activity'],
|
||||||
// permission: ['lti.daily_checklist.list'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Konfigurasi',
|
text: 'Konfigurasi',
|
||||||
link: '/daily-checklist/master-data/configuration',
|
link: '/daily-checklist/master-data/configuration',
|
||||||
// TODO: add permission
|
permission: ['lti.daily_checklist.master_data.configuration'],
|
||||||
// permission: ['lti.daily_checklist.list'],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user