diff --git a/frontend/dashboard.css b/frontend/dashboard.css
index b245c22..302bbd5 100644
--- a/frontend/dashboard.css
+++ b/frontend/dashboard.css
@@ -94,6 +94,10 @@ textarea { resize: vertical; min-height: 120px; }
.plan-today-item { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:8px; align-items:center; padding:10px; border:1px solid #1f3a5f; border-radius:12px; background:#0f1d33; }
.plan-today-item-copy { min-width:0; overflow-wrap:anywhere; }
.plan-today-item-actions { display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; }
+.plan-today-candidate-actions { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:6px; }
+.plan-preview-actions { position:fixed; z-index:76; right:16px; bottom:calc(16px + env(safe-area-inset-bottom)); display:grid; grid-template-columns:1fr 1fr; gap:8px; width:min(420px,calc(100vw - 32px)); padding:10px; border:1px solid #31577f; border-radius:12px; background:rgba(11,21,38,.98); box-shadow:0 12px 36px rgba(0,0,0,.45); }
+.plan-preview-actions[hidden] { display:none; }
+.plan-preview-actions button { min-height:44px; }
.plan-today-actions { position:sticky; bottom:0; display:grid; grid-template-columns:1fr 1fr; gap:8px; margin:16px -6px -6px; padding:12px 6px; padding-bottom:calc(12px + env(safe-area-inset-bottom)); background:rgba(11,21,38,.98); border-top:1px solid #2a496e; }
.plan-today-actions button { min-height:44px; width:100%; }
.my-work-header { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; }
@@ -365,6 +369,7 @@ textarea { resize: vertical; min-height: 120px; }
.plan-today-panel { width:100%; border-left:0; padding:14px; }
.plan-today-item { grid-template-columns:1fr; }
.plan-today-item-actions { display:grid; grid-template-columns:repeat(3,1fr); width:100%; }
+ .plan-today-candidate-actions { grid-template-columns:repeat(2,1fr); width:100%; }
.plan-today-item-actions button { min-width:0; padding-inline:4px; }
.work-filters { width:100%; }
.work-filter { flex:1 1 calc(50% - 8px); }
diff --git a/frontend/dashboard.js b/frontend/dashboard.js
index 4bf8e31..2d4996d 100644
--- a/frontend/dashboard.js
+++ b/frontend/dashboard.js
@@ -728,7 +728,7 @@
const title = escapeHtml(item.title || 'Untitled work');
const controls = selected ?
'
' :
- '';
+ '';
return '' + key + '' + title + '
' + controls + '';
}
@@ -747,6 +747,15 @@
qs('#plan-today-error').textContent = result === 'full' ? 'Today is full. Remove an item before adding another.' : '';
renderPlanToday();
}));
+ document.querySelectorAll('[data-plan-preview]').forEach(button => button.addEventListener('click', () => {
+ const item = planToday.item(button.dataset.planPreview);
+ if (!canPreviewPlanItem(item)) {
+ qs('#plan-today-error').textContent = 'Preview unavailable offline. Reconnect or add the item without previewing it.';
+ return;
+ }
+ qs('#plan-today-error').textContent = '';
+ if (planTodayPreview.open(item, button)) taskOverlayHistory.open('plan-today-preview');
+ }));
document.querySelectorAll('[data-plan-remove]').forEach(button => button.addEventListener('click', () => {
planToday.toggle(planToday.item(button.dataset.planRemove));
qs('#plan-today-error').textContent = '';
@@ -792,6 +801,58 @@
},
});
+ function canPreviewPlanItem(item) {
+ if (!item || !offlineWorkMode) return Boolean(item);
+ const login = planningOwnerLogin || confirmedOwnerLogin ||
+ String(offlineWorkStore.load()?.user?.login || '').trim();
+ return Boolean(offlineWorkStore.loadDetail(login, item));
+ }
+
+ function openPlanPreviewDetail(item, trigger) {
+ qs('#plan-today-sheet').hidden = true;
+ qs('#plan-preview-actions').hidden = false;
+ if (offlineWorkMode) {
+ openRoutedWork(item, trigger);
+ } else if (item.kind === 'update' && item.has_update) {
+ updateTrigger = trigger;
+ notificationReader.open(item, lastMyWork);
+ } else if (item.is_review || item.kind === 'review') {
+ reviewTrigger = trigger;
+ openReviewSheet(item, trigger);
+ } else if (item.kind === 'issue') {
+ issueTrigger = trigger;
+ openIssueSheet(item, trigger);
+ } else if (item.kind === 'pull') {
+ pullTrigger = trigger;
+ openPullSheet(item, trigger);
+ }
+ }
+
+ const planTodayPreview = createPlanTodayPreview({
+ planner: planToday,
+ identity: item => todayWork.identity(item),
+ getScroll: () => qs('.plan-today-panel').scrollTop,
+ setScroll: value => requestAnimationFrame(() => { qs('.plan-today-panel').scrollTop = value; }),
+ onOpen: openPlanPreviewDetail,
+ onClose: (_item, trigger) => {
+ closeOpenWorkSheets();
+ qs('#plan-preview-actions').hidden = true;
+ qs('#plan-today-sheet').hidden = false;
+ renderPlanToday();
+ requestAnimationFrame(() => trigger?.focus());
+ },
+ });
+ let addPlanPreviewOnReturn = false;
+
+ qs('#back-to-plan').addEventListener('click', () => {
+ addPlanPreviewOnReturn = false;
+ taskOverlayHistory.close();
+ });
+ qs('#add-plan-preview').addEventListener('click', () => {
+ addPlanPreviewOnReturn = true;
+ taskOverlayHistory.close();
+ });
+
function openPlanToday(trigger, navigate = true) {
if (!planningOwnerLogin) {
qs('#my-work-action-status').textContent = 'Planning is unavailable until your operator identity is restored.';
@@ -2652,11 +2713,16 @@
mobileSearchViewport.close();
qs('#open-palette').focus();
}
- if (previous === 'plan-today' && kind !== 'plan-today') closePlanToday(false);
+ if (previous === 'plan-today-preview' && kind !== 'plan-today-preview') {
+ if (addPlanPreviewOnReturn) planTodayPreview.close({ add:true });
+ else planTodayPreview.close();
+ addPlanPreviewOnReturn = false;
+ }
+ if (previous === 'plan-today' && kind !== 'plan-today' && kind !== 'plan-today-preview') closePlanToday(false);
if (kind === 'new' && previous !== 'new') openCreateIssueSheet(false);
if (kind === 'find' && previous !== 'find') openFindWorkSheet(false);
if (kind === 'search' && previous !== 'search-preview') openCommandPalette(false);
- if (kind === 'plan-today' && previous !== 'plan-today') openPlanToday(planTodayTrigger, false);
+ if (kind === 'plan-today' && previous !== 'plan-today' && previous !== 'plan-today-preview') openPlanToday(planTodayTrigger, false);
},
});
taskOverlayHistory.start();
diff --git a/frontend/index.html b/frontend/index.html
index d6018c3..0d1dee9 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -196,6 +196,11 @@
+
+
+
+
+