@@ -1,9 +1,10 @@ | |||
"use server"; | |||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
import { serverFetchBlob, serverFetchJson } from "@/app/utils/fetchUtil"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
import { Dayjs } from "dayjs"; | |||
import { cache } from "react"; | |||
import { FileResponse } from "../reports/actions"; | |||
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 | |||
hoursSpent: number; | |||
hoursSpentOther: number; | |||
currentStaffHoursSpent: number; | |||
currentStaffHoursSpentOther: number; | |||
hoursAllocated: number; | |||
} | |||
@@ -19,9 +19,10 @@ 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 { exportFinancialSummaryByClientExcel, searchFinancialSummaryByClient,searchFinancialSummaryByProject } from "@/app/api/financialsummary/actions"; | |||
import ProjectFinancialCard from "./ProjectFinancialCard"; | |||
import VisibilityIcon from '@mui/icons-material/Visibility'; | |||
import { downloadFile } from "@/app/utils/commonUtil"; | |||
const ProjectFinancialSummary: React.FC = () => { | |||
const [SearchCriteria, setSearchCriteria] = React.useState({}); | |||
@@ -458,7 +459,11 @@ const columns2 = [ | |||
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) | |||
}; | |||
@@ -16,9 +16,11 @@ import { Props as UserWorkspaceProps } from "./UserWorkspacePage"; | |||
interface Props { | |||
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"); | |||
// Projects | |||
@@ -78,7 +80,7 @@ const AssignedProjects: React.FC<Props> = ({ assignedProjects }) => { | |||
</Stack> | |||
</CardContent> | |||
</Card> | |||
<ProjectGrid projects={filteredProjects} /> | |||
<ProjectGrid projects={filteredProjects} maintainNormalStaffWorkspaceAbility={maintainNormalStaffWorkspaceAbility} maintainManagementStaffWorkspaceAbility={maintainManagementStaffWorkspaceAbility}/> | |||
</> | |||
); | |||
}; | |||
@@ -6,9 +6,11 @@ import { AssignedProject } from "@/app/api/projects"; | |||
interface Props { | |||
projects: AssignedProject[]; | |||
maintainNormalStaffWorkspaceAbility?: boolean; | |||
maintainManagementStaffWorkspaceAbility?: boolean; | |||
} | |||
const ProjectGrid: React.FC<Props> = ({ projects }) => { | |||
const ProjectGrid: React.FC<Props> = ({ projects, maintainNormalStaffWorkspaceAbility, maintainManagementStaffWorkspaceAbility }) => { | |||
const { t } = useTranslation("home"); | |||
return ( | |||
<Box> | |||
@@ -30,7 +32,7 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => { | |||
{project.name} | |||
</Typography> | |||
{/* Hours Spent */} | |||
<Typography variant="subtitle2">{t("Hours Spent:")}</Typography> | |||
{(Boolean(maintainNormalStaffWorkspaceAbility) || Boolean(maintainManagementStaffWorkspaceAbility)) && <><Typography variant="subtitle2">{t("Hours Spent:")}</Typography> | |||
<Box | |||
sx={{ | |||
display: "flex", | |||
@@ -40,7 +42,7 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => { | |||
> | |||
<Typography variant="caption">{t("Normal")}</Typography> | |||
<Typography> | |||
{manhourFormatter.format(project.hoursSpent)} | |||
{manhourFormatter.format(Boolean(maintainManagementStaffWorkspaceAbility) ? project.hoursSpent : project.currentStaffHoursSpent)} | |||
</Typography> | |||
</Box> | |||
<Box | |||
@@ -52,11 +54,11 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => { | |||
> | |||
<Typography variant="caption">{t("(Others)")}</Typography> | |||
<Typography>{`(${manhourFormatter.format( | |||
project.hoursSpentOther, | |||
Boolean(maintainManagementStaffWorkspaceAbility) ? project.hoursSpentOther : project.currentStaffHoursSpentOther, | |||
)})`}</Typography> | |||
</Box> | |||
</Box></>} | |||
{/* Hours Allocated */} | |||
<Box | |||
{Boolean(maintainManagementStaffWorkspaceAbility) && <Box | |||
sx={{ | |||
display: "flex", | |||
justifyContent: "space-between", | |||
@@ -69,7 +71,7 @@ const ProjectGrid: React.FC<Props> = ({ projects }) => { | |||
<Typography> | |||
{manhourFormatter.format(project.hoursAllocated)} | |||
</Typography> | |||
</Box> | |||
</Box>} | |||
</CardContent> | |||
</Card> | |||
</Grid> | |||
@@ -35,7 +35,9 @@ export interface Props { | |||
holidays: HolidaysResult[]; | |||
teamTimesheets: TeamTimeSheets; | |||
teamLeaves: TeamLeaves; | |||
fastEntryEnabled?: boolean; | |||
fastEntryEnabled: boolean; | |||
maintainNormalStaffWorkspaceAbility: boolean; | |||
maintainManagementStaffWorkspaceAbility: boolean; | |||
} | |||
const menuItemSx: SxProps = { | |||
@@ -53,6 +55,8 @@ const UserWorkspacePage: React.FC<Props> = ({ | |||
teamTimesheets, | |||
teamLeaves, | |||
fastEntryEnabled, | |||
maintainNormalStaffWorkspaceAbility, | |||
maintainManagementStaffWorkspaceAbility, | |||
}) => { | |||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); | |||
@@ -190,7 +194,7 @@ const UserWorkspacePage: React.FC<Props> = ({ | |||
timesheetRecords={defaultTimesheets} | |||
/> | |||
{assignedProjects.length > 0 ? ( | |||
<AssignedProjects assignedProjects={assignedProjects} /> | |||
<AssignedProjects assignedProjects={assignedProjects} maintainNormalStaffWorkspaceAbility={maintainNormalStaffWorkspaceAbility} maintainManagementStaffWorkspaceAbility={maintainManagementStaffWorkspaceAbility}/> | |||
) : ( | |||
<Typography variant="subtitle1"> | |||
{t("You have no assigned projects!")} | |||
@@ -12,7 +12,7 @@ import { | |||
} from "@/app/api/timesheets"; | |||
import { fetchHolidays } from "@/app/api/holidays"; | |||
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 [ | |||
@@ -38,6 +38,8 @@ const UserWorkspaceWrapper: React.FC = async () => { | |||
]); | |||
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 ( | |||
<UserWorkspacePage | |||
@@ -51,6 +53,8 @@ const UserWorkspaceWrapper: React.FC = async () => { | |||
holidays={holidays} | |||
// Change to access check | |||
fastEntryEnabled={fastEntryEnabled} | |||
maintainNormalStaffWorkspaceAbility={maintainNormalStaffWorkspaceAbility} | |||
maintainManagementStaffWorkspaceAbility={maintainManagementStaffWorkspaceAbility} | |||
/> | |||
); | |||
}; | |||
@@ -58,6 +58,8 @@ export const [ | |||
DELETE_PROJECT, | |||
MAINTAIN_TIMESHEET_FAST_TIME_ENTRY, | |||
VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, | |||
MAINTAIN_NORMAL_STAFF_WORKSPACE, | |||
MAINTAIN_MANAGEMENT_STAFF_WORKSPACE, | |||
] = [ | |||
'MAINTAIN_USER', | |||
'MAINTAIN_TIMESHEET', | |||
@@ -96,7 +98,9 @@ export const [ | |||
'MAINTAIN_PROJECT', | |||
'DELETE_PROJECT', | |||
'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 = [ | |||