Переглянути джерело

update claim, add EX02 report

tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui 1 рік тому
джерело
коміт
dedeff4b41
19 змінених файлів з 223 додано та 15 видалено
  1. +25
    -0
      src/app/(main)/analytics/EX02ProjectCashFlowReport/page.tsx
  2. +1
    -1
      src/app/api/claims/actions.ts
  3. +18
    -0
      src/app/api/reports/actions.ts
  4. +8
    -0
      src/app/api/reports/index.ts
  5. +16
    -0
      src/app/utils/commonUtil.ts
  6. +23
    -3
      src/app/utils/fetchUtil.ts
  7. +1
    -0
      src/components/Breadcrumb/Breadcrumb.tsx
  8. +2
    -1
      src/components/ClaimDetail/ClaimDetail.tsx
  9. +5
    -4
      src/components/ClaimDetail/ClaimFormInputGrid.tsx
  10. +6
    -6
      src/components/ClaimSearch/ClaimSearch.tsx
  11. +52
    -0
      src/components/GenerateEX02ProjectCashFlowReport/GenerateEX02ProjectCashFlowReport.tsx
  12. +38
    -0
      src/components/GenerateEX02ProjectCashFlowReport/GenerateEX02ProjectCashFlowReportLoading.tsx
  13. +18
    -0
      src/components/GenerateEX02ProjectCashFlowReport/GenerateEX02ProjectCashFlowReportWrapper.tsx
  14. +1
    -0
      src/components/GenerateEX02ProjectCashFlowReport/index.ts
  15. +1
    -0
      src/components/NavigationContent/NavigationContent.tsx
  16. +1
    -0
      src/i18n/en/claim.json
  17. +3
    -0
      src/i18n/en/report.json
  18. +1
    -0
      src/i18n/zh/claim.json
  19. +3
    -0
      src/i18n/zh/report.json

+ 25
- 0
src/app/(main)/analytics/EX02ProjectCashFlowReport/page.tsx Переглянути файл

@@ -0,0 +1,25 @@
import { Metadata } from "next";
import { Suspense } from "react";
import { I18nProvider } from "@/i18n";
import { fetchProjects } from "@/app/api/projects";
import GenerateEX02ProjectCashFlowReport from "@/components/GenerateEX02ProjectCashFlowReport";

export const metadata: Metadata = {
title: "EX02 - Project Cash Flow Report",
};

const ProjectCashFlowReport: React.FC = async () => {
fetchProjects();

return (
<>
<I18nProvider namespaces={["report", "common"]}>
<Suspense fallback={<GenerateEX02ProjectCashFlowReport.Loading />}>
<GenerateEX02ProjectCashFlowReport />
</Suspense>
</I18nProvider>
</>
);
};

export default ProjectCashFlowReport;

+ 1
- 1
src/app/api/claims/actions.ts Переглянути файл

@@ -21,7 +21,7 @@ export interface ClaimDetailTable {
id: number;
invoiceDate: Date;
description: string;
project: ProjectCombo;
project: number;
amount: number;
supportingDocumentName: string;
oldSupportingDocument: SupportingDocument;


+ 18
- 0
src/app/api/reports/actions.ts Переглянути файл

@@ -0,0 +1,18 @@
"use server";

import { serverFetchBlob, serverFetchJson } from "@/app/utils/fetchUtil";
import { EX02ProjectCashFlowReportRequest } from ".";
import { BASE_API_URL } from "@/config/api";

export const fetchEX02ProjectCashFlowReport = async (data: EX02ProjectCashFlowReportRequest) => {
const reportBlob = await serverFetchBlob(
`${BASE_API_URL}/reports/EX02-ProjectCashFlowReport`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);

return reportBlob
};

+ 8
- 0
src/app/api/reports/index.ts Переглянути файл

@@ -0,0 +1,8 @@
// EX02 - Project Cash Flow Report
export interface EX02ProjectCashFlowReportFilter {
project: string[];
}

export interface EX02ProjectCashFlowReportRequest {
projectId: number;
}

+ 16
- 0
src/app/utils/commonUtil.ts Переглянути файл

@@ -20,4 +20,20 @@ export const dateInRange = (currentDate: string, startDate: string, endDate: str
return true
}
}
}

function s2ab(s: string) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}

export const downloadFile = (blob: Blob | string, type: string, filename: string) => {

const url = URL.createObjectURL(typeof blob === "string" ? new Blob([blob], { type: type }) : blob);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", filename);
link.click();
}

+ 23
- 3
src/app/utils/fetchUtil.ts Переглянути файл

@@ -14,9 +14,9 @@ export const serverFetch: typeof fetch = async (input, init) => {
...init?.headers,
...(accessToken
? {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json"
}
Authorization: `Bearer ${accessToken}`,
Accept: "application/json"
}
: {}),
},
});
@@ -56,6 +56,26 @@ export async function serverFetchWithNoContent(...args: FetchParams) {
}
}

export async function serverFetchBlob(...args: FetchParams) {
const response = await serverFetch(...args);

if (response.ok) {
console.log(response)
const blob = await response.blob()
const blobText = await blob.text();
const blobType = await blob.type;
return {filename: response.headers.get("filename"), blobText: blobText, blobType: blobType};
} else {
switch (response.status) {
case 401:
signOutUser();
default:
console.error(await response.text());
throw Error("Something went wrong fetching data in server.");
}
}
}

export const signOutUser = () => {
const headersList = headers();
const referer = headersList.get("referer");


+ 1
- 0
src/components/Breadcrumb/Breadcrumb.tsx Переглянути файл

@@ -28,6 +28,7 @@ const pathToLabelMap: { [path: string]: string } = {
"/settings/position": "Position",
"/settings/position/new": "Create Position",
"/settings/salarys": "Salary",
"/analytics/EX02ProjectCashFlowReport": "EX02 - Project Cash Flow Report",
};

const Breadcrumb = () => {


+ 2
- 1
src/components/ClaimDetail/ClaimDetail.tsx Переглянути файл

@@ -75,9 +75,10 @@ const ClaimDetail: React.FC<Props> = ({ projectCombo }) => {
const formData = new FormData()
formData.append("expenseType", data.expenseType)
data.addClaimDetails.forEach((claimDetail) => {
console.log(claimDetail)
formData.append("addClaimDetailIds", JSON.stringify(claimDetail.id))
formData.append("addClaimDetailInvoiceDates", convertDateToString(claimDetail.invoiceDate, "YYYY-MM-DD"))
formData.append("addClaimDetailProjectIds", JSON.stringify(claimDetail.project.id))
formData.append("addClaimDetailProjectIds", JSON.stringify(claimDetail.project))
formData.append("addClaimDetailDescriptions", claimDetail.description)
formData.append("addClaimDetailAmounts", JSON.stringify(claimDetail.amount))
formData.append("addClaimDetailNewSupportingDocuments", claimDetail.newSupportingDocument)


+ 5
- 4
src/components/ClaimDetail/ClaimFormInputGrid.tsx Переглянути файл

@@ -371,20 +371,21 @@ const ClaimFormInputGrid: React.FC<ClaimFormInputGridProps> = ({
flex: 1,
editable: true,
type: "singleSelect",
getOptionLabel: (value: any) => {
getOptionLabel: (value: ProjectCombo) => {
return !value?.code || value?.code.length === 0 ? `${value?.name}` : `${value?.code} - ${value?.name}`;
},
getOptionValue: (value: any) => value,
getOptionValue: (value: ProjectCombo) => value.id,
valueOptions: () => {
const options = projectCombo ?? []

if (options.length === 0) {
options.push({ id: -1, code: "", name: "No Projects" })
}
return options;

return options as ProjectCombo[];
},
valueGetter: (params) => {
return params.value ?? projectCombo[0].id ?? -1
return params.value ?? projectCombo[0] ?? { id: -1, code: "", name: "No Projects" } as ProjectCombo
},
},
{


+ 6
- 6
src/components/ClaimSearch/ClaimSearch.tsx Переглянути файл

@@ -50,12 +50,12 @@ const ClaimSearch: React.FC<Props> = ({ claims }) => {

const columns = useMemo<Column<Claim>[]>(
() => [
// {
// name: "action",
// label: t("Actions"),
// onClick: onClaimClick,
// buttonIcon: <EditNote />,
// },
{
name: "id",
label: t("Details"),
onClick: onClaimClick,
buttonIcon: <EditNote />,
},
{ name: "created", label: t("Creation Date"), type: "date" },
{ name: "code", label: t("Claim Code") },
// { name: "project", label: t("Related Project Name") },


+ 52
- 0
src/components/GenerateEX02ProjectCashFlowReport/GenerateEX02ProjectCashFlowReport.tsx Переглянути файл

@@ -0,0 +1,52 @@
"use client";

import React, { useMemo } from "react";
import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import { ProjectResult } from "@/app/api/projects";
import { EX02ProjectCashFlowReportFilter } from "@/app/api/reports";
import { fetchEX02ProjectCashFlowReport } from "@/app/api/reports/actions";
import { downloadFile } from "@/app/utils/commonUtil";

interface Props {
projects: ProjectResult[];
}

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

const GenerateEX02ProjectCashFlowReport: React.FC<Props> = ({ projects }) => {
const { t } = useTranslation();
const projectCombo = projects.map(project => `${project.code} - ${project.name}`)

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Project"), paramName: "project", type: "select", options: projectCombo},
],
[t],
);

return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={async (query) => {
const projectIndex = projectCombo.findIndex(project => project === query.project)
const response = await fetchEX02ProjectCashFlowReport({projectId: projects[projectIndex].id})
console.log(response)
if (response) {
downloadFile(response.blobText, response.blobType, response.filename!!)
}

// const url = URL.createObjectURL(response.blob);
// const link = document.createElement("a");
// link.href = url;
// link.setAttribute("download", "abc.xlsx");
// link.click();
}}
/>
</>
);
};

export default GenerateEX02ProjectCashFlowReport;

+ 38
- 0
src/components/GenerateEX02ProjectCashFlowReport/GenerateEX02ProjectCashFlowReportLoading.tsx Переглянути файл

@@ -0,0 +1,38 @@
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Skeleton from "@mui/material/Skeleton";
import Stack from "@mui/material/Stack";
import React from "react";

// Can make this nicer
export const GenerateEX02ProjectCashFlowReportLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default GenerateEX02ProjectCashFlowReportLoading;

+ 18
- 0
src/components/GenerateEX02ProjectCashFlowReport/GenerateEX02ProjectCashFlowReportWrapper.tsx Переглянути файл

@@ -0,0 +1,18 @@
import React from "react";
import GenerateEX02ProjectCashFlowReportLoading from "./GenerateEX02ProjectCashFlowReportLoading";
import { fetchProjects } from "@/app/api/projects";
import GenerateEX02ProjectCashFlowReport from "./GenerateEX02ProjectCashFlowReport";

interface SubComponents {
Loading: typeof GenerateEX02ProjectCashFlowReportLoading;
}

const GenerateEX02ProjectCashFlowReportWrapper: React.FC & SubComponents = async () => {
const projects = await fetchProjects();

return <GenerateEX02ProjectCashFlowReport projects={projects} />;
};

GenerateEX02ProjectCashFlowReportWrapper.Loading = GenerateEX02ProjectCashFlowReportLoading;

export default GenerateEX02ProjectCashFlowReportWrapper;

+ 1
- 0
src/components/GenerateEX02ProjectCashFlowReport/index.ts Переглянути файл

@@ -0,0 +1 @@
export { default } from "./GenerateEX02ProjectCashFlowReportWrapper";

+ 1
- 0
src/components/NavigationContent/NavigationContent.tsx Переглянути файл

@@ -110,6 +110,7 @@ const navigationItems: NavigationItem[] = [
{icon: <Analytics />, label:"Completion Report with Outstanding Un-billed Hours Report", path: "/analytics/ProjectCompletionReportWO"},
{icon: <Analytics />, label:"Project Claims Report", path: "/analytics/ProjectClaimsReport"},
{icon: <Analytics />, label:"Project P&L Report", path: "/analytics/ProjectPLReport"},
{icon: <Analytics />, label:"EX02 - Project Cash Flow Report", path: "/analytics/EX02ProjectCashFlowReport"},
],
},
{


+ 1
- 0
src/i18n/en/claim.json Переглянути файл

@@ -31,6 +31,7 @@
"Please ensure the projects are selected": "Please ensure the projects are selected",
"Please ensure the amount are correct": "Please ensure the amount are correct",

"Details": "Details",
"Description": "Description",
"Actions": "Actions"
}

+ 3
- 0
src/i18n/en/report.json Переглянути файл

@@ -0,0 +1,3 @@
{
"Project": "Project"
}

+ 1
- 0
src/i18n/zh/claim.json Переглянути файл

@@ -31,6 +31,7 @@
"Please ensure the projects are selected": "請確保所有項目欄位已選擇",
"Please ensure the amount are correct": "請確保所有金額輸入正確",

"Details": "詳請",
"Description": "描述",
"Actions": "行動"
}

+ 3
- 0
src/i18n/zh/report.json Переглянути файл

@@ -0,0 +1,3 @@
{
"Project": "項目"
}

Завантаження…
Відмінити
Зберегти