@softdin/portal-admin 0.0.0-stage → 0.2.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.
Files changed (96) hide show
  1. package/README.md +95 -2
  2. package/dist-lib/chunks/AppLayout-COCPfLWo.js +303 -0
  3. package/dist-lib/chunks/AuditoriaView-Bcf_kOqI.js +643 -0
  4. package/dist-lib/chunks/CarnetView-BsdUtYeu.js +450 -0
  5. package/dist-lib/chunks/CarteleraView-CRnVjt-S.js +996 -0
  6. package/dist-lib/chunks/CertificadosView-DvHDflcN.js +549 -0
  7. package/dist-lib/chunks/ChatBandejaView-HqhdSw7G.js +274 -0
  8. package/dist-lib/chunks/ChatConfiguracionView-BdH5gDdR.js +716 -0
  9. package/dist-lib/chunks/ChatConocimientoView-nKTAKxFa.js +263 -0
  10. package/dist-lib/chunks/ChatConversacionView-Do74V09l.js +477 -0
  11. package/dist-lib/chunks/ChatInformeView-BIiAH9L_.js +280 -0
  12. package/dist-lib/chunks/ChatRespuestasView-QFXbyg8U.js +183 -0
  13. package/dist-lib/chunks/EmpleadosView-BtqrO5-8.js +311 -0
  14. package/dist-lib/chunks/HomologacionView-UCZk4u6R.js +949 -0
  15. package/dist-lib/chunks/IngresoSsoView-BKkcnrYO.js +98 -0
  16. package/dist-lib/chunks/SolicitudesView-CYPD2A1s.js +900 -0
  17. package/dist-lib/chunks/TiposSolicitudView-CrWTVizl.js +168 -0
  18. package/dist-lib/chunks/ToggleSwitch-BYrIyheq.js +55 -0
  19. package/dist-lib/chunks/_plugin-vue_export-helper-BOaGB7Aw.js +8 -0
  20. package/dist-lib/chunks/api-M7D1c3IT.js +97 -0
  21. package/dist-lib/chunks/apiError-BHGbwz97.js +15 -0
  22. package/dist-lib/chunks/auth.store-BrGKZg-7.js +220 -0
  23. package/dist-lib/chunks/chat.service-C7ZQf4w3.js +241 -0
  24. package/dist-lib/chunks/cliente-interno.store-De39VsEE.js +203 -0
  25. package/dist-lib/chunks/homologacion-CoeHD7cB.js +131 -0
  26. package/dist-lib/chunks/portal-empleado-6KQrnsQJ.js +120 -0
  27. package/dist-lib/chunks/tipos-D93bLfFC.js +22 -0
  28. package/dist-lib/portal-admin.js +196 -0
  29. package/dist-lib/style.css +4813 -0
  30. package/dist-lib/types/App.vue.d.ts +3 -0
  31. package/dist-lib/types/components/AppLayout.vue.d.ts +3 -0
  32. package/dist-lib/types/components/CarnetClientesInternos.vue.d.ts +11 -0
  33. package/dist-lib/types/components/InvitacionPortalModal.vue.d.ts +20 -0
  34. package/dist-lib/types/components/MenuUsuario.vue.d.ts +3 -0
  35. package/dist-lib/types/components/VistoBuenoPortalModal.vue.d.ts +23 -0
  36. package/dist-lib/types/components/auditoria/AuditoriaFila.vue.d.ts +16 -0
  37. package/dist-lib/types/components/auditoria/AuditoriaFiltros.vue.d.ts +30 -0
  38. package/dist-lib/types/components/auditoria/AuditoriaPaginador.vue.d.ts +14 -0
  39. package/dist-lib/types/components/cartelera/PaginaCarteleraImagen.vue.d.ts +14 -0
  40. package/dist-lib/types/components/cartelera/PublicacionFormModal.vue.d.ts +27 -0
  41. package/dist-lib/types/components/cartelera/VistaCelularCartelera.vue.d.ts +34 -0
  42. package/dist-lib/types/components/certificados/AtenderCertificadoModal.vue.d.ts +22 -0
  43. package/dist-lib/types/components/certificados/RechazarCertificadoModal.vue.d.ts +18 -0
  44. package/dist-lib/types/components/certificados/ResumenSolicitudCertificado.vue.d.ts +8 -0
  45. package/dist-lib/types/components/chat/ChatClientesInternos.vue.d.ts +21 -0
  46. package/dist-lib/types/components/chat/ChatHorarioCampos.vue.d.ts +21 -0
  47. package/dist-lib/types/components/forms/ExportExcelButton.vue.d.ts +30 -0
  48. package/dist-lib/types/components/forms/ToggleSwitch.vue.d.ts +17 -0
  49. package/dist-lib/types/components/homologacion/HomologacionFila.vue.d.ts +30 -0
  50. package/dist-lib/types/components/homologacion/TipoPermisoFormModal.vue.d.ts +26 -0
  51. package/dist-lib/types/components/homologacion/TipoSolicitudFormModal.vue.d.ts +27 -0
  52. package/dist-lib/types/lib/auditoria.d.ts +25 -0
  53. package/dist-lib/types/lib/cartelera-paginas.d.ts +26 -0
  54. package/dist-lib/types/lib/cartelera-vigencia.d.ts +3 -0
  55. package/dist-lib/types/lib/export-excel-csv.d.ts +4 -0
  56. package/dist-lib/types/lib/export-excel.d.ts +9 -0
  57. package/dist-lib/types/lib/exportar-listado.d.ts +68 -0
  58. package/dist-lib/types/lib/homologacion.d.ts +41 -0
  59. package/dist-lib/types/lib/portal-empleado.d.ts +32 -0
  60. package/dist-lib/types/modulo.d.ts +31 -0
  61. package/dist-lib/types/portal-admin/config.d.ts +17 -0
  62. package/dist-lib/types/portal-admin/rutas.d.ts +17 -0
  63. package/dist-lib/types/portal-admin/tipos.d.ts +68 -0
  64. package/dist-lib/types/router/index.d.ts +7 -0
  65. package/dist-lib/types/services/api.d.ts +29 -0
  66. package/dist-lib/types/services/auditoria.service.d.ts +63 -0
  67. package/dist-lib/types/services/auth.service.d.ts +61 -0
  68. package/dist-lib/types/services/carnet.service.d.ts +56 -0
  69. package/dist-lib/types/services/cartelera.service.d.ts +69 -0
  70. package/dist-lib/types/services/chat.service.d.ts +279 -0
  71. package/dist-lib/types/services/configuracion-cliente-interno.service.d.ts +23 -0
  72. package/dist-lib/types/services/homologacion.service.d.ts +105 -0
  73. package/dist-lib/types/services/portal-empleado.service.d.ts +112 -0
  74. package/dist-lib/types/services/solicitudes-certificado.service.d.ts +59 -0
  75. package/dist-lib/types/stores/auth.store.d.ts +125 -0
  76. package/dist-lib/types/stores/cliente-interno.store.d.ts +65 -0
  77. package/dist-lib/types/utils/apiError.d.ts +4 -0
  78. package/dist-lib/types/views/AuditoriaView.vue.d.ts +3 -0
  79. package/dist-lib/types/views/CarnetView.vue.d.ts +3 -0
  80. package/dist-lib/types/views/CarteleraView.vue.d.ts +3 -0
  81. package/dist-lib/types/views/CertificadosView.vue.d.ts +3 -0
  82. package/dist-lib/types/views/EmpleadosView.vue.d.ts +3 -0
  83. package/dist-lib/types/views/HomologacionView.vue.d.ts +4 -0
  84. package/dist-lib/types/views/IngresoSsoView.vue.d.ts +3 -0
  85. package/dist-lib/types/views/LoginView.vue.d.ts +3 -0
  86. package/dist-lib/types/views/OlvideView.vue.d.ts +3 -0
  87. package/dist-lib/types/views/RestablecerView.vue.d.ts +3 -0
  88. package/dist-lib/types/views/SolicitudesView.vue.d.ts +3 -0
  89. package/dist-lib/types/views/TiposSolicitudView.vue.d.ts +3 -0
  90. package/dist-lib/types/views/chat/ChatBandejaView.vue.d.ts +3 -0
  91. package/dist-lib/types/views/chat/ChatConfiguracionView.vue.d.ts +3 -0
  92. package/dist-lib/types/views/chat/ChatConocimientoView.vue.d.ts +3 -0
  93. package/dist-lib/types/views/chat/ChatConversacionView.vue.d.ts +11 -0
  94. package/dist-lib/types/views/chat/ChatInformeView.vue.d.ts +3 -0
  95. package/dist-lib/types/views/chat/ChatRespuestasView.vue.d.ts +3 -0
  96. package/package.json +66 -4
@@ -0,0 +1,643 @@
1
+ import { t as api } from "./api-M7D1c3IT.js";
2
+ import { t as useAuthStore } from "./auth.store-BrGKZg-7.js";
3
+ import { t as useClienteInternoStore } from "./cliente-interno.store-De39VsEE.js";
4
+ import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BOaGB7Aw.js";
5
+ import { t as mensajeError } from "./apiError-BHGbwz97.js";
6
+ import axios from "axios";
7
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, onMounted, openBlock, ref, renderList, toDisplayString, unref, useModel, vModelSelect, watch, withDirectives, withModifiers } from "vue";
8
+ //#region src/lib/auditoria.ts
9
+ /** Textos de la auditoría del portal, como los ve RRHH. */
10
+ var ACTORES_AUDITORIA = {
11
+ empleado: "Empleado",
12
+ rrhh: "RRHH",
13
+ conductor: "Conductor",
14
+ softdin: "SoftDIN",
15
+ anonimo: "Sin identificar"
16
+ };
17
+ var RESULTADOS_AUDITORIA = {
18
+ exito: {
19
+ texto: "Éxito",
20
+ clase: "estado-ok"
21
+ },
22
+ rechazado: {
23
+ texto: "Rechazado",
24
+ clase: "estado-espera"
25
+ },
26
+ fallo: {
27
+ texto: "Fallo",
28
+ clase: "estado-error"
29
+ }
30
+ };
31
+ /** Fecha local `YYYY-MM-DD` (sin pasar por UTC, que en Colombia corre el día por la noche). */
32
+ function fechaIso(d) {
33
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
34
+ }
35
+ /** Por defecto: los últimos 7 días contando hoy. */
36
+ function rangoPorDefecto(hoy = /* @__PURE__ */ new Date()) {
37
+ return {
38
+ desde: fechaIso(new Date(hoy.getFullYear(), hoy.getMonth(), hoy.getDate() - 6)),
39
+ hasta: fechaIso(hoy)
40
+ };
41
+ }
42
+ /** Mismo límite que el servidor: el rango no puede pasar de un año. */
43
+ function errorRango(desde, hasta) {
44
+ if (!desde || !hasta) return "Indique desde y hasta qué fecha.";
45
+ if (desde > hasta) return "La fecha inicial no puede ser posterior a la final.";
46
+ const [a, m, d] = desde.split("-").map(Number);
47
+ if (!a || !m || !d) return "Fecha inicial no válida.";
48
+ if (hasta > fechaIso(new Date(a + 1, m - 1, d))) return "El rango no puede pasar de un año.";
49
+ return null;
50
+ }
51
+ /**
52
+ * `2026-10-09 14:05:33.120` o `2026-10-09T14:05:33-05:00` → `09/10/2026 14:05:33`. Se lee el texto
53
+ * tal cual (ya viene en hora de Colombia): convertir con Date lo movería a la zona del navegador.
54
+ */
55
+ function fechaHora(valor) {
56
+ if (!valor) return "—";
57
+ const m = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?/.exec(valor);
58
+ if (!m) return valor;
59
+ return `${m[3]}/${m[2]}/${m[1]} ${m[4]}:${m[5]}${m[6] ? `:${m[6]}` : ""}`;
60
+ }
61
+ /** Guarda un Blob como archivo con el nombre dado. */
62
+ function descargarArchivo(blob, nombre) {
63
+ const url = URL.createObjectURL(blob);
64
+ const a = document.createElement("a");
65
+ a.href = url;
66
+ a.download = nombre;
67
+ document.body.appendChild(a);
68
+ a.click();
69
+ document.body.removeChild(a);
70
+ URL.revokeObjectURL(url);
71
+ }
72
+ /** El detalle saneado, legible: cada clave en su línea; objetos anidados como JSON con sangría. */
73
+ function detalleLegible(detalle) {
74
+ if (detalle === null || detalle === void 0) return [];
75
+ if (typeof detalle !== "object" || Array.isArray(detalle)) return [["Detalle", formatear(detalle)]];
76
+ return Object.entries(detalle).map(([k, v]) => [k, formatear(v)]);
77
+ }
78
+ function formatear(v) {
79
+ if (v === null || v === void 0) return "—";
80
+ if (typeof v === "string") return v;
81
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
82
+ return JSON.stringify(v, null, 2);
83
+ }
84
+ //#endregion
85
+ //#region src/components/auditoria/AuditoriaFiltros.vue?vue&type=script&setup=true&lang.ts
86
+ var _hoisted_1$3 = { class: "campo" };
87
+ var _hoisted_2$3 = ["value"];
88
+ var _hoisted_3$3 = ["value"];
89
+ var _hoisted_4$3 = { class: "campo" };
90
+ var _hoisted_5$3 = ["value"];
91
+ var _hoisted_6$2 = ["value"];
92
+ var _hoisted_7$2 = { class: "campo" };
93
+ var _hoisted_8$2 = ["value"];
94
+ var _hoisted_9$2 = ["value"];
95
+ var _hoisted_10$2 = { class: "campo" };
96
+ var _hoisted_11$2 = ["value"];
97
+ var _hoisted_12$2 = { class: "campo" };
98
+ var _hoisted_13$2 = ["value"];
99
+ var _hoisted_14$1 = { class: "campo campo-busqueda" };
100
+ var _hoisted_15 = { class: "busqueda" };
101
+ var _hoisted_16 = ["value"];
102
+ var _hoisted_17 = ["disabled"];
103
+ //#endregion
104
+ //#region src/components/auditoria/AuditoriaFiltros.vue
105
+ var AuditoriaFiltros_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
106
+ __name: "AuditoriaFiltros",
107
+ props: /*@__PURE__*/ mergeModels({
108
+ acciones: {},
109
+ deshabilitado: { type: Boolean }
110
+ }, {
111
+ "modelValue": { required: true },
112
+ "modelModifiers": {}
113
+ }),
114
+ emits: /*@__PURE__*/ mergeModels(["buscar"], ["update:modelValue"]),
115
+ setup(__props, { emit: __emit }) {
116
+ const emit = __emit;
117
+ const modelo = useModel(__props, "modelValue");
118
+ function poner(clave, valor) {
119
+ modelo.value = {
120
+ ...modelo.value,
121
+ [clave]: valor
122
+ };
123
+ }
124
+ const valorDe = (e) => e.target.value;
125
+ return (_ctx, _cache) => {
126
+ return openBlock(), createElementBlock("form", {
127
+ class: "filtros",
128
+ role: "search",
129
+ "aria-label": "Filtros de la auditoría",
130
+ onSubmit: _cache[6] || (_cache[6] = withModifiers(($event) => emit("buscar"), ["prevent"]))
131
+ }, [
132
+ createElementVNode("div", _hoisted_1$3, [_cache[8] || (_cache[8] = createElementVNode("label", {
133
+ for: "aud-actor",
134
+ class: "label"
135
+ }, "Tipo de actor", -1)), createElementVNode("select", {
136
+ id: "aud-actor",
137
+ class: "input",
138
+ value: modelo.value.actor_tipo,
139
+ onChange: _cache[0] || (_cache[0] = ($event) => poner("actor_tipo", valorDe($event)))
140
+ }, [_cache[7] || (_cache[7] = createElementVNode("option", { value: "" }, "Todos", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(ACTORES_AUDITORIA), (texto, clave) => {
141
+ return openBlock(), createElementBlock("option", {
142
+ key: clave,
143
+ value: clave
144
+ }, toDisplayString(texto), 9, _hoisted_3$3);
145
+ }), 128))], 40, _hoisted_2$3)]),
146
+ createElementVNode("div", _hoisted_4$3, [_cache[10] || (_cache[10] = createElementVNode("label", {
147
+ for: "aud-accion",
148
+ class: "label"
149
+ }, "Acción", -1)), createElementVNode("select", {
150
+ id: "aud-accion",
151
+ class: "input",
152
+ value: modelo.value.accion,
153
+ onChange: _cache[1] || (_cache[1] = ($event) => poner("accion", valorDe($event)))
154
+ }, [_cache[9] || (_cache[9] = createElementVNode("option", { value: "" }, "Todas", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.acciones, (a) => {
155
+ return openBlock(), createElementBlock("option", {
156
+ key: a.clave,
157
+ value: a.clave
158
+ }, toDisplayString(a.etiqueta), 9, _hoisted_6$2);
159
+ }), 128))], 40, _hoisted_5$3)]),
160
+ createElementVNode("div", _hoisted_7$2, [_cache[12] || (_cache[12] = createElementVNode("label", {
161
+ for: "aud-resultado",
162
+ class: "label"
163
+ }, "Resultado", -1)), createElementVNode("select", {
164
+ id: "aud-resultado",
165
+ class: "input",
166
+ value: modelo.value.resultado,
167
+ onChange: _cache[2] || (_cache[2] = ($event) => poner("resultado", valorDe($event)))
168
+ }, [_cache[11] || (_cache[11] = createElementVNode("option", { value: "" }, "Todos", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(RESULTADOS_AUDITORIA), (r, clave) => {
169
+ return openBlock(), createElementBlock("option", {
170
+ key: clave,
171
+ value: clave
172
+ }, toDisplayString(r.texto), 9, _hoisted_9$2);
173
+ }), 128))], 40, _hoisted_8$2)]),
174
+ createElementVNode("div", _hoisted_10$2, [_cache[13] || (_cache[13] = createElementVNode("label", {
175
+ for: "aud-desde",
176
+ class: "label"
177
+ }, "Desde", -1)), createElementVNode("input", {
178
+ id: "aud-desde",
179
+ type: "date",
180
+ class: "input",
181
+ value: modelo.value.desde,
182
+ onChange: _cache[3] || (_cache[3] = ($event) => poner("desde", valorDe($event)))
183
+ }, null, 40, _hoisted_11$2)]),
184
+ createElementVNode("div", _hoisted_12$2, [_cache[14] || (_cache[14] = createElementVNode("label", {
185
+ for: "aud-hasta",
186
+ class: "label"
187
+ }, "Hasta", -1)), createElementVNode("input", {
188
+ id: "aud-hasta",
189
+ type: "date",
190
+ class: "input",
191
+ value: modelo.value.hasta,
192
+ onChange: _cache[4] || (_cache[4] = ($event) => poner("hasta", valorDe($event)))
193
+ }, null, 40, _hoisted_13$2)]),
194
+ createElementVNode("div", _hoisted_14$1, [_cache[15] || (_cache[15] = createElementVNode("label", {
195
+ for: "aud-q",
196
+ class: "label"
197
+ }, "Buscar", -1)), createElementVNode("div", _hoisted_15, [createElementVNode("input", {
198
+ id: "aud-q",
199
+ type: "search",
200
+ class: "input",
201
+ placeholder: "Actor, acción o recurso",
202
+ value: modelo.value.q,
203
+ onInput: _cache[5] || (_cache[5] = ($event) => poner("q", valorDe($event)))
204
+ }, null, 40, _hoisted_16), createElementVNode("button", {
205
+ type: "submit",
206
+ class: "btn-primary",
207
+ disabled: __props.deshabilitado
208
+ }, "Buscar", 8, _hoisted_17)])])
209
+ ], 32);
210
+ };
211
+ }
212
+ }), [["__scopeId", "data-v-8ea2bbf3"]]);
213
+ //#endregion
214
+ //#region src/components/auditoria/AuditoriaFila.vue?vue&type=script&setup=true&lang.ts
215
+ var _hoisted_1$2 = {
216
+ class: "nowrap",
217
+ "data-label": "Fecha y hora"
218
+ };
219
+ var _hoisted_2$2 = { "data-label": "Actor" };
220
+ var _hoisted_3$2 = { class: "sub" };
221
+ var _hoisted_4$2 = { "data-label": "Cliente" };
222
+ var _hoisted_5$2 = { "data-label": "Acción" };
223
+ var _hoisted_6$1 = { "data-label": "Recurso" };
224
+ var _hoisted_7$1 = { "data-label": "Resultado" };
225
+ var _hoisted_8$1 = {
226
+ key: 0,
227
+ class: "sub"
228
+ };
229
+ var _hoisted_9$1 = {
230
+ class: "nowrap",
231
+ "data-label": "IP"
232
+ };
233
+ var _hoisted_10$1 = ["aria-expanded", "aria-controls"];
234
+ var _hoisted_11$1 = ["id"];
235
+ var _hoisted_12$1 = { colspan: "8" };
236
+ var _hoisted_13$1 = { class: "detalle" };
237
+ var _hoisted_14 = {
238
+ key: 0,
239
+ class: "sub"
240
+ };
241
+ //#endregion
242
+ //#region src/components/auditoria/AuditoriaFila.vue
243
+ var AuditoriaFila_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
244
+ __name: "AuditoriaFila",
245
+ props: {
246
+ registro: {},
247
+ expandido: { type: Boolean }
248
+ },
249
+ emits: ["alternar"],
250
+ setup(__props, { emit: __emit }) {
251
+ const props = __props;
252
+ const emit = __emit;
253
+ const r = computed(() => props.registro);
254
+ const idDetalle = computed(() => `aud-detalle-${props.registro.id}`);
255
+ const resultado = computed(() => RESULTADOS_AUDITORIA[r.value.resultado]);
256
+ const recurso = computed(() => {
257
+ const rec = r.value.recurso;
258
+ if (!rec || rec.tipo == null && rec.id == null) return "—";
259
+ return [rec.tipo, rec.id != null ? `#${rec.id}` : null].filter(Boolean).join(" ");
260
+ });
261
+ const detalle = computed(() => detalleLegible(r.value.detalle));
262
+ return (_ctx, _cache) => {
263
+ return openBlock(), createElementBlock(Fragment, null, [createElementVNode("tr", null, [
264
+ createElementVNode("td", _hoisted_1$2, toDisplayString(unref(fechaHora)(r.value.ocurrido_en)), 1),
265
+ createElementVNode("td", _hoisted_2$2, [createTextVNode(toDisplayString(r.value.actor.nombre ?? "—") + " ", 1), createElementVNode("span", _hoisted_3$2, [createTextVNode(toDisplayString(unref(ACTORES_AUDITORIA)[r.value.actor.tipo] ?? r.value.actor.tipo), 1), r.value.actor.identificacion ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" · " + toDisplayString(r.value.actor.identificacion), 1)], 64)) : createCommentVNode("", true)])]),
266
+ createElementVNode("td", _hoisted_4$2, toDisplayString(r.value.cliente_interno?.nombre ?? "—"), 1),
267
+ createElementVNode("td", _hoisted_5$2, toDisplayString(r.value.etiqueta || r.value.accion), 1),
268
+ createElementVNode("td", _hoisted_6$1, toDisplayString(recurso.value), 1),
269
+ createElementVNode("td", _hoisted_7$1, [createElementVNode("span", { class: normalizeClass(["estado", resultado.value?.clase]) }, toDisplayString(resultado.value?.texto ?? r.value.resultado), 3), r.value.estado_http ? (openBlock(), createElementBlock("span", _hoisted_8$1, "HTTP " + toDisplayString(r.value.estado_http), 1)) : createCommentVNode("", true)]),
270
+ createElementVNode("td", _hoisted_9$1, toDisplayString(r.value.ip ?? "—"), 1),
271
+ createElementVNode("td", null, [createElementVNode("button", {
272
+ type: "button",
273
+ class: "btn-link",
274
+ "aria-expanded": __props.expandido,
275
+ "aria-controls": idDetalle.value,
276
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("alternar"))
277
+ }, toDisplayString(__props.expandido ? "Ocultar detalle" : "Ver detalle"), 9, _hoisted_10$1)])
278
+ ]), __props.expandido ? (openBlock(), createElementBlock("tr", {
279
+ key: 0,
280
+ id: idDetalle.value,
281
+ class: "fila-detalle"
282
+ }, [createElementVNode("td", _hoisted_12$1, [createElementVNode("dl", _hoisted_13$1, [
283
+ _cache[1] || (_cache[1] = createElementVNode("dt", null, "Acción (clave)", -1)),
284
+ createElementVNode("dd", null, toDisplayString(r.value.accion), 1),
285
+ _cache[2] || (_cache[2] = createElementVNode("dt", null, "Navegador", -1)),
286
+ createElementVNode("dd", null, toDisplayString(r.value.user_agent ?? "—"), 1),
287
+ (openBlock(true), createElementBlock(Fragment, null, renderList(detalle.value, ([clave, valor]) => {
288
+ return openBlock(), createElementBlock(Fragment, { key: clave }, [createElementVNode("dt", null, toDisplayString(clave), 1), createElementVNode("dd", null, toDisplayString(valor), 1)], 64);
289
+ }), 128))
290
+ ]), detalle.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_14, "Sin parámetros registrados.")) : createCommentVNode("", true)])], 8, _hoisted_11$1)) : createCommentVNode("", true)], 64);
291
+ };
292
+ }
293
+ }), [["__scopeId", "data-v-d778f13a"]]);
294
+ //#endregion
295
+ //#region src/components/auditoria/AuditoriaPaginador.vue?vue&type=script&setup=true&lang.ts
296
+ var _hoisted_1$1 = {
297
+ class: "paginador",
298
+ "aria-label": "Paginación de la auditoría"
299
+ };
300
+ var _hoisted_2$1 = {
301
+ class: "paginador-texto",
302
+ "aria-live": "polite"
303
+ };
304
+ var _hoisted_3$1 = { class: "paginador-botones" };
305
+ var _hoisted_4$1 = ["disabled"];
306
+ var _hoisted_5$1 = ["disabled"];
307
+ //#endregion
308
+ //#region src/components/auditoria/AuditoriaPaginador.vue
309
+ var AuditoriaPaginador_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
310
+ __name: "AuditoriaPaginador",
311
+ props: {
312
+ pagina: {},
313
+ porPagina: {},
314
+ total: {},
315
+ deshabilitado: { type: Boolean }
316
+ },
317
+ emits: ["cambiar"],
318
+ setup(__props, { emit: __emit }) {
319
+ const props = __props;
320
+ const emit = __emit;
321
+ const paginas = computed(() => Math.max(1, Math.ceil(props.total / Math.max(1, props.porPagina))));
322
+ const desde = computed(() => props.total === 0 ? 0 : (props.pagina - 1) * props.porPagina + 1);
323
+ const hasta = computed(() => Math.min(props.total, props.pagina * props.porPagina));
324
+ const fmt = (n) => n.toLocaleString("es-CO");
325
+ return (_ctx, _cache) => {
326
+ return openBlock(), createElementBlock("nav", _hoisted_1$1, [createElementVNode("p", _hoisted_2$1, " Mostrando " + toDisplayString(fmt(desde.value)) + "–" + toDisplayString(fmt(hasta.value)) + " de " + toDisplayString(fmt(__props.total)), 1), createElementVNode("div", _hoisted_3$1, [
327
+ createElementVNode("button", {
328
+ type: "button",
329
+ class: "btn-secondary",
330
+ disabled: __props.deshabilitado || __props.pagina <= 1,
331
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("cambiar", __props.pagina - 1))
332
+ }, " Anterior ", 8, _hoisted_4$1),
333
+ createElementVNode("span", null, "Página " + toDisplayString(__props.pagina) + " de " + toDisplayString(paginas.value), 1),
334
+ createElementVNode("button", {
335
+ type: "button",
336
+ class: "btn-secondary",
337
+ disabled: __props.deshabilitado || __props.pagina >= paginas.value,
338
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("cambiar", __props.pagina + 1))
339
+ }, " Siguiente ", 8, _hoisted_5$1)
340
+ ])]);
341
+ };
342
+ }
343
+ }), [["__scopeId", "data-v-6a295c77"]]);
344
+ //#endregion
345
+ //#region src/services/auditoria.service.ts
346
+ /** Solo los filtros con valor: el servidor no recibe `accion=` vacíos. */
347
+ function parametrosAuditoria(f) {
348
+ const p = {};
349
+ for (const [clave, valor] of Object.entries(f)) {
350
+ if (valor === null || valor === void 0) continue;
351
+ const v = typeof valor === "string" ? valor.trim() : valor;
352
+ if (v === "") continue;
353
+ p[clave] = v;
354
+ }
355
+ return p;
356
+ }
357
+ /** Con `responseType: 'blob'` el cuerpo de un error también llega como Blob: se vuelve JSON para leer su mensaje. */
358
+ async function errorLegible(e) {
359
+ if (axios.isAxiosError(e) && e.response && e.response.data instanceof Blob) try {
360
+ e.response.data = JSON.parse(await e.response.data.text());
361
+ } catch {
362
+ e.response.data = void 0;
363
+ }
364
+ return e;
365
+ }
366
+ var auditoriaService = {
367
+ async listar(filtros, pagina = 1, porPagina = 25) {
368
+ const { data } = await api.get("/auditoria", { params: {
369
+ ...parametrosAuditoria(filtros),
370
+ page: pagina,
371
+ por_pagina: porPagina
372
+ } });
373
+ return {
374
+ data: data.data ?? [],
375
+ meta: data.meta ?? {
376
+ pagina,
377
+ por_pagina: porPagina,
378
+ total: 0
379
+ },
380
+ acciones: data.acciones ?? []
381
+ };
382
+ },
383
+ /** CSV UTF-8 con BOM (abre bien en Excel), con los mismos filtros del listado. */
384
+ async exportar(filtros) {
385
+ try {
386
+ const { data } = await api.get("/auditoria/exportar", {
387
+ params: parametrosAuditoria(filtros),
388
+ responseType: "blob"
389
+ });
390
+ return data;
391
+ } catch (e) {
392
+ throw await errorLegible(e);
393
+ }
394
+ }
395
+ };
396
+ //#endregion
397
+ //#region src/views/AuditoriaView.vue?vue&type=script&setup=true&lang.ts
398
+ var _hoisted_1 = { class: "page-shell" };
399
+ var _hoisted_2 = { class: "page-subtitle" };
400
+ var _hoisted_3 = {
401
+ key: 0,
402
+ class: "panel-muted",
403
+ role: "status"
404
+ };
405
+ var _hoisted_4 = { class: "toolbar" };
406
+ var _hoisted_5 = ["value"];
407
+ var _hoisted_6 = ["disabled"];
408
+ var _hoisted_7 = ["disabled"];
409
+ var _hoisted_8 = {
410
+ key: 0,
411
+ class: "panel-error",
412
+ role: "alert"
413
+ };
414
+ var _hoisted_9 = {
415
+ key: 1,
416
+ class: "panel-error",
417
+ role: "alert"
418
+ };
419
+ var _hoisted_10 = {
420
+ key: 2,
421
+ class: "panel-muted",
422
+ role: "status"
423
+ };
424
+ var _hoisted_11 = {
425
+ key: 3,
426
+ class: "panel-muted"
427
+ };
428
+ var _hoisted_12 = ["aria-busy"];
429
+ var _hoisted_13 = { class: "data-table" };
430
+ //#endregion
431
+ //#region src/views/AuditoriaView.vue
432
+ var AuditoriaView_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
433
+ __name: "AuditoriaView",
434
+ setup(__props) {
435
+ const auth = useAuthStore();
436
+ const clientesInternos = useClienteInternoStore();
437
+ const POR_PAGINA = [
438
+ 25,
439
+ 50,
440
+ 100
441
+ ];
442
+ const filtros = ref({
443
+ actor_tipo: "",
444
+ accion: "",
445
+ resultado: "",
446
+ ...rangoPorDefecto(),
447
+ q: ""
448
+ });
449
+ /** La búsqueda de texto se aplica al pulsar «Buscar», no con cada tecla. */
450
+ const qAplicada = ref("");
451
+ const pagina = ref(1);
452
+ const porPagina = ref(25);
453
+ const items = ref([]);
454
+ const total = ref(0);
455
+ const acciones = ref([]);
456
+ const expandidos = ref(/* @__PURE__ */ new Set());
457
+ const loading = ref(false);
458
+ const error = ref(null);
459
+ const exportando = ref(false);
460
+ const errorExportar = ref(null);
461
+ const rangoInvalido = computed(() => errorRango(filtros.value.desde, filtros.value.hasta));
462
+ function consulta() {
463
+ const f = filtros.value;
464
+ return {
465
+ cliente_interno_id: clientesInternos.seleccionado,
466
+ actor_tipo: f.actor_tipo,
467
+ accion: f.accion,
468
+ resultado: f.resultado,
469
+ desde: f.desde,
470
+ hasta: f.hasta,
471
+ q: qAplicada.value
472
+ };
473
+ }
474
+ function mensaje(e, porDefecto) {
475
+ if (axios.isAxiosError(e) && e.response?.status === 403) return "Solo un administrador del cliente puede consultar la auditoría.";
476
+ return mensajeError(e, porDefecto);
477
+ }
478
+ /** Cada consulta lleva su número: si llega tarde una anterior, se descarta. */
479
+ let ultima = 0;
480
+ async function cargar() {
481
+ if (!auth.esAdmin) return;
482
+ if (rangoInvalido.value) {
483
+ error.value = rangoInvalido.value;
484
+ items.value = [];
485
+ total.value = 0;
486
+ return;
487
+ }
488
+ const n = ++ultima;
489
+ loading.value = true;
490
+ error.value = null;
491
+ try {
492
+ const r = await auditoriaService.listar(consulta(), pagina.value, porPagina.value);
493
+ if (n !== ultima) return;
494
+ items.value = r.data;
495
+ total.value = r.meta.total;
496
+ if (r.acciones.length > 0) acciones.value = r.acciones;
497
+ expandidos.value = /* @__PURE__ */ new Set();
498
+ } catch (e) {
499
+ if (n !== ultima) return;
500
+ error.value = mensaje(e, "No se pudo cargar la auditoría.");
501
+ items.value = [];
502
+ total.value = 0;
503
+ } finally {
504
+ if (n === ultima) loading.value = false;
505
+ }
506
+ }
507
+ function desdeElPrincipio() {
508
+ pagina.value = 1;
509
+ cargar();
510
+ }
511
+ function buscar() {
512
+ qAplicada.value = filtros.value.q.trim();
513
+ desdeElPrincipio();
514
+ }
515
+ function irA(p) {
516
+ pagina.value = p;
517
+ cargar();
518
+ }
519
+ function alternar(id) {
520
+ const s = new Set(expandidos.value);
521
+ if (s.has(id)) s.delete(id);
522
+ else s.add(id);
523
+ expandidos.value = s;
524
+ }
525
+ watch([
526
+ () => filtros.value.actor_tipo,
527
+ () => filtros.value.accion,
528
+ () => filtros.value.resultado,
529
+ () => filtros.value.desde,
530
+ () => filtros.value.hasta,
531
+ porPagina,
532
+ () => clientesInternos.seleccionado
533
+ ], desdeElPrincipio);
534
+ onMounted(() => void cargar());
535
+ async function exportar() {
536
+ if (exportando.value || rangoInvalido.value) return;
537
+ exportando.value = true;
538
+ errorExportar.value = null;
539
+ try {
540
+ descargarArchivo(await auditoriaService.exportar(consulta()), `auditoria-portal-${filtros.value.desde}-a-${filtros.value.hasta}.csv`);
541
+ } catch (e) {
542
+ errorExportar.value = mensaje(e, "No se pudo exportar la auditoría.");
543
+ } finally {
544
+ exportando.value = false;
545
+ }
546
+ }
547
+ return (_ctx, _cache) => {
548
+ return openBlock(), createElementBlock("section", _hoisted_1, [createElementVNode("header", null, [_cache[4] || (_cache[4] = createElementVNode("h1", { class: "page-title" }, "Auditoría", -1)), createElementVNode("p", _hoisted_2, [
549
+ _cache[2] || (_cache[2] = createTextVNode(" Acciones registradas en el portal: ingresos, solicitudes, descargas de documentos y cambios hechos por empleados, conductores y RRHH", -1)),
550
+ unref(clientesInternos).seleccionado !== null ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" de " + toDisplayString(unref(clientesInternos).nombre(unref(clientesInternos).seleccionado)), 1)], 64)) : createCommentVNode("", true),
551
+ _cache[3] || (_cache[3] = createTextVNode(". Es un registro de solo lectura: no se puede modificar ni borrar. ", -1))
552
+ ])]), !unref(auth).esAdmin ? (openBlock(), createElementBlock("p", _hoisted_3, " Solo un administrador del cliente puede consultar la auditoría. ")) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
553
+ createVNode(AuditoriaFiltros_default, {
554
+ modelValue: filtros.value,
555
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => filtros.value = $event),
556
+ acciones: acciones.value,
557
+ deshabilitado: loading.value,
558
+ onBuscar: buscar
559
+ }, null, 8, [
560
+ "modelValue",
561
+ "acciones",
562
+ "deshabilitado"
563
+ ]),
564
+ createElementVNode("div", _hoisted_4, [
565
+ _cache[5] || (_cache[5] = createElementVNode("label", {
566
+ for: "aud-por-pagina",
567
+ class: "label"
568
+ }, "Por página", -1)),
569
+ withDirectives(createElementVNode("select", {
570
+ id: "aud-por-pagina",
571
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => porPagina.value = $event),
572
+ class: "input",
573
+ style: { "width": "auto" }
574
+ }, [(openBlock(), createElementBlock(Fragment, null, renderList(POR_PAGINA, (n) => {
575
+ return createElementVNode("option", {
576
+ key: n,
577
+ value: n
578
+ }, toDisplayString(n), 9, _hoisted_5);
579
+ }), 64))], 512), [[
580
+ vModelSelect,
581
+ porPagina.value,
582
+ void 0,
583
+ { number: true }
584
+ ]]),
585
+ createElementVNode("button", {
586
+ type: "button",
587
+ class: "btn-secondary",
588
+ disabled: exportando.value || loading.value || total.value === 0 || rangoInvalido.value !== null,
589
+ onClick: exportar
590
+ }, toDisplayString(exportando.value ? "Exportando…" : "Exportar a Excel"), 9, _hoisted_6),
591
+ createElementVNode("button", {
592
+ type: "button",
593
+ class: "btn-secondary",
594
+ disabled: loading.value,
595
+ onClick: cargar
596
+ }, "Actualizar", 8, _hoisted_7)
597
+ ]),
598
+ errorExportar.value ? (openBlock(), createElementBlock("p", _hoisted_8, toDisplayString(errorExportar.value), 1)) : createCommentVNode("", true),
599
+ error.value ? (openBlock(), createElementBlock("p", _hoisted_9, toDisplayString(error.value), 1)) : loading.value && items.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_10, "Cargando…")) : items.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_11, "No hay registros con estos filtros.")) : (openBlock(), createElementBlock(Fragment, { key: 4 }, [createElementVNode("div", {
600
+ class: "table-wrap",
601
+ "aria-busy": loading.value
602
+ }, [createElementVNode("table", _hoisted_13, [
603
+ _cache[6] || (_cache[6] = createElementVNode("caption", { class: "sr-only" }, "Registros de auditoría del portal", -1)),
604
+ _cache[7] || (_cache[7] = createElementVNode("thead", null, [createElementVNode("tr", null, [
605
+ createElementVNode("th", { scope: "col" }, "Fecha y hora"),
606
+ createElementVNode("th", { scope: "col" }, "Actor"),
607
+ createElementVNode("th", { scope: "col" }, "Cliente"),
608
+ createElementVNode("th", { scope: "col" }, "Acción"),
609
+ createElementVNode("th", { scope: "col" }, "Recurso"),
610
+ createElementVNode("th", { scope: "col" }, "Resultado"),
611
+ createElementVNode("th", { scope: "col" }, "IP"),
612
+ createElementVNode("th", { scope: "col" }, [createElementVNode("span", { class: "sr-only" }, "Detalle")])
613
+ ])], -1)),
614
+ createElementVNode("tbody", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(items.value, (r) => {
615
+ return openBlock(), createBlock(AuditoriaFila_default, {
616
+ key: r.id,
617
+ registro: r,
618
+ expandido: expandidos.value.has(r.id),
619
+ onAlternar: ($event) => alternar(r.id)
620
+ }, null, 8, [
621
+ "registro",
622
+ "expandido",
623
+ "onAlternar"
624
+ ]);
625
+ }), 128))])
626
+ ])], 8, _hoisted_12), createVNode(AuditoriaPaginador_default, {
627
+ pagina: pagina.value,
628
+ "por-pagina": porPagina.value,
629
+ total: total.value,
630
+ deshabilitado: loading.value,
631
+ onCambiar: irA
632
+ }, null, 8, [
633
+ "pagina",
634
+ "por-pagina",
635
+ "total",
636
+ "deshabilitado"
637
+ ])], 64))
638
+ ], 64))]);
639
+ };
640
+ }
641
+ }), [["__scopeId", "data-v-6314ccc5"]]);
642
+ //#endregion
643
+ export { AuditoriaView_default as default };