OCR + reportistica
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* API client per la Dashboard e Reportistica (Fase 7).
|
||||
*/
|
||||
|
||||
import { apiClient } from './client'
|
||||
|
||||
// ─── Tipi ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface KpiSummary {
|
||||
received_today: number
|
||||
sent_today: number
|
||||
received_7d: number
|
||||
sent_7d: number
|
||||
received_30d: number
|
||||
sent_30d: number
|
||||
anomalie_attive: number
|
||||
tasso_consegna: number
|
||||
caselle_in_errore: number
|
||||
messaggi_non_letti: number
|
||||
totale_messaggi: number
|
||||
}
|
||||
|
||||
export interface DailyStat {
|
||||
day: string // "YYYY-MM-DD"
|
||||
received: number
|
||||
sent: number
|
||||
}
|
||||
|
||||
export interface OutboundStateStat {
|
||||
state: string
|
||||
count: number
|
||||
}
|
||||
|
||||
export interface MailboxStat {
|
||||
mailbox_id: string
|
||||
email_address: string
|
||||
display_name: string | null
|
||||
status: string
|
||||
received_total: number
|
||||
sent_total: number
|
||||
anomalie: number
|
||||
non_letti: number
|
||||
last_sync_at: string | null
|
||||
}
|
||||
|
||||
export interface ReportSummaryResponse {
|
||||
generated_at: string
|
||||
period_days: number
|
||||
kpi: KpiSummary
|
||||
daily_stats: DailyStat[]
|
||||
outbound_states: OutboundStateStat[]
|
||||
mailbox_stats: MailboxStat[]
|
||||
}
|
||||
|
||||
// ─── API ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const reportsApi = {
|
||||
/**
|
||||
* Recupera il riepilogo KPI + grafici per la dashboard.
|
||||
* @param days Numero di giorni per la serie storica (default 7)
|
||||
*/
|
||||
getSummary: async (days = 7): Promise<ReportSummaryResponse> => {
|
||||
const res = await apiClient.get<ReportSummaryResponse>('/reports/summary', {
|
||||
params: { days },
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
|
||||
/**
|
||||
* Scarica il report in formato CSV.
|
||||
* Il browser riceverà un file da scaricare.
|
||||
*/
|
||||
exportCsv: (params?: {
|
||||
date_from?: string
|
||||
date_to?: string
|
||||
mailbox_id?: string
|
||||
}) => {
|
||||
const url = new URL('/api/v1/reports/export', window.location.origin)
|
||||
url.searchParams.set('format', 'csv')
|
||||
if (params?.date_from) url.searchParams.set('date_from', params.date_from)
|
||||
if (params?.date_to) url.searchParams.set('date_to', params.date_to)
|
||||
if (params?.mailbox_id) url.searchParams.set('mailbox_id', params.mailbox_id)
|
||||
return url.toString()
|
||||
},
|
||||
|
||||
/**
|
||||
* Scarica il report in formato PDF.
|
||||
*/
|
||||
exportPdf: (params?: {
|
||||
date_from?: string
|
||||
date_to?: string
|
||||
}) => {
|
||||
const url = new URL('/api/v1/reports/export', window.location.origin)
|
||||
url.searchParams.set('format', 'pdf')
|
||||
if (params?.date_from) url.searchParams.set('date_from', params.date_from)
|
||||
if (params?.date_to) url.searchParams.set('date_to', params.date_to)
|
||||
return url.toString()
|
||||
},
|
||||
}
|
||||
@@ -2,13 +2,17 @@
|
||||
* API client per le impostazioni del tenant.
|
||||
*
|
||||
* Endpoint:
|
||||
* GET /api/v1/settings → legge configurazione (admin)
|
||||
* PUT /api/v1/settings → aggiorna configurazione (admin)
|
||||
* GET /api/v1/settings -> legge configurazione (admin)
|
||||
* PUT /api/v1/settings -> aggiorna configurazione (admin)
|
||||
* GET /api/v1/settings/indexing/stats -> statistiche indicizzazione
|
||||
* GET /api/v1/settings/indexing/status -> stato job reindex
|
||||
* POST /api/v1/settings/indexing/reindex -> avvia reindex
|
||||
* DELETE /api/v1/settings/indexing/reindex -> cancella reindex
|
||||
*/
|
||||
|
||||
import { apiClient } from './client'
|
||||
|
||||
// ─── Tipi ──────────────────────────────────────────────────────────────────
|
||||
// ─── Tipi impostazioni generali ────────────────────────────────────────────
|
||||
|
||||
export type ArchivalMode = 'mock' | 'production'
|
||||
|
||||
@@ -37,7 +41,38 @@ export interface TenantSettingsUpdate {
|
||||
archival_notes?: string
|
||||
}
|
||||
|
||||
// ─── Client ────────────────────────────────────────────────────────────────
|
||||
// ─── Tipi indicizzazione full-text ─────────────────────────────────────────
|
||||
|
||||
export interface IndexingStats {
|
||||
total_messages: number
|
||||
indexed_messages: number
|
||||
unindexed_messages: number
|
||||
coverage_pct: number // 0-100
|
||||
|
||||
attachments_total: number
|
||||
attachments_extracted: number
|
||||
attachments_pct: number // 0-100
|
||||
}
|
||||
|
||||
export type ReindexMode = 'full' | 'differential'
|
||||
export type JobStatus = 'idle' | 'running' | 'completed' | 'failed' | 'cancelled'
|
||||
|
||||
export interface IndexingJobStatus {
|
||||
status: JobStatus
|
||||
mode: ReindexMode | null
|
||||
total: number
|
||||
processed: number
|
||||
progress_pct: number
|
||||
|
||||
started_at: string | null // ISO datetime
|
||||
finished_at: string | null // ISO datetime
|
||||
started_by: string | null // email
|
||||
elapsed_seconds: number | null
|
||||
is_stale: boolean // running da piu' di 2 ore
|
||||
error: string | null
|
||||
}
|
||||
|
||||
// ─── Client impostazioni generali ──────────────────────────────────────────
|
||||
|
||||
export const settingsApi = {
|
||||
/**
|
||||
@@ -57,4 +92,97 @@ export const settingsApi = {
|
||||
const { data } = await apiClient.put<TenantSettingsResponse>('/settings', payload)
|
||||
return data
|
||||
},
|
||||
|
||||
// ── Indicizzazione full-text ──────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Restituisce le statistiche di copertura dell'indicizzazione.
|
||||
* @param tenantId - (solo super_admin) UUID del tenant target
|
||||
*/
|
||||
getIndexingStats: async (tenantId?: string): Promise<IndexingStats> => {
|
||||
const params = tenantId ? { tenant_id: tenantId } : undefined
|
||||
const { data } = await apiClient.get<IndexingStats>('/settings/indexing/stats', { params })
|
||||
return data
|
||||
},
|
||||
|
||||
/**
|
||||
* Restituisce lo stato del job di reindex in corso (o idle se nessuno).
|
||||
* @param tenantId - (solo super_admin) UUID del tenant target
|
||||
*/
|
||||
getIndexingStatus: async (tenantId?: string): Promise<IndexingJobStatus> => {
|
||||
const params = tenantId ? { tenant_id: tenantId } : undefined
|
||||
const { data } = await apiClient.get<IndexingJobStatus>('/settings/indexing/status', { params })
|
||||
return data
|
||||
},
|
||||
|
||||
/**
|
||||
* Avvia un job di reindex in background.
|
||||
* @param mode - 'differential' (solo NULL) o 'full' (tutti i messaggi)
|
||||
* @param tenantId - (solo super_admin) UUID del tenant target
|
||||
*/
|
||||
startReindex: async (mode: ReindexMode, tenantId?: string): Promise<IndexingJobStatus> => {
|
||||
const params = tenantId ? { tenant_id: tenantId } : undefined
|
||||
const { data } = await apiClient.post<IndexingJobStatus>(
|
||||
'/settings/indexing/reindex',
|
||||
{ mode },
|
||||
{ params }
|
||||
)
|
||||
return data
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancella il job di reindex in corso.
|
||||
* @param tenantId - (solo super_admin) UUID del tenant target
|
||||
*/
|
||||
cancelReindex: async (tenantId?: string): Promise<IndexingJobStatus> => {
|
||||
const params = tenantId ? { tenant_id: tenantId } : undefined
|
||||
const { data } = await apiClient.delete<IndexingJobStatus>(
|
||||
'/settings/indexing/reindex',
|
||||
{ params }
|
||||
)
|
||||
return data
|
||||
},
|
||||
|
||||
// ── Scansione allegati ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Restituisce lo stato del job di scansione allegati (idle se nessuno).
|
||||
* @param tenantId - (solo super_admin) UUID del tenant target
|
||||
*/
|
||||
getRescanStatus: async (tenantId?: string): Promise<IndexingJobStatus> => {
|
||||
const params = tenantId ? { tenant_id: tenantId } : undefined
|
||||
const { data } = await apiClient.get<IndexingJobStatus>(
|
||||
'/settings/indexing/rescan-status',
|
||||
{ params }
|
||||
)
|
||||
return data
|
||||
},
|
||||
|
||||
/**
|
||||
* Avvia un job di scansione allegati in background.
|
||||
* @param force - false: solo allegati senza testo estratto; true: tutti
|
||||
* @param tenantId - (solo super_admin) UUID del tenant target
|
||||
*/
|
||||
startRescan: async (force: boolean = false, tenantId?: string): Promise<IndexingJobStatus> => {
|
||||
const params = tenantId ? { tenant_id: tenantId } : undefined
|
||||
const { data } = await apiClient.post<IndexingJobStatus>(
|
||||
'/settings/indexing/rescan',
|
||||
{ force },
|
||||
{ params }
|
||||
)
|
||||
return data
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancella il job di scansione allegati in corso.
|
||||
* @param tenantId - (solo super_admin) UUID del tenant target
|
||||
*/
|
||||
cancelRescan: async (tenantId?: string): Promise<IndexingJobStatus> => {
|
||||
const params = tenantId ? { tenant_id: tenantId } : undefined
|
||||
const { data } = await apiClient.delete<IndexingJobStatus>(
|
||||
'/settings/indexing/rescan',
|
||||
{ params }
|
||||
)
|
||||
return data
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user