fix(offerings): open the editor for consultation and subscription plans - #1088
Conversation
Editing either type always 404'd. The edit page built its candidate list from the planner query, but that payload carries only webinars, classes and participant counts — consultationPlans/subscriptionPlans were never on it, so notFound() fired unconditionally for those two. Each type now reads the source that actually owns it, and only that one runs. Also in the authoring path: - The planner's Edit control no longer navigates with a missing id. Both spellings produced an unreachable URL (`id ?? ""` collapsed to a double slash, a bare id stringified "undefined"), and the card now disables Edit and Delete for a row that has no id to address. - Breadcrumbs stop linking segments that own no page. `offerings` and `participants` only have dynamic children, and a dynamic param's VALUE is never a URL of its own, so Next was prefetching routes that 404. - ClassPlanSchema's start date is renamed to the field the manifest authors (`schedulingStartDate`); it was `startDate` here, so the resolver stripped the value and no class ever sent one. The adapter now maps it to the API's ISO `startDate` and hydrates it back from the Class row's schedulingPeriodStartsAt. - SubscriptionPlanSchema gains `subscriptionContents`. Absent, the resolver stripped the roadmap and every save posted an empty list, which the PUT treats as "replace with nothing". - The consultation and subscription list reads now include `faqs`. The editor hydrates from them and PUTs the whole array back, so omitting them meant saving an empty set over a plan's FAQ. Co-authored-by: Cursor <cursoragent@cursor.com>
The billing page is a server component that imported workspaceBillingQueryKey from useWorkspaceBilling.ts to SSR-prefetch the roll-up. That module is "use client", so every export of it is a client reference and calling one from the server threw "Attempted to call workspaceBillingQueryKey() from the server but workspaceBillingQueryKey is on the client" — taking the live page down. The key moves to a module with no directive, callable from either side. The operator home shell had the identical import and the same latent crash, so it moves too. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
✅ Deploy Preview for familiarise ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
|
Warning Review limit reached
Next review available in: 12 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (18)
📝 WalkthroughWalkthroughThe PR updates offering schemas and retrieval, fixes type-specific offering editor loading, normalizes class scheduling dates, safeguards planner actions, makes consultant breadcrumbs route-aware, and centralizes workspace billing query keys. ChangesOffering workflows
Consultant dashboard breadcrumbs
Workspace billing query keys
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@app/dashboard/consultant/`[consultantId]/(features)/offerings/[type]/[offeringId]/edit/page.tsx:
- Around line 50-66: Replace the paginated plan-list lookup in the edit page
with an authenticated type-and-ID query for the requested offering, using the
existing planner/consultation/subscription query symbols as appropriate. Keep
notFound() reserved for a successful lookup returning no resource, and render or
propagate query failures through the page’s error handling instead of treating
them as 404s.
In `@components/offerings/editor/adapters.ts`:
- Around line 45-53: Update the PATCH payload flow involving toIsoDate and
ClassService.buildRequestBody to distinguish an omitted start date from an
explicitly cleared date. Preserve an omission sentinel for unchanged fields,
while serializing a cleared editor value as the nullable/empty date
representation consumed by crud-with-plan/route.ts. Ensure the route updates
schedulingPeriodStartsAt for explicit clears but leaves it unchanged when the
field was omitted.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 8f5eb5fa-91b7-4523-8510-da8295206a38
📒 Files selected for processing (15)
app/api/bookings/classes/crud-with-plan/route.tsapp/api/plans/consultations/route.tsapp/api/plans/subscriptions/route.tsapp/dashboard/consultant/[consultantId]/(features)/offerings/[type]/[offeringId]/edit/page.tsxapp/dashboard/consultant/[consultantId]/layout.tsxapp/dashboard/org-workspace/[orgWorkspaceId]/billing/page.tsxapp/dashboard/org-workspace/[orgWorkspaceId]/home/page.tsxapp/dashboard/org-workspace/[orgWorkspaceId]/hooks/useWorkspaceBilling.tsapp/dashboard/org-workspace/[orgWorkspaceId]/workspace-billing-keys.tscomponents/offerings/editor/adapters.tscomponents/planner/components/EventCard.tsxcomponents/planner/components/EventCarousel.tsxcomponents/planner/components/EventManagementDashboard.tsxcomponents/planner/hooks/usePlanner.tsschemas/plans.ts
The dashboard shell let tall pages grow the document, so window-scroll carried the context bar and sidebar off-screen with the form. Clip the shell to the viewport and scroll only <main>. Pin the offering title + section tabs (Basics / Pricing / …) under that bar with sticky positioning and scroll-margin on each section so the tabs stay clickable and land the right block. Offerings breadcrumbs now link to the Event Planner listings instead of a pathless URL. Co-authored-by: Cursor <cursoragent@cursor.com>
…t date
The right-panel overflow-hidden created a second scrollport so Basics /
Pricing / FAQ never stuck under the context bar. Clip only the shell;
sticky chrome stays on <main>.
Edit fetches /api/plans/{type}/{id} instead of a paginated marketplace
list, so ORG_ONLY and off-page plans no longer 404. Cleared class start
dates serialize as null so PATCH can null schedulingPeriodStartsAt.
Part of #1088.
Co-authored-by: Cursor <cursoragent@cursor.com>
…croll Drop the 500px calendar cap and size Timings/Allocate/Reschedule to the dashboard content column so the week grid owns the only scrollport. Co-authored-by: Cursor <cursoragent@cursor.com>
|




Clicking Edit on a consultation or subscription plan in the Event Planner always 404'd. Webinars and classes worked.
Root cause
The edit page sourced its candidate rows from the planner query for all four offering types:
But
/api/dashboard/consultant/[consultantId]/plannerreturns only{ webinars, classes, participantCounts }.consultationPlansandsubscriptionPlansare never on that payload, so for those two types the row was never found andnotFound()fired every time. The doc comment on the page asserted the planner "already loads every offering this consultant owns", which is what made the mistake durable — it has been corrected.An earlier theory blamed an empty id collapsing a double slash in the URL. That was wrong: the URLs are well-formed with valid ids, confirmed in the browser.
Fix
Consultation and subscription rows now come from the same
/api/plans/*endpoints the planner list itself uses, viauseConsultationPlans/useSubscriptionPlans. Only the query for the requested type runs; the other two stay disabled.Two details that matter:
{ type, id, consultationPlan }), because that is what the editor's adapters read.notFound()is unreachable while any enabled query is in flight, so the editor no longer 404s on a row that was about to arrive.Also fixed
A silent data-loss bug found on the way.
GET /api/plans/consultationsand/api/plans/subscriptionsdid not include FAQ/content relations. The editor hydrates from that list and PUTs the whole array back, so opening a plan and saving it wrote an empty set over the existing FAQs. Both list endpoints now include the content relations.Breadcrumb prefetch 404s. The consultant layout linked every non-id path segment, including
.../offeringsand.../offerings/subscription, neither of which has a route. Next prefetched them and the console filled with?_rsc=404s. Segments with no route now render unlinked.Fields that did not round-trip.
imageUrlwas in the manifests and on all four Prisma models but missing from the Zod schemas and every save path, so a chosen image was silently dropped. The class start-date field was namedschedulingStartDatein the manifest andstartDatein the schema, and the adapter never passed it on save.subscriptionContentswas bound in the editor but absent fromSubscriptionPlanSchema. No schema changes were needed for any of these.Org billing page server crash (separate commit):
Attempted to call workspaceBillingQueryKey() from the server but workspaceBillingQueryKey is on the client. The query key now lives in its own module on the correct side of the boundary.Test plan
tsc --noEmitclean from a cold.tsbuildinfoeslintclean on all changed files, zero warnings?_rsc=404s on an offerings URLMade with Cursor
Summary by CodeRabbit
New Features
Bug Fixes