Procházet zdrojové kódy

update cross team report

(cherry picked from commit 13b6cc9f12)
tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui před 1 rokem
rodič
revize
0deeda45a9
5 změnil soubory, kde provedl 150 přidání a 11 odebrání
  1. +2
    -0
      src/app/api/reports/index.ts
  2. +18
    -4
      src/components/GenerateCrossTeamChargeReport/GenerateCrossTeamChargeReport.tsx
  3. +6
    -1
      src/components/GenerateCrossTeamChargeReport/GenerateCrossTeamChargeReportWrapper.tsx
  4. +52
    -2
      src/components/NavigationContent/NavigationContent.tsx
  5. +72
    -4
      src/middleware.ts

+ 2
- 0
src/app/api/reports/index.ts Zobrazit soubor

@@ -120,8 +120,10 @@ export interface CostAndExpenseReportRequest {
// - Cross Team Charge Report // - Cross Team Charge Report
export interface CrossTeamChargeReportFilter { export interface CrossTeamChargeReportFilter {
month: string; month: string;
team: string[];
} }


export interface CrossTeamChargeReportRequest { export interface CrossTeamChargeReportRequest {
month: string; month: string;
teamId: number | "All";
} }

+ 18
- 4
src/components/GenerateCrossTeamChargeReport/GenerateCrossTeamChargeReport.tsx Zobrazit soubor

@@ -6,15 +6,20 @@ import { useTranslation } from "react-i18next";
import { CrossTeamChargeReportFilter } from "@/app/api/reports"; import { CrossTeamChargeReportFilter } from "@/app/api/reports";
import { fetchCrossTeamChargeReport } from "@/app/api/reports/actions"; import { fetchCrossTeamChargeReport } from "@/app/api/reports/actions";
import { downloadFile } from "@/app/utils/commonUtil"; import { downloadFile } from "@/app/utils/commonUtil";
import { TeamResult } from "@/app/api/team";
import { SessionStaff } from "@/config/authConfig";


interface Props { interface Props {
teams: TeamResult[];
userStaff: SessionStaff;
} }


type SearchQuery = Partial<Omit<CrossTeamChargeReportFilter, "id">>; type SearchQuery = Partial<Omit<CrossTeamChargeReportFilter, "id">>;
type SearchParamNames = keyof SearchQuery; type SearchParamNames = keyof SearchQuery;


const GenerateCrossTeamChargeReport: React.FC<Props> = () => {
const GenerateCrossTeamChargeReport: React.FC<Props> = ({ teams, userStaff }) => {
const { t } = useTranslation("report"); const { t } = useTranslation("report");
const teamCombo = teams.map(team => `${team.code} - ${team.name}`)


const searchCriteria: Criterion<SearchParamNames>[] = useMemo( const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [ () => [
@@ -23,6 +28,13 @@ const GenerateCrossTeamChargeReport: React.FC<Props> = () => {
paramName: "month", paramName: "month",
type: "monthYear", type: "monthYear",
}, },
{
label: t("Team"),
paramName: "team",
type: "select",
options: teamCombo,
needAll: !Boolean(userStaff?.isTeamLead)
},
], ],
[t], [t],
); );
@@ -33,10 +45,12 @@ const GenerateCrossTeamChargeReport: React.FC<Props> = () => {
criteria={searchCriteria} criteria={searchCriteria}
onSearch={async (query) => { 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 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) { if (response) {
downloadFile(new Uint8Array(response.blobValue), response.filename!!) downloadFile(new Uint8Array(response.blobValue), response.filename!!)
} }


+ 6
- 1
src/components/GenerateCrossTeamChargeReport/GenerateCrossTeamChargeReportWrapper.tsx Zobrazit soubor

@@ -1,13 +1,18 @@
import React from "react"; import React from "react";
import GenerateCrossTeamChargeReportLoading from "./GenerateCrossTeamChargeReportLoading"; import GenerateCrossTeamChargeReportLoading from "./GenerateCrossTeamChargeReportLoading";
import GenerateCrossTeamChargeReport from "./GenerateCrossTeamChargeReport"; import GenerateCrossTeamChargeReport from "./GenerateCrossTeamChargeReport";
import { fetchTeam } from "@/app/api/team";
import { getUserStaff } from "@/app/utils/commonUtil";


interface SubComponents { interface SubComponents {
Loading: typeof GenerateCrossTeamChargeReportLoading; Loading: typeof GenerateCrossTeamChargeReportLoading;
} }


const GenerateCrossTeamChargeReportWrapper: React.FC & SubComponents = async () => { 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; GenerateCrossTeamChargeReportWrapper.Loading = GenerateCrossTeamChargeReportLoading;


+ 52
- 2
src/components/NavigationContent/NavigationContent.tsx Zobrazit soubor

@@ -36,7 +36,6 @@ import ManageAccountsIcon from "@mui/icons-material/ManageAccounts";
import EmojiEventsIcon from "@mui/icons-material/EmojiEvents"; import EmojiEventsIcon from "@mui/icons-material/EmojiEvents";
import FileUploadIcon from '@mui/icons-material/FileUpload'; import FileUploadIcon from '@mui/icons-material/FileUpload';
import { import {
GENERATE_REPORTS,
IMPORT_INVOICE, IMPORT_INVOICE,
IMPORT_RECEIPT, IMPORT_RECEIPT,
MAINTAIN_PROJECT, MAINTAIN_PROJECT,
@@ -68,6 +67,16 @@ import {
MAINTAIN_GROUP, MAINTAIN_GROUP,
MAINTAIN_HOLIDAY, MAINTAIN_HOLIDAY,
VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, 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"; } from "@/middleware";
import { SessionWithAbilities } from "../AppBar/NavigationToggle"; import { SessionWithAbilities } from "../AppBar/NavigationToggle";
import { authOptions } from "@/config/authConfig"; import { authOptions } from "@/config/authConfig";
@@ -180,7 +189,18 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => {
icon: <Analytics />, icon: <Analytics />,
label: "Analysis Report", label: "Analysis Report",
path: "", 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), abilities!.includes(ability),
), ),
children: [ children: [
@@ -188,26 +208,41 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => {
icon: <Analytics />, icon: <Analytics />,
label: "Late Start Report", label: "Late Start Report",
path: "/analytics/LateStartReport", path: "/analytics/LateStartReport",
isHidden: ![GENERATE_LATE_START_REPORTS].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Project Potential Delay Report", label: "Project Potential Delay Report",
path: "/analytics/ProjectPotentialDelayReport", path: "/analytics/ProjectPotentialDelayReport",
isHidden: ![GENERATE_PROJECT_POTENTIAL_DELAY_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Resource Overconsumption Report", label: "Resource Overconsumption Report",
path: "/analytics/ResourceOverconsumptionReport", path: "/analytics/ResourceOverconsumptionReport",
isHidden: ![GENERATE_RESOURCE_OVERCONSUMPTION_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Cost and Expense Report", label: "Cost and Expense Report",
path: "/analytics/CostandExpenseReport", path: "/analytics/CostandExpenseReport",
isHidden: ![GENERATE_COST_ANT_EXPENSE_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Project Completion Report", label: "Project Completion Report",
path: "/analytics/ProjectCompletionReport", path: "/analytics/ProjectCompletionReport",
isHidden: ![GENERATE_PROJECT_COMPLETION_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
// { // {
// icon: <Analytics />, // icon: <Analytics />,
@@ -223,26 +258,41 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => {
icon: <Analytics />, icon: <Analytics />,
label: "Project P&L Report", label: "Project P&L Report",
path: "/analytics/ProjectPandLReport", path: "/analytics/ProjectPandLReport",
isHidden: ![GENERATE_PROJECT_COMPLETION_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Financial Status Report", label: "Financial Status Report",
path: "/analytics/FinancialStatusReport", path: "/analytics/FinancialStatusReport",
isHidden: ![GENERATE_FINANCIAL_STATUS_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Project Cash Flow Report", label: "Project Cash Flow Report",
path: "/analytics/ProjectCashFlowReport", path: "/analytics/ProjectCashFlowReport",
isHidden: ![GENERATE_PROJECT_CASH_FLOW_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Staff Monthly Work Hours Analysis Report", label: "Staff Monthly Work Hours Analysis Report",
path: "/analytics/StaffMonthlyWorkHoursAnalysisReport", path: "/analytics/StaffMonthlyWorkHoursAnalysisReport",
isHidden: ![GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
{ {
icon: <Analytics />, icon: <Analytics />,
label: "Cross Team Charge Report", label: "Cross Team Charge Report",
path: "/analytics/CrossTeamChargeReport", path: "/analytics/CrossTeamChargeReport",
isHidden: ![GENERATE_CROSS_TEAM_CHARGE_REPORT].some((ability) =>
abilities!.includes(ability),
),
}, },
], ],
}, },


+ 72
- 4
src/middleware.ts Zobrazit soubor

@@ -48,7 +48,6 @@ export const [
VIEW_DASHBOARD_SELF, VIEW_DASHBOARD_SELF,
VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_ALL,
IMPORT_INVOICE, IMPORT_INVOICE,
GENERATE_REPORTS,
VIEW_STAFF_PROFILE, VIEW_STAFF_PROFILE,
IMPORT_RECEIPT, IMPORT_RECEIPT,
MAINTAIN_TASK_TEMPLATE, MAINTAIN_TASK_TEMPLATE,
@@ -60,6 +59,16 @@ export const [
VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING,
MAINTAIN_NORMAL_STAFF_WORKSPACE, 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_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_USER',
'MAINTAIN_TIMESHEET', 'MAINTAIN_TIMESHEET',
@@ -89,7 +98,6 @@ export const [
'VIEW_DASHBOARD_SELF', 'VIEW_DASHBOARD_SELF',
'VIEW_DASHBOARD_ALL', 'VIEW_DASHBOARD_ALL',
'IMPORT_INVOICE', 'IMPORT_INVOICE',
'GENERATE_REPORTS',
'VIEW_STAFF_PROFILE', 'VIEW_STAFF_PROFILE',
'IMPORT_RECEIPT', 'IMPORT_RECEIPT',
'MAINTAIN_TASK_TEMPLATE', 'MAINTAIN_TASK_TEMPLATE',
@@ -100,7 +108,17 @@ export const [
'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_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 = [ const PRIVATE_ROUTES = [
@@ -224,7 +242,57 @@ export default async function middleware(
} }


if (req.nextUrl.pathname.startsWith('/analytics')) { 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')) { if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) {


Načítá se…
Zrušit
Uložit