@@ -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 | |||||
}) |
@@ -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; | ||||
} | } | ||||
@@ -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) | ||||
}; | }; | ||||
@@ -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}/> | |||||
</> | </> | ||||
); | ); | ||||
}; | }; | ||||
@@ -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> | ||||
@@ -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!")} | ||||
@@ -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} | |||||
/> | /> | ||||
); | ); | ||||
}; | }; | ||||
@@ -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 = [ | ||||