Merge pull request 'Update Project Review, export' (#102) from that-fe into master
Reviewed-on: #102
This commit is contained in:
		
						commit
						fc6587bcff
					
				| 
						 | 
				
			
			@ -8,9 +8,11 @@ use App\Services\JiraService;
 | 
			
		|||
use Carbon\Carbon;
 | 
			
		||||
use Modules\Admin\app\Models\TechnicalUser;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
use Modules\Admin\app\Models\ProjectReview;
 | 
			
		||||
use Modules\Admin\app\Models\UserCriteria;
 | 
			
		||||
use PhpOffice\PhpWord\IOFactory;
 | 
			
		||||
use PhpOffice\PhpWord\PhpWord;
 | 
			
		||||
use PhpOffice\PhpWord\SimpleType\Jc;
 | 
			
		||||
 | 
			
		||||
class EvaluationController extends Controller
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -222,6 +224,50 @@ class EvaluationController extends Controller
 | 
			
		|||
                '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)
 | 
			
		||||
            $section->addPageBreak();
 | 
			
		||||
        //Technical
 | 
			
		||||
| 
						 | 
				
			
			@ -260,4 +306,156 @@ class EvaluationController extends Controller
 | 
			
		|||
 | 
			
		||||
        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,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\CriteriasController;
 | 
			
		||||
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\TechnicalController;
 | 
			
		||||
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('/technical', [EvaluationController::class, 'technical'])->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([
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -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 updateNoteLeave =
 | 
			
		||||
  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
 | 
			
		||||
export const getTickets = API_URL + 'v1/admin/ticket/all'
 | 
			
		||||
| 
						 | 
				
			
			@ -79,14 +80,24 @@ export const updateProfilesData =
 | 
			
		|||
  API_URL + 'v1/admin/criterias/profiles-data/update'
 | 
			
		||||
 | 
			
		||||
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 getAllTechByUserId =
 | 
			
		||||
  API_URL + 'v1/admin/technical/get-tech-by-user-id'
 | 
			
		||||
 | 
			
		||||
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 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 updateProfileFolder = API_URL + 'v1/admin/profile/update-profile'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,6 +84,7 @@ export const DataTableAll = ({
 | 
			
		|||
  checkBox,
 | 
			
		||||
  size,
 | 
			
		||||
  infoTotal,
 | 
			
		||||
  componentRight,
 | 
			
		||||
}: {
 | 
			
		||||
  data: any[]
 | 
			
		||||
  columns: Column[]
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +93,7 @@ export const DataTableAll = ({
 | 
			
		|||
  checkBox?: boolean
 | 
			
		||||
  size: string
 | 
			
		||||
  infoTotal?: React.ReactNode // Set the type to ReactNode to allow JSX elements
 | 
			
		||||
  componentRight?: React.ReactNode
 | 
			
		||||
}) => {
 | 
			
		||||
  const [Tdata, setTData] = useState<any[]>(data)
 | 
			
		||||
  // const [tempData, setTempData] = useState<any[]>([])
 | 
			
		||||
| 
						 | 
				
			
			@ -325,10 +327,7 @@ export const DataTableAll = ({
 | 
			
		|||
            </Text>
 | 
			
		||||
          )}
 | 
			
		||||
        </Box>
 | 
			
		||||
        <Box
 | 
			
		||||
          className={classes.totalBox}
 | 
			
		||||
          display={infoTotal ? 'flex' : 'none'}
 | 
			
		||||
        >
 | 
			
		||||
        <Box className={classes.totalBox} display={infoTotal ? 'flex' : 'none'}>
 | 
			
		||||
          <Text fz={'sm'} ta={'right'}>
 | 
			
		||||
            {infoTotal}
 | 
			
		||||
          </Text>
 | 
			
		||||
| 
						 | 
				
			
			@ -368,6 +367,7 @@ export const DataTableAll = ({
 | 
			
		|||
            }}
 | 
			
		||||
          />
 | 
			
		||||
        </Box>
 | 
			
		||||
        {componentRight}
 | 
			
		||||
      </Box>
 | 
			
		||||
      <Box className={classes.box}>
 | 
			
		||||
        <Table
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,6 +55,8 @@
 | 
			
		|||
  display: flex;
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
  gap: 10px;
 | 
			
		||||
  max-height: 72vh;
 | 
			
		||||
  overflow-y: scroll;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.titleSidebar {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,17 +2,43 @@ import {
 | 
			
		|||
  evaluation,
 | 
			
		||||
  getAllTechByUserId,
 | 
			
		||||
  getAllUser,
 | 
			
		||||
  projectReview,
 | 
			
		||||
  sprintReview,
 | 
			
		||||
  projectReviewAdd,
 | 
			
		||||
  projectReviewUpdate,
 | 
			
		||||
  projectReviewDelete,
 | 
			
		||||
  evaluationReportAllUsers,
 | 
			
		||||
} from '@/api/Admin'
 | 
			
		||||
import DataTableAll from '@/components/DataTable/DataTable'
 | 
			
		||||
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
 | 
			
		||||
import { get, getDownloadFile } from '@/rtk/helpers/apiService'
 | 
			
		||||
import { Box, Button, Loader, Select, Text, Title } from '@mantine/core'
 | 
			
		||||
import { get, getDownloadFile, post } from '@/rtk/helpers/apiService'
 | 
			
		||||
import {
 | 
			
		||||
  Box,
 | 
			
		||||
  Button,
 | 
			
		||||
  Dialog,
 | 
			
		||||
  Group,
 | 
			
		||||
  Loader,
 | 
			
		||||
  Modal,
 | 
			
		||||
  Select,
 | 
			
		||||
  Tabs,
 | 
			
		||||
  Text,
 | 
			
		||||
  Textarea,
 | 
			
		||||
  TextInput,
 | 
			
		||||
  Title,
 | 
			
		||||
} from '@mantine/core'
 | 
			
		||||
import { DateInput } from '@mantine/dates'
 | 
			
		||||
import { notifications } from '@mantine/notifications'
 | 
			
		||||
import moment from 'moment'
 | 
			
		||||
import { useEffect, useState } from 'react'
 | 
			
		||||
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 {
 | 
			
		||||
  id: number
 | 
			
		||||
| 
						 | 
				
			
			@ -40,17 +66,51 @@ interface DataTechnical {
 | 
			
		|||
  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 [loading, setLoading] = useState(false)
 | 
			
		||||
  const [loadingTechnical, setLoadingTechnical] = useState(false)
 | 
			
		||||
  const [dataProfile, setDataProfile] = useState<any>([])
 | 
			
		||||
  const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
 | 
			
		||||
  const [dataProjectReview, setDataProjectReview] = useState<
 | 
			
		||||
    DataProjectReview[]
 | 
			
		||||
  >([])
 | 
			
		||||
  const [listUsers, setListUsers] = useState<User[]>([])
 | 
			
		||||
  const [filter, setFilter] = useState<Filter>({
 | 
			
		||||
    userID: '',
 | 
			
		||||
    fromDate: 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 () => {
 | 
			
		||||
    try {
 | 
			
		||||
| 
						 | 
				
			
			@ -92,14 +152,19 @@ const StaffEvaluation = () => {
 | 
			
		|||
          ? moment(filterSearch.toDate).format('YYYY-MM-DD')
 | 
			
		||||
          : null,
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const user = listUsers.find(
 | 
			
		||||
        (el) => el.id.toString() === filterSearch.userID,
 | 
			
		||||
      )
 | 
			
		||||
      setLoadingExport(true)
 | 
			
		||||
      const res = await getDownloadFile(evaluation, params)
 | 
			
		||||
 | 
			
		||||
      if (res.status) {
 | 
			
		||||
        const fileURL = window.URL.createObjectURL(new Blob([res.data]))
 | 
			
		||||
        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.setAttribute('download', fileName)
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +173,43 @@ const StaffEvaluation = () => {
 | 
			
		|||
        fileLink.click()
 | 
			
		||||
        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) {
 | 
			
		||||
      notifications.show({
 | 
			
		||||
        title: 'Error',
 | 
			
		||||
| 
						 | 
				
			
			@ -168,12 +270,39 @@ const StaffEvaluation = () => {
 | 
			
		|||
    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(() => {
 | 
			
		||||
    if (filter?.userID) {
 | 
			
		||||
      setLoading(true)
 | 
			
		||||
      const fetchData = async () => {
 | 
			
		||||
        const result = await getListProfilesData(filter)
 | 
			
		||||
        const resultProject = await getListProjectReview(filter)
 | 
			
		||||
        setDataProfile(result ?? [])
 | 
			
		||||
        setDataProjectReview(resultProject ?? [])
 | 
			
		||||
        setLoading(false)
 | 
			
		||||
      }
 | 
			
		||||
      fetchData()
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +413,148 @@ const StaffEvaluation = () => {
 | 
			
		|||
      </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 (
 | 
			
		||||
    <div>
 | 
			
		||||
      <div className={classes.title}>
 | 
			
		||||
| 
						 | 
				
			
			@ -291,10 +562,38 @@ const StaffEvaluation = () => {
 | 
			
		|||
          <Text>Admin/</Text>
 | 
			
		||||
          Staff Evaluation
 | 
			
		||||
        </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>
 | 
			
		||||
 | 
			
		||||
      <Box w="100%" display={'flex'} mt={15} ml={10}>
 | 
			
		||||
        <Box w="50%" display={'flex'}>
 | 
			
		||||
        <Box w="80%" display={'flex'} style={{ alignItems: 'center' }}>
 | 
			
		||||
          <Text
 | 
			
		||||
            mr={'xs'}
 | 
			
		||||
            style={{ alignContent: 'center' }}
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +603,7 @@ const StaffEvaluation = () => {
 | 
			
		|||
            User:
 | 
			
		||||
          </Text>
 | 
			
		||||
          <Select
 | 
			
		||||
            style={{ width: '30%' }}
 | 
			
		||||
            style={{ width: '20%' }}
 | 
			
		||||
            label={''}
 | 
			
		||||
            placeholder="Select user"
 | 
			
		||||
            maxLength={255}
 | 
			
		||||
| 
						 | 
				
			
			@ -317,131 +616,340 @@ const StaffEvaluation = () => {
 | 
			
		|||
            value={filter.userID}
 | 
			
		||||
            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
 | 
			
		||||
          w="50%"
 | 
			
		||||
          w="20%"
 | 
			
		||||
          display={'flex'}
 | 
			
		||||
          style={{ justifyContent: 'flex-end' }}
 | 
			
		||||
          mr={10}
 | 
			
		||||
        >
 | 
			
		||||
          <Button
 | 
			
		||||
            // m={5}
 | 
			
		||||
            style={{ display: filter.userID != '' ? 'flex' : 'none' }}
 | 
			
		||||
            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
 | 
			
		||||
          {loadingExport ? (
 | 
			
		||||
            <Button
 | 
			
		||||
              disabled
 | 
			
		||||
              style={{
 | 
			
		||||
                marginTop: '10%',
 | 
			
		||||
                textAlign: 'center',
 | 
			
		||||
                display: 'block',
 | 
			
		||||
                display: 'flex',
 | 
			
		||||
                justifyContent: 'center',
 | 
			
		||||
                width: '80px',
 | 
			
		||||
              }}
 | 
			
		||||
              onClick={() => {}}
 | 
			
		||||
            >
 | 
			
		||||
              <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>
 | 
			
		||||
              <Loader size={'sm'} color="green" type="oval" m={'0 auto'} />
 | 
			
		||||
            </Button>
 | 
			
		||||
          ) : (
 | 
			
		||||
            <DataTableAll
 | 
			
		||||
              data={dataTechnical}
 | 
			
		||||
              columns={columns}
 | 
			
		||||
              size=""
 | 
			
		||||
              searchInput
 | 
			
		||||
              infoTotal={infoTotal()}
 | 
			
		||||
            />
 | 
			
		||||
            <Button
 | 
			
		||||
              // m={5}
 | 
			
		||||
              style={{
 | 
			
		||||
                display: filter.userID != '' ? 'flex' : 'none',
 | 
			
		||||
                width: '80px',
 | 
			
		||||
              }}
 | 
			
		||||
              onClick={() => downloadFile(filter)}
 | 
			
		||||
            >
 | 
			
		||||
              Export
 | 
			
		||||
            </Button>
 | 
			
		||||
          )}
 | 
			
		||||
        </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>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue