Bổ sung nút report excel trang timekeeping , leave management

This commit is contained in:
Truong Vo 2024-12-06 09:07:54 +07:00
parent 93030b73c7
commit 7fdf6f3e25
5 changed files with 187 additions and 23 deletions

View File

@ -151,9 +151,14 @@ class LeaveManagementController extends Controller
return response()->json(['status' => false, 'message' => 'No data found']); return response()->json(['status' => false, 'message' => 'No data found']);
} }
// Lọc chỉ lấy user có permission bao gồm staff
$staffData = $leaveDays->filter(function($user) {
return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
});
$currentDate = date('d_His'); $currentDate = date('d_His');
return Excel::download( return Excel::download(
new LeaveManagementExport($leaveDays), new LeaveManagementExport($staffData),
"LeaveManagement_{$year}_{$currentDate}.xlsx" "LeaveManagement_{$year}_{$currentDate}.xlsx"
); );
} }

View File

@ -205,9 +205,9 @@ class TimekeepingController extends Controller
return response()->json(['status' => false, 'message' => 'No data found']); return response()->json(['status' => false, 'message' => 'No data found']);
} }
// Lọc chỉ lấy user có permission staff // Lọc chỉ lấy user có permission bao gồm staff
$staffData = array_filter($responseData['data'], function($user) { $staffData = array_filter($responseData['data'], function($user) {
return isset($user['user']['permission']) && $user['user']['permission'] === 'staff'; return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
}); });
$currentDate = date('d_His'); $currentDate = date('d_His');

View File

@ -4,13 +4,15 @@ namespace App\Exports;
use Carbon\Carbon; use Carbon\Carbon;
use Maatwebsite\Excel\Concerns\FromArray; use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithStyles; use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Alignment;
class LeaveManagementExport implements FromArray, WithHeadings, WithStyles class LeaveManagementExport implements FromArray, WithHeadings, WithStyles, WithEvents
{ {
protected $data; protected $data;
protected $year; protected $year;
@ -36,6 +38,7 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
$headers = $this->headings(); // Lấy tiêu đề $headers = $this->headings(); // Lấy tiêu đề
$rows = []; $rows = [];
$stt = 0;
foreach ($this->data as $index => $user) { foreach ($this->data as $index => $user) {
$totalDayOff = 0; $totalDayOff = 0;
$totalDayLeave = $user['leaveDay']['ld_day'] + $user['leaveDay']['ld_date_additional']; $totalDayLeave = $user['leaveDay']['ld_day'] + $user['leaveDay']['ld_date_additional'];
@ -49,7 +52,7 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
// Tạo dòng dữ liệu // Tạo dòng dữ liệu
$row = [ $row = [
$index + 1, $stt + 1,
$user['user']['name'] $user['user']['name']
]; ];
@ -65,11 +68,61 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
$row[] = $user['leaveDay']['ld_note'] ?? ''; $row[] = $user['leaveDay']['ld_note'] ?? '';
$rows[] = $row; $rows[] = $row;
$stt++;
} }
return array_merge([$headers], $rows); // Thêm tiêu đề vào đầu mảng return array_merge([$headers], $rows); // Thêm tiêu đề vào đầu mảng
} }
public function registerEvents(): array
{
return [
AfterSheet::class => function(AfterSheet $event) {
$sheet = $event->sheet->getDelegate();
$lastRow = count($this->data) + 2;
$noteColumn = 'R'; // Cột Notes
// Xử lý đặc biệt cho cột Notes
$sheet->getStyle("{$noteColumn}3:{$noteColumn}{$lastRow}")
->getAlignment()
->setWrapText(true)
->setVertical(Alignment::VERTICAL_TOP)
->setHorizontal(Alignment::HORIZONTAL_LEFT);
// Tắt auto-size cho cột Notes và set độ rộng cố định
$sheet->getColumnDimension($noteColumn)
->setAutoSize(false)
->setWidth(60);
// Tự động điều chỉnh chiều cao cho từng dòng có nội dung
for ($row = 3; $row <= $lastRow; $row++) {
$cellValue = $sheet->getCell($noteColumn . $row)->getValue();
if (!empty($cellValue)) {
// Tính toán chiều cao dựa trên nội dung
$sheet->getRowDimension($row)->setRowHeight(-1);
// Tính toán lại chiều cao dựa trên số dòng trong nội dung
$lineCount = substr_count($cellValue, "\n") + 1;
$minHeight = max(30, $lineCount * 15); // 15 pixels cho mỗi dòng
// Lấy chiều cao hiện tại sau khi auto-size
$currentHeight = $sheet->getRowDimension($row)->getRowHeight();
// Nếu chiều cao tự động nhỏ hơn chiều cao tối thiểu, sử dụng chiều cao tối thiểu
if ($currentHeight < $minHeight) {
$sheet->getRowDimension($row)->setRowHeight($minHeight);
}
} else {
$sheet->getRowDimension($row)->setRowHeight(30);
}
}
// Refresh các tính toán của Excel
$sheet->calculateColumnWidths();
},
];
}
public function styles(Worksheet $sheet) public function styles(Worksheet $sheet)
{ {
$lastRow = count($this->data) + 2; $lastRow = count($this->data) + 2;
@ -129,6 +182,13 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
$sheet->getColumnDimension('Q')->setWidth(12); // Remaining $sheet->getColumnDimension('Q')->setWidth(12); // Remaining
$sheet->getColumnDimension('R')->setWidth(30); // Notes $sheet->getColumnDimension('R')->setWidth(30); // Notes
// Điều chỉnh style cho cột Notes
$sheet->getStyle("R3:R{$lastRow}")
->getAlignment()
->setHorizontal(Alignment::HORIZONTAL_LEFT)
->setVertical(Alignment::VERTICAL_TOP)
->setWrapText(true);
return $sheet; return $sheet;
} }
} }

View File

@ -4,13 +4,15 @@ namespace App\Exports;
use Carbon\Carbon; use Carbon\Carbon;
use Maatwebsite\Excel\Concerns\FromArray; use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithStyles; use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Alignment;
class TimekeepingExport implements FromArray, WithHeadings, WithStyles class TimekeepingExport implements FromArray, WithHeadings, WithStyles, WithEvents
{ {
protected $data; protected $data;
protected $month; protected $month;
@ -27,6 +29,55 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
$this->daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); $this->daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
} }
public function registerEvents(): array
{
return [
AfterSheet::class => function(AfterSheet $event) {
$sheet = $event->sheet->getDelegate();
$lastRow = count($this->data) + 4;
$noteColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4);
// Xử lý đặc biệt cho cột Notes
$sheet->getStyle("{$noteColumn}5:{$noteColumn}{$lastRow}")
->getAlignment()
->setWrapText(true)
->setVertical(Alignment::VERTICAL_TOP)
->setHorizontal(Alignment::HORIZONTAL_LEFT);
// Tắt auto-size cho cột Notes và set độ rộng cố định
$sheet->getColumnDimension($noteColumn)
->setAutoSize(false)
->setWidth(60);
// Tự động điều chỉnh chiều cao cho từng dòng có nội dung
for ($row = 5; $row <= $lastRow; $row++) {
$cellValue = $sheet->getCell($noteColumn . $row)->getValue();
if (!empty($cellValue)) {
// Tính toán chiều cao dựa trên nội dung
$sheet->getRowDimension($row)->setRowHeight(-1);
// Tính toán lại chiều cao dựa trên số dòng trong nội dung
$lineCount = substr_count($cellValue, "\n") + 1;
$minHeight = max(30, $lineCount * 15); // 15 pixels cho mỗi dòng
// Lấy chiều cao hiện tại sau khi auto-size
$currentHeight = $sheet->getRowDimension($row)->getRowHeight();
// Nếu chiều cao tự động nhỏ hơn chiều cao tối thiểu, sử dụng chiều cao tối thiểu
if ($currentHeight < $minHeight) {
$sheet->getRowDimension($row)->setRowHeight($minHeight);
}
} else {
$sheet->getRowDimension($row)->setRowHeight(30);
}
}
// Refresh các tính toán của Excel
$sheet->calculateColumnWidths();
},
];
}
public function array(): array public function array(): array
{ {
// Lấy headers // Lấy headers
@ -35,11 +86,6 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
// Lấy dữ liệu người dùng // Lấy dữ liệu người dùng
$userRows = []; $userRows = [];
foreach ($this->data as $user) { foreach ($this->data as $user) {
// Kiểm tra permission staff
if (!isset($user['user']['permission']) || $user['user']['permission'] !== 'staff') {
continue;
}
$row = [ $row = [
$user['user']['name'] ?? 'Unknown', $user['user']['name'] ?? 'Unknown',
0, // Total days 0, // Total days
@ -120,6 +166,8 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
public function styles(Worksheet $sheet) public function styles(Worksheet $sheet)
{ {
$lastColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4); $lastColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4);
$lastRow = count($this->data) + 4;
$noteColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4);
// Title and working days // Title and working days
$sheet->mergeCells("A1:{$lastColumn}1"); $sheet->mergeCells("A1:{$lastColumn}1");
@ -136,9 +184,6 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
$sheet->setCellValue('B4', 'Total'); $sheet->setCellValue('B4', 'Total');
$sheet->setCellValue('C4', 'Off'); $sheet->setCellValue('C4', 'Off');
// Calculate last row (2 title rows + 2 header rows + data rows)
$lastRow = count($this->data) + 4;
// Styling // Styling
$sheet->getStyle("A1:{$lastColumn}1")->getFont()->setBold(true)->setSize(14); $sheet->getStyle("A1:{$lastColumn}1")->getFont()->setBold(true)->setSize(14);
$sheet->getStyle("A2:{$lastColumn}2")->getFont()->setBold(true); $sheet->getStyle("A2:{$lastColumn}2")->getFont()->setBold(true);
@ -162,11 +207,33 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
->setHorizontal(Alignment::HORIZONTAL_CENTER) ->setHorizontal(Alignment::HORIZONTAL_CENTER)
->setVertical(Alignment::VERTICAL_CENTER); ->setVertical(Alignment::VERTICAL_CENTER);
// Left align Notes column // Left align Notes column và bật wrap text
$noteColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4);
$sheet->getStyle("{$noteColumn}5:{$noteColumn}{$lastRow}") $sheet->getStyle("{$noteColumn}5:{$noteColumn}{$lastRow}")
->getAlignment() ->getAlignment()
->setHorizontal(Alignment::HORIZONTAL_LEFT); ->setHorizontal(Alignment::HORIZONTAL_LEFT)
->setVertical(Alignment::VERTICAL_TOP)
->setWrapText(true);
// Set width for Note column - tăng độ rộng để hiển thị tốt hơn
$sheet->getColumnDimension($noteColumn)->setWidth(60);
// Tự động điều chỉnh chiều cao cho các dòng có nội dung Notes
for ($row = 5; $row <= $lastRow; $row++) {
$cellValue = $sheet->getCell($noteColumn . $row)->getValue();
if (!empty($cellValue)) {
// Đết chiều cao tự động
$sheet->getRowDimension($row)->setRowHeight(-1);
// Đảm bảo chiều cao tối thiểu
$currentHeight = $sheet->getRowDimension($row)->getRowHeight();
if ($currentHeight < 30) {
$sheet->getRowDimension($row)->setRowHeight(30);
}
} else {
// Chiều cao mặc định cho các dòng không có note
$sheet->getRowDimension($row)->setRowHeight(30);
}
}
// Set column widths // Set column widths
$sheet->getColumnDimension('A')->setWidth(30); $sheet->getColumnDimension('A')->setWidth(30);
@ -176,9 +243,6 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
$sheet->getColumnDimension(\PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i))->setWidth(5); $sheet->getColumnDimension(\PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i))->setWidth(5);
} }
// Set width for Note column
$sheet->getColumnDimension($noteColumn)->setWidth(40);
return []; return [];
} }
} }

View File

@ -23,6 +23,7 @@ import {
Textarea, Textarea,
TextInput, TextInput,
Tooltip, Tooltip,
Modal,
} from '@mantine/core' } from '@mantine/core'
import { useDisclosure } from '@mantine/hooks' import { useDisclosure } from '@mantine/hooks'
import { notifications } from '@mantine/notifications' import { notifications } from '@mantine/notifications'
@ -143,6 +144,9 @@ const Timekeeping = () => {
const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([]) const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
const [dataReason, setDataReason] = useState<DataReason[]>([]) const [dataReason, setDataReason] = useState<DataReason[]>([])
const [exportModalOpened, setExportModalOpened] = useState(false)
const [exportOption, setExportOption] = useState('default')
const getListMasterByType = async (type: string) => { const getListMasterByType = async (type: string) => {
try { try {
const params = { const params = {
@ -437,7 +441,7 @@ const Timekeeping = () => {
}) })
} }
const handleExport = async () => { const handleExport = async (option: string) => {
try { try {
const timestamp = moment().format('DDMMYYYY_HHmmss') const timestamp = moment().format('DDMMYYYY_HHmmss')
const fileName = `Timekeeping_${date.month}_${date.year}_${timestamp}.xlsx` const fileName = `Timekeeping_${date.month}_${date.year}_${timestamp}.xlsx`
@ -447,10 +451,12 @@ const Timekeeping = () => {
{ {
month: date.month, month: date.month,
year: date.year, year: date.year,
working_days: workingDays working_days: workingDays,
option: option
}, },
fileName fileName
) )
setExportModalOpened(false)
} catch (error) { } catch (error) {
console.error('Export error:', error) console.error('Export error:', error)
} }
@ -707,7 +713,7 @@ const Timekeeping = () => {
</Button> </Button>
</Tooltip> </Tooltip>
<Button <Button
onClick={handleExport} onClick={() => setExportModalOpened(true)}
size="xs" size="xs"
ml="xl" ml="xl"
leftSection={<IconFileExcel size={16} />} leftSection={<IconFileExcel size={16} />}
@ -1054,6 +1060,35 @@ const Timekeeping = () => {
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</Box> </Box>
<Modal
opened={exportModalOpened}
onClose={() => setExportModalOpened(false)}
title={
<Text pl={'sm'} fw={700} fz={'lg'}>
Export
</Text>
}
size="sm"
>
<Select
label="Option"
placeholder="Choose an option"
value={exportOption}
onChange={(value) => setExportOption(value || 'default')}
data={[
{ value: 'default', label: 'Default' }
]}
mb="md"
/>
<Box style={{ display: 'flex', justifyContent: 'flex-end', gap: '10px' }}>
<Button variant="outline" onClick={() => setExportModalOpened(false)}>
Close
</Button>
<Button onClick={() => handleExport(exportOption)}>
Export
</Button>
</Box>
</Modal>
</div> </div>
) )
} }