Pārlūkot izejas kodu

update staff reimbursement

update create claim
minor update on timesheet input
tags/Baseline_30082024_FRONTEND_UAT
kelvinsuen pirms 1 gada
vecāks
revīzija
f9f7af69d5
19 mainītis faili ar 891 papildinājumiem un 117 dzēšanām
  1. +0
    -0
      src/app/(main)/staffReimbursement/ClaimApproval/page.tsx
  2. +11
    -0
      src/app/(main)/staffReimbursement/ClaimSummary/page.tsx
  3. +21
    -0
      src/app/(main)/staffReimbursement/create/page.tsx
  4. +47
    -0
      src/app/(main)/staffReimbursement/page.tsx
  5. +50
    -0
      src/app/api/claims/index.ts
  6. +91
    -0
      src/components/ClaimSearch/ClaimSearch.tsx
  7. +40
    -0
      src/components/ClaimSearch/ClaimSearchLoading.tsx
  8. +18
    -0
      src/components/ClaimSearch/ClaimSearchWrapper.tsx
  9. +1
    -0
      src/components/ClaimSearch/index.ts
  10. +79
    -0
      src/components/CreateClaim/ClaimDetails.tsx
  11. +406
    -0
      src/components/CreateClaim/ClaimInputGrid.tsx
  12. +48
    -0
      src/components/CreateClaim/CreateClaim.tsx
  13. +1
    -0
      src/components/CreateClaim/index.ts
  14. +5
    -5
      src/components/EnterTimesheet/EnterTimesheetModal.tsx
  15. +0
    -62
      src/components/EnterTimesheet/ProjectGrid.tsx
  16. +56
    -48
      src/components/EnterTimesheet/TimesheetInputGrid.tsx
  17. +4
    -1
      src/components/NavigationContent/NavigationContent.tsx
  18. +1
    -1
      src/components/UserWorkspacePage/ProjectGrid.tsx
  19. +12
    -0
      src/theme/colorConst.js

src/app/(main)/claim/page.tsx → src/app/(main)/staffReimbursement/ClaimApproval/page.tsx Parādīt failu


+ 11
- 0
src/app/(main)/staffReimbursement/ClaimSummary/page.tsx Parādīt failu

@@ -0,0 +1,11 @@
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Claim",
};

const Claim: React.FC = async () => {
return "Claim";
};

export default Claim;

+ 21
- 0
src/app/(main)/staffReimbursement/create/page.tsx Parādīt failu

@@ -0,0 +1,21 @@
import CreateClaim from "@/components/CreateClaim";
import { getServerI18n } from "@/i18n";
import Typography from "@mui/material/Typography";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Create Claim",
};

const CreateClaims: React.FC = async () => {
const { t } = await getServerI18n("claims");

return (
<>
<Typography variant="h4">{t("Create Claim")}</Typography>
<CreateClaim />
</>
);
};

export default CreateClaims;

+ 47
- 0
src/app/(main)/staffReimbursement/page.tsx Parādīt failu

@@ -0,0 +1,47 @@
import { preloadClaims } from "@/app/api/claims";
import ClaimSearch from "@/components/ClaimSearch";
import { getServerI18n } from "@/i18n";
import Add from "@mui/icons-material/Add";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { Metadata } from "next";
import Link from "next/link";
import { Suspense } from "react";

export const metadata: Metadata = {
title: "Claims",
};

const StaffReimbursement: React.FC = async () => {
const { t } = await getServerI18n("claims");
preloadClaims();

return (
<>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Typography variant="h4" marginInlineEnd={2}>
{t("Staff Reimbursement")}
</Typography>
<Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/staffReimbursement/create"
>
{t("Create Claim")}
</Button>
</Stack>
<Suspense fallback={<ClaimSearch.Loading />}>
<ClaimSearch />
</Suspense>
</>
);
};

export default StaffReimbursement;

+ 50
- 0
src/app/api/claims/index.ts Parādīt failu

@@ -0,0 +1,50 @@
import { cache } from "react";
import "server-only";

export interface ClaimResult {
id: number;
created: string;
name: string;
cost: number;
type: "Expense" | "Petty Cash";
status: "Not Submitted" | "Waiting for Approval" | "Approved" | "Rejected";
remarks: string;
}

export const preloadClaims = () => {
fetchClaims();
};

export const fetchClaims = cache(async () => {
return mockClaims;
});

const mockClaims: ClaimResult[] = [
{
id: 1,
created: "2023-11-22",
name: "Consultancy Project A",
cost: 121.00,
type: "Expense",
status: "Not Submitted",
remarks: "",
},
{
id: 2,
created: "2023-11-30",
name: "Consultancy Project A",
cost: 4300.00,
type: "Expense",
status: "Waiting for Approval",
remarks: "",
},
{
id: 3,
created: "2023-12-12",
name: "Construction Project C",
cost: 3675.00,
type: "Petty Cash",
status: "Rejected",
remarks: "Duplicate Claim Form",
},
];

+ 91
- 0
src/components/ClaimSearch/ClaimSearch.tsx Parādīt failu

@@ -0,0 +1,91 @@
"use client";

import { ClaimResult } from "@/app/api/claims";
import React, { useCallback, useMemo, useState } from "react";
import SearchBox, { Criterion } from "../SearchBox/index";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults/index";
import EditNote from "@mui/icons-material/EditNote";

interface Props {
claims: ClaimResult[];
}

type SearchQuery = Partial<Omit<ClaimResult, "id">>;
type SearchParamNames = keyof SearchQuery;

const ClaimSearch: React.FC<Props> = ({ claims }) => {
const { t } = useTranslation("claims");

// If claim searching is done on the server-side, then no need for this.
const [filteredClaims, setFilteredClaims] = useState(claims);

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Creation Date"), paramName: "created", type: "dateRange" },
{ label: t("Related Project Name"), paramName: "name", type: "text" },
{
label: t("Cost (HKD)"),
paramName: "cost",
type: "text",
},
{
label: t("Expense Type"),
paramName: "type",
type: "select",
options: ["Expense", "Petty Cash"],
},
{
label: t("Status"),
paramName: "status",
type: "select",
options: ["Not Submitted", "Waiting for Approval", "Approved", "Rejected"]
},
{
label: t("Remarks"),
paramName: "remarks",
type: "text",
},
],
[t],
);

const onClaimClick = useCallback((claim: ClaimResult) => {
console.log(claim);
}, []);

const columns = useMemo<Column<ClaimResult>[]>(
() => [
{
name: "action",
label: t("Actions"),
onClick: onClaimClick,
buttonIcon: <EditNote />,
},
{ name: "created", label: t("Creation Date") },
{ name: "name", label: t("Related Project Name") },
{ name: "cost", label: t("Cost (HKD)") },
{ name: "type", label: t("Expense Type") },
{ name: "status", label: t("Status") },
{ name: "remarks", label: t("Remarks") },
],
[t, onClaimClick],
);

return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
console.log(query);
}}
/>
<SearchResults<ClaimResult>
items={filteredClaims}
columns={columns}
/>
</>
);
};

export default ClaimSearch;

+ 40
- 0
src/components/ClaimSearch/ClaimSearchLoading.tsx Parādīt failu

@@ -0,0 +1,40 @@
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Skeleton from "@mui/material/Skeleton";
import Stack from "@mui/material/Stack";
import React from "react";

// Can make this nicer
export const ClaimSearchLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default ClaimSearchLoading;

+ 18
- 0
src/components/ClaimSearch/ClaimSearchWrapper.tsx Parādīt failu

@@ -0,0 +1,18 @@
import { fetchClaims } from "@/app/api/claims";
import React from "react";
import ClaimSearch from "./ClaimSearch";
import ClaimSearchLoading from "./ClaimSearchLoading";

interface SubComponents {
Loading: typeof ClaimSearchLoading;
}

const ClaimSearchWrapper: React.FC & SubComponents = async () => {
const claims = await fetchClaims();

return <ClaimSearch claims={claims} />;
};

ClaimSearchWrapper.Loading = ClaimSearchLoading;

export default ClaimSearchWrapper;

+ 1
- 0
src/components/ClaimSearch/index.ts Parādīt failu

@@ -0,0 +1 @@
export { default } from "./ClaimSearchWrapper";

+ 79
- 0
src/components/CreateClaim/ClaimDetails.tsx Parādīt failu

@@ -0,0 +1,79 @@
"use client";

import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import FormControl from "@mui/material/FormControl";
import Grid from "@mui/material/Grid";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import Select from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import CardActions from "@mui/material/CardActions";
import RestartAlt from "@mui/icons-material/RestartAlt";
import Button from "@mui/material/Button";
import ClaimInputGrid from "./ClaimInputGrid";

const ClaimDetails: React.FC = () => {
const { t } = useTranslation();

return (
<Card>
<CardContent component={Stack} spacing={4}>
<Box>
{/* <Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Related Project")}
</Typography> */}
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Related Project")}</InputLabel>
<Select
label={t("Project Category")}
>
<MenuItem value={"M1001"}>
{t("M1001")}
</MenuItem>
<MenuItem value={"M1301"}>
{t("M1301")}
</MenuItem>
<MenuItem value={"M1354"}>
{t("M1354")}
</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Expense Type")}</InputLabel>
<Select label={t("Team Lead")}>
<MenuItem value={"Petty Cash"}>
{"Petty Cash"}
</MenuItem>
<MenuItem value={"Expense"}>
{"Expense"}
</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
</Box>
<Card>
<ClaimInputGrid/>
</Card>

{/* <CardActions sx={{ justifyContent: "flex-end" }}>
<Button variant="text" startIcon={<RestartAlt />}>
{t("Reset")}
</Button>
</CardActions> */}
</CardContent>
</Card>
);
};

export default ClaimDetails;

+ 406
- 0
src/components/CreateClaim/ClaimInputGrid.tsx Parādīt failu

@@ -0,0 +1,406 @@
"use client";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import PageTitle from "../PageTitle/PageTitle";
import { Suspense } from "react";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Link from "next/link";
import { t } from 'i18next';
import { Box, Container, Modal, Select, SelectChangeEvent, Typography } from "@mui/material";
import { Close } from '@mui/icons-material';
import AddIcon from '@mui/icons-material/Add';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/DeleteOutlined';
import SaveIcon from '@mui/icons-material/Save';
import CancelIcon from '@mui/icons-material/Close';
import AddPhotoAlternateOutlinedIcon from '@mui/icons-material/AddPhotoAlternateOutlined';
import ImageNotSupportedOutlinedIcon from '@mui/icons-material/ImageNotSupportedOutlined';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Swal from "sweetalert2";
import { msg } from "../Swal/CustomAlerts";
import React from "react";
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import {
GridRowsProp,
GridRowModesModel,
GridRowModes,
DataGrid,
GridColDef,
GridToolbarContainer,
GridFooterContainer,
GridActionsCellItem,
GridEventListener,
GridRowId,
GridRowModel,
GridRowEditStopReasons,
GridEditInputCell,
GridValueSetterParams,
} from '@mui/x-data-grid';
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs";
import { Props } from "react-intl/src/components/relative";
import palette from "@/theme/devias-material-kit/palette";

const weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

interface BottomBarProps {
getCostTotal: () => number;
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
setRowModesModel: (
newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
) => void;
}

interface EditToolbarProps {
// setDay: (newDay : dayjs.Dayjs) => void;
setDay: (newDay: (oldDay: dayjs.Dayjs) => dayjs.Dayjs) => void;
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
setRowModesModel: (
newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
) => void;
}

interface EditFooterProps {
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
setRowModesModel: (
newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
) => void;
}

const BottomBar = (props: BottomBarProps) => {
const { setRows, setRowModesModel, getCostTotal } = props;
// const getCostTotal = props.getCostTotal;
const [newId, setNewId] = useState(-1);
const [invalidDays, setInvalidDays] = useState(0);

const handleAddClick = () => {
const id = newId;
setNewId(newId - 1);
setRows((oldRows) => [...oldRows, { id, projectCode: '', task: '', isNew: true }]);
setRowModesModel((oldModel) => ({
...oldModel,
[id]: { mode: GridRowModes.Edit, fieldToFocus: 'projectCode' },
}));
};

const totalColDef = {
flex:1,
// style: {color:getCostTotal('mon')>24?"red":"black"}
};

const TotalCell = ({value}: Props) => {
const [invalid, setInvalid] = useState(false);

useEffect(()=> {
const newInvalid = (value ?? 0) < 0;
setInvalid(newInvalid);
}, [value]);

return (
<Box flex={1} style={{color: invalid?"red":"black"}}>
$ {value}
</Box>
);
}

return (
<div>
<div style={{ display: 'flex', justifyContent: 'flex', width: '100%' }}>
<Box flex={1.5} textAlign={'right'} marginRight='4rem'>
<b>Total:</b>
</Box>
<TotalCell value={getCostTotal()}/>
</div>
<Button variant="outlined" color="primary" startIcon={<AddIcon />} onClick={handleAddClick} sx={{margin:'20px'}}>
Add record
</Button>
</div>
);
}

const EditFooter = (props: EditFooterProps) => {
return (
<div style={{ display: 'flex', justifyContent: 'flex', width: '100%' }}>
<Box flex={1}>
<b>Total: </b>
</Box>
<Box flex={2}>test</Box>
</div>
);
}

interface ClaimInputGridProps {
onClose?: () => void;
}

const initialRows: GridRowsProp = [
{
id: 1,
date: new Date(),
description: "Taxi to client office",
cost: 169.5,
document: 'taxi_receipt.jpg',
},
{
id: 2,
date: dayjs().add(-14, 'days').toDate(),
description: "MTR fee to Kowloon Bay Office",
cost: 15.5,
document: 'octopus_invoice.jpg',
},
{
id: 3,
date: dayjs().add(-44, 'days').toDate(),
description: "Starbucks",
cost: 504,
},
];

const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {

const [rows, setRows] = useState(initialRows);
const [day, setDay] = useState(dayjs());
const [rowModesModel, setRowModesModel] = React.useState<GridRowModesModel>({});

const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
if (params.reason === GridRowEditStopReasons.rowFocusOut) {
event.defaultMuiPrevented = true;
}
};

const handleEditClick = (id: GridRowId) => () => {
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } });
};

const handleSaveClick = (id: GridRowId) => () => {

setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
};

const handleDeleteClick = (id: GridRowId) => () => {
setRows(rows.filter((row) => row.id !== id));
};

const handleCancelClick = (id: GridRowId) => () => {
setRowModesModel({
...rowModesModel,
[id]: { mode: GridRowModes.View, ignoreModifications: true },
});

const editedRow = rows.find((row) => row.id === id);
if (editedRow!.isNew) {
setRows(rows.filter((row) => row.id !== id));
}
};

const processRowUpdate = (newRow: GridRowModel) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
return updatedRow;
};

const handleRowModesModelChange = (newRowModesModel: GridRowModesModel) => {
setRowModesModel(newRowModesModel);
};

const getCostTotal = () => {
let sum = 0;
rows.forEach((row) => {
sum += row['cost']??0;
});
return sum;
};

const commonGridColConfig : any = {
type: 'number',
// sortable: false,
//width: 100,
flex: 1,
align: 'left',
headerAlign: 'left',
// headerClassName: 'header',
editable: true,
renderEditCell: (value : any) => (
<GridEditInputCell
{...value}
inputProps={{
max: 24,
min: 0,
step: 0.25,
}}
/>
),
};

const columns: GridColDef[] = [
{
field: 'actions',
type: 'actions',
headerName: 'Actions',
width: 100,
cellClassName: 'actions',
getActions: ({ id }) => {
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit;

if (isInEditMode) {
return [
<GridActionsCellItem
icon={<SaveIcon />}
title="Save"
label="Save"
sx={{
color: 'primary.main',
}}
onClick={handleSaveClick(id)}
/>,
<GridActionsCellItem
icon={<CancelIcon />}
title="Cancel"
label="Cancel"
className="textPrimary"
onClick={handleCancelClick(id)}
color="inherit"
/>,
];
}

return [
<GridActionsCellItem
icon={<EditIcon />}
title="Edit"
label="Edit"
className="textPrimary"
onClick={handleEditClick(id)}
color="inherit"
/>,
<GridActionsCellItem
title="Delete"
label="Delete"
icon={<DeleteIcon />}
onClick={handleDeleteClick(id)}
sx={{color:"red"}}
/>,
];
},
},
{
field: 'date',
headerName: 'Invoice Date',
// width: 220,
flex: 1,
editable: true,
type: 'date',
},
{
field: 'description',
headerName: 'Description',
// width: 220,
flex: 2,
editable: true,
type: 'string',
},
{
field: 'cost',
headerName: 'Cost (HKD)',
editable: true,
type: 'number',
valueFormatter: (params) => {
return `$ ${params.value??0}`;
},
},
{
field: 'document',
headerName: 'Supporting Document',
type: 'string',
editable: true,
flex: 2,
renderCell: (params) => {
return params.value?
(
<span>
<a href="" target="_blank" rel="noopener noreferrer">
{params.value}
</a>
</span>
) :
(<span style={{color: palette.text.disabled}}>No Documents</span>)
},
renderEditCell: (params) => {
return params.value?
(
<span>
<a href="" target="_blank" rel="noopener noreferrer">
{params.value}
</a>
<Button title='Remove Document' onClick={(event) => console.log(event)}>
<ImageNotSupportedOutlinedIcon sx={{fontSize: '25px', color:"red"}}/>
</Button>
</span>
) : (
<Button title='Add Document'>
<AddPhotoAlternateOutlinedIcon sx={{fontSize: '25px', color:"green"}}/>
</Button>
)
},
},
];

return (
<Box
sx={{
// marginBottom: '-5px',
display: 'flex',
'flex-direction': 'column',
// 'justify-content': 'flex-end',
height: '100%',//'25rem',
width: '100%',
'& .actions': {
color: 'text.secondary',
},
'& .header': {
backgroundColor: "#F8F9FA",
// border: 1,
// 'border-width': '1px',
// 'border-color': 'grey',
},
'& .textPrimary': {
color: 'text.primary',
},
}}
>
<DataGrid
sx={{flex:1}}
rows={rows}
columns={columns}
editMode="row"
rowModesModel={rowModesModel}
onRowModesModelChange={handleRowModesModelChange}
onRowEditStop={handleRowEditStop}
processRowUpdate={processRowUpdate}
disableRowSelectionOnClick={true}
disableColumnMenu={true}
hideFooterPagination={true}
slots={{
// footer: EditFooter,
}}
slotProps={{
// footer: { setDay, setRows, setRowModesModel },
}}
initialState={{
pagination: { paginationModel: { pageSize: 100 } },
}}
/>
<BottomBar getCostTotal={getCostTotal} setRows={setRows} setRowModesModel={setRowModesModel}
sx={{flex:2}}/>
</Box>
);
}

export default ClaimInputGrid;

+ 48
- 0
src/components/CreateClaim/CreateClaim.tsx Parādīt failu

@@ -0,0 +1,48 @@
"use client";

import Check from "@mui/icons-material/Check";
import Close from "@mui/icons-material/Close";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Tab from "@mui/material/Tab";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import { useRouter } from "next/navigation";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import ClaimProjectDetails from "./ClaimDetails";
import TaskSetup from "./TaskSetup";
import StaffAllocation from "./StaffAllocation";
import ResourceMilestone from "./ResourceMilestone";

const CreateProject: React.FC = () => {
const [tabIndex, setTabIndex] = useState(0);
const { t } = useTranslation();
const router = useRouter();

const handleCancel = () => {
router.back();
};

const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
},
[],
);

return (
<>
<ClaimProjectDetails />
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button variant="outlined" startIcon={<Close />} onClick={handleCancel}>
{t("Cancel")}
</Button>
<Button variant="contained" startIcon={<Check />}>
{t("Confirm")}
</Button>
</Stack>
</>
);
};

export default CreateProject;

+ 1
- 0
src/components/CreateClaim/index.ts Parādīt failu

@@ -0,0 +1 @@
export { default } from "./CreateClaim";

+ 5
- 5
src/components/EnterTimesheet/EnterTimesheetModal.tsx Parādīt failu

@@ -11,7 +11,7 @@ import Stack from "@mui/material/Stack";
import { Add } from '@mui/icons-material';
import Link from "next/link";
import { t } from 'i18next';
import { Modal, Typography } from "@mui/material";
import { Card, Modal, Typography } from "@mui/material";
import CustomModal from "../CustomModal/CustomModal";
import { PROJECT_MODAL_STYLE } from "@/theme/colorConst";
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
@@ -51,15 +51,15 @@ const EnterTimesheetModal: React.FC<EnterTimesheetModalProps> = ({ ...props }) =
return (
<Modal open={props.isOpen} onClose={props.onClose}>
<div style={PROJECT_MODAL_STYLE}>
<Typography variant="h6" id="modal-title" sx={{flex:1}}>
{/* <Typography variant="h5" id="modal-title" sx={{flex:1}}>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
Timesheet Input
</div>
</Typography>
</Typography> */}

<div style={{flex: 10}}>
<Card style={{flex: 10, marginBottom:'20px'}}>
<TimesheetInputGrid setLockConfirm={setLockConfirm}/>
</div>
</Card>

<div style={{
display: 'flex', justifyContent: 'space-between', width: '100%', flex: 1


+ 0
- 62
src/components/EnterTimesheet/ProjectGrid.tsx Parādīt failu

@@ -1,62 +0,0 @@
"use client";
import * as React from "react";
import Grid from "@mui/material/Grid";
import { useEffect, useState } from 'react'
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {Card,CardContent,CardHeader} from '@mui/material';
import CustomCardGrid from '../CustomCardGrid/CustomCardGrid';
import '../../app/global.css';
import { PROJECT_CARD_STYLE } from "@/theme/colorConst";

interface ProjectGridProps {
tab: number;
}


const ProjectGrid: React.FC<ProjectGridProps> = (props) => {
const [items, setItems] = React.useState<Object[]>([])
useEffect(() => {
if (props.tab == 0) {
setItems(cards)
}
else {
const filteredItems = cards;//cards.filter(item => (item.track == props.tab));
setItems(filteredItems);
}
}, [props.tab]);
const cards = [
{code: 'M1001 (C)', name: 'Consultancy Project A', hr_spent: 12.75, hr_spent_normal: 0.00, hr_alloc: 150.00, hr_alloc_normal: 30.00},
{code: 'M1301 (C)', name: 'Consultancy Project AAA', hr_spent: 4.25, hr_spent_normal: 0.25, hr_alloc: 30.00, hr_alloc_normal: 0.00},
{code: 'M1354 (C)', name: 'Consultancy Project BBB', hr_spent: 57.00, hr_spent_normal: 6.50, hr_alloc: 100.00, hr_alloc_normal: 20.00},
{code: 'M1973 (C)', name: 'Construction Project CCC', hr_spent: 12.75, hr_spent_normal: 0.00, hr_alloc: 150.00, hr_alloc_normal: 30.00},
{code: 'M2014 (T)', name: 'Consultancy Project DDD', hr_spent: 1.00, hr_spent_normal: 0.00, hr_alloc: 10.00, hr_alloc_normal: 0.00},
];
const cardLayout = (item: Record<string, string>) => {
return (
<Card style={PROJECT_CARD_STYLE}>
<CardHeader style={{backgroundColor:'red'}} 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 //
return (
<Grid container md={12} style={{backgroundColor:"yellow"}}>
{/* <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>
);
};

export default ProjectGrid;

+ 56
- 48
src/components/EnterTimesheet/TimesheetInputGrid.tsx Parādīt failu

@@ -7,7 +7,6 @@ import PageTitle from "../PageTitle/PageTitle";
import { Suspense } from "react";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import { Add, SettingsEthernet } from '@mui/icons-material';
import Link from "next/link";
import { t } from 'i18next';
import { Box, Container, Modal, Select, SelectChangeEvent, Typography } from "@mui/material";
@@ -21,7 +20,6 @@ import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Swal from "sweetalert2";
import { msg } from "../Swal/CustomAlerts";
import ComboEditor from "../ComboEditor/ComboEditor";
import React from "react";
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import {
@@ -72,6 +70,53 @@ interface EditFooterProps {
) => void;
}

const EditToolbar = (props: EditToolbarProps) => {
const { setDay } = props;
const [selectedDate, setSelectedDate] = useState<dayjs.Dayjs>(dayjs());

const handleClickLeft = () => {
if (selectedDate) {
const newDate = selectedDate.add(-7, 'day');
setSelectedDate(newDate);
}
};
const handleClickRight = () => {
if (selectedDate) {
const newDate = selectedDate.add(7, 'day') > dayjs()? dayjs(): selectedDate.add(7, 'day');
setSelectedDate(newDate);
}
};

const handleDateChange = (date: dayjs.Dayjs | Date | null) => {
const newDate = dayjs(date);
setSelectedDate(newDate);
};

useEffect(() => {
setDay((oldDay) => selectedDate);
}, [selectedDate]);

return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%', paddingBottom:'20px'}}>
<Typography variant="h5" id="modal-title" sx={{flex:1}}>
Timesheet Input
</Typography>
<Button sx={{"border-radius":"30%", marginRight:'20px'}} variant="contained" onClick={handleClickLeft}>
<ArrowBackIcon/>
</Button>
<DatePicker
value={selectedDate}
onChange={handleDateChange}
disableFuture={true}/>
<Button sx={{"border-radius":"30%", margin:'0px 20px 0px 20px'}} variant="contained" onClick={handleClickRight}>
<ArrowForwardIcon/>
</Button>
</div>
</LocalizationProvider>
);
}

const BottomBar = (props: BottomBarProps) => {
const { setRows, setRowModesModel, getHoursTotal, setLockConfirm } = props;
// const getHoursTotal = props.getHoursTotal;
@@ -136,49 +181,6 @@ const BottomBar = (props: BottomBarProps) => {
);
}

const EditToolbar = (props: EditToolbarProps) => {
const { setDay } = props;
const [selectedDate, setSelectedDate] = useState<dayjs.Dayjs>(dayjs());

const handleClickLeft = () => {
if (selectedDate) {
const newDate = selectedDate.add(-7, 'day');
setSelectedDate(newDate);
}
};
const handleClickRight = () => {
if (selectedDate) {
const newDate = selectedDate.add(7, 'day') > dayjs()? dayjs(): selectedDate.add(7, 'day');
setSelectedDate(newDate);
}
};

const handleDateChange = (date: dayjs.Dayjs | Date | null) => {
const newDate = dayjs(date);
setSelectedDate(newDate);
};

useEffect(() => {
setDay((oldDay) => selectedDate);
}, [selectedDate]);

return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%' }}>
<Button sx={{"border-radius":"30%", marginRight:'20px'}} variant="contained" onClick={handleClickLeft}>
<ArrowBackIcon/>
</Button>
<DatePicker
value={selectedDate}
onChange={handleDateChange}
disableFuture={true}/>
<Button sx={{"border-radius":"30%", margin:'0px 20px 0px 20px'}} variant="contained" onClick={handleClickRight}>
<ArrowForwardIcon/>
</Button>
</div>
</LocalizationProvider>
);
}

const EditFooter = (props: EditFooterProps) => {
return (
@@ -307,7 +309,7 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
{
field: 'actions',
type: 'actions',
headerName: '',
headerName: 'Actions',
width: 100,
cellClassName: 'actions',
getActions: ({ id }) => {
@@ -455,7 +457,11 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
<Box
sx={{
// marginBottom: '-5px',
height: '30rem',
display: 'flex',
'flex-direction': 'column',
// 'justify-content': 'flex-end',
padding: '20px',
height: '100%',//'30rem',
width: '100%',
'& .actions': {
color: 'text.secondary',
@@ -492,9 +498,11 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
initialState={{
pagination: { paginationModel: { pageSize: 100 } },
}}
sx={{flex:1}}
/>

<BottomBar getHoursTotal={getHoursTotal} setRows={setRows} setRowModesModel={setRowModesModel} setLockConfirm={setLockConfirm}/>
<BottomBar getHoursTotal={getHoursTotal} setRows={setRows} setRowModesModel={setRowModesModel} setLockConfirm={setLockConfirm}
sx={{flex:3}}/>
</Box>
);
}


+ 4
- 1
src/components/NavigationContent/NavigationContent.tsx Parādīt failu

@@ -35,7 +35,10 @@ const navigationItems: NavigationItem[] = [
{ icon: <Dashboard />, label: "Project Cash Flow", path: "/dashboard/ProjectCashFlow" },
{ icon: <Dashboard />, label: "Project Status by Client", path: "/dashboard/ProjectStatusByClient" },
]},
{ icon: <RequestQuote />, label: "Expense Claim", path: "/claim" },
{ icon: <RequestQuote />, label: "Staff Reimbursement", path: "/staffReimbursement", children: [
{ icon: <RequestQuote />, label: "ClaimApproval", path: "/staffReimbursement/ClaimApproval"},
{ icon: <RequestQuote />, label: "ClaimSummary", path: "/staffReimbursement/ClaimSummary"}
] },
{ icon: <Assignment />, label: "Project Management", path: "/projects" },
{ icon: <Task />, label: "Task Template", path: "/tasks" },
{ icon: <Payments />, label: "Invoice", path: "/invoice" },


+ 1
- 1
src/components/UserWorkspacePage/ProjectGrid.tsx Parādīt failu

@@ -38,7 +38,7 @@ const ProjectGrid: React.FC<ProjectGridProps> = (props) => {
const cardLayout = (item: Record<string, string>) => {
return (
<Card style={PROJECT_CARD_STYLE}>
<CardHeader style={{backgroundColor:'red'}} title={item.code + '\u000A' + item.name}/>
<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>


+ 12
- 0
src/theme/colorConst.js Parādīt failu

@@ -78,6 +78,18 @@ export const PROJECT_MODAL_STYLE = {
flexDirection: 'column',
};

export const DATAGRID_STYLE = {
boxShadow: 2,
border: 2,
borderColor: 'primary.light',
'& .MuiDataGrid-cell:hover': {
color: 'primary.main'
},
'& .MuiDataGrid-root': {
overflow: 'auto',
}
};

export const TAB_THEME = {
components: {
MuiTab: {


Notiek ielāde…
Atcelt
Saglabāt