feat(dashboard): Planning embedded view is a real two-pane like Missions

Left becomes a full-height .planning-sidebar (Missions-matched surface/border/width) with a scrolling list and a bottom-pinned footer; New Session moves to that footer using btn btn-primary (same CTA tokens as the Missions create button — size/color match). Right .planning-detail fills remaining width + full height, so it reads as a true two-pane view. Non-embedded modal presentation unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-06-22 10:19:12 -07:00
parent 2dc36d9ef4
commit d3da3cc301
2 changed files with 38 additions and 48 deletions

View File

@@ -136,29 +136,33 @@ The embedded planning title must read like other embedded-view titles (Command C
position: relative;
}
/* Sidebar */
/*
FNXC:Planning 2026-06-23-01:15:
Embedded Planning is a real two-pane view mirroring Missions (mission-manager__split): the left sidebar is a full-height flex column (list scrolls, footer pinned to bottom) and the right detail pane fills the remaining width AND height. The sidebar surface/border/width treatment matches mission-manager__sidebar — var(--surface) background, a single border-right divider — so the two views read consistently. The flex chain planning-view -> planning-modal--embedded -> planning-modal-body keeps the whole layout filling the main-content pane to the bottom.
*/
.planning-sidebar {
width: 260px;
width: calc(var(--space-lg) * 18.75);
flex-shrink: 0;
border-right: 1px solid var(--border);
background: var(--card);
border-right: var(--btn-border-width) solid var(--border);
background: var(--surface);
display: flex;
flex-direction: column;
min-height: 0;
overflow: hidden;
}
.planning-sidebar-header {
padding: var(--space-md);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
/*
FNXC:Planning 2026-06-23-01:15:
The sidebar footer pins the primary "New session" action to the bottom (parity with mission-manager__sidebar-footer) with the show/hide-archived link beneath it. A top border separates it from the scrolling list.
*/
.planning-sidebar-footer {
display: flex;
flex-direction: column;
align-items: stretch;
gap: var(--space-sm);
}
.planning-sidebar-footer {
padding: var(--space-sm) var(--space-md) var(--space-md);
padding: var(--space-md);
border-top: var(--btn-border-width) solid var(--border);
flex-shrink: 0;
text-align: center;
}
@@ -181,37 +185,18 @@ The embedded planning title must read like other embedded-view titles (Command C
box-shadow: var(--focus-ring-strong);
}
/*
FNXC:Planning 2026-06-23-01:15:
The New session button carries the shared "btn btn-primary" classes so its size/color match Missions' primary sidebar CTA (mission-manager__sidebar-cta) exactly; this local rule only stretches it full-width and centers the icon+label. The .active state (no session selected) keeps the primary fill and adds an inset ring so the user can tell they're on the new-session view without losing the Missions color match.
*/
.planning-sidebar-new {
display: flex;
align-items: center;
width: 100%;
justify-content: center;
gap: var(--space-sm);
width: 100%;
padding: var(--space-sm) var(--space-md);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-md);
color: var(--text);
font-size: calc(var(--space-sm) + var(--space-xs) * 1.25);
font-weight: 500;
cursor: pointer;
transition: background var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.planning-sidebar-new:hover {
background: var(--card-hover);
border-color: var(--todo);
}
.planning-sidebar-new.active {
background: color-mix(in srgb, var(--todo) 15%, transparent);
border-color: var(--todo);
color: var(--todo);
}
.planning-sidebar-new:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
box-shadow: inset 0 0 0 var(--btn-border-width) var(--cta-text);
}
.planning-sidebar-list {

View File

@@ -3156,17 +3156,10 @@ function PlanningSessionList({
const { t } = useTranslation("app");
return (
<aside className="planning-sidebar" aria-label={t("planning.planningSessions", "Planning sessions")}>
<div className="planning-sidebar-header">
<button
className={`planning-sidebar-new ${selectedSessionId === null ? "active" : ""}`}
onClick={onNewSession}
type="button"
>
<MessageSquarePlus size={16} />
<span>{t("planning.newSession", "New session")}</span>
</button>
</div>
{/*
FNXC:Planning 2026-06-23-01:15:
The embedded Planning view reads as a real two-pane layout matching Missions: the left sidebar is a full-height flex column whose session list scrolls and whose primary action ("New session") is pinned to a bottom footer (parity with MissionManager's mission-manager__sidebar-footer + sidebar-cta). The header that previously held the New session button is removed so the list owns the top of the sidebar like the Missions list.
*/}
<div className="planning-sidebar-list">
{sessions.length === 0 && !loading && (
<div className="planning-sidebar-empty text-muted">
@@ -3266,6 +3259,18 @@ function PlanningSessionList({
})}
</div>
<div className="planning-sidebar-footer">
{/*
FNXC:Planning 2026-06-23-01:15:
The New session CTA mirrors Missions' primary sidebar action: it reuses the shared "btn btn-primary" look (same base button class MissionManager pairs with mission-manager__sidebar-cta) so size and color match the Missions create button exactly, full-width and bottom-anchored. The "active" state (no session selected) keeps a subtle accent so the user can tell they're on the new-session view.
*/}
<button
className={`btn btn-primary planning-sidebar-new ${selectedSessionId === null ? "active" : ""}`}
onClick={onNewSession}
type="button"
>
<MessageSquarePlus size={16} />
<span>{t("planning.newSession", "New session")}</span>
</button>
<a
href="#"
className="planning-sidebar-toggle-archived-link"