commit
824b2fd87c
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Modules\Admin\app\Http\Controllers;
|
||||
|
||||
use App\Exports\LeaveManagementExport;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\InitializeLeaveDays;
|
||||
use App\Models\LeaveDays;
|
||||
|
|
@ -9,6 +10,7 @@ use App\Models\Notes;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
||||
class LeaveManagementController extends Controller
|
||||
{
|
||||
|
|
@ -139,4 +141,20 @@ class LeaveManagementController extends Controller
|
|||
|
||||
return response()->json(['status' => true, 'message' => 'Updated successfully']);
|
||||
}
|
||||
|
||||
public function export(Request $request)
|
||||
{
|
||||
$year = $request->query('year', now()->year);
|
||||
$leaveDays = $this->getDataByYear($year);
|
||||
|
||||
if ($leaveDays->isEmpty()) {
|
||||
return response()->json(['status' => false, 'message' => 'No data found']);
|
||||
}
|
||||
|
||||
$currentDate = date('d_His');
|
||||
return Excel::download(
|
||||
new LeaveManagementExport($leaveDays),
|
||||
"LeaveManagement_{$year}_{$currentDate}.xlsx"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ use Illuminate\Support\Facades\DB;
|
|||
use Modules\Admin\app\Models\Admin;
|
||||
use Modules\Admin\app\Models\MonthlyTimekeeping;
|
||||
use Modules\Admin\app\Models\Tracking;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use App\Exports\TimekeepingExport;
|
||||
|
||||
class TimekeepingController extends Controller
|
||||
{
|
||||
|
|
@ -149,7 +151,7 @@ class TimekeepingController extends Controller
|
|||
|
||||
return response()->json(['status' => true, 'message' => 'Add successfully']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function updateCacheMonth(Request $request)
|
||||
|
|
@ -185,4 +187,38 @@ class TimekeepingController extends Controller
|
|||
|
||||
return response()->json(['message' => 'Delete fail', 'status' => false]);
|
||||
}
|
||||
|
||||
public function export(Request $request)
|
||||
{
|
||||
// Validate request
|
||||
$request->validate([
|
||||
'month' => 'required|numeric|between:1,12',
|
||||
'year' => 'required|numeric|min:2000',
|
||||
'working_days' => 'required|numeric|between:1,31'
|
||||
]);
|
||||
|
||||
// Reuse get() function to fetch data
|
||||
$response = $this->get($request);
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
|
||||
if (!$responseData['status']) {
|
||||
return response()->json(['status' => false, 'message' => 'No data found']);
|
||||
}
|
||||
|
||||
// Lọc chỉ lấy user có permission staff
|
||||
$staffData = array_filter($responseData['data'], function($user) {
|
||||
return isset($user['user']['permission']) && $user['user']['permission'] === 'staff';
|
||||
});
|
||||
|
||||
$currentDate = date('d_His');
|
||||
return Excel::download(
|
||||
new TimekeepingExport(
|
||||
array_values($staffData), // Convert to indexed array after filtering
|
||||
$request->month,
|
||||
$request->year,
|
||||
$request->working_days
|
||||
),
|
||||
"Timekeeping_{$request->month}_{$request->year}_{$currentDate}.xlsx"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ Route::middleware('api')
|
|||
Route::get('/delete', [TimekeepingController::class, 'deleteNote'])->middleware('check.permission:admin.hr');
|
||||
Route::post('/update-cache-month', [TimekeepingController::class, 'updateCacheMonth'])->middleware('check.permission:admin');
|
||||
Route::post('/update-working-days', [TimekeepingController::class, 'saveWorkingDays'])->middleware('check.permission:admin.hr');
|
||||
Route::get('/export', [TimekeepingController::class, 'export'])->middleware('check.permission:admin.hr.staff.accountant');
|
||||
});
|
||||
|
||||
Route::group([
|
||||
|
|
@ -146,6 +147,7 @@ Route::middleware('api')
|
|||
'prefix' => 'leave-management',
|
||||
], function () {
|
||||
Route::get('/', [LeaveManagementController::class, 'get'])->middleware('check.permission:admin.hr.staff.accountant');
|
||||
Route::get('/export', [LeaveManagementController::class, 'export'])->middleware('check.permission:admin.hr.staff.accountant');
|
||||
Route::post('/saveNoteLeave', [LeaveManagementController::class, 'saveNoteLeave'])->middleware('check.permission:admin.hr');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Maatwebsite\Excel\Concerns\FromArray;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
|
||||
class LeaveManagementExport implements FromArray, WithHeadings, WithStyles
|
||||
{
|
||||
protected $data;
|
||||
protected $year;
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->year = Carbon::now()->year;
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
$months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
return array_merge(
|
||||
['No.', 'User'],
|
||||
$months,
|
||||
['Total', 'Off', 'Remaining', 'Notes']
|
||||
);
|
||||
}
|
||||
|
||||
public function array(): array
|
||||
{
|
||||
$headers = $this->headings(); // Lấy tiêu đề
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->data as $index => $user) {
|
||||
$totalDayOff = 0;
|
||||
$totalDayLeave = $user['leaveDay']['ld_day'] + $user['leaveDay']['ld_date_additional'];
|
||||
|
||||
// Tính tổng ngày nghỉ theo tháng
|
||||
$monthlyLeaves = array_fill(1, 12, 0);
|
||||
foreach ($user['monthlyLeaveDays'] as $leaveDay) {
|
||||
$monthlyLeaves[$leaveDay['month']] += $leaveDay['leave_days'];
|
||||
$totalDayOff += $leaveDay['leave_days'];
|
||||
}
|
||||
|
||||
// Tạo dòng dữ liệu
|
||||
$row = [
|
||||
$index + 1,
|
||||
$user['user']['name']
|
||||
];
|
||||
|
||||
// Thêm dữ liệu các tháng
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$row[] = $monthlyLeaves[$month] ?: '';
|
||||
}
|
||||
|
||||
// Thêm tổng số ngày
|
||||
$row[] = $totalDayLeave;
|
||||
$row[] = $totalDayOff;
|
||||
$row[] = $totalDayLeave - $totalDayOff;
|
||||
$row[] = $user['leaveDay']['ld_note'] ?? '';
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
return array_merge([$headers], $rows); // Thêm tiêu đề vào đầu mảng
|
||||
}
|
||||
|
||||
public function styles(Worksheet $sheet)
|
||||
{
|
||||
$lastRow = count($this->data) + 2;
|
||||
$lastColumn = 'R';
|
||||
|
||||
// Thêm và style title
|
||||
$sheet->mergeCells("A1:{$lastColumn}1");
|
||||
$sheet->setCellValue('A1', "DANH SÁCH NGÀY NGHỈ NĂM {$this->year}");
|
||||
$sheet->getStyle("A1:{$lastColumn}1")->applyFromArray([
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 14
|
||||
],
|
||||
'alignment' => [
|
||||
'horizontal' => Alignment::HORIZONTAL_CENTER,
|
||||
'vertical' => Alignment::VERTICAL_CENTER
|
||||
]
|
||||
]);
|
||||
|
||||
// Style cho header (dời xuống row 2)
|
||||
$sheet->getStyle("A2:{$lastColumn}2")->applyFromArray([
|
||||
'font' => ['bold' => true],
|
||||
'alignment' => [
|
||||
'horizontal' => Alignment::HORIZONTAL_CENTER,
|
||||
'vertical' => Alignment::VERTICAL_CENTER
|
||||
]
|
||||
]);
|
||||
|
||||
// Style cho toàn bộ bảng (bắt đầu từ row 1)
|
||||
$sheet->getStyle("A1:{$lastColumn}{$lastRow}")->applyFromArray([
|
||||
'borders' => [
|
||||
'allBorders' => [
|
||||
'borderStyle' => Border::BORDER_THIN
|
||||
]
|
||||
],
|
||||
'alignment' => [
|
||||
'vertical' => Alignment::VERTICAL_CENTER
|
||||
]
|
||||
]);
|
||||
|
||||
// Căn giữa cho các cột số liệu (điều chỉnh range bắt đầu từ row 3)
|
||||
for ($col = 'C'; $col <= 'P'; $col++) {
|
||||
$sheet->getStyle("{$col}3:{$col}{$lastRow}")
|
||||
->getAlignment()
|
||||
->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||
}
|
||||
|
||||
// Set độ rộng cột
|
||||
$sheet->getColumnDimension('A')->setWidth(5); // No.
|
||||
$sheet->getColumnDimension('B')->setWidth(30); // User
|
||||
// Các tháng
|
||||
for ($i = 'C'; $i <= 'N'; $i++) {
|
||||
$sheet->getColumnDimension($i)->setWidth(8);
|
||||
}
|
||||
$sheet->getColumnDimension('O')->setWidth(8); // Total
|
||||
$sheet->getColumnDimension('P')->setWidth(8); // Off
|
||||
$sheet->getColumnDimension('Q')->setWidth(12); // Remaining
|
||||
$sheet->getColumnDimension('R')->setWidth(30); // Notes
|
||||
|
||||
return $sheet;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Maatwebsite\Excel\Concerns\FromArray;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
|
||||
class TimekeepingExport implements FromArray, WithHeadings, WithStyles
|
||||
{
|
||||
protected $data;
|
||||
protected $month;
|
||||
protected $year;
|
||||
protected $workingDays;
|
||||
protected $daysInMonth;
|
||||
|
||||
public function __construct($data, $month, $year, $workingDays)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->month = $month;
|
||||
$this->year = $year;
|
||||
$this->workingDays = $workingDays;
|
||||
$this->daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
||||
}
|
||||
|
||||
public function array(): array
|
||||
{
|
||||
// Lấy headers
|
||||
$headers = $this->headings();
|
||||
|
||||
// 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
|
||||
$this->workingDays, // Off days (initialize with working days)
|
||||
];
|
||||
|
||||
$totalDays = 0;
|
||||
// Add data for each day in month
|
||||
for ($day = 1; $day <= $this->daysInMonth; $day++) {
|
||||
$dayData = '';
|
||||
if (isset($user['history'])) {
|
||||
foreach ($user['history'] as $history) {
|
||||
if ($history['day'] === $day) {
|
||||
$total = $history['total'] ?? 0;
|
||||
if ($total >= 7 * 3600) {
|
||||
$dayData = '1';
|
||||
$totalDays += 1;
|
||||
} else if ($total >= 3.5 * 3600) {
|
||||
$dayData = '0.5';
|
||||
$totalDays += 0.5;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$row[] = $dayData;
|
||||
}
|
||||
|
||||
// Update total and off days
|
||||
$row[1] = $totalDays;
|
||||
$row[2] = $this->workingDays - $totalDays;
|
||||
|
||||
// Add Notes column with formatted content
|
||||
$notes = [];
|
||||
if (isset($user['history'])) {
|
||||
foreach ($user['history'] as $history) {
|
||||
if (!empty($history['notes'])) {
|
||||
$dayNotes = [];
|
||||
foreach ($history['notes'] as $note) {
|
||||
$dayNotes[] = "- {$note['reasonName']} ({$note['timeTypeName']}): {$note['note']}";
|
||||
}
|
||||
if (!empty($dayNotes)) {
|
||||
$notes[] = "Day {$history['day']}:\n" . implode("\n", $dayNotes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$row[] = !empty($notes) ? implode("\n\n", $notes) : '';
|
||||
|
||||
$userRows[] = $row;
|
||||
}
|
||||
|
||||
return array_merge($headers, $userRows);
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
|
||||
$firstRow = ['Day', '', ''];
|
||||
// Second row: Day of week
|
||||
$secondRow = ['', '', ''];
|
||||
|
||||
$date = Carbon::create($this->year, $this->month, 1);
|
||||
|
||||
for ($day = 1; $day <= $this->daysInMonth; $day++) {
|
||||
$firstRow[] = $day;
|
||||
$secondRow[] = $date->format('D');
|
||||
$date->addDay();
|
||||
}
|
||||
|
||||
// Add Notes column
|
||||
$firstRow[] = 'Notes';
|
||||
$secondRow[] = '';
|
||||
|
||||
return [$firstRow, $secondRow];
|
||||
}
|
||||
|
||||
public function styles(Worksheet $sheet)
|
||||
{
|
||||
$lastColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4);
|
||||
|
||||
// Title and working days
|
||||
$sheet->mergeCells("A1:{$lastColumn}1");
|
||||
$sheet->setCellValue('A1', "DANH SÁCH CHẤM CÔNG THÁNG {$this->month} NĂM {$this->year}");
|
||||
$sheet->mergeCells("A2:{$lastColumn}2");
|
||||
$sheet->setCellValue('A2', "Số ngày làm việc: {$this->workingDays}");
|
||||
|
||||
// Merge cells for "Day" title and set value
|
||||
$sheet->mergeCells("A3:C3");
|
||||
$sheet->setCellValue('A3', 'Day');
|
||||
|
||||
// Set values for A4, B4, C4
|
||||
$sheet->setCellValue('A4', 'User');
|
||||
$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);
|
||||
$sheet->getStyle("A3:{$lastColumn}4")->getFont()->setBold(true);
|
||||
|
||||
// Border style
|
||||
$borderStyle = [
|
||||
'borders' => [
|
||||
'allBorders' => [
|
||||
'borderStyle' => Border::BORDER_THIN,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Apply borders to the data area
|
||||
$sheet->getStyle("A1:{$lastColumn}{$lastRow}")->applyFromArray($borderStyle);
|
||||
|
||||
// Center align all cells except Notes column
|
||||
$sheet->getStyle("A1:{$lastColumn}{$lastRow}")
|
||||
->getAlignment()
|
||||
->setHorizontal(Alignment::HORIZONTAL_CENTER)
|
||||
->setVertical(Alignment::VERTICAL_CENTER);
|
||||
|
||||
// Left align Notes column
|
||||
$noteColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($this->daysInMonth + 4);
|
||||
$sheet->getStyle("{$noteColumn}5:{$noteColumn}{$lastRow}")
|
||||
->getAlignment()
|
||||
->setHorizontal(Alignment::HORIZONTAL_LEFT);
|
||||
|
||||
// Set column widths
|
||||
$sheet->getColumnDimension('A')->setWidth(30);
|
||||
$sheet->getColumnDimension('B')->setWidth(10);
|
||||
$sheet->getColumnDimension('C')->setWidth(10);
|
||||
for ($i = 4; $i <= $this->daysInMonth + 3; $i++) {
|
||||
$sheet->getColumnDimension(\PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i))->setWidth(5);
|
||||
}
|
||||
|
||||
// Set width for Note column
|
||||
$sheet->getColumnDimension($noteColumn)->setWidth(40);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -20,6 +20,7 @@ export const getDetailIssByKey = API_URL + 'v1/admin/jira/issue/detail'
|
|||
|
||||
//Timekeeping
|
||||
export const getTheTimesheet = API_URL + 'v1/admin/timekeeping'
|
||||
export const exportTimekeeping = API_URL + 'v1/admin/timekeeping/export'
|
||||
export const updateMultipleUserWorkingTime =
|
||||
API_URL + 'v1/admin/timekeeping/addMutilple'
|
||||
export const updateNote = API_URL + 'v1/admin/timekeeping/addNote'
|
||||
|
|
@ -36,6 +37,7 @@ export const getListMaster = API_URL + 'v1/admin/category/get-list-master'
|
|||
export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
|
||||
export const updateNoteLeave =
|
||||
API_URL + 'v1/admin/leave-management/saveNoteLeave'
|
||||
export const exportLeaveManagement = API_URL + 'v1/admin/leave-management/export'
|
||||
|
||||
//Tickets
|
||||
export const getTickets = API_URL + 'v1/admin/ticket/all'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { getLeaveManagement, updateNoteLeave } from '@/api/Admin'
|
||||
import { getLeaveManagement, updateNoteLeave, exportLeaveManagement } from '@/api/Admin'
|
||||
import { update } from '@/rtk/helpers/CRUD'
|
||||
import { get } from '@/rtk/helpers/apiService'
|
||||
import { get, exportFile } from '@/rtk/helpers/apiService'
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
|
|
@ -20,7 +20,7 @@ import { notifications } from '@mantine/notifications'
|
|||
import moment from 'moment'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { IconEdit } from '@tabler/icons-react'
|
||||
import { IconEdit, IconFileExcel } from '@tabler/icons-react'
|
||||
|
||||
import classes from './LeaveManagement.module.css'
|
||||
|
||||
|
|
@ -245,6 +245,29 @@ const LeaveManagement = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
const timestamp = moment().format('DDMMYYYY_HHmmss')
|
||||
const fileName = `LeaveManagement_${date.year}_${timestamp}.xlsx`
|
||||
|
||||
await exportFile(
|
||||
exportLeaveManagement,
|
||||
{
|
||||
year: parseInt(date.year)
|
||||
},
|
||||
fileName
|
||||
)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Export error:', error)
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: 'Export failed',
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.title}>
|
||||
|
|
@ -391,22 +414,18 @@ const LeaveManagement = () => {
|
|||
pl={200}
|
||||
style={{
|
||||
display: 'flex',
|
||||
// alignItems: 'end',
|
||||
justifyContent: 'end',
|
||||
}}
|
||||
>
|
||||
<Box display={'flex'} style={{ alignItems: 'end' }}>
|
||||
{/* <Tooltip label="Save working days">
|
||||
<Button
|
||||
size="xs"
|
||||
ml={'sm'}
|
||||
onClick={() => {
|
||||
//form add user new
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</Tooltip> */}
|
||||
<Button
|
||||
size="xs"
|
||||
ml={'sm'}
|
||||
onClick={handleExport}
|
||||
leftSection={<IconFileExcel size={16} />}
|
||||
>
|
||||
Export Excel
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
deleteNote,
|
||||
exportTimekeeping,
|
||||
getListMaster,
|
||||
getTheTimesheet,
|
||||
updateMultipleUserWorkingTime,
|
||||
|
|
@ -7,7 +8,7 @@ import {
|
|||
updateWorkingDays,
|
||||
} from '@/api/Admin'
|
||||
import { update, Xdelete } from '@/rtk/helpers/CRUD'
|
||||
import { get } from '@/rtk/helpers/apiService'
|
||||
import { exportFile, get } from '@/rtk/helpers/apiService'
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
|
|
@ -31,6 +32,7 @@ import {
|
|||
IconPointFilled,
|
||||
IconTrash,
|
||||
IconX,
|
||||
IconFileExcel,
|
||||
} from '@tabler/icons-react'
|
||||
import moment from 'moment'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
|
@ -435,12 +437,29 @@ const Timekeeping = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
const timestamp = moment().format('DDMMYYYY_HHmmss')
|
||||
const fileName = `Timekeeping_${date.month}_${date.year}_${timestamp}.xlsx`
|
||||
|
||||
await exportFile(
|
||||
exportTimekeeping,
|
||||
{
|
||||
month: date.month,
|
||||
year: date.year,
|
||||
working_days: workingDays
|
||||
},
|
||||
fileName
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Export error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.title}>
|
||||
<h3>
|
||||
Timekeeping
|
||||
</h3>
|
||||
<h3>Timekeeping</h3>
|
||||
</div>
|
||||
<Drawer
|
||||
opened={opened1}
|
||||
|
|
@ -511,7 +530,6 @@ const Timekeeping = () => {
|
|||
<span style={{ paddingLeft: '10px', paddingRight: '10px' }}>|</span>
|
||||
<span style={{ fontWeight: 'bold' }}>Day</span>: {customAddNotes.day}
|
||||
</p>
|
||||
|
||||
<Select
|
||||
mb={'md'}
|
||||
searchable
|
||||
|
|
@ -688,6 +706,14 @@ const Timekeeping = () => {
|
|||
Save
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button
|
||||
onClick={handleExport}
|
||||
size="xs"
|
||||
ml="xl"
|
||||
leftSection={<IconFileExcel size={16} />}
|
||||
>
|
||||
Export Excel
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
|
|
@ -866,13 +892,22 @@ const Timekeeping = () => {
|
|||
// offset={{ mainAxis: 5, crossAxis: 0 }}
|
||||
label={showTooltipAllNote(user)}
|
||||
>
|
||||
<div style={{display:'flex', alignItems:'center'}}><Avatar size={'md'} mr={'md'} src={import.meta.env.VITE_BACKEND_URL.includes('local')
|
||||
? import.meta.env.VITE_BACKEND_URL +
|
||||
'storage/' +
|
||||
user.user.avatar
|
||||
: import.meta.env.VITE_BACKEND_URL +
|
||||
'image/storage/' +
|
||||
user.user.avatar}/>{user.user.name}</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Avatar
|
||||
size={'md'}
|
||||
mr={'md'}
|
||||
src={
|
||||
import.meta.env.VITE_BACKEND_URL.includes('local')
|
||||
? import.meta.env.VITE_BACKEND_URL +
|
||||
'storage/' +
|
||||
user.user.avatar
|
||||
: import.meta.env.VITE_BACKEND_URL +
|
||||
'image/storage/' +
|
||||
user.user.avatar
|
||||
}
|
||||
/>
|
||||
{user.user.name}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
<Table.Td ta={'center'}>{totalDays}</Table.Td>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { getFormDataHeader, getHeaderInfo } from '@/rtk/helpers/tokenCreator'
|
|||
import { removeTokens } from '@/rtk/localStorage'
|
||||
import { notifications } from '@mantine/notifications'
|
||||
import axios from 'axios'
|
||||
import moment from 'moment'
|
||||
const handleResponse = (response: any) => {
|
||||
if (response.status === 401) {
|
||||
removeTokens()
|
||||
|
|
@ -156,3 +157,45 @@ export const postImage = async (url: string, body: any, method: any) => {
|
|||
throw handleResponse(err.response)
|
||||
}
|
||||
}
|
||||
|
||||
export const exportFile = async (url: string, params: any = {}, fileName: string) => {
|
||||
const header = await getHeaderInfo()
|
||||
|
||||
try {
|
||||
const response = await axios.get(url, {
|
||||
...header,
|
||||
params,
|
||||
responseType: 'blob'
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const blob = new Blob([response.data])
|
||||
const downloadUrl = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = downloadUrl
|
||||
link.download = fileName
|
||||
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
window.URL.revokeObjectURL(downloadUrl)
|
||||
document.body.removeChild(link)
|
||||
|
||||
// notifications.show({
|
||||
// title: 'Success',
|
||||
// message: 'Export successfully',
|
||||
// color: 'green',
|
||||
// })
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
throw new Error('Export failed')
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message || 'Export failed',
|
||||
color: 'red',
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue