Merge pull request 'master' (#103) from master into dev
Reviewed-on: #103
This commit is contained in:
		
						commit
						4d94e26bb9
					
				| 
						 | 
					@ -8,9 +8,11 @@ use App\Services\JiraService;
 | 
				
			||||||
use Carbon\Carbon;
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Modules\Admin\app\Models\TechnicalUser;
 | 
					use Modules\Admin\app\Models\TechnicalUser;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\ProjectReview;
 | 
				
			||||||
use Modules\Admin\app\Models\UserCriteria;
 | 
					use Modules\Admin\app\Models\UserCriteria;
 | 
				
			||||||
use PhpOffice\PhpWord\IOFactory;
 | 
					use PhpOffice\PhpWord\IOFactory;
 | 
				
			||||||
use PhpOffice\PhpWord\PhpWord;
 | 
					use PhpOffice\PhpWord\PhpWord;
 | 
				
			||||||
 | 
					use PhpOffice\PhpWord\SimpleType\Jc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EvaluationController extends Controller
 | 
					class EvaluationController extends Controller
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -222,6 +224,50 @@ class EvaluationController extends Controller
 | 
				
			||||||
                'spaceAfter' => 600,
 | 
					                'spaceAfter' => 600,
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // **ProjectReview Section**
 | 
				
			||||||
 | 
					        // Fetch Project Reviews
 | 
				
			||||||
 | 
					        $projectReviews = ProjectReview::where('user_id', $user->id);
 | 
				
			||||||
 | 
					        if ($startDate && $endDate) {
 | 
				
			||||||
 | 
					            $projectReviews->whereBetween('updated_at', [$startDate, $endDate . ' 23:59:59']);
 | 
				
			||||||
 | 
					        } elseif ($startDate) {
 | 
				
			||||||
 | 
					            $projectReviews->where('updated_at', '>=', $startDate);
 | 
				
			||||||
 | 
					        } elseif ($endDate) {
 | 
				
			||||||
 | 
					            $projectReviews->where('updated_at', '<=', $endDate . ' 23:59:59');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($projectReviews->get()->count() > 0) {
 | 
				
			||||||
 | 
					            $section->addText("Project Reviews", ['bold' => true, 'size' => 14, 'color' => '000080'], ['alignment' => Jc::CENTER]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table = $section->addTable([
 | 
				
			||||||
 | 
					                'borderColor' => '000000',
 | 
				
			||||||
 | 
					                'borderSize' => 6,
 | 
				
			||||||
 | 
					                'cellMargin' => 80,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Table Header
 | 
				
			||||||
 | 
					            $table->addRow();
 | 
				
			||||||
 | 
					            $table->addCell(3500)->addText('Project Name', ['bold' => true]);
 | 
				
			||||||
 | 
					            $table->addCell(2500)->addText('Role', ['bold' => true]);
 | 
				
			||||||
 | 
					            $table->addCell(5000)->addText('Note', ['bold' => true]);
 | 
				
			||||||
 | 
					            $table->addCell(2500)->addText('Created At', ['bold' => true]);
 | 
				
			||||||
 | 
					            $table->addCell(2500)->addText('Updated At', ['bold' => true]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($projectReviews->get() as $review) {
 | 
				
			||||||
 | 
					                $table->addRow();
 | 
				
			||||||
 | 
					                $table->addCell(3500)->addText($review->name);
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText($review->role);
 | 
				
			||||||
 | 
					                $table->addCell(5000)->addText($review->note);
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText(Carbon::parse($review->created_at)->format('d/m/Y H:i:s'));
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText(Carbon::parse($review->updated_at)->format('d/m/Y H:i:s'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $section->addText(' ', [], [
 | 
				
			||||||
 | 
					                'spaceAfter' => 600,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if ($technicalData)
 | 
					        if ($technicalData)
 | 
				
			||||||
            $section->addPageBreak();
 | 
					            $section->addPageBreak();
 | 
				
			||||||
        //Technical
 | 
					        //Technical
 | 
				
			||||||
| 
						 | 
					@ -260,4 +306,156 @@ class EvaluationController extends Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return response()->download($tempFile, "$user->name.docx")->deleteFileAfterSend(true);
 | 
					        return response()->download($tempFile, "$user->name.docx")->deleteFileAfterSend(true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function reportAllUsers(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $request->validate([
 | 
				
			||||||
 | 
					            'fromDate' => 'nullable|date',
 | 
				
			||||||
 | 
					            'toDate' => 'nullable|date',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $startDate = $request->input('fromDate');
 | 
				
			||||||
 | 
					        $endDate = $request->input('toDate');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $users = User::all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $phpWord = new PhpWord();
 | 
				
			||||||
 | 
					        $phpWord->setDefaultFontName('Times New Roman');
 | 
				
			||||||
 | 
					        $phpWord->setDefaultFontSize(12);
 | 
				
			||||||
 | 
					        $section = $phpWord->addSection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($users as $index => $user) {
 | 
				
			||||||
 | 
					            $userEmail = $user->email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add user heading
 | 
				
			||||||
 | 
					            $section->addText("Staff Evaluation", ['bold' => true, 'size' => 20, 'color' => '000000'], ['align' => 'center']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($startDate) {
 | 
				
			||||||
 | 
					                $fromDate = Carbon::parse($startDate)->format('d-m-Y');
 | 
				
			||||||
 | 
					                $section->addText("From: " . $fromDate, ['size' => 12], ['align' => 'end']);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($endDate) {
 | 
				
			||||||
 | 
					                $toDate = Carbon::parse($endDate)->format('d-m-Y');
 | 
				
			||||||
 | 
					                $section->addText("To: " . $toDate, ['size' => 12], ['align' => 'end']);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $section->addText("{$user->name}", ['bold' => true, 'size' => 14, 'color' => '000000'], ['spaceAfter' => 400]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // **Projects Data**
 | 
				
			||||||
 | 
					            $projectsData = self::getProjectReviewByParams($startDate, $endDate, $userEmail);
 | 
				
			||||||
 | 
					            if (!empty($projectsData)) {
 | 
				
			||||||
 | 
					                foreach ($projectsData as $project) {
 | 
				
			||||||
 | 
					                    $section->addText("Project: {$project['name']}", ['bold' => true, 'size' => 14, 'color' => '000080']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    foreach ($project['sprints'] as $sprint) {
 | 
				
			||||||
 | 
					                        $section->addText("Sprint: {$sprint['name']}", ['bold' => true, 'italic' => true, 'size' => 12]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        $table = $section->addTable(['borderSize' => 6, 'cellMargin' => 80]);
 | 
				
			||||||
 | 
					                        $table->addRow();
 | 
				
			||||||
 | 
					                        $table->addCell(3000)->addText('Criteria', ['bold' => true]);
 | 
				
			||||||
 | 
					                        $table->addCell(3000)->addText('Note', ['bold' => true]);
 | 
				
			||||||
 | 
					                        $table->addCell(2500)->addText('Created By', ['bold' => true]);
 | 
				
			||||||
 | 
					                        $table->addCell(1500)->addText('Point', ['bold' => true]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        foreach ($sprint['criterias'] as $criteria) {
 | 
				
			||||||
 | 
					                            $table->addRow();
 | 
				
			||||||
 | 
					                            $table->addCell(3000)->addText($criteria['criteria']);
 | 
				
			||||||
 | 
					                            $table->addCell(3000)->addText($criteria['note']);
 | 
				
			||||||
 | 
					                            $table->addCell(2500)->addText($criteria['createdBy']);
 | 
				
			||||||
 | 
					                            $table->addCell(1500)->addText($criteria['point'] > 0 ? $criteria['point'] : '');
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $section->addText(' ', [], [
 | 
				
			||||||
 | 
					                        'spaceAfter' => 600,
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // **ProjectReview Section**
 | 
				
			||||||
 | 
					            // Fetch Project Reviews
 | 
				
			||||||
 | 
					            $projectReviews = ProjectReview::where('user_id', $user->id);
 | 
				
			||||||
 | 
					            if ($startDate && $endDate) {
 | 
				
			||||||
 | 
					                $projectReviews->whereBetween('updated_at', [$startDate, $endDate . ' 23:59:59']);
 | 
				
			||||||
 | 
					            } elseif ($startDate) {
 | 
				
			||||||
 | 
					                $projectReviews->where('updated_at', '>=', $startDate);
 | 
				
			||||||
 | 
					            } elseif ($endDate) {
 | 
				
			||||||
 | 
					                $projectReviews->where('updated_at', '<=', $endDate . ' 23:59:59');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($projectReviews->get()->count() > 0) {
 | 
				
			||||||
 | 
					                $section->addText("Project Reviews", ['bold' => true, 'size' => 14, 'color' => '000080'], ['alignment' => Jc::CENTER]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $table = $section->addTable([
 | 
				
			||||||
 | 
					                    'borderColor' => '000000',
 | 
				
			||||||
 | 
					                    'borderSize' => 6,
 | 
				
			||||||
 | 
					                    'cellMargin' => 80,
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Table Header
 | 
				
			||||||
 | 
					                $table->addRow();
 | 
				
			||||||
 | 
					                $table->addCell(3500)->addText('Project Name', ['bold' => true]);
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText('Role', ['bold' => true]);
 | 
				
			||||||
 | 
					                $table->addCell(5000)->addText('Note', ['bold' => true]);
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText('Created At', ['bold' => true]);
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText('Updated At', ['bold' => true]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach ($projectReviews->get() as $review) {
 | 
				
			||||||
 | 
					                    $table->addRow();
 | 
				
			||||||
 | 
					                    $table->addCell(3500)->addText($review->name);
 | 
				
			||||||
 | 
					                    $table->addCell(2500)->addText($review->role);
 | 
				
			||||||
 | 
					                    $table->addCell(5000)->addText($review->note);
 | 
				
			||||||
 | 
					                    $table->addCell(2500)->addText(Carbon::parse($review->created_at)->format('d/m/Y H:i:s'));
 | 
				
			||||||
 | 
					                    $table->addCell(2500)->addText(Carbon::parse($review->updated_at)->format('d/m/Y H:i:s'));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $section->addText(' ', [], [
 | 
				
			||||||
 | 
					                    'spaceAfter' => 600,
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // **Technical Section**
 | 
				
			||||||
 | 
					            $section->addText("Technicals", ['bold' => true, 'size' => 14, 'color' => '000080'], ['alignment' => Jc::CENTER]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table = $section->addTable([
 | 
				
			||||||
 | 
					                'borderColor' => '000000',
 | 
				
			||||||
 | 
					                'borderSize' => 6,
 | 
				
			||||||
 | 
					                'cellMargin' => 80,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->addRow();
 | 
				
			||||||
 | 
					            $table->addCell(1500)->addText('Level', ['bold' => true]);
 | 
				
			||||||
 | 
					            $table->addCell(3500)->addText('Name', ['bold' => true]);
 | 
				
			||||||
 | 
					            $table->addCell(2500)->addText('Point', ['bold' => true]);
 | 
				
			||||||
 | 
					            $table->addCell(2500)->addText('Last Update', ['bold' => true]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Fetch Technical Data
 | 
				
			||||||
 | 
					            $technicalData = TechnicalController::getDataTechnicalsByUserId($user->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($technicalData as $technical) {
 | 
				
			||||||
 | 
					                $updated_at = $technical['updated_at'] ? Carbon::parse($technical['updated_at'])->format('d/m/Y H:i:s') : null;
 | 
				
			||||||
 | 
					                $table->addRow();
 | 
				
			||||||
 | 
					                $table->addCell(1500)->addText($technical['level']);
 | 
				
			||||||
 | 
					                $table->addCell(3500)->addText($technical['name']);
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText($technical['point']);
 | 
				
			||||||
 | 
					                $table->addCell(2500)->addText($updated_at);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add page break between users (except last one)
 | 
				
			||||||
 | 
					            if ($index < count($users) - 1) {
 | 
				
			||||||
 | 
					                $section->addPageBreak();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Save & Download Word File
 | 
				
			||||||
 | 
					        $tempFile = tempnam(sys_get_temp_dir(), 'word');
 | 
				
			||||||
 | 
					        $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
 | 
				
			||||||
 | 
					        $objWriter->save($tempFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->download($tempFile, "All_Users_Report.docx")->deleteFileAfterSend(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,91 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Modules\Admin\app\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Http\Controllers\AbstractController;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\ProjectReview;
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use DateTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProjectReviewController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Display a listing of the resource.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function getListReviews(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $request->validate([
 | 
				
			||||||
 | 
					            'userID' => 'required|exists:users,id',
 | 
				
			||||||
 | 
					            'fromDate' => 'nullable|date',
 | 
				
			||||||
 | 
					            'toDate' => 'nullable|date',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        $userID = $request->input('userID');
 | 
				
			||||||
 | 
					        $startDate = $request->input('fromDate');
 | 
				
			||||||
 | 
					        $endDate = $request->input('toDate');
 | 
				
			||||||
 | 
					        $projectsData = ProjectReview::where('user_id', $userID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($startDate && $endDate) {
 | 
				
			||||||
 | 
					            $projectsData->whereBetween('updated_at', [$startDate, $endDate . ' 23:59:59']);
 | 
				
			||||||
 | 
					        } elseif ($startDate) {
 | 
				
			||||||
 | 
					            $projectsData->where('updated_at', '>=', $startDate);
 | 
				
			||||||
 | 
					        } elseif ($endDate) {
 | 
				
			||||||
 | 
					            $projectsData->where('updated_at', '<=', $endDate . ' 23:59:59');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return AbstractController::ResultSuccess($projectsData->get());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Store a newly created resource in storage.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function create(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $request->validate([
 | 
				
			||||||
 | 
					            'name' => 'required|string',
 | 
				
			||||||
 | 
					            'role' => 'required|string',
 | 
				
			||||||
 | 
					            'note' => 'required|string',
 | 
				
			||||||
 | 
					            'user_id' => 'required|exists:users,id',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $review = ProjectReview::create($request->all());
 | 
				
			||||||
 | 
					        return response()->json($review, 201);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update the specified resource in storage.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function update(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $request->validate([
 | 
				
			||||||
 | 
					            'name' => 'sometimes|required|string',
 | 
				
			||||||
 | 
					            'role' => 'sometimes|required|string',
 | 
				
			||||||
 | 
					            'note' => 'sometimes|required|string',
 | 
				
			||||||
 | 
					            'user_id' => 'sometimes|required|exists:users,id',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        $id = $request->get('id');
 | 
				
			||||||
 | 
					        $projectReview = ProjectReview::find($id);
 | 
				
			||||||
 | 
					        $payload = $request->all();
 | 
				
			||||||
 | 
					        // if ($request->has('created_at')) {
 | 
				
			||||||
 | 
					        //     $created_at = Carbon::create($request->get('created_at'))->setTimezone(env('TIME_ZONE'));
 | 
				
			||||||
 | 
					        //     $payload['created_at'] = $created_at;
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        if ($projectReview) {
 | 
				
			||||||
 | 
					            $projectReview->update($payload);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return response()->json([
 | 
				
			||||||
 | 
					            'data' => $projectReview,
 | 
				
			||||||
 | 
					            'status' => true
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Remove the specified resource from storage.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function destroy(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $id = $request->get('id');
 | 
				
			||||||
 | 
					        ProjectReview::destroy($id);
 | 
				
			||||||
 | 
					        return response()->json(['message' => 'Deleted successfully', 'status' => true]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Modules\Admin\app\Http\Controllers;
 | 
				
			||||||
 | 
					use App\Traits\AnalyzeData;
 | 
				
			||||||
 | 
					class DataAnalyzer
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use AnalyzeData;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Modules\Admin\app\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProjectReview extends Model
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $fillable = ['name', 'role', 'note', 'user_id'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Relationship: A review belongs to a user
 | 
				
			||||||
 | 
					    public function user()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->belongsTo(User::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@ use Modules\Admin\app\Http\Controllers\TimekeepingController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\TrackingController;
 | 
					use Modules\Admin\app\Http\Controllers\TrackingController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\CriteriasController;
 | 
					use Modules\Admin\app\Http\Controllers\CriteriasController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\EvaluationController;
 | 
					use Modules\Admin\app\Http\Controllers\EvaluationController;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Http\Controllers\ProjectReviewController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\ProfileController;
 | 
					use Modules\Admin\app\Http\Controllers\ProfileController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\TechnicalController;
 | 
					use Modules\Admin\app\Http\Controllers\TechnicalController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\TestCaseForSprintController;
 | 
					use Modules\Admin\app\Http\Controllers\TestCaseForSprintController;
 | 
				
			||||||
| 
						 | 
					@ -192,6 +193,11 @@ Route::middleware('api')
 | 
				
			||||||
                Route::get('/sprint-review', [EvaluationController::class, 'sprintReview'])->middleware('check.permission:admin');
 | 
					                Route::get('/sprint-review', [EvaluationController::class, 'sprintReview'])->middleware('check.permission:admin');
 | 
				
			||||||
                Route::get('/technical', [EvaluationController::class, 'technical'])->middleware('check.permission:admin');
 | 
					                Route::get('/technical', [EvaluationController::class, 'technical'])->middleware('check.permission:admin');
 | 
				
			||||||
                Route::get('/report', [EvaluationController::class, 'report'])->middleware('check.permission:admin');
 | 
					                Route::get('/report', [EvaluationController::class, 'report'])->middleware('check.permission:admin');
 | 
				
			||||||
 | 
					                Route::get('/report-all-users', [EvaluationController::class, 'reportAllUsers'])->middleware('check.permission:admin');
 | 
				
			||||||
 | 
					                Route::get('/project-review', [ProjectReviewController::class, 'getListReviews'])->middleware('check.permission:admin');
 | 
				
			||||||
 | 
					                Route::post('/project-review/create', [ProjectReviewController::class, 'create'])->middleware('check.permission:admin');
 | 
				
			||||||
 | 
					                Route::post('/project-review/update', [ProjectReviewController::class, 'update'])->middleware('check.permission:admin');
 | 
				
			||||||
 | 
					                Route::get('/project-review/delete', [ProjectReviewController::class, 'destroy'])->middleware('check.permission:admin');
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,7 +101,7 @@ return [
 | 
				
			||||||
    |
 | 
					    |
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'ttl' => env('JWT_TTL', 60*24),
 | 
					    'ttl' => env('JWT_TTL', 60*24*365),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
    |--------------------------------------------------------------------------
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::create('project_reviews', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->id();
 | 
				
			||||||
 | 
					            $table->string('name');
 | 
				
			||||||
 | 
					            $table->string('role');
 | 
				
			||||||
 | 
					            $table->longText('note');
 | 
				
			||||||
 | 
					            $table->foreignId('user_id')->constrained('users')->onDelete('cascade'); // Khóa ngoại tới bảng users
 | 
				
			||||||
 | 
					            $table->timestamps();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::dropIfExists('project_reviews');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					dataset
 | 
				
			||||||
 | 
					test
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def organize_files_by_username(folder_path, dest_folder_path):
 | 
				
			||||||
 | 
					    # Lấy danh sách các tệp trong thư mục
 | 
				
			||||||
 | 
					    files = [f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for file in files:
 | 
				
			||||||
 | 
					        # Kiểm tra định dạng tên tệp: <username>_checkin_date.png
 | 
				
			||||||
 | 
					        if "_" in file and file.endswith(".png"):
 | 
				
			||||||
 | 
					            username = file.split("_")[0]  # Lấy phần username từ tên tệp
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Tạo đường dẫn thư mục con
 | 
				
			||||||
 | 
					            subfolder_path = os.path.join(folder_path, username)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Tạo thư mục con nếu chưa tồn tại
 | 
				
			||||||
 | 
					            if not os.path.exists(subfolder_path):
 | 
				
			||||||
 | 
					                os.makedirs(subfolder_path)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Di chuyển tệp vào thư mục con
 | 
				
			||||||
 | 
					            shutil.move(os.path.join(folder_path, file), os.path.join(subfolder_path, file))
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    print("Hoàn thành sắp xếp tệp theo username.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Đường dẫn tới thư mục chứa các tệp
 | 
				
			||||||
 | 
					folder_path = "/home/joseph/screenshot"
 | 
				
			||||||
 | 
					dest_folder_path = "/home/joseph/DetectFace/dataset"
 | 
				
			||||||
 | 
					organize_files_by_username(folder_path, dest_folder_path)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import face_recognition
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import pickle 
 | 
				
			||||||
 | 
					datasetPath = "dataset"
 | 
				
			||||||
 | 
					images = []
 | 
				
			||||||
 | 
					classNames = []
 | 
				
			||||||
 | 
					lisFileTrain = os.listdir(datasetPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for file in lisFileTrain:
 | 
				
			||||||
 | 
					    currentImg = cv2.imread(f"{datasetPath}/{file}")
 | 
				
			||||||
 | 
					    images.append(currentImg)
 | 
				
			||||||
 | 
					    classNames.append(os.path.splitext(file)[0].split('_')[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print(len(images))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def encodeImgs(images, save_path="encodings.pkl"):
 | 
				
			||||||
 | 
					    if os.path.exists(save_path):
 | 
				
			||||||
 | 
					        print(f"Loading encodings from {save_path}...")
 | 
				
			||||||
 | 
					        with open(save_path, "rb") as f:
 | 
				
			||||||
 | 
					            return pickle.load(f)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    encodeList = []
 | 
				
			||||||
 | 
					    for i, img in enumerate(images):
 | 
				
			||||||
 | 
					        print(i+1)
 | 
				
			||||||
 | 
					        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 | 
				
			||||||
 | 
					        encode = face_recognition.face_encodings(img)
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					        if encode:  # Check if encodings list is not empty
 | 
				
			||||||
 | 
					            encodeList.append(encode[0])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            print("No face detected in an image. Skipping...")
 | 
				
			||||||
 | 
					            os.remove(f"{datasetPath}/{lisFileTrain[i]}")
 | 
				
			||||||
 | 
					    # Lưu encodeList vào file
 | 
				
			||||||
 | 
					    print(f"Saving encodings to {save_path}...")
 | 
				
			||||||
 | 
					    with open(save_path, "wb") as f:
 | 
				
			||||||
 | 
					        pickle.dump(encodeList, f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return encodeList
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeListKnow = encodeImgs(images)
 | 
				
			||||||
 | 
					print("Load data success")
 | 
				
			||||||
 | 
					print(len(encodeListKnow))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cap = cv2.VideoCapture(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while True:
 | 
				
			||||||
 | 
					    ret, frame = cap.read()
 | 
				
			||||||
 | 
					    frameS = cv2.resize(frame, (0,0), None, fx=1, fy=1)
 | 
				
			||||||
 | 
					    frameS = cv2.cvtColor(frameS, cv2.COLOR_BGR2RGB)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    faceCurFrame = face_recognition.face_locations(frameS)
 | 
				
			||||||
 | 
					    encodeCurFrame = face_recognition.face_encodings(frameS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for encodeFace, faceLoc in zip(encodeCurFrame, faceCurFrame):
 | 
				
			||||||
 | 
					        matches = face_recognition.compare_faces(encodeListKnow, encodeFace)
 | 
				
			||||||
 | 
					        faceDis = face_recognition.face_distance(encodeListKnow, encodeFace)
 | 
				
			||||||
 | 
					        print(faceDis)
 | 
				
			||||||
 | 
					        matchIndex = np.argmin(faceDis)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if faceDis[matchIndex] < 0.3:
 | 
				
			||||||
 | 
					            name = classNames[matchIndex].upper()
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            name = "Unknow"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        y1, x2, y2, x1 = faceLoc
 | 
				
			||||||
 | 
					        y1, x2, y2, x1 = y1, x2, y2, x1
 | 
				
			||||||
 | 
					        cv2.rectangle(frame, (x1,y1), (x2,y2), (0,255,0), 2)
 | 
				
			||||||
 | 
					        cv2.putText(frame, name + f"({(1 - round(faceDis[matchIndex], 2))*100}%)", (x2, y2), cv2.FONT_HERSHEY_COMPLEX, 1, (255,255,255), 2)
 | 
				
			||||||
 | 
					    cv2.imshow('Face decting', frame)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if cv2.waitKey(1) == ord("q"):
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cap.release()
 | 
				
			||||||
 | 
					cv2.destroyAllWindows()
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 390 KiB  | 
| 
						 | 
					@ -0,0 +1,425 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{ # this ensures the entire script is downloaded #
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_has() {
 | 
				
			||||||
 | 
					  type "$1" > /dev/null 2>&1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_default_install_dir() {
 | 
				
			||||||
 | 
					  [ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_install_dir() {
 | 
				
			||||||
 | 
					  if [ -n "$NVM_DIR" ]; then
 | 
				
			||||||
 | 
					    printf %s "${NVM_DIR}"
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    nvm_default_install_dir
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_latest_version() {
 | 
				
			||||||
 | 
					  echo "v0.35.0"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_profile_is_bash_or_zsh() {
 | 
				
			||||||
 | 
					  local TEST_PROFILE
 | 
				
			||||||
 | 
					  TEST_PROFILE="${1-}"
 | 
				
			||||||
 | 
					  case "${TEST_PROFILE-}" in
 | 
				
			||||||
 | 
					    *"/.bashrc" | *"/.bash_profile" | *"/.zshrc")
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    ;;
 | 
				
			||||||
 | 
					    *)
 | 
				
			||||||
 | 
					      return 1
 | 
				
			||||||
 | 
					    ;;
 | 
				
			||||||
 | 
					  esac
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Outputs the location to NVM depending on:
 | 
				
			||||||
 | 
					# * The availability of $NVM_SOURCE
 | 
				
			||||||
 | 
					# * The method used ("script" or "git" in the script, defaults to "git")
 | 
				
			||||||
 | 
					# NVM_SOURCE always takes precedence unless the method is "script-nvm-exec"
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					nvm_source() {
 | 
				
			||||||
 | 
					  local NVM_METHOD
 | 
				
			||||||
 | 
					  NVM_METHOD="$1"
 | 
				
			||||||
 | 
					  local NVM_SOURCE_URL
 | 
				
			||||||
 | 
					  NVM_SOURCE_URL="$NVM_SOURCE"
 | 
				
			||||||
 | 
					  if [ "_$NVM_METHOD" = "_script-nvm-exec" ]; then
 | 
				
			||||||
 | 
					    NVM_SOURCE_URL="https://raw.githubusercontent.com/nvm-sh/nvm/$(nvm_latest_version)/nvm-exec"
 | 
				
			||||||
 | 
					  elif [ "_$NVM_METHOD" = "_script-nvm-bash-completion" ]; then
 | 
				
			||||||
 | 
					    NVM_SOURCE_URL="https://raw.githubusercontent.com/nvm-sh/nvm/$(nvm_latest_version)/bash_completion"
 | 
				
			||||||
 | 
					  elif [ -z "$NVM_SOURCE_URL" ]; then
 | 
				
			||||||
 | 
					    if [ "_$NVM_METHOD" = "_script" ]; then
 | 
				
			||||||
 | 
					      NVM_SOURCE_URL="https://raw.githubusercontent.com/nvm-sh/nvm/$(nvm_latest_version)/nvm.sh"
 | 
				
			||||||
 | 
					    elif [ "_$NVM_METHOD" = "_git" ] || [ -z "$NVM_METHOD" ]; then
 | 
				
			||||||
 | 
					      NVM_SOURCE_URL="https://github.com/nvm-sh/nvm.git"
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      echo >&2 "Unexpected value \"$NVM_METHOD\" for \$NVM_METHOD"
 | 
				
			||||||
 | 
					      return 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  echo "$NVM_SOURCE_URL"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Node.js version to install
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					nvm_node_version() {
 | 
				
			||||||
 | 
					  echo "$NODE_VERSION"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_download() {
 | 
				
			||||||
 | 
					  if nvm_has "curl"; then
 | 
				
			||||||
 | 
					    curl --compressed -q "$@"
 | 
				
			||||||
 | 
					  elif nvm_has "wget"; then
 | 
				
			||||||
 | 
					    # Emulate curl with wget
 | 
				
			||||||
 | 
					    ARGS=$(echo "$*" | command sed -e 's/--progress-bar /--progress=bar /' \
 | 
				
			||||||
 | 
					                            -e 's/-L //' \
 | 
				
			||||||
 | 
					                            -e 's/--compressed //' \
 | 
				
			||||||
 | 
					                            -e 's/-I /--server-response /' \
 | 
				
			||||||
 | 
					                            -e 's/-s /-q /' \
 | 
				
			||||||
 | 
					                            -e 's/-o /-O /' \
 | 
				
			||||||
 | 
					                            -e 's/-C - /-c /')
 | 
				
			||||||
 | 
					    # shellcheck disable=SC2086
 | 
				
			||||||
 | 
					    eval wget $ARGS
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					install_nvm_from_git() {
 | 
				
			||||||
 | 
					  local INSTALL_DIR
 | 
				
			||||||
 | 
					  INSTALL_DIR="$(nvm_install_dir)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ -d "$INSTALL_DIR/.git" ]; then
 | 
				
			||||||
 | 
					    echo "=> nvm is already installed in $INSTALL_DIR, trying to update using git"
 | 
				
			||||||
 | 
					    command printf '\r=> '
 | 
				
			||||||
 | 
					    command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" fetch origin tag "$(nvm_latest_version)" --depth=1 2> /dev/null || {
 | 
				
			||||||
 | 
					      echo >&2 "Failed to update nvm, run 'git fetch' in $INSTALL_DIR yourself."
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    # Cloning to $INSTALL_DIR
 | 
				
			||||||
 | 
					    echo "=> Downloading nvm from git to '$INSTALL_DIR'"
 | 
				
			||||||
 | 
					    command printf '\r=> '
 | 
				
			||||||
 | 
					    mkdir -p "${INSTALL_DIR}"
 | 
				
			||||||
 | 
					    if [ "$(ls -A "${INSTALL_DIR}")" ]; then
 | 
				
			||||||
 | 
					      command git init "${INSTALL_DIR}" || {
 | 
				
			||||||
 | 
					        echo >&2 'Failed to initialize nvm repo. Please report this!'
 | 
				
			||||||
 | 
					        exit 2
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      command git --git-dir="${INSTALL_DIR}/.git" remote add origin "$(nvm_source)" 2> /dev/null \
 | 
				
			||||||
 | 
					        || command git --git-dir="${INSTALL_DIR}/.git" remote set-url origin "$(nvm_source)" || {
 | 
				
			||||||
 | 
					        echo >&2 'Failed to add remote "origin" (or set the URL). Please report this!'
 | 
				
			||||||
 | 
					        exit 2
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      command git --git-dir="${INSTALL_DIR}/.git" fetch origin tag "$(nvm_latest_version)" --depth=1 || {
 | 
				
			||||||
 | 
					        echo >&2 'Failed to fetch origin with tags. Please report this!'
 | 
				
			||||||
 | 
					        exit 2
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      command git -c advice.detachedHead=false clone "$(nvm_source)" -b "$(nvm_latest_version)" --depth=1 "${INSTALL_DIR}" || {
 | 
				
			||||||
 | 
					        echo >&2 'Failed to clone nvm repo. Please report this!'
 | 
				
			||||||
 | 
					        exit 2
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  command git -c advice.detachedHead=false --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" checkout -f --quiet "$(nvm_latest_version)"
 | 
				
			||||||
 | 
					  if [ -n "$(command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" show-ref refs/heads/master)" ]; then
 | 
				
			||||||
 | 
					    if command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch --quiet 2>/dev/null; then
 | 
				
			||||||
 | 
					      command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch --quiet -D master >/dev/null 2>&1
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      echo >&2 "Your version of git is out of date. Please update it!"
 | 
				
			||||||
 | 
					      command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch -D master >/dev/null 2>&1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  echo "=> Compressing and cleaning up git repository"
 | 
				
			||||||
 | 
					  if ! command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" reflog expire --expire=now --all; then
 | 
				
			||||||
 | 
					    echo >&2 "Your version of git is out of date. Please update it!"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  if ! command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" gc --auto --aggressive --prune=now ; then
 | 
				
			||||||
 | 
					    echo >&2 "Your version of git is out of date. Please update it!"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Automatically install Node.js
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					nvm_install_node() {
 | 
				
			||||||
 | 
					  local NODE_VERSION_LOCAL
 | 
				
			||||||
 | 
					  NODE_VERSION_LOCAL="$(nvm_node_version)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ -z "$NODE_VERSION_LOCAL" ]; then
 | 
				
			||||||
 | 
					    return 0
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  echo "=> Installing Node.js version $NODE_VERSION_LOCAL"
 | 
				
			||||||
 | 
					  nvm install "$NODE_VERSION_LOCAL"
 | 
				
			||||||
 | 
					  local CURRENT_NVM_NODE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  CURRENT_NVM_NODE="$(nvm_version current)"
 | 
				
			||||||
 | 
					  if [ "$(nvm_version "$NODE_VERSION_LOCAL")" == "$CURRENT_NVM_NODE" ]; then
 | 
				
			||||||
 | 
					    echo "=> Node.js version $NODE_VERSION_LOCAL has been successfully installed"
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    echo >&2 "Failed to install Node.js $NODE_VERSION_LOCAL"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					install_nvm_as_script() {
 | 
				
			||||||
 | 
					  local INSTALL_DIR
 | 
				
			||||||
 | 
					  INSTALL_DIR="$(nvm_install_dir)"
 | 
				
			||||||
 | 
					  local NVM_SOURCE_LOCAL
 | 
				
			||||||
 | 
					  NVM_SOURCE_LOCAL="$(nvm_source script)"
 | 
				
			||||||
 | 
					  local NVM_EXEC_SOURCE
 | 
				
			||||||
 | 
					  NVM_EXEC_SOURCE="$(nvm_source script-nvm-exec)"
 | 
				
			||||||
 | 
					  local NVM_BASH_COMPLETION_SOURCE
 | 
				
			||||||
 | 
					  NVM_BASH_COMPLETION_SOURCE="$(nvm_source script-nvm-bash-completion)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Downloading to $INSTALL_DIR
 | 
				
			||||||
 | 
					  mkdir -p "$INSTALL_DIR"
 | 
				
			||||||
 | 
					  if [ -f "$INSTALL_DIR/nvm.sh" ]; then
 | 
				
			||||||
 | 
					    echo "=> nvm is already installed in $INSTALL_DIR, trying to update the script"
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    echo "=> Downloading nvm as script to '$INSTALL_DIR'"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  nvm_download -s "$NVM_SOURCE_LOCAL" -o "$INSTALL_DIR/nvm.sh" || {
 | 
				
			||||||
 | 
					    echo >&2 "Failed to download '$NVM_SOURCE_LOCAL'"
 | 
				
			||||||
 | 
					    return 1
 | 
				
			||||||
 | 
					  } &
 | 
				
			||||||
 | 
					  nvm_download -s "$NVM_EXEC_SOURCE" -o "$INSTALL_DIR/nvm-exec" || {
 | 
				
			||||||
 | 
					    echo >&2 "Failed to download '$NVM_EXEC_SOURCE'"
 | 
				
			||||||
 | 
					    return 2
 | 
				
			||||||
 | 
					  } &
 | 
				
			||||||
 | 
					  nvm_download -s "$NVM_BASH_COMPLETION_SOURCE" -o "$INSTALL_DIR/bash_completion" || {
 | 
				
			||||||
 | 
					    echo >&2 "Failed to download '$NVM_BASH_COMPLETION_SOURCE'"
 | 
				
			||||||
 | 
					    return 2
 | 
				
			||||||
 | 
					  } &
 | 
				
			||||||
 | 
					  for job in $(jobs -p | command sort)
 | 
				
			||||||
 | 
					  do
 | 
				
			||||||
 | 
					    wait "$job" || return $?
 | 
				
			||||||
 | 
					  done
 | 
				
			||||||
 | 
					  chmod a+x "$INSTALL_DIR/nvm-exec" || {
 | 
				
			||||||
 | 
					    echo >&2 "Failed to mark '$INSTALL_DIR/nvm-exec' as executable"
 | 
				
			||||||
 | 
					    return 3
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_try_profile() {
 | 
				
			||||||
 | 
					  if [ -z "${1-}" ] || [ ! -f "${1}" ]; then
 | 
				
			||||||
 | 
					    return 1
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  echo "${1}"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Detect profile file if not specified as environment variable
 | 
				
			||||||
 | 
					# (eg: PROFILE=~/.myprofile)
 | 
				
			||||||
 | 
					# The echo'ed path is guaranteed to be an existing file
 | 
				
			||||||
 | 
					# Otherwise, an empty string is returned
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					nvm_detect_profile() {
 | 
				
			||||||
 | 
					  if [ "${PROFILE-}" = '/dev/null' ]; then
 | 
				
			||||||
 | 
					    # the user has specifically requested NOT to have nvm touch their profile
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ -n "${PROFILE}" ] && [ -f "${PROFILE}" ]; then
 | 
				
			||||||
 | 
					    echo "${PROFILE}"
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local DETECTED_PROFILE
 | 
				
			||||||
 | 
					  DETECTED_PROFILE=''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ -n "${BASH_VERSION-}" ]; then
 | 
				
			||||||
 | 
					    if [ -f "$HOME/.bashrc" ]; then
 | 
				
			||||||
 | 
					      DETECTED_PROFILE="$HOME/.bashrc"
 | 
				
			||||||
 | 
					    elif [ -f "$HOME/.bash_profile" ]; then
 | 
				
			||||||
 | 
					      DETECTED_PROFILE="$HOME/.bash_profile"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					  elif [ -n "${ZSH_VERSION-}" ]; then
 | 
				
			||||||
 | 
					    DETECTED_PROFILE="$HOME/.zshrc"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ -z "$DETECTED_PROFILE" ]; then
 | 
				
			||||||
 | 
					    for EACH_PROFILE in ".profile" ".bashrc" ".bash_profile" ".zshrc"
 | 
				
			||||||
 | 
					    do
 | 
				
			||||||
 | 
					      if DETECTED_PROFILE="$(nvm_try_profile "${HOME}/${EACH_PROFILE}")"; then
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      fi
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ -n "$DETECTED_PROFILE" ]; then
 | 
				
			||||||
 | 
					    echo "$DETECTED_PROFILE"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Check whether the user has any globally-installed npm modules in their system
 | 
				
			||||||
 | 
					# Node, and warn them if so.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					nvm_check_global_modules() {
 | 
				
			||||||
 | 
					  command -v npm >/dev/null 2>&1 || return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local NPM_VERSION
 | 
				
			||||||
 | 
					  NPM_VERSION="$(npm --version)"
 | 
				
			||||||
 | 
					  NPM_VERSION="${NPM_VERSION:--1}"
 | 
				
			||||||
 | 
					  [ "${NPM_VERSION%%[!-0-9]*}" -gt 0 ] || return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local NPM_GLOBAL_MODULES
 | 
				
			||||||
 | 
					  NPM_GLOBAL_MODULES="$(
 | 
				
			||||||
 | 
					    npm list -g --depth=0 |
 | 
				
			||||||
 | 
					    command sed -e '/ npm@/d' -e '/ (empty)$/d'
 | 
				
			||||||
 | 
					  )"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local MODULE_COUNT
 | 
				
			||||||
 | 
					  MODULE_COUNT="$(
 | 
				
			||||||
 | 
					    command printf %s\\n "$NPM_GLOBAL_MODULES" |
 | 
				
			||||||
 | 
					    command sed -ne '1!p' |                     # Remove the first line
 | 
				
			||||||
 | 
					    wc -l | command tr -d ' '                   # Count entries
 | 
				
			||||||
 | 
					  )"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ "${MODULE_COUNT}" != '0' ]; then
 | 
				
			||||||
 | 
					    # shellcheck disable=SC2016
 | 
				
			||||||
 | 
					    echo '=> You currently have modules installed globally with `npm`. These will no'
 | 
				
			||||||
 | 
					    # shellcheck disable=SC2016
 | 
				
			||||||
 | 
					    echo '=> longer be linked to the active version of Node when you install a new node'
 | 
				
			||||||
 | 
					    # shellcheck disable=SC2016
 | 
				
			||||||
 | 
					    echo '=> with `nvm`; and they may (depending on how you construct your `$PATH`)'
 | 
				
			||||||
 | 
					    # shellcheck disable=SC2016
 | 
				
			||||||
 | 
					    echo '=> override the binaries of modules installed with `nvm`:'
 | 
				
			||||||
 | 
					    echo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    command printf %s\\n "$NPM_GLOBAL_MODULES"
 | 
				
			||||||
 | 
					    echo '=> If you wish to uninstall them at a later point (or re-install them under your'
 | 
				
			||||||
 | 
					    # shellcheck disable=SC2016
 | 
				
			||||||
 | 
					    echo '=> `nvm` Nodes), you can remove them from the system Node as follows:'
 | 
				
			||||||
 | 
					    echo
 | 
				
			||||||
 | 
					    echo '     $ nvm use system'
 | 
				
			||||||
 | 
					    echo '     $ npm uninstall -g a_module'
 | 
				
			||||||
 | 
					    echo
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nvm_do_install() {
 | 
				
			||||||
 | 
					  if [ -n "${NVM_DIR-}" ] && ! [ -d "${NVM_DIR}" ]; then
 | 
				
			||||||
 | 
					    if [ -e "${NVM_DIR}" ]; then
 | 
				
			||||||
 | 
					      echo >&2 "File \"${NVM_DIR}\" has the same name as installation directory."
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if [ "${NVM_DIR}" = "$(nvm_default_install_dir)" ]; then
 | 
				
			||||||
 | 
					      mkdir "${NVM_DIR}"
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      echo >&2 "You have \$NVM_DIR set to \"${NVM_DIR}\", but that directory does not exist. Check your profile files and environment."
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  if [ -z "${METHOD}" ]; then
 | 
				
			||||||
 | 
					    # Autodetect install method
 | 
				
			||||||
 | 
					    if nvm_has git; then
 | 
				
			||||||
 | 
					      install_nvm_from_git
 | 
				
			||||||
 | 
					    elif nvm_has nvm_download; then
 | 
				
			||||||
 | 
					      install_nvm_as_script
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      echo >&2 'You need git, curl, or wget to install nvm'
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					  elif [ "${METHOD}" = 'git' ]; then
 | 
				
			||||||
 | 
					    if ! nvm_has git; then
 | 
				
			||||||
 | 
					      echo >&2 "You need git to install nvm"
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    install_nvm_from_git
 | 
				
			||||||
 | 
					  elif [ "${METHOD}" = 'script' ]; then
 | 
				
			||||||
 | 
					    if ! nvm_has nvm_download; then
 | 
				
			||||||
 | 
					      echo >&2 "You need curl or wget to install nvm"
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    install_nvm_as_script
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    echo >&2 "The environment variable \$METHOD is set to \"${METHOD}\", which is not recognized as a valid installation method."
 | 
				
			||||||
 | 
					    exit 1
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  echo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local NVM_PROFILE
 | 
				
			||||||
 | 
					  NVM_PROFILE="$(nvm_detect_profile)"
 | 
				
			||||||
 | 
					  local PROFILE_INSTALL_DIR
 | 
				
			||||||
 | 
					  PROFILE_INSTALL_DIR="$(nvm_install_dir | command sed "s:^$HOME:\$HOME:")"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  SOURCE_STR="\\nexport NVM_DIR=\"${PROFILE_INSTALL_DIR}\"\\n[ -s \"\$NVM_DIR/nvm.sh\" ] && \\. \"\$NVM_DIR/nvm.sh\"  # This loads nvm\\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # shellcheck disable=SC2016
 | 
				
			||||||
 | 
					  COMPLETION_STR='[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion\n'
 | 
				
			||||||
 | 
					  BASH_OR_ZSH=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [ -z "${NVM_PROFILE-}" ] ; then
 | 
				
			||||||
 | 
					    local TRIED_PROFILE
 | 
				
			||||||
 | 
					    if [ -n "${PROFILE}" ]; then
 | 
				
			||||||
 | 
					      TRIED_PROFILE="${NVM_PROFILE} (as defined in \$PROFILE), "
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    echo "=> Profile not found. Tried ${TRIED_PROFILE-}~/.bashrc, ~/.bash_profile, ~/.zshrc, and ~/.profile."
 | 
				
			||||||
 | 
					    echo "=> Create one of them and run this script again"
 | 
				
			||||||
 | 
					    echo "   OR"
 | 
				
			||||||
 | 
					    echo "=> Append the following lines to the correct file yourself:"
 | 
				
			||||||
 | 
					    command printf "${SOURCE_STR}"
 | 
				
			||||||
 | 
					    echo
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    if nvm_profile_is_bash_or_zsh "${NVM_PROFILE-}"; then
 | 
				
			||||||
 | 
					      BASH_OR_ZSH=true
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    if ! command grep -qc '/nvm.sh' "$NVM_PROFILE"; then
 | 
				
			||||||
 | 
					      echo "=> Appending nvm source string to $NVM_PROFILE"
 | 
				
			||||||
 | 
					      command printf "${SOURCE_STR}" >> "$NVM_PROFILE"
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      echo "=> nvm source string already in ${NVM_PROFILE}"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    # shellcheck disable=SC2016
 | 
				
			||||||
 | 
					    if ${BASH_OR_ZSH} && ! command grep -qc '$NVM_DIR/bash_completion' "$NVM_PROFILE"; then
 | 
				
			||||||
 | 
					      echo "=> Appending bash_completion source string to $NVM_PROFILE"
 | 
				
			||||||
 | 
					      command printf "$COMPLETION_STR" >> "$NVM_PROFILE"
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      echo "=> bash_completion source string already in ${NVM_PROFILE}"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  if ${BASH_OR_ZSH} && [ -z "${NVM_PROFILE-}" ] ; then
 | 
				
			||||||
 | 
					    echo "=> Please also append the following lines to the if you are using bash/zsh shell:"
 | 
				
			||||||
 | 
					    command printf "${COMPLETION_STR}"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Source nvm
 | 
				
			||||||
 | 
					  # shellcheck source=/dev/null
 | 
				
			||||||
 | 
					  \. "$(nvm_install_dir)/nvm.sh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nvm_check_global_modules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nvm_install_node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nvm_reset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  echo "=> Close and reopen your terminal to start using nvm or run the following to use it now:"
 | 
				
			||||||
 | 
					  command printf "${SOURCE_STR}"
 | 
				
			||||||
 | 
					  if ${BASH_OR_ZSH} ; then
 | 
				
			||||||
 | 
					    command printf "${COMPLETION_STR}"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Unsets the various functions defined
 | 
				
			||||||
 | 
					# during the execution of the install script
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					nvm_reset() {
 | 
				
			||||||
 | 
					  unset -f nvm_has nvm_install_dir nvm_latest_version nvm_profile_is_bash_or_zsh \
 | 
				
			||||||
 | 
					    nvm_source nvm_node_version nvm_download install_nvm_from_git nvm_install_node \
 | 
				
			||||||
 | 
					    install_nvm_as_script nvm_try_profile nvm_detect_profile nvm_check_global_modules \
 | 
				
			||||||
 | 
					    nvm_do_install nvm_reset nvm_default_install_dir
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ "_$NVM_ENV" = "_testing" ] || nvm_do_install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} # this ensures the entire script is downloaded #
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,8 @@ export const getListMaster = API_URL + 'v1/admin/category/get-list-master'
 | 
				
			||||||
export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
 | 
					export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
 | 
				
			||||||
export const updateNoteLeave =
 | 
					export const updateNoteLeave =
 | 
				
			||||||
  API_URL + 'v1/admin/leave-management/saveNoteLeave'
 | 
					  API_URL + 'v1/admin/leave-management/saveNoteLeave'
 | 
				
			||||||
export const exportLeaveManagement = API_URL + 'v1/admin/leave-management/export'
 | 
					export const exportLeaveManagement =
 | 
				
			||||||
 | 
					  API_URL + 'v1/admin/leave-management/export'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//Tickets
 | 
					//Tickets
 | 
				
			||||||
export const getTickets = API_URL + 'v1/admin/ticket/all'
 | 
					export const getTickets = API_URL + 'v1/admin/ticket/all'
 | 
				
			||||||
| 
						 | 
					@ -79,14 +80,24 @@ export const updateProfilesData =
 | 
				
			||||||
  API_URL + 'v1/admin/criterias/profiles-data/update'
 | 
					  API_URL + 'v1/admin/criterias/profiles-data/update'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const listUserTechnical = API_URL + 'v1/admin/technical/get-tech-of-user'
 | 
					export const listUserTechnical = API_URL + 'v1/admin/technical/get-tech-of-user'
 | 
				
			||||||
export const updateUserTechnical = API_URL + 'v1/admin/technical/technicals-user/update'
 | 
					export const updateUserTechnical =
 | 
				
			||||||
 | 
					  API_URL + 'v1/admin/technical/technicals-user/update'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getAllUser = API_URL + 'v1/admin/technical/get-all-user'
 | 
					export const getAllUser = API_URL + 'v1/admin/technical/get-all-user'
 | 
				
			||||||
export const getAllTechByUserId =
 | 
					export const getAllTechByUserId =
 | 
				
			||||||
  API_URL + 'v1/admin/technical/get-tech-by-user-id'
 | 
					  API_URL + 'v1/admin/technical/get-tech-by-user-id'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const evaluation = API_URL + 'v1/admin/evaluation/report'
 | 
					export const evaluation = API_URL + 'v1/admin/evaluation/report'
 | 
				
			||||||
 | 
					export const evaluationReportAllUsers =
 | 
				
			||||||
 | 
					  API_URL + 'v1/admin/evaluation/report-all-users'
 | 
				
			||||||
export const sprintReview = API_URL + 'v1/admin/evaluation/sprint-review'
 | 
					export const sprintReview = API_URL + 'v1/admin/evaluation/sprint-review'
 | 
				
			||||||
 | 
					export const projectReview = API_URL + 'v1/admin/evaluation/project-review'
 | 
				
			||||||
 | 
					export const projectReviewAdd =
 | 
				
			||||||
 | 
					  API_URL + 'v1/admin/evaluation/project-review/create'
 | 
				
			||||||
 | 
					export const projectReviewUpdate =
 | 
				
			||||||
 | 
					  API_URL + 'v1/admin/evaluation/project-review/update'
 | 
				
			||||||
 | 
					export const projectReviewDelete =
 | 
				
			||||||
 | 
					  API_URL + 'v1/admin/evaluation/project-review/delete'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getAllFilesInProfiles = API_URL + 'v1/admin/profile/all-files'
 | 
					export const getAllFilesInProfiles = API_URL + 'v1/admin/profile/all-files'
 | 
				
			||||||
export const updateProfileFolder = API_URL + 'v1/admin/profile/update-profile'
 | 
					export const updateProfileFolder = API_URL + 'v1/admin/profile/update-profile'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,6 +84,7 @@ export const DataTableAll = ({
 | 
				
			||||||
  checkBox,
 | 
					  checkBox,
 | 
				
			||||||
  size,
 | 
					  size,
 | 
				
			||||||
  infoTotal,
 | 
					  infoTotal,
 | 
				
			||||||
 | 
					  componentRight,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
  data: any[]
 | 
					  data: any[]
 | 
				
			||||||
  columns: Column[]
 | 
					  columns: Column[]
 | 
				
			||||||
| 
						 | 
					@ -92,6 +93,7 @@ export const DataTableAll = ({
 | 
				
			||||||
  checkBox?: boolean
 | 
					  checkBox?: boolean
 | 
				
			||||||
  size: string
 | 
					  size: string
 | 
				
			||||||
  infoTotal?: React.ReactNode // Set the type to ReactNode to allow JSX elements
 | 
					  infoTotal?: React.ReactNode // Set the type to ReactNode to allow JSX elements
 | 
				
			||||||
 | 
					  componentRight?: React.ReactNode
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
  const [Tdata, setTData] = useState<any[]>(data)
 | 
					  const [Tdata, setTData] = useState<any[]>(data)
 | 
				
			||||||
  // const [tempData, setTempData] = useState<any[]>([])
 | 
					  // const [tempData, setTempData] = useState<any[]>([])
 | 
				
			||||||
| 
						 | 
					@ -325,10 +327,7 @@ export const DataTableAll = ({
 | 
				
			||||||
            </Text>
 | 
					            </Text>
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
        <Box
 | 
					        <Box className={classes.totalBox} display={infoTotal ? 'flex' : 'none'}>
 | 
				
			||||||
          className={classes.totalBox}
 | 
					 | 
				
			||||||
          display={infoTotal ? 'flex' : 'none'}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <Text fz={'sm'} ta={'right'}>
 | 
					          <Text fz={'sm'} ta={'right'}>
 | 
				
			||||||
            {infoTotal}
 | 
					            {infoTotal}
 | 
				
			||||||
          </Text>
 | 
					          </Text>
 | 
				
			||||||
| 
						 | 
					@ -368,6 +367,7 @@ export const DataTableAll = ({
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
 | 
					        {componentRight}
 | 
				
			||||||
      </Box>
 | 
					      </Box>
 | 
				
			||||||
      <Box className={classes.box}>
 | 
					      <Box className={classes.box}>
 | 
				
			||||||
        <Table
 | 
					        <Table
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,8 @@
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  margin-top: 20px;
 | 
					  margin-top: 20px;
 | 
				
			||||||
  gap: 10px;
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					  max-height: 72vh;
 | 
				
			||||||
 | 
					  overflow-y: scroll;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.titleSidebar {
 | 
					.titleSidebar {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,17 +2,43 @@ import {
 | 
				
			||||||
  evaluation,
 | 
					  evaluation,
 | 
				
			||||||
  getAllTechByUserId,
 | 
					  getAllTechByUserId,
 | 
				
			||||||
  getAllUser,
 | 
					  getAllUser,
 | 
				
			||||||
 | 
					  projectReview,
 | 
				
			||||||
  sprintReview,
 | 
					  sprintReview,
 | 
				
			||||||
 | 
					  projectReviewAdd,
 | 
				
			||||||
 | 
					  projectReviewUpdate,
 | 
				
			||||||
 | 
					  projectReviewDelete,
 | 
				
			||||||
 | 
					  evaluationReportAllUsers,
 | 
				
			||||||
} from '@/api/Admin'
 | 
					} from '@/api/Admin'
 | 
				
			||||||
import DataTableAll from '@/components/DataTable/DataTable'
 | 
					import DataTableAll from '@/components/DataTable/DataTable'
 | 
				
			||||||
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
 | 
					import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
 | 
				
			||||||
import { get, getDownloadFile } from '@/rtk/helpers/apiService'
 | 
					import { get, getDownloadFile, post } from '@/rtk/helpers/apiService'
 | 
				
			||||||
import { Box, Button, Loader, Select, Text, Title } from '@mantine/core'
 | 
					import {
 | 
				
			||||||
 | 
					  Box,
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  Dialog,
 | 
				
			||||||
 | 
					  Group,
 | 
				
			||||||
 | 
					  Loader,
 | 
				
			||||||
 | 
					  Modal,
 | 
				
			||||||
 | 
					  Select,
 | 
				
			||||||
 | 
					  Tabs,
 | 
				
			||||||
 | 
					  Text,
 | 
				
			||||||
 | 
					  Textarea,
 | 
				
			||||||
 | 
					  TextInput,
 | 
				
			||||||
 | 
					  Title,
 | 
				
			||||||
 | 
					} from '@mantine/core'
 | 
				
			||||||
import { DateInput } from '@mantine/dates'
 | 
					import { DateInput } from '@mantine/dates'
 | 
				
			||||||
import { notifications } from '@mantine/notifications'
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
import moment from 'moment'
 | 
					import moment from 'moment'
 | 
				
			||||||
import { useEffect, useState } from 'react'
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
import classes from './StaffEvaluation.module.css'
 | 
					import classes from './StaffEvaluation.module.css'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  IconClearAll,
 | 
				
			||||||
 | 
					  IconEdit,
 | 
				
			||||||
 | 
					  IconPresentationAnalytics,
 | 
				
			||||||
 | 
					  IconX,
 | 
				
			||||||
 | 
					} from '@tabler/icons-react'
 | 
				
			||||||
 | 
					import { useForm } from '@mantine/form'
 | 
				
			||||||
 | 
					import { update, Xdelete } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface User {
 | 
					interface User {
 | 
				
			||||||
  id: number
 | 
					  id: number
 | 
				
			||||||
| 
						 | 
					@ -40,17 +66,51 @@ interface DataTechnical {
 | 
				
			||||||
  updated_at: string
 | 
					  updated_at: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface DataProjectReview {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  role: string
 | 
				
			||||||
 | 
					  note: string
 | 
				
			||||||
 | 
					  user_id: number
 | 
				
			||||||
 | 
					  created_at: string
 | 
				
			||||||
 | 
					  updated_at: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StaffEvaluation = () => {
 | 
					const StaffEvaluation = () => {
 | 
				
			||||||
  const [loading, setLoading] = useState(false)
 | 
					  const [loading, setLoading] = useState(false)
 | 
				
			||||||
  const [loadingTechnical, setLoadingTechnical] = useState(false)
 | 
					  const [loadingTechnical, setLoadingTechnical] = useState(false)
 | 
				
			||||||
  const [dataProfile, setDataProfile] = useState<any>([])
 | 
					  const [dataProfile, setDataProfile] = useState<any>([])
 | 
				
			||||||
  const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
 | 
					  const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
 | 
				
			||||||
 | 
					  const [dataProjectReview, setDataProjectReview] = useState<
 | 
				
			||||||
 | 
					    DataProjectReview[]
 | 
				
			||||||
 | 
					  >([])
 | 
				
			||||||
  const [listUsers, setListUsers] = useState<User[]>([])
 | 
					  const [listUsers, setListUsers] = useState<User[]>([])
 | 
				
			||||||
  const [filter, setFilter] = useState<Filter>({
 | 
					  const [filter, setFilter] = useState<Filter>({
 | 
				
			||||||
    userID: '',
 | 
					    userID: '',
 | 
				
			||||||
    fromDate: null,
 | 
					    fromDate: null,
 | 
				
			||||||
    toDate: null,
 | 
					    toDate: null,
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					  const [action, setAction] = useState('')
 | 
				
			||||||
 | 
					  const [item, setItem] = useState({ id: 0 })
 | 
				
			||||||
 | 
					  const [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
 | 
					  const [activeBtn, setActiveBtn] = useState(false)
 | 
				
			||||||
 | 
					  const [loadingExport, setLoadingExport] = useState(false)
 | 
				
			||||||
 | 
					  const [loadingExportAll, setLoadingExportAll] = useState(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const form = useForm({
 | 
				
			||||||
 | 
					    initialValues: {
 | 
				
			||||||
 | 
					      id: 0,
 | 
				
			||||||
 | 
					      name: '',
 | 
				
			||||||
 | 
					      role: '',
 | 
				
			||||||
 | 
					      note: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    validate: {
 | 
				
			||||||
 | 
					      name: (value) =>
 | 
				
			||||||
 | 
					        value.length === 0 ? 'Please enter project name' : null,
 | 
				
			||||||
 | 
					      role: (value) => (value.length === 0 ? 'Please enter role' : null),
 | 
				
			||||||
 | 
					      note: (value) => (value.length === 0 ? 'Please enter note' : null),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getListUser = async () => {
 | 
					  const getListUser = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
| 
						 | 
					@ -92,14 +152,19 @@ const StaffEvaluation = () => {
 | 
				
			||||||
          ? moment(filterSearch.toDate).format('YYYY-MM-DD')
 | 
					          ? moment(filterSearch.toDate).format('YYYY-MM-DD')
 | 
				
			||||||
          : null,
 | 
					          : null,
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      const user = listUsers.find(
 | 
				
			||||||
 | 
					        (el) => el.id.toString() === filterSearch.userID,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      setLoadingExport(true)
 | 
				
			||||||
      const res = await getDownloadFile(evaluation, params)
 | 
					      const res = await getDownloadFile(evaluation, params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (res.status) {
 | 
					      if (res.status) {
 | 
				
			||||||
        const fileURL = window.URL.createObjectURL(new Blob([res.data]))
 | 
					        const fileURL = window.URL.createObjectURL(new Blob([res.data]))
 | 
				
			||||||
        const fileLink = document.createElement('a')
 | 
					        const fileLink = document.createElement('a')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const fileName = `EXPORT_SPRINT_REVIEW_AND_TECHNICAL_EVALUATION_${getFormattedDateTime()}.docx`
 | 
					        const fileName = `STAFF_EVALUATION_${user?.name
 | 
				
			||||||
 | 
					          ?.split(' ')
 | 
				
			||||||
 | 
					          .join('_')}_${getFormattedDateTime()}.docx`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fileLink.href = fileURL
 | 
					        fileLink.href = fileURL
 | 
				
			||||||
        fileLink.setAttribute('download', fileName)
 | 
					        fileLink.setAttribute('download', fileName)
 | 
				
			||||||
| 
						 | 
					@ -108,6 +173,43 @@ const StaffEvaluation = () => {
 | 
				
			||||||
        fileLink.click()
 | 
					        fileLink.click()
 | 
				
			||||||
        fileLink.remove()
 | 
					        fileLink.remove()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      setLoadingExport(false)
 | 
				
			||||||
 | 
					    } catch (error: any) {
 | 
				
			||||||
 | 
					      notifications.show({
 | 
				
			||||||
 | 
					        title: 'Error',
 | 
				
			||||||
 | 
					        message: error.message ?? error,
 | 
				
			||||||
 | 
					        color: 'red',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return []
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const downloadFileAll = async (filterSearch: Filter) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const params = {
 | 
				
			||||||
 | 
					        fromDate: filterSearch.fromDate
 | 
				
			||||||
 | 
					          ? moment(filterSearch.fromDate).format('YYYY-MM-DD')
 | 
				
			||||||
 | 
					          : null,
 | 
				
			||||||
 | 
					        toDate: filterSearch.toDate
 | 
				
			||||||
 | 
					          ? moment(filterSearch.toDate).format('YYYY-MM-DD')
 | 
				
			||||||
 | 
					          : null,
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      setLoadingExportAll(true)
 | 
				
			||||||
 | 
					      const res = await getDownloadFile(evaluationReportAllUsers, params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (res.status) {
 | 
				
			||||||
 | 
					        const fileURL = window.URL.createObjectURL(new Blob([res.data]))
 | 
				
			||||||
 | 
					        const fileLink = document.createElement('a')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const fileName = `STAFF_EVALUATION_All_USERS_${getFormattedDateTime()}.docx`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fileLink.href = fileURL
 | 
				
			||||||
 | 
					        fileLink.setAttribute('download', fileName)
 | 
				
			||||||
 | 
					        document.body.appendChild(fileLink)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fileLink.click()
 | 
				
			||||||
 | 
					        fileLink.remove()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      setLoadingExportAll(false)
 | 
				
			||||||
    } catch (error: any) {
 | 
					    } catch (error: any) {
 | 
				
			||||||
      notifications.show({
 | 
					      notifications.show({
 | 
				
			||||||
        title: 'Error',
 | 
					        title: 'Error',
 | 
				
			||||||
| 
						 | 
					@ -168,12 +270,39 @@ const StaffEvaluation = () => {
 | 
				
			||||||
    return []
 | 
					    return []
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getListProjectReview = async (filterSearch: Filter) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const params = {
 | 
				
			||||||
 | 
					        userID: filterSearch.userID ?? '',
 | 
				
			||||||
 | 
					        fromDate: filterSearch.fromDate
 | 
				
			||||||
 | 
					          ? moment(filterSearch.fromDate).format('YYYY-MM-DD')
 | 
				
			||||||
 | 
					          : null,
 | 
				
			||||||
 | 
					        toDate: filterSearch.toDate
 | 
				
			||||||
 | 
					          ? moment(filterSearch.toDate).format('YYYY-MM-DD')
 | 
				
			||||||
 | 
					          : null,
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      const res = await get(projectReview, params)
 | 
				
			||||||
 | 
					      if (res.status) {
 | 
				
			||||||
 | 
					        return res.data
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error: any) {
 | 
				
			||||||
 | 
					      notifications.show({
 | 
				
			||||||
 | 
					        title: 'Error',
 | 
				
			||||||
 | 
					        message: error.message ?? error,
 | 
				
			||||||
 | 
					        color: 'red',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return []
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (filter?.userID) {
 | 
					    if (filter?.userID) {
 | 
				
			||||||
      setLoading(true)
 | 
					      setLoading(true)
 | 
				
			||||||
      const fetchData = async () => {
 | 
					      const fetchData = async () => {
 | 
				
			||||||
        const result = await getListProfilesData(filter)
 | 
					        const result = await getListProfilesData(filter)
 | 
				
			||||||
 | 
					        const resultProject = await getListProjectReview(filter)
 | 
				
			||||||
        setDataProfile(result ?? [])
 | 
					        setDataProfile(result ?? [])
 | 
				
			||||||
 | 
					        setDataProjectReview(resultProject ?? [])
 | 
				
			||||||
        setLoading(false)
 | 
					        setLoading(false)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      fetchData()
 | 
					      fetchData()
 | 
				
			||||||
| 
						 | 
					@ -284,6 +413,148 @@ const StaffEvaluation = () => {
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const columnsProjectReview = [
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					    //   name: 'id',
 | 
				
			||||||
 | 
					    //   size: '5%',
 | 
				
			||||||
 | 
					    //   header: 'Num',
 | 
				
			||||||
 | 
					    //   render: (row: any) => {
 | 
				
			||||||
 | 
					    //     return (
 | 
				
			||||||
 | 
					    //       <Box fw={500} ta="center" p={4}>
 | 
				
			||||||
 | 
					    //         {row?.id ? row.id : ''}
 | 
				
			||||||
 | 
					    //       </Box>
 | 
				
			||||||
 | 
					    //     )
 | 
				
			||||||
 | 
					    //   },
 | 
				
			||||||
 | 
					    // },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'name',
 | 
				
			||||||
 | 
					      size: '15%',
 | 
				
			||||||
 | 
					      header: 'Project Name',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'role',
 | 
				
			||||||
 | 
					      size: '15%',
 | 
				
			||||||
 | 
					      header: 'Role',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'note',
 | 
				
			||||||
 | 
					      size: '',
 | 
				
			||||||
 | 
					      header: 'Note',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'created_at',
 | 
				
			||||||
 | 
					      size: '10%',
 | 
				
			||||||
 | 
					      header: 'Created at',
 | 
				
			||||||
 | 
					      render: (row: any) => {
 | 
				
			||||||
 | 
					        if (row?.created_at)
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <div
 | 
				
			||||||
 | 
					              style={{
 | 
				
			||||||
 | 
					                display: 'flex',
 | 
				
			||||||
 | 
					                justifyContent: 'center',
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              {moment(row?.created_at).format('DD/MM/YYYY HH:mm:ss')}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'updated_at',
 | 
				
			||||||
 | 
					      size: '10%',
 | 
				
			||||||
 | 
					      header: 'Last update',
 | 
				
			||||||
 | 
					      render: (row: any) => {
 | 
				
			||||||
 | 
					        if (row?.updated_at)
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <div
 | 
				
			||||||
 | 
					              style={{
 | 
				
			||||||
 | 
					                display: 'flex',
 | 
				
			||||||
 | 
					                justifyContent: 'center',
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              {moment(row?.updated_at).format('DD/MM/YYYY HH:mm:ss')}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: '#',
 | 
				
			||||||
 | 
					      size: '5%',
 | 
				
			||||||
 | 
					      header: 'Action',
 | 
				
			||||||
 | 
					      render: (row: DataProjectReview) => {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					          <Box className={classes.optionIcon}>
 | 
				
			||||||
 | 
					            <IconEdit
 | 
				
			||||||
 | 
					              className={classes.editIcon}
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                setAction('edit')
 | 
				
			||||||
 | 
					                form.setValues(row)
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              width={20}
 | 
				
			||||||
 | 
					              height={20}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <IconX
 | 
				
			||||||
 | 
					              className={classes.deleteIcon}
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                setAction('delete')
 | 
				
			||||||
 | 
					                setItem(row)
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              width={20}
 | 
				
			||||||
 | 
					              height={20}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleCreate = async (values: DataProjectReview) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const { id, ...data } = values
 | 
				
			||||||
 | 
					      const res = await post(projectReviewAdd, {
 | 
				
			||||||
 | 
					        ...data,
 | 
				
			||||||
 | 
					        user_id: filter.userID,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res.id) {
 | 
				
			||||||
 | 
					        setAction('')
 | 
				
			||||||
 | 
					        form.reset()
 | 
				
			||||||
 | 
					        const resultProject = await getListProjectReview(filter)
 | 
				
			||||||
 | 
					        setDataProjectReview(resultProject ?? [])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleUpdate = async (values: DataProjectReview) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await update(projectReviewUpdate, {
 | 
				
			||||||
 | 
					        ...values,
 | 
				
			||||||
 | 
					        user_id: filter.userID,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res) {
 | 
				
			||||||
 | 
					        setAction('')
 | 
				
			||||||
 | 
					        form.reset()
 | 
				
			||||||
 | 
					        const resultProject = await getListProjectReview(filter)
 | 
				
			||||||
 | 
					        setDataProjectReview(resultProject ?? [])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleDelete = async (id: number) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await Xdelete(projectReviewDelete, { id: id }, async () => {
 | 
				
			||||||
 | 
					        const resultProject = await getListProjectReview(filter)
 | 
				
			||||||
 | 
					        setDataProjectReview(resultProject ?? [])
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <div className={classes.title}>
 | 
					      <div className={classes.title}>
 | 
				
			||||||
| 
						 | 
					@ -291,10 +562,38 @@ const StaffEvaluation = () => {
 | 
				
			||||||
          <Text>Admin/</Text>
 | 
					          <Text>Admin/</Text>
 | 
				
			||||||
          Staff Evaluation
 | 
					          Staff Evaluation
 | 
				
			||||||
        </h3>
 | 
					        </h3>
 | 
				
			||||||
 | 
					        <Box
 | 
				
			||||||
 | 
					          w="20%"
 | 
				
			||||||
 | 
					          display={'flex'}
 | 
				
			||||||
 | 
					          style={{ justifyContent: 'flex-end' }}
 | 
				
			||||||
 | 
					          mr={10}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          {loadingExportAll ? (
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              disabled
 | 
				
			||||||
 | 
					              style={{
 | 
				
			||||||
 | 
					                display: 'flex',
 | 
				
			||||||
 | 
					                justifyContent: 'center',
 | 
				
			||||||
 | 
					                width: '135px',
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              onClick={() => {}}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              <Loader size={'sm'} color="green" type="oval" m={'0 auto'} />
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          ) : (
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              // m={5}
 | 
				
			||||||
 | 
					              style={{ display: 'flex', width: '135px' }}
 | 
				
			||||||
 | 
					              onClick={() => downloadFileAll(filter)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Export all user
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Box w="100%" display={'flex'} mt={15} ml={10}>
 | 
					      <Box w="100%" display={'flex'} mt={15} ml={10}>
 | 
				
			||||||
        <Box w="50%" display={'flex'}>
 | 
					        <Box w="80%" display={'flex'} style={{ alignItems: 'center' }}>
 | 
				
			||||||
          <Text
 | 
					          <Text
 | 
				
			||||||
            mr={'xs'}
 | 
					            mr={'xs'}
 | 
				
			||||||
            style={{ alignContent: 'center' }}
 | 
					            style={{ alignContent: 'center' }}
 | 
				
			||||||
| 
						 | 
					@ -304,7 +603,7 @@ const StaffEvaluation = () => {
 | 
				
			||||||
            User:
 | 
					            User:
 | 
				
			||||||
          </Text>
 | 
					          </Text>
 | 
				
			||||||
          <Select
 | 
					          <Select
 | 
				
			||||||
            style={{ width: '30%' }}
 | 
					            style={{ width: '20%' }}
 | 
				
			||||||
            label={''}
 | 
					            label={''}
 | 
				
			||||||
            placeholder="Select user"
 | 
					            placeholder="Select user"
 | 
				
			||||||
            maxLength={255}
 | 
					            maxLength={255}
 | 
				
			||||||
| 
						 | 
					@ -317,131 +616,340 @@ const StaffEvaluation = () => {
 | 
				
			||||||
            value={filter.userID}
 | 
					            value={filter.userID}
 | 
				
			||||||
            onChange={(e) => setFilter({ ...filter, userID: e! })}
 | 
					            onChange={(e) => setFilter({ ...filter, userID: e! })}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 | 
					          <Box
 | 
				
			||||||
 | 
					            display={'flex'}
 | 
				
			||||||
 | 
					            mr={10}
 | 
				
			||||||
 | 
					            ms={10}
 | 
				
			||||||
 | 
					            style={{ alignItems: 'center' }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <Text
 | 
				
			||||||
 | 
					              mr={'xs'}
 | 
				
			||||||
 | 
					              style={{ alignContent: 'center' }}
 | 
				
			||||||
 | 
					              fw={600}
 | 
				
			||||||
 | 
					              size={'md'}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              From Date:
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <DateInput
 | 
				
			||||||
 | 
					              placeholder="Select date"
 | 
				
			||||||
 | 
					              clearable
 | 
				
			||||||
 | 
					              size="xs"
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
 | 
					              label={''}
 | 
				
			||||||
 | 
					              value={filter.fromDate ? new Date(filter.fromDate) : null}
 | 
				
			||||||
 | 
					              valueFormat="DD/MM/YYYY"
 | 
				
			||||||
 | 
					              onChange={(e) => setFilter({ ...filter, fromDate: e! })}
 | 
				
			||||||
 | 
					            ></DateInput>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <Box display={'flex'} mr={10} style={{ alignItems: 'center' }}>
 | 
				
			||||||
 | 
					            <Text
 | 
				
			||||||
 | 
					              mr={'xs'}
 | 
				
			||||||
 | 
					              style={{ alignContent: 'center' }}
 | 
				
			||||||
 | 
					              fw={600}
 | 
				
			||||||
 | 
					              size={'md'}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              To Date:
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					            <DateInput
 | 
				
			||||||
 | 
					              placeholder="Select date"
 | 
				
			||||||
 | 
					              clearable
 | 
				
			||||||
 | 
					              size="xs"
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
 | 
					              label={''}
 | 
				
			||||||
 | 
					              value={filter.toDate ? new Date(filter.toDate) : null}
 | 
				
			||||||
 | 
					              valueFormat="DD/MM/YYYY"
 | 
				
			||||||
 | 
					              onChange={(e) => setFilter({ ...filter, toDate: e! })}
 | 
				
			||||||
 | 
					            ></DateInput>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
        <Box
 | 
					        <Box
 | 
				
			||||||
          w="50%"
 | 
					          w="20%"
 | 
				
			||||||
          display={'flex'}
 | 
					          display={'flex'}
 | 
				
			||||||
          style={{ justifyContent: 'flex-end' }}
 | 
					          style={{ justifyContent: 'flex-end' }}
 | 
				
			||||||
          mr={10}
 | 
					          mr={10}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <Button
 | 
					          {loadingExport ? (
 | 
				
			||||||
            // m={5}
 | 
					            <Button
 | 
				
			||||||
            style={{ display: filter.userID != '' ? 'flex' : 'none' }}
 | 
					              disabled
 | 
				
			||||||
            onClick={() => downloadFile(filter)}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Export
 | 
					 | 
				
			||||||
          </Button>
 | 
					 | 
				
			||||||
        </Box>
 | 
					 | 
				
			||||||
      </Box>
 | 
					 | 
				
			||||||
      <Box className={classes.userInfoSection} display="flex">
 | 
					 | 
				
			||||||
        <Box className={classes.projectInvolvement}>
 | 
					 | 
				
			||||||
          <Box
 | 
					 | 
				
			||||||
            w="100%"
 | 
					 | 
				
			||||||
            display={'flex'}
 | 
					 | 
				
			||||||
            mt={15}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <Box display={'flex'} mr={10}>
 | 
					 | 
				
			||||||
              <Text
 | 
					 | 
				
			||||||
                mr={'xs'}
 | 
					 | 
				
			||||||
                style={{ alignContent: 'center' }}
 | 
					 | 
				
			||||||
                fw={600}
 | 
					 | 
				
			||||||
                size={'md'}
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                From Date:
 | 
					 | 
				
			||||||
              </Text>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              <DateInput
 | 
					 | 
				
			||||||
                placeholder="Select date"
 | 
					 | 
				
			||||||
                clearable
 | 
					 | 
				
			||||||
                size='xs'
 | 
					 | 
				
			||||||
                required
 | 
					 | 
				
			||||||
                label={''}
 | 
					 | 
				
			||||||
                value={filter.fromDate ? new Date(filter.fromDate) : null}
 | 
					 | 
				
			||||||
                valueFormat="DD/MM/YYYY"
 | 
					 | 
				
			||||||
                onChange={(e) => setFilter({ ...filter, fromDate: e! })}
 | 
					 | 
				
			||||||
              ></DateInput>
 | 
					 | 
				
			||||||
            </Box>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <Box display={'flex'} mr={10}>
 | 
					 | 
				
			||||||
              <Text
 | 
					 | 
				
			||||||
                mr={'xs'}
 | 
					 | 
				
			||||||
                style={{ alignContent: 'center' }}
 | 
					 | 
				
			||||||
                fw={600}
 | 
					 | 
				
			||||||
                size={'md'}
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                To Date:
 | 
					 | 
				
			||||||
              </Text>
 | 
					 | 
				
			||||||
              <DateInput
 | 
					 | 
				
			||||||
                placeholder="Select date"
 | 
					 | 
				
			||||||
                clearable
 | 
					 | 
				
			||||||
                size='xs'
 | 
					 | 
				
			||||||
                required
 | 
					 | 
				
			||||||
                label={''}
 | 
					 | 
				
			||||||
                value={filter.toDate ? new Date(filter.toDate) : null}
 | 
					 | 
				
			||||||
                valueFormat="DD/MM/YYYY"
 | 
					 | 
				
			||||||
                onChange={(e) => setFilter({ ...filter, toDate: e! })}
 | 
					 | 
				
			||||||
              ></DateInput>
 | 
					 | 
				
			||||||
            </Box>
 | 
					 | 
				
			||||||
          </Box>
 | 
					 | 
				
			||||||
          <Box
 | 
					 | 
				
			||||||
            style={{
 | 
					 | 
				
			||||||
              marginTop: '10%',
 | 
					 | 
				
			||||||
              textAlign: 'center',
 | 
					 | 
				
			||||||
              display: loading ? 'block' : 'none',
 | 
					 | 
				
			||||||
              // display: 'none',
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
 | 
					 | 
				
			||||||
            <Text fw={600} c={'gray'}>
 | 
					 | 
				
			||||||
              Loading . . .
 | 
					 | 
				
			||||||
            </Text>
 | 
					 | 
				
			||||||
          </Box>
 | 
					 | 
				
			||||||
          {!loading && dataProfile.length == 0 && (
 | 
					 | 
				
			||||||
            <Box
 | 
					 | 
				
			||||||
              style={{
 | 
					              style={{
 | 
				
			||||||
                marginTop: '10%',
 | 
					                display: 'flex',
 | 
				
			||||||
                textAlign: 'center',
 | 
					                justifyContent: 'center',
 | 
				
			||||||
                display: 'block',
 | 
					                width: '80px',
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
 | 
					              onClick={() => {}}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              <Text fw={600} c={'gray'}>
 | 
					              <Loader size={'sm'} color="green" type="oval" m={'0 auto'} />
 | 
				
			||||||
                No Data Sprint
 | 
					            </Button>
 | 
				
			||||||
              </Text>
 | 
					 | 
				
			||||||
            </Box>
 | 
					 | 
				
			||||||
          )}
 | 
					 | 
				
			||||||
          {!loading && (
 | 
					 | 
				
			||||||
            <ProjectInvolvement dataProfile={dataProfile} page="admin" />
 | 
					 | 
				
			||||||
          )}
 | 
					 | 
				
			||||||
        </Box>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <Box className={classes.sidebar}>
 | 
					 | 
				
			||||||
          <Title order={3} className={classes.titleSidebar}>
 | 
					 | 
				
			||||||
            Technicals
 | 
					 | 
				
			||||||
          </Title>
 | 
					 | 
				
			||||||
          {loadingTechnical ? (
 | 
					 | 
				
			||||||
            <Box
 | 
					 | 
				
			||||||
              style={{
 | 
					 | 
				
			||||||
                marginTop: '10%',
 | 
					 | 
				
			||||||
                textAlign: 'center',
 | 
					 | 
				
			||||||
                display: 'block',
 | 
					 | 
				
			||||||
              }}
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
 | 
					 | 
				
			||||||
              <Text fw={600} c={'gray'}>
 | 
					 | 
				
			||||||
                Loading . . .
 | 
					 | 
				
			||||||
              </Text>
 | 
					 | 
				
			||||||
            </Box>
 | 
					 | 
				
			||||||
          ) : (
 | 
					          ) : (
 | 
				
			||||||
            <DataTableAll
 | 
					            <Button
 | 
				
			||||||
              data={dataTechnical}
 | 
					              // m={5}
 | 
				
			||||||
              columns={columns}
 | 
					              style={{
 | 
				
			||||||
              size=""
 | 
					                display: filter.userID != '' ? 'flex' : 'none',
 | 
				
			||||||
              searchInput
 | 
					                width: '80px',
 | 
				
			||||||
              infoTotal={infoTotal()}
 | 
					              }}
 | 
				
			||||||
            />
 | 
					              onClick={() => downloadFile(filter)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Export
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
      </Box>
 | 
					      </Box>
 | 
				
			||||||
 | 
					      <Tabs mt={8} variant="outline" defaultValue="general">
 | 
				
			||||||
 | 
					        <Tabs.List>
 | 
				
			||||||
 | 
					          <Tabs.Tab
 | 
				
			||||||
 | 
					            value="general"
 | 
				
			||||||
 | 
					            leftSection={<IconClearAll size={16} color="teal" />}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <span style={{ fontSize: '16px' }}>General</span>
 | 
				
			||||||
 | 
					          </Tabs.Tab>
 | 
				
			||||||
 | 
					          <Tabs.Tab
 | 
				
			||||||
 | 
					            value="project_review"
 | 
				
			||||||
 | 
					            leftSection={<IconPresentationAnalytics size={16} color="blue" />}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <span style={{ fontSize: '16px' }}>Project review</span>
 | 
				
			||||||
 | 
					          </Tabs.Tab>
 | 
				
			||||||
 | 
					        </Tabs.List>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Tabs.Panel value="general">
 | 
				
			||||||
 | 
					          <Box className={classes.userInfoSection} display="flex">
 | 
				
			||||||
 | 
					            <Box className={classes.projectInvolvement}>
 | 
				
			||||||
 | 
					              <Box
 | 
				
			||||||
 | 
					                style={{
 | 
				
			||||||
 | 
					                  marginTop: '10%',
 | 
				
			||||||
 | 
					                  textAlign: 'center',
 | 
				
			||||||
 | 
					                  display: loading ? 'block' : 'none',
 | 
				
			||||||
 | 
					                  // display: 'none',
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
 | 
				
			||||||
 | 
					                <Text fw={600} c={'gray'}>
 | 
				
			||||||
 | 
					                  Loading . . .
 | 
				
			||||||
 | 
					                </Text>
 | 
				
			||||||
 | 
					              </Box>
 | 
				
			||||||
 | 
					              {!loading && dataProfile.length == 0 && (
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    marginTop: '10%',
 | 
				
			||||||
 | 
					                    textAlign: 'center',
 | 
				
			||||||
 | 
					                    display: 'block',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text fw={600} c={'gray'}>
 | 
				
			||||||
 | 
					                    No Data Sprint
 | 
				
			||||||
 | 
					                  </Text>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					              {!loading && (
 | 
				
			||||||
 | 
					                <ProjectInvolvement dataProfile={dataProfile} page="admin" />
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <Box className={classes.sidebar}>
 | 
				
			||||||
 | 
					              <Title order={3} className={classes.titleSidebar}>
 | 
				
			||||||
 | 
					                Technicals
 | 
				
			||||||
 | 
					              </Title>
 | 
				
			||||||
 | 
					              {loadingTechnical ? (
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    marginTop: '10%',
 | 
				
			||||||
 | 
					                    textAlign: 'center',
 | 
				
			||||||
 | 
					                    display: 'block',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
 | 
				
			||||||
 | 
					                  <Text fw={600} c={'gray'}>
 | 
				
			||||||
 | 
					                    Loading . . .
 | 
				
			||||||
 | 
					                  </Text>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					              ) : (
 | 
				
			||||||
 | 
					                <DataTableAll
 | 
				
			||||||
 | 
					                  data={dataTechnical}
 | 
				
			||||||
 | 
					                  columns={columns}
 | 
				
			||||||
 | 
					                  size=""
 | 
				
			||||||
 | 
					                  searchInput
 | 
				
			||||||
 | 
					                  infoTotal={infoTotal()}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            </Box>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Tabs.Panel>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Tabs.Panel value="project_review">
 | 
				
			||||||
 | 
					          <Box className={classes.userInfoSection} display="flex">
 | 
				
			||||||
 | 
					            {loading ? (
 | 
				
			||||||
 | 
					              <Box
 | 
				
			||||||
 | 
					                style={{ width: '100%', display: loading ? 'block' : 'none' }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    marginTop: '10%',
 | 
				
			||||||
 | 
					                    textAlign: 'center',
 | 
				
			||||||
 | 
					                    // display: 'none',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
 | 
				
			||||||
 | 
					                  <Text fw={600} c={'gray'}>
 | 
				
			||||||
 | 
					                    Loading . . .
 | 
				
			||||||
 | 
					                  </Text>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					              </Box>
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              <DataTableAll
 | 
				
			||||||
 | 
					                data={dataProjectReview}
 | 
				
			||||||
 | 
					                columns={columnsProjectReview}
 | 
				
			||||||
 | 
					                size=""
 | 
				
			||||||
 | 
					                searchInput
 | 
				
			||||||
 | 
					                // infoTotal={infoTotal()}
 | 
				
			||||||
 | 
					                componentRight={
 | 
				
			||||||
 | 
					                  <Box
 | 
				
			||||||
 | 
					                    w="100%"
 | 
				
			||||||
 | 
					                    display={'flex'}
 | 
				
			||||||
 | 
					                    style={{ justifyContent: 'flex-end' }}
 | 
				
			||||||
 | 
					                    mr={10}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <Button
 | 
				
			||||||
 | 
					                      color="teal"
 | 
				
			||||||
 | 
					                      style={{ display: filter.userID != '' ? 'flex' : 'none' }}
 | 
				
			||||||
 | 
					                      onClick={() => {
 | 
				
			||||||
 | 
					                        setAction('add')
 | 
				
			||||||
 | 
					                        form.reset()
 | 
				
			||||||
 | 
					                      }}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      Add
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                  </Box>
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Tabs.Panel>
 | 
				
			||||||
 | 
					      </Tabs>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {/* Add/Edit User modal */}
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        opened={action === 'add' || action === 'edit'}
 | 
				
			||||||
 | 
					        onClose={() => {
 | 
				
			||||||
 | 
					          setAction('')
 | 
				
			||||||
 | 
					          form.reset()
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        title={
 | 
				
			||||||
 | 
					          <Text pl={'sm'} fw={700} fz={'lg'}>
 | 
				
			||||||
 | 
					            {action === 'add' ? 'Add Review' : 'Update Review'}
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <form
 | 
				
			||||||
 | 
					          onSubmit={form.onSubmit(async (values) => {
 | 
				
			||||||
 | 
					            setDisableBtn(true)
 | 
				
			||||||
 | 
					            action === 'edit'
 | 
				
			||||||
 | 
					              ? await handleUpdate(values)
 | 
				
			||||||
 | 
					              : await handleCreate(values)
 | 
				
			||||||
 | 
					            setDisableBtn(false)
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Box pl={'md'} pr={'md'}>
 | 
				
			||||||
 | 
					            <TextInput
 | 
				
			||||||
 | 
					              placeholder="Input name"
 | 
				
			||||||
 | 
					              label={
 | 
				
			||||||
 | 
					                <span>
 | 
				
			||||||
 | 
					                  Name project: <span style={{ color: 'red' }}>*</span>
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              mb={'md'}
 | 
				
			||||||
 | 
					              value={form.values.name}
 | 
				
			||||||
 | 
					              error={form.errors.name}
 | 
				
			||||||
 | 
					              onChange={(e) => form.setFieldValue('name', e.target.value)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <TextInput
 | 
				
			||||||
 | 
					              placeholder="Input role"
 | 
				
			||||||
 | 
					              label={
 | 
				
			||||||
 | 
					                <span>
 | 
				
			||||||
 | 
					                  Role: <span style={{ color: 'red' }}>*</span>
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              mb={'md'}
 | 
				
			||||||
 | 
					              value={form.values.role}
 | 
				
			||||||
 | 
					              error={form.errors.role}
 | 
				
			||||||
 | 
					              onChange={(e) => form.setFieldValue('role', e.target.value)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <Textarea
 | 
				
			||||||
 | 
					              placeholder="Input notes"
 | 
				
			||||||
 | 
					              rows={4}
 | 
				
			||||||
 | 
					              label={
 | 
				
			||||||
 | 
					                <span>
 | 
				
			||||||
 | 
					                  Note: <span style={{ color: 'red' }}>*</span>
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              mb={'md'}
 | 
				
			||||||
 | 
					              value={form.values.note}
 | 
				
			||||||
 | 
					              error={form.errors.note}
 | 
				
			||||||
 | 
					              onChange={(e) => form.setFieldValue('note', e.target.value)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <Box ta={'center'}>
 | 
				
			||||||
 | 
					              {action === 'add' ? (
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                  mt={'lg'}
 | 
				
			||||||
 | 
					                  bg={'green'}
 | 
				
			||||||
 | 
					                  type="submit"
 | 
				
			||||||
 | 
					                  disabled={disableBtn}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  Create
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              ) : (
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                  mt={'lg'}
 | 
				
			||||||
 | 
					                  bg={'green'}
 | 
				
			||||||
 | 
					                  type="submit"
 | 
				
			||||||
 | 
					                  disabled={disableBtn}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  Save
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            </Box>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Dialog
 | 
				
			||||||
 | 
					        className={classes.dialog}
 | 
				
			||||||
 | 
					        opened={action === 'delete'}
 | 
				
			||||||
 | 
					        withCloseButton
 | 
				
			||||||
 | 
					        onClose={() => setAction('')}
 | 
				
			||||||
 | 
					        size="lg"
 | 
				
			||||||
 | 
					        radius="md"
 | 
				
			||||||
 | 
					        position={{ top: 30, right: 10 }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Text className={classes.dialogText} size="sm" mb="xs" fw={500}>
 | 
				
			||||||
 | 
					          Do you want to delete this review?
 | 
				
			||||||
 | 
					          <Group justify="center" m={10}>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              disabled={activeBtn}
 | 
				
			||||||
 | 
					              fw={700}
 | 
				
			||||||
 | 
					              size="xs"
 | 
				
			||||||
 | 
					              variant="light"
 | 
				
			||||||
 | 
					              onClick={async () => {
 | 
				
			||||||
 | 
					                setActiveBtn(true)
 | 
				
			||||||
 | 
					                await handleDelete(item.id)
 | 
				
			||||||
 | 
					                setActiveBtn(false)
 | 
				
			||||||
 | 
					                setAction('')
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Yes
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              fw={700}
 | 
				
			||||||
 | 
					              size="xs"
 | 
				
			||||||
 | 
					              variant="light"
 | 
				
			||||||
 | 
					              onClick={() => setAction('')}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Cancel
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          </Group>
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					      </Dialog>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -655,9 +655,7 @@ const Timekeeping = () => {
 | 
				
			||||||
              data={Array.from({ length: 12 }, (_, index) => {
 | 
					              data={Array.from({ length: 12 }, (_, index) => {
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
                  value: (1 + index).toString(),
 | 
					                  value: (1 + index).toString(),
 | 
				
			||||||
                  label: (1 + index).toString(),
 | 
					                  label: (1 + index).toString()
 | 
				
			||||||
                  disabled:
 | 
					 | 
				
			||||||
                    1 + index > parseInt(moment(Date.now()).format('MM')),
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              })}
 | 
					              })}
 | 
				
			||||||
              onChange={(e) => {
 | 
					              onChange={(e) => {
 | 
				
			||||||
| 
						 | 
					@ -715,10 +713,11 @@ const Timekeeping = () => {
 | 
				
			||||||
            <Button 
 | 
					            <Button 
 | 
				
			||||||
              onClick={() => setExportModalOpened(true)} 
 | 
					              onClick={() => setExportModalOpened(true)} 
 | 
				
			||||||
              size="xs" 
 | 
					              size="xs" 
 | 
				
			||||||
              ml="xl"
 | 
					              ml="md"
 | 
				
			||||||
 | 
					              bg={'green'}
 | 
				
			||||||
              leftSection={<IconFileExcel size={16} />}
 | 
					              leftSection={<IconFileExcel size={16} />}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              Export Excel
 | 
					              Export
 | 
				
			||||||
            </Button>
 | 
					            </Button>
 | 
				
			||||||
          </Box>
 | 
					          </Box>
 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,7 @@
 | 
				
			||||||
pyttsx3
 | 
					#MacOS
 | 
				
			||||||
openpyxl
 | 
					    python3 -m venv path/to/venv
 | 
				
			||||||
pyzbar
 | 
					    source path/to/venv/bin/activate
 | 
				
			||||||
opencv-python
 | 
					    pip install pyttsx3 openpyxl pyzbar opencv-python qrcode pyautogui requests pillow
 | 
				
			||||||
qrcode
 | 
					    pip install wheel setuptools pip --upgrade 
 | 
				
			||||||
pyautogui
 | 
					    pip install git+https://github.com/ageitgey/face_recognition_models --verbose
 | 
				
			||||||
requests
 | 
					    pip install face_recognition
 | 
				
			||||||
pillow
 | 
					 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,10 @@ import pyautogui
 | 
				
			||||||
from pyzbar import pyzbar
 | 
					from pyzbar import pyzbar
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
import requests
 | 
					import requests
 | 
				
			||||||
 | 
					import face_recognition
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import pickle
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
# Khởi tạo danh sách rỗng để lưu trữ thông tin người dùng
 | 
					# Khởi tạo danh sách rỗng để lưu trữ thông tin người dùng
 | 
				
			||||||
user_data = []
 | 
					user_data = []
 | 
				
			||||||
history = []
 | 
					history = []
 | 
				
			||||||
| 
						 | 
					@ -14,6 +18,7 @@ screen_height = 1100
 | 
				
			||||||
WINDOW_QR_CODE = "QR Code"
 | 
					WINDOW_QR_CODE = "QR Code"
 | 
				
			||||||
WINDOW_TRACKING = "Tracking"
 | 
					WINDOW_TRACKING = "Tracking"
 | 
				
			||||||
WINDOW_HISTORY = "History"
 | 
					WINDOW_HISTORY = "History"
 | 
				
			||||||
 | 
					# URL_API = "http://localhost:8000/api/v1"
 | 
				
			||||||
URL_API = "https://ms.prology.net/api/v1"
 | 
					URL_API = "https://ms.prology.net/api/v1"
 | 
				
			||||||
data = [0]
 | 
					data = [0]
 | 
				
			||||||
# Hàm thông báo bằng giọng nói
 | 
					# Hàm thông báo bằng giọng nói
 | 
				
			||||||
| 
						 | 
					@ -177,10 +182,10 @@ def process_qr_code(frame):
 | 
				
			||||||
            cv.resizeWindow(WINDOW_QR_CODE, screen_width, screen_height)
 | 
					            cv.resizeWindow(WINDOW_QR_CODE, screen_width, screen_height)
 | 
				
			||||||
            cv.imshow(WINDOW_QR_CODE, frame)
 | 
					            cv.imshow(WINDOW_QR_CODE, frame)
 | 
				
			||||||
            cv.moveWindow(WINDOW_QR_CODE, 10, 10)
 | 
					            cv.moveWindow(WINDOW_QR_CODE, 10, 10)
 | 
				
			||||||
            cv.waitKey(5000)  # Chờ 5 giây
 | 
					            cv.waitKey(3000)  # Chờ 5 giây
 | 
				
			||||||
            screenshot_window(file_name)
 | 
					            screenshot_window(file_name)
 | 
				
			||||||
            send_image(id_log, file_name)
 | 
					 | 
				
			||||||
            cv.destroyWindow(WINDOW_QR_CODE)
 | 
					            cv.destroyWindow(WINDOW_QR_CODE)
 | 
				
			||||||
 | 
					            send_image(id_log, file_name)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            display_text(frame, f"QR invalid", (25, 25), 0.7, (6, 6, 255), 2)
 | 
					            display_text(frame, f"QR invalid", (25, 25), 0.7, (6, 6, 255), 2)
 | 
				
			||||||
            display_text(frame, f"Failed", (25, 50), 0.7, (6, 6, 255), 2)
 | 
					            display_text(frame, f"Failed", (25, 50), 0.7, (6, 6, 255), 2)
 | 
				
			||||||
| 
						 | 
					@ -193,8 +198,76 @@ def process_qr_code(frame):
 | 
				
			||||||
            cv.destroyWindow(WINDOW_QR_CODE)
 | 
					            cv.destroyWindow(WINDOW_QR_CODE)
 | 
				
			||||||
    return frame
 | 
					    return frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Hàm để xử lý quá trình quét mã QR code
 | 
				
			||||||
 | 
					def process_face_detect(text, frame):
 | 
				
			||||||
 | 
					    if text.endswith("\n\n"):
 | 
				
			||||||
 | 
					        file_name = ""
 | 
				
			||||||
 | 
					        status = ""
 | 
				
			||||||
 | 
					        id_log = 0
 | 
				
			||||||
 | 
					        if text not in [user["name"] for user in user_data]:
 | 
				
			||||||
 | 
					            print(f"{text} đã check in lúc {datetime.now()}")
 | 
				
			||||||
 | 
					            status += "check in"
 | 
				
			||||||
 | 
					            file_name+=text.split('\n')[0]+"_"+f"{status}_at_{datetime.now().strftime("%Y_%m_%d_%H_%M_%S")}.png"
 | 
				
			||||||
 | 
					            # screenshot_window(file_name)
 | 
				
			||||||
 | 
					            res = check_in(text, frame, text)
 | 
				
			||||||
 | 
					            id_log = res.get('data').get('id')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            print(f"{text} đã check out lúc {datetime.now()}")
 | 
				
			||||||
 | 
					            status += "check out"
 | 
				
			||||||
 | 
					            file_name+=text.split('\n')[0]+"_"+f"{status}_at_{datetime.now().strftime("%Y_%m_%d_%H_%M_%S")}.png"
 | 
				
			||||||
 | 
					            # screenshot_window(file_name)
 | 
				
			||||||
 | 
					            res = check_out(text, frame, text)
 | 
				
			||||||
 | 
					            id_log = res.get('data').get('id')
 | 
				
			||||||
 | 
					        screenshot_window(file_name)
 | 
				
			||||||
 | 
					        cv.namedWindow(WINDOW_QR_CODE, cv.WINDOW_NORMAL)
 | 
				
			||||||
 | 
					        cv.resizeWindow(WINDOW_QR_CODE, screen_width, screen_height)
 | 
				
			||||||
 | 
					        cv.imshow(WINDOW_QR_CODE, frame)
 | 
				
			||||||
 | 
					        cv.moveWindow(WINDOW_QR_CODE, 10, 10)
 | 
				
			||||||
 | 
					        # Chạy vòng lặp không chặn trong 2 giây
 | 
				
			||||||
 | 
					        cv.waitKey(2000)  # Chờ 10ms
 | 
				
			||||||
 | 
					        cv.destroyWindow(WINDOW_QR_CODE)
 | 
				
			||||||
 | 
					        send_image(id_log, file_name)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        display_text(frame, f"QR invalid", (25, 25), 0.7, (6, 6, 255), 2)
 | 
				
			||||||
 | 
					        display_text(frame, f"Failed", (25, 50), 0.7, (6, 6, 255), 2)
 | 
				
			||||||
 | 
					        speak("Failed")   
 | 
				
			||||||
 | 
					        cv.namedWindow(WINDOW_QR_CODE, cv.WINDOW_NORMAL)
 | 
				
			||||||
 | 
					        cv.resizeWindow(WINDOW_QR_CODE, screen_width, screen_height)
 | 
				
			||||||
 | 
					        cv.imshow(WINDOW_QR_CODE, frame)
 | 
				
			||||||
 | 
					        cv.moveWindow(WINDOW_QR_CODE, 10, 10)
 | 
				
			||||||
 | 
					        cv.waitKey(2000)
 | 
				
			||||||
 | 
					        cv.destroyWindow(WINDOW_QR_CODE)
 | 
				
			||||||
 | 
					    return frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					datasetPath = "../DetectFace/dataset"
 | 
				
			||||||
 | 
					listFilesPath = '../DetectFace/listFiles.pkl'
 | 
				
			||||||
 | 
					images = []
 | 
				
			||||||
 | 
					classNames = []
 | 
				
			||||||
 | 
					lisFileTrain = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if os.path.exists(listFilesPath):
 | 
				
			||||||
 | 
					    with open(listFilesPath, 'rb') as f:
 | 
				
			||||||
 | 
					        lisFileTrain = pickle.load(f)
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    lisFileTrain = os.listdir(datasetPath)
 | 
				
			||||||
 | 
					    with open(listFilesPath, 'wb') as f:
 | 
				
			||||||
 | 
					        pickle.dump(lisFileTrain, f)
 | 
				
			||||||
 | 
					for file in lisFileTrain:
 | 
				
			||||||
 | 
					    classNames.append(os.path.splitext(file)[0].split('_')[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def encodeImgs(save_path="../DetectFace/encodings.pkl"):
 | 
				
			||||||
 | 
					    if os.path.exists(save_path):
 | 
				
			||||||
 | 
					        print(f"Loading encodings from {save_path}...")
 | 
				
			||||||
 | 
					        with open(save_path, "rb") as f:
 | 
				
			||||||
 | 
					            return pickle.load(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeListKnow = encodeImgs()
 | 
				
			||||||
 | 
					print("Load data success")
 | 
				
			||||||
# Khởi tạo camera
 | 
					# Khởi tạo camera
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
 | 
					    recognized_faces = {}
 | 
				
			||||||
 | 
					    name_history = {}
 | 
				
			||||||
    cap = cv.VideoCapture(0)
 | 
					    cap = cv.VideoCapture(0)
 | 
				
			||||||
    face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml')
 | 
					    face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml')
 | 
				
			||||||
    cv.namedWindow(WINDOW_TRACKING, cv.WINDOW_NORMAL)
 | 
					    cv.namedWindow(WINDOW_TRACKING, cv.WINDOW_NORMAL)
 | 
				
			||||||
| 
						 | 
					@ -203,20 +276,67 @@ def main():
 | 
				
			||||||
        ret, frame = cap.read()
 | 
					        ret, frame = cap.read()
 | 
				
			||||||
        if not ret:
 | 
					        if not ret:
 | 
				
			||||||
            break
 | 
					            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        frameS = cv.resize(frame, (0,0), None, fx=0.5, fy=0.5)
 | 
				
			||||||
 | 
					        frameS = cv.cvtColor(frameS, cv.COLOR_BGR2RGB)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        faceCurFrame = face_recognition.face_locations(frameS, model='hog')
 | 
				
			||||||
 | 
					        encodeCurFrame = face_recognition.face_encodings(frameS)
 | 
				
			||||||
 | 
					        frame = process_qr_code(frame)
 | 
				
			||||||
 | 
					        current_time = time.time()
 | 
				
			||||||
 | 
					        for encodeFace, faceLoc in zip(encodeCurFrame, faceCurFrame):
 | 
				
			||||||
 | 
					            matches = face_recognition.compare_faces(encodeListKnow, encodeFace)
 | 
				
			||||||
 | 
					            faceDis = face_recognition.face_distance(encodeListKnow, encodeFace)
 | 
				
			||||||
 | 
					            # print(faceDis)
 | 
				
			||||||
 | 
					            matchIndex = np.argmin(faceDis)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if faceDis[matchIndex] < 0.35:
 | 
				
			||||||
 | 
					                name = classNames[matchIndex].upper()
 | 
				
			||||||
 | 
					                 # If the face is recognized, track the timestamp
 | 
				
			||||||
 | 
					                if name not in recognized_faces:
 | 
				
			||||||
 | 
					                    recognized_faces[name] = current_time  # Store first detection time
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    elapsed_time = current_time - recognized_faces[name]
 | 
				
			||||||
 | 
					                    if (name not in name_history) or (current_time - name_history[name] >= 60):
 | 
				
			||||||
 | 
					                        if elapsed_time >= 2.5:  # If face is seen for 2s, execute script
 | 
				
			||||||
 | 
					                            process_face_detect(f"{name}\n{"Staff"}\n\n", frame)
 | 
				
			||||||
 | 
					                            name_history[name] = time.time()
 | 
				
			||||||
 | 
					                            del recognized_faces[name]
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            display_text(frame, f"Checking: "+str(round((elapsed_time/2.5)*100,2))+"%", (700, 55), 1, (0, 255, 255), 2)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        display_text(frame, f"Checked. Try after {round(60-(current_time - name_history[name]),0)}s", (600, 55), 1, (0, 255, 255), 2)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                name = "Unknow"
 | 
				
			||||||
 | 
					                recognized_faces = {}
 | 
				
			||||||
 | 
					                display_text(frame, f"Face not found - use QRcode", (20, 55), 0.7, (6, 6, 255), 2)
 | 
				
			||||||
 | 
					            y1, x2, y2, x1 = faceLoc
 | 
				
			||||||
 | 
					            y1, x2, y2, x1 = y1*2, x2*2, y2*2, x1*2
 | 
				
			||||||
 | 
					            cv.rectangle(frame, (x1,y1), (x2,y2), (0,255,0), 2)
 | 
				
			||||||
 | 
					            cv.putText(frame, name + f"({(1 - round(faceDis[matchIndex], 2))*100}%)", (x1, y1), cv.FONT_HERSHEY_COMPLEX, 0.8, (0,255,0), 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Convert the frame to grayscale
 | 
					        # Convert the frame to grayscale
 | 
				
			||||||
        gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
 | 
					        # gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Detect faces in the frame
 | 
					        # # Detect faces in the frame
 | 
				
			||||||
        faces = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=25, minSize=(30, 30))
 | 
					        # faces = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=25, minSize=(30, 30))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Draw rectangles around the faces
 | 
					        # # Draw rectangles around the faces
 | 
				
			||||||
        if len(faces) == 1:
 | 
					        # if len(faces) == 1:
 | 
				
			||||||
            for (x, y, w, h) in faces:
 | 
					        #     for (x, y, w, h) in faces:
 | 
				
			||||||
                cv.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
 | 
					        #         cv.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
 | 
				
			||||||
                display_text(frame, f"Face detected", (430, 25), 0.7, (0, 255, 0), 2)
 | 
					        #         display_text(frame, f"Face detected", (430, 25), 0.7, (0, 255, 0), 2)
 | 
				
			||||||
                frame = process_qr_code(frame)
 | 
					        #         frame = process_qr_code(frame)
 | 
				
			||||||
        else:
 | 
					        # else:
 | 
				
			||||||
            display_text(frame, f"Face not found", (430, 25), 0.7, (6, 6, 255), 2)
 | 
					        #     display_text(frame, f"Face not found", (430, 25), 0.7, (6, 6, 255), 2)
 | 
				
			||||||
        cv.imshow(WINDOW_TRACKING, frame)
 | 
					        cv.imshow(WINDOW_TRACKING, frame)
 | 
				
			||||||
        cv.moveWindow(WINDOW_TRACKING, 10, 10)
 | 
					        cv.moveWindow(WINDOW_TRACKING, 10, 10)
 | 
				
			||||||
        if cv.waitKey(1) == ord('q'):
 | 
					        if cv.waitKey(1) == ord('q'):
 | 
				
			||||||
| 
						 | 
					@ -225,4 +345,5 @@ def main():
 | 
				
			||||||
    cap.release()
 | 
					    cap.release()
 | 
				
			||||||
    cv.destroyAllWindows()
 | 
					    cv.destroyAllWindows()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
main()
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
		Loading…
	
		Reference in New Issue