update doing

This commit is contained in:
JOSEPH LE 2024-09-16 07:47:38 +07:00
parent cfe9ee5da9
commit 8c06526757
10 changed files with 4761 additions and 5 deletions

View File

@ -246,4 +246,18 @@ class JiraController extends Controller
// dd($tasksByUser); // dd($tasksByUser);
return $tasksByUser; return $tasksByUser;
} }
public function getAllUserDoing(Request $request)
{
try {
$doing = $this->jiraService->getAllUserDoing();
return response()->json([
'data' => $doing,
'status' => true
], 200);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
}
} }

View File

@ -149,6 +149,7 @@ class TimekeepingController extends Controller
return response()->json(['status' => true, 'message' => 'Add successfully']); return response()->json(['status' => true, 'message' => 'Add successfully']);
} }
public function updateCacheMonth(Request $request) public function updateCacheMonth(Request $request)

View File

@ -103,6 +103,7 @@ Route::middleware('api')
Route::get('/all-project', [JiraController::class, 'getAllProject']); Route::get('/all-project', [JiraController::class, 'getAllProject']);
Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']); Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']);
Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs'])->middleware('check.permission:admin.staff'); Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs'])->middleware('check.permission:admin.staff');
Route::get('/allocation', [JiraController::class, 'getAllUserDoing'])->middleware('check.permission:admin.staff');
}); });
Route::group([ Route::group([

View File

@ -73,7 +73,7 @@ class JiraService
'expand' => ['names', 'schema', 'operations'], 'expand' => ['names', 'schema', 'operations'],
'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project'], 'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project'],
'jql' => sprintf( 'jql' => sprintf(
"assignee = '%s' AND status IN ('backlog', 'todo', 'in progress')", "assignee = '%s' AND status IN ('backlog', 'to do', 'in progress')",
$accountId $accountId
), ),
'maxResults' => 50, 'maxResults' => 50,
@ -188,4 +188,64 @@ class JiraService
return $workLogs; return $workLogs;
} }
public function getAllUserDoing()
{
$users = $this->getAllUsers();
// $projects = $this->getAllProjects();
$groupedIssues = [];
$users_data = [];
foreach ($users as $user) {
$users_data[$user['displayName']]['user'] = $user;
$users_data[$user['displayName']]['total_spent'] = 0;
$users_data[$user['displayName']]['total_est'] = 0;
$body = [
'expand' => ['names', 'schema'],
'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project'],
'jql' => sprintf(
"assignee = '%s' AND status IN ('to do', 'in progress')",
$user['accountId']
),
'maxResults' => 50,
'startAt' => 0
];
$response = $this->client->post('/rest/api/3/search', [
'body' => json_encode($body)
]);
$issues = json_decode($response->getBody()->getContents(), true);
foreach ($issues['issues'] as $issue) {
$projectName = $issue['fields']['project']['name'];
$username = $issue['fields']['assignee']['displayName'];
if (!isset($groupedIssues[$projectName])) {
$groupedIssues[$projectName] = [];
$groupedIssues[$projectName]['project'] = $issue['fields']['project'];
}
if (!isset($groupedIssues[$projectName]['users'][$username])) {
$groupedIssues[$projectName]['users'][$username] = [];
$groupedIssues[$projectName]['users'][$username]['user'] = $issue['fields']['assignee'];
$groupedIssues[$projectName]['users'][$username]['p_total_spent'] = 0;
$groupedIssues[$projectName]['users'][$username]['p_total_est'] = 0;
}
$groupedIssues[$projectName]['users'][$username]['issues'][] = $issue;
$groupedIssues[$projectName]['users'][$username]['p_total_spent'] = $groupedIssues[$projectName]['users'][$username]['p_total_spent'] + $issue['fields']['timespent'];
$groupedIssues[$projectName]['users'][$username]['p_total_est'] = $groupedIssues[$projectName]['users'][$username]['p_total_est'] + ($issue['fields']['timeoriginalestimate'] ?? 0);
$users_data[$user['displayName']]['total_spent'] = $users_data[$user['displayName']]['total_spent'] + $issue['fields']['timespent'];
$users_data[$user['displayName']]['total_est'] = $users_data[$user['displayName']]['total_est'] + ($issue['fields']['timeoriginalestimate'] ?? 0);
}
}
return ['projects' => $groupedIssues, 'users' => $users_data];
// return $projects;
}
} }

View File

@ -0,0 +1,28 @@
<?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::table('users', function (Blueprint $table) {
$table->string('avatar');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('avatar');
});
}
};

View File

@ -14,6 +14,7 @@ export const exportIssues = API_URL + 'v1/admin/jira/export-issues'
export const getAllProjects = API_URL + 'v1/admin/jira/all-project' export const getAllProjects = API_URL + 'v1/admin/jira/all-project'
export const getAllIssuesByProject = API_URL + 'v1/admin/jira/all-issue-by-project' export const getAllIssuesByProject = API_URL + 'v1/admin/jira/all-issue-by-project'
export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs' export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs'
export const getAllUserDoing = API_URL + 'v1/admin/jira/allocation'
//Timekeeping //Timekeeping
export const getTheTimesheet = API_URL + 'v1/admin/timekeeping' export const getTheTimesheet = API_URL + 'v1/admin/timekeeping'

View File

@ -20,11 +20,13 @@ import {
} from '@mantine/core' } from '@mantine/core'
import { notifications } from '@mantine/notifications' import { notifications } from '@mantine/notifications'
import { import {
IconBinaryTree2,
IconCalendar, IconCalendar,
IconCalendarClock, IconCalendarClock,
IconDevices, IconDevices,
IconLayoutSidebarLeftExpand, IconLayoutSidebarLeftExpand,
IconLayoutSidebarRightExpand, IconLayoutSidebarRightExpand,
IconListCheck,
IconLogout, IconLogout,
// IconMail, // IconMail,
IconMoon, IconMoon,
@ -35,13 +37,12 @@ import {
IconSun, IconSun,
IconTicket, IconTicket,
IconUsersGroup, IconUsersGroup,
IconZoomExclamation, IconZoomExclamation
} from '@tabler/icons-react' } from '@tabler/icons-react'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import PasswordRequirementInput from '../PasswordRequirementInput/PasswordRequirementInput' import PasswordRequirementInput from '../PasswordRequirementInput/PasswordRequirementInput'
import classes from './NavbarSimpleColored.module.css' import classes from './NavbarSimpleColored.module.css'
import { IconListCheck } from '@tabler/icons-react'
const data = [ const data = [
// { link: '/dashboard', label: 'Dashboard', icon: IconHome }, // { link: '/dashboard', label: 'Dashboard', icon: IconHome },
@ -94,6 +95,12 @@ const data = [
icon: IconZoomExclamation, icon: IconZoomExclamation,
group: 'admin', group: 'admin',
}, },
{
link: '/allocation',
label: 'Personnel allocation',
icon: IconBinaryTree2,
group: 'admin',
},
// { link: '/jira', label: 'Jira', icon: IconSubtask }, // { link: '/jira', label: 'Jira', icon: IconSubtask },
// { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush }, // { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
// { link: '/general-setting', label: 'General Setting', icon: IconSettings }, // { link: '/general-setting', label: 'General Setting', icon: IconSettings },
@ -400,7 +407,7 @@ const Navbar = ({
</span> </span>
</a> </a>
<div className={classes.footer}></div> <div className={classes.footer}></div>
<a href="#" className={classes.link} onClick={handleSetCompactMenu}> <a href="#" className={classes.link} onClick={handleSetCompactMenu} style={{margin:"0 20px", padding:"0 0 10px 0"}}>
{isCompactMenu ? ( {isCompactMenu ? (
<IconLayoutSidebarLeftExpand <IconLayoutSidebarLeftExpand
className={classes.linkIcon} className={classes.linkIcon}

View File

@ -48,7 +48,7 @@
font-size: var(--mantine-font-size-sm); font-size: var(--mantine-font-size-sm);
color: rgb(161, 161, 161); color: rgb(161, 161, 161);
padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm); padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm);
margin: var(--mantine-spacing-xs); margin: 0 var(--mantine-spacing-xs);
border-radius: var(--mantine-radius-sm); border-radius: var(--mantine-radius-sm);
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
import ResetPassword from '@/components/Authentication/ResetPassword' import ResetPassword from '@/components/Authentication/ResetPassword'
import BasePage from '@/components/BasePage/BasePage' import BasePage from '@/components/BasePage/BasePage'
import ProtectedRoute from '@/components/ProtectedRoute/ProtectedRoute' import ProtectedRoute from '@/components/ProtectedRoute/ProtectedRoute'
import Allocation from '@/pages/Allocation/Allocation'
import PageLogin from '@/pages/Auth/Login/Login' import PageLogin from '@/pages/Auth/Login/Login'
import LeaveManagement from '@/pages/LeaveManagement/LeaveManagement' import LeaveManagement from '@/pages/LeaveManagement/LeaveManagement'
import PageNotFound from '@/pages/NotFound/NotFound' import PageNotFound from '@/pages/NotFound/NotFound'
@ -174,6 +175,20 @@ const mainRoutes = [
</ProtectedRoute> </ProtectedRoute>
), ),
}, },
{
path: '/allocation',
element: (
<ProtectedRoute mode="route" permission="admin">
<BasePage
main={
<>
<Allocation />
</>
}
></BasePage>
</ProtectedRoute>
),
},
// { // {
// path: '/packages', // path: '/packages',
// element: ( // element: (