Next.js app for collecting and routing service feedback for church teams. Deploys to Railway. Auth and teams come from Planning Center Online (PCO); drivers and team leaders are assigned in-app.
- Driver: Elevated role. Can create feedback (goes straight to leader) and review member-submitted feedback (approve → leader, or reject/return).
- Team Leader (per team): Reviews feedback that has passed driver review; accepts and can add comments. Assigned per team in-app.
- Team member: Can submit feedback (tag team); sees accepted feedback for their team. Default view: last 90 days; can load older.
Flow: Member submits → Driver reviews → Team Leader accepts → Members see accepted (90 days default, load older). Driver-created feedback skips the first step.
- Next.js 16 (App Router), TypeScript, Tailwind
- Auth: NextAuth.js with custom PCO OAuth – sign-in only (identifies user; no user token used for API calls).
- PCO API credentials: All PCO API calls use
PCO_API_ID+PCO_API_SECRET(env) as HTTP Basic Auth inlib/pco.ts. OAuth is identity only; visibility from cached data (DB: drivers, leaders, teams, team positions + assignments, feedback). - DB: PostgreSQL (Railway) + Prisma 7 with
@prisma/adapter-pg - API: Single GraphQL endpoint via Yoga + Pothos at
/api/graphql - Frontend GraphQL: Apollo Client + gql.tada typed operations
- Teams: Fetched from PCO Services API. Sync: service types → teams → leaders + members (pg-boss worker job).
- Auth:
lib/auth.ts(PCO provider, JWT/session),app/api/auth/[...nextauth]/route.ts,middleware.ts(protect routes) - GraphQL API:
app/api/graphql/route.ts,lib/graphql/schema.ts,lib/graphql/context.ts - GraphQL client:
lib/graphql/apollo-client.tsx,lib/graphql/apollo-rsc.ts,lib/graphql/operations.ts,lib/graphql/gql.ts - GraphQL typing artifacts:
lib/graphql/generated/introspection.json,lib/graphql/generated/introspection.ts,lib/graphql/generated/schema.graphql, generated byscripts/generate-graphql-introspection.ts - DB:
lib/db.ts,prisma/schema.prisma - PCO:
lib/pco.ts(fetchServiceTypes, fetchTeams, fetchLeaders, fetchTeamMemberPersonIds, fetchPerson; API key as Bearer) - Jobs:
lib/pg-boss.ts(getBoss),lib/jobs/(queues + handlers; worker runsnpm run worker) - Pages:
app/login,app/dashboard,app/feedback/new,app/driver,app/driver/new,app/driver/feedback/[id],app/leader,app/leader/feedback/[id],app/my-feedback. Team routes:teams/[teamId](overview),teams/[teamId]/feedback,teams/[teamId]/members; role/position underteam/[teamId]/[positionId](description) andteam/[teamId]/[positionId]/goals.
See .env.example: DATABASE_URL, NEXTAUTH_*, PCO_CLIENT_ID, PCO_CLIENT_SECRET, PCO_API_ID, PCO_API_SECRET (required for PCO API; HTTP Basic Auth; OAuth not used for API). Jobs: PCO sync (all service types → teams → leaders + members) is a pg-boss job; Railway worker (npm run worker) runs it every 15 min.
Useful commands:
npm run graphql:generate(refresh gql.tada introspection/types after schema changes)npm run build(runsprisma generatethen Next build)
- App/API reads should flow through GraphQL operations/resolvers; direct Prisma usage is limited to GraphQL server internals and background jobs.
- Use NextAuth for auth (identity only). PCO API:
PCO_API_ID+PCO_API_SECRET(Basic Auth); visibility from cached DB data. - Drivers/team leaders are in
Driver/Leadertables; Person is PCO identity only (no password). - Feedback status:
pending_driver_review|pending_leader_review|accepted. Source:driver|member.