Parcourir la source

update workspace, export finanical status by client excel

tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui il y a 1 an
Parent
révision
383944b844
8 fichiers modifiés avec 67 ajouts et 16 suppressions
  1. +29
    -1
      src/app/api/financialsummary/actions.ts
  2. +2
    -0
      src/app/api/projects/index.ts
  3. +7
    -2
      src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx
  4. +4
    -2
      src/components/UserWorkspacePage/AssignedProjects.tsx
  5. +9
    -7
      src/components/UserWorkspacePage/ProjectGrid.tsx
  6. +6
    -2
      src/components/UserWorkspacePage/UserWorkspacePage.tsx
  7. +5
    -1
      src/components/UserWorkspacePage/UserWorkspaceWrapper.tsx
  8. +5
    -1
      src/middleware.ts

+ 29
- 1
src/app/api/financialsummary/actions.ts Voir le fichier

@@ -1,9 +1,10 @@
"use server"; "use server";


import { serverFetchJson } from "@/app/utils/fetchUtil";
import { serverFetchBlob, serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api"; import { BASE_API_URL } from "@/config/api";
import { Dayjs } from "dayjs"; import { Dayjs } from "dayjs";
import { cache } from "react"; import { cache } from "react";
import { FileResponse } from "../reports/actions";




export interface FinancialSummaryByClientResult { export interface FinancialSummaryByClientResult {
@@ -65,3 +66,30 @@ export const searchFinancialSummaryByProject = cache(async (teamId?: number, cus
} }
}); });

export interface FinancialSummaryByClientExcel {
customerCode: string;
customerName: string;
projectNo: number;
totalFee: number;
cumulativeExpenditure: number;
totalInvoiced: number;
totalReceived: number;
}

export interface ExportFinancialSummaryByClientExcel {
financialSummaryByClients: FinancialSummaryByClientExcel[]
}

export const exportFinancialSummaryByClientExcel = cache(async (data: ExportFinancialSummaryByClientExcel) => {
const reportBlob = await serverFetchBlob<FileResponse>(
`${BASE_API_URL}/dashboard/exportFinancialSummaryByClientExcel`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);

return reportBlob
})

+ 2
- 0
src/app/api/projects/index.ts Voir le fichier

@@ -92,6 +92,8 @@ export interface AssignedProject extends ProjectWithTasks {
// Manhour info // Manhour info
hoursSpent: number; hoursSpent: number;
hoursSpentOther: number; hoursSpentOther: number;
currentStaffHoursSpent: number;
currentStaffHoursSpentOther: number;
hoursAllocated: number; hoursAllocated: number;
} }




+ 7
- 2
src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx Voir le fichier

@@ -19,9 +19,10 @@ import SearchBox, { Criterion } from "../SearchBox";
import ProgressByClientSearch from "@/components/ProgressByClientSearch"; import ProgressByClientSearch from "@/components/ProgressByClientSearch";
import { Suspense } from "react"; 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 { exportFinancialSummaryByClientExcel, searchFinancialSummaryByClient,searchFinancialSummaryByProject } from "@/app/api/financialsummary/actions";
import ProjectFinancialCard from "./ProjectFinancialCard"; import ProjectFinancialCard from "./ProjectFinancialCard";
import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityIcon from '@mui/icons-material/Visibility';
import { downloadFile } from "@/app/utils/commonUtil";


const ProjectFinancialSummary: React.FC = () => { const ProjectFinancialSummary: React.FC = () => {
const [SearchCriteria, setSearchCriteria] = React.useState({}); const [SearchCriteria, setSearchCriteria] = React.useState({});
@@ -458,7 +459,11 @@ const columns2 = [
fetchProjectTableData(params.row.teamId,params.row.cid) fetchProjectTableData(params.row.teamId,params.row.cid)
}; };


const handleExportByClient = () => {
const handleExportByClient = async () => {
const response = await exportFinancialSummaryByClientExcel({financialSummaryByClients: clientFinancialRows})
if (response) {
downloadFile(new Uint8Array(response.blobValue), response.filename!!)
}
console.log(clientFinancialRows) console.log(clientFinancialRows)
}; };




+ 4
- 2
src/components/UserWorkspacePage/AssignedProjects.tsx Voir le fichier

@@ -16,9 +16,11 @@ import { Props as UserWorkspaceProps } from "./UserWorkspacePage";


interface Props { interface Props {
assignedProjects: UserWorkspaceProps["assignedProjects"]; assignedProjects: UserWorkspaceProps["assignedProjects"];
maintainNormalStaffWorkspaceAbility?: boolean;
maintainManagementStaffWorkspaceAbility?: boolean;
} }


const AssignedProjects: React.FC<Props> = ({ assignedProjects }) => {
const AssignedProjects: React.FC<Props> = ({ assignedProjects, maintainNormalStaffWorkspaceAbility, maintainManagementStaffWorkspaceAbility }) => {
const { t } = useTranslation("home"); const { t } = useTranslation("home");


// Projects // Projects
@@ -78,7 +80,7 @@ const AssignedProjects: React.FC<Props> = ({ assignedProjects }) => {
</Stack> </Stack>
</CardContent> </CardContent>
</Card> </Card>
<ProjectGrid projects={filteredProjects} />
<ProjectGrid projects={filteredProjects} maintainNormalStaffWorkspaceAbility={maintainNormalStaffWorkspaceAbility} maintainManagementStaffWorkspaceAbility={maintainManagementStaffWorkspaceAbility}/>
</> </>
); );
}; };


+ 9
- 7
src/components/UserWorkspacePage/ProjectGrid.tsx Voir le fichier

@@ -6,9 +6,11 @@ import { AssignedProject } from "@/app/api/projects";


interface Props { interface Props {
projects: AssignedProject[]; projects: AssignedProject[];
maintainNormalStaffWorkspaceAbility?: boolean;
maintainManagementStaffWorkspaceAbility?: boolean;
} }


const ProjectGrid: React.FC<Props> = ({ projects }) => {
const ProjectGrid: React.FC<Props> = ({ projects, maintainNormalStaffWorkspaceAbility, maintainManagementStaffWorkspaceAbility }) => {
const { t } = useTranslation("home"); const { t } = useTranslation("home");
return ( return (
<Box> <Box>
@@ -30,7 +32,7 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => {
{project.name} {project.name}
</Typography> </Typography>
{/* Hours Spent */} {/* Hours Spent */}
<Typography variant="subtitle2">{t("Hours Spent:")}</Typography>
{(Boolean(maintainNormalStaffWorkspaceAbility) || Boolean(maintainManagementStaffWorkspaceAbility)) && <><Typography variant="subtitle2">{t("Hours Spent:")}</Typography>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@@ -40,7 +42,7 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => {
> >
<Typography variant="caption">{t("Normal")}</Typography> <Typography variant="caption">{t("Normal")}</Typography>
<Typography> <Typography>
{manhourFormatter.format(project.hoursSpent)}
{manhourFormatter.format(Boolean(maintainManagementStaffWorkspaceAbility) ? project.hoursSpent : project.currentStaffHoursSpent)}
</Typography> </Typography>
</Box> </Box>
<Box <Box
@@ -52,11 +54,11 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => {
> >
<Typography variant="caption">{t("(Others)")}</Typography> <Typography variant="caption">{t("(Others)")}</Typography>
<Typography>{`(${manhourFormatter.format( <Typography>{`(${manhourFormatter.format(
project.hoursSpentOther,
Boolean(maintainManagementStaffWorkspaceAbility) ? project.hoursSpentOther : project.currentStaffHoursSpentOther,
)})`}</Typography> )})`}</Typography>
</Box>
</Box></>}
{/* Hours Allocated */} {/* Hours Allocated */}
<Box
{Boolean(maintainManagementStaffWorkspaceAbility) && <Box
sx={{ sx={{
display: "flex", display: "flex",
justifyContent: "space-between", justifyContent: "space-between",
@@ -69,7 +71,7 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => {
<Typography> <Typography>
{manhourFormatter.format(project.hoursAllocated)} {manhourFormatter.format(project.hoursAllocated)}
</Typography> </Typography>
</Box>
</Box>}
</CardContent> </CardContent>
</Card> </Card>
</Grid> </Grid>


+ 6
- 2
src/components/UserWorkspacePage/UserWorkspacePage.tsx Voir le fichier

@@ -35,7 +35,9 @@ export interface Props {
holidays: HolidaysResult[]; holidays: HolidaysResult[];
teamTimesheets: TeamTimeSheets; teamTimesheets: TeamTimeSheets;
teamLeaves: TeamLeaves; teamLeaves: TeamLeaves;
fastEntryEnabled?: boolean;
fastEntryEnabled: boolean;
maintainNormalStaffWorkspaceAbility: boolean;
maintainManagementStaffWorkspaceAbility: boolean;
} }


const menuItemSx: SxProps = { const menuItemSx: SxProps = {
@@ -53,6 +55,8 @@ const UserWorkspacePage: React.FC<Props> = ({
teamTimesheets, teamTimesheets,
teamLeaves, teamLeaves,
fastEntryEnabled, fastEntryEnabled,
maintainNormalStaffWorkspaceAbility,
maintainManagementStaffWorkspaceAbility,
}) => { }) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);


@@ -190,7 +194,7 @@ const UserWorkspacePage: React.FC<Props> = ({
timesheetRecords={defaultTimesheets} timesheetRecords={defaultTimesheets}
/> />
{assignedProjects.length > 0 ? ( {assignedProjects.length > 0 ? (
<AssignedProjects assignedProjects={assignedProjects} />
<AssignedProjects assignedProjects={assignedProjects} maintainNormalStaffWorkspaceAbility={maintainNormalStaffWorkspaceAbility} maintainManagementStaffWorkspaceAbility={maintainManagementStaffWorkspaceAbility}/>
) : ( ) : (
<Typography variant="subtitle1"> <Typography variant="subtitle1">
{t("You have no assigned projects!")} {t("You have no assigned projects!")}


+ 5
- 1
src/components/UserWorkspacePage/UserWorkspaceWrapper.tsx Voir le fichier

@@ -12,7 +12,7 @@ import {
} from "@/app/api/timesheets"; } from "@/app/api/timesheets";
import { fetchHolidays } from "@/app/api/holidays"; import { fetchHolidays } from "@/app/api/holidays";
import { getUserAbilities } from "@/app/utils/commonUtil"; import { getUserAbilities } from "@/app/utils/commonUtil";
import { MAINTAIN_TIMESHEET_FAST_TIME_ENTRY } from "@/middleware";
import { MAINTAIN_TIMESHEET_FAST_TIME_ENTRY, MAINTAIN_NORMAL_STAFF_WORKSPACE, MAINTAIN_MANAGEMENT_STAFF_WORKSPACE } from "@/middleware";


const UserWorkspaceWrapper: React.FC = async () => { const UserWorkspaceWrapper: React.FC = async () => {
const [ const [
@@ -38,6 +38,8 @@ const UserWorkspaceWrapper: React.FC = async () => {
]); ]);


const fastEntryEnabled = abilities.includes(MAINTAIN_TIMESHEET_FAST_TIME_ENTRY) const fastEntryEnabled = abilities.includes(MAINTAIN_TIMESHEET_FAST_TIME_ENTRY)
const maintainNormalStaffWorkspaceAbility = abilities.includes(MAINTAIN_NORMAL_STAFF_WORKSPACE)
const maintainManagementStaffWorkspaceAbility = abilities.includes(MAINTAIN_MANAGEMENT_STAFF_WORKSPACE)


return ( return (
<UserWorkspacePage <UserWorkspacePage
@@ -51,6 +53,8 @@ const UserWorkspaceWrapper: React.FC = async () => {
holidays={holidays} holidays={holidays}
// Change to access check // Change to access check
fastEntryEnabled={fastEntryEnabled} fastEntryEnabled={fastEntryEnabled}
maintainNormalStaffWorkspaceAbility={maintainNormalStaffWorkspaceAbility}
maintainManagementStaffWorkspaceAbility={maintainManagementStaffWorkspaceAbility}
/> />
); );
}; };


+ 5
- 1
src/middleware.ts Voir le fichier

@@ -58,6 +58,8 @@ export const [
DELETE_PROJECT, DELETE_PROJECT,
MAINTAIN_TIMESHEET_FAST_TIME_ENTRY, MAINTAIN_TIMESHEET_FAST_TIME_ENTRY,
VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING,
MAINTAIN_NORMAL_STAFF_WORKSPACE,
MAINTAIN_MANAGEMENT_STAFF_WORKSPACE,
] = [ ] = [
'MAINTAIN_USER', 'MAINTAIN_USER',
'MAINTAIN_TIMESHEET', 'MAINTAIN_TIMESHEET',
@@ -96,7 +98,9 @@ export const [
'MAINTAIN_PROJECT', 'MAINTAIN_PROJECT',
'DELETE_PROJECT', 'DELETE_PROJECT',
'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY', 'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY',
'VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING'
'VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING',
'MAINTAIN_NORMAL_STAFF_WORKSPACE',
'MAINTAIN_MANAGEMENT_STAFF_WORKSPACE'
] ]


const PRIVATE_ROUTES = [ const PRIVATE_ROUTES = [


Chargement…
Annuler
Enregistrer