@cccarv82/freya 3.3.1 → 3.4.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/cli/web-ui.css CHANGED
@@ -1803,43 +1803,56 @@ body:not([data-page="kanban"]) .kanban-col-body {
1803
1803
  min-width: 180px;
1804
1804
  }
1805
1805
 
1806
- /* Kanban blockers strip */
1807
- .kanban-blockers-list {
1806
+ /* Blocker Kanban Board */
1807
+ .blocker-kanban-board {
1808
+ display: grid;
1809
+ grid-template-columns: repeat(3, 1fr);
1810
+ gap: 12px;
1811
+ min-height: 120px;
1812
+ }
1813
+
1814
+ @media (max-width: 900px) {
1815
+ .blocker-kanban-board { grid-template-columns: 1fr; }
1816
+ }
1817
+
1818
+ .blocker-kanban-col {
1808
1819
  display: flex;
1809
- flex-wrap: wrap;
1810
- gap: 8px;
1820
+ flex-direction: column;
1821
+ background: var(--bg);
1822
+ border: 1px solid var(--line2);
1823
+ min-height: 80px;
1811
1824
  }
1812
1825
 
1813
- .kanban-blocker-card {
1826
+ .blocker-kanban-col-body {
1827
+ flex: 1;
1828
+ padding: 8px;
1814
1829
  display: flex;
1815
- align-items: center;
1830
+ flex-direction: column;
1816
1831
  gap: 8px;
1817
- padding: 8px 12px;
1818
- background: rgba(239, 68, 68, 0.04);
1819
- border: 1px solid rgba(239, 68, 68, 0.15);
1820
- border-left: 3px solid #ef4444;
1821
- flex: 1 1 300px;
1822
- max-width: 500px;
1832
+ overflow-y: auto;
1833
+ max-height: 300px;
1834
+ min-height: 50px;
1835
+ transition: background 0.15s;
1823
1836
  }
1824
1837
 
1825
- .kanban-blocker-sev {
1826
- font-size: 9px;
1827
- font-weight: 800;
1828
- letter-spacing: 0.5px;
1829
- padding: 2px 6px;
1830
- border: 1px solid;
1831
- flex-shrink: 0;
1838
+ .blocker-kanban-col-body.drag-over {
1839
+ background: rgba(239, 68, 68, 0.06);
1832
1840
  }
1833
1841
 
1834
- .kanban-blocker-title {
1835
- font-size: 12px;
1836
- font-weight: 600;
1837
- color: var(--text);
1838
- flex: 1;
1839
- min-width: 0;
1840
- overflow: hidden;
1841
- text-overflow: ellipsis;
1842
- white-space: nowrap;
1842
+ /* Blocker column header colors */
1843
+ .kanban-col-head.blocker-open { border-bottom-color: #ef4444; }
1844
+ .kanban-col-head.blocker-open .kanban-col-title { color: #ef4444; }
1845
+ .kanban-col-head.blocker-open .kanban-col-count { background: #ef444422; color: #ef4444; }
1846
+ .kanban-col-head.blocker-mitigating { border-bottom-color: #f59e0b; }
1847
+ .kanban-col-head.blocker-mitigating .kanban-col-title { color: #f59e0b; }
1848
+ .kanban-col-head.blocker-mitigating .kanban-col-count { background: #f59e0b22; color: #f59e0b; }
1849
+ .kanban-col-head.blocker-resolved { border-bottom-color: #22c55e; }
1850
+ .kanban-col-head.blocker-resolved .kanban-col-title { color: #22c55e; }
1851
+ .kanban-col-head.blocker-resolved .kanban-col-count { background: #22c55e22; color: #22c55e; }
1852
+
1853
+ /* Blocker card in kanban (reuses .kanban-card) */
1854
+ .kanban-card.blocker-card {
1855
+ border-left: 3px solid #ef4444;
1843
1856
  }
1844
1857
 
1845
1858
  /* ── Quick-Add Modal ── */
package/cli/web-ui.js CHANGED
@@ -3232,7 +3232,7 @@
3232
3232
  // Drag events
3233
3233
  if (cat !== 'COMPLETED') {
3234
3234
  card.addEventListener('dragstart', function(e) {
3235
- e.dataTransfer.setData('text/plain', t.id);
3235
+ e.dataTransfer.setData('text/task-id', t.id);
3236
3236
  e.dataTransfer.effectAllowed = 'move';
3237
3237
  card.classList.add('dragging');
3238
3238
  });
@@ -3252,34 +3252,10 @@
3252
3252
  };
3253
3253
  }
3254
3254
 
3255
- // Edit button - inline edit via prompt
3255
+ // Edit button - open full edit modal
3256
3256
  var editBtn = card.querySelector('.edit-btn');
3257
3257
  if (editBtn) {
3258
- editBtn.onclick = async function() {
3259
- var newCat = prompt('Categoria (DO_NOW|SCHEDULE|DELEGATE):', cat);
3260
- if (!newCat) return;
3261
- var newSlug = prompt('Projeto (slug):', t.projectSlug || '');
3262
- if (newSlug === null) return;
3263
- var currentDueBR = t.dueDate ? fmtDateBR(t.dueDate) : '';
3264
- var newDueBR = prompt('Due date (dd/mm/aaaa):', currentDueBR);
3265
- if (newDueBR === null) return;
3266
- // Convert dd/mm/aaaa back to YYYY-MM-DD for API
3267
- var newDue = '';
3268
- if (newDueBR && newDueBR.indexOf('/') > -1) {
3269
- var dp = newDueBR.split('/');
3270
- if (dp.length === 3) newDue = dp[2] + '-' + dp[1] + '-' + dp[0];
3271
- } else {
3272
- newDue = newDueBR; // fallback: accept YYYY-MM-DD too
3273
- }
3274
- try {
3275
- await api('/api/tasks/update', {
3276
- dir: dirOrDefault(), id: t.id,
3277
- patch: { category: newCat, projectSlug: newSlug, dueDate: newDue || null }
3278
- });
3279
- showToast('ok', 'Atualizada');
3280
- await loadKanban();
3281
- } catch { showToast('err', 'Falhou'); }
3282
- };
3258
+ editBtn.onclick = function() { openTaskEdit(t); };
3283
3259
  }
3284
3260
  }
3285
3261
 
@@ -3299,7 +3275,7 @@
3299
3275
  el.addEventListener('drop', async function(e) {
3300
3276
  e.preventDefault();
3301
3277
  el.classList.remove('drag-over');
3302
- var taskId = e.dataTransfer.getData('text/plain');
3278
+ var taskId = e.dataTransfer.getData('text/task-id');
3303
3279
  if (!taskId) return;
3304
3280
  try {
3305
3281
  await api('/api/tasks/update', {
@@ -3316,33 +3292,229 @@
3316
3292
 
3317
3293
  function renderKanbanBlockers() {
3318
3294
  var wrap = $('kanbanBlockers');
3319
- var list = $('kanbanBlockersList');
3320
- if (!wrap || !list) return;
3295
+ if (!wrap) return;
3321
3296
 
3322
3297
  var sel = $('kanbanFilterProject');
3323
3298
  var filter = sel ? sel.value : '';
3324
- var blockers = _kanbanData.blockers;
3299
+ var blockers = _kanbanData.blockers || [];
3325
3300
  if (filter) blockers = blockers.filter(function(b) { return b.projectSlug === filter; });
3326
3301
 
3327
3302
  if (blockers.length === 0) { wrap.style.display = 'none'; return; }
3328
3303
  wrap.style.display = 'block';
3329
- list.innerHTML = '';
3330
3304
 
3305
+ // Sort into columns
3306
+ var cols = { OPEN: [], MITIGATING: [], RESOLVED: [] };
3331
3307
  blockers.forEach(function(b) {
3332
- var card = document.createElement('div');
3333
- card.className = 'kanban-blocker-card';
3334
- var color = sevColor(b.severity);
3335
- card.innerHTML = '<span class="kanban-blocker-sev" style="background:' + color + '22; color:' + color + '; border-color:' + color + '44;">' + escapeHtml(b.severity || 'BLOCKER') + '</span>'
3336
- + '<span class="kanban-blocker-title">' + escapeHtml(b.title || '') + '</span>'
3337
- + (b.projectSlug ? '<span class="kanban-tag">' + escapeHtml(b.projectSlug) + '</span>' : '')
3338
- + (b.owner ? '<span class="kanban-owner">' + escapeHtml(b.owner) + '</span>' : '');
3339
- card.style.cursor = 'pointer';
3340
- card.addEventListener('click', function() { showDetailPanel(b, 'blocker'); });
3341
- list.appendChild(card);
3308
+ if (cols[b.status]) cols[b.status].push(b);
3309
+ });
3310
+
3311
+ var idMap = { OPEN: 'colBlockerOpen', MITIGATING: 'colBlockerMitigating', RESOLVED: 'colBlockerResolved' };
3312
+ var countMap = { OPEN: 'countBlockerOpen', MITIGATING: 'countBlockerMitigating', RESOLVED: 'countBlockerResolved' };
3313
+
3314
+ ['OPEN', 'MITIGATING', 'RESOLVED'].forEach(function(status) {
3315
+ var el = $(idMap[status]);
3316
+ var countEl = $(countMap[status]);
3317
+ if (!el) return;
3318
+ el.innerHTML = '';
3319
+ if (countEl) countEl.textContent = cols[status].length;
3320
+
3321
+ cols[status].forEach(function(b) {
3322
+ var card = document.createElement('div');
3323
+ card.className = 'kanban-card blocker-card';
3324
+ card.setAttribute('data-blocker-id', b.id);
3325
+ if (status !== 'RESOLVED') card.draggable = true;
3326
+
3327
+ var color = sevColor(b.severity);
3328
+ var html = '<div class="kanban-card-header">'
3329
+ + '<span class="kanban-priority-dot" style="background:' + color + ';" title="' + escapeHtml(b.severity) + '"></span>'
3330
+ + '<span class="kanban-card-desc">' + escapeHtml(b.title || '') + '</span>'
3331
+ + '</div>';
3332
+ html += '<div class="kanban-card-meta">';
3333
+ if (b.projectSlug) html += '<span class="kanban-tag">' + escapeHtml(b.projectSlug) + '</span>';
3334
+ if (b.owner) html += '<span class="kanban-tag" style="background:var(--chip);">' + escapeHtml(b.owner) + '</span>';
3335
+ html += '</div>';
3336
+ if (status !== 'RESOLVED') {
3337
+ html += '<div class="kanban-card-actions">'
3338
+ + '<button class="edit-btn" title="Editar">'
3339
+ + '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>'
3340
+ + '</button></div>';
3341
+ }
3342
+ card.innerHTML = html;
3343
+
3344
+ // Click → detail panel (only if not dragging)
3345
+ var _bDragged = false;
3346
+ card.addEventListener('mousedown', function() { _bDragged = false; });
3347
+ card.addEventListener('mousemove', function() { _bDragged = true; });
3348
+ card.addEventListener('click', function(e) {
3349
+ if (_bDragged) return;
3350
+ if (e.target.closest('.kanban-card-actions')) return;
3351
+ showDetailPanel(b, 'blocker');
3352
+ });
3353
+
3354
+ // Drag events
3355
+ if (status !== 'RESOLVED') {
3356
+ card.addEventListener('dragstart', function(e) {
3357
+ e.dataTransfer.setData('text/blocker-id', b.id);
3358
+ e.dataTransfer.effectAllowed = 'move';
3359
+ card.classList.add('dragging');
3360
+ });
3361
+ card.addEventListener('dragend', function() {
3362
+ card.classList.remove('dragging');
3363
+ });
3364
+ }
3365
+
3366
+ // Edit button
3367
+ var editBtn = card.querySelector('.edit-btn');
3368
+ if (editBtn) {
3369
+ editBtn.onclick = function(e) { e.stopPropagation(); openBlockerEdit(b); };
3370
+ }
3371
+
3372
+ el.appendChild(card);
3373
+ });
3374
+
3375
+ // Drop zone
3376
+ el.addEventListener('dragover', function(e) {
3377
+ e.preventDefault();
3378
+ e.dataTransfer.dropEffect = 'move';
3379
+ el.classList.add('drag-over');
3380
+ });
3381
+ el.addEventListener('dragleave', function() {
3382
+ el.classList.remove('drag-over');
3383
+ });
3384
+ el.addEventListener('drop', async function(e) {
3385
+ e.preventDefault();
3386
+ el.classList.remove('drag-over');
3387
+ var blockerId = e.dataTransfer.getData('text/blocker-id');
3388
+ if (!blockerId) return;
3389
+ try {
3390
+ await api('/api/blockers/update', {
3391
+ dir: dirOrDefault(), id: blockerId,
3392
+ patch: { status: status }
3393
+ });
3394
+ showToast('ok', 'Blocker → ' + status);
3395
+ await loadKanban();
3396
+ } catch { showToast('err', 'Falhou'); }
3397
+ });
3342
3398
  });
3343
3399
  }
3344
3400
 
3401
+ // === Task Edit Modal ===
3402
+ function openTaskEdit(t) {
3403
+ var overlay = $('taskEditOverlay');
3404
+ if (!overlay) return;
3405
+ $('teTaskId').value = t.id;
3406
+ $('teDesc').value = t.description || '';
3407
+ $('teCat').value = t.category || 'DO_NOW';
3408
+ $('tePriority').value = t.priority || '';
3409
+ $('teSlug').value = t.projectSlug || '';
3410
+ $('teDue').value = t.dueDate ? fmtDateBR(t.dueDate) : '';
3411
+ var stream = $('teStream');
3412
+ if (stream) stream.value = t.streamSlug || '';
3413
+ overlay.style.display = 'flex';
3414
+ // Wire date picker sync
3415
+ var picker = $('teDuePicker');
3416
+ var txt = $('teDue');
3417
+ if (picker && txt) {
3418
+ picker.onchange = function() {
3419
+ if (!picker.value) return;
3420
+ var p = picker.value.split('-');
3421
+ if (p.length === 3) txt.value = p[2] + '/' + p[1] + '/' + p[0];
3422
+ };
3423
+ }
3424
+ $('teDesc').focus();
3425
+ }
3426
+
3427
+ function closeTaskEdit() {
3428
+ var overlay = $('taskEditOverlay');
3429
+ if (overlay) overlay.style.display = 'none';
3430
+ }
3431
+
3432
+ async function submitTaskEdit() {
3433
+ var id = $('teTaskId').value;
3434
+ if (!id) return;
3435
+ var desc = ($('teDesc').value || '').trim();
3436
+ var cat = $('teCat').value;
3437
+ var priority = $('tePriority').value;
3438
+ var slug = ($('teSlug').value || '').trim();
3439
+ var dueBR = ($('teDue').value || '').trim();
3440
+ var stream = ($('teStream') ? $('teStream').value : '').trim();
3441
+
3442
+ // Convert dd/mm/aaaa → YYYY-MM-DD
3443
+ var due = null;
3444
+ if (dueBR && dueBR.indexOf('/') > -1) {
3445
+ var dp = dueBR.split('/');
3446
+ if (dp.length === 3) due = dp[2] + '-' + dp[1] + '-' + dp[0];
3447
+ } else if (dueBR) {
3448
+ due = dueBR;
3449
+ }
3450
+
3451
+ try {
3452
+ await api('/api/tasks/update', {
3453
+ dir: dirOrDefault(), id: id,
3454
+ patch: { description: desc, category: cat, priority: priority, projectSlug: slug, dueDate: due, streamSlug: stream }
3455
+ });
3456
+ closeTaskEdit();
3457
+ showToast('ok', 'Task atualizada');
3458
+ await loadKanban();
3459
+ } catch { showToast('err', 'Falhou'); }
3460
+ }
3461
+
3462
+ // === Blocker Edit Modal ===
3463
+ function openBlockerEdit(b) {
3464
+ var overlay = $('blockerEditOverlay');
3465
+ if (!overlay) return;
3466
+ $('beBlockerId').value = b.id;
3467
+ $('beTitle').value = b.title || '';
3468
+ $('beSeverity').value = b.severity || 'MEDIUM';
3469
+ $('beStatus').value = b.status || 'OPEN';
3470
+ $('beSlug').value = b.projectSlug || '';
3471
+ $('beOwner').value = b.owner || '';
3472
+ $('beNextAction').value = b.nextAction || '';
3473
+ overlay.style.display = 'flex';
3474
+ $('beTitle').focus();
3475
+ }
3476
+
3477
+ function closeBlockerEdit() {
3478
+ var overlay = $('blockerEditOverlay');
3479
+ if (overlay) overlay.style.display = 'none';
3480
+ }
3481
+
3482
+ async function submitBlockerEdit() {
3483
+ var id = $('beBlockerId').value;
3484
+ if (!id) return;
3485
+ var title = ($('beTitle').value || '').trim();
3486
+ var severity = $('beSeverity').value;
3487
+ var status = $('beStatus').value;
3488
+ var slug = ($('beSlug').value || '').trim();
3489
+ var owner = ($('beOwner').value || '').trim();
3490
+ var nextAction = ($('beNextAction').value || '').trim();
3491
+
3492
+ try {
3493
+ await api('/api/blockers/update', {
3494
+ dir: dirOrDefault(), id: id,
3495
+ patch: { title: title, severity: severity, status: status, projectSlug: slug, owner: owner, nextAction: nextAction }
3496
+ });
3497
+ closeBlockerEdit();
3498
+ showToast('ok', 'Blocker atualizado');
3499
+ await loadKanban();
3500
+ } catch { showToast('err', 'Falhou'); }
3501
+ }
3502
+
3503
+ // Close modals on Escape
3504
+ document.addEventListener('keydown', function(e) {
3505
+ if (e.key === 'Escape') {
3506
+ if ($('taskEditOverlay') && $('taskEditOverlay').style.display !== 'none') closeTaskEdit();
3507
+ if ($('blockerEditOverlay') && $('blockerEditOverlay').style.display !== 'none') closeBlockerEdit();
3508
+ }
3509
+ });
3510
+
3345
3511
  window.loadKanban = loadKanban;
3346
3512
  window.filterKanban = filterKanban;
3347
3513
  window.loadDelta = loadDelta;
3514
+ window.openTaskEdit = openTaskEdit;
3515
+ window.closeTaskEdit = closeTaskEdit;
3516
+ window.submitTaskEdit = submitTaskEdit;
3517
+ window.openBlockerEdit = openBlockerEdit;
3518
+ window.closeBlockerEdit = closeBlockerEdit;
3519
+ window.submitBlockerEdit = submitBlockerEdit;
3348
3520
  })();
package/cli/web.js CHANGED
@@ -1110,6 +1110,79 @@ function buildDocsHtml(safeDefault, appVersion) {
1110
1110
  </div>
1111
1111
  </div>
1112
1112
 
1113
+ <!-- Task Edit Modal -->
1114
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
1115
+ <div class="qa-modal" style="width:520px;">
1116
+ <div class="qa-header">
1117
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
1118
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
1119
+ </div>
1120
+ <input type="hidden" id="teTaskId" />
1121
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
1122
+ <div class="qa-row">
1123
+ <select id="teCat" class="qa-select">
1124
+ <option value="DO_NOW">DO_NOW</option>
1125
+ <option value="SCHEDULE">SCHEDULE</option>
1126
+ <option value="DELEGATE">DELEGATE</option>
1127
+ </select>
1128
+ <select id="tePriority" class="qa-select">
1129
+ <option value="">Prioridade</option>
1130
+ <option value="critical">Cr\u00edtica</option>
1131
+ <option value="high">Alta</option>
1132
+ <option value="medium">M\u00e9dia</option>
1133
+ <option value="low">Baixa</option>
1134
+ </select>
1135
+ </div>
1136
+ <div class="qa-row">
1137
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1138
+ <div class="qa-date-wrap">
1139
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
1140
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
1141
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
1142
+ </div>
1143
+ </div>
1144
+ <div class="qa-row">
1145
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
1146
+ </div>
1147
+ <div class="qa-row" style="justify-content:flex-end;">
1148
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
1149
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
1150
+ </div>
1151
+ </div>
1152
+ </div>
1153
+ <!-- Blocker Edit Modal -->
1154
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
1155
+ <div class="qa-modal" style="width:520px;">
1156
+ <div class="qa-header">
1157
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
1158
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
1159
+ </div>
1160
+ <input type="hidden" id="beBlockerId" />
1161
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
1162
+ <div class="qa-row">
1163
+ <select id="beSeverity" class="qa-select">
1164
+ <option value="CRITICAL">Critical</option>
1165
+ <option value="HIGH">High</option>
1166
+ <option value="MEDIUM">Medium</option>
1167
+ <option value="LOW">Low</option>
1168
+ </select>
1169
+ <select id="beStatus" class="qa-select">
1170
+ <option value="OPEN">Open</option>
1171
+ <option value="MITIGATING">Mitigating</option>
1172
+ <option value="RESOLVED">Resolved</option>
1173
+ </select>
1174
+ </div>
1175
+ <div class="qa-row">
1176
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1177
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
1178
+ </div>
1179
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
1180
+ <div class="qa-row" style="justify-content:flex-end;">
1181
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
1182
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
1183
+ </div>
1184
+ </div>
1185
+ </div>
1113
1186
  <script>
1114
1187
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
1115
1188
  </script>
@@ -1289,13 +1362,35 @@ function buildHtml(safeDefault, appVersion) {
1289
1362
  </div>
1290
1363
  </div>
1291
1364
 
1292
- <!-- Blockers strip below kanban -->
1365
+ <!-- Blocker Kanban Board -->
1293
1366
  <div id="kanbanBlockers" style="margin-top:20px; display:none;">
1294
1367
  <div style="font-size:13px; font-weight:700; color:var(--text); margin-bottom:8px; display:flex; align-items:center; gap:6px;">
1295
1368
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
1296
- Blockers Ativos
1369
+ Blockers
1370
+ </div>
1371
+ <div id="blockerKanbanBoard" class="blocker-kanban-board">
1372
+ <div class="blocker-kanban-col" data-blocker-status="OPEN">
1373
+ <div class="kanban-col-head blocker-open">
1374
+ <span class="kanban-col-title">OPEN</span>
1375
+ <span class="kanban-col-count" id="countBlockerOpen">0</span>
1376
+ </div>
1377
+ <div class="blocker-kanban-col-body" id="colBlockerOpen"></div>
1378
+ </div>
1379
+ <div class="blocker-kanban-col" data-blocker-status="MITIGATING">
1380
+ <div class="kanban-col-head blocker-mitigating">
1381
+ <span class="kanban-col-title">MITIGATING</span>
1382
+ <span class="kanban-col-count" id="countBlockerMitigating">0</span>
1383
+ </div>
1384
+ <div class="blocker-kanban-col-body" id="colBlockerMitigating"></div>
1385
+ </div>
1386
+ <div class="blocker-kanban-col" data-blocker-status="RESOLVED">
1387
+ <div class="kanban-col-head blocker-resolved">
1388
+ <span class="kanban-col-title">RESOLVED (7d)</span>
1389
+ <span class="kanban-col-count" id="countBlockerResolved">0</span>
1390
+ </div>
1391
+ <div class="blocker-kanban-col-body" id="colBlockerResolved"></div>
1392
+ </div>
1297
1393
  </div>
1298
- <div id="kanbanBlockersList" class="kanban-blockers-list"></div>
1299
1394
  </div>
1300
1395
 
1301
1396
  <!-- Insights de bloqueios -->
@@ -1353,6 +1448,79 @@ function buildHtml(safeDefault, appVersion) {
1353
1448
  </div>
1354
1449
  </div>
1355
1450
 
1451
+ <!-- Task Edit Modal -->
1452
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
1453
+ <div class="qa-modal" style="width:520px;">
1454
+ <div class="qa-header">
1455
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
1456
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
1457
+ </div>
1458
+ <input type="hidden" id="teTaskId" />
1459
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
1460
+ <div class="qa-row">
1461
+ <select id="teCat" class="qa-select">
1462
+ <option value="DO_NOW">DO_NOW</option>
1463
+ <option value="SCHEDULE">SCHEDULE</option>
1464
+ <option value="DELEGATE">DELEGATE</option>
1465
+ </select>
1466
+ <select id="tePriority" class="qa-select">
1467
+ <option value="">Prioridade</option>
1468
+ <option value="critical">Cr\u00edtica</option>
1469
+ <option value="high">Alta</option>
1470
+ <option value="medium">M\u00e9dia</option>
1471
+ <option value="low">Baixa</option>
1472
+ </select>
1473
+ </div>
1474
+ <div class="qa-row">
1475
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1476
+ <div class="qa-date-wrap">
1477
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
1478
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
1479
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
1480
+ </div>
1481
+ </div>
1482
+ <div class="qa-row">
1483
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
1484
+ </div>
1485
+ <div class="qa-row" style="justify-content:flex-end;">
1486
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
1487
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
1488
+ </div>
1489
+ </div>
1490
+ </div>
1491
+ <!-- Blocker Edit Modal -->
1492
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
1493
+ <div class="qa-modal" style="width:520px;">
1494
+ <div class="qa-header">
1495
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
1496
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
1497
+ </div>
1498
+ <input type="hidden" id="beBlockerId" />
1499
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
1500
+ <div class="qa-row">
1501
+ <select id="beSeverity" class="qa-select">
1502
+ <option value="CRITICAL">Critical</option>
1503
+ <option value="HIGH">High</option>
1504
+ <option value="MEDIUM">Medium</option>
1505
+ <option value="LOW">Low</option>
1506
+ </select>
1507
+ <select id="beStatus" class="qa-select">
1508
+ <option value="OPEN">Open</option>
1509
+ <option value="MITIGATING">Mitigating</option>
1510
+ <option value="RESOLVED">Resolved</option>
1511
+ </select>
1512
+ </div>
1513
+ <div class="qa-row">
1514
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1515
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
1516
+ </div>
1517
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
1518
+ <div class="qa-row" style="justify-content:flex-end;">
1519
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
1520
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
1521
+ </div>
1522
+ </div>
1523
+ </div>
1356
1524
  <script>
1357
1525
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
1358
1526
  </script>
@@ -1469,6 +1637,79 @@ function buildReportsHtml(safeDefault, appVersion) {
1469
1637
  </div>
1470
1638
  </div>
1471
1639
 
1640
+ <!-- Task Edit Modal -->
1641
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
1642
+ <div class="qa-modal" style="width:520px;">
1643
+ <div class="qa-header">
1644
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
1645
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
1646
+ </div>
1647
+ <input type="hidden" id="teTaskId" />
1648
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
1649
+ <div class="qa-row">
1650
+ <select id="teCat" class="qa-select">
1651
+ <option value="DO_NOW">DO_NOW</option>
1652
+ <option value="SCHEDULE">SCHEDULE</option>
1653
+ <option value="DELEGATE">DELEGATE</option>
1654
+ </select>
1655
+ <select id="tePriority" class="qa-select">
1656
+ <option value="">Prioridade</option>
1657
+ <option value="critical">Cr\u00edtica</option>
1658
+ <option value="high">Alta</option>
1659
+ <option value="medium">M\u00e9dia</option>
1660
+ <option value="low">Baixa</option>
1661
+ </select>
1662
+ </div>
1663
+ <div class="qa-row">
1664
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1665
+ <div class="qa-date-wrap">
1666
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
1667
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
1668
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
1669
+ </div>
1670
+ </div>
1671
+ <div class="qa-row">
1672
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
1673
+ </div>
1674
+ <div class="qa-row" style="justify-content:flex-end;">
1675
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
1676
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
1677
+ </div>
1678
+ </div>
1679
+ </div>
1680
+ <!-- Blocker Edit Modal -->
1681
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
1682
+ <div class="qa-modal" style="width:520px;">
1683
+ <div class="qa-header">
1684
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
1685
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
1686
+ </div>
1687
+ <input type="hidden" id="beBlockerId" />
1688
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
1689
+ <div class="qa-row">
1690
+ <select id="beSeverity" class="qa-select">
1691
+ <option value="CRITICAL">Critical</option>
1692
+ <option value="HIGH">High</option>
1693
+ <option value="MEDIUM">Medium</option>
1694
+ <option value="LOW">Low</option>
1695
+ </select>
1696
+ <select id="beStatus" class="qa-select">
1697
+ <option value="OPEN">Open</option>
1698
+ <option value="MITIGATING">Mitigating</option>
1699
+ <option value="RESOLVED">Resolved</option>
1700
+ </select>
1701
+ </div>
1702
+ <div class="qa-row">
1703
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1704
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
1705
+ </div>
1706
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
1707
+ <div class="qa-row" style="justify-content:flex-end;">
1708
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
1709
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
1710
+ </div>
1711
+ </div>
1712
+ </div>
1472
1713
  <script>
1473
1714
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
1474
1715
  </script>
@@ -1565,6 +1806,79 @@ function buildProjectsHtml(safeDefault, appVersion) {
1565
1806
  </div>
1566
1807
  </div>
1567
1808
 
1809
+ <!-- Task Edit Modal -->
1810
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
1811
+ <div class="qa-modal" style="width:520px;">
1812
+ <div class="qa-header">
1813
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
1814
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
1815
+ </div>
1816
+ <input type="hidden" id="teTaskId" />
1817
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
1818
+ <div class="qa-row">
1819
+ <select id="teCat" class="qa-select">
1820
+ <option value="DO_NOW">DO_NOW</option>
1821
+ <option value="SCHEDULE">SCHEDULE</option>
1822
+ <option value="DELEGATE">DELEGATE</option>
1823
+ </select>
1824
+ <select id="tePriority" class="qa-select">
1825
+ <option value="">Prioridade</option>
1826
+ <option value="critical">Cr\u00edtica</option>
1827
+ <option value="high">Alta</option>
1828
+ <option value="medium">M\u00e9dia</option>
1829
+ <option value="low">Baixa</option>
1830
+ </select>
1831
+ </div>
1832
+ <div class="qa-row">
1833
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1834
+ <div class="qa-date-wrap">
1835
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
1836
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
1837
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
1838
+ </div>
1839
+ </div>
1840
+ <div class="qa-row">
1841
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
1842
+ </div>
1843
+ <div class="qa-row" style="justify-content:flex-end;">
1844
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
1845
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
1846
+ </div>
1847
+ </div>
1848
+ </div>
1849
+ <!-- Blocker Edit Modal -->
1850
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
1851
+ <div class="qa-modal" style="width:520px;">
1852
+ <div class="qa-header">
1853
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
1854
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
1855
+ </div>
1856
+ <input type="hidden" id="beBlockerId" />
1857
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
1858
+ <div class="qa-row">
1859
+ <select id="beSeverity" class="qa-select">
1860
+ <option value="CRITICAL">Critical</option>
1861
+ <option value="HIGH">High</option>
1862
+ <option value="MEDIUM">Medium</option>
1863
+ <option value="LOW">Low</option>
1864
+ </select>
1865
+ <select id="beStatus" class="qa-select">
1866
+ <option value="OPEN">Open</option>
1867
+ <option value="MITIGATING">Mitigating</option>
1868
+ <option value="RESOLVED">Resolved</option>
1869
+ </select>
1870
+ </div>
1871
+ <div class="qa-row">
1872
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1873
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
1874
+ </div>
1875
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
1876
+ <div class="qa-row" style="justify-content:flex-end;">
1877
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
1878
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
1879
+ </div>
1880
+ </div>
1881
+ </div>
1568
1882
  <script>
1569
1883
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
1570
1884
  </script>
@@ -1771,6 +2085,79 @@ function buildGraphHtml(safeDefault, appVersion) {
1771
2085
  </div>
1772
2086
  </div>
1773
2087
 
2088
+ <!-- Task Edit Modal -->
2089
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
2090
+ <div class="qa-modal" style="width:520px;">
2091
+ <div class="qa-header">
2092
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
2093
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
2094
+ </div>
2095
+ <input type="hidden" id="teTaskId" />
2096
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
2097
+ <div class="qa-row">
2098
+ <select id="teCat" class="qa-select">
2099
+ <option value="DO_NOW">DO_NOW</option>
2100
+ <option value="SCHEDULE">SCHEDULE</option>
2101
+ <option value="DELEGATE">DELEGATE</option>
2102
+ </select>
2103
+ <select id="tePriority" class="qa-select">
2104
+ <option value="">Prioridade</option>
2105
+ <option value="critical">Cr\u00edtica</option>
2106
+ <option value="high">Alta</option>
2107
+ <option value="medium">M\u00e9dia</option>
2108
+ <option value="low">Baixa</option>
2109
+ </select>
2110
+ </div>
2111
+ <div class="qa-row">
2112
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
2113
+ <div class="qa-date-wrap">
2114
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
2115
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
2116
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
2117
+ </div>
2118
+ </div>
2119
+ <div class="qa-row">
2120
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
2121
+ </div>
2122
+ <div class="qa-row" style="justify-content:flex-end;">
2123
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
2124
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
2125
+ </div>
2126
+ </div>
2127
+ </div>
2128
+ <!-- Blocker Edit Modal -->
2129
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
2130
+ <div class="qa-modal" style="width:520px;">
2131
+ <div class="qa-header">
2132
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
2133
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
2134
+ </div>
2135
+ <input type="hidden" id="beBlockerId" />
2136
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
2137
+ <div class="qa-row">
2138
+ <select id="beSeverity" class="qa-select">
2139
+ <option value="CRITICAL">Critical</option>
2140
+ <option value="HIGH">High</option>
2141
+ <option value="MEDIUM">Medium</option>
2142
+ <option value="LOW">Low</option>
2143
+ </select>
2144
+ <select id="beStatus" class="qa-select">
2145
+ <option value="OPEN">Open</option>
2146
+ <option value="MITIGATING">Mitigating</option>
2147
+ <option value="RESOLVED">Resolved</option>
2148
+ </select>
2149
+ </div>
2150
+ <div class="qa-row">
2151
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
2152
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
2153
+ </div>
2154
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
2155
+ <div class="qa-row" style="justify-content:flex-end;">
2156
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
2157
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
2158
+ </div>
2159
+ </div>
2160
+ </div>
1774
2161
  <script>
1775
2162
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
1776
2163
  </script>
@@ -1907,6 +2294,79 @@ function buildCompanionHtml(safeDefault, appVersion) {
1907
2294
  </div>
1908
2295
  </div>
1909
2296
 
2297
+ <!-- Task Edit Modal -->
2298
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
2299
+ <div class="qa-modal" style="width:520px;">
2300
+ <div class="qa-header">
2301
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
2302
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
2303
+ </div>
2304
+ <input type="hidden" id="teTaskId" />
2305
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
2306
+ <div class="qa-row">
2307
+ <select id="teCat" class="qa-select">
2308
+ <option value="DO_NOW">DO_NOW</option>
2309
+ <option value="SCHEDULE">SCHEDULE</option>
2310
+ <option value="DELEGATE">DELEGATE</option>
2311
+ </select>
2312
+ <select id="tePriority" class="qa-select">
2313
+ <option value="">Prioridade</option>
2314
+ <option value="critical">Cr\u00edtica</option>
2315
+ <option value="high">Alta</option>
2316
+ <option value="medium">M\u00e9dia</option>
2317
+ <option value="low">Baixa</option>
2318
+ </select>
2319
+ </div>
2320
+ <div class="qa-row">
2321
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
2322
+ <div class="qa-date-wrap">
2323
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
2324
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
2325
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
2326
+ </div>
2327
+ </div>
2328
+ <div class="qa-row">
2329
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
2330
+ </div>
2331
+ <div class="qa-row" style="justify-content:flex-end;">
2332
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
2333
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
2334
+ </div>
2335
+ </div>
2336
+ </div>
2337
+ <!-- Blocker Edit Modal -->
2338
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
2339
+ <div class="qa-modal" style="width:520px;">
2340
+ <div class="qa-header">
2341
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
2342
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
2343
+ </div>
2344
+ <input type="hidden" id="beBlockerId" />
2345
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
2346
+ <div class="qa-row">
2347
+ <select id="beSeverity" class="qa-select">
2348
+ <option value="CRITICAL">Critical</option>
2349
+ <option value="HIGH">High</option>
2350
+ <option value="MEDIUM">Medium</option>
2351
+ <option value="LOW">Low</option>
2352
+ </select>
2353
+ <select id="beStatus" class="qa-select">
2354
+ <option value="OPEN">Open</option>
2355
+ <option value="MITIGATING">Mitigating</option>
2356
+ <option value="RESOLVED">Resolved</option>
2357
+ </select>
2358
+ </div>
2359
+ <div class="qa-row">
2360
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
2361
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
2362
+ </div>
2363
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
2364
+ <div class="qa-row" style="justify-content:flex-end;">
2365
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
2366
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
2367
+ </div>
2368
+ </div>
2369
+ </div>
1910
2370
  <script>
1911
2371
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
1912
2372
  </script>
@@ -4080,11 +4540,14 @@ async function cmdWeb({ port, dir, open, dev }) {
4080
4540
  queryUpdates.push('description = ?');
4081
4541
  params.push(patch.description.trim());
4082
4542
  }
4083
- if (typeof patch.priority === 'string') {
4543
+ // Handle all metadata fields in one read-modify-write
4544
+ const hasMetaPatch = typeof patch.priority === 'string' || typeof patch.streamSlug === 'string';
4545
+ if (hasMetaPatch) {
4084
4546
  const row = dl.db.prepare('SELECT metadata FROM tasks WHERE id = ?').get(id);
4085
4547
  let meta = {};
4086
4548
  try { meta = row && row.metadata ? JSON.parse(row.metadata) : {}; } catch { meta = {}; }
4087
- meta.priority = patch.priority.trim().toLowerCase();
4549
+ if (typeof patch.priority === 'string') meta.priority = patch.priority.trim().toLowerCase() || undefined;
4550
+ if (typeof patch.streamSlug === 'string') meta.streamSlug = patch.streamSlug.trim() || undefined;
4088
4551
  queryUpdates.push('metadata = ?');
4089
4552
  params.push(JSON.stringify(meta));
4090
4553
  }
@@ -4343,12 +4806,15 @@ async function cmdWeb({ port, dir, open, dev }) {
4343
4806
  };
4344
4807
  });
4345
4808
 
4809
+ const sevenDaysAgo = new Date(Date.now() - 7 * 86400000).toISOString();
4346
4810
  const openBlockers = dl.db.prepare(`
4347
4811
  SELECT * FROM blockers WHERE status IN ('OPEN', 'MITIGATING')
4812
+ OR (status = 'RESOLVED' AND resolved_at >= ?)
4348
4813
  ORDER BY
4814
+ CASE status WHEN 'OPEN' THEN 0 WHEN 'MITIGATING' THEN 1 WHEN 'RESOLVED' THEN 2 ELSE 9 END ASC,
4349
4815
  CASE severity WHEN 'CRITICAL' THEN 0 WHEN 'HIGH' THEN 1 WHEN 'MEDIUM' THEN 2 WHEN 'LOW' THEN 3 ELSE 9 END ASC,
4350
4816
  created_at ASC
4351
- `).all().map(b => {
4817
+ `).all(sevenDaysAgo).map(b => {
4352
4818
  let meta = {};
4353
4819
  try { meta = b.metadata ? JSON.parse(b.metadata) : {}; } catch { meta = {}; }
4354
4820
  return {
@@ -4605,6 +5071,79 @@ function buildSettingsHtml(safeDefault, appVersion) {
4605
5071
  </div>
4606
5072
  </div>
4607
5073
 
5074
+ <!-- Task Edit Modal -->
5075
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
5076
+ <div class="qa-modal" style="width:520px;">
5077
+ <div class="qa-header">
5078
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
5079
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
5080
+ </div>
5081
+ <input type="hidden" id="teTaskId" />
5082
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
5083
+ <div class="qa-row">
5084
+ <select id="teCat" class="qa-select">
5085
+ <option value="DO_NOW">DO_NOW</option>
5086
+ <option value="SCHEDULE">SCHEDULE</option>
5087
+ <option value="DELEGATE">DELEGATE</option>
5088
+ </select>
5089
+ <select id="tePriority" class="qa-select">
5090
+ <option value="">Prioridade</option>
5091
+ <option value="critical">Cr\u00edtica</option>
5092
+ <option value="high">Alta</option>
5093
+ <option value="medium">M\u00e9dia</option>
5094
+ <option value="low">Baixa</option>
5095
+ </select>
5096
+ </div>
5097
+ <div class="qa-row">
5098
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
5099
+ <div class="qa-date-wrap">
5100
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
5101
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
5102
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
5103
+ </div>
5104
+ </div>
5105
+ <div class="qa-row">
5106
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
5107
+ </div>
5108
+ <div class="qa-row" style="justify-content:flex-end;">
5109
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
5110
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
5111
+ </div>
5112
+ </div>
5113
+ </div>
5114
+ <!-- Blocker Edit Modal -->
5115
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
5116
+ <div class="qa-modal" style="width:520px;">
5117
+ <div class="qa-header">
5118
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
5119
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
5120
+ </div>
5121
+ <input type="hidden" id="beBlockerId" />
5122
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
5123
+ <div class="qa-row">
5124
+ <select id="beSeverity" class="qa-select">
5125
+ <option value="CRITICAL">Critical</option>
5126
+ <option value="HIGH">High</option>
5127
+ <option value="MEDIUM">Medium</option>
5128
+ <option value="LOW">Low</option>
5129
+ </select>
5130
+ <select id="beStatus" class="qa-select">
5131
+ <option value="OPEN">Open</option>
5132
+ <option value="MITIGATING">Mitigating</option>
5133
+ <option value="RESOLVED">Resolved</option>
5134
+ </select>
5135
+ </div>
5136
+ <div class="qa-row">
5137
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
5138
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
5139
+ </div>
5140
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
5141
+ <div class="qa-row" style="justify-content:flex-end;">
5142
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
5143
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
5144
+ </div>
5145
+ </div>
5146
+ </div>
4608
5147
  <script>
4609
5148
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
4610
5149
  </script>
@@ -4728,13 +5267,35 @@ function buildKanbanHtml(safeDefault, appVersion) {
4728
5267
  </div>
4729
5268
  </div>
4730
5269
 
4731
- <!-- Blockers strip below kanban -->
5270
+ <!-- Blocker Kanban Board -->
4732
5271
  <div id="kanbanBlockers" style="margin-top:20px; display:none;">
4733
5272
  <div style="font-size:13px; font-weight:700; color:var(--text); margin-bottom:8px; display:flex; align-items:center; gap:6px;">
4734
5273
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
4735
- Blockers Ativos
5274
+ Blockers
5275
+ </div>
5276
+ <div id="blockerKanbanBoard" class="blocker-kanban-board">
5277
+ <div class="blocker-kanban-col" data-blocker-status="OPEN">
5278
+ <div class="kanban-col-head blocker-open">
5279
+ <span class="kanban-col-title">OPEN</span>
5280
+ <span class="kanban-col-count" id="countBlockerOpen">0</span>
5281
+ </div>
5282
+ <div class="blocker-kanban-col-body" id="colBlockerOpen"></div>
5283
+ </div>
5284
+ <div class="blocker-kanban-col" data-blocker-status="MITIGATING">
5285
+ <div class="kanban-col-head blocker-mitigating">
5286
+ <span class="kanban-col-title">MITIGATING</span>
5287
+ <span class="kanban-col-count" id="countBlockerMitigating">0</span>
5288
+ </div>
5289
+ <div class="blocker-kanban-col-body" id="colBlockerMitigating"></div>
5290
+ </div>
5291
+ <div class="blocker-kanban-col" data-blocker-status="RESOLVED">
5292
+ <div class="kanban-col-head blocker-resolved">
5293
+ <span class="kanban-col-title">RESOLVED (7d)</span>
5294
+ <span class="kanban-col-count" id="countBlockerResolved">0</span>
5295
+ </div>
5296
+ <div class="blocker-kanban-col-body" id="colBlockerResolved"></div>
5297
+ </div>
4736
5298
  </div>
4737
- <div id="kanbanBlockersList" class="kanban-blockers-list"></div>
4738
5299
  </div>
4739
5300
  </div>
4740
5301
  </main>
@@ -4779,6 +5340,79 @@ function buildKanbanHtml(safeDefault, appVersion) {
4779
5340
  </div>
4780
5341
  </div>
4781
5342
 
5343
+ <!-- Task Edit Modal -->
5344
+ <div id="taskEditOverlay" class="qa-overlay" style="display:none;">
5345
+ <div class="qa-modal" style="width:520px;">
5346
+ <div class="qa-header">
5347
+ <span style="font-weight:700; font-size:14px;">Editar Task</span>
5348
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()" style="padding:2px 8px;">&times;</button>
5349
+ </div>
5350
+ <input type="hidden" id="teTaskId" />
5351
+ <textarea id="teDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
5352
+ <div class="qa-row">
5353
+ <select id="teCat" class="qa-select">
5354
+ <option value="DO_NOW">DO_NOW</option>
5355
+ <option value="SCHEDULE">SCHEDULE</option>
5356
+ <option value="DELEGATE">DELEGATE</option>
5357
+ </select>
5358
+ <select id="tePriority" class="qa-select">
5359
+ <option value="">Prioridade</option>
5360
+ <option value="critical">Cr\u00edtica</option>
5361
+ <option value="high">Alta</option>
5362
+ <option value="medium">M\u00e9dia</option>
5363
+ <option value="low">Baixa</option>
5364
+ </select>
5365
+ </div>
5366
+ <div class="qa-row">
5367
+ <input id="teSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
5368
+ <div class="qa-date-wrap">
5369
+ <input id="teDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
5370
+ <input id="teDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
5371
+ <button type="button" class="qa-date-btn" onclick="var p=document.getElementById('teDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
5372
+ </div>
5373
+ </div>
5374
+ <div class="qa-row">
5375
+ <input id="teStream" class="qa-input" placeholder="Stream / frente (opcional)" style="flex:1;" />
5376
+ </div>
5377
+ <div class="qa-row" style="justify-content:flex-end;">
5378
+ <button class="btn small" type="button" onclick="window.closeTaskEdit()">Cancelar</button>
5379
+ <button class="btn primary small" type="button" onclick="window.submitTaskEdit()">Salvar</button>
5380
+ </div>
5381
+ </div>
5382
+ </div>
5383
+ <!-- Blocker Edit Modal -->
5384
+ <div id="blockerEditOverlay" class="qa-overlay" style="display:none;">
5385
+ <div class="qa-modal" style="width:520px;">
5386
+ <div class="qa-header">
5387
+ <span style="font-weight:700; font-size:14px;">Editar Blocker</span>
5388
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()" style="padding:2px 8px;">&times;</button>
5389
+ </div>
5390
+ <input type="hidden" id="beBlockerId" />
5391
+ <textarea id="beTitle" class="qa-input" placeholder="T\u00edtulo do blocker..." rows="2"></textarea>
5392
+ <div class="qa-row">
5393
+ <select id="beSeverity" class="qa-select">
5394
+ <option value="CRITICAL">Critical</option>
5395
+ <option value="HIGH">High</option>
5396
+ <option value="MEDIUM">Medium</option>
5397
+ <option value="LOW">Low</option>
5398
+ </select>
5399
+ <select id="beStatus" class="qa-select">
5400
+ <option value="OPEN">Open</option>
5401
+ <option value="MITIGATING">Mitigating</option>
5402
+ <option value="RESOLVED">Resolved</option>
5403
+ </select>
5404
+ </div>
5405
+ <div class="qa-row">
5406
+ <input id="beSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
5407
+ <input id="beOwner" class="qa-input" placeholder="Respons\u00e1vel" style="flex:1;" />
5408
+ </div>
5409
+ <textarea id="beNextAction" class="qa-input" placeholder="Pr\u00f3xima a\u00e7\u00e3o..." rows="2"></textarea>
5410
+ <div class="qa-row" style="justify-content:flex-end;">
5411
+ <button class="btn small" type="button" onclick="window.closeBlockerEdit()">Cancelar</button>
5412
+ <button class="btn primary small" type="button" onclick="window.submitBlockerEdit()">Salvar</button>
5413
+ </div>
5414
+ </div>
5415
+ </div>
4782
5416
  <script>
4783
5417
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
4784
5418
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "3.3.1",
3
+ "version": "3.4.0",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js && node scripts/validate-structure.js",