Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 

336 rindas
12 KiB

  1. //src\components\ReportSearchBox\SearchBox.tsx
  2. "use client";
  3. import Grid from "@mui/material/Grid";
  4. import Card from "@mui/material/Card";
  5. import CardContent from "@mui/material/CardContent";
  6. import Typography from "@mui/material/Typography";
  7. import React, { useCallback, useMemo, useState } from "react";
  8. import { useTranslation } from "react-i18next";
  9. import TextField from "@mui/material/TextField";
  10. import FormControl from "@mui/material/FormControl";
  11. import InputLabel from "@mui/material/InputLabel";
  12. import Select, { SelectChangeEvent } from "@mui/material/Select";
  13. import MenuItem from "@mui/material/MenuItem";
  14. import CardActions from "@mui/material/CardActions";
  15. import Button from "@mui/material/Button";
  16. import RestartAlt from "@mui/icons-material/RestartAlt";
  17. import Search from "@mui/icons-material/Search";
  18. import dayjs from "dayjs";
  19. import "dayjs/locale/zh-hk";
  20. import { DatePicker } from "@mui/x-date-pickers/DatePicker";
  21. import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
  22. import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
  23. import { Box } from "@mui/material";
  24. import * as XLSX from 'xlsx-js-style';
  25. //import { DownloadReportButton } from '../LateStartReportGen/DownloadReportButton';
  26. interface BaseCriterion<T extends string> {
  27. label: string;
  28. label2?: string;
  29. paramName: T;
  30. paramName2?: T;
  31. }
  32. interface TextCriterion<T extends string> extends BaseCriterion<T> {
  33. type: "text";
  34. }
  35. interface SelectCriterion<T extends string> extends BaseCriterion<T> {
  36. type: "select";
  37. options: string[];
  38. }
  39. interface DateRangeCriterion<T extends string> extends BaseCriterion<T> {
  40. type: "dateRange";
  41. }
  42. export type Criterion<T extends string> =
  43. | TextCriterion<T>
  44. | SelectCriterion<T>
  45. | DateRangeCriterion<T>;
  46. interface Props<T extends string> {
  47. criteria: Criterion<T>[];
  48. onSearch: (inputs: Record<T, string>) => void;
  49. onReset?: () => void;
  50. }
  51. function SearchBox<T extends string>({
  52. criteria,
  53. onSearch,
  54. onReset,
  55. }: Props<T>) {
  56. const { t } = useTranslation("common");
  57. const defaultInputs = useMemo(
  58. () =>
  59. criteria.reduce<Record<T, string>>(
  60. (acc, c) => {
  61. return { ...acc, [c.paramName]: c.type === "select" ? "All" : "" };
  62. },
  63. {} as Record<T, string>,
  64. ),
  65. [criteria],
  66. );
  67. const [inputs, setInputs] = useState(defaultInputs);
  68. const makeInputChangeHandler = useCallback(
  69. (paramName: T): React.ChangeEventHandler<HTMLInputElement> => {
  70. return (e) => {
  71. setInputs((i) => ({ ...i, [paramName]: e.target.value }));
  72. };
  73. },
  74. [],
  75. );
  76. const makeSelectChangeHandler = useCallback((paramName: T) => {
  77. return (e: SelectChangeEvent) => {
  78. setInputs((i) => ({ ...i, [paramName]: e.target.value }));
  79. };
  80. }, []);
  81. const makeDateChangeHandler = useCallback((paramName: T) => {
  82. return (e: any) => {
  83. setInputs((i) => ({ ...i, [paramName]: dayjs(e).format("YYYY-MM-DD") }));
  84. };
  85. }, []);
  86. const makeDateToChangeHandler = useCallback((paramName: T) => {
  87. return (e: any) => {
  88. setInputs((i) => ({
  89. ...i,
  90. [paramName + "To"]: dayjs(e).format("YYYY-MM-DD"),
  91. }));
  92. };
  93. }, []);
  94. const handleReset = () => {
  95. setInputs(defaultInputs);
  96. onReset?.();
  97. };
  98. const handleSearch = () => {
  99. onSearch(inputs);
  100. };
  101. const handleDownload = async () => {
  102. try {
  103. const response = await fetch('/api/reports', {
  104. method: 'POST',
  105. headers: {
  106. 'Content-Type': 'application/json',
  107. },
  108. body: JSON.stringify({ projectId: '123' }), // Example payload
  109. });
  110. if (!response.ok) throw new Error('Network response was not ok.');
  111. const data = await response.blob();
  112. const url = window.URL.createObjectURL(data);
  113. const a = document.createElement('a');
  114. a.href = url;
  115. a.download = "Project_Cash_Flow_Report.xlsx";
  116. document.body.appendChild(a);
  117. a.click();
  118. a.remove();
  119. } catch (error) {
  120. console.error('Error downloading the file: ', error);
  121. }
  122. };
  123. // const handleDownload = async () => {
  124. // //setIsLoading(true);
  125. // try {
  126. // const response = await fetch('/temp/AR01_Late Start Report.xlsx', {
  127. // headers: {
  128. // 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  129. // },
  130. // });
  131. // if (!response.ok) throw new Error('Network response was not ok.');
  132. // const data = await response.blob();
  133. // const reader = new FileReader();
  134. // reader.onload = (e) => {
  135. // if (e.target && e.target.result) {
  136. // const ab = e.target.result as ArrayBuffer;
  137. // const workbook = XLSX.read(ab, { type: 'array' });
  138. // const firstSheetName = workbook.SheetNames[0];
  139. // const worksheet = workbook.Sheets[firstSheetName];
  140. // // Add the current date to cell C2
  141. // const cellAddress = 'C2';
  142. // const date = new Date().toISOString().split('T')[0]; // Format YYYY-MM-DD
  143. // const formattedDate = date.replace(/-/g, '/'); // Change format to YYYY/MM/DD
  144. // XLSX.utils.sheet_add_aoa(worksheet, [[formattedDate]], { origin: cellAddress });
  145. // // Style for cell A1: Font size 16 and bold
  146. // if (worksheet['A1']) {
  147. // worksheet['A1'].s = {
  148. // font: {
  149. // bold: true,
  150. // sz: 16, // Font size 16
  151. // //name: 'Times New Roman' // Specify font
  152. // }
  153. // };
  154. // }
  155. // // Apply styles from A2 to A4 (bold)
  156. // ['A2', 'A3', 'A4'].forEach(cell => {
  157. // if (worksheet[cell]) {
  158. // worksheet[cell].s = { font: { bold: true } };
  159. // }
  160. // });
  161. // // Formatting from A6 to J6
  162. // // Apply styles from A6 to J6 (bold, bottom border, center alignment)
  163. // for (let col = 0; col < 10; col++) { // Columns A to J
  164. // const cellRef = XLSX.utils.encode_col(col) + '6';
  165. // if (worksheet[cellRef]) {
  166. // worksheet[cellRef].s = {
  167. // font: { bold: true },
  168. // alignment: { horizontal: 'center' },
  169. // border: {
  170. // bottom: { style: 'thin', color: { auto: 1 } }
  171. // }
  172. // };
  173. // }
  174. // }
  175. // const firstTableData = [
  176. // ['Column1', 'Column2', 'Column3'], // Row 1
  177. // ['Data1', 'Data2', 'Data3'], // Row 2
  178. // // ... more rows as needed
  179. // ];
  180. // // Find the last row of the first table
  181. // let lastRowOfFirstTable = 6; // Starting row for data in the first table
  182. // while (worksheet[XLSX.utils.encode_cell({ c: 0, r: lastRowOfFirstTable })]) {
  183. // lastRowOfFirstTable++;
  184. // }
  185. // // Calculate the maximum length of content in each column and set column width
  186. // const colWidths: number[] = [];
  187. // const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "", blankrows: true }) as (string | number)[][];
  188. // jsonData.forEach((row: (string | number)[]) => {
  189. // row.forEach((cell: string | number, index: number) => {
  190. // const valueLength = cell.toString().length;
  191. // colWidths[index] = Math.max(colWidths[index] || 0, valueLength);
  192. // });
  193. // });
  194. // // Apply calculated widths to each column, skipping column A
  195. // worksheet['!cols'] = colWidths.map((width, index) => {
  196. // if (index === 0) {
  197. // return { wch: 8 }; // Set default or specific width for column A if needed
  198. // }
  199. // return { wch: width + 2 }; // Add padding to width
  200. // });
  201. // // Format filename with date
  202. // const today = new Date().toISOString().split('T')[0].replace(/-/g, '_'); // Get current date and format as YYYY_MM_DD
  203. // const filename = `AR01_Late_Start_Report_${today}.xlsx`; // Append formatted date to the filename
  204. // // Convert workbook back to XLSX file
  205. // XLSX.writeFile(workbook, filename);
  206. // } else {
  207. // throw new Error('Failed to load file');
  208. // }
  209. // };
  210. // reader.readAsArrayBuffer(data);
  211. // } catch (error) {
  212. // console.error('Error downloading the file: ', error);
  213. // }
  214. // //setIsLoading(false);
  215. // };
  216. return (
  217. <Card>
  218. <CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
  219. <Typography variant="overline">{t("Search Criteria")}</Typography>
  220. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  221. {criteria.map((c) => {
  222. return (
  223. <Grid key={c.paramName} item xs={6}>
  224. {c.type === "text" && (
  225. <TextField
  226. label={c.label}
  227. fullWidth
  228. onChange={makeInputChangeHandler(c.paramName)}
  229. value={inputs[c.paramName]}
  230. />
  231. )}
  232. {c.type === "select" && (
  233. <FormControl fullWidth>
  234. <InputLabel>{c.label}</InputLabel>
  235. <Select
  236. label={c.label}
  237. onChange={makeSelectChangeHandler(c.paramName)}
  238. value={inputs[c.paramName]}
  239. >
  240. <MenuItem value={"All"}>{t("All")}</MenuItem>
  241. {c.options.map((option, index) => (
  242. <MenuItem key={`${option}-${index}`} value={option}>
  243. {option}
  244. </MenuItem>
  245. ))}
  246. </Select>
  247. </FormControl>
  248. )}
  249. {c.type === "dateRange" && (
  250. <LocalizationProvider
  251. dateAdapter={AdapterDayjs}
  252. // TODO: Should maybe use a custom adapterLocale here to support YYYY-MM-DD
  253. adapterLocale="zh-hk"
  254. >
  255. <Box display="flex">
  256. <FormControl fullWidth>
  257. <DatePicker
  258. label={c.label}
  259. onChange={makeDateChangeHandler(c.paramName)}
  260. value={inputs[c.paramName] ? dayjs(inputs[c.paramName]) : null}
  261. />
  262. </FormControl>
  263. <Box
  264. display="flex"
  265. alignItems="center"
  266. justifyContent="center"
  267. marginInline={2}
  268. >
  269. {"-"}
  270. </Box>
  271. <FormControl fullWidth>
  272. <DatePicker
  273. label={c.label2}
  274. onChange={makeDateToChangeHandler(c.paramName)}
  275. value={inputs[c.paramName.concat("To") as T] ? dayjs(inputs[c.paramName.concat("To") as T]) : null}
  276. />
  277. </FormControl>
  278. </Box>
  279. </LocalizationProvider>
  280. )}
  281. </Grid>
  282. );
  283. })}
  284. </Grid>
  285. <CardActions sx={{ justifyContent: "flex-end" }}>
  286. <Button
  287. variant="text"
  288. startIcon={<RestartAlt />}
  289. onClick={handleReset}
  290. >
  291. {t("Reset")}
  292. </Button>
  293. <Button
  294. variant="outlined"
  295. startIcon={<Search />}
  296. onClick={handleDownload}
  297. >
  298. {t("Download")}
  299. </Button>
  300. </CardActions>
  301. </CardContent>
  302. </Card>
  303. );
  304. }
  305. export default SearchBox;