| @@ -32,6 +32,7 @@ interface SemiFGProductionAnalysisReportProps { | |||||
| loading: boolean; | loading: boolean; | ||||
| setLoading: (loading: boolean) => void; | setLoading: (loading: boolean) => void; | ||||
| reportTitle?: string; | reportTitle?: string; | ||||
| onExportSuccess?: (format: "pdf" | "excel") => void; | |||||
| } | } | ||||
| export default function SemiFGProductionAnalysisReport({ | export default function SemiFGProductionAnalysisReport({ | ||||
| @@ -40,6 +41,7 @@ export default function SemiFGProductionAnalysisReport({ | |||||
| loading, | loading, | ||||
| setLoading, | setLoading, | ||||
| reportTitle = '成品/半成品生產分析報告', | reportTitle = '成品/半成品生產分析報告', | ||||
| onExportSuccess, | |||||
| }: SemiFGProductionAnalysisReportProps) { | }: SemiFGProductionAnalysisReportProps) { | ||||
| const [showConfirmDialog, setShowConfirmDialog] = useState(false); | const [showConfirmDialog, setShowConfirmDialog] = useState(false); | ||||
| const [selectedItemCodesInfo, setSelectedItemCodesInfo] = useState<ItemCodeWithCategory[]>([]); | const [selectedItemCodesInfo, setSelectedItemCodesInfo] = useState<ItemCodeWithCategory[]>([]); | ||||
| @@ -101,6 +103,7 @@ export default function SemiFGProductionAnalysisReport({ | |||||
| } else { | } else { | ||||
| await generateSemiFGProductionAnalysisReport(criteria, reportTitle); | await generateSemiFGProductionAnalysisReport(criteria, reportTitle); | ||||
| } | } | ||||
| onExportSuccess?.(format); | |||||
| setShowConfirmDialog(false); | setShowConfirmDialog(false); | ||||
| } catch (error) { | } catch (error) { | ||||
| console.error('Failed to generate report:', error); | console.error('Failed to generate report:', error); | ||||
| @@ -27,6 +27,11 @@ import { | |||||
| fetchSemiFGItemCodesWithCategory | fetchSemiFGItemCodesWithCategory | ||||
| } from './semiFGProductionAnalysisApi'; | } from './semiFGProductionAnalysisApi'; | ||||
| import { generateGrnReportExcel } from './grnReportApi'; | import { generateGrnReportExcel } from './grnReportApi'; | ||||
| import { | |||||
| FEATURE_USAGE, | |||||
| FEATURE_USAGE_ACTION, | |||||
| logFeatureUsage, | |||||
| } from '@/lib/featureUsageLog'; | |||||
| interface ItemCodeWithName { | interface ItemCodeWithName { | ||||
| code: string; | code: string; | ||||
| @@ -138,6 +143,10 @@ export default function ReportPage() { | |||||
| setDynamicOptions({}); | setDynamicOptions({}); | ||||
| }, [selectedReportId]); | }, [selectedReportId]); | ||||
| useEffect(() => { | |||||
| logFeatureUsage(FEATURE_USAGE.REPORT_MANAGEMENT, FEATURE_USAGE_ACTION.PAGE_VIEW); | |||||
| }, []); | |||||
| const validateRequiredFields = () => { | const validateRequiredFields = () => { | ||||
| if (!currentReport) return true; | if (!currentReport) return true; | ||||
| @@ -220,6 +229,13 @@ export default function ReportPage() { | |||||
| link.remove(); | link.remove(); | ||||
| window.URL.revokeObjectURL(downloadUrl); | window.URL.revokeObjectURL(downloadUrl); | ||||
| } | } | ||||
| if (currentReport) { | |||||
| logFeatureUsage( | |||||
| FEATURE_USAGE.REPORT_MANAGEMENT, | |||||
| FEATURE_USAGE_ACTION.DOWNLOAD, | |||||
| `${currentReport.id}:excel`, | |||||
| ); | |||||
| } | |||||
| setShowConfirmDialog(false); | setShowConfirmDialog(false); | ||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Failed to generate Excel report:", error); | console.error("Failed to generate Excel report:", error); | ||||
| @@ -265,6 +281,12 @@ export default function ReportPage() { | |||||
| link.click(); | link.click(); | ||||
| link.remove(); | link.remove(); | ||||
| window.URL.revokeObjectURL(downloadUrl); | window.URL.revokeObjectURL(downloadUrl); | ||||
| logFeatureUsage( | |||||
| FEATURE_USAGE.REPORT_MANAGEMENT, | |||||
| FEATURE_USAGE_ACTION.DOWNLOAD, | |||||
| `${currentReport.id}:pdf`, | |||||
| ); | |||||
| setShowConfirmDialog(false); | setShowConfirmDialog(false); | ||||
| } catch (error) { | } catch (error) { | ||||
| @@ -503,6 +525,13 @@ export default function ReportPage() { | |||||
| loading={loading} | loading={loading} | ||||
| setLoading={setLoading} | setLoading={setLoading} | ||||
| reportTitle={currentReport.title} | reportTitle={currentReport.title} | ||||
| onExportSuccess={(format) => { | |||||
| logFeatureUsage( | |||||
| FEATURE_USAGE.REPORT_MANAGEMENT, | |||||
| FEATURE_USAGE_ACTION.DOWNLOAD, | |||||
| `${currentReport.id}:${format}`, | |||||
| ); | |||||
| }} | |||||
| /> | /> | ||||
| ) : currentReport.id === 'rep-013' || currentReport.id === 'rep-009' || currentReport.id === 'rep-012' || currentReport.id === 'rep-004' || currentReport.id === 'rep-007' || currentReport.id === 'rep-008' || currentReport.id === 'rep-011' ? ( | ) : currentReport.id === 'rep-013' || currentReport.id === 'rep-009' || currentReport.id === 'rep-012' || currentReport.id === 'rep-004' || currentReport.id === 'rep-007' || currentReport.id === 'rep-008' || currentReport.id === 'rep-011' ? ( | ||||
| <> | <> | ||||
| @@ -45,6 +45,11 @@ import TruckRoutingSummaryTab, { TruckRoutingSummaryFilters } from "./TruckRouti | |||||
| import { clientAuthFetch } from "@/app/utils/clientAuthFetch"; | import { clientAuthFetch } from "@/app/utils/clientAuthFetch"; | ||||
| import { NEXT_PUBLIC_API_URL } from "@/config/api"; | import { NEXT_PUBLIC_API_URL } from "@/config/api"; | ||||
| import { fetchTruckRoutingSummaryPrecheck } from "@/app/(main)/report/truckRoutingSummaryApi"; | import { fetchTruckRoutingSummaryPrecheck } from "@/app/(main)/report/truckRoutingSummaryApi"; | ||||
| import { | |||||
| FEATURE_USAGE, | |||||
| FEATURE_USAGE_ACTION, | |||||
| logFeatureUsage, | |||||
| } from "@/lib/featureUsageLog"; | |||||
| interface Props { | interface Props { | ||||
| // pickOrders: PickOrderResult[]; | // pickOrders: PickOrderResult[]; | ||||
| @@ -350,6 +355,11 @@ const [selectedPrinterForDraft, setSelectedPrinterForDraft] = useState<PrinterCo | |||||
| const errorText = await response.text(); | const errorText = await response.text(); | ||||
| throw new Error(`HTTP ${response.status}: ${errorText}`); | throw new Error(`HTTP ${response.status}: ${errorText}`); | ||||
| } | } | ||||
| logFeatureUsage( | |||||
| FEATURE_USAGE.TRUCK_ROUTING_SUMMARY, | |||||
| FEATURE_USAGE_ACTION.PRINT, | |||||
| `direct:${storeId}:${truckLanceCode}:${date}`, | |||||
| ); | |||||
| Swal.fire({ | Swal.fire({ | ||||
| position: "bottom-end", | position: "bottom-end", | ||||
| icon: "success", | icon: "success", | ||||
| @@ -382,6 +392,12 @@ const [selectedPrinterForDraft, setSelectedPrinterForDraft] = useState<PrinterCo | |||||
| fetchReleasedOrderCount(); | fetchReleasedOrderCount(); | ||||
| }, [fetchReleasedOrderCount]); | }, [fetchReleasedOrderCount]); | ||||
| useEffect(() => { | |||||
| if (tabIndex === 5) { | |||||
| logFeatureUsage(FEATURE_USAGE.TRUCK_ROUTING_SUMMARY, FEATURE_USAGE_ACTION.PAGE_VIEW); | |||||
| } | |||||
| }, [tabIndex]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| const onAssigned = () => { | const onAssigned = () => { | ||||
| localStorage.removeItem('hideCompletedUntilNext'); | localStorage.removeItem('hideCompletedUntilNext'); | ||||
| @@ -11,6 +11,11 @@ import { | |||||
| fetchTruckRoutingSummaryPrecheck, | fetchTruckRoutingSummaryPrecheck, | ||||
| ReportOption, | ReportOption, | ||||
| } from "@/app/(main)/report/truckRoutingSummaryApi"; | } from "@/app/(main)/report/truckRoutingSummaryApi"; | ||||
| import { | |||||
| FEATURE_USAGE, | |||||
| FEATURE_USAGE_ACTION, | |||||
| logFeatureUsage, | |||||
| } from "@/lib/featureUsageLog"; | |||||
| export type TruckRoutingSummaryFilters = { | export type TruckRoutingSummaryFilters = { | ||||
| storeId: string; | storeId: string; | ||||
| @@ -94,6 +99,11 @@ const TruckRoutingSummaryTab: React.FC<Props> = ({ onFiltersChange }) => { | |||||
| link.click(); | link.click(); | ||||
| link.remove(); | link.remove(); | ||||
| window.URL.revokeObjectURL(downloadUrl); | window.URL.revokeObjectURL(downloadUrl); | ||||
| logFeatureUsage( | |||||
| FEATURE_USAGE.TRUCK_ROUTING_SUMMARY, | |||||
| FEATURE_USAGE_ACTION.DOWNLOAD, | |||||
| `pdf:${storeId}:${truckLanceCode}:${date}`, | |||||
| ); | |||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Failed to download Truck Routing Summary", error); | console.error("Failed to download Truck Routing Summary", error); | ||||
| alert("下載送貨路線摘要失敗,請稍後再試。"); | alert("下載送貨路線摘要失敗,請稍後再試。"); | ||||
| @@ -111,5 +111,15 @@ | |||||
| "Show Supplier Code": "Show Supplier Code", | "Show Supplier Code": "Show Supplier Code", | ||||
| "Show Purchase Order Codes": "Show Purchase Order Codes", | "Show Purchase Order Codes": "Show Purchase Order Codes", | ||||
| "x/y orders received": "x/y orders received", | "x/y orders received": "x/y orders received", | ||||
| "Goods Receipt Status New": "Goods Receipt Status" | |||||
| "Goods Receipt Status New": "Goods Receipt Status", | |||||
| "Usage statistics": "Usage statistics", | |||||
| "Usage stats description": "Report management and truck routing summary: page views, downloads, and direct prints.", | |||||
| "Usage stats report section": "Report management (報告管理)", | |||||
| "Usage stats truck routing section": "Truck routing summary (送貨路線摘要)", | |||||
| "Usage stats user": "User", | |||||
| "Usage stats page views": "Page views", | |||||
| "Usage stats downloads": "Downloads", | |||||
| "Usage stats prints": "Direct prints", | |||||
| "Usage stats refresh": "Refresh", | |||||
| "Usage stats load error": "Could not load usage statistics." | |||||
| } | } | ||||
| @@ -119,5 +119,15 @@ | |||||
| "Auto-refresh every 5 minutes": "每5分鐘自動刷新", | "Auto-refresh every 5 minutes": "每5分鐘自動刷新", | ||||
| "Auto-refresh every 10 minutes": "每10分鐘自動刷新", | "Auto-refresh every 10 minutes": "每10分鐘自動刷新", | ||||
| "Auto-refresh every 15 minutes": "每15分鐘自動刷新", | "Auto-refresh every 15 minutes": "每15分鐘自動刷新", | ||||
| "Auto-refresh every 1 minute": "每1分鐘自動刷新" | |||||
| "Auto-refresh every 1 minute": "每1分鐘自動刷新", | |||||
| "Usage statistics": "報告管理與送貨路線摘要使用統計", | |||||
| "Usage stats description": "報告管理與送貨路線摘要:進入頁面次數、下載與直接列印次數。", | |||||
| "Usage stats report section": "報告管理", | |||||
| "Usage stats truck routing section": "送貨路線摘要", | |||||
| "Usage stats user": "使用者", | |||||
| "Usage stats page views": "進入頁面次數", | |||||
| "Usage stats downloads": "下載次數", | |||||
| "Usage stats prints": "直接列印次數", | |||||
| "Usage stats refresh": "重新整理", | |||||
| "Usage stats load error": "無法載入使用統計。" | |||||
| } | } | ||||