diff --git a/BACKEND/Modules/Admin/app/Http/Controllers/JiraController.php b/BACKEND/Modules/Admin/app/Http/Controllers/JiraController.php index 47621e3..00e67ed 100644 --- a/BACKEND/Modules/Admin/app/Http/Controllers/JiraController.php +++ b/BACKEND/Modules/Admin/app/Http/Controllers/JiraController.php @@ -260,4 +260,14 @@ class JiraController extends Controller return response()->json(['error' => $e->getMessage()], 500); } } + + public function getDetailIssueById(Request $request) + { + $data = $this->jiraService->getDetailIssueByKey($request->issueKey); + + return response()->json([ + 'data' => $data, + 'status' => true + ], 200); + } } diff --git a/BACKEND/Modules/Admin/routes/api.php b/BACKEND/Modules/Admin/routes/api.php index 39e2c99..fda88cf 100755 --- a/BACKEND/Modules/Admin/routes/api.php +++ b/BACKEND/Modules/Admin/routes/api.php @@ -104,6 +104,7 @@ Route::middleware('api') Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']); 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::group([ diff --git a/BACKEND/app/Services/JiraService.php b/BACKEND/app/Services/JiraService.php index 75a2b34..88d14bd 100644 --- a/BACKEND/app/Services/JiraService.php +++ b/BACKEND/app/Services/JiraService.php @@ -238,9 +238,9 @@ class JiraService $users_data[$user['displayName']]['total_est'] = 0; $body = [ 'expand' => ['names', 'schema'], - 'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project'], + 'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project', 'updated'], 'jql' => sprintf( - "assignee = '%s' AND status IN ('to do', 'in progress')", + "assignee = '%s' AND status IN ('to do', 'in progress') ORDER BY updated DESC", $user['accountId'] ), 'maxResults' => 50, @@ -289,4 +289,11 @@ class JiraService return ['projects' => $groupedIssues, 'users' => $users_data, 'warningList' => $user_warning]; // return $projects; } + + public function getDetailIssueByKey($issueKey) + { + $issueResponse = $this->client->get("/rest/api/3/issue/{$issueKey}?expand=changelog"); + $issueDetail = json_decode($issueResponse->getBody()->getContents(), true); + return $issueDetail; + } } diff --git a/FRONTEND/src/api/Admin.ts b/FRONTEND/src/api/Admin.ts index c6eece4..461c625 100755 --- a/FRONTEND/src/api/Admin.ts +++ b/FRONTEND/src/api/Admin.ts @@ -15,6 +15,7 @@ export const getAllProjects = API_URL + 'v1/admin/jira/all-project' export const getAllIssuesByProject = API_URL + 'v1/admin/jira/all-issue-by-project' 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' //Timekeeping export const getTheTimesheet = API_URL + 'v1/admin/timekeeping' diff --git a/FRONTEND/src/pages/Allocation/Allocation.tsx b/FRONTEND/src/pages/Allocation/Allocation.tsx index c275a3b..849f83c 100644 --- a/FRONTEND/src/pages/Allocation/Allocation.tsx +++ b/FRONTEND/src/pages/Allocation/Allocation.tsx @@ -1,16 +1,20 @@ import { Avatar, + Badge, Box, Card, Loader, + Modal, Popover, - Text + Text, + Tooltip } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' import { useEffect, useState } from 'react' import classes from './Allocation.module.css' import { get } from '@/rtk/helpers/apiService' -import { getAllUserDoing } from '@/api/Admin' +import { getAllUserDoing, getDetailIssByKey } from '@/api/Admin' +import moment from 'moment' interface UserInfo { self: string accountId: string @@ -42,6 +46,7 @@ interface IssueFields { timeoriginalestimate: number project: ProjectInfo status: StatusInfo + updated: string } interface ProjectInfo { @@ -71,7 +76,9 @@ interface StatusInfo { } const Allocation = () => { - const [loading, { toggle }] = useDisclosure(true) + const [loading, setLoading] = useState(true) + const [opened, setOpened] = useState(false) + const [issDetail, setIssDetail] = useState("") // const [data, setData] = useState({ // projects: { // IPS_Pro: { @@ -3281,7 +3288,7 @@ const Allocation = () => { try { const res = await get(getAllUserDoing) if (res.status) { - toggle() + setLoading(false) setData(res.data) } } catch (error) { @@ -3289,6 +3296,30 @@ const Allocation = () => { } } // console.log(data) + + const getStateChanges = (changelog:any) => { + return changelog.histories.map((history:any) => { + const author = history.author.displayName; + const created = history.created; + const changes = history.items.map((item:any) => { + return `${item.field} changed from ${item.fromString} ➔ ${item.toString}`; + }).join(', '); + + return changes ? `${author} made changes on ${created}: ${changes}` : null; + }).filter(Boolean).join('\n'); +}; + + const getDetailIssueByKey = async(key:string) =>{ + try { + const res = await get(getDetailIssByKey, {issueKey: key}) + if(res.status){ + setIssDetail(getStateChanges(res?.data?.changelog)) + setOpened(true) + } + } catch (error) { + console.log(error) + } + } useEffect(() => { getAll() }, []) @@ -3436,10 +3467,19 @@ const Allocation = () => { - + + {userData.issues?.map((iss: Issue) => { + const date = new Date(iss.fields.updated) return ( - + + 172800000*5 ? classes['blinking-background']: ''} + style={{margin:'10px 0', borderRadius:'10px', cursor:'pointer'}} + onClick={async()=>{ + setLoading(true) + await getDetailIssueByKey(iss.key) + setLoading(false)}} + > { Time spent:{' '} - {iss.fields.timespent / 60 / 60} + {iss.fields.timespent / 60 / 60}h EST:{' '} - {iss.fields.timeoriginalestimate / + {iss.fields.timeoriginalestimate / 60 / - 60} + 60}h + + + Updated:{' '} + {iss.fields.updated} + ) })} + @@ -3549,7 +3595,11 @@ const Allocation = () => { + HISTORY} onClose={()=>setOpened(false) } opened={opened} fullScreen> +
{issDetail}
+
+ ) }