Skip to content

feat: compose message form component #132

@kevinrutledge

Description

@kevinrutledge

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

Image

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):

  1. 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
  2. Select a Channel: Two options with icons

    • Text Message (chat bubble icon) - checkbox/toggle
    • Email (envelope icon) - checkbox/toggle
    • Both can be selected simultaneously
  3. 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)
  4. 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")
  5. 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
  6. 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

  • Component created at src/components/messages/compose-message-form.tsx
  • "New Message" heading in Angkor font matching page pattern
  • "To:" section with Select Groups and Select Members dropdowns
  • "All" option in groups dropdown for blast messages
  • "Select a Channel" with Text Message and Email options
  • Both channels can be selected simultaneously
  • SMS text area visible when Text Message selected
  • Character count displays "Character Count X/160" inside the SMS text card
  • Email subject + body fields visible when Email selected
  • SMS Consent warning shows recipient counts when Text Message selected
  • "Send" brown button fires onSend with form data
  • Form validates SMS body length (160 char max)
  • Uses shadcn Select, Textarea, Input
  • Exported as named export
  • Preview rendered on your team page (/team/[yourname]) with hardcoded groups/members
  • Screenshot of the rendered preview posted in the PR
  • npm run build passes

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew functionality or enhancement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions