dealerId = $dealerId; $this->startDate = $startDate; $this->endDate = $endDate; $this->technicianReportService = new TechnicianReportService(); // Get mechanics and prepare headings $this->prepareHeadings(); $this->prepareFilterInfo(); } private function prepareHeadings() { try { $reportData = $this->technicianReportService->getTechnicianReportData( $this->dealerId, $this->startDate, $this->endDate ); $this->mechanics = $reportData['mechanics']; // Build headings - simplified structure $this->headings = [ 'No', 'Nama Pekerjaan', 'Kode Pekerjaan', 'Kategori' ]; // Add mechanic columns (only total, no completed/pending) foreach ($this->mechanics as $mechanic) { $mechanicName = $this->cleanName($mechanic->name); $this->headings[] = $mechanicName; } // Add total column at the end $this->headings[] = 'Total'; } catch (\Exception $e) { Log::error('Error preparing headings: ' . $e->getMessage()); $this->headings = ['Error preparing data']; $this->mechanics = collect(); } } private function prepareFilterInfo() { $this->filterInfo = []; // Dealer filter if ($this->dealerId) { $dealer = \App\Models\Dealer::find($this->dealerId); $dealerName = $dealer ? $dealer->name : 'Unknown Dealer'; $this->filterInfo[] = "Dealer: {$dealerName}"; } else { // Check user access for "Semua Dealer" $user = auth()->user(); if ($user && $user->role_id) { $role = \App\Models\Role::with('dealers')->find($user->role_id); if ($role) { $technicianReportService = new \App\Services\TechnicianReportService(); if ($technicianReportService->isAdminRole($role)) { $this->filterInfo[] = "Dealer: Semua Dealer (Admin)"; } else if ($role->dealers->count() > 0) { $dealerNames = $role->dealers->pluck('name')->implode(', '); $this->filterInfo[] = "Dealer: Semua Dealer (Pivot: {$dealerNames})"; } else { $this->filterInfo[] = "Dealer: Semua Dealer"; } } else { $this->filterInfo[] = "Dealer: Semua Dealer"; } } else { $this->filterInfo[] = "Dealer: Semua Dealer"; } } // Date range filter if ($this->startDate && $this->endDate) { $startDateFormatted = Carbon::parse($this->startDate)->format('d/m/Y'); $endDateFormatted = Carbon::parse($this->endDate)->format('d/m/Y'); $this->filterInfo[] = "Periode: {$startDateFormatted} - {$endDateFormatted}"; } elseif ($this->startDate) { $startDateFormatted = Carbon::parse($this->startDate)->format('d/m/Y'); $this->filterInfo[] = "Tanggal Mulai: {$startDateFormatted}"; } elseif ($this->endDate) { $endDateFormatted = Carbon::parse($this->endDate)->format('d/m/Y'); $this->filterInfo[] = "Tanggal Akhir: {$endDateFormatted}"; } else { $this->filterInfo[] = "Periode: Semua Periode"; } // Export date $exportDate = Carbon::now()->format('d/m/Y H:i:s'); $this->filterInfo[] = "Tanggal Export: {$exportDate}"; } /** * Clean name for Excel compatibility */ private function cleanName($name) { // Remove special characters and limit length $cleaned = preg_replace('/[^a-zA-Z0-9\s]/', '', $name); $cleaned = trim($cleaned); // Limit to 31 characters (Excel sheet name limit) if (strlen($cleaned) > 31) { $cleaned = substr($cleaned, 0, 31); } return $cleaned ?: 'Unknown'; } public function collection() { try { $reportData = $this->technicianReportService->getTechnicianReportData( $this->dealerId, $this->startDate, $this->endDate ); $data = []; $no = 1; $columnTotals = []; foreach ($this->mechanics as $mechanic) { $columnTotals["mechanic_{$mechanic->id}_total"] = 0; } $columnTotals['row_total'] = 0; foreach ($reportData['data'] as $row) { $rowTotal = 0; $exportRow = [ $no++, $row['work_name'], $row['work_code'], $row['category_name'] ]; foreach ($this->mechanics as $mechanic) { $mechanicTotal = $row["mechanic_{$mechanic->id}_total"] ?? 0; $exportRow[] = $mechanicTotal; $rowTotal += $mechanicTotal; $columnTotals["mechanic_{$mechanic->id}_total"] += $mechanicTotal; } $exportRow[] = $rowTotal; $columnTotals['row_total'] += $rowTotal; $data[] = $exportRow; } // Add total row $totalRow = ['', 'TOTAL', '', '']; foreach ($this->mechanics as $mechanic) { $totalRow[] = $columnTotals["mechanic_{$mechanic->id}_total"]; } $totalRow[] = $columnTotals['row_total']; $data[] = $totalRow; return collect($data); } catch (\Exception $e) { Log::error('Error in collection: ' . $e->getMessage()); return collect([['Error loading data']]); } } public function headings(): array { return $this->headings; } public function styles(Worksheet $sheet) { try { $lastColumn = $sheet->getHighestColumn(); $lastRow = $sheet->getHighestRow(); // Calculate positions $titleRow = 1; $headerRow = 1; // Headers are now in row 2 $dataStartRow = 2; // Data starts in row 3 // Calculate total row position (after data) $dataRows = count($this->technicianReportService->getTechnicianReportData($this->dealerId, $this->startDate, $this->endDate)['data']); $totalRow = $dataStartRow + $dataRows; $filterStartRow = $totalRow + 2; // After total row + empty row // Style the title row (row 1) $sheet->getStyle('A' . $titleRow . ':' . $lastColumn . $titleRow)->applyFromArray([ 'font' => [ 'bold' => true, 'size' => 16, ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, ], ]); // Header styling (row 2) $sheet->getStyle('A' . $headerRow . ':' . $lastColumn . $headerRow)->applyFromArray([ 'font' => [ 'bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 10, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '2E5BBA'], ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, ], 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '000000'], ], ], ]); // Data styling (starting from row 3) if ($lastRow > $headerRow) { $dataEndRow = $totalRow; $sheet->getStyle('A' . $dataStartRow . ':' . $lastColumn . $dataEndRow)->applyFromArray([ 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '000000'], ], ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, ], ]); // Left align text columns $sheet->getStyle('B' . $dataStartRow . ':D' . $dataEndRow)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); // Style the total row $sheet->getStyle('A' . $totalRow . ':' . $lastColumn . $totalRow)->applyFromArray([ 'font' => [ 'bold' => true, 'size' => 11, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F2F2F2'], ], 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '000000'], ], ], ]); } // Style the export information section if ($filterStartRow <= $lastRow) { $exportInfoRow = $totalRow + 2; // After total row + empty row $filterEndRow = $lastRow; // Style the "INFORMASI EXPORT" title $sheet->getStyle('A' . $exportInfoRow . ':' . $lastColumn . $exportInfoRow)->applyFromArray([ 'font' => [ 'bold' => true, 'size' => 12, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'E6E6E6'], ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_CENTER, ], ]); // Style the filter info rows $filterInfoStartRow = $exportInfoRow + 3; // After title + empty + "Filter yang Digunakan:" $sheet->getStyle('A' . $filterInfoStartRow . ':' . $lastColumn . $filterEndRow)->applyFromArray([ 'font' => [ 'size' => 10, ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_TOP, ], ]); } // Auto-size columns foreach (range('A', $lastColumn) as $column) { if ($column === 'A') { // Set specific width for column A (No) - don't auto-size $sheet->getColumnDimension($column)->setWidth(5); } else { $sheet->getColumnDimension($column)->setAutoSize(true); } } } catch (\Exception $e) { Log::error('Error applying styles: ' . $e->getMessage()); } } public function columnWidths(): array { $widths = [ 'A' => 5, // No - reduced from 8 to 5 'B' => 30, // Nama Pekerjaan 'C' => 15, // Kode Pekerjaan 'D' => 20, // Kategori ]; // Add widths for mechanic columns $currentColumn = 'E'; foreach ($this->mechanics as $mechanic) { $widths[$currentColumn++] = 15; // Mechanic total } // Add width for total column $widths[$currentColumn] = 15; // Total return $widths; } public function registerEvents(): array { return [ AfterSheet::class => function(AfterSheet $event) { $sheet = $event->sheet->getDelegate(); $highestColumn = $sheet->getHighestColumn(); $highestRow = $sheet->getHighestRow(); // Header styling ONLY for row 1 $sheet->getStyle('A1:' . $highestColumn . '1')->applyFromArray([ 'font' => [ 'bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 10, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '2E5BBA'], ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, ], 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '000000'], ], ], ]); // Total row styling (only last row) $sheet->getStyle('A' . $highestRow . ':' . $highestColumn . $highestRow)->applyFromArray([ 'font' => [ 'bold' => true, 'size' => 11, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F2F2F2'], ], 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '000000'], ], ], ]); // Export info below table $infoStartRow = $highestRow + 2; $sheet->setCellValue('A' . $infoStartRow, 'INFORMASI EXPORT'); $sheet->getStyle('A' . $infoStartRow . ':' . $highestColumn . $infoStartRow)->applyFromArray([ 'font' => [ 'bold' => true, 'size' => 12, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'E6E6E6'], ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_CENTER, ], ]); $sheet->setCellValue('A' . ($infoStartRow + 2), 'Filter yang Digunakan:'); $row = $infoStartRow + 3; foreach ($this->filterInfo as $info) { $sheet->setCellValue('A' . $row, $info); $row++; } $sheet->getStyle('A' . ($infoStartRow + 2) . ':A' . ($row-1))->applyFromArray([ 'font' => [ 'size' => 10 ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_TOP, ], ]); } ]; } }