FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

210 строки
6.7 KiB

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