This commit is contained in:
parent
9ecd725e04
commit
effe22c4b5
Binary file not shown.
@ -20,6 +20,17 @@ interface DeviceData {
|
||||
readings: Record<string, Record<string, string>>;
|
||||
}
|
||||
|
||||
interface EventData {
|
||||
building: string;
|
||||
floor: string;
|
||||
device_name: string;
|
||||
event_time: string;
|
||||
event_type: string;
|
||||
temperature: string;
|
||||
humidity: string;
|
||||
battery: string;
|
||||
}
|
||||
|
||||
interface ReportParams {
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
@ -65,7 +76,7 @@ export class ReadingReportController extends Controller {
|
||||
// ---------------- Title ----------------
|
||||
worksheet.mergeCells('B1:G2');
|
||||
const titleCell = worksheet.getCell('B1');
|
||||
titleCell.value = 'Event Report';
|
||||
titleCell.value = 'Reading Report';
|
||||
titleCell.alignment = { horizontal: 'center', vertical: 'middle' };
|
||||
titleCell.font = { bold: true, size: 18 };
|
||||
|
||||
@ -169,10 +180,10 @@ export class ReadingReportController extends Controller {
|
||||
col.width = max + 2;
|
||||
});
|
||||
|
||||
worksheet.autoFilter = {
|
||||
from: { row: headerRowIndex, column: 1 },
|
||||
to: { row: currentRow - 1, column: headers.length },
|
||||
};
|
||||
// worksheet.autoFilter = {
|
||||
// from: { row: headerRowIndex, column: 1 },
|
||||
// to: { row: currentRow - 1, column: headers.length },
|
||||
// };
|
||||
|
||||
// ---------------- Save file ----------------
|
||||
const outputPath = path.resolve('event_report.xlsx');
|
||||
@ -183,6 +194,106 @@ export class ReadingReportController extends Controller {
|
||||
return createFileOutput(readStream, 'event_report.xlsx');
|
||||
},
|
||||
},
|
||||
generateEventsReport: {
|
||||
handler: async (FullEventSchema) => {
|
||||
const { data, report_params } = FullEventSchema;
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet('Event Records');
|
||||
|
||||
// ---------------- Logos ----------------
|
||||
const jecLogoId = workbook.addImage({
|
||||
filename: path.resolve('asset/jec_logo.jpg'),
|
||||
extension: 'jpeg',
|
||||
});
|
||||
const fastLogoId = workbook.addImage({
|
||||
filename: path.resolve('asset/fast_logo.jpg'),
|
||||
extension: 'jpeg',
|
||||
});
|
||||
|
||||
worksheet.addImage(jecLogoId, {
|
||||
tl: { col: 0, row: 0 },
|
||||
ext: { width: 120, height: 120 },
|
||||
});
|
||||
worksheet.addImage(fastLogoId, {
|
||||
tl: { col: 7, row: 0 },
|
||||
ext: { width: 120, height: 60 },
|
||||
});
|
||||
|
||||
// ---------------- Title ----------------
|
||||
worksheet.mergeCells('B1:G2');
|
||||
const titleCell = worksheet.getCell('B1');
|
||||
titleCell.value = 'Event Report';
|
||||
titleCell.alignment = { horizontal: 'center', vertical: 'middle' };
|
||||
titleCell.font = { bold: true, size: 18 };
|
||||
|
||||
// ---------------- Fixed params ----------------
|
||||
worksheet.getCell(4, 2).value = 'Start Date:';
|
||||
worksheet.getCell(4, 3).value = report_params.start_date;
|
||||
worksheet.getCell(5, 2).value = 'End Date:';
|
||||
worksheet.getCell(5, 3).value = report_params.end_date;
|
||||
|
||||
// ---------------- Headers ----------------
|
||||
const headers = [
|
||||
'Building',
|
||||
'Floor',
|
||||
'Device Name',
|
||||
'Event Time',
|
||||
'Event Type',
|
||||
'Temperature',
|
||||
'Humidity',
|
||||
'Battery',
|
||||
];
|
||||
|
||||
const headerRowIndex = 7;
|
||||
const headerRow = worksheet.getRow(headerRowIndex);
|
||||
headerRow.values = headers;
|
||||
headerRow.eachCell((cell) => {
|
||||
cell.font = { bold: true };
|
||||
cell.fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFD9D9D9' },
|
||||
};
|
||||
});
|
||||
|
||||
// ---------------- Data ----------------
|
||||
let currentRow = headerRowIndex + 1;
|
||||
|
||||
data.forEach((event) => {
|
||||
const row = worksheet.getRow(currentRow);
|
||||
row.getCell(1).value = event.building;
|
||||
row.getCell(2).value = event.floor;
|
||||
row.getCell(3).value = event.device_name;
|
||||
row.getCell(4).value = event.event_time;
|
||||
row.getCell(5).value = event.event_type;
|
||||
row.getCell(6).value = event.temperature;
|
||||
row.getCell(7).value = event.humidity;
|
||||
row.getCell(8).value = event.battery;
|
||||
|
||||
currentRow++;
|
||||
});
|
||||
|
||||
// ---------------- Auto-width ----------------
|
||||
worksheet.columns.forEach((col) => {
|
||||
let max = 10;
|
||||
col.eachCell!({ includeEmpty: true }, (cell) => {
|
||||
const len = String(cell.value ?? '').length;
|
||||
max = Math.max(max, len);
|
||||
});
|
||||
col.width = max + 2;
|
||||
});
|
||||
|
||||
// ---------------- Save file ----------------
|
||||
const outputPath = path.resolve('event_report.xlsx');
|
||||
await workbook.xlsx.writeFile(outputPath);
|
||||
console.log(`Excel report generated: ${outputPath}`);
|
||||
|
||||
// ---------------- Return as stream ----------------
|
||||
const readStream = fs.createReadStream(outputPath);
|
||||
return createFileOutput(readStream, 'event_report.xlsx'); // your helper
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@ -51,3 +51,30 @@ export const FullReportInput = z.object({
|
||||
});
|
||||
|
||||
export type FullReportInput = z.infer<typeof FullReportInput>;
|
||||
|
||||
export const FullEventInput = z.object({
|
||||
data: z.array(
|
||||
z.object({
|
||||
building: z.string(),
|
||||
floor: z.string(),
|
||||
device_name: z.string(),
|
||||
event_time: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||
message: 'Invalid event_time',
|
||||
}),
|
||||
event_type: z.string(),
|
||||
temperature: z.string(),
|
||||
humidity: z.string(),
|
||||
battery: z.string(),
|
||||
}),
|
||||
),
|
||||
report_params: z.object({
|
||||
start_date: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||
message: 'Invalid start_date',
|
||||
}),
|
||||
end_date: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||
message: 'Invalid end_date',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export type FullEventInput = z.infer<typeof FullEventInput>;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { A, buildContract } from '@jig-software/trest-core';
|
||||
import { FullReportInput } from '../constants';
|
||||
import { FullReportInput, FullEventInput } from '../constants';
|
||||
|
||||
export const readingReportContract = buildContract((c) =>
|
||||
c
|
||||
@ -12,5 +12,11 @@ export const readingReportContract = buildContract((c) =>
|
||||
.path('/reading-report')
|
||||
.prepare((p) => p.body.json(FullReportInput).annotate<[query: A]>())
|
||||
.parse((p) => p.success.file(200)),
|
||||
generateEventsReport: (e) =>
|
||||
e
|
||||
.method('POST')
|
||||
.path('/event-report')
|
||||
.prepare((p) => p.body.json(FullEventInput).annotate<[query: A]>())
|
||||
.parse((p) => p.success.file(200)),
|
||||
}),
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user