What needs to be built?
A form component for composing and sending messages to groups or all members via email and/or SMS. The form has recipient selection (groups and members dropdowns), channel selection (Text Message and/or Email), conditional body fields based on selected channels, an SMS consent warning showing how many recipients can receive SMS, and a Send button. This component renders as page content, not a modal. Fonts and layout should match existing page patterns (Angkor headings, consistent spacing).
Design reference
This was designed as a modal but will be rendered as a page at /messages/compose. Update the heading font and layout to match existing page patterns.
Figma link: https://www.figma.com/design/O8reTcdtMARvnTzCtlsvmp/Paso-Food-Co-Op?node-id=407-4433&p=f&t=wZjCheHOOyiYBbH4-0
Documentation
Existing patterns:
Official docs:
Technical notes
Create the component at src/components/messages/compose-message-form.tsx.
Form sections (top to bottom):
-
To: Two dropdowns side by side
- "Select Groups" dropdown: "All" option + list of group names
- "Select Members" dropdown: "All" option + individual members (for blast)
- Selecting "All" in groups triggers a blast message
-
Select a Channel: Two options with icons
- Text Message (chat bubble icon) - checkbox/toggle
- Email (envelope icon) - checkbox/toggle
- Both can be selected simultaneously
-
Send Text section (visible when Text Message selected):
- "Send Text" bold section header above the card
- Sender's circular avatar (photo when available, initials fallback) on the left of the card
- Text area inside the card with placeholder "Write your text here"
- "Character Count X/160" label displayed at the bottom inside the card
- SMS body limited to 160 characters (validated by
BaseMessageSchema)
-
Compose Email section (visible when Email selected):
- "Compose Email" bold section header above the card
- Card with "Subject Line" bold header and email body textarea below (placeholder "Write your email here")
-
SMS Consent (visible when Text Message selected):
- Warning icon + "X of Y recipients can receive SMS"
- "Z will NOT receive this message" in bold
- Uses
previewRecipientCounts() to calculate
-
Send button (brown) at bottom
interface ComposeMessageFormProps {
groups: Array<{ id: number; name: string }>;
members: Array<{ memberId: number; ownername: string }>;
onSend: (data: {
groupId: number | null;
isBlast: boolean;
subject: string;
body: string;
smsBody?: string;
sendEmail: boolean;
sendSms: boolean;
}) => void;
isSending?: boolean;
}
The parent page passes groups and members lists. onSend fires when the form is submitted. The parent handles calling the appropriate action (sendGroupMessage or sendBlastMessage) and showing the success state.
Acceptance criteria
What needs to be built?
A form component for composing and sending messages to groups or all members via email and/or SMS. The form has recipient selection (groups and members dropdowns), channel selection (Text Message and/or Email), conditional body fields based on selected channels, an SMS consent warning showing how many recipients can receive SMS, and a Send button. This component renders as page content, not a modal. Fonts and layout should match existing page patterns (Angkor headings, consistent spacing).
Design reference
This was designed as a modal but will be rendered as a page at
/messages/compose. Update the heading font and layout to match existing page patterns.Figma link: https://www.figma.com/design/O8reTcdtMARvnTzCtlsvmp/Paso-Food-Co-Op?node-id=407-4433&p=f&t=wZjCheHOOyiYBbH4-0
Documentation
Existing patterns:
src/actions/contact-group.ts-sendGroupMessageAction(),sendBlastMessageAction(),fetchRecipientPreview()src/schema/contact-group.ts-BaseMessageSchemawith SMS 160-char validationsrc/services/message.ts-previewRecipientCounts()for SMS consent statssrc/components/ui/select.tsx- shadcn Select for dropdownssrc/components/ui/textarea.tsx- shadcn TextareaOfficial docs:
Technical notes
Create the component at
src/components/messages/compose-message-form.tsx.Form sections (top to bottom):
To: Two dropdowns side by side
Select a Channel: Two options with icons
Send Text section (visible when Text Message selected):
BaseMessageSchema)Compose Email section (visible when Email selected):
SMS Consent (visible when Text Message selected):
previewRecipientCounts()to calculateSend button (brown) at bottom
The parent page passes groups and members lists.
onSendfires when the form is submitted. The parent handles calling the appropriate action (sendGroupMessage or sendBlastMessage) and showing the success state.Acceptance criteria
src/components/messages/compose-message-form.tsxonSendwith form data/team/[yourname]) with hardcoded groups/membersnpm run buildpasses