Files
hackerspace.sh/docs/COMPONENT_REFERENCE.md
T

617 lines
13 KiB
Markdown

# Hackerspace.sh - Component Reference
> **Last Updated**: 2026-03-10
> Complete reference for all React components, their props, and usage patterns.
---
## Table of Contents
1. [App Shell Components](#1-app-shell-components)
2. [Page Components](#2-page-components)
3. [Client Components](#3-client-components)
4. [UI Components](#4-ui-components)
5. [Component Patterns](#5-component-patterns)
---
## 1. App Shell Components
### `AppSidebar`
**File**: `components/app-sidebar.tsx`
**Purpose**: Main navigation sidebar for the app.
**Props**:
```typescript
interface AppSidebarProps {
member: SpaceMember & { spaces: Space }
taskBadge?: number // Count for tasks badge
commsBadge?: number // Count for unread messages
paymentBadge?: number // Count for unlinked payments
}
```
**Usage**:
```tsx
<AppSidebar
member={member}
taskBadge={5}
paymentBadge={3}
/>
```
**Features**:
- Renders navigation grouped by section (Workspace, People, Admin)
- Shows badges for tasks and payments
- Displays user info with sign out
- Highlights active route
- Admin section only visible to admin/board roles
**Navigation Structure**:
```
WORKSPACE
├── Dashboard /dashboard
├── Tasks & Chores /tasks
├── Projects /projects
├── Ops & Facilities /ops
└── Comms /comms
PEOPLE
├── Members /members
├── Payments /payments
└── Contacts /contacts
ADMIN (admin/board only)
├── Import / Sync /import
└── Settings /settings
```
---
### `AppShell`
**File**: `components/app-shell.tsx`
**Purpose**: Wrapper component for app layout (currently unused).
**Status**: Available but not integrated into layout.
---
## 2. Page Components
### Page Structure Pattern
All feature pages follow a consistent Server Component + Client Component pattern:
```
feature/
├── page.tsx # Server Component (data fetching)
└── feature-client.tsx # Client Component (UI + interactions)
```
### Dashboard Page
**File**: `app/(app)/dashboard/page.tsx`
**Type**: Server Component
**Data Fetched**:
- Member stats (active members count)
- Task stats (open tasks count)
- Payment stats (unlinked payments count)
- Recent tasks (top 5)
- Active projects (in_progress, blocked, review)
- Recent activity (last 6 items)
**Features**:
- 4 stat cards with badges
- Quick chores list with claim buttons
- Active projects with progress bars
- Payment alerts section
- Recent activity feed
---
### Tasks Page
**Files**:
- `app/(app)/tasks/page.tsx` (Server)
- `app/(app)/tasks/tasks-client.tsx` (Client)
**Client Props**:
```typescript
interface Props {
tasks: Task[]
members: { id: string; display_name: string; user_id: string }[]
currentUserId: string
spaceId: string
}
```
**Features**:
- Tabbed view (Chores, Ongoing, My Tasks, Done)
- Area filter dropdown
- Create task modal
- Claim/Complete/Delete actions
- Status badges
**State Management**:
- Local state for tasks (optimistic updates)
- Form state for create modal
- Tab and filter state
---
### Projects Page
**Files**:
- `app/(app)/projects/page.tsx` (Server)
- `app/(app)/projects/projects-client.tsx` (Client)
**Client Props**:
```typescript
interface Props {
projects: Project[]
spaceId: string
}
```
**Features**:
- 4-column Kanban (Backlog, In Progress, Review, Done)
- Status change via dropdown
- Create project modal
- Delete action
- Progress bars
---
### Members Page
**Files**:
- `app/(app)/members/page.tsx` (Server)
- `app/(app)/members/members-client.tsx` (Client)
**Client Props**:
```typescript
interface Props {
members: Member[]
currentRole: string
}
```
**Features**:
- Tabbed view (All, Payment Issues, Pending Approval, Inactive)
- Search and tier filter
- Add member modal (admin/board only)
- Edit member modal
- Approve/Remove actions
- Payment status indicators
---
### Payments Page
**Files**:
- `app/(app)/payments/page.tsx` (Server)
- `app/(app)/payments/payments-client.tsx` (Client)
**Client Props**:
```typescript
interface Props {
payments: Payment[]
members: Member[]
integrations: Integration[]
currentRole: string
spaceId: string
}
```
**Features**:
- Platform summary cards (PayPal, Zeffy, Venmo, Cash)
- Transactions table with filtering
- Log cash modal
- Link member modal
- Platform connection status
---
### Comms Page
**Files**:
- `app/(app)/comms/page.tsx` (Server)
- `app/(app)/comms/comms-client.tsx` (Client)
**Client Props**:
```typescript
interface Props {
member: any
space: any
channels: any[]
}
```
**Features**:
- Channel sidebar (General, Areas, Projects)
- Real-time message subscription
- Message input with send
- User avatars and timestamps
- @mention and #channel highlighting
**Real-time**:
```typescript
const subscription = supabase
.channel(`channel:${selectedChannel.id}`)
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'comms_messages',
filter: `channel_id=eq.${selectedChannel.id}`
}, payload => {
if (payload.eventType === 'INSERT') {
setMessages(prev => [...prev, payload.new])
}
})
.subscribe()
```
---
### Contacts Page
**Files**:
- `app/(app)/contacts/page.tsx` (Server)
- `app/(app)/contacts/contacts-client.tsx` (Client)
**Features**:
- Search functionality
- Grouped by type (Vendors, Landlord, Partners)
- Add/Edit contact modals
- Delete with confirmation
- Contact type badges
---
### Ops Page
**File**: `app/(app)/ops/page.tsx` (Server Component)
**Features**:
- Tabbed view (Knowledge Base, Processes, Secrets, Area Leads)
- Search knowledge base
- Pinned/Critical entries section
- Area-based entries
- Area leads sidebar
- Secrets vault (admin/board only)
---
### Settings Page
**Files**:
- `app/(app)/settings/page.tsx` (Server)
- `app/(app)/settings/settings-client.tsx` (Client)
**Client Props**:
```typescript
interface Props {
space: Space
isAdmin: boolean
integrations: Integration[]
currentRole: string
}
```
**Features**:
- Space settings tab (name, slug, approval settings)
- Roles & permissions info
- Integrations management (PayPal, Zeffy, Venmo, Stripe)
- Webhook secret management
---
### Import Page
**File**: `app/(app)/import/page.tsx` (Server Component)
**Features**:
- Step indicator (Upload, Map, Preview, Import)
- File drop zone
- Column mapping preview
- Database connector form
**Status**: UI only, file processing not implemented.
---
## 3. Client Components
### TaskClaimButton
**File**: `components/task-claim-button.tsx`
**Props**:
```typescript
interface Props {
taskId: string
onClaim?: () => void
}
```
**Usage**:
```tsx
<TaskClaimButton taskId={task.id} onClaim={() => refresh()} />
```
---
### ThemeProvider
**File**: `components/theme-provider.tsx`
**Purpose**: Wraps app with next-themes provider.
**Usage** (in layout):
```tsx
<ThemeProvider attribute="class" defaultTheme="dark">
{children}
</ThemeProvider>
```
---
## 4. UI Components
All shadcn/ui components are available in `components/ui/`. Key components:
### Layout Components
- `Card`, `CardHeader`, `CardTitle`, `CardContent`, `CardFooter`
- `Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`
- `Sheet`, `SheetTrigger`, `SheetContent`
- `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`
- `Table`, `TableHeader`, `TableRow`, `TableCell`
### Form Components
- `Button` - Primary actions
- `Input` - Text input
- `Textarea` - Multi-line input
- `Select`, `SelectTrigger`, `SelectContent`, `SelectItem`
- `Checkbox` - Boolean input
- `Switch` - Toggle input
- `Label` - Form labels
### Feedback Components
- `Badge` - Status indicators
- `Alert`, `AlertTitle`, `AlertDescription`
- `Spinner` - Loading indicator
- `Skeleton` - Loading placeholder
- `Toast`, `Toaster` - Notifications
### Navigation Components
- `Breadcrumb`, `BreadcrumbItem`, `BreadcrumbLink`
- `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`
- `NavigationMenu`
---
## 5. Component Patterns
### Server/Client Split
```typescript
// page.tsx (Server Component)
export default async function FeaturePage() {
const supabase = await createClient()
const { data } = await supabase.from('table').select('*')
return <FeatureClient data={data ?? []} />
}
// feature-client.tsx (Client Component)
'use client'
export function FeatureClient({ data }: { data: Item[] }) {
const [items, setItems] = useState(data)
// Interactive UI
}
```
### Modal Pattern
```tsx
const [showModal, setShowModal] = useState(false)
const [form, setForm] = useState({ field: '' })
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setLoading(true)
setError('')
const result = await serverAction(form)
if (result.error) {
setError(result.error)
setLoading(false)
return
}
// Update local state
setShowModal(false)
setForm({ field: '' })
setLoading(false)
}
return (
<>
<button onClick={() => setShowModal(true)}>Open</button>
{showModal && (
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
<div className="bg-card border border-border rounded-lg">
<form onSubmit={handleSubmit}>
{/* Form fields */}
{error && <p className="text-red-500">{error}</p>}
<button disabled={loading}>
{loading ? 'Saving...' : 'Save'}
</button>
</form>
</div>
</div>
)}
</>
)
```
### Optimistic Update Pattern
```tsx
async function handleAction(id: string) {
// Optimistically update UI
setItems(prev => prev.map(item =>
item.id === id ? { ...item, status: 'updated' } : item
))
// Call server
const result = await serverAction(id)
if (result.error) {
// Revert on error
setItems(prev => prev.map(item =>
item.id === id ? { ...item, status: 'original' } : item
))
}
}
```
### Tab Pattern
```tsx
const [activeTab, setActiveTab] = useState<'tab1' | 'tab2'>('tab1')
const tabData = {
tab1: items.filter(i => i.type === 'type1'),
tab2: items.filter(i => i.type === 'type2'),
}
return (
<>
<div className="flex gap-4 border-b">
{(['tab1', 'tab2'] as const).map(tab => (
<button
key={tab}
onClick={() => setActiveTab(tab)}
className={activeTab === tab ? 'border-b-2 border-primary' : ''}
>
{tab} ({tabData[tab].length})
</button>
))}
</div>
<div>
{tabData[activeTab].map(item => (
<div key={item.id}>{/* Render item */}</div>
))}
</div>
</>
)
```
### Filter Pattern
```tsx
const [search, setSearch] = useState('')
const [filter, setFilter] = useState('')
const filtered = items.filter(item => {
const matchesSearch = !search ||
item.name.toLowerCase().includes(search.toLowerCase())
const matchesFilter = !filter || item.type === filter
return matchesSearch && matchesFilter
})
return (
<>
<input
type="text"
value={search}
onChange={e => setSearch(e.target.value)}
placeholder="Search..."
/>
<select value={filter} onChange={e => setFilter(e.target.value)}>
<option value="">All</option>
<option value="type1">Type 1</option>
</select>
{filtered.map(item => (
<div key={item.id}>{/* Render */}</div>
))}
</>
)
```
---
## 6. Styling Patterns
### Page Header
```tsx
<div className="bg-sidebar px-6 py-3 flex items-center justify-between">
<h1 className="text-white font-sans text-lg font-semibold">Page Title</h1>
<button className="flex items-center gap-1.5 bg-primary text-white text-xs font-sans px-3 py-1.5 rounded">
<PlusIcon className="w-3.5 h-3.5" />
Action
</button>
</div>
```
### Tab Bar
```tsx
<div className="bg-card border-b border-border px-6 flex gap-6">
{tabs.map(tab => (
<button
key={tab}
className={`font-sans text-sm py-3 border-b-2 transition ${
activeTab === tab
? 'border-primary text-primary'
: 'border-transparent text-muted-foreground hover:text-foreground'
}`}
>
{tab}
</button>
))}
</div>
```
### Card List
```tsx
<div className="bg-card rounded border border-border divide-y divide-border">
{items.map(item => (
<div key={item.id} className="flex items-center gap-3 px-4 py-3">
{/* Item content */}
</div>
))}
</div>
```
### Status Badge
```tsx
<span className={`font-mono text-[10px] px-2 py-0.5 rounded ${
status === 'active' ? 'text-primary bg-primary/10' :
status === 'warning' ? 'text-orange-600 bg-orange-50' :
'text-muted-foreground bg-muted'
}`}>
{status.toUpperCase()}
</span>
```
### Monospace Label
```tsx
<p className="font-mono text-[10px] tracking-widest text-muted-foreground uppercase">
SECTION LABEL
</p>
```