feat: rework new opportunity page to match profile edit layout#693
Conversation
Replaces the simple card form with the same ProfilePage section-card layout used by the opportunity profile. Title and contact details are presented as SectionCards in edit mode; contact fields are pre-filled from the current agent's representative on load (keepDirtyValues so manual edits are not overwritten). Validation uses zod + react-hook-form with the same phone/email rules as the opportunity contact details schema. Save POSTs to /api/opportunity/ and redirects to the new opportunity's profile page on success. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contact details are removed from the creation form; the BE will derive
them from the agentId + authenticated user once the SDK/BE support is ready.
Form now just takes a title, POSTs { title, agentId } and redirects to the
new opportunity's profile on success.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds:
- Volunteer type toggle (Regular / Accompanying / Event) in the header
card at the top of the creation form, styled to match the profile
header's StatusRowField layout; the selected type controls which
sections appear below.
- Opportunity Details section (description, languages, availability grid
for Regular, event date+time for Events, activities, skills,
number of volunteers) — always shown, always in edit mode.
- Accompanying Details section (address, postcode, date, time, refugee
info, appointment language) — shown only when Accompanying is selected.
- Create flow: POST { title, volunteerType, agentId } then immediately
PATCH the opportunity details and (if applicable) accompanying details
using the returned ID, then redirect to the new opportunity's profile.
- Contact details section will follow once the SDK/BE propagates them
from agentId + authenticated user.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upgrades need4deed-sdk to v0.0.108 which adds OpportunityFormDataWithAgentSubmitter - Replaces the two-step create+PATCH flow with a single POST using the new SDK type; all form data (title, type, details, availability, accompanying) is sent in one request to POST /api/opportunity/ - Adds availabilityToTimeslots helper to convert the AvailabilityGrid format to the [weekday, slotId][] timeslots format the new endpoint expects - Fixes OpportunityTableRow: volunteerNames removed from ApiVolunteerOpportunityGetList in v0.0.108; falls back to showing numberOfVolunteers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upgrade need4deed-sdk to 0.0.109; fix buildCreatePayload for new OpportunityLegacyFormDataProps shape (string[] ids, language field, opportunity_type "accompanying"|"volunteering" mapping) - After new opportunity creation redirect to dashboard home instead of opportunity profile - Agent (NGO) home page now shows only their linked opportunities as cards plus the create-opportunity button; all other sections (newest volunteers, tagged comments, newest opportunities) are coordinator/ admin only Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nadavosa
left a comment
There was a problem hiding this comment.
Review
Overall the approach is solid — three-form aggregation, profile-style layout, role-aware home page, correct SDK upgrade path. A few things worth addressing before merge:
Issues:
-
AccompanyingDetailsEditno-op handlers (NewOpportunity.tsx~line 823):onCancel={() => {}}andonSubmit={() => {}}mean the Cancel and Submit buttons inside that component render and are clickable but do nothing. The agent will click them expecting something to happen. Either suppress those buttons (ifAccompanyingDetailsEditsupports that) or document that submission is driven by the outer Create button only. -
Empty state missing in
AgentOpportunityCards: Whenopportunitiesis an empty array (a freshly registered agent with no opportunities yet), the component renders nothing — no message. Add a simple empty-state string so the home page isn't just a button with a blank area below it. -
category/category_idsent as""(buildCreatePayload): These are required fields inOpportunityFormDataWithAgentSubmitterand we're sending empty strings. If the BE rejects blank values here a creation attempt will silently fail. Worth confirming with BE what the correct sentinel is (null? omit entirely? a real default?).
Minor notes:
keyToLabel/labelToKeymaps are rebuilt on every render in the component body — should beuseMemo(() => ..., [apiLanguages])since they only change whenapiLanguageschanges.opportunity_type: isAccompanying ? "accompanying" : "volunteering"— events map to"volunteering". The SDK type only has two values so this is correct, but it's worth a short comment that the BE distinguishes events from regular viaonetime_date_time.useGetCurrentAgentinAgentOpportunityCardsfires two requests (/me→/agent/{id}) when we only need theagentIdfrom/me. Not a blocker but a minor over-fetch.
…tion form Add hideButtons prop to AccompanyingDetailsEdit; pass it from NewOpportunity so only the outer Create button drives submission. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
PageContainer+SectionCardlayout used by the opportunity profile pageopportunityContactDetailsSchema{ title, contact: { name, phone, email }, agentId }toPOST /api/opportunity/and redirects to the new opportunity's profile on successCloses #445
Test plan
🤖 Generated with Claude Code