Scope
Opportunity Profile → Opportunity Details section
(src/components/Dashboard/Profile/sections/OpportunityDetails/)
Context
Today the section:
- Maps Main communication →
profileLanguage with purpose: "general" (OpportunityDetailsDisplay.tsx:25, OpportunityDetailsEdit.tsx:91/106)
- Maps Residents speak →
profileLanguage with purpose: "recipient" (OpportunityDetailsDisplay.tsx:26, OpportunityDetailsEdit.tsx:92/107)
- Treats several fields as mandatory via
opportunityDetailsSchema.ts (description, number of volunteers, both language fields, activities), and the Save button is disabled={!isDirty || !isValid} (OpportunityDetailsEdit.tsx:327).
LangPurpose (need4deed-sdk) = general | translation | recipient.
Required behavior
Field → data mapping
- Main communication — show
profileLanguage entries with purpose: "general". A language must not be displayed more than once (dedupe).
- Residents speak — show
profileLanguage entries with purpose: "recipient" or purpose: "translation". A language must not be displayed more than once — dedupe across the two purposes (since a language can be tagged with both).
- All other fields are the same (description, schedule/event, number of volunteers, activities, skills).
Edit behavior
- Remove all mandatory rules — every field is optional (description, number of volunteers, Main communication, Residents speak, activities, etc.). An empty/blank field must not block saving.
- The only thing that blocks submitting is a real validation failure (e.g. description over
MAX_DESCRIPTION_LENGTH, malformed value) — not "required/empty" checks.
Files likely touched
OpportunityDetailsDisplay.tsx — Residents speak aggregates recipient + translation with dedupe; dedupe Main communication too (lines 25–26).
formatters.ts — formatLanguagesByPurpose takes a single purpose; extend to accept multiple purposes + dedupe (or add a helper). languagesToFormValues likewise needs to seed Residents speak from both purposes.
OpportunityDetailsEdit.tsx — split defaultValues so Residents speak = recipient + translation (deduped); adjust submit payload; relax the Save gating (currently disabled={!isDirty || !isValid}).
opportunityDetailsSchema.ts — drop the required checks: description.min(1), numberOfVolunteers refine, languagesValidator "at least one language", activities.min(1). Keep only format/length validations (e.g. description.max(MAX_DESCRIPTION_LENGTH)).
Open questions
- Edit round-trip for the merged field. When a user edits Residents speak (now
recipient + translation), what purpose do new/edited entries save as? Do existing translation-purpose languages get preserved on PATCH, or is the aggregation display-only? Current submit sends languagesMain / languagesResidents — confirm the payload shape for the merged field.
- Dedupe key — by language
id (recommended) or by title?
!isDirty guard. Spec says "the only block for submitting should be validation fail." Confirm whether the !isDirty part of the Save gating should also be dropped (allow saving an unchanged form) or kept.
Scope
Opportunity Profile → Opportunity Details section
(
src/components/Dashboard/Profile/sections/OpportunityDetails/)Context
Today the section:
profileLanguagewithpurpose: "general"(OpportunityDetailsDisplay.tsx:25,OpportunityDetailsEdit.tsx:91/106)profileLanguagewithpurpose: "recipient"(OpportunityDetailsDisplay.tsx:26,OpportunityDetailsEdit.tsx:92/107)opportunityDetailsSchema.ts(description, number of volunteers, both language fields, activities), and the Save button isdisabled={!isDirty || !isValid}(OpportunityDetailsEdit.tsx:327).LangPurpose(need4deed-sdk) =general | translation | recipient.Required behavior
Field → data mapping
profileLanguageentries withpurpose: "general". A language must not be displayed more than once (dedupe).profileLanguageentries withpurpose: "recipient"orpurpose: "translation". A language must not be displayed more than once — dedupe across the two purposes (since a language can be tagged with both).Edit behavior
MAX_DESCRIPTION_LENGTH, malformed value) — not "required/empty" checks.Files likely touched
OpportunityDetailsDisplay.tsx— Residents speak aggregatesrecipient+translationwith dedupe; dedupe Main communication too (lines 25–26).formatters.ts—formatLanguagesByPurposetakes a single purpose; extend to accept multiple purposes + dedupe (or add a helper).languagesToFormValueslikewise needs to seed Residents speak from both purposes.OpportunityDetailsEdit.tsx— splitdefaultValuesso Residents speak = recipient + translation (deduped); adjust submit payload; relax the Save gating (currentlydisabled={!isDirty || !isValid}).opportunityDetailsSchema.ts— drop the required checks:description.min(1),numberOfVolunteersrefine,languagesValidator"at least one language",activities.min(1). Keep only format/length validations (e.g.description.max(MAX_DESCRIPTION_LENGTH)).Open questions
recipient+translation), whatpurposedo new/edited entries save as? Do existingtranslation-purpose languages get preserved on PATCH, or is the aggregation display-only? Current submit sendslanguagesMain/languagesResidents— confirm the payload shape for the merged field.id(recommended) or by title?!isDirtyguard. Spec says "the only block for submitting should be validation fail." Confirm whether the!isDirtypart of the Save gating should also be dropped (allow saving an unchanged form) or kept.