@tinya-code/gas-admin-ui 0.0.0-stage → 0.1.0-alpha.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/README.md +63 -2
- package/fesm2022/tinya-code-gas-admin-ui-api.service-CZrUeclM.mjs +281 -0
- package/fesm2022/tinya-code-gas-admin-ui-api.service-CZrUeclM.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-r-RXDE37.mjs +1022 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-r-RXDE37.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-BBzWUuHl.mjs +101 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-BBzWUuHl.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-LD8AM3Rn.mjs +89 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-LD8AM3Rn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-Ci0UpXtV.mjs +45 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-Ci0UpXtV.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dg2N3XQ2.mjs +408 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dg2N3XQ2.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-BwGupSbi.mjs +4514 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-BwGupSbi.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-Bjw9ImlS.mjs +416 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-Bjw9ImlS.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-CeGueI57.mjs +439 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-CeGueI57.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-CMzl9MkX.mjs +637 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-CMzl9MkX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-CuWwAfdY.mjs +251 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-CuWwAfdY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-B3W3Ynsp.mjs +309 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-B3W3Ynsp.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B_D5Jhp9.mjs +546 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B_D5Jhp9.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-CDrz9pVN.mjs +131 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-CDrz9pVN.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-CNbQtREA.mjs +80 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-CNbQtREA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-DzaRfHXO.mjs +292 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-DzaRfHXO.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-C1g545w5.mjs +60 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-C1g545w5.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs +4400 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
- package/package.json +31 -3
- package/types/tinya-code-gas-admin-ui.d.ts +3091 -0
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
import { i as getSchema } from "./tinya-code-gas-admin-ui-schema-catalog-CDrz9pVN.mjs";
|
|
2
|
+
import { t as Button } from "./tinya-code-gas-admin-ui-button-BQjNW8KB.mjs";
|
|
3
|
+
import { t as DataSourceService } from "./tinya-code-gas-admin-ui-data-source.service-LD8AM3Rn.mjs";
|
|
4
|
+
import { t as ErrorMapperService } from "./tinya-code-gas-admin-ui-error-mapper.service-Ci0UpXtV.mjs";
|
|
5
|
+
import { t as EmptyState } from "./tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs";
|
|
6
|
+
import { t as Skeleton } from "./tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs";
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
import { ChangeDetectionStrategy, Component, computed, effect, inject, signal } from "@angular/core";
|
|
9
|
+
import { ActivatedRoute } from "@angular/router";
|
|
10
|
+
import { LucideChevronLeft, LucideChevronRight } from "@lucide/angular";
|
|
11
|
+
import { toSignal } from "@angular/core/rxjs-interop";
|
|
12
|
+
const DAY_MS = 864e5;
|
|
13
|
+
const REASON_UNDATED = "Sin fecha de inicio válida";
|
|
14
|
+
const REASON_INVALID_END = "Fecha de fin inválida";
|
|
15
|
+
const REASON_END_BEFORE_START = "El fin es anterior al inicio";
|
|
16
|
+
const MONTHS = [
|
|
17
|
+
"enero",
|
|
18
|
+
"febrero",
|
|
19
|
+
"marzo",
|
|
20
|
+
"abril",
|
|
21
|
+
"mayo",
|
|
22
|
+
"junio",
|
|
23
|
+
"julio",
|
|
24
|
+
"agosto",
|
|
25
|
+
"septiembre",
|
|
26
|
+
"octubre",
|
|
27
|
+
"noviembre",
|
|
28
|
+
"diciembre"
|
|
29
|
+
];
|
|
30
|
+
const MONTHS_SHORT = [
|
|
31
|
+
"ene",
|
|
32
|
+
"feb",
|
|
33
|
+
"mar",
|
|
34
|
+
"abr",
|
|
35
|
+
"may",
|
|
36
|
+
"jun",
|
|
37
|
+
"jul",
|
|
38
|
+
"ago",
|
|
39
|
+
"sep",
|
|
40
|
+
"oct",
|
|
41
|
+
"nov",
|
|
42
|
+
"dic"
|
|
43
|
+
];
|
|
44
|
+
const WEEKDAYS_SHORT = [
|
|
45
|
+
"lun",
|
|
46
|
+
"mar",
|
|
47
|
+
"mié",
|
|
48
|
+
"jue",
|
|
49
|
+
"vie",
|
|
50
|
+
"sáb",
|
|
51
|
+
"dom"
|
|
52
|
+
];
|
|
53
|
+
const WEEKDAYS = [
|
|
54
|
+
"lunes",
|
|
55
|
+
"martes",
|
|
56
|
+
"miércoles",
|
|
57
|
+
"jueves",
|
|
58
|
+
"viernes",
|
|
59
|
+
"sábado",
|
|
60
|
+
"domingo"
|
|
61
|
+
];
|
|
62
|
+
const WEEKDAY_HEADINGS = WEEKDAYS_SHORT;
|
|
63
|
+
const DAY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
64
|
+
function parseDay(value) {
|
|
65
|
+
if (typeof value !== "string") return null;
|
|
66
|
+
const match = DAY_PATTERN.exec(value.trim());
|
|
67
|
+
if (match === null) return null;
|
|
68
|
+
const year = Number(match[1]);
|
|
69
|
+
const month = Number(match[2]);
|
|
70
|
+
const day = Number(match[3]);
|
|
71
|
+
const ms = Date.UTC(year, month - 1, day);
|
|
72
|
+
const back = new Date(ms);
|
|
73
|
+
if (back.getUTCFullYear() !== year || back.getUTCMonth() !== month - 1 || back.getUTCDate() !== day) return null;
|
|
74
|
+
return ms;
|
|
75
|
+
}
|
|
76
|
+
function formatDay(ms) {
|
|
77
|
+
const date = new Date(ms);
|
|
78
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
79
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
80
|
+
return `${date.getUTCFullYear()}-${month}-${day}`;
|
|
81
|
+
}
|
|
82
|
+
function todayCivil() {
|
|
83
|
+
const now = /* @__PURE__ */ new Date();
|
|
84
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
85
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
86
|
+
return `${now.getFullYear()}-${month}-${day}`;
|
|
87
|
+
}
|
|
88
|
+
function addDays(ms, days) {
|
|
89
|
+
return ms + days * DAY_MS;
|
|
90
|
+
}
|
|
91
|
+
function dayOfWeekMon0(ms) {
|
|
92
|
+
return (new Date(ms).getUTCDay() + 6) % 7;
|
|
93
|
+
}
|
|
94
|
+
function shiftMonths(ms, months) {
|
|
95
|
+
const date = new Date(ms);
|
|
96
|
+
const target = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + months, 1));
|
|
97
|
+
const lastDay = new Date(Date.UTC(target.getUTCFullYear(), target.getUTCMonth() + 1, 0)).getUTCDate();
|
|
98
|
+
return Date.UTC(target.getUTCFullYear(), target.getUTCMonth(), Math.min(date.getUTCDate(), lastDay));
|
|
99
|
+
}
|
|
100
|
+
function monthGridStartMs(cursorMs) {
|
|
101
|
+
const date = new Date(cursorMs);
|
|
102
|
+
const firstOfMonth = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
|
|
103
|
+
return addDays(firstOfMonth, -dayOfWeekMon0(firstOfMonth));
|
|
104
|
+
}
|
|
105
|
+
function monthGridDays(cursorDay) {
|
|
106
|
+
const cursor = parseDay(cursorDay);
|
|
107
|
+
if (cursor === null) return [];
|
|
108
|
+
const start = monthGridStartMs(cursor);
|
|
109
|
+
return Array.from({ length: 42 }, (_, index) => formatDay(addDays(start, index)));
|
|
110
|
+
}
|
|
111
|
+
function weekDays(cursorDay) {
|
|
112
|
+
const cursor = parseDay(cursorDay);
|
|
113
|
+
if (cursor === null) return [];
|
|
114
|
+
const start = addDays(cursor, -dayOfWeekMon0(cursor));
|
|
115
|
+
return Array.from({ length: 7 }, (_, index) => formatDay(addDays(start, index)));
|
|
116
|
+
}
|
|
117
|
+
function daysInRange(range) {
|
|
118
|
+
const from = parseDay(range.desde);
|
|
119
|
+
const to = parseDay(range.hasta);
|
|
120
|
+
if (from === null || to === null || to < from) return [];
|
|
121
|
+
const days = [];
|
|
122
|
+
for (let ms = from; ms <= to; ms = addDays(ms, 1)) days.push(formatDay(ms));
|
|
123
|
+
return days;
|
|
124
|
+
}
|
|
125
|
+
function visibleRangeFor(mode, cursorDay) {
|
|
126
|
+
const cursor = parseDay(cursorDay);
|
|
127
|
+
if (cursor === null) return {
|
|
128
|
+
desde: "",
|
|
129
|
+
hasta: ""
|
|
130
|
+
};
|
|
131
|
+
switch (mode) {
|
|
132
|
+
case "month": {
|
|
133
|
+
const start = monthGridStartMs(cursor);
|
|
134
|
+
return {
|
|
135
|
+
desde: formatDay(start),
|
|
136
|
+
hasta: formatDay(addDays(start, 41))
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
case "week": {
|
|
140
|
+
const start = addDays(cursor, -dayOfWeekMon0(cursor));
|
|
141
|
+
return {
|
|
142
|
+
desde: formatDay(start),
|
|
143
|
+
hasta: formatDay(addDays(start, 6))
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
case "day": return {
|
|
147
|
+
desde: formatDay(cursor),
|
|
148
|
+
hasta: formatDay(cursor)
|
|
149
|
+
};
|
|
150
|
+
case "agenda": return {
|
|
151
|
+
desde: formatDay(cursor),
|
|
152
|
+
hasta: formatDay(addDays(cursor, 29))
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function shiftCursor(mode, cursorDay, delta) {
|
|
157
|
+
const cursor = parseDay(cursorDay) ?? parseDay(todayCivil()) ?? Date.UTC(1970, 0, 1);
|
|
158
|
+
switch (mode) {
|
|
159
|
+
case "month": return formatDay(shiftMonths(cursor, delta));
|
|
160
|
+
case "week": return formatDay(addDays(cursor, 7 * delta));
|
|
161
|
+
case "day": return formatDay(addDays(cursor, delta));
|
|
162
|
+
case "agenda": return formatDay(addDays(cursor, 30 * delta));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function sameMonth(a, b) {
|
|
166
|
+
const msA = parseDay(a);
|
|
167
|
+
const msB = parseDay(b);
|
|
168
|
+
if (msA === null || msB === null) return false;
|
|
169
|
+
const dateA = new Date(msA);
|
|
170
|
+
const dateB = new Date(msB);
|
|
171
|
+
return dateA.getUTCFullYear() === dateB.getUTCFullYear() && dateA.getUTCMonth() === dateB.getUTCMonth();
|
|
172
|
+
}
|
|
173
|
+
function findFieldByKey(fields, key) {
|
|
174
|
+
for (const field of fields) {
|
|
175
|
+
if (field.key === key) return field;
|
|
176
|
+
if (field.type === "group") {
|
|
177
|
+
const hit = findFieldByKey(field.fields, key);
|
|
178
|
+
if (hit !== void 0) return hit;
|
|
179
|
+
} else if (field.type === "list") {
|
|
180
|
+
const hit = findFieldByKey(field.itemFields, key);
|
|
181
|
+
if (hit !== void 0) return hit;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function mapCalendarRows(rows, config, options) {
|
|
186
|
+
const events = [];
|
|
187
|
+
const undated = [];
|
|
188
|
+
const invalid = [];
|
|
189
|
+
for (const row of rows) {
|
|
190
|
+
const startMs = parseDay(row[config.startField]);
|
|
191
|
+
if (startMs === null) {
|
|
192
|
+
undated.push(row);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
let endMs = startMs;
|
|
196
|
+
if (config.endField !== void 0 && options.endFieldType !== "time") {
|
|
197
|
+
const rawEnd = row[config.endField];
|
|
198
|
+
if (!(rawEnd === null || rawEnd === void 0 || rawEnd === "")) {
|
|
199
|
+
const parsedEnd = parseDay(rawEnd);
|
|
200
|
+
if (parsedEnd === null) {
|
|
201
|
+
invalid.push({
|
|
202
|
+
row,
|
|
203
|
+
reason: REASON_INVALID_END
|
|
204
|
+
});
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (parsedEnd < startMs) {
|
|
208
|
+
invalid.push({
|
|
209
|
+
row,
|
|
210
|
+
reason: REASON_END_BEFORE_START
|
|
211
|
+
});
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
endMs = parsedEnd;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const key = String(row[options.keyField] ?? "");
|
|
218
|
+
const title = String(row[config.titleField] ?? "").trim() || key;
|
|
219
|
+
const rawColor = config.colorField !== void 0 ? row[config.colorField] : void 0;
|
|
220
|
+
const color = typeof rawColor === "string" && rawColor !== "" ? rawColor : void 0;
|
|
221
|
+
events.push({
|
|
222
|
+
key,
|
|
223
|
+
title,
|
|
224
|
+
startDay: formatDay(startMs),
|
|
225
|
+
endDay: formatDay(endMs),
|
|
226
|
+
color,
|
|
227
|
+
row
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
events,
|
|
232
|
+
undated,
|
|
233
|
+
invalid
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function eventsByDay(events, range) {
|
|
237
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
238
|
+
const from = parseDay(range.desde);
|
|
239
|
+
const to = parseDay(range.hasta);
|
|
240
|
+
if (from === null || to === null || to < from) return byDay;
|
|
241
|
+
for (const event of events) {
|
|
242
|
+
const start = parseDay(event.startDay);
|
|
243
|
+
const end = parseDay(event.endDay);
|
|
244
|
+
if (start === null || end === null) continue;
|
|
245
|
+
const paintFrom = Math.max(start, from);
|
|
246
|
+
const paintTo = Math.min(end, to);
|
|
247
|
+
for (let ms = paintFrom; ms <= paintTo; ms = addDays(ms, 1)) {
|
|
248
|
+
const day = formatDay(ms);
|
|
249
|
+
const bucket = byDay.get(day);
|
|
250
|
+
if (bucket === void 0) byDay.set(day, [event]);
|
|
251
|
+
else bucket.push(event);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return byDay;
|
|
255
|
+
}
|
|
256
|
+
function dayLabel(day) {
|
|
257
|
+
const ms = parseDay(day);
|
|
258
|
+
if (ms === null) return day;
|
|
259
|
+
const date = new Date(ms);
|
|
260
|
+
return `${date.getUTCDate()} de ${MONTHS[date.getUTCMonth()]} de ${date.getUTCFullYear()}`;
|
|
261
|
+
}
|
|
262
|
+
function weekdayShort(day) {
|
|
263
|
+
const ms = parseDay(day);
|
|
264
|
+
if (ms === null) return "";
|
|
265
|
+
return WEEKDAYS_SHORT[dayOfWeekMon0(ms)];
|
|
266
|
+
}
|
|
267
|
+
function weekdayFull(day) {
|
|
268
|
+
const ms = parseDay(day);
|
|
269
|
+
if (ms === null) return "";
|
|
270
|
+
return WEEKDAYS[dayOfWeekMon0(ms)];
|
|
271
|
+
}
|
|
272
|
+
function agendaDayLabel(day) {
|
|
273
|
+
const weekday = weekdayFull(day);
|
|
274
|
+
return weekday !== "" ? `${weekday}, ${dayLabel(day)}` : dayLabel(day);
|
|
275
|
+
}
|
|
276
|
+
function rangeLabelFor(mode, cursorDay) {
|
|
277
|
+
const cursor = parseDay(cursorDay);
|
|
278
|
+
if (cursor === null) return "";
|
|
279
|
+
const date = new Date(cursor);
|
|
280
|
+
if (mode === "month") return `${MONTHS[date.getUTCMonth()]} de ${date.getUTCFullYear()}`;
|
|
281
|
+
const range = visibleRangeFor(mode, cursorDay);
|
|
282
|
+
const desde = parseDay(range.desde);
|
|
283
|
+
const hasta = parseDay(range.hasta);
|
|
284
|
+
if (desde === null || hasta === null) return "";
|
|
285
|
+
const startDate = new Date(desde);
|
|
286
|
+
const endDate = new Date(hasta);
|
|
287
|
+
const sameYear = startDate.getUTCFullYear() === endDate.getUTCFullYear();
|
|
288
|
+
return `${`${startDate.getUTCDate()} ${MONTHS_SHORT[startDate.getUTCMonth()]}${sameYear ? "" : ` ${startDate.getUTCFullYear()}`}`} – ${endDate.getUTCDate()} ${MONTHS_SHORT[endDate.getUTCMonth()]} ${endDate.getUTCFullYear()}`;
|
|
289
|
+
}
|
|
290
|
+
function calendarConfigOf(view) {
|
|
291
|
+
if (!view || typeof view !== "object") return void 0;
|
|
292
|
+
const candidate = view;
|
|
293
|
+
return candidate.type === "calendar" && candidate.config !== void 0 ? candidate.config : void 0;
|
|
294
|
+
}
|
|
295
|
+
const MODE_OPTIONS = [
|
|
296
|
+
{
|
|
297
|
+
mode: "month",
|
|
298
|
+
label: "Mes"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
mode: "week",
|
|
302
|
+
label: "Semana"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
mode: "day",
|
|
306
|
+
label: "Día"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
mode: "agenda",
|
|
310
|
+
label: "Agenda"
|
|
311
|
+
}
|
|
312
|
+
];
|
|
313
|
+
const NO_EVENTS = [];
|
|
314
|
+
var CalendarView = class CalendarView {
|
|
315
|
+
route = inject(ActivatedRoute);
|
|
316
|
+
routeData = toSignal(this.route.data, { requireSync: true });
|
|
317
|
+
dataSource = inject(DataSourceService);
|
|
318
|
+
errorMapper = inject(ErrorMapperService);
|
|
319
|
+
schema = computed(() => {
|
|
320
|
+
const data = this.routeData();
|
|
321
|
+
const explicit = data["schema"];
|
|
322
|
+
if (explicit !== void 0) return explicit;
|
|
323
|
+
const resourceId = data["resourceId"];
|
|
324
|
+
return typeof resourceId === "string" ? getSchema(resourceId) : void 0;
|
|
325
|
+
}, ...ngDevMode ? [{ debugName: "schema" }] : /* istanbul ignore next */ []);
|
|
326
|
+
config = computed(() => {
|
|
327
|
+
const schema = this.schema();
|
|
328
|
+
const viewName = this.routeData()["uiView"];
|
|
329
|
+
if (schema === void 0 || typeof viewName !== "string") return void 0;
|
|
330
|
+
const cfg = calendarConfigOf(schema.uiViews?.[viewName]);
|
|
331
|
+
if (cfg === void 0) return void 0;
|
|
332
|
+
return typeof cfg.startField === "string" && cfg.startField !== "" && typeof cfg.titleField === "string" && cfg.titleField !== "" ? cfg : void 0;
|
|
333
|
+
}, ...ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []);
|
|
334
|
+
explicitMode = signal(void 0, ...ngDevMode ? [{ debugName: "explicitMode" }] : /* istanbul ignore next */ []);
|
|
335
|
+
mode = computed(() => this.explicitMode() ?? this.config()?.defaultView ?? "month", ...ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []);
|
|
336
|
+
cursor = signal(todayCivil(), ...ngDevMode ? [{ debugName: "cursor" }] : /* istanbul ignore next */ []);
|
|
337
|
+
visibleRange = computed(() => this.config() === void 0 ? null : visibleRangeFor(this.mode(), this.cursor()), ...ngDevMode ? [{ debugName: "visibleRange" }] : /* istanbul ignore next */ []);
|
|
338
|
+
rangeLabel = computed(() => this.config() === void 0 ? "" : rangeLabelFor(this.mode(), this.cursor()), ...ngDevMode ? [{ debugName: "rangeLabel" }] : /* istanbul ignore next */ []);
|
|
339
|
+
status = signal("loading", ...ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []);
|
|
340
|
+
rows = signal([], ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
|
|
341
|
+
seleccionada = signal(null, ...ngDevMode ? [{ debugName: "seleccionada" }] : /* istanbul ignore next */ []);
|
|
342
|
+
errorMessage = signal("", ...ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []);
|
|
343
|
+
modeOptions = MODE_OPTIONS;
|
|
344
|
+
weekdayHeadings = WEEKDAY_HEADINGS;
|
|
345
|
+
reasonUndated = REASON_UNDATED;
|
|
346
|
+
loadEpoch = 0;
|
|
347
|
+
constructor() {
|
|
348
|
+
effect(() => {
|
|
349
|
+
const cfg = this.config();
|
|
350
|
+
const range = this.visibleRange();
|
|
351
|
+
if (cfg === void 0 || range === null) return;
|
|
352
|
+
this.load(cfg, range);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
mapping = computed(() => {
|
|
356
|
+
const cfg = this.config();
|
|
357
|
+
const schema = this.schema();
|
|
358
|
+
if (cfg === void 0 || schema === void 0) return {
|
|
359
|
+
events: [],
|
|
360
|
+
undated: [],
|
|
361
|
+
invalid: []
|
|
362
|
+
};
|
|
363
|
+
const endFieldType = cfg.endField !== void 0 ? findFieldByKey(schema.fields, cfg.endField)?.type : void 0;
|
|
364
|
+
return mapCalendarRows(this.rows(), cfg, {
|
|
365
|
+
keyField: schema.keyField,
|
|
366
|
+
endFieldType
|
|
367
|
+
});
|
|
368
|
+
}, ...ngDevMode ? [{ debugName: "mapping" }] : /* istanbul ignore next */ []);
|
|
369
|
+
eventsByDay = computed(() => {
|
|
370
|
+
const range = this.visibleRange();
|
|
371
|
+
if (range === null) return /* @__PURE__ */ new Map();
|
|
372
|
+
return eventsByDay(this.mapping().events, range);
|
|
373
|
+
}, ...ngDevMode ? [{ debugName: "eventsByDay" }] : /* istanbul ignore next */ []);
|
|
374
|
+
monthGrid = computed(() => monthGridDays(this.cursor()), ...ngDevMode ? [{ debugName: "monthGrid" }] : /* istanbul ignore next */ []);
|
|
375
|
+
weekColumnDays = computed(() => weekDays(this.cursor()), ...ngDevMode ? [{ debugName: "weekColumnDays" }] : /* istanbul ignore next */ []);
|
|
376
|
+
agendaDays = computed(() => {
|
|
377
|
+
const range = this.visibleRange();
|
|
378
|
+
if (range === null) return [];
|
|
379
|
+
return daysInRange(range).filter((day) => this.eventsByDay().has(day));
|
|
380
|
+
}, ...ngDevMode ? [{ debugName: "agendaDays" }] : /* istanbul ignore next */ []);
|
|
381
|
+
hasProblemRecords = computed(() => this.mapping().undated.length > 0 || this.mapping().invalid.length > 0, ...ngDevMode ? [{ debugName: "hasProblemRecords" }] : /* istanbul ignore next */ []);
|
|
382
|
+
previous() {
|
|
383
|
+
this.cursor.set(shiftCursor(this.mode(), this.cursor(), -1));
|
|
384
|
+
}
|
|
385
|
+
next() {
|
|
386
|
+
this.cursor.set(shiftCursor(this.mode(), this.cursor(), 1));
|
|
387
|
+
}
|
|
388
|
+
today() {
|
|
389
|
+
this.cursor.set(todayCivil());
|
|
390
|
+
}
|
|
391
|
+
setMode(mode) {
|
|
392
|
+
this.explicitMode.set(mode);
|
|
393
|
+
}
|
|
394
|
+
retry() {
|
|
395
|
+
const cfg = this.config();
|
|
396
|
+
const range = this.visibleRange();
|
|
397
|
+
if (cfg !== void 0 && range !== null) this.load(cfg, range);
|
|
398
|
+
}
|
|
399
|
+
binding() {
|
|
400
|
+
return this.routeData()["select"];
|
|
401
|
+
}
|
|
402
|
+
seleccionar(event) {
|
|
403
|
+
const binding = this.binding();
|
|
404
|
+
if (binding === void 0) return;
|
|
405
|
+
if (binding.mode === "inline") {
|
|
406
|
+
this.seleccionada.set(event.key);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (binding.mode === "none") return;
|
|
410
|
+
const schema = this.schema();
|
|
411
|
+
const emit = this.routeData()["emit"];
|
|
412
|
+
emit?.({
|
|
413
|
+
resource: schema?.id ?? String(this.routeData()["resourceId"] ?? ""),
|
|
414
|
+
key: event.key,
|
|
415
|
+
action: binding.on
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
seleccionable() {
|
|
419
|
+
const binding = this.binding();
|
|
420
|
+
return binding !== void 0 && binding.mode !== "none";
|
|
421
|
+
}
|
|
422
|
+
esSeleccionada(event) {
|
|
423
|
+
return this.seleccionable() && this.seleccionada() === event.key;
|
|
424
|
+
}
|
|
425
|
+
eventsFor(day) {
|
|
426
|
+
return this.eventsByDay().get(day) ?? NO_EVENTS;
|
|
427
|
+
}
|
|
428
|
+
dayNumber(day) {
|
|
429
|
+
return Number(day.slice(8, 10));
|
|
430
|
+
}
|
|
431
|
+
isInCursorMonth(day) {
|
|
432
|
+
return sameMonth(day, this.cursor());
|
|
433
|
+
}
|
|
434
|
+
weekdayShort(day) {
|
|
435
|
+
return weekdayShort(day);
|
|
436
|
+
}
|
|
437
|
+
dayHeading(day) {
|
|
438
|
+
return agendaDayLabel(day);
|
|
439
|
+
}
|
|
440
|
+
recordLabel(row) {
|
|
441
|
+
const schema = this.schema();
|
|
442
|
+
if (schema === void 0) return "";
|
|
443
|
+
const title = String(row[schema.titleField] ?? "").trim();
|
|
444
|
+
return title !== "" ? title : String(row[schema.keyField] ?? "");
|
|
445
|
+
}
|
|
446
|
+
async load(cfg, range) {
|
|
447
|
+
const schema = this.schema();
|
|
448
|
+
if (schema === void 0) return;
|
|
449
|
+
const epoch = ++this.loadEpoch;
|
|
450
|
+
this.status.set("loading");
|
|
451
|
+
const ctx = cfg.source?.view !== void 0 && cfg.source.view !== "" ? {
|
|
452
|
+
desde: range.desde,
|
|
453
|
+
hasta: range.hasta
|
|
454
|
+
} : void 0;
|
|
455
|
+
const source = cfg.source ?? { resource: schema.id };
|
|
456
|
+
try {
|
|
457
|
+
const rows = await this.dataSource.rows(source, ctx);
|
|
458
|
+
if (epoch !== this.loadEpoch) return;
|
|
459
|
+
this.rows.set(rows);
|
|
460
|
+
this.errorMessage.set("");
|
|
461
|
+
this.status.set("ready");
|
|
462
|
+
} catch (error) {
|
|
463
|
+
if (epoch !== this.loadEpoch) return;
|
|
464
|
+
this.errorMessage.set(this.errorMapper.map(error).general ?? "No se pudo cargar el calendario.");
|
|
465
|
+
this.status.set("error");
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
469
|
+
minVersion: "12.0.0",
|
|
470
|
+
version: "22.2.3",
|
|
471
|
+
ngImport: i0,
|
|
472
|
+
type: CalendarView,
|
|
473
|
+
deps: [],
|
|
474
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
475
|
+
});
|
|
476
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
477
|
+
minVersion: "17.0.0",
|
|
478
|
+
version: "22.2.3",
|
|
479
|
+
type: CalendarView,
|
|
480
|
+
isStandalone: true,
|
|
481
|
+
selector: "app-calendar-view",
|
|
482
|
+
ngImport: i0,
|
|
483
|
+
template: `
|
|
484
|
+
@if (config(); as cfg) {
|
|
485
|
+
<section class="space-y-4" aria-label="Calendario" [attr.aria-busy]="status() === 'loading'">
|
|
486
|
+
<header class="flex flex-wrap items-center justify-between gap-3">
|
|
487
|
+
<h2 class="font-display text-xl font-semibold">{{ rangeLabel() }}</h2>
|
|
488
|
+
<div class="flex items-center gap-1">
|
|
489
|
+
<button
|
|
490
|
+
app-button
|
|
491
|
+
variant="outline"
|
|
492
|
+
size="icon"
|
|
493
|
+
type="button"
|
|
494
|
+
aria-label="Período anterior"
|
|
495
|
+
title="Período anterior"
|
|
496
|
+
(click)="previous()"
|
|
497
|
+
>
|
|
498
|
+
<svg lucideChevronLeft size="16" aria-hidden="true" />
|
|
499
|
+
</button>
|
|
500
|
+
<button app-button variant="outline" size="sm" type="button" (click)="today()">
|
|
501
|
+
Hoy
|
|
502
|
+
</button>
|
|
503
|
+
<button
|
|
504
|
+
app-button
|
|
505
|
+
variant="outline"
|
|
506
|
+
size="icon"
|
|
507
|
+
type="button"
|
|
508
|
+
aria-label="Período siguiente"
|
|
509
|
+
title="Período siguiente"
|
|
510
|
+
(click)="next()"
|
|
511
|
+
>
|
|
512
|
+
<svg lucideChevronRight size="16" aria-hidden="true" />
|
|
513
|
+
</button>
|
|
514
|
+
</div>
|
|
515
|
+
<div class="flex items-center gap-1" role="group" aria-label="Vista del calendario">
|
|
516
|
+
@for (option of modeOptions; track option.mode) {
|
|
517
|
+
<button
|
|
518
|
+
app-button
|
|
519
|
+
variant="ghost"
|
|
520
|
+
size="sm"
|
|
521
|
+
type="button"
|
|
522
|
+
[attr.aria-pressed]="mode() === option.mode"
|
|
523
|
+
(click)="setMode(option.mode)"
|
|
524
|
+
>
|
|
525
|
+
{{ option.label }}
|
|
526
|
+
</button>
|
|
527
|
+
}
|
|
528
|
+
</div>
|
|
529
|
+
</header>
|
|
530
|
+
|
|
531
|
+
@switch (status()) {
|
|
532
|
+
@case ('loading') {
|
|
533
|
+
<app-skeleton [lines]="8" [height]="32" />
|
|
534
|
+
}
|
|
535
|
+
@case ('error') {
|
|
536
|
+
<app-empty-state title="No se pudo cargar el calendario" [message]="errorMessage()">
|
|
537
|
+
<button app-button variant="outline" type="button" (click)="retry()">
|
|
538
|
+
Reintentar
|
|
539
|
+
</button>
|
|
540
|
+
</app-empty-state>
|
|
541
|
+
}
|
|
542
|
+
@case ('ready') {
|
|
543
|
+
@if (mapping().events.length === 0) {
|
|
544
|
+
<app-empty-state
|
|
545
|
+
title="Sin eventos"
|
|
546
|
+
message="No hay eventos con fecha válida en este período."
|
|
547
|
+
/>
|
|
548
|
+
} @else {
|
|
549
|
+
@switch (mode()) {
|
|
550
|
+
@case ('month') {
|
|
551
|
+
<div class="overflow-x-auto rounded-xl border border-neutral/20 bg-surface">
|
|
552
|
+
<div class="grid min-w-[640px] grid-cols-7">
|
|
553
|
+
@for (heading of weekdayHeadings; track heading) {
|
|
554
|
+
<div
|
|
555
|
+
class="border-b border-neutral/15 px-2 py-1.5 text-xs font-medium uppercase text-neutral"
|
|
556
|
+
>
|
|
557
|
+
{{ heading }}
|
|
558
|
+
</div>
|
|
559
|
+
}
|
|
560
|
+
@for (day of monthGrid(); track day) {
|
|
561
|
+
<div
|
|
562
|
+
class="min-h-24 border-b border-r border-neutral/10 p-1.5"
|
|
563
|
+
[attr.data-day]="day"
|
|
564
|
+
[class.opacity-40]="!isInCursorMonth(day)"
|
|
565
|
+
>
|
|
566
|
+
<span class="text-xs font-medium">{{ dayNumber(day) }}</span>
|
|
567
|
+
<ul class="mt-1 space-y-0.5">
|
|
568
|
+
@for (event of eventsFor(day); track event.key) {
|
|
569
|
+
<li
|
|
570
|
+
class="flex items-center gap-1 text-xs"
|
|
571
|
+
[class.cursor-pointer]="seleccionable()"
|
|
572
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
573
|
+
[attr.data-event-key]="event.key"
|
|
574
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
575
|
+
(click)="seleccionar(event)"
|
|
576
|
+
>
|
|
577
|
+
<span
|
|
578
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
579
|
+
[style.background-color]="event.color ?? null"
|
|
580
|
+
aria-hidden="true"
|
|
581
|
+
></span>
|
|
582
|
+
<span class="truncate">{{ event.title }}</span>
|
|
583
|
+
</li>
|
|
584
|
+
}
|
|
585
|
+
</ul>
|
|
586
|
+
</div>
|
|
587
|
+
}
|
|
588
|
+
</div>
|
|
589
|
+
</div>
|
|
590
|
+
}
|
|
591
|
+
@case ('week') {
|
|
592
|
+
<div class="overflow-x-auto rounded-xl border border-neutral/20 bg-surface">
|
|
593
|
+
<div class="grid min-w-[560px] grid-cols-7">
|
|
594
|
+
@for (day of weekColumnDays(); track day) {
|
|
595
|
+
<div class="min-h-32 border-r border-neutral/10 p-2" [attr.data-day]="day">
|
|
596
|
+
<span class="text-xs font-medium uppercase text-neutral">
|
|
597
|
+
{{ weekdayShort(day) }} {{ dayNumber(day) }}
|
|
598
|
+
</span>
|
|
599
|
+
<ul class="mt-2 space-y-1">
|
|
600
|
+
@for (event of eventsFor(day); track event.key) {
|
|
601
|
+
<li
|
|
602
|
+
class="flex items-center gap-1.5 text-sm"
|
|
603
|
+
[class.cursor-pointer]="seleccionable()"
|
|
604
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
605
|
+
[attr.data-event-key]="event.key"
|
|
606
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
607
|
+
(click)="seleccionar(event)"
|
|
608
|
+
>
|
|
609
|
+
<span
|
|
610
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
611
|
+
[style.background-color]="event.color ?? null"
|
|
612
|
+
aria-hidden="true"
|
|
613
|
+
></span>
|
|
614
|
+
<span class="truncate">{{ event.title }}</span>
|
|
615
|
+
</li>
|
|
616
|
+
}
|
|
617
|
+
</ul>
|
|
618
|
+
</div>
|
|
619
|
+
}
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
}
|
|
623
|
+
@case ('day') {
|
|
624
|
+
<div
|
|
625
|
+
class="rounded-xl border border-neutral/20 bg-surface p-4"
|
|
626
|
+
[attr.data-day]="cursor()"
|
|
627
|
+
>
|
|
628
|
+
<h3 class="font-display text-base font-semibold">
|
|
629
|
+
{{ dayHeading(cursor()) }}
|
|
630
|
+
</h3>
|
|
631
|
+
@if (eventsFor(cursor()).length === 0) {
|
|
632
|
+
<p class="mt-2 text-sm text-neutral">Sin eventos este día.</p>
|
|
633
|
+
} @else {
|
|
634
|
+
<ul class="mt-3 space-y-2">
|
|
635
|
+
@for (event of eventsFor(cursor()); track event.key) {
|
|
636
|
+
<li
|
|
637
|
+
class="flex items-center gap-1 text-xs"
|
|
638
|
+
[class.cursor-pointer]="seleccionable()"
|
|
639
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
640
|
+
[attr.data-event-key]="event.key"
|
|
641
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
642
|
+
(click)="seleccionar(event)"
|
|
643
|
+
>
|
|
644
|
+
<span
|
|
645
|
+
class="size-2.5 shrink-0 rounded-full bg-neutral/30"
|
|
646
|
+
[style.background-color]="event.color ?? null"
|
|
647
|
+
aria-hidden="true"
|
|
648
|
+
></span>
|
|
649
|
+
<span>{{ event.title }}</span>
|
|
650
|
+
</li>
|
|
651
|
+
}
|
|
652
|
+
</ul>
|
|
653
|
+
}
|
|
654
|
+
</div>
|
|
655
|
+
}
|
|
656
|
+
@case ('agenda') {
|
|
657
|
+
<ol class="space-y-4">
|
|
658
|
+
@for (day of agendaDays(); track day) {
|
|
659
|
+
<li class="rounded-xl border border-neutral/20 bg-surface p-4">
|
|
660
|
+
<h3 class="text-sm font-semibold">{{ dayHeading(day) }}</h3>
|
|
661
|
+
<ul class="mt-2 space-y-1">
|
|
662
|
+
@for (event of eventsFor(day); track event.key) {
|
|
663
|
+
<li
|
|
664
|
+
class="flex items-center gap-2 text-sm"
|
|
665
|
+
[class.cursor-pointer]="seleccionable()"
|
|
666
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
667
|
+
[attr.data-event-key]="event.key"
|
|
668
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
669
|
+
(click)="seleccionar(event)"
|
|
670
|
+
>
|
|
671
|
+
<span
|
|
672
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
673
|
+
[style.background-color]="event.color ?? null"
|
|
674
|
+
aria-hidden="true"
|
|
675
|
+
></span>
|
|
676
|
+
<span>{{ event.title }}</span>
|
|
677
|
+
</li>
|
|
678
|
+
}
|
|
679
|
+
</ul>
|
|
680
|
+
</li>
|
|
681
|
+
}
|
|
682
|
+
</ol>
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
<!-- Problem records are VISIBLE, never silently dropped: a row
|
|
688
|
+
without a valid start or with an impossible interval cannot
|
|
689
|
+
be painted, but the operator must see why. -->
|
|
690
|
+
@if (hasProblemRecords()) {
|
|
691
|
+
<section
|
|
692
|
+
class="rounded-xl border border-neutral/30 p-4"
|
|
693
|
+
aria-label="Registros sin fecha válida"
|
|
694
|
+
>
|
|
695
|
+
<h3 class="font-display text-sm font-semibold">Registros sin fecha válida</h3>
|
|
696
|
+
<p class="mt-1 text-help text-neutral">
|
|
697
|
+
Estos registros no se pueden pintar en el calendario:
|
|
698
|
+
</p>
|
|
699
|
+
<ul class="mt-2 space-y-1 text-sm">
|
|
700
|
+
@for (row of mapping().undated; track $index) {
|
|
701
|
+
<li>{{ recordLabel(row) }} — {{ reasonUndated }}</li>
|
|
702
|
+
}
|
|
703
|
+
@for (item of mapping().invalid; track $index) {
|
|
704
|
+
<li>{{ recordLabel(item.row) }} — {{ item.reason }}</li>
|
|
705
|
+
}
|
|
706
|
+
</ul>
|
|
707
|
+
</section>
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
</section>
|
|
712
|
+
} @else {
|
|
713
|
+
<app-empty-state
|
|
714
|
+
title="Configuración inválida"
|
|
715
|
+
message="Esta vista no declara una configuración de calendario válida."
|
|
716
|
+
/>
|
|
717
|
+
}
|
|
718
|
+
`,
|
|
719
|
+
isInline: true,
|
|
720
|
+
dependencies: [
|
|
721
|
+
{
|
|
722
|
+
kind: "component",
|
|
723
|
+
type: Button,
|
|
724
|
+
selector: "button[app-button]",
|
|
725
|
+
inputs: [
|
|
726
|
+
"variant",
|
|
727
|
+
"size",
|
|
728
|
+
"type",
|
|
729
|
+
"disabled",
|
|
730
|
+
"loading"
|
|
731
|
+
]
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
kind: "component",
|
|
735
|
+
type: EmptyState,
|
|
736
|
+
selector: "app-empty-state",
|
|
737
|
+
inputs: ["title", "message"]
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
kind: "component",
|
|
741
|
+
type: LucideChevronLeft,
|
|
742
|
+
selector: "svg[lucideChevronLeft]"
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
kind: "component",
|
|
746
|
+
type: LucideChevronRight,
|
|
747
|
+
selector: "svg[lucideChevronRight]"
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
kind: "component",
|
|
751
|
+
type: Skeleton,
|
|
752
|
+
selector: "app-skeleton",
|
|
753
|
+
inputs: [
|
|
754
|
+
"lines",
|
|
755
|
+
"height",
|
|
756
|
+
"width"
|
|
757
|
+
]
|
|
758
|
+
}
|
|
759
|
+
],
|
|
760
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
761
|
+
});
|
|
762
|
+
};
|
|
763
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
764
|
+
minVersion: "12.0.0",
|
|
765
|
+
version: "22.2.3",
|
|
766
|
+
ngImport: i0,
|
|
767
|
+
type: CalendarView,
|
|
768
|
+
decorators: [{
|
|
769
|
+
type: Component,
|
|
770
|
+
args: [{
|
|
771
|
+
selector: "app-calendar-view",
|
|
772
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
773
|
+
imports: [
|
|
774
|
+
Button,
|
|
775
|
+
EmptyState,
|
|
776
|
+
LucideChevronLeft,
|
|
777
|
+
LucideChevronRight,
|
|
778
|
+
Skeleton
|
|
779
|
+
],
|
|
780
|
+
template: `
|
|
781
|
+
@if (config(); as cfg) {
|
|
782
|
+
<section class="space-y-4" aria-label="Calendario" [attr.aria-busy]="status() === 'loading'">
|
|
783
|
+
<header class="flex flex-wrap items-center justify-between gap-3">
|
|
784
|
+
<h2 class="font-display text-xl font-semibold">{{ rangeLabel() }}</h2>
|
|
785
|
+
<div class="flex items-center gap-1">
|
|
786
|
+
<button
|
|
787
|
+
app-button
|
|
788
|
+
variant="outline"
|
|
789
|
+
size="icon"
|
|
790
|
+
type="button"
|
|
791
|
+
aria-label="Período anterior"
|
|
792
|
+
title="Período anterior"
|
|
793
|
+
(click)="previous()"
|
|
794
|
+
>
|
|
795
|
+
<svg lucideChevronLeft size="16" aria-hidden="true" />
|
|
796
|
+
</button>
|
|
797
|
+
<button app-button variant="outline" size="sm" type="button" (click)="today()">
|
|
798
|
+
Hoy
|
|
799
|
+
</button>
|
|
800
|
+
<button
|
|
801
|
+
app-button
|
|
802
|
+
variant="outline"
|
|
803
|
+
size="icon"
|
|
804
|
+
type="button"
|
|
805
|
+
aria-label="Período siguiente"
|
|
806
|
+
title="Período siguiente"
|
|
807
|
+
(click)="next()"
|
|
808
|
+
>
|
|
809
|
+
<svg lucideChevronRight size="16" aria-hidden="true" />
|
|
810
|
+
</button>
|
|
811
|
+
</div>
|
|
812
|
+
<div class="flex items-center gap-1" role="group" aria-label="Vista del calendario">
|
|
813
|
+
@for (option of modeOptions; track option.mode) {
|
|
814
|
+
<button
|
|
815
|
+
app-button
|
|
816
|
+
variant="ghost"
|
|
817
|
+
size="sm"
|
|
818
|
+
type="button"
|
|
819
|
+
[attr.aria-pressed]="mode() === option.mode"
|
|
820
|
+
(click)="setMode(option.mode)"
|
|
821
|
+
>
|
|
822
|
+
{{ option.label }}
|
|
823
|
+
</button>
|
|
824
|
+
}
|
|
825
|
+
</div>
|
|
826
|
+
</header>
|
|
827
|
+
|
|
828
|
+
@switch (status()) {
|
|
829
|
+
@case ('loading') {
|
|
830
|
+
<app-skeleton [lines]="8" [height]="32" />
|
|
831
|
+
}
|
|
832
|
+
@case ('error') {
|
|
833
|
+
<app-empty-state title="No se pudo cargar el calendario" [message]="errorMessage()">
|
|
834
|
+
<button app-button variant="outline" type="button" (click)="retry()">
|
|
835
|
+
Reintentar
|
|
836
|
+
</button>
|
|
837
|
+
</app-empty-state>
|
|
838
|
+
}
|
|
839
|
+
@case ('ready') {
|
|
840
|
+
@if (mapping().events.length === 0) {
|
|
841
|
+
<app-empty-state
|
|
842
|
+
title="Sin eventos"
|
|
843
|
+
message="No hay eventos con fecha válida en este período."
|
|
844
|
+
/>
|
|
845
|
+
} @else {
|
|
846
|
+
@switch (mode()) {
|
|
847
|
+
@case ('month') {
|
|
848
|
+
<div class="overflow-x-auto rounded-xl border border-neutral/20 bg-surface">
|
|
849
|
+
<div class="grid min-w-[640px] grid-cols-7">
|
|
850
|
+
@for (heading of weekdayHeadings; track heading) {
|
|
851
|
+
<div
|
|
852
|
+
class="border-b border-neutral/15 px-2 py-1.5 text-xs font-medium uppercase text-neutral"
|
|
853
|
+
>
|
|
854
|
+
{{ heading }}
|
|
855
|
+
</div>
|
|
856
|
+
}
|
|
857
|
+
@for (day of monthGrid(); track day) {
|
|
858
|
+
<div
|
|
859
|
+
class="min-h-24 border-b border-r border-neutral/10 p-1.5"
|
|
860
|
+
[attr.data-day]="day"
|
|
861
|
+
[class.opacity-40]="!isInCursorMonth(day)"
|
|
862
|
+
>
|
|
863
|
+
<span class="text-xs font-medium">{{ dayNumber(day) }}</span>
|
|
864
|
+
<ul class="mt-1 space-y-0.5">
|
|
865
|
+
@for (event of eventsFor(day); track event.key) {
|
|
866
|
+
<li
|
|
867
|
+
class="flex items-center gap-1 text-xs"
|
|
868
|
+
[class.cursor-pointer]="seleccionable()"
|
|
869
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
870
|
+
[attr.data-event-key]="event.key"
|
|
871
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
872
|
+
(click)="seleccionar(event)"
|
|
873
|
+
>
|
|
874
|
+
<span
|
|
875
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
876
|
+
[style.background-color]="event.color ?? null"
|
|
877
|
+
aria-hidden="true"
|
|
878
|
+
></span>
|
|
879
|
+
<span class="truncate">{{ event.title }}</span>
|
|
880
|
+
</li>
|
|
881
|
+
}
|
|
882
|
+
</ul>
|
|
883
|
+
</div>
|
|
884
|
+
}
|
|
885
|
+
</div>
|
|
886
|
+
</div>
|
|
887
|
+
}
|
|
888
|
+
@case ('week') {
|
|
889
|
+
<div class="overflow-x-auto rounded-xl border border-neutral/20 bg-surface">
|
|
890
|
+
<div class="grid min-w-[560px] grid-cols-7">
|
|
891
|
+
@for (day of weekColumnDays(); track day) {
|
|
892
|
+
<div class="min-h-32 border-r border-neutral/10 p-2" [attr.data-day]="day">
|
|
893
|
+
<span class="text-xs font-medium uppercase text-neutral">
|
|
894
|
+
{{ weekdayShort(day) }} {{ dayNumber(day) }}
|
|
895
|
+
</span>
|
|
896
|
+
<ul class="mt-2 space-y-1">
|
|
897
|
+
@for (event of eventsFor(day); track event.key) {
|
|
898
|
+
<li
|
|
899
|
+
class="flex items-center gap-1.5 text-sm"
|
|
900
|
+
[class.cursor-pointer]="seleccionable()"
|
|
901
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
902
|
+
[attr.data-event-key]="event.key"
|
|
903
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
904
|
+
(click)="seleccionar(event)"
|
|
905
|
+
>
|
|
906
|
+
<span
|
|
907
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
908
|
+
[style.background-color]="event.color ?? null"
|
|
909
|
+
aria-hidden="true"
|
|
910
|
+
></span>
|
|
911
|
+
<span class="truncate">{{ event.title }}</span>
|
|
912
|
+
</li>
|
|
913
|
+
}
|
|
914
|
+
</ul>
|
|
915
|
+
</div>
|
|
916
|
+
}
|
|
917
|
+
</div>
|
|
918
|
+
</div>
|
|
919
|
+
}
|
|
920
|
+
@case ('day') {
|
|
921
|
+
<div
|
|
922
|
+
class="rounded-xl border border-neutral/20 bg-surface p-4"
|
|
923
|
+
[attr.data-day]="cursor()"
|
|
924
|
+
>
|
|
925
|
+
<h3 class="font-display text-base font-semibold">
|
|
926
|
+
{{ dayHeading(cursor()) }}
|
|
927
|
+
</h3>
|
|
928
|
+
@if (eventsFor(cursor()).length === 0) {
|
|
929
|
+
<p class="mt-2 text-sm text-neutral">Sin eventos este día.</p>
|
|
930
|
+
} @else {
|
|
931
|
+
<ul class="mt-3 space-y-2">
|
|
932
|
+
@for (event of eventsFor(cursor()); track event.key) {
|
|
933
|
+
<li
|
|
934
|
+
class="flex items-center gap-1 text-xs"
|
|
935
|
+
[class.cursor-pointer]="seleccionable()"
|
|
936
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
937
|
+
[attr.data-event-key]="event.key"
|
|
938
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
939
|
+
(click)="seleccionar(event)"
|
|
940
|
+
>
|
|
941
|
+
<span
|
|
942
|
+
class="size-2.5 shrink-0 rounded-full bg-neutral/30"
|
|
943
|
+
[style.background-color]="event.color ?? null"
|
|
944
|
+
aria-hidden="true"
|
|
945
|
+
></span>
|
|
946
|
+
<span>{{ event.title }}</span>
|
|
947
|
+
</li>
|
|
948
|
+
}
|
|
949
|
+
</ul>
|
|
950
|
+
}
|
|
951
|
+
</div>
|
|
952
|
+
}
|
|
953
|
+
@case ('agenda') {
|
|
954
|
+
<ol class="space-y-4">
|
|
955
|
+
@for (day of agendaDays(); track day) {
|
|
956
|
+
<li class="rounded-xl border border-neutral/20 bg-surface p-4">
|
|
957
|
+
<h3 class="text-sm font-semibold">{{ dayHeading(day) }}</h3>
|
|
958
|
+
<ul class="mt-2 space-y-1">
|
|
959
|
+
@for (event of eventsFor(day); track event.key) {
|
|
960
|
+
<li
|
|
961
|
+
class="flex items-center gap-2 text-sm"
|
|
962
|
+
[class.cursor-pointer]="seleccionable()"
|
|
963
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
964
|
+
[attr.data-event-key]="event.key"
|
|
965
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
966
|
+
(click)="seleccionar(event)"
|
|
967
|
+
>
|
|
968
|
+
<span
|
|
969
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
970
|
+
[style.background-color]="event.color ?? null"
|
|
971
|
+
aria-hidden="true"
|
|
972
|
+
></span>
|
|
973
|
+
<span>{{ event.title }}</span>
|
|
974
|
+
</li>
|
|
975
|
+
}
|
|
976
|
+
</ul>
|
|
977
|
+
</li>
|
|
978
|
+
}
|
|
979
|
+
</ol>
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
<!-- Problem records are VISIBLE, never silently dropped: a row
|
|
985
|
+
without a valid start or with an impossible interval cannot
|
|
986
|
+
be painted, but the operator must see why. -->
|
|
987
|
+
@if (hasProblemRecords()) {
|
|
988
|
+
<section
|
|
989
|
+
class="rounded-xl border border-neutral/30 p-4"
|
|
990
|
+
aria-label="Registros sin fecha válida"
|
|
991
|
+
>
|
|
992
|
+
<h3 class="font-display text-sm font-semibold">Registros sin fecha válida</h3>
|
|
993
|
+
<p class="mt-1 text-help text-neutral">
|
|
994
|
+
Estos registros no se pueden pintar en el calendario:
|
|
995
|
+
</p>
|
|
996
|
+
<ul class="mt-2 space-y-1 text-sm">
|
|
997
|
+
@for (row of mapping().undated; track $index) {
|
|
998
|
+
<li>{{ recordLabel(row) }} — {{ reasonUndated }}</li>
|
|
999
|
+
}
|
|
1000
|
+
@for (item of mapping().invalid; track $index) {
|
|
1001
|
+
<li>{{ recordLabel(item.row) }} — {{ item.reason }}</li>
|
|
1002
|
+
}
|
|
1003
|
+
</ul>
|
|
1004
|
+
</section>
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
</section>
|
|
1009
|
+
} @else {
|
|
1010
|
+
<app-empty-state
|
|
1011
|
+
title="Configuración inválida"
|
|
1012
|
+
message="Esta vista no declara una configuración de calendario válida."
|
|
1013
|
+
/>
|
|
1014
|
+
}
|
|
1015
|
+
`
|
|
1016
|
+
}]
|
|
1017
|
+
}],
|
|
1018
|
+
ctorParameters: () => []
|
|
1019
|
+
});
|
|
1020
|
+
export { CalendarView };
|
|
1021
|
+
|
|
1022
|
+
//# sourceMappingURL=tinya-code-gas-admin-ui-calendar-view-r-RXDE37.mjs.map
|