| @@ -131,6 +131,9 @@ export function convertLocaleStringToNumber(numberString: string): number { | |||
| } | |||
| export function timestampToDateString(timestamp: string): string { | |||
| if (timestamp === null){ | |||
| return "-" | |||
| } | |||
| const date = new Date(timestamp); | |||
| const year = date.getFullYear(); | |||
| const month = String(date.getMonth() + 1).padStart(2, "0"); | |||
| @@ -344,7 +344,7 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| {/* <Grid item xs={6}> | |||
| <Checkbox | |||
| {...register("isClpProject")} | |||
| checked={Boolean(watch("isClpProject"))} | |||
| @@ -353,7 +353,7 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| <Typography variant="overline" display="inline"> | |||
| {t("CLP Project")} | |||
| </Typography> | |||
| </Grid> | |||
| </Grid> */} | |||
| </Grid> | |||
| </Box> | |||
| @@ -86,7 +86,7 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice, invoic | |||
| const formData = new FormData(); | |||
| formData.append('multipartFileList', file); | |||
| const response = await importInvoices(formData); | |||
| const response = await importIssuedInovice(formData); | |||
| // response: status, message, projectList, emptyRowList, invoiceList | |||
| console.log(response) | |||
| @@ -236,7 +236,7 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice, invoic | |||
| { name: "projectName", label: t("Project Name") }, | |||
| { name: "team", label: t("Team") }, | |||
| { name: "issuedDate", label: t("Issue Date") }, | |||
| { name: "receivedAmount", label: t("Amount (HKD)") }, | |||
| { name: "issuedAmount", label: t("Amount (HKD)") }, | |||
| { name: "receiptDate", label: t("Settle Date") }, | |||
| { name: "receivedAmount", label: t("Actual Received Amount (HKD)") }, | |||
| ], | |||
| @@ -271,8 +271,8 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice, invoic | |||
| flexWrap="wrap" | |||
| spacing={2} | |||
| > | |||
| {/* <ButtonGroup variant="contained"> */} | |||
| {/* <Button startIcon={<FileUploadIcon />} variant="contained" component="label"> | |||
| <ButtonGroup variant="contained"> | |||
| <Button startIcon={<FileUploadIcon />} variant="contained" component="label"> | |||
| <input | |||
| id='importExcel' | |||
| type='file' | |||
| @@ -291,9 +291,9 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice, invoic | |||
| onChange={(event) => {handleRecImportClick(event)}} | |||
| /> | |||
| {t("Import Invoice Amount Receive Summary")} | |||
| </Button> */} | |||
| {/* </ButtonGroup> */} | |||
| <Button startIcon={<FileUploadIcon />} variant="contained" component="label"> | |||
| </Button> | |||
| </ButtonGroup> | |||
| {/* <Button startIcon={<FileUploadIcon />} variant="contained" component="label"> | |||
| <input | |||
| id='importExcel' | |||
| type='file' | |||
| @@ -302,7 +302,7 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice, invoic | |||
| onChange={(event) => {handleImportClick(event)}} | |||
| /> | |||
| {t("Import Invoice Summary")} | |||
| </Button> | |||
| </Button> */} | |||
| </Stack> | |||
| { | |||
| // tabIndex == 0 && | |||
| @@ -56,9 +56,9 @@ const InvoiceSearchWrapper: React.FC & SubComponents = async () => { | |||
| projectName: invoice.projectName, | |||
| team: invoice.team, | |||
| issuedDate: timestampToDateString(invoice.invoiceDate)!!, | |||
| receiptDate: timestampToDateString(invoice.receiptDate??0)!!, | |||
| receiptDate: timestampToDateString(invoice.receiptDate??null)!!, | |||
| issuedAmount: moneyFormatter.format(invoice.issueAmount), | |||
| receivedAmount: moneyFormatter.format(invoice.paidAmount) | |||
| receivedAmount: invoice.paidAmount === null ? "-" : moneyFormatter.format(invoice.paidAmount) | |||
| } | |||
| }) | |||
| @@ -67,16 +67,16 @@ import { | |||
| MAINTAIN_GROUP, | |||
| MAINTAIN_HOLIDAY, | |||
| VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, | |||
| GENERATE_LATE_START_REPORTS, | |||
| GENERATE_LATE_START_REPORT, | |||
| GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
| GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
| GENERATE_COST_ANT_EXPENSE_REPORT, | |||
| GENERATE_COST_AND_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, | |||
| GENERATE_CROSS_TEAM_CHARGE_REPORT | |||
| } from "@/middleware"; | |||
| import { SessionWithAbilities } from "../AppBar/NavigationToggle"; | |||
| import { authOptions } from "@/config/authConfig"; | |||
| @@ -190,16 +190,16 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||
| label: "Analysis Report", | |||
| path: "", | |||
| isHidden: ![ | |||
| GENERATE_LATE_START_REPORTS, | |||
| GENERATE_LATE_START_REPORT, | |||
| GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
| GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
| GENERATE_COST_ANT_EXPENSE_REPORT, | |||
| GENERATE_COST_AND_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, | |||
| GENERATE_CROSS_TEAM_CHARGE_REPORT | |||
| ].some((ability) => | |||
| abilities!.includes(ability), | |||
| ), | |||
| @@ -208,7 +208,7 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||
| icon: <Analytics />, | |||
| label: "Late Start Report", | |||
| path: "/analytics/LateStartReport", | |||
| isHidden: ![GENERATE_LATE_START_REPORTS].some((ability) => | |||
| isHidden: ![GENERATE_LATE_START_REPORT].some((ability) => | |||
| abilities!.includes(ability), | |||
| ), | |||
| }, | |||
| @@ -232,7 +232,7 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||
| icon: <Analytics />, | |||
| label: "Cost and Expense Report", | |||
| path: "/analytics/CostandExpenseReport", | |||
| isHidden: ![GENERATE_COST_ANT_EXPENSE_REPORT].some((ability) => | |||
| isHidden: ![GENERATE_COST_AND_EXPENSE_REPORT].some((ability) => | |||
| abilities!.includes(ability), | |||
| ), | |||
| }, | |||
| @@ -954,7 +954,7 @@ const ProjectCashFlow: React.FC = () => { | |||
| className="text-sm font-medium ml-5" | |||
| style={{ color: "#898d8d" }} | |||
| > | |||
| Accounts Receivable | |||
| Remaining Budget | |||
| </div> | |||
| <div | |||
| className="text-lg font-medium ml-5 mb-2" | |||
| @@ -362,7 +362,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
| const startCount = weeklyPlanned[i].startCount | |||
| const endCount = weeklyPlanned[i].endCount | |||
| for (var j = 0; j < weeklyPlanned[i].searchDuration; j++) { | |||
| if (j >= startCount && j < endCount) { | |||
| if (j >= startCount && j <= endCount) { | |||
| weeklyPlannedSubList.push(weeklyPlanned[i].AverageManhours) | |||
| } else { | |||
| weeklyPlannedSubList.push(0) | |||
| @@ -503,7 +503,8 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
| const fetchMonthlyUnsubmittedData = async () => { | |||
| const fetchResult = await fetchMonthlyUnsubmit(teamUnsubmitTeamId, unsubmitMonthlyFromValue.format('YYYY-MM-DD'), unsubmitMonthlyToValue.endOf('month').format('YYYY-MM-DD'), holidayDates); | |||
| const fetchResult = await fetchMonthlyUnsubmit(teamUnsubmitTeamId, unsubmitMonthlyFromValue.startOf('month').format('YYYY-MM-DD'), unsubmitMonthlyToValue.endOf('month').format('YYYY-MM-DD'), holidayDates); | |||
| const result = [] | |||
| const staffList = [] | |||
| var maxValue = 5 | |||
| @@ -59,16 +59,16 @@ export const [ | |||
| VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING, | |||
| MAINTAIN_NORMAL_STAFF_WORKSPACE, | |||
| MAINTAIN_MANAGEMENT_STAFF_WORKSPACE, | |||
| GENERATE_LATE_START_REPORTS, | |||
| GENERATE_LATE_START_REPORT, | |||
| GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
| GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
| GENERATE_COST_ANT_EXPENSE_REPORT, | |||
| GENERATE_COST_AND_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, | |||
| GENERATE_CROSS_TEAM_CHARGE_REPORT | |||
| ] = [ | |||
| 'MAINTAIN_USER', | |||
| 'MAINTAIN_TIMESHEET', | |||
| @@ -109,16 +109,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_P&L_REPORT', | |||
| 'GENERATE_FINANCIAL_STATUS_REPORT', | |||
| 'GENERATE_PROJECT_CASH_FLOW_REPORT', | |||
| 'GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT', | |||
| 'GENERATE_CROSS_TEAM_CHARGE_REPORT', | |||
| 'G_LATE_START_REPORT', | |||
| 'G_PROJECT_POTENTIAL_DELAY_REPORT', | |||
| 'G_RESOURCE_OVERCONSUMPTION_REPORT', | |||
| 'G_COST_AND_EXPENSE_REPORT', | |||
| 'G_PROJECT_COMPLETION_REPORT', | |||
| 'G_PROJECT_P&L_REPORT', | |||
| 'G_FINANCIAL_STATUS_REPORT', | |||
| 'G_PROJECT_CASH_FLOW_REPORT', | |||
| 'G_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT', | |||
| 'G_CROSS_TEAM_CHARGE_REPORT' | |||
| ] | |||
| const PRIVATE_ROUTES = [ | |||
| @@ -243,20 +243,21 @@ export default async function middleware( | |||
| if (req.nextUrl.pathname.startsWith('/analytics')) { | |||
| isAuth = [ | |||
| GENERATE_LATE_START_REPORTS, | |||
| GENERATE_LATE_START_REPORT, | |||
| GENERATE_PROJECT_POTENTIAL_DELAY_REPORT, | |||
| GENERATE_RESOURCE_OVERCONSUMPTION_REPORT, | |||
| GENERATE_COST_ANT_EXPENSE_REPORT, | |||
| GENERATE_COST_AND_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)); | |||
| 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)); | |||
| isAuth = [GENERATE_LATE_START_REPORT].some((ability) => abilities.includes(ability)); | |||
| } | |||
| if (req.nextUrl.pathname.startsWith('/analytics/ProjectPotentialDelayReport')) { | |||
| @@ -268,7 +269,7 @@ export default async function middleware( | |||
| } | |||
| if (req.nextUrl.pathname.startsWith('/analytics/CostandExpenseReport')) { | |||
| isAuth = [GENERATE_COST_ANT_EXPENSE_REPORT].some((ability) => abilities.includes(ability)); | |||
| isAuth = [GENERATE_COST_AND_EXPENSE_REPORT].some((ability) => abilities.includes(ability)); | |||
| } | |||
| if (req.nextUrl.pathname.startsWith('/analytics/ProjectCompletionReport')) { | |||