A web application for organizing recurring board game nights. Members can RSVP to events, manage their game collections, propose games with interest-based joining, and coordinate multi-session campaigns with specific players.
Admins define a recurring schedule (e.g. every Friday from 20:45 to 03:00) that automatically generates game night events. They can also create ad-hoc game nights for special occasions. Each night has an optional location and attendee cap.
Any authenticated member can RSVP to upcoming game nights. The attendee list is visible to everyone so players know who's coming before proposing games.
Each member manages a personal game library. Games can be added manually (name, player count, duration, complexity) or imported from BoardGameGeek via search. BGG imports pull metadata including image, player count, playing time, and weight rating.
Once RSVP'd to a night, a member can propose a game from their library. Other attendees join proposals with an interest level (High, Medium, or Low). When a proposal is full:
- A new candidate can bump an existing participant if their interest is strictly higher.
- The bumping algorithm uses a hybrid priority system: it first sorts candidates by lowest interest, then by most recent join time, ensuring fair rotation.
A single person can join multiple proposals on the same night, expressing their preference order through interest levels.
A campaign ties a specific game to a specific group of players — ideal for multi-session or legacy games (e.g. a Gloomhaven campaign). The system automatically checks upcoming game nights and flags when all campaign members have RSVP'd to the same event. From there, the creator can instantly convert the campaign into a game proposal with one click.
Two roles exist: Admin and Member.
- Admins can: create/edit/delete game nights, manage recurring schedules, create ad-hoc events, invite other admins, change user roles, and remove game proposals.
- Members can: RSVP to nights, manage their game library, propose games, join proposals, and create campaigns.
The system uses an invitation-based model — the first user is an admin who then invites others.
Social login via Microsoft, Google, and Discord (custom OpenID Connect), powered by MSAL and Azure AD B2C.
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Vite, Tailwind CSS |
| Backend | Azure Functions v4 (TypeScript, new programming model) |
| Database | Azure Cosmos DB (NoSQL) |
| Auth | MSAL + Azure AD B2C (Microsoft, Google, Discord) |
| Hosting | Azure Static Web Apps |
| Infrastructure | Bicep (IaC) |
| Game Data | BoardGameGeek XML API v2 |
| Icons | Lucide React |
bg-night-planner/
├── src/ # React frontend
│ ├── auth/ # MSAL configuration
│ │ └── msalInstance.ts
│ ├── components/ # Shared UI components
│ │ └── Navigation.tsx
│ ├── hooks/ # Custom React hooks
│ │ └── useAuth.ts # Auth state + RBAC helper
│ ├── pages/ # Page components
│ │ ├── Dashboard.tsx # Stats, upcoming nights, quick actions
│ │ ├── GameLibrary.tsx # Game collection + BGG search
│ │ ├── GameNights.tsx # RSVP, proposals, interest joining
│ │ ├── Campaigns.tsx # Campaign management + availability
│ │ └── AdminPanel.tsx # Schedules, users, ad-hoc events
│ ├── services/ # API client layer
│ │ ├── apiClient.ts # Axios instance with MSAL token
│ │ ├── userService.ts
│ │ ├── gameService.ts
│ │ ├── gameNightService.ts
│ │ ├── gameProposalService.ts
│ │ └── campaignService.ts
│ ├── types/ # TypeScript interfaces & enums
│ │ └── index.ts
│ ├── App.tsx # Router + MSAL wrapper
│ └── main.tsx # Entry point
├── api/ # Azure Functions backend
│ ├── shared/ # Shared backend utilities
│ │ ├── cosmosClient.ts # Cosmos DB client + container init
│ │ └── auth.ts # Request auth helpers
│ ├── users/index.ts # User CRUD, admin invite, role mgmt
│ ├── games/index.ts # Game CRUD + BGG integration
│ ├── gameNights/index.ts # Game night CRUD, RSVP, schedules
│ ├── gameProposals/index.ts # Proposals, participation, bumping
│ ├── campaigns/index.ts # Campaign CRUD, availability, game creation
│ └── health/index.ts # Health check endpoint
├── infra/
│ ├── main.bicep # Main Bicep orchestration (subscription scope)
│ ├── abbreviations.json # Azure resource name prefix abbreviations
│ └── core/ # Reusable Bicep modules
│ ├── host/
│ │ ├── staticwebapp.bicep # Azure Static Web App
│ │ ├── functions.bicep # Azure Functions app
│ │ └── appserviceplan.bicep # App Service Plan (Consumption)
│ ├── database/cosmos/sql/
│ │ └── cosmos-sql-db.bicep # Cosmos DB account + database + containers
│ ├── storage/
│ │ └── storage-account.bicep # Storage account
│ ├── security/
│ │ ├── keyvault.bicep # Key Vault with RBAC
│ │ └── keyvault-access.bicep # Key Vault role assignment
│ ├── monitor/
│ │ └── monitoring.bicep # Log Analytics + App Insights
│ └── communication/
│ └── communication-services.bicep # Azure Communication Services
├── staticwebapp.config.json # SWA routing + auth providers
├── azure.yaml # Azure Developer CLI config
└── .env.example # Required environment variables
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /users/me?email=… |
Member | Get current user profile |
| POST | /users |
Member | Register new user |
| POST | /users/invite-admin |
Admin | Invite a new admin |
| PUT | /users/role |
Admin | Change a user's role |
| GET | /users |
Admin | List all users |
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /games/my-games |
Member | List user's game library |
| POST | /games |
Member | Add game manually |
| GET | /games/bgg-search?q=… |
Member | Search BoardGameGeek |
| POST | /games/from-bgg |
Member | Import game from BGG by ID |
| PUT | /games/{id} |
Member | Update a game |
| DELETE | /games/{id} |
Member | Remove game from library |
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /game-nights |
Member | List upcoming game nights |
| GET | /game-nights/{id} |
Member | Get single game night |
| POST | /game-nights |
Admin | Create a game night |
| PUT | /game-nights/{id} |
Admin | Update a game night |
| DELETE | /game-nights/{id} |
Admin | Delete a game night |
| POST | /game-nights/{id}/rsvp |
Member | RSVP to a game night |
| DELETE | /game-nights/{id}/rsvp |
Member | Cancel RSVP |
| GET | /recurring-schedules |
Admin | List recurring schedules |
| POST | /recurring-schedules |
Admin | Create recurring schedule |
| PUT | /recurring-schedules/{id} |
Admin | Update recurring schedule |
| DELETE | /recurring-schedules/{id} |
Admin | Delete recurring schedule |
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /game-proposals?gameNightId=… |
Member | List proposals for a night |
| POST | /game-proposals |
Member | Propose a game for a night |
| DELETE | /game-proposals/{id} |
Admin/Owner | Remove a proposal |
| POST | /game-proposals/{id}/participate |
Member | Join with interest level |
| PUT | /game-proposals/{id}/participate |
Member | Update interest level |
| DELETE | /game-proposals/{id}/participate |
Member | Leave a proposal |
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /campaigns |
Member | List user's campaigns |
| GET | /campaigns/{id} |
Member | Get campaign details |
| POST | /campaigns |
Member | Create a campaign |
| PUT | /campaigns/{id} |
Owner | Update a campaign |
| DELETE | /campaigns/{id} |
Owner | Delete a campaign |
| GET | /campaigns/{id}/check-availability |
Member | Check if all members are available |
| POST | /campaigns/{id}/create-game |
Member | Create game proposal from campaign |
Displays live stats (upcoming game nights, game count, active campaigns), quick-action buttons for navigation, and a list of the next 5 game nights with attendee counts.
Grid of owned games with cover images, player counts, duration, and complexity weight. Two ways to add games: a BGG search modal that queries BoardGameGeek and imports full metadata, or a manual add form. Games link back to their BGG page when applicable.
Expandable cards for each upcoming night showing date, location, and attendee count. Each card has an RSVP button. When expanded, shows game proposals where attendees can join with High/Medium/Low interest using color-coded buttons. Admins see a "New Game Night" creation button. RSVP'd members can propose games from their library.
Lists active campaigns with their game, participant count, and real-time availability status. When all campaign members have RSVP'd to the same night, a green "Create Game" button appears. A creation modal lets users pick a game from their library and select participants from the user list.
Tabbed interface with three sections:
- Recurring Schedules — create/delete weekly game night patterns (day, start/end time, location)
- User Management — table of all users with role badges and a dropdown to change roles; admin invite modal
- Ad-hoc Events — form to create one-off game nights
Six Cosmos DB containers with the following partition keys:
| Container | Partition Key | Description |
|---|---|---|
users |
/email |
User profiles and roles |
games |
/ownerId |
Per-user game libraries |
gameNights |
/id |
Scheduled game night events |
gameProposals |
/gameNightId |
Game proposals grouped by night |
campaigns |
/createdById |
Campaign definitions per creator |
campaignSuggestions |
/campaignId |
Auto-generated campaign suggestions |
The infra/main.bicep template provisions:
- Azure Static Web App (frontend hosting + API routing)
- Azure Functions App (backend API) on a Consumption plan
- Azure Cosmos DB account with all 6 containers
- Azure Communication Services (for email notifications)
- Azure Key Vault (secrets management)
- Azure Storage Account (Functions runtime)
- Azure Application Insights + Log Analytics (monitoring)
The app supports three social login providers. All are configured in staticwebapp.config.json and use Azure Static Web Apps built-in auth.
- Go to Azure Portal > Microsoft Entra ID > App registrations > New registration
- Set:
- Name:
BG Night Planner - Supported account types: Accounts in any organizational directory and personal Microsoft accounts
- Redirect URI: select Single-page application (SPA) and enter
http://localhost:5173(add your production URL later)
- Name:
- From the app's Overview, copy the Application (client) ID
- Go to Certificates & secrets > New client secret, copy the secret Value
- Go to API permissions > Add a permission > Microsoft Graph > Delegated: add
openid,profile,email,User.Read, then Grant admin consent - Set environment variables:
VITE_CLIENT_IDandAZURE_CLIENT_ID= the Application (client) IDAZURE_CLIENT_SECRET= the client secret valueVITE_AUTHORITY=https://login.microsoftonline.com/common(or replacecommonwith your tenant ID to restrict access)
- Go to Google Cloud Console > APIs & Services > Credentials > Create Credentials > OAuth client ID
- Select Web application, name it
BG Night Planner - Under Authorized redirect URIs, add:
https://<your-swa>.azurestaticapps.net/.auth/login/google/callback - Copy the Client ID and Client Secret
- In OAuth consent screen, add scopes:
email,profile,openid - Set in Azure SWA Application settings:
GOOGLE_CLIENT_ID= your Google OAuth client IDGOOGLE_CLIENT_SECRET= your Google OAuth client secret
- Go to Discord Developer Portal > New Application
- Name it
BG Night Planner - Go to OAuth2, copy the Client ID and reset/copy the Client Secret
- Under Redirects, add:
https://<your-swa>.azurestaticapps.net/.auth/login/discord/callback - Set in Azure SWA Application settings:
DISCORD_CLIENT_ID= your Discord application client IDDISCORD_CLIENT_SECRET= your Discord client secret
| Provider | Login URL | Callback URL |
|---|---|---|
| Microsoft | /.auth/login/aad |
/.auth/login/aad/callback |
/.auth/login/google |
/.auth/login/google/callback |
|
| Discord | /.auth/login/discord |
/.auth/login/discord/callback |
Unauthenticated requests to /api/* are redirected to /.auth/login/aad by default (configured in staticwebapp.config.json).
- Node.js 18+
- Azure CLI + Azure Developer CLI (
azd) - An Azure subscription
- At least one authentication provider configured (see Authentication Setup)
- Clone the repository
- Copy
.env.exampleto.envand fill in your Azure credentials - Install dependencies:
npm install cd api && npm install
- Start the frontend:
npm run dev
- Start the API (in another terminal):
cd api && npm start
azd upThis provisions all Azure resources via Bicep and deploys both the frontend and API.
See LICENSE.