feat: Activity History system — full audit coverage, UI, AI tools

Infrastructure (Phase 1):
- AuditLog schema: add source, entityName, summary fields + index
- createAuditEntry() helper: auto-diff, auto-summary, fire-and-forget
- auditLog query router: list, getByEntity, getTimeline, getActivitySummary

Audit Coverage (Phase 2 — 14 routers, 50+ mutations):
- vacation: create, approve, reject, cancel, batch ops (8 mutations)
- user: create, updateRole, setPermissions, resetPermissions (5 mutations)
- entitlement: set, bulkSet (3 mutations)
- client: create, update, delete, batchUpdateSortOrder
- org-unit: create, update, deactivate
- country: create, update, createCity, updateCity, deleteCity
- management-level: createGroup, updateGroup, createLevel, updateLevel, deleteLevel
- settings: updateSystemSettings (sensitive fields sanitized), testSmtp
- blueprint: create, update, updateRolePresets, delete, batchDelete, setGlobal
- rate-card: create, update, deactivate, addLine, updateLine, deleteLine, replaceLines
- calculation-rules: create, update, delete
- effort-rule: create, update, delete
- experience-multiplier: create, update, delete
- utilization-category: create, update

Admin UI (Phase 3):
- /admin/activity-log page with global searchable timeline
- Filters: entity type, action, user, date range, text search
- Expandable before/after diff view per entry
- Summary cards showing top entity types by change count
- EntityHistory reusable component for entity detail pages
- Sidebar nav link with clock icon

AI Assistant (Phase 4):
- query_change_history tool: "Who changed project X?"
- get_entity_timeline tool: "What happened to resource Y?"

Regression: 283 engine + 37 staffing tests pass. TypeScript clean.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-22 22:39:30 +01:00
parent 3d117708ff
commit 66878f18f4
25 changed files with 2255 additions and 156 deletions
@@ -76,6 +76,9 @@ function NotificationsIcon() {
function BroadcastIcon() {
return <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>;
}
function ActivityLogIcon() {
return <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>;
}
function AdminIcon() {
return <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M12 8a4 4 0 100 8 4 4 0 000-8zm8 4l-2.1.7a7.9 7.9 0 01-.6 1.5l1 2-2.1 2.1-2-1a7.9 7.9 0 01-1.5.6L12 20l-1.7-2.1a7.9 7.9 0 01-1.5-.6l-2 1-2.1-2.1 1-2a7.9 7.9 0 01-.6-1.5L4 12l2.1-1.7a7.9 7.9 0 01.6-1.5l-1-2 2.1-2.1 2 1a7.9 7.9 0 011.5-.6L12 4l1.7 2.1a7.9 7.9 0 011.5.6l2-1 2.1 2.1-1 2a7.9 7.9 0 01.6 1.5L20 12z" /></svg>;
}
@@ -189,6 +192,7 @@ const adminNavEntries: AdminEntry[] = [
{ href: "/admin/notifications", label: "Broadcasts", icon: <BroadcastIcon /> },
{ href: "/admin/webhooks", label: "Webhooks", icon: <AdminIcon /> },
{ href: "/admin/dispo-imports", label: "Dispo Import", icon: <AdminIcon /> },
{ href: "/admin/activity-log", label: "Activity Log", icon: <ActivityLogIcon /> },
];
/**