Merge pull request 'Update Working review' (#105) from that-fe into master
Reviewed-on: #105
This commit is contained in:
commit
9b472544b6
|
|
@ -337,4 +337,20 @@ class JiraController extends Controller
|
|||
Mail::to([$email])->cc(['admin@apactech.io', 'joseph@apactech.io'])->send(new WarningLongTask($user_info[$email]));
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserProjectParticipating(Request $request)
|
||||
{
|
||||
$userID = $request->input('userID');
|
||||
$startDate = $request->input('fromDate');
|
||||
$endDate = $request->input('toDate');
|
||||
$user = User::find($userID);
|
||||
$userJira = $this->jiraService->getUserByEmail($user->email);
|
||||
$projects = $this->jiraService->getUserWorkLogs($userJira[0]['accountId'], $startDate, $endDate);
|
||||
|
||||
return response()->json([
|
||||
'data' => $projects,
|
||||
"accountId" => $userJira[0]['accountId'],
|
||||
'status' => true
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class TrackingController extends Controller
|
|||
use HasFilterRequest;
|
||||
use HasSearchRequest;
|
||||
use AnalyzeData;
|
||||
|
||||
|
||||
private $CHECK_IN = 'check in';
|
||||
private $CHECK_OUT = 'check out';
|
||||
|
||||
|
|
@ -207,15 +207,14 @@ class TrackingController extends Controller
|
|||
// $status = $this->CHECK_IN;
|
||||
// $lastCheck =Tracking::find(1)->created_at;
|
||||
// $user = Admin::where('name', 'LE TAN LUAN')->first();
|
||||
if($status === $this->CHECK_IN){
|
||||
if ($status === $this->CHECK_IN) {
|
||||
$morning_time = Carbon::createFromTimeString('07:30AM')->setTimezone(env('TIME_ZONE'));
|
||||
$morning_condition_time = Carbon::createFromTimeString('07:40AM')->setTimezone(env('TIME_ZONE'));
|
||||
$afternoon_time = Carbon::createFromTimeString('11:30AM')->setTimezone(env('TIME_ZONE'));
|
||||
$afternoon_condition_time = Carbon::createFromTimeString('01:10PM')->setTimezone(env('TIME_ZONE'));
|
||||
$admin_mails = Admin::where('permission', 'like', '%admin%')->pluck('email');
|
||||
|
||||
if ($time->greaterThan($morning_condition_time) && $time->lessThan($afternoon_time))
|
||||
{
|
||||
if ($time->greaterThan($morning_condition_time) && $time->lessThan($afternoon_time)) {
|
||||
$period = 'morning';
|
||||
$minutes_late = $morning_time->diffInMinutes($time);
|
||||
$data = array(
|
||||
|
|
@ -223,9 +222,9 @@ class TrackingController extends Controller
|
|||
"email" => $user->email,
|
||||
"name" => $user->name,
|
||||
"admin_mails" => $admin_mails,
|
||||
"message1" => "Your ". $period ." starts ". $minutes_late ." minutes late",
|
||||
"message2" => "You checked in at [" . $time ."]",
|
||||
"url" => env('ADMIN_URL')."/tracking?search=&per_page=10&page=1&timezone=Asia%2FSaigon&name=".$user->name."&time_string=".$time->format("Y-m-d H:i")."&status=check+in",
|
||||
"message1" => "Your " . $period . " starts " . $minutes_late . " minutes late",
|
||||
"message2" => "You checked in at [" . $time . "]",
|
||||
"url" => env('ADMIN_URL') . "/tracking?search=&per_page=10&page=1&timezone=Asia%2FSaigon&name=" . $user->name . "&time_string=" . $time->format("Y-m-d H:i") . "&status=check+in",
|
||||
"subject" => "[Management System] Late warning - " . $user->name
|
||||
);
|
||||
Mail::to($user->email)->cc($admin_mails)->send(new CheckinLateMail($data));
|
||||
|
|
@ -247,7 +246,81 @@ class TrackingController extends Controller
|
|||
// Mail::to($user->email)->cc($admin_mails)->send(new CheckinLateMail($data));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function getSummaryTracking(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');
|
||||
|
||||
$trackingData = Tracking::where('user_id', $userID)
|
||||
->where('status', 'check in');
|
||||
|
||||
if ($startDate && $endDate) {
|
||||
$trackingData->whereBetween('updated_at', [$startDate, $endDate . ' 23:59:59']);
|
||||
} elseif ($startDate) {
|
||||
$trackingData->where('updated_at', '>=', $startDate);
|
||||
} elseif ($endDate) {
|
||||
$trackingData->where('updated_at', '<=', $endDate . ' 23:59:59');
|
||||
}
|
||||
|
||||
$onTimeMorning = 0;
|
||||
$lateMorning = 0;
|
||||
$onTimeAfternoon = 0;
|
||||
$lateAfternoon = 0;
|
||||
$datesChecked = [];
|
||||
|
||||
$trackingData->get()->groupBy(function ($record) {
|
||||
return Carbon::parse($record->time_string)->toDateString();
|
||||
})->each(function ($records, $date) use (&$onTimeMorning, &$lateMorning, &$onTimeAfternoon, &$lateAfternoon, &$datesChecked) {
|
||||
$morningCheck = $records->filter(function ($record) {
|
||||
return Carbon::parse($record->time_string)->hour < 12;
|
||||
})->sortBy('time_string')->first();
|
||||
|
||||
$afternoonCheck = $records->filter(function ($record) {
|
||||
return Carbon::parse($record->time_string)->hour >= 12;
|
||||
})->sortBy('time_string')->first();
|
||||
|
||||
$morningTime = Carbon::parse($date)->setTime(7, 40, 0);
|
||||
$afternoonTime = Carbon::parse($date)->setTime(13, 10, 0);
|
||||
|
||||
if ($morningCheck) {
|
||||
$checkInTime = Carbon::parse($morningCheck->time_string);
|
||||
if ($checkInTime->lessThanOrEqualTo($morningTime)) {
|
||||
$onTimeMorning++;
|
||||
} else {
|
||||
$lateMorning++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($afternoonCheck) {
|
||||
$checkInTime = Carbon::parse($afternoonCheck->time_string);
|
||||
if ($checkInTime->lessThanOrEqualTo($afternoonTime)) {
|
||||
$onTimeAfternoon++;
|
||||
} else {
|
||||
$lateAfternoon++;
|
||||
}
|
||||
} else {
|
||||
// Default to on time if no afternoon check-in
|
||||
$onTimeAfternoon++;
|
||||
}
|
||||
});
|
||||
|
||||
return AbstractController::ResultSuccess([
|
||||
'on_time_morning' => $onTimeMorning,
|
||||
'late_morning' => $lateMorning,
|
||||
'on_time_afternoon' => $onTimeAfternoon,
|
||||
'late_afternoon' => $lateAfternoon,
|
||||
'value' => $trackingData->get()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ Route::middleware('api')
|
|||
Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs'])->middleware('check.permission:admin.staff');
|
||||
Route::get('/allocation', [JiraController::class, 'getAllUserDoing'])->middleware('check.permission:admin.staff');
|
||||
Route::get('/issue/detail', [JiraController::class, 'getDetailIssueById'])->middleware('check.permission:admin.staff');
|
||||
Route::get('/project-participating', [JiraController::class, 'getUserProjectParticipating'])->middleware('check.permission:admin');
|
||||
});
|
||||
|
||||
Route::group([
|
||||
|
|
@ -136,6 +137,7 @@ Route::middleware('api')
|
|||
Route::post('/create', [TrackingController::class, 'create'])->middleware('check.permission:admin.hr');
|
||||
Route::post('/update', [TrackingController::class, 'update'])->middleware('check.permission:admin.hr');
|
||||
Route::get('/delete', [TrackingController::class, 'delete'])->middleware('check.permission:admin.hr');
|
||||
Route::get('/summary', [TrackingController::class, 'getSummaryTracking'])->middleware('check.permission:admin');
|
||||
});
|
||||
|
||||
Route::group([
|
||||
|
|
|
|||
|
|
@ -231,12 +231,12 @@ class JiraService
|
|||
$users_data = [];
|
||||
$user_warning = [];
|
||||
foreach ($users as $user) {
|
||||
$user = (array)$user[0];
|
||||
$user = (array) $user[0];
|
||||
$users_data[$user['displayName']]['user'] = $user;
|
||||
$users_data[$user['displayName']]['total_spent'] = 0;
|
||||
$users_data[$user['displayName']]['total_est'] = 0;
|
||||
$body = [
|
||||
'expand' => ['names', 'schema','changelog'],
|
||||
'expand' => ['names', 'schema', 'changelog'],
|
||||
'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project', 'updated'],
|
||||
'jql' => sprintf(
|
||||
"assignee = '%s' AND status IN ('to do', 'todo', 'in progress') ORDER BY updated DESC",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -12,6 +12,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@mantine/charts": "^7.16.3",
|
||||
"@mantine/core": "^7.13.2",
|
||||
"@mantine/dates": "^7.13.2",
|
||||
"@mantine/form": "^7.13.2",
|
||||
|
|
@ -36,7 +37,7 @@
|
|||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.19.0",
|
||||
"reactstrap": "^9.2.2",
|
||||
"recharts": "^2.11.0",
|
||||
"recharts": "^2.15.1",
|
||||
"tailwind-merge": "^2.0.0",
|
||||
"tests": "^0.4.2"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { BrowserRouter, useRoutes } from 'react-router-dom'
|
|||
import mainRoutes from '@/routes/main'
|
||||
import classes from '@/App.module.css'
|
||||
import '@mantine/dates/styles.css'
|
||||
import '@mantine/charts/styles.css'
|
||||
|
||||
export const App = () => {
|
||||
const element = useRoutes(mainRoutes)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export const getListTracking = API_URL + 'v1/admin/tracking'
|
|||
export const addTracking = API_URL + 'v1/admin/tracking/create'
|
||||
export const updateTracking = API_URL + 'v1/admin/tracking/update'
|
||||
export const deleteTracking = API_URL + 'v1/admin/tracking/delete'
|
||||
export const getListTrackingSummary = API_URL + 'v1/admin/tracking/summary'
|
||||
|
||||
// Worklogs
|
||||
export const fetchAllIssues = API_URL + 'v1/admin/jira/fetch-issues'
|
||||
|
|
@ -17,6 +18,8 @@ export const getAllIssuesByProject =
|
|||
export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs'
|
||||
export const getAllUserDoing = API_URL + 'v1/admin/jira/allocation'
|
||||
export const getDetailIssByKey = API_URL + 'v1/admin/jira/issue/detail'
|
||||
export const getPJParticipating =
|
||||
API_URL + 'v1/admin/jira/project-participating'
|
||||
|
||||
//Timekeeping
|
||||
export const getTheTimesheet = API_URL + 'v1/admin/timekeeping'
|
||||
|
|
|
|||
|
|
@ -200,3 +200,46 @@
|
|||
width: rem(20px);
|
||||
height: rem(22px);
|
||||
}
|
||||
|
||||
.chartContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.boxContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pjParticipatingContainer {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.boxColorLime {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #82c91e;
|
||||
}
|
||||
|
||||
.boxColorRed {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #fa5252;
|
||||
}
|
||||
|
||||
.boxColorOrange {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #fd7e14;
|
||||
}
|
||||
|
||||
.boxColorTeal {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #12b886;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import {
|
|||
projectReviewUpdate,
|
||||
projectReviewDelete,
|
||||
evaluationReportAllUsers,
|
||||
getListTrackingSummary,
|
||||
getPJParticipating,
|
||||
} from '@/api/Admin'
|
||||
import DataTableAll from '@/components/DataTable/DataTable'
|
||||
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
|
||||
|
|
@ -35,10 +37,12 @@ import {
|
|||
IconClearAll,
|
||||
IconEdit,
|
||||
IconPresentationAnalytics,
|
||||
IconReportAnalytics,
|
||||
IconX,
|
||||
} from '@tabler/icons-react'
|
||||
import { useForm } from '@mantine/form'
|
||||
import { update, Xdelete } from '@/rtk/helpers/CRUD'
|
||||
import { PieChart } from '@mantine/charts'
|
||||
|
||||
interface User {
|
||||
id: number
|
||||
|
|
@ -76,8 +80,32 @@ interface DataProjectReview {
|
|||
updated_at: string
|
||||
}
|
||||
|
||||
interface DataPJParticipating {
|
||||
name: string
|
||||
total_task: number
|
||||
total_time_spent: number
|
||||
}
|
||||
|
||||
type TLog = {
|
||||
id: number
|
||||
name: string
|
||||
status: string
|
||||
time_string: Date
|
||||
}
|
||||
|
||||
interface DataSummaryTracking {
|
||||
on_time_morning: number
|
||||
late_morning: number
|
||||
on_time_afternoon: number
|
||||
late_afternoon: number
|
||||
value: TLog[]
|
||||
}
|
||||
|
||||
const StaffEvaluation = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [loadingReview, setLoadingReview] = useState(false)
|
||||
const [loadingWorkingStyle, setLoadingWorkingStyle] = useState(false)
|
||||
const [loadingPJParticipating, setLoadingPJParticipating] = useState(false)
|
||||
const [loadingTechnical, setLoadingTechnical] = useState(false)
|
||||
const [dataProfile, setDataProfile] = useState<any>([])
|
||||
const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
|
||||
|
|
@ -96,6 +124,17 @@ const StaffEvaluation = () => {
|
|||
const [activeBtn, setActiveBtn] = useState(false)
|
||||
const [loadingExport, setLoadingExport] = useState(false)
|
||||
const [loadingExportAll, setLoadingExportAll] = useState(false)
|
||||
const [dataPJParticipating, setDataPJParticipating] = useState<
|
||||
DataPJParticipating[]
|
||||
>([])
|
||||
const [dataSummaryTracking, setDataSummaryTracking] =
|
||||
useState<DataSummaryTracking>({
|
||||
on_time_morning: 0,
|
||||
late_morning: 0,
|
||||
on_time_afternoon: 0,
|
||||
late_afternoon: 0,
|
||||
value: [],
|
||||
})
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
|
|
@ -298,17 +337,147 @@ const StaffEvaluation = () => {
|
|||
return []
|
||||
}
|
||||
|
||||
const getListSummaryTracking = 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(getListTrackingSummary, params)
|
||||
if (res.status) {
|
||||
return res.data
|
||||
}
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message ?? error,
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const getListProjectParticipating = async (filterSearch: Filter) => {
|
||||
try {
|
||||
const fromDate = filterSearch.fromDate
|
||||
? moment(filterSearch.fromDate).format('YYYY-MM-DD')
|
||||
: moment(new Date()).format('YYYY-MM-DD')
|
||||
const toDate = filterSearch.toDate
|
||||
? moment(filterSearch.toDate).format('YYYY-MM-DD')
|
||||
: moment(new Date()).format('YYYY-MM-DD')
|
||||
const params = {
|
||||
userID: filterSearch.userID ?? '',
|
||||
fromDate: fromDate,
|
||||
toDate: toDate,
|
||||
}
|
||||
const res = await get(getPJParticipating, params)
|
||||
if (res.status) {
|
||||
const value = processJiraData(res.data, fromDate, toDate, res.accountId)
|
||||
return value
|
||||
}
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message ?? error,
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function processJiraData(
|
||||
data: any,
|
||||
startDate: any,
|
||||
endDate: any,
|
||||
accountId: string,
|
||||
) {
|
||||
const projectSummary: any = {}
|
||||
const start = new Date(startDate)
|
||||
const end = new Date(endDate)
|
||||
|
||||
data.issues.forEach((issue: any) => {
|
||||
const projectName = issue.fields.project.name
|
||||
const worklogs = issue.fields.worklog.worklogs
|
||||
|
||||
// Filter worklogs based on 'started' date range
|
||||
const filteredWorklogs = worklogs.filter((log: any) => {
|
||||
const logDate = new Date(log.started)
|
||||
return (
|
||||
logDate >= start &&
|
||||
logDate <= end &&
|
||||
accountId === log?.updateAuthor?.accountId
|
||||
)
|
||||
})
|
||||
|
||||
if (filteredWorklogs.length === 0) return // Skip if no worklogs in range
|
||||
|
||||
if (!projectSummary[projectName]) {
|
||||
projectSummary[projectName] = {
|
||||
project_name: projectName,
|
||||
total_task: 0,
|
||||
total_time_spent: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Get unique issueIds within the filtered worklogs
|
||||
const uniqueIssues = new Set(
|
||||
filteredWorklogs.map((log: any) => log.issueId),
|
||||
)
|
||||
|
||||
// Sum up total time spent from filtered worklogs
|
||||
const totalTimeSpent = filteredWorklogs.reduce(
|
||||
(sum: number, log: any) => sum + log.timeSpentSeconds,
|
||||
0,
|
||||
)
|
||||
|
||||
projectSummary[projectName].total_task += uniqueIssues.size
|
||||
projectSummary[projectName].total_time_spent += totalTimeSpent
|
||||
})
|
||||
|
||||
const returnValue: DataPJParticipating[] = Object.values(projectSummary)
|
||||
|
||||
return returnValue
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (filter?.userID) {
|
||||
setLoading(true)
|
||||
setLoadingReview(true)
|
||||
setLoadingWorkingStyle(true)
|
||||
const fetchData = async () => {
|
||||
const result = await getListProfilesData(filter)
|
||||
setDataProfile(result ?? [])
|
||||
setLoading(false)
|
||||
}
|
||||
const fetchDataProject = async () => {
|
||||
const result = await getListProfilesData(filter)
|
||||
const resultProject = await getListProjectReview(filter)
|
||||
setDataProfile(result ?? [])
|
||||
setDataProjectReview(resultProject ?? [])
|
||||
setLoading(false)
|
||||
setLoadingReview(false)
|
||||
}
|
||||
const fetchDataTracking = async () => {
|
||||
const resultTracking = await getListSummaryTracking(filter)
|
||||
setDataSummaryTracking(resultTracking ?? [])
|
||||
setLoadingWorkingStyle(false)
|
||||
}
|
||||
const fetchDataPJParticipating = async () => {
|
||||
const resultPJParticipating = await getListProjectParticipating(filter)
|
||||
setDataPJParticipating(resultPJParticipating ?? [])
|
||||
setLoadingPJParticipating(false)
|
||||
}
|
||||
fetchData()
|
||||
fetchDataProject()
|
||||
fetchDataTracking()
|
||||
if (filter?.fromDate && filter?.toDate) {
|
||||
setLoadingPJParticipating(true)
|
||||
fetchDataPJParticipating()
|
||||
}
|
||||
}
|
||||
}, [filter])
|
||||
|
||||
|
|
@ -433,7 +602,7 @@ const StaffEvaluation = () => {
|
|||
{
|
||||
name: 'name',
|
||||
size: '15%',
|
||||
header: 'Project Name',
|
||||
header: 'Name',
|
||||
},
|
||||
{
|
||||
name: 'role',
|
||||
|
|
@ -512,6 +681,27 @@ const StaffEvaluation = () => {
|
|||
},
|
||||
]
|
||||
|
||||
const columnsPJParticipating = [
|
||||
{
|
||||
name: 'project_name',
|
||||
size: '50%',
|
||||
header: 'Name',
|
||||
},
|
||||
{
|
||||
name: 'total_task',
|
||||
size: '25%',
|
||||
header: 'Total task',
|
||||
},
|
||||
{
|
||||
name: 'total_time_spent',
|
||||
size: '25%',
|
||||
header: 'Total time spent',
|
||||
render: (row: any) => {
|
||||
return <div>{row?.total_time_spent / 60 / 60}h</div>
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const handleCreate = async (values: DataProjectReview) => {
|
||||
try {
|
||||
const res = await post(projectReviewAdd, {
|
||||
|
|
@ -717,6 +907,12 @@ const StaffEvaluation = () => {
|
|||
>
|
||||
<span style={{ fontSize: '16px' }}>Project review</span>
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
value="working_style"
|
||||
leftSection={<IconReportAnalytics size={16} color="#fab005" />}
|
||||
>
|
||||
<span style={{ fontSize: '16px' }}>Working review</span>
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value="general">
|
||||
|
|
@ -785,9 +981,12 @@ const StaffEvaluation = () => {
|
|||
|
||||
<Tabs.Panel value="project_review">
|
||||
<Box className={classes.userInfoSection} display="flex">
|
||||
{loading ? (
|
||||
{loadingReview ? (
|
||||
<Box
|
||||
style={{ width: '100%', display: loading ? 'block' : 'none' }}
|
||||
style={{
|
||||
width: '100%',
|
||||
display: loadingReview ? 'block' : 'none',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
|
|
@ -832,6 +1031,129 @@ const StaffEvaluation = () => {
|
|||
)}
|
||||
</Box>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="working_style">
|
||||
{loadingWorkingStyle ? (
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
display: loadingWorkingStyle ? '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>
|
||||
) : (
|
||||
<Box>
|
||||
<Box className={classes.chartContainer} display="flex">
|
||||
<PieChart
|
||||
withLabelsLine
|
||||
labelsPosition="outside"
|
||||
labelsType="value"
|
||||
withLabels={dataSummaryTracking.value.length > 0}
|
||||
withTooltip
|
||||
data={
|
||||
dataSummaryTracking.value.length > 0
|
||||
? [
|
||||
{
|
||||
name: 'On time morning',
|
||||
value: dataSummaryTracking?.on_time_morning ?? 0,
|
||||
color: 'lime',
|
||||
},
|
||||
{
|
||||
name: 'Late morning',
|
||||
value: dataSummaryTracking?.late_morning ?? 0,
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
name: 'On time afternoon',
|
||||
value: dataSummaryTracking?.on_time_afternoon ?? 0,
|
||||
color: 'teal',
|
||||
},
|
||||
{
|
||||
name: 'Late afternoon',
|
||||
value: dataSummaryTracking?.late_afternoon ?? 0,
|
||||
color: 'orange',
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: 'No data',
|
||||
value: 1,
|
||||
color: 'gray.6',
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<Box className={classes.boxContainer} display="flex">
|
||||
<Box className={classes.boxContainer} display="flex">
|
||||
<div className={classes.boxColorLime}></div>
|
||||
<div style={{ paddingLeft: '10px', paddingRight: '20px' }}>
|
||||
On time morning
|
||||
</div>
|
||||
</Box>
|
||||
<Box className={classes.boxContainer} display="flex">
|
||||
<div className={classes.boxColorRed}></div>
|
||||
<div style={{ paddingLeft: '10px', paddingRight: '20px' }}>
|
||||
Late morning
|
||||
</div>
|
||||
</Box>
|
||||
<Box className={classes.boxContainer} display="flex">
|
||||
<div className={classes.boxColorTeal}></div>
|
||||
<div style={{ paddingLeft: '10px', paddingRight: '20px' }}>
|
||||
On time afternoon
|
||||
</div>
|
||||
</Box>
|
||||
<Box className={classes.boxContainer} display="flex">
|
||||
<div className={classes.boxColorOrange}></div>
|
||||
<div style={{ paddingLeft: '10px' }}>Late afternoon</div>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
<Box className={classes.pjParticipatingContainer}>
|
||||
<Title order={4}>Project Participating</Title>
|
||||
</Box>
|
||||
<Box className={classes.boxContainer} display="flex">
|
||||
{loadingPJParticipating ? (
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
display: loadingPJParticipating ? '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'}>
|
||||
Analyzing . . .
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<DataTableAll
|
||||
data={dataPJParticipating}
|
||||
columns={columnsPJParticipating}
|
||||
size=""
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
|
||||
{/* Add/Edit User modal */}
|
||||
|
|
|
|||
Loading…
Reference in New Issue