Skip to content

Volunteer groups expose only accepted volunteers — pending/invited/requested memberships are invisible to clients #5359

@noman2002

Description

@noman2002

Summary

EventVolunteerGroup.volunteers only returns volunteers whose membership status === "accepted". There is no GraphQL field that lets a client read pending / invited / requested / rejected memberships, so a freshly-invited volunteer disappears from the UI as soon as the screen reloads — until they personally accept the invite, the inviter has no way to see they were ever invited.

This makes the "invite then accept" workflow effectively single-shot: a client can show the new invite immediately after the mutation (because the mutation response surfaces it), but can never re-derive that state from the server afterward.

Reproduction

  1. As an event creator / org admin, create a volunteer group on an event.
  2. Invite a member (createEventVolunteer / equivalent invite mutation). Server stores a row in event_volunteer_memberships with status: "invited" (or "pending"-flavored), eventVolunteers.hasAccepted: false.
  3. The Manage Group screen optimistically shows that volunteer with status Pending (rendered from the mutation response held in client state).
  4. Navigate back to the volunteer-groups list and back into the same group.
  5. The client now refetches: EventVolunteerGroup.volunteers returns []. The just-invited volunteer has vanished from the UI.

The data is still in the database (the event_volunteer_memberships row with status != "accepted" exists); the schema simply doesn't surface it.

Where the gap lives

src/graphql/types/EventVolunteerGroup/volunteers.ts:38

.where(
  and(
    eq(eventVolunteerMembershipsTable.groupId, parent.id),
    eq(eventVolunteerMembershipsTable.status, "accepted"), // Only accepted volunteers
    eq(eventVolunteersTable.hasAccepted, true),
  ),
)

The status enum (src/drizzle/tables/eventVolunteerMemberships.ts:21) defines four states, but only "accepted" is reachable through the schema:

export const volunteerMembershipStatusEnum = [
  "invited",
  "requested",
  "accepted",
  "rejected",
] as const;

There is no field on EventVolunteerGroup (or anywhere else, as far as I can see) that exposes membership status to the client. EventVolunteer.hasAccepted exists on the volunteer record, but it doesn't carry the per-group membership status, and there is no top-level/embedded query for memberships filtered by group.

Suggested resolution

Expose the per-group membership status. Two reasonable shapes:

Option A — extend the existing volunteers field with an optional filter

extend type EventVolunteerGroup {
  volunteers(status: VolunteerMembershipStatus): [EventVolunteer!]
}

with VolunteerMembershipStatus = INVITED | REQUESTED | ACCEPTED | REJECTED and status defaulting to ACCEPTED (preserving today's behavior for existing callers). Each returned EventVolunteer would carry the membership row's status, e.g. via a new derived field:

extend type EventVolunteer {
  membershipStatus(groupId: ID!): VolunteerMembershipStatus
}

Option B — surface the membership table directly

type EventVolunteerMembership {
  id: ID!
  status: VolunteerMembershipStatus!
  volunteer: EventVolunteer!
  group: EventVolunteerGroup!
  invitedAt: DateTime
  respondedAt: DateTime
}

extend type EventVolunteerGroup {
  memberships(status: VolunteerMembershipStatus): [EventVolunteerMembership!]
}

Option B is closer to the underlying data model, makes per-status pagination/filtering straightforward, and lets clients render mixed-status lists (Pending / Accepted / Rejected) without N round-trips.

Whichever shape is picked, the same authorization check that's already on volunteers.ts (require authenticated; only org admin / event creator can read) should apply to the new field.

Surfaced from

Talawa mobile app, Manage Volunteer Group screen.

Repro on api-test.talawa.io:

  • Right after invite: client locally shows admin + Wilt Shepherd as Pending.
  • After leaving + re-entering the group: list shows "No volunteers yet". getEventVolunteerGroups(where: {eventId}) returns the group with volunteers: [] despite the membership rows existing in DB.

Acceptance criteria

  • A non-empty client-readable response after re-entry that includes both pending and accepted invites for a given group.
  • Membership status is queryable per volunteer per group (no client-side guessing or local Hive caching needed).
  • Existing callers that rely on the current "accepted-only" volunteers field don't break (default behavior preserved, or an explicit migration path for callers).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions