@@ -15,72 +15,10 @@ export interface ClientProjectResult { | |||
projectNo: number; | |||
} | |||
// export interface ClientSubsidiaryProjectResult { | |||
// projectId: number; | |||
// projectCode: string; | |||
// projectName: string; | |||
// team: string; | |||
// teamLead: string; | |||
// expectedStage: string; | |||
// budgetedManhour: number; | |||
// spentManhour: number; | |||
// remainedManhour: number; | |||
// manhourConsumptionPercentage: number; | |||
// comingPaymentMilestone: string; | |||
// } | |||
export const preloadClientProjects = () => { | |||
fetchAllClientProjects(); | |||
}; | |||
// export const fetchClientProjects = cache(async () => { | |||
// return mockProjects; | |||
// }); | |||
export const fetchAllClientProjects = cache(async () => { | |||
return serverFetchJson<ClientProjectResult[]>(`${BASE_API_URL}/dashboard/searchCustomerSubsidiary`); | |||
}); | |||
// export const fetchAllClientSubsidiaryProjects = cache(async (customerId: number, subsidiaryId: number) => { | |||
// return serverFetchJson<ClientSubsidiaryProjectResult>( | |||
// `${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject/?${customerId}&${subsidiaryId}`, | |||
// { | |||
// next: { tags: [`allClientSubsidiaryProjects`] }, | |||
// }, | |||
// ); | |||
// }); | |||
// const mockProjects: ClientProjectResult[] = [ | |||
// { | |||
// id: 1, | |||
// clientCode: "CUST-001", | |||
// clientName: "Client A", | |||
// SubsidiaryClientCode: "N/A", | |||
// SubsidiaryClientName: "N/A", | |||
// NoOfProjects: 5, | |||
// }, | |||
// { | |||
// id: 2, | |||
// clientCode: "CUST-001", | |||
// clientName: "Client A", | |||
// SubsidiaryClientCode: "SUBS-001", | |||
// SubsidiaryClientName: "Subsidiary A", | |||
// NoOfProjects: 5, | |||
// }, | |||
// { | |||
// id: 3, | |||
// clientCode: "CUST-001", | |||
// clientName: "Client A", | |||
// SubsidiaryClientCode: "SUBS-002", | |||
// SubsidiaryClientName: "Subsidiary B", | |||
// NoOfProjects: 3, | |||
// }, | |||
// { | |||
// id: 4, | |||
// clientCode: "CUST-001", | |||
// clientName: "Client A", | |||
// SubsidiaryClientCode: "SUBS-003", | |||
// SubsidiaryClientName: "Subsidiary C", | |||
// NoOfProjects: 1, | |||
// }, | |||
// ]; |
@@ -0,0 +1,54 @@ | |||
"use server"; | |||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
import { Dayjs } from "dayjs"; | |||
import { cache } from "react"; | |||
export interface FinancialSummaryByClientResult { | |||
teamId:number; | |||
id:number; | |||
customerCode: string; | |||
customerName: string; | |||
projectNo: number; | |||
totalFee: number; | |||
totalBudget: number; | |||
cumulativeExpenditure: number; | |||
totalInvoiced: number; | |||
totalReceived: number; | |||
cashFlowStatus: string; | |||
cpi: number; | |||
totalUninvoiced: number; | |||
} | |||
export interface FinancialSummaryByProjectResult { | |||
teamId:number; | |||
id:number; | |||
projectCode: string; | |||
projectName: string; | |||
customerName: string; | |||
projectNo: number; | |||
totalFee: number; | |||
totalBudget: number; | |||
cumulativeExpenditure: number; | |||
totalInvoiced: number; | |||
totalReceived: number; | |||
cashFlowStatus: string; | |||
cpi: number; | |||
totalUninvoiced: number; | |||
} | |||
export const searchFinancialSummaryByClient = cache(async (teamId: number) => { | |||
return serverFetchJson<FinancialSummaryByClientResult[]>( | |||
`${BASE_API_URL}/dashboard/searchFinancialSummaryByClient?teamId=${teamId}` | |||
); | |||
}); | |||
export const searchFinancialSummaryByProject = cache(async (teamId: number) => { | |||
return serverFetchJson<FinancialSummaryByProjectResult[]>( | |||
`${BASE_API_URL}/dashboard/searchFinancialSummaryByProject?teamId=${teamId}` | |||
); | |||
}); |
@@ -0,0 +1,27 @@ | |||
"use server"; | |||
import { cache } from "react"; | |||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
// import "server-only"; | |||
export interface FinancialSummaryCardResult { | |||
teamId: number; | |||
teamName: string; | |||
projectNo: number; | |||
totalFee: number; | |||
totalBudget: number; | |||
cumulativeExpenditure: number; | |||
totalInvoiced: number; | |||
totalReceived: number; | |||
cashFlowStatus: string; | |||
cpi: number; | |||
} | |||
export const preloadFinancialSummaryCard = () => { | |||
fetchFinancialSummaryCard(); | |||
}; | |||
export const fetchFinancialSummaryCard = cache(async () => { | |||
return serverFetchJson<FinancialSummaryCardResult[]>(`${BASE_API_URL}/dashboard/searchFinancialSummaryCard`); | |||
}); |
@@ -0,0 +1,29 @@ | |||
"use server"; | |||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
import { Dayjs } from "dayjs"; | |||
import { cache } from "react"; | |||
export interface ClientSubsidiaryProjectResult { | |||
color: string; | |||
projectId: number; | |||
projectCode: string; | |||
projectName: string; | |||
team: string; | |||
teamLead: string; | |||
expectedStage: string; | |||
budgetedManhour: number; | |||
spentManhour: number; | |||
remainedManhour: number; | |||
manhourConsumptionPercentage: number; | |||
comingPaymentMilestone: string; | |||
} | |||
export const fetchAllTeamProjects = cache(async (teamLeadId: number) => { | |||
return serverFetchJson<ClientSubsidiaryProjectResult[]>( | |||
`${BASE_API_URL}/dashboard/searchTeamProject?teamLeadId=${teamLeadId}` | |||
); | |||
}); |
@@ -1,10 +1,15 @@ | |||
import { cache } from "react"; | |||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
import "server-only"; | |||
export interface TeamProjectResult { | |||
id: number; | |||
teamId: number; | |||
teamLeadId: number; | |||
teamCode: string; | |||
teamName: string; | |||
NoOfProjects: number; | |||
projectNo: number; | |||
} | |||
export const preloadProjects = () => { | |||
@@ -12,32 +17,6 @@ export const preloadProjects = () => { | |||
}; | |||
export const fetchTeamProjects = cache(async () => { | |||
return mockProjects; | |||
return serverFetchJson<TeamProjectResult[]>(`${BASE_API_URL}/dashboard/searchTeamProjectNo`); | |||
}); | |||
const mockProjects: TeamProjectResult[] = [ | |||
{ | |||
id: 1, | |||
teamCode: "TEAM-001", | |||
teamName: "Team A", | |||
NoOfProjects: 5, | |||
}, | |||
{ | |||
id: 2, | |||
teamCode: "TEAM-002", | |||
teamName: "Team B", | |||
NoOfProjects: 5, | |||
}, | |||
{ | |||
id: 3, | |||
teamCode: "TEAM-003", | |||
teamName: "Team C", | |||
NoOfProjects: 3, | |||
}, | |||
{ | |||
id: 4, | |||
teamCode: "TEAM-004", | |||
teamName: "Team D", | |||
NoOfProjects: 1, | |||
}, | |||
]; |
@@ -303,130 +303,6 @@ const ProgressByClient: React.FC<Props> = () => { | |||
flex:0.1 | |||
}, | |||
]; | |||
const optionstest: ApexOptions = { | |||
chart: { | |||
height: 350, | |||
type: "line", | |||
}, | |||
stroke: { | |||
width: [0, 0, 2, 2], | |||
}, | |||
plotOptions: { | |||
bar: { | |||
horizontal: false, | |||
distributed: false, | |||
}, | |||
}, | |||
dataLabels: { | |||
enabled: false, | |||
}, | |||
xaxis: { | |||
categories: [ | |||
"Q1", | |||
"Q2", | |||
"Q3", | |||
"Q4", | |||
"Q5", | |||
"Q6", | |||
"Q7", | |||
"Q8", | |||
"Q9", | |||
"Q10", | |||
"Q11", | |||
"Q12", | |||
], | |||
}, | |||
yaxis: [ | |||
{ | |||
title: { | |||
text: "Monthly Income and Expenditure(HKD)", | |||
}, | |||
min: 0, | |||
max: 350000, | |||
tickAmount: 5, | |||
labels: { | |||
formatter: function (val) { | |||
return val.toLocaleString() | |||
} | |||
} | |||
}, | |||
{ | |||
show: false, | |||
seriesName: "Monthly_Expenditure", | |||
title: { | |||
text: "Monthly Expenditure (HKD)", | |||
}, | |||
min: 0, | |||
max: 350000, | |||
tickAmount: 5, | |||
}, | |||
{ | |||
seriesName: "Cumulative_Income", | |||
opposite: true, | |||
title: { | |||
text: "Cumulative Income and Expenditure(HKD)", | |||
}, | |||
min: 0, | |||
max: 850000, | |||
tickAmount: 5, | |||
labels: { | |||
formatter: function (val) { | |||
return val.toLocaleString() | |||
} | |||
} | |||
}, | |||
{ | |||
show: false, | |||
seriesName: "Cumulative_Expenditure", | |||
opposite: true, | |||
title: { | |||
text: "Cumulative Expenditure (HKD)", | |||
}, | |||
min: 0, | |||
max: 850000, | |||
tickAmount: 5, | |||
}, | |||
], | |||
grid: { | |||
borderColor: "#f1f1f1", | |||
}, | |||
annotations: {}, | |||
series: [ | |||
{ | |||
name: "Monthly_Income", | |||
type: "column", | |||
color: "#ffde91", | |||
data: [0, 110000, 0, 0, 185000, 0, 0, 189000, 0, 0, 300000, 0], | |||
}, | |||
{ | |||
name: "Monthly_Expenditure", | |||
type: "column", | |||
color: "#82b59a", | |||
data: [ | |||
0, 160000, 120000, 120000, 55000, 55000, 55000, 55000, 55000, 70000, | |||
55000, 55000, | |||
], | |||
}, | |||
{ | |||
name: "Cumulative_Income", | |||
type: "line", | |||
color: "#EE6D7A", | |||
data: [ | |||
0, 100000, 100000, 100000, 300000, 300000, 300000, 500000, 500000, | |||
500000, 800000, 800000, | |||
], | |||
}, | |||
{ | |||
name: "Cumulative_Expenditure", | |||
type: "line", | |||
color: "#7cd3f2", | |||
data: [ | |||
0, 198000, 240000, 400000, 410000, 430000, 510000, 580000, 600000, | |||
710000, 730000, 790000, | |||
], | |||
}, | |||
], | |||
}; | |||
const options2: ApexOptions = { | |||
chart: { | |||
@@ -18,9 +18,13 @@ import { AnyARecord, AnyCnameRecord } from "dns"; | |||
import SearchBox, { Criterion } from "../SearchBox"; | |||
import ProgressByTeamSearch from "@/components/ProgressByTeamSearch"; | |||
import { Suspense } from "react"; | |||
import { useSearchParams } from 'next/navigation'; | |||
import { fetchAllTeamProjects} from "@/app/api/teamprojects/actions"; | |||
// const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); | |||
const ProgressByTeam: React.FC = () => { | |||
const searchParams = useSearchParams(); | |||
const teamLeadId = searchParams.get('teamLeadId'); | |||
const [activeTab, setActiveTab] = useState("financialSummary"); | |||
const [SearchCriteria, setSearchCriteria] = React.useState({}); | |||
const { t } = useTranslation("dashboard"); | |||
@@ -44,6 +48,64 @@ const ProgressByTeam: React.FC = () => { | |||
const [receiptFromDate, setReceiptFromDate] = useState(null); | |||
const [receiptToDate, setReceiptToDate] = useState(null); | |||
const [selectedRows, setSelectedRows] = useState([]); | |||
const [chartProjectName, setChartProjectName]:any[] = useState([]); | |||
const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]); | |||
const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | |||
"#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | |||
"#f595a6", "#88f1cc", "#9dcff5", "#a39bf5", "#f8de83", | |||
"#f5a0b1", "#82eec7", "#a8d4f1", "#9f9ef1", "#f6ea7f", | |||
"#f5abb4", "#7cebca", "#b3d9ed", "#9ba1ed", "#f4f67b", | |||
"#f5b6b7", "#76e8cd", "#bed6e9", "#97a4e9", "#f2fa77", | |||
"#f5c1ba", "#70e5d0", "#c9d3e5", "#93a7e5", "#f0fe73", | |||
"#f5ccbd", "#6ae2d3", "#d4d0e1", "#8faae1", "#eefe6f", | |||
"#f5d7c0", "#64dfd6", "#dfc5dd", "#8badd5", "#ecfe6b", | |||
"#f5e2c3", "#5edcd9", "#eabada", "#87b0c9", "#eafc67", | |||
"#f5edc6", "#58d9dc", "#f5afd6", "#83b3bd", "#e8fc63", | |||
"#f5f8c9", "#52d6df", "#ffacd2", "#7fb6b1", "#e6fc5f", | |||
"#f5ffcc", "#4cd3e2", "#ffa9ce", "#7bb9a5", "#e4fc5b", | |||
"#f2ffcf", "#46d0e5", "#ffa6ca", "#77bc99", "#e2fc57", | |||
"#efffd2", "#40cde8", "#ffa3c6", "#73bf8d", "#e0fc53", | |||
"#ecffd5", "#3acaeb", "#ffa0c2", "#6fc281", "#defb4f", | |||
"#e9ffd8", "#34c7ee", "#ff9dbe", "#6bc575", "#dcfb4b", | |||
"#e6ffdb", "#2ec4f1", "#ff9aba", "#67c869", "#dafb47", | |||
"#e3ffde", "#28c1f4", "#ff97b6", "#63cb5d", "#d8fb43", | |||
"#e0ffe1", "#22bef7", "#ff94b2", "#5fce51", "#d6fb3f", | |||
"#ddfee4", "#1cbbfa", "#ff91ae", "#5bd145", "#d4fb3b", | |||
"#dafee7", "#16b8fd", "#ff8eaa", "#57d439", "#d2fb37", | |||
"#d7feea", "#10b5ff", "#ff8ba6", "#53d72d", "#d0fb33", | |||
"#d4feed", "#0ab2ff", "#ff88a2", "#4fda21", "#cefb2f", | |||
"#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"]; | |||
const [teamProjectResult, setTeamProjectResult]:any[] = useState([]); | |||
const fetchData = async () => { | |||
if (teamLeadId) { | |||
try { | |||
const clickResult = await fetchAllTeamProjects( | |||
Number(teamLeadId)) | |||
console.log(clickResult) | |||
setTeamProjectResult(clickResult); | |||
} catch (error) { | |||
console.error('Error fetching team projects:', error); | |||
} | |||
} | |||
} | |||
useEffect(() => { | |||
const projectName = [] | |||
const manhourConsumptionPercentage = [] | |||
for (let i = 0; i < teamProjectResult.length; i++){ | |||
teamProjectResult[i].color = color[i] | |||
projectName.push(teamProjectResult[i].projectName) | |||
manhourConsumptionPercentage.push(teamProjectResult[i].manhourConsumptionPercentage) | |||
} | |||
setChartProjectName(projectName) | |||
setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | |||
}, [teamProjectResult]); | |||
useEffect(() => { | |||
fetchData() | |||
}, [teamLeadId]); | |||
const rows = [ | |||
{ | |||
id: 1, | |||
@@ -373,7 +435,35 @@ const ProgressByTeam: React.FC = () => { | |||
type: "bar", | |||
height: 350, | |||
}, | |||
colors: ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b"], | |||
series: [{ | |||
name: "Project Resource Consumption Percentage", | |||
data: chartManhourConsumptionPercentage, | |||
},], | |||
colors: ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | |||
"#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | |||
"#f595a6", "#88f1cc", "#9dcff5", "#a39bf5", "#f8de83", | |||
"#f5a0b1", "#82eec7", "#a8d4f1", "#9f9ef1", "#f6ea7f", | |||
"#f5abb4", "#7cebca", "#b3d9ed", "#9ba1ed", "#f4f67b", | |||
"#f5b6b7", "#76e8cd", "#bed6e9", "#97a4e9", "#f2fa77", | |||
"#f5c1ba", "#70e5d0", "#c9d3e5", "#93a7e5", "#f0fe73", | |||
"#f5ccbd", "#6ae2d3", "#d4d0e1", "#8faae1", "#eefe6f", | |||
"#f5d7c0", "#64dfd6", "#dfc5dd", "#8badd5", "#ecfe6b", | |||
"#f5e2c3", "#5edcd9", "#eabada", "#87b0c9", "#eafc67", | |||
"#f5edc6", "#58d9dc", "#f5afd6", "#83b3bd", "#e8fc63", | |||
"#f5f8c9", "#52d6df", "#ffacd2", "#7fb6b1", "#e6fc5f", | |||
"#f5ffcc", "#4cd3e2", "#ffa9ce", "#7bb9a5", "#e4fc5b", | |||
"#f2ffcf", "#46d0e5", "#ffa6ca", "#77bc99", "#e2fc57", | |||
"#efffd2", "#40cde8", "#ffa3c6", "#73bf8d", "#e0fc53", | |||
"#ecffd5", "#3acaeb", "#ffa0c2", "#6fc281", "#defb4f", | |||
"#e9ffd8", "#34c7ee", "#ff9dbe", "#6bc575", "#dcfb4b", | |||
"#e6ffdb", "#2ec4f1", "#ff9aba", "#67c869", "#dafb47", | |||
"#e3ffde", "#28c1f4", "#ff97b6", "#63cb5d", "#d8fb43", | |||
"#e0ffe1", "#22bef7", "#ff94b2", "#5fce51", "#d6fb3f", | |||
"#ddfee4", "#1cbbfa", "#ff91ae", "#5bd145", "#d4fb3b", | |||
"#dafee7", "#16b8fd", "#ff8eaa", "#57d439", "#d2fb37", | |||
"#d7feea", "#10b5ff", "#ff8ba6", "#53d72d", "#d0fb33", | |||
"#d4feed", "#0ab2ff", "#ff88a2", "#4fda21", "#cefb2f", | |||
"#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"], | |||
plotOptions: { | |||
bar: { | |||
horizontal: true, | |||
@@ -384,13 +474,7 @@ const ProgressByTeam: React.FC = () => { | |||
enabled: false, | |||
}, | |||
xaxis: { | |||
categories: [ | |||
"Consultancy Project 123", | |||
"Consultancy Project 456", | |||
"Construction Project A", | |||
"Construction Project B", | |||
"Construction Project C", | |||
], | |||
categories: chartProjectName, | |||
}, | |||
yaxis: { | |||
title: { | |||
@@ -414,7 +498,7 @@ const ProgressByTeam: React.FC = () => { | |||
}; | |||
const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { | |||
const selectedRowsData = rows2.filter((row) => | |||
const selectedRowsData = teamProjectResult.filter((row:any) => | |||
newSelectionModel.includes(row.id), | |||
); | |||
console.log(selectedRowsData); | |||
@@ -427,7 +511,7 @@ const ProgressByTeam: React.FC = () => { | |||
if (i === selectedRowsData.length && i > 0) { | |||
projectArray.push("Remained"); | |||
} else if (selectedRowsData.length > 0) { | |||
projectArray.push(selectedRowsData[i].project); | |||
projectArray.push(selectedRowsData[i].projectName); | |||
totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour); | |||
totalSpent += Number(selectedRowsData[i].spentManhour); | |||
pieChartColorArray.push(selectedRowsData[i].color); | |||
@@ -485,7 +569,7 @@ const ProgressByTeam: React.FC = () => { | |||
<div style={{ display: "inline-block", width: "99%" }}> | |||
<ReactApexChart | |||
options={options} | |||
series={series} | |||
series={options.series} | |||
type="bar" | |||
height={350} | |||
/> | |||
@@ -507,7 +591,7 @@ const ProgressByTeam: React.FC = () => { | |||
style={{ display: "inline-block", width: "99%", marginLeft: 10 }} | |||
> | |||
<CustomDatagrid | |||
rows={rows2} | |||
rows={teamProjectResult} | |||
columns={columns2} | |||
columnWidth={200} | |||
dataGridHeight={300} | |||
@@ -1,11 +1,13 @@ | |||
"use client"; | |||
import { ProjectResult } from "@/app/api/projects"; | |||
import React, { useMemo, useState } from "react"; | |||
import React, { useMemo, useState, useCallback } from "react"; | |||
import SearchBox, { Criterion } from "../SearchBox"; | |||
import { useTranslation } from "react-i18next"; | |||
import SearchResults, { Column } from "../SearchResults"; | |||
import { TeamProjectResult } from "@/app/api/teamprojects"; | |||
import VisibilityIcon from '@mui/icons-material/Visibility'; | |||
import { useRouter, useSearchParams } from "next/navigation"; | |||
interface Props { | |||
projects: TeamProjectResult[]; | |||
@@ -15,7 +17,7 @@ type SearchParamNames = keyof SearchQuery; | |||
const ProgressByClientSearch: React.FC<Props> = ({ projects }) => { | |||
const { t } = useTranslation("projects"); | |||
const router = useRouter(); | |||
// If project searching is done on the server-side, then no need for this. | |||
const [filteredProjects, setFilteredProjects] = useState(projects); | |||
@@ -27,13 +29,31 @@ const ProgressByClientSearch: React.FC<Props> = ({ projects }) => { | |||
[t], | |||
); | |||
const onTaskClick = useCallback(async (teamProjectResult: TeamProjectResult) => { | |||
try { | |||
console.log(teamProjectResult) | |||
router.push( | |||
`/dashboard/ProjectStatusByTeam?teamLeadId=${teamProjectResult.teamLeadId}` | |||
); | |||
} catch (error) { | |||
console.error('Error fetching team projects:', error); | |||
} | |||
}, []); | |||
const columns = useMemo<Column<TeamProjectResult>[]>( | |||
() => [ | |||
{ | |||
name: "id", | |||
label: t("Details"), | |||
onClick: onTaskClick, | |||
buttonIcon: <VisibilityIcon />, | |||
}, | |||
{ name: "teamCode", label: t("Team Code") }, | |||
{ name: "teamName", label: t("Team Name") }, | |||
{ name: "NoOfProjects", label: t("No. of Projects") }, | |||
{ name: "projectNo", label: t("No. of Projects") }, | |||
], | |||
[t], | |||
[onTaskClick, t], | |||
); | |||
return ( | |||
@@ -41,7 +61,13 @@ const ProgressByClientSearch: React.FC<Props> = ({ projects }) => { | |||
<SearchBox | |||
criteria={searchCriteria} | |||
onSearch={(query) => { | |||
console.log(query); | |||
setFilteredProjects( | |||
projects.filter( | |||
(cp) => | |||
cp.teamCode.toLowerCase().includes(query.teamCode.toLowerCase()) && | |||
cp.teamName.toLowerCase().includes(query.teamName.toLowerCase()) | |||
), | |||
); | |||
}} | |||
/> | |||
<SearchResults<TeamProjectResult> | |||
@@ -20,14 +20,14 @@ import { Suspense } from "react"; | |||
interface Props { | |||
Title: string; | |||
TotalActiveProjectNumber: string; | |||
TotalFees: string; | |||
TotalBudget: string; | |||
TotalCumulative: string; | |||
TotalInvoicedAmount: string; | |||
TotalReceivedAmount: string; | |||
TotalActiveProjectNumber: number; | |||
TotalFees: number; | |||
TotalBudget: number; | |||
TotalCumulative: number; | |||
TotalInvoicedAmount: number; | |||
TotalReceivedAmount: number; | |||
CashFlowStatus: string; | |||
CostPerformanceIndex: string; | |||
CostPerformanceIndex: number; | |||
ClickedIndex: number; | |||
Index: number; | |||
} | |||
@@ -77,42 +77,42 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||
Total Active Project | |||
</div> | |||
<div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}> | |||
{TotalActiveProjectNumber} | |||
{TotalActiveProjectNumber.toLocaleString()} | |||
</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} | |||
{TotalFees.toLocaleString()} | |||
</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} | |||
{TotalBudget.toLocaleString()} | |||
</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} | |||
{TotalCumulative.toLocaleString()} | |||
</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} | |||
{TotalInvoicedAmount.toLocaleString()} | |||
</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} | |||
{TotalReceivedAmount.toLocaleString()} | |||
</div> | |||
<hr /> | |||
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> | |||
@@ -18,86 +18,32 @@ import { AnyARecord, AnyCnameRecord } from "dns"; | |||
import SearchBox, { Criterion } from "../SearchBox"; | |||
import ProgressByClientSearch from "@/components/ProgressByClientSearch"; | |||
import { Suspense } from "react"; | |||
import { fetchFinancialSummaryCard } from "@/app/api/financialsummary"; | |||
import { searchFinancialSummaryByClient,searchFinancialSummaryByProject } from "@/app/api/financialsummary/actions"; | |||
import ProjectFinancialCard from "./ProjectFinancialCard"; | |||
const ProjectFinancialSummary: React.FC = () => { | |||
const [SearchCriteria, setSearchCriteria] = React.useState({}); | |||
const { t } = useTranslation("dashboard"); | |||
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", | |||
}, | |||
]; | |||
const [projectFinancialData, setProjectFinancialData]: any[] = React.useState([]); | |||
const [clientFinancialRows, setClientFinancialRows]: any[] = React.useState([]); | |||
const [projectFinancialRows, setProjectFinancialRows]: any[] = React.useState([]); | |||
const fetchData = async () => { | |||
const financialSummaryCard = await fetchFinancialSummaryCard(); | |||
setProjectFinancialData(financialSummaryCard) | |||
} | |||
const fetchTableData = async (teamId:any) => { | |||
const financialSummaryByClient = await searchFinancialSummaryByClient(teamId); | |||
const financialSummaryByProject = await searchFinancialSummaryByProject(teamId); | |||
console.log(financialSummaryByClient) | |||
console.log(financialSummaryByProject) | |||
setClientFinancialRows(financialSummaryByClient) | |||
setProjectFinancialRows(financialSummaryByProject) | |||
} | |||
useEffect(() => { | |||
fetchData() | |||
}, []); | |||
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"}, | |||
@@ -115,45 +61,46 @@ const ProjectFinancialSummary: React.FC = () => { | |||
{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,projectCode:"M1354",projectName:"Consultanct Project BBB",clientName:"Client D",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"} | |||
] | |||
// const projectFinancialRows = [{id: 1,projectCode:"M1354",projectName:"Consultanct Project BBB",clientName:"Client D",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"} | |||
// ] | |||
const clientFinancialRows =[{id: 1,clientCode:"Cust-02",clientName:"Client B",totalProjectInvolved:"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"}, | |||
{id: 2,clientCode:"Cust-03",clientName:"Client C",totalProjectInvolved:"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"}, | |||
{id: 3,clientCode:"Cust-04",clientName:"Client D",totalProjectInvolved:"4",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"} | |||
] | |||
// const clientFinancialRows =[{id: 1,clientCode:"Cust-02",clientName:"Client B",totalProjectInvolved:"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"}, | |||
// {id: 2,clientCode:"Cust-03",clientName:"Client C",totalProjectInvolved:"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"}, | |||
// {id: 3,clientCode:"Cust-04",clientName:"Client D",totalProjectInvolved:"4",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"} | |||
// ] | |||
const [isCardClickedIndex, setIsCardClickedIndex] = React.useState(0); | |||
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); | |||
} | |||
fetchTableData(r.teamId) | |||
// setIsCardClickedIndex(r); | |||
// if (r === 0) { | |||
// setSelectedTeamData(rows0); | |||
// } else if (r === 1) { | |||
// setSelectedTeamData(rows1); | |||
// } else if (r === 2) { | |||
// setSelectedTeamData(rows2); | |||
// } | |||
}; | |||
const columns = [ | |||
{ | |||
id: 'clientCode', | |||
field: 'clientCode', | |||
id: 'customerCode', | |||
field: 'customerCode', | |||
headerName: "Client Code", | |||
flex: 0.7, | |||
}, | |||
{ | |||
id: 'clientName', | |||
field: 'clientName', | |||
id: 'customerName', | |||
field: 'customerName', | |||
headerName: "Client Name", | |||
flex: 1, | |||
}, | |||
{ | |||
id: 'totalProjectInvolved', | |||
field: 'totalProjectInvolved', | |||
id: 'projectNo', | |||
field: 'projectNo', | |||
headerName: "Total Project Involved", | |||
flex: 1, | |||
}, | |||
@@ -163,6 +110,7 @@ const ProjectFinancialSummary: React.FC = () => { | |||
headerName: "Cash Flow Status", | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
console.log(params.row) | |||
if (params.row.cashFlowStatus === "Positive") { | |||
return ( | |||
<span className="text-lime-500">{params.row.cashFlowStatus}</span> | |||
@@ -192,13 +140,13 @@ const ProjectFinancialSummary: React.FC = () => { | |||
}, | |||
}, | |||
{ | |||
id: 'totalFees', | |||
field: 'totalFees', | |||
id: 'totalFee', | |||
field: 'totalFee', | |||
headerName: "Total Fees (HKD)", | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalFees}</span> | |||
<span>${params.row.totalFee}</span> | |||
) | |||
}, | |||
}, | |||
@@ -214,46 +162,46 @@ const ProjectFinancialSummary: React.FC = () => { | |||
}, | |||
}, | |||
{ | |||
id: 'totalCumulativeExpenditure', | |||
field: 'totalCumulativeExpenditure', | |||
id: 'cumulativeExpenditure', | |||
field: 'cumulativeExpenditure', | |||
headerName: "Total Cumulative Expenditure (HKD)", | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalCumulativeExpenditure}</span> | |||
<span>${params.row.cumulativeExpenditure}</span> | |||
) | |||
}, | |||
}, | |||
{ | |||
id: 'totalInvoicedAmount', | |||
field: 'totalInvoicedAmount', | |||
id: 'totalInvoiced', | |||
field: 'totalInvoiced', | |||
headerName: "Total Invoiced Amount (HKD)", | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalInvoicedAmount}</span> | |||
<span>${params.row.totalInvoiced}</span> | |||
) | |||
}, | |||
}, | |||
{ | |||
id: 'totalUnInvoicedAmount', | |||
field: 'totalUnInvoicedAmount', | |||
id: 'totalUnInvoiced', | |||
field: 'totalUnInvoiced', | |||
headerName: "Total Un-invoiced Amount (HKD)", | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalUnInvoicedAmount}</span> | |||
<span>${params.row.totalUninvoiced}</span> | |||
) | |||
}, | |||
}, | |||
{ | |||
id: 'totalReceivedAmount', | |||
field: 'totalReceivedAmount', | |||
id: 'totalReceived', | |||
field: 'totalReceived', | |||
headerName: "Total Received Amount (HKD)", | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalReceivedAmount}</span> | |||
<span>${params.row.totalReceived}</span> | |||
) | |||
}, | |||
}, | |||
@@ -322,8 +270,8 @@ const columns2 = [ | |||
flex: 1, | |||
}, | |||
{ | |||
id: 'clientName', | |||
field: 'clientName', | |||
id: 'customerName', | |||
field: 'customerName', | |||
headerName: "Client Name", | |||
flex: 1, | |||
}, | |||
@@ -365,7 +313,7 @@ const columns2 = [ | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalFees}</span> | |||
<span>${params.row.totalFee}</span> | |||
) | |||
}, | |||
}, | |||
@@ -387,7 +335,7 @@ const columns2 = [ | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalCumulativeExpenditure}</span> | |||
<span>${params.row.cumulativeExpenditure}</span> | |||
) | |||
}, | |||
}, | |||
@@ -398,7 +346,7 @@ const columns2 = [ | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalInvoicedAmount}</span> | |||
<span>${params.row.totalInvoiced}</span> | |||
) | |||
}, | |||
}, | |||
@@ -409,7 +357,7 @@ const columns2 = [ | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalUnInvoicedAmount}</span> | |||
<span>${params.row.totalUninvoiced}</span> | |||
) | |||
}, | |||
}, | |||
@@ -420,7 +368,7 @@ const columns2 = [ | |||
flex: 1, | |||
renderCell: (params:any) => { | |||
return ( | |||
<span>${params.row.totalReceivedAmount}</span> | |||
<span>${params.row.totalReceived}</span> | |||
) | |||
}, | |||
}, | |||
@@ -438,9 +386,9 @@ const columns2 = [ | |||
<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}/> | |||
{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)}> | |||
<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> | |||