@cccarv82/freya 2.1.7 → 2.2.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/cli/web-ui.js +56 -0
- package/cli/web.js +129 -1
- package/package.json +1 -1
package/cli/web-ui.js
CHANGED
|
@@ -656,6 +656,48 @@
|
|
|
656
656
|
grid.appendChild(card); }
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
+
function renderProjects() {
|
|
660
|
+
const el = $('projectsGrid');
|
|
661
|
+
if (!el) return;
|
|
662
|
+
const filter = String(($('projectsFilter') && $('projectsFilter').value) || '').toLowerCase();
|
|
663
|
+
const items = Array.isArray(state.projects) ? state.projects : [];
|
|
664
|
+
const filtered = items.filter((p) => {
|
|
665
|
+
const hay = [p.client, p.program, p.stream, p.project, p.slug, (p.tags||[]).join(' ')].join(' ').toLowerCase();
|
|
666
|
+
return !filter || hay.includes(filter);
|
|
667
|
+
});
|
|
668
|
+
el.innerHTML = '';
|
|
669
|
+
for (const p of filtered) {
|
|
670
|
+
const card = document.createElement('div');
|
|
671
|
+
card.className = 'reportCard';
|
|
672
|
+
card.innerHTML = '<div class="reportHead">'
|
|
673
|
+
+ '<div><div class="reportTitle">' + escapeHtml(p.project || p.slug || 'Projeto') + '</div>'
|
|
674
|
+
+ '<div class="reportMeta">' + escapeHtml([p.client, p.program, p.stream].filter(Boolean).join(' · ')) + '</div></div>'
|
|
675
|
+
+ '<div class="reportActions">' + (p.active ? '<span class="pill ok">ativo</span>' : '<span class="pill warn">inativo</span>') + '</div>'
|
|
676
|
+
+ '</div>'
|
|
677
|
+
+ '<div class="help" style="margin-top:8px">' + escapeHtml(p.currentStatus || 'Sem status') + '</div>'
|
|
678
|
+
+ '<div class="reportMeta" style="margin-top:8px">Última atualização: ' + escapeHtml(p.lastUpdated || '—') + '</div>'
|
|
679
|
+
+ '<div class="reportMeta">Eventos: ' + escapeHtml(String(p.historyCount || 0)) + '</div>';
|
|
680
|
+
el.appendChild(card);
|
|
681
|
+
}
|
|
682
|
+
if (!filtered.length) {
|
|
683
|
+
const empty = document.createElement('div');
|
|
684
|
+
empty.className = 'help';
|
|
685
|
+
empty.textContent = 'Nenhum projeto encontrado.';
|
|
686
|
+
el.appendChild(empty);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
async function refreshProjects() {
|
|
691
|
+
try {
|
|
692
|
+
const r = await api('/api/projects/list', { dir: dirOrDefault() });
|
|
693
|
+
state.projects = r.projects || [];
|
|
694
|
+
renderProjects();
|
|
695
|
+
} catch (e) {
|
|
696
|
+
const el = $('projectsGrid');
|
|
697
|
+
if (el) el.textContent = 'Falha ao carregar projetos.';
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
659
701
|
async function refreshReportsPage() {
|
|
660
702
|
try {
|
|
661
703
|
setPill('run', 'carregando…');
|
|
@@ -680,6 +722,7 @@
|
|
|
680
722
|
function wireRailNav() {
|
|
681
723
|
const dash = $('railDashboard');
|
|
682
724
|
const rep = $('railReports');
|
|
725
|
+
const proj = $('railProjects');
|
|
683
726
|
const health = $('railCompanion');
|
|
684
727
|
if (dash) {
|
|
685
728
|
dash.onclick = () => {
|
|
@@ -698,6 +741,12 @@
|
|
|
698
741
|
if (!isReports) window.location.href = '/reports';
|
|
699
742
|
};
|
|
700
743
|
}
|
|
744
|
+
if (proj) {
|
|
745
|
+
proj.onclick = () => {
|
|
746
|
+
const isProjects = document.body && document.body.dataset && document.body.dataset.page === 'projects';
|
|
747
|
+
if (!isProjects) window.location.href = '/projects';
|
|
748
|
+
};
|
|
749
|
+
}
|
|
701
750
|
if (health) {
|
|
702
751
|
health.onclick = () => {
|
|
703
752
|
const isHealth = document.body && document.body.dataset && document.body.dataset.page === 'companion';
|
|
@@ -1314,6 +1363,7 @@
|
|
|
1314
1363
|
} catch {}
|
|
1315
1364
|
|
|
1316
1365
|
const isReportsPage = document.body && document.body.dataset && document.body.dataset.page === 'reports';
|
|
1366
|
+
const isProjectsPage = document.body && document.body.dataset && document.body.dataset.page === 'projects';
|
|
1317
1367
|
const isCompanionPage = document.body && document.body.dataset && document.body.dataset.page === 'companion';
|
|
1318
1368
|
|
|
1319
1369
|
// Load persisted settings from the workspace + bootstrap (auto-init + auto-health)
|
|
@@ -1342,6 +1392,11 @@
|
|
|
1342
1392
|
return;
|
|
1343
1393
|
}
|
|
1344
1394
|
|
|
1395
|
+
if (isProjectsPage) {
|
|
1396
|
+
await refreshProjects();
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1345
1400
|
if (isCompanionPage) {
|
|
1346
1401
|
await refreshHealthChecklist();
|
|
1347
1402
|
return;
|
|
@@ -1385,6 +1440,7 @@
|
|
|
1385
1440
|
window.renderReportsList = renderReportsList;
|
|
1386
1441
|
window.renderReportsPage = renderReportsPage;
|
|
1387
1442
|
window.refreshReportsPage = refreshReportsPage;
|
|
1443
|
+
window.refreshProjects = refreshProjects;
|
|
1388
1444
|
window.refreshBlockersInsights = refreshBlockersInsights;
|
|
1389
1445
|
window.refreshHealthChecklist = refreshHealthChecklist;
|
|
1390
1446
|
window.copyOut = copyOut;
|
package/cli/web.js
CHANGED
|
@@ -877,6 +877,11 @@ function reportsHtml(defaultDir) {
|
|
|
877
877
|
return buildReportsHtml(safeDefault, APP_VERSION);
|
|
878
878
|
}
|
|
879
879
|
|
|
880
|
+
function projectsHtml(defaultDir) {
|
|
881
|
+
const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
882
|
+
return buildProjectsHtml(safeDefault, APP_VERSION);
|
|
883
|
+
}
|
|
884
|
+
|
|
880
885
|
function companionHtml(defaultDir) {
|
|
881
886
|
const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
882
887
|
return buildCompanionHtml(safeDefault, APP_VERSION);
|
|
@@ -905,6 +910,7 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
905
910
|
<button class="railBtn active" id="railDashboard" type="button" title="Dashboard">D</button>
|
|
906
911
|
<button class="railBtn" id="railReports" type="button" title="Relatórios">R</button>
|
|
907
912
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">C</button>
|
|
913
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">P</button>
|
|
908
914
|
</div>
|
|
909
915
|
<div class="railBottom">
|
|
910
916
|
<div class="railStatus" id="railStatus" title="status"></div>
|
|
@@ -1155,6 +1161,7 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1155
1161
|
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">D</button>
|
|
1156
1162
|
<button class="railBtn active" id="railReports" type="button" title="Relatórios">R</button>
|
|
1157
1163
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">C</button>
|
|
1164
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">P</button>
|
|
1158
1165
|
</div>
|
|
1159
1166
|
<div class="railBottom">
|
|
1160
1167
|
<div class="railStatus" id="railStatus" title="status"></div>
|
|
@@ -1209,6 +1216,84 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1209
1216
|
</html>`
|
|
1210
1217
|
}
|
|
1211
1218
|
|
|
1219
|
+
function buildProjectsHtml(safeDefault, appVersion) {
|
|
1220
|
+
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
1221
|
+
return `<!doctype html>
|
|
1222
|
+
<html>
|
|
1223
|
+
<head>
|
|
1224
|
+
<meta charset="utf-8" />
|
|
1225
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1226
|
+
<title>Projects</title>
|
|
1227
|
+
<link rel="stylesheet" href="/app.css" />
|
|
1228
|
+
</head>
|
|
1229
|
+
<body data-page="projects">
|
|
1230
|
+
<div class="app">
|
|
1231
|
+
<div class="frame">
|
|
1232
|
+
<div class="shell">
|
|
1233
|
+
|
|
1234
|
+
<aside class="rail">
|
|
1235
|
+
<div class="railTop">
|
|
1236
|
+
<div class="railLogo">F</div>
|
|
1237
|
+
</div>
|
|
1238
|
+
<div class="railNav">
|
|
1239
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">D</button>
|
|
1240
|
+
<button class="railBtn" id="railReports" type="button" title="Relatorios">R</button>
|
|
1241
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">C</button>
|
|
1242
|
+
<button class="railBtn active" id="railProjects" type="button" title="Projects">P</button>
|
|
1243
|
+
</div>
|
|
1244
|
+
<div class="railBottom">
|
|
1245
|
+
<div class="railStatus" id="railStatus" title="status"></div>
|
|
1246
|
+
</div>
|
|
1247
|
+
</aside>
|
|
1248
|
+
|
|
1249
|
+
<main class="center reportsPage" id="projectsPage">
|
|
1250
|
+
<div class="topbar">
|
|
1251
|
+
<div class="brandLine">
|
|
1252
|
+
<span class="spark"></span>
|
|
1253
|
+
<div class="brandStack">
|
|
1254
|
+
<div class="brand">FREYA</div>
|
|
1255
|
+
<div class="brandSub">Projects</div>
|
|
1256
|
+
</div>
|
|
1257
|
+
</div>
|
|
1258
|
+
<div class="topActions">
|
|
1259
|
+
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
1260
|
+
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
1261
|
+
</div>
|
|
1262
|
+
</div>
|
|
1263
|
+
|
|
1264
|
+
<div class="centerBody">
|
|
1265
|
+
<input id="dir" type="hidden" />
|
|
1266
|
+
|
|
1267
|
+
<section class="reportsHeader">
|
|
1268
|
+
<div>
|
|
1269
|
+
<div class="reportsTitle">Projects</div>
|
|
1270
|
+
<div class="reportsSubtitle">Status por projeto com ultima atualizacao e riscos.</div>
|
|
1271
|
+
</div>
|
|
1272
|
+
<div class="reportsActions">
|
|
1273
|
+
<button class="btn small" type="button" onclick="refreshProjects()">Atualizar</button>
|
|
1274
|
+
</div>
|
|
1275
|
+
</section>
|
|
1276
|
+
|
|
1277
|
+
<section class="reportsTools">
|
|
1278
|
+
<input id="projectsFilter" placeholder="filtrar (cliente, projeto, tag)" oninput="renderProjects()" />
|
|
1279
|
+
</section>
|
|
1280
|
+
|
|
1281
|
+
<section class="reportsGrid" id="projectsGrid"></section>
|
|
1282
|
+
</div>
|
|
1283
|
+
</main>
|
|
1284
|
+
|
|
1285
|
+
</div>
|
|
1286
|
+
</div>
|
|
1287
|
+
</div>
|
|
1288
|
+
|
|
1289
|
+
<script>
|
|
1290
|
+
window.__FREYA_DEFAULT_DIR = "${safeDefault}";
|
|
1291
|
+
</script>
|
|
1292
|
+
<script src="/app.js"></script>
|
|
1293
|
+
</body>
|
|
1294
|
+
</html>`;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1212
1297
|
function buildCompanionHtml(safeDefault, appVersion) {
|
|
1213
1298
|
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
1214
1299
|
return `<!doctype html>
|
|
@@ -1232,6 +1317,7 @@ function buildCompanionHtml(safeDefault, appVersion) {
|
|
|
1232
1317
|
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">D</button>
|
|
1233
1318
|
<button class="railBtn" id="railReports" type="button" title="Relatórios">R</button>
|
|
1234
1319
|
<button class="railBtn active" id="railCompanion" type="button" title="Companion">C</button>
|
|
1320
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">P</button>
|
|
1235
1321
|
</div>
|
|
1236
1322
|
<div class="railBottom">
|
|
1237
1323
|
<div class="railStatus" id="railStatus" title="status"></div>
|
|
@@ -1616,6 +1702,14 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1616
1702
|
return;
|
|
1617
1703
|
}
|
|
1618
1704
|
|
|
1705
|
+
if (req.method === 'GET' && req.url === '/projects') {
|
|
1706
|
+
try { res.__freyaDebug.workspaceDir = normalizeWorkspaceDir(dir || './freya'); } catch {}
|
|
1707
|
+
const body = projectsHtml(dir || './freya');
|
|
1708
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
1709
|
+
res.end(body);
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1619
1713
|
if (req.method === 'GET' && req.url === '/app.css') {
|
|
1620
1714
|
const css = fs.readFileSync(path.join(__dirname, 'web-ui.css'), 'utf8');
|
|
1621
1715
|
res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
@@ -1701,7 +1795,41 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1701
1795
|
return safeJson(res, 200, { ok: true, map: out });
|
|
1702
1796
|
}
|
|
1703
1797
|
|
|
1704
|
-
if (req.url === '/api/
|
|
1798
|
+
if (req.url === '/api/projects/list') {
|
|
1799
|
+
const base = path.join(workspaceDir, 'data', 'Clients');
|
|
1800
|
+
const items = [];
|
|
1801
|
+
if (exists(base)) {
|
|
1802
|
+
const stack = [base];
|
|
1803
|
+
while (stack.length) {
|
|
1804
|
+
const dirp = stack.pop();
|
|
1805
|
+
const entries = fs.readdirSync(dirp, { withFileTypes: true });
|
|
1806
|
+
for (const ent of entries) {
|
|
1807
|
+
const full = path.join(dirp, ent.name);
|
|
1808
|
+
if (ent.isDirectory()) stack.push(full);
|
|
1809
|
+
else if (ent.isFile() && ent.name === 'status.json') {
|
|
1810
|
+
const doc = readJsonOrNull(full) || {};
|
|
1811
|
+
const rel = path.relative(base, path.dirname(full)).replace(/\\/g, '/');
|
|
1812
|
+
items.push({
|
|
1813
|
+
slug: rel,
|
|
1814
|
+
client: doc.client || null,
|
|
1815
|
+
program: doc.program || null,
|
|
1816
|
+
stream: doc.stream || null,
|
|
1817
|
+
project: doc.project || null,
|
|
1818
|
+
active: doc.active !== false,
|
|
1819
|
+
currentStatus: doc.currentStatus || '',
|
|
1820
|
+
lastUpdated: doc.lastUpdated || '',
|
|
1821
|
+
tags: Array.isArray(doc.tags) ? doc.tags : [],
|
|
1822
|
+
historyCount: Array.isArray(doc.history) ? doc.history.length : 0
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
items.sort((a,b)=> String(b.lastUpdated||'').localeCompare(String(a.lastUpdated||'')));
|
|
1829
|
+
return safeJson(res, 200, { ok: true, projects: items });
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
if (req.url === '/api/reports/list') {
|
|
1705
1833
|
const reports = listReports(workspaceDir);
|
|
1706
1834
|
return safeJson(res, 200, { reports });
|
|
1707
1835
|
}
|
package/package.json
CHANGED