@mostajs/audit 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ {
2
+ "_author": "Dr Hamid MADANI drmdh@msn.com",
3
+ "title": "Journal d'audit",
4
+ "fields": {
5
+ "timestamp": "Date/Heure",
6
+ "user": "Utilisateur",
7
+ "action": "Action",
8
+ "module": "Module",
9
+ "resource": "Ressource",
10
+ "status": "Statut",
11
+ "details": "D\u00e9tails"
12
+ },
13
+ "filters": {
14
+ "module": "Module",
15
+ "action": "Rechercher action..."
16
+ },
17
+ "modules": {
18
+ "clients": "Clients",
19
+ "tickets": "Tickets",
20
+ "scan": "Scan",
21
+ "lockers": "Vestiaires",
22
+ "rfid": "RFID",
23
+ "access": "Acc\u00e8s",
24
+ "users": "Utilisateurs",
25
+ "activities": "Activit\u00e9s",
26
+ "plans": "Plans",
27
+ "settings": "Param\u00e8tres"
28
+ }
29
+ }
package/dist/index.d.ts CHANGED
@@ -3,4 +3,6 @@ export { AuditLogRepository } from './repositories/audit-log.repository';
3
3
  export { AuditLogSchema } from './schemas/audit-log.schema';
4
4
  export { createAuditHandlers } from './api/route';
5
5
  export { auditMenuContribution } from './lib/menu';
6
+ export { default as AuditPage } from './pages/AuditPage';
7
+ export { t as auditT } from './lib/i18n';
6
8
  export type { MostaAuditConfig, AuditParams, AuditFilters, AuditLogDTO, } from './types/index';
package/dist/index.js CHANGED
@@ -9,3 +9,7 @@ export { AuditLogSchema } from './schemas/audit-log.schema';
9
9
  export { createAuditHandlers } from './api/route';
10
10
  // Menu contribution
11
11
  export { auditMenuContribution } from './lib/menu';
12
+ // Pages
13
+ export { default as AuditPage } from './pages/AuditPage';
14
+ // I18n
15
+ export { t as auditT } from './lib/i18n';
@@ -0,0 +1 @@
1
+ export declare function t(key: string, params?: Record<string, string | number>): string;
@@ -0,0 +1,28 @@
1
+ // @mostajs/audit — Minimal i18n helper
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ import audit from '../i18n/fr/audit.json' with { type: 'json' };
4
+ function getNestedValue(obj, path) {
5
+ const keys = path.split('.');
6
+ let current = obj;
7
+ for (const key of keys) {
8
+ if (current && typeof current === 'object' && key in current) {
9
+ current = current[key];
10
+ }
11
+ else {
12
+ return path;
13
+ }
14
+ }
15
+ return typeof current === 'string' ? current : path;
16
+ }
17
+ export function t(key, params) {
18
+ const [namespace, ...rest] = key.split('.');
19
+ if (namespace !== 'audit')
20
+ return key;
21
+ let value = getNestedValue(audit, rest.join('.'));
22
+ if (params) {
23
+ Object.entries(params).forEach(([paramKey, paramValue]) => {
24
+ value = value.replace(`{{${paramKey}}}`, String(paramValue));
25
+ });
26
+ }
27
+ return value;
28
+ }
@@ -0,0 +1 @@
1
+ export default function AuditPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,73 @@
1
+ // @mostajs/audit — AuditPage component
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { useState, useEffect } from 'react';
6
+ import { Card, CardContent, CardHeader, CardTitle } from '@mostajs/ui/card';
7
+ import { Button } from '@mostajs/ui/button';
8
+ import { Input } from '@mostajs/ui/input';
9
+ import { Badge } from '@mostajs/ui/badge';
10
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@mostajs/ui/select';
11
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@mostajs/ui/table';
12
+ import { FileText, Search, ChevronLeft, ChevronRight } from 'lucide-react';
13
+ import { t } from '../lib/i18n.js';
14
+ const MODULES = [
15
+ 'clients', 'tickets', 'scan', 'lockers', 'rfid',
16
+ 'access', 'users', 'activities', 'plans', 'settings',
17
+ ];
18
+ const MODULE_COLORS = {
19
+ clients: 'bg-blue-100 text-blue-800',
20
+ tickets: 'bg-amber-100 text-amber-800',
21
+ scan: 'bg-green-100 text-green-800',
22
+ lockers: 'bg-purple-100 text-purple-800',
23
+ rfid: 'bg-cyan-100 text-cyan-800',
24
+ access: 'bg-orange-100 text-orange-800',
25
+ users: 'bg-red-100 text-red-800',
26
+ activities: 'bg-teal-100 text-teal-800',
27
+ plans: 'bg-indigo-100 text-indigo-800',
28
+ settings: 'bg-gray-100 text-gray-800',
29
+ };
30
+ export default function AuditPage() {
31
+ const [logs, setLogs] = useState([]);
32
+ const [loading, setLoading] = useState(true);
33
+ const [page, setPage] = useState(1);
34
+ const [totalPages, setTotalPages] = useState(1);
35
+ const [total, setTotal] = useState(0);
36
+ const [moduleFilter, setModuleFilter] = useState('');
37
+ const [actionFilter, setActionFilter] = useState('');
38
+ const [dateFrom, setDateFrom] = useState('');
39
+ const [dateTo, setDateTo] = useState('');
40
+ async function fetchLogs() {
41
+ setLoading(true);
42
+ try {
43
+ const params = new URLSearchParams({ page: String(page), limit: '30' });
44
+ if (moduleFilter)
45
+ params.set('module', moduleFilter);
46
+ if (actionFilter)
47
+ params.set('action', actionFilter);
48
+ if (dateFrom)
49
+ params.set('dateFrom', dateFrom);
50
+ if (dateTo)
51
+ params.set('dateTo', dateTo);
52
+ const res = await fetch(`/api/audit-log?${params}`);
53
+ const data = await res.json();
54
+ setLogs(data.data || []);
55
+ setTotalPages(data.pagination?.totalPages || 1);
56
+ setTotal(data.pagination?.total || 0);
57
+ }
58
+ catch {
59
+ setLogs([]);
60
+ }
61
+ finally {
62
+ setLoading(false);
63
+ }
64
+ }
65
+ useEffect(() => {
66
+ fetchLogs();
67
+ }, [page, moduleFilter]);
68
+ function handleSearch() {
69
+ setPage(1);
70
+ fetchLogs();
71
+ }
72
+ return (_jsxs("div", { className: "space-y-6", children: [_jsx("h1", { className: "text-2xl font-bold text-gray-900", children: t('audit.title') }), _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex flex-wrap gap-3", children: [_jsxs(Select, { value: moduleFilter, onValueChange: (v) => { setModuleFilter(v === 'all' ? '' : v); setPage(1); }, children: [_jsx(SelectTrigger, { className: "w-[180px]", children: _jsx(SelectValue, { placeholder: t('audit.filters.module') }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Tous les modules" }), MODULES.map((m) => (_jsx(SelectItem, { value: m, children: t(`audit.modules.${m}`) }, m)))] })] }), _jsx(Input, { placeholder: t('audit.filters.action'), value: actionFilter, onChange: (e) => setActionFilter(e.target.value), className: "w-[200px]" }), _jsx(Input, { type: "date", value: dateFrom, onChange: (e) => setDateFrom(e.target.value), className: "w-[160px]" }), _jsx(Input, { type: "date", value: dateTo, onChange: (e) => setDateTo(e.target.value), className: "w-[160px]" }), _jsxs(Button, { onClick: handleSearch, children: [_jsx(Search, { className: "mr-2 h-4 w-4" }), "Filtrer"] })] }) }) }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "h-5 w-5" }), t('audit.title'), " (", total, ")"] }) }), _jsxs(CardContent, { children: [loading ? (_jsx("div", { className: "py-8 text-center text-gray-400", children: "Chargement..." })) : logs.length === 0 ? (_jsx("div", { className: "py-8 text-center text-gray-400", children: "Aucune entr\u00E9e" })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[160px]", children: "Date" }), _jsx(TableHead, { children: "Utilisateur" }), _jsx(TableHead, { children: "Module" }), _jsx(TableHead, { children: "Action" }), _jsx(TableHead, { children: "Ressource" }), _jsx(TableHead, { className: "w-[80px]", children: "Statut" })] }) }), _jsx(TableBody, { children: logs.map((log) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "text-xs text-gray-500", children: new Date(log.timestamp).toLocaleString('fr-FR') }), _jsxs(TableCell, { children: [_jsx("div", { className: "text-sm font-medium", children: log.userName }), _jsx("div", { className: "text-xs text-gray-400", children: log.userRole })] }), _jsx(TableCell, { children: _jsx(Badge, { className: MODULE_COLORS[log.module] || 'bg-gray-100 text-gray-800', children: log.module }) }), _jsx(TableCell, { className: "text-sm", children: log.action }), _jsxs(TableCell, { className: "text-xs text-gray-500", children: [log.resource && `${log.resource}`, log.resourceId && ` #${log.resourceId.slice(-6)}`] }), _jsx(TableCell, { children: _jsx(Badge, { variant: log.status === 'success' ? 'default' : 'destructive', children: log.status }) })] }, log.id))) })] })), totalPages > 1 && (_jsxs("div", { className: "mt-4 flex items-center justify-between", children: [_jsxs("div", { className: "text-sm text-gray-500", children: ["Page ", page, " / ", totalPages] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: page <= 1, onClick: () => setPage((p) => p - 1), children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "sm", disabled: page >= totalPages, onClick: () => setPage((p) => p + 1), children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })] }))] })] })] }));
73
+ }
package/dist/register.js CHANGED
@@ -4,6 +4,7 @@ import { AuditLogSchema } from './schemas/audit-log.schema.js';
4
4
  import { AuditLogRepository } from './repositories/audit-log.repository.js';
5
5
  import { auditMenuContribution } from './lib/menu.js';
6
6
  import { GET as auditLogGET } from './lib/handlers/audit-log.js';
7
+ import AuditPage from './pages/AuditPage.js';
7
8
  export function register(registry) {
8
9
  registry.register({
9
10
  manifest: {
@@ -36,6 +37,9 @@ export function register(registry) {
36
37
  routes: [
37
38
  { path: 'audit-log', handlers: { GET: auditLogGET }, permission: 'audit:view' },
38
39
  ],
40
+ pages: [
41
+ { path: 'audit', component: AuditPage, permission: 'audit:view' },
42
+ ],
39
43
  menu: auditMenuContribution,
40
44
  });
41
45
  }
@@ -0,0 +1,29 @@
1
+ {
2
+ "_author": "Dr Hamid MADANI drmdh@msn.com",
3
+ "title": "Journal d'audit",
4
+ "fields": {
5
+ "timestamp": "Date/Heure",
6
+ "user": "Utilisateur",
7
+ "action": "Action",
8
+ "module": "Module",
9
+ "resource": "Ressource",
10
+ "status": "Statut",
11
+ "details": "D\u00e9tails"
12
+ },
13
+ "filters": {
14
+ "module": "Module",
15
+ "action": "Rechercher action..."
16
+ },
17
+ "modules": {
18
+ "clients": "Clients",
19
+ "tickets": "Tickets",
20
+ "scan": "Scan",
21
+ "lockers": "Vestiaires",
22
+ "rfid": "RFID",
23
+ "access": "Acc\u00e8s",
24
+ "users": "Utilisateurs",
25
+ "activities": "Activit\u00e9s",
26
+ "plans": "Plans",
27
+ "settings": "Param\u00e8tres"
28
+ }
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/audit",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "Reusable audit logging module — fire-and-forget logAudit() with paginated consultation",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "MIT",
@@ -37,10 +37,21 @@
37
37
  "types": "./dist/register.d.ts",
38
38
  "import": "./dist/register.js",
39
39
  "default": "./dist/register.js"
40
+ },
41
+ "./pages/AuditPage": {
42
+ "types": "./dist/pages/AuditPage.d.ts",
43
+ "import": "./dist/pages/AuditPage.js",
44
+ "default": "./dist/pages/AuditPage.js"
45
+ },
46
+ "./lib/i18n": {
47
+ "types": "./dist/lib/i18n.d.ts",
48
+ "import": "./dist/lib/i18n.js",
49
+ "default": "./dist/lib/i18n.js"
40
50
  }
41
51
  },
42
52
  "files": [
43
53
  "dist",
54
+ "i18n",
44
55
  "wire.json",
45
56
  "audit.wire.json",
46
57
  "LICENSE",
@@ -71,6 +82,8 @@
71
82
  "peerDependencies": {
72
83
  "@mostajs/menu": ">=1.0.2",
73
84
  "@mostajs/socle": ">=2.0.0",
85
+ "@mostajs/ui": ">=1.0.0",
86
+ "lucide-react": ">=0.400.0",
74
87
  "next": ">=14",
75
88
  "react": ">=18"
76
89
  },
@@ -83,12 +96,20 @@
83
96
  },
84
97
  "@mostajs/menu": {
85
98
  "optional": true
99
+ },
100
+ "@mostajs/ui": {
101
+ "optional": true
102
+ },
103
+ "lucide-react": {
104
+ "optional": true
86
105
  }
87
106
  },
88
107
  "devDependencies": {
89
108
  "@mostajs/menu": "^1.0.2",
90
109
  "@mostajs/socle": "^2.0.0",
110
+ "@mostajs/ui": "^1.0.3",
91
111
  "@types/react": "^19.0.0",
112
+ "lucide-react": "^0.575.0",
92
113
  "next": "^16.1.6",
93
114
  "react": "^19.0.0",
94
115
  "typescript": "^5.6.0"