Skip to main content

Facilities Manager

Facilities Manager

Last Updated: April 15, 2026 Status: Phase 1 (MVP)

Overview

Manage church facilities including room scheduling, interactive floor plans, and work order tracking. This tool provides a comprehensive solution for facility management with visual floor plan overlays, calendar-based room booking, and maintenance request workflows.

File: src/features/facilities/components/FacilitiesManager.tsx DB Module: src/lib/db/facilities.ts Route / navigation: Path /tools/facilities-manager, Zustand toolsTool = facilities-manager. See 00-TOOLS-HUB.md. Desktop only: The tool renders a DesktopOnlyWarning on viewports narrower than md — editing hotspots, dragging work orders, and the floor-plan canvas all require a larger screen.

Shell layout (Styling Guide compliant)

  • BreadcrumbPageHeader (centered, default text-foreground) → 4-up stat cards with stagger-fade-in-blur → shared PillTabs (Rooms / Floor Plan / Work Orders).
  • Stat tiles use STAT_AMOUNT from @/lib/ui-constants and the useCountUp hook, per §29 of the Styling Guide.
  • Internal tabs are local state only — they do not participate in toolsTool URL sync for Phase 1.

Features

Room Scheduling (rooms tab)

  • Space Catalog — CRUD for spaces with name, building, floor, capacity, amenities, and active flag (read-only users see details but no Save/Delete buttons).
  • Calendar ViewResponsiveCalendar with status-colored events (pending=warning, approved=success, rejected=destructive, cancelled=muted).
  • Reservation Sheet — Create/edit reservations in a Sheet drawer with datetime inputs, notes, and a Director-gated status selector.
  • Status & Space Filters — Filter by reservation status and/or by space.
  • Conflict Prevention — Enforced at the DB via a GIST exclusion constraint on overlapping pending/approved rows.

Floor Plan Viewer (floor-plan tab)

  • Upload pipeline — Files are compressed to 2048px max width at 0.85 quality via compressImage and stored in the public `facility-media` bucket under {organization_id}/floor-plans/{timestamp}_{rand}.jpg. See STORAGE bucket migration.
  • Interactive Maps — Pan/zoom via react-zoom-pan-pinch with reset, zoom-in, and zoom-out controls pinned top-right.
  • SVG Hotspots — Click-to-interact room polygons overlaid on the floor-plan image. Polygons use percentage (0-100) coordinates in a viewBox="0 0 100 100" SVG, so hotspots scale with the image.
  • Space-status coloring — Active spaces render in --success tones; inactive/unknown fall back to muted tokens.
  • Hotspot Editor — Directors + canWrite only. Clicking the plan appends points; ≥3 points are required before assigning the shape to a space. Polygons are saved in one batch via saveFloorPlanHotspots.

Work Orders (work-orders tab)

  • Kanban View (default) — Four columns (Open, In Progress, On Hold, Completed) with drag-and-drop via @dnd-kit/core. Dropping a card onto another column mutates status through updateWorkOrder.
  • List View — Table with status/priority/due columns. Row actions (Edit, Delete) appear only for users with canWrite.
  • Priority & Status Badges — Tailwind semantic tokens (bg-destructive, bg-warning/10, bg-primary/10, bg-muted).
  • Category / Priority / Space / Status filters plus client-side search by title/description.
  • Comments Thread — Each work order drawer includes an inline comments ScrollArea with an Add-Comment input.
  • Current limits (Phase 1):
  • The list view fetches pageSize: 500 from get_facility_work_orders_page and does not yet render visible pagination controls.
  • There is no assignee picker UI yet — the Sheet has no member-directory selector, so assigned_to is preserved from the existing record on edit but cannot be set/changed from the UI. Assignee assignment is a Phase 2 follow-up.

Database

facility_spaces table

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| organization_id | UUID | FK → organizations |
| name | TEXT | Required |
| building | TEXT | Optional |
| floor | TEXT | Optional |
| capacity | INTEGER | Optional |
| amenities | JSONB | Array of strings |
| hourly_rate | NUMERIC(10,2) | Default 0 |
| daily_rate | NUMERIC(10,2) | Default 0 |
| photos | TEXT[] | Array of URLs |
| is_active | BOOLEAN | Default true |
| created_at | TIMESTAMPTZ | Auto |
| updated_at | TIMESTAMPTZ | Auto |

facility_floor_plans table

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| organization_id | UUID | FK → organizations |
| name | TEXT | Required |
| image_url | TEXT | Public URL to uploaded image |
| building | TEXT | Optional |
| floor | TEXT | Optional |
| created_at | TIMESTAMPTZ | Auto |

facility_floor_plan_hotspots table

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| floor_plan_id | UUID | FK → facility_floor_plans (cascade) |
| space_id | UUID | FK → facility_spaces (cascade) |
| coordinates | JSONB | Array of {x, y} points (percentages 0-100) |
| created_at | TIMESTAMPTZ | Auto |

facility_reservations table

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| organization_id | UUID | FK → organizations |
| space_id | UUID | FK → facility_spaces |
| event_id | UUID | FK → events (optional) |
| title | TEXT | Required |
| requested_by | UUID | FK → auth.users |
| approved_by | UUID | FK → auth.users (optional) |
| status | TEXT | pending, approved, rejected, cancelled |
| start_at | TIMESTAMPTZ | Required |
| end_at | TIMESTAMPTZ | Required, must be after start_at |
| recurrence_rule | TEXT | Optional RRULE string |
| notes | TEXT | Optional |
| rental_fee | NUMERIC(10,2) | Default 0 |
| created_at | TIMESTAMPTZ | Auto |
| updated_at | TIMESTAMPTZ | Auto |

Constraint: Exclusion constraint prevents overlapping pending/approved reservations for the same space.

facility_work_orders table

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| organization_id | UUID | FK → organizations |
| space_id | UUID | FK → facility_spaces (optional) |
| title | TEXT | Required |
| description | TEXT | Optional |
| category | TEXT | plumbing, electrical, hvac, custodial, grounds, technology, other |
| priority | TEXT | urgent, high, medium, low |
| status | TEXT | open, in_progress, on_hold, completed |
| reported_by | UUID | FK → auth.users |
| assigned_to | UUID | FK → auth.users (optional) |
| due_date | DATE | Optional |
| completed_at | TIMESTAMPTZ | Auto-set when status becomes completed |
| photos | TEXT[] | Array of URLs |
| cost | NUMERIC(10,2) | Optional |
| fund_id | UUID | Optional FK for expense tracking |
| created_at | TIMESTAMPTZ | Auto |
| updated_at | TIMESTAMPTZ | Auto |

facility_work_order_comments table

| Column | Type | Notes |
|--------|------|-------|
| id | UUID | PK |
| work_order_id | UUID | FK → facility_work_orders (cascade) |
| user_id | UUID | FK → auth.users |
| content | TEXT | Required |
| created_at | TIMESTAMPTZ | Auto |

Permissions

The UI uses two gates:

  • canWrite (from usePermissions()) controls whether users can access mutating UI actions.
  • isDirector adds an extra gate for reservation status changes and floor-plan editor/upload controls.
| Action | Director + canWrite | Non-director + canWrite | Read-only |
|--------|----------------------|--------------------------|-----------|
| View all tabs | ✓ | ✓ | ✓ |
| Create/edit/delete spaces | ✓ | ✓ | ✗ |
| Create/edit/delete reservations | ✓ | ✓ | ✗ |
| Change reservation `status` (approve/reject) | ✓ | ✗ | ✗ |
| Upload/delete floor plans | ✓ | ✗ | ✗ |
| Draw / save / delete hotspots | ✓ | ✗ | ✗ |
| Create/edit/delete work orders | ✓ | ✓ | ✗ |
| Drag work orders between columns | ✓ | ✓ | ✗ |
| Open/post work-order comments in the edit drawer | ✓ | ✓ | ✗ |

RLS on the underlying tables is the source of truth; the UI gates above prevent users from seeing dead-end controls but do not weaken the database rules.

Tier Requirements

  • Minimum Tier: Plus
  • Available for all org types (churches, nonprofits)

Related Files

  • src/features/facilities/components/rooms/RoomScheduler.tsx — Room scheduling calendar
  • src/features/facilities/components/floor-plan/FloorPlanViewer.tsx — Interactive floor plan
  • src/features/facilities/components/work-orders/WorkOrderManager.tsx — Work order kanban/list
  • src/lib/db/facilities.ts — Typed Supabase accessors
  • src/i18n/locales/en/tools.jsonfacilities.* namespace. A top-level common block inside the tools namespace supplies the shared Cancel/Save/Create/Edit/Delete/Upload labels (t('common.cancel') etc. resolves inside the tools namespace).
  • supabase/migrations/20260415120000_create_facilities_tables.sql — Database schema (tables, RLS, exclusion constraint)
  • supabase/migrations/20260415130000_create_facility_media_bucket.sqlfacility-media storage bucket + RLS (public read, org-scoped write, 10 MB limit, image/jpeg|png|webp only)

Dependencies

  • react-zoom-pan-pinch — Pan/zoom for floor plan images
  • @dnd-kit/core — Drag-and-drop for work order kanban (already installed)
  • ResponsiveCalendar — Shared calendar component
  • @/lib/imageUtilscompressImage(file, maxWidth, quality) — used to downsize uploads before Supabase Storage

Future Enhancements (Phase 2+)

  • Member-directory assignee picker for work orders
  • Paginated list view with visible page controls
  • Public booking page with Stripe payment
  • Preventive maintenance auto-scheduling
  • Asset tracking and key management
  • Facility inspections checklist
  • Heat map utilization overlays
  • HVAC/smart lock integrations

Synced from IFMmvp-Frontend documentation: pages/tools/16-FACILITIES-MANAGER.md

Ready to Get Started?

See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.

Questions? Email us at steven@getalignmint.org

Ready to get started?Start Plus Trial