@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,900 @@
1
+ import { r as portalSolicitudesService, t as useClienteInternoStore } from "./cliente-interno.store-De39VsEE.js";
2
+ import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BOaGB7Aw.js";
3
+ import { t as mensajeError } from "./apiError-BHGbwz97.js";
4
+ import { a as estadoSoporte, i as esTipoConSoporte, o as fechaCorta, r as datosNoClinicos, s as nombreTipoSolicitud, t as ESTADOS_SOLICITUD_PORTAL } from "./portal-empleado-6KQrnsQJ.js";
5
+ import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, toDisplayString, unref, useModel, vModelSelect, vModelText, watch, withDirectives } from "vue";
6
+ import FormCloseButton from "@softdin/shared/components/forms/FormCloseButton.vue";
7
+ import FormHelpButton from "@softdin/shared/components/forms/FormHelpButton.vue";
8
+ import FormHelpModal from "@softdin/shared/components/forms/FormHelpModal.vue";
9
+ //#region src/components/forms/ExportExcelButton.vue?vue&type=script&setup=true&lang.ts
10
+ var _hoisted_1$2 = [
11
+ "data-tooltip",
12
+ "aria-label",
13
+ "aria-expanded",
14
+ "disabled"
15
+ ];
16
+ var _hoisted_2$2 = {
17
+ key: 0,
18
+ viewBox: "0 0 24 24",
19
+ "aria-hidden": "true",
20
+ class: "export-spinner"
21
+ };
22
+ var _hoisted_3$2 = {
23
+ key: 1,
24
+ viewBox: "0 0 24 24",
25
+ "aria-hidden": "true",
26
+ class: "export-btn-icono"
27
+ };
28
+ var _hoisted_4$2 = {
29
+ key: 0,
30
+ class: "export-opciones"
31
+ };
32
+ //#endregion
33
+ //#region src/components/forms/ExportExcelButton.vue
34
+ var ExportExcelButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
35
+ __name: "ExportExcelButton",
36
+ props: {
37
+ label: { default: "Exportar" },
38
+ disabled: {
39
+ type: Boolean,
40
+ default: false
41
+ },
42
+ loading: {
43
+ type: Boolean,
44
+ default: false
45
+ }
46
+ },
47
+ emits: ["exportar"],
48
+ setup(__props, { emit: __emit }) {
49
+ const props = __props;
50
+ const emit = __emit;
51
+ const abierto = ref(false);
52
+ const raiz = ref(null);
53
+ const primeraOpcion = ref(null);
54
+ const inhabilitado = computed(() => props.disabled || props.loading);
55
+ function alternar() {
56
+ if (inhabilitado.value) return;
57
+ abierto.value = !abierto.value;
58
+ }
59
+ function elegir(formato) {
60
+ abierto.value = false;
61
+ emit("exportar", formato);
62
+ }
63
+ /**
64
+ * Se cierra al pulsar fuera y con Escape.
65
+ *
66
+ * Los dos hacen falta: sin el clic fuera el menú se queda abierto tapando la
67
+ * tabla que se iba a exportar, y sin Escape quien navega con teclado no tiene
68
+ * forma de salir sin elegir un formato que no quería.
69
+ */
70
+ function alPulsarFuera(evento) {
71
+ if (abierto.value && raiz.value && !raiz.value.contains(evento.target)) abierto.value = false;
72
+ }
73
+ function alPulsarTecla(evento) {
74
+ if (evento.key !== "Escape" || !abierto.value) return;
75
+ abierto.value = false;
76
+ const disparador = raiz.value?.querySelector(".export-btn");
77
+ if (disparador instanceof HTMLElement) disparador.focus();
78
+ }
79
+ onMounted(() => {
80
+ document.addEventListener("mousedown", alPulsarFuera);
81
+ document.addEventListener("keydown", alPulsarTecla);
82
+ });
83
+ onBeforeUnmount(() => {
84
+ document.removeEventListener("mousedown", alPulsarFuera);
85
+ document.removeEventListener("keydown", alPulsarTecla);
86
+ });
87
+ watch(abierto, async (valor) => {
88
+ if (!valor) return;
89
+ await Promise.resolve();
90
+ primeraOpcion.value?.focus();
91
+ });
92
+ watch(() => props.loading, (valor) => {
93
+ if (valor) abierto.value = false;
94
+ });
95
+ return (_ctx, _cache) => {
96
+ return openBlock(), createElementBlock("div", {
97
+ ref_key: "raiz",
98
+ ref: raiz,
99
+ class: "export-wrap"
100
+ }, [createElementVNode("button", {
101
+ type: "button",
102
+ class: normalizeClass(["export-btn", { "export-btn-abierto": abierto.value }]),
103
+ "data-tooltip": __props.loading ? "Exportando…" : __props.label,
104
+ "aria-label": __props.label,
105
+ "aria-expanded": abierto.value,
106
+ "aria-haspopup": "true",
107
+ disabled: inhabilitado.value,
108
+ onClick: alternar
109
+ }, [__props.loading ? (openBlock(), createElementBlock("svg", _hoisted_2$2, [..._cache[2] || (_cache[2] = [createElementVNode("circle", {
110
+ cx: "12",
111
+ cy: "12",
112
+ r: "10",
113
+ fill: "none",
114
+ stroke: "currentColor",
115
+ "stroke-width": "3",
116
+ opacity: "0.25"
117
+ }, null, -1), createElementVNode("path", {
118
+ fill: "currentColor",
119
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z",
120
+ opacity: "0.75"
121
+ }, null, -1)])])) : (openBlock(), createElementBlock("svg", _hoisted_3$2, [..._cache[3] || (_cache[3] = [createElementVNode("path", {
122
+ fill: "none",
123
+ stroke: "currentColor",
124
+ "stroke-width": "2.2",
125
+ "stroke-linecap": "round",
126
+ "stroke-linejoin": "round",
127
+ d: "M12 3v11m0 0l-4-4m4 4l4-4M4 17v2a2 2 0 002 2h12a2 2 0 002-2v-2"
128
+ }, null, -1)])]))], 10, _hoisted_1$2), abierto.value ? (openBlock(), createElementBlock("div", _hoisted_4$2, [createElementVNode("button", {
129
+ ref_key: "primeraOpcion",
130
+ ref: primeraOpcion,
131
+ type: "button",
132
+ class: "export-opcion export-opcion-csv",
133
+ "data-tooltip": "CSV · texto separado por comas",
134
+ "aria-label": "Exportar a CSV, texto separado por comas",
135
+ onClick: _cache[0] || (_cache[0] = ($event) => elegir("csv"))
136
+ }, [..._cache[4] || (_cache[4] = [createElementVNode("svg", {
137
+ viewBox: "0 0 24 24",
138
+ "aria-hidden": "true",
139
+ class: "export-opcion-icono"
140
+ }, [createElementVNode("path", {
141
+ fill: "currentColor",
142
+ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm-1 7V3.5L18.5 9H13z"
143
+ }), createElementVNode("text", {
144
+ x: "12",
145
+ y: "19",
146
+ "text-anchor": "middle",
147
+ "font-size": "7.5",
148
+ "font-weight": "700",
149
+ fill: "#fff"
150
+ }, "CSV")], -1)])], 512), createElementVNode("button", {
151
+ type: "button",
152
+ class: "export-opcion export-opcion-xlsx",
153
+ "data-tooltip": "XLSX · libro de Excel",
154
+ "aria-label": "Exportar a XLSX, libro de Excel",
155
+ onClick: _cache[1] || (_cache[1] = ($event) => elegir("xlsx"))
156
+ }, [..._cache[5] || (_cache[5] = [createElementVNode("svg", {
157
+ viewBox: "0 0 24 24",
158
+ "aria-hidden": "true",
159
+ class: "export-opcion-icono"
160
+ }, [createElementVNode("path", {
161
+ fill: "currentColor",
162
+ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm-1 7V3.5L18.5 9H13z"
163
+ }), createElementVNode("text", {
164
+ x: "12",
165
+ y: "20",
166
+ "text-anchor": "middle",
167
+ "font-size": "11",
168
+ "font-weight": "700",
169
+ fill: "#fff"
170
+ }, "X")], -1)])])])) : createCommentVNode("", true)], 512);
171
+ };
172
+ }
173
+ }), [["__scopeId", "data-v-89096941"]]);
174
+ //#endregion
175
+ //#region src/lib/export-excel-csv.ts
176
+ /**
177
+ * Exporta filas a CSV UTF-8 con BOM (compatible con Excel).
178
+ */
179
+ function exportRowsToExcelCsv(filename, headers, rows) {
180
+ const escape = (value) => {
181
+ const text = value == null ? "" : String(value);
182
+ if (/[",\n\r]/.test(text)) return `"${text.replace(/"/g, "\"\"")}"`;
183
+ return text;
184
+ };
185
+ const lines = [headers.map(escape).join(","), ...rows.map((row) => row.map(escape).join(","))];
186
+ const blob = new Blob(["" + lines.join("\r\n")], { type: "text/csv;charset=utf-8;" });
187
+ const url = URL.createObjectURL(blob);
188
+ const anchor = document.createElement("a");
189
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
190
+ anchor.href = url;
191
+ anchor.download = `${filename}-${stamp}.csv`;
192
+ document.body.appendChild(anchor);
193
+ anchor.click();
194
+ document.body.removeChild(anchor);
195
+ URL.revokeObjectURL(url);
196
+ }
197
+ //#endregion
198
+ //#region src/lib/export-excel.ts
199
+ /**
200
+ * Exportador nativo a Microsoft Excel (.xlsx) en TypeScript puro.
201
+ * Genera un archivo OpenXML (.xlsx) empaquetado en ZIP con compresión DEFLATE estándar.
202
+ * Se abre directamente en Microsoft Excel sin advertencias ni conversión de delimitadores.
203
+ */
204
+ var crcTable = /* @__PURE__ */ new Uint32Array(256);
205
+ for (let i = 0; i < 256; i++) {
206
+ let c = i;
207
+ for (let j = 0; j < 8; j++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
208
+ crcTable[i] = c;
209
+ }
210
+ function crc32(buf) {
211
+ let crc = 4294967295;
212
+ for (let i = 0; i < buf.length; i++) crc = crcTable[(crc ^ buf[i]) & 255] ^ crc >>> 8;
213
+ return (crc ^ 4294967295) >>> 0;
214
+ }
215
+ async function deflateRaw(buf) {
216
+ const cs = new CompressionStream("deflate-raw");
217
+ const writer = cs.writable.getWriter();
218
+ writer.write(buf);
219
+ writer.close();
220
+ const res = await new Response(cs.readable).arrayBuffer();
221
+ return new Uint8Array(res);
222
+ }
223
+ function getDosTimeDate(date = /* @__PURE__ */ new Date()) {
224
+ return {
225
+ time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
226
+ date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
227
+ };
228
+ }
229
+ async function createZip(files) {
230
+ const te = new TextEncoder();
231
+ const { time, date } = getDosTimeDate();
232
+ const processedFiles = [];
233
+ for (const file of files) {
234
+ const data = typeof file.content === "string" ? te.encode(file.content) : file.content;
235
+ const uncompressedSize = data.length;
236
+ const fileCrc = crc32(data);
237
+ const compressed = await deflateRaw(data);
238
+ const compressedSize = compressed.length;
239
+ const nameBytes = te.encode(file.path);
240
+ processedFiles.push({
241
+ nameBytes,
242
+ uncompressedSize,
243
+ compressedSize,
244
+ crc: fileCrc,
245
+ compressed,
246
+ offset: 0
247
+ });
248
+ }
249
+ let offset = 0;
250
+ const localHeaders = [];
251
+ for (const f of processedFiles) {
252
+ f.offset = offset;
253
+ const header = new Uint8Array(30 + f.nameBytes.length);
254
+ const dv = new DataView(header.buffer);
255
+ dv.setUint32(0, 67324752, true);
256
+ dv.setUint16(4, 20, true);
257
+ dv.setUint16(6, 2048, true);
258
+ dv.setUint16(8, 8, true);
259
+ dv.setUint16(10, time, true);
260
+ dv.setUint16(12, date, true);
261
+ dv.setUint32(14, f.crc, true);
262
+ dv.setUint32(18, f.compressedSize, true);
263
+ dv.setUint32(22, f.uncompressedSize, true);
264
+ dv.setUint16(26, f.nameBytes.length, true);
265
+ dv.setUint16(28, 0, true);
266
+ header.set(f.nameBytes, 30);
267
+ localHeaders.push(header, f.compressed);
268
+ offset += header.length + f.compressed.length;
269
+ }
270
+ const centralDirStart = offset;
271
+ const cdHeaders = [];
272
+ for (const f of processedFiles) {
273
+ const cd = new Uint8Array(46 + f.nameBytes.length);
274
+ const dv = new DataView(cd.buffer);
275
+ dv.setUint32(0, 33639248, true);
276
+ dv.setUint16(4, 20, true);
277
+ dv.setUint16(6, 20, true);
278
+ dv.setUint16(8, 2048, true);
279
+ dv.setUint16(10, 8, true);
280
+ dv.setUint16(12, time, true);
281
+ dv.setUint16(14, date, true);
282
+ dv.setUint32(16, f.crc, true);
283
+ dv.setUint32(20, f.compressedSize, true);
284
+ dv.setUint32(24, f.uncompressedSize, true);
285
+ dv.setUint16(28, f.nameBytes.length, true);
286
+ dv.setUint16(30, 0, true);
287
+ dv.setUint16(32, 0, true);
288
+ dv.setUint16(34, 0, true);
289
+ dv.setUint16(36, 0, true);
290
+ dv.setUint32(38, 0, true);
291
+ dv.setUint32(42, f.offset, true);
292
+ cd.set(f.nameBytes, 46);
293
+ cdHeaders.push(cd);
294
+ offset += cd.length;
295
+ }
296
+ const centralDirSize = offset - centralDirStart;
297
+ const eocd = /* @__PURE__ */ new Uint8Array(22);
298
+ const dvEocd = new DataView(eocd.buffer);
299
+ dvEocd.setUint32(0, 101010256, true);
300
+ dvEocd.setUint16(4, 0, true);
301
+ dvEocd.setUint16(6, 0, true);
302
+ dvEocd.setUint16(8, processedFiles.length, true);
303
+ dvEocd.setUint16(10, processedFiles.length, true);
304
+ dvEocd.setUint32(12, centralDirSize, true);
305
+ dvEocd.setUint32(16, centralDirStart, true);
306
+ dvEocd.setUint16(20, 0, true);
307
+ const allParts = [
308
+ ...localHeaders,
309
+ ...cdHeaders,
310
+ eocd
311
+ ];
312
+ const totalLength = allParts.reduce((sum, p) => sum + p.length, 0);
313
+ const out = new Uint8Array(totalLength);
314
+ let pos = 0;
315
+ for (const part of allParts) {
316
+ out.set(part, pos);
317
+ pos += part.length;
318
+ }
319
+ return out;
320
+ }
321
+ function escapeXml(str) {
322
+ if (str == null) return "";
323
+ return String(str).replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
324
+ }
325
+ function colToLetter(colIndex) {
326
+ let temp;
327
+ let letter = "";
328
+ let idx = colIndex;
329
+ while (idx >= 0) {
330
+ temp = idx % 26;
331
+ letter = String.fromCharCode(temp + 65) + letter;
332
+ idx = Math.floor(idx / 26) - 1;
333
+ }
334
+ return letter;
335
+ }
336
+ /**
337
+ * Genera y descarga un archivo Microsoft Excel (.xlsx) nativo.
338
+ */
339
+ async function exportRowsToXlsx(filename, headers, rows, sheetName = "Datos") {
340
+ const contentTypes = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
341
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
342
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
343
+ <Default Extension="xml" ContentType="application/xml"/>
344
+ <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
345
+ <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
346
+ <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
347
+ </Types>`;
348
+ const rels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
349
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
350
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
351
+ </Relationships>`;
352
+ const workbookRels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
353
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
354
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
355
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
356
+ </Relationships>`;
357
+ const workbook = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
358
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
359
+ <sheets>
360
+ <sheet name="${escapeXml(sheetName)}" sheetId="1" r:id="rId1"/>
361
+ </sheets>
362
+ </workbook>`;
363
+ const styles = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
364
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
365
+ <fonts count="2">
366
+ <font><sz val="11"/><name val="Calibri"/></font>
367
+ <font><b/><sz val="11"/><name val="Calibri"/><color rgb="FF0F172A"/></font>
368
+ </fonts>
369
+ <fills count="3">
370
+ <fill><patternFill patternType="none"/></fill>
371
+ <fill><patternFill patternType="gray125"/></fill>
372
+ <fill><patternFill patternType="solid"><fgColor rgb="FFF1F5F9"/></patternFill></fill>
373
+ </fills>
374
+ <borders count="2">
375
+ <border><left/><right/><top/><bottom/></border>
376
+ <border>
377
+ <left style="thin"><color rgb="FFE2E8F0"/></left>
378
+ <right style="thin"><color rgb="FFE2E8F0"/></right>
379
+ <top style="thin"><color rgb="FFE2E8F0"/></top>
380
+ <bottom style="medium"><color rgb="FFCBD5E1"/></bottom>
381
+ </border>
382
+ </borders>
383
+ <cellStyleXfs count="1"><xf/></cellStyleXfs>
384
+ <cellXfs count="2">
385
+ <xf fontId="0" fillId="0" borderId="0"/>
386
+ <xf fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>
387
+ </cellXfs>
388
+ </styleSheet>`;
389
+ const colLengths = headers.map((h) => String(h).length);
390
+ for (const row of rows) row.forEach((val, idx) => {
391
+ if (val != null) {
392
+ const len = String(val).length;
393
+ if (len > (colLengths[idx] ?? 0)) colLengths[idx] = len;
394
+ }
395
+ });
396
+ let colsXml = "<cols>";
397
+ colLengths.forEach((maxLen, idx) => {
398
+ const width = Math.max(12, Math.min(50, maxLen + 4));
399
+ colsXml += `<col min="${idx + 1}" max="${idx + 1}" width="${width}" customWidth="1"/>`;
400
+ });
401
+ colsXml += "</cols>";
402
+ let sheetData = "";
403
+ sheetData += "<row r=\"1\">";
404
+ headers.forEach((h, colIdx) => {
405
+ const ref = `${colToLetter(colIdx)}1`;
406
+ sheetData += `<c r="${ref}" t="inlineStr" s="1"><is><t xml:space="preserve">${escapeXml(h)}</t></is></c>`;
407
+ });
408
+ sheetData += "</row>";
409
+ rows.forEach((row, rowIdx) => {
410
+ const rNum = rowIdx + 2;
411
+ sheetData += `<row r="${rNum}">`;
412
+ row.forEach((val, colIdx) => {
413
+ const ref = `${colToLetter(colIdx)}${rNum}`;
414
+ if (val == null || val === "") {} else if (typeof val === "number" && !isNaN(val)) sheetData += `<c r="${ref}"><v>${val}</v></c>`;
415
+ else sheetData += `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(val)}</t></is></c>`;
416
+ });
417
+ sheetData += "</row>";
418
+ });
419
+ const worksheet = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
420
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
421
+ ${colsXml}
422
+ <sheetData>
423
+ ${sheetData}
424
+ </sheetData>
425
+ </worksheet>`;
426
+ const zipBytes = await createZip([
427
+ {
428
+ path: "[Content_Types].xml",
429
+ content: contentTypes
430
+ },
431
+ {
432
+ path: "_rels/.rels",
433
+ content: rels
434
+ },
435
+ {
436
+ path: "xl/_rels/workbook.xml.rels",
437
+ content: workbookRels
438
+ },
439
+ {
440
+ path: "xl/workbook.xml",
441
+ content: workbook
442
+ },
443
+ {
444
+ path: "xl/styles.xml",
445
+ content: styles
446
+ },
447
+ {
448
+ path: "xl/worksheets/sheet1.xml",
449
+ content: worksheet
450
+ }
451
+ ]);
452
+ const blob = new Blob([zipBytes], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
453
+ const url = URL.createObjectURL(blob);
454
+ const anchor = document.createElement("a");
455
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
456
+ anchor.href = url;
457
+ anchor.download = `${filename}-${stamp}.xlsx`;
458
+ document.body.appendChild(anchor);
459
+ anchor.click();
460
+ document.body.removeChild(anchor);
461
+ URL.revokeObjectURL(url);
462
+ }
463
+ //#endregion
464
+ //#region src/lib/exportar-listado.ts
465
+ /**
466
+ * Baja un listado en el formato que eligió quien pulsó el botón.
467
+ *
468
+ * Los dos formatos ya existían por separado (`exportRowsToExcelCsv` y
469
+ * `exportRowsToXlsx`); lo que faltaba era un sitio donde vivieran juntos. Cada
470
+ * pantalla que exporta repetía el mismo `if (formato === 'csv')`, y con más de
471
+ * cincuenta listados esa decisión —qué función va con qué formato, y que el
472
+ * `.xlsx` hay que esperarlo— se habría copiado cincuenta veces. Aquí se toma
473
+ * una sola vez y las vistas solo dicen qué datos quieren bajar.
474
+ *
475
+ * `hoja` nombra la pestaña del libro de Excel; el `.csv` no tiene pestañas y lo
476
+ * ignora. Sin ella, Excel abre el archivo con una pestaña llamada «Datos».
477
+ */
478
+ async function exportarListado(formato, archivo, encabezados, filas, hoja) {
479
+ if (formato === "csv") {
480
+ exportRowsToExcelCsv(archivo, encabezados, filas);
481
+ return;
482
+ }
483
+ await exportRowsToXlsx(archivo, encabezados, filas, hoja);
484
+ }
485
+ /**
486
+ * Valor de una celda para el archivo, no para la pantalla.
487
+ *
488
+ * En la tabla un dato ausente se pinta como «—» y un booleano como un
489
+ * interruptor; en una hoja de cálculo eso estorba: el guion largo no es un dato
490
+ * y hay que borrarlo a mano antes de sumar o filtrar. Aquí el vacío se queda
491
+ * vacío y el booleano se dice con palabras.
492
+ */
493
+ function celdaTexto(valor) {
494
+ if (valor == null || valor === "") return "";
495
+ if (typeof valor === "boolean") return valor ? "SÍ" : "NO";
496
+ return String(valor);
497
+ }
498
+ //#endregion
499
+ //#region src/components/VistoBuenoPortalModal.vue?vue&type=script&setup=true&lang.ts
500
+ var _hoisted_1$1 = {
501
+ key: 0,
502
+ class: "modal-backdrop"
503
+ };
504
+ var _hoisted_2$1 = {
505
+ class: "modal-panel",
506
+ role: "dialog",
507
+ "aria-modal": "true",
508
+ "aria-labelledby": "visto-bueno-titulo"
509
+ };
510
+ var _hoisted_3$1 = { class: "modal-header" };
511
+ var _hoisted_4$1 = { class: "modal-header-actions" };
512
+ var _hoisted_5$1 = { class: "modal-body" };
513
+ var _hoisted_6$1 = { class: "resumen" };
514
+ var _hoisted_7$1 = { key: 0 };
515
+ var _hoisted_8$1 = { key: 1 };
516
+ var _hoisted_9$1 = { "data-soporte": "" };
517
+ var _hoisted_10$1 = {
518
+ key: 0,
519
+ class: "alert-warn"
520
+ };
521
+ var _hoisted_11$1 = {
522
+ key: 1,
523
+ class: "alert-info",
524
+ "data-recordatorio-soporte": ""
525
+ };
526
+ var _hoisted_12$1 = {
527
+ key: 2,
528
+ class: "alert-error",
529
+ role: "alert"
530
+ };
531
+ var _hoisted_13$1 = { class: "modal-footer" };
532
+ var _hoisted_14$1 = ["disabled"];
533
+ var _hoisted_15$1 = ["disabled"];
534
+ //#endregion
535
+ //#region src/components/VistoBuenoPortalModal.vue
536
+ var VistoBuenoPortalModal_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
537
+ __name: "VistoBuenoPortalModal",
538
+ props: /*@__PURE__*/ mergeModels({ solicitud: {} }, {
539
+ "open": {
540
+ type: Boolean,
541
+ required: true
542
+ },
543
+ "openModifiers": {}
544
+ }),
545
+ emits: /*@__PURE__*/ mergeModels(["decidida"], ["update:open"]),
546
+ setup(__props, { emit: __emit }) {
547
+ const props = __props;
548
+ const open = useModel(__props, "open");
549
+ const emit = __emit;
550
+ const comentario = ref("");
551
+ const enviando = ref(null);
552
+ const error = ref(null);
553
+ const helpOpen = ref(false);
554
+ const sinNovedad = computed(() => props.solicitud?.novedad_manual === true);
555
+ /** Incapacidad y licencias: nómina valida contra el soporte, que está solo en SoftDIN. */
556
+ const conSoporte = computed(() => !!props.solicitud && esTipoConSoporte(props.solicitud.tipo));
557
+ const soporte = computed(() => props.solicitud ? estadoSoporte(props.solicitud) : null);
558
+ const datos = computed(() => props.solicitud ? datosNoClinicos(props.solicitud) : null);
559
+ watch(open, (abierto) => {
560
+ if (abierto) {
561
+ comentario.value = "";
562
+ error.value = null;
563
+ }
564
+ });
565
+ async function decidir(aprueba) {
566
+ if (!props.solicitud) return;
567
+ error.value = null;
568
+ if (!aprueba && comentario.value.trim().length < 5) {
569
+ error.value = "Escriba el motivo: el empleado lo verá en su portal.";
570
+ return;
571
+ }
572
+ enviando.value = aprueba ? "aprobar" : "rechazar";
573
+ try {
574
+ await portalSolicitudesService.vistoBueno(props.solicitud.id, aprueba, comentario.value.trim() || null);
575
+ emit("decidida");
576
+ open.value = false;
577
+ } catch (e) {
578
+ error.value = mensajeError(e, "No se pudo registrar la decisión.");
579
+ } finally {
580
+ enviando.value = null;
581
+ }
582
+ }
583
+ return (_ctx, _cache) => {
584
+ return openBlock(), createElementBlock(Fragment, null, [(openBlock(), createBlock(Teleport, { to: "body" }, [open.value && __props.solicitud ? (openBlock(), createElementBlock("div", _hoisted_1$1, [createElementVNode("div", _hoisted_2$1, [createElementVNode("header", _hoisted_3$1, [_cache[6] || (_cache[6] = createElementVNode("h2", {
585
+ id: "visto-bueno-titulo",
586
+ class: "modal-title"
587
+ }, "Visto bueno de nómina", -1)), createElementVNode("div", _hoisted_4$1, [createVNode(FormHelpButton, { onClick: _cache[0] || (_cache[0] = ($event) => helpOpen.value = true) }), createVNode(FormCloseButton, {
588
+ disabled: enviando.value !== null,
589
+ onClick: _cache[1] || (_cache[1] = ($event) => open.value = false)
590
+ }, null, 8, ["disabled"])])]), createElementVNode("div", _hoisted_5$1, [
591
+ createElementVNode("dl", _hoisted_6$1, [
592
+ createElementVNode("div", null, [_cache[7] || (_cache[7] = createElementVNode("dt", null, "Empleado", -1)), createElementVNode("dd", null, toDisplayString(__props.solicitud.empleado ?? "—") + " · " + toDisplayString(__props.solicitud.identificacion), 1)]),
593
+ createElementVNode("div", null, [_cache[8] || (_cache[8] = createElementVNode("dt", null, "Cargo", -1)), createElementVNode("dd", null, toDisplayString(__props.solicitud.cargo ?? "—"), 1)]),
594
+ createElementVNode("div", null, [_cache[9] || (_cache[9] = createElementVNode("dt", null, "Solicitud", -1)), createElementVNode("dd", null, toDisplayString(unref(nombreTipoSolicitud)(__props.solicitud.tipo)), 1)]),
595
+ datos.value ? (openBlock(), createElementBlock("div", _hoisted_7$1, [_cache[10] || (_cache[10] = createElementVNode("dt", null, "Datos", -1)), createElementVNode("dd", null, toDisplayString(datos.value), 1)])) : createCommentVNode("", true),
596
+ createElementVNode("div", null, [_cache[11] || (_cache[11] = createElementVNode("dt", null, "Fechas", -1)), createElementVNode("dd", null, toDisplayString(unref(fechaCorta)(__props.solicitud.fecha_inicio)) + " – " + toDisplayString(unref(fechaCorta)(__props.solicitud.fecha_fin)), 1)]),
597
+ createElementVNode("div", null, [_cache[12] || (_cache[12] = createElementVNode("dt", null, "Motivo", -1)), createElementVNode("dd", null, toDisplayString(__props.solicitud.motivo), 1)]),
598
+ soporte.value ? (openBlock(), createElementBlock("div", _hoisted_8$1, [_cache[13] || (_cache[13] = createElementVNode("dt", null, "Soporte", -1)), createElementVNode("dd", _hoisted_9$1, toDisplayString(soporte.value.texto), 1)])) : createCommentVNode("", true),
599
+ createElementVNode("div", null, [_cache[14] || (_cache[14] = createElementVNode("dt", null, "Novedad", -1)), createElementVNode("dd", null, [sinNovedad.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode("No se pudo crear sola: regístrela en Novedades de seguridad social de SoftDIN.")], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode("Creada en SoftDIN, inactiva hasta el visto bueno")], 64))])])
600
+ ]),
601
+ sinNovedad.value ? (openBlock(), createElementBlock("p", _hoisted_10$1, " La aprobación del flujo quedó registrada, pero la novedad no se creó (fecha bloqueada o concepto sin configurar en el grupo de nómina). Regístrela a mano antes de dar el visto bueno. ")) : createCommentVNode("", true),
602
+ conSoporte.value ? (openBlock(), createElementBlock("p", _hoisted_11$1, [
603
+ _cache[15] || (_cache[15] = createTextVNode(" Antes de dar el visto bueno, abra el soporte en el expediente del empleado en SoftDIN (documento reservado) y compare fechas, días", -1)),
604
+ __props.solicitud.tipo === "incapacidad" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(", origen y prórroga")], 64)) : createCommentVNode("", true),
605
+ _cache[16] || (_cache[16] = createTextVNode(" con el certificado. El portal no guarda ni muestra el documento ni el diagnóstico. ", -1)),
606
+ soporte.value?.pendiente ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(" El soporte aún no ha llegado.")], 64)) : createCommentVNode("", true)
607
+ ])) : createCommentVNode("", true),
608
+ createElementVNode("div", null, [_cache[17] || (_cache[17] = createElementVNode("label", {
609
+ for: "visto-bueno-comentario",
610
+ class: "label"
611
+ }, "Comentario", -1)), withDirectives(createElementVNode("textarea", {
612
+ id: "visto-bueno-comentario",
613
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => comentario.value = $event),
614
+ class: "input",
615
+ rows: "3",
616
+ maxlength: "500",
617
+ placeholder: "Obligatorio si no da el visto bueno: el empleado lo verá como motivo."
618
+ }, null, 512), [[vModelText, comentario.value]])]),
619
+ error.value ? (openBlock(), createElementBlock("p", _hoisted_12$1, toDisplayString(error.value), 1)) : createCommentVNode("", true),
620
+ createElementVNode("footer", _hoisted_13$1, [createElementVNode("button", {
621
+ type: "button",
622
+ class: "btn-danger",
623
+ disabled: enviando.value !== null,
624
+ onClick: _cache[3] || (_cache[3] = ($event) => decidir(false))
625
+ }, toDisplayString(enviando.value === "rechazar" ? "Guardando…" : "No dar visto bueno"), 9, _hoisted_14$1), createElementVNode("button", {
626
+ type: "button",
627
+ class: "btn-primary",
628
+ disabled: enviando.value !== null,
629
+ onClick: _cache[4] || (_cache[4] = ($event) => decidir(true))
630
+ }, toDisplayString(enviando.value === "aprobar" ? "Guardando…" : "Dar visto bueno"), 9, _hoisted_15$1)])
631
+ ])])])) : createCommentVNode("", true)])), createVNode(FormHelpModal, {
632
+ open: helpOpen.value,
633
+ "onUpdate:open": _cache[5] || (_cache[5] = ($event) => helpOpen.value = $event),
634
+ title: "Visto bueno de nómina",
635
+ intro: "Solicitudes que el empleado radicó en su Portal de autogestión y que ya aprobó su flujo (jefe y demás niveles). Antes de liquidarse, nómina confirma que la novedad está bien.",
636
+ steps: [
637
+ {
638
+ title: "Revise fechas y motivo.",
639
+ body: "La novedad ya existe pero está inactiva: el motor de nómina no la liquida hasta su visto bueno."
640
+ },
641
+ {
642
+ title: "Incapacidades y licencias: valide el soporte en SoftDIN.",
643
+ body: "Llegan sin pasar por el jefe. El certificado o registro civil está en el expediente del empleado en SoftDIN (reservado); compárelo con las fechas antes de decidir."
644
+ },
645
+ {
646
+ title: "Dé el visto bueno.",
647
+ body: "La novedad queda activa y entra en la próxima liquidación. El empleado ve la solicitud como aprobada."
648
+ },
649
+ {
650
+ title: "O no lo dé, con el motivo.",
651
+ body: "La novedad sigue inactiva (no se borra) y el empleado ve «Sin visto bueno de nómina» con su comentario."
652
+ },
653
+ {
654
+ title: "Si no hay novedad.",
655
+ body: "Cuando el sistema no pudo crearla (fecha bloqueada o concepto sin configurar), regístrela en Novedades de seguridad social antes de dar el visto bueno."
656
+ }
657
+ ]
658
+ }, null, 8, ["open"])], 64);
659
+ };
660
+ }
661
+ }), [["__scopeId", "data-v-62d955aa"]]);
662
+ //#endregion
663
+ //#region src/views/SolicitudesView.vue?vue&type=script&setup=true&lang.ts
664
+ var _hoisted_1 = { class: "page-shell" };
665
+ var _hoisted_2 = { class: "toolbar" };
666
+ var _hoisted_3 = ["disabled"];
667
+ var _hoisted_4 = ["value"];
668
+ var _hoisted_5 = ["disabled"];
669
+ var _hoisted_6 = {
670
+ key: 0,
671
+ class: "panel-error",
672
+ role: "alert"
673
+ };
674
+ var _hoisted_7 = {
675
+ key: 1,
676
+ class: "panel-muted"
677
+ };
678
+ var _hoisted_8 = {
679
+ key: 2,
680
+ class: "panel-muted"
681
+ };
682
+ var _hoisted_9 = {
683
+ key: 3,
684
+ class: "table-wrap"
685
+ };
686
+ var _hoisted_10 = { class: "data-table table-cards" };
687
+ var _hoisted_11 = {
688
+ class: "cell-primary",
689
+ "data-label": "Empleado"
690
+ };
691
+ var _hoisted_12 = { class: "sub" };
692
+ var _hoisted_13 = { "data-label": "Solicitud" };
693
+ var _hoisted_14 = {
694
+ key: 0,
695
+ class: "sub"
696
+ };
697
+ var _hoisted_15 = {
698
+ key: 1,
699
+ class: "sub"
700
+ };
701
+ var _hoisted_16 = { "data-label": "Fechas" };
702
+ var _hoisted_17 = {
703
+ class: "cell-optional",
704
+ "data-label": "Motivo"
705
+ };
706
+ var _hoisted_18 = { "data-label": "Estado" };
707
+ var _hoisted_19 = {
708
+ key: 0,
709
+ class: "sub"
710
+ };
711
+ var _hoisted_20 = {
712
+ key: 1,
713
+ class: "sub aviso"
714
+ };
715
+ var _hoisted_21 = {
716
+ key: 2,
717
+ class: "sub"
718
+ };
719
+ var _hoisted_22 = {
720
+ class: "actions-cell",
721
+ "data-label": "Acciones"
722
+ };
723
+ var _hoisted_23 = ["onClick"];
724
+ //#endregion
725
+ //#region src/views/SolicitudesView.vue
726
+ var SolicitudesView_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
727
+ __name: "SolicitudesView",
728
+ setup(__props) {
729
+ const clientesInternos = useClienteInternoStore();
730
+ const estado = ref("pendiente_visto_bueno");
731
+ const items = ref([]);
732
+ const loading = ref(false);
733
+ const error = ref(null);
734
+ const seleccionada = ref(null);
735
+ const modalOpen = ref(false);
736
+ const filtros = [
737
+ {
738
+ valor: "pendiente_visto_bueno",
739
+ texto: "Pendientes de visto bueno"
740
+ },
741
+ {
742
+ valor: "en_aprobacion",
743
+ texto: "En aprobación"
744
+ },
745
+ {
746
+ valor: "aprobada",
747
+ texto: "Aprobadas"
748
+ },
749
+ {
750
+ valor: "rechazada",
751
+ texto: "Rechazadas en el flujo"
752
+ },
753
+ {
754
+ valor: "rechazada_nomina",
755
+ texto: "Sin visto bueno de nómina"
756
+ },
757
+ {
758
+ valor: "cancelada",
759
+ texto: "Canceladas"
760
+ },
761
+ {
762
+ valor: "",
763
+ texto: "Todas"
764
+ }
765
+ ];
766
+ const textoVacio = computed(() => estado.value === "pendiente_visto_bueno" ? "No hay solicitudes esperando su visto bueno." : "No hay solicitudes con este estado.");
767
+ async function cargar() {
768
+ loading.value = true;
769
+ error.value = null;
770
+ try {
771
+ items.value = await portalSolicitudesService.listar(estado.value || void 0, clientesInternos.seleccionado);
772
+ } catch (e) {
773
+ error.value = mensajeError(e, "No se pudo cargar la bandeja.");
774
+ } finally {
775
+ loading.value = false;
776
+ }
777
+ }
778
+ function abrirVistoBueno(s) {
779
+ seleccionada.value = s;
780
+ modalOpen.value = true;
781
+ }
782
+ watch([estado, () => clientesInternos.seleccionado], () => void cargar());
783
+ onMounted(() => void cargar());
784
+ const exportando = ref(false);
785
+ async function exportar(formato) {
786
+ if (exportando.value || items.value.length === 0) return;
787
+ exportando.value = true;
788
+ try {
789
+ await exportarListado(formato, "solicitudes-portal", [
790
+ "Empleado",
791
+ "Identificación",
792
+ "Cargo",
793
+ "Tipo",
794
+ "Concepto",
795
+ "Desde",
796
+ "Hasta",
797
+ "Motivo",
798
+ "Estado",
799
+ "Novedad a mano",
800
+ "Soporte",
801
+ "Radicada",
802
+ "Último comentario"
803
+ ], items.value.map((s) => [
804
+ celdaTexto(s.empleado),
805
+ celdaTexto(s.identificacion),
806
+ celdaTexto(s.cargo),
807
+ celdaTexto(nombreTipoSolicitud(s.tipo)),
808
+ celdaTexto(s.concepto ?? datosNoClinicos(s)),
809
+ celdaTexto(s.fecha_inicio),
810
+ celdaTexto(s.fecha_fin),
811
+ celdaTexto(s.motivo),
812
+ celdaTexto(ESTADOS_SOLICITUD_PORTAL[s.estado]?.texto ?? s.estado),
813
+ celdaTexto(s.novedad_manual ? "Sí" : "No"),
814
+ celdaTexto(estadoSoporte(s)?.texto ?? null),
815
+ celdaTexto(s.radicada),
816
+ celdaTexto(s.ultimo_comentario)
817
+ ]), "Solicitudes");
818
+ } finally {
819
+ exportando.value = false;
820
+ }
821
+ }
822
+ return (_ctx, _cache) => {
823
+ return openBlock(), createElementBlock("section", _hoisted_1, [
824
+ _cache[4] || (_cache[4] = createElementVNode("header", { class: "page-header" }, [createElementVNode("div", null, [createElementVNode("h1", { class: "page-title" }, "Solicitudes del portal"), createElementVNode("p", { class: "page-subtitle descripcion-en-ayuda" }, " Permisos, licencias, vacaciones e incapacidades que los empleados radicaron en su Portal de autogestión. Los niveles de aprobación los deciden los jefes en la bandeja de aprobaciones de SoftDIN; aquí nómina da el visto bueno final. Incapacidades y licencias de maternidad y paternidad llegan directo aquí: su soporte se revisa en el expediente del empleado en SoftDIN. ")])], -1)),
825
+ createElementVNode("div", _hoisted_2, [
826
+ _cache[2] || (_cache[2] = createElementVNode("label", {
827
+ for: "portal-estado",
828
+ class: "label"
829
+ }, "Estado", -1)),
830
+ withDirectives(createElementVNode("select", {
831
+ id: "portal-estado",
832
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => estado.value = $event),
833
+ class: "input",
834
+ disabled: loading.value
835
+ }, [(openBlock(), createElementBlock(Fragment, null, renderList(filtros, (f) => {
836
+ return createElementVNode("option", {
837
+ key: f.valor,
838
+ value: f.valor
839
+ }, toDisplayString(f.texto), 9, _hoisted_4);
840
+ }), 64))], 8, _hoisted_3), [[vModelSelect, estado.value]]),
841
+ createVNode(ExportExcelButton_default, {
842
+ loading: exportando.value,
843
+ disabled: loading.value || items.value.length === 0,
844
+ onExportar: exportar
845
+ }, null, 8, ["loading", "disabled"]),
846
+ createElementVNode("button", {
847
+ type: "button",
848
+ class: "btn-secondary",
849
+ disabled: loading.value,
850
+ onClick: cargar
851
+ }, "Actualizar", 8, _hoisted_5)
852
+ ]),
853
+ error.value ? (openBlock(), createElementBlock("p", _hoisted_6, toDisplayString(error.value), 1)) : loading.value ? (openBlock(), createElementBlock("p", _hoisted_7, "Cargando…")) : items.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_8, toDisplayString(textoVacio.value), 1)) : (openBlock(), createElementBlock("div", _hoisted_9, [createElementVNode("table", _hoisted_10, [_cache[3] || (_cache[3] = createElementVNode("thead", null, [createElementVNode("tr", null, [
854
+ createElementVNode("th", null, "Empleado"),
855
+ createElementVNode("th", null, "Solicitud"),
856
+ createElementVNode("th", null, "Fechas"),
857
+ createElementVNode("th", null, "Motivo"),
858
+ createElementVNode("th", null, "Estado"),
859
+ createElementVNode("th")
860
+ ])], -1)), createElementVNode("tbody", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(items.value, (s) => {
861
+ return openBlock(), createElementBlock("tr", { key: s.id }, [
862
+ createElementVNode("td", _hoisted_11, [createTextVNode(toDisplayString(s.empleado ?? "—") + " ", 1), createElementVNode("span", _hoisted_12, [createTextVNode(toDisplayString(s.identificacion), 1), s.cargo ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" · " + toDisplayString(s.cargo), 1)], 64)) : createCommentVNode("", true)])]),
863
+ createElementVNode("td", _hoisted_13, [
864
+ createTextVNode(toDisplayString(unref(nombreTipoSolicitud)(s.tipo)) + " ", 1),
865
+ s.concepto ? (openBlock(), createElementBlock("span", _hoisted_14, toDisplayString(s.concepto), 1)) : createCommentVNode("", true),
866
+ unref(datosNoClinicos)(s) ? (openBlock(), createElementBlock("span", _hoisted_15, toDisplayString(unref(datosNoClinicos)(s)), 1)) : createCommentVNode("", true),
867
+ unref(estadoSoporte)(s) ? (openBlock(), createElementBlock("span", {
868
+ key: 2,
869
+ class: normalizeClass(["sub", unref(estadoSoporte)(s).pendiente ? "aviso" : "soporte"]),
870
+ "data-soporte": ""
871
+ }, toDisplayString(unref(estadoSoporte)(s).texto), 3)) : createCommentVNode("", true)
872
+ ]),
873
+ createElementVNode("td", _hoisted_16, toDisplayString(unref(fechaCorta)(s.fecha_inicio)) + " – " + toDisplayString(unref(fechaCorta)(s.fecha_fin)), 1),
874
+ createElementVNode("td", _hoisted_17, toDisplayString(s.motivo), 1),
875
+ createElementVNode("td", _hoisted_18, [
876
+ createElementVNode("span", { class: normalizeClass(["estado", unref(ESTADOS_SOLICITUD_PORTAL)[s.estado]?.clase]) }, toDisplayString(unref(ESTADOS_SOLICITUD_PORTAL)[s.estado]?.texto ?? s.estado), 3),
877
+ s.estado === "en_aprobacion" && s.nivel_actual && s.niveles_total ? (openBlock(), createElementBlock("span", _hoisted_19, " Nivel " + toDisplayString(s.nivel_actual) + " de " + toDisplayString(s.niveles_total), 1)) : createCommentVNode("", true),
878
+ s.novedad_manual && s.estado === "pendiente_visto_bueno" ? (openBlock(), createElementBlock("span", _hoisted_20, " Sin novedad: registrar a mano ")) : createCommentVNode("", true),
879
+ s.ultimo_comentario ? (openBlock(), createElementBlock("span", _hoisted_21, "«" + toDisplayString(s.ultimo_comentario) + "»", 1)) : createCommentVNode("", true)
880
+ ]),
881
+ createElementVNode("td", _hoisted_22, [s.estado === "pendiente_visto_bueno" ? (openBlock(), createElementBlock("button", {
882
+ key: 0,
883
+ type: "button",
884
+ class: "btn-primary",
885
+ onClick: ($event) => abrirVistoBueno(s)
886
+ }, " Visto bueno ", 8, _hoisted_23)) : createCommentVNode("", true)])
887
+ ]);
888
+ }), 128))])])])),
889
+ createVNode(VistoBuenoPortalModal_default, {
890
+ open: modalOpen.value,
891
+ "onUpdate:open": _cache[1] || (_cache[1] = ($event) => modalOpen.value = $event),
892
+ solicitud: seleccionada.value,
893
+ onDecidida: cargar
894
+ }, null, 8, ["open", "solicitud"])
895
+ ]);
896
+ };
897
+ }
898
+ }), [["__scopeId", "data-v-d7b34c5c"]]);
899
+ //#endregion
900
+ export { SolicitudesView_default as default };