Browse Source

Fix some lint issues

tags/Baseline_30082024_FRONTEND_UAT
Wayne 1 year ago
parent
commit
7cad94ef04
38 changed files with 4982 additions and 3149 deletions
  1. +7
    -7
      next.config.js
  2. +3
    -5
      src/app/(main)/dashboard/CompanyTeamCashFlow/page.tsx
  3. +3
    -5
      src/app/(main)/dashboard/ProjectCashFlow/page.tsx
  4. +2
    -4
      src/app/(main)/dashboard/ProjectFinancialSummary/page.tsx
  5. +3
    -5
      src/app/(main)/dashboard/ProjectStatusByClient/page.tsx
  6. +2
    -4
      src/app/(main)/dashboard/StaffUtilization/page.tsx
  7. +2
    -6
      src/app/(main)/dashboard/page.tsx
  8. +2
    -2
      src/app/(main)/home/page.tsx
  9. +2
    -2
      src/app/api/cashflow/index.ts
  10. +3
    -3
      src/app/api/claims/index.ts
  11. +1
    -1
      src/components/AppBar/AppBar.tsx
  12. +150
    -79
      src/components/AssignedProjectGrid/AssignedProjectGrid.tsx
  13. +7
    -5
      src/components/ClaimSearch/ClaimSearch.tsx
  14. +207
    -186
      src/components/CompanyTeamCashFlow/CompanyTeamCashFlow.tsx
  15. +8
    -20
      src/components/CreateClaim/ClaimDetails.tsx
  16. +133
    -98
      src/components/CreateClaim/ClaimInputGrid.tsx
  17. +140
    -71
      src/components/CustomCardGrid/CustomCardGrid.tsx
  18. +241
    -177
      src/components/CustomDatagrid/CustomDatagrid.tsx
  19. +46
    -27
      src/components/CustomModal/CustomModal.tsx
  20. +179
    -68
      src/components/CustomSearchForm/CustomSearchForm.tsx
  21. +4
    -4
      src/components/DashboardPage/DashboardPage.tsx
  22. +43
    -43
      src/components/DashboardPage/DashboardTabButton.tsx
  23. +370
    -229
      src/components/DashboardPage/ProgressByClient.tsx
  24. +50
    -30
      src/components/EnterTimesheet/EnterTimesheetModal.tsx
  25. +163
    -126
      src/components/EnterTimesheet/TimesheetInputGrid.tsx
  26. +64
    -24
      src/components/NavigationContent/NavigationContent.tsx
  27. +147
    -28
      src/components/PageTitle/PageTitle.tsx
  28. +500
    -291
      src/components/ProgressByClient/ProgressByClient.tsx
  29. +95
    -59
      src/components/ProgressCashFlowSearch/ProgressCashFlowSearch.tsx
  30. +486
    -287
      src/components/ProjectCashFlow/ProjectCashFlow.tsx
  31. +160
    -62
      src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx
  32. +432
    -230
      src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx
  33. +849
    -550
      src/components/StaffUtilization/StaffUtilization.tsx
  34. +17
    -18
      src/components/Swal/CustomAlerts.js
  35. +66
    -28
      src/components/UserWorkspacePage/ProjectGrid.tsx
  36. +31
    -34
      src/components/UserWorkspacePage/UserWorkspacePage.tsx
  37. +5
    -6
      src/theme.ts
  38. +359
    -325
      src/theme/colorConst.js

+ 7
- 7
next.config.js View File

@@ -1,10 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// eslint: {
// // Warning: This allows production builds to successfully complete even if
// // your project has ESLint errors.
// ignoreDuringBuilds: true,
// },
}
// eslint: {
// // Warning: This allows production builds to successfully complete even if
// // your project has ESLint errors.
// ignoreDuringBuilds: true,
// },
};

module.exports = nextConfig
module.exports = nextConfig;

+ 3
- 5
src/app/(main)/dashboard/CompanyTeamCashFlow/page.tsx View File

@@ -3,25 +3,23 @@ import { I18nProvider } from "@/i18n";
import DashboardPage from "@/components/DashboardPage/DashboardPage";
import DashboardPageButton from "@/components/DashboardPage/DashboardTabButton";
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
import { Suspense} from "react";
import { Suspense } from "react";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Typography from "@mui/material/Typography";
import CompanyTeamCashFlowComponent from '@/components/CompanyTeamCashFlow'
import CompanyTeamCashFlowComponent from "@/components/CompanyTeamCashFlow";

export const metadata: Metadata = {
title: "Project Status by Client",
};


const CompanyTeamCashFlow: React.FC = () => {

return (
<I18nProvider namespaces={["dashboard"]}>
<Typography variant="h4" marginInlineEnd={2}>
Company / Team Cash Flow
</Typography>
<CompanyTeamCashFlowComponent/>
<CompanyTeamCashFlowComponent />
</I18nProvider>
);
};


+ 3
- 5
src/app/(main)/dashboard/ProjectCashFlow/page.tsx View File

@@ -3,19 +3,17 @@ import { I18nProvider } from "@/i18n";
import DashboardPage from "@/components/DashboardPage/DashboardPage";
import DashboardPageButton from "@/components/DashboardPage/DashboardTabButton";
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
import { Suspense} from "react";
import { Suspense } from "react";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Typography from "@mui/material/Typography";
import ProjectCashFlowComponent from '@/components/ProjectCashFlow'
import ProjectCashFlowComponent from "@/components/ProjectCashFlow";

export const metadata: Metadata = {
title: "Project Status by Client",
};


const ProjectCashFlow: React.FC = () => {

return (
<I18nProvider namespaces={["dashboard"]}>
<Typography variant="h4" marginInlineEnd={2}>
@@ -24,7 +22,7 @@ const ProjectCashFlow: React.FC = () => {
{/* <Suspense fallback={<ProgressCashFlowSearch.Loading />}>
<ProgressCashFlowSearch/>
</Suspense> */}
<ProjectCashFlowComponent/>
<ProjectCashFlowComponent />
</I18nProvider>
);
};


+ 2
- 4
src/app/(main)/dashboard/ProjectFinancialSummary/page.tsx View File

@@ -3,7 +3,7 @@ import { I18nProvider } from "@/i18n";
import DashboardPage from "@/components/DashboardPage/DashboardPage";
import DashboardPageButton from "@/components/DashboardPage/DashboardTabButton";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense} from "react";
import { Suspense } from "react";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Typography from "@mui/material/Typography";
@@ -13,15 +13,13 @@ export const metadata: Metadata = {
title: "Project Status by Client",
};


const ProjectFinancialSummary: React.FC = () => {

return (
<I18nProvider namespaces={["dashboard"]}>
<Typography variant="h4" marginInlineEnd={2}>
Project Financial Summary
</Typography>
<ProjectFinancialSummaryComponents/>
<ProjectFinancialSummaryComponents />
</I18nProvider>
);
};


+ 3
- 5
src/app/(main)/dashboard/ProjectStatusByClient/page.tsx View File

@@ -3,7 +3,7 @@ import { I18nProvider } from "@/i18n";
import DashboardPage from "@/components/DashboardPage/DashboardPage";
import DashboardPageButton from "@/components/DashboardPage/DashboardTabButton";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense} from "react";
import { Suspense } from "react";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Typography from "@mui/material/Typography";
@@ -13,18 +13,16 @@ export const metadata: Metadata = {
title: "Project Status by Client",
};


const ProjectStatusByClient: React.FC = () => {

return (
<I18nProvider namespaces={["dashboard"]}>
<Typography variant="h4" marginInlineEnd={2}>
Project Status by Client
</Typography>
<Suspense fallback={<ProgressByClientSearch.Loading />}>
<ProgressByClientSearch/>
<ProgressByClientSearch />
</Suspense>
<ProgressByClient/>
<ProgressByClient />
</I18nProvider>
);
};


+ 2
- 4
src/app/(main)/dashboard/StaffUtilization/page.tsx View File

@@ -3,7 +3,7 @@ import { I18nProvider } from "@/i18n";
import DashboardPage from "@/components/DashboardPage/DashboardPage";
import DashboardPageButton from "@/components/DashboardPage/DashboardTabButton";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense} from "react";
import { Suspense } from "react";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Typography from "@mui/material/Typography";
@@ -13,15 +13,13 @@ export const metadata: Metadata = {
title: "Project Status by Client",
};


const StaffUtilization: React.FC = () => {

return (
<I18nProvider namespaces={["dashboard"]}>
<Typography variant="h4" marginInlineEnd={2}>
Staff Utilization
</Typography>
<StaffUtilizationComponent/>
<StaffUtilizationComponent />
</I18nProvider>
);
};


+ 2
- 6
src/app/(main)/dashboard/page.tsx View File

@@ -3,7 +3,7 @@ import { I18nProvider } from "@/i18n";
import DashboardPage from "@/components/DashboardPage/DashboardPage";
import DashboardPageButton from "@/components/DashboardPage/DashboardTabButton";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense} from "react";
import { Suspense } from "react";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";

@@ -11,12 +11,8 @@ export const metadata: Metadata = {
title: "Dashboard",
};


const Dashboard: React.FC = () => {

return (
<div>test</div>
);
return <div>test</div>;
};

export default Dashboard;

+ 2
- 2
src/app/(main)/home/page.tsx View File

@@ -9,8 +9,8 @@ export const metadata: Metadata = {
const Home: React.FC = async () => {
return (
<I18nProvider namespaces={["home"]}>
<UserWorkspacePage/>
</I18nProvider>
<UserWorkspacePage />
</I18nProvider>
);
};



+ 2
- 2
src/app/api/cashflow/index.ts View File

@@ -34,6 +34,6 @@ const mockProjects: CashFlow[] = [
startDateTo: "5",
targetEndDate: "s",
client: "ss",
subsidiary:"ss",
}
subsidiary: "ss",
},
];

+ 3
- 3
src/app/api/claims/index.ts View File

@@ -24,7 +24,7 @@ const mockClaims: ClaimResult[] = [
id: 1,
created: "2023-11-22",
name: "Consultancy Project A",
cost: 121.00,
cost: 121.0,
type: "Expense",
status: "Not Submitted",
remarks: "",
@@ -33,7 +33,7 @@ const mockClaims: ClaimResult[] = [
id: 2,
created: "2023-11-30",
name: "Consultancy Project A",
cost: 4300.00,
cost: 4300.0,
type: "Expense",
status: "Waiting for Approval",
remarks: "",
@@ -42,7 +42,7 @@ const mockClaims: ClaimResult[] = [
id: 3,
created: "2023-12-12",
name: "Construction Project C",
cost: 3675.00,
cost: 3675.0,
type: "Petty Cash",
status: "Rejected",
remarks: "Duplicate Claim Form",


+ 1
- 1
src/components/AppBar/AppBar.tsx View File

@@ -16,7 +16,7 @@ const AppBar: React.FC<AppBarProps> = ({ avatarImageSrc, profileName }) => {
<I18nProvider namespaces={["common"]}>
<MUIAppBar position="sticky" color="default" elevation={4}>
<Toolbar>
<NavigationToggle/>
<NavigationToggle />
<Box
sx={{ flexGrow: 1, display: "flex", justifyContent: "flex-end" }}
>


+ 150
- 79
src/components/AssignedProjectGrid/AssignedProjectGrid.tsx View File

@@ -1,15 +1,26 @@
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';

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[];
// rows: any[];
// columns: any[];
columnWidth?: number;
Style?: boolean;
@@ -47,7 +58,7 @@ function CustomTabPanel(props: TabPanelProps) {
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
"aria-controls": `simple-tabpanel-${index}`,
};
}

@@ -72,67 +83,117 @@ const AssignedProjectGrid: React.FC<AssignedProjectGridProps> = ({
// return { ...row };
// });

const getBackgroundColor = (color: string, mode: 'light' | 'dark') =>
mode === 'dark' ? darken(color, 0.7) : lighten(color, 0.7);
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 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 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 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)
"& .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,
),
},
},
'&.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)
"& .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,
),
},
},
'&.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)
"& .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,
),
},
},
'&.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)
"& .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)
}
}
}
"&.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);
@@ -142,20 +203,30 @@ const AssignedProjectGrid: React.FC<AssignedProjectGridProps> = ({
};

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}>
<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}>
@@ -164,12 +235,12 @@ const AssignedProjectGrid: React.FC<AssignedProjectGridProps> = ({
<CustomTabPanel value={value} index={2}>
Item {value}
</CustomTabPanel> */}
</ThemeProvider>
</div>
</CardContent>
<AllProjectGrid tab={value} />
</Card>
</div>
</ThemeProvider>
</div>
</CardContent>
<AllProjectGrid tab={value} />
</Card>
</div>
);
};



+ 7
- 5
src/components/ClaimSearch/ClaimSearch.tsx View File

@@ -39,7 +39,12 @@ const ClaimSearch: React.FC<Props> = ({ claims }) => {
label: t("Status"),
paramName: "status",
type: "select",
options: ["Not Submitted", "Waiting for Approval", "Approved", "Rejected"]
options: [
"Not Submitted",
"Waiting for Approval",
"Approved",
"Rejected",
],
},
{
label: t("Remarks"),
@@ -80,10 +85,7 @@ const ClaimSearch: React.FC<Props> = ({ claims }) => {
console.log(query);
}}
/>
<SearchResults<ClaimResult>
items={filteredClaims}
columns={columns}
/>
<SearchResults<ClaimResult> items={filteredClaims} columns={columns} />
</>
);
};


+ 207
- 186
src/components/CompanyTeamCashFlow/CompanyTeamCashFlow.tsx View File

@@ -1,129 +1,131 @@
"use client";
import * as React from "react";
import Grid from "@mui/material/Grid";
import { useState,useEffect, useMemo } from 'react'
import { useState, useEffect, useMemo } from "react";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {Card,CardHeader} from '@mui/material';
import { Card, CardHeader } from "@mui/material";
import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
import CustomDatagrid from '../CustomDatagrid/CustomDatagrid';
import ReactApexChart from 'react-apexcharts';
import { ApexOptions } from 'apexcharts';
import { GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import ReportProblemIcon from '@mui/icons-material/ReportProblem';
import dynamic from 'next/dynamic';
import '../../app/global.css';
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import ReactApexChart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import ReportProblemIcon from "@mui/icons-material/ReportProblem";
import dynamic from "next/dynamic";
import "../../app/global.css";
import { AnyARecord, AnyCnameRecord } from "dns";
import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense } from "react";
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
import {Input,Label} from "reactstrap";
import Select, {components} from 'react-select'
import { Input, Label } from "reactstrap";
import Select, { components } from "react-select";

const CompanyTeamCashFlow: React.FC = () => {
const todayDate = new Date;
const [selectionModel, setSelectionModel] : any[] = React.useState([]);
const [cashFlowYear, setCashFlowYear] : any[] = React.useState(todayDate.getFullYear());
const todayDate = new Date();
const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const [cashFlowYear, setCashFlowYear]: any[] = React.useState(
todayDate.getFullYear(),
);

const teamOptions = [
{ value: 1, label: 'XXX Team' },
{ value: 2, label: 'YYY Team' },
{ value: 3, label: 'ZZZ Team' }
]
{ value: 1, label: "XXX Team" },
{ value: 2, label: "YYY Team" },
{ value: 3, label: "ZZZ Team" },
];

const columns = [
{
id: 'projectCode',
field: 'projectCode',
id: "projectCode",
field: "projectCode",
headerName: "Project Code",
flex: 1,
},
{
id: 'projectName',
field: 'projectName',
},
{
id: "projectName",
field: "projectName",
headerName: "Project Name",
flex: 1,
},
{
id: 'team',
field: 'team',
headerName: "Team",
flex: 1,
},
{
id: 'teamLeader',
field: 'teamLeader',
headerName: "Team Leader",
flex: 1,
},
{
id: 'startDate',
field: 'startDate',
headerName: "Start Date",
flex: 1,
},
{
id: 'targetEndDate',
field: 'targetEndDate',
headerName: "Target End Date",
flex: 1,
},
{
id: 'client',
field: 'client',
headerName: "Client",
flex: 1,
},
{
id: 'subsidiary',
field: 'subsidiary',
headerName: "Subsidiary",
flex: 1,
},
},
{
id: "team",
field: "team",
headerName: "Team",
flex: 1,
},
{
id: "teamLeader",
field: "teamLeader",
headerName: "Team Leader",
flex: 1,
},
{
id: "startDate",
field: "startDate",
headerName: "Start Date",
flex: 1,
},
{
id: "targetEndDate",
field: "targetEndDate",
headerName: "Target End Date",
flex: 1,
},
{
id: "client",
field: "client",
headerName: "Client",
flex: 1,
},
{
id: "subsidiary",
field: "subsidiary",
headerName: "Subsidiary",
flex: 1,
},
];

const ledgerColumns = [
{
id: 'date',
field: 'date',
id: "date",
field: "date",
headerName: "Date",
flex: 0.5,
},
{
id: 'expenditure',
field: 'expenditure',
},
{
id: "expenditure",
field: "expenditure",
headerName: "Expenditure (HKD)",
flex: 0.6,
},
{
id: 'income',
field: 'income',
headerName: "Income (HKD)",
flex: 0.6,
},
{
id: 'cashFlowBalance',
field: 'cashFlowBalance',
headerName: "Cash Flow Balance (HKD)",
flex: 0.6,
},
{
id: 'remarks',
field: 'remarks',
headerName: "Remarks",
flex: 1,
},
},
{
id: "income",
field: "income",
headerName: "Income (HKD)",
flex: 0.6,
},
{
id: "cashFlowBalance",
field: "cashFlowBalance",
headerName: "Cash Flow Balance (HKD)",
flex: 0.6,
},
{
id: "remarks",
field: "remarks",
headerName: "Remarks",
flex: 1,
},
];

const options: ApexOptions = {
chart: {
height: 350,
type: 'line',
type: "line",
},
stroke: {
width: [0, 0, 2, 2]
width: [0, 0, 2, 2],
},
plotOptions: {
bar: {
@@ -132,152 +134,171 @@ const CompanyTeamCashFlow: React.FC = () => {
},
},
dataLabels: {
enabled: false
enabled: false,
},
xaxis: {
categories: [
'Q1',
'Q2',
'Q3',
'Q4',
'Q5',
'Q6',
'Q7',
'Q8',
'Q9',
'Q10',
'Q11',
'Q12',
"Q1",
"Q2",
"Q3",
"Q4",
"Q5",
"Q6",
"Q7",
"Q8",
"Q9",
"Q10",
"Q11",
"Q12",
],
},
yaxis: [

{
title: {
text: 'Monthly Income and Expenditure(HKD)'
text: "Monthly Income and Expenditure(HKD)",
},
min: 0,
max: 3700000,
tickAmount: 5
tickAmount: 5,
},
{
show:false,
seriesName: 'Monthly_Expenditure',
show: false,
seriesName: "Monthly_Expenditure",
title: {
text: 'Monthly Expenditure (HKD)'
text: "Monthly Expenditure (HKD)",
},
min: 0,
max: 3700000,
tickAmount: 5
tickAmount: 5,
},
{
seriesName: 'Cumulative_Income',
seriesName: "Cumulative_Income",
opposite: true,
title: {
text: 'Cumulative Income and Expenditure(HKD)'
text: "Cumulative Income and Expenditure(HKD)",
},
min: 0,
max: 21000000,
tickAmount: 5
tickAmount: 5,
},
{
show:false,
seriesName: 'Cumulative_Expenditure',
show: false,
seriesName: "Cumulative_Expenditure",
opposite: true,
title: {
text: 'Cumulative Expenditure (HKD)'
text: "Cumulative Expenditure (HKD)",
},
min: 0,
max: 21000000,
tickAmount: 5
}
],
tickAmount: 5,
},
],
grid: {
borderColor: '#f1f1f1',
borderColor: "#f1f1f1",
},
annotations: {
},
series:[
annotations: {},
series: [
{
name:"Monthly_Income",
type:"column",
name: "Monthly_Income",
type: "column",
color: "#ffde91",
data:[1280000,170000,3600000,2400000,1000000,1800000,1800000,1200000,1250000,1200000,600000,2400000],
data: [
1280000, 170000, 3600000, 2400000, 1000000, 1800000, 1800000, 1200000,
1250000, 1200000, 600000, 2400000,
],
},
{
name:"Monthly_Expenditure",
type:"column",
name: "Monthly_Expenditure",
type: "column",
color: "#82b59a",
data:[1200000,1400000,2000000,1400000,1450000,1800000,1200000,1400000,1200000,1600000,2000000,1600000]
data: [
1200000, 1400000, 2000000, 1400000, 1450000, 1800000, 1200000,
1400000, 1200000, 1600000, 2000000, 1600000,
],
},
{
name:"Cumulative_Income",
type:"line",
name: "Cumulative_Income",
type: "line",
color: "#EE6D7A",
data:[500000,3000000,7000000,9000000,10000000,13000000,14000000,16000000,17000000,17500000,18000000,20000000]
data: [
500000, 3000000, 7000000, 9000000, 10000000, 13000000, 14000000,
16000000, 17000000, 17500000, 18000000, 20000000,
],
},
{
name:"Cumulative_Expenditure",
type:"line",
name: "Cumulative_Expenditure",
type: "line",
color: "#7cd3f2",
data:[400000,2800000,4000000,5200000,7100000,8000000,10000000,11000000,12100000,14000000,15400000,17200000]
}
]
data: [
400000, 2800000, 4000000, 5200000, 7100000, 8000000, 10000000,
11000000, 12100000, 14000000, 15400000, 17200000,
],
},
],
};

return (
<>
<Grid item sm>
<div style={{display:"inline-block",width:"100%"}}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Company and Team Cash Flow by Month"/>
<div style={{display:"inline-block",width:"99%"}}>
<div className="inline-block">
<Label className="text-slate-500 font-medium ml-6">
Period:&nbsp;
</Label>
<Input
id={'cashFlowYear'}
value={cashFlowYear}
readOnly={true}
bsSize="lg"
className="rounded-md text-base w-12"
/>
</div>
<div className="inline-block ml-1">
<button onClick={() => setCashFlowYear(cashFlowYear - 1)} className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base">
&lt;
</button>
</div>
<div className="inline-block ml-1">
<button onClick={() => setCashFlowYear(cashFlowYear + 1)} className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base">
&gt;
</button>
</div>
<div className="inline-block ml-2">
<Label className="text-slate-500 font-medium">
Team:&nbsp;
</Label>
</div>
<div className="inline-block ml-1 w-60">
<Select
placeholder= "All Team"
options={teamOptions}
isClearable={true}
/>
</div>
<ReactApexChart
options={options}
series={options.series}
type="line"
height="500"
/>
</div>
</Card>
</Grid>
</div>
</Grid>
<Grid item sm>
<div style={{ display: "inline-block", width: "100%" }}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader
className="text-slate-500"
title="Company and Team Cash Flow by Month"
/>
<div style={{ display: "inline-block", width: "99%" }}>
<div className="inline-block">
<Label className="text-slate-500 font-medium ml-6">
Period:&nbsp;
</Label>
<Input
id={"cashFlowYear"}
value={cashFlowYear}
readOnly={true}
bsSize="lg"
className="rounded-md text-base w-12"
/>
</div>
<div className="inline-block ml-1">
<button
onClick={() => setCashFlowYear(cashFlowYear - 1)}
className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
>
&lt;
</button>
</div>
<div className="inline-block ml-1">
<button
onClick={() => setCashFlowYear(cashFlowYear + 1)}
className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
>
&gt;
</button>
</div>
<div className="inline-block ml-2">
<Label className="text-slate-500 font-medium">
Team:&nbsp;
</Label>
</div>
<div className="inline-block ml-1 w-60">
<Select
placeholder="All Team"
options={teamOptions}
isClearable={true}
/>
</div>
<ReactApexChart
options={options}
series={options.series}
type="line"
height="500"
/>
</div>
</Card>
</Grid>
</div>
</Grid>
</>
);
};


+ 8
- 20
src/components/CreateClaim/ClaimDetails.tsx View File

@@ -31,18 +31,10 @@ const ClaimDetails: React.FC = () => {
<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 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>
@@ -50,20 +42,16 @@ const ClaimDetails: React.FC = () => {
<FormControl fullWidth>
<InputLabel>{t("Expense Type")}</InputLabel>
<Select label={t("Team Lead")}>
<MenuItem value={"Petty Cash"}>
{"Petty Cash"}
</MenuItem>
<MenuItem value={"Expense"}>
{"Expense"}
</MenuItem>
<MenuItem value={"Petty Cash"}>{"Petty Cash"}</MenuItem>
<MenuItem value={"Expense"}>{"Expense"}</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
</Box>
<Card>
<ClaimInputGrid/>
<ClaimInputGrid />
</Card>

{/* <CardActions sx={{ justifyContent: "flex-end" }}>


+ 133
- 98
src/components/CreateClaim/ClaimInputGrid.tsx View File

@@ -8,22 +8,29 @@ 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 { 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 { DatePicker } from "@mui/x-date-pickers/DatePicker";
import {
GridRowsProp,
GridRowModesModel,
@@ -39,14 +46,14 @@ import {
GridRowEditStopReasons,
GridEditInputCell,
GridValueSetterParams,
} from '@mui/x-data-grid';
} 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'];
const weekdays = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];

interface BottomBarProps {
getCostTotal: () => number;
@@ -81,58 +88,67 @@ const BottomBar = (props: BottomBarProps) => {
const handleAddClick = () => {
const id = newId;
setNewId(newId - 1);
setRows((oldRows) => [...oldRows, { id, projectCode: '', task: '', isNew: true }]);
setRows((oldRows) => [
...oldRows,
{ id, projectCode: "", task: "", isNew: true },
]);
setRowModesModel((oldModel) => ({
...oldModel,
[id]: { mode: GridRowModes.Edit, fieldToFocus: 'projectCode' },
[id]: { mode: GridRowModes.Edit, fieldToFocus: "projectCode" },
}));
};

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

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

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

return (
<Box flex={1} style={{color: invalid?"red":"black"}}>
<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'>
<div style={{ display: "flex", justifyContent: "flex", width: "100%" }}>
<Box flex={1.5} textAlign={"right"} marginRight="4rem">
<b>Total:</b>
</Box>
<TotalCell value={getCostTotal()}/>
<TotalCell value={getCostTotal()} />
</div>
<Button variant="outlined" color="primary" startIcon={<AddIcon />} onClick={handleAddClick} sx={{margin:'20px'}}>
<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%' }}>
<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;
@@ -144,30 +160,34 @@ const initialRows: GridRowsProp = [
date: new Date(),
description: "Taxi to client office",
cost: 169.5,
document: 'taxi_receipt.jpg',
document: "taxi_receipt.jpg",
},
{
id: 2,
date: dayjs().add(-14, 'days').toDate(),
date: dayjs().add(-14, "days").toDate(),
description: "MTR fee to Kowloon Bay Office",
cost: 15.5,
document: 'octopus_invoice.jpg',
document: "octopus_invoice.jpg",
},
{
id: 3,
date: dayjs().add(-44, 'days').toDate(),
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 [rowModesModel, setRowModesModel] = React.useState<GridRowModesModel>(
{},
);

const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
const handleRowEditStop: GridEventListener<"rowEditStop"> = (
params,
event,
) => {
if (params.reason === GridRowEditStopReasons.rowFocusOut) {
event.defaultMuiPrevented = true;
}
@@ -178,7 +198,6 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {
};

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

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

@@ -211,21 +230,21 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {
const getCostTotal = () => {
let sum = 0;
rows.forEach((row) => {
sum += row['cost']??0;
sum += row["cost"] ?? 0;
});
return sum;
};

const commonGridColConfig : any = {
type: 'number',
const commonGridColConfig: any = {
type: "number",
// sortable: false,
//width: 100,
flex: 1,
align: 'left',
headerAlign: 'left',
align: "left",
headerAlign: "left",
// headerClassName: 'header',
editable: true,
renderEditCell: (value : any) => (
renderEditCell: (value: any) => (
<GridEditInputCell
{...value}
inputProps={{
@@ -239,26 +258,28 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {

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

if (isInEditMode) {
return [
<GridActionsCellItem
key={`actions-${id}-save`}
icon={<SaveIcon />}
title="Save"
label="Save"
sx={{
color: 'primary.main',
color: "primary.main",
}}
onClick={handleSaveClick(id)}
/>,
<GridActionsCellItem
key={`actions-${id}-cancel`}
icon={<CancelIcon />}
title="Cancel"
label="Cancel"
@@ -271,6 +292,7 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {

return [
<GridActionsCellItem
key={`actions-${id}-edit`}
icon={<EditIcon />}
title="Edit"
label="Edit"
@@ -279,74 +301,80 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {
color="inherit"
/>,
<GridActionsCellItem
key={`actions-${id}-delete`}
title="Delete"
label="Delete"
icon={<DeleteIcon />}
onClick={handleDeleteClick(id)}
sx={{color:"red"}}
sx={{ color: "red" }}
/>,
];
},
},
{
field: 'date',
headerName: 'Invoice Date',
field: "date",
headerName: "Invoice Date",
// width: 220,
flex: 1,
editable: true,
type: 'date',
type: "date",
},
{
field: 'description',
headerName: 'Description',
field: "description",
headerName: "Description",
// width: 220,
flex: 2,
editable: true,
type: 'string',
type: "string",
},
{
field: 'cost',
headerName: 'Cost (HKD)',
field: "cost",
headerName: "Cost (HKD)",
editable: true,
type: 'number',
type: "number",
valueFormatter: (params) => {
return `$ ${params.value??0}`;
return `$ ${params.value ?? 0}`;
},
},
{
field: 'document',
headerName: 'Supporting Document',
type: 'string',
field: "document",
headerName: "Supporting Document",
type: "string",
editable: true,
flex: 2,
renderCell: (params) => {
return params.value?
(
return params.value ? (
<span>
<a href="" target="_blank" rel="noopener noreferrer">
{params.value}
</a>
</span>
) :
(<span style={{color: palette.text.disabled}}>No Documents</span>)
) : (
<span style={{ color: palette.text.disabled }}>No Documents</span>
);
},
renderEditCell: (params) => {
return params.value?
(
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>
<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 title="Add Document">
<AddPhotoAlternateOutlinedIcon
sx={{ fontSize: "25px", color: "green" }}
/>
</Button>
)
);
},
},
];
@@ -355,27 +383,27 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {
<Box
sx={{
// marginBottom: '-5px',
display: 'flex',
'flex-direction': 'column',
display: "flex",
"flex-direction": "column",
// 'justify-content': 'flex-end',
height: '100%',//'25rem',
width: '100%',
'& .actions': {
color: 'text.secondary',
height: "100%", //'25rem',
width: "100%",
"& .actions": {
color: "text.secondary",
},
'& .header': {
"& .header": {
backgroundColor: "#F8F9FA",
// border: 1,
// 'border-width': '1px',
// 'border-color': 'grey',
},
'& .textPrimary': {
color: 'text.primary',
"& .textPrimary": {
color: "text.primary",
},
}}
>
<DataGrid
sx={{flex:1}}
sx={{ flex: 1 }}
rows={rows}
columns={columns}
editMode="row"
@@ -386,22 +414,29 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => {
disableRowSelectionOnClick={true}
disableColumnMenu={true}
hideFooterPagination={true}
slots={{
// footer: EditFooter,
}}
slotProps={{
// footer: { setDay, setRows, setRowModesModel },
}}
slots={
{
// footer: EditFooter,
}
}
slotProps={
{
// footer: { setDay, setRows, setRowModesModel },
}
}
initialState={{
pagination: { paginationModel: { pageSize: 100 } },
}}
/>
<BottomBar getCostTotal={getCostTotal} setRows={setRows} setRowModesModel={setRowModesModel}
// sx={{flex:2}}

<BottomBar
getCostTotal={getCostTotal}
setRows={setRows}
setRowModesModel={setRowModesModel}
// sx={{flex:2}}
/>
</Box>
);
}
};

export default ClaimInputGrid;

+ 140
- 71
src/components/CustomCardGrid/CustomCardGrid.tsx View File

@@ -1,11 +1,18 @@
import * as React from 'react';
import { Card, CardHeader, CardContent, SxProps, Theme, Grid } from '@mui/material';
import { DataGrid, GridColDef } from '@mui/x-data-grid';
import { darken, lighten, styled } from '@mui/material/styles';
import { PROJECT_CARD_STYLE } from '@/theme/colorConst';
import { useRef, useEffect, useState } from 'react';
import Swal from 'sweetalert2';
import styledcmp from 'styled-components';
import * as React from "react";
import {
Card,
CardHeader,
CardContent,
SxProps,
Theme,
Grid,
} from "@mui/material";
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import { darken, lighten, styled } from "@mui/material/styles";
import { PROJECT_CARD_STYLE } from "@/theme/colorConst";
import { useRef, useEffect, useState } from "react";
import Swal from "sweetalert2";
import styledcmp from "styled-components";

const CardWrapper = styledcmp.div`
/* Styles for the card when not hovered */
@@ -23,7 +30,7 @@ const CardWrapper = styledcmp.div`
interface CustomCardGridProps {
Title?: string;
cardsPerRow?: number;
rows?: any[];
rows?: any[];
columns?: any[];
items: any[];
columnWidth?: number;
@@ -46,73 +53,124 @@ const CustomCardGrid: React.FC<CustomCardGridProps> = ({
dataGridHeight,
...props
}) => {
const getBackgroundColor = (color: string, mode: 'light' | 'dark') =>
mode === 'dark' ? darken(color, 0.7) : lighten(color, 0.7);
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 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 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 getSelectedHoverBackgroundColor = (
color: string,
mode: "light" | "dark",
) => (mode === "dark" ? darken(color, 0.4) : lighten(color, 0.4));

const StyledCard = styled(Card)(({ theme }) => ({
'& .super-app-theme--Open': {
backgroundColor: getBackgroundColor(theme.palette.info.main, theme.palette.mode),
'&:hover': {
backgroundColor: getHoverBackgroundColor(theme.palette.info.main, theme.palette.mode)
"& .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,
),
},
},
'&.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)
"& .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,
),
},
},
'&.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)
"& .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,
),
},
},
'&.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)
"& .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)
}
}
}
"&.Mui-selected": {
backgroundColor: getSelectedBackgroundColor(
theme.palette.error.main,
theme.palette.mode,
),
"&:hover": {
backgroundColor: getSelectedHoverBackgroundColor(
theme.palette.error.main,
theme.palette.mode,
),
},
},
},
}));

const CardItem = (item: any) => {
const cardItem = item.item as Record<string, string>;
return props.cardStyle?? (
// <Grid item sx={{ m: 3 }}>
return (
props.cardStyle ?? (
// <Grid item sx={{ m: 3 }}>
<StyledCard style={PROJECT_CARD_STYLE}>
<CardContent>
{Object.keys(cardItem).map((key) => (
@@ -122,28 +180,36 @@ const CustomCardGrid: React.FC<CustomCardGridProps> = ({
))}
</CardContent>
</StyledCard>
// </Grid>
// </Grid>
)
);
};

const containerRef = useRef<HTMLDivElement>(null!);

const [cardMargin, setCardMargin] = useState(1.5);
useEffect(() => {
console.log(CardItem);
const resizeHandler = () => {
const containerWidth = containerRef.current.offsetWidth;
const cardCount = items.length;
const rootSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
setCardMargin((containerWidth - cardsPerRow * (rootSize * parseInt(PROJECT_CARD_STYLE.width.slice(0, -3),10))) /(2 * cardsPerRow));
const rootSize = parseFloat(
getComputedStyle(document.documentElement).fontSize,
);
setCardMargin(
(containerWidth -
cardsPerRow *
(rootSize * parseInt(PROJECT_CARD_STYLE.width.slice(0, -3), 10))) /
(2 * cardsPerRow),
);
// Set the cardMargin value using style={{margin: `${cardMargin}px`, ...PROJECT_CARD_STYLE}}
};
window.addEventListener('resize', resizeHandler);
window.addEventListener("resize", resizeHandler);
resizeHandler(); // Initial calculation
// Swal.fire({
// title: 'Error! ',
// text: `Card Count is ${items.length}`,
@@ -152,16 +218,19 @@ const CustomCardGrid: React.FC<CustomCardGridProps> = ({
// })

return () => {
window.removeEventListener('resize', resizeHandler);
window.removeEventListener("resize", resizeHandler);
};
}, [items]);

return (
<div ref={containerRef} style={{display:'flex', flexWrap:'wrap', alignItems: 'flex-start'}}>
<div
ref={containerRef}
style={{ display: "flex", flexWrap: "wrap", alignItems: "flex-start" }}
>
{/* <p>width is {containerRef.current == null? "idk":containerRef.current.offsetWidth}, margin is {cardMargin}</p> */}
{items.map((item, index) => (
<div key={index}>
{props.cardStyle? props.cardStyle(item) : <CardItem item={item}/>}
{props.cardStyle ? props.cardStyle(item) : <CardItem item={item} />}
</div>
))}
</div>


+ 241
- 177
src/components/CustomDatagrid/CustomDatagrid.tsx View File

@@ -1,13 +1,13 @@
"use client";
import * as React from 'react';
import { Card, CardHeader, CardContent, SxProps, Theme } from '@mui/material';
import { DataGrid, GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import { darken, lighten, styled } from '@mui/material/styles';
import { useState } from 'react'
import * as React from "react";
import { Card, CardHeader, CardContent, SxProps, Theme } from "@mui/material";
import { DataGrid, GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { darken, lighten, styled } from "@mui/material/styles";
import { useState } from "react";

interface CustomDatagridProps {
Title?: string;
rows: any[];
rows: any[];
columns: any[];
columnWidth?: number;
Style?: boolean;
@@ -15,7 +15,9 @@ interface CustomDatagridProps {
dataGridHeight?: number;
[key: string]: any;
checkboxSelection?: boolean;
onRowSelectionModelChange?: (newSelectionModel: GridRowSelectionModel) => void;
onRowSelectionModelChange?: (
newSelectionModel: GridRowSelectionModel,
) => void;
selectionModel?: any;
}

@@ -44,200 +46,262 @@ const CustomDatagrid: React.FC<CustomDatagridProps> = ({
});

// Event handler to be called when the selection changes
const handleSelectionModelChange = (newSelectionModel: GridRowSelectionModel) => {
const handleSelectionModelChange = (
newSelectionModel: GridRowSelectionModel,
) => {
// setSelectionModel(newSelectionModel);
// To log selected row data, filter rows based on the new selection model
const selectedRowsData = rows.filter((row) =>
newSelectionModel.includes(row.id)
newSelectionModel.includes(row.id),
);
console.log(selectedRowsData);
};

const getBackgroundColor = (color: string, mode: 'light' | 'dark') =>
mode === 'dark' ? darken(color, 0.7) : lighten(color, 0.7);
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 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 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 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)
"& .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,
),
},
},
'&.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)
"& .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,
),
},
},
'&.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)
"& .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,
),
},
},
'&.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)
"& .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)
}
}
}
"&.Mui-selected": {
backgroundColor: getSelectedBackgroundColor(
theme.palette.error.main,
theme.palette.mode,
),
"&:hover": {
backgroundColor: getSelectedHoverBackgroundColor(
theme.palette.error.main,
theme.palette.mode,
),
},
},
},
}));

return (
<div className="mt-5 mb-5" style={{ height: dataGridHeight ?? 400, width: '100%'}}>
<div
className="mt-5 mb-5"
style={{ height: dataGridHeight ?? 400, width: "100%" }}
>
{Title ? (
<Card style={{marginRight:10}}>
{Title && <CardHeader className="text-slate-500" title={Title} />}
<CardContent style={{ display: "flex", alignItems: "center", justifyContent: "center", marginTop:-20 }}>
{Style ? (
<StyledDataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 1,
border: 0,
borderColor: 'primary.light',
'& .MuiDataGrid-cell:hover': {
color: 'primary.main'
},
height: dataGridHeight ?? 400,
'& .MuiDataGrid-root': {
overflow: 'auto',
},
'& .MuiDataGrid-columnHeaderTitle': {
fontWeight: 'bold',
},
...sx
}}
{...props}
/>
) : (
<DataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 2,
border: 2,
borderColor: 'primary.light',
'& .MuiDataGrid-cell:hover': {
color: 'primary.main'
},
height: 300,
'& .MuiDataGrid-root': {
overflow: 'auto',
},
...sx
}}
{...props}
/>
)}
<Card style={{ marginRight: 10 }}>
{Title && <CardHeader className="text-slate-500" title={Title} />}
<CardContent
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
marginTop: -20,
}}
>
{Style ? (
<StyledDataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 1,
border: 0,
borderColor: "primary.light",
"& .MuiDataGrid-cell:hover": {
color: "primary.main",
},
height: dataGridHeight ?? 400,
"& .MuiDataGrid-root": {
overflow: "auto",
},
"& .MuiDataGrid-columnHeaderTitle": {
fontWeight: "bold",
},
...sx,
}}
{...props}
/>
) : (
<DataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 2,
border: 2,
borderColor: "primary.light",
"& .MuiDataGrid-cell:hover": {
color: "primary.main",
},
height: 300,
"& .MuiDataGrid-root": {
overflow: "auto",
},
...sx,
}}
{...props}
/>
)}
</CardContent>
</Card>)
: (Style ? (
<StyledDataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
style={{marginRight:20}}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 1,
border: 0,
borderColor: 'primary.light',
'& .MuiDataGrid-cell:hover': {
color: 'primary.main'
},
height: dataGridHeight ?? 400,
'& .MuiDataGrid-root': {
overflow: 'auto',
},
'& .MuiDataGrid-columnHeaderTitle': {
fontWeight: 'bold',
},
...sx
}}
{...props}
/>
) : (
<DataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
style={{marginRight:0}}
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 2,
border: 2,
borderColor: 'primary.light',
'& .MuiDataGrid-cell:hover': {
color: 'primary.main'
},
height: 300,
'& .MuiDataGrid-root': {
overflow: 'auto',
},
...sx
}}
{...props}
/>
))}
</Card>
) : Style ? (
<StyledDataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
style={{ marginRight: 20 }}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 1,
border: 0,
borderColor: "primary.light",
"& .MuiDataGrid-cell:hover": {
color: "primary.main",
},
height: dataGridHeight ?? 400,
"& .MuiDataGrid-root": {
overflow: "auto",
},
"& .MuiDataGrid-columnHeaderTitle": {
fontWeight: "bold",
},
...sx,
}}
{...props}
/>
) : (
<DataGrid
rows={rowsWithDefaultValues}
columns={modifiedColumns}
editMode="row"
style={{ marginRight: 0 }}
checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange}
initialState={{
pagination: { paginationModel: { pageSize: 10 } },
}}
className="customDataGrid"
sx={{
boxShadow: 2,
border: 2,
borderColor: "primary.light",
"& .MuiDataGrid-cell:hover": {
color: "primary.main",
},
height: 300,
"& .MuiDataGrid-root": {
overflow: "auto",
},
...sx,
}}
{...props}
/>
)}
</div>
);
};


+ 46
- 27
src/components/CustomModal/CustomModal.tsx View File

@@ -1,11 +1,21 @@
import * as React from 'react';
import { Card, CardHeader, CardContent, SxProps, Theme, Grid, Modal, Typography, Button } from '@mui/material';
import { DataGrid, GridColDef } from '@mui/x-data-grid';
import { darken, lighten, styled } from '@mui/material/styles';
import { PROJECT_MODAL_STYLE } from '@/theme/colorConst';
import { useRef, useEffect, useState } from 'react';
import Swal from 'sweetalert2';
import styledcmp from 'styled-components';
import * as React from "react";
import {
Card,
CardHeader,
CardContent,
SxProps,
Theme,
Grid,
Modal,
Typography,
Button,
} from "@mui/material";
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import { darken, lighten, styled } from "@mui/material/styles";
import { PROJECT_MODAL_STYLE } from "@/theme/colorConst";
import { useRef, useEffect, useState } from "react";
import Swal from "sweetalert2";
import styledcmp from "styled-components";

const CardWrapper = styledcmp.div`
/* Styles for the card when not hovered */
@@ -28,35 +38,44 @@ interface CustomModalProps {
}

const CustomModal: React.FC<CustomModalProps> = ({ ...props }) => {

const ModalContent = () => {
return (
// <Grid item sx={{ m: 3 }}>
<div >
<Typography variant="h6" id="modal-title">
{props.title??"Modal Title"}
</Typography>
<Typography variant="h6" id="modal-title" style={{ alignSelf: 'flex-start', margin: '10px' }}>
Modal Content
</Typography>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<Button variant="contained" onClick={props.onClose}>
Confirm
</Button>
<Button variant="contained" onClick={props.onClose}>
Cancel
</Button>
</div>
<div>
<Typography variant="h6" id="modal-title">
{props.title ?? "Modal Title"}
</Typography>
<Typography
variant="h6"
id="modal-title"
style={{ alignSelf: "flex-start", margin: "10px" }}
>
Modal Content
</Typography>
<div
style={{
display: "flex",
justifyContent: "space-between",
width: "100%",
}}
>
<Button variant="contained" onClick={props.onClose}>
Confirm
</Button>
<Button variant="contained" onClick={props.onClose}>
Cancel
</Button>
</div>
</div>
// </Grid>
);
};

return (
<Modal open={props.isOpen} onClose={props.onClose}>
{props.modalStyle? <props.modalStyle props={props}/> : <ModalContent/>}
{props.modalStyle ? <props.modalStyle props={props} /> : <ModalContent />}
</Modal>
);
};
export default CustomModal;
export default CustomModal;

+ 179
- 68
src/components/CustomSearchForm/CustomSearchForm.tsx View File

@@ -1,5 +1,5 @@
"use client";
import React, { useState, FC } from 'react';
import React, { useState, FC } from "react";
import {
Stack,
Typography,
@@ -15,16 +15,16 @@ import {
InputLabel,
Select,
MenuItem,
ThemeProvider
} from '@mui/material';
import { useForm, Controller } from 'react-hook-form';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import dayjs from 'dayjs';
import SearchIcon from '@mui/icons-material/Search';
import RefreshIcon from '@mui/icons-material/Refresh';
import { DemoItem } from '@mui/x-date-pickers/internals/demo';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
ThemeProvider,
} from "@mui/material";
import { useForm, Controller } from "react-hook-form";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs";
import SearchIcon from "@mui/icons-material/Search";
import RefreshIcon from "@mui/icons-material/Refresh";
import { DemoItem } from "@mui/x-date-pickers/internals/demo";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";

interface Field {
id: any;
@@ -50,29 +50,38 @@ interface SearchFormProps {
sx?: any;
}

const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx }) => {
const FormComponent: FC<FormComponentProps> = ({
fields,
onSubmit,
resetForm,
sx,
}) => {
const { reset, register, handleSubmit, control } = useForm();
const [fromDate, setFromDate] = useState<dayjs.Dayjs | null>(null);
const [dayRangeFromDate, setDayRangeFromDate] = useState<dayjs.Dayjs | null>(null);
const [dayRangeToDate, setDayRangeToDate] = useState<dayjs.Dayjs | null>(null);
const [dayRangeFromDate, setDayRangeFromDate] = useState<dayjs.Dayjs | null>(
null,
);
const [dayRangeToDate, setDayRangeToDate] = useState<dayjs.Dayjs | null>(
null,
);
const [value, setValue] = useState<{ [key: string]: any }>({});
const [checkbox1, setCheckbox1] = useState(false);

const handleFormSubmit = (data: any) => {
if (fromDate != null || fromDate != undefined) {
data.fromDate = dayjs(fromDate).format('YYYY-MM-DD');
data.fromDate = dayjs(fromDate).format("YYYY-MM-DD");
}
if (value !== null) {
data.dropdownCombo = value
data.dropdownCombo = value;
}
if (value !== null) {
data.checkbox = checkbox1;
}
if (dayRangeFromDate != null || dayRangeFromDate != undefined) {
data.dayRangeFromDate = dayjs(dayRangeFromDate).format('YYYY-MM-DD');
data.dayRangeFromDate = dayjs(dayRangeFromDate).format("YYYY-MM-DD");
}
if (dayRangeToDate != null || dayRangeToDate != undefined) {
data.dayRangeToDate = dayjs(dayRangeToDate).format('YYYY-MM-DD');
data.dayRangeToDate = dayjs(dayRangeToDate).format("YYYY-MM-DD");
}
onSubmit(data);
};
@@ -82,8 +91,8 @@ const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx
resetForm();
setFromDate(null);
fields.forEach((field) => {
if (typeof(field.setValue) === "function") {
field.setValue(typeof (field.value) === "boolean" ? false : null);
if (typeof field.setValue === "function") {
field.setValue(typeof field.value === "boolean" ? false : null);
} else if (Array.isArray(field.setValue)) {
field.setValue.forEach((setFunc) => {
setFunc(null);
@@ -96,11 +105,21 @@ const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx
<form onSubmit={handleSubmit(handleFormSubmit)}>
<Grid container alignItems="center">
{fields.map((field) => {
if (field.type === 'dropdown') {
if (field.type === "dropdown") {
return (
<Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}>
<Grid
item
xs={12}
sm={5.5}
md={5.5}
lg={5.5}
sx={{ ml: 3, mr: 3, mb: 3 }}
key={field.id}
>
<FormControl fullWidth>
<InputLabel id={`${field.id}-label`}>{field.label}</InputLabel>
<InputLabel id={`${field.id}-label`}>
{field.label}
</InputLabel>
<Controller
name={field.id}
control={control}
@@ -115,11 +134,13 @@ const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx
}}
>
{field.options?.map((option) => (
<MenuItem
value={option.id ?? JSON.stringify(option)}
<MenuItem
value={option.id ?? JSON.stringify(option)}
key={option.id ?? JSON.stringify(option)}
>
{option.id !== undefined ? option.label : JSON.stringify(option)}
{option.id !== undefined
? option.label
: JSON.stringify(option)}
</MenuItem>
))}
</Select>
@@ -128,19 +149,27 @@ const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx
</FormControl>
</Grid>
);
} else if (field.type === 'date') {
} else if (field.type === "date") {
return (
<Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}>
<Grid
item
xs={12}
sm={5.5}
md={5.5}
lg={5.5}
sx={{ ml: 3, mr: 3, mb: 3 }}
key={field.id}
>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DemoItem>
<DatePicker
slotProps={{
textField: {
id:field.id,
id: field.id,
},
}}
label={field.label}
value={fromDate === null ? null : dayjs(fromDate)}
value={fromDate === null ? null : dayjs(fromDate)}
onChange={(newValue) => {
setFromDate(newValue);
}}
@@ -149,16 +178,24 @@ const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx
</LocalizationProvider>
</Grid>
);
} else if (field.type === 'checkbox') {
} else if (field.type === "checkbox") {
return (
<Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}>
<Grid
item
xs={12}
sm={5.5}
md={5.5}
lg={5.5}
sx={{ ml: 3, mr: 3, mb: 3 }}
key={field.id}
>
<FormControlLabel
control={
<Checkbox
id={field.id}
checked={field.value}
onChange={(event) => {
if (typeof field.setValue === 'function') {
if (typeof field.setValue === "function") {
field.setValue(event.target.checked);
setCheckbox1(event.target.checked);
}
@@ -166,102 +203,176 @@ const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx
color="primary"
/>
}
label={<Typography style={{fontSize:"1.15em"}}>{field.label}</Typography>}
label={
<Typography style={{ fontSize: "1.15em" }}>
{field.label}
</Typography>
}
/>
</Grid>
)
} else if (field.type === 'dateRange') {
);
} else if (field.type === "dateRange") {
return (
<Grid container key={field.id[0]}>
<Grid item xs={12} sm={7} md={7} lg={7} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Grid
item
xs={12}
sm={7}
md={7}
lg={7}
sx={{ ml: 3, mr: 3, mb: 3 }}
>
<Grid container>
<Grid>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
label={field.label[0]}
value={field.value[0]}
onChange={(newValue) => setDayRangeFromDate(newValue)}
/>
<DatePicker
label={field.label[0]}
value={field.value[0]}
onChange={(newValue) => setDayRangeFromDate(newValue)}
/>
</LocalizationProvider>
</Grid>

<Grid item xs={1.5} sm={1.5} md={1.5} lg={1.5} sx={{ display: 'flex', justifyContent: "center", alignItems: 'center' }}>
To
</Grid>
<Grid
item
xs={1.5}
sm={1.5}
md={1.5}
lg={1.5}
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
To
</Grid>

<Grid item xs={5.25} sm={5.25} md={5.25} lg={5.25}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
label={field.label[1]}
value={field.value[1]}
onChange={(newValue) => setDayRangeToDate(newValue)}
/>

<DatePicker
label={field.label[1]}
value={field.value[1]}
onChange={(newValue) => setDayRangeToDate(newValue)}
/>
</LocalizationProvider>
</Grid>
</Grid>
</Grid>
</Grid>
)
);
}
return (
<Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}>
<Grid
item
xs={12}
sm={5.5}
md={5.5}
lg={5.5}
sx={{ ml: 3, mr: 3, mb: 3 }}
key={field.id}
>
<TextField
fullWidth
{...register(field.id)}
id={field.id}
label={field.label}
defaultValue={field.value !== undefined && field.value !== null ? `${field.value}` : ''}
defaultValue={
field.value !== undefined && field.value !== null
? `${field.value}`
: ""
}
required={field.required === true ? field.required : false}
sx={{ ...sx }}
InputProps={{
style: {
borderRadius: "10px",
}
},
}}
/>
</Grid>
);
})}
</Grid>
<Grid container maxWidth="lg" justifyContent="space-between" style={{marginTop:-20}}>
<Grid
container
maxWidth="lg"
justifyContent="space-between"
style={{ marginTop: -20 }}
>
<Stack direction="row">
<Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}>
<Button className="h-12 w-32" style={{backgroundColor:"#92c1e9",color:"white",fontSize:"1.15em",fontWeight:100,borderRadius:10}} type="submit">
<SearchIcon/>&nbsp;Search
<Button
className="h-12 w-32"
style={{
backgroundColor: "#92c1e9",
color: "white",
fontSize: "1.15em",
fontWeight: 100,
borderRadius: 10,
}}
type="submit"
>
<SearchIcon />
&nbsp;Search
</Button>
</Grid>
<Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}>
<Button className="h-12 w-32" style={{backgroundColor:"#f890a5",color:"white",fontSize:"1.15em",fontWeight:100,borderRadius:10}} onClick={handleFormReset}>
<RefreshIcon/>&nbsp;Reset
<Button
className="h-12 w-32"
style={{
backgroundColor: "#f890a5",
color: "white",
fontSize: "1.15em",
fontWeight: 100,
borderRadius: 10,
}}
onClick={handleFormReset}
>
<RefreshIcon />
&nbsp;Reset
</Button>
</Grid>
</Stack>
</Grid>
</Grid>
</form>
);
};

const CustomSearchForm: FC<SearchFormProps> = ({ applySearch, fields, title, sx }) => {
const CustomSearchForm: FC<SearchFormProps> = ({
applySearch,
fields,
title,
sx,
}) => {
const Title = title || "Searching Criteria";

const handleSubmit = (data: any) => {
if (applySearch) {
applySearch(data);
} else {
console.log('applySearch function is null');
console.log("applySearch function is null");
}
};

const handleFormReset = () => {
console.log('Form Reset');
console.log("Form Reset");
};

return (
<Card style={{marginRight:20}}>
<CardHeader className="text-slate-500 " style={{marginTop:-5}} title={Title}></CardHeader>
<FormComponent fields={fields} onSubmit={handleSubmit} resetForm={handleFormReset} sx={sx} />
</Card>
<Card style={{ marginRight: 20 }}>
<CardHeader
className="text-slate-500 "
style={{ marginTop: -5 }}
title={Title}
></CardHeader>
<FormComponent
fields={fields}
onSubmit={handleSubmit}
resetForm={handleFormReset}
sx={sx}
/>
</Card>
);
};



+ 4
- 4
src/components/DashboardPage/DashboardPage.tsx View File

@@ -1,12 +1,12 @@
"use client"
"use client";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import PageTitle from "../PageTitle/PageTitle";
import DashboardTabButton from "./DashboardTabButton";
import { ThemeProvider } from '@mui/material/styles';
import theme from '../../theme';
import { ThemeProvider } from "@mui/material/styles";
import theme from "../../theme";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import React, { useCallback, useState } from "react";
@@ -38,7 +38,7 @@ const DashboardPage: React.FC = () => {
<Tab label="Staff Utilization" />
</Tabs>
{tabIndex === 2 && <ProgressByClient />}
{/* <Grid container height="100vh" style={{ backgroundColor: theme.palette.background.default}}>
{/* <Grid container height="100vh" style={{ backgroundColor: theme.palette.background.default}}>
<Grid item sm>
<PageTitle BigTitle={"Dashboards"}/>
<DashboardTabButton/>


+ 43
- 43
src/components/DashboardPage/DashboardTabButton.tsx View File

@@ -1,6 +1,6 @@
"use client";
import Grid from "@mui/material/Grid";
import { useState,useCallback} from 'react'
import { useState, useCallback } from "react";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
@@ -8,22 +8,22 @@ import PageTitle from "../PageTitle/PageTitle";
import ProgressByClient from "./ProgressByClient";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import '../../app/global.css';
import "../../app/global.css";

const DashboardTabButton: React.FC = () => {
const [activeTab, setActiveTab] = useState('financialSummary');
const [activeTab, setActiveTab] = useState("financialSummary");
const { t } = useTranslation("dashboard");
const renderContent = () => {
switch (activeTab) {
case 'financialSummary':
case "financialSummary":
return <div>Project Financial Summary</div>;
case 'cashFlow':
case "cashFlow":
return <div>Project Cash Flow</div>;
case 'progressByClient':
return <ProgressByClient/>;
case 'resourceUtilization':
case "progressByClient":
return <ProgressByClient />;
case "resourceUtilization":
return <div>Project Resource Utilization</div>;
case 'staffUtilization':
case "staffUtilization":
return <div>Staff Utilization</div>;
default:
return <div>Project Financial Summary</div>;
@@ -37,40 +37,40 @@ const DashboardTabButton: React.FC = () => {
[],
);
return (
// <Grid item sm>
// <div style={{marginLeft:20}}>
// {activeTab !== 'financialSummary' ?
// <button onClick={() => setActiveTab('financialSummary')}className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:40,width:250,fontSize:18}}>Project Financial Summary</button> :
// <button onClick={() => setActiveTab('financialSummary')}className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:40,width:250,fontSize:18}}>Project Financial Summary</button>
// }
// {activeTab !== 'cashFlow' ?
// <button onClick={() => setActiveTab('cashFlow')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Cash Flow</button> :
// <button onClick={() => setActiveTab('cashFlow')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Cash Flow</button>
// }
// {activeTab !== 'progressByClient' ?
// <button onClick={() => setActiveTab('progressByClient')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Progress by Client</button> :
// <button onClick={() => setActiveTab('progressByClient')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Progress by Client</button>
// }
// {activeTab !== 'resourceUtilization' ?
// <button onClick={() => setActiveTab('resourceUtilization')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Resource Utilization</button> :
// <button onClick={() => setActiveTab('resourceUtilization')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Resource Utilization</button>
// }
// {activeTab !== 'staffUtilization' ?
// <button onClick={() => setActiveTab('staffUtilization')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Staff Utilization</button> :
// <button onClick={() => setActiveTab('staffUtilization')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Staff Utilization</button>
// }
// </div>
// <div style={{marginLeft:20,marginTop:20}}>
// {renderContent()}
// </div>
// </Grid>
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab label="Project Financial Summary" />
<Tab label="Project Cash Flow" />
<Tab label="Project Progress by Client" />
<Tab label="Project Resource Utilization" />
<Tab label="Staff Utilization" />
</Tabs>
// <Grid item sm>
// <div style={{marginLeft:20}}>
// {activeTab !== 'financialSummary' ?
// <button onClick={() => setActiveTab('financialSummary')}className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:40,width:250,fontSize:18}}>Project Financial Summary</button> :
// <button onClick={() => setActiveTab('financialSummary')}className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:40,width:250,fontSize:18}}>Project Financial Summary</button>
// }
// {activeTab !== 'cashFlow' ?
// <button onClick={() => setActiveTab('cashFlow')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Cash Flow</button> :
// <button onClick={() => setActiveTab('cashFlow')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Cash Flow</button>
// }
// {activeTab !== 'progressByClient' ?
// <button onClick={() => setActiveTab('progressByClient')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Progress by Client</button> :
// <button onClick={() => setActiveTab('progressByClient')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Progress by Client</button>
// }
// {activeTab !== 'resourceUtilization' ?
// <button onClick={() => setActiveTab('resourceUtilization')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Resource Utilization</button> :
// <button onClick={() => setActiveTab('resourceUtilization')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Project Resource Utilization</button>
// }
// {activeTab !== 'staffUtilization' ?
// <button onClick={() => setActiveTab('staffUtilization')} className="hover:bg-sky-100 hover:cursor-pointer rounded-lg bg-transparent border-slate-400 border-solid text-slate-400 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Staff Utilization</button> :
// <button onClick={() => setActiveTab('staffUtilization')} className="rounded-lg bg-sky-100 border-cyan-500 border-solid text-cyan-500 ml-0.5 mt-0.5" style={{height:39,width:250,fontSize:18}}>Staff Utilization</button>
// }
// </div>
// <div style={{marginLeft:20,marginTop:20}}>
// {renderContent()}
// </div>
// </Grid>
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab label="Project Financial Summary" />
<Tab label="Project Cash Flow" />
<Tab label="Project Progress by Client" />
<Tab label="Project Resource Utilization" />
<Tab label="Staff Utilization" />
</Tabs>
);
};



+ 370
- 229
src/components/DashboardPage/ProgressByClient.tsx View File

@@ -1,76 +1,160 @@

import * as React from "react";
import Grid from "@mui/material/Grid";
import { useState,useEffect, useMemo } from 'react'
import { useState, useEffect, useMemo } from "react";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {Card,CardHeader} from '@mui/material';
import { Card, CardHeader } from "@mui/material";
import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
import CustomDatagrid from '../CustomDatagrid/CustomDatagrid';
import ReactApexChart from 'react-apexcharts';
import { ApexOptions } from 'apexcharts';
import { GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import ReportProblemIcon from '@mui/icons-material/ReportProblem';
import dynamic from 'next/dynamic';
import '../../app/global.css';
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import ReactApexChart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import ReportProblemIcon from "@mui/icons-material/ReportProblem";
import dynamic from "next/dynamic";
import "../../app/global.css";
import { AnyARecord } from "dns";
import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense } from "react";

const ProgressByClient: React.FC = () => {
const [activeTab, setActiveTab] = useState('financialSummary');
const [SearchCriteria, setSearchCriteria] = React.useState({})
const [activeTab, setActiveTab] = useState("financialSummary");
const [SearchCriteria, setSearchCriteria] = React.useState({});
const { t } = useTranslation("dashboard");

const [clientCode, setClientCode] = useState('');
const [clientName, setClientName] = useState('');
const [subsidiaryClientCode, setSubsidiaryClientCode] = useState('');
const [subsidiaryClientName, setSubsidiaryClientName] = useState('');
const [projectArray, setProjectArray] : any[] = useState([]);
const [percentageArray, setPercentageArray] : any[] = useState([]);
const [selectionModel, setSelectionModel] : any[] = React.useState([]);
const [dropdownDemo, setDropdownDemo] = useState('');
const [clientCode, setClientCode] = useState("");
const [clientName, setClientName] = useState("");
const [subsidiaryClientCode, setSubsidiaryClientCode] = useState("");
const [subsidiaryClientName, setSubsidiaryClientName] = useState("");
const [projectArray, setProjectArray]: any[] = useState([]);
const [percentageArray, setPercentageArray]: any[] = useState([]);
const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const [dropdownDemo, setDropdownDemo] = useState("");
const [dateDemo, setDateDemo] = useState(null);
const [checkboxDemo, setCheckboxDemo] = useState(false);
const [receiptFromDate, setReceiptFromDate] = useState(null);
const [receiptToDate, setReceiptToDate] = useState(null);
const [selectedRows, setSelectedRows] = useState([]);
const rows = [{id: 1,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"N/A", clientSubsidiaryName:"N/A", noOfProjects:"5"},
{id: 2,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"SUBS-001", clientSubsidiaryName:"Subsidiary A", noOfProjects:"5"},
{id: 3,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"SUBS-002", clientSubsidiaryName:"Subsidiary B", noOfProjects:"3"},
{id: 4,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"SUBS-003", clientSubsidiaryName:"Subsidiary C", noOfProjects:"1"}
]
const rows2 = [{id: 1,project:"Consultancy Project 123",team:"XXX", teamLeader:"XXX", currentStage:"Contract Documentation", budgetedManhour:"200.00",spentManhour:"120.00",remainedManhour:"80.00",comingPaymentMilestone:"31/03/2024",alert:false},
{id: 2,project:"Consultancy Project 456",team:"XXX", teamLeader:"XXX", currentStage:"Report Preparation", budgetedManhour:"400.00",spentManhour:"200.00",remainedManhour:"200.00",comingPaymentMilestone:"20/02/2024",alert:false},
{id: 3,project:"Construction Project A",team:"YYY", teamLeader:"YYY", currentStage:"Construction", budgetedManhour:"187.50",spentManhour:"200.00",remainedManhour:"12.50",comingPaymentMilestone:"13/12/2023",alert:true},
{id: 4,project:"Construction Project B",team:"XXX", teamLeader:"XXX", currentStage:"Post Construction", budgetedManhour:"100.00",spentManhour:"40.00",remainedManhour:"60.00",comingPaymentMilestone:"05/01/2024",alert:false},
{id: 5,project:"Construction Project C",team:"YYY", teamLeader:"YYY", currentStage:"Construction", budgetedManhour:"300.00",spentManhour:"150.00",remainedManhour:"150.00",comingPaymentMilestone:"31/03/2024",alert:false},
]
const rows = [
{
id: 1,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "N/A",
clientSubsidiaryName: "N/A",
noOfProjects: "5",
},
{
id: 2,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "SUBS-001",
clientSubsidiaryName: "Subsidiary A",
noOfProjects: "5",
},
{
id: 3,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "SUBS-002",
clientSubsidiaryName: "Subsidiary B",
noOfProjects: "3",
},
{
id: 4,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "SUBS-003",
clientSubsidiaryName: "Subsidiary C",
noOfProjects: "1",
},
];
const rows2 = [
{
id: 1,
project: "Consultancy Project 123",
team: "XXX",
teamLeader: "XXX",
currentStage: "Contract Documentation",
budgetedManhour: "200.00",
spentManhour: "120.00",
remainedManhour: "80.00",
comingPaymentMilestone: "31/03/2024",
alert: false,
},
{
id: 2,
project: "Consultancy Project 456",
team: "XXX",
teamLeader: "XXX",
currentStage: "Report Preparation",
budgetedManhour: "400.00",
spentManhour: "200.00",
remainedManhour: "200.00",
comingPaymentMilestone: "20/02/2024",
alert: false,
},
{
id: 3,
project: "Construction Project A",
team: "YYY",
teamLeader: "YYY",
currentStage: "Construction",
budgetedManhour: "187.50",
spentManhour: "200.00",
remainedManhour: "12.50",
comingPaymentMilestone: "13/12/2023",
alert: true,
},
{
id: 4,
project: "Construction Project B",
team: "XXX",
teamLeader: "XXX",
currentStage: "Post Construction",
budgetedManhour: "100.00",
spentManhour: "40.00",
remainedManhour: "60.00",
comingPaymentMilestone: "05/01/2024",
alert: false,
},
{
id: 5,
project: "Construction Project C",
team: "YYY",
teamLeader: "YYY",
currentStage: "Construction",
budgetedManhour: "300.00",
spentManhour: "150.00",
remainedManhour: "150.00",
comingPaymentMilestone: "31/03/2024",
alert: false,
},
];

const columns = [
{
id: 'clientCode',
field: 'clientCode',
headerName: "Client Code",
flex: 1,
{
id: "clientCode",
field: "clientCode",
headerName: "Client Code",
flex: 1,
},
{
id: 'clientName',
field: 'clientName',
headerName: "Client Name",
flex: 1,
id: "clientName",
field: "clientName",
headerName: "Client Name",
flex: 1,
},
{
id: 'clientSubsidiaryCode',
field: 'clientSubsidiaryCode',
id: "clientSubsidiaryCode",
field: "clientSubsidiaryCode",
headerName: "Client Subsidiary Code",
flex: 1,
},
{
id: 'noOfProjects',
field: 'noOfProjects',
id: "noOfProjects",
field: "noOfProjects",
headerName: "No. of Projects",
flex: 1,
},
@@ -78,99 +162,119 @@ const ProgressByClient: React.FC = () => {

const columns2 = [
{
id: 'project',
field: 'project',
id: "project",
field: "project",
headerName: "Project",
flex: 1,
},
{
id: 'team',
field: 'team',
},
{
id: "team",
field: "team",
headerName: "Team",
flex: 1,
},
{
id: 'teamLeader',
field: 'teamLeader',
headerName: "Team Leader",
flex: 1,
},
{
id: 'currentStage',
field: 'currentStage',
headerName: "Current Stage",
flex: 1,
},
{
id: 'budgetedManhour',
field: 'budgetedManhour',
headerName: "Budgeted Manhour",
flex: 1,
},
{
id: 'spentManhour',
field: 'spentManhour',
headerName: "Spent Manhour",
renderCell: (params:any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return(
<span className="text-red-300">{params.row.spentManhour}</span>
)
} else {
return (
<span>{params.row.spentManhour}</span>
)
}
},
flex: 1,
},
{
id: 'remainedManhour',
field: 'remainedManhour',
headerName: "Remained Manhour",
renderCell: (params:any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return(
<span className="text-red-300">({params.row.remainedManhour})</span>
)
} else {
return (
<span>{params.row.remainedManhour}</span>
)
}
{
id: "teamLeader",
field: "teamLeader",
headerName: "Team Leader",
flex: 1,
},
flex: 1,
},
{
id: 'comingPaymentMilestone',
field: 'comingPaymentMilestone',
headerName: "Coming Payment Milestone",
flex: 1,
},
{
id: 'alert',
field: 'alert',
headerName: "Alert",
renderCell: (params:any) => {
if (params.row.alert === true) {
return (
<span className="text-red-300 text-center"><ReportProblemIcon/></span>
)
} else {
return (
<span></span>
)
}
{
id: "currentStage",
field: "currentStage",
headerName: "Current Stage",
flex: 1,
},
{
id: "budgetedManhour",
field: "budgetedManhour",
headerName: "Budgeted Manhour",
flex: 1,
},
{
id: "spentManhour",
field: "spentManhour",
headerName: "Spent Manhour",
renderCell: (params: any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return (
<span className="text-red-300">{params.row.spentManhour}</span>
);
} else {
return <span>{params.row.spentManhour}</span>;
}
},
flex: 1,
},
{
id: "remainedManhour",
field: "remainedManhour",
headerName: "Remained Manhour",
renderCell: (params: any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return (
<span className="text-red-300">({params.row.remainedManhour})</span>
);
} else {
return <span>{params.row.remainedManhour}</span>;
}
},
flex: 1,
},
{
id: "comingPaymentMilestone",
field: "comingPaymentMilestone",
headerName: "Coming Payment Milestone",
flex: 1,
},
{
id: "alert",
field: "alert",
headerName: "Alert",
renderCell: (params: any) => {
if (params.row.alert === true) {
return (
<span className="text-red-300 text-center">
<ReportProblemIcon />
</span>
);
} else {
return <span></span>;
}
},
flex: 1,
},
flex: 1,
},
];
];

const InputFields = [
{ id: "clientCode", label: "Client Code", type: 'text', value: clientCode, setValue: setClientCode },
{ id: "clientName", label: "Client Name", type: 'text', value: clientName, setValue: setClientName },
{ id: "subsidiaryClientCode", label: "Subsidiary Client Code", type:'text', value:subsidiaryClientCode, setValue: setSubsidiaryClientCode},
{ id: "subsidiaryClientName", label: "Subsidiary Client Name", type:'text', value:subsidiaryClientName, setValue: setSubsidiaryClientName},
{
id: "clientCode",
label: "Client Code",
type: "text",
value: clientCode,
setValue: setClientCode,
},
{
id: "clientName",
label: "Client Name",
type: "text",
value: clientName,
setValue: setClientName,
},
{
id: "subsidiaryClientCode",
label: "Subsidiary Client Code",
type: "text",
value: subsidiaryClientCode,
setValue: setSubsidiaryClientCode,
},
{
id: "subsidiaryClientName",
label: "Subsidiary Client Name",
type: "text",
value: subsidiaryClientName,
setValue: setSubsidiaryClientName,
},
// { id: 'dropdownDemo', label: "dropdownDemo", type: 'dropdown', options: [{id:"1", label:"1"}], value: dropdownDemo, setValue: setDropdownDemo },
// { id: 'dateDemo', label:'dateDemo', type: 'date', value: dateDemo, setValue: setDateDemo },
// { id: 'checkboxDemo', label:'checkboxDemo', type: 'checkbox', value: checkboxDemo, setValue: setCheckboxDemo },
@@ -178,54 +282,66 @@ const ProgressByClient: React.FC = () => {
// setValue: [setReceiptFromDate, setReceiptToDate],type: 'dateRange' },
];

const stageDeadline = ["31/03/2024","20/02/2024","01/12/2023","05/01/2024","31/03/2023"]
const stageDeadline = [
"31/03/2024",
"20/02/2024",
"01/12/2023",
"05/01/2024",
"31/03/2023",
];

const series2: ApexAxisChartSeries | ApexNonAxisChartSeries = [{
data: [17.1, 28.6, 5.7, 48.6],
}];
const series2: ApexAxisChartSeries | ApexNonAxisChartSeries = [
{
data: [17.1, 28.6, 5.7, 48.6],
},
];

const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [{
name: 'Current Stage Completion Percentage',
data: [80, 55, 40, 65, 70],
}];
const options2 : ApexOptions = {
const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [
{
name: "Current Stage Completion Percentage",
data: [80, 55, 40, 65, 70],
},
];

const options2: ApexOptions = {
chart: {
type: 'donut',
type: "donut",
},
plotOptions: {
pie: {
donut:{
labels:{
show:false,
}
}
donut: {
labels: {
show: false,
},
},
},
},
labels: [projectArray],
legend: {
show: false,
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 200
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 200,
},
legend: {
position: "bottom",
show: false,
},
},
legend: {
position: 'bottom',
show:false
}
}
}]
}
},
],
};

const options: ApexOptions = {
chart: {
type: 'bar',
height: 350
type: "bar",
height: 350,
},
colors: ['#FF4560', '#00E396', '#008FFB', '#775DD0', '#FEB019'],
colors: ["#FF4560", "#00E396", "#008FFB", "#775DD0", "#FEB019"],
plotOptions: {
bar: {
horizontal: true,
@@ -233,91 +349,93 @@ const ProgressByClient: React.FC = () => {
},
},
dataLabels: {
enabled: false
enabled: false,
},
xaxis: {
categories: [
'Consultancy Project 123',
'Consultancy Project 456',
'Construction Project A',
'Construction Project B',
'Construction Project C',
"Consultancy Project 123",
"Consultancy Project 456",
"Construction Project A",
"Construction Project B",
"Construction Project C",
],
},
yaxis: {
title: {
text: 'Projects'
text: "Projects",
},
labels: {
maxWidth: 200,
maxWidth: 200,
style: {
cssClass: 'apexcharts-yaxis-label',
cssClass: "apexcharts-yaxis-label",
},
},
},
title: {
text: 'Current Stage Completion Percentage',
align: 'center'
text: "Current Stage Completion Percentage",
align: "center",
},
grid: {
borderColor: '#f1f1f1',
borderColor: "#f1f1f1",
},
annotations: {
}
annotations: {},
};
const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
const selectedRowsData = rows2.filter((row) =>
newSelectionModel.includes(row.id)
newSelectionModel.includes(row.id),
);
console.log(selectedRowsData)
const projectArray:any[] = []
let otherPercentage = 100
let totalBudgetManhour = 0
const percentageArray = []
console.log(selectedRowsData);
const projectArray: any[] = [];
let otherPercentage = 100;
let totalBudgetManhour = 0;
const percentageArray = [];
for (let i = 0; i <= selectedRowsData.length; i++) {
if (i === selectedRowsData.length) {
projectArray.push("Other")
projectArray.push("Other");
} else {
projectArray.push(selectedRowsData[i].project)
totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour)
projectArray.push(selectedRowsData[i].project);
totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour);
}
}
for (let i = 0; i <= selectedRowsData.length; i++) {
if (i === selectedRowsData.length) {
percentageArray.push(otherPercentage)
percentageArray.push(otherPercentage);
} else {
let percentage = ((Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) * 100).toFixed(1)
percentageArray.push(Number(percentage))
otherPercentage -= Number(percentage)
const percentage = (
(Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) *
100
).toFixed(1);
percentageArray.push(Number(percentage));
otherPercentage -= Number(percentage);
}
}
setSelectionModel(newSelectionModel)
setProjectArray(projectArray)
setPercentageArray(percentageArray)
setSelectionModel(newSelectionModel);
setProjectArray(projectArray);
setPercentageArray(percentageArray);
};

const applySearch = (data: any) => {
console.log(data)
setSearchCriteria(data)
}
console.log(data);
setSearchCriteria(data);
};
return (
<Grid item sm>
{/* <CustomSearchForm applySearch={applySearch} fields={InputFields}/> */}
{/* <CustomDatagrid rows={rows} columns={columns} columnWidth={200} dataGridHeight={300}/> */}
<div style={{display:"inline-block",width:"70%"}}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Project Progress"/>
<div style={{display:"inline-block",width:"99%"}}>
<ReactApexChart
options={options}
series={series}
type="bar"
height={350}
/>
</div>
{/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}>
<Grid item sm>
{/* <CustomSearchForm applySearch={applySearch} fields={InputFields}/> */}
{/* <CustomDatagrid rows={rows} columns={columns} columnWidth={200} dataGridHeight={300}/> */}
<div style={{ display: "inline-block", width: "70%" }}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Project Progress" />
<div style={{ display: "inline-block", width: "99%" }}>
<ReactApexChart
options={options}
series={series}
type="bar"
height={350}
/>
</div>
{/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}>
<p><strong><u>Stage Deadline</u></strong></p>
{stageDeadline.map((date, index) => {
const marginTop = index === 0 ? 25 : 20;
@@ -326,26 +444,49 @@ const ProgressByClient: React.FC = () => {
);
})}
</div> */}
<CardHeader className="text-slate-500" title="Current Stage Due Date"/>
<div style={{display:"inline-block",width:"99%",marginLeft:10}}>
<CustomDatagrid rows={rows2} columns={columns2} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/>
</div>
</Card>
</Grid>
</div>
<div style={{display:"inline-block",width:"30%",verticalAlign:"top",marginLeft:0}}>
<Grid item xs={12} md={12} lg={12}>
<Card style={{marginLeft:15,marginRight:20}}>
<CardHeader className="text-slate-500" title="Overall Progress per Project"/>
<ReactApexChart
options={options2}
series={percentageArray}
type="donut"
/>
</Card>
</Grid>
<CardHeader
className="text-slate-500"
title="Current Stage Due Date"
/>
<div
style={{ display: "inline-block", width: "99%", marginLeft: 10 }}
>
<CustomDatagrid
rows={rows2}
columns={columns2}
columnWidth={200}
dataGridHeight={300}
checkboxSelection={true}
onRowSelectionModelChange={handleSelectionChange}
selectionModel={selectionModel}
/>
</div>
</Grid>
</Card>
</Grid>
</div>
<div
style={{
display: "inline-block",
width: "30%",
verticalAlign: "top",
marginLeft: 0,
}}
>
<Grid item xs={12} md={12} lg={12}>
<Card style={{ marginLeft: 15, marginRight: 20 }}>
<CardHeader
className="text-slate-500"
title="Overall Progress per Project"
/>
<ReactApexChart
options={options2}
series={percentageArray}
type="donut"
/>
</Card>
</Grid>
</div>
</Grid>
);
};



+ 50
- 30
src/components/EnterTimesheet/EnterTimesheetModal.tsx View File

@@ -8,14 +8,14 @@ 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 { Add } from "@mui/icons-material";
import Link from "next/link";
import { t } from 'i18next';
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 { DataGrid } from "@mui/x-data-grid";
import TimesheetInputGrid from "./TimesheetInputGrid";

interface EnterTimesheetModalProps {
@@ -24,55 +24,75 @@ interface EnterTimesheetModalProps {
modalStyle?: any;
}

const EnterTimesheetModal: React.FC<EnterTimesheetModalProps> = ({ ...props }) => {
const EnterTimesheetModal: React.FC<EnterTimesheetModalProps> = ({
...props
}) => {
const [lockConfirm, setLockConfirm] = useState(false);
const columns = [
{
id: 'projectCode',
field: 'projectCode',
id: "projectCode",
field: "projectCode",
headerName: "Project Code and Name",
flex: 1,
},
{
id: 'task',
field: 'task',
headerName: "Task",
flex: 1,
id: "task",
field: "task",
headerName: "Task",
flex: 1,
},
];

const rows = [{
id: 1, projectCode: "M1001", task: "1.2"
const rows = [
{
id: 1,
projectCode: "M1001",
task: "1.2",
},
{
id: 2, projectCode: "M1301", task: "1.1"
}];
id: 2,
projectCode: "M1301",
task: "1.1",
},
];

return (
<Modal open={props.isOpen} onClose={props.onClose}>
<div>
{/* <Typography variant="h5" id="modal-title" sx={{flex:1}}>
<div>
{/* <Typography variant="h5" id="modal-title" sx={{flex:1}}>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
Timesheet Input
</div>
</Typography> */}

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

<div style={{
display: 'flex', justifyContent: 'space-between', width: '100%', flex: 1
}}>
<Button disabled={lockConfirm} variant="contained" onClick={props.onClose}>
Confirm
</Button>
<Button variant="contained" onClick={props.onClose}
sx={{"background-color":"#F890A5"}}>
Cancel
</Button>
</div>
<div
style={{
display: "flex",
justifyContent: "space-between",
width: "100%",
flex: 1,
}}
>
<Button
disabled={lockConfirm}
variant="contained"
onClick={props.onClose}
>
Confirm
</Button>
<Button
variant="contained"
onClick={props.onClose}
sx={{ "background-color": "#F890A5" }}
>
Cancel
</Button>
</div>
</div>
</Modal>
);
};


+ 163
- 126
src/components/EnterTimesheet/TimesheetInputGrid.tsx View File

@@ -8,20 +8,27 @@ 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 ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
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 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 { DatePicker } from "@mui/x-date-pickers/DatePicker";
import {
GridRowsProp,
GridRowModesModel,
@@ -37,17 +44,17 @@ import {
GridRowEditStopReasons,
GridEditInputCell,
GridValueSetterParams,
} from '@mui/x-data-grid';
} 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";

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

interface BottomBarProps {
getHoursTotal: (column: string) => number;
setLockConfirm: (newLock: (oldLock: Boolean) => Boolean) => void;
setLockConfirm: (newLock: (oldLock: boolean) => boolean) => void;
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
setRowModesModel: (
newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
@@ -76,13 +83,16 @@ const EditToolbar = (props: EditToolbarProps) => {

const handleClickLeft = () => {
if (selectedDate) {
const newDate = selectedDate.add(-7, 'day');
const newDate = selectedDate.add(-7, "day");
setSelectedDate(newDate);
}
};
const handleClickRight = () => {
if (selectedDate) {
const newDate = selectedDate.add(7, 'day') > dayjs()? dayjs(): selectedDate.add(7, 'day');
const newDate =
selectedDate.add(7, "day") > dayjs()
? dayjs()
: selectedDate.add(7, "day");
setSelectedDate(newDate);
}
};
@@ -98,24 +108,40 @@ const EditToolbar = (props: EditToolbarProps) => {

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/>
<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/>
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;
@@ -126,75 +152,84 @@ const BottomBar = (props: BottomBarProps) => {
const handleAddClick = () => {
const id = newId;
setNewId(newId - 1);
setRows((oldRows) => [...oldRows, { id, projectCode: '', task: '', isNew: true }]);
setRows((oldRows) => [
...oldRows,
{ id, projectCode: "", task: "", isNew: true },
]);
setRowModesModel((oldModel) => ({
...oldModel,
[id]: { mode: GridRowModes.Edit, fieldToFocus: 'projectCode' },
[id]: { mode: GridRowModes.Edit, fieldToFocus: "projectCode" },
}));
};

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

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

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

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

const checkUnlockConfirmBtn = () => {
// setLockConfirm((oldLock)=> valid);
setLockConfirm((oldLock)=> weekdays.every(weekday => {
getHoursTotal(weekday) <= 24
}));
}
setLockConfirm((oldLock) =>
weekdays.every((weekday) => {
getHoursTotal(weekday) <= 24;
}),
);
};

return (
<div>
<div style={{ display: 'flex', justifyContent: 'flex', width: '100%' }}>
<Box flex={5.7} textAlign={'right'} marginRight='4rem'>
<div style={{ display: "flex", justifyContent: "flex", width: "100%" }}>
<Box flex={5.7} textAlign={"right"} marginRight="4rem">
<b>Total:</b>
</Box>
<TotalCell value={getHoursTotal('mon')}/>
<TotalCell value={getHoursTotal('tue')}/>
<TotalCell value={getHoursTotal('wed')}/>
<TotalCell value={getHoursTotal('thu')}/>
<TotalCell value={getHoursTotal('fri')}/>
<TotalCell value={getHoursTotal('sat')}/>
<TotalCell value={getHoursTotal('sun')}/>
<TotalCell value={getHoursTotal("mon")} />
<TotalCell value={getHoursTotal("tue")} />
<TotalCell value={getHoursTotal("wed")} />
<TotalCell value={getHoursTotal("thu")} />
<TotalCell value={getHoursTotal("fri")} />
<TotalCell value={getHoursTotal("sat")} />
<TotalCell value={getHoursTotal("sun")} />
</div>
<Button variant="outlined" color="primary" startIcon={<AddIcon />} onClick={handleAddClick}>
<Button
variant="outlined"
color="primary"
startIcon={<AddIcon />}
onClick={handleAddClick}
>
Add record
</Button>
</div>
);
}

};

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

interface TimesheetInputGridProps {
setLockConfirm: (newLock: (oldLock: Boolean) => Boolean) => void;
setLockConfirm: (newLock: (oldLock: boolean) => boolean) => void;
onClose?: () => void;
}

@@ -213,30 +248,37 @@ const initialRows: GridRowsProp = [
},
];

const options=["M1001", "M1301", "M1354", "M1973"];
const options2=[
const options = ["M1001", "M1301", "M1354", "M1973"];
const options2 = [
"1.1 - Preparation of preliminary Cost Estimate / Cost Plan",
"1.2 - Cash flow forecast",
"1.3 - Cost studies fo alterative design solutions",
"1.4 = Attend design co-ordination / project review meetings",
"1.5 - Prepare / Review RIC"];
"1.5 - Prepare / Review RIC",
];

const getDateForHeader = (date : dayjs.Dayjs, weekday : number) => {
const getDateForHeader = (date: dayjs.Dayjs, weekday: number) => {
if (date.day() == 0) {
return date.add((weekday - date.day() - 7), 'day').format('DD MMM');
return date.add(weekday - date.day() - 7, "day").format("DD MMM");
} else {
return date.add(weekday - date.day(), 'day').format('DD MMM');
return date.add(weekday - date.day(), "day").format("DD MMM");
}
}

const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) => {
};

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

const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
const handleRowEditStop: GridEventListener<"rowEditStop"> = (
params,
event,
) => {
if (params.reason === GridRowEditStopReasons.rowFocusOut) {
event.defaultMuiPrevented = true;
}
@@ -247,7 +289,6 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
};

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

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

@@ -277,23 +318,23 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
setRowModesModel(newRowModesModel);
};

const getHoursTotal = (column : any) => {
const getHoursTotal = (column: any) => {
let sum = 0;
rows.forEach((row) => {
sum += row[column]??0;
sum += row[column] ?? 0;
});
return sum;
};

const weekdayColConfig : any = {
type: 'number',
const weekdayColConfig: any = {
type: "number",
// sortable: false,
//width: 100,
flex: 1,
align: 'left',
headerAlign: 'left',
align: "left",
headerAlign: "left",
editable: true,
renderEditCell: (value : any) => (
renderEditCell: (value: any) => (
<GridEditInputCell
{...value}
inputProps={{
@@ -307,26 +348,28 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>

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

if (isInEditMode) {
return [
<GridActionsCellItem
key={`actions-${id}-save`}
icon={<SaveIcon />}
title="Save"
label="Save"
sx={{
color: 'primary.main',
color: "primary.main",
}}
onClick={handleSaveClick(id)}
/>,
<GridActionsCellItem
key={`actions-${id}-cancel`}
icon={<CancelIcon />}
title="Cancel"
label="Cancel"
@@ -339,6 +382,7 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>

return [
<GridActionsCellItem
key={`actions-${id}-edit`}
icon={<EditIcon />}
title="Edit"
label="Edit"
@@ -347,100 +391,89 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
color="inherit"
/>,
<GridActionsCellItem
key={`actions-${id}-delete`}
title="Delete"
label="Delete"
icon={<DeleteIcon />}
onClick={handleDeleteClick(id)}
sx={{color:"red"}}
sx={{ color: "red" }}
/>,
];
},
},
{
field: 'projectCode',
headerName: 'Project Code',
field: "projectCode",
headerName: "Project Code",
// width: 220,
flex: 2,
editable: true,
type: 'singleSelect',
type: "singleSelect",
valueOptions: options,
},
{
field: 'task',
headerName: 'Task',
field: "task",
headerName: "Task",
// width: 220,
flex: 3,
editable: true,
type: 'singleSelect',
type: "singleSelect",
valueOptions: options2,
},
{
// Mon
field: 'mon',
field: "mon",
...weekdayColConfig,
renderHeader: () => {
return (
<div>Mon - {getDateForHeader(day, 1)}</div>
);
return <div>Mon - {getDateForHeader(day, 1)}</div>;
},
},
{
// Tue
field: 'tue',
field: "tue",
...weekdayColConfig,
renderHeader: () => {
return (
<div>Tue - {getDateForHeader(day, 2)}</div>
);
return <div>Tue - {getDateForHeader(day, 2)}</div>;
},
},
{
// Wed
field: 'wed',
field: "wed",
...weekdayColConfig,
renderHeader: () => {
return (
<div>Wed - {getDateForHeader(day, 3)}</div>
);
return <div>Wed - {getDateForHeader(day, 3)}</div>;
},
},
{
// Thu
field: 'thu',
field: "thu",
...weekdayColConfig,
renderHeader: () => {
return (
<div>Thu - {getDateForHeader(day, 4)}</div>
);
return <div>Thu - {getDateForHeader(day, 4)}</div>;
},
},
{
// Fri
field: 'fri',
field: "fri",
...weekdayColConfig,
renderHeader: () => {
return (
<div>Fri - {getDateForHeader(day, 5)}</div>
);
return <div>Fri - {getDateForHeader(day, 5)}</div>;
},
},
{
// Sat
field: 'sat',
field: "sat",
...weekdayColConfig,
renderHeader: () => {
return (
<div>Sat - {getDateForHeader(day, 6)}</div>
);
return <div>Sat - {getDateForHeader(day, 6)}</div>;
},
},
{
// Sun
field: 'sun',
field: "sun",
...weekdayColConfig,
renderHeader: () => {
return (
<div style={{color:"red"}}>Sun - {getDateForHeader(day, 7)}</div>
<div style={{ color: "red" }}>Sun - {getDateForHeader(day, 7)}</div>
);
},
},
@@ -457,22 +490,22 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
<Box
sx={{
// marginBottom: '-5px',
display: 'flex',
'flex-direction': 'column',
display: "flex",
"flex-direction": "column",
// 'justify-content': 'flex-end',
padding: '20px',
height: '100%',//'30rem',
width: '100%',
'& .actions': {
color: 'text.secondary',
padding: "20px",
height: "100%", //'30rem',
width: "100%",
"& .actions": {
color: "text.secondary",
},
'& .header': {
"& .header": {
// border: 1,
// 'border-width': '1px',
// 'border-color': 'grey',
},
'& .textPrimary': {
color: 'text.primary',
"& .textPrimary": {
color: "text.primary",
},
}}
>
@@ -498,14 +531,18 @@ const TimesheetInputGrid: React.FC<TimesheetInputGridProps> = ({ ...props }) =>
initialState={{
pagination: { paginationModel: { pageSize: 100 } },
}}
sx={{flex:1}}
sx={{ flex: 1 }}
/>

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

export default TimesheetInputGrid;

+ 64
- 24
src/components/NavigationContent/NavigationContent.tsx View File

@@ -7,11 +7,11 @@ import ListItemText from "@mui/material/ListItemText";
import ListItemIcon from "@mui/material/ListItemIcon";
import WorkHistory from "@mui/icons-material/WorkHistory";
import Dashboard from "@mui/icons-material/Dashboard";
import SummarizeIcon from '@mui/icons-material/Summarize';
import PaymentsIcon from '@mui/icons-material/Payments';
import AccountTreeIcon from '@mui/icons-material/AccountTree';
import SummarizeIcon from "@mui/icons-material/Summarize";
import PaymentsIcon from "@mui/icons-material/Payments";
import AccountTreeIcon from "@mui/icons-material/AccountTree";
import RequestQuote from "@mui/icons-material/RequestQuote";
import PeopleIcon from '@mui/icons-material/People';
import PeopleIcon from "@mui/icons-material/People";
import Task from "@mui/icons-material/Task";
import Assignment from "@mui/icons-material/Assignment";
import Settings from "@mui/icons-material/Settings";
@@ -22,8 +22,8 @@ import Typography from "@mui/material/Typography";
import { usePathname } from "next/navigation";
import Link from "next/link";
import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig";
import ArrowCircleLeftOutlinedIcon from '@mui/icons-material/ArrowCircleLeftOutlined';
import ArrowCircleLeftRoundedIcon from '@mui/icons-material/ArrowCircleLeftRounded';
import ArrowCircleLeftOutlinedIcon from "@mui/icons-material/ArrowCircleLeftOutlined";
import ArrowCircleLeftRoundedIcon from "@mui/icons-material/ArrowCircleLeftRounded";

interface NavigationItem {
icon: React.ReactNode;
@@ -34,17 +34,55 @@ interface NavigationItem {

const navigationItems: NavigationItem[] = [
{ icon: <WorkHistory />, label: "User Workspace", path: "/home" },
{ icon: <Dashboard />, label: "Dashboard", path: "", children: [
{ icon: <SummarizeIcon />, label: "Project Financial Summary", path: "/dashboard/ProjectFinancialSummary" },
{ icon: <PaymentsIcon />, label: "Company / Team Cash Flow", path: "/dashboard/CompanyTeamCashFlow" },
{ icon: <PaymentsIcon />, label: "Project Cash Flow", path: "/dashboard/ProjectCashFlow" },
{ icon: <AccountTreeIcon />, label: "Project Status by Client", path: "/dashboard/ProjectStatusByClient" },
{ icon: <PeopleIcon />, label: "Staff Utilization", path: "/dashboard/StaffUtilization" },
]},
{ icon: <RequestQuote />, label: "Staff Reimbursement", path: "/staffReimbursement", children: [
{ icon: <RequestQuote />, label: "ClaimApproval", path: "/staffReimbursement/ClaimApproval"},
{ icon: <RequestQuote />, label: "ClaimSummary", path: "/staffReimbursement/ClaimSummary"}
] },
{
icon: <Dashboard />,
label: "Dashboard",
path: "",
children: [
{
icon: <SummarizeIcon />,
label: "Project Financial Summary",
path: "/dashboard/ProjectFinancialSummary",
},
{
icon: <PaymentsIcon />,
label: "Company / Team Cash Flow",
path: "/dashboard/CompanyTeamCashFlow",
},
{
icon: <PaymentsIcon />,
label: "Project Cash Flow",
path: "/dashboard/ProjectCashFlow",
},
{
icon: <AccountTreeIcon />,
label: "Project Status by Client",
path: "/dashboard/ProjectStatusByClient",
},
{
icon: <PeopleIcon />,
label: "Staff Utilization",
path: "/dashboard/StaffUtilization",
},
],
},
{
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" },
@@ -58,10 +96,10 @@ const NavigationContent: React.FC = () => {

const [openItems, setOpenItems] = React.useState<string[]>([]);
const toggleItem = (path: string) => {
setOpenItems(prevOpenItems =>
setOpenItems((prevOpenItems) =>
prevOpenItems.includes(path)
? prevOpenItems.filter(item => item !== path)
: [...prevOpenItems, path]
? prevOpenItems.filter((item) => item !== path)
: [...prevOpenItems, path],
);
};

@@ -84,25 +122,27 @@ const NavigationContent: React.FC = () => {
</ListItemButton>
{item.children && isOpen && (
<List sx={{ pl: 2 }}>
{item.children.map(child => renderNavigationItem(child))}
{item.children.map((child) => renderNavigationItem(child))}
</List>
)}
</Box>
);
};
return (
<Box sx={{ width: NAVIGATION_CONTENT_WIDTH }}>
<Box sx={{ p: "1.5rem" }}>
{/* Replace this with company logo and/or name */}
<Typography style={{display:"inline-block"}}variant="h4">TSMS</Typography>
<Typography style={{ display: "inline-block" }} variant="h4">
TSMS
</Typography>
{/* <button className="float-right bg-transparent border-transparent" >
<ArrowCircleLeftRoundedIcon className="text-slate-400 hover:text-blue-400 hover:cursor-pointer " style={{ fontSize: '35px' }} />
</button> */}
</Box>
<Divider />
<List component="nav">
{navigationItems.map(item => renderNavigationItem(item))}
{navigationItems.map((item) => renderNavigationItem(item))}
{/* {navigationItems.map(({ icon, label, path }, index) => {
return (
<Box


+ 147
- 28
src/components/PageTitle/PageTitle.tsx View File

@@ -11,36 +11,155 @@ type PageTitleProps = {
FourthTitle?: string;
};

const PageTitle: React.FC<PageTitleProps> = ({ BigTitle, SecondTitle = '', ThirdTitle = '', FourthTitle = ''}) => {
const PageTitle: React.FC<PageTitleProps> = ({
BigTitle,
SecondTitle = "",
ThirdTitle = "",
FourthTitle = "",
}) => {
const { t } = useTranslation("dashboard");
return (
<Grid item sm>
<div style={{marginTop:20,marginLeft:20}}>
<h1 style={{display:"inline-block",color:"#636363",verticalAlign:"top"}}>{BigTitle}</h1>&nbsp;&nbsp;
<hr color="#D6DCE1" style={{display:"inline-block",width:1,height:35,marginTop:25}} />&nbsp;&nbsp;
<h3 style={{display:"inline-block",color:"#6BAFD8",verticalAlign:"top",marginTop:32}}>Overview</h3>&nbsp;
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{">"}</h3>&nbsp;
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{BigTitle}</h3>&nbsp;
{SecondTitle !== '' ?
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{">"}&nbsp;</h3>
: ""}
{SecondTitle !== '' ?
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{SecondTitle}&nbsp;</h3>
: ""}
{ThirdTitle !== '' ?
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{">"}&nbsp;</h3>
: ""}
{ThirdTitle !== '' ?
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{ThirdTitle}&nbsp;</h3>
: ""}
{FourthTitle !== '' ?
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{">"}&nbsp;</h3>
: ""}
{FourthTitle !== '' ?
<h3 style={{display:"inline-block",color:"#6E6B7B",verticalAlign:"top",marginTop:32}}>{FourthTitle}&nbsp;</h3>
: ""}
</div>
</Grid>
<Grid item sm>
<div style={{ marginTop: 20, marginLeft: 20 }}>
<h1
style={{
display: "inline-block",
color: "#636363",
verticalAlign: "top",
}}
>
{BigTitle}
</h1>
&nbsp;&nbsp;
<hr
color="#D6DCE1"
style={{
display: "inline-block",
width: 1,
height: 35,
marginTop: 25,
}}
/>
&nbsp;&nbsp;
<h3
style={{
display: "inline-block",
color: "#6BAFD8",
verticalAlign: "top",
marginTop: 32,
}}
>
Overview
</h3>
&nbsp;
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{">"}
</h3>
&nbsp;
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{BigTitle}
</h3>
&nbsp;
{SecondTitle !== "" ? (
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{">"}&nbsp;
</h3>
) : (
""
)}
{SecondTitle !== "" ? (
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{SecondTitle}&nbsp;
</h3>
) : (
""
)}
{ThirdTitle !== "" ? (
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{">"}&nbsp;
</h3>
) : (
""
)}
{ThirdTitle !== "" ? (
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{ThirdTitle}&nbsp;
</h3>
) : (
""
)}
{FourthTitle !== "" ? (
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{">"}&nbsp;
</h3>
) : (
""
)}
{FourthTitle !== "" ? (
<h3
style={{
display: "inline-block",
color: "#6E6B7B",
verticalAlign: "top",
marginTop: 32,
}}
>
{FourthTitle}&nbsp;
</h3>
) : (
""
)}
</div>
</Grid>
);
};



+ 500
- 291
src/components/ProgressByClient/ProgressByClient.tsx View File

@@ -1,84 +1,175 @@
"use client";
import * as React from "react";
import Grid from "@mui/material/Grid";
import { useState,useEffect, useMemo } from 'react'
import { useState, useEffect, useMemo } from "react";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {Card,CardHeader} from '@mui/material';
import { Card, CardHeader } from "@mui/material";
import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
import CustomDatagrid from '../CustomDatagrid/CustomDatagrid';
import ReactApexChart from 'react-apexcharts';
import { ApexOptions } from 'apexcharts';
import { GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import ReportProblemIcon from '@mui/icons-material/ReportProblem';
import dynamic from 'next/dynamic';
import '../../app/global.css';
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import ReactApexChart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import ReportProblemIcon from "@mui/icons-material/ReportProblem";
import dynamic from "next/dynamic";
import "../../app/global.css";
import { AnyARecord, AnyCnameRecord } from "dns";
import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense } from "react";

const ProgressByClient: React.FC = () => {
const [activeTab, setActiveTab] = useState('financialSummary');
const [SearchCriteria, setSearchCriteria] = React.useState({})
const [activeTab, setActiveTab] = useState("financialSummary");
const [SearchCriteria, setSearchCriteria] = React.useState({});
const { t } = useTranslation("dashboard");

const [clientCode, setClientCode] = useState('');
const [clientName, setClientName] = useState('');
const [subsidiaryClientCode, setSubsidiaryClientCode] = useState('');
const [subsidiaryClientName, setSubsidiaryClientName] = useState('');
const [projectArray, setProjectArray] : any[] = useState([]);
const [percentageArray, setPercentageArray] : any[] = useState([]);
const [colorArray, setColorArray] : any[] = useState([]);
const [selectionModel, setSelectionModel] : any[] = React.useState([]);
const [pieChartColor, setPieChartColor] : any[] = React.useState([]);
const [totalSpentPercentage, setTotalSpentPercentage] : any = React.useState();
const [projectBudgetManhour, setProjectBudgetManhour] :any = React.useState('-');
const [actualManhourSpent, setActualManhourSpent] :any = React.useState('-');
const [remainedManhour, setRemainedManhour] :any = React.useState('-');
const [lastUpdate, setLastUpdate] :any = React.useState('-');
const [dropdownDemo, setDropdownDemo] = useState('');
const [clientCode, setClientCode] = useState("");
const [clientName, setClientName] = useState("");
const [subsidiaryClientCode, setSubsidiaryClientCode] = useState("");
const [subsidiaryClientName, setSubsidiaryClientName] = useState("");
const [projectArray, setProjectArray]: any[] = useState([]);
const [percentageArray, setPercentageArray]: any[] = useState([]);
const [colorArray, setColorArray]: any[] = useState([]);
const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const [pieChartColor, setPieChartColor]: any[] = React.useState([]);
const [totalSpentPercentage, setTotalSpentPercentage]: any = React.useState();
const [projectBudgetManhour, setProjectBudgetManhour]: any =
React.useState("-");
const [actualManhourSpent, setActualManhourSpent]: any = React.useState("-");
const [remainedManhour, setRemainedManhour]: any = React.useState("-");
const [lastUpdate, setLastUpdate]: any = React.useState("-");
const [dropdownDemo, setDropdownDemo] = useState("");
const [dateDemo, setDateDemo] = useState(null);
const [checkboxDemo, setCheckboxDemo] = useState(false);
const [receiptFromDate, setReceiptFromDate] = useState(null);
const [receiptToDate, setReceiptToDate] = useState(null);
const [selectedRows, setSelectedRows] = useState([]);
const rows = [{id: 1,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"N/A", clientSubsidiaryName:"N/A", noOfProjects:"5"},
{id: 2,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"SUBS-001", clientSubsidiaryName:"Subsidiary A", noOfProjects:"5"},
{id: 3,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"SUBS-002", clientSubsidiaryName:"Subsidiary B", noOfProjects:"3"},
{id: 4,clientCode:"CUST-001",clientName:"Client A", clientSubsidiaryCode:"SUBS-003", clientSubsidiaryName:"Subsidiary C", noOfProjects:"1"}
]
const rows = [
{
id: 1,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "N/A",
clientSubsidiaryName: "N/A",
noOfProjects: "5",
},
{
id: 2,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "SUBS-001",
clientSubsidiaryName: "Subsidiary A",
noOfProjects: "5",
},
{
id: 3,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "SUBS-002",
clientSubsidiaryName: "Subsidiary B",
noOfProjects: "3",
},
{
id: 4,
clientCode: "CUST-001",
clientName: "Client A",
clientSubsidiaryCode: "SUBS-003",
clientSubsidiaryName: "Subsidiary C",
noOfProjects: "1",
},
];
//['#f57f90', '#94f7d6', '#87c5f5', '#ab95f5', '#fcd68b']
const rows2 = [{id: 1,project:"Consultancy Project 123",team:"XXX", teamLeader:"XXX", currentStage:"Contract Documentation", budgetedManhour:"200.00",spentManhour:"120.00",remainedManhour:"80.00",comingPaymentMilestone:"31/03/2024",alert:false,color:"#f57f90"},
{id: 2,project:"Consultancy Project 456",team:"XXX", teamLeader:"XXX", currentStage:"Report Preparation", budgetedManhour:"400.00",spentManhour:"200.00",remainedManhour:"200.00",comingPaymentMilestone:"20/02/2024",alert:false,color:"#94f7d6"},
{id: 3,project:"Construction Project A",team:"YYY", teamLeader:"YYY", currentStage:"Construction", budgetedManhour:"187.50",spentManhour:"200.00",remainedManhour:"12.50",comingPaymentMilestone:"13/12/2023",alert:true,color:"#87c5f5"},
{id: 4,project:"Construction Project B",team:"XXX", teamLeader:"XXX", currentStage:"Post Construction", budgetedManhour:"100.00",spentManhour:"40.00",remainedManhour:"60.00",comingPaymentMilestone:"05/01/2024",alert:false,color:"#ab95f5"},
{id: 5,project:"Construction Project C",team:"YYY", teamLeader:"YYY", currentStage:"Construction", budgetedManhour:"300.00",spentManhour:"150.00",remainedManhour:"150.00",comingPaymentMilestone:"31/03/2024",alert:false,color:"#fcd68b"},
]
const rows2 = [
{
id: 1,
project: "Consultancy Project 123",
team: "XXX",
teamLeader: "XXX",
currentStage: "Contract Documentation",
budgetedManhour: "200.00",
spentManhour: "120.00",
remainedManhour: "80.00",
comingPaymentMilestone: "31/03/2024",
alert: false,
color: "#f57f90",
},
{
id: 2,
project: "Consultancy Project 456",
team: "XXX",
teamLeader: "XXX",
currentStage: "Report Preparation",
budgetedManhour: "400.00",
spentManhour: "200.00",
remainedManhour: "200.00",
comingPaymentMilestone: "20/02/2024",
alert: false,
color: "#94f7d6",
},
{
id: 3,
project: "Construction Project A",
team: "YYY",
teamLeader: "YYY",
currentStage: "Construction",
budgetedManhour: "187.50",
spentManhour: "200.00",
remainedManhour: "12.50",
comingPaymentMilestone: "13/12/2023",
alert: true,
color: "#87c5f5",
},
{
id: 4,
project: "Construction Project B",
team: "XXX",
teamLeader: "XXX",
currentStage: "Post Construction",
budgetedManhour: "100.00",
spentManhour: "40.00",
remainedManhour: "60.00",
comingPaymentMilestone: "05/01/2024",
alert: false,
color: "#ab95f5",
},
{
id: 5,
project: "Construction Project C",
team: "YYY",
teamLeader: "YYY",
currentStage: "Construction",
budgetedManhour: "300.00",
spentManhour: "150.00",
remainedManhour: "150.00",
comingPaymentMilestone: "31/03/2024",
alert: false,
color: "#fcd68b",
},
];

const columns = [
{
id: 'clientCode',
field: 'clientCode',
headerName: "Client Code",
flex: 1,
{
id: "clientCode",
field: "clientCode",
headerName: "Client Code",
flex: 1,
},
{
id: 'clientName',
field: 'clientName',
headerName: "Client Name",
flex: 1,
id: "clientName",
field: "clientName",
headerName: "Client Name",
flex: 1,
},
{
id: 'clientSubsidiaryCode',
field: 'clientSubsidiaryCode',
id: "clientSubsidiaryCode",
field: "clientSubsidiaryCode",
headerName: "Client Subsidiary Code",
flex: 1,
},
{
id: 'noOfProjects',
field: 'noOfProjects',
id: "noOfProjects",
field: "noOfProjects",
headerName: "No. of Projects",
flex: 1,
},
@@ -86,110 +177,139 @@ const ProgressByClient: React.FC = () => {

const columns2 = [
{
id:"color",
field:'color',
headerName:'',
renderCell: (params:any) => {
id: "color",
field: "color",
headerName: "",
renderCell: (params: any) => {
return (
<span className="dot" style={{height:"15px",width:"15px",borderRadius:"50%",backgroundColor:`${params.row.color}`,display:"inline-block"}}></span>
)
<span
className="dot"
style={{
height: "15px",
width: "15px",
borderRadius: "50%",
backgroundColor: `${params.row.color}`,
display: "inline-block",
}}
></span>
);
},
flex:0.1,
flex: 0.1,
},
{
id: 'project',
field: 'project',
id: "project",
field: "project",
headerName: "Project",
flex: 1,
},
{
id: 'team',
field: 'team',
},
{
id: "team",
field: "team",
headerName: "Team",
flex: 0.8,
},
{
id: 'teamLeader',
field: 'teamLeader',
headerName: "Team Leader",
flex: 0.8,
},
{
id: 'currentStage',
field: 'currentStage',
headerName: "Current Stage",
flex: 1,
},
{
id: 'budgetedManhour',
field: 'budgetedManhour',
headerName: "Budgeted Manhour",
flex: 0.8,
},
{
id: 'spentManhour',
field: 'spentManhour',
headerName: "Spent Manhour",
renderCell: (params:any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return(
<span className="text-red-300">{params.row.spentManhour}</span>
)
} else {
return (
<span>{params.row.spentManhour}</span>
)
}
},
flex: 0.8,
},
{
id: 'remainedManhour',
field: 'remainedManhour',
headerName: "Remained Manhour",
renderCell: (params:any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return(
<span className="text-red-300">({params.row.remainedManhour})</span>
)
} else {
return (
<span>{params.row.remainedManhour}</span>
)
}
{
id: "teamLeader",
field: "teamLeader",
headerName: "Team Leader",
flex: 0.8,
},
flex: 1,
},
{
id: 'comingPaymentMilestone',
field: 'comingPaymentMilestone',
headerName: "Coming Payment Milestone",
flex: 1,
},
{
id: 'alert',
field: 'alert',
headerName: "Alert",
renderCell: (params:any) => {
if (params.row.alert === true) {
return (
<span className="text-red-300 text-center"><ReportProblemIcon/></span>
)
} else {
return (
<span></span>
)
}
{
id: "currentStage",
field: "currentStage",
headerName: "Current Stage",
flex: 1,
},
{
id: "budgetedManhour",
field: "budgetedManhour",
headerName: "Budgeted Manhour",
flex: 0.8,
},
flex: 0.2,
},
];
{
id: "spentManhour",
field: "spentManhour",
headerName: "Spent Manhour",
renderCell: (params: any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return (
<span className="text-red-300">{params.row.spentManhour}</span>
);
} else {
return <span>{params.row.spentManhour}</span>;
}
},
flex: 0.8,
},
{
id: "remainedManhour",
field: "remainedManhour",
headerName: "Remained Manhour",
renderCell: (params: any) => {
if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
return (
<span className="text-red-300">({params.row.remainedManhour})</span>
);
} else {
return <span>{params.row.remainedManhour}</span>;
}
},
flex: 1,
},
{
id: "comingPaymentMilestone",
field: "comingPaymentMilestone",
headerName: "Coming Payment Milestone",
flex: 1,
},
{
id: "alert",
field: "alert",
headerName: "Alert",
renderCell: (params: any) => {
if (params.row.alert === true) {
return (
<span className="text-red-300 text-center">
<ReportProblemIcon />
</span>
);
} else {
return <span></span>;
}
},
flex: 0.2,
},
];

const InputFields = [
{ id: "clientCode", label: "Client Code", type: 'text', value: clientCode, setValue: setClientCode },
{ id: "clientName", label: "Client Name", type: 'text', value: clientName, setValue: setClientName },
{ id: "subsidiaryClientCode", label: "Subsidiary Client Code", type:'text', value:subsidiaryClientCode, setValue: setSubsidiaryClientCode},
{ id: "subsidiaryClientName", label: "Subsidiary Client Name", type:'text', value:subsidiaryClientName, setValue: setSubsidiaryClientName},
{
id: "clientCode",
label: "Client Code",
type: "text",
value: clientCode,
setValue: setClientCode,
},
{
id: "clientName",
label: "Client Name",
type: "text",
value: clientName,
setValue: setClientName,
},
{
id: "subsidiaryClientCode",
label: "Subsidiary Client Code",
type: "text",
value: subsidiaryClientCode,
setValue: setSubsidiaryClientCode,
},
{
id: "subsidiaryClientName",
label: "Subsidiary Client Name",
type: "text",
value: subsidiaryClientName,
setValue: setSubsidiaryClientName,
},
// { id: 'dropdownDemo', label: "dropdownDemo", type: 'dropdown', options: [{id:"1", label:"1"}], value: dropdownDemo, setValue: setDropdownDemo },
// { id: 'dateDemo', label:'dateDemo', type: 'date', value: dateDemo, setValue: setDateDemo },
// { id: 'checkboxDemo', label:'checkboxDemo', type: 'checkbox', value: checkboxDemo, setValue: setCheckboxDemo },
@@ -197,73 +317,85 @@ const ProgressByClient: React.FC = () => {
// setValue: [setReceiptFromDate, setReceiptToDate],type: 'dateRange' },
];

const stageDeadline = ["31/03/2024","20/02/2024","01/12/2023","05/01/2024","31/03/2023"]
const stageDeadline = [
"31/03/2024",
"20/02/2024",
"01/12/2023",
"05/01/2024",
"31/03/2023",
];

const series2: ApexAxisChartSeries | ApexNonAxisChartSeries = [
{
data: [17.1, 28.6, 5.7, 48.6],
},
];

const series2: ApexAxisChartSeries | ApexNonAxisChartSeries = [{
data: [17.1, 28.6, 5.7, 48.6],
}];
const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [
{
name: "Current Stage Completion Percentage",
data: [80, 55, 40, 65, 70],
},
];

const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [{
name: 'Current Stage Completion Percentage',
data: [80, 55, 40, 65, 70],
}];
const options2 : ApexOptions = {
const options2: ApexOptions = {
chart: {
type: 'donut',
type: "donut",
},
colors: colorArray,
plotOptions: {
pie: {
donut:{
labels:{
show:true,
name:{
show:true,
donut: {
labels: {
show: true,
name: {
show: true,
},
value:{
show:true,
value: {
show: true,
fontWeight: 500,
fontSize: '30px',
fontSize: "30px",
color: "#3e98c7",
},
total:{
total: {
show: true,
showAlways: true,
label: 'Spent',
fontFamily: 'sans-serif',
label: "Spent",
fontFamily: "sans-serif",
formatter: function (val) {
return totalSpentPercentage + "%"
}
}
}
}
return totalSpentPercentage + "%";
},
},
},
},
},
},
labels: projectArray,
legend: {
show: false,
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 200
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 200,
},
legend: {
position: "bottom",
show: false,
},
},
legend: {
position: 'bottom',
show:false
}
}
}]
}
},
],
};

const options: ApexOptions = {
chart: {
type: 'bar',
height: 350
type: "bar",
height: 350,
},
colors: ['#f57f90', '#94f7d6', '#87c5f5', '#ab95f5', '#fcd68b'],
colors: ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b"],
plotOptions: {
bar: {
horizontal: true,
@@ -271,107 +403,116 @@ const ProgressByClient: React.FC = () => {
},
},
dataLabels: {
enabled: false
enabled: false,
},
xaxis: {
categories: [
'Consultancy Project 123',
'Consultancy Project 456',
'Construction Project A',
'Construction Project B',
'Construction Project C',
"Consultancy Project 123",
"Consultancy Project 456",
"Construction Project A",
"Construction Project B",
"Construction Project C",
],
},
yaxis: {
title: {
text: 'Projects'
text: "Projects",
},
labels: {
maxWidth: 200,
maxWidth: 200,
style: {
cssClass: 'apexcharts-yaxis-label',
cssClass: "apexcharts-yaxis-label",
},
},
},
title: {
text: 'Current Stage Completion Percentage',
align: 'center'
text: "Current Stage Completion Percentage",
align: "center",
},
grid: {
borderColor: '#f1f1f1',
borderColor: "#f1f1f1",
},
annotations: {
}
annotations: {},
};
const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
const selectedRowsData = rows2.filter((row) =>
newSelectionModel.includes(row.id)
newSelectionModel.includes(row.id),
);
console.log(selectedRowsData)
const projectArray = []
const pieChartColorArray = []
let totalSpent = 0
let totalBudgetManhour = 0
const percentageArray = []
console.log(selectedRowsData);
const projectArray = [];
const pieChartColorArray = [];
let totalSpent = 0;
let totalBudgetManhour = 0;
const percentageArray = [];
for (let i = 0; i <= selectedRowsData.length; i++) {
if (i === selectedRowsData.length && i > 0) {
projectArray.push("Remained")
projectArray.push("Remained");
} else if (selectedRowsData.length > 0) {
projectArray.push(selectedRowsData[i].project)
totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour)
totalSpent += Number(selectedRowsData[i].spentManhour)
pieChartColorArray.push(selectedRowsData[i].color)
projectArray.push(selectedRowsData[i].project);
totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour);
totalSpent += Number(selectedRowsData[i].spentManhour);
pieChartColorArray.push(selectedRowsData[i].color);
}
}
for (let i = 0; i <= selectedRowsData.length; i++) {
if (i === selectedRowsData.length && i > 0) {
const remainedManhour = (totalBudgetManhour - totalSpent)
percentageArray.push(Number(((remainedManhour / totalBudgetManhour) * 100).toFixed(1)))
const remainedManhour = totalBudgetManhour - totalSpent;
percentageArray.push(
Number(((remainedManhour / totalBudgetManhour) * 100).toFixed(1)),
);
} else if (selectedRowsData.length > 0) {
let percentage = ((Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) * 100).toFixed(1)
percentageArray.push(Number(percentage))
const percentage = (
(Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) *
100
).toFixed(1);
percentageArray.push(Number(percentage));
}
}
setProjectBudgetManhour(totalBudgetManhour.toFixed(2))
setActualManhourSpent(totalSpent.toFixed(2))
setRemainedManhour((totalBudgetManhour - totalSpent).toFixed(2))
setLastUpdate(new Date().toLocaleDateString('en-GB'))
setSelectionModel(newSelectionModel)
console.log(projectArray)
setProjectArray(projectArray)
setPercentageArray(percentageArray)
console.log(percentageArray)
setTotalSpentPercentage(((totalSpent/totalBudgetManhour)*100).toFixed(1))
setProjectBudgetManhour(totalBudgetManhour.toFixed(2));
setActualManhourSpent(totalSpent.toFixed(2));
setRemainedManhour((totalBudgetManhour - totalSpent).toFixed(2));
setLastUpdate(new Date().toLocaleDateString("en-GB"));
setSelectionModel(newSelectionModel);
console.log(projectArray);
setProjectArray(projectArray);
setPercentageArray(percentageArray);
console.log(percentageArray);
setTotalSpentPercentage(
((totalSpent / totalBudgetManhour) * 100).toFixed(1),
);
if (projectArray.length > 0 && projectArray.includes("Remained")) {
const nonLastRecordColors = pieChartColorArray;
setColorArray([...nonLastRecordColors.slice(0, projectArray.length - 1), "#a3a3a3"]);
setColorArray([
...nonLastRecordColors.slice(0, projectArray.length - 1),
"#a3a3a3",
]);
} else {
setColorArray(pieChartColorArray);
}
};

const applySearch = (data: any) => {
console.log(data)
setSearchCriteria(data)
}
console.log(data);
setSearchCriteria(data);
};
return (
<Grid item sm>
{/* <CustomSearchForm applySearch={applySearch} fields={InputFields}/> */}
{/* <CustomDatagrid rows={rows} columns={columns} columnWidth={200} dataGridHeight={300}/> */}
<div style={{display:"inline-block",width:"70%"}}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Project Progress"/>
<div style={{display:"inline-block",width:"99%"}}>
<ReactApexChart
options={options}
series={series}
type="bar"
height={350}
/>
</div>
{/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}>
<Grid item sm>
{/* <CustomSearchForm applySearch={applySearch} fields={InputFields}/> */}
{/* <CustomDatagrid rows={rows} columns={columns} columnWidth={200} dataGridHeight={300}/> */}
<div style={{ display: "inline-block", width: "70%" }}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Project Progress" />
<div style={{ display: "inline-block", width: "99%" }}>
<ReactApexChart
options={options}
series={series}
type="bar"
height={350}
/>
</div>
{/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}>
<p><strong><u>Stage Deadline</u></strong></p>
{stageDeadline.map((date, index) => {
const marginTop = index === 0 ? 25 : 20;
@@ -380,54 +521,122 @@ const ProgressByClient: React.FC = () => {
);
})}
</div> */}
<CardHeader className="text-slate-500" title="Current Stage Due Date"/>
<div style={{display:"inline-block",width:"99%",marginLeft:10}}>
<CustomDatagrid rows={rows2} columns={columns2} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/>
</div>
</Card>
</Grid>
<CardHeader
className="text-slate-500"
title="Current Stage Due Date"
/>
<div
style={{ display: "inline-block", width: "99%", marginLeft: 10 }}
>
<CustomDatagrid
rows={rows2}
columns={columns2}
columnWidth={200}
dataGridHeight={300}
checkboxSelection={true}
onRowSelectionModelChange={handleSelectionChange}
selectionModel={selectionModel}
/>
</div>
</Card>
</Grid>
</div>
<div
style={{
display: "inline-block",
width: "30%",
verticalAlign: "top",
marginLeft: 0,
}}
>
<Grid item xs={12} md={12} lg={12}>
<Card style={{ marginLeft: 15, marginRight: 20 }}>
<CardHeader
className="text-slate-500"
title="Overall Progress per Project"
/>
{percentageArray.length === 0 && (
<div
className="mt-10 mb-10 ml-5 mr-5 text-lg font-medium text-center"
style={{ color: "#898d8d" }}
>
Please select the project you want to check.
</div>
)}
{percentageArray.length > 0 && (
<ReactApexChart
options={options2}
series={percentageArray}
type="donut"
/>
)}
</Card>
</Grid>
<Grid item xs={12} md={12} lg={12}>
<Card style={{ marginLeft: 15, marginRight: 20, marginTop: 20 }}>
<div>
<div
className="mt-5 text-lg font-medium"
style={{ color: "#898d8d" }}
>
<span style={{ marginLeft: "5%" }}>Project Budget Manhour</span>
</div>
<div
className="mt-2 text-2xl font-extrabold"
style={{ color: "#6b87cf" }}
>
<span style={{ marginLeft: "5%" }}>{projectBudgetManhour}</span>
</div>
</div>
<hr />
<div>
<div
className="mt-2 text-lg font-medium"
style={{ color: "#898d8d" }}
>
<span style={{ marginLeft: "5%" }}>Actual Manhour Spent</span>
</div>
<div
className="mt-2 text-2xl font-extrabold"
style={{ color: "#6b87cf" }}
>
<span style={{ marginLeft: "5%" }}>{actualManhourSpent}</span>
</div>
</div>
<hr />
<div>
<div
className="mt-2 text-lg font-medium"
style={{ color: "#898d8d" }}
>
<span style={{ marginLeft: "5%" }}>Remained Manhour</span>
</div>
<div
className="mt-2 text-2xl font-extrabold"
style={{ color: "#6b87cf" }}
>
<span style={{ marginLeft: "5%" }}>{remainedManhour}</span>
</div>
</div>
<div style={{display:"inline-block",width:"30%",verticalAlign:"top",marginLeft:0}}>
<Grid item xs={12} md={12} lg={12}>
<Card style={{marginLeft:15,marginRight:20}}>
<CardHeader className="text-slate-500" title="Overall Progress per Project"/>
{percentageArray.length === 0 &&(
<div className="mt-10 mb-10 ml-5 mr-5 text-lg font-medium text-center" style={{color:"#898d8d"}}>Please select the project you want to check.</div>
)}
{percentageArray.length > 0 &&(
<ReactApexChart
options={options2}
series={percentageArray}
type="donut"
/>
)}
</Card>
</Grid>
<Grid item xs={12} md={12} lg={12}>
<Card style={{marginLeft:15,marginRight:20,marginTop:20}}>
<div>
<div className="mt-5 text-lg font-medium" style={{color:"#898d8d"}}><span style={{marginLeft:"5%"}}>Project Budget Manhour</span></div>
<div className="mt-2 text-2xl font-extrabold" style={{color:"#6b87cf"}}><span style={{marginLeft:"5%"}}>{projectBudgetManhour}</span></div>
</div>
<hr/>
<div>
<div className="mt-2 text-lg font-medium" style={{color:"#898d8d"}}><span style={{marginLeft:"5%"}}>Actual Manhour Spent</span></div>
<div className="mt-2 text-2xl font-extrabold" style={{color:"#6b87cf"}}><span style={{marginLeft:"5%"}}>{actualManhourSpent}</span></div>
</div>
<hr/>
<div>
<div className="mt-2 text-lg font-medium" style={{color:"#898d8d"}}><span style={{marginLeft:"5%"}}>Remained Manhour</span></div>
<div className="mt-2 text-2xl font-extrabold" style={{color:"#6b87cf"}}><span style={{marginLeft:"5%"}}>{remainedManhour}</span></div>
</div>
<hr/>
<div>
<div className="mt-2 text-lg font-medium" style={{color:"#898d8d"}}><span style={{marginLeft:"5%"}}>Last Update</span></div>
<div className="mt-2 mb-5 text-2xl font-extrabold" style={{color:"#6b87cf"}}><span style={{marginLeft:"5%"}}>{lastUpdate}</span></div>
</div>
</Card>
</Grid>
<hr />
<div>
<div
className="mt-2 text-lg font-medium"
style={{ color: "#898d8d" }}
>
<span style={{ marginLeft: "5%" }}>Last Update</span>
</div>
<div
className="mt-2 mb-5 text-2xl font-extrabold"
style={{ color: "#6b87cf" }}
>
<span style={{ marginLeft: "5%" }}>{lastUpdate}</span>
</div>
</div>
</Grid>
</Card>
</Grid>
</div>
</Grid>
);
};



+ 95
- 59
src/components/ProgressCashFlowSearch/ProgressCashFlowSearch.tsx View File

@@ -6,9 +6,9 @@ import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import { CashFlow } from "@/app/api/cashflow";
import CustomDatagrid from '../CustomDatagrid/CustomDatagrid';
import { GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import ProjectCashFlow from '../ProjectCashFlow'
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import ProjectCashFlow from "../ProjectCashFlow";

interface Props {
projects: CashFlow[];
@@ -16,70 +16,101 @@ interface Props {
type SearchQuery = Partial<Omit<CashFlow, "id">>;
type SearchParamNames = keyof SearchQuery;

const ProgressByClientSearch: React.FC<Props> = ({ projects}) => {
const ProgressByClientSearch: React.FC<Props> = ({ projects }) => {
const { t } = useTranslation("projects");
const [selectionModel, setSelectionModel] : any[] = React.useState([]);
const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const columns = [
{
id: 'projectCode',
field: 'projectCode',
id: "projectCode",
field: "projectCode",
headerName: "Project Code",
flex: 1,
},
{
id: 'projectName',
field: 'projectName',
},
{
id: "projectName",
field: "projectName",
headerName: "Project Name",
flex: 1,
},
{
id: 'team',
field: 'team',
headerName: "Team",
flex: 1,
},
{
id: 'teamLeader',
field: 'teamLeader',
headerName: "Team Leader",
flex: 1,
},
{
id: 'startDate',
field: 'startDate',
headerName: "Start Date",
flex: 1,
},
{
id: 'targetEndDate',
field: 'targetEndDate',
headerName: "Target End Date",
flex: 1,
},
{
id: 'client',
field: 'client',
headerName: "Client",
flex: 1,
},
{
id: 'subsidiary',
field: 'subsidiary',
headerName: "Subsidiary",
flex: 1,
},
];
const rows = [{id: 1,projectCode:"M1001",projectName:"Consultancy Project A", team:"XXX", teamLeader:"XXX", startDate:"01/07/2022", targetEndDate: "01/04/2024", client:"Client B", subsidiary:"N/A"},
{id: 2,projectCode:"M1301",projectName:"Consultancy Project AAAA", team:"XXX", teamLeader:"XXX", startDate:"01/09/2022", targetEndDate: "20/02/2024", client:"Client C", subsidiary:"Subsidiary A"},
{id: 3,projectCode:"M1354",projectName:"Consultancy Project BBB", team:"YYY", teamLeader:"YYY", startDate:"01/02/2023", targetEndDate: "31/01/2024", client:"Client D", subsidiary:"Subsidiary C"}
]
const [selectedTeamData, setSelectedTeamData] : any[] = React.useState(rows);
},
{
id: "team",
field: "team",
headerName: "Team",
flex: 1,
},
{
id: "teamLeader",
field: "teamLeader",
headerName: "Team Leader",
flex: 1,
},
{
id: "startDate",
field: "startDate",
headerName: "Start Date",
flex: 1,
},
{
id: "targetEndDate",
field: "targetEndDate",
headerName: "Target End Date",
flex: 1,
},
{
id: "client",
field: "client",
headerName: "Client",
flex: 1,
},
{
id: "subsidiary",
field: "subsidiary",
headerName: "Subsidiary",
flex: 1,
},
];
const rows = [
{
id: 1,
projectCode: "M1001",
projectName: "Consultancy Project A",
team: "XXX",
teamLeader: "XXX",
startDate: "01/07/2022",
targetEndDate: "01/04/2024",
client: "Client B",
subsidiary: "N/A",
},
{
id: 2,
projectCode: "M1301",
projectName: "Consultancy Project AAAA",
team: "XXX",
teamLeader: "XXX",
startDate: "01/09/2022",
targetEndDate: "20/02/2024",
client: "Client C",
subsidiary: "Subsidiary A",
},
{
id: 3,
projectCode: "M1354",
projectName: "Consultancy Project BBB",
team: "YYY",
teamLeader: "YYY",
startDate: "01/02/2023",
targetEndDate: "31/01/2024",
client: "Client D",
subsidiary: "Subsidiary C",
},
];
const [selectedTeamData, setSelectedTeamData]: any[] = React.useState(rows);
const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
const selectedRowsData = selectedTeamData.filter((row:any) =>
newSelectionModel.includes(row.id)
const selectedRowsData = selectedTeamData.filter((row: any) =>
newSelectionModel.includes(row.id),
);
console.log(selectedRowsData)
}
console.log(selectedRowsData);
};

// If project searching is done on the server-side, then no need for this.
const [filteredProjects, setFilteredProjects] = useState(projects);
@@ -88,7 +119,12 @@ const ProgressByClientSearch: React.FC<Props> = ({ projects}) => {
() => [
{ label: "Project Code", paramName: "projectCode", type: "text" },
{ label: "Project Name", paramName: "projectName", type: "text" },
{ label: "Start Date From",label2: "Start Date To", paramName: "startDateFrom", type: "dateRange" },
{
label: "Start Date From",
label2: "Start Date To",
paramName: "startDateFrom",
type: "dateRange",
},
],
[t],
);


+ 486
- 287
src/components/ProjectCashFlow/ProjectCashFlow.tsx View File

@@ -1,121 +1,123 @@
"use client";
import * as React from "react";
import Grid from "@mui/material/Grid";
import { useState,useEffect, useMemo } from 'react'
import { useState, useEffect, useMemo } from "react";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {Card,CardHeader} from '@mui/material';
import { Card, CardHeader } from "@mui/material";
import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
import CustomDatagrid from '../CustomDatagrid/CustomDatagrid';
import ReactApexChart from 'react-apexcharts';
import { ApexOptions } from 'apexcharts';
import { GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import ReportProblemIcon from '@mui/icons-material/ReportProblem';
import dynamic from 'next/dynamic';
import '../../app/global.css';
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import ReactApexChart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import ReportProblemIcon from "@mui/icons-material/ReportProblem";
import dynamic from "next/dynamic";
import "../../app/global.css";
import { AnyARecord, AnyCnameRecord } from "dns";
import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense } from "react";
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
import {Input,Label} from "reactstrap";
import { Input, Label } from "reactstrap";

const ProjectCashFlow: React.FC = () => {
const todayDate = new Date;
const [selectionModel, setSelectionModel] : any[] = React.useState([]);
const [cashFlowYear, setCashFlowYear] : any[] = React.useState(todayDate.getFullYear());
const todayDate = new Date();
const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const [cashFlowYear, setCashFlowYear]: any[] = React.useState(
todayDate.getFullYear(),
);
const columns = [
{
id: 'projectCode',
field: 'projectCode',
id: "projectCode",
field: "projectCode",
headerName: "Project Code",
flex: 1,
},
{
id: 'projectName',
field: 'projectName',
},
{
id: "projectName",
field: "projectName",
headerName: "Project Name",
flex: 1,
},
{
id: 'team',
field: 'team',
headerName: "Team",
flex: 1,
},
{
id: 'teamLeader',
field: 'teamLeader',
headerName: "Team Leader",
flex: 1,
},
{
id: 'startDate',
field: 'startDate',
headerName: "Start Date",
flex: 1,
},
{
id: 'targetEndDate',
field: 'targetEndDate',
headerName: "Target End Date",
flex: 1,
},
{
id: 'client',
field: 'client',
headerName: "Client",
flex: 1,
},
{
id: 'subsidiary',
field: 'subsidiary',
headerName: "Subsidiary",
flex: 1,
},
},
{
id: "team",
field: "team",
headerName: "Team",
flex: 1,
},
{
id: "teamLeader",
field: "teamLeader",
headerName: "Team Leader",
flex: 1,
},
{
id: "startDate",
field: "startDate",
headerName: "Start Date",
flex: 1,
},
{
id: "targetEndDate",
field: "targetEndDate",
headerName: "Target End Date",
flex: 1,
},
{
id: "client",
field: "client",
headerName: "Client",
flex: 1,
},
{
id: "subsidiary",
field: "subsidiary",
headerName: "Subsidiary",
flex: 1,
},
];

const ledgerColumns = [
{
id: 'date',
field: 'date',
id: "date",
field: "date",
headerName: "Date",
flex: 0.5,
},
{
id: 'expenditure',
field: 'expenditure',
},
{
id: "expenditure",
field: "expenditure",
headerName: "Expenditure (HKD)",
flex: 0.6,
},
{
id: 'income',
field: 'income',
headerName: "Income (HKD)",
flex: 0.6,
},
{
id: 'cashFlowBalance',
field: 'cashFlowBalance',
headerName: "Cash Flow Balance (HKD)",
flex: 0.6,
},
{
id: 'remarks',
field: 'remarks',
headerName: "Remarks",
flex: 1,
},
},
{
id: "income",
field: "income",
headerName: "Income (HKD)",
flex: 0.6,
},
{
id: "cashFlowBalance",
field: "cashFlowBalance",
headerName: "Cash Flow Balance (HKD)",
flex: 0.6,
},
{
id: "remarks",
field: "remarks",
headerName: "Remarks",
flex: 1,
},
];

const options: ApexOptions = {
chart: {
height: 350,
type: 'line',
type: "line",
},
stroke: {
width: [0, 0, 2, 2]
width: [0, 0, 2, 2],
},
plotOptions: {
bar: {
@@ -124,160 +126,167 @@ const ProjectCashFlow: React.FC = () => {
},
},
dataLabels: {
enabled: false
enabled: false,
},
xaxis: {
categories: [
'Q1',
'Q2',
'Q3',
'Q4',
'Q5',
'Q6',
'Q7',
'Q8',
'Q9',
'Q10',
'Q11',
'Q12',
"Q1",
"Q2",
"Q3",
"Q4",
"Q5",
"Q6",
"Q7",
"Q8",
"Q9",
"Q10",
"Q11",
"Q12",
],
},
yaxis: [

{
title: {
text: 'Monthly Income and Expenditure(HKD)'
text: "Monthly Income and Expenditure(HKD)",
},
min: 0,
max: 350000,
tickAmount: 5
tickAmount: 5,
},
{
show:false,
seriesName: 'Monthly_Expenditure',
show: false,
seriesName: "Monthly_Expenditure",
title: {
text: 'Monthly Expenditure (HKD)'
text: "Monthly Expenditure (HKD)",
},
min: 0,
max: 350000,
tickAmount: 5
tickAmount: 5,
},
{
seriesName: 'Cumulative_Income',
seriesName: "Cumulative_Income",
opposite: true,
title: {
text: 'Cumulative Income and Expenditure(HKD)'
text: "Cumulative Income and Expenditure(HKD)",
},
min: 0,
max: 850000,
tickAmount: 5
tickAmount: 5,
},
{
show:false,
seriesName: 'Cumulative_Expenditure',
show: false,
seriesName: "Cumulative_Expenditure",
opposite: true,
title: {
text: 'Cumulative Expenditure (HKD)'
text: "Cumulative Expenditure (HKD)",
},
min: 0,
max: 850000,
tickAmount: 5
}
],
tickAmount: 5,
},
],
grid: {
borderColor: '#f1f1f1',
borderColor: "#f1f1f1",
},
annotations: {
},
series:[
annotations: {},
series: [
{
name:"Monthly_Income",
type:"column",
name: "Monthly_Income",
type: "column",
color: "#ffde91",
data:[0,110000,0,0,185000,0,0,189000,0,0,300000,0],
data: [0, 110000, 0, 0, 185000, 0, 0, 189000, 0, 0, 300000, 0],
},
{
name:"Monthly_Expenditure",
type:"column",
name: "Monthly_Expenditure",
type: "column",
color: "#82b59a",
data:[0,160000,120000,120000,55000,55000,55000,55000,55000,70000,55000,55000]
data: [
0, 160000, 120000, 120000, 55000, 55000, 55000, 55000, 55000, 70000,
55000, 55000,
],
},
{
name:"Cumulative_Income",
type:"line",
name: "Cumulative_Income",
type: "line",
color: "#EE6D7A",
data:[0,100000,100000,100000,300000,300000,300000,500000,500000,500000,800000,800000]
data: [
0, 100000, 100000, 100000, 300000, 300000, 300000, 500000, 500000,
500000, 800000, 800000,
],
},
{
name:"Cumulative_Expenditure",
type:"line",
name: "Cumulative_Expenditure",
type: "line",
color: "#7cd3f2",
data:[0,198000,240000,400000,410000,430000,510000,580000,600000,710000,730000,790000]
}
]
data: [
0, 198000, 240000, 400000, 410000, 430000, 510000, 580000, 600000,
710000, 730000, 790000,
],
},
],
};

const accountsReceivableOptions: ApexOptions = {
colors: ["#20E647"],
series: [80],
chart: {
height: 350,
type: 'radialBar',
colors: ["#20E647"],
series: [80],
chart: {
height: 350,
type: "radialBar",
},
plotOptions: {
radialBar: {
hollow: {
size: "70%",
background: "#ffffff",
},
plotOptions: {
radialBar: {
hollow: {
size: '70%',
background: "#ffffff"
},
track: {
dropShadow: {
enabled: true,
top: 2,
left: 0,
blur: 4,
opacity: 0.15
}
},
dataLabels: {
name:{
show:false,
},
value: {
color: "#3e98c7",
fontSize: "3em",
show: true
}
},
track: {
dropShadow: {
enabled: true,
top: 2,
left: 0,
blur: 4,
opacity: 0.15,
},
},
fill: {
type: "gradient",
gradient: {
shade: "dark",
type: "vertical",
gradientToColors: ["#87D4F9"],
stops: [0, 100]
}
},
stroke: {
lineCap: "round"
dataLabels: {
name: {
show: false,
},
value: {
color: "#3e98c7",
fontSize: "3em",
show: true,
},
},
labels: ['AccountsReceivable'],
},
},
fill: {
type: "gradient",
gradient: {
shade: "dark",
type: "vertical",
gradientToColors: ["#87D4F9"],
stops: [0, 100],
},
},
stroke: {
lineCap: "round",
},
labels: ["AccountsReceivable"],
};

const expenditureOptions: ApexOptions = {
colors: ["#20E647"],
series: [95],
chart: {
chart: {
height: 350,
type: 'radialBar',
type: "radialBar",
},
plotOptions: {
radialBar: {
hollow: {
size: '70%',
background: "#ffffff"
size: "70%",
background: "#ffffff",
},
track: {
dropShadow: {
@@ -285,18 +294,18 @@ const ProjectCashFlow: React.FC = () => {
top: 2,
left: 0,
blur: 4,
opacity: 0.15
}
opacity: 0.15,
},
},
dataLabels: {
name:{
show:false,
name: {
show: false,
},
value: {
color: "#3e98c7",
fontSize: "3em",
show: true
}
show: true,
},
},
},
},
@@ -306,132 +315,322 @@ const ProjectCashFlow: React.FC = () => {
shade: "dark",
type: "vertical",
gradientToColors: ["#87D4F9"],
stops: [0, 100]
}
stops: [0, 100],
},
},
stroke: {
lineCap: "round"
lineCap: "round",
},
labels: ['AccountsReceivable'],
};
labels: ["AccountsReceivable"],
};

const rows = [{id: 1,projectCode:"M1001",projectName:"Consultancy Project A", team:"XXX", teamLeader:"XXX", startDate:"01/07/2022", targetEndDate: "01/04/2024", client:"Client B", subsidiary:"N/A"},
{id: 2,projectCode:"M1301",projectName:"Consultancy Project AAAA", team:"XXX", teamLeader:"XXX", startDate:"01/09/2022", targetEndDate: "20/02/2024", client:"Client C", subsidiary:"Subsidiary A"},
{id: 3,projectCode:"M1354",projectName:"Consultancy Project BBB", team:"YYY", teamLeader:"YYY", startDate:"01/02/2023", targetEndDate: "31/01/2024", client:"Client D", subsidiary:"Subsidiary C"}
]
const rows = [
{
id: 1,
projectCode: "M1001",
projectName: "Consultancy Project A",
team: "XXX",
teamLeader: "XXX",
startDate: "01/07/2022",
targetEndDate: "01/04/2024",
client: "Client B",
subsidiary: "N/A",
},
{
id: 2,
projectCode: "M1301",
projectName: "Consultancy Project AAAA",
team: "XXX",
teamLeader: "XXX",
startDate: "01/09/2022",
targetEndDate: "20/02/2024",
client: "Client C",
subsidiary: "Subsidiary A",
},
{
id: 3,
projectCode: "M1354",
projectName: "Consultancy Project BBB",
team: "YYY",
teamLeader: "YYY",
startDate: "01/02/2023",
targetEndDate: "31/01/2024",
client: "Client D",
subsidiary: "Subsidiary C",
},
];

const ledgerRows = [{id: 1,date:"Feb 2023",expenditure:"-", income:"100,000.00", cashFlowBalance:"100,000.00", remarks:"Payment Milestone 1 (10%)"},
{id: 2,date:"Feb 2023",expenditure:"160,000.00", income:"-", cashFlowBalance:"(60,000.00)", remarks:"Monthly Manpower Expenditure"},
{id: 3,date:"Mar 2023",expenditure:"160,000.00", income:"-", cashFlowBalance:"(180,000.00)", remarks:"Monthly Manpower Expenditure"},
{id: 4,date:"Apr 2023",expenditure:"120,000.00", income:"-", cashFlowBalance:"(300,000.00)", remarks:"Monthly Manpower Expenditure"},
{id: 5,date:"May 2023",expenditure:"-", income:"200,000.00", cashFlowBalance:"(100,000.00)", remarks:"Payment Milestone 2 (20%)"},
{id: 6,date:"May 2023",expenditure:"40,000.00", income:"-", cashFlowBalance:"(140,000.00)", remarks:"Monthly Manpower Expenditure"}
]
const ledgerRows = [
{
id: 1,
date: "Feb 2023",
expenditure: "-",
income: "100,000.00",
cashFlowBalance: "100,000.00",
remarks: "Payment Milestone 1 (10%)",
},
{
id: 2,
date: "Feb 2023",
expenditure: "160,000.00",
income: "-",
cashFlowBalance: "(60,000.00)",
remarks: "Monthly Manpower Expenditure",
},
{
id: 3,
date: "Mar 2023",
expenditure: "160,000.00",
income: "-",
cashFlowBalance: "(180,000.00)",
remarks: "Monthly Manpower Expenditure",
},
{
id: 4,
date: "Apr 2023",
expenditure: "120,000.00",
income: "-",
cashFlowBalance: "(300,000.00)",
remarks: "Monthly Manpower Expenditure",
},
{
id: 5,
date: "May 2023",
expenditure: "-",
income: "200,000.00",
cashFlowBalance: "(100,000.00)",
remarks: "Payment Milestone 2 (20%)",
},
{
id: 6,
date: "May 2023",
expenditure: "40,000.00",
income: "-",
cashFlowBalance: "(140,000.00)",
remarks: "Monthly Manpower Expenditure",
},
];

const [projectData, setProjectData] : any[] = React.useState(rows);
const [ledgerData, setLedgerData] : any[] = React.useState(ledgerRows);
const [projectData, setProjectData]: any[] = React.useState(rows);
const [ledgerData, setLedgerData]: any[] = React.useState(ledgerRows);
const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
const selectedRowsData = projectData.filter((row:any) =>
newSelectionModel.includes(row.id)
const selectedRowsData = projectData.filter((row: any) =>
newSelectionModel.includes(row.id),
);
console.log(selectedRowsData)
}
console.log(selectedRowsData);
};

return (
<>
<Suspense fallback={<ProgressCashFlowSearch.Loading />}>
<ProgressCashFlowSearch/>
<ProgressCashFlowSearch />
</Suspense>
<CustomDatagrid rows={projectData} columns={columns} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/>
<Grid item sm>
<div style={{display:"inline-block",width:"50%"}}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Project Cash Flow by Month"/>
<div style={{display:"inline-block",width:"99%"}}>
<div className="inline-block">
<Label className="text-slate-500 font-medium ml-6">
Period:&nbsp;
</Label>
<Input
id={'cashFlowYear'}
value={cashFlowYear}
readOnly={true}
bsSize="lg"
className="rounded-md text-base w-12"
/>
</div>
<div className="inline-block ml-1">
<button onClick={() => setCashFlowYear(cashFlowYear - 1)} className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base">
&lt;
</button>
</div>
<div className="inline-block ml-1">
<button onClick={() => setCashFlowYear(cashFlowYear + 1)} className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base">
&gt;
</button>
</div>
<ReactApexChart
options={options}
series={options.series}
type="line"
height="auto"
/>
</div>
</Card>
</Grid>
</div>
<div style={{display:"inline-block",width:"24%", verticalAlign:"top", marginLeft:10}}>
<CustomDatagrid
rows={projectData}
columns={columns}
columnWidth={200}
dataGridHeight={300}
checkboxSelection={true}
onRowSelectionModelChange={handleSelectionChange}
selectionModel={selectionModel}
/>
<Grid item sm>
<div style={{ display: "inline-block", width: "50%" }}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Accounts Receivable (HKD)"/>
<Card>
<CardHeader
className="text-slate-500"
title="Project Cash Flow by Month"
/>
<div style={{ display: "inline-block", width: "99%" }}>
<div className="inline-block">
<Label className="text-slate-500 font-medium ml-6">
Period:&nbsp;
</Label>
<Input
id={"cashFlowYear"}
value={cashFlowYear}
readOnly={true}
bsSize="lg"
className="rounded-md text-base w-12"
/>
</div>
<div className="inline-block ml-1">
<button
onClick={() => setCashFlowYear(cashFlowYear - 1)}
className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
>
&lt;
</button>
</div>
<div className="inline-block ml-1">
<button
onClick={() => setCashFlowYear(cashFlowYear + 1)}
className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
>
&gt;
</button>
</div>
<ReactApexChart
options={accountsReceivableOptions}
series={accountsReceivableOptions.series}
type="radialBar"
/>
<Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
<div className="text-sm font-medium ml-5 mt-2" style={{color:"#898d8d"}}>Total A. Receivable</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>1,000,000.00</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Amount Received</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>800,000.00</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Remaining Balance</div>
<div className="text-lg font-medium ml-5 mb-2" style={{color:"#6b87cf"}}>200,000.00</div>
</Card>
</Card>
options={options}
series={options.series}
type="line"
height="auto"
/>
</div>
</Card>
</Grid>
</div>
<div style={{display:"inline-block",width:"24%", verticalAlign:"top", marginLeft:10}}>
</div>
<div
style={{
display: "inline-block",
width: "24%",
verticalAlign: "top",
marginLeft: 10,
}}
>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Expenditure (HKD)"/>
<ReactApexChart
options={expenditureOptions}
series={expenditureOptions.series}
type="radialBar"
/>
<Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
<div className="text-sm font-medium ml-5 mt-2" style={{color:"#898d8d"}}>Budgeted Expenditure</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>800,000.00</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Actual Expenditure</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>760,000.00</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Remaining Balance</div>
<div className="text-lg font-medium ml-5 mb-2" style={{color:"#6b87cf"}}>40,000.00</div>
</Card>
<Card>
<CardHeader
className="text-slate-500"
title="Accounts Receivable (HKD)"
/>
<ReactApexChart
options={accountsReceivableOptions}
series={accountsReceivableOptions.series}
type="radialBar"
/>
<Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
<div
className="text-sm font-medium ml-5 mt-2"
style={{ color: "#898d8d" }}
>
Total A. Receivable
</div>
<div
className="text-lg font-medium ml-5"
style={{ color: "#6b87cf" }}
>
1,000,000.00
</div>
<hr />
<div
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
Amount Received
</div>
<div
className="text-lg font-medium ml-5"
style={{ color: "#6b87cf" }}
>
800,000.00
</div>
<hr />
<div
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
Remaining Balance
</div>
<div
className="text-lg font-medium ml-5 mb-2"
style={{ color: "#6b87cf" }}
>
200,000.00
</div>
</Card>
</Card>
</Grid>
</div>
<div className="mt-5" style={{display:"inline-block",width:"100%", verticalAlign:"top"}}>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader className="text-slate-500" title="Cash Flow Ledger by Month"/>
<div className="ml-4 mr-4">
<CustomDatagrid rows={ledgerData} columns={ledgerColumns} columnWidth={200} dataGridHeight={300}/>
</div>
<div
style={{
display: "inline-block",
width: "24%",
verticalAlign: "top",
marginLeft: 10,
}}
>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader
className="text-slate-500"
title="Expenditure (HKD)"
/>
<ReactApexChart
options={expenditureOptions}
series={expenditureOptions.series}
type="radialBar"
/>
<Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
<div
className="text-sm font-medium ml-5 mt-2"
style={{ color: "#898d8d" }}
>
Budgeted Expenditure
</div>
<div
className="text-lg font-medium ml-5"
style={{ color: "#6b87cf" }}
>
800,000.00
</div>
<hr />
<div
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
Actual Expenditure
</div>
<div
className="text-lg font-medium ml-5"
style={{ color: "#6b87cf" }}
>
760,000.00
</div>
<hr />
<div
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
Remaining Balance
</div>
<div
className="text-lg font-medium ml-5 mb-2"
style={{ color: "#6b87cf" }}
>
40,000.00
</div>
</Card>
</Grid>
</div>
</Grid>
</Card>
</Grid>
</div>
<div
className="mt-5"
style={{
display: "inline-block",
width: "100%",
verticalAlign: "top",
}}
>
<Grid item xs={12} md={12} lg={12}>
<Card>
<CardHeader
className="text-slate-500"
title="Cash Flow Ledger by Month"
/>
<div className="ml-4 mr-4">
<CustomDatagrid
rows={ledgerData}
columns={ledgerColumns}
columnWidth={200}
dataGridHeight={300}
/>
</div>
</Card>
</Grid>
</div>
</Grid>
</>
);
};


+ 160
- 62
src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx View File

@@ -1,75 +1,173 @@
import * as React from "react";
import Grid from "@mui/material/Grid";
import { useState,useEffect, useMemo } from 'react'
import { useState, useEffect, useMemo } from "react";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {Card,CardHeader} from '@mui/material';
import { Card, CardHeader } from "@mui/material";
import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
import CustomDatagrid from '../CustomDatagrid/CustomDatagrid';
import ReactApexChart from 'react-apexcharts';
import { ApexOptions } from 'apexcharts';
import { GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import ReportProblemIcon from '@mui/icons-material/ReportProblem';
import dynamic from 'next/dynamic';
import '../../app/global.css';
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import ReactApexChart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import ReportProblemIcon from "@mui/icons-material/ReportProblem";
import dynamic from "next/dynamic";
import "../../app/global.css";
import { AnyARecord, AnyCnameRecord } from "dns";
import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense } from "react";

interface Props {
Title: string;
TotalActiveProjectNumber: string;
TotalFees: string;
TotalBudget: string;
TotalCumulative: string;
TotalInvoicedAmount: string;
TotalReceivedAmount: string;
CashFlowStatus: string;
CostPerformanceIndex: string;
ClickedIndex: number;
Index: number;
}
Title: string;
TotalActiveProjectNumber: string;
TotalFees: string;
TotalBudget: string;
TotalCumulative: string;
TotalInvoicedAmount: string;
TotalReceivedAmount: string;
CashFlowStatus: string;
CostPerformanceIndex: string;
ClickedIndex: number;
Index: number;
}

const ProjectFinancialCard: React.FC<Props> = ({Title,TotalActiveProjectNumber,TotalFees,TotalBudget,TotalCumulative,TotalInvoicedAmount,TotalReceivedAmount,CashFlowStatus,CostPerformanceIndex,ClickedIndex,Index}) => {
const [SearchCriteria, setSearchCriteria] = React.useState({})
const { t } = useTranslation("dashboard");
const borderColor = CashFlowStatus === "Negative" ? "border-red-300 border-solid" : "border-green-200 border-solid"
const selectedBackgroundColor = ClickedIndex === Index ? "rgb(235 235 235)" : "rgb(255 255 255)"
console.log(ClickedIndex)
console.log(Index)
return (
<Card style={{maxWidth:"25%",minWidth:"280px",boxShadow:"0 0px 10px 0 rgba(0, 0, 0, 0.08), 0 0px 10px 0 rgba(0, 0, 0, 0.08)", backgroundColor:selectedBackgroundColor}} className={`${borderColor}`}>
<div className="text-xl mt-2 font-medium" style={{width:"100%",textAlign:"center",color:"#898d8d"}}>{Title}</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Total Active Project</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>{TotalActiveProjectNumber}</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Total Fees</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>{TotalFees}</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Total Budget</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>{TotalBudget}</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Total Cumulative Expenditure</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>{TotalCumulative}</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Total Invoiced Amount</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>{TotalInvoicedAmount}</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Total Received Amount</div>
<div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>{TotalReceivedAmount}</div><hr/>
<div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Cash Flow Status</div>
{CashFlowStatus === "Negative" && (
<><div className="text-lg font-medium ml-5" style={{color:"#f896aa"}}>{CashFlowStatus}</div><hr/></>
)}
{CashFlowStatus === "Positive" && (
<><div className="text-lg font-medium ml-5" style={{color:"#71d19e"}}>{CashFlowStatus}</div><hr/></>
)}
<div className="text-sm mt-2 font-medium ml-5" style={{color:"#898d8d"}}>Cost Performance Index (CPI)</div>
{Number(CostPerformanceIndex) < 1 && (
<><div className="text-lg font-medium ml-5 mb-2" style={{color:"#f896aa"}}>{CostPerformanceIndex}</div></>
)}
{Number(CostPerformanceIndex) >= 1 && (
<><div className="text-lg font-medium ml-5 mb-2" style={{color:"#71d19e"}}>{CostPerformanceIndex}</div></>
)}
</Card>
);
};
export default ProjectFinancialCard;
const ProjectFinancialCard: React.FC<Props> = ({
Title,
TotalActiveProjectNumber,
TotalFees,
TotalBudget,
TotalCumulative,
TotalInvoicedAmount,
TotalReceivedAmount,
CashFlowStatus,
CostPerformanceIndex,
ClickedIndex,
Index,
}) => {
const [SearchCriteria, setSearchCriteria] = React.useState({});
const { t } = useTranslation("dashboard");
const borderColor =
CashFlowStatus === "Negative"
? "border-red-300 border-solid"
: "border-green-200 border-solid";
const selectedBackgroundColor =
ClickedIndex === Index ? "rgb(235 235 235)" : "rgb(255 255 255)";
console.log(ClickedIndex);
console.log(Index);
return (
<Card
style={{
maxWidth: "25%",
minWidth: "280px",
boxShadow:
"0 0px 10px 0 rgba(0, 0, 0, 0.08), 0 0px 10px 0 rgba(0, 0, 0, 0.08)",
backgroundColor: selectedBackgroundColor,
}}
className={`${borderColor}`}
>
<div
className="text-xl mt-2 font-medium"
style={{ width: "100%", textAlign: "center", color: "#898d8d" }}
>
{Title}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Active Project
</div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalActiveProjectNumber}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Fees
</div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalFees}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Budget
</div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalBudget}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Cumulative Expenditure
</div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalCumulative}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Invoiced Amount
</div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalInvoicedAmount}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Received Amount
</div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalReceivedAmount}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Cash Flow Status
</div>
{CashFlowStatus === "Negative" && (
<>
<div
className="text-lg font-medium ml-5"
style={{ color: "#f896aa" }}
>
{CashFlowStatus}
</div>
<hr />
</>
)}
{CashFlowStatus === "Positive" && (
<>
<div
className="text-lg font-medium ml-5"
style={{ color: "#71d19e" }}
>
{CashFlowStatus}
</div>
<hr />
</>
)}
<div
className="text-sm mt-2 font-medium ml-5"
style={{ color: "#898d8d" }}
>
Cost Performance Index (CPI)
</div>
{Number(CostPerformanceIndex) < 1 && (
<>
<div
className="text-lg font-medium ml-5 mb-2"
style={{ color: "#f896aa" }}
>
{CostPerformanceIndex}
</div>
</>
)}
{Number(CostPerformanceIndex) >= 1 && (
<>
<div
className="text-lg font-medium ml-5 mb-2"
style={{ color: "#71d19e" }}
>
{CostPerformanceIndex}
</div>
</>
)}
</Card>
);
};

export default ProjectFinancialCard;

+ 432
- 230
src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx View File

@@ -1,19 +1,19 @@
"use client";
import * as React from "react";
import Grid from "@mui/material/Grid";
import { useState,useEffect, useMemo } from 'react'
import { useState, useEffect, useMemo } from "react";
import Paper from "@mui/material/Paper";
import { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {Card,CardHeader} from '@mui/material';
import { Card, CardHeader } from "@mui/material";
import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
import CustomDatagrid from '../CustomDatagrid/CustomDatagrid';
import ReactApexChart from 'react-apexcharts';
import { ApexOptions } from 'apexcharts';
import { GridColDef, GridRowSelectionModel} from '@mui/x-data-grid';
import ReportProblemIcon from '@mui/icons-material/ReportProblem';
import dynamic from 'next/dynamic';
import '../../app/global.css';
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import ReactApexChart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import ReportProblemIcon from "@mui/icons-material/ReportProblem";
import dynamic from "next/dynamic";
import "../../app/global.css";
import { AnyARecord, AnyCnameRecord } from "dns";
import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch";
@@ -21,251 +21,453 @@ import { Suspense } from "react";
import ProjectFinancialCard from "./ProjectFinancialCard";

const ProjectFinancialSummary: React.FC = () => {
const [SearchCriteria, setSearchCriteria] = React.useState({})
const [SearchCriteria, setSearchCriteria] = React.useState({});
const { t } = useTranslation("dashboard");
const [selectionModel, setSelectionModel] : any[] = React.useState([]);
const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const projectFinancialData = [
{id:1,title:"All Teams",activeProject:"147",fees:"22,800,000.00",budget:"18,240,000.00",cumulativeExpenditure:"17,950,000.00",invoicedAmount:"18,240,000.00",receivedAmount:"10,900,000.00",cashFlowStatus:"Negative",CPI:"0.69"},
{id:2,title:"XXX Team",activeProject:"25",fees:"1,500,000.00",budget:"1,200,000.00",cumulativeExpenditure:"1,250,000.00",invoicedAmount:"900,000.00",receivedAmount:"650,000.00",cashFlowStatus:"Negative",CPI:"0.72"},
{id:3,title:"YYY Team",activeProject:"35",fees:"5,000,000.00",budget:"4,000,000.00",cumulativeExpenditure:"3,200,000.00",invoicedAmount:"3,500,000.00",receivedAmount:"3,500,000.00",cashFlowStatus:"Positive",CPI:"1.09"},
{id:4,title:"ZZZ Team",activeProject:"50",fees:"3,500,000.00",budget:"2,800,000.00",cumulativeExpenditure:"5,600,000.00",invoicedAmount:"2,500,000.00",receivedAmount:"2,200,000.00",cashFlowStatus:"Negative",CPI:"0.45"},
{id:5,title:"AAA Team",activeProject:"15",fees:"4,800,000.00",budget:"3,840,000.00",cumulativeExpenditure:"2,500,000.00",invoicedAmount:"1,500,000.00",receivedAmount:"750,000.00",cashFlowStatus:"Negative",CPI:"0.60"},
{id:6,title:"BBB Team",activeProject:"22",fees:"8,000,000.00",budget:"6,400,000.00",cumulativeExpenditure:"5,400,000.00",invoicedAmount:"4,000,000.00",receivedAmount:"3,800,000.00",cashFlowStatus:"Negative",CPI:"0.74"}
]
{
id: 1,
title: "All Teams",
activeProject: "147",
fees: "22,800,000.00",
budget: "18,240,000.00",
cumulativeExpenditure: "17,950,000.00",
invoicedAmount: "18,240,000.00",
receivedAmount: "10,900,000.00",
cashFlowStatus: "Negative",
CPI: "0.69",
},
{
id: 2,
title: "XXX Team",
activeProject: "25",
fees: "1,500,000.00",
budget: "1,200,000.00",
cumulativeExpenditure: "1,250,000.00",
invoicedAmount: "900,000.00",
receivedAmount: "650,000.00",
cashFlowStatus: "Negative",
CPI: "0.72",
},
{
id: 3,
title: "YYY Team",
activeProject: "35",
fees: "5,000,000.00",
budget: "4,000,000.00",
cumulativeExpenditure: "3,200,000.00",
invoicedAmount: "3,500,000.00",
receivedAmount: "3,500,000.00",
cashFlowStatus: "Positive",
CPI: "1.09",
},
{
id: 4,
title: "ZZZ Team",
activeProject: "50",
fees: "3,500,000.00",
budget: "2,800,000.00",
cumulativeExpenditure: "5,600,000.00",
invoicedAmount: "2,500,000.00",
receivedAmount: "2,200,000.00",
cashFlowStatus: "Negative",
CPI: "0.45",
},
{
id: 5,
title: "AAA Team",
activeProject: "15",
fees: "4,800,000.00",
budget: "3,840,000.00",
cumulativeExpenditure: "2,500,000.00",
invoicedAmount: "1,500,000.00",
receivedAmount: "750,000.00",
cashFlowStatus: "Negative",
CPI: "0.60",
},
{
id: 6,
title: "BBB Team",
activeProject: "22",
fees: "8,000,000.00",
budget: "6,400,000.00",
cumulativeExpenditure: "5,400,000.00",
invoicedAmount: "4,000,000.00",
receivedAmount: "3,800,000.00",
cashFlowStatus: "Negative",
CPI: "0.74",
},
];

const rows0 = [{id: 1,projectCode:"M1201",projectName:"Consultancy Project C", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "01/05/2024", client:"Client A", subsidiary:"N/A"},
{id: 2,projectCode:"M1321",projectName:"Consultancy Project CCC", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "20/01/2024", client:"Client E", subsidiary:"Subsidiary B"},
{id: 3,projectCode:"M1001",projectName:"Consultancy Project A", team:"YYY", teamLeader:"YYY", startDate:"01/07/2022", targetEndDate: "01/04/2024", client:"Client B", subsidiary:"N/A"},
{id: 4,projectCode:"M1301",projectName:"Consultancy Project AAAA", team:"YYY", teamLeader:"YYY", startDate:"01/09/2022", targetEndDate: "20/02/2024", client:"Client C", subsidiary:"Subsidiary A"},
{id: 5,projectCode:"M1354",projectName:"Consultancy Project BBB", team:"YYY", teamLeader:"YYY", startDate:"01/02/2023", targetEndDate: "31/01/2024", client:"Client D", subsidiary:"Subsidiary C"}
]
const rows0 = [
{
id: 1,
projectCode: "M1201",
projectName: "Consultancy Project C",
team: "XXX",
teamLeader: "XXX",
startDate: "01/08/2022",
targetEndDate: "01/05/2024",
client: "Client A",
subsidiary: "N/A",
},
{
id: 2,
projectCode: "M1321",
projectName: "Consultancy Project CCC",
team: "XXX",
teamLeader: "XXX",
startDate: "01/08/2022",
targetEndDate: "20/01/2024",
client: "Client E",
subsidiary: "Subsidiary B",
},
{
id: 3,
projectCode: "M1001",
projectName: "Consultancy Project A",
team: "YYY",
teamLeader: "YYY",
startDate: "01/07/2022",
targetEndDate: "01/04/2024",
client: "Client B",
subsidiary: "N/A",
},
{
id: 4,
projectCode: "M1301",
projectName: "Consultancy Project AAAA",
team: "YYY",
teamLeader: "YYY",
startDate: "01/09/2022",
targetEndDate: "20/02/2024",
client: "Client C",
subsidiary: "Subsidiary A",
},
{
id: 5,
projectCode: "M1354",
projectName: "Consultancy Project BBB",
team: "YYY",
teamLeader: "YYY",
startDate: "01/02/2023",
targetEndDate: "31/01/2024",
client: "Client D",
subsidiary: "Subsidiary C",
},
];

const rows1 = [{id: 1,projectCode:"M1201",projectName:"Consultancy Project C", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "01/05/2024", client:"Client A", subsidiary:"N/A"},
{id: 2,projectCode:"M1321",projectName:"Consultancy Project CCC", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "20/01/2024", client:"Client E", subsidiary:"Subsidiary B"},
]
const rows1 = [
{
id: 1,
projectCode: "M1201",
projectName: "Consultancy Project C",
team: "XXX",
teamLeader: "XXX",
startDate: "01/08/2022",
targetEndDate: "01/05/2024",
client: "Client A",
subsidiary: "N/A",
},
{
id: 2,
projectCode: "M1321",
projectName: "Consultancy Project CCC",
team: "XXX",
teamLeader: "XXX",
startDate: "01/08/2022",
targetEndDate: "20/01/2024",
client: "Client E",
subsidiary: "Subsidiary B",
},
];

const rows2 = [{id: 3,projectCode:"M1001",projectName:"Consultancy Project A", team:"YYY", teamLeader:"YYY", startDate:"01/07/2022", targetEndDate: "01/04/2024", client:"Client B", subsidiary:"N/A"},
{id: 4,projectCode:"M1301",projectName:"Consultancy Project AAAA", team:"YYY", teamLeader:"YYY", startDate:"01/09/2022", targetEndDate: "20/02/2024", client:"Client C", subsidiary:"Subsidiary A"},
{id: 5,projectCode:"M1354",projectName:"Consultancy Project BBB", team:"YYY", teamLeader:"YYY", startDate:"01/02/2023", targetEndDate: "31/01/2024", client:"Client D", subsidiary:"Subsidiary C"}
]
const rows2 = [
{
id: 3,
projectCode: "M1001",
projectName: "Consultancy Project A",
team: "YYY",
teamLeader: "YYY",
startDate: "01/07/2022",
targetEndDate: "01/04/2024",
client: "Client B",
subsidiary: "N/A",
},
{
id: 4,
projectCode: "M1301",
projectName: "Consultancy Project AAAA",
team: "YYY",
teamLeader: "YYY",
startDate: "01/09/2022",
targetEndDate: "20/02/2024",
client: "Client C",
subsidiary: "Subsidiary A",
},
{
id: 5,
projectCode: "M1354",
projectName: "Consultancy Project BBB",
team: "YYY",
teamLeader: "YYY",
startDate: "01/02/2023",
targetEndDate: "31/01/2024",
client: "Client D",
subsidiary: "Subsidiary C",
},
];

const projectFinancialRows = [{id: 1,cashFlowStatus:"Positive",cpi:"1.25", totalFees:"500,000.00", totalBudget:"400,000.00", totalCumulativeExpenditure:"280,000.00", totalInvoicedAmount: "350,000.00", totalUnInvoicedAmount:"150,000.00", totalReceivedAmount:"350,000.00", totalUnReceivedAmount:"0.00"}
]
const projectFinancialRows = [
{
id: 1,
cashFlowStatus: "Positive",
cpi: "1.25",
totalFees: "500,000.00",
totalBudget: "400,000.00",
totalCumulativeExpenditure: "280,000.00",
totalInvoicedAmount: "350,000.00",
totalUnInvoicedAmount: "150,000.00",
totalReceivedAmount: "350,000.00",
totalUnReceivedAmount: "0.00",
},
];

const [isCardClickedIndex, setIsCardClickedIndex] = React.useState(0);

const [selectedTeamData, setSelectedTeamData] : any[] = React.useState(rows0);
const [selectedTeamData, setSelectedTeamData]: any[] = React.useState(rows0);

const handleCardClick = (r:any) => {
setIsCardClickedIndex(r)
if (r === 0) {
setSelectedTeamData(rows0)
} else if (r === 1) {
setSelectedTeamData(rows1)
} else if (r === 2) {
setSelectedTeamData(rows2)
}
const handleCardClick = (r: any) => {
setIsCardClickedIndex(r);
if (r === 0) {
setSelectedTeamData(rows0);
} else if (r === 1) {
setSelectedTeamData(rows1);
} else if (r === 2) {
setSelectedTeamData(rows2);
}
};


const columns = [
{
id: 'projectCode',
field: 'projectCode',
id: "projectCode",
field: "projectCode",
headerName: "Project Code",
flex: 1,
},
{
id: 'projectName',
field: 'projectName',
},
{
id: "projectName",
field: "projectName",
headerName: "Project Name",
flex: 1,
},
{
id: 'team',
field: 'team',
headerName: "Team",
flex: 1,
},
{
id: 'teamLeader',
field: 'teamLeader',
headerName: "Team Leader",
flex: 1,
},
{
id: 'startDate',
field: 'startDate',
headerName: "Start Date",
flex: 1,
},
{
id: 'targetEndDate',
field: 'targetEndDate',
headerName: "Target End Date",
flex: 1,
},
{
id: 'client',
field: 'client',
headerName: "Client",
flex: 1,
},
{
id: 'subsidiary',
field: 'subsidiary',
headerName: "Subsidiary",
flex: 1,
},
];
},
{
id: "team",
field: "team",
headerName: "Team",
flex: 1,
},
{
id: "teamLeader",
field: "teamLeader",
headerName: "Team Leader",
flex: 1,
},
{
id: "startDate",
field: "startDate",
headerName: "Start Date",
flex: 1,
},
{
id: "targetEndDate",
field: "targetEndDate",
headerName: "Target End Date",
flex: 1,
},
{
id: "client",
field: "client",
headerName: "Client",
flex: 1,
},
{
id: "subsidiary",
field: "subsidiary",
headerName: "Subsidiary",
flex: 1,
},
];

const columns2 = [
{
id: 'cashFlowStatus',
field: 'cashFlowStatus',
headerName: "Cash Flow Status",
flex: 1,
renderCell: (params:any) => {
if (params.row.cashFlowStatus === "Positive") {
return (
<span className="text-lime-500">{params.row.cashFlowStatus}</span>
)
} else if (params.row.cashFlowStatus === "Negative") {
return (
<span className="text-red-500">{params.row.cashFlowStatus}</span>
)
}
},
},
{
id: 'cpi',
field: 'cpi',
headerName: "CPI",
flex: 0.7,
renderCell: (params:any) => {
if (params.row.cpi >= 1) {
return (
<span className="text-lime-500">{params.row.cpi}</span>
)
} else if (params.row.cpi < 1) {
return (
<span className="text-red-500">{params.row.cpi}</span>
)
}
},
},
{
id: 'totalFees',
field: 'totalFees',
headerName: "Total Fees (HKD)",
flex: 1,
renderCell: (params:any) => {
return (
<span>${params.row.totalFees}</span>
)
},
},
{
id: 'totalBudget',
field: 'totalBudget',
headerName: "Total Budget (HKD)",
flex: 1,
renderCell: (params:any) => {
return (
<span>${params.row.totalBudget}</span>
)
},
},
{
id: 'totalCumulativeExpenditure',
field: 'totalCumulativeExpenditure',
headerName: "Total Cumulative Expenditure (HKD)",
flex: 1,
renderCell: (params:any) => {
return (
<span>${params.row.totalCumulativeExpenditure}</span>
)
},
},
{
id: 'totalInvoicedAmount',
field: 'totalInvoicedAmount',
headerName: "Total Invoiced Amount (HKD)",
flex: 1,
renderCell: (params:any) => {
return (
<span>${params.row.totalInvoicedAmount}</span>
)
},
},
{
id: 'totalUnInvoicedAmount',
field: 'totalUnInvoicedAmount',
headerName: "Total Un-invoiced Amount (HKD)",
flex: 1,
renderCell: (params:any) => {
return (
<span>${params.row.totalUnInvoicedAmount}</span>
)
},
},
{
id: 'totalReceivedAmount',
field: 'totalReceivedAmount',
headerName: "Total Received Amount (HKD)",
flex: 1,
renderCell: (params:any) => {
return (
<span>${params.row.totalReceivedAmount}</span>
)
},
},
{
id: 'totalUnReceivedAmount',
field: 'totalUnReceivedAmount',
headerName: "Total Un-received Amount (HKD)",
flex: 1,
renderCell: (params:any) => {
return (
<span>${params.row.totalUnReceivedAmount}</span>
)
},
},
];
const columns2 = [
{
id: "cashFlowStatus",
field: "cashFlowStatus",
headerName: "Cash Flow Status",
flex: 1,
renderCell: (params: any) => {
if (params.row.cashFlowStatus === "Positive") {
return (
<span className="text-lime-500">{params.row.cashFlowStatus}</span>
);
} else if (params.row.cashFlowStatus === "Negative") {
return (
<span className="text-red-500">{params.row.cashFlowStatus}</span>
);
}
},
},
{
id: "cpi",
field: "cpi",
headerName: "CPI",
flex: 0.7,
renderCell: (params: any) => {
if (params.row.cpi >= 1) {
return <span className="text-lime-500">{params.row.cpi}</span>;
} else if (params.row.cpi < 1) {
return <span className="text-red-500">{params.row.cpi}</span>;
}
},
},
{
id: "totalFees",
field: "totalFees",
headerName: "Total Fees (HKD)",
flex: 1,
renderCell: (params: any) => {
return <span>${params.row.totalFees}</span>;
},
},
{
id: "totalBudget",
field: "totalBudget",
headerName: "Total Budget (HKD)",
flex: 1,
renderCell: (params: any) => {
return <span>${params.row.totalBudget}</span>;
},
},
{
id: "totalCumulativeExpenditure",
field: "totalCumulativeExpenditure",
headerName: "Total Cumulative Expenditure (HKD)",
flex: 1,
renderCell: (params: any) => {
return <span>${params.row.totalCumulativeExpenditure}</span>;
},
},
{
id: "totalInvoicedAmount",
field: "totalInvoicedAmount",
headerName: "Total Invoiced Amount (HKD)",
flex: 1,
renderCell: (params: any) => {
return <span>${params.row.totalInvoicedAmount}</span>;
},
},
{
id: "totalUnInvoicedAmount",
field: "totalUnInvoicedAmount",
headerName: "Total Un-invoiced Amount (HKD)",
flex: 1,
renderCell: (params: any) => {
return <span>${params.row.totalUnInvoicedAmount}</span>;
},
},
{
id: "totalReceivedAmount",
field: "totalReceivedAmount",
headerName: "Total Received Amount (HKD)",
flex: 1,
renderCell: (params: any) => {
return <span>${params.row.totalReceivedAmount}</span>;
},
},
{
id: "totalUnReceivedAmount",
field: "totalUnReceivedAmount",
headerName: "Total Un-received Amount (HKD)",
flex: 1,
renderCell: (params: any) => {
return <span>${params.row.totalUnReceivedAmount}</span>;
},
},
];

const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
const selectedRowsData = selectedTeamData.filter((row:any) =>
newSelectionModel.includes(row.id)
const selectedRowsData = selectedTeamData.filter((row: any) =>
newSelectionModel.includes(row.id),
);
console.log(selectedRowsData)
}
console.log(selectedRowsData);
};

return (
<Grid item sm>
<Card>
<CardHeader className="text-slate-500" title="Active Project Financial Status"/>
<div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}>
{projectFinancialData.map((record, index) => (
<div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={index} onClick={(r) => handleCardClick(index)}>
<ProjectFinancialCard Title={record.title} TotalActiveProjectNumber={record.activeProject} TotalFees={record.fees} TotalBudget={record.budget} TotalCumulative={record.cumulativeExpenditure} TotalInvoicedAmount={record.invoicedAmount} TotalReceivedAmount={record.receivedAmount} CashFlowStatus={record.cashFlowStatus} CostPerformanceIndex={record.CPI} ClickedIndex={isCardClickedIndex} Index={index}/>
</div>
))}
</div>
</Card>
<Card className="mt-5">
<CardHeader className="text-slate-500" title="Selected Team's Project"/>
<div style={{display:"inline-block",width:"99%",marginLeft:10}}>
<CustomDatagrid rows={selectedTeamData} columns={columns} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/>
</div>
</Card>
<Card className="mt-5">
<CardHeader className="text-slate-500" title="Individual Project Financial Status"/>
<div style={{display:"inline-block",width:"99%",marginLeft:10}}>
<CustomDatagrid rows={projectFinancialRows} columns={columns2} columnWidth={200} dataGridHeight={300}/>
</div>
</Card>
</Grid>
<Grid item sm>
<Card>
<CardHeader
className="text-slate-500"
title="Active Project Financial Status"
/>
<div
className="ml-10 mr-10"
style={{ display: "flex", flexWrap: "wrap", justifyContent: "start" }}
>
{projectFinancialData.map((record, index) => (
<div
className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block"
key={index}
onClick={(r) => handleCardClick(index)}
>
<ProjectFinancialCard
Title={record.title}
TotalActiveProjectNumber={record.activeProject}
TotalFees={record.fees}
TotalBudget={record.budget}
TotalCumulative={record.cumulativeExpenditure}
TotalInvoicedAmount={record.invoicedAmount}
TotalReceivedAmount={record.receivedAmount}
CashFlowStatus={record.cashFlowStatus}
CostPerformanceIndex={record.CPI}
ClickedIndex={isCardClickedIndex}
Index={index}
/>
</div>
))}
</div>
</Card>
<Card className="mt-5">
<CardHeader
className="text-slate-500"
title="Selected Team's Project"
/>
<div style={{ display: "inline-block", width: "99%", marginLeft: 10 }}>
<CustomDatagrid
rows={selectedTeamData}
columns={columns}
columnWidth={200}
dataGridHeight={300}
checkboxSelection={true}
onRowSelectionModelChange={handleSelectionChange}
selectionModel={selectionModel}
/>
</div>
</Card>
<Card className="mt-5">
<CardHeader
className="text-slate-500"
title="Individual Project Financial Status"
/>
<div style={{ display: "inline-block", width: "99%", marginLeft: 10 }}>
<CustomDatagrid
rows={projectFinancialRows}
columns={columns2}
columnWidth={200}
dataGridHeight={300}
/>
</div>
</Card>
</Grid>
);
};



+ 849
- 550
src/components/StaffUtilization/StaffUtilization.tsx
File diff suppressed because it is too large
View File


+ 17
- 18
src/components/Swal/CustomAlerts.js View File

@@ -1,23 +1,22 @@
import Swal from "sweetalert2";


export const msg = (text) => {
Swal.mixin({
toast: true,
position: "bottom-end",
showConfirmButton: false,
timer: 3000,
timerProgressBar: true,
didOpen: (toast) => {
toast.onmouseenter = Swal.stopTimer;
toast.onmouseleave = Swal.resumeTimer;
}
}).fire({
icon: "Success",
title: text
});
}
Swal.mixin({
toast: true,
position: "bottom-end",
showConfirmButton: false,
timer: 3000,
timerProgressBar: true,
didOpen: (toast) => {
toast.onmouseenter = Swal.stopTimer;
toast.onmouseleave = Swal.resumeTimer;
},
}).fire({
icon: "Success",
title: text,
});
};

export const popup = (text) => {
Swal.fire(text);
}
Swal.fire(text);
};

+ 66
- 28
src/components/UserWorkspacePage/ProjectGrid.tsx View File

@@ -1,44 +1,78 @@
"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 { 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";

interface ProjectGridProps {
tab: number;
}

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<Object[]>([])
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(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:'pink'}} 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>
@@ -47,15 +81,19 @@ const ProjectGrid: React.FC<ProjectGridProps> = (props) => {
</CardContent>
</Card>
);
}
};
// Apply the preset style to the cards in child, if not specified //
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>
{/* <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>
);
};



+ 31
- 34
src/components/UserWorkspacePage/UserWorkspacePage.tsx View File

@@ -8,9 +8,9 @@ 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 { Add } from "@mui/icons-material";
import Link from "next/link";
import { t } from 'i18next';
import { t } from "i18next";
import { Modal } from "@mui/material";
import CustomModal from "../CustomModal/CustomModal";
import EnterTimesheetModal from "../EnterTimesheet/EnterTimesheetModal";
@@ -18,7 +18,6 @@ import EnterTimesheetModal from "../EnterTimesheet/EnterTimesheetModal";
const UserWorkspacePage: React.FC = () => {
const [isModalVisible, setModalVisible] = useState(false);
const { t } = useTranslation("home");

const handleButtonClick = () => {
setModalVisible(true);
@@ -29,38 +28,36 @@ const UserWorkspacePage: React.FC = () => {
};

return (
<Grid container height="100vh" >
<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={handleButtonClick}
sx={{marginRight:"2rem"}}
>
Enter Timesheet
</Button>
<Button
variant="contained"
startIcon={<Add />}
sx={{marginRight:"2rem"}}
LinkComponent={Link}
href="/projects/create"
>
Record Leave
</Button>
</Stack>
<Suspense> {/*fallback={<ProjectSearch.Loading />}>*/}
</Suspense>
</div>
<EnterTimesheetModal isOpen={isModalVisible} onClose={handleCloseModal}/>
<AssignedProjectGrid Title="Assigned Project"/>
<PageTitle BigTitle={"User Workspace"} />
<div>
<Stack direction="row" justifyContent="right" flexWrap="wrap">
<Button
variant="contained"
startIcon={<Add />}
onClick={handleButtonClick}
sx={{ marginRight: "2rem" }}
>
Enter Timesheet
</Button>
<Button
variant="contained"
startIcon={<Add />}
sx={{ marginRight: "2rem" }}
LinkComponent={Link}
href="/projects/create"
>
Record Leave
</Button>
</Stack>
<Suspense> {/*fallback={<ProjectSearch.Loading />}>*/}</Suspense>
</div>
<EnterTimesheetModal
isOpen={isModalVisible}
onClose={handleCloseModal}
/>
<AssignedProjectGrid Title="Assigned Project" />
</Grid>
</Grid>
);


+ 5
- 6
src/theme.ts View File

@@ -1,12 +1,11 @@
import { createTheme } from '@mui/material/styles';
import { createTheme } from "@mui/material/styles";

const theme = createTheme({
palette: {
background: {
default: '#fcfcfc'
}
}

default: "#fcfcfc",
},
},
});

export default theme;
export default theme;

+ 359
- 325
src/theme/colorConst.js View File

@@ -4,51 +4,85 @@ import { aborted } from "util";
// - - - - - - WORK IN PROGRESS - - - - - - //

export const chartColor = [
'#CB4047', '#ED3A41', '#F47B50', '#FBA647',
'#FDB64C', '#CCBB32', '#9ACC59', '#57B962',
'#1E83C5', '#7C4A9D'
"#CB4047",
"#ED3A41",
"#F47B50",
"#FBA647",
"#FDB64C",
"#CCBB32",
"#9ACC59",
"#57B962",
"#1E83C5",
"#7C4A9D",
];

export const chartSingleColor = [
'#f2969a', '#fc9599', '#faa789', '#f7ae94',
'#ffd491', '#ede5a1', '#d1f5a2', '#9de0a4',
'#a2d4f5', '#b685d6'
"#f2969a",
"#fc9599",
"#faa789",
"#f7ae94",
"#ffd491",
"#ede5a1",
"#d1f5a2",
"#9de0a4",
"#a2d4f5",
"#b685d6",
];

export const rankColor = [
'#FFD700', '#C0C0C0', '#CD853F', '#57B962', '#57B962',
'#57B962', '#57B962', '#57B962', '#57B962', '#57B962'
"#FFD700",
"#C0C0C0",
"#CD853F",
"#57B962",
"#57B962",
"#57B962",
"#57B962",
"#57B962",
"#57B962",
"#57B962",
];

export const piechartColor1 = [
'#E84A3E', '#F2883C', '#FDCD4D', '#CE478A', '#B63D2A',
'#6A8B9E', '#60667E', '#58865F', '#2F763E', '#7D80B5',
"#E84A3E",
"#F2883C",
"#FDCD4D",
"#CE478A",
"#B63D2A",
"#6A8B9E",
"#60667E",
"#58865F",
"#2F763E",
"#7D80B5",
];

export const piechartColor2 = [
'#6A8B9E', '#60667E', '#58865F', '#2F763E', '#7D80B5',
'#E84A3E', '#F2883C', '#FDCD4D', '#CE478A', '#B63D2A',
"#6A8B9E",
"#60667E",
"#58865F",
"#2F763E",
"#7D80B5",
"#E84A3E",
"#F2883C",
"#FDCD4D",
"#CE478A",
"#B63D2A",
];

export const cardBorderColor = [
'#efb142', '#4bb641', '#448df2', '#e03c04'
];
export const cardBorderColor = ["#efb142", "#4bb641", "#448df2", "#e03c04"];

export const chartLineColor = [
'#FFFFFF', '#D9D9D9'
];
export const chartLineColor = ["#FFFFFF", "#D9D9D9"];

export const GENERAL_RED_COLOR = '#e03c04';
export const GENERAL_RED_COLOR = "#e03c04";

export const TABLE_HEADER_TEXT_COLOR = "#3367D1";

export const GENERAL_INFO_COLOR = '#448df2';
export const GENERAL_INFO_COLOR = "#448df2";

export const GENERAL_SETTING_COLOR = '#666666';
export const GENERAL_SETTING_COLOR = "#666666";

export const GENERAL_BORDER_COLOR = '#e6ebf1';
export const GENERAL_BORDER_COLOR = "#e6ebf1";

export const GENERAL_TEXT_COLOR = '#262626';
export const GENERAL_TEXT_COLOR = "#262626";

export const FONT_SIZE_L = "1.875rem";

@@ -57,327 +91,327 @@ export const FONT_SIZE_M = "1.5rem";
export const FONT_SIZE_S = "1.25rem";

export const PROJECT_CARD_STYLE = {
borderRadius: '10px',
//border: '10px dotted #ccc',
width: '20rem',
margin: '20px',
//backgroundColor:"pink"
borderRadius: "10px",
//border: '10px dotted #ccc',
width: "20rem",
margin: "20px",
//backgroundColor:"pink"
};

export const PROJECT_MODAL_STYLE = {
position: 'absolute',
width: '85%',
borderRadius: '10px',
height: '75%',
// top: '50%',
// left: '50%',
transform: 'translate(10%, 15%)',
backgroundColor: 'white',
padding: '20px',
display: 'flex',
flexDirection: 'column',
};
position: "absolute",
width: "85%",
borderRadius: "10px",
height: "75%",
// top: '50%',
// left: '50%',
transform: "translate(10%, 15%)",
backgroundColor: "white",
padding: "20px",
display: "flex",
flexDirection: "column",
};

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

export const TAB_THEME = {
components: {
MuiTab: {
styleOverrides: {
root: {
// fontSize: '1.0rem',
fontSize: '1.25rem'//'20px',
// height: '40px',
// width: '40vw', // Default width for xs screen sizes
// '@media (min-width: 600px)': { // sm breakpoint
// width: '20vw',
// },
// '@media (min-width: 960px)': { // md breakpoint
// width: '15vw',
// },
// '@media (min-width: 1280px)': { // lg breakpoint
// width: '7vw',
// },
// textTransform: "none",
// alignItems: 'center'
},
},
},
}
};
components: {
MuiTab: {
styleOverrides: {
root: {
// fontSize: '1.0rem',
fontSize: "1.25rem", //'20px',
// height: '40px',
// width: '40vw', // Default width for xs screen sizes
// '@media (min-width: 600px)': { // sm breakpoint
// width: '20vw',
// },
// '@media (min-width: 960px)': { // md breakpoint
// width: '15vw',
// },
// '@media (min-width: 1280px)': { // lg breakpoint
// width: '7vw',
// },
// textTransform: "none",
// alignItems: 'center'
},
},
},
},
};

// copy from MTMS
export const TSMS_BUTTON_THEME = createTheme({
palette: {
primary: {
main: '#92C1E9',
contrastText: '#FFFFFF',
},
secondary: {
main: '#898D8D',
contrastText: '#FFFFFF',
},
success: {
main: '#ADCAB8',
contrastText: '#FFFFFF',
},
danger: {
main: '#F890A5',
contrastText: '#FFFFFF',
},
warning: {
main: '#EFBE7D',
contrastText: '#FFFFFF',
},
disable: {
main: '#B2B4B2',
contrastText: '#FFFFFF',
},
create: {
// main: '#57B962',
main: '#ADCAB8',
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
contrastText: '#FFFFFF',
},
delete: {
// main: '#E03C04',
main: '#F890A5',
contrastText: '#FFFFFF',

},
cancel: {
// main: '#999999',
main: '#F890A5',
contrastText: '#FFFFFF',
},
back: {
// main: '#999999',
main: '#898D8D',
contrastText: '#FFFFFF',
},
reset: {
main: '#EFBE7D',
contrastText: '#FFFFFF',
},
save: {
// main: '#448DF2',
main: '#92C1E9',
contrastText: '#FFFFFF',
},
export: {
main: '#8C52FF',
contrastText: '#FFFFFF',
},
import: {
main: '#92C1E9',
contrastText: '#FFFFFF',
},
saveAs: {
main: '#FFBD59',
contrastText: '#FFFFFF',
}
},
components: {
MuiButton: {
styleOverrides: {
root: {
'& .MuiButtonBase-root-MuiButton-root': {
fontSize: FONT_SIZE_S
},
}
}
},
MuiButtonBase: {
styleOverrides: {
root: {
'&.MuiChip-root.Mui-disabled': {
opacity: 0.75,
},
'&.MuiButton-root': {
fontSize: FONT_SIZE_S
},
}
}
},
}
palette: {
primary: {
main: "#92C1E9",
contrastText: "#FFFFFF",
},
secondary: {
main: "#898D8D",
contrastText: "#FFFFFF",
},
success: {
main: "#ADCAB8",
contrastText: "#FFFFFF",
},
danger: {
main: "#F890A5",
contrastText: "#FFFFFF",
},
warning: {
main: "#EFBE7D",
contrastText: "#FFFFFF",
},
disable: {
main: "#B2B4B2",
contrastText: "#FFFFFF",
},
create: {
// main: '#57B962',
main: "#ADCAB8",
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
contrastText: "#FFFFFF",
},
delete: {
// main: '#E03C04',
main: "#F890A5",
contrastText: "#FFFFFF",
},
cancel: {
// main: '#999999',
main: "#F890A5",
contrastText: "#FFFFFF",
},
back: {
// main: '#999999',
main: "#898D8D",
contrastText: "#FFFFFF",
},
reset: {
main: "#EFBE7D",
contrastText: "#FFFFFF",
},
save: {
// main: '#448DF2',
main: "#92C1E9",
contrastText: "#FFFFFF",
},
export: {
main: "#8C52FF",
contrastText: "#FFFFFF",
},
import: {
main: "#92C1E9",
contrastText: "#FFFFFF",
},
saveAs: {
main: "#FFBD59",
contrastText: "#FFFFFF",
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
"& .MuiButtonBase-root-MuiButton-root": {
fontSize: FONT_SIZE_S,
},
},
},
},
MuiButtonBase: {
styleOverrides: {
root: {
"&.MuiChip-root.Mui-disabled": {
opacity: 0.75,
},
"&.MuiButton-root": {
fontSize: FONT_SIZE_S,
},
},
},
},
},
});

export const formTheme = createTheme({
components: {
MuiFormLabel: {
root: { // Name of the rule
color: "rgba(0, 0, 0, 1)",
},
styleOverrides: {
asterisk: {
color: "#db3131",
"&$error": {
color: "#db3131",
},
},
},
},
components: {
MuiFormLabel: {
root: {
// Name of the rule
color: "rgba(0, 0, 0, 1)",
},
styleOverrides: {
asterisk: {
color: "#db3131",
"&$error": {
color: "#db3131",
},
},
},
},
},
});


export const ARS_BUTTON_THEME = createTheme({
palette: {
create: {
main: '#57B962',
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
contrastText: '#FFFFFF',
},
delete: {
main: '#E03C04',
contrastText: '#FFFFFF',

},
cancel: {
main: '#999999',
contrastText: '#FFFFFF',

},
save: {
main: '#448DF2',
contrastText: '#FFFFFF',
},
export: {
main: '#8C52FF',
contrastText: '#FFFFFF',
},
saveAs: {
main: '#FFBD59',
contrastText: '#FFFFFF',
},
edit: {
main: '#F3AF2B',
contrastText: '#FFFFFF',
},
exportExcel: {
main: '#6A8B9E',
contrastText: '#FFFFFF',
}
},
components: {
MuiDataGrid: {
styleOverrides: {
actionsCell: {
'& .MuiDataGrid-actionsContainer .MuiIconButton-root': {
fontSize: '80px', // Set the desired icon size here
},
},
},
},
MuiButton: {
styleOverrides: {
root: {
// fontSize: '1.0rem',
fontSize: '1.25rem',
height: '40px',
width: '40vw', // Default width for xs screen sizes
'@media (min-width: 600px)': { // sm breakpoint
width: '20vw',
},
'@media (min-width: 960px)': { // md breakpoint
width: '15vw',
},
'@media (min-width: 1280px)': { // lg breakpoint
width: '7vw',
},
textTransform: "none",
alignItems: 'center'
},
},
},
}
palette: {
create: {
main: "#57B962",
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
contrastText: "#FFFFFF",
},
delete: {
main: "#E03C04",
contrastText: "#FFFFFF",
},
cancel: {
main: "#999999",
contrastText: "#FFFFFF",
},
save: {
main: "#448DF2",
contrastText: "#FFFFFF",
},
export: {
main: "#8C52FF",
contrastText: "#FFFFFF",
},
saveAs: {
main: "#FFBD59",
contrastText: "#FFFFFF",
},
edit: {
main: "#F3AF2B",
contrastText: "#FFFFFF",
},
exportExcel: {
main: "#6A8B9E",
contrastText: "#FFFFFF",
},
},
components: {
MuiDataGrid: {
styleOverrides: {
actionsCell: {
"& .MuiDataGrid-actionsContainer .MuiIconButton-root": {
fontSize: "80px", // Set the desired icon size here
},
},
},
},
MuiButton: {
styleOverrides: {
root: {
// fontSize: '1.0rem',
fontSize: "1.25rem",
height: "40px",
width: "40vw", // Default width for xs screen sizes
"@media (min-width: 600px)": {
// sm breakpoint
width: "20vw",
},
"@media (min-width: 960px)": {
// md breakpoint
width: "15vw",
},
"@media (min-width: 1280px)": {
// lg breakpoint
width: "7vw",
},
textTransform: "none",
alignItems: "center",
},
},
},
},
});

//from ARS
export const TSMS_LONG_BUTTON_THEME = createTheme({
palette: {
create: {
main: '#57B962',
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
contrastText: '#FFFFFF',
},
delete: {
main: '#E03C04',
contrastText: '#FFFFFF',

},
cancel: {
main: '#999999',
contrastText: '#FFFFFF',

},
save: {
main: '#448DF2',
contrastText: '#FFFFFF',
},
export: {
main: '#8C52FF',
contrastText: '#FFFFFF',
},
saveAs: {
main: '#FFBD59',
contrastText: '#FFFFFF',
},
edit: {
main: '#F3AF2B',
contrastText: '#FFFFFF',
},
exportExcel: {
main: '#60667E',
contrastText: '#FFFFFF',
}
},
components: {
MuiDataGrid: {
styleOverrides: {
actionsCell: {
'& .MuiDataGrid-actionsContainer .MuiIconButton-root': {
fontSize: '80px', // Set the desired icon size here
},
},
},
},
MuiButton: {
styleOverrides: {
root: {
fontSize: '1.25rem',
height: '40px',
width: '40vw', // Default width for xs screen sizes
'@media (min-width: 600px)': { // sm breakpoint
width: '30vw',
},
'@media (min-width: 960px)': { // md breakpoint
width: '25vw',
},
'@media (min-width: 1280px)': { // lg breakpoint
width: '14vw',
},
textTransform: "none",
alignItems: 'center'
},
},
},
}
});
palette: {
create: {
main: "#57B962",
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
contrastText: "#FFFFFF",
},
delete: {
main: "#E03C04",
contrastText: "#FFFFFF",
},
cancel: {
main: "#999999",
contrastText: "#FFFFFF",
},
save: {
main: "#448DF2",
contrastText: "#FFFFFF",
},
export: {
main: "#8C52FF",
contrastText: "#FFFFFF",
},
saveAs: {
main: "#FFBD59",
contrastText: "#FFFFFF",
},
edit: {
main: "#F3AF2B",
contrastText: "#FFFFFF",
},
exportExcel: {
main: "#60667E",
contrastText: "#FFFFFF",
},
},
components: {
MuiDataGrid: {
styleOverrides: {
actionsCell: {
"& .MuiDataGrid-actionsContainer .MuiIconButton-root": {
fontSize: "80px", // Set the desired icon size here
},
},
},
},
MuiButton: {
styleOverrides: {
root: {
fontSize: "1.25rem",
height: "40px",
width: "40vw", // Default width for xs screen sizes
"@media (min-width: 600px)": {
// sm breakpoint
width: "30vw",
},
"@media (min-width: 960px)": {
// md breakpoint
width: "25vw",
},
"@media (min-width: 1280px)": {
// lg breakpoint
width: "14vw",
},
textTransform: "none",
alignItems: "center",
},
},
},
},
});

Loading…
Cancel
Save