Minor improvements on offline usage report

This commit is contained in:
Carlos Monastyrski
2025-09-04 09:45:44 -03:00
parent 1909fae076
commit 839b27d5bf
3 changed files with 24 additions and 29 deletions
@@ -1,5 +1,5 @@
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas"; import { ProjectType, TableName } from "@app/db/schemas";
export type TOfflineUsageReportDALFactory = ReturnType<typeof offlineUsageReportDALFactory>; export type TOfflineUsageReportDALFactory = ReturnType<typeof offlineUsageReportDALFactory>;
@@ -110,6 +110,7 @@ export const offlineUsageReportDALFactory = (db: TDbClient) => {
.leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id") .leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id")
.leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id") .leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id")
.leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id") .leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id")
.where("p.type", ProjectType.SecretManager)
.groupBy("p.id") .groupBy("p.id")
.whereNotNull("p.id")) as Array<{ projectId: string; count: string }>; .whereNotNull("p.id")) as Array<{ projectId: string; count: string }>;
@@ -144,6 +145,7 @@ export const offlineUsageReportDALFactory = (db: TDbClient) => {
.leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id") .leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id")
.leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id") .leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id")
.leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id") .leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id")
.where("p.type", ProjectType.SecretManager)
.groupBy("p.id", "p.name") .groupBy("p.id", "p.name")
.whereNotNull("p.id")) as Array<{ projectId: string; projectName: string; secretCount: string }>; .whereNotNull("p.id")) as Array<{ projectId: string; projectName: string; secretCount: string }>;
+12
View File
@@ -4,3 +4,15 @@ export const downloadTxtFile = (filename: string, content: string) => {
const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
FileSaver.saveAs(blob, filename); FileSaver.saveAs(blob, filename);
}; };
export const downloadFile = (content: string, filename: string, mimeType: string = "text/csv") => {
const blob = new Blob([content], { type: mimeType });
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
};
@@ -1,32 +1,18 @@
import { useState } from "react";
import { faDownload, faFileAlt, faSpinner } from "@fortawesome/free-solid-svg-icons"; import { faDownload, faFileAlt, faSpinner } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, Card, CardTitle } from "@app/components/v2"; import { Button, Card, CardTitle } from "@app/components/v2";
import { apiRequest } from "@app/config/request"; import { downloadFile } from "@app/helpers/download";
import { useGenerateUsageReport } from "@app/hooks/api/admin/mutation";
export const UsageReportSection = () => { export const UsageReportSection = () => {
const [isGenerating, setIsGenerating] = useState(false); const generateUsageReport = useGenerateUsageReport();
const downloadFile = (content: string, filename: string, mimeType: string = "text/csv") => {
const blob = new Blob([content], { type: mimeType });
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
};
const handleGenerateReport = async () => { const handleGenerateReport = async () => {
try { try {
setIsGenerating(true); const response = await generateUsageReport.mutateAsync();
const { csvContent, filename } = response;
const response = await apiRequest.post("/api/v1/admin/usage-report/generate", {});
const { csvContent, filename } = response.data;
downloadFile(csvContent, filename, "text/csv"); downloadFile(csvContent, filename, "text/csv");
@@ -40,8 +26,6 @@ export const UsageReportSection = () => {
text: "Failed to generate usage report. Please try again.", text: "Failed to generate usage report. Please try again.",
type: "error" type: "error"
}); });
} finally {
setIsGenerating(false);
} }
}; };
@@ -53,19 +37,16 @@ export const UsageReportSection = () => {
</CardTitle> </CardTitle>
<div className="mb-4 text-sm text-gray-400"> <div className="mb-4 text-sm text-gray-400">
Generate tamper-proof, cryptographically signed usage reports to ensure offline license Generate secure usage reports for offline license compliance and billing verification.
compliance and accurate billing verification. Reports capture user counts, machine
identities, project details, and secrets metadata, providing a secure and verifiable audit
trail.
</div> </div>
<Button <Button
onClick={handleGenerateReport} onClick={handleGenerateReport}
className="w-fit" className="w-fit"
isLoading={isGenerating} isLoading={generateUsageReport.isPending}
leftIcon={<FontAwesomeIcon icon={isGenerating ? faSpinner : faDownload} />} leftIcon={<FontAwesomeIcon icon={generateUsageReport.isPending ? faSpinner : faDownload} />}
> >
{isGenerating ? "Generating..." : "Generate Report"} {generateUsageReport.isPending ? "Generating..." : "Generate Report"}
</Button> </Button>
</Card> </Card>
); );