estela 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -0
- package/dist/billing/amortize.d.ts +44 -0
- package/dist/billing/amortize.d.ts.map +1 -0
- package/dist/billing/amortize.js +83 -0
- package/dist/billing/amortize.js.map +1 -0
- package/dist/billing/amortize.test.d.ts +2 -0
- package/dist/billing/amortize.test.d.ts.map +1 -0
- package/dist/billing/amortize.test.js +121 -0
- package/dist/billing/amortize.test.js.map +1 -0
- package/dist/billing/format.test.d.ts +2 -0
- package/dist/billing/format.test.d.ts.map +1 -0
- package/dist/billing/format.test.js +110 -0
- package/dist/billing/format.test.js.map +1 -0
- package/dist/billing/invoice.d.ts +52 -0
- package/dist/billing/invoice.d.ts.map +1 -0
- package/dist/billing/invoice.js +148 -0
- package/dist/billing/invoice.js.map +1 -0
- package/dist/billing/invoice.test.d.ts +2 -0
- package/dist/billing/invoice.test.d.ts.map +1 -0
- package/dist/billing/invoice.test.js +200 -0
- package/dist/billing/invoice.test.js.map +1 -0
- package/dist/billing/sessionize.d.ts +104 -0
- package/dist/billing/sessionize.d.ts.map +1 -0
- package/dist/billing/sessionize.js +0 -0
- package/dist/billing/sessionize.js.map +1 -0
- package/dist/billing/sessionize.test.d.ts +2 -0
- package/dist/billing/sessionize.test.d.ts.map +1 -0
- package/dist/billing/sessionize.test.js +67 -0
- package/dist/billing/sessionize.test.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1091 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud/auth.d.ts +4 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +98 -0
- package/dist/cloud/auth.js.map +1 -0
- package/dist/cloud/client.d.ts +12 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +34 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/db/schema.d.ts +11 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +424 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/schema.test.d.ts +2 -0
- package/dist/db/schema.test.d.ts.map +1 -0
- package/dist/db/schema.test.js +335 -0
- package/dist/db/schema.test.js.map +1 -0
- package/dist/db/store.d.ts +113 -0
- package/dist/db/store.d.ts.map +1 -0
- package/dist/db/store.js +550 -0
- package/dist/db/store.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +229 -0
- package/dist/doctor.js.map +1 -0
- package/dist/export/csv.d.ts +9 -0
- package/dist/export/csv.d.ts.map +1 -0
- package/dist/export/csv.js +81 -0
- package/dist/export/csv.js.map +1 -0
- package/dist/export/invoice-pdf.d.ts +21 -0
- package/dist/export/invoice-pdf.d.ts.map +1 -0
- package/dist/export/invoice-pdf.js +135 -0
- package/dist/export/invoice-pdf.js.map +1 -0
- package/dist/export/panel.d.ts +78 -0
- package/dist/export/panel.d.ts.map +1 -0
- package/dist/export/panel.js +693 -0
- package/dist/export/panel.js.map +1 -0
- package/dist/export/panel.test.d.ts +2 -0
- package/dist/export/panel.test.d.ts.map +1 -0
- package/dist/export/panel.test.js +273 -0
- package/dist/export/panel.test.js.map +1 -0
- package/dist/export/pdf.d.ts +32 -0
- package/dist/export/pdf.d.ts.map +1 -0
- package/dist/export/pdf.js +229 -0
- package/dist/export/pdf.js.map +1 -0
- package/dist/export/pdf.test.d.ts +2 -0
- package/dist/export/pdf.test.d.ts.map +1 -0
- package/dist/export/pdf.test.js +151 -0
- package/dist/export/pdf.test.js.map +1 -0
- package/dist/export/share.d.ts +42 -0
- package/dist/export/share.d.ts.map +1 -0
- package/dist/export/share.js +181 -0
- package/dist/export/share.js.map +1 -0
- package/dist/export/share.test.d.ts +2 -0
- package/dist/export/share.test.d.ts.map +1 -0
- package/dist/export/share.test.js +116 -0
- package/dist/export/share.test.js.map +1 -0
- package/dist/metrics/index.d.ts +101 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +221 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/metrics.test.d.ts +2 -0
- package/dist/metrics/metrics.test.d.ts.map +1 -0
- package/dist/metrics/metrics.test.js +162 -0
- package/dist/metrics/metrics.test.js.map +1 -0
- package/dist/metrics/people.d.ts +46 -0
- package/dist/metrics/people.d.ts.map +1 -0
- package/dist/metrics/people.js +126 -0
- package/dist/metrics/people.js.map +1 -0
- package/dist/metrics/people.test.d.ts +2 -0
- package/dist/metrics/people.test.d.ts.map +1 -0
- package/dist/metrics/people.test.js +84 -0
- package/dist/metrics/people.test.js.map +1 -0
- package/dist/metrics/team.d.ts +41 -0
- package/dist/metrics/team.d.ts.map +1 -0
- package/dist/metrics/team.js +0 -0
- package/dist/metrics/team.js.map +1 -0
- package/dist/metrics/team.test.d.ts +2 -0
- package/dist/metrics/team.test.d.ts.map +1 -0
- package/dist/metrics/team.test.js +90 -0
- package/dist/metrics/team.test.js.map +1 -0
- package/dist/pricing/catalog.d.ts +39 -0
- package/dist/pricing/catalog.d.ts.map +1 -0
- package/dist/pricing/catalog.js +59 -0
- package/dist/pricing/catalog.js.map +1 -0
- package/dist/pricing/cost.d.ts +25 -0
- package/dist/pricing/cost.d.ts.map +1 -0
- package/dist/pricing/cost.js +50 -0
- package/dist/pricing/cost.js.map +1 -0
- package/dist/pricing/cost.test.d.ts +2 -0
- package/dist/pricing/cost.test.d.ts.map +1 -0
- package/dist/pricing/cost.test.js +74 -0
- package/dist/pricing/cost.test.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1141 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +70 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +223 -0
- package/dist/setup.js.map +1 -0
- package/dist/setup.test.d.ts +2 -0
- package/dist/setup.test.d.ts.map +1 -0
- package/dist/setup.test.js +131 -0
- package/dist/setup.test.js.map +1 -0
- package/dist/watchers/claude.d.ts +34 -0
- package/dist/watchers/claude.d.ts.map +1 -0
- package/dist/watchers/claude.js +235 -0
- package/dist/watchers/claude.js.map +1 -0
- package/dist/watchers/claude.test.d.ts +2 -0
- package/dist/watchers/claude.test.d.ts.map +1 -0
- package/dist/watchers/claude.test.js +180 -0
- package/dist/watchers/claude.test.js.map +1 -0
- package/dist/watchers/git.d.ts +42 -0
- package/dist/watchers/git.d.ts.map +1 -0
- package/dist/watchers/git.js +218 -0
- package/dist/watchers/git.js.map +1 -0
- package/dist/watchers/git.test.d.ts +2 -0
- package/dist/watchers/git.test.d.ts.map +1 -0
- package/dist/watchers/git.test.js +73 -0
- package/dist/watchers/git.test.js.map +1 -0
- package/dist/watchers/identity.d.ts +34 -0
- package/dist/watchers/identity.d.ts.map +1 -0
- package/dist/watchers/identity.js +85 -0
- package/dist/watchers/identity.js.map +1 -0
- package/dist/watchers/identity.test.d.ts +2 -0
- package/dist/watchers/identity.test.d.ts.map +1 -0
- package/dist/watchers/identity.test.js +48 -0
- package/dist/watchers/identity.test.js.map +1 -0
- package/dist/web-ui.test.d.ts +2 -0
- package/dist/web-ui.test.d.ts.map +1 -0
- package/dist/web-ui.test.js +312 -0
- package/dist/web-ui.test.js.map +1 -0
- package/node_modules/@estela/shared/dist/index.d.ts +3 -0
- package/node_modules/@estela/shared/dist/index.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/index.js +19 -0
- package/node_modules/@estela/shared/dist/index.js.map +1 -0
- package/node_modules/@estela/shared/dist/money.d.ts +71 -0
- package/node_modules/@estela/shared/dist/money.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/money.js +171 -0
- package/node_modules/@estela/shared/dist/money.js.map +1 -0
- package/node_modules/@estela/shared/dist/types.d.ts +365 -0
- package/node_modules/@estela/shared/dist/types.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/types.js +24 -0
- package/node_modules/@estela/shared/dist/types.js.map +1 -0
- package/node_modules/@estela/shared/package.json +5 -0
- package/package.json +41 -0
- package/web/app.css +749 -0
- package/web/app.js +1257 -0
- package/web/apple-touch-icon.png +0 -0
- package/web/favicon.png +0 -0
- package/web/index.html +132 -0
- package/web/lib.js +94 -0
- package/web/logo-mark.png +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
8
|
+
const shared_1 = require("@estela/shared");
|
|
9
|
+
const invoice_pdf_js_1 = require("./invoice-pdf.js");
|
|
10
|
+
const pdf_js_1 = require("./pdf.js");
|
|
11
|
+
(0, node_test_1.test)("produce un PDF con cabecera, xref y EOF", () => {
|
|
12
|
+
const doc = new pdf_js_1.PdfDocument();
|
|
13
|
+
doc.text(50, 50, "Hola");
|
|
14
|
+
const pdf = doc.toBuffer();
|
|
15
|
+
const text = pdf.toString("latin1");
|
|
16
|
+
strict_1.default.ok(text.startsWith("%PDF-1.4"), "cabecera PDF");
|
|
17
|
+
strict_1.default.ok(text.includes("/Type /Catalog"));
|
|
18
|
+
strict_1.default.ok(text.includes("/Type /Pages"));
|
|
19
|
+
strict_1.default.ok(text.includes("xref"));
|
|
20
|
+
strict_1.default.ok(text.trimEnd().endsWith("%%EOF"));
|
|
21
|
+
});
|
|
22
|
+
(0, node_test_1.test)("los offsets de xref apuntan de verdad a cada objeto", () => {
|
|
23
|
+
// Un xref mal calculado abre en algunos lectores y falla en otros. Este test
|
|
24
|
+
// comprueba lo que un lector estricto comprueba.
|
|
25
|
+
const doc = new pdf_js_1.PdfDocument();
|
|
26
|
+
doc.text(50, 50, "Prueba");
|
|
27
|
+
doc.newPage();
|
|
28
|
+
doc.text(50, 50, "Segunda");
|
|
29
|
+
const pdf = doc.toBuffer();
|
|
30
|
+
const text = pdf.toString("latin1");
|
|
31
|
+
const startxref = Number(/startxref\s+(\d+)/.exec(text)[1]);
|
|
32
|
+
strict_1.default.ok(text.slice(startxref).startsWith("xref"), "startxref apunta a la tabla");
|
|
33
|
+
const entries = [...text.slice(startxref).matchAll(/^(\d{10}) 00000 n $/gm)];
|
|
34
|
+
strict_1.default.ok(entries.length >= 5);
|
|
35
|
+
entries.forEach((entry, i) => {
|
|
36
|
+
const offset = Number(entry[1]);
|
|
37
|
+
strict_1.default.ok(text.slice(offset).startsWith(`${i + 1} 0 obj`), `el objeto ${i + 1} debe empezar en el offset ${offset}`);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
(0, node_test_1.test)("los acentos y el euro se codifican en WinAnsi", () => {
|
|
41
|
+
const doc = new pdf_js_1.PdfDocument();
|
|
42
|
+
doc.text(50, 50, "Migración de diseño — €13,00/h en Cataluña");
|
|
43
|
+
const text = doc.toBuffer().toString("latin1");
|
|
44
|
+
strict_1.default.ok(text.includes("\\200"), "el euro se escapa como octal 200 (WinAnsi)");
|
|
45
|
+
strict_1.default.ok(text.includes("\\361"), "la eñe se escapa como octal 361");
|
|
46
|
+
strict_1.default.ok(text.includes("\\363"), "la o acentuada se escapa como octal 363");
|
|
47
|
+
});
|
|
48
|
+
(0, node_test_1.test)("los paréntesis y la barra invertida se escapan", () => {
|
|
49
|
+
const doc = new pdf_js_1.PdfDocument();
|
|
50
|
+
doc.text(50, 50, "feat(auth): C:\\ruta (importante)");
|
|
51
|
+
const text = doc.toBuffer().toString("latin1");
|
|
52
|
+
strict_1.default.ok(text.includes("feat\\(auth\\)"), "paréntesis escapados");
|
|
53
|
+
strict_1.default.ok(text.includes("C:\\\\ruta"), "barra invertida escapada");
|
|
54
|
+
});
|
|
55
|
+
(0, node_test_1.test)("las anchuras permiten alinear a la derecha", () => {
|
|
56
|
+
// Si measure() devolviera siempre lo mismo, la columna de importes bailaría.
|
|
57
|
+
const ancho = pdf_js_1.PdfDocument.measure("MMMMM", "Helvetica", 10);
|
|
58
|
+
const estrecho = pdf_js_1.PdfDocument.measure("iiiii", "Helvetica", 10);
|
|
59
|
+
strict_1.default.ok(ancho > estrecho * 2, `M debe ser mucho más ancha que i (${ancho} vs ${estrecho})`);
|
|
60
|
+
// Courier es monoespaciada: cinco caracteres siempre miden lo mismo.
|
|
61
|
+
strict_1.default.equal(pdf_js_1.PdfDocument.measure("MMMMM", "Courier", 10), pdf_js_1.PdfDocument.measure("iiiii", "Courier", 10));
|
|
62
|
+
});
|
|
63
|
+
(0, node_test_1.test)("una vocal acentuada mide lo mismo que su base", () => {
|
|
64
|
+
strict_1.default.equal(pdf_js_1.PdfDocument.measure("a", "Helvetica", 10), pdf_js_1.PdfDocument.measure("á", "Helvetica", 10));
|
|
65
|
+
strict_1.default.equal(pdf_js_1.PdfDocument.measure("n", "Helvetica", 10), pdf_js_1.PdfDocument.measure("ñ", "Helvetica", 10));
|
|
66
|
+
});
|
|
67
|
+
(0, node_test_1.test)("trunca en vez de desbordar la columna", () => {
|
|
68
|
+
const largo = "feat(permisos): rediseño completo del gestor de tablas y sus filtros";
|
|
69
|
+
const corto = pdf_js_1.PdfDocument.truncate(largo, "Helvetica", 10, 120);
|
|
70
|
+
strict_1.default.ok(corto.endsWith("..."));
|
|
71
|
+
strict_1.default.ok(pdf_js_1.PdfDocument.measure(corto, "Helvetica", 10) <= 120);
|
|
72
|
+
strict_1.default.ok(corto.length < largo.length);
|
|
73
|
+
});
|
|
74
|
+
(0, node_test_1.test)("wrap parte el texto respetando el ancho", () => {
|
|
75
|
+
const lines = pdf_js_1.PdfDocument.wrap("Pago a 30 días mediante transferencia bancaria a la cuenta indicada.", "Helvetica", 9, 150);
|
|
76
|
+
strict_1.default.ok(lines.length > 1);
|
|
77
|
+
for (const line of lines) {
|
|
78
|
+
strict_1.default.ok(pdf_js_1.PdfDocument.measure(line, "Helvetica", 9) <= 150, `"${line}" cabe`);
|
|
79
|
+
}
|
|
80
|
+
strict_1.default.equal(lines.join(" ").replace(/\s+/g, " "), "Pago a 30 días mediante transferencia bancaria a la cuenta indicada.");
|
|
81
|
+
});
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
const CLIENT = { id: "eurosistema", name: "Eurosistema", currency: "EUR", taxId: "B-12345678" };
|
|
84
|
+
const PROJECT = {
|
|
85
|
+
id: "p", clientId: "eurosistema", name: "Eurosistema Blazor", repoPaths: [],
|
|
86
|
+
billable: true, roundingMinutes: 0, aiCostPolicy: "absorbed", kind: "client",
|
|
87
|
+
};
|
|
88
|
+
function invoice(lineCount) {
|
|
89
|
+
const lines = Array.from({ length: lineCount }, (_, i) => ({
|
|
90
|
+
description: `Desarrollo en servicios-socios-cartera día ${i + 1}`,
|
|
91
|
+
seconds: 3600, hourlyRate: (0, shared_1.money)(1300, "EUR"), amount: (0, shared_1.money)(1300, "EUR"),
|
|
92
|
+
}));
|
|
93
|
+
return {
|
|
94
|
+
id: "inv", number: "F-2026-001", clientId: "eurosistema", projectId: "p",
|
|
95
|
+
issuedAt: new Date("2026-08-26T10:00:00Z"),
|
|
96
|
+
cutoffAt: new Date("2026-08-26T23:59:59Z"),
|
|
97
|
+
periodStart: new Date("2026-07-29T00:00:00Z"),
|
|
98
|
+
currency: "EUR", lines,
|
|
99
|
+
subtotal: (0, shared_1.money)(1300 * lineCount, "EUR"), total: (0, shared_1.money)(1300 * lineCount, "EUR"),
|
|
100
|
+
totalSeconds: 3600 * lineCount, aiCost: { microUsd: 315_950_000 },
|
|
101
|
+
usdFxRate: null, aiCostBilled: null, aiAmortized: null,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
(0, node_test_1.test)("el informe en PDF incluye los datos clave", () => {
|
|
105
|
+
const pdf = (0, invoice_pdf_js_1.invoiceToPdf)(invoice(3), CLIENT, PROJECT, {
|
|
106
|
+
issuer: { name: "Jonathan León", taxId: "1712345678", email: "jd@ejemplo.com" },
|
|
107
|
+
});
|
|
108
|
+
const text = pdf.toString("latin1");
|
|
109
|
+
strict_1.default.ok(text.includes("INFORME DE HORAS"));
|
|
110
|
+
strict_1.default.ok(!text.includes("FACTURA"), "no se presenta como documento fiscal");
|
|
111
|
+
strict_1.default.ok(text.includes("F-2026-001"));
|
|
112
|
+
strict_1.default.ok(text.includes("Eurosistema"));
|
|
113
|
+
strict_1.default.ok(text.includes("Le\\363n"), "el emisor con acento va codificado");
|
|
114
|
+
strict_1.default.ok(pdf.length > 1500);
|
|
115
|
+
});
|
|
116
|
+
(0, node_test_1.test)("muchos conceptos generan varias páginas en vez de desbordar", () => {
|
|
117
|
+
const una = (0, invoice_pdf_js_1.invoiceToPdf)(invoice(5), CLIENT, PROJECT);
|
|
118
|
+
const muchas = (0, invoice_pdf_js_1.invoiceToPdf)(invoice(80), CLIENT, PROJECT);
|
|
119
|
+
const count = (b) => (b.toString("latin1").match(/\/Type \/Page[^s]/g) ?? []).length;
|
|
120
|
+
strict_1.default.equal(count(una), 1);
|
|
121
|
+
strict_1.default.ok(count(muchas) >= 3, `80 conceptos deben ocupar varias páginas, salieron ${count(muchas)}`);
|
|
122
|
+
});
|
|
123
|
+
(0, node_test_1.test)("una descripción larga no invade la columna de tiempo", () => {
|
|
124
|
+
// El fallo que esto previene: reservar un hueco fijo en vez del ancho real de
|
|
125
|
+
// la columna TIEMPO, que se alinea a la derecha y crece hacia la izquierda.
|
|
126
|
+
const inv = invoice(1);
|
|
127
|
+
const largo = {
|
|
128
|
+
...inv.lines[0],
|
|
129
|
+
description: "feat: permisos por tabla y rediseño del generador de informes con filtros avanzados",
|
|
130
|
+
seconds: 4 * 3600 + 16 * 60,
|
|
131
|
+
};
|
|
132
|
+
const text = (0, invoice_pdf_js_1.invoiceToPdf)({ ...inv, lines: [largo] }, CLIENT, PROJECT).toString("latin1");
|
|
133
|
+
// Extrae los dos textos de esa fila y comprueba que no se solapan.
|
|
134
|
+
const ops = [...text.matchAll(/BT \/F1 10 Tf ([\d.]+) ([\d.]+) Td \((.*?)\) Tj ET/g)];
|
|
135
|
+
const desc = ops.find((o) => o[3].startsWith("feat"));
|
|
136
|
+
const time = ops.find((o) => o[3] === "4h 16m");
|
|
137
|
+
strict_1.default.ok(desc, "la descripción está en el PDF");
|
|
138
|
+
strict_1.default.ok(time, "el tiempo está en el PDF");
|
|
139
|
+
strict_1.default.equal(desc[2], time[2], "van en la misma fila");
|
|
140
|
+
const descEnd = Number(desc[1]) + pdf_js_1.PdfDocument.measure(desc[3].replace(/\\(\d{3})/g, (_, o) => String.fromCharCode(parseInt(o, 8)))
|
|
141
|
+
.replace(/\\([()\\])/g, "$1"), "Helvetica", 10);
|
|
142
|
+
strict_1.default.ok(descEnd <= Number(time[1]), `la descripción acaba en ${descEnd.toFixed(1)} y el tiempo empieza en ${time[1]}`);
|
|
143
|
+
});
|
|
144
|
+
(0, node_test_1.test)("el coste de IA aparece como nota interna y nunca se comparte", () => {
|
|
145
|
+
const inv = invoice(2);
|
|
146
|
+
const text = (0, invoice_pdf_js_1.invoiceToPdf)(inv, CLIENT, PROJECT).toString("latin1");
|
|
147
|
+
strict_1.default.ok(text.includes("NOTA INTERNA"));
|
|
148
|
+
strict_1.default.ok(text.includes("no se comparte"));
|
|
149
|
+
strict_1.default.equal(inv.total.amount, 2600, "el valor sigue siendo solo las horas");
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=pdf.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf.test.js","sourceRoot":"","sources":["../../src/export/pdf.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AiPayer, Client, Project, TimeEntry } from "@estela/shared";
|
|
2
|
+
import { type Money } from "@estela/shared";
|
|
3
|
+
/**
|
|
4
|
+
* Informe compartible: un único fichero HTML, autónomo.
|
|
5
|
+
*
|
|
6
|
+
* Se manda por correo y se abre sin instalar nada, sin cuenta y sin depender de
|
|
7
|
+
* que la máquina de quien lo genera esté encendida. Esa independencia es el
|
|
8
|
+
* punto: un enlace que apunta al portátil de un freelance está caído la mitad
|
|
9
|
+
* del tiempo, y justo cuando quieres causar buena impresión.
|
|
10
|
+
*
|
|
11
|
+
* Lo que contiene y lo que no:
|
|
12
|
+
*
|
|
13
|
+
* - Siempre: horas, fechas, descripciones y commits. Es el respaldo del
|
|
14
|
+
* trabajo, que es lo que el cliente necesita para dar el visto bueno.
|
|
15
|
+
* - Opcional: el valor económico. Algunos lo adjuntan a su factura y otros no.
|
|
16
|
+
* - Nunca, cuando la IA la pagas tú: consumo y coste de IA. No es una casilla
|
|
17
|
+
* desactivada por defecto — el dato no llega hasta aquí. Ver `AiPayer`.
|
|
18
|
+
*/
|
|
19
|
+
export interface ShareOptions {
|
|
20
|
+
readonly project: Project;
|
|
21
|
+
readonly client: Client;
|
|
22
|
+
readonly entries: readonly TimeEntry[];
|
|
23
|
+
readonly from: string;
|
|
24
|
+
readonly to: string;
|
|
25
|
+
/** Autor del informe. Aparece en la cabecera. */
|
|
26
|
+
readonly authorName?: string;
|
|
27
|
+
/** Incluir tarifa e importes. Fuera por defecto. */
|
|
28
|
+
readonly withAmounts?: boolean;
|
|
29
|
+
/** Función de tarifa vigente. Solo se usa si `withAmounts`. */
|
|
30
|
+
readonly rateAt?: (at: Date) => Money | null;
|
|
31
|
+
/**
|
|
32
|
+
* Quién paga la IA de este proyecto. Con "self" el consumo no se incluye,
|
|
33
|
+
* y no hay opción para incluirlo.
|
|
34
|
+
*/
|
|
35
|
+
readonly aiPayer?: AiPayer;
|
|
36
|
+
readonly commitsOf?: (hashes: readonly string[]) => readonly {
|
|
37
|
+
hash: string;
|
|
38
|
+
subject: string;
|
|
39
|
+
}[];
|
|
40
|
+
}
|
|
41
|
+
export declare function buildShareReport(options: ShareOptions): string;
|
|
42
|
+
//# sourceMappingURL=share.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../../src/export/share.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAA0C,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEpF;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAClG;AAQD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAsC9D"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildShareReport = buildShareReport;
|
|
4
|
+
const shared_1 = require("@estela/shared");
|
|
5
|
+
function buildShareReport(options) {
|
|
6
|
+
const { project, client, entries, from, to } = options;
|
|
7
|
+
const included = entries
|
|
8
|
+
.filter((e) => e.billable)
|
|
9
|
+
.filter((e) => {
|
|
10
|
+
const day = (0, shared_1.localDate)(e.startedAt);
|
|
11
|
+
return day >= from && day <= to;
|
|
12
|
+
})
|
|
13
|
+
.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime());
|
|
14
|
+
const days = groupByDay(included);
|
|
15
|
+
const totalSeconds = included.reduce((s, e) => s + e.seconds, 0);
|
|
16
|
+
const showAmounts = options.withAmounts === true && options.rateAt !== undefined;
|
|
17
|
+
let totalAmount = null;
|
|
18
|
+
if (showAmounts) {
|
|
19
|
+
let minor = 0;
|
|
20
|
+
let currency = client.currency;
|
|
21
|
+
for (const entry of included) {
|
|
22
|
+
const rate = options.rateAt(entry.startedAt);
|
|
23
|
+
if (rate) {
|
|
24
|
+
minor += Math.round((rate.amount * entry.seconds) / 3600);
|
|
25
|
+
currency = rate.currency;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
totalAmount = { amount: minor, currency };
|
|
29
|
+
}
|
|
30
|
+
const rows = days.map((day) => renderDay(day, options, showAmounts)).join("\n");
|
|
31
|
+
return page({
|
|
32
|
+
title: `${project.name} — ${from} al ${to}`,
|
|
33
|
+
project, client, from, to, totalSeconds, totalAmount,
|
|
34
|
+
dayCount: days.length,
|
|
35
|
+
blockCount: included.length,
|
|
36
|
+
authorName: options.authorName ?? null,
|
|
37
|
+
body: included.length
|
|
38
|
+
? rows
|
|
39
|
+
: `<p class="empty">No hay trabajo registrado en este periodo.</p>`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function groupByDay(entries) {
|
|
43
|
+
const map = new Map();
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const day = (0, shared_1.localDate)(entry.startedAt);
|
|
46
|
+
const bucket = map.get(day);
|
|
47
|
+
if (bucket)
|
|
48
|
+
bucket.push(entry);
|
|
49
|
+
else
|
|
50
|
+
map.set(day, [entry]);
|
|
51
|
+
}
|
|
52
|
+
return [...map.entries()]
|
|
53
|
+
.map(([date, list]) => ({
|
|
54
|
+
date, entries: list,
|
|
55
|
+
seconds: list.reduce((s, e) => s + e.seconds, 0),
|
|
56
|
+
}))
|
|
57
|
+
.sort((a, b) => a.date.localeCompare(b.date));
|
|
58
|
+
}
|
|
59
|
+
function renderDay(day, options, showAmounts) {
|
|
60
|
+
const items = day.entries.map((entry) => {
|
|
61
|
+
const commits = options.commitsOf?.(entry.commitHashes) ?? [];
|
|
62
|
+
const commitHtml = commits.length
|
|
63
|
+
? `<ul class="commits">${commits.map((c) => `<li><code>${esc(c.hash.slice(0, 7))}</code> ${esc(c.subject)}</li>`).join("")}</ul>`
|
|
64
|
+
: "";
|
|
65
|
+
let amount = "";
|
|
66
|
+
if (showAmounts) {
|
|
67
|
+
const rate = options.rateAt(entry.startedAt);
|
|
68
|
+
if (rate) {
|
|
69
|
+
amount = `<td class="amount">${esc((0, shared_1.formatMoney)({
|
|
70
|
+
amount: Math.round((rate.amount * entry.seconds) / 3600), currency: rate.currency,
|
|
71
|
+
}))}</td>`;
|
|
72
|
+
}
|
|
73
|
+
else
|
|
74
|
+
amount = `<td class="amount">—</td>`;
|
|
75
|
+
}
|
|
76
|
+
return `<tr>
|
|
77
|
+
<td class="task"><span class="desc">${esc(entry.description)}</span>${commitHtml}</td>
|
|
78
|
+
<td class="time">${esc((0, shared_1.formatDuration)(entry.seconds))}</td>
|
|
79
|
+
${amount}
|
|
80
|
+
</tr>`;
|
|
81
|
+
}).join("\n");
|
|
82
|
+
return `<section class="day">
|
|
83
|
+
<h3>${esc(longDate(day.date))} <span class="day-total">${esc((0, shared_1.formatDuration)(day.seconds))}</span></h3>
|
|
84
|
+
<table>${items}</table>
|
|
85
|
+
</section>`;
|
|
86
|
+
}
|
|
87
|
+
function page(d) {
|
|
88
|
+
const amountBlock = d.totalAmount
|
|
89
|
+
? `<div class="kpi"><span class="kpi-v">${esc((0, shared_1.formatMoney)(d.totalAmount))}</span>
|
|
90
|
+
<span class="kpi-l">valor del trabajo</span></div>`
|
|
91
|
+
: "";
|
|
92
|
+
return `<!doctype html>
|
|
93
|
+
<html lang="es">
|
|
94
|
+
<head>
|
|
95
|
+
<meta charset="utf-8">
|
|
96
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
97
|
+
<title>${esc(d.title)}</title>
|
|
98
|
+
<style>
|
|
99
|
+
:root{--paper:#fff;--ink:#191d1b;--ink-2:#555d58;--ink-3:#8a938d;--line:#e3e5df;
|
|
100
|
+
--jade:#0a5f52;--jade-bg:#e8f2ef;--radius:10px}
|
|
101
|
+
@media(prefers-color-scheme:dark){:root{--paper:#101312;--ink:#e9eeeb;--ink-2:#a6b1ab;
|
|
102
|
+
--ink-3:#78837d;--line:#2a322f;--jade:#5fcfb6;--jade-bg:#10322c}}
|
|
103
|
+
*{box-sizing:border-box}
|
|
104
|
+
body{margin:0;background:var(--paper);color:var(--ink);
|
|
105
|
+
font:15px/1.55 ui-sans-serif,-apple-system,"Segoe UI",Roboto,sans-serif;
|
|
106
|
+
-webkit-font-smoothing:antialiased}
|
|
107
|
+
.wrap{max-width:720px;margin:0 auto;padding:48px 24px 64px}
|
|
108
|
+
h1{font-size:1.5rem;font-weight:650;letter-spacing:-.02em;margin:0 0 6px}
|
|
109
|
+
.meta{color:var(--ink-3);font-size:.92rem}
|
|
110
|
+
.kpis{display:flex;gap:10px;margin:28px 0 36px;flex-wrap:wrap}
|
|
111
|
+
.kpi{flex:1;min-width:130px;background:var(--jade-bg);border-radius:var(--radius);padding:16px 18px}
|
|
112
|
+
.kpi-v{display:block;font-size:1.6rem;font-weight:640;letter-spacing:-.03em;color:var(--jade);
|
|
113
|
+
font-variant-numeric:tabular-nums}
|
|
114
|
+
.kpi-l{display:block;font-size:.82rem;color:var(--jade);opacity:.85;margin-top:3px}
|
|
115
|
+
.day{margin-bottom:30px}
|
|
116
|
+
.day h3{font-size:.82rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
|
|
117
|
+
color:var(--ink-3);margin:0 0 8px;display:flex;justify-content:space-between;
|
|
118
|
+
border-bottom:1px solid var(--line);padding-bottom:7px}
|
|
119
|
+
.day-total{font-variant-numeric:tabular-nums;color:var(--ink-2)}
|
|
120
|
+
table{width:100%;border-collapse:collapse}
|
|
121
|
+
td{padding:11px 0;vertical-align:top;border-bottom:1px solid var(--line)}
|
|
122
|
+
tr:last-child td{border-bottom:0}
|
|
123
|
+
.desc{font-weight:550}
|
|
124
|
+
.time{text-align:right;white-space:nowrap;width:80px;font-variant-numeric:tabular-nums;
|
|
125
|
+
padding-left:16px;font-weight:550}
|
|
126
|
+
.amount{text-align:right;white-space:nowrap;width:92px;padding-left:16px;
|
|
127
|
+
font-variant-numeric:tabular-nums;color:var(--jade);font-weight:600}
|
|
128
|
+
.commits{list-style:none;margin:7px 0 0;padding:0;display:flex;flex-direction:column;gap:4px}
|
|
129
|
+
.commits li{font-size:.85rem;color:var(--ink-2)}
|
|
130
|
+
.commits code{font-family:ui-monospace,Menlo,monospace;font-size:.78rem;color:var(--jade);
|
|
131
|
+
background:var(--jade-bg);padding:1px 5px;border-radius:4px;margin-right:6px}
|
|
132
|
+
.empty{color:var(--ink-3);text-align:center;padding:48px 0}
|
|
133
|
+
footer{margin-top:44px;padding-top:20px;border-top:1px solid var(--line);
|
|
134
|
+
font-size:.82rem;color:var(--ink-3);line-height:1.6}
|
|
135
|
+
footer a{color:var(--jade)}
|
|
136
|
+
@media print{
|
|
137
|
+
body{background:#fff;color:#000}
|
|
138
|
+
.wrap{padding:0;max-width:none}
|
|
139
|
+
.day{break-inside:avoid}
|
|
140
|
+
footer{break-before:avoid}
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
143
|
+
</head>
|
|
144
|
+
<body>
|
|
145
|
+
<div class="wrap">
|
|
146
|
+
<header>
|
|
147
|
+
<h1>${esc(d.project.name)}</h1>
|
|
148
|
+
<p class="meta">Informe de horas${d.authorName ? ` de ${esc(d.authorName)}` : ""}
|
|
149
|
+
para ${esc(d.client.name)} · ${esc(d.from)} al ${esc(d.to)}</p>
|
|
150
|
+
</header>
|
|
151
|
+
|
|
152
|
+
<div class="kpis">
|
|
153
|
+
<div class="kpi"><span class="kpi-v">${esc((0, shared_1.formatDuration)(d.totalSeconds))}</span>
|
|
154
|
+
<span class="kpi-l">horas trabajadas</span></div>
|
|
155
|
+
<div class="kpi"><span class="kpi-v">${d.dayCount}</span>
|
|
156
|
+
<span class="kpi-l">${d.dayCount === 1 ? "día" : "días"} con actividad</span></div>
|
|
157
|
+
${amountBlock}
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
${d.body}
|
|
161
|
+
|
|
162
|
+
<footer>
|
|
163
|
+
Generado con Estela a partir de la actividad real de Git y del editor.
|
|
164
|
+
Cada bloque está respaldado por sus commits.<br>
|
|
165
|
+
<a href="https://getestela.dev">getestela.dev</a>
|
|
166
|
+
</footer>
|
|
167
|
+
</div>
|
|
168
|
+
</body>
|
|
169
|
+
</html>`;
|
|
170
|
+
}
|
|
171
|
+
const MONTHS = ["enero", "febrero", "marzo", "abril", "mayo", "junio",
|
|
172
|
+
"julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"];
|
|
173
|
+
const DAYS = ["domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"];
|
|
174
|
+
function longDate(iso) {
|
|
175
|
+
const d = new Date(`${iso}T12:00:00Z`);
|
|
176
|
+
return `${DAYS[d.getUTCDay()]} ${d.getUTCDate()} de ${MONTHS[d.getUTCMonth()]}`;
|
|
177
|
+
}
|
|
178
|
+
function esc(text) {
|
|
179
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=share.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.js","sourceRoot":"","sources":["../../src/export/share.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.test.d.ts","sourceRoot":"","sources":["../../src/export/share.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
8
|
+
const shared_1 = require("@estela/shared");
|
|
9
|
+
const share_js_1 = require("./share.js");
|
|
10
|
+
const CLIENT = { id: "eurosistema", name: "Eurosistema", currency: "EUR" };
|
|
11
|
+
const PROJECT = {
|
|
12
|
+
id: "euro", clientId: "eurosistema", name: "Eurosistema Blazor", repoPaths: [],
|
|
13
|
+
billable: true, roundingMinutes: 0, aiCostPolicy: "absorbed", kind: "client",
|
|
14
|
+
};
|
|
15
|
+
function entry(id, dayIso, seconds, micro = 250_000_000) {
|
|
16
|
+
const startedAt = new Date(`${dayIso}T09:00:00Z`);
|
|
17
|
+
return {
|
|
18
|
+
id, projectId: "euro", startedAt,
|
|
19
|
+
endedAt: new Date(startedAt.getTime() + seconds * 1000),
|
|
20
|
+
seconds, description: `Trabajo del ${dayIso}`, billable: true, invoiceId: null,
|
|
21
|
+
aiCost: { microUsd: micro }, agentSeconds: seconds,
|
|
22
|
+
commitHashes: ["abc1234def"], agents: ["claude-code"],
|
|
23
|
+
source: "agent", kind: "development", branch: "main",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const BASE = {
|
|
27
|
+
project: PROJECT, client: CLIENT, from: "2026-08-01", to: "2026-08-31",
|
|
28
|
+
commitsOf: () => [{ hash: "abc1234def", subject: "feat: validación de cédula" }],
|
|
29
|
+
};
|
|
30
|
+
// ── La garantía que no puede romperse ──────────────────────────────────
|
|
31
|
+
(0, node_test_1.test)("el consumo de IA NUNCA llega al informe compartido", () => {
|
|
32
|
+
// Si un cliente ve qué parte del trabajo generó una IA, tiene un argumento
|
|
33
|
+
// nuevo para negociar la tarifa. Con `aiPayer: "self"` el dato no viaja, y no
|
|
34
|
+
// hay ninguna bandera que lo haga viajar.
|
|
35
|
+
const html = (0, share_js_1.buildShareReport)({
|
|
36
|
+
...BASE,
|
|
37
|
+
entries: [entry("a", "2026-08-10", 7200, 999_000_000)],
|
|
38
|
+
aiPayer: "self",
|
|
39
|
+
});
|
|
40
|
+
for (const leak of ["microUsd", "aiCost", "tarifa API", "consumo de IA",
|
|
41
|
+
"tokens", "Claude", "999", "USD"]) {
|
|
42
|
+
strict_1.default.ok(!html.includes(leak), `el informe no debe contener "${leak}"`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
(0, node_test_1.test)("los importes quedan fuera salvo que se pidan explícitamente", () => {
|
|
46
|
+
const entries = [entry("a", "2026-08-10", 3600)];
|
|
47
|
+
const rateAt = () => (0, shared_1.money)(1300, "EUR");
|
|
48
|
+
const sin = (0, share_js_1.buildShareReport)({ ...BASE, entries, rateAt });
|
|
49
|
+
strict_1.default.ok(!sin.includes("€"), "sin --with-amounts no aparece ningún importe");
|
|
50
|
+
strict_1.default.ok(!sin.includes("valor del trabajo"));
|
|
51
|
+
const con = (0, share_js_1.buildShareReport)({ ...BASE, entries, rateAt, withAmounts: true });
|
|
52
|
+
strict_1.default.ok(con.includes("€13,00"), "con --with-amounts sí aparece");
|
|
53
|
+
strict_1.default.ok(con.includes("valor del trabajo"));
|
|
54
|
+
});
|
|
55
|
+
(0, node_test_1.test)("pedir importes sin función de tarifa no rompe ni inventa cifras", () => {
|
|
56
|
+
const html = (0, share_js_1.buildShareReport)({
|
|
57
|
+
...BASE, entries: [entry("a", "2026-08-10", 3600)], withAmounts: true,
|
|
58
|
+
});
|
|
59
|
+
strict_1.default.ok(!html.includes("€"));
|
|
60
|
+
});
|
|
61
|
+
// ── Autonomía del fichero ──────────────────────────────────────────────
|
|
62
|
+
(0, node_test_1.test)("no depende de ningún recurso externo salvo el enlace del pie", () => {
|
|
63
|
+
// Se abre desde un adjunto de correo, sin red y sin servidor. Cualquier
|
|
64
|
+
// src= o link rel=stylesheet lo dejaría a medio pintar.
|
|
65
|
+
const html = (0, share_js_1.buildShareReport)({ ...BASE, entries: [entry("a", "2026-08-10", 3600)] });
|
|
66
|
+
strict_1.default.ok(!html.includes("<script"), "sin JavaScript");
|
|
67
|
+
strict_1.default.ok(!/src\s*=/.test(html), "sin recursos externos");
|
|
68
|
+
strict_1.default.ok(!/<link\b/.test(html), "sin hojas de estilo enlazadas");
|
|
69
|
+
const hrefs = [...html.matchAll(/href="([^"]+)"/g)].map((m) => m[1]);
|
|
70
|
+
strict_1.default.deepEqual(hrefs, ["https://getestela.dev"], "solo el enlace del pie");
|
|
71
|
+
});
|
|
72
|
+
(0, node_test_1.test)("escapa el contenido para que un mensaje de commit no rompa el HTML", () => {
|
|
73
|
+
const malicioso = {
|
|
74
|
+
...entry("a", "2026-08-10", 3600),
|
|
75
|
+
description: `<script>alert("x")</script> & "comillas"`,
|
|
76
|
+
};
|
|
77
|
+
const html = (0, share_js_1.buildShareReport)({ ...BASE, entries: [malicioso], commitsOf: () => [] });
|
|
78
|
+
strict_1.default.ok(!html.includes("<script>alert"), "la etiqueta va escapada");
|
|
79
|
+
strict_1.default.ok(html.includes("<script>"));
|
|
80
|
+
strict_1.default.ok(html.includes("&"));
|
|
81
|
+
});
|
|
82
|
+
// ── Contenido ──────────────────────────────────────────────────────────
|
|
83
|
+
(0, node_test_1.test)("agrupa por día y suma las horas", () => {
|
|
84
|
+
const html = (0, share_js_1.buildShareReport)({
|
|
85
|
+
...BASE,
|
|
86
|
+
entries: [
|
|
87
|
+
entry("a", "2026-08-10", 3600),
|
|
88
|
+
entry("b", "2026-08-10", 1800),
|
|
89
|
+
entry("c", "2026-08-12", 7200),
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
strict_1.default.ok(html.includes("3h 30m"), "total de 1h + 30m + 2h");
|
|
93
|
+
strict_1.default.ok(html.includes("2 días con actividad") || html.includes(">2<"));
|
|
94
|
+
});
|
|
95
|
+
(0, node_test_1.test)("incluye los commits como respaldo", () => {
|
|
96
|
+
const html = (0, share_js_1.buildShareReport)({ ...BASE, entries: [entry("a", "2026-08-10", 3600)] });
|
|
97
|
+
strict_1.default.ok(html.includes("abc1234"), "hash abreviado");
|
|
98
|
+
strict_1.default.ok(html.includes("validación de cédula"), "asunto del commit");
|
|
99
|
+
});
|
|
100
|
+
(0, node_test_1.test)("solo el periodo pedido, y solo lo facturable", () => {
|
|
101
|
+
const fuera = entry("fuera", "2026-09-15", 3600);
|
|
102
|
+
const noFacturable = { ...entry("nf", "2026-08-11", 3600), billable: false };
|
|
103
|
+
const html = (0, share_js_1.buildShareReport)({
|
|
104
|
+
...BASE,
|
|
105
|
+
entries: [entry("dentro", "2026-08-10", 3600), fuera, noFacturable],
|
|
106
|
+
});
|
|
107
|
+
strict_1.default.ok(html.includes("Trabajo del 2026-08-10"));
|
|
108
|
+
strict_1.default.ok(!html.includes("Trabajo del 2026-09-15"), "fuera del periodo");
|
|
109
|
+
strict_1.default.ok(!html.includes("Trabajo del 2026-08-11"), "no facturable");
|
|
110
|
+
});
|
|
111
|
+
(0, node_test_1.test)("un periodo vacío da un informe válido, no un error", () => {
|
|
112
|
+
const html = (0, share_js_1.buildShareReport)({ ...BASE, entries: [] });
|
|
113
|
+
strict_1.default.ok(html.startsWith("<!doctype html>"));
|
|
114
|
+
strict_1.default.ok(html.includes("No hay trabajo registrado"));
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=share.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.test.js","sourceRoot":"","sources":["../../src/export/share.test.ts"],"names":[],"mappings":"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"}
|