import { Check, ChevronDown, ChevronsUpDown, ChevronUp, ExternalLink, Minus } from 'lucide-react' import Link from 'next/link' import { useState } from 'react' import { Badge, cn, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui' import type { FeatureType } from '@/data/features' import { PRODUCT_STAGES } from '@/data/features' type SortColumn = 'title' | 'stage' | 'selfHosted' type SortDirection = 'asc' | 'desc' const STAGE_ORDER: Record = { [PRODUCT_STAGES.PRIVATE_ALPHA]: 0, [PRODUCT_STAGES.PUBLIC_ALPHA]: 1, [PRODUCT_STAGES.BETA]: 2, [PRODUCT_STAGES.PUBLIC_BETA]: 3, [PRODUCT_STAGES.GA]: 4, } export function stageBadgeVariant( stage: PRODUCT_STAGES ): 'default' | 'warning' | 'success' | 'destructive' { switch (stage) { case PRODUCT_STAGES.GA: return 'success' case PRODUCT_STAGES.PUBLIC_BETA: case PRODUCT_STAGES.BETA: return 'warning' case PRODUCT_STAGES.PRIVATE_ALPHA: return 'destructive' default: return 'default' } } export function stageLabel(stage: PRODUCT_STAGES): string { switch (stage) { case PRODUCT_STAGES.GA: return 'GA' case PRODUCT_STAGES.PUBLIC_BETA: return 'Public Beta' case PRODUCT_STAGES.BETA: return 'Beta' case PRODUCT_STAGES.PUBLIC_ALPHA: return 'Public Alpha' case PRODUCT_STAGES.PRIVATE_ALPHA: return 'Private Alpha' } } export function productLabel(product: string): string { const labels: Record = { database: 'Database', authentication: 'Auth', storage: 'Storage', functions: 'Functions', realtime: 'Realtime', cron: 'Cron', queues: 'Queues', vector: 'Vector', platform: 'Platform', studio: 'Studio', } return labels[product] ?? product } function SortIcon({ column, sortColumn, sortDirection, }: { column: SortColumn sortColumn: SortColumn sortDirection: SortDirection }) { if (sortColumn !== column) return return sortDirection === 'asc' ? : } interface FeaturesMatrixProps { features: FeatureType[] } export function FeaturesMatrix({ features }: FeaturesMatrixProps) { const [sortColumn, setSortColumn] = useState('title') const [sortDirection, setSortDirection] = useState('asc') function handleSort(col: SortColumn) { if (sortColumn === col) { setSortDirection((d) => (d === 'asc' ? 'desc' : 'asc')) } else { setSortColumn(col) setSortDirection('asc') } } const sorted = [...features].sort((a, b) => { const dir = sortDirection === 'asc' ? 1 : -1 if (sortColumn === 'title') { return a.title.localeCompare(b.title) * dir } if (sortColumn === 'stage') { const stageA = STAGE_ORDER[a.status?.stage ?? PRODUCT_STAGES.GA] const stageB = STAGE_ORDER[b.status?.stage ?? PRODUCT_STAGES.GA] return (stageA - stageB) * dir } if (sortColumn === 'selfHosted') { const valA = a.status?.availableOnSelfHosted ? 1 : 0 const valB = b.status?.availableOnSelfHosted ? 1 : 0 return (valA - valB) * dir } return 0 }) const thClass = 'px-3 py-2.5 text-left text-xs font-medium text-foreground-lighter uppercase tracking-wider whitespace-nowrap' const sortableThClass = cn(thClass, 'cursor-pointer select-none') return (
handleSort('title')} > Feature Products handleSort('stage')}> Stage handleSort('selfHosted')}> Self-Hosted {sorted.map((feature, i) => { const selfHosted = feature.status?.availableOnSelfHosted const tooling = feature.status?.selfHostedTooling return (
{feature.title} {feature.subtitle}
{feature.products.map((product) => ( {productLabel(product)} ))}
{feature.status ? ( {stageLabel(feature.status.stage)} ) : ( — )} {selfHosted ? ( ) : ( )}
) })}
{sorted.length === 0 && (
No features found with these filters
)}
) }