init
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"log_data": [
|
||||
{
|
||||
"log_day": "2023-09-18",
|
||||
"logs": [
|
||||
{
|
||||
"project_name": "Data leakage detection system",
|
||||
"project_key": "DLDS",
|
||||
"task_key_num": "22",
|
||||
"task_name": "fgdfgdf",
|
||||
"avatar_url": null,
|
||||
"created_at": "2024-11-18T08:12:26.232Z",
|
||||
"end_date": "2024-11-19T00:00:00.000Z",
|
||||
"start_date": "2024-11-12T00:00:00.000Z",
|
||||
"task_key": "GHF-2",
|
||||
"time_spent": "24.000000",
|
||||
"time_spent_string": "0m 24s",
|
||||
"user_email": "johndoe1990@gmail.com",
|
||||
"user_name": "John Doe"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"log_day": "2024-11-18",
|
||||
"logs": [
|
||||
{
|
||||
"project_name": "Data leakage detection system",
|
||||
"project_key": "DLDS",
|
||||
"task_key_num": "1",
|
||||
"task_name": "test task",
|
||||
"avatar_url": null,
|
||||
"created_at": "2024-11-18T14:04:20.811Z",
|
||||
"end_date": "2024-11-19T00:00:00.000Z",
|
||||
"start_date": "2024-11-12T00:00:00.000Z",
|
||||
"task_key": "GFG-1",
|
||||
"time_spent": "13.000000",
|
||||
"time_spent_string": "0m 13s",
|
||||
"user_email": "johndoe1990@gmail.com",
|
||||
"user_name": "John Doe"
|
||||
},
|
||||
{
|
||||
"project_name": "gfgdf",
|
||||
"project_key": "GFG",
|
||||
"task_key_num": "4",
|
||||
"task_name": "fdfs",
|
||||
"avatar_url": null,
|
||||
"created_at": "2024-11-18T08:10:20.473Z",
|
||||
"end_date": "2024-11-19T00:00:00.000Z",
|
||||
"start_date": "2024-11-12T00:00:00.000Z",
|
||||
"task_key": "GFG-4",
|
||||
"time_spent": "74.000000",
|
||||
"time_spent_string": "1m 14s",
|
||||
"user_email": "johndoe1990@gmail.com",
|
||||
"user_name": "John Doe"
|
||||
},
|
||||
{
|
||||
"project_name": "gfgdf",
|
||||
"project_key": "GFG",
|
||||
"task_key_num": "1",
|
||||
"task_name": "test task",
|
||||
"avatar_url": null,
|
||||
"created_at": "2024-11-18T08:10:06.902Z",
|
||||
"end_date": "2024-11-19T00:00:00.000Z",
|
||||
"start_date": "2024-11-12T00:00:00.000Z",
|
||||
"task_key": "GFG-1",
|
||||
"time_spent": "86.000000",
|
||||
"time_spent_string": "1m 26s",
|
||||
"user_email": "johndoe1990@gmail.com",
|
||||
"user_name": "John Doe"
|
||||
},
|
||||
{
|
||||
"project_name": "gfgdf",
|
||||
"project_key": "GFG",
|
||||
"task_key_num": "1",
|
||||
"task_name": "test task",
|
||||
"avatar_url": null,
|
||||
"created_at": "2024-11-18T08:09:15.810Z",
|
||||
"end_date": "2024-11-19T00:00:00.000Z",
|
||||
"start_date": "2024-11-12T00:00:00.000Z",
|
||||
"task_key": "GFG-1",
|
||||
"time_spent": "14.000000",
|
||||
"time_spent_string": "0m 14s",
|
||||
"user_email": "johndoe1990@gmail.com",
|
||||
"user_name": "John Doe"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"log_day": "2024-11-19",
|
||||
"logs": [
|
||||
{
|
||||
"project_name": "gfgdf",
|
||||
"project_key": "GFG",
|
||||
"task_key_num": "1",
|
||||
"task_name": "test task",
|
||||
"avatar_url": null,
|
||||
"created_at": "2024-11-19T06:42:56.726Z",
|
||||
"end_date": "2024-11-19T00:00:00.000Z",
|
||||
"start_date": "2024-11-12T00:00:00.000Z",
|
||||
"task_key": "GFG-1",
|
||||
"time_spent": "8.000000",
|
||||
"time_spent_string": "0m 8s",
|
||||
"user_email": "johndoe1990@gmail.com",
|
||||
"user_name": "John Doe"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.time-log-card .ant-card-body {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import { Avatar, Button, Card, Divider, Drawer, Tag, Timeline, Typography } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { toggleTimeLogDrawer } from './timeLogSlice';
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
import jsonData from './ProjectTimeLog.json';
|
||||
import { AvatarNamesMap, durations } from '../../../shared/constants';
|
||||
import './ProjectTimeLogDrawer.css';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import TimeWiseFilter from '@/components/reporting/time-wise-filter';
|
||||
import { IProjectLogsBreakdown, ITimeLogBreakdownReq } from '@/types/reporting/reporting.types';
|
||||
import { reportingTimesheetApiService } from '@/api/reporting/reporting.timesheet.api.service';
|
||||
import { useAuthService } from '@/hooks/useAuth';
|
||||
import logger from '@/utils/errorLogger';
|
||||
|
||||
const ProjectTimeLogDrawer: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation('time-report');
|
||||
const currentSession = useAuthService().getCurrentSession();
|
||||
|
||||
const { selectedLabel, isTimeLogDrawerOpen } = useAppSelector(state => state.timeLogReducer);
|
||||
const {
|
||||
teams,
|
||||
loadingTeams,
|
||||
categories,
|
||||
loadingCategories,
|
||||
projects: filterProjects,
|
||||
loadingProjects,
|
||||
billable,
|
||||
archived,
|
||||
} = useAppSelector(state => state.timeReportsOverviewReducer);
|
||||
const { duration, dateRange } = useAppSelector(state => state.reportingReducer);
|
||||
const [projectTimeLogs, setProjectTimeLogs] = useState<IProjectLogsBreakdown[]>([]);
|
||||
|
||||
// Format date to desired format
|
||||
const formatDate = (date: string) => {
|
||||
const formattedDate = new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
});
|
||||
return formattedDate;
|
||||
};
|
||||
|
||||
const handleDrawerOpen = async () => {
|
||||
if (!selectedLabel?.id) return;
|
||||
try {
|
||||
const body: ITimeLogBreakdownReq = {
|
||||
id: selectedLabel.id,
|
||||
duration: duration ? duration : durations[1].key,
|
||||
date_range: dateRange,
|
||||
time_zone: currentSession?.timezone_name
|
||||
? (currentSession?.timezone_name as string)
|
||||
: (Intl.DateTimeFormat().resolvedOptions().timeZone as string),
|
||||
};
|
||||
const res = await reportingTimesheetApiService.getProjectTimeLogs(body);
|
||||
if (res.done) {
|
||||
setProjectTimeLogs(res.body || []);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error fetching project time logs:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
width={736}
|
||||
open={isTimeLogDrawerOpen}
|
||||
onClose={() => dispatch(toggleTimeLogDrawer())}
|
||||
title={
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
|
||||
<Typography.Title level={5}>{selectedLabel?.name}</Typography.Title>
|
||||
<TimeWiseFilter />
|
||||
</div>
|
||||
}
|
||||
destroyOnClose
|
||||
afterOpenChange={() => {
|
||||
handleDrawerOpen();
|
||||
}}
|
||||
>
|
||||
<div style={{ textAlign: 'right', width: '100%', height: '40px' }}>
|
||||
<Button size="small" icon={<DownloadOutlined />}>
|
||||
{t('exportToExcel')}
|
||||
</Button>
|
||||
</div>
|
||||
{projectTimeLogs.map(logItem => (
|
||||
<div key={logItem.log_day}>
|
||||
<Card
|
||||
className="time-log-card"
|
||||
title={
|
||||
<Typography.Text
|
||||
style={{ fontWeight: 500, fontSize: '16px', overflowWrap: 'break-word' }}
|
||||
>
|
||||
{formatDate(logItem.log_day)}
|
||||
</Typography.Text>
|
||||
}
|
||||
>
|
||||
<Timeline>
|
||||
{logItem.logs.map((log, index) => (
|
||||
<Timeline.Item key={index}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
|
||||
<Avatar
|
||||
style={{
|
||||
backgroundColor: AvatarNamesMap[log.user_name.charAt(0)],
|
||||
width: '26px',
|
||||
height: '26px',
|
||||
}}
|
||||
>
|
||||
{log.user_name.charAt(0).toUpperCase()}
|
||||
</Avatar>
|
||||
<Typography.Text>
|
||||
<b>{log.user_name}</b> {t('logged')} <b>{log.time_spent_string}</b> {t('for')}{' '}
|
||||
<b>{log.task_name}</b> <Tag>{log.task_key}</Tag>
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</Timeline.Item>
|
||||
))}
|
||||
</Timeline>
|
||||
</Card>
|
||||
<Divider />
|
||||
</div>
|
||||
))}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectTimeLogDrawer;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { IRPTTimeProject } from '@/types/reporting/reporting.types';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
interface timeLogState {
|
||||
isTimeLogDrawerOpen: boolean;
|
||||
selectedLabel: IRPTTimeProject | null;
|
||||
}
|
||||
|
||||
const initialState: timeLogState = {
|
||||
isTimeLogDrawerOpen: false,
|
||||
selectedLabel: null,
|
||||
};
|
||||
|
||||
const timeLogSlice = createSlice({
|
||||
name: 'timeLogReducer',
|
||||
initialState,
|
||||
reducers: {
|
||||
toggleTimeLogDrawer: state => {
|
||||
state.isTimeLogDrawerOpen
|
||||
? (state.isTimeLogDrawerOpen = false)
|
||||
: (state.isTimeLogDrawerOpen = true);
|
||||
},
|
||||
setSelectedLabel(state, action) {
|
||||
state.selectedLabel = action.payload;
|
||||
},
|
||||
setLabelAndToggleDrawer(state, action) {
|
||||
state.selectedLabel = action.payload;
|
||||
state.isTimeLogDrawerOpen = true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { toggleTimeLogDrawer, setSelectedLabel, setLabelAndToggleDrawer } = timeLogSlice.actions;
|
||||
export default timeLogSlice.reducer;
|
||||
Reference in New Issue
Block a user