@drax/audit-vue 0.38.0 → 0.39.1
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.
- package/package.json +4 -4
- package/src/components/AuditView.vue +185 -362
- package/src/cruds/AuditCrud.ts +20 -4
- package/src/pages/crud/AuditCrudPage.vue +20 -1
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.39.1",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"module": "./src/index.ts",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"format": "prettier --write src/"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@drax/crud-front": "^0.
|
|
28
|
-
"@drax/crud-share": "^0.
|
|
27
|
+
"@drax/crud-front": "^0.39.0",
|
|
28
|
+
"@drax/crud-share": "^0.39.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"pinia": "^2.2.2",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"vue-tsc": "^2.0.11",
|
|
63
63
|
"vuetify": "^3.7.1"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "f765e15e64c21dca72b8d4a8d2d6279c7fdb32d5"
|
|
66
66
|
}
|
|
@@ -1,380 +1,203 @@
|
|
|
1
|
+
<template>
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
<!-- BLOQUE DE ATRIBUTOS EN COLUMNA -->
|
|
4
|
+
<div class="text-body-2 mt-3">
|
|
5
|
+
|
|
6
|
+
<div class="mb-1 audit-row">
|
|
7
|
+
<strong class="audit-label">{{ t('audit.field.action') }}:</strong>
|
|
8
|
+
<span>{{ audit.action }}</span>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="mb-1 audit-row">
|
|
12
|
+
<strong class="audit-label">{{ t('audit.field.entity') }}:</strong>
|
|
13
|
+
<span>{{ audit.entity }}</span>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="mb-1 audit-row">
|
|
17
|
+
<strong class="audit-label">{{ t('audit.field.resourceId') }}:</strong>
|
|
18
|
+
<span>{{ audit.resourceId }}</span>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
<div class="mb-1 audit-row">
|
|
23
|
+
<strong class="audit-label">{{ t('audit.field.user') }}:</strong>
|
|
24
|
+
<span>{{ audit.user.username }}</span> <span v-if="audit.user.rolName"><{{ audit.user.rolName }}></span> <span>(ID {{
|
|
25
|
+
audit.user.id
|
|
26
|
+
}})</span>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div
|
|
30
|
+
v-if="audit.apiKey?.id"
|
|
31
|
+
class="mb-1 audit-row"
|
|
32
|
+
>
|
|
33
|
+
<strong class="audit-label">{{ t('audit.field.apiKey') }}:</strong>
|
|
34
|
+
<span>{{ audit.apiKey?.name }}</span> <span>(ID {{ audit.apiKey?.id }})</span>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div class="mb-1 audit-row">
|
|
38
|
+
<strong class="audit-label">{{ t('audit.field.ip') }}:</strong>
|
|
39
|
+
<span>{{ audit.ip }}</span>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="mb-1 audit-row">
|
|
43
|
+
<strong class="audit-label">{{ t('audit.field.userAgent') }}:</strong>
|
|
44
|
+
<span class="d-inline-block">
|
|
45
|
+
{{ audit.userAgent }}
|
|
46
|
+
</span>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div
|
|
50
|
+
v-if="audit.tenant"
|
|
51
|
+
class="mb-1 audit-row"
|
|
52
|
+
>
|
|
53
|
+
<strong class="audit-label">{{ t('audit.field.tenant') }}:</strong>
|
|
54
|
+
<span>{{ audit.tenant?.name }}</span><span v-if="audit.tenant.id">(ID {{ audit.tenant?.id }})</span>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
<div
|
|
59
|
+
class="mb-1 audit-row"
|
|
60
|
+
>
|
|
61
|
+
<strong class="audit-label">{{ t('audit.field.sessionId') }}:</strong>
|
|
62
|
+
<span>{{ audit.sessionId }}</span>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div
|
|
66
|
+
v-if="audit.requestId"
|
|
67
|
+
class="mb-1 audit-row"
|
|
68
|
+
>
|
|
69
|
+
<strong class="audit-label">{{ t('audit.field.requestId') }}:</strong>
|
|
70
|
+
<span>{{ audit.requestId }}</span>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div
|
|
74
|
+
v-if="audit.createdAt"
|
|
75
|
+
class="mb-1 audit-row"
|
|
76
|
+
>
|
|
77
|
+
<strong class="audit-label">{{ t('audit.field.createdAt') }}:</strong>
|
|
78
|
+
<span>{{ formatDateTime(audit.createdAt) }}</span>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<!-- DETAIL -->
|
|
85
|
+
<v-row
|
|
86
|
+
v-if="audit.detail"
|
|
87
|
+
class="mt-2"
|
|
88
|
+
>
|
|
89
|
+
<v-col cols="12">
|
|
90
|
+
<span class="font-weight-medium">{{ t('audit.field.detail') }}:</span>
|
|
91
|
+
<v-sheet
|
|
92
|
+
color="grey-lighten-4"
|
|
93
|
+
rounded
|
|
94
|
+
class="pa-3 text-body-2 mt-1"
|
|
95
|
+
>
|
|
96
|
+
{{ audit.detail }}
|
|
97
|
+
</v-sheet>
|
|
98
|
+
</v-col>
|
|
99
|
+
</v-row>
|
|
100
|
+
|
|
101
|
+
<!-- CAMBIOS REGISTRADOS (SIN PAGINACIÓN) -->
|
|
102
|
+
<v-row
|
|
103
|
+
v-if="audit.changes && audit.changes.length"
|
|
104
|
+
class="mt-2"
|
|
105
|
+
>
|
|
106
|
+
<v-col cols="12">
|
|
107
|
+
<span class="font-weight-medium">{{ t('audit.field.changes') }}</span>
|
|
108
|
+
|
|
109
|
+
<v-data-table
|
|
110
|
+
class="mt-2"
|
|
111
|
+
density="compact"
|
|
112
|
+
:headers="changeHeaders"
|
|
113
|
+
:items="audit.changes"
|
|
114
|
+
:items-per-page="-1"
|
|
115
|
+
hide-default-footer
|
|
116
|
+
>
|
|
117
|
+
<template #item.old="{ item }">
|
|
118
|
+
<span
|
|
119
|
+
class="text-red-darken-2 text-body-2"
|
|
120
|
+
style="white-space: pre-wrap; word-break: break-all;"
|
|
121
|
+
>
|
|
122
|
+
{{ item.old ?? '-' }}
|
|
123
|
+
</span>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<template #item.new="{ item }">
|
|
127
|
+
<span
|
|
128
|
+
class="text-green-darken-2 text-body-2"
|
|
129
|
+
style="white-space: pre-wrap; word-break: break-all;"
|
|
130
|
+
>
|
|
131
|
+
{{ item.new ?? '-' }}
|
|
132
|
+
</span>
|
|
133
|
+
</template>
|
|
134
|
+
</v-data-table>
|
|
135
|
+
</v-col>
|
|
136
|
+
</v-row>
|
|
9
137
|
|
|
10
|
-
|
|
138
|
+
</template>
|
|
11
139
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
140
|
+
<script setup lang="ts">
|
|
141
|
+
import {useI18n} from 'vue-i18n'
|
|
142
|
+
import {formatDateTime} from '@drax/common-front'
|
|
143
|
+
|
|
144
|
+
interface IAudit {
|
|
145
|
+
_id: string
|
|
146
|
+
entity: string
|
|
147
|
+
resourceId?: string
|
|
148
|
+
user: {
|
|
149
|
+
id: string
|
|
150
|
+
username: string
|
|
151
|
+
rolName: string
|
|
19
152
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
153
|
+
action: string
|
|
154
|
+
ip: string
|
|
155
|
+
userAgent: string
|
|
156
|
+
changes?: Array<{
|
|
157
|
+
field: string
|
|
158
|
+
old?: string
|
|
159
|
+
new?: string
|
|
160
|
+
}>
|
|
161
|
+
sessionId?: string
|
|
162
|
+
requestId?: string
|
|
163
|
+
detail?: string
|
|
164
|
+
tenant?: {
|
|
165
|
+
id: string
|
|
166
|
+
name: string
|
|
30
167
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
year: 'numeric',
|
|
38
|
-
month: '2-digit',
|
|
39
|
-
day: '2-digit',
|
|
40
|
-
hour: '2-digit',
|
|
41
|
-
minute: '2-digit',
|
|
42
|
-
second: '2-digit'
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const hasChanges = computed(() => {
|
|
47
|
-
return props.audit.changes && props.audit.changes.length > 0
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
const copyToClipboard = (text: string) => {
|
|
51
|
-
navigator.clipboard.writeText(text)
|
|
168
|
+
apiKey?: {
|
|
169
|
+
id: string
|
|
170
|
+
name: string
|
|
171
|
+
}
|
|
172
|
+
createdAt?: string
|
|
173
|
+
updatedAt?: string
|
|
52
174
|
}
|
|
53
|
-
</script>
|
|
54
|
-
|
|
55
|
-
<template>
|
|
56
|
-
<v-card class="audit-view-card" elevation="2">
|
|
57
|
-
<!-- Header con acción y fecha -->
|
|
58
|
-
<v-card-title class="d-flex align-center pa-4 bg-gradient">
|
|
59
|
-
<v-icon :color="actionColor" size="32" class="mr-3">
|
|
60
|
-
{{ actionIcon }}
|
|
61
|
-
</v-icon>
|
|
62
|
-
<div class="flex-grow-1">
|
|
63
|
-
<div class="text-h6 font-weight-bold">
|
|
64
|
-
{{ audit.action }}
|
|
65
|
-
</div>
|
|
66
|
-
<div class="text-caption text-medium-emphasis">
|
|
67
|
-
{{ formattedDate }}
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
<v-chip :color="actionColor" variant="flat" size="small" class="font-weight-bold">
|
|
71
|
-
{{ audit.entity }}
|
|
72
|
-
</v-chip>
|
|
73
|
-
</v-card-title>
|
|
74
175
|
|
|
75
|
-
|
|
176
|
+
const {t} = useI18n()
|
|
76
177
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<div class="section-header mb-4">
|
|
81
|
-
<v-icon color="primary" class="mr-2">mdi-information</v-icon>
|
|
82
|
-
<span class="text-subtitle-1 font-weight-bold">Información General</span>
|
|
83
|
-
</div>
|
|
84
|
-
<v-card variant="outlined" class="pa-4">
|
|
85
|
-
<div class="info-row mb-4">
|
|
86
|
-
<span class="info-label">Acción:</span>
|
|
87
|
-
<v-chip :color="actionColor" variant="tonal" size="small" class="ml-2">
|
|
88
|
-
{{ audit.action }}
|
|
89
|
-
</v-chip>
|
|
90
|
-
</div>
|
|
91
|
-
<v-divider class="my-3" />
|
|
92
|
-
<div class="info-row">
|
|
93
|
-
<span class="info-label">Entidad:</span>
|
|
94
|
-
<span class="info-value">{{ audit.entity }}</span>
|
|
95
|
-
</div>
|
|
96
|
-
</v-card>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<!-- Información del Usuario -->
|
|
100
|
-
<div class="mb-8">
|
|
101
|
-
<div class="section-header mb-4">
|
|
102
|
-
<v-icon color="primary" class="mr-2">mdi-account-circle</v-icon>
|
|
103
|
-
<span class="text-subtitle-1 font-weight-bold">Usuario</span>
|
|
104
|
-
</div>
|
|
105
|
-
<v-card variant="outlined" class="pa-4">
|
|
106
|
-
<div class="info-row mb-4">
|
|
107
|
-
<span class="info-label">Nombre de Usuario:</span>
|
|
108
|
-
<span class="info-value">{{ audit.user.username }}</span>
|
|
109
|
-
</div>
|
|
110
|
-
<v-divider class="my-3" />
|
|
111
|
-
<div class="info-row mb-4">
|
|
112
|
-
<span class="info-label">Rol:</span>
|
|
113
|
-
<v-chip size="small" color="primary" variant="tonal" class="ml-2">
|
|
114
|
-
{{ audit.user.rolName }}
|
|
115
|
-
</v-chip>
|
|
116
|
-
</div>
|
|
117
|
-
<v-divider class="my-3" />
|
|
118
|
-
<div class="info-row">
|
|
119
|
-
<span class="info-label">ID de Usuario:</span>
|
|
120
|
-
<div class="d-flex align-center gap-2 ml-2">
|
|
121
|
-
<span class="info-value font-monospace text-caption">{{ audit.user.id }}</span>
|
|
122
|
-
<v-btn
|
|
123
|
-
icon
|
|
124
|
-
size="x-small"
|
|
125
|
-
variant="text"
|
|
126
|
-
@click="copyToClipboard(audit.user.id)"
|
|
127
|
-
>
|
|
128
|
-
<v-icon size="small">mdi-content-copy</v-icon>
|
|
129
|
-
</v-btn>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
</v-card>
|
|
133
|
-
</div>
|
|
134
|
-
|
|
135
|
-
<!-- Información de Conexión -->
|
|
136
|
-
<div class="mb-8">
|
|
137
|
-
<div class="section-header mb-4">
|
|
138
|
-
<v-icon color="info" class="mr-2">mdi-network</v-icon>
|
|
139
|
-
<span class="text-subtitle-1 font-weight-bold">Información de Conexión</span>
|
|
140
|
-
</div>
|
|
141
|
-
<v-card variant="outlined" class="pa-4">
|
|
142
|
-
<div class="info-row mb-4">
|
|
143
|
-
<span class="info-label">
|
|
144
|
-
<v-icon size="x-small" class="mr-1">mdi-ip</v-icon>
|
|
145
|
-
Dirección IP:
|
|
146
|
-
</span>
|
|
147
|
-
<div class="d-flex align-center gap-2 ml-2">
|
|
148
|
-
<v-chip size="small" variant="tonal" color="info">
|
|
149
|
-
{{ audit.ip }}
|
|
150
|
-
</v-chip>
|
|
151
|
-
<v-btn
|
|
152
|
-
icon
|
|
153
|
-
size="x-small"
|
|
154
|
-
variant="text"
|
|
155
|
-
@click="copyToClipboard(audit.ip)"
|
|
156
|
-
>
|
|
157
|
-
<v-icon size="small">mdi-content-copy</v-icon>
|
|
158
|
-
</v-btn>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
<v-divider class="my-3" />
|
|
162
|
-
<div class="info-row">
|
|
163
|
-
<span class="info-label">
|
|
164
|
-
<v-icon size="x-small" class="mr-1">mdi-web</v-icon>
|
|
165
|
-
User Agent:
|
|
166
|
-
</span>
|
|
167
|
-
<span class="info-value text-caption">{{ audit.userAgent }}</span>
|
|
168
|
-
</div>
|
|
169
|
-
</v-card>
|
|
170
|
-
</div>
|
|
171
|
-
|
|
172
|
-
<!-- Cambios Realizados -->
|
|
173
|
-
<div v-if="hasChanges" class="mb-8">
|
|
174
|
-
<div class="section-header mb-4">
|
|
175
|
-
<v-icon color="warning" class="mr-2">mdi-file-document-edit</v-icon>
|
|
176
|
-
<span class="text-subtitle-1 font-weight-bold">Cambios Realizados</span>
|
|
177
|
-
<v-chip size="x-small" color="warning" variant="tonal" class="ml-2">
|
|
178
|
-
{{ audit.changes?.length }} cambios
|
|
179
|
-
</v-chip>
|
|
180
|
-
</div>
|
|
181
|
-
<v-card variant="outlined" class="overflow-hidden">
|
|
182
|
-
<v-list lines="three" class="pa-0">
|
|
183
|
-
<template v-for="(change, index) in audit.changes" :key="index">
|
|
184
|
-
<v-list-item class="change-item">
|
|
185
|
-
<template #prepend>
|
|
186
|
-
<v-avatar color="warning" size="40" class="font-weight-bold">
|
|
187
|
-
<v-icon size="small">mdi-swap-horizontal</v-icon>
|
|
188
|
-
</v-avatar>
|
|
189
|
-
</template>
|
|
190
|
-
<div class="w-100">
|
|
191
|
-
<v-list-item-title class="font-weight-bold text-base mb-3">
|
|
192
|
-
Campo: {{ change.field }}
|
|
193
|
-
</v-list-item-title>
|
|
194
|
-
<div v-if="change.old" class="mb-2">
|
|
195
|
-
<span class="info-label">Valor Anterior:</span>
|
|
196
|
-
<v-chip
|
|
197
|
-
size="small"
|
|
198
|
-
color="error"
|
|
199
|
-
variant="tonal"
|
|
200
|
-
class="font-monospace ml-2 mt-1"
|
|
201
|
-
>
|
|
202
|
-
<v-icon start size="x-small">mdi-minus</v-icon>
|
|
203
|
-
{{ change.old }}
|
|
204
|
-
</v-chip>
|
|
205
|
-
</div>
|
|
206
|
-
<div v-if="change.new">
|
|
207
|
-
<span class="info-label">Valor Nuevo:</span>
|
|
208
|
-
<v-chip
|
|
209
|
-
size="small"
|
|
210
|
-
color="success"
|
|
211
|
-
variant="tonal"
|
|
212
|
-
class="font-monospace ml-2 mt-1"
|
|
213
|
-
>
|
|
214
|
-
<v-icon start size="x-small">mdi-plus</v-icon>
|
|
215
|
-
{{ change.new }}
|
|
216
|
-
</v-chip>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
219
|
-
</v-list-item>
|
|
220
|
-
<v-divider v-if="index < audit.changes!.length - 1" />
|
|
221
|
-
</template>
|
|
222
|
-
</v-list>
|
|
223
|
-
</v-card>
|
|
224
|
-
</div>
|
|
178
|
+
defineProps<{
|
|
179
|
+
audit: IAudit
|
|
180
|
+
}>()
|
|
225
181
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
</div>
|
|
232
|
-
<v-card variant="outlined" class="pa-4">
|
|
233
|
-
<pre class="detail-text">{{ audit.detail }}</pre>
|
|
234
|
-
</v-card>
|
|
235
|
-
</div>
|
|
182
|
+
const changeHeaders = [
|
|
183
|
+
{title: t('audit.field.field'), key: 'field'},
|
|
184
|
+
{title: t('audit.field.old'), key: 'old'},
|
|
185
|
+
{title: t('audit.field.new'), key: 'new'},
|
|
186
|
+
]
|
|
236
187
|
|
|
237
|
-
<!-- Información del Tenant -->
|
|
238
|
-
<div v-if="audit.tenant" class="mb-8">
|
|
239
|
-
<div class="section-header mb-4">
|
|
240
|
-
<v-icon color="secondary" class="mr-2">mdi-domain</v-icon>
|
|
241
|
-
<span class="text-subtitle-1 font-weight-bold">Tenant</span>
|
|
242
|
-
</div>
|
|
243
|
-
<v-card variant="outlined" class="pa-4">
|
|
244
|
-
<div class="info-row mb-4">
|
|
245
|
-
<span class="info-label">Nombre del Tenant:</span>
|
|
246
|
-
<span class="info-value">{{ audit.tenant.name }}</span>
|
|
247
|
-
</div>
|
|
248
|
-
<v-divider class="my-3" />
|
|
249
|
-
<div class="info-row">
|
|
250
|
-
<span class="info-label">ID del Tenant:</span>
|
|
251
|
-
<div class="d-flex align-center gap-2 ml-2">
|
|
252
|
-
<span class="info-value text-caption font-monospace">{{ audit.tenant.id }}</span>
|
|
253
|
-
<v-btn
|
|
254
|
-
icon
|
|
255
|
-
size="x-small"
|
|
256
|
-
variant="text"
|
|
257
|
-
@click="copyToClipboard(audit.tenant.id)"
|
|
258
|
-
>
|
|
259
|
-
<v-icon size="small">mdi-content-copy</v-icon>
|
|
260
|
-
</v-btn>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
</v-card>
|
|
264
|
-
</div>
|
|
265
188
|
|
|
266
|
-
|
|
267
|
-
<div>
|
|
268
|
-
<div class="section-header mb-4">
|
|
269
|
-
<v-icon color="grey" class="mr-2">mdi-identifier</v-icon>
|
|
270
|
-
<span class="text-subtitle-1 font-weight-bold">Identificadores de Sesión</span>
|
|
271
|
-
</div>
|
|
272
|
-
<v-card variant="outlined" class="pa-4">
|
|
273
|
-
<div class="info-row mb-4">
|
|
274
|
-
<span class="info-label">ID de Auditoría:</span>
|
|
275
|
-
<div class="d-flex align-center gap-2 ml-2">
|
|
276
|
-
<span class="info-value text-caption font-monospace">{{ audit._id }}</span>
|
|
277
|
-
<v-btn
|
|
278
|
-
icon
|
|
279
|
-
size="x-small"
|
|
280
|
-
variant="text"
|
|
281
|
-
@click="copyToClipboard(audit._id)"
|
|
282
|
-
>
|
|
283
|
-
<v-icon size="small">mdi-content-copy</v-icon>
|
|
284
|
-
</v-btn>
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
<v-divider class="my-3" />
|
|
288
|
-
<div v-if="audit.sessionId" class="info-row mb-4">
|
|
289
|
-
<span class="info-label">ID de Sesión:</span>
|
|
290
|
-
<div class="d-flex align-center gap-2 ml-2">
|
|
291
|
-
<span class="info-value text-caption font-monospace">{{ audit.sessionId }}</span>
|
|
292
|
-
<v-btn
|
|
293
|
-
icon
|
|
294
|
-
size="x-small"
|
|
295
|
-
variant="text"
|
|
296
|
-
@click="copyToClipboard(audit.sessionId)"
|
|
297
|
-
>
|
|
298
|
-
<v-icon size="small">mdi-content-copy</v-icon>
|
|
299
|
-
</v-btn>
|
|
300
|
-
</div>
|
|
301
|
-
</div>
|
|
302
|
-
<v-divider class="my-3" />
|
|
303
|
-
<div v-if="audit.requestId" class="info-row">
|
|
304
|
-
<span class="info-label">ID de Petición:</span>
|
|
305
|
-
<div class="d-flex align-center gap-2 ml-2">
|
|
306
|
-
<span class="info-value text-caption font-monospace">{{ audit.requestId }}</span>
|
|
307
|
-
<v-btn
|
|
308
|
-
icon
|
|
309
|
-
size="x-small"
|
|
310
|
-
variant="text"
|
|
311
|
-
@click="copyToClipboard(audit.requestId)"
|
|
312
|
-
>
|
|
313
|
-
<v-icon size="small">mdi-content-copy</v-icon>
|
|
314
|
-
</v-btn>
|
|
315
|
-
</div>
|
|
316
|
-
</div>
|
|
317
|
-
</v-card>
|
|
318
|
-
</div>
|
|
319
|
-
</v-card-text>
|
|
320
|
-
</v-card>
|
|
321
|
-
</template>
|
|
189
|
+
</script>
|
|
322
190
|
|
|
323
191
|
<style scoped>
|
|
324
|
-
.audit-
|
|
325
|
-
border-radius: 12px;
|
|
326
|
-
overflow: hidden;
|
|
327
|
-
transition: all 0.3s ease;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.audit-view-card:hover {
|
|
331
|
-
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12) !important;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.bg-gradient {
|
|
335
|
-
background: linear-gradient(135deg, rgb(var(--v-theme-surface-variant)) 0%, rgb(var(--v-theme-surface)) 100%);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
.section-header {
|
|
192
|
+
.audit-row {
|
|
339
193
|
display: flex;
|
|
340
|
-
align-items:
|
|
341
|
-
|
|
342
|
-
border-bottom: 2px solid rgba(var(--v-theme-primary), 0.2);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
.info-row {
|
|
346
|
-
display: flex;
|
|
347
|
-
flex-direction: column;
|
|
348
|
-
gap: 6px;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.info-label {
|
|
352
|
-
font-size: 0.875rem;
|
|
353
|
-
font-weight: 700;
|
|
354
|
-
color: rgb(var(--v-theme-on-surface-variant));
|
|
355
|
-
text-transform: uppercase;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
.info-value {
|
|
359
|
-
font-size: 0.875rem;
|
|
360
|
-
color: rgb(var(--v-theme-on-surface));
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
.detail-text {
|
|
364
|
-
white-space: pre-wrap;
|
|
365
|
-
word-break: break-word;
|
|
366
|
-
margin: 0;
|
|
367
|
-
padding: 0;
|
|
368
|
-
font-family: 'Courier New', monospace;
|
|
369
|
-
font-size: 0.875rem;
|
|
370
|
-
color: rgb(var(--v-theme-on-surface));
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
.change-item {
|
|
374
|
-
transition: background-color 0.2s ease;
|
|
194
|
+
align-items: flex-start;
|
|
195
|
+
gap: 12px;
|
|
375
196
|
}
|
|
376
197
|
|
|
377
|
-
.
|
|
378
|
-
|
|
198
|
+
.audit-label {
|
|
199
|
+
min-width: 150px;
|
|
200
|
+
text-align: right;
|
|
201
|
+
flex-shrink: 0;
|
|
379
202
|
}
|
|
380
203
|
</style>
|
package/src/cruds/AuditCrud.ts
CHANGED
|
@@ -42,16 +42,16 @@ class AuditCrud extends EntityCrud implements IEntityCrud {
|
|
|
42
42
|
get headers(): IEntityCrudHeader[] {
|
|
43
43
|
return [
|
|
44
44
|
{title: 'createdAt',key:'createdAt', align: 'start'},
|
|
45
|
+
{title: 'action',key:'action', align: 'start'},
|
|
45
46
|
{title: 'entity',key:'entity', align: 'start'},
|
|
47
|
+
{title: 'tenant',key:'tenant', align: 'start'},
|
|
46
48
|
{title: 'user',key:'user', align: 'start'},
|
|
47
|
-
{title: 'action',key:'action', align: 'start'},
|
|
48
49
|
{title: 'ip',key:'ip', align: 'start'},
|
|
49
50
|
{title: 'userAgent',key:'userAgent', align: 'start'},
|
|
50
51
|
{title: 'changes',key:'changes', align: 'start'},
|
|
51
52
|
{title: 'sessionId',key:'sessionId', align: 'start'},
|
|
52
53
|
{title: 'requestId',key:'requestId', align: 'start'},
|
|
53
|
-
{title: 'detail',key:'detail', align: 'start'}
|
|
54
|
-
{title: 'tenant',key:'tenant', align: 'start'}
|
|
54
|
+
{title: 'detail',key:'detail', align: 'start'}
|
|
55
55
|
]
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -99,6 +99,7 @@ class AuditCrud extends EntityCrud implements IEntityCrud {
|
|
|
99
99
|
|
|
100
100
|
get fields(): IEntityCrudField[] {
|
|
101
101
|
return [
|
|
102
|
+
{name: 'action', type: 'string', label: 'action', default: ''},
|
|
102
103
|
{name: 'entity', type: 'string', label: 'entity', default: ''},
|
|
103
104
|
{
|
|
104
105
|
name: 'user',
|
|
@@ -109,7 +110,6 @@ class AuditCrud extends EntityCrud implements IEntityCrud {
|
|
|
109
110
|
{name: 'username', type: 'string', label: 'username', default: ''},
|
|
110
111
|
{name: 'rolName', type: 'string', label: 'rolName', default: ''}]
|
|
111
112
|
},
|
|
112
|
-
{name: 'action', type: 'string', label: 'action', default: ''},
|
|
113
113
|
{name: 'ip', type: 'string', label: 'ip', default: ''},
|
|
114
114
|
{name: 'userAgent', type: 'string', label: 'userAgent', default: ''},
|
|
115
115
|
{
|
|
@@ -138,9 +138,25 @@ class AuditCrud extends EntityCrud implements IEntityCrud {
|
|
|
138
138
|
get filters(): IEntityCrudFilter[] {
|
|
139
139
|
return [
|
|
140
140
|
//{name: '_id', type: 'string', label: 'ID', default: '', operator: 'eq' },
|
|
141
|
+
{name: 'action', type: 'string', label: 'action', default: '', operator: 'eq'},
|
|
142
|
+
{name: 'entity', type: 'string', label: 'entity', default: '', operator: 'eq'},
|
|
143
|
+
{name: 'tenant.name', type: 'string', label: 'tenant', default: '', operator: 'eq'},
|
|
144
|
+
{
|
|
145
|
+
name: 'user.username',
|
|
146
|
+
type: 'string',
|
|
147
|
+
label: 'Username',
|
|
148
|
+
default: '',
|
|
149
|
+
operator:'eq'
|
|
150
|
+
},
|
|
151
|
+
{name: 'ip', type: 'string', label: 'ip', default: '', operator: 'eq'},
|
|
152
|
+
{name: 'userAgent', type: 'string', label: 'userAgent', default: '', operator: 'eq'},
|
|
141
153
|
]
|
|
142
154
|
}
|
|
143
155
|
|
|
156
|
+
get searchEnable() {
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
|
|
144
160
|
get isViewable() {
|
|
145
161
|
return true
|
|
146
162
|
}
|
|
@@ -16,6 +16,26 @@ const store = useCrudStore();
|
|
|
16
16
|
{{ formatDateTime(value) }}
|
|
17
17
|
</template>
|
|
18
18
|
|
|
19
|
+
<template v-slot:item.user="{value}">
|
|
20
|
+
{{ value.username }} ({{value.rolName}})
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<template v-slot:item.tenant="{value}">
|
|
24
|
+
{{ value?.name }}
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<template v-slot:item.changes="{value}">
|
|
28
|
+
<div v-if="value && value.length > 0" class="changes-container">
|
|
29
|
+
<div v-for="(change, index) in value" :key="index" class="change-item">
|
|
30
|
+
<span class="field-name">{{ change.field }}:</span>
|
|
31
|
+
<span class="old-value">{{ change.old }}</span>
|
|
32
|
+
<span class="arrow">→</span>
|
|
33
|
+
<span class="new-value">{{ change.new }}</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<span v-else class="no-changes">Sin cambios</span>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
19
39
|
<template v-slot:form>
|
|
20
40
|
<audit-view :audit="store.form"></audit-view>
|
|
21
41
|
</template>
|
|
@@ -25,4 +45,3 @@ const store = useCrudStore();
|
|
|
25
45
|
<style scoped>
|
|
26
46
|
|
|
27
47
|
</style>
|
|
28
|
-
|