Files
emba-course-solver/openspec/changes/ui-improvements/design.md
Bill Ballou f8bab9ee33 UI improvements: responsive layout, unified panels, credit legend
- Add responsive 2-panel layout (mobile single-col, tablet/desktop grid)
- Unify specialization ranking with credit bars, status badges, and
  expandable allocation breakdowns (remove standalone ResultsDashboard)
- Inline decision tree ceiling data on course buttons with spec counts
- Add Clear All button to reset all course selections
- Add collapsible CreditLegend explaining bars, badges, and limits
- Extract ModeComparison and MutualExclusionWarnings to Notifications
- Add useMediaQuery hook with matchMedia-based breakpoint detection
2026-02-28 21:17:50 -05:00

84 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
The app currently uses a fixed 3-column grid layout (`280px 1fr 1fr`): specialization ranking on the left, course selection in the middle, results dashboard on the right. This breaks completely on mobile — it requires a minimum ~960px viewport. Specialization progress (credit bars, status badges) lives in a separate Results panel from the ranking list, forcing users to cross-reference two panels. The decision tree is buried at the bottom of the Results panel, disconnected from the course selection it's meant to guide.
Current component structure:
- `App.tsx` — 3-column grid, wires state to components
- `SpecializationRanking.tsx` — drag-and-drop ranking with status badges (no credit bars)
- `CourseSelection.tsx` — elective sets grouped by term, pin/unpin per set
- `ResultsDashboard.tsx` — credit bars, allocation breakdown, decision tree, mode comparison, mutual exclusion warnings
- `ModeToggle.tsx` — toggle between maximize-count and priority-order
- `state/appState.ts` — useReducer with reorder/setMode/pinCourse/unpinCourse actions
## Goals / Non-Goals
**Goals:**
- Usable on mobile phones (360px+), tablets, and desktops
- Single specialization panel that shows rank, reorder controls, status, and credit progress together
- Course selection UI that shows decision tree ceiling outcomes inline with each course option
- Clear all selections with one action
- New users can understand the credit bars and status badges without external docs
**Non-Goals:**
- Complete visual redesign / theming — keep existing colors and inline style approach
- Offline/PWA support
- Changing the optimization engine or data layer
- Touch-based drag reordering on mobile (arrow buttons already work; drag is a nice-to-have that already has TouchSensor)
## Decisions
### 1. Responsive layout: CSS media queries via inline styles with a `useMediaQuery` hook
Use a custom `useMediaQuery` hook that returns the current breakpoint. App.tsx switches between layouts:
- **Mobile (<640px)**: Single column, stacked vertically. Specializations panel, then course panel. Each is full-width.
- **Tablet (6401024px)**: Two columns, specializations left (300px), courses right (flex).
- **Desktop (>1024px)**: Same two columns with more breathing room.
Why not CSS classes / a CSS framework: The entire app uses inline styles. Adding a CSS framework for just responsive layout would be inconsistent. A hook-based approach keeps the pattern uniform and avoids adding dependencies.
### 2. Unified specialization panel: Extend `SpecializationRanking` to include credit bars
Merge the per-spec progress display from `ResultsDashboard` directly into `SpecializationRanking`'s `SortableItem`. Each row becomes:
```
[▲▼] [⠿] [rank] [name] [credits/9.0] [status badge]
[====credit bar====]
```
The row is clickable to expand allocation breakdown (for achieved specs). This replaces the top section of `ResultsDashboard`.
`ResultsDashboard` is reduced to just global notifications: mode comparison banner, mutual exclusion warnings, and the summary count — displayed above the specialization panel in the layout, not as a separate column.
### 3. Unified course panel: Inline decision tree data per elective set
Extend `ElectiveSet` to accept optional ceiling analysis data. When an open set has tree results, each course button shows its ceiling outcome on the right:
```
[Mergers & Acquisitions 3 specs (BNK, FIN, LCM)]
[Digital Strategy 3 specs (BNK, FIN, LCM)]
```
The standalone `DecisionTree` component at the bottom of ResultsDashboard is removed. The "high impact" indicator moves to the set header. Loading state shows a subtle spinner on sets still being analyzed.
### 4. Clear all: New reducer action + button in course selection header
Add a `clearAll` action to the reducer that resets `pinnedCourses` to `{}`. Place a "Clear All" button in the `CourseSelection` header, visible only when at least one course is pinned. Styled as a small text button (consistent with the per-set "clear" buttons).
### 5. Credit explainer: Collapsible legend above the specialization panel
Add a small "How to read this" toggle that expands to show:
- What the credit bar segments mean (dark = allocated from pinned courses, light = potential from open sets, tick mark = 9-credit threshold)
- What each status badge means (achieved, achievable, missing required course, unreachable)
- Brief note that max 3 specializations can be achieved (30 credits / 9 per spec)
Collapsed by default to avoid visual noise for returning users. State is not persisted (resets on reload).
### 6. Notifications area: Mode comparison and warnings float above the spec panel
`ModeComparison` and `MutualExclusionWarnings` render as banners at the top of the page (below the header/mode toggle, above the specialization panel). They're not tied to a specific column.
## Risks / Trade-offs
- **Inline responsive styles are verbose** → Accepted; keeps the project consistent and avoids adding a CSS framework for one change. The `useMediaQuery` hook keeps conditional logic manageable.
- **Unified spec rows are denser on mobile** → Mitigated by making credit bar slim (already 6px) and keeping text sizes small. Allocation breakdown is tap-to-expand.
- **Decision tree data arrives asynchronously** → Course buttons render immediately without ceiling data; outcomes appear progressively as the worker completes each set. No layout shift since the ceiling text is right-aligned and fits on the same line.
- **14 spec rows + 12 elective sets is a lot of vertical content on mobile** → Accepted trade-off; all content is important. The two-section layout (specs then courses) gives a clear reading order. Users can scroll naturally.