Skip to content

Venue Closure Reporting

Feature: User-submitted closure reports with admin review workflow

Users can report venues as closed directly from the venue detail view. Reports are queued for admin/dev review before the venue is actually marked as closed in the system.


User Flow

  1. User views venue details

    • Navigates to a venue → "Learn more about this venue"
    • Sees "Report this place as closed" button at bottom of details
  2. User submits closure report

    • Clicks "Report this place as closed"
    • Modal opens with form:
      • Reason (required dropdown):
        • Permanently closed
        • Temporarily closed
        • Relocated to new address
        • Never existed / Wrong information
        • Other
      • Additional details (optional text, max 500 chars)
    • Submits report
    • Success message shows: "Report submitted! We'll review this soon."
  3. Report stored for review

    • Report saved to venueClosureReports collection
    • Status: pending
    • Venue remains active and visible
  4. Admin reviews report (via npm script)

    • Admin runs: npm run review:closures --env=dev (or --env=prod)
    • Tool shows pending reports with venue details
    • Admin chooses: Approve, Reject, or Skip
    • If approved → Venue status updated to closed, hidden from app
    • If rejected → Report marked as rejected, venue remains active

Technical Architecture

Frontend Components

Location: src/components/dashboard/VenueView.jsx

  • "Report this place as closed" button in venue details
  • Opens ClosureReportModal on click
  • Shows success message after submission

Modal: src/components/dashboard/ClosureReportModal.jsx

  • Dropdown for closure reason
  • Textarea for additional details (optional)
  • Submit/Cancel actions
  • Loading states and error handling

Backend Service

Service: src/lib/venueClosureReportService.js

Functions:

  • createClosureReport(venueId, userId, reason, details) - Submit new report
  • getPendingClosureReports() - Fetch all pending reports (admin)
  • getVenueClosureReports(venueId) - Get all reports for specific venue
  • approveClosureReport(reportId, adminId, notes) - Approve report
  • rejectClosureReport(reportId, adminId, notes) - Reject report
  • getClosureReportStats() - Get stats (total, pending, approved, rejected)

Firestore Schema

Collection: venueClosureReports

Document structure:

js
{
  venueId: string,           // ID of reported venue
  venueName: string,         // Venue name (for display)
  venueAddress: string|null, // Venue address
  venueCategory: string|null,// Venue category
  reportedBy: string,        // User ID who submitted report
  reason: string,            // Closure reason (enum)
  details: string|null,      // Optional additional details
  status: string,            // 'pending' | 'approved' | 'rejected'
  createdAt: timestamp,      // When report was created
  updatedAt: timestamp,      // Last update
  reviewedAt: timestamp|null,// When admin reviewed
  reviewedBy: string|null,   // Admin ID who reviewed
  reviewNotes: string|null   // Admin notes (optional)
}

Security Rules: firestore.rules

  • Create: Authenticated users only, status must be 'pending'
  • Read: Authenticated users (admins only in practice)
  • Update: Blocked (use admin script only)
  • Delete: Blocked (audit trail)

Admin Review Tool

Script: scripts/review-closure-reports.mjs

Usage:

bash
# Review dev reports
npm run review:closures -- --env=dev

# Review prod reports
npm run review:closures -- --env=prod

Workflow:

  1. Admin runs script with environment flag
  2. Prompted for admin ID (for audit trail)
  3. Script shows stats: total, pending, approved, rejected
  4. For each pending report:
    • Displays venue info, reason, details, reporter, date
    • Prompts for action: [A]pprove | [R]eject | [S]kip | [Q]uit
    • If approved: Venue status → 'closed' (hidden from app)
    • If rejected: Report status → 'rejected' (venue stays active)
  5. Changes committed to Firestore in batches

Requirements:

  • Firebase Admin SDK
  • Service account at: ~/repos/secrets/firebase/lantern-app-{env}-firebase-adminsdk.json

Workflow Examples

Example: False Report (Venue Still Open)

  1. User reports "Coffee Lab" as permanently closed
  2. Admin reviews report
  3. Admin verifies venue is still open (checks Google Maps, social media)
  4. Admin rejects report with note: "Verified open via Instagram post today"
  5. Report marked as rejected, venue remains active
  6. Venue continues to show in app

Example: Confirmed Closure

  1. User reports "Books & Brews" as permanently closed
  2. Admin reviews report
  3. Admin confirms closure (Google shows "Permanently closed")
  4. Admin approves report
  5. Venue status updated to 'closed'
  6. Venue hidden from app (no longer shows in search/map)
  7. Existing lanterns at venue auto-expire per normal TTL

Example: Duplicate Reports

  1. Multiple users report same venue
  2. All reports stored separately with pending status
  3. Admin approves first report → venue marked closed
  4. Subsequent reports can be bulk-approved or skipped (venue already closed)

Data Flow Diagram

┌─────────────────────────────────────────────────────────────┐
│                     USER REPORTS CLOSURE                    │
└─────────────────────────────────────────────────────────────┘


                 ┌──────────────────────┐
                 │   VenueView.jsx      │
                 │  (Detail screen)     │
                 └──────────────────────┘


                 ┌──────────────────────┐
                 │ ClosureReportModal   │
                 │  (Reason + details)  │
                 └──────────────────────┘


           ┌────────────────────────────────────┐
           │  venueClosureReportService.js      │
           │  createClosureReport()             │
           └────────────────────────────────────┘


           ┌────────────────────────────────────┐
           │  Firestore Collection              │
           │  'venueClosureReports'             │
           │  status: 'pending'                 │
           └────────────────────────────────────┘

                            │ (Waits for admin review)

┌─────────────────────────────────────────────────────────────┐
│               ADMIN REVIEWS VIA NPM SCRIPT                  │
└─────────────────────────────────────────────────────────────┘


           ┌────────────────────────────────────┐
           │  npm run review:closures --env=dev │
           │  (Interactive CLI tool)            │
           └────────────────────────────────────┘

                    ┌───────┴───────┐
                    │               │
                    ▼               ▼
              [Approve]         [Reject]
                    │               │
                    ▼               ▼
        ┌──────────────────┐   ┌──────────────────┐
        │ Venue status →   │   │ Report status →  │
        │ 'closed'         │   │ 'rejected'       │
        │                  │   │                  │
        │ Hidden from app  │   │ Venue stays      │
        │                  │   │ active           │
        └──────────────────┘   └──────────────────┘

Testing

Manual Testing Steps

  1. Test report submission:

    • Sign in as a user
    • Navigate to any venue details
    • Click "Report this place as closed"
    • Select reason, add optional details
    • Submit and verify success message
  2. Verify Firestore data:

    bash
    # In Firebase console, check venueClosureReports collection
    # Should see new document with:
    # - status: 'pending'
    # - reportedBy: {your user ID}
    # - reason, details, timestamps
  3. Test admin review (dev):

    bash
    npm run review:closures --env=dev
    # Enter your admin ID
    # Approve a report
    # Verify venue status changed to 'closed' in venues collection
  4. Test rejection:

    bash
    npm run review:closures --env=dev
    # Reject a report
    # Verify venue status remains 'open'
    # Verify report status changed to 'rejected'

Future Enhancements

V2 Features

  • Admin web dashboard - Replace CLI tool with web UI
  • Bulk actions - Approve/reject multiple reports at once
  • Auto-flagging - Auto-approve if X users report same venue
  • OSM sync - Cross-check reports against OSM data
  • Notification system - Notify reporters when their report is reviewed
  • Report analytics - Track false positive rates, reporter accuracy

Production Considerations

  • Admin role management - Use Firebase custom claims for admin roles
  • Rate limiting - Prevent spam reports from single user
  • Report validation - Check if user has actually been to venue
  • Appeal process - Allow venue owners to dispute closures
  • Automated verification - Integrate with Google Places API to auto-verify closures


Changelog

2026-01-23 - Initial implementation

  • User-facing closure report button in venue details
  • Modal for submitting reports with reason + details
  • Firestore collection for pending reports
  • Interactive npm script for admin review
  • Firestore security rules for closure reports
  • Complete documentation

Built with VitePress