dev #101

Merged
joseph merged 2 commits from dev into master 2024-12-06 13:10:36 +11:00
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']);
}
// 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');
return Excel::download(
new LeaveManagementExport($leaveDays),
new LeaveManagementExport($staffData),
"LeaveManagement_{$year}_{$currentDate}.xlsx"
);
}

View File

@ -205,9 +205,9 @@ class TimekeepingController extends Controller
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) {
return isset($user['user']['permission']) && $user['user']['permission'] === 'staff';
return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
});
$currentDate = date('d_His');

View File

@ -4,13 +4,15 @@ namespace App\Exports;
use Carbon\Carbon;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
class LeaveManagementExport implements FromArray, WithHeadings, WithStyles, WithEvents
{
protected $data;
protected $year;
@ -36,6 +38,7 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
$headers = $this->headings(); // Lấy tiêu đề
$rows = [];
$stt = 0;
foreach ($this->data as $index => $user) {
$totalDayOff = 0;
$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
$row = [
$index + 1,
$stt + 1,
$user['user']['name']
];
@ -65,11 +68,61 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
$row[] = $user['leaveDay']['ld_note'] ?? '';
$rows[] = $row;
$stt++;
}
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)
{
$lastRow = count($this->data) + 2;
@ -129,6 +182,13 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
$sheet->getColumnDimension('Q')->setWidth(12); // Remaining
$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;
}
}

View File

@ -4,13 +4,15 @@ namespace App\Exports;
use Carbon\Carbon;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
class TimekeepingExport implements FromArray, WithHeadings, WithStyles
class TimekeepingExport implements FromArray, WithHeadings, WithStyles, WithEvents
{
protected $data;
protected $month;
@ -27,6 +29,55 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
$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
{
// Lấy headers
@ -35,11 +86,6 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
// Lấy dữ liệu người dùng
$userRows = [];
foreach ($this->data as $user) {
// Kiểm tra permission staff
if (!isset($user['user']['permission']) || $user['user']['permission'] !== 'staff') {
continue;
}
$row = [
$user['user']['name'] ?? 'Unknown',
0, // Total days
@ -120,6 +166,8 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
public function styles(Worksheet $sheet)
{
$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
$sheet->mergeCells("A1:{$lastColumn}1");
@ -136,9 +184,6 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
$sheet->setCellValue('B4', 'Total');
$sheet->setCellValue('C4', 'Off');
// Calculate last row (2 title rows + 2 header rows + data rows)
$lastRow = count($this->data) + 4;
// Styling
$sheet->getStyle("A1:{$lastColumn}1")->getFont()->setBold(true)->setSize(14);
$sheet->getStyle("A2:{$lastColumn}2")->getFont()->setBold(true);
@ -162,11 +207,33 @@ class TimekeepingExport implements FromArray, WithHeadings, WithStyles
->setHorizontal(Alignment::HORIZONTAL_CENTER)
->setVertical(Alignment::VERTICAL_CENTER);
// Left align Notes column
$noteColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4);
// Left align Notes column và bật wrap text
$sheet->getStyle("{$noteColumn}5:{$noteColumn}{$lastRow}")
->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
$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);
}
// Set width for Note column
$sheet->getColumnDimension($noteColumn)->setWidth(40);
return [];
}
}

View File

@ -23,6 +23,7 @@ import {
Textarea,
TextInput,
Tooltip,
Modal,
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { notifications } from '@mantine/notifications'
@ -143,6 +144,9 @@ const Timekeeping = () => {
const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
const [dataReason, setDataReason] = useState<DataReason[]>([])
const [exportModalOpened, setExportModalOpened] = useState(false)
const [exportOption, setExportOption] = useState('default')
const getListMasterByType = async (type: string) => {
try {
const params = {
@ -437,7 +441,7 @@ const Timekeeping = () => {
})
}
const handleExport = async () => {
const handleExport = async (option: string) => {
try {
const timestamp = moment().format('DDMMYYYY_HHmmss')
const fileName = `Timekeeping_${date.month}_${date.year}_${timestamp}.xlsx`
@ -447,10 +451,12 @@ const Timekeeping = () => {
{
month: date.month,
year: date.year,
working_days: workingDays
working_days: workingDays,
option: option
},
fileName
)
setExportModalOpened(false)
} catch (error) {
console.error('Export error:', error)
}
@ -707,7 +713,7 @@ const Timekeeping = () => {
</Button>
</Tooltip>
<Button
onClick={handleExport}
onClick={() => setExportModalOpened(true)}
size="xs"
ml="xl"
leftSection={<IconFileExcel size={16} />}
@ -1054,6 +1060,35 @@ const Timekeeping = () => {
</Table.Tbody>
</Table>
</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>
)
}