Browse Source

update dashboard

tags/Baseline_30082024_FRONTEND_UAT
Mac\David 1 year ago
parent
commit
898e74b819
7 changed files with 97 additions and 54 deletions
  1. +4
    -17
      src/app/api/cashflow/index.ts
  2. +21
    -9
      src/app/api/financialsummary/actions.ts
  3. +1
    -0
      src/components/Breadcrumb/Breadcrumb.tsx
  4. +6
    -0
      src/components/CustomDatagrid/CustomDatagrid.tsx
  5. +41
    -6
      src/components/ProjectCashFlow/ProjectCashFlow.tsx
  6. +5
    -7
      src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx
  7. +19
    -15
      src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx

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

@@ -1,4 +1,7 @@
"use server";
import { cache } from "react"; import { cache } from "react";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";


export interface CashFlow { export interface CashFlow {
id: number; id: number;
@@ -19,21 +22,5 @@ export const preloadProjects = () => {
}; };


export const fetchProjectsCashFlow = cache(async () => { export const fetchProjectsCashFlow = cache(async () => {
return mockProjects;
return serverFetchJson<CashFlow[]>(`${BASE_API_URL}/dashboard/searchCashFlowProject`);
}); });

const mockProjects: CashFlow[] = [
{
id: 1,
projectCode: "CUST-001",
projectName: "Client A",
team: "N/A",
teamLeader: "N/A",
startDate: "5",
startDateFrom: "5",
startDateTo: "5",
targetEndDate: "s",
client: "ss",
subsidiary: "ss",
},
];

+ 21
- 9
src/app/api/financialsummary/actions.ts View File

@@ -39,16 +39,28 @@ export interface FinancialSummaryByProjectResult {
totalUninvoiced: number; totalUninvoiced: number;
} }


export const searchFinancialSummaryByClient = cache(async (teamId: number) => {
return serverFetchJson<FinancialSummaryByClientResult[]>(
`${BASE_API_URL}/dashboard/searchFinancialSummaryByClient?teamId=${teamId}`
);
export const searchFinancialSummaryByClient = cache(async (teamId?: number) => {
if (teamId === undefined) {
return serverFetchJson<FinancialSummaryByClientResult[]>(
`${BASE_API_URL}/dashboard/searchFinancialSummaryByClient`
);
} else {
return serverFetchJson<FinancialSummaryByClientResult[]>(
`${BASE_API_URL}/dashboard/searchFinancialSummaryByClient?teamId=${teamId}`
);
}
}); });


export const searchFinancialSummaryByProject = cache(async (teamId: number) => {
export const searchFinancialSummaryByProject = cache(async (teamId?: number, customerId?:number) => {
if (teamId === undefined) {
return serverFetchJson<FinancialSummaryByProjectResult[]>(
`${BASE_API_URL}/dashboard/searchFinancialSummaryByProject`
);
} else {
return serverFetchJson<FinancialSummaryByProjectResult[]>(
`${BASE_API_URL}/dashboard/searchFinancialSummaryByProject?teamId=${teamId}&customerId=${customerId}`
);
}
return serverFetchJson<FinancialSummaryByProjectResult[]>(
`${BASE_API_URL}/dashboard/searchFinancialSummaryByProject?teamId=${teamId}`
);
}); });

+ 1
- 0
src/components/Breadcrumb/Breadcrumb.tsx View File

@@ -13,6 +13,7 @@ import { I18nProvider } from "@/i18n";
const pathToLabelMap: { [path: string]: string } = { const pathToLabelMap: { [path: string]: string } = {
"": "Overview", "": "Overview",
"/home": "User Workspace", "/home": "User Workspace",
"/dashboard": "Dashboard",
"/projects": "Projects", "/projects": "Projects",
"/projects/create": "Create Project", "/projects/create": "Create Project",
"/projects/createSub": "Sub Project", "/projects/createSub": "Sub Project",


+ 6
- 0
src/components/CustomDatagrid/CustomDatagrid.tsx View File

@@ -15,6 +15,7 @@ interface CustomDatagridProps {
dataGridHeight?: number | string; dataGridHeight?: number | string;
[key: string]: any; [key: string]: any;
checkboxSelection?: boolean; checkboxSelection?: boolean;
onRowClick?: any;
onRowSelectionModelChange?: ( onRowSelectionModelChange?: (
newSelectionModel: GridRowSelectionModel, newSelectionModel: GridRowSelectionModel,
) => void; ) => void;
@@ -34,6 +35,7 @@ const CustomDatagrid: React.FC<CustomDatagridProps> = ({
checkboxSelection, // Destructure the new prop checkboxSelection, // Destructure the new prop
onRowSelectionModelChange, // Destructure the new prop onRowSelectionModelChange, // Destructure the new prop
selectionModel, selectionModel,
onRowClick,
columnGroupingModel, columnGroupingModel,
pageSize, pageSize,
...props ...props
@@ -195,6 +197,7 @@ const CustomDatagrid: React.FC<CustomDatagridProps> = ({
rows={rowsWithDefaultValues} rows={rowsWithDefaultValues}
columns={modifiedColumns} columns={modifiedColumns}
editMode="row" editMode="row"
onRowClick={onRowClick}
checkboxSelection={checkboxSelection} checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange} onRowSelectionModelChange={onRowSelectionModelChange}
experimentalFeatures={{ columnGrouping: true }} experimentalFeatures={{ columnGrouping: true }}
@@ -226,6 +229,7 @@ const CustomDatagrid: React.FC<CustomDatagridProps> = ({
rows={rowsWithDefaultValues} rows={rowsWithDefaultValues}
columns={modifiedColumns} columns={modifiedColumns}
editMode="row" editMode="row"
onRowClick={onRowClick}
checkboxSelection={checkboxSelection} checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange} onRowSelectionModelChange={onRowSelectionModelChange}
experimentalFeatures={{ columnGrouping: true }} experimentalFeatures={{ columnGrouping: true }}
@@ -257,6 +261,7 @@ const CustomDatagrid: React.FC<CustomDatagridProps> = ({
rows={rowsWithDefaultValues} rows={rowsWithDefaultValues}
columns={modifiedColumns} columns={modifiedColumns}
editMode="row" editMode="row"
onRowClick={onRowClick}
checkboxSelection={checkboxSelection} checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange} onRowSelectionModelChange={onRowSelectionModelChange}
experimentalFeatures={{ columnGrouping: true }} experimentalFeatures={{ columnGrouping: true }}
@@ -289,6 +294,7 @@ const CustomDatagrid: React.FC<CustomDatagridProps> = ({
rows={rowsWithDefaultValues} rows={rowsWithDefaultValues}
columns={modifiedColumns} columns={modifiedColumns}
editMode="row" editMode="row"
onRowClick={onRowClick}
style={{ marginRight: 0 }} style={{ marginRight: 0 }}
checkboxSelection={checkboxSelection} checkboxSelection={checkboxSelection}
onRowSelectionModelChange={onRowSelectionModelChange} onRowSelectionModelChange={onRowSelectionModelChange}


+ 41
- 6
src/components/ProjectCashFlow/ProjectCashFlow.tsx View File

@@ -19,17 +19,34 @@ import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch"; import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense } from "react"; import { Suspense } from "react";
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
import { fetchProjectsCashFlow} from "@/app/api/cashflow";
import { Input, Label } from "reactstrap"; import { Input, Label } from "reactstrap";
import { CashFlow } from "@/app/api/cashflow";

interface Props {
projects: CashFlow[];
}
type SearchQuery = Partial<Omit<CashFlow, "id">>;
type SearchParamNames = keyof SearchQuery;


const ProjectCashFlow: React.FC = () => { const ProjectCashFlow: React.FC = () => {
const { t } = useTranslation("projects");
const todayDate = new Date(); const todayDate = new Date();
const [selectionModel, setSelectionModel]: any[] = React.useState([]); const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const [projectData, setProjectData]: any[] = React.useState([]);
const [cashFlowYear, setCashFlowYear]: any[] = React.useState( const [cashFlowYear, setCashFlowYear]: any[] = React.useState(
todayDate.getFullYear(), todayDate.getFullYear(),
); );
const [anticipateCashFlowYear, setAnticipateCashFlowYear]: any[] = React.useState( const [anticipateCashFlowYear, setAnticipateCashFlowYear]: any[] = React.useState(
todayDate.getFullYear(), todayDate.getFullYear(),
); );
const fetchData = async () => {
const cashFlowProject = await fetchProjectsCashFlow();
setProjectData(cashFlowProject)
}
useEffect(() => {
fetchData()
}, []);
const columns = [ const columns = [
{ {
id: "projectCode", id: "projectCode",
@@ -50,8 +67,8 @@ const ProjectCashFlow: React.FC = () => {
flex: 1, flex: 1,
}, },
{ {
id: "teamLeader",
field: "teamLeader",
id: "teamLead",
field: "teamLead",
headerName: "Team Leader", headerName: "Team Leader",
flex: 1, flex: 1,
}, },
@@ -530,8 +547,6 @@ const ProjectCashFlow: React.FC = () => {
remarks: "Monthly Manpower Expenditure", remarks: "Monthly Manpower Expenditure",
}, },
]; ];

const [projectData, setProjectData]: any[] = React.useState(rows);
const [ledgerData, setLedgerData]: any[] = React.useState(ledgerRows); const [ledgerData, setLedgerData]: any[] = React.useState(ledgerRows);
const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
const selectedRowsData = projectData.filter((row: any) => const selectedRowsData = projectData.filter((row: any) =>
@@ -540,11 +555,31 @@ const ProjectCashFlow: React.FC = () => {
console.log(selectedRowsData); console.log(selectedRowsData);
}; };


const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ 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",
},
],
[t],
);

return ( return (
<> <>
<Suspense fallback={<ProgressCashFlowSearch.Loading />}>
{/* <Suspense fallback={<ProgressCashFlowSearch.Loading />}>
<ProgressCashFlowSearch /> <ProgressCashFlowSearch />
</Suspense>
</Suspense> */}
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
console.log(query);
}}
/>
<CustomDatagrid <CustomDatagrid
rows={projectData} rows={projectData}
columns={columns} columns={columns}


+ 5
- 7
src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx View File

@@ -53,8 +53,6 @@ const ProjectFinancialCard: React.FC<Props> = ({
: "border-green-200 border-solid"; : "border-green-200 border-solid";
const selectedBackgroundColor = const selectedBackgroundColor =
ClickedIndex === Index ? "rgb(235 235 235)" : "rgb(255 255 255)"; ClickedIndex === Index ? "rgb(235 235 235)" : "rgb(255 255 255)";
console.log(ClickedIndex);
console.log(Index);
return ( return (
<Card <Card
style={{ style={{
@@ -84,35 +82,35 @@ const ProjectFinancialCard: React.FC<Props> = ({
Total Fees Total Fees
</div> </div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}> <div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalFees.toLocaleString()}
{TotalFees.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div> </div>
<hr /> <hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Budget Total Budget
</div> </div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}> <div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalBudget.toLocaleString()}
{TotalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div> </div>
<hr /> <hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Cumulative Expenditure Total Cumulative Expenditure
</div> </div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}> <div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalCumulative.toLocaleString()}
{TotalCumulative.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div> </div>
<hr /> <hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Invoiced Amount Total Invoiced Amount
</div> </div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}> <div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalInvoicedAmount.toLocaleString()}
{TotalInvoicedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div> </div>
<hr /> <hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
Total Received Amount Total Received Amount
</div> </div>
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}> <div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}>
{TotalReceivedAmount.toLocaleString()}
{TotalReceivedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div> </div>
<hr /> <hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>


+ 19
- 15
src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx View File

@@ -21,6 +21,7 @@ import { Suspense } from "react";
import { fetchFinancialSummaryCard } from "@/app/api/financialsummary"; import { fetchFinancialSummaryCard } from "@/app/api/financialsummary";
import { searchFinancialSummaryByClient,searchFinancialSummaryByProject } from "@/app/api/financialsummary/actions"; import { searchFinancialSummaryByClient,searchFinancialSummaryByProject } from "@/app/api/financialsummary/actions";
import ProjectFinancialCard from "./ProjectFinancialCard"; import ProjectFinancialCard from "./ProjectFinancialCard";
import VisibilityIcon from '@mui/icons-material/Visibility';


const ProjectFinancialSummary: React.FC = () => { const ProjectFinancialSummary: React.FC = () => {
const [SearchCriteria, setSearchCriteria] = React.useState({}); const [SearchCriteria, setSearchCriteria] = React.useState({});
@@ -33,16 +34,16 @@ const ProjectFinancialSummary: React.FC = () => {
const financialSummaryCard = await fetchFinancialSummaryCard(); const financialSummaryCard = await fetchFinancialSummaryCard();
setProjectFinancialData(financialSummaryCard) setProjectFinancialData(financialSummaryCard)
} }
const fetchTableData = async (teamId:any) => {
const fetchTableData = async (teamId?:any) => {
const financialSummaryByClient = await searchFinancialSummaryByClient(teamId); const financialSummaryByClient = await searchFinancialSummaryByClient(teamId);
const financialSummaryByProject = await searchFinancialSummaryByProject(teamId);
console.log(financialSummaryByClient) console.log(financialSummaryByClient)
console.log(financialSummaryByProject)
// console.log(financialSummaryByProject)
setClientFinancialRows(financialSummaryByClient) setClientFinancialRows(financialSummaryByClient)
setProjectFinancialRows(financialSummaryByProject)
} }
useEffect(() => { useEffect(() => {
fetchData() fetchData()
fetchTableData(undefined)
}, []); }, []);


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"}, 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"},
@@ -73,16 +74,9 @@ const ProjectFinancialSummary: React.FC = () => {


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


const handleCardClick = (r: any) => {
const handleCardClick = (r: any, index:any) => {
fetchTableData(r.teamId) fetchTableData(r.teamId)
// setIsCardClickedIndex(r);
// if (r === 0) {
// setSelectedTeamData(rows0);
// } else if (r === 1) {
// setSelectedTeamData(rows1);
// } else if (r === 2) {
// setSelectedTeamData(rows2);
// }
setIsCardClickedIndex(index)
}; };


const columns = [ const columns = [
@@ -380,6 +374,16 @@ const columns2 = [
); );
console.log(selectedRowsData); console.log(selectedRowsData);
}; };
const fetchProjectTableData = async (teamId?:any,customerId?:any) => {
const financialSummaryByProject = await searchFinancialSummaryByProject(teamId,customerId);
setProjectFinancialRows(financialSummaryByProject)
}

const handleRowClick = (params:any) => {
console.log(params.row.teamId);
fetchProjectTableData(params.row.teamId,params.row.cid)
};


return ( return (
<Grid item sm> <Grid item sm>
@@ -387,7 +391,7 @@ const columns2 = [
<CardHeader className="text-slate-500" title="Active Project Financial Status"/> <CardHeader className="text-slate-500" title="Active Project Financial Status"/>
<div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}> <div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}>
{projectFinancialData.map((record:any, index:any) => ( {projectFinancialData.map((record:any, index:any) => (
<div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={index} onClick={(r) => handleCardClick(record)}>
<div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={index} onClick={(r) => handleCardClick(record,index)}>
<ProjectFinancialCard Title={record.teamName} TotalActiveProjectNumber={record.projectNo} TotalFees={record.totalFee} TotalBudget={record.totalBudget} TotalCumulative={record.cumulativeExpenditure} TotalInvoicedAmount={record.totalInvoiced} TotalReceivedAmount={record.totalReceived} CashFlowStatus={record.cashFlowStatus} CostPerformanceIndex={record.cpi} ClickedIndex={isCardClickedIndex} Index={index}/> <ProjectFinancialCard Title={record.teamName} TotalActiveProjectNumber={record.projectNo} TotalFees={record.totalFee} TotalBudget={record.totalBudget} TotalCumulative={record.cumulativeExpenditure} TotalInvoicedAmount={record.totalInvoiced} TotalReceivedAmount={record.totalReceived} CashFlowStatus={record.cashFlowStatus} CostPerformanceIndex={record.cpi} ClickedIndex={isCardClickedIndex} Index={index}/>
</div> </div>
))} ))}
@@ -397,7 +401,7 @@ const columns2 = [
<CardHeader className="text-slate-500" title="Financial Status (by Client)"/> <CardHeader className="text-slate-500" title="Financial Status (by Client)"/>
<div style={{display:"inline-block",width:"99%",marginLeft:10}}> <div style={{display:"inline-block",width:"99%",marginLeft:10}}>
{/* <CustomDatagrid rows={clientFinancialRows} columns={columns} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/> */} {/* <CustomDatagrid rows={clientFinancialRows} columns={columns} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/> */}
<CustomDatagrid rows={clientFinancialRows} columns={columns} columnWidth={200} dataGridHeight={300}/>
<CustomDatagrid onRowClick={handleRowClick} rows={clientFinancialRows} columns={columns} columnWidth={200} dataGridHeight={300}/>
</div> </div>
</Card> </Card>
<Card className="mt-5"> <Card className="mt-5">


Loading…
Cancel
Save