feat(reports): add reports page and export notification downloads

This commit is contained in:
2026-04-27 16:15:41 +03:30
parent 4befb50eb7
commit 61a1dc238d
13 changed files with 1978 additions and 9 deletions

View File

@@ -106,3 +106,67 @@ export const buildNotificationStreamUrl = (token: string) => {
const cleanBaseUrl = API_BASE_URL.replace(/\/+$/, "")
return `${cleanBaseUrl}/api/notifications/stream/?token=${encodeURIComponent(token)}`
}
const REPORT_EXPORT_DOWNLOAD_PATTERN = /\/api\/reports\/exports\/[^/]+\/download\/?$/
const toApiEndpoint = (actionUrl: string) => {
const cleanBaseUrl = API_BASE_URL.replace(/\/+$/, "")
if (actionUrl.startsWith("http://") || actionUrl.startsWith("https://")) {
if (!actionUrl.startsWith(cleanBaseUrl)) {
return null
}
return actionUrl.slice(cleanBaseUrl.length) || "/"
}
return actionUrl.startsWith("/") ? actionUrl : `/${actionUrl}`
}
const getFilenameFromDisposition = (contentDisposition: string | null) => {
if (!contentDisposition) return null
const utfMatch = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i)
if (utfMatch?.[1]) {
return decodeURIComponent(utfMatch[1])
}
const plainMatch = contentDisposition.match(/filename="?([^"]+)"?/i)
return plainMatch?.[1] || null
}
export const isReportExportDownloadUrl = (actionUrl?: string | null) => {
if (!actionUrl) return false
const endpoint = toApiEndpoint(actionUrl)
return !!endpoint && REPORT_EXPORT_DOWNLOAD_PATTERN.test(endpoint)
}
export const downloadNotificationFile = async (
actionUrl: string,
fallbackFilename?: string | null,
) => {
const endpoint = toApiEndpoint(actionUrl)
if (!endpoint) {
throw new Error("Unsupported download url")
}
const response = await authFetch(endpoint, {
method: "GET",
})
if (!response.ok) {
throw new Error("Failed to download file")
}
const blob = await response.blob()
const objectUrl = window.URL.createObjectURL(blob)
const filename =
getFilenameFromDisposition(response.headers.get("content-disposition")) ||
fallbackFilename ||
"download"
const anchor = document.createElement("a")
anchor.href = objectUrl
anchor.download = filename
document.body.appendChild(anchor)
anchor.click()
anchor.remove()
window.URL.revokeObjectURL(objectUrl)
}