(cherry picked from commit 13b6cc9f12
)
tags/Baseline_30082024_FRONTEND_UAT
@@ -120,8 +120,10 @@ export interface CostAndExpenseReportRequest { | |||
// - Cross Team Charge Report | |||
export interface CrossTeamChargeReportFilter { | |||
month: string; | |||
team: string[]; | |||
} | |||
export interface CrossTeamChargeReportRequest { | |||
month: string; | |||
teamId: number | "All"; | |||
} |
@@ -6,15 +6,20 @@ import { useTranslation } from "react-i18next"; | |||
import { CrossTeamChargeReportFilter } from "@/app/api/reports"; | |||
import { fetchCrossTeamChargeReport } from "@/app/api/reports/actions"; | |||
import { downloadFile } from "@/app/utils/commonUtil"; | |||
import { TeamResult } from "@/app/api/team"; | |||
import { SessionStaff } from "@/config/authConfig"; | |||
interface Props { | |||
teams: TeamResult[]; | |||
userStaff: SessionStaff; | |||
} | |||
type SearchQuery = Partial<Omit<CrossTeamChargeReportFilter, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const GenerateCrossTeamChargeReport: React.FC<Props> = () => { | |||
const GenerateCrossTeamChargeReport: React.FC<Props> = ({ teams, userStaff }) => { | |||
const { t } = useTranslation("report"); | |||
const teamCombo = teams.map(team => `${team.code} - ${team.name}`) | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => [ | |||
@@ -23,6 +28,13 @@ const GenerateCrossTeamChargeReport: React.FC<Props> = () => { | |||
paramName: "month", | |||
type: "monthYear", | |||
}, | |||
{ | |||
label: t("Team"), | |||
paramName: "team", | |||
type: "select", | |||
options: teamCombo, | |||
needAll: !Boolean(userStaff?.isTeamLead) | |||
}, | |||
], | |||
[t], | |||
); | |||
@@ -33,10 +45,12 @@ const GenerateCrossTeamChargeReport: React.FC<Props> = () => { | |||
criteria={searchCriteria} | |||
onSearch={async (query) => { | |||
console.log(query.month) | |||
if (Boolean(query.month)) { | |||
console.log(query) | |||
if (Boolean(query.month) && Boolean(query.team)) { | |||
// const projectIndex = projectCombo.findIndex(({value}) => value === parseInt(query.project)) | |||
const response = await fetchCrossTeamChargeReport({ month: query.month }) | |||
const teamIndex = teamCombo.findIndex(team => team === query.team) | |||
const response = await fetchCrossTeamChargeReport({ month: query.month, teamId: teamIndex >= 0 ? teams[teamIndex].id : "All", }) | |||
if (response) { | |||
downloadFile(new Uint8Array(response.blobValue), response.filename!!) | |||
} | |||
@@ -1,13 +1,18 @@ | |||
import React from "react"; | |||
import GenerateCrossTeamChargeReportLoading from "./GenerateCrossTeamChargeReportLoading"; | |||
import GenerateCrossTeamChargeReport from "./GenerateCrossTeamChargeReport"; | |||
import { fetchTeam } from "@/app/api/team"; | |||
import { getUserStaff } from "@/app/utils/commonUtil"; | |||
interface SubComponents { | |||
Loading: typeof GenerateCrossTeamChargeReportLoading; | |||
} | |||
const GenerateCrossTeamChargeReportWrapper: React.FC & SubComponents = async () => { | |||
return <GenerateCrossTeamChargeReport/>; | |||
const [teams, userStaff] = await Promise.all([fetchTeam(), getUserStaff()]) | |||
return <GenerateCrossTeamChargeReport teams={!Boolean(userStaff?.isTeamLead) ? teams : teams.filter(team => team.id === userStaff?.teamId)} userStaff={userStaff}/>; | |||
}; | |||
GenerateCrossTeamChargeReportWrapper.Loading = GenerateCrossTeamChargeReportLoading; | |||
@@ -36,7 +36,6 @@ import ManageAccountsIcon from "@mui/icons-material/ManageAccounts"; | |||
import EmojiEventsIcon from "@mui/icons-material/EmojiEvents"; | |||
import FileUploadIcon from '@mui/icons-material/FileUpload'; | |||
import { | |||
GENERATE_REPORTS, | |||
IMPORT_INVOICE, | |||
IMPORT_RECEIPT, | |||
MAINTAIN_PROJECT, | |||
@@ -68,6 +67,16 @@ import { | |||
MAINTAIN_GROUP, | |||
MAINTAIN_HOLIDAY, | |||
VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, | |||
GENERATE_LATE_START_REPORTS, | |||
GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
GENERATE_COST_ANT_EXPENSE_REPORT, | |||
GENERATE_PROJECT_COMPLETION_REPORT, | |||
GENERATE_PROJECT_PANDL_REPORT, | |||
GENERATE_FINANCIAL_STATUS_REPORT, | |||
GENERATE_PROJECT_CASH_FLOW_REPORT, | |||
GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT, | |||
GENERATE_CROSS_TEAM_CHARGE_REPORT, | |||
} from "@/middleware"; | |||
import { SessionWithAbilities } from "../AppBar/NavigationToggle"; | |||
import { authOptions } from "@/config/authConfig"; | |||
@@ -180,7 +189,18 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||
icon: <Analytics />, | |||
label: "Analysis Report", | |||
path: "", | |||
isHidden: ![GENERATE_REPORTS].some((ability) => | |||
isHidden: ![ | |||
GENERATE_LATE_START_REPORTS, | |||
GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
GENERATE_COST_ANT_EXPENSE_REPORT, | |||
GENERATE_PROJECT_COMPLETION_REPORT, | |||
GENERATE_PROJECT_PANDL_REPORT, | |||
GENERATE_FINANCIAL_STATUS_REPORT, | |||
GENERATE_PROJECT_CASH_FLOW_REPORT, | |||
GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT, | |||
GENERATE_CROSS_TEAM_CHARGE_REPORT, | |||
].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
children: [ | |||
@@ -188,26 +208,41 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||
icon: <Analytics />, | |||
label: "Late Start Report", | |||
path: "/analytics/LateStartReport", | |||
isHidden: ![GENERATE_LATE_START_REPORTS].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Project Potential Delay Report", | |||
path: "/analytics/ProjectPotentialDelayReport", | |||
isHidden: ![GENERATE_PROJECT_POTENTIAL_DELAY_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Resource Overconsumption Report", | |||
path: "/analytics/ResourceOverconsumptionReport", | |||
isHidden: ![GENERATE_RESOURCE_OVERCONSUMPTION_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Cost and Expense Report", | |||
path: "/analytics/CostandExpenseReport", | |||
isHidden: ![GENERATE_COST_ANT_EXPENSE_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Project Completion Report", | |||
path: "/analytics/ProjectCompletionReport", | |||
isHidden: ![GENERATE_PROJECT_COMPLETION_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
// { | |||
// icon: <Analytics />, | |||
@@ -223,26 +258,41 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||
icon: <Analytics />, | |||
label: "Project P&L Report", | |||
path: "/analytics/ProjectPandLReport", | |||
isHidden: ![GENERATE_PROJECT_COMPLETION_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Financial Status Report", | |||
path: "/analytics/FinancialStatusReport", | |||
isHidden: ![GENERATE_FINANCIAL_STATUS_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Project Cash Flow Report", | |||
path: "/analytics/ProjectCashFlowReport", | |||
isHidden: ![GENERATE_PROJECT_CASH_FLOW_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Staff Monthly Work Hours Analysis Report", | |||
path: "/analytics/StaffMonthlyWorkHoursAnalysisReport", | |||
isHidden: ![GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
{ | |||
icon: <Analytics />, | |||
label: "Cross Team Charge Report", | |||
path: "/analytics/CrossTeamChargeReport", | |||
isHidden: ![GENERATE_CROSS_TEAM_CHARGE_REPORT].some((ability) => | |||
abilities!.includes(ability), | |||
), | |||
}, | |||
], | |||
}, | |||
@@ -48,7 +48,6 @@ export const [ | |||
VIEW_DASHBOARD_SELF, | |||
VIEW_DASHBOARD_ALL, | |||
IMPORT_INVOICE, | |||
GENERATE_REPORTS, | |||
VIEW_STAFF_PROFILE, | |||
IMPORT_RECEIPT, | |||
MAINTAIN_TASK_TEMPLATE, | |||
@@ -60,6 +59,16 @@ export const [ | |||
VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, | |||
MAINTAIN_NORMAL_STAFF_WORKSPACE, | |||
MAINTAIN_MANAGEMENT_STAFF_WORKSPACE, | |||
GENERATE_LATE_START_REPORTS, | |||
GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
GENERATE_COST_ANT_EXPENSE_REPORT, | |||
GENERATE_PROJECT_COMPLETION_REPORT, | |||
GENERATE_PROJECT_PANDL_REPORT, | |||
GENERATE_FINANCIAL_STATUS_REPORT, | |||
GENERATE_PROJECT_CASH_FLOW_REPORT, | |||
GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT, | |||
GENERATE_CROSS_TEAM_CHARGE_REPORT, | |||
] = [ | |||
'MAINTAIN_USER', | |||
'MAINTAIN_TIMESHEET', | |||
@@ -89,7 +98,6 @@ export const [ | |||
'VIEW_DASHBOARD_SELF', | |||
'VIEW_DASHBOARD_ALL', | |||
'IMPORT_INVOICE', | |||
'GENERATE_REPORTS', | |||
'VIEW_STAFF_PROFILE', | |||
'IMPORT_RECEIPT', | |||
'MAINTAIN_TASK_TEMPLATE', | |||
@@ -100,7 +108,17 @@ export const [ | |||
'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY', | |||
'VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING', | |||
'MAINTAIN_NORMAL_STAFF_WORKSPACE', | |||
'MAINTAIN_MANAGEMENT_STAFF_WORKSPACE' | |||
'MAINTAIN_MANAGEMENT_STAFF_WORKSPACE', | |||
'GENERATE_LATE_START_REPORTS', | |||
'GENERATE_PROJECT_POTENTIAL_DELAY_REPORT', | |||
'GENERATE_RESOURCE_OVERCONSUMPTION_REPORT', | |||
'GENERATE_COST_ANT_EXPENSE_REPORT', | |||
'GENERATE_PROJECT_COMPLETION_REPORT', | |||
'GENERATE_PROJECT_P&L_REPORT', | |||
'GENERATE_FINANCIAL_STATUS_REPORT', | |||
'GENERATE_PROJECT_CASH_FLOW_REPORT', | |||
'GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT', | |||
'GENERATE_CROSS_TEAM_CHARGE_REPORT', | |||
] | |||
const PRIVATE_ROUTES = [ | |||
@@ -224,7 +242,57 @@ export default async function middleware( | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics')) { | |||
isAuth = [GENERATE_REPORTS].some((ability) => abilities.includes(ability)); | |||
isAuth = [ | |||
GENERATE_LATE_START_REPORTS, | |||
GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
GENERATE_COST_ANT_EXPENSE_REPORT, | |||
GENERATE_PROJECT_COMPLETION_REPORT, | |||
GENERATE_PROJECT_PANDL_REPORT, | |||
GENERATE_FINANCIAL_STATUS_REPORT, | |||
GENERATE_PROJECT_CASH_FLOW_REPORT, | |||
GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT, | |||
GENERATE_CROSS_TEAM_CHARGE_REPORT,].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/LateStartReport')) { | |||
isAuth = [GENERATE_LATE_START_REPORTS].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/ProjectPotentialDelayReport')) { | |||
isAuth = [GENERATE_PROJECT_POTENTIAL_DELAY_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/ResourceOverconsumptionReport')) { | |||
isAuth = [GENERATE_RESOURCE_OVERCONSUMPTION_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/CostandExpenseReport')) { | |||
isAuth = [GENERATE_COST_ANT_EXPENSE_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/ProjectCompletionReport')) { | |||
isAuth = [GENERATE_PROJECT_COMPLETION_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/ProjectPandLReport')) { | |||
isAuth = [GENERATE_PROJECT_PANDL_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/FinancialStatusReport')) { | |||
isAuth = [GENERATE_FINANCIAL_STATUS_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/ProjectCashFlowReport')) { | |||
isAuth = [GENERATE_PROJECT_CASH_FLOW_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/StaffMonthlyWorkHoursAnalysisReport')) { | |||
isAuth = [GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/analytics/CrossTeamChargeReport')) { | |||
isAuth = [GENERATE_CROSS_TEAM_CHARGE_REPORT].some((ability) => abilities.includes(ability)); | |||
} | |||
if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) { | |||