# kirkkala/hoopfinder architecture

> Architecture diagram of the GitHub repository kirkkala/hoopfinder, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-12). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/kirkkala/hoopfinder
- Repository: https://github.com/kirkkala/hoopfinder

## Overview

Hoop Finder is a Finnish-first directory for finding outdoor basketball courts in Finland, with map, city listings, and court pages. Visitors can also suggest courts and corrections, upload photos, and send feedback; administrators review and publish submissions. The public catalog uses a committed snapshot rather than querying source APIs at runtime. Edges are limited to README-documented behavior and relationships shown by the excerpts or SOURCE INDEX; some page-to-component wiring is therefore omitted.

Main entry points: [`src/lib/submitted-courts.ts`](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/submitted-courts.ts) (Submission workflow), [`src/app/api/court-edits/route.ts`](https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/court-edits/route.ts) (Correction API), [`src/app/api/admin/courts/[id]/route.ts`](https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/admin/courts/%5Bid%5D/route.ts) (Admin court API), [`src/lib/catalog.ts`](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/catalog.ts) (Court catalog)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_discovery["Court discovery"]
  node_map["Court map<br/>[page.tsx]"]
  node_city_list["City listings<br/>[page.tsx]"]
  node_city_page["City courts<br/>[page.tsx]"]
  node_court_page["Court details<br/>[CourtDetails.tsx]"]
  node_explorer["Map explorer<br/>[CourtExplorer.tsx]"]
  node_catalog["Court catalog<br/>[catalog.ts]"]
  node_snapshot["Court snapshot<br/>[courts.json]"]
end

subgraph group_contributions["Visitor contributions"]
  node_add_form["Suggest a court<br/>[AddCourtView.tsx]"]
  node_add_api["Court submission API<br/>[route.ts]"]
  node_confirm["Email confirmation<br/>[page.tsx]"]
  node_submission_store["Submission workflow"]
  node_edit_api["Correction API<br/>[route.ts]"]
  node_photo_api["Court photos API<br/>[route.ts]"]
  node_photo_service["Photo service<br/>[court-photos.ts]"]
  node_feedback_api["Feedback API<br/>[route.ts]"]
  node_feedback_service["Feedback service<br/>[feedback.ts]"]
end

subgraph group_operations["Administration"]
  node_admin["Admin workspace"]
  node_admin_api["Admin court API<br/>[route.ts]"]
end

subgraph group_shared["Shared services"]
  node_court_data["Court rules<br/>[courts.ts]"]
  node_auth["Admin authentication<br/>[auth.ts]"]
  node_database[("Postgres access<br/>[db.ts]")]
  node_email["Transactional email<br/>[email.ts]"]
end

node_visitor(("Visitor"))

node_visitor -->|"finds courts"| node_map
node_visitor -->|"browses cities"| node_city_list
node_visitor -->|"opens court"| node_court_page
node_visitor -->|"suggests court"| node_add_form
node_visitor -->|"suggests correction"| node_edit_api
node_visitor -->|"adds photos"| node_photo_api
node_visitor -->|"sends feedback"| node_feedback_api
node_catalog -->|"reads snapshot"| node_snapshot
node_catalog -->|"merges courts"| node_court_data
node_add_api -->|"creates submission"| node_submission_store
node_edit_api -->|"creates correction"| node_submission_store
node_edit_api -->|"checks session"| node_auth
node_submission_store -->|"stores submissions"| node_database
node_submission_store -->|"sends confirmations"| node_email
node_visitor -->|"confirms by email"| node_confirm
node_admin -->|"publishes courts"| node_admin_api
node_admin_api -->|"checks admin"| node_auth
node_admin_api -->|"updates submissions"| node_submission_store
node_photo_api -->|"handles photos"| node_photo_service
node_feedback_api -->|"handles feedback"| node_feedback_service
node_photo_service -->|"stores metadata"| node_database

click node_map "https://github.com/kirkkala/hoopfinder/blob/main/src/app/kartta/page.tsx"
click node_city_list "https://github.com/kirkkala/hoopfinder/blob/main/src/app/koripallokentat/page.tsx"
click node_city_page "https://github.com/kirkkala/hoopfinder/blob/main/src/app/koripallokentat/%5Bcity%5D/page.tsx"
click node_court_page "https://github.com/kirkkala/hoopfinder/blob/main/src/components/court/CourtDetails.tsx"
click node_explorer "https://github.com/kirkkala/hoopfinder/blob/main/src/components/explorer/CourtExplorer.tsx"
click node_catalog "https://github.com/kirkkala/hoopfinder/blob/main/src/lib/catalog.ts"
click node_snapshot "https://github.com/kirkkala/hoopfinder/blob/main/data/courts.json"
click node_court_data "https://github.com/kirkkala/hoopfinder/blob/main/src/lib/courts.ts"
click node_add_form "https://github.com/kirkkala/hoopfinder/blob/main/src/components/add-court/AddCourtView.tsx"
click node_add_api "https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/submitted-courts/route.ts"
click node_confirm "https://github.com/kirkkala/hoopfinder/blob/main/src/app/add/confirm/%5Btoken%5D/page.tsx"
click node_submission_store "https://github.com/kirkkala/hoopfinder/blob/main/src/lib/submitted-courts.ts"
click node_edit_api "https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/court-edits/route.ts"
click node_photo_api "https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/court-photos/route.ts"
click node_photo_service "https://github.com/kirkkala/hoopfinder/blob/main/src/lib/court-photos.ts"
click node_feedback_api "https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/feedback/route.ts"
click node_feedback_service "https://github.com/kirkkala/hoopfinder/blob/main/src/lib/feedback.ts"
click node_admin "https://github.com/kirkkala/hoopfinder/blob/main/src/components/admin/AdminCourtsView.tsx"
click node_admin_api "https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/admin/courts/%5Bid%5D/route.ts"
click node_auth "https://github.com/kirkkala/hoopfinder/blob/main/src/auth.ts"
click node_database "https://github.com/kirkkala/hoopfinder/blob/main/src/lib/db.ts"
click node_email "https://github.com/kirkkala/hoopfinder/blob/main/src/lib/email.ts"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_map,node_city_list,node_city_page,node_court_page,node_explorer,node_catalog,node_snapshot toneBlue
class node_add_form,node_add_api,node_confirm,node_submission_store,node_edit_api,node_photo_api,node_photo_service,node_feedback_api,node_feedback_service toneAmber
class node_admin,node_admin_api toneMint
class node_court_data,node_auth,node_database,node_email toneRose
class node_visitor toneIndigo
```

## Components

### Court discovery

- [Court map](https://github.com/kirkkala/hoopfinder/blob/main/src/app/kartta/page.tsx): `src/app/kartta/page.tsx`
- [City listings](https://github.com/kirkkala/hoopfinder/blob/main/src/app/koripallokentat/page.tsx): `src/app/koripallokentat/page.tsx`
- [City courts](https://github.com/kirkkala/hoopfinder/blob/main/src/app/koripallokentat/%5Bcity%5D/page.tsx): `src/app/koripallokentat/[city]/page.tsx`
- [Court details](https://github.com/kirkkala/hoopfinder/blob/main/src/components/court/CourtDetails.tsx): `src/components/court/CourtDetails.tsx`
- [Map explorer](https://github.com/kirkkala/hoopfinder/blob/main/src/components/explorer/CourtExplorer.tsx): `src/components/explorer/CourtExplorer.tsx`
- [Court catalog](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/catalog.ts): `src/lib/catalog.ts`
- [Court snapshot](https://github.com/kirkkala/hoopfinder/blob/main/data/courts.json): `data/courts.json`

### Visitor contributions

- [Suggest a court](https://github.com/kirkkala/hoopfinder/blob/main/src/components/add-court/AddCourtView.tsx): `src/components/add-court/AddCourtView.tsx`
- [Court submission API](https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/submitted-courts/route.ts): `src/app/api/submitted-courts/route.ts`
- [Email confirmation](https://github.com/kirkkala/hoopfinder/blob/main/src/app/add/confirm/%5Btoken%5D/page.tsx): `src/app/add/confirm/[token]/page.tsx`
- [Submission workflow](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/submitted-courts.ts): `src/lib/submitted-courts.ts`
- [Correction API](https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/court-edits/route.ts): `src/app/api/court-edits/route.ts`
- [Court photos API](https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/court-photos/route.ts): `src/app/api/court-photos/route.ts`
- [Photo service](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/court-photos.ts): `src/lib/court-photos.ts`
- [Feedback API](https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/feedback/route.ts): `src/app/api/feedback/route.ts`
- [Feedback service](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/feedback.ts): `src/lib/feedback.ts`

### Administration

- [Admin workspace](https://github.com/kirkkala/hoopfinder/blob/main/src/components/admin/AdminCourtsView.tsx): `src/components/admin/AdminCourtsView.tsx`
- [Admin court API](https://github.com/kirkkala/hoopfinder/blob/main/src/app/api/admin/courts/%5Bid%5D/route.ts): `src/app/api/admin/courts/[id]/route.ts`

### Shared services

- [Court rules](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/courts.ts): `src/lib/courts.ts`
- [Admin authentication](https://github.com/kirkkala/hoopfinder/blob/main/src/auth.ts): `src/auth.ts`
- [Postgres access](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/db.ts): `src/lib/db.ts`
- [Transactional email](https://github.com/kirkkala/hoopfinder/blob/main/src/lib/email.ts): `src/lib/email.ts`

### Other components

- **Visitor**

## Connections

- Visitor → Court map: finds courts (evidence: `README.md`)
- Visitor → City listings: browses cities (evidence: `README.md`)
- Visitor → Court details: opens court (evidence: `README.md`)
- Visitor → Suggest a court: suggests court (evidence: `README.md`)
- Visitor → Correction API: suggests correction (evidence: `README.md`)
- Visitor → Court photos API: adds photos (evidence: `README.md`)
- Visitor → Feedback API: sends feedback (evidence: `README.md`)
- Court catalog → Court snapshot: reads snapshot (evidence: `src/lib/catalog.ts`)
- Court catalog → Court rules: merges courts (evidence: `src/lib/catalog.ts`)
- Court submission API → Submission workflow: creates submission (evidence: `src/app/api/submitted-courts/route.ts`)
- Correction API → Submission workflow: creates correction (evidence: `src/app/api/court-edits/route.ts`)
- Correction API → Admin authentication: checks session (evidence: `src/app/api/court-edits/route.ts`)
- Submission workflow → Postgres access: stores submissions (evidence: `src/lib/submitted-courts.ts`)
- Submission workflow → Transactional email: sends confirmations (evidence: `src/lib/submitted-courts.ts`)
- Visitor → Email confirmation: confirms by email (evidence: `README.md`)
- Admin workspace → Admin court API: publishes courts (evidence: `README.md`)
- Admin court API → Admin authentication: checks admin (evidence: `src/app/api/admin/courts/[id]/route.ts`)
- Admin court API → Submission workflow: updates submissions (evidence: `src/app/api/admin/courts/[id]/route.ts`)
- Court photos API → Photo service: handles photos (evidence: `src/app/api/court-photos/route.ts`)
- Feedback API → Feedback service: handles feedback (evidence: `src/app/api/feedback/route.ts`)
- Photo service → Postgres access: stores metadata (evidence: `src/lib/court-photos.ts`)
