FPSMS-frontend
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 

212 righe
6.7 KiB

  1. "use client";
  2. import React, { useState, useEffect } from 'react';
  3. import { useTranslation } from "react-i18next";
  4. import {
  5. Dialog,
  6. DialogTitle,
  7. DialogContent,
  8. DialogActions,
  9. Button,
  10. Table,
  11. TableBody,
  12. TableCell,
  13. TableContainer,
  14. TableHead,
  15. TableRow,
  16. Paper,
  17. Chip,
  18. Typography,
  19. } from '@mui/material';
  20. import DownloadIcon from '@mui/icons-material/Download';
  21. import {
  22. fetchSemiFGItemCodes,
  23. fetchSemiFGItemCodesWithCategory,
  24. generateSemiFGProductionAnalysisReport,
  25. generateSemiFGProductionAnalysisReportExcel,
  26. ItemCodeWithCategory,
  27. } from './semiFGProductionAnalysisApi';
  28. interface SemiFGProductionAnalysisReportProps {
  29. criteria: Record<string, string>;
  30. requiredFieldLabels: string[];
  31. loading: boolean;
  32. setLoading: (loading: boolean) => void;
  33. reportTitle?: string;
  34. onExportSuccess?: (format: "pdf" | "excel") => void;
  35. }
  36. export default function SemiFGProductionAnalysisReport({
  37. criteria,
  38. requiredFieldLabels,
  39. loading,
  40. setLoading,
  41. reportTitle = '成品/半成品生產分析報告',
  42. onExportSuccess,
  43. }: SemiFGProductionAnalysisReportProps) {
  44. const { t } = useTranslation("report");
  45. const [showConfirmDialog, setShowConfirmDialog] = useState(false);
  46. const [selectedItemCodesInfo, setSelectedItemCodesInfo] = useState<ItemCodeWithCategory[]>([]);
  47. const [itemCodesWithCategory, setItemCodesWithCategory] = useState<Record<string, ItemCodeWithCategory>>({});
  48. const [exportFormat, setExportFormat] = useState<'pdf' | 'excel'>('pdf');
  49. // Fetch item codes with category when stockCategory changes
  50. useEffect(() => {
  51. const stockCategory = criteria.stockCategory || '';
  52. if (stockCategory) {
  53. fetchSemiFGItemCodesWithCategory(stockCategory)
  54. .then((items) => {
  55. const categoryMap: Record<string, ItemCodeWithCategory> = {};
  56. items.forEach((item) => {
  57. categoryMap[item.code] = item;
  58. });
  59. setItemCodesWithCategory((prev) => ({ ...prev, ...categoryMap }));
  60. })
  61. .catch((error) => {
  62. console.error('Failed to fetch item codes with category:', error);
  63. });
  64. }
  65. }, [criteria.stockCategory]);
  66. const handleExportClick = async (format: 'pdf' | 'excel') => {
  67. setExportFormat(format);
  68. // Validate required fields
  69. if (requiredFieldLabels.length > 0) {
  70. alert(t('missingRequired', { fields: requiredFieldLabels.join('\n- ') }));
  71. return;
  72. }
  73. // If no itemCode is selected, export directly without confirmation
  74. if (!criteria.itemCode) {
  75. await executeExport(format);
  76. return;
  77. }
  78. // If itemCode is selected, show confirmation dialog
  79. const selectedCodes = criteria.itemCode.split(',').filter((code) => code.trim());
  80. const itemCodesInfo: ItemCodeWithCategory[] = selectedCodes.map((code) => {
  81. const codeTrimmed = code.trim();
  82. const categoryInfo = itemCodesWithCategory[codeTrimmed];
  83. return {
  84. code: codeTrimmed,
  85. category: categoryInfo?.category || 'Unknown',
  86. name: categoryInfo?.name || '',
  87. };
  88. });
  89. setSelectedItemCodesInfo(itemCodesInfo);
  90. setShowConfirmDialog(true);
  91. };
  92. const executeExport = async (format: 'pdf' | 'excel' = exportFormat) => {
  93. setLoading(true);
  94. try {
  95. if (format === 'excel') {
  96. await generateSemiFGProductionAnalysisReportExcel(criteria, reportTitle);
  97. } else {
  98. await generateSemiFGProductionAnalysisReport(criteria, reportTitle);
  99. }
  100. onExportSuccess?.(format);
  101. setShowConfirmDialog(false);
  102. } catch (error) {
  103. console.error('Failed to generate report:', error);
  104. alert(t('generateError'));
  105. } finally {
  106. setLoading(false);
  107. }
  108. };
  109. return (
  110. <>
  111. <div style={{ display: 'flex', gap: 16 }}>
  112. <Button
  113. variant="contained"
  114. size="large"
  115. startIcon={<DownloadIcon />}
  116. onClick={() => handleExportClick('pdf')}
  117. disabled={loading}
  118. sx={{ px: 4 }}
  119. >
  120. {loading ? t('generatingPdf') : t('downloadPdf')}
  121. </Button>
  122. <Button
  123. variant="outlined"
  124. size="large"
  125. startIcon={<DownloadIcon />}
  126. onClick={() => handleExportClick('excel')}
  127. disabled={loading}
  128. sx={{ px: 4 }}
  129. >
  130. {loading ? t('generatingExcel') : t('downloadExcel')}
  131. </Button>
  132. </div>
  133. {/* Confirmation Dialog for 成品/半成品生產分析報告 */}
  134. <Dialog
  135. open={showConfirmDialog}
  136. onClose={() => setShowConfirmDialog(false)}
  137. maxWidth="md"
  138. fullWidth
  139. >
  140. <DialogTitle>
  141. <Typography variant="h6" fontWeight="bold">
  142. {t('semiFgConfirmTitle')}
  143. </Typography>
  144. </DialogTitle>
  145. <DialogContent>
  146. <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
  147. {t('semiFgConfirmHint')}
  148. </Typography>
  149. <TableContainer component={Paper} variant="outlined">
  150. <Table>
  151. <TableHead>
  152. <TableRow>
  153. <TableCell>
  154. <strong>{t('semiFgColItem')}</strong>
  155. </TableCell>
  156. <TableCell>
  157. <strong>{t('semiFgColCategory')}</strong>
  158. </TableCell>
  159. </TableRow>
  160. </TableHead>
  161. <TableBody>
  162. {selectedItemCodesInfo.map((item, index) => {
  163. const displayName = item.name ? `${item.code} ${item.name}` : item.code;
  164. return (
  165. <TableRow key={index}>
  166. <TableCell>{displayName}</TableCell>
  167. <TableCell>
  168. <Chip
  169. label={item.category || 'Unknown'}
  170. color={item.category === 'FG' ? 'primary' : item.category === 'WIP' ? 'secondary' : 'default'}
  171. size="small"
  172. />
  173. </TableCell>
  174. </TableRow>
  175. );
  176. })}
  177. </TableBody>
  178. </Table>
  179. </TableContainer>
  180. </DialogContent>
  181. <DialogActions sx={{ p: 2 }}>
  182. <Button onClick={() => setShowConfirmDialog(false)}>{t('cancel')}</Button>
  183. <Button
  184. variant="contained"
  185. onClick={() => executeExport()}
  186. disabled={loading}
  187. startIcon={<DownloadIcon />}
  188. >
  189. {loading
  190. ? exportFormat === 'excel'
  191. ? t('generatingExcel')
  192. : t('generatingPdf')
  193. : exportFormat === 'excel'
  194. ? t('confirmDownloadExcel')
  195. : t('confirmDownloadPdf')}
  196. </Button>
  197. </DialogActions>
  198. </Dialog>
  199. </>
  200. );
  201. }