import { isResponseError } from '@/lib/api-helper'; import { BaseApiService } from '@/services/api/base'; import { httpClient, httpClientFetcher } from '@/services/http/client'; import { BaseApiResponse } from '@/types/api/api-general'; import { Marketing, CreateSalesOrderPayload, UpdateSalesOrderPayload, CreateDeliveryOrderPayload, UpdateDeliveryOrderPayload, } from '@/types/api/marketing/marketing'; import toast from 'react-hot-toast'; import * as XLSX from 'xlsx'; import { formatCurrency, formatDate, formatTitleCase } from '@/lib/helper'; /** * 💡 Helper untuk membuat respons dummy * @param data Data yang akan dimasukkan ke dalam body respons */ const createDummyResponse = (data: T): BaseApiResponse => ({ code: 200, status: 'success', message: 'Data retrieved successfully (MOCK)', data: data, }); export class SalesOrderService extends BaseApiService< Marketing, CreateSalesOrderPayload, UpdateSalesOrderPayload > { constructor(basePath: string = '/marketing') { super(basePath); } /** * Approve single marketing data */ async singleApproval( id: number, action: 'APPROVED' | 'REJECTED', notes?: string ): Promise | undefined> { try { const path = `${this.basePath}/approvals`; return await httpClient>(path, { method: 'POST', body: { action: action, approvable_ids: [id], notes: notes || `${action} marketing ${id}`, }, }); } catch (error) { throw error; } } /** * Bulk approve */ async bulkApprovals( ids: number[], action: 'APPROVED' | 'REJECTED', notes?: string ): Promise | undefined> { try { const path = `${this.basePath}/approvals`; return await httpClient>(path, { method: 'POST', body: { action: action, approvable_ids: ids, notes: notes || `${action} marketing ${ids.join(', ')}`, }, }); } catch (error) { throw error; } } /** * Delivery */ async delivery( id: number, notes?: string ): Promise | undefined> { try { const path = `${this.basePath}/approvals`; return await httpClient>(path, { method: 'POST', body: { action: 'APPROVED', approvable_ids: [id], notes: notes || `Delivery marketing ${id}`, }, }); } catch (error) { throw error; } } } class MarketingExportService extends BaseApiService< Marketing, unknown, unknown > { constructor(basePath: string = '/marketing') { super(basePath); } /** * Export to Excel */ async exportToExcel(initialQueryString: string) { const params = new URLSearchParams(initialQueryString); const queryString = `?${params.toString()}`; try { const marketingData = await httpClientFetcher< BaseApiResponse >(`${this.basePath}${queryString}`); if (isResponseError(marketingData)) { toast.error('Gagal melakukan export marketing! Coba lagi.'); return; } const rows = marketingData.data; const formattedRows = []; for (let i = 0; i < rows.length; i++) { const row = rows[i]; const approval = row.latest_approval; const isRejected = approval?.action === 'REJECTED'; // Calculate grand total from sales_order products const grandTotal = row.sales_order ?.map((product) => product.total_price) .reduce((a, b) => a + b, 0) ?? 0; // Get product names const products = row.sales_order ?.map((product) => product.product_warehouse?.product?.name) .filter(Boolean) .join(', ') ?? ''; formattedRows.push({ 'No. Order': row.so_number, Tanggal: formatDate(row.so_date, 'DD-MM-YYYY'), Status: isRejected ? 'Ditolak' : formatTitleCase(approval?.step_name || ''), Customer: row.customer?.name || '', 'Grand Total': formatCurrency(grandTotal), Products: products, Notes: row.notes || '', }); } const ws = XLSX.utils.json_to_sheet(formattedRows); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'marketing'); // triggers download in browser XLSX.writeFile(wb, 'marketing.xlsx'); } catch (error) { toast.error('Gagal melakukan export marketing! Coba lagi.'); } } } export const SalesOrderApi = new SalesOrderService('/marketing/sales-orders'); export const DeliveryOrderApi = new BaseApiService< Marketing, CreateDeliveryOrderPayload, UpdateDeliveryOrderPayload >('/marketing/delivery-orders'); export const MarketingApi = new MarketingExportService('/marketing');