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
|
Binary file
|
package/web/favicon.png
ADDED
|
Binary file
|
package/web/index.html
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Estela</title>
|
|
7
|
+
<link rel="icon" href="/favicon.png" type="image/png">
|
|
8
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
9
|
+
<link rel="stylesheet" href="/app.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
|
|
13
|
+
<header class="top">
|
|
14
|
+
<div class="brand">
|
|
15
|
+
<img class="mark" src="/logo-mark.png" alt="" aria-hidden="true">
|
|
16
|
+
Estela
|
|
17
|
+
</div>
|
|
18
|
+
<nav class="tabs" role="tablist">
|
|
19
|
+
<button type="button" class="tab is-on" data-view="summary" role="tab">Resumen</button>
|
|
20
|
+
<button type="button" class="tab" data-view="day" role="tab">Mi día</button>
|
|
21
|
+
<button type="button" class="tab" data-view="team" role="tab">Equipo</button>
|
|
22
|
+
<button type="button" class="tab" data-view="reports" role="tab">Informes</button>
|
|
23
|
+
<button type="button" class="tab" data-view="projects" role="tab">Proyectos</button>
|
|
24
|
+
</nav>
|
|
25
|
+
<div class="top-right" id="top-right">
|
|
26
|
+
<span class="sync-warn" id="sync-warn" hidden></span>
|
|
27
|
+
<button type="button" class="sync" id="sync" title="Buscar trabajo nuevo (R)">
|
|
28
|
+
<span class="sync-dot" aria-hidden="true"></span>
|
|
29
|
+
<span id="sync-label">Actualizar</span>
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
</header>
|
|
33
|
+
|
|
34
|
+
<main>
|
|
35
|
+
<!-- ══════════════ EQUIPO — quién trabaja en qué ══════════════ -->
|
|
36
|
+
<section class="view" id="view-team">
|
|
37
|
+
<div class="section-head">
|
|
38
|
+
<div>
|
|
39
|
+
<h1>Equipo</h1>
|
|
40
|
+
<p class="sub" id="team-sub">…</p>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="periods">
|
|
43
|
+
<select class="filter" id="team-project"><option value="">Todos los proyectos</option></select>
|
|
44
|
+
<button type="button" class="period" data-tperiod="month">Este mes</button>
|
|
45
|
+
<button type="button" class="period is-on" data-tperiod="quarter">3 meses</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div id="team-alerts"></div>
|
|
50
|
+
<div class="kpis kpis-4" id="team-kpis"></div>
|
|
51
|
+
<div id="team-body"></div>
|
|
52
|
+
</section>
|
|
53
|
+
|
|
54
|
+
<!-- ══════════════ RESUMEN — todos los proyectos ══════════════ -->
|
|
55
|
+
<section class="view is-on" id="view-summary">
|
|
56
|
+
<div class="section-head">
|
|
57
|
+
<div>
|
|
58
|
+
<h1>Resumen</h1>
|
|
59
|
+
<p class="sub" id="summary-range">…</p>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="periods" id="periods">
|
|
62
|
+
<button type="button" class="period" data-period="week">Esta semana</button>
|
|
63
|
+
<button type="button" class="period is-on" data-period="month">Este mes</button>
|
|
64
|
+
<button type="button" class="period" data-period="quarter">3 meses</button>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="kpis" id="kpis"></div>
|
|
69
|
+
<div id="summary-body"></div>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<!-- ══════════════ MI DÍA ══════════════ -->
|
|
73
|
+
<section class="view" id="view-day">
|
|
74
|
+
<div class="daynav">
|
|
75
|
+
<button type="button" class="arrow" id="prev-day" aria-label="Día anterior">←</button>
|
|
76
|
+
<div class="daynav-label">
|
|
77
|
+
<h1 id="day-title">…</h1>
|
|
78
|
+
<button type="button" class="today-link" id="go-today" hidden>Volver a hoy</button>
|
|
79
|
+
</div>
|
|
80
|
+
<button type="button" class="arrow" id="next-day" aria-label="Día siguiente">→</button>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<div class="hero" id="hero" hidden>
|
|
84
|
+
<div class="hero-main">
|
|
85
|
+
<div class="hero-value" id="day-hours">—</div>
|
|
86
|
+
<div class="hero-label" id="day-blocks">trabajadas</div>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="hero-main hero-money">
|
|
89
|
+
<div class="hero-value" id="day-amount">—</div>
|
|
90
|
+
<div class="hero-label" id="day-amount-label">registradas</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div id="day-body"></div>
|
|
95
|
+
|
|
96
|
+
<div class="approve" id="approve-bar" hidden>
|
|
97
|
+
<button type="button" class="approve-jump" id="approve-text"></button>
|
|
98
|
+
<button type="button" class="btn-main" id="approve-btn">Aprobar el día</button>
|
|
99
|
+
</div>
|
|
100
|
+
</section>
|
|
101
|
+
|
|
102
|
+
<!-- ══════════════ PROYECTOS ══════════════ -->
|
|
103
|
+
<section class="view" id="view-projects">
|
|
104
|
+
<div class="section-head">
|
|
105
|
+
<div>
|
|
106
|
+
<h1>Proyectos</h1>
|
|
107
|
+
<p class="sub">Qué repositorios pertenecen a quién y a qué tarifa. Subir un precio
|
|
108
|
+
no reescribe lo ya trabajado: la tarifa nueva empieza el día que digas.</p>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div id="projects-body"></div>
|
|
112
|
+
</section>
|
|
113
|
+
|
|
114
|
+
<!-- ══════════════ INFORMES ══════════════ -->
|
|
115
|
+
<section class="view" id="view-reports">
|
|
116
|
+
<div class="section-head">
|
|
117
|
+
<div>
|
|
118
|
+
<h1>Informes</h1>
|
|
119
|
+
<p class="sub">Respaldo del trabajo hecho: horas y commits, para adjuntar a tu factura
|
|
120
|
+
o compartir con quien dirige el proyecto.</p>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div id="reports-body"></div>
|
|
124
|
+
</section>
|
|
125
|
+
</main>
|
|
126
|
+
|
|
127
|
+
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
|
128
|
+
|
|
129
|
+
<script src="/lib.js"></script>
|
|
130
|
+
<script src="/app.js"></script>
|
|
131
|
+
</body>
|
|
132
|
+
</html>
|
package/web/lib.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/* Lógica pura de la interfaz: fechas, formatos y rangos.
|
|
4
|
+
*
|
|
5
|
+
* Vive aparte para poder probarla sin navegador. Tres de los fallos que
|
|
6
|
+
* llegaron a producción —el "2h 60m", el día de hoy calculado en UTC y el
|
|
7
|
+
* salto de día al navegar— eran de aquí, y todos habrían caído con un test.
|
|
8
|
+
*
|
|
9
|
+
* Se carga como script normal en el navegador y con require() en los tests, sin
|
|
10
|
+
* empaquetador de por medio. */
|
|
11
|
+
|
|
12
|
+
(function (root) {
|
|
13
|
+
const DAYS = ["domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"];
|
|
14
|
+
const MONTHS = ["enero", "febrero", "marzo", "abril", "mayo", "junio",
|
|
15
|
+
"julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"];
|
|
16
|
+
const SYMBOLS = { EUR: "€", USD: "$", GBP: "£", BRL: "R$" };
|
|
17
|
+
|
|
18
|
+
/** Fecha local en YYYY-MM-DD. Nunca `toISOString()` a secas: eso da la de Greenwich. */
|
|
19
|
+
function localDay(date) {
|
|
20
|
+
return new Date(date.getTime() - date.getTimezoneOffset() * 60000)
|
|
21
|
+
.toISOString().slice(0, 10);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function localToday(offsetDays = 0, now = new Date()) {
|
|
25
|
+
const d = new Date(now);
|
|
26
|
+
d.setDate(d.getDate() + offsetDays);
|
|
27
|
+
return localDay(d);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Suma días a una fecha en YYYY-MM-DD.
|
|
32
|
+
*
|
|
33
|
+
* Se ancla a mediodía: a las 00:30, sumar un día operando en UTC saltaría dos
|
|
34
|
+
* en el calendario de quien mira.
|
|
35
|
+
*/
|
|
36
|
+
function shiftDay(iso, days) {
|
|
37
|
+
const d = new Date(`${iso}T12:00:00`);
|
|
38
|
+
d.setDate(d.getDate() + days);
|
|
39
|
+
return localDay(d);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Duración legible.
|
|
44
|
+
*
|
|
45
|
+
* Redondea a minutos ANTES de separar las horas: al revés, 2h 59m 50s sale
|
|
46
|
+
* como "2h 60m", y eso llegó a imprimirse en un informe.
|
|
47
|
+
*/
|
|
48
|
+
function fmtDuration(seconds) {
|
|
49
|
+
const total = Math.round(seconds / 60);
|
|
50
|
+
const h = Math.floor(total / 60);
|
|
51
|
+
const m = total % 60;
|
|
52
|
+
if (h === 0) return `${m}m`;
|
|
53
|
+
return m === 0 ? `${h}h` : `${h}h ${String(m).padStart(2, "0")}m`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Importe desde unidades menores. `null` cuando no hay dato, no "0". */
|
|
57
|
+
function fmtMoney(minor, currency) {
|
|
58
|
+
if (minor === null || minor === undefined) return null;
|
|
59
|
+
const value = (minor / 100).toLocaleString("es-ES", {
|
|
60
|
+
minimumFractionDigits: 2, maximumFractionDigits: 2,
|
|
61
|
+
});
|
|
62
|
+
const symbol = SYMBOLS[currency];
|
|
63
|
+
return symbol ? `${symbol}${value}` : `${value} ${currency}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function fmtDayTitle(iso, now = new Date()) {
|
|
67
|
+
if (iso === localToday(0, now)) return "Hoy";
|
|
68
|
+
if (iso === localToday(-1, now)) return "Ayer";
|
|
69
|
+
const d = new Date(`${iso}T12:00:00`);
|
|
70
|
+
return `${DAYS[d.getDay()]} ${d.getDate()} de ${MONTHS[d.getMonth()]}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Rango de un periodo. "all" devuelve vacío: todo lo pendiente, sin acotar. */
|
|
74
|
+
function periodRange(period, now = new Date()) {
|
|
75
|
+
if (period === "all") return { from: "", to: "" };
|
|
76
|
+
const to = localToday(0, now);
|
|
77
|
+
const from = new Date(now);
|
|
78
|
+
if (period === "week") from.setDate(from.getDate() - 6);
|
|
79
|
+
else if (period === "month") from.setDate(1);
|
|
80
|
+
else from.setMonth(from.getMonth() - 2, 1);
|
|
81
|
+
return { from: localDay(from), to };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function esc(text) {
|
|
85
|
+
return String(text).replace(/[&<>"']/g, (c) =>
|
|
86
|
+
({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const api = { DAYS, MONTHS, localDay, localToday, shiftDay, fmtDuration,
|
|
90
|
+
fmtMoney, fmtDayTitle, periodRange, esc };
|
|
91
|
+
|
|
92
|
+
if (typeof module !== "undefined" && module.exports) module.exports = api;
|
|
93
|
+
else Object.assign(root, api);
|
|
94
|
+
})(typeof globalThis !== "undefined" ? globalThis : this);
|
|
Binary file
|