Skip to content

Migrate custom_admin Schedule Builder to Radix UI#4664

Open
marcoacierno wants to merge 3 commits into
mainfrom
worktree-schedule-builder-radix-ui
Open

Migrate custom_admin Schedule Builder to Radix UI#4664
marcoacierno wants to merge 3 commits into
mainfrom
worktree-schedule-builder-radix-ui

Conversation

@marcoacierno
Copy link
Copy Markdown
Member

What

Migrates the custom_admin Schedule Builder UI to @radix-ui/themes, matching the pattern already used by the Invitation Letter Document Builder.

Changes

  • Custom div Modal → Radix Dialog (content guarded on data so the close animation can't deref null after data is cleared)
  • Native <input> / <select> / <button>TextField / Select / Button
  • Raw <h1>/<strong>/<ul><li> markup → Heading / Text / Card / Badge / DataList
  • Pending-items basket 👈/👉 → IconButton + lucide chevrons
  • Removed now-unused shared/modal.tsx and the .btn utility class
  • Dropped a stray console.log and an empty else in search-event

Unchanged (deliberately)

CSS-grid calendar layout, react-dnd drag-and-drop, GraphQL, and the speaker-availability badges/tooltips.

Verification

  • ✅ Biome (project-pinned, via pre-commit hook) passes
  • ⚠️ pnpm build not run in this environment: it needs pnpm codegen artifacts (git-ignored) generated against a live admin GraphQL backend. Please compile-check in a normal dev env. Watch on first run: Radix Card/TextField ref forwarding to drag/focus, and Select portal layering above the Dialog overlay.

Spec: backend/custom_admin/SPEC-schedule-builder-radix.md

🤖 Generated with Claude Code

Replace the schedule builder's ad-hoc UI with @radix-ui/themes, matching the
invitation-letter document builder pattern.

- Custom div Modal -> Radix Dialog (guard content on data so the close
  animation never dereferences null after the data is cleared)
- Native <input>/<select>/<button> -> TextField / Select / Button
- Raw <h1>/<strong>/<ul><li> markup -> Heading / Text / Card / Badge / DataList
- Pending-items basket emoji scroll controls -> IconButton + lucide chevrons
- Remove now-unused shared/modal.tsx and the .btn utility class
- Drop a stray console.log and an empty else in search-event

CSS-grid calendar layout, react-dnd drag-and-drop, GraphQL, and the speaker
availability badges/tooltips are unchanged.

Spec: backend/custom_admin/SPEC-schedule-builder-radix.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pycon Error Error Jun 2, 2026 2:05pm

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 2, 2026

This PR migrates the Schedule Builder UI in custom_admin from hand-rolled HTML/Tailwind and a custom Modal component to @radix-ui/themes primitives (Dialog, TextField, Select, Button, Card, Badge, etc.), following the pattern used by the Invitation Letter Document Builder. The CSS grid, react-dnd, and GraphQL layers are unchanged.


Error Handling

Silent discard of backend error messages — all three error states show a generic "Could not add to the schedule. Please try again." string. Apollo's errors array contains message fields from the backend (e.g. "slot already occupied", "speaker conflict"). These are discarded. Worth surfacing at minimum errors[0].message.

Error state not cleared on tab switch — in add-custom-event.tsx, the error state lives in the component wrapping both "defined options" and the manual form. An error from clicking a predefined option persists visually when the user switches to the manual input form.

Missing error handling in PendingItemsBasketchangeScheduleItemSlot is called without a try/catch or errors check. If the mutation fails, the item disappears from the calendar with no feedback.


Architecture & Design

AVAILABILITY_META mixes concerns — the shared object in utils/availability.ts carries both Radix color tokens ("green" | "blue" | "red") and raw hex bg/text fields needed only by AvailabilityBadge in item.tsx (which can't use Radix Badge inside a Tooltip). The hex values are an exception driving the shape of a shared type. Cleaner to keep Radix tokens in the shared meta and handle hex values locally in item.tsx.

Missing TypeScript annotation — SlotCreation in slot-creation.tsx has ({ dayId }) without a type; dayId is implicitly any.


Testing

No pnpm build or tsc --noEmit was run before submission (noted by the author). Given 10+ components changed and a new shared utility introduced, a type-check pass is needed before merging.


Minor

  • useEffect in search-event.tsx has [debouncedSearch] as its dependency array but omits conferenceId and runSearch — a hooks lint violation that could cause stale closure bugs.
  • Hardcoded strings with emoji (Lunch 🍝, Coffee Break ☕️) are inline in add-custom-event.tsx rather than in a config constant.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.52%. Comparing base (5d8f9c3) to head (e5a34e9).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4664   +/-   ##
=======================================
  Coverage   92.52%   92.52%           
=======================================
  Files         359      359           
  Lines       10800    10800           
  Branches      821      821           
=======================================
  Hits         9993     9993           
  Misses        696      696           
  Partials      111      111           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Delete the committed AI planning spec (no value as repo docs)
- Add Dialog.Description to add-item modal (a11y; silences Radix warning)
- Gate "No events found" on debouncedSearch so it doesn't show pre-search
- Add type="button" to the calendar "Edit day in admin" Button
- Add a return null fallback for unknown event __typename in search results

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Only close the add-item modal on mutation success; surface a red Callout
  on graphql/network errors instead of closing silently (add-custom-event,
  proposal-preview, keynote-preview)
- Consolidate the two divergent AVAILABILITY_BADGE maps into a single
  AVAILABILITY_META source of truth in utils/availability.ts, consumed by
  both the inline tooltip badge and the Radix Badge
- Render the event-type Select with position="popper" to sidestep portal
  stacking against the Dialog overlay

Modal form/search state already resets on reopen: closing sets data to null,
which unmounts the search/custom-event subtree via the existing `{data && ...}`
guard, so each open remounts with fresh useState.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant