Skip to content

refactor(examlense): dedupe model-meta + extract shared UI components #14

Merged
zhngharry merged 10 commits into
mainfrom
refactor/examlense-results-exams-dedup
Jul 16, 2026
Merged

refactor(examlense): dedupe model-meta + extract shared UI components #14
zhngharry merged 10 commits into
mainfrom
refactor/examlense-results-exams-dedup

Conversation

@zhngharry

Copy link
Copy Markdown
Contributor

No description provided.

zhngharry and others added 10 commits July 15, 2026 17:36
Phase 1 of the pages/ reuse roadmap (results + exams list):

- Reuse scoreRollup() in ByQuestionType, FiguresComparison, and AllTasks
  instead of re-implementing the earned/max reduce.
- Extract <ScoreBar> (5 call sites) and <RollupRow>, plus formatScoreSummary()
  for the shared "{count} tasks · {earned}/{max} ({pct}%)" label.
- Delete dead OverallScoreCard (no importers).
- TaskBreakdownTable: compute the intentional binary perfect/zeroed coloring
  once instead of inlining the conditions in both row and cell.
- Consolidate exam-status label/style/sort-rank into one EXAM_STATUS_META
  table (lib/exam/exam-status), consumed by ExamStatusBadge and the ExamsTable
  Status sort; removes the duplicated ready→Draft collapse rule.

Behavior-preserving; typecheck, vitest (18), and build all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the 13 CRUD mutators plus sectionIdForTask/sectionIdForBlock/
unconfirmIfNeeded out of ExamEdit into a page-local useExamMutations hook,
with shared withSaveStatus + optimisticListUpdate helpers for the plain-
envelope paths. Add-paths, duplicateTask, addSection, deleteSection and
persistReorder keep their bespoke bodies (behavior-preserving). Drops
ExamEdit from 1442 to 1127 lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the duplicated section-grouping / taskLetter / figureLabel memos and
the hash-based current-section bootstrap from ExamEdit and GradingView into a
shared useSectionGroups hook (parameterized by includeEmpty) plus
useCurrentSectionId (editor passes introGate). Add index-based slug helpers
(sectionIndexSlug, UNASSIGNED_SLUG) to exam-helpers and a unit test for the
pure grouping/letter logic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both effects computed state that can be derived during render (see React's
"You Might Not Need an Effect"):

- AllTasksList: resolve the active section slug during render (fall back to the
  first section when the stored slug is empty/stale) instead of syncing it in an
  effect — also removes an extra render pass.
- ExamsTable: derive `safePage` by clamping to totalPages during render (drops
  the clamp effect); reset to page 1 on sort inside `onSort`, and on the Title
  search prop change via the render-time prev-value pattern (drops the reset
  effect). Pagination controls now read `safePage`.

No behavior change. Typecheck (my files) and vitest pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse the byte-identical inline-edit state machine and textarea/MarkdownView
toggle duplicated in TaskCard (prompt) and ContextBlockCard (content) into a
shared useInlineTextEdit hook and MarkdownEditField component. Preserves each
card's distinct read-view surface (TaskCard's bordered surface vs the context
block's card-less hover tint).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the destructive AlertDialog scaffold (task delete + convert, context
and figure delete) and the near-identical block action dropdowns with shared
ConfirmDeleteDialog and BlockActionsMenu components. TaskCard passes its
Duplicate + Convert-type items as menu children. (ExamEdit's section-delete
dialog is migrated in the following commit alongside its other edits.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the ~150-line renderBlockItem closure into a BlockItem subcomponent with a
pure blockRowDescriptor helper (relocating taskTypeIcon), and migrate the
section-delete dialog to the shared ConfirmDeleteDialog. ExamEdit's render now
maps items to <BlockItem/>. Drops ExamEdit to ~857 lines.

The working tree already carried the useExamBundle data-hook swap (roadmap
item 19); it is interleaved with these import changes in ExamEdit, so it lands
here together with the new use-exam-bundle.ts it depends on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the editing/draft + Enter-blur/Escape-revert machine duplicated by
InlineTitle and SectionTitleInput into a useClickToEdit hook; both compose it.
Each keeps its own placeholder and commit guard (SectionTitleInput still skips
the patch when the trimmed name is unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Extract useExamRealtime wrapping the subscribeExam + invalidate effect shared
  by ExamEdit and GradingView.
- Hoist preventNumberWheelChange to lib/utils (was copied in TaskCard and
  TaskGradingPanel).
- Merge ExamEdit's handleJumpToTask + handleGoToScore into one
  jumpToTask(taskId, { focusScore }).

GradingView also picks up the working tree's useExamBundle swap (roadmap item
19), interleaved with the realtime change; use-exam-bundle.ts landed earlier.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold solver-model-meta into a shared model-meta module, split the admin
panels into MetricsTable/PanelMessage/StatCardGrid, and share ModelPickerList
across the parser/solver start-exam steps. Net -300 lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zhngharry
zhngharry requested review from florivn and yinggez July 15, 2026 23:06

@florivn florivn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the refactor. The shared model metadata, UI components, and extracted hooks preserve the existing behavior across the editor, grading, and results flows. The added section-group tests cover the relevant ordering and grouping cases. CI is green and I found no blocking issues. Approving.

@zhngharry
zhngharry merged commit 1f8b500 into main Jul 16, 2026
10 checks passed
@zhngharry
zhngharry deleted the refactor/examlense-results-exams-dedup branch July 16, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants