Quellcode durchsuchen

add report ex01

tags/Baseline_30082024_FRONTEND_UAT
leoho2fi vor 1 Jahr
Ursprung
Commit
6c16db51f5
12 geänderte Dateien mit 676 neuen und 0 gelöschten Zeilen
  1. BIN
      public/temp/EX01_Financial Status Report.xlsx
  2. +24
    -0
      src/app/(main)/analytics/FinancialStatusReport/page.tsx
  3. +42
    -0
      src/app/api/reporte1/index.ts
  4. +1
    -0
      src/components/NavigationContent/NavigationContent.tsx
  5. +17
    -0
      src/components/Report/FinancialStatusReport/FinancialStatusReport.tsx
  6. +2
    -0
      src/components/Report/FinancialStatusReport/index.ts
  7. +43
    -0
      src/components/Report/FinancialStatusReportGen/FinancialStatusReportGen.tsx
  8. +41
    -0
      src/components/Report/FinancialStatusReportGen/FinancialStatusReportGenLoading.tsx
  9. +19
    -0
      src/components/Report/FinancialStatusReportGen/FinancialStatusReportGenWrapper.tsx
  10. +2
    -0
      src/components/Report/FinancialStatusReportGen/index.ts
  11. +482
    -0
      src/components/Report/ReportSearchBoxe1/SearchBoxe1.tsx
  12. +3
    -0
      src/components/Report/ReportSearchBoxe1/index.ts

BIN
public/temp/EX01_Financial Status Report.xlsx Datei anzeigen


+ 24
- 0
src/app/(main)/analytics/FinancialStatusReport/page.tsx Datei anzeigen

@@ -0,0 +1,24 @@
//src\app\(main)\analytics\DelayReport\page.tsx
import { Metadata } from "next";
import { I18nProvider } from "@/i18n";
import Typography from "@mui/material/Typography";
import FinancialStatusReportComponent from "@/components/Report/FinancialStatusReport";

export const metadata: Metadata = {
title: "Financial Status Report",
};

const ProjectFinancialStatusReport: React.FC = () => {
return (
<I18nProvider namespaces={["analytics"]}>
<Typography variant="h4" marginInlineEnd={2}>
Financial Status Report
</Typography>
{/* <Suspense fallback={<ProgressCashFlowSearch.Loading />}>
<ProgressCashFlowSearch/>
</Suspense> */}
<FinancialStatusReportComponent />
</I18nProvider>
);
};
export default ProjectFinancialStatusReport;

+ 42
- 0
src/app/api/reporte1/index.ts Datei anzeigen

@@ -0,0 +1,42 @@
//src\app\api\report\index.ts
import { cache } from "react";

export interface FinancialStatus {
id: number;
projectCode: string;
projectName: string;
team: string;
teamLeader: string;
startDate: string;
startDateFrom: string;
startDateTo: string;
targetEndDate: string;
client: string;
subsidiary: string;
status: string;
}

export const preloadProjects = () => {
fetchProjectsFinancialStatus();
};

export const fetchProjectsFinancialStatus = cache(async () => {
return mockProjects;
});

const mockProjects: FinancialStatus[] = [
{
id: 1,
projectCode: "CUST-001",
projectName: "Client A",
team: "N/A",
teamLeader: "N/A",
startDate: "5",
startDateFrom: "5",
startDateTo: "5",
targetEndDate: "s",
client: "ss",
subsidiary: "ss",
status: "1",
},
];

+ 1
- 0
src/components/NavigationContent/NavigationContent.tsx Datei anzeigen

@@ -108,6 +108,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:"Financial Status Report", path: "/analytics/FinancialStatusReport"},
],
},
{


+ 17
- 0
src/components/Report/FinancialStatusReport/FinancialStatusReport.tsx Datei anzeigen

@@ -0,0 +1,17 @@
//src\components\DelayReport\DelayReport.tsx
"use client";
import * as React from "react";
import "../../../app/global.css";
import { Suspense } from "react";
import FinancialStatusReportGen from "@/components/Report/FinancialStatusReportGen";

const FinancialStatusReport: React.FC = () => {

return (
<Suspense fallback={<FinancialStatusReportGen.Loading />}>
<FinancialStatusReportGen />
</Suspense>
);
};

export default FinancialStatusReport;

+ 2
- 0
src/components/Report/FinancialStatusReport/index.ts Datei anzeigen

@@ -0,0 +1,2 @@
//src\components\LateStartReport\index.ts
export { default } from "./FinancialStatusReport";

+ 43
- 0
src/components/Report/FinancialStatusReportGen/FinancialStatusReportGen.tsx Datei anzeigen

@@ -0,0 +1,43 @@
//src\components\LateStartReportGen\LateStartReportGen.tsx
"use client";
import React, { useMemo, useState } from "react";
import SearchBox, { Criterion } from "../ReportSearchBoxe1";
import { useTranslation } from "react-i18next";
import { FinancialStatus } from "@/app/api/reporte1";
//import { DownloadReportButton } from './DownloadReportButton';
interface Props {
projects: FinancialStatus[];
}
type SearchQuery = Partial<Omit<FinancialStatus, "id">>;
type SearchParamNames = keyof SearchQuery;

const ProgressByClientSearch: React.FC<Props> = ({ projects }) => {
const { t } = useTranslation("projects");

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: "{Project Code}", paramName: "projectCode", type: "select", options: ["M1234", "M1268", "M1352", "M1393"] },
// {
// label: "Status",
// label2: "Remained Date To",
// paramName: "targetEndDate",
// type: "dateRange",
// },
],
[t],
);

return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
console.log(query);
}}
/>
{/* <DownloadReportButton /> */}
</>
);
};

export default ProgressByClientSearch;

+ 41
- 0
src/components/Report/FinancialStatusReportGen/FinancialStatusReportGenLoading.tsx Datei anzeigen

@@ -0,0 +1,41 @@
//src\components\LateStartReportGen\LateStartReportGenLoading.tsx
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 FinancialStatusReportGenLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<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 FinancialStatusReportGenLoading;

+ 19
- 0
src/components/Report/FinancialStatusReportGen/FinancialStatusReportGenWrapper.tsx Datei anzeigen

@@ -0,0 +1,19 @@
//src\components\LateStartReportGen\LateStartReportGenWrapper.tsx
import { fetchProjectsFinancialStatus } from "@/app/api/reporte1";
import React from "react";
import FinancialStatusReportGen from "./FinancialStatusReportGen";
import FinancialStatusReportGenLoading from "./FinancialStatusReportGenLoading";

interface SubComponents {
Loading: typeof FinancialStatusReportGenLoading;
}

const FinancialStatusReportGenWrapper: React.FC & SubComponents = async () => {
const clentprojects = await fetchProjectsFinancialStatus();

return <FinancialStatusReportGen projects={clentprojects} />;
};

FinancialStatusReportGenWrapper.Loading = FinancialStatusReportGenLoading;

export default FinancialStatusReportGenWrapper;

+ 2
- 0
src/components/Report/FinancialStatusReportGen/index.ts Datei anzeigen

@@ -0,0 +1,2 @@
//src\components\DelayReportGen\index.ts
export { default } from "./FinancialStatusReportGenWrapper";

+ 482
- 0
src/components/Report/ReportSearchBoxe1/SearchBoxe1.tsx Datei anzeigen

@@ -0,0 +1,482 @@
//src\components\ReportSearchBox\SearchBox2.tsx
"use client";

import Grid from "@mui/material/Grid";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Typography from "@mui/material/Typography";
import React, { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import TextField from "@mui/material/TextField";
import FormControl from "@mui/material/FormControl";
import InputLabel from "@mui/material/InputLabel";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import CardActions from "@mui/material/CardActions";
import Button from "@mui/material/Button";
import RestartAlt from "@mui/icons-material/RestartAlt";
import Search from "@mui/icons-material/Search";
import dayjs from "dayjs";
import "dayjs/locale/zh-hk";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { Box } from "@mui/material";
import * as XLSX from 'xlsx-js-style';
//import { DownloadReportButton } from '../LateStartReportGen/DownloadReportButton';

interface BaseCriterion<T extends string> {
label: string;
label2?: string;
paramName: T;
paramName2?: T;
}

interface TextCriterion<T extends string> extends BaseCriterion<T> {
type: "text";
}

interface SelectCriterion<T extends string> extends BaseCriterion<T> {
type: "select";
options: string[];
}

interface DateRangeCriterion<T extends string> extends BaseCriterion<T> {
type: "dateRange";
}

export type Criterion<T extends string> =
| TextCriterion<T>
| SelectCriterion<T>
| DateRangeCriterion<T>;

interface Props<T extends string> {
criteria: Criterion<T>[];
onSearch: (inputs: Record<T, string>) => void;
onReset?: () => void;
}

function SearchBox<T extends string>({
criteria,
onSearch,
onReset,
}: Props<T>) {
const { t } = useTranslation("common");
const defaultInputs = useMemo(
() =>
criteria.reduce<Record<T, string>>(
(acc, c) => {
return { ...acc, [c.paramName]: c.type === "select" ? "All" : "" };
},
{} as Record<T, string>,
),
[criteria],
);
const [inputs, setInputs] = useState(defaultInputs);

const makeInputChangeHandler = useCallback(
(paramName: T): React.ChangeEventHandler<HTMLInputElement> => {
return (e) => {
setInputs((i) => ({ ...i, [paramName]: e.target.value }));
};
},
[],
);

const makeSelectChangeHandler = useCallback((paramName: T) => {
return (e: SelectChangeEvent) => {
setInputs((i) => ({ ...i, [paramName]: e.target.value }));
};
}, []);

const makeDateChangeHandler = useCallback((paramName: T) => {
return (e: any) => {
setInputs((i) => ({ ...i, [paramName]: dayjs(e).format("YYYY-MM-DD") }));
};
}, []);

const makeDateToChangeHandler = useCallback((paramName: T) => {
return (e: any) => {
setInputs((i) => ({
...i,
[paramName + "To"]: dayjs(e).format("YYYY-MM-DD"),
}));
};
}, []);

interface CellValue {
v: number | string; // Value of the cell
t: 'n' | 's'; // Type of the cell value: 'n' for number, 's' for string
s?: XLSX.CellStyle; // Optional style for the cell
}

const handleReset = () => {
setInputs(defaultInputs);
onReset?.();
};

const handleSearch = () => {
onSearch(inputs);
};

// Function to merge cells from A2:B2 to A14:B14
function mergeCells(worksheet: XLSX.WorkSheet) {
// Ensure the 'merges' array exists in the worksheet
if (!worksheet['!merges']) worksheet['!merges'] = [];

// Loop through rows 2 to 14 (0-indexed + 1)
for (let row = 1; row <= 13; row++) {
// Define the range for current row to merge A and B columns
const mergeRange = {
s: { c: 0, r: row }, // Start cell (Column A)
e: { c: 1, r: row } // End cell (Column B)
};
// Add the range to the 'merges' array in the worksheet
worksheet['!merges'].push(mergeRange);
// Apply center alignment to the merged cell
const mergedCellRef = XLSX.utils.encode_cell({ c: 0, r: row });
if (!worksheet[mergedCellRef]) {
worksheet[mergedCellRef] = {}; // Create the cell if it doesn't exist
}
worksheet[mergedCellRef].s = {
alignment: { horizontal: "left", wrapText: true }
};
}
}

// Processing and inserting table data with calculations
function processDataAndInsert(worksheet: XLSX.WorkSheet, startRow:number, data:(string|number)[][]) {
data.forEach((row, rowIndex) => {
const r = startRow + rowIndex;

// Direct assignments for columns A-F as strings
const stringCols = ['A', 'B', 'C', 'D', 'E', 'F'];
stringCols.forEach((col, index) => {
const cellRef = col + r;
worksheet[cellRef] = { v: row[index], t: 's' }; // Force type as string
});

// Assignments for columns G-O as numbers
const numberCols = ['G', 'H', 'I', 'K', 'N'];
const colIndices = [6, 7, 8, 9, 10]; // Indices in the data array corresponding to G, H, I, K, N
numberCols.forEach((col, index) => {
const cellRef = col + r;
worksheet[cellRef] = { v: row[colIndices[index]], t: 'n' }; // Force type as number
});

// Calculations for columns J, L, M, O
const h = row[6] as number;
const i = row[7] as number;
const k = row[9] as number;
const n = row[10] as number;

// Column J: H - I
worksheet['J' + r] = { v: h - i, t: 'n' };

// Column L: IF(H<I, H-K, I-K)
worksheet['L' + r] = { v: h < i ? h - k : i - k, t: 'n' };

// Column M: K / I
worksheet['M' + r] = { v: k / i, t: 'n' };

// Column O: K - N
worksheet['O' + r] = { v: k - n, t: 'n' };
});
// Calculate sums for specified columns and insert them in the next row after the last data row
const sumRow = startRow + data.length;

// // Define the range of cells to merge
// const mergeRangeA1 = {
// s: { c: 0, r: sumRow-1}, // Start cell
// e: { c: 5, r: sumRow-1} // End cell
// };
// // Add the range to the 'merges' array in the worksheet if it doesn't exist
// if (!worksheet['!merges']) worksheet['!merges'] = [];
// worksheet['!merges'].push(mergeRangeA1);
// // Apply center alignment to the merged cell


const sumCols = ['G', 'H', 'I', 'J', 'K', 'L', 'N', 'O'];
sumCols.forEach(col => {
const cellRefs = data.map((_, index) => col + (startRow + index));
const formula = `=SUM(${cellRefs.join(',')})`;
worksheet[col + sumRow] = { f: formula, t: 'n', s: {
border: {
top: {style: 'thin', color: {auto: 1}},
bottom: {style: 'double', color: {auto: 1}}
}
}};
});
XLSX.utils.sheet_add_aoa(worksheet, [['Sub-total']], { origin: { c: 0, r: (sumRow-1) } });

// const mergedCellRefA1 = XLSX.utils.encode_cell({ c: 0, r: sumRow-1});
// if (!worksheet[mergedCellRefA1]) {
// worksheet[mergedCellRefA1] = {}; // Create the cell if it doesn't exist
// }
// // Apply right alignment, center vertical alignment, wrap text, and border styles to the 'Sub-total' cell
// worksheet[mergedCellRefA1].s = {
// alignment: {horizontal: "right", vertical: "center", wrapText: true},
// border: {
// top: {style: 'thin', color: {auto: 1}},
// bottom: {style: 'double', color: {auto: 1}}
// }
// };
// Define the range of cells to merge for 'Sub-total'
const mergeRangeSubTotal = {
s: { c: 0, r: sumRow-1}, // Start at column A
e: { c: 5, r: sumRow-1} // End at column F
};
// // Add the range to the 'merges' array in the worksheet if it doesn't exist
// if (!worksheet['!merges']) worksheet['!merges'] = [];
// worksheet['!merges'].push(mergeRangeSubTotal);

// Update styles for the merged cell range where 'Sub-total' is located
const mergedCellRefSubTotal = XLSX.utils.encode_cell({ c: 0, r: sumRow-1 });
if (!worksheet[mergedCellRefSubTotal]) {
worksheet[mergedCellRefSubTotal] = {}; // Create the cell if it doesn't exist
}
worksheet[mergedCellRefSubTotal].s = {
alignment: {horizontal: "right", vertical: "center", wrapText: true},
border: {
top: {style: 'thin', color: {auto: 1}},
bottom: {style: 'double', color: {auto: 1}}}
};
// Add the range to the 'merges' array in the worksheet if it doesn't exist
if (!worksheet['!merges']) worksheet['!merges'] = [];
worksheet['!merges'].push(mergeRangeSubTotal)


const mergedCellRefM1 = XLSX.utils.encode_cell({ c: 12, r: sumRow});
if (!worksheet[mergedCellRefM1]) {
worksheet[mergedCellRefM1] = {}; // Create the cell if it doesn't exist
}
worksheet[mergedCellRefM1].s = {
alignment: {horizontal: "right", vertical: "center", wrapText: true},
border: {
top: {style: 'thin', color: {auto: 1}},
bottom: {style: 'double', color: {auto: 1}}
}
};

}
const firstTableData = [
['Code1', 'PJName1', 'Client1','Team1','2011/01/01','2011/02/01','625','500','350','350','171'], // Row 1
['Code2', 'PJName2', 'Client2','Team2','2011/03/01','2011/04/01','1000','800','565','565','565'],// Row 2
['Code2', 'PJName2', 'Client2','Team2','2011/03/01','2011/04/01','1000','800','565','565','565'],// Row 3
// ... more rows as needed
];

const handleDownload = async () => {
//setIsLoading(true);

try {
const response = await fetch('/temp/EX01_Financial Status Report.xlsx', {
headers: {
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
});
if (!response.ok) throw new Error('Network response was not ok.');

const data = await response.blob();
const reader = new FileReader();
reader.onload = (e) => {
if (e.target && e.target.result) {
const ab = e.target.result as ArrayBuffer;
const workbook = XLSX.read(ab, { type: 'array' });
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
// Add the current date to cell C2
const cellAddress = 'C2';
const date = new Date().toISOString().split('T')[0]; // Format YYYY-MM-DD
const formattedDate = date.replace(/-/g, '/'); // Change format to YYYY/MM/DD
XLSX.utils.sheet_add_aoa(worksheet, [[formattedDate]], { origin: cellAddress });

mergeCells(worksheet);

// Style for cell A1: Font size 16 and bold
if (worksheet['A1']) {
worksheet['A1'].s = {
font: {bold: true,sz: 16,},alignment: { horizontal: 'center' } // Font size 16 //name: 'Times New Roman' // Specify font
};
}

// Apply styles from A2 A3 A5 (bold)
['A2', 'A3', 'A5','A14'].forEach(cell => {
if (worksheet[cell]) {
worksheet[cell].s = { font: { bold: true },alignment: { horizontal: 'left' } };
}
});

// Apply styles from A2 A3 A5 (bold)
['A6', 'A7', 'A8','A9','A10','A11','A12'].forEach(cell => {
if (worksheet[cell]) {
worksheet[cell].s = { font: { bold: false },alignment: { horizontal: 'left' } };
}
});

// Formatting from A15 to O15
// Apply styles from A6 to K6 (bold, bottom border, center alignment)
for (let col = 0; col < 15; col++) { // Columns A to O
const cellRef = XLSX.utils.encode_col(col) + '15';
if (worksheet[cellRef]) {
worksheet[cellRef].s = {
font: { bold: true },
alignment: { horizontal: 'center' },
border: {
bottom: { style: 'thin', color: { auto: 1 } }
}
};
}
}

// Find the last row of the first table
let lastRowOfFirstTable = 16; // Starting row for data in the first table
while (worksheet[XLSX.utils.encode_cell({ c: 0, r: lastRowOfFirstTable })]) {
lastRowOfFirstTable++;
}
// Insert the first data form into the worksheet at the desired location
//XLSX.utils.sheet_add_aoa(worksheet, firstTableData, { origin: { c: 0, r: lastRowOfFirstTable } });
// Assuming worksheet is already defined, and we start inserting from row 16
processDataAndInsert(worksheet, 16, firstTableData);
// Update lastRowOfFirstTable to account for the new data
lastRowOfFirstTable += firstTableData.length;
// Now insert the text that goes between the two tables
// Calculate the maximum length of content in each column and set column width
const colWidths: number[] = [];

// Start with a base width for each column (optional, but can help with columns that have no data)
// Check if worksheet['!ref'] is defined to prevent errors
const maxCol = worksheet['!ref'] ? worksheet['!ref'].split(':')[1].charCodeAt(0) - 'A'.charCodeAt(0) + 1 : 0;
for (let col = 0; col < maxCol; col++) {
colWidths[col] = 10; // Default base width
}

const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "", blankrows: true }) as (string | number)[][];

// Skip the first row in the jsonData
for (let row = 1; row < jsonData.length; row++) {
jsonData[row].forEach((cell, index) => {
// Only process if the cell is not null/undefined
if (cell) {
const valueLength = cell.toString().length;
colWidths[index] = Math.max(colWidths[index] || 0, valueLength);
}
});
}

// Check if worksheet exists before setting '!cols'
if (worksheet) {
worksheet['!cols'] = colWidths.map((width) => ({ wch: width + 2 })); // +2 for a little extra padding
}

// Format filename with date
const today = new Date().toISOString().split('T')[0].replace(/-/g, '_'); // Get current date and format as YYYY_MM_DD
const filename = `EX01_Financial_Status_Report_${today}.xlsx`; // Append formatted date to the filename

// Convert workbook back to XLSX file
XLSX.writeFile(workbook, filename);
} else {
throw new Error('Failed to load file');
}
};
reader.readAsArrayBuffer(data);
} catch (error) {
console.error('Error downloading the file: ', error);
}

//setIsLoading(false);
};
return (
<Card>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Typography variant="overline">{t("Search Criteria")}</Typography>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
{criteria.map((c) => {
return (
<Grid key={c.paramName} item xs={6}>
{c.type === "text" && (
<TextField
label={c.label}
fullWidth
onChange={makeInputChangeHandler(c.paramName)}
value={inputs[c.paramName]}
/>
)}
{c.type === "select" && (
<FormControl fullWidth>
<InputLabel>{c.label}</InputLabel>
<Select
label={c.label}
onChange={makeSelectChangeHandler(c.paramName)}
value={inputs[c.paramName]}
>
<MenuItem value={"All"}>{t("All")}</MenuItem>
{c.options.map((option, index) => (
<MenuItem key={`${option}-${index}`} value={option}>
{option}
</MenuItem>
))}
</Select>
</FormControl>
)}
{c.type === "dateRange" && (
<LocalizationProvider
dateAdapter={AdapterDayjs}
// TODO: Should maybe use a custom adapterLocale here to support YYYY-MM-DD
adapterLocale="zh-hk"
>
<Box display="flex">
<FormControl fullWidth>
<DatePicker
label={c.label}
onChange={makeDateChangeHandler(c.paramName)}
value={inputs[c.paramName] ? dayjs(inputs[c.paramName]) : null}
/>
</FormControl>
<Box
display="flex"
alignItems="center"
justifyContent="center"
marginInline={2}
>
{"-"}
</Box>
<FormControl fullWidth>
<DatePicker
label={c.label2}
onChange={makeDateToChangeHandler(c.paramName)}
value={inputs[c.paramName.concat("To") as T] ? dayjs(inputs[c.paramName.concat("To") as T]) : null}
/>
</FormControl>
</Box>
</LocalizationProvider>
)}
</Grid>
);
})}
</Grid>
<CardActions sx={{ justifyContent: "flex-end" }}>
<Button
variant="text"
startIcon={<RestartAlt />}
onClick={handleReset}
>
{t("Reset")}
</Button>
<Button
variant="outlined"
startIcon={<Search />}
onClick={handleDownload}
>
{t("Download")}
</Button>
</CardActions>
</CardContent>
</Card>
);
}

export default SearchBox;

+ 3
- 0
src/components/Report/ReportSearchBoxe1/index.ts Datei anzeigen

@@ -0,0 +1,3 @@
//src\components\SearchBox\index.ts
export { default } from "./SearchBoxe1";
export type { Criterion } from "./SearchBoxe1";

Laden…
Abbrechen
Speichern