feat(activity): update summary cards to toggles and introduce export menu

This commit is contained in:
Florian Sylvain committed 2026-10-12 05:13:25 +02:00
1 parent 73f4b539fe
commit 03b39e64f2
30 files changed
+2376 -238

No files matched your search

+62 -12
View File
@@ -110,6 +110,15 @@ export function summaryRangeChanged(a: ActivityFilters, b: ActivityFilters) {
return a.range !== b.range || a.from !== b.from || a.to !== b.to
}
function rangeParams(range: ExportRange, from: string, to: string) {
const params = new URLSearchParams({ range })
if (range === 'custom') {
params.set('from', from)
params.set('to', to)
}
return params
}
function filterParams(filters: ActivityFilters) {
const params = new URLSearchParams()
if (filters.q) params.set('q', filters.q)
@@ -117,11 +126,9 @@ function filterParams(filters: ActivityFilters) {
if (filters.category) params.set('category', filters.category)
if (filters.outcome !== 'all') params.set('outcome', filters.outcome)
if (filters.security) params.set('security', 'true')
params.set('range', filters.range)
if (filters.range === 'custom') {
params.set('from', filters.from)
params.set('to', filters.to)
}
rangeParams(filters.range, filters.from, filters.to).forEach((value, key) =>
params.set(key, value),
)
return params
}
@@ -136,13 +143,55 @@ export function exportQuery(filters: ActivityFilters) {
return filterParams(filters).toString()
}
export const OWNERS_ONLY_COPY = 'Only owners and admins can export all activity.'
export const NOTHING_TO_EXPORT_COPY = 'Nothing to export. No events match these filters.'
export type ExportRange = 'all' | RangeKey
export const EXPORT_RANGE_KEYS: ExportRange[] = ['all', 'today', '7d', '30d', 'custom']
export function exportRangeLabel(range: ExportRange) {
return range === 'all' ? t('All time') : rangeLabel(range)
}
export function exportAllQuery(range: ExportRange, from: string, to: string) {
return rangeParams(range, from, to).toString()
}
export function isExportRangeValid(range: ExportRange, from: string, to: string) {
return range !== 'custom' || (from !== '' && to !== '' && from <= to)
}
function localDateStamp() {
const now = new Date()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
return `${now.getFullYear()}-${month}-${day}`
}
export function currentViewFileName() {
return `activity-${localDateStamp()}.csv`
}
export function allActivityFileName() {
return `activity-all-${localDateStamp()}.csv`
}
export function readBlobText(blob: Blob) {
return new Promise<string>((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(String(reader.result))
reader.onerror = () => reject(reader.error)
reader.readAsText(blob)
})
}
export function isHeaderOnlyCsv(text: string) {
return text.trim().split(/\r?\n/).length <= 1
}
export function summaryQuery(filters: ActivityFilters) {
const params = new URLSearchParams({ range: filters.range })
if (filters.range === 'custom') {
params.set('from', filters.from)
params.set('to', filters.to)
}
return params.toString()
return rangeParams(filters.range, filters.from, filters.to).toString()
}
export function categoryLabel(category: ActivityCategory) {
@@ -231,7 +280,8 @@ export function activeChips(filters: ActivityFilters): ActivityChip[] {
key: 'outcome',
label: t('Outcome: {0}', [outcomeFilterLabel(filters.outcome)]),
})
if (filters.security) chips.push({ key: 'security', label: t('Security events') })
if (filters.security)
chips.push({ key: 'security', label: t('Type: {0}', [t('Security events')]) })
if (filters.range !== DEFAULT_RANGE) {
const value =
filters.range === 'custom'