felt/docs/felt_phase3_spec.md

36 KiB

Project Felt — Phase 3 Product Specification

Complete Venue Platform

Version: 0.1 Draft Date: 2026-02-28 Depends on: Phase 1 (Tournaments) + Phase 2 (Cash Games)


Table of Contents

  1. Overview
  2. Dealer Management
  3. Player Loyalty System
  4. Private Venues & Memberships
  5. Venue Analytics & Reporting
  6. Public Venue Presence
  7. Data Architecture
  8. API Design
  9. Roadmap

1. Overview

What Phase 3 Adds

Phase 1 manages tournaments. Phase 2 manages cash games. Phase 3 manages everything else — the people, the relationships, and the business intelligence that turn a poker room into a venue that players return to.

Phase 3 has four pillars:

  1. Dealer Management — Scheduling, skills, shifts, rotation
  2. Player Loyalty — Points, tiers, rewards, promotions
  3. Private Venues & Memberships — Access control, member management, invitations
  4. Analytics & Public Presence — Business intelligence, venue profiles, event publishing

Design Principles

  1. Everything connects. Dealer scheduling integrates with tournament and cash table assignments. Loyalty points accrue from tournaments AND cash games. Analytics aggregate across all operations.
  2. Progressive adoption. Venues can enable Phase 3 features individually. A small bar might only want dealer scheduling. A large room wants everything. No feature forces adoption of another.
  3. Player-visible value. Players should see the value of Phase 3 — loyalty rewards, membership benefits, career stats. This drives word-of-mouth to other venues.
  4. Operator simplicity. Every Phase 3 feature must be simpler to use than the spreadsheet/whiteboard it replaces. If it's not, we've failed.

2. Dealer Management

2.1 Dealer Profiles

Each dealer is a user in the system (they may also be a player — dual roles supported). Their profile contains:

Identity: Name, contact info, photo, employee ID (venue-assigned)

Skill Tags:

Category Tags (examples)
Game variants Hold'em, Omaha, PLO Hi-Lo, Stud, Razz, Draw, Short Deck, Mixed
Betting structures No-Limit, Pot-Limit, Fixed-Limit
Format specialization Cash games, Tournaments, Both
Experience level Trainee, Standard, Experienced, Senior
Special skills High-stakes tables, Final table dealing, Celebrity/VIP events
Languages Danish, English, German, etc.

Skills are tagged by the venue manager. A dealer can request skill additions (e.g., after training on a new game), subject to manager approval.

Availability Windows: Dealers set weekly recurring availability (e.g., "Available Mon-Fri 4PM-12AM, Sat 12PM-12AM, not available Sunday"). They can override specific dates (e.g., "Unavailable Dec 24-26"). The scheduling system respects these.

2.2 Schedule Management

Shift Templates: Venues define recurring shift templates:

Morning:    10:00 — 18:00
Afternoon:  14:00 — 22:00
Evening:    18:00 — 02:00
Tournament: Based on tournament schedule (variable)
Split:      10:00 — 14:00, 18:00 — 22:00

Templates are building blocks. The actual schedule is assembled from templates assigned to specific dealers on specific dates.

Schedule Builder (Operator UI):

Week view with dealers on the Y-axis and days on the X-axis. Drag-and-drop shift assignment. Color-coded by shift type. Conflict detection (double-booking, availability violations, skill mismatch) shown in real-time.

┌──────────────────────────────────────────────────────────────┐
│  DEALER SCHEDULE — Week of Mar 2                             │
│                                                              │
│              Mon    Tue    Wed    Thu    Fri    Sat    Sun    │
│  Anna K.    [Eve]  [Eve]  [---]  [Eve]  [Eve]  [Aft]  [---] │
│  Mike S.    [Mor]  [Mor]  [Mor]  [Mor]  [Mor]  [---]  [---] │
│  Chris L.   [Aft]  [---]  [Aft]  [Aft]  [Eve]  [Eve]  [Aft] │
│  Sarah D.   [---]  [Aft]  [Eve]  [---]  [Aft]  [Eve]  [Eve] │
│  Tom H.     [Mor]  [Mor]  [Mor]  [Mor]  [---]  [Mor]  [---] │
│                                                              │
│  ⚠ Warning: Friday Evening needs 3 dealers, only 2 assigned │
│  ⚠ Warning: Chris L. exceeds 40h this week                  │
└──────────────────────────────────────────────────────────────┘

Scheduling constraints (auto-enforced):

  • Maximum hours per week (configurable, default 40)
  • Minimum rest between shifts (configurable, default 10 hours)
  • Availability windows respected
  • Skill requirements per table/game met
  • Minimum staffing levels per shift (configurable)

2.3 Shift Trading

Dealers can trade shifts with each other — the system facilitates this while enforcing skill requirements.

Trading flow:

  1. Dealer A posts a shift for trade ("Friday Evening available")
  2. System shows the post to all dealers who: (a) are available that day, (b) have the required skills, (c) wouldn't exceed hour limits
  3. Dealer B offers to take it
  4. If manager approval is required (venue-configurable): manager approves/rejects
  5. If no approval required: trade is instant
  6. Both dealers notified. Schedule updated. Audit log recorded.

Drop without trade: A dealer can drop a shift (with enough notice, venue-configurable). The system posts it as an open shift. If unclaimed by a threshold time, the manager is notified.

2.4 Dealer Assignment to Tables

Phase 3 connects dealer scheduling to Phase 1 (tournaments) and Phase 2 (cash games):

Cash game dealer assignment:

  • Operator assigns a dealer to a cash table from the on-shift dealer pool
  • System shows only dealers who: (a) are on shift, (b) have the matching game skill
  • Dealer rotation timer (configurable: push every 30/45/60 minutes)
  • When the rotation timer hits, system suggests the next dealer and shows who's available

Tournament dealer assignment:

  • Tournament tables can have assigned dealers
  • Multi-table tournaments: dealers rotate across tables per the rotation schedule
  • Final table: system can flag dealers with "final table" skill for assignment

2.5 Dealer Mobile Experience

Dealers access their schedule and shift management via the same mobile PWA (role-based views):

  • My Schedule: See upcoming shifts, with venue details and game/table assignments
  • Trade Board: See available shifts for trade. Offer to take shifts. Post own shifts.
  • Clock In/Out: Tap to start/end shift. Automatic time tracking.
  • Notifications: Shift reminders, trade offers, schedule changes, rotation alerts
  • Hours Summary: Current week hours, pay period total, overtime status

2.6 Work Hours & Reporting

Automatic tracking: Clock-in/clock-out via mobile or Leaf UI. Break tracking (configurable: paid/unpaid breaks, minimum break duration compliance).

Reports (for venue manager):

  • Hours per dealer per period (week, month, pay period)
  • Overtime alerts
  • No-show / late arrival log
  • Shift trade history
  • Skill utilization (which game skills are most in-demand)

Export: CSV for payroll integration. Standard format compatible with common payroll systems.


3. Player Loyalty System

3.1 Points Engine

Points are the currency of loyalty. Venues configure how players earn them.

Point accrual rules (all configurable):

Activity Default Points Example
Tournament entry 1 point per €1 buy-in €50 tournament = 50 points
Tournament finish (top 10%) Bonus 50 points ITM finish = bonus
Cash game play 1 point per hour 4-hour session = 4 points
Cash game buy-in 0.5 points per €1 €200 buy-in = 100 points
League participation 10 points per event Season regular = loyalty bonus
Referral 25 points per new player Bring a friend
Special events Custom Venue-defined promotions

Multipliers (stackable):

  • Time-based: "2x points on Tuesday nights" (drive traffic on slow nights)
  • Game-based: "1.5x points on PLO tables" (incentivize new games)
  • Tier-based: "Gold members earn 1.5x base points"
  • Event-based: "3x points during anniversary week"

Point expiry: Configurable. Options: never expire, expire after X months of inactivity, expire after calendar year. Venues choose.

3.2 Tier System

Players progress through tiers based on point accumulation:

Tier Threshold (example) Benefits (example)
Bronze 0 points Base point earning, basic profile
Silver 500 points/quarter 1.25x point multiplier, priority waitlist
Gold 2,000 points/quarter 1.5x multiplier, free tournament entry/month, comp credits
Platinum 5,000 points/quarter 2x multiplier, VIP event access, dedicated floor contact

Tier configuration:

  • Tier names, thresholds, and benefits are fully customizable per venue
  • Evaluation period: monthly, quarterly, or annual
  • Tier maintenance: "maintain or drop" vs. "earn once, keep forever" (venue choice)
  • Grace period: configurable period before tier demotion (e.g., 1 month grace after missing threshold)

3.3 Rewards Catalog

Venues define what players can spend points on:

Category Examples
Tournament entries Free buy-in to weekly tournament (500 points)
Food & beverage €10 bar credit (200 points), free coffee (50 points)
Merchandise Venue t-shirt (1,000 points), branded card protector (300 points)
Seat upgrades Priority seat selection in next tournament (100 points)
Cash game Free hour of rake-free play (800 points)
Events VIP event invitation (2,000 points)
Custom Whatever the venue wants to offer

Redemption flow:

  1. Player views rewards on mobile → selects reward → confirms
  2. System deducts points, generates reward voucher with unique code
  3. Player shows voucher to staff (or staff scans QR code)
  4. Staff marks voucher as redeemed in the system
  5. All tracked for analytics (which rewards are popular, cost per redemption)

3.4 Automated Promotions

Venues can create promotions that trigger automatically:

Promotion types:

  • Time-based: "Double points every Wednesday 2-6PM"
  • Birthday: "100 bonus points on your birthday month"
  • Win-back: "Been away 30+ days? Come back and earn 3x points this visit"
  • Achievement: "Play your 100th tournament? 500 bonus points"
  • Seasonal: "Holiday tournament series — 2x points on all December events"
  • Referral: "Refer a friend who plays 3 events — both get 200 bonus points"

Promotions are created in the operator UI with start/end dates, targeting rules, and automatic application. No manual tracking.

3.5 Cross-Venue Loyalty (Multi-Venue Operators)

For operators running multiple venues:

  • Player loyalty tier is shared across all venues
  • Points earned at any venue count toward tier status
  • Rewards can be redeemed at any venue (or restricted to earning venue — operator choice)
  • Leaderboards can span all venues or be venue-specific
  • Corporate-level promotions push to all venues

3.6 Display & Mobile Integration

Player mobile view:

┌─────────────────────────────┐
│  MY LOYALTY                  │
│                              │
│  ★ GOLD MEMBER               │
│  2,450 / 5,000 to Platinum   │
│  ████████░░░░ 49%            │
│                              │
│  Available Points: 1,200     │
│  Lifetime Points: 8,750      │
│                              │
│  [View Rewards]  [History]   │
│                              │
│  Active Promotions:          │
│  🔥 2x points tonight!       │
│  🎂 Birthday month bonus     │
└─────────────────────────────┘

Display node views:

  • Loyalty leaderboard (top earners this month/quarter)
  • Current promotions ("2x Points Tonight!")
  • Tier benefits overview
  • All auto-cycle in signage playlists

4. Private Venues & Memberships

4.1 Venue Privacy Modes

Mode Visibility Registration
Public Anyone sees schedule, events, games Open — anyone can join
Semi-Private Schedule visible, limited detail Requires application + approval
Private Invisible to non-members Invite-only

Privacy mode affects:

  • Public venue page visibility
  • Whether the venue appears in search/discovery
  • Whether events are listed publicly
  • Whether non-members can join waitlists or register for tournaments

4.2 Membership Management

Membership tiers (venue-defined):

Tier Example Access
Standard €20/month Tournament access, cash game access
Premium €50/month + Priority waitlist, league eligibility
VIP €100/month + High-stakes games, private events, guest passes
Founding By invitation Full access, lifetime rate, governance input

Member lifecycle:

  1. Application: Player applies via venue page or invitation link
  2. Approval: Venue operator reviews application. Auto-approve rules (e.g., referred by existing member) available.
  3. Payment: Monthly/annual fee (tracked in Felt — payment processing via Stripe or venue's own system)
  4. Active membership: Access granted per tier
  5. Renewal: Auto-reminder before expiry. Grace period configurable.
  6. Suspension/Cancellation: Operator can suspend or cancel. Player can self-cancel with configurable notice period.

Invite system:

  • Members generate invite links (limited per member per month — configurable)
  • Invited players get a pre-approved application
  • Referral credit for the inviting member (loyalty points or other reward)
  • Guest passes: temporary access for specific events (configurable per tier — e.g., VIP members get 2 guest passes per month)

4.3 Member Communications

In-app messaging:

  • Venue → all members (announcements)
  • Venue → tier-specific (e.g., VIP event invitations)
  • Venue → individual member (private message)
  • Delivered via mobile PWA notification + optional email/SMS

Automated communications:

  • Welcome message on membership approval
  • Renewal reminders (7 days, 1 day before expiry)
  • Event announcements (new tournaments, special events)
  • Membership tier changes (upgrade/downgrade notifications)
  • Win-back messages for lapsed members

4.4 Member Directory (Optional)

Venues can enable a member directory (privacy settings per member):

  • Members can see who else is a member (first name + last initial)
  • Opt-in: members choose what to share (photo, game preferences, bio)
  • Useful for building community in private clubs
  • Disabled by default — venue must explicitly enable

5. Venue Analytics & Reporting

5.1 Revenue Dashboard

Real-time revenue tracking:

  • Tournament revenue: entry fees collected, house take
  • Cash game revenue: rake collected per table, per game type
  • Membership revenue: fees collected
  • Combined: total revenue by day/week/month/year

Visualizations:

  • Revenue trend line (last 30/90/365 days)
  • Revenue by source (pie: tournaments vs. cash vs. memberships)
  • Revenue by game type (which games make the most money?)
  • Revenue by day of week (which nights are strongest?)
  • Revenue by time of day (when are the peak revenue hours?)

5.2 Player Analytics

Player engagement metrics:

  • Active players (played in last 7/30/90 days)
  • New player acquisition rate
  • Player retention rate (% returning within 30 days)
  • Player lifetime value (total revenue attributed per player)
  • Visit frequency distribution

Player segmentation:

  • By game preference (tournament vs. cash vs. both)
  • By stakes level
  • By loyalty tier
  • By visit frequency (regular, occasional, lapsed)
  • By value (high-value, medium, low)

All player analytics are aggregated — individual player data is not exposed to other players.

5.3 Operational Analytics

Table utilization:

  • Seats occupied vs. available over time (heatmap by hour of day / day of week)
  • Average table utilization rate
  • Table open/close patterns

Tournament analytics:

  • Fill rates (registered vs. capacity)
  • Average entries per tournament
  • Most popular tournament formats
  • Late registration patterns
  • Prize pool trends

Waitlist analytics:

  • Average wait time by game/stakes
  • Waitlist conversion rate (joined → seated)
  • Waitlist abandonment rate
  • Peak demand times

Dealer analytics:

  • Hours worked per dealer
  • Shift coverage rates
  • Trade frequency
  • Rotation compliance

5.4 Multi-Venue Benchmarking

For operators with multiple venues:

  • Side-by-side venue comparison (revenue, utilization, player engagement)
  • Identify best practices from top-performing venues
  • Flag underperforming venues with specific metrics
  • Cross-venue player flow (which players play at multiple venues?)

5.5 Export & Integration

  • CSV export: All reports exportable for Excel/accounting
  • PDF reports: Formatted reports for stakeholders
  • API access: All analytics available via REST API for custom integrations
  • Scheduled reports: Auto-generate and email weekly/monthly reports to venue owner

6. Public Venue Presence

6.1 Venue Profile Page

Each venue gets a public page at venue-name.felt.io (or custom domain):

  • Venue name, logo, photos, description
  • Location with map
  • Hours of operation
  • Current games running (live, for public venues)
  • Upcoming events and tournaments
  • Game offerings (types and stakes)
  • Contact information

SEO-optimized for "poker near [city]" searches.

6.2 Event Publishing

Automated schedule publishing:

  • Weekly tournament schedule auto-published to venue page
  • Upcoming special events with registration
  • League schedules and current standings

External publishing (API integrations):

  • Poker Atlas format export (automatic submission)
  • Hendon Mob format export (tournament results)
  • Social media API integration (auto-post new events to venue's Facebook/Instagram)
  • iCal feed for players to subscribe to venue schedule

6.3 Online Registration

Players can register for upcoming tournaments from the venue page:

  • View tournament details (format, buy-in, blind structure, starting stack)
  • Register (with or without pre-payment — venue configurable)
  • See current registrant count and capacity
  • Cancel registration (within venue's cancellation policy)
  • Automatic reminders before the event

7. Data Architecture

New Tables (extends Phase 1 + 2 schema)

-- =====================
-- DEALER MANAGEMENT
-- =====================

CREATE TABLE dealers (
    id              TEXT PRIMARY KEY,
    player_id       TEXT REFERENCES players(id),  -- Dealer may also be a player
    name            TEXT NOT NULL,
    email           TEXT,
    phone           TEXT,
    photo_url       TEXT,
    employee_id     TEXT,
    status          TEXT NOT NULL DEFAULT 'active',  -- active, inactive, terminated
    hire_date       DATE,
    notes           TEXT,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE dealer_skills (
    id          TEXT PRIMARY KEY,
    dealer_id   TEXT NOT NULL REFERENCES dealers(id),
    skill_type  TEXT NOT NULL,    -- game_variant, betting, format, experience, special, language
    skill_value TEXT NOT NULL,    -- "holdem", "no_limit", "tournament", "senior", "high_stakes", "danish"
    verified    BOOLEAN NOT NULL DEFAULT false,
    verified_by TEXT,
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE shift_templates (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,           -- "Evening"
    start_time  TIME NOT NULL,           -- "18:00"
    end_time    TIME NOT NULL,           -- "02:00"
    break_minutes INTEGER DEFAULT 0,
    color       TEXT,                    -- Hex color for schedule UI
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE dealer_schedules (
    id              TEXT PRIMARY KEY,
    dealer_id       TEXT NOT NULL REFERENCES dealers(id),
    date            DATE NOT NULL,
    template_id     TEXT REFERENCES shift_templates(id),
    start_time      DATETIME NOT NULL,
    end_time        DATETIME NOT NULL,
    status          TEXT NOT NULL DEFAULT 'scheduled',  -- scheduled, confirmed, in_progress, completed, no_show, traded
    actual_start    DATETIME,
    actual_end      DATETIME,
    notes           TEXT,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(dealer_id, date, template_id)
);

CREATE TABLE dealer_availability (
    id          TEXT PRIMARY KEY,
    dealer_id   TEXT NOT NULL REFERENCES dealers(id),
    day_of_week INTEGER,            -- 0=Mon, 6=Sun (NULL for specific date override)
    specific_date DATE,             -- For one-off availability changes
    available_from TIME,            -- NULL = not available
    available_to   TIME,
    is_available BOOLEAN NOT NULL DEFAULT true,
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE shift_trades (
    id              TEXT PRIMARY KEY,
    schedule_id     TEXT NOT NULL REFERENCES dealer_schedules(id),
    offering_dealer TEXT NOT NULL REFERENCES dealers(id),
    taking_dealer   TEXT REFERENCES dealers(id),    -- NULL = open for offers
    status          TEXT NOT NULL DEFAULT 'posted',  -- posted, offered, approved, completed, cancelled
    requires_approval BOOLEAN NOT NULL DEFAULT false,
    approved_by     TEXT,
    approved_at     DATETIME,
    notes           TEXT,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE dealer_time_clock (
    id          TEXT PRIMARY KEY,
    dealer_id   TEXT NOT NULL REFERENCES dealers(id),
    schedule_id TEXT REFERENCES dealer_schedules(id),
    clock_in    DATETIME NOT NULL,
    clock_out   DATETIME,
    break_start DATETIME,
    break_end   DATETIME,
    total_hours REAL,              -- Calculated on clock-out
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- LOYALTY SYSTEM
-- =====================

CREATE TABLE loyalty_config (
    id              TEXT PRIMARY KEY,
    venue_id        TEXT NOT NULL,
    point_expiry_months INTEGER,          -- NULL = never expire
    evaluation_period TEXT DEFAULT 'quarterly',  -- monthly, quarterly, annual
    grace_period_days INTEGER DEFAULT 30,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE loyalty_tiers (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,           -- "Gold"
    sort_order      INTEGER NOT NULL,
    threshold_points INTEGER NOT NULL,       -- Points needed per evaluation period
    point_multiplier REAL NOT NULL DEFAULT 1.0,
    color           TEXT,                    -- Hex color for UI
    icon            TEXT,                    -- Icon identifier
    benefits_json   TEXT,                    -- JSON description of benefits
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE loyalty_accrual_rules (
    id              TEXT PRIMARY KEY,
    activity_type   TEXT NOT NULL,           -- tournament_entry, cash_hour, cash_buyin, league_event, referral, custom
    points_per_unit REAL NOT NULL,           -- Points per €1, per hour, per event, etc.
    unit            TEXT NOT NULL,           -- "currency", "hour", "event", "flat"
    conditions_json TEXT,                    -- Optional JSON conditions (game type, stakes, time window)
    is_active       BOOLEAN NOT NULL DEFAULT true,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE loyalty_multipliers (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,           -- "Tuesday Double Points"
    multiplier      REAL NOT NULL,           -- 2.0 for double
    conditions_json TEXT NOT NULL,           -- Time windows, game types, tier requirements
    start_date      DATETIME,
    end_date        DATETIME,
    is_active       BOOLEAN NOT NULL DEFAULT true,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE player_loyalty (
    id              TEXT PRIMARY KEY,
    player_id       TEXT NOT NULL REFERENCES players(id) UNIQUE,
    current_tier_id TEXT REFERENCES loyalty_tiers(id),
    current_points  INTEGER NOT NULL DEFAULT 0,
    lifetime_points INTEGER NOT NULL DEFAULT 0,
    period_points   INTEGER NOT NULL DEFAULT 0,    -- Points in current evaluation period
    tier_locked_until DATETIME,                     -- Grace period end
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE loyalty_transactions (
    id              TEXT PRIMARY KEY,
    player_id       TEXT NOT NULL REFERENCES players(id),
    type            TEXT NOT NULL,           -- earn, redeem, expire, adjust
    points          INTEGER NOT NULL,        -- Positive for earn, negative for redeem/expire
    source          TEXT,                    -- tournament_id, session_id, promotion_id, reward_id
    description     TEXT,                    -- "Tournament entry: Friday €50 NLH"
    balance_after   INTEGER NOT NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE loyalty_rewards (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,           -- "Free Tournament Entry"
    description     TEXT,
    category        TEXT NOT NULL,           -- tournament, food_beverage, merchandise, seat, cash_game, event, custom
    points_cost     INTEGER NOT NULL,
    min_tier_id     TEXT REFERENCES loyalty_tiers(id),  -- Minimum tier to access this reward
    quantity_available INTEGER,              -- NULL = unlimited
    quantity_redeemed INTEGER NOT NULL DEFAULT 0,
    is_active       BOOLEAN NOT NULL DEFAULT true,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE loyalty_redemptions (
    id          TEXT PRIMARY KEY,
    player_id   TEXT NOT NULL REFERENCES players(id),
    reward_id   TEXT NOT NULL REFERENCES loyalty_rewards(id),
    points_spent INTEGER NOT NULL,
    voucher_code TEXT NOT NULL UNIQUE,
    status      TEXT NOT NULL DEFAULT 'issued',  -- issued, redeemed, expired, cancelled
    redeemed_at DATETIME,
    redeemed_by TEXT,                        -- Staff member who confirmed redemption
    expires_at  DATETIME,
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE loyalty_promotions (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,
    type            TEXT NOT NULL,           -- time_based, birthday, winback, achievement, seasonal, referral
    config_json     TEXT NOT NULL,           -- Trigger conditions, point amounts, multipliers
    start_date      DATETIME,
    end_date        DATETIME,
    is_active       BOOLEAN NOT NULL DEFAULT true,
    auto_apply      BOOLEAN NOT NULL DEFAULT true,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- MEMBERSHIPS
-- =====================

CREATE TABLE membership_tiers (
    id              TEXT PRIMARY KEY,
    name            TEXT NOT NULL,           -- "VIP"
    description     TEXT,
    monthly_fee     INTEGER,                 -- Cents (NULL = free tier)
    annual_fee      INTEGER,                 -- Cents (discounted annual option)
    max_members     INTEGER,                 -- NULL = unlimited
    guest_passes_monthly INTEGER DEFAULT 0,
    benefits_json   TEXT,                    -- JSON description of access and benefits
    sort_order      INTEGER NOT NULL,
    is_active       BOOLEAN NOT NULL DEFAULT true,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE memberships (
    id              TEXT PRIMARY KEY,
    player_id       TEXT NOT NULL REFERENCES players(id),
    tier_id         TEXT NOT NULL REFERENCES membership_tiers(id),
    status          TEXT NOT NULL DEFAULT 'pending',  -- pending, active, suspended, cancelled, expired
    applied_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    approved_at     DATETIME,
    approved_by     TEXT,
    billing_cycle   TEXT DEFAULT 'monthly',           -- monthly, annual
    current_period_start DATETIME,
    current_period_end   DATETIME,
    auto_renew      BOOLEAN NOT NULL DEFAULT true,
    cancelled_at    DATETIME,
    cancel_reason   TEXT,
    invited_by      TEXT REFERENCES players(id),
    invite_code     TEXT,
    notes           TEXT,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE guest_passes (
    id              TEXT PRIMARY KEY,
    member_id       TEXT NOT NULL REFERENCES memberships(id),
    guest_name      TEXT NOT NULL,
    guest_email     TEXT,
    event_id        TEXT,                    -- Specific event, or NULL for general access
    valid_date      DATE NOT NULL,
    status          TEXT NOT NULL DEFAULT 'issued',  -- issued, used, expired, cancelled
    used_at         DATETIME,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE venue_settings_privacy (
    id              TEXT PRIMARY KEY,
    privacy_mode    TEXT NOT NULL DEFAULT 'public',  -- public, semi_private, private
    show_schedule   BOOLEAN NOT NULL DEFAULT true,
    show_games      BOOLEAN NOT NULL DEFAULT true,
    show_waitlist   BOOLEAN NOT NULL DEFAULT false,
    require_membership BOOLEAN NOT NULL DEFAULT false,
    member_directory_enabled BOOLEAN NOT NULL DEFAULT false,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

8. API Design

Dealer Management

GET    /dealers                              List dealers
POST   /dealers                              Create dealer profile
GET    /dealers/:id                          Get dealer detail
PUT    /dealers/:id                          Update dealer
POST   /dealers/:id/skills                   Add skill
DELETE /dealers/:id/skills/:skillId          Remove skill

GET    /schedule                             Get schedule (date range, filters)
POST   /schedule                             Assign shift
PUT    /schedule/:id                         Update shift
DELETE /schedule/:id                         Remove shift

GET    /shifts/templates                     List shift templates
POST   /shifts/templates                     Create template

POST   /shifts/trades                        Post shift for trade
GET    /shifts/trades                        List available trades
POST   /shifts/trades/:id/offer             Offer to take shift
POST   /shifts/trades/:id/approve           Approve trade (manager)

POST   /dealers/:id/clock-in                Clock in
POST   /dealers/:id/clock-out               Clock out
GET    /dealers/:id/hours                    Hours report (date range)

Loyalty System

GET    /loyalty/config                       Get loyalty configuration
PUT    /loyalty/config                       Update loyalty configuration
GET    /loyalty/tiers                        List tiers
POST   /loyalty/tiers                        Create tier
PUT    /loyalty/tiers/:id                    Update tier

GET    /loyalty/rules                        List accrual rules
POST   /loyalty/rules                        Create rule
GET    /loyalty/multipliers                  List active multipliers
POST   /loyalty/multipliers                  Create multiplier

GET    /loyalty/rewards                      List available rewards
POST   /loyalty/rewards                      Create reward
POST   /loyalty/redeem                       Redeem reward (player)
POST   /loyalty/redemptions/:id/confirm      Confirm redemption (staff)

GET    /loyalty/promotions                   List promotions
POST   /loyalty/promotions                   Create promotion

GET    /loyalty/leaderboard                  Loyalty leaderboard

Player Loyalty (Mobile)

GET    /me/loyalty                           My loyalty status (tier, points, promotions)
GET    /me/loyalty/history                   My point transaction history
GET    /me/loyalty/rewards                   Available rewards for my tier
POST   /me/loyalty/redeem                    Redeem a reward
GET    /me/loyalty/vouchers                  My active vouchers

Memberships

GET    /memberships/tiers                    List membership tiers
POST   /memberships/apply                    Apply for membership
GET    /memberships                          List all memberships (operator)
PUT    /memberships/:id                      Update membership (approve, suspend, etc.)
POST   /memberships/:id/renew               Trigger renewal

POST   /memberships/:id/guest-pass          Issue guest pass
GET    /memberships/:id/guest-passes         List guest passes

GET    /me/membership                        My membership status
POST   /me/membership/cancel                Cancel my membership
POST   /me/membership/invite                Generate invite link

Analytics

GET    /analytics/revenue                    Revenue dashboard (date range, source filters)
GET    /analytics/players                    Player engagement metrics
GET    /analytics/tables                     Table utilization metrics
GET    /analytics/tournaments                Tournament performance metrics
GET    /analytics/waitlist                   Waitlist analytics
GET    /analytics/dealers                    Dealer metrics
GET    /analytics/loyalty                    Loyalty program metrics
GET    /analytics/export                     Export report (CSV/PDF)

Public Venue

GET    /venue/profile                        Public venue profile
PUT    /venue/profile                        Update venue profile (operator)
GET    /venue/schedule                       Public event schedule
GET    /venue/games                          Current games (public venues)
POST   /venue/events/:id/register            Register for event (player)
GET    /venue/events/:id/registrations        List registrations (operator)

9. Roadmap

v3.0 — Dealer Management

  • Dealer profiles with skill tags
  • Shift templates and schedule builder
  • Shift trading system
  • Clock-in/clock-out with hours tracking
  • Dealer assignment to cash tables and tournaments
  • Dealer mobile experience (schedule, trade board, clock)
  • Basic reporting (hours, shifts, trades)

v3.1 — Player Loyalty

  • Points engine with configurable accrual rules
  • Tier system with automated evaluation
  • Rewards catalog and redemption flow
  • Point multipliers (time-based, game-based, tier-based)
  • Loyalty display on mobile and venue screens
  • Basic promotions (birthday, time-based)

v3.2 — Memberships & Privacy

  • Venue privacy modes (public, semi-private, private)
  • Membership tiers with fee tracking
  • Application and approval workflow
  • Invite system and guest passes
  • Member communications
  • Optional member directory

v3.3 — Analytics & Public Presence

  • Revenue dashboards
  • Player engagement analytics
  • Operational analytics (tables, waitlists, tournaments)
  • Public venue profile page
  • Event publishing with online registration
  • External publishing (Poker Atlas, Hendon Mob, social)
  • Scheduled reports

v3.4 — Advanced Features

  • Cross-venue loyalty (multi-venue operators)
  • Multi-venue benchmarking analytics
  • Dealer rotation automation
  • Advanced promotions engine (winback, achievement, referral chains)
  • Custom reporting builder
  • API access for third-party integrations
  • White-label options for enterprise

Phase 3 turns Felt from a poker management tool into the operating system for the venue. At this point, every tournament, every cash game, every dealer shift, every player visit, every dollar — it all flows through Felt. That's the endgame.