import type { PaymentReport } from '@/types/services/analytic' export class ExcelExportPaymentService { /** * Export Payment Method Report to Excel */ static async exportPaymentMethodToExcel(paymentData: PaymentReport, filename?: string) { try { // Dynamic import untuk xlsx library const XLSX = await import('xlsx') // Prepare data untuk Excel const worksheetData: any[][] = [] // Header dengan report info (baris 1-2) worksheetData.push(['LAPORAN METODE PEMBAYARAN']) // Row 0 - Main title worksheetData.push([`Periode: ${paymentData.date_from.split('T')[0]} - ${paymentData.date_to.split('T')[0]}`]) // Row 1 - Period worksheetData.push([]) // Empty row // Add Summary Section worksheetData.push(['RINGKASAN PERIODE']) // Section header worksheetData.push([]) // Empty row const summaryData = [ ['Total Amount:', `Rp ${paymentData.summary.total_amount.toLocaleString('id-ID')}`], ['Total Orders:', paymentData.summary.total_orders.toString()], ['Total Payments:', paymentData.summary.total_payments.toString()], ['Average Order Value:', `Rp ${paymentData.summary.average_order_value.toLocaleString('id-ID')}`] ] summaryData.forEach(row => { worksheetData.push([row[0], row[1]]) // Only 2 columns needed }) worksheetData.push([]) // Empty row worksheetData.push([]) // Empty row // Payment Method Details Section Header worksheetData.push(['RINCIAN METODE PEMBAYARAN']) // Section header worksheetData.push([]) // Empty row // Header row untuk tabel payment method data const headerRow = ['No', 'Metode Pembayaran', 'Tipe', 'Jumlah Order', 'Total Amount', 'Persentase'] worksheetData.push(headerRow) // Add payment method data rows paymentData.data.forEach((payment, index) => { const rowData = [ index + 1, // No payment.payment_method_name, payment.payment_method_type.toUpperCase(), payment.order_count, payment.total_amount, // Store as number for Excel formatting `${(payment.percentage ?? 0).toFixed(1)}%` ] worksheetData.push(rowData) }) // Add total row const totalRow = ['TOTAL', '', '', paymentData.summary.total_orders, paymentData.summary.total_amount, '100.0%'] worksheetData.push(totalRow) // Create workbook dan worksheet const workbook = XLSX.utils.book_new() const worksheet = XLSX.utils.aoa_to_sheet(worksheetData) // Apply basic formatting this.applyBasicFormatting(worksheet, worksheetData.length, XLSX) // Add worksheet ke workbook XLSX.utils.book_append_sheet(workbook, worksheet, 'Metode Pembayaran') // Generate filename const exportFilename = filename || this.generateFilename('Metode_Pembayaran') // Download file XLSX.writeFile(workbook, exportFilename) return { success: true, filename: exportFilename } } catch (error) { console.error('Error exporting to Excel:', error) return { success: false, error: 'Failed to export Excel file' } } } /** * Apply basic formatting (SheetJS compatible) */ private static applyBasicFormatting(worksheet: any, totalRows: number, XLSX: any) { // Set column widths const colWidths = [ { wch: 8 }, // No { wch: 25 }, // Metode Pembayaran { wch: 12 }, // Tipe { wch: 15 }, // Jumlah Order { wch: 20 }, // Total Amount { wch: 12 } // Persentase ] worksheet['!cols'] = colWidths // Set row heights for better spacing worksheet['!rows'] = [ { hpt: 30 }, // Title row { hpt: 25 }, // Period row { hpt: 15 }, // Empty row { hpt: 25 }, // Summary header { hpt: 15 } // Empty row ] // Merge cells untuk headers const merges = [ { s: { r: 0, c: 0 }, e: { r: 0, c: 5 } }, // Title (span across all columns) { s: { r: 1, c: 0 }, e: { r: 1, c: 5 } }, // Period (span across all columns) { s: { r: 3, c: 0 }, e: { r: 3, c: 5 } } // Summary header (span across all columns) ] // Find and add merge for payment method details header for (let i = 0; i < totalRows; i++) { const cell = worksheet[XLSX.utils.encode_cell({ r: i, c: 0 })] if (cell && cell.v === 'RINCIAN METODE PEMBAYARAN') { merges.push({ s: { r: i, c: 0 }, e: { r: i, c: 5 } }) // Span across all columns break } } worksheet['!merges'] = merges // Apply number formatting untuk currency cells this.applyNumberFormatting(worksheet, totalRows, XLSX) } /** * Apply number formatting for currency and styling */ private static applyNumberFormatting(worksheet: any, totalRows: number, XLSX: any) { // Find table data start (after header row) let dataStartRow = -1 for (let i = 0; i < totalRows; i++) { const cell = worksheet[XLSX.utils.encode_cell({ r: i, c: 0 })] if (cell && cell.v === 'No') { dataStartRow = i + 1 break } } if (dataStartRow === -1) return // Count actual data rows (excluding total row) const dataRowsCount = totalRows - dataStartRow - 1 // -1 for total row // Apply currency formatting to Total Amount column (column 4 - index 4) for (let row = dataStartRow; row <= dataStartRow + dataRowsCount; row++) { // Include total row const cellAddress = XLSX.utils.encode_cell({ r: row, c: 4 }) // Total Amount column const cell = worksheet[cellAddress] if (cell && typeof cell.v === 'number') { // Apply Indonesian currency format cell.z = '#,##0' cell.t = 'n' } } // Apply styling to header row const headerRowIndex = dataStartRow - 1 for (let col = 0; col < 6; col++) { const cellAddress = XLSX.utils.encode_cell({ r: headerRowIndex, c: col }) const cell = worksheet[cellAddress] if (cell) { // Apply bold formatting (basic approach for SheetJS) cell.s = { font: { bold: true }, fill: { fgColor: { rgb: 'F3F4F6' } }, // Light gray background border: { bottom: { style: 'medium', color: { rgb: '000000' } } } } } } // Apply styling to total row const totalRowIndex = dataStartRow + dataRowsCount for (let col = 0; col < 6; col++) { const cellAddress = XLSX.utils.encode_cell({ r: totalRowIndex, c: col }) const cell = worksheet[cellAddress] if (cell) { // Apply bold formatting for total row cell.s = { font: { bold: true }, border: { top: { style: 'medium', color: { rgb: '000000' } } } } } } } /** * Generate filename with timestamp */ private static generateFilename(prefix: string): string { const now = new Date() const year = now.getFullYear() const month = (now.getMonth() + 1).toString().padStart(2, '0') const day = now.getDate().toString().padStart(2, '0') const hour = now.getHours().toString().padStart(2, '0') const minute = now.getMinutes().toString().padStart(2, '0') return `${prefix}_${year}_${month}_${day}_${hour}${minute}.xlsx` } /** * Export Payment Method data with custom configuration */ static async exportCustomPaymentData( data: any[][], sheetName: string = 'Payment Method', filename?: string, options?: { colWidths?: { wch: number }[] merges?: { s: { r: number; c: number }; e: { r: number; c: number } }[] } ) { try { const XLSX = await import('xlsx') const workbook = XLSX.utils.book_new() const worksheet = XLSX.utils.aoa_to_sheet(data) // Apply options if (options?.colWidths) { worksheet['!cols'] = options.colWidths } if (options?.merges) { worksheet['!merges'] = options.merges } XLSX.utils.book_append_sheet(workbook, worksheet, sheetName) const exportFilename = filename || this.generateFilename('Payment_Method_Export') XLSX.writeFile(workbook, exportFilename) return { success: true, filename: exportFilename } } catch (error) { console.error('Error exporting to Excel:', error) return { success: false, error: 'Failed to export Excel file' } } } }