v1.5.1: Show completed plan in Top Plans
Once every elective set is pinned, the Top Plans panel now renders the user's completed selection as a single "Your Plan" card showing the achievement count and pinned courses. Previously the panel went blank because the all-pinned branch in useAppState cleared topPlans, even though the spec strip was already showing the achievement. Synthesizes a single PlanOutcome from pinnedAssignments + optimizationResult.achieved + the priority scorer; TopPlans detects the all-pinned state via ELECTIVE_SETS.every(...) and bypasses the length>0 filter so a 0-spec completed plan still renders honestly.
This commit is contained in:
@@ -38,7 +38,9 @@ function formatScore(n: number): string {
|
||||
|
||||
export function TopPlans({ plans, partial, loading, progress, pinnedCourses, ranking, showAnimatedBar = true, onAdopt, onPin, onUnpin }: TopPlansProps) {
|
||||
const rankWeight = useMemo(() => makePriorityRankWeight(ranking), [ranking]);
|
||||
const visible = plans.filter((p) => p.achievedSpecs.length > 0);
|
||||
const allPinned = ELECTIVE_SETS.every((s) => pinnedCourses[s.id]);
|
||||
const visible = allPinned ? plans : plans.filter((p) => p.achievedSpecs.length > 0);
|
||||
const heading = allPinned ? 'Your Plan' : 'Top Plans';
|
||||
|
||||
const pct = progress && progress.iterationsTotal > 0
|
||||
? Math.min(100, (progress.iterations / progress.iterationsTotal) * 100)
|
||||
@@ -55,8 +57,8 @@ export function TopPlans({ plans, partial, loading, progress, pinnedCourses, ran
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: '8px', gap: '8px', flexWrap: 'wrap' }}>
|
||||
<h3 style={{ fontSize: '14px', margin: 0, color: '#444' }}>
|
||||
Top Plans
|
||||
{visible.length > 0 && (
|
||||
{heading}
|
||||
{!allPinned && visible.length > 0 && (
|
||||
<span style={{ fontSize: '11px', color: '#888', fontWeight: 400, marginLeft: '6px' }}>
|
||||
ranked by specs achieved
|
||||
</span>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
selectPriorityTarget,
|
||||
} from '../solver/decisionTree';
|
||||
import { computeUpperBounds } from '../solver/feasibility';
|
||||
import { makePriorityScorer } from '../solver/priority';
|
||||
import type { SetAnalysis, PlanOutcome } from '../solver/decisionTree';
|
||||
import type { WorkerRequest, WorkerResponse } from '../workers/decisionTree.worker';
|
||||
import { cancelledCourseIds, courseIdsByName, courseById } from '../data/lookups';
|
||||
@@ -183,8 +184,16 @@ export function useAppState() {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
|
||||
if (openSetIds.length === 0) {
|
||||
// Selection is complete — synthesize a single PlanOutcome from the
|
||||
// pinned assignments so Top Plans can render the user's completed plan.
|
||||
const scorer = makePriorityScorer(state.ranking);
|
||||
const completed: PlanOutcome = {
|
||||
courseAssignments: pinnedAssignments,
|
||||
achievedSpecs: optimizationResult.achieved,
|
||||
priorityScore: scorer(optimizationResult.achieved),
|
||||
};
|
||||
setTreeResults([]);
|
||||
setTopPlans([]);
|
||||
setTopPlans([completed]);
|
||||
setTopPlansPartial(false);
|
||||
setSearchProgress(null);
|
||||
setTreeLoading(false);
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import react from '@vitejs/plugin-react'
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
define: {
|
||||
__APP_VERSION__: JSON.stringify('1.5.0'),
|
||||
__APP_VERSION__: JSON.stringify('1.5.1'),
|
||||
__APP_VERSION_DATE__: JSON.stringify('2026-05-10'),
|
||||
},
|
||||
server: {
|
||||
|
||||
Reference in New Issue
Block a user