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
- As an event creator / org admin, create a volunteer group on an event.
- Invite a member (
createEventVolunteer / equivalent invite mutation). Server stores a row in event_volunteer_memberships with status: "invited" (or "pending"-flavored), eventVolunteers.hasAccepted: false.
- The Manage Group screen optimistically shows that volunteer with status Pending (rendered from the mutation response held in client state).
- Navigate back to the volunteer-groups list and back into the same group.
- 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).
Summary
EventVolunteerGroup.volunteersonly returns volunteers whose membershipstatus === "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
createEventVolunteer/ equivalent invite mutation). Server stores a row inevent_volunteer_membershipswithstatus: "invited"(or"pending"-flavored),eventVolunteers.hasAccepted: false.EventVolunteerGroup.volunteersreturns[]. The just-invited volunteer has vanished from the UI.The data is still in the database (the
event_volunteer_membershipsrow withstatus != "accepted"exists); the schema simply doesn't surface it.Where the gap lives
src/graphql/types/EventVolunteerGroup/volunteers.ts:38The status enum (
src/drizzle/tables/eventVolunteerMemberships.ts:21) defines four states, but only"accepted"is reachable through the schema:There is no field on
EventVolunteerGroup(or anywhere else, as far as I can see) that exposes membership status to the client.EventVolunteer.hasAcceptedexists 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
volunteersfield with an optional filterwith
VolunteerMembershipStatus = INVITED | REQUESTED | ACCEPTED | REJECTEDandstatusdefaulting toACCEPTED(preserving today's behavior for existing callers). Each returnedEventVolunteerwould carry the membership row's status, e.g. via a new derived field:Option B — surface the membership table directly
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:getEventVolunteerGroups(where: {eventId})returns the group withvolunteers: []despite the membership rows existing in DB.Acceptance criteria
volunteersfield don't break (default behavior preserved, or an explicit migration path for callers).