Event Attendance Management System
CredoPass is a comprehensive attendance tracking platform designed for organizations that meet regularly and need to track who actually shows up. Unlike ticketing systems like EventBrite that manage payments and ticket scanning, CredoPass focuses on detailed attendance tracking—capturing check-in times, check-out times, and actual attendance data that ticketing platforms don't provide.
Perfect for churches, book clubs, jazz clubs, recurring meetups, and any organization that needs to:
- Track attendance without requiring tickets
- Work alongside existing event systems (EventBrite, Meetup, etc.)
- Integrate with existing member databases
- Capture detailed check-in/check-out times
- Generate attendance analytics and insights
- Features
- Technology Stack
- Project Structure
- Quick Start
- Development
- Key Files & Directories
- Documentation
- Project Status
- Attendance Tracking - Detailed check-in/check-out times and attendance records
- Member Management - Import and manage your existing member database
- Event Management - Create and schedule recurring or one-time events
- Integration Ready - Works alongside EventBrite, Meetup, and other event platforms
- No Tickets Required - Perfect for free events or paid events managed elsewhere
- Loyalty Program - Reward frequent attendees with points and tiers
- Analytics Dashboard - Attendance trends, no-show rates, and engagement metrics
- Offline-First - Local data sync for check-ins even without internet
- Responsive Design - Mobile-friendly for on-site check-in tablets or phones
- Framework: React 19.2.1 (with React Compiler optimization)
- Build Tool: Vite 7.3.0 (Rolldown variant)
- Routing: TanStack Router v1.140.5 (file-based routing)
- State Management:
- Zustand v5.0.9 (global state)
- TanStack Query v5.90.12 (server state)
- TanStack DB v0.1.60 (offline-first local collections)
- UI Components: shadcn/ui with Base UI React v1.0.0
- Styling: TailwindCSS v4.1.18
- Data Visualization:
- AG Grid Community v35.0.0 (data tables)
- FullCalendar v6.1.19 (calendar views)
- Recharts 3.6.0 (charts & analytics)
- Icons: Lucide React v0.562.0
- Layout: React Grid Layout v2.0.0
- Runtime: Bun >= 1.3.0
- Framework: Hono v4.10.7 (lightweight web framework)
- Database: PostgreSQL 16 (production) / PGlite (development fallback)
- ORM: Drizzle ORM v0.45.1 with Drizzle Kit v0.31.0
- Validation: Zod v4.3.5 with @hono/zod-validator
- Environment: t3-env v3.10.0
- Monorepo Manager: Nx v22.3.3
- Package Manager: Bun (with workspaces)
- Linting: ESLint v9.39.2 with TypeScript ESLint
- TypeScript: v5.9.3
- Frontend: Vercel (with API proxy rewrites)
- Backend: Google Cloud Run (Docker containers)
- Database: PostgreSQL 16 (Docker Compose for local development)
credopass-monorepo/
├── apps/
│ └── web/ # React web application (Vercel)
│ ├── src/
│ │ ├── main.tsx # App entry point (TanStack Router)
│ │ ├── routes.tsx # Route tree configuration
│ │ ├── config.ts # App configuration
│ │ ├── components/ # Reusable React components
│ │ ├── containers/ # Feature containers (EventForm, TopNavBar, etc.)
│ │ ├── Pages/ # Page components (Home, Members, Events, Analytics)
│ │ ├── routes/ # Route definitions
│ │ ├── stores/ # Zustand stores (useAppStore, useLauncherStore)
│ │ ├── lib/
│ │ │ ├── utils.ts # Utility functions
│ │ │ └── tanstack-db/ # TanStack DB collections (users, events, etc.)
│ │ └── hooks/ # Custom React hooks
│ ├── public/ # Static assets
│ ├── index.html # HTML template
│ ├── vite.config.ts # Vite configuration (proxy, build)
│ ├── vercel.json # Vercel deployment config
│ ├── tsconfig.json # TypeScript config
│ └── project.json # Nx project configuration
│
├── services/
│ └── core/ # Hono API server (Google Cloud Run)
│ ├── src/
│ │ ├── index.ts # Server entry (Hono + middleware)
│ │ ├── routes/ # API route handlers
│ │ │ ├── users.ts # User CRUD endpoints
│ │ │ ├── events.ts # Event management endpoints
│ │ │ ├── attendance.ts # Attendance tracking endpoints
│ │ │ └── loyalty.ts # Loyalty program endpoints
│ │ ├── api/ # API client (type-safe fetch wrapper)
│ │ │ ├── client.ts # Base API client
│ │ │ └── endpoints/ # Endpoint definitions
│ │ └── db/ # Database layer
│ │ ├── client.ts # DB client (PostgreSQL auto-detect)
│ │ ├── index.ts # DB exports
│ │ └── seed.ts # Database seeding script
│ ├── drizzle/ # Database migrations
│ ├── Dockerfile # Multi-stage Docker build
│ ├── drizzle.config.ts # Drizzle Kit configuration (points to @credopass/lib schemas)
│ ├── tsconfig.json # TypeScript config
│ └── project.json # Nx project configuration
│
├── packages/
│ ├── lib/ # Shared utilities & validation (@credopass/lib)
│ │ ├── src/
│ │ │ ├── schemas/ # Single source of truth for schemas
│ │ │ │ ├── tables/ # Drizzle table definitions
│ │ │ │ │ ├── users.ts # Users table schema
│ │ │ │ │ ├── events.ts # Events table schema
│ │ │ │ │ ├── attendance.ts # Attendance table schema
│ │ │ │ │ ├── loyalty.ts # Loyalty table schema
│ │ │ │ │ └── index.ts # Table exports with relations
│ │ │ │ ├── user.schema.ts # Zod schemas generated from Drizzle (Create, Update, Insert)
│ │ │ │ ├── event.schema.ts # Event validation schemas (drizzle-zod)
│ │ │ │ ├── attendance.schema.ts # Attendance validation schemas
│ │ │ │ ├── loyalty.schema.ts # Loyalty validation schemas
│ │ │ │ ├── enums.ts # Shared Zod enums
│ │ │ │ └── index.ts # Barrel exports (tables + schemas)
│ │ │ ├── hooks/ # Shared React hooks
│ │ │ │ └── use-cookies.ts
│ │ │ ├── util/ # Utility functions
│ │ │ ├── grid-layout.tsx # React Grid Layout wrapper (shared component)
│ │ │ ├── constants.ts # App constants
│ │ │ └── index.ts # Package exports
│ │ ├── tsconfig.json
│ │ └── project.json
│ │
│ └── ui/ # Shared UI components (@credopass/ui)
│ ├── src/
│ │ ├── components/ # shadcn/ui components
│ │ │ ├── button.tsx
│ │ │ ├── card.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── input.tsx
│ │ │ ├── select.tsx
│ │ │ ├── chart.tsx # Recharts integration
│ │ │ ├── sidebar.tsx
│ │ │ └── index.ts # Component exports
│ │ ├── hooks/ # UI-specific hooks
│ │ │ └── use-mobile.ts
│ │ ├── lib/
│ │ │ └── utils.ts # cn() helper, etc.
│ │ └── styles/
│ │ └── globals.css # Global styles
│ ├── components.json # shadcn/ui config
│ ├── tailwind.config.ts # Tailwind configuration
│ ├── tsconfig.json
│ └── project.json
│
├── docker/
│ └── docker-compose.yml # PostgreSQL 16 container setup
│
├── tools/ # DevOps scripts
│ ├── nm-reset.sh # Node modules cleanup
│ ├── setup-gcp.sh # Google Cloud Platform setup
│ └── setup-vercel.sh # Vercel setup
│
├── nx.json # Nx workspace configuration
├── package.json # Root dependencies & scripts
├── tsconfig.base.json # Base TypeScript configuration
├── eslint.config.js # ESLint configuration
├── ARCHITECTURE.md # Legacy architecture docs
├── REFACTORING_SUMMARY.md # Consolidation notes
└── README.md # This file
- Bun >= 1.3.0 (Install Bun)
- Docker (for PostgreSQL)
- Google Cloud SDK (for deployment)
# Clone repository
git clone <repository-url>
cd credopass
# Install dependencies
bun install
# Start PostgreSQL database
bun run postgres:up
# Run database migrations
nx run coreservice:migrate
# Start development servers (in separate terminals)
# Terminal 1: Frontend (http://localhost:5173)
nx run web:serve
# Terminal 2: Backend (http://localhost:3000)
nx run coreservice:startCreate a .env file in the root:
# Database
DATABASE_URL=postgresql://postgres:Ax!rtrysoph123@localhost:5432/credopass_db
# API Configuration
API_BASE_URL=http://localhost:3000
NODE_ENV=development
# Optional: Enable throttle middleware for testing
THROTTLE=falseSee docs/SETUP.md for detailed setup instructions
# Frontend Development
nx run web:serve # Start dev server (localhost:5173)
nx run web:build # Production build
nx run web:preview # Preview production build
# Backend Development
nx run coreservice:start # Start API server (localhost:3000)
nx run coreservice:build # Bundle with Bun
nx run coreservice:docker:build # Build Docker image
# Database
bun run postgres:up # Start PostgreSQL container
bun run postgres:down # Stop and remove PostgreSQL
nx run coreservice:generate # Generate migration from schema changes
nx run coreservice:migrate # Run pending migrations
nx run coreservice:studio # Open Drizzle Studio (DB UI)
# Monorepo
nx graph # View dependency graph
nx affected:test # Test affected projects
nx format:write # Format code- Frontend Changes: Edit files in
apps/web/src/→ Hot reload on save - Backend Changes: Edit files in
services/core/src/→ Auto-restart with--watch - Schema Changes: Edit
packages/lib/src/schemas/tables/→ Runnx run coreservice:generate→ Runnx run coreservice:migrate - UI Components: Edit
packages/ui/src/components/→ Changes reflect in web app - Validation Schemas: Edit
packages/lib/src/schemas/*.schema.ts→ Available in both frontend & backend (auto-generated from Drizzle tables)
Development Mode:
Frontend (localhost:5173) → Vite Proxy → Backend (localhost:3000)
- Configured in
apps/web/vite.config.ts - All
/api/*requests proxied tohttp://localhost:3000
Production Mode:
Frontend (vercel.app) → Vercel Rewrite → https://api.credopass.com/api/*
- Configured in
apps/web/vercel.json - API domain set via environment variables
| File/Directory | Purpose |
|---|---|
| Frontend | |
apps/web/src/main.tsx |
React app entry point with TanStack Router setup |
apps/web/src/routes.tsx |
Explicit route tree configuration |
apps/web/src/stores/store.ts |
Zustand stores (useAppStore, useLauncherStore) |
apps/web/src/lib/tanstack-db/ |
TanStack DB collections for offline-first data |
apps/web/src/Pages/ |
Page components (Home, Members, Events, Analytics, Tables) |
apps/web/src/containers/ |
Feature containers (EventForm, TopNavBar, SignInModal, etc.) |
apps/web/vite.config.ts |
Vite build config with proxy and code-splitting |
apps/web/vercel.json |
Vercel deployment with API rewrites & security headers |
| Backend | |
services/core/src/index.ts |
Hono server with CORS, logger, throttle middleware |
services/core/src/routes/ |
API route handlers (users, events, attendance, loyalty) |
services/core/src/db/client.ts |
Database client factory (PostgreSQL auto-detect) |
services/core/Dockerfile |
Multi-stage Docker build for Cloud Run |
services/core/drizzle.config.ts |
Drizzle migration configuration (points to lib schemas) |
| Shared Packages | |
packages/lib/src/schemas/tables/ |
Drizzle table definitions (single source of truth) |
packages/lib/src/schemas/*.schema.ts |
Zod validation schemas (auto-generated via drizzle-zod) |
packages/lib/src/constants.ts |
Application constants |
packages/ui/src/components/ |
shadcn/ui component library |
packages/ui/components.json |
shadcn/ui configuration |
| Infrastructure | |
docker/docker-compose.yml |
PostgreSQL 16 container definition |
nx.json |
Nx workspace configuration & task pipelines |
tsconfig.base.json |
Base TypeScript config with path mappings |
package.json |
Root workspace dependencies & scripts |
Comprehensive documentation is available in the /docs directory:
- Architecture Guide - Detailed architectural patterns, routing, state management, API patterns, validation layer
- Setup Guide - Complete setup instructions, environment variables, database configuration, troubleshooting
- Database Guide - Schema definitions, relationships, migrations, seeding data
- API Reference - Endpoint documentation, request/response examples
- Deployment Guide - Build processes, Vercel deployment, Docker & Cloud Run, production migrations
Current Phase: Post-Refactoring Development
The Gap: EventBrite and similar platforms handle ticket sales and scanning, but don't provide:
- Detailed attendance data (who actually showed up vs. who bought tickets)
- Check-in and check-out timestamps
- Attendance tracking for free/non-ticketed events
- Integration with your existing member database
The Solution: CredoPass fills this gap by focusing exclusively on attendance tracking. Use EventBrite for ticketing, use CredoPass for knowing who attended and when.
The project recently underwent schema consolidation to eliminate duplication and establish a single source of truth:
✅ Completed:
- Moved Drizzle table definitions from
services/core/src/db/schema/topackages/lib/src/schemas/tables/ - Integrated
drizzle-zodto auto-generate Zod validation schemas from Drizzle tables - Eliminated manual schema duplication (was maintaining 2 separate definitions per entity)
- Updated Zod version from v4.1.13 to v4.3.5 across all packages
- All routes now import schemas directly from
@credopass/lib/schemas - Drizzle config updated to use shared schema location
Benefits:
- No More Drift: Schema changes in one place automatically propagate to validation
- Less Code: Removed ~400 lines of duplicate schema definitions
- Type Safety: Drizzle tables generate both Zod schemas and TypeScript types
- Better DX: Add a field once, get DB schema + validation + types automatically
- Simpler Structure: Fewer packages = easier navigation
- Type Safety: End-to-end TypeScript with Zod validation
- Modern Stack: React 19, Hono, Drizzle, Bun, TanStack ecosystem
- Developer Experience: Fast builds (Bun), hot reload (Vite), excellent tooling (Nx, Drizzle Studio)
- Offline-First: TanStack DB collections for local data persistence
- Clean Separation: Clear boundaries between apps, services, and packages
-
Global UI State (Zustand):
useAppStore: Sidebar state, action eventsuseLauncherStore: Modal launcher state
-
Server State (TanStack Query):
- Automatic caching and invalidation
- Used implicitly by TanStack DB collections
-
Local-First Data (TanStack DB):
- Collections: users, events, attendance, loyalty
- Syncs with backend API via collections
- Enables offline functionality
User Interaction
↓
React Component
↓
TanStack Query/DB → API Client → Hono Server
↓
Zod Validation
↓
Drizzle ORM
↓
PostgreSQL
Architecture:
- Drizzle Tables (
packages/lib/src/schemas/tables/): Database schema definitions (PostgreSQL) - Zod Schemas (
packages/lib/src/schemas/*.schema.ts): Auto-generated viadrizzle-zodwith custom refinements - Both exported from
@credopass/lib/schemasfor use across frontend and backend
Workflow:
- Edit Drizzle table definition in
packages/lib/src/schemas/tables/users.ts - Zod schemas in
packages/lib/src/schemas/user.schema.tsauto-update viadrizzle-zod - Generate migration:
nx run coreservice:generate - Apply migration:
nx run coreservice:migrate - TypeScript types and validation automatically available everywhere
Example:
// packages/lib/src/schemas/tables/users.ts (Source of Truth)
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: text('email').notNull().unique(),
firstName: text('firstName').notNull(),
// ...
});
// packages/lib/src/schemas/user.schema.ts (Auto-generated + Refinements)
import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
import { users } from './tables/users';
export const UserSchema = createSelectSchema(users);
export const CreateUserSchema = createInsertSchema(users, {
email: z.string().email(), // Custom refinement
firstName: z.string().min(1, 'Required'),
}).omit({ id: true, createdAt: true, updatedAt: true });The backend automatically detects the environment:
- Production: Uses PostgreSQL via
DATABASE_URL - Development Fallback: Uses PGlite if PostgreSQL unavailable
# Automatic deployment on push to main branch
# Or manual deployment:
vercel deployConfiguration: apps/web/vercel.json
# Deploy to Cloud Run
nx run coreservice:deploy
# Or manually:
cd services/core
gcloud run deploy credopass-api \
--source . \
--region us-central1 \
--allow-unauthenticatedSee docs/DEPLOYMENT.md for complete deployment instructions
See LICENSE file for details.
This is a private project. For questions or issues, contact the development team.
Built with ❤️ for organizations that value attendance insights