@@ -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", | |||||
}, | |||||
]; |
@@ -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}` | |||||
); | |||||
}); | }); |
@@ -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", | ||||
@@ -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} | ||||
@@ -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} | ||||
@@ -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" }}> | ||||
@@ -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"> | ||||