Merge pull request 'refactor create, handle ticket' (#125) from vi.ticket-email into dev
Reviewed-on: #125
This commit is contained in:
commit
a11f67f252
File diff suppressed because it is too large
Load Diff
|
|
@ -17,6 +17,7 @@ use Modules\Admin\app\Models\MonthlyTimekeeping;
|
||||||
use Modules\Admin\app\Models\Tracking;
|
use Modules\Admin\app\Models\Tracking;
|
||||||
use Maatwebsite\Excel\Facades\Excel;
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
use App\Exports\TimekeepingExport;
|
use App\Exports\TimekeepingExport;
|
||||||
|
use Modules\Admin\app\Models\Ticket;
|
||||||
|
|
||||||
class TimekeepingController extends Controller
|
class TimekeepingController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -177,78 +178,92 @@ class TimekeepingController extends Controller
|
||||||
$year = $request->year;
|
$year = $request->year;
|
||||||
|
|
||||||
$note = Notes::find($id);
|
$note = Notes::find($id);
|
||||||
if ($note) {
|
if (!$note) {
|
||||||
$n_month = $note->n_month;
|
return response()->json(['message' => 'Note not found', 'status' => false]);
|
||||||
$n_year = $note->n_year;
|
|
||||||
|
|
||||||
if ($note->n_reason == "ONLEAVE") {
|
|
||||||
// Get note reason ONLEAVE by $n_month, $n_year not include $note->id & include $note->n_user_id
|
|
||||||
// $onleave = Notes::getNotesByMonthAndYearAndUserId($n_month, $n_year, $note->n_user_id, $note->id);
|
|
||||||
|
|
||||||
// Get note reason LEAVE_WITHOUT_PAY by $n_month, $n_year & include $note->n_user_id
|
|
||||||
$leaveWithoutPay = Notes::getNotesByMonthAndYearAndUserIdAndReason($n_month, $n_year, $note->n_user_id, 'LEAVE_WITHOUT_PAY');
|
|
||||||
|
|
||||||
if (count($leaveWithoutPay) > 0) {
|
|
||||||
$deletedValue = ($note->n_time_type === 'ALL') ? 1.0 : 0.5;
|
|
||||||
$needUpdate = $deletedValue;
|
|
||||||
// dd($needUpdate, $leaveWithoutPay);
|
|
||||||
foreach ($leaveWithoutPay as $lwNote) {
|
|
||||||
if ($needUpdate <= 0) break;
|
|
||||||
|
|
||||||
if ($lwNote->n_time_type === 'ALL') {
|
|
||||||
if ($needUpdate == 1.0) {
|
|
||||||
// Chuyển cả note ALL thành phép
|
|
||||||
$lwNote->update(['n_reason' => 'ONLEAVE']);
|
|
||||||
$needUpdate = 0;
|
|
||||||
break;
|
|
||||||
} else { // $needUpdate == 0.5
|
|
||||||
// Tách ALL thành 2 note S và C, chuyển S thành phép, C giữ không phép
|
|
||||||
Notes::create([
|
|
||||||
'n_user_id' => $lwNote->n_user_id,
|
|
||||||
'n_day' => $lwNote->n_day,
|
|
||||||
'n_month' => $lwNote->n_month,
|
|
||||||
'n_year' => $lwNote->n_year,
|
|
||||||
'n_time_type' => 'S',
|
|
||||||
'n_reason' => 'ONLEAVE',
|
|
||||||
'n_note' => $lwNote->n_note
|
|
||||||
]);
|
|
||||||
Notes::create([
|
|
||||||
'n_user_id' => $lwNote->n_user_id,
|
|
||||||
'n_day' => $lwNote->n_day,
|
|
||||||
'n_month' => $lwNote->n_month,
|
|
||||||
'n_year' => $lwNote->n_year,
|
|
||||||
'n_time_type' => 'C',
|
|
||||||
'n_reason' => 'LEAVE_WITHOUT_PAY',
|
|
||||||
'n_note' => $lwNote->n_note
|
|
||||||
]);
|
|
||||||
$lwNote->delete();
|
|
||||||
$needUpdate = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Nếu $lwNote->n_time_type == 'S' hoặc 'C' => 0.5
|
|
||||||
if ($needUpdate == 1.0) {
|
|
||||||
// Chuyển cả note ALL thành phép
|
|
||||||
$lwNote->update(['n_reason' => 'ONLEAVE']);
|
|
||||||
$needUpdate -= 0.5;
|
|
||||||
} else { // $needUpdate == 0.5
|
|
||||||
// S hoặc C, chỉ cần chuyển đúng 0.5 ngày
|
|
||||||
$lwNote->update(['n_reason' => 'ONLEAVE']);
|
|
||||||
$needUpdate = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Khi note phép và k tồn tại nghỉ không phép => phép + dồn cho tháng sau
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$note->delete();
|
|
||||||
$this->createOrUpdateRecordForCurrentMonth($month, $year);
|
|
||||||
return response()->json(['message' => 'Delete success', 'status' => true]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['message' => 'Delete fail', 'status' => false]);
|
$ticket = Ticket::find($note->ticket_id);
|
||||||
|
if (!$ticket) {
|
||||||
|
return response()->json(['message' => 'Ticket not found, can not delete note', 'status' => false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$admin = auth('admins')->user();
|
||||||
|
$ticket->updated_by = $admin->name;
|
||||||
|
$ticket->status = "REFUSED";
|
||||||
|
$ticket->save();
|
||||||
|
|
||||||
|
Notes::where('ticket_id', $ticket->id)->delete();
|
||||||
|
$this->createOrUpdateRecordForCurrentMonth($month, $year);
|
||||||
|
return response()->json(['message' => 'Delete success', 'status' => true]);
|
||||||
|
|
||||||
|
// $n_month = $note->n_month;
|
||||||
|
// $n_year = $note->n_year;
|
||||||
|
|
||||||
|
// if ($note->n_reason == "ONLEAVE") {
|
||||||
|
// // Get note reason ONLEAVE by $n_month, $n_year not include $note->id & include $note->n_user_id
|
||||||
|
// // $onleave = Notes::getNotesByMonthAndYearAndUserId($n_month, $n_year, $note->n_user_id, $note->id);
|
||||||
|
|
||||||
|
// // Get note reason LEAVE_WITHOUT_PAY by $n_month, $n_year & include $note->n_user_id
|
||||||
|
// $leaveWithoutPay = Notes::getNotesByMonthAndYearAndUserIdAndReason($n_month, $n_year, $note->n_user_id, 'LEAVE_WITHOUT_PAY');
|
||||||
|
|
||||||
|
// if (count($leaveWithoutPay) > 0) {
|
||||||
|
// $deletedValue = ($note->n_time_type === 'ALL') ? 1.0 : 0.5;
|
||||||
|
// $needUpdate = $deletedValue;
|
||||||
|
// // dd($needUpdate, $leaveWithoutPay);
|
||||||
|
// foreach ($leaveWithoutPay as $lwNote) {
|
||||||
|
// if ($needUpdate <= 0) break;
|
||||||
|
|
||||||
|
// if ($lwNote->n_time_type === 'ALL') {
|
||||||
|
// if ($needUpdate == 1.0) {
|
||||||
|
// // Chuyển cả note ALL thành phép
|
||||||
|
// $lwNote->update(['n_reason' => 'ONLEAVE']);
|
||||||
|
// $needUpdate = 0;
|
||||||
|
// break;
|
||||||
|
// } else { // $needUpdate == 0.5
|
||||||
|
// // Tách ALL thành 2 note S và C, chuyển S thành phép, C giữ không phép
|
||||||
|
// Notes::create([
|
||||||
|
// 'n_user_id' => $lwNote->n_user_id,
|
||||||
|
// 'n_day' => $lwNote->n_day,
|
||||||
|
// 'n_month' => $lwNote->n_month,
|
||||||
|
// 'n_year' => $lwNote->n_year,
|
||||||
|
// 'n_time_type' => 'S',
|
||||||
|
// 'n_reason' => 'ONLEAVE',
|
||||||
|
// 'n_note' => $lwNote->n_note
|
||||||
|
// ]);
|
||||||
|
// Notes::create([
|
||||||
|
// 'n_user_id' => $lwNote->n_user_id,
|
||||||
|
// 'n_day' => $lwNote->n_day,
|
||||||
|
// 'n_month' => $lwNote->n_month,
|
||||||
|
// 'n_year' => $lwNote->n_year,
|
||||||
|
// 'n_time_type' => 'C',
|
||||||
|
// 'n_reason' => 'LEAVE_WITHOUT_PAY',
|
||||||
|
// 'n_note' => $lwNote->n_note
|
||||||
|
// ]);
|
||||||
|
// $lwNote->delete();
|
||||||
|
// $needUpdate = 0;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // Nếu $lwNote->n_time_type == 'S' hoặc 'C' => 0.5
|
||||||
|
// if ($needUpdate == 1.0) {
|
||||||
|
// // Chuyển cả note ALL thành phép
|
||||||
|
// $lwNote->update(['n_reason' => 'ONLEAVE']);
|
||||||
|
// $needUpdate -= 0.5;
|
||||||
|
// } else { // $needUpdate == 0.5
|
||||||
|
// // S hoặc C, chỉ cần chuyển đúng 0.5 ngày
|
||||||
|
// $lwNote->update(['n_reason' => 'ONLEAVE']);
|
||||||
|
// $needUpdate = 0;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // Khi note phép và k tồn tại nghỉ không phép => phép + dồn cho tháng sau
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// $note->delete();
|
||||||
|
// $this->createOrUpdateRecordForCurrentMonth($month, $year);
|
||||||
|
// return response()->json(['message' => 'Delete success', 'status' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function export(Request $request)
|
public function export(Request $request)
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ Route::middleware('api')
|
||||||
], function () {
|
], function () {
|
||||||
Route::get('/all', [TicketController::class, 'getAll'])->middleware('check.permission:admin.hr');
|
Route::get('/all', [TicketController::class, 'getAll'])->middleware('check.permission:admin.hr');
|
||||||
Route::get('/getByUserId', [TicketController::class, 'getByUserId'])->middleware('check.permission:admin.hr.staff');
|
Route::get('/getByUserId', [TicketController::class, 'getByUserId'])->middleware('check.permission:admin.hr.staff');
|
||||||
|
Route::post('/update', [TicketController::class, 'updateTicket'])->middleware('check.permission:admin.hr');
|
||||||
Route::post('/create', [TicketController::class, 'createTicket'])->middleware('check.permission:admin.hr.staff');
|
Route::post('/create', [TicketController::class, 'createTicket'])->middleware('check.permission:admin.hr.staff');
|
||||||
Route::get('/delete', [TicketController::class, 'deleteTicket'])->middleware('check.permission:admin.hr.staff');
|
Route::get('/delete', [TicketController::class, 'deleteTicket'])->middleware('check.permission:admin.hr.staff');
|
||||||
Route::post('/handle-ticket', [TicketController::class, 'handleTicket'])->middleware('check.permission:admin');
|
Route::post('/handle-ticket', [TicketController::class, 'handleTicket'])->middleware('check.permission:admin');
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,15 @@
|
||||||
namespace Modules\Auth\app\Http\Controllers;
|
namespace Modules\Auth\app\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\LeaveDays;
|
||||||
use App\Traits\IsAPI;
|
use App\Traits\IsAPI;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Modules\Auth\app\Models\User;
|
use Modules\Auth\app\Models\User;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Modules\Admin\app\Models\Category;
|
||||||
use SimpleSoftwareIO\QrCode\Facades\QrCode;
|
use SimpleSoftwareIO\QrCode\Facades\QrCode;
|
||||||
|
|
||||||
class UserController extends Controller
|
class UserController extends Controller
|
||||||
|
|
@ -34,9 +36,36 @@ class UserController extends Controller
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($request->has('id')) {
|
if ($request->has('id')) {
|
||||||
$payload = $request->only(['name', 'email', 'permission']);
|
$payload = $request->only(['name', 'email', 'permission', 'is_permanent']);
|
||||||
$user = User::find($request->id);
|
$user = User::find($request->id);
|
||||||
|
|
||||||
|
// Không cho chuyển từ chính thức thành lại thử việc
|
||||||
|
if (!$request->is_permanent && $user->is_permanent) {
|
||||||
|
return response()->json(['status' => false, 'message' => 'You cannot change an employee from permanent to probationary.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thêm ngày phép khi thành nhân viên chính thức
|
||||||
|
if ($request->is_permanent && !$user->is_permanent) {
|
||||||
|
$userLeaveDay = LeaveDays::where('ld_user_id', $user->id)
|
||||||
|
->where('ld_year', Carbon::now()->year)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($userLeaveDay) {
|
||||||
|
$permanentCategory = Category::where('c_type', 'PERMANENT_ONLEAVE')->where('c_code', "PERMANENT")->first();
|
||||||
|
$permanentDefault = (int) $permanentCategory->c_value; // Ngày phép khi thành nv chính thức
|
||||||
|
$userLeaveDay->ld_day_total = $permanentDefault;
|
||||||
|
|
||||||
|
$newNote = "Cộng ngày phép cho nhân viên chính thức"; // Thêm ghi chú
|
||||||
|
if (!empty($userLeaveDay->ld_note)) {
|
||||||
|
$userLeaveDay->ld_note = $userLeaveDay->ld_note . "\n" . $newNote;
|
||||||
|
} else {
|
||||||
|
$userLeaveDay->ld_note = $newNote;
|
||||||
|
}
|
||||||
|
$userLeaveDay->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload['permanent_date'] = Carbon::now()->toDateString();
|
||||||
$user->update($payload);
|
$user->update($payload);
|
||||||
return response()->json(['data' => $user, 'status' => true, 'message' => 'Update successful']);
|
return response()->json(['data' => $user, 'status' => true, 'message' => 'Update successful']);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -44,7 +73,19 @@ class UserController extends Controller
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'email' => $request->email,
|
'email' => $request->email,
|
||||||
'password' => bcrypt('Work@1234'),
|
'password' => bcrypt('Work@1234'),
|
||||||
'permission' => $request->permission
|
'permission' => $request->permission,
|
||||||
|
'is_permanent' => false
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Khởi tạo LeaveDays cho nhân viên mới
|
||||||
|
LeaveDays::insert([
|
||||||
|
'ld_user_id' => $user->id,
|
||||||
|
'ld_day_total' => 0,
|
||||||
|
'ld_year' => Carbon::now()->year,
|
||||||
|
'ld_additional_day' => 0,
|
||||||
|
'ld_note' => '',
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user_res = [
|
$user_res = [
|
||||||
|
|
@ -98,8 +139,6 @@ class UserController extends Controller
|
||||||
return response()->json(['data' => ['user' => $user_res, 'gitea' => "dev", 'zulip' => "dev"], 'status' => true, 'message' => 'Create successful']);
|
return response()->json(['data' => ['user' => $user_res, 'gitea' => "dev", 'zulip' => "dev"], 'status' => true, 'message' => 'Create successful']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['status' => false, 'message' => 'Process fail']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ class User extends Authenticatable implements JWTSubject
|
||||||
'name',
|
'name',
|
||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
'permission'
|
'permission',
|
||||||
|
'is_permanent',
|
||||||
|
'permanent_date'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
|
||||||
// ->dailyAt('18:00');
|
// ->dailyAt('18:00');
|
||||||
|
|
||||||
// Chạy command vào ngày 31/12 lúc 23:59:59 mỗi năm
|
// Chạy command vào ngày 31/12 lúc 23:59:59 mỗi năm
|
||||||
// $schedule->command('initialize:leavedays')->yearlyOn(12, 31, '23:59:59');
|
$schedule->command('initialize:leavedays')->yearlyOn(12, 31, '23:59:59');
|
||||||
$schedule->command('leave:deduct')->yearlyOn(3, 31, '23:59:59');
|
$schedule->command('leave:deduct')->yearlyOn(3, 31, '23:59:59');
|
||||||
|
|
||||||
// Chạy buổi sáng lúc 12:00
|
// Chạy buổi sáng lúc 12:00
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Modules\Admin\app\Models\Category;
|
||||||
|
|
||||||
class AddMonthlyLeaveDays implements ShouldQueue
|
class AddMonthlyLeaveDays implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
@ -18,6 +19,8 @@ class AddMonthlyLeaveDays implements ShouldQueue
|
||||||
protected $month;
|
protected $month;
|
||||||
protected $year;
|
protected $year;
|
||||||
|
|
||||||
|
private const ONLEAVE_PER_MONTH = 1; // Ngày phép cộng mỗi tháng
|
||||||
|
|
||||||
public function __construct($month = null, $year = null)
|
public function __construct($month = null, $year = null)
|
||||||
{
|
{
|
||||||
$this->month = $month ?? Carbon::now()->month;
|
$this->month = $month ?? Carbon::now()->month;
|
||||||
|
|
@ -29,6 +32,11 @@ class AddMonthlyLeaveDays implements ShouldQueue
|
||||||
$users = User::get();
|
$users = User::get();
|
||||||
|
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
|
// Nếu là nhân viên chưa chính thức, ko cộng phép
|
||||||
|
if (!$user->is_permanent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$leaveDay = LeaveDays::where('ld_user_id', $user->id)
|
$leaveDay = LeaveDays::where('ld_user_id', $user->id)
|
||||||
->where('ld_year', $this->year)
|
->where('ld_year', $this->year)
|
||||||
->first();
|
->first();
|
||||||
|
|
@ -46,11 +54,36 @@ class AddMonthlyLeaveDays implements ShouldQueue
|
||||||
]);
|
]);
|
||||||
$leaveDay->save();
|
$leaveDay->save();
|
||||||
} else {
|
} else {
|
||||||
// Kiểm tra nếu số ngày phép hiện tại nhỏ hơn tháng hiện tại
|
// Check có phải là nhân viên chính thức trong năm nay (Nhân viên mới)
|
||||||
|
if ($user->permanent_date && $user->permanent_date !== '0000-00-00') {
|
||||||
|
$permenantYear = Carbon::parse($user->permanent_date)->year;
|
||||||
|
|
||||||
|
if ($permenantYear === $this->year) {
|
||||||
|
$permanentCategory = Category::where('c_type', 'PERMANENT_ONLEAVE')->where('c_code', "PERMANENT")->first();
|
||||||
|
$permanentDefault = (int) $permanentCategory->c_value; // Ngày phép khi thành nv chính thức
|
||||||
|
|
||||||
|
$permanentMonth = Carbon::parse($user->permanent_date)->month;
|
||||||
|
if ($this->month > $leaveDay->ld_day_total - ($permanentDefault - $permanentMonth)) {
|
||||||
|
$leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH;
|
||||||
|
|
||||||
|
// Xử lý ghi chú
|
||||||
|
$newNote = "Cập nhật ngày phép đến tháng " . $this->month;
|
||||||
|
if (!empty($leaveDay->ld_note)) {
|
||||||
|
// Nếu đã có ghi chú, thêm ghi chú mới vào và xuống dòng
|
||||||
|
$leaveDay->ld_note = $leaveDay->ld_note . "\n" . $newNote;
|
||||||
|
} else {
|
||||||
|
// Nếu chưa có ghi chú, gán ghi chú mới
|
||||||
|
$leaveDay->ld_note = $newNote;
|
||||||
|
}
|
||||||
|
$leaveDay->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kiểm tra nếu số ngày phép hiện tại nhỏ hơn tháng hiện tại (Nhân viên cũ)
|
||||||
if ($leaveDay->ld_day_total < $this->month) {
|
if ($leaveDay->ld_day_total < $this->month) {
|
||||||
// Cập nhật số ngày phép bằng với tháng hiện tại
|
// Cộng mỗi tháng 1 ngày phép cho nhân viên
|
||||||
$oldDays = $leaveDay->ld_day_total;
|
$leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH;
|
||||||
$leaveDay->ld_day_total = $this->month;
|
|
||||||
|
|
||||||
// Xử lý ghi chú
|
// Xử lý ghi chú
|
||||||
$newNote = "Cập nhật ngày phép đến tháng " . $this->month;
|
$newNote = "Cập nhật ngày phép đến tháng " . $this->month;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
||||||
class DeductLeaveDays implements ShouldQueue
|
class DeductLeaveDays implements ShouldQueue
|
||||||
|
|
@ -42,38 +41,19 @@ class DeductLeaveDays implements ShouldQueue
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
|
// Lấy tổng ngày nghỉ phép 3 tháng đầu trong năm
|
||||||
|
$usedOnleaveDaysTotal = Notes::join('categories', function ($join) {
|
||||||
$join->on('notes.n_time_type', '=', 'categories.c_code')
|
$join->on('notes.n_time_type', '=', 'categories.c_code')
|
||||||
->where('categories.c_type', 'TIME_TYPE');
|
->where('categories.c_type', 'TIME_TYPE');
|
||||||
})
|
})
|
||||||
->select(
|
->where('n_user_id', $user->id)
|
||||||
DB::raw('notes.n_user_id as n_user_id'),
|
->where('n_year', $this->year)
|
||||||
DB::raw('notes.n_year as year'),
|
->where('n_month', "<=", 3)
|
||||||
DB::raw('SUM(categories.c_value) as leave_days')
|
->where('n_reason', 'ONLEAVE')
|
||||||
)
|
->sum('categories.c_value');
|
||||||
->where('notes.n_year', $this->year)
|
|
||||||
->where('notes.n_user_id', $user->id)
|
|
||||||
->where('notes.n_reason', 'ONLEAVE')
|
|
||||||
->groupBy(DB::raw('notes.n_year'))
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($totalLeaveDaysByMonth) {
|
$existingData->ld_additional_day = $usedOnleaveDaysTotal ?? 0;
|
||||||
//Nếu ngày phép thừa năm trước chưa sử dụng hết => cập nhật lại ngày đó (Ngày tồn đọng - ngày sử dụng)
|
$existingData->save();
|
||||||
if ($existingData->ld_additional_day > $totalLeaveDaysByMonth->leave_days) {
|
|
||||||
LeaveDays::where('ld_year', $this->year)
|
|
||||||
->where('ld_user_id', $user->id)
|
|
||||||
->update([
|
|
||||||
'ld_additional_day' => $totalLeaveDaysByMonth->leave_days,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Nếu không sử dụng ngày nghỉ còn lại ở năm rồi thì xóa => theo luật ld
|
|
||||||
LeaveDays::where('ld_year', $this->year)
|
|
||||||
->where('ld_user_id', $user->id)
|
|
||||||
->update([
|
|
||||||
'ld_additional_day' => "0",
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ class InitializeLeaveDays implements ShouldQueue
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$users = User::get();
|
$users = User::get();
|
||||||
$ld_day_total = 12;
|
$ld_day_total = Carbon::now()->month; // Khởi tạo phép hiện có bằng tháng hiện tại
|
||||||
|
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
// Kiểm tra xem dữ liệu của user này đã tồn tại cho năm hiện tại chưa
|
// Kiểm tra xem dữ liệu của user này đã tồn tại cho năm hiện tại chưa
|
||||||
$existingData = LeaveDays::where('ld_user_id', $user->id)
|
$existingData = LeaveDays::where('ld_user_id', $user->id)
|
||||||
|
|
@ -82,7 +83,7 @@ class InitializeLeaveDays implements ShouldQueue
|
||||||
// Tạo dữ liệu cho năm hiện tại
|
// Tạo dữ liệu cho năm hiện tại
|
||||||
LeaveDays::insert([
|
LeaveDays::insert([
|
||||||
'ld_user_id' => $user->id,
|
'ld_user_id' => $user->id,
|
||||||
'ld_day_total' => $ld_day_total,
|
'ld_day_total' => $user->is_permanent ? $ld_day_total : 0, // Nếu là nhân viên mới, ko cấp phép
|
||||||
'ld_year' => $this->year,
|
'ld_year' => $this->year,
|
||||||
'ld_additional_day' => $ld_additional_day,
|
'ld_additional_day' => $ld_additional_day,
|
||||||
'ld_note' => $ld_note,
|
'ld_note' => $ld_note,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ class Notes extends Model
|
||||||
'n_time_type',
|
'n_time_type',
|
||||||
'n_reason',
|
'n_reason',
|
||||||
'n_note',
|
'n_note',
|
||||||
|
'ticket_id'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('notes', function (Blueprint $table) {
|
||||||
|
$table->foreignId('ticket_id')
|
||||||
|
->nullable()
|
||||||
|
->constrained('tickets')
|
||||||
|
->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('notes', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['ticket_id']);
|
||||||
|
$table->dropColumn('ticket_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_permanent')->default(true);
|
||||||
|
$table->date('permanent_date');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_permanent');
|
||||||
|
$table->dropColumn('permanent_date');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
DB::table('categories')->insert([
|
||||||
|
[
|
||||||
|
'c_code' => 'PERMANENT',
|
||||||
|
'c_name' => 'Phép cộng nhân viên chính thức',
|
||||||
|
'c_type' => 'PERMANENT_ONLEAVE',
|
||||||
|
'c_value' => 3,
|
||||||
|
'c_active' => 1,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
DB::table('categories')->where('c_code', 'PERMANENT')->delete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
|
|
@ -37,199 +36,167 @@
|
||||||
<title>{{ $data['subject'] }}</title>
|
<title>{{ $data['subject'] }}</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body style="
|
||||||
|
|
||||||
<body style="
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
background-color: #edf2f7;
|
background-color: #edf2f7;
|
||||||
">
|
">
|
||||||
<table style="margin: 0 auto">
|
<table style="margin: 0 auto">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<table
|
<table
|
||||||
style="
|
style="
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 768px;
|
width: 768px;
|
||||||
">
|
">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="top" style="padding: 36px 24px;">
|
<td align="center" valign="top" style="padding: 36px 24px;">
|
||||||
<a href="{{ config('app.url') }}" target="_blank" style="display: inline-block;">
|
<a href="{{ config('app.url') }}" target="_blank" style="display: inline-block;">
|
||||||
<img src="https://apactech.io/wp-content/uploads/2022/12/APAC-TECH_side-e1670975093601-190x78.png"
|
<img src="https://apactech.io/wp-content/uploads/2022/12/APAC-TECH_side-e1670975093601-190x78.png"
|
||||||
alt="Logo" border="0" width="100"
|
alt="Logo" border="0" width="100"
|
||||||
style="display: block; width: 100px; max-width: 100px; min-width: 48px;">
|
style="display: block; width: 100px; max-width: 100px; min-width: 48px;">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<table
|
<table
|
||||||
style="
|
style="
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
width: 768px;
|
width: 768px;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
">
|
">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<h3 style="color: #222222; margin: 5px 0 0 0; font-weight: bold">
|
<h3 style="color: #222222; margin: 5px 0 0 0; font-weight: bold">
|
||||||
Dear Admin,
|
Dear Admin,
|
||||||
</h3>
|
</h3>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<p style=" white-space:pre-line; margin: 0; margin-bottom: 5px">
|
<p style=" white-space:pre-line; margin: 0; margin-bottom: 5px">
|
||||||
Employee <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span> has sent a request ticket, the specific content is as follows:
|
Employee <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span> has sent a <a href="{{ config('app.client_url') . $data['link'] }}"> request ticket</a>, the specific content is as follows:
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div style="padding-left: 10px;color: #696969; margin-bottom: 15px">
|
|
||||||
<p style="padding: 3px;">Name: <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span></p>
|
|
||||||
<p style="padding: 3px;">Date: <span style="color: #222222;font-weight: bold;">{{ $data['date'] }}</span></p>
|
|
||||||
<p style="padding: 3px;">Type: <span style="color: #222222;font-weight: bold;">{{ $data['type'] }}</span></p>
|
|
||||||
<p style="padding: 3px;">Note: <span style="color: #222222;font-weight: bold;">{{ $data['note'] }}</span></p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p
|
|
||||||
style="
|
|
||||||
margin: 0 0 16px;
|
|
||||||
padding: 5px;
|
|
||||||
margin: 5px;
|
|
||||||
text-align: center;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href="{{ config('app.client_url') . $data['link'] }}"
|
|
||||||
style="
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: rgba(68, 115, 196);
|
|
||||||
background-image: linear-gradient(
|
|
||||||
to top left,
|
|
||||||
rgba(0, 0, 0, 0.2),
|
|
||||||
rgba(0, 0, 0, 0.2) 30%,
|
|
||||||
rgba(0, 0, 0, 0)
|
|
||||||
);
|
|
||||||
text-decoration: none;
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 150%;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px 12px;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
Check now</a
|
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="padding-left: 10px;color: #696969; margin-bottom: 15px">
|
||||||
|
<p style="padding: 3px;">Name: <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span></p>
|
||||||
|
<p style="padding: 3px;">Date: <span style="color: #222222;font-weight: bold;">{{ $data['date'] }}</span></p>
|
||||||
|
<p style="padding: 3px;">Type: <span style="color: #222222;font-weight: bold;">{{ $data['type'] }}</span></p>
|
||||||
|
<p style="padding: 3px;">Note: <span style="color: #222222;font-weight: bold;">{{ $data['note'] }}</span></p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
<p style="text-align: center">
|
<p style="text-align: center">
|
||||||
Or you can quick
|
You can quick
|
||||||
<span style="font-weight: bold">Confirm</span> or
|
<span style="font-weight: bold">Confirm</span> or
|
||||||
<span style="font-weight: bold">Refuse</span> here:
|
<span style="font-weight: bold">Refuse</span> here:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<a
|
<a
|
||||||
href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'confirm', 'admin_email' => $data['admin_email']]) }}"
|
href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'confirm', 'admin_email' => $data['admin_email']]) }}"
|
||||||
style="
|
style="
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: #12b886;
|
background-color: #12b886;
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
to top left,
|
to top left,
|
||||||
rgba(0, 0, 0, 0.2),
|
rgba(0, 0, 0, 0.2),
|
||||||
rgba(0, 0, 0, 0.2) 30%,
|
rgba(0, 0, 0, 0.2) 30%,
|
||||||
rgba(0, 0, 0, 0)
|
rgba(0, 0, 0, 0)
|
||||||
);
|
);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 150%;
|
line-height: 150%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
"
|
">
|
||||||
>
|
Confirm</a>
|
||||||
Confirm</a
|
|
||||||
>
|
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'refuse', 'admin_email' => $data['admin_email']]) }}"
|
href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'refuse', 'admin_email' => $data['admin_email']]) }}"
|
||||||
style="
|
style="
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: #f03e3e;
|
background-color: #f03e3e;
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
to top left,
|
to top left,
|
||||||
rgba(0, 0, 0, 0.2),
|
rgba(0, 0, 0, 0.2),
|
||||||
rgba(0, 0, 0, 0.2) 30%,
|
rgba(0, 0, 0, 0.2) 30%,
|
||||||
rgba(0, 0, 0, 0)
|
rgba(0, 0, 0, 0)
|
||||||
);
|
);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 150%;
|
line-height: 150%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
"
|
">
|
||||||
>
|
Refuse</a>
|
||||||
Refuse</a
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td style="color: #222222;">
|
||||||
<tr>
|
<div style="margin-top: 3rem">
|
||||||
<td>
|
<p><span style="font-weight: bold">Note</span>: If you are redirected to a <span style="font-weight: bold">404 page</span>, it means:</p>
|
||||||
<table
|
<p>1. The ticket has already been approved by another admin.</p>
|
||||||
style="
|
<p>2. The ticket has been deleted.</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table
|
||||||
|
style="
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
width: 768px;
|
width: 768px;
|
||||||
">
|
">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<h5
|
<h5
|
||||||
style="
|
style="
|
||||||
color: #222222;
|
color: #222222;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 10px 36px;
|
padding: 10px 36px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
">
|
">
|
||||||
<p>© 2024 APAC Tech.</p>
|
<p>© 2024 APAC Tech.</p>
|
||||||
</h5>
|
</h5>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</body>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
|
|
@ -48,6 +48,7 @@ export const getTickets = API_URL + 'v1/admin/ticket/all'
|
||||||
export const getTicketsOfUser = API_URL + 'v1/admin/ticket/getByUserId'
|
export const getTicketsOfUser = API_URL + 'v1/admin/ticket/getByUserId'
|
||||||
export const deleteTicket = API_URL + 'v1/admin/ticket/delete'
|
export const deleteTicket = API_URL + 'v1/admin/ticket/delete'
|
||||||
export const addTicket = API_URL + 'v1/admin/ticket/create'
|
export const addTicket = API_URL + 'v1/admin/ticket/create'
|
||||||
|
export const updateTicket = API_URL + 'v1/admin/ticket/update'
|
||||||
export const handleTicket = API_URL + 'v1/admin/ticket/handle-ticket'
|
export const handleTicket = API_URL + 'v1/admin/ticket/handle-ticket'
|
||||||
|
|
||||||
//Users
|
//Users
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,42 @@
|
||||||
export const PageNotFound = () => {
|
import {
|
||||||
return <>{'Not found!'}</>
|
Box,
|
||||||
|
Button,
|
||||||
|
Container,
|
||||||
|
Image,
|
||||||
|
SimpleGrid,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from '@mantine/core'
|
||||||
|
import image404 from '../../../public/404Image.jpg'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
|
const PageNotFound = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container mt="lg">
|
||||||
|
<SimpleGrid spacing={{ base: 40, sm: 80 }} cols={{ base: 1, sm: 2 }}>
|
||||||
|
<Box py="3rem">
|
||||||
|
<Title> Something is not right...</Title>
|
||||||
|
<Text c="dimmed" size="lg">
|
||||||
|
Page you are trying to open does not exist. You may have mistyped
|
||||||
|
the address, or the page has been moved to another URL. If you think
|
||||||
|
this is an error contact support.
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="md"
|
||||||
|
mt="xl"
|
||||||
|
onClick={() => navigate('/')}
|
||||||
|
>
|
||||||
|
Get back to home page
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Image src={image404} />
|
||||||
|
</SimpleGrid>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PageNotFound
|
export default PageNotFound
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,37 @@
|
||||||
.dialogText {
|
.dialogText {
|
||||||
color: light-dark(#2d353c, white);
|
color: light-dark(#2d353c, white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Thêm styles cho Modal xác nhận xóa */
|
||||||
|
.deleteModal {
|
||||||
|
background-color: light-dark(white, #2d353c);
|
||||||
|
text-align: center;
|
||||||
|
border: solid 1px #ff4646;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteModalTitle {
|
||||||
|
color: #ff4646;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteModalContent {
|
||||||
|
color: light-dark(#2d353c, white);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteModalFooter {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteButton {
|
||||||
|
background-color: #ff4646;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteButton:hover {
|
||||||
|
background-color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
import { getListMaster, getTickets, handleTicket } from '@/api/Admin'
|
import {
|
||||||
|
getListMaster,
|
||||||
|
getTickets,
|
||||||
|
handleTicket,
|
||||||
|
updateTicket,
|
||||||
|
} from '@/api/Admin'
|
||||||
import { DataTablePagination } from '@/components/DataTable/DataTable'
|
import { DataTablePagination } from '@/components/DataTable/DataTable'
|
||||||
import { create } from '@/rtk/helpers/CRUD'
|
import { create, update } from '@/rtk/helpers/CRUD'
|
||||||
import { get } from '@/rtk/helpers/apiService'
|
import { get } from '@/rtk/helpers/apiService'
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
|
|
@ -9,12 +14,13 @@ import {
|
||||||
HoverCard,
|
HoverCard,
|
||||||
Modal,
|
Modal,
|
||||||
Select,
|
Select,
|
||||||
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
Textarea,
|
Textarea,
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
import { useForm } from '@mantine/form'
|
import { useForm } from '@mantine/form'
|
||||||
import { notifications } from '@mantine/notifications'
|
import { notifications } from '@mantine/notifications'
|
||||||
import { IconCheckbox, IconSquareXFilled } from '@tabler/icons-react'
|
import { IconCheckbox, IconEdit, IconSquareXFilled } from '@tabler/icons-react'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import classes from './TicketsManagement.module.css'
|
import classes from './TicketsManagement.module.css'
|
||||||
|
|
@ -24,6 +30,7 @@ type TTickets = {
|
||||||
ticket_id: number
|
ticket_id: number
|
||||||
admin_note: string
|
admin_note: string
|
||||||
action: string
|
action: string
|
||||||
|
status: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type TListTickets = {
|
type TListTickets = {
|
||||||
|
|
@ -78,6 +85,7 @@ const TicketsManagement = () => {
|
||||||
end_period: '',
|
end_period: '',
|
||||||
reason: '',
|
reason: '',
|
||||||
type: '',
|
type: '',
|
||||||
|
status: '',
|
||||||
})
|
})
|
||||||
const [disableBtn, setDisableBtn] = useState(false)
|
const [disableBtn, setDisableBtn] = useState(false)
|
||||||
const [filter, setFilter] = useState({
|
const [filter, setFilter] = useState({
|
||||||
|
|
@ -86,6 +94,7 @@ const TicketsManagement = () => {
|
||||||
})
|
})
|
||||||
const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
|
const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
|
||||||
const [dataReason, setDataReason] = useState<DataReason[]>([])
|
const [dataReason, setDataReason] = useState<DataReason[]>([])
|
||||||
|
const [isRefuseConfirmOpen, setIsRefuseConfirmOpen] = useState<boolean>(false)
|
||||||
|
|
||||||
const getListMasterByType = async (type: string) => {
|
const getListMasterByType = async (type: string) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -156,7 +165,7 @@ const TicketsManagement = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'reason',
|
name: 'reason',
|
||||||
size: '15%',
|
size: '10%',
|
||||||
header: 'Notes',
|
header: 'Notes',
|
||||||
render: (row: any) => {
|
render: (row: any) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -198,7 +207,7 @@ const TicketsManagement = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'admin_note',
|
name: 'admin_note',
|
||||||
size: '15%',
|
size: '10%',
|
||||||
header: 'Admin Notes',
|
header: 'Admin Notes',
|
||||||
render: (row: any) => {
|
render: (row: any) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -226,6 +235,18 @@ const TicketsManagement = () => {
|
||||||
size: '10%',
|
size: '10%',
|
||||||
header: 'Updated By',
|
header: 'Updated By',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'updated_at',
|
||||||
|
size: '10%',
|
||||||
|
header: 'Updated At',
|
||||||
|
render: (row: any) => {
|
||||||
|
if (row?.updated_at) {
|
||||||
|
return (
|
||||||
|
<Box>{moment(row?.updated_at).format('HH:mm:ss DD/MM/YYYY')}</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '#',
|
name: '#',
|
||||||
size: '5%',
|
size: '5%',
|
||||||
|
|
@ -254,7 +275,23 @@ const TicketsManagement = () => {
|
||||||
height={20}
|
height={20}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
) : null
|
) : (
|
||||||
|
<Box className={classes.optionIcon}>
|
||||||
|
<IconEdit
|
||||||
|
className={classes.editIcon}
|
||||||
|
onClick={() => {
|
||||||
|
setAction('update')
|
||||||
|
setItem(row)
|
||||||
|
form.reset()
|
||||||
|
console.log(row)
|
||||||
|
form.setFieldValue('status', row.status)
|
||||||
|
form.setFieldValue('admin_note', row.admin_note)
|
||||||
|
}}
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
@ -347,6 +384,27 @@ const TicketsManagement = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleUpdate = async (values: TTickets) => {
|
||||||
|
try {
|
||||||
|
const res = await update(
|
||||||
|
updateTicket + `?ticket_id=${item?.id}`,
|
||||||
|
{
|
||||||
|
ticket_id: item.id,
|
||||||
|
admin_note: values.admin_note,
|
||||||
|
status: values.status,
|
||||||
|
},
|
||||||
|
getAllTickets,
|
||||||
|
)
|
||||||
|
if (res === true) {
|
||||||
|
setAction('')
|
||||||
|
setIsRefuseConfirmOpen(false)
|
||||||
|
form.reset()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAllTickets()
|
getAllTickets()
|
||||||
}, [filter])
|
}, [filter])
|
||||||
|
|
@ -356,6 +414,7 @@ const TicketsManagement = () => {
|
||||||
ticket_id: 0,
|
ticket_id: 0,
|
||||||
action: '',
|
action: '',
|
||||||
admin_note: '',
|
admin_note: '',
|
||||||
|
status: '',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -416,21 +475,38 @@ const TicketsManagement = () => {
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
opened={action === 'confirm' || action === 'refuse'}
|
opened={
|
||||||
|
action === 'confirm' || action === 'refuse' || action === 'update'
|
||||||
|
}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setAction('')
|
setAction('')
|
||||||
|
setIsRefuseConfirmOpen(false)
|
||||||
form.reset()
|
form.reset()
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
<Text pl={'sm'} fw={700} fz={'lg'}>
|
<Text pl={'sm'} fw={700} fz={'lg'}>
|
||||||
{action === 'confirm' ? 'Confirm Ticket' : 'Refuse Ticket'}
|
{action === 'confirm'
|
||||||
|
? 'Confirm Ticket'
|
||||||
|
: action === 'refuse'
|
||||||
|
? 'Refuse Ticket'
|
||||||
|
: 'Update Ticket'}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.onSubmit(async (values) => {
|
onSubmit={form.onSubmit(async (values) => {
|
||||||
setDisableBtn(true)
|
setDisableBtn(true)
|
||||||
await handleSave(values)
|
|
||||||
|
if (action === 'update') {
|
||||||
|
if (values.status === 'REFUSED') {
|
||||||
|
setIsRefuseConfirmOpen(true)
|
||||||
|
} else {
|
||||||
|
await handleUpdate(values)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await handleSave(values)
|
||||||
|
}
|
||||||
|
|
||||||
setDisableBtn(false)
|
setDisableBtn(false)
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|
@ -509,14 +585,39 @@ const TicketsManagement = () => {
|
||||||
|
|
||||||
<Textarea
|
<Textarea
|
||||||
label="Admin Notes"
|
label="Admin Notes"
|
||||||
// required
|
|
||||||
value={form.values.admin_note}
|
value={form.values.admin_note}
|
||||||
onChange={(e) => form.setFieldValue('admin_note', e.target.value)}
|
onChange={(e) => form.setFieldValue('admin_note', e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{action === 'update' && item.status !== 'REFUSED' ? (
|
||||||
|
<Switch
|
||||||
|
mt="md"
|
||||||
|
color="red"
|
||||||
|
label={
|
||||||
|
<Text size="sm">
|
||||||
|
Change status to{' '}
|
||||||
|
<Text span color="red" fw="bold">
|
||||||
|
Refuse
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
style={{ width: 'fit-content' }}
|
||||||
|
checked={form.values.status === 'REFUSED'}
|
||||||
|
onChange={(event) =>
|
||||||
|
form.setFieldValue(
|
||||||
|
'status',
|
||||||
|
event.currentTarget.checked ? 'REFUSED' : 'CONFIRMED',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
<Box ta={'center'}>
|
<Box ta={'center'}>
|
||||||
<Button
|
<Button
|
||||||
mt={'lg'}
|
mt={'lg'}
|
||||||
bg={'green'}
|
bg={action === 'update' ? 'blue' : 'green'}
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={disableBtn}
|
disabled={disableBtn}
|
||||||
>
|
>
|
||||||
|
|
@ -526,6 +627,50 @@ const TicketsManagement = () => {
|
||||||
</Box>
|
</Box>
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
opened={isRefuseConfirmOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsRefuseConfirmOpen(false)
|
||||||
|
}}
|
||||||
|
centered
|
||||||
|
size="sm"
|
||||||
|
classNames={{
|
||||||
|
content: classes.deleteModal,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text className={classes.deleteModalTitle}>Confirm Update</Text>
|
||||||
|
<Text className={classes.deleteModalContent}>
|
||||||
|
Changing ticket status to <strong>Refused</strong> will also delete
|
||||||
|
all related notes.
|
||||||
|
</Text>
|
||||||
|
<Text className={classes.deleteModalContent}>
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Box className={classes.deleteModalFooter}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setIsRefuseConfirmOpen(false)
|
||||||
|
}}
|
||||||
|
disabled={disableBtn}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={classes.deleteButton}
|
||||||
|
onClick={async () => {
|
||||||
|
setDisableBtn(true)
|
||||||
|
await handleUpdate(form.values)
|
||||||
|
setDisableBtn(false)
|
||||||
|
}}
|
||||||
|
disabled={disableBtn}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -493,7 +493,11 @@ const Timekeeping = () => {
|
||||||
>
|
>
|
||||||
<Text className={classes.deleteModalTitle}>Confirm Delete</Text>
|
<Text className={classes.deleteModalTitle}>Confirm Delete</Text>
|
||||||
<Text className={classes.deleteModalContent}>
|
<Text className={classes.deleteModalContent}>
|
||||||
Are you sure you want to delete this note?
|
This action will change the ticket status to <strong>Refused</strong>{' '}
|
||||||
|
and delete all related notes.
|
||||||
|
</Text>
|
||||||
|
<Text className={classes.deleteModalContent}>
|
||||||
|
Are you sure you want to proceed?
|
||||||
</Text>
|
</Text>
|
||||||
<Box className={classes.deleteModalFooter}>
|
<Box className={classes.deleteModalFooter}>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,37 @@
|
||||||
.dialogText {
|
.dialogText {
|
||||||
color: light-dark(#2d353c, white);
|
color: light-dark(#2d353c, white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Thêm styles cho Modal xác nhận xóa */
|
||||||
|
.deleteModal {
|
||||||
|
background-color: light-dark(white, #2d353c);
|
||||||
|
text-align: center;
|
||||||
|
border: solid 1px rgb(9, 132, 132);
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteModalTitle {
|
||||||
|
color: rgb(9, 132, 132);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteModalContent {
|
||||||
|
color: light-dark(#2d353c, white);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteModalFooter {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteButton {
|
||||||
|
background-color: rgb(9, 132, 132);
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteButton:hover {
|
||||||
|
background-color: rgb(9, 132, 132);
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
Group,
|
Group,
|
||||||
Modal,
|
Modal,
|
||||||
MultiSelect,
|
MultiSelect,
|
||||||
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
|
|
@ -24,28 +25,31 @@ const UsersManagement = () => {
|
||||||
const [users, setUsers] = useState<TUser[]>([])
|
const [users, setUsers] = useState<TUser[]>([])
|
||||||
const [action, setAction] = useState('')
|
const [action, setAction] = useState('')
|
||||||
const [activeBtn, setActiveBtn] = useState(false)
|
const [activeBtn, setActiveBtn] = useState(false)
|
||||||
const [item, setItem] = useState({ id: 0 })
|
const [item, setItem] = useState({ id: 0, is_permanent: false })
|
||||||
const [disableBtn, setDisableBtn] = useState(false)
|
const [disableBtn, setDisableBtn] = useState(false)
|
||||||
const [info, setInfo] = useState('')
|
const [info, setInfo] = useState('')
|
||||||
|
const [isPermanentConfirmOpen, setIsPermanentConfirmOpen] =
|
||||||
|
useState<boolean>(false)
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'id',
|
name: 'id',
|
||||||
size: '3%',
|
size: '5%',
|
||||||
header: 'ID',
|
header: 'ID',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
size: '17%',
|
size: '20%',
|
||||||
header: 'Name',
|
header: 'Name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'email',
|
name: 'email',
|
||||||
size: '26%',
|
size: '25%',
|
||||||
header: 'Email',
|
header: 'Email',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'permission',
|
name: 'permission',
|
||||||
size: '10%',
|
size: '20%',
|
||||||
header: 'Permission',
|
header: 'Permission',
|
||||||
render: (row: TUser) => {
|
render: (row: TUser) => {
|
||||||
if (row.permission.includes(',')) {
|
if (row.permission.includes(',')) {
|
||||||
|
|
@ -57,9 +61,21 @@ const UsersManagement = () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'is_permanent',
|
||||||
|
size: '20%',
|
||||||
|
header: 'Employment Type',
|
||||||
|
render: (row: TUser) => {
|
||||||
|
return row.is_permanent ? (
|
||||||
|
<Badge color="teal">Permanent</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge color="violet">Probation</Badge>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '#',
|
name: '#',
|
||||||
size: '5%',
|
size: '10%',
|
||||||
header: 'Action',
|
header: 'Action',
|
||||||
render: (row: TUser) => {
|
render: (row: TUser) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -68,6 +84,8 @@ const UsersManagement = () => {
|
||||||
className={classes.editIcon}
|
className={classes.editIcon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAction('edit')
|
setAction('edit')
|
||||||
|
setItem(row)
|
||||||
|
form.reset()
|
||||||
form.setValues(row)
|
form.setValues(row)
|
||||||
}}
|
}}
|
||||||
width={20}
|
width={20}
|
||||||
|
|
@ -94,6 +112,7 @@ const UsersManagement = () => {
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
permission: '',
|
permission: '',
|
||||||
|
is_permanent: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -128,6 +147,7 @@ const UsersManagement = () => {
|
||||||
const res = await update(createOrUpdateUser, values, getAll)
|
const res = await update(createOrUpdateUser, values, getAll)
|
||||||
if (res === true) {
|
if (res === true) {
|
||||||
setAction('')
|
setAction('')
|
||||||
|
setIsPermanentConfirmOpen(false)
|
||||||
form.reset()
|
form.reset()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -172,6 +192,7 @@ const UsersManagement = () => {
|
||||||
opened={action === 'add' || action === 'edit'}
|
opened={action === 'add' || action === 'edit'}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setAction('')
|
setAction('')
|
||||||
|
setIsPermanentConfirmOpen(false)
|
||||||
form.reset()
|
form.reset()
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
|
|
@ -183,9 +204,15 @@ const UsersManagement = () => {
|
||||||
<form
|
<form
|
||||||
onSubmit={form.onSubmit(async (values) => {
|
onSubmit={form.onSubmit(async (values) => {
|
||||||
setDisableBtn(true)
|
setDisableBtn(true)
|
||||||
action === 'edit'
|
if (action === 'edit') {
|
||||||
? await handleUpdate(values)
|
if (values.is_permanent && !item.is_permanent) {
|
||||||
: await handleCreate(values)
|
setIsPermanentConfirmOpen(true)
|
||||||
|
} else {
|
||||||
|
await handleUpdate(values)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await handleCreate(values)
|
||||||
|
}
|
||||||
setDisableBtn(false)
|
setDisableBtn(false)
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|
@ -196,6 +223,7 @@ const UsersManagement = () => {
|
||||||
value={form.values.name}
|
value={form.values.name}
|
||||||
error={form.errors.name}
|
error={form.errors.name}
|
||||||
onChange={(e) => form.setFieldValue('name', e.target.value)}
|
onChange={(e) => form.setFieldValue('name', e.target.value)}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|
@ -204,6 +232,7 @@ const UsersManagement = () => {
|
||||||
value={form.values.email}
|
value={form.values.email}
|
||||||
error={form.errors.email}
|
error={form.errors.email}
|
||||||
onChange={(e) => form.setFieldValue('email', e.target.value)}
|
onChange={(e) => form.setFieldValue('email', e.target.value)}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
|
|
@ -224,7 +253,25 @@ const UsersManagement = () => {
|
||||||
e!.filter((p) => p.trim() !== '').join(','),
|
e!.filter((p) => p.trim() !== '').join(','),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
mb={'md'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{action === 'edit' && !item.is_permanent ? (
|
||||||
|
<Switch
|
||||||
|
label="Permanent employee"
|
||||||
|
style={{ width: 'fit-content' }}
|
||||||
|
checked={form.values.is_permanent}
|
||||||
|
onChange={(event) =>
|
||||||
|
form.setFieldValue(
|
||||||
|
'is_permanent',
|
||||||
|
event.currentTarget.checked,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
<Box ta={'center'}>
|
<Box ta={'center'}>
|
||||||
{action === 'add' ? (
|
{action === 'add' ? (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -273,6 +320,50 @@ const UsersManagement = () => {
|
||||||
{info}
|
{info}
|
||||||
</Code>
|
</Code>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* Confirm change to permanent employee */}
|
||||||
|
<Modal
|
||||||
|
opened={isPermanentConfirmOpen}
|
||||||
|
onClose={() => setIsPermanentConfirmOpen(false)}
|
||||||
|
centered
|
||||||
|
size="sm"
|
||||||
|
classNames={{
|
||||||
|
content: classes.deleteModal,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text className={classes.deleteModalTitle}>Confirm Update</Text>
|
||||||
|
<Text className={classes.deleteModalContent}>
|
||||||
|
This action will change the employment type from{' '}
|
||||||
|
<strong>Probation</strong> to <strong>Permanent</strong>.
|
||||||
|
</Text>
|
||||||
|
<Text className={classes.deleteModalContent}>
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Box className={classes.deleteModalFooter}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setIsPermanentConfirmOpen(false)
|
||||||
|
}}
|
||||||
|
disabled={disableBtn}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={classes.deleteButton}
|
||||||
|
onClick={async () => {
|
||||||
|
setDisableBtn(true)
|
||||||
|
await handleUpdate(form.values)
|
||||||
|
setDisableBtn(false)
|
||||||
|
}}
|
||||||
|
disabled={disableBtn}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
className={classes.dialog}
|
className={classes.dialog}
|
||||||
opened={action === 'delete'}
|
opened={action === 'delete'}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ export type TUser = {
|
||||||
email: string
|
email: string
|
||||||
name: string
|
name: string
|
||||||
permission: string
|
permission: string
|
||||||
|
is_permanent: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DataReason = {
|
export type DataReason = {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue