aarbitandClaude Sonnet 5 6cdfde822d Add site-wide announcements, account migration, and profile self-service
Announcements: a Super Admin (or an Admin individually flagged via
profiles.can_post_announcements) can post/retire a site-wide banner.
Account migration: a Super Admin can move a locked-out user's diagrams,
private catalog entries, and submissions to another account, with a
migration-history log; ProfileModal adds the self-service half (link a new
Google identity via Supabase manual linking, then unlink the old one,
while signed in as the account being migrated). Also reworks the top bar's
flat button row into grouped dropdown menus (Diagram / Admin / Account) now
that there are enough entries to need it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DUU6CnxECCDeqDNYJgr5x
2026-09-28 10:05:11 -05:00
2026-09-06 23:28:25 -05:00

AV Planner

Plan out AV/network installs: define devices and their ports, wire them together on a canvas, and get a bill of materials (devices + cables, with lengths) for what you'll need to buy.

Stack

  • React 19 + TypeScript + Vite
  • @xyflow/react (React Flow) for the device/wiring canvas
  • Zustand for app state
  • Tailwind CSS v4 for styling

Architecture

src/
  domain/       Framework-agnostic types + logic (compatibility rules, BOM math,
                the built-in connector/cable/device library). No React, no
                React Flow — safe to unit test or reuse from a future backend.
  data/         Storage abstraction. `DiagramRepository` is the interface the
                rest of the app codes against; `SupabaseDiagramRepository` is
                the active implementation, with `LocalStorageDiagramRepository`
                kept as a reference/fallback. Swapping storage means adding
                one new implementation of that interface, not touching
                state/UI code.
  state/        Zustand store (`diagramStore`) — the single source of truth for
                the current diagram. Auto-saves to the repository (debounced)
                on every change.
  components/
    canvas/     React Flow wiring surface: custom device node, custom cable
                edge, connection validation.
    palette/    Device library sidebar + the "new custom device" editor.
    inspector/  Right-panel editors for the selected device or cable.
    bom/        Bill-of-materials view.
    layout/     App shell, top bar, panels.

Data model

  • PortType — a connector kind (HDMI, XLR, Cat5e/6, ...). Two ports can be wired together only if their PortTypes share a compatibility family.
  • DeviceTemplate — a reusable device shape (its ports), shown in the palette. Built-ins live in domain/library.ts and seed the public catalog; custom ones are your own private catalog entries (or submitted for public review).
  • Device — an instance placed on the canvas, with its own copy of ports (editing an instance never mutates its template).
  • CableType — a physical cable spec, scoped to a PortType family.
  • Connection — a wire between two ports, referencing a CableType and an optional user-entered distance (there's no floor plan / scale model, so length is a manual estimate per connection).
  • Diagram — devices + connections. This is the one object persisted (auto-saved to Supabase, or explicitly exported/imported as JSON); the catalog (PortType/CableType/DeviceTemplate/category) is shared and global, not part of any one diagram.

Development

npm install
npm run dev      # start the dev server
npm run build    # typecheck + production build
npm run lint      # oxlint
S
Description
Visual AV planner
Readme
694 KiB
Languages
TypeScript 79.7%
PLpgSQL 16.3%
HTML 1.6%
JavaScript 1.3%
CSS 1.1%