diff --git a/frontend/src/components/admin/AuditLogView.jsx b/frontend/src/components/admin/AuditLogView.jsx index df47c710..5750b066 100644 --- a/frontend/src/components/admin/AuditLogView.jsx +++ b/frontend/src/components/admin/AuditLogView.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useMemo } from 'react'; import { FilePlus, RefreshCw, @@ -6,12 +6,25 @@ import { UserPlus, Clock, User as UserIcon, - AlertCircle + AlertCircle, + Search, + MapPin, + ShieldCheck } from 'lucide-react'; const AuditLogView = ({ logs = [], loading }) => { + // 1. States för filtrering + const [searchTerm, setSearchTerm] = useState(''); + const [roleFilter, setRoleFilter] = useState('ALL'); + const [actionFilter, setActionFilter] = useState('ALL'); + const [clinicFilter, setClinicFilter] = useState('ALL'); + const [dateFilter, setDateFilter] = useState(''); + + const clinics = useMemo(() => { + const uniqueClinics = [...new Set(logs.map(log => log.clinicName))].filter(Boolean); + return uniqueClinics.sort(); + }, [logs]); - // Mappa actions till färger och ikoner const getActionStyle = (action) => { switch (action) { case 'CASE_CREATED': return { icon: , color: 'text-emerald-500', bg: 'bg-emerald-50' }; @@ -22,57 +35,193 @@ const AuditLogView = ({ logs = [], loading }) => { } }; - if (loading) return
Laddar historik...
; - if (logs.length === 0) return ( -
- -

Ingen aktivitet hittades

-
- ); + + const filteredLogs = useMemo(() => { + return logs.filter(log => { + const searchLower = searchTerm.toLowerCase(); + const matchesSearch = + log.performedByName?.toLowerCase().includes(searchLower) || + log.description?.toLowerCase().includes(searchLower) || + log.clinicName?.toLowerCase().includes(searchLower) || + log.petName?.toLowerCase().includes(searchLower) || + log.recordId?.includes(searchTerm); + + const matchesAction = actionFilter === 'ALL' || log.action === actionFilter; + const matchesRole = roleFilter === 'ALL' || + log.performedByRole?.replace('ROLE_', '') === roleFilter.replace('ROLE_', ''); + + const matchesClinic = clinicFilter === 'ALL' || log.clinicName === clinicFilter; + const matchesDate = !dateFilter || (log.createdAt?.startsWith(dateFilter) ?? false); + + return matchesSearch && matchesAction && matchesRole && matchesDate && matchesClinic; + }); + }, [logs, searchTerm, actionFilter, roleFilter, clinicFilter, dateFilter]); + + if (loading) return
Laddar administrativ historik...
; return ( -
- {logs.map((log) => { - const style = getActionStyle(log.action); - const date = new Date(log.createdAt).toLocaleString('sv-SE', { - month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' - }); - - return ( -
- {/* Timeline line */} -
- - {/* Icon */} -
- {style.icon} -
- - {/* Content */} -
-
-

- {log.action.replace('_', ' ')} -

- - {date} - -
-

{log.description}

- -
-
- - - {log.performedByName} - +
+ {/* --- ADMIN FILTER PANEL --- */} +
+
+ +
+ + setSearchTerm(e.target.value)} + /> +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ setDateFilter(e.target.value)} + className="w-full bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs font-bold outline-none focus:ring-2 focus:ring-[#003f5a] text-slate-600" + /> +
+
+
+ + {/* --- LOG LIST --- */} +
+ {filteredLogs.length === 0 ? ( +
+ +

Inga loggar matchar administratörens filter

+
+ ) : ( +
+ {filteredLogs.map((log) => { + const style = getActionStyle(log.action); + const date = new Date(log.createdAt).toLocaleString('sv-SE', { + month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' + }); + + return ( +
+
+ +
+ {style.icon} +
+ +
+
+
+

+ {log.action.replace('_', ' ')} +

+ {/* Roll-tagg */} + + + {log.performedByRole?.replace('ROLE_', '') ?? 'SYSTEM'} + +
+ + {date} + +
+ +

{log.description}

+ +
+ {/* Utförare */} +
+ + + {log.performedByName} + +
+ {/* DJURETS NAMN (NYTT) */} +
+ Patient: + + {log.petName} + +
+ + {/* Klinik */} +
+ + + {log.clinicName || 'System'} + +
+ + {/* Ärende-ID */} +
+ Ref: + + {log.recordId} + +
+
+
- ID: {log.recordId.substring(0, 8)}... -
-
+ ); + })}
- ); - })} + )} +
); }; diff --git a/src/main/java/org/example/vet1177/dto/response/activitylog/ActivityLogResponse.java b/src/main/java/org/example/vet1177/dto/response/activitylog/ActivityLogResponse.java index e966df77..ac43ab8c 100644 --- a/src/main/java/org/example/vet1177/dto/response/activitylog/ActivityLogResponse.java +++ b/src/main/java/org/example/vet1177/dto/response/activitylog/ActivityLogResponse.java @@ -13,7 +13,10 @@ public record ActivityLogResponse( String description, UUID performedById, String performedByName, + String performedByRole, UUID recordId, + String clinicName, + String petName, Instant createdAt ) { @@ -24,7 +27,10 @@ public static ActivityLogResponse from(ActivityLog log) { log.getDescription(), log.getPerformedBy().getId(), log.getPerformedBy().getName(), + log.getPerformedBy().getRole() != null ? log.getPerformedBy().getRole().name() : "ROLE_UNKNOWN", log.getMedicalRecord().getId(), + log.getMedicalRecord().getClinic() != null ? log.getMedicalRecord().getClinic().getName() : "Okänd klinik", + log.getMedicalRecord().getPet() != null ? log.getMedicalRecord().getPet().getName() : "Okänt djur", log.getCreatedAt() ); }