update allocation

This commit is contained in:
JOSEPH LE 2024-09-17 16:31:09 +07:00
parent fe75a08c3d
commit a855787a47
5 changed files with 80 additions and 11 deletions

View File

@ -260,4 +260,14 @@ class JiraController extends Controller
return response()->json(['error' => $e->getMessage()], 500); 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);
}
} }

View File

@ -104,6 +104,7 @@ Route::middleware('api')
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::get('/allocation', [JiraController::class, 'getAllUserDoing'])->middleware('check.permission:admin.staff');
Route::get('/issue/detail', [JiraController::class, 'getDetailIssueById'])->middleware('check.permission:admin.staff');
}); });
Route::group([ Route::group([

View File

@ -238,9 +238,9 @@ class JiraService
$users_data[$user['displayName']]['total_est'] = 0; $users_data[$user['displayName']]['total_est'] = 0;
$body = [ $body = [
'expand' => ['names', 'schema'], 'expand' => ['names', 'schema'],
'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project'], 'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project', 'updated'],
'jql' => sprintf( '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'] $user['accountId']
), ),
'maxResults' => 50, 'maxResults' => 50,
@ -289,4 +289,11 @@ class JiraService
return ['projects' => $groupedIssues, 'users' => $users_data, 'warningList' => $user_warning]; return ['projects' => $groupedIssues, 'users' => $users_data, 'warningList' => $user_warning];
// return $projects; // 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;
}
} }

View File

@ -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 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' export const getAllUserDoing = API_URL + 'v1/admin/jira/allocation'
export const getDetailIssByKey = API_URL + 'v1/admin/jira/issue/detail'
//Timekeeping //Timekeeping
export const getTheTimesheet = API_URL + 'v1/admin/timekeeping' export const getTheTimesheet = API_URL + 'v1/admin/timekeeping'

View File

@ -1,16 +1,20 @@
import { import {
Avatar, Avatar,
Badge,
Box, Box,
Card, Card,
Loader, Loader,
Modal,
Popover, Popover,
Text Text,
Tooltip
} from '@mantine/core' } from '@mantine/core'
import { useDisclosure } from '@mantine/hooks' import { useDisclosure } from '@mantine/hooks'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import classes from './Allocation.module.css' import classes from './Allocation.module.css'
import { get } from '@/rtk/helpers/apiService' import { get } from '@/rtk/helpers/apiService'
import { getAllUserDoing } from '@/api/Admin' import { getAllUserDoing, getDetailIssByKey } from '@/api/Admin'
import moment from 'moment'
interface UserInfo { interface UserInfo {
self: string self: string
accountId: string accountId: string
@ -42,6 +46,7 @@ interface IssueFields {
timeoriginalestimate: number timeoriginalestimate: number
project: ProjectInfo project: ProjectInfo
status: StatusInfo status: StatusInfo
updated: string
} }
interface ProjectInfo { interface ProjectInfo {
@ -71,7 +76,9 @@ interface StatusInfo {
} }
const Allocation = () => { 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<any>({ // const [data, setData] = useState<any>({
// projects: { // projects: {
// IPS_Pro: { // IPS_Pro: {
@ -3281,7 +3288,7 @@ const Allocation = () => {
try { try {
const res = await get(getAllUserDoing) const res = await get(getAllUserDoing)
if (res.status) { if (res.status) {
toggle() setLoading(false)
setData(res.data) setData(res.data)
} }
} catch (error) { } catch (error) {
@ -3289,6 +3296,30 @@ const Allocation = () => {
} }
} }
// console.log(data) // 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(() => { useEffect(() => {
getAll() getAll()
}, []) }, [])
@ -3436,10 +3467,19 @@ const Allocation = () => {
</Box> </Box>
</Popover.Target> </Popover.Target>
<Popover.Dropdown> <Popover.Dropdown>
<Text size="xs">
<Text size="xs" style={{maxHeight:'50vh', overflow:'auto'}}>
{userData.issues?.map((iss: Issue) => { {userData.issues?.map((iss: Issue) => {
const date = new Date(iss.fields.updated)
return ( return (
<Box> <Tooltip label='Click to view history'>
<Box className={ Date.now() - date.getTime() > 172800000*5 ? classes['blinking-background']: ''}
style={{margin:'10px 0', borderRadius:'10px', cursor:'pointer'}}
onClick={async()=>{
setLoading(true)
await getDetailIssueByKey(iss.key)
setLoading(false)}}
>
<Text fw={600}> <Text fw={600}>
<a <a
href={ href={
@ -3464,18 +3504,24 @@ const Allocation = () => {
</Text> </Text>
<Text fw={600} ml={'50px'}> <Text fw={600} ml={'50px'}>
Time spent:{' '} Time spent:{' '}
{iss.fields.timespent / 60 / 60} <Badge size='xs' bg={'orange'}>{iss.fields.timespent / 60 / 60}h</Badge>
</Text> </Text>
<Text fw={600} ml={'50px'}> <Text fw={600} ml={'50px'}>
EST:{' '} EST:{' '}
{iss.fields.timeoriginalestimate / <Badge size='xs'>{iss.fields.timeoriginalestimate /
60 / 60 /
60} 60}h</Badge>
</Text>
<Text fw={600} ml={'50px'}>
Updated:{' '}
<Badge size='xs'>{iss.fields.updated}</Badge>
</Text> </Text>
</Box> </Box>
</Tooltip>
) )
})} })}
</Text> </Text>
</Popover.Dropdown> </Popover.Dropdown>
</Popover> </Popover>
</Box> </Box>
@ -3549,7 +3595,11 @@ const Allocation = () => {
</Box> </Box>
</Box> </Box>
</div> </div>
<Modal title={<b>HISTORY</b>} onClose={()=>setOpened(false) } opened={opened} fullScreen>
<pre>{issDetail}</pre>
</Modal>
</div> </div>
) )
} }