Update Project Review, export
This commit is contained in:
parent
4fd18b988e
commit
174c8889ce
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ 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 App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use DateTime;
|
||||
|
||||
class ProjectReviewController extends Controller
|
||||
{
|
||||
|
|
@ -20,23 +21,19 @@ class ProjectReviewController extends Controller
|
|||
'fromDate' => 'nullable|date',
|
||||
'toDate' => 'nullable|date',
|
||||
]);
|
||||
|
||||
$userID = $request->input('userID');
|
||||
$startDate = $request->input('fromDate');
|
||||
$endDate = $request->input('toDate');
|
||||
$projectsData = ProjectReview::where('user_id', $userID);
|
||||
|
||||
$projectsData = ProjectReview::where('user_id', $request->input('userID'))
|
||||
->whereHas('user', function ($query) use ($startDate, $endDate) {
|
||||
if ($startDate && $endDate) {
|
||||
$query->whereBetween('updated_at', [$startDate, $endDate]);
|
||||
} elseif ($startDate) {
|
||||
$query->where('updated_at', '>=', $startDate);
|
||||
} elseif ($endDate) {
|
||||
$query->where('updated_at', '<=', $endDate);
|
||||
}
|
||||
})
|
||||
->get();
|
||||
|
||||
return AbstractController::ResultSuccess($projectsData);
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -58,7 +55,7 @@ class ProjectReviewController extends Controller
|
|||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, ProjectReview $projectReview)
|
||||
public function update(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'sometimes|required|string',
|
||||
|
|
@ -66,17 +63,29 @@ class ProjectReviewController extends Controller
|
|||
'note' => 'sometimes|required|string',
|
||||
'user_id' => 'sometimes|required|exists:users,id',
|
||||
]);
|
||||
|
||||
$projectReview->update($request->all());
|
||||
return response()->json($projectReview);
|
||||
$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(ProjectReview $projectReview)
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$projectReview->delete();
|
||||
return response()->json(['message' => 'Deleted successfully']);
|
||||
$id = $request->get('id');
|
||||
ProjectReview::destroy($id);
|
||||
return response()->json(['message' => 'Deleted successfully', 'status' => true]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,9 +193,10 @@ 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::get('/project-review/create', [ProjectReviewController::class, 'create'])->middleware('check.permission:admin');
|
||||
Route::get('/project-review/update', [ProjectReviewController::class, 'update'])->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');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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