@@ -33,6 +33,7 @@ | |||
"react-hook-form": "^7.49.2", | |||
"react-i18next": "^13.5.0", | |||
"react-intl": "^6.5.5", | |||
"react-number-format": "^5.3.4", | |||
"react-select": "^5.8.0", | |||
"reactstrap": "^9.2.2", | |||
"styled-components": "^6.1.8", | |||
@@ -8031,6 +8032,18 @@ | |||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", | |||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" | |||
}, | |||
"node_modules/react-number-format": { | |||
"version": "5.3.4", | |||
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.3.4.tgz", | |||
"integrity": "sha512-2hHN5mbLuCDUx19bv0Q8wet67QqYK6xmtLQeY5xx+h7UXiMmRtaCwqko4mMPoKXLc6xAzwRrutg8XbTRlsfjRg==", | |||
"dependencies": { | |||
"prop-types": "^15.7.2" | |||
}, | |||
"peerDependencies": { | |||
"react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", | |||
"react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" | |||
} | |||
}, | |||
"node_modules/react-popper": { | |||
"version": "2.3.0", | |||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", | |||
@@ -34,6 +34,7 @@ | |||
"react-hook-form": "^7.49.2", | |||
"react-i18next": "^13.5.0", | |||
"react-intl": "^6.5.5", | |||
"react-number-format": "^5.3.4", | |||
"react-select": "^5.8.0", | |||
"reactstrap": "^9.2.2", | |||
"styled-components": "^6.1.8", | |||
@@ -1,6 +1,6 @@ | |||
import { Metadata } from "next"; | |||
import { I18nProvider } from "@/i18n"; | |||
import UserWorkspacePage from "@/components/UserWorkspacePage/UserWorkspacePage"; | |||
import UserWorkspacePage from "@/components/UserWorkspacePage"; | |||
export const metadata: Metadata = { | |||
title: "User Workspace", | |||
@@ -1,247 +0,0 @@ | |||
import * as React from "react"; | |||
import { | |||
Card, | |||
CardHeader, | |||
CardContent, | |||
SxProps, | |||
Theme, | |||
Tabs, | |||
Tab, | |||
Box, | |||
Typography, | |||
Grid, | |||
Link, | |||
} from "@mui/material"; | |||
import { DataGrid, GridColDef } from "@mui/x-data-grid"; | |||
import { darken, lighten, styled } from "@mui/material/styles"; | |||
import { ThemeProvider } from "@emotion/react"; | |||
import { TAB_THEME } from "@/theme/colorConst"; | |||
import AllProjectGrid from "../UserWorkspacePage/ProjectGrid"; | |||
interface AssignedProjectGridProps { | |||
Title?: string; | |||
// rows: any[]; | |||
// columns: any[]; | |||
columnWidth?: number; | |||
Style?: boolean; | |||
sx?: SxProps<Theme>; | |||
height?: number; | |||
[key: string]: any; | |||
} | |||
interface TabPanelProps { | |||
children?: React.ReactNode; | |||
index: number; | |||
value: number; | |||
} | |||
function CustomTabPanel(props: TabPanelProps) { | |||
const { children, value, index, ...other } = props; | |||
return ( | |||
<div | |||
role="tabpanel" | |||
hidden={value !== index} | |||
id={`simple-tabpanel-${index}`} | |||
aria-labelledby={`simple-tab-${index}`} | |||
{...other} | |||
> | |||
{value === index && ( | |||
<Box sx={{ p: 3 }}> | |||
<Typography>{children}</Typography> | |||
</Box> | |||
)} | |||
</div> | |||
); | |||
} | |||
function a11yProps(index: number) { | |||
return { | |||
id: `simple-tab-${index}`, | |||
"aria-controls": `simple-tabpanel-${index}`, | |||
}; | |||
} | |||
const AssignedProjectGrid: React.FC<AssignedProjectGridProps> = ({ | |||
Title, | |||
rows, | |||
columns, | |||
columnWidth, | |||
Style = true, | |||
sx, | |||
height, | |||
...props | |||
}) => { | |||
// const modifiedColumns = columns.map((column) => { | |||
// return { | |||
// ...column, | |||
// width: columnWidth ?? 150, | |||
// }; | |||
// }); | |||
// const rowsWithDefaultValues = rows.map((row) => { | |||
// return { ...row }; | |||
// }); | |||
const getBackgroundColor = (color: string, mode: "light" | "dark") => | |||
mode === "dark" ? darken(color, 0.7) : lighten(color, 0.7); | |||
const getHoverBackgroundColor = (color: string, mode: "light" | "dark") => | |||
mode === "dark" ? darken(color, 0.6) : lighten(color, 0.6); | |||
const getSelectedBackgroundColor = (color: string, mode: "light" | "dark") => | |||
mode === "dark" ? darken(color, 0.5) : lighten(color, 0.5); | |||
const getSelectedHoverBackgroundColor = ( | |||
color: string, | |||
mode: "light" | "dark", | |||
) => (mode === "dark" ? darken(color, 0.4) : lighten(color, 0.4)); | |||
const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ | |||
"& .super-app-theme--Open": { | |||
backgroundColor: getBackgroundColor( | |||
theme.palette.info.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getHoverBackgroundColor( | |||
theme.palette.info.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
"&.Mui-selected": { | |||
backgroundColor: getSelectedBackgroundColor( | |||
theme.palette.info.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getSelectedHoverBackgroundColor( | |||
theme.palette.info.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
}, | |||
}, | |||
"& .super-app-theme--finish": { | |||
backgroundColor: getBackgroundColor( | |||
theme.palette.success.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getHoverBackgroundColor( | |||
theme.palette.success.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
"&.Mui-selected": { | |||
backgroundColor: getSelectedBackgroundColor( | |||
theme.palette.success.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getSelectedHoverBackgroundColor( | |||
theme.palette.success.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
}, | |||
}, | |||
"& .super-app-theme--danger": { | |||
backgroundColor: getBackgroundColor( | |||
theme.palette.warning.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getHoverBackgroundColor( | |||
theme.palette.warning.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
"&.Mui-selected": { | |||
backgroundColor: getSelectedBackgroundColor( | |||
theme.palette.warning.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getSelectedHoverBackgroundColor( | |||
theme.palette.warning.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
}, | |||
}, | |||
"& .super-app-theme--warning": { | |||
backgroundColor: getBackgroundColor( | |||
theme.palette.error.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getHoverBackgroundColor( | |||
theme.palette.error.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
"&.Mui-selected": { | |||
backgroundColor: getSelectedBackgroundColor( | |||
theme.palette.error.main, | |||
theme.palette.mode, | |||
), | |||
"&:hover": { | |||
backgroundColor: getSelectedHoverBackgroundColor( | |||
theme.palette.error.main, | |||
theme.palette.mode, | |||
), | |||
}, | |||
}, | |||
}, | |||
})); | |||
const [value, setValue] = React.useState(0); | |||
const handleChange = (event: React.SyntheticEvent, newValue: number) => { | |||
setValue(newValue); | |||
}; | |||
return ( | |||
<div style={{ height: height ?? 400, width: "100%" }}> | |||
<Card style={{ margin: "auto 20px auto 20px" }}> | |||
{Title && <CardHeader title={Title} />} | |||
<CardContent | |||
style={{ | |||
padding: "0px 24px 24px 24px", | |||
display: "flex", | |||
alignItems: "center", | |||
}} | |||
> | |||
<div> | |||
<ThemeProvider theme={TAB_THEME}> | |||
<Box sx={{ borderBottom: 4, borderColor: "divider" }}> | |||
<Tabs | |||
value={value} | |||
onChange={handleChange} | |||
aria-label="Manage assigned project" | |||
> | |||
<Tab label="All Projects" {...a11yProps(0)} /> | |||
<Tab label="On Track" {...a11yProps(1)} /> | |||
<Tab label="Potential Delay" {...a11yProps(2)} /> | |||
</Tabs> | |||
</Box> | |||
{/* <CustomTabPanel value={value} index={0}> | |||
Item {value} | |||
</CustomTabPanel> | |||
<CustomTabPanel value={value} index={1}> | |||
Item {value} | |||
</CustomTabPanel> | |||
<CustomTabPanel value={value} index={2}> | |||
Item {value} | |||
</CustomTabPanel> */} | |||
</ThemeProvider> | |||
</div> | |||
</CardContent> | |||
<AllProjectGrid tab={value} /> | |||
</Card> | |||
</div> | |||
); | |||
}; | |||
export default AssignedProjectGrid; |
@@ -1 +0,0 @@ | |||
export { default } from "./AssignedProjectGrid"; |
@@ -9,6 +9,7 @@ import { useTranslation } from "react-i18next"; | |||
const pathToLabelMap: { [path: string]: string } = { | |||
"": "Overview", | |||
"/home": "User Workspace", | |||
"/projects": "Projects", | |||
"/projects/create": "Create Project", | |||
"/tasks": "Task Template", | |||
@@ -26,7 +27,7 @@ const pathToLabelMap: { [path: string]: string } = { | |||
const Breadcrumb = () => { | |||
const pathname = usePathname(); | |||
const segments = pathname.split("/"); | |||
// const { t } = useTranslation("customer"); | |||
return ( | |||
@@ -1,25 +1,9 @@ | |||
"use client"; | |||
// import { testing } from "@/app/api/timesheets"; | |||
import Grid from "@mui/material/Grid"; | |||
import Paper from "@mui/material/Paper"; | |||
import { useState } from "react"; | |||
import { useTranslation } from "react-i18next"; | |||
import AssignedProjectGrid from "../AssignedProjectGrid/AssignedProjectGrid"; | |||
import PageTitle from "../PageTitle/PageTitle"; | |||
import { Suspense } from "react"; | |||
import Button from "@mui/material/Button"; | |||
import Stack from "@mui/material/Stack"; | |||
import { Add } from "@mui/icons-material"; | |||
import Link from "next/link"; | |||
import { t } from "i18next"; | |||
import { Card, Modal, Typography } from "@mui/material"; | |||
import CustomModal from "../CustomModal/CustomModal"; | |||
import { PROJECT_MODAL_STYLE } from "@/theme/colorConst"; | |||
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid"; | |||
import { DataGrid } from "@mui/x-data-grid"; | |||
import { Card, Modal } from "@mui/material"; | |||
import TimesheetInputGrid from "./LeaveInputGrid"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
// import { fetchLeaves } from "@/app/api/leave"; | |||
@@ -1,25 +1,9 @@ | |||
"use client"; | |||
// import { testing } from "@/app/api/timesheets"; | |||
import Grid from "@mui/material/Grid"; | |||
import Paper from "@mui/material/Paper"; | |||
import { useState } from "react"; | |||
import { useTranslation } from "react-i18next"; | |||
import AssignedProjectGrid from "../AssignedProjectGrid/AssignedProjectGrid"; | |||
import PageTitle from "../PageTitle/PageTitle"; | |||
import { Suspense } from "react"; | |||
import Button from "@mui/material/Button"; | |||
import Stack from "@mui/material/Stack"; | |||
import { Add } from "@mui/icons-material"; | |||
import Link from "next/link"; | |||
import { t } from "i18next"; | |||
import { Card, Modal, Typography } from "@mui/material"; | |||
import CustomModal from "../CustomModal/CustomModal"; | |||
import { PROJECT_MODAL_STYLE } from "@/theme/colorConst"; | |||
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid"; | |||
import { DataGrid } from "@mui/x-data-grid"; | |||
import { Card, Modal } from "@mui/material"; | |||
import TimesheetInputGrid from "./TimesheetInputGrid"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
// import { fetchTimesheets } from "@/app/api/timesheets"; | |||
@@ -0,0 +1,117 @@ | |||
import React, { useEffect, useMemo } from "react"; | |||
import { | |||
Card, | |||
CardContent, | |||
FormControl, | |||
Grid, | |||
IconButton, | |||
InputAdornment, | |||
InputLabel, | |||
MenuItem, | |||
Select, | |||
SelectChangeEvent, | |||
Stack, | |||
TextField, | |||
Typography, | |||
} from "@mui/material"; | |||
import { useTranslation } from "react-i18next"; | |||
import { Clear, Search } from "@mui/icons-material"; | |||
import ProjectGrid from "./ProjectGrid"; | |||
import { Props as UserWorkspaceProps } from "./UserWorkspacePage"; | |||
import uniq from "lodash/uniq"; | |||
const AssignedProjects: React.FC<UserWorkspaceProps> = ({ allProjects }) => { | |||
const { t } = useTranslation("home"); | |||
// Projects | |||
const [filteredProjects, setFilterProjects] = React.useState(allProjects); | |||
// Query related | |||
const [query, setQuery] = React.useState(""); | |||
const onQueryInputChange = React.useCallback< | |||
React.ChangeEventHandler<HTMLInputElement> | |||
>((e) => { | |||
setQuery(e.target.value); | |||
}, []); | |||
const clearQueryInput = React.useCallback(() => { | |||
setQuery(""); | |||
}, []); | |||
// Filter | |||
const allStatuses = useMemo(() => { | |||
return uniq([ | |||
"All", | |||
...allProjects.map((project) => project.projectStatus), | |||
]); | |||
}, [allProjects]); | |||
const [statusFilter, setStatusFilter] = React.useState("All"); | |||
const onStatusChange = React.useCallback((e: SelectChangeEvent) => { | |||
setStatusFilter(e.target.value); | |||
}, []); | |||
useEffect(() => { | |||
setFilterProjects( | |||
allProjects.filter( | |||
(p) => | |||
(p.code.toLowerCase().includes(query.toLowerCase()) || | |||
p.name.toLowerCase().includes(query.toLowerCase())) && | |||
(p.projectStatus === statusFilter || statusFilter === "All"), | |||
), | |||
); | |||
}, [allProjects, query, statusFilter]); | |||
return ( | |||
<> | |||
<Card> | |||
<CardContent> | |||
<Stack gap={2}> | |||
<Typography variant="overline" display="block"> | |||
{t("Assigned Projects")} | |||
</Typography> | |||
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
<Grid item xs={6} display="flex" alignItems="center"> | |||
<Search sx={{ marginInlineEnd: 1 }} /> | |||
<TextField | |||
variant="standard" | |||
fullWidth | |||
onChange={onQueryInputChange} | |||
value={query} | |||
placeholder={t("Search projects by name or code")} | |||
InputProps={{ | |||
endAdornment: query && ( | |||
<InputAdornment position="end"> | |||
<IconButton onClick={clearQueryInput}> | |||
<Clear /> | |||
</IconButton> | |||
</InputAdornment> | |||
), | |||
}} | |||
/> | |||
</Grid> | |||
<Grid item xs={3}> | |||
<FormControl fullWidth> | |||
<InputLabel size="small">{t("Project Status")}</InputLabel> | |||
<Select | |||
label={t("Project Status")} | |||
size="small" | |||
value={statusFilter} | |||
onChange={onStatusChange} | |||
> | |||
{allStatuses.map((option, index) => ( | |||
<MenuItem key={`${option}-${index}`} value={option}> | |||
{option} | |||
</MenuItem> | |||
))} | |||
</Select> | |||
</FormControl> | |||
</Grid> | |||
</Grid> | |||
</Stack> | |||
</CardContent> | |||
</Card> | |||
<ProjectGrid projects={filteredProjects} /> | |||
</> | |||
); | |||
}; | |||
export default AssignedProjects; |
@@ -1,99 +1,111 @@ | |||
"use client"; | |||
import * as React from "react"; | |||
import Grid from "@mui/material/Grid"; | |||
import { useEffect } from "react"; | |||
import { Card, CardContent, CardHeader } from "@mui/material"; | |||
import CustomCardGrid from "../CustomCardGrid/CustomCardGrid"; | |||
import "../../app/global.css"; | |||
import { PROJECT_CARD_STYLE } from "@/theme/colorConst"; | |||
import React from "react"; | |||
import { ProjectHours } from "./UserWorkspaceWrapper"; | |||
import { Box, Card, CardContent, Chip, Grid, Typography } from "@mui/material"; | |||
import { useTranslation } from "react-i18next"; | |||
import { manhourFormatter } from "@/app/utils/formatUtil"; | |||
interface ProjectGridProps { | |||
tab: number; | |||
interface Props { | |||
projects: ProjectHours[]; | |||
} | |||
const cards = [ | |||
{ | |||
code: "M1001 (C)", | |||
name: "Consultancy Project A", | |||
hr_spent: 12.75, | |||
hr_spent_normal: 0.0, | |||
hr_alloc: 150.0, | |||
hr_alloc_normal: 30.0, | |||
}, | |||
{ | |||
code: "M1301 (C)", | |||
name: "Consultancy Project AAA", | |||
hr_spent: 4.25, | |||
hr_spent_normal: 0.25, | |||
hr_alloc: 30.0, | |||
hr_alloc_normal: 0.0, | |||
}, | |||
{ | |||
code: "M1354 (C)", | |||
name: "Consultancy Project BBB", | |||
hr_spent: 57.0, | |||
hr_spent_normal: 6.5, | |||
hr_alloc: 100.0, | |||
hr_alloc_normal: 20.0, | |||
}, | |||
{ | |||
code: "M1973 (C)", | |||
name: "Construction Project CCC", | |||
hr_spent: 12.75, | |||
hr_spent_normal: 0.0, | |||
hr_alloc: 150.0, | |||
hr_alloc_normal: 30.0, | |||
}, | |||
{ | |||
code: "M2014 (T)", | |||
name: "Consultancy Project DDD", | |||
hr_spent: 1.0, | |||
hr_spent_normal: 0.0, | |||
hr_alloc: 10.0, | |||
hr_alloc_normal: 0.0, | |||
}, | |||
]; | |||
const ProjectGrid: React.FC<ProjectGridProps> = (props) => { | |||
const [items, setItems] = React.useState<typeof cards>([]); | |||
useEffect(() => { | |||
if (props.tab == 0) { | |||
setItems(cards); | |||
} else { | |||
const filteredItems = cards; //cards.filter(item => (item.track == props.tab)); | |||
setItems(filteredItems); | |||
} | |||
}, [props.tab]); | |||
const cardLayout = (item: Record<string, string>) => { | |||
return ( | |||
<Card style={PROJECT_CARD_STYLE}> | |||
<CardHeader | |||
style={{ backgroundColor: "pink" }} | |||
title={item.code + "\u000A" + item.name} | |||
/> | |||
<CardContent> | |||
<p>Hours Spent: {item.hr_spent}</p> | |||
<p>Normal (Others): {item.hr_spent_normal}</p> | |||
<p>Hours Allocated: {item.hr_alloc}</p> | |||
<p>Normal (Others): {item.hr_alloc_normal}</p> | |||
</CardContent> | |||
</Card> | |||
); | |||
}; | |||
// Apply the preset style to the cards in child, if not specified // | |||
const ProjectGrid: React.FC<Props> = ({ projects }) => { | |||
const { t } = useTranslation("home"); | |||
return ( | |||
<Grid container md={12}> | |||
{/* <CustomSearchForm applySearch={applySearch} fields={InputFields}/> */} | |||
{/* item count = {items?.length??"idk"} , track/tab = {props.tab} */} | |||
<CustomCardGrid | |||
Title={props.tab.toString()} | |||
items={items} | |||
cardStyle={cardLayout} | |||
/> | |||
{/* <CustomCardGrid Title={props.tab.toString()} rows={rows} columns={columns} columnWidth={200} items={items}/> */} | |||
</Grid> | |||
<Box> | |||
<Grid container columns={{ xs: 4, sm: 8, md: 12, lg: 16 }} spacing={2}> | |||
{projects.map((project, idx) => ( | |||
<Grid key={`${project.code}${idx}`} item xs={4}> | |||
<Card> | |||
<CardContent> | |||
<Box | |||
sx={{ | |||
display: "flex", | |||
justifyContent: "flex-end", | |||
marginBlockEnd: 1, | |||
}} | |||
> | |||
<Chip | |||
size="small" | |||
label={project.projectStatus} | |||
color={ | |||
project.projectStatus === "On Track" | |||
? "success" | |||
: "warning" | |||
} | |||
/> | |||
</Box> | |||
<Typography variant="overline">{project.code}</Typography> | |||
<Typography | |||
variant="h6" | |||
sx={{ | |||
overflow: "hidden", | |||
textOverflow: "ellipsis", | |||
whiteSpace: "nowrap", | |||
marginBlockEnd: 3, | |||
}} | |||
> | |||
{project.name} | |||
</Typography> | |||
{/* Hours Spent */} | |||
<Typography variant="subtitle2">{t("Hours Spent:")}</Typography> | |||
<Box | |||
sx={{ | |||
display: "flex", | |||
justifyContent: "space-between", | |||
alignItems: "baseline", | |||
}} | |||
> | |||
<Typography variant="caption">{t("Normal")}</Typography> | |||
<Typography> | |||
{manhourFormatter.format(project.hoursSpent)} | |||
</Typography> | |||
</Box> | |||
<Box | |||
sx={{ | |||
display: "flex", | |||
justifyContent: "space-between", | |||
alignItems: "baseline", | |||
}} | |||
> | |||
<Typography variant="caption">{t("(Others)")}</Typography> | |||
<Typography>{`(${manhourFormatter.format( | |||
project.hoursSpentOther, | |||
)})`}</Typography> | |||
</Box> | |||
{/* Hours Allocated */} | |||
<Typography variant="subtitle2" sx={{ marginBlockStart: 2 }}> | |||
{t("Hours Allocated:")} | |||
</Typography> | |||
<Box | |||
sx={{ | |||
display: "flex", | |||
justifyContent: "space-between", | |||
alignItems: "baseline", | |||
}} | |||
> | |||
<Typography variant="caption">{t("Normal")}</Typography> | |||
<Typography> | |||
{manhourFormatter.format(project.hoursAllocated)} | |||
</Typography> | |||
</Box> | |||
<Box | |||
sx={{ | |||
display: "flex", | |||
justifyContent: "space-between", | |||
alignItems: "baseline", | |||
}} | |||
> | |||
<Typography variant="caption">{t("(Others)")}</Typography> | |||
<Typography>{`(${manhourFormatter.format( | |||
project.hoursAllocatedOther, | |||
)})`}</Typography> | |||
</Box> | |||
</CardContent> | |||
</Card> | |||
</Grid> | |||
))} | |||
</Grid> | |||
</Box> | |||
); | |||
}; | |||
@@ -1,80 +1,79 @@ | |||
"use client"; | |||
import Grid from "@mui/material/Grid"; | |||
import Paper from "@mui/material/Paper"; | |||
import { useState } from "react"; | |||
import { useCallback, useState } from "react"; | |||
import { useTranslation } from "react-i18next"; | |||
import AssignedProjectGrid from "../AssignedProjectGrid/AssignedProjectGrid"; | |||
import PageTitle from "../PageTitle/PageTitle"; | |||
import { Suspense } from "react"; | |||
import Button from "@mui/material/Button"; | |||
import Stack from "@mui/material/Stack"; | |||
import { Add } from "@mui/icons-material"; | |||
import Link from "next/link"; | |||
import { t } from "i18next"; | |||
import { Modal } from "@mui/material"; | |||
import CustomModal from "../CustomModal/CustomModal"; | |||
import { Typography } from "@mui/material"; | |||
import EnterTimesheetModal from "../EnterTimesheet/EnterTimesheetModal"; | |||
import EnterLeaveModal from "../EnterLeave/EnterLeaveModal"; | |||
import ButtonGroup from "@mui/material/ButtonGroup"; | |||
import AssignedProjects from "./AssignedProjects"; | |||
import { ProjectHours } from "./UserWorkspaceWrapper"; | |||
export interface Props { | |||
allProjects: ProjectHours[]; | |||
} | |||
const UserWorkspacePage: React.FC = () => { | |||
const UserWorkspacePage: React.FC<Props> = ({ allProjects }) => { | |||
const [isTimeheetModalVisible, setTimeheetModalVisible] = useState(false); | |||
const [isLeaveModalVisible, setLeaveModalVisible] = useState(false); | |||
const { t } = useTranslation("home"); | |||
const handleAddTimesheetButtonClick = () => { | |||
const handleAddTimesheetButtonClick = useCallback(() => { | |||
setTimeheetModalVisible(true); | |||
}; | |||
}, []); | |||
const handleCloseTimesheetModal = () => { | |||
const handleCloseTimesheetModal = useCallback(() => { | |||
setTimeheetModalVisible(false); | |||
}; | |||
}, []); | |||
const handleAddLeaveButtonClick = () => { | |||
const handleAddLeaveButtonClick = useCallback(() => { | |||
setLeaveModalVisible(true); | |||
}; | |||
}, []); | |||
const handleCloseLeaveModal = () => { | |||
const handleCloseLeaveModal = useCallback(() => { | |||
setLeaveModalVisible(false); | |||
}; | |||
}, []); | |||
return ( | |||
<Grid container height="100vh"> | |||
<Grid item sm> | |||
<PageTitle BigTitle={"User Workspace"} /> | |||
<div> | |||
<Stack direction="row" justifyContent="right" flexWrap="wrap"> | |||
<Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
onClick={handleAddTimesheetButtonClick} | |||
sx={{ marginRight: "2rem" }} | |||
> | |||
Enter Timesheet | |||
<> | |||
<Stack | |||
direction="row" | |||
justifyContent="space-between" | |||
flexWrap="wrap" | |||
rowGap={2} | |||
> | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("User Workspace")} | |||
</Typography> | |||
<Stack | |||
direction="row" | |||
justifyContent="right" | |||
flexWrap="wrap" | |||
spacing={2} | |||
> | |||
<ButtonGroup variant="contained"> | |||
<Button startIcon={<Add />} onClick={handleAddTimesheetButtonClick}> | |||
{t("Enter Time")} | |||
</Button> | |||
<Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
sx={{ marginRight: "2rem" }} | |||
// LinkComponent={Link} | |||
// href="/projects/create" | |||
onClick={handleAddLeaveButtonClick} | |||
> | |||
Record Leave | |||
<Button startIcon={<Add />} onClick={handleAddLeaveButtonClick}> | |||
{t("Record Leave")} | |||
</Button> | |||
</Stack> | |||
<Suspense> {/*fallback={<ProjectSearch.Loading />}>*/}</Suspense> | |||
</div> | |||
<EnterTimesheetModal | |||
isOpen={isTimeheetModalVisible} | |||
onClose={handleCloseTimesheetModal} | |||
/> | |||
<EnterLeaveModal | |||
isOpen={isLeaveModalVisible} | |||
onClose={handleCloseLeaveModal} | |||
/> | |||
<AssignedProjectGrid Title="Assigned Project" /> | |||
</Grid> | |||
</Grid> | |||
</ButtonGroup> | |||
</Stack> | |||
</Stack> | |||
<EnterTimesheetModal | |||
isOpen={isTimeheetModalVisible} | |||
onClose={handleCloseTimesheetModal} | |||
/> | |||
<EnterLeaveModal | |||
isOpen={isLeaveModalVisible} | |||
onClose={handleCloseLeaveModal} | |||
/> | |||
<AssignedProjects allProjects={allProjects} /> | |||
</> | |||
); | |||
}; | |||
@@ -0,0 +1,65 @@ | |||
import UserWorkspacePage from "./UserWorkspacePage"; | |||
export interface ProjectHours { | |||
code: string; | |||
name: string; | |||
hoursSpent: number; | |||
hoursSpentOther: number; | |||
hoursAllocated: number; | |||
hoursAllocatedOther: number; | |||
projectStatus: "On Track" | "Potential Delay"; | |||
} | |||
const mockProjectCards: ProjectHours[] = [ | |||
{ | |||
code: "M1001 (C)", | |||
name: "Consultancy Project A", | |||
hoursSpent: 12.75, | |||
hoursSpentOther: 0.0, | |||
hoursAllocated: 150.0, | |||
hoursAllocatedOther: 30.0, | |||
projectStatus: "On Track", | |||
}, | |||
{ | |||
code: "M1301 (C)", | |||
name: "Consultancy Project AAA", | |||
hoursSpent: 4.25, | |||
hoursSpentOther: 0.25, | |||
hoursAllocated: 30.0, | |||
hoursAllocatedOther: 0.0, | |||
projectStatus: "On Track", | |||
}, | |||
{ | |||
code: "M1354 (C)", | |||
name: "Consultancy Project BBB", | |||
hoursSpent: 57.0, | |||
hoursSpentOther: 6.5, | |||
hoursAllocated: 100.0, | |||
hoursAllocatedOther: 20.0, | |||
projectStatus: "On Track", | |||
}, | |||
{ | |||
code: "M1973 (C)", | |||
name: "Construction Project CCC", | |||
hoursSpent: 12.75, | |||
hoursSpentOther: 0.0, | |||
hoursAllocated: 150.0, | |||
hoursAllocatedOther: 30.0, | |||
projectStatus: "Potential Delay", | |||
}, | |||
{ | |||
code: "M2014 (T)", | |||
name: "Consultancy Project DDD", | |||
hoursSpent: 1.0, | |||
hoursSpentOther: 0.0, | |||
hoursAllocated: 10.0, | |||
hoursAllocatedOther: 0.0, | |||
projectStatus: "Potential Delay", | |||
}, | |||
]; | |||
const UserWorkspaceWrapper: React.FC = () => { | |||
return <UserWorkspacePage allProjects={mockProjectCards} />; | |||
}; | |||
export default UserWorkspaceWrapper; |
@@ -1 +1 @@ | |||
export { default } from "./UserWorkspacePage"; | |||
export { default } from "./UserWorkspaceWrapper"; |