From a7cfb94674c8bf9aa7cc02f3efbda69ac4f26f0e Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Sun, 15 Feb 2026 18:28:49 -0400 Subject: [PATCH] detaisls --- packages/supa-mock/index.tsx | 2 +- packages/supa-mock/src/SupaMock.tsx | 2 + packages/supa-mock/src/layout/MockHeader.tsx | 44 +- .../src/layout/MockHeaderDropdowns.tsx | 198 ++++++++ .../src/providers/MockProjectContext.tsx | 10 +- .../supa-mock/src/providers/MockProviders.tsx | 4 + packages/supa-mock/src/screens/AuthScreen.tsx | 78 ++- packages/supa-mock/src/screens/HomeScreen.tsx | 448 ++++++++++++++---- .../supa-mock/src/screens/StorageScreen.tsx | 152 +++++- .../src/screens/TableEditorScreen.tsx | 248 +++++----- packages/supa-mock/src/types.ts | 7 + 11 files changed, 873 insertions(+), 320 deletions(-) create mode 100644 packages/supa-mock/src/layout/MockHeaderDropdowns.tsx diff --git a/packages/supa-mock/index.tsx b/packages/supa-mock/index.tsx index e8e7fb9cc2c..656ce35850e 100644 --- a/packages/supa-mock/index.tsx +++ b/packages/supa-mock/index.tsx @@ -1,2 +1,2 @@ export { SupaMock } from './src/SupaMock' -export type { SupaMockProps } from './src/types' +export type { SupaMockProps, SupaMockOrganization } from './src/types' diff --git a/packages/supa-mock/src/SupaMock.tsx b/packages/supa-mock/src/SupaMock.tsx index a30825627b8..f72f9bb62d0 100644 --- a/packages/supa-mock/src/SupaMock.tsx +++ b/packages/supa-mock/src/SupaMock.tsx @@ -11,6 +11,7 @@ export function SupaMock({ organizationName, organizationPlan, branchName, + organizations, }: SupaMockProps) { return (
diff --git a/packages/supa-mock/src/layout/MockHeader.tsx b/packages/supa-mock/src/layout/MockHeader.tsx index 7ccc3cea5a9..8ea56ad3acb 100644 --- a/packages/supa-mock/src/layout/MockHeader.tsx +++ b/packages/supa-mock/src/layout/MockHeader.tsx @@ -1,6 +1,8 @@ -import { Badge, Button, cn } from 'ui' -import { Boxes, Box, ChevronsUpDown, Plug } from 'lucide-react' +import { Plug } from 'lucide-react' +import { Button, cn } from 'ui' + import { useMockProject } from '../providers/MockProjectContext' +import { BranchDropdown, OrgDropdown, ProjectDropdown } from './MockHeaderDropdowns' const LayoutHeaderDivider = ({ className }: { className?: string }) => ( @@ -21,7 +23,7 @@ const LayoutHeaderDivider = ({ className }: { className?: string }) => ( ) export function MockHeader() { - const { project } = useMockProject() + const { project, organizations } = useMockProject() return (
@@ -77,44 +79,14 @@ export function MockHeader() {
- {/* Organization */} -
- - {project.organization.name} - {project.organization.plan} -
- + + + + + + + {hasOrgs ? ( + organizations.map((org) => { + const isCurrent = + org.slug === project.organization.slug || + org.name === project.organization.name + return ( + { + window.open( + `https://supabase.com/dashboard/org/${org.slug}`, + '_blank', + 'noopener,noreferrer' + ) + setOpen(false) + }} + className="flex items-center justify-between gap-2 cursor-pointer" + > + {org.name} + {isCurrent && ( + + )} + + ) + }) + ) : ( + + {project.organization.name} + + + )} + + + + + All Organizations + + + + + + + New organization + + + + + + + ) +} + +// ─── Project Dropdown ─────────────────────────────────────────────────── + +export function ProjectDropdown({ project }: { project: MockProject }) { + const [open, setOpen] = useState(false) + + return ( + + + + + + + + + + + {project.name} + + + + + + + + New project + + + + + + + ) +} + +// ─── Branch Dropdown ──────────────────────────────────────────────────── + +export function BranchDropdown({ project }: { project: MockProject }) { + const [open, setOpen] = useState(false) + + return ( + + + + + + + + + +
+ + {project.branchName} +
+ +
+
+ + + + + Create branch + + + + Manage branches + + + + + + +
+ Branching feedback + Join GitHub Discussion +
+
+
+
+
+
+
+ ) +} diff --git a/packages/supa-mock/src/providers/MockProjectContext.tsx b/packages/supa-mock/src/providers/MockProjectContext.tsx index 153a0cad201..dd132a17a18 100644 --- a/packages/supa-mock/src/providers/MockProjectContext.tsx +++ b/packages/supa-mock/src/providers/MockProjectContext.tsx @@ -1,6 +1,6 @@ import { createContext, useContext, useMemo, type ReactNode } from 'react' -import type { MockProject, MockProjectContextType } from '../types' +import type { MockProject, MockProjectContextType, SupaMockOrganization } from '../types' const MockProjectContext = createContext(null) @@ -17,6 +17,7 @@ interface MockProjectProviderProps { organizationName?: string organizationPlan?: string branchName?: string + organizations?: SupaMockOrganization[] children: ReactNode } @@ -38,6 +39,7 @@ export function MockProjectProvider({ organizationName, organizationPlan, branchName, + organizations = [], children, }: MockProjectProviderProps) { const project = useMemo( @@ -64,5 +66,9 @@ export function MockProjectProvider({ [projectName, organizationName, organizationPlan, branchName] ) - return {children} + return ( + + {children} + + ) } diff --git a/packages/supa-mock/src/providers/MockProviders.tsx b/packages/supa-mock/src/providers/MockProviders.tsx index 1f7008308ee..5511ecb27e8 100644 --- a/packages/supa-mock/src/providers/MockProviders.tsx +++ b/packages/supa-mock/src/providers/MockProviders.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from 'react' import { MockRouterProvider } from '../router/MockRouterContext' +import type { SupaMockOrganization } from '../types' import { MockProjectProvider } from './MockProjectContext' import { MockUserProvider } from './MockUserContext' @@ -10,6 +11,7 @@ interface MockProvidersProps { organizationName?: string organizationPlan?: string branchName?: string + organizations?: SupaMockOrganization[] children: ReactNode } @@ -19,6 +21,7 @@ export function MockProviders({ organizationName, organizationPlan, branchName, + organizations, children, }: MockProvidersProps) { return ( @@ -28,6 +31,7 @@ export function MockProviders({ organizationName={organizationName} organizationPlan={organizationPlan} branchName={branchName} + organizations={organizations} > {children} diff --git a/packages/supa-mock/src/screens/AuthScreen.tsx b/packages/supa-mock/src/screens/AuthScreen.tsx index 1e3a6286e04..09c3727b46e 100644 --- a/packages/supa-mock/src/screens/AuthScreen.tsx +++ b/packages/supa-mock/src/screens/AuthScreen.tsx @@ -97,58 +97,36 @@ interface MockAuthUser { } const MOCK_NAMES: (string | null)[] = [ - null, - null, - null, - null, - 'Afonso Resendes', - null, - 'Aisulu Kalshabekova', - null, - 'Alan Daniel', - null, - null, - null, - null, - null, - 'Alyssa Gerasimoff', - null, - null, - null, - null, - 'asd', - null, - null, - null, - 'Ben Miller', + 'Alice Johnson', + 'Bob Smith', + 'Carol Williams', + 'Dave Brown', + 'Eve Davis', + 'Frank Wilson', + 'Grace Lee', + 'Henry Taylor', + 'Ivy Chen', + 'Jack Martin', + 'Karen White', + 'Leo Garcia', + 'Mia Robinson', + 'Noah Clark', + 'Olivia Hall', + 'Paul Adams', + 'Quinn Foster', + 'Ruby Scott', + 'Sam Turner', + 'Tina Brooks', + 'Uma Patel', + 'Victor Hayes', + 'Wendy Ross', + 'Xander Cole', ] -const MOCK_EMAILS = [ - 'a@thesimple.co', - 'a34mail2@gmail.com', - 'abdulrahimmirani@gmail.com', - 'abhi.jain@sjsu.edu', - 'adaaanniel@gmail.com', - 'adambowker98@gmail.com', - 'admin@admin.com', - 'afischbeck@gmx.com', - 'afonsoresendes03@gmail.com', - 'agrancini@snapchat.com', - 'aisulu@icecore.ai', - 'alaister@supabase.io', - 'alan.daniel@supabase.io', - 'alejandra@domu.ai', - 'alek1122t@gmail.com', - 'alhwyn@alhwyn.com', - 'aliciaww@gmail.com', - 'alroma79@gmail.com', - 'alvinapink@virgilian.com', - 'alyssag022@gmail.com', - 'amicus_nays.5y@icloud.com', - 'ananya.mac98@gmail.com', - 'anasjapananime7711@gmail.com', - 'ben.miller@example.com', -] +const MOCK_EMAILS = MOCK_NAMES.map((name) => { + const [first, last] = name!.toLowerCase().split(' ') + return `${first}.${last}@example.com` +}) const MOCK_USERS: MockAuthUser[] = Array.from({ length: 24 }, (_, i) => ({ id: mockUUID(i + 7700), diff --git a/packages/supa-mock/src/screens/HomeScreen.tsx b/packages/supa-mock/src/screens/HomeScreen.tsx index 0aac5de5454..8d039e2ba52 100644 --- a/packages/supa-mock/src/screens/HomeScreen.tsx +++ b/packages/supa-mock/src/screens/HomeScreen.tsx @@ -1,5 +1,6 @@ import { Auth, Database, Realtime, Storage } from 'icons' -import { Activity, ExternalLink, Shield } from 'lucide-react' +import { ChevronLeft, ExternalLink, Shield, X } from 'lucide-react' +import { useMemo, useState } from 'react' import { Badge, Button, @@ -23,28 +24,116 @@ import { useMockProject } from '../providers/MockProjectContext' // ─── Mock Data ────────────────────────────────────────────────────────── +function seededRandom(seed: number): number { + const x = Math.sin(seed + 1) * 10000 + return x - Math.floor(x) +} + const MOCK_STATS = { tables: 20, functions: 15, replicas: 0 } -const MOCK_USAGE_DATA = Array.from({ length: 24 }, (_, i) => ({ - hour: `${i}:00`, - rest: Math.floor(Math.random() * 200 + 50), - auth: Math.floor(Math.random() * 80 + 10), - storage: Math.floor(Math.random() * 60 + 5), - realtime: Math.floor(Math.random() * 40 + 5), -})) +type TimeRange = '60min' | '24h' | '7d' -const MOCK_SECURITY_LINTS = [ - 'Function `public.sudo` has a role mutable search_path', - 'Function `public.sudo` has a role mutable search_path', - 'Function `public.get_storage_base_path` has a role mutable search_path', - 'Function `public.euro_worker` has a role mutable search_path', - 'Function `public.get_secret` has a role mutable search_path', - 'Function `public.add_cron_job` has a role mutable search_path', +const TIME_RANGE_OPTIONS: { key: TimeRange; label: string; description: string }[] = [ + { key: '60min', label: 'Last 60 minutes', description: 'Statistics for last 60 minutes' }, + { key: '24h', label: 'Last 24 hours', description: 'Statistics for last 24 hours' }, + { key: '7d', label: 'Last 7 days', description: 'Statistics for last 7 days' }, +] + +function generateUsageData(range: TimeRange) { + const offset = range === '60min' ? 100 : range === '7d' ? 200 : 0 + const count = range === '60min' ? 12 : range === '7d' ? 7 : 24 + const restScale = range === '60min' ? 30 : range === '7d' ? 1400 : 200 + const restBase = range === '60min' ? 5 : range === '7d' ? 300 : 50 + const authScale = range === '60min' ? 10 : range === '7d' ? 500 : 80 + const authBase = range === '60min' ? 1 : range === '7d' ? 60 : 10 + const storageScale = range === '60min' ? 8 : range === '7d' ? 400 : 60 + const storageBase = range === '60min' ? 0 : range === '7d' ? 30 : 5 + const realtimeScale = range === '60min' ? 6 : range === '7d' ? 300 : 40 + const realtimeBase = range === '60min' ? 1 : range === '7d' ? 20 : 5 + + return Array.from({ length: count }, (_, i) => ({ + rest: Math.floor(seededRandom((i + offset) * 4) * restScale + restBase), + auth: Math.floor(seededRandom((i + offset) * 4 + 1) * authScale + authBase), + storage: Math.floor(seededRandom((i + offset) * 4 + 2) * storageScale + storageBase), + realtime: Math.floor(seededRandom((i + offset) * 4 + 3) * realtimeScale + realtimeBase), + })) +} + +interface SecurityLint { + summary: string + title: string + severity: 'warning' | 'error' + entity: string + issue: string + description: string +} + +const MOCK_SECURITY_LINTS: SecurityLint[] = [ + { + summary: 'Table `public.products` has a row level security policy `Allow read...', + title: 'Auth RLS Initialization Plan', + severity: 'warning', + entity: 'public.products', + issue: + 'Table `public.products` has a row level security policy `Allow read access` that re-evaluates `current_setting()` or `auth.()` for each row. This produces suboptimal query performance at scale. Resolve the issue by replacing `auth.()` with `(select auth.())`.', + description: + 'Detects if calls to `current_setting()` and `auth.()` in RLS policies are being unnecessarily re-evaluated for each row.', + }, + { + summary: 'Table `public.products` has a row level security policy `Allow insert...', + title: 'Auth RLS Initialization Plan', + severity: 'warning', + entity: 'public.products', + issue: + 'Table `public.products` has a row level security policy `Allow insert` that re-evaluates `current_setting()` or `auth.()` for each row. This produces suboptimal query performance at scale.', + description: + 'Detects if calls to `current_setting()` and `auth.()` in RLS policies are being unnecessarily re-evaluated for each row.', + }, + { + summary: 'Table `public.orders` has a row level security policy `Allow auth...', + title: 'Auth RLS Initialization Plan', + severity: 'warning', + entity: 'public.orders', + issue: + 'Table `public.orders` has a row level security policy `Allow auth users` that re-evaluates `current_setting()` or `auth.()` for each row.', + description: + 'Detects if calls to `current_setting()` and `auth.()` in RLS policies are being unnecessarily re-evaluated for each row.', + }, + { + summary: 'Table `public.orders` has a row level security policy `Allow user...', + title: 'Auth RLS Initialization Plan', + severity: 'warning', + entity: 'public.orders', + issue: + 'Table `public.orders` has a row level security policy `Allow user access` that re-evaluates `current_setting()` or `auth.()` for each row.', + description: + 'Detects if calls to `current_setting()` and `auth.()` in RLS policies are being unnecessarily re-evaluated for each row.', + }, + { + summary: 'Function `public.get_config` has a role mutable search_path', + title: 'Function Search Path Mutable', + severity: 'warning', + entity: 'public.get_config', + issue: + 'Function `public.get_config` has a role mutable `search_path`, meaning the function could potentially be hijacked by a malicious user.', + description: + 'Detects functions where the search_path is mutable, which could allow search path injection attacks.', + }, + { + summary: 'Function `public.run_task` has a role mutable search_path', + title: 'Function Search Path Mutable', + severity: 'warning', + entity: 'public.run_task', + issue: + 'Function `public.run_task` has a role mutable `search_path`, meaning the function could potentially be hijacked by a malicious user.', + description: + 'Detects functions where the search_path is mutable, which could allow search path injection attacks.', + }, ] const MOCK_SLOW_QUERIES = [ - { query: 'select edge.generate_embedding($1) limit $2', avgTime: '2.15s', calls: 8 }, - { query: 'select public.euro_worker( jsonb_build_object($1,$2))', avgTime: '4.10s', calls: 12 }, + { query: 'select public.compute_aggregates($1) limit $2', avgTime: '2.15s', calls: 8 }, + { query: 'select public.sync_records(jsonb_build_object($1,$2))', avgTime: '4.10s', calls: 12 }, { query: 'SELECT name FROM pg_timezone_names', avgTime: '0.47s', calls: 192 }, { query: 'with table_info as ( select n.nspname::text as schem...', avgTime: '5.37s', calls: 1 }, { query: 'with table_info as ( select n.nspname::text as schem...', avgTime: '5.26s', calls: 1 }, @@ -52,21 +141,36 @@ const MOCK_SLOW_QUERIES = [ // ─── Mini Bar Chart (mocked) ──────────────────────────────────────────── -function MiniBarChart({ data, color = '#3ECF8E' }: { data: number[]; color?: string }) { +function MiniBarChart({ + data, + color = '#3ECF8E', + animationKey, +}: { + data: number[] + color?: string + animationKey?: string +}) { const max = Math.max(...data, 1) return ( -
+
{data.map((val, i) => (
))} +
) } @@ -79,12 +183,14 @@ function UsagePanel({ metricLabel, data, total, + animationKey, }: { icon: React.ReactNode title: string metricLabel: string data: number[] total: number + animationKey?: string }) { return (
@@ -97,7 +203,7 @@ function UsagePanel({

{metricLabel}

{total.toLocaleString()}

- +
) @@ -105,7 +211,89 @@ function UsagePanel({ // ─── Advisor Widget ───────────────────────────────────────────────────── -function MockAdvisorWidget() { +function InlineCode({ children }: { children: string }) { + return {children} +} + +function RenderWithCode({ text }: { text: string }) { + return ( + <> + {text + .split('`') + .map((part, i) => + i % 2 === 1 ? {part} : {part} + )} + + ) +} + +function LintDetailPanel({ lint, onClose }: { lint: SecurityLint; onClose: () => void }) { + return ( +
+
+ + {lint.title} + + {lint.severity} + + +
+
+
+

Entity

+
+ + {lint.entity} +
+
+
+

Issue

+

+ + {'. '} + See {' '} + for more info. +

+
+
+

Description

+

+ +

+
+
+

Resolve

+
+ + + +
+
+
+
+ ) +} + +function MockAdvisorWidget({ onSelectLint }: { onSelectLint: (lint: SecurityLint) => void }) { const totalIssues = MOCK_SECURITY_LINTS.length return ( @@ -141,14 +329,20 @@ function MockAdvisorWidget() {
    {MOCK_SECURITY_LINTS.map((lint, i) => ( -
  • +
  • onSelectLint(lint)} + >
    -

    {lint}

    +

    + {lint.summary} +

  • ))} @@ -206,100 +400,144 @@ function MockAdvisorWidget() { export function HomeScreen() { const { project } = useMockProject() + const [timeRange, setTimeRange] = useState('24h') + const [rangeDropdownOpen, setRangeDropdownOpen] = useState(false) + const [selectedLint, setSelectedLint] = useState(null) - const restTotal = MOCK_USAGE_DATA.reduce((sum, d) => sum + d.rest, 0) - const authTotal = MOCK_USAGE_DATA.reduce((sum, d) => sum + d.auth, 0) - const storageTotal = MOCK_USAGE_DATA.reduce((sum, d) => sum + d.storage, 0) - const realtimeTotal = MOCK_USAGE_DATA.reduce((sum, d) => sum + d.realtime, 0) + const usageData = useMemo(() => generateUsageData(timeRange), [timeRange]) + const selectedOption = TIME_RANGE_OPTIONS.find((o) => o.key === timeRange)! + + const restTotal = usageData.reduce((sum, d) => sum + d.rest, 0) + const authTotal = usageData.reduce((sum, d) => sum + d.auth, 0) + const storageTotal = usageData.reduce((sum, d) => sum + d.storage, 0) + const realtimeTotal = usageData.reduce((sum, d) => sum + d.realtime, 0) return ( -
    - {/* Section 1: Header */} -
    -
    -
    -
    -

    {project.name}

    -
    - Nano +
    +
    + {/* Section 1: Header */} +
    +
    +
    +
    +

    {project.name}

    +
    + Nano +
    -
    -
    -
    - Tables -

    {MOCK_STATS.tables}

    -
    -
    - Functions -

    {MOCK_STATS.functions}

    -
    -
    - Replicas -

    {MOCK_STATS.replicas}

    -
    -
    - +
    +
    + Tables +

    {MOCK_STATS.tables}

    +
    +
    + Functions +

    {MOCK_STATS.functions}

    +
    +
    + Replicas +

    {MOCK_STATS.replicas}

    +
    +
    + +
    -
    - {/* Section 2: Usage */} -
    -
    -
    -
    - - Statistics for last 24 hours -
    -
    - } - title="Database" - metricLabel="REST Requests" - data={MOCK_USAGE_DATA.map((d) => d.rest)} - total={restTotal} - /> - } - title="Auth" - metricLabel="Auth Requests" - data={MOCK_USAGE_DATA.map((d) => d.auth)} - total={authTotal} - /> - } - title="Storage" - metricLabel="Storage Requests" - data={MOCK_USAGE_DATA.map((d) => d.storage)} - total={storageTotal} - /> - } - title="Realtime" - metricLabel="Realtime Connections" - data={MOCK_USAGE_DATA.map((d) => d.realtime)} - total={realtimeTotal} - /> + {/* Section 2: Usage */} +
    +
    +
    +
    +
    + + {rangeDropdownOpen && ( +
    + {TIME_RANGE_OPTIONS.map((opt) => ( + + ))} +
    + )} +
    + {selectedOption.description} +
    +
    + } + title="Database" + metricLabel="REST Requests" + data={usageData.map((d) => d.rest)} + total={restTotal} + animationKey={timeRange} + /> + } + title="Auth" + metricLabel="Auth Requests" + data={usageData.map((d) => d.auth)} + total={authTotal} + animationKey={timeRange} + /> + } + title="Storage" + metricLabel="Storage Requests" + data={usageData.map((d) => d.storage)} + total={storageTotal} + animationKey={timeRange} + /> + } + title="Realtime" + metricLabel="Realtime Connections" + data={usageData.map((d) => d.realtime)} + total={realtimeTotal} + animationKey={timeRange} + /> +
    + + {/* Advisor */} +
    - - {/* Advisor */} -
    + + {/* Right-side lint detail panel */} + {selectedLint && ( + setSelectedLint(null)} /> + )}
    ) } diff --git a/packages/supa-mock/src/screens/StorageScreen.tsx b/packages/supa-mock/src/screens/StorageScreen.tsx index 4fd28b1e6ed..43fdf777982 100644 --- a/packages/supa-mock/src/screens/StorageScreen.tsx +++ b/packages/supa-mock/src/screens/StorageScreen.tsx @@ -1,7 +1,9 @@ import { ArrowDownNarrowWide, + Book, ChevronLeft, ChevronRight, + Copy, Download, File, FolderClosed, @@ -15,7 +17,21 @@ import { Upload, } from 'lucide-react' import { useState } from 'react' -import { Badge, Button, cn } from 'ui' +import { + Badge, + Button, + Card, + CardContent, + CardFooter, + cn, + Switch, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from 'ui' // ─── Storage Menu ─────────────────────────────────────────────────────── @@ -42,7 +58,7 @@ const STORAGE_MENU: StorageMenuGroup[] = [ }, { title: 'Configuration', - items: [{ name: 'S3', key: 's3', disabled: true }], + items: [{ name: 'S3', key: 's3' }], }, ] @@ -93,16 +109,16 @@ function generateFiles(count: number): MockFile[] { const MOCK_BUCKETS: MockBucket[] = [ { - id: 'submissions', + id: 'uploads', isPublic: true, policies: 0, fileSizeLimit: '100 MB', allowedMimeTypes: 'image/png, image/jpeg, image/webp, video/mp4', - folders: ['logos', 'screenshots'], + folders: ['thumbnails', 'originals'], files: generateFiles(15), }, { - id: 'images', + id: 'assets', isPublic: true, policies: 4, fileSizeLimit: '100 MB', @@ -111,7 +127,7 @@ const MOCK_BUCKETS: MockBucket[] = [ files: generateFiles(28), }, { - id: 'submission-media', + id: 'media', isPublic: true, policies: 4, fileSizeLimit: '100 MB', @@ -487,6 +503,128 @@ function BucketsContent({ onOpenBucket }: { onOpenBucket: (bucket: MockBucket) = ) } +// ─── S3 Configuration ─────────────────────────────────────────────────── + +function CopyInput({ value }: { value: string }) { + return ( +
    + + +
    + ) +} + +function S3Content() { + return ( +
    +
    + {/* Page Title */} +
    +

    S3 Configuration

    +
    + + {/* Connection Section */} +
    +
    +
    +

    Connection

    +

    + Connect to your bucket using any S3-compatible service via the S3 protocol +

    +
    + +
    + + + +
    +
    + S3 protocol connection + + Allow clients to connect to Supabase Storage via the S3 protocol + +
    + +
    +
    + + +
    + Endpoint +
    + +
    +
    +
    + + +
    + Region +
    + +
    +
    +
    + + + + +
    +
    + + {/* Access Keys Section */} +
    +
    +
    +

    Access keys

    +

    + Manage your access keys for this project +

    +
    + +
    + + + + + + Name + Key ID + Created at + + + + + +

    No access keys created

    +

    + There are no access keys associated with your project yet +

    +
    +
    +
    +
    +
    +
    +
    +
    + ) +} + // ─── Storage Screen ───────────────────────────────────────────────────── export function StorageScreen() { @@ -569,6 +707,8 @@ export function StorageScreen() { ) : ( ) + ) : activeKey === 's3' ? ( + ) : (

    diff --git a/packages/supa-mock/src/screens/TableEditorScreen.tsx b/packages/supa-mock/src/screens/TableEditorScreen.tsx index 7efb4b3087b..4f6390c6098 100644 --- a/packages/supa-mock/src/screens/TableEditorScreen.tsx +++ b/packages/supa-mock/src/screens/TableEditorScreen.tsx @@ -33,114 +33,122 @@ interface MockColumn { isPrimary?: boolean } -const HACKATHON_IDS = ['f47ac10b-58cc-4372-a567-0e02b2c3d500', '1a93ba5-f676-4d65-baa8-2fa3fdf...'] - -const MOCK_NAMES = [ - 'SuperDuperAi', - 'Matthew Habtezgi', - 'Snap Banana', - 'BonAppeTeam', - 'Ittybit', - 'Peyote', - 'Table Review', - 'ScamSniper', - '5-Dee Studios', - 'Moldable', - 'Pitched App', - 'MomentoAI', - 'Svelte Supabase Auth', - "Women's Wellness Tracker", - 'Repatch', - 'My Events Super Base', - 'WhatTheStack?!', - 'PaintPal', - 'HotOrSlop', - 'Linkus', - 'Expressify', - 'Supa Events', - 'honk honk', - 'Alicia', - 'jsonMartin', - 'Kimchi', - 'Interfaze', - 'MexicoGPT', - 'call/cc', - 'DevDash', - 'SupaFlow', - 'CodeBuddy', - 'AIssistant', - 'DataVault', - 'CloudPilot', - 'PixelForge', - 'StreamLine', - 'NeuralNest', - 'ByteCraft', - 'FormStack', - 'QueryMaster', - 'SyncWave', - 'BlinkDB', - 'HexaGrid', - 'VectorVault', - 'GraphQL Hero', - 'AuthGuard', - 'EdgeRunner', - 'LambdaLab', - 'ServerPulse', +const CATEGORY_IDS = [ + 'c1a2b3c4-d5e6-4f70-a890-1b2c3d4e5f60', + 'a9b8c7d6-e5f4-4321-b098-7654321fedcb', ] -const MOCK_DESCRIPTIONS = [ - 'AI Filmmaking Storyboard in Figma', - 'A mail platform for AI agents', - 'An AI-Powered Tower Defense Game Ger...', - '2 homo sapiens with hands full of fingers.', - null, - null, - 'A Supabase-powered web app connectin...', - null, - 'WZRD.Studio : Creative Interface to gene...', - 'make your app customizable', - 'Pitched reimagines online dating by lettin...', - 'Choose Your Mentor. Change Your Life. P...', - "I'm working on extending the (now unsup...", - 'This application tracks health and wellne...', - 'Autogenerate AI patchnote newsletters a...', - 'My Events Super Base', - 'WhatTheStack?! is the simplest way to ch...', - 'A friendly painting app for Spectacles. Dr...', - null, - null, - "It's augmentative communication mobile/...", - null, - 'kill zombie costs on AWS instantly', - null, - null, - null, - 'We think postgres should be able to call /...', - 'Building AI native solutions for LATAM', - null, - null, - null, - 'Your AI pair programming companion', - 'An intelligent assistant for developers', - 'Secure data storage solution', - 'Cloud infrastructure manager', - 'Design tool for pixel art', - 'Workflow automation platform', - 'Neural network playground', - 'Byte-level data manipulation', - 'Dynamic form builder', - 'Advanced database query tool', - 'Real-time data synchronization', - 'Lightning fast database', - 'Hexagonal grid system', - 'Vector embedding storage', - 'GraphQL schema generator', - 'Authentication guard middleware', - 'Edge computing framework', - 'Serverless function lab', - 'Server monitoring dashboard', +const PRODUCT_NAMES = [ + 'Wireless Headphones', + 'Mechanical Keyboard', + 'USB-C Hub', + 'Standing Desk', + 'Monitor Light Bar', + 'Ergonomic Mouse', + 'Webcam HD', + 'Desk Mat XL', + 'Cable Organizer', + 'Laptop Stand', + 'Portable Charger', + 'Smart Plug', + 'LED Strip Lights', + 'Bluetooth Speaker', + 'Noise Machine', + 'Screen Protector', + 'Phone Mount', + 'Ring Light', + 'Drawing Tablet', + 'Mesh Wi-Fi Router', + 'External SSD', + 'Docking Station', + 'Trackpad', + 'Desk Shelf', + 'Air Purifier', + 'Desk Fan', + 'Footrest', + 'Wrist Rest', + 'Monitor Arm', + 'Surge Protector', + 'Desk Lamp', + 'Wireless Charger', + 'Noise-Canceling Earbuds', + 'Smart Thermostat', + 'Portable Monitor', + 'Keyboard Tray', + 'Cable Sleeve', + 'Headphone Stand', + 'Document Scanner', + 'Label Maker', + 'Pen Holder', + 'Whiteboard', + 'Timer Cube', + 'Desk Clock', + 'Power Strip', + 'HDMI Adapter', + 'Privacy Screen', + 'Clip-on Light', + 'Mini Projector', + 'Card Reader', ] +const PRODUCT_DESCRIPTIONS: (string | null)[] = [ + 'Over-ear with active noise cancellation', + 'Cherry MX switches, RGB backlight', + '7-in-1 adapter with 4K HDMI output', + 'Electric height-adjustable, 60x30 inch', + null, + null, + '1080p with autofocus and built-in mic', + null, + 'Premium felt, 90x40cm', + 'Silicone cable clips, pack of 12', + 'Aluminum alloy, adjustable height', + '10000mAh, USB-C PD fast charging', + 'Wi-Fi enabled, energy monitoring', + null, + '10W stereo, waterproof IPX7', + 'White noise and nature sounds', + null, + null, + 'Tempered glass, matte finish', + null, + 'Magnetic car mount, MagSafe compatible', + null, + null, + null, + '10-inch with stylus, pressure sensitive', + 'Tri-band, covers up to 5000 sq ft', + null, + null, + null, + '1TB NVMe, USB 3.2 Gen 2', + 'Triple display support, 100W PD', + 'Multi-touch, Bluetooth 5.0', + 'Bamboo desktop organizer', + 'HEPA filter, covers 400 sq ft', + 'Rechargeable, oscillating, USB powered', + 'Adjustable tilt, anti-slip surface', + 'Memory foam, for 60% keyboards', + 'Gas spring, VESA 75/100 compatible', + '8-outlet, 2 USB ports, 6ft cord', + 'Adjustable color temperature, CRI 95+', + '15W Qi fast charge, slim profile', + 'ANC, 30hr battery, multipoint pairing', + 'Smart scheduling, energy saving', + '15.6 inch, 1080p IPS, USB-C powered', + 'Clamp-on, adjustable height', + 'Neoprene, 10ft length, zip closure', + 'Walnut wood, universal fit', + 'Duplex scanning, 600 DPI, Wi-Fi', + 'Thermal, Bluetooth, label tape included', + 'Bamboo, 4 compartments', +] + +function seededRandom(seed: number): number { + const x = Math.sin(seed + 1) * 10000 + return x - Math.floor(x) +} + function generateUUID(seed: number): string { const hex = (n: number) => n.toString(16).padStart(8, '0') return `${hex(seed)}-${hex(seed * 7).slice(0, 4)}-${hex(seed * 13).slice(0, 4)}-${hex(seed * 17).slice(0, 4)}-${hex( @@ -156,53 +164,53 @@ function generateTimestamp(seed: number): string { return base.toISOString().replace('T', ' ').replace('Z', '+00') } -function generateSubmissionRows(): Record[] { +function generateProductRows(): Record[] { const rows: Record[] = [] for (let i = 0; i < 100; i++) { rows.push({ id: generateUUID(i + 1000), - hackathon_id: HACKATHON_IDS[i % 2], - name: MOCK_NAMES[i % MOCK_NAMES.length], - description: MOCK_DESCRIPTIONS[i % MOCK_DESCRIPTIONS.length], + category_id: CATEGORY_IDS[i % 2], + name: PRODUCT_NAMES[i % PRODUCT_NAMES.length], + description: PRODUCT_DESCRIPTIONS[i % PRODUCT_DESCRIPTIONS.length], created_at: generateTimestamp(i), }) } return rows } -const SUBMISSIONS_TABLE: MockTable = { +const PRODUCTS_TABLE: MockTable = { id: 1, - name: 'submissions', + name: 'products', hasRls: true, columns: [ { name: 'id', type: 'uuid', isPrimary: true }, - { name: 'hackathon_id', type: 'uuid' }, + { name: 'category_id', type: 'uuid' }, { name: 'name', type: 'text' }, { name: 'description', type: 'text' }, { name: 'created_at', type: 'timestamptz' }, ], - rows: generateSubmissionRows(), + rows: generateProductRows(), } -const TEAMS_TABLE: MockTable = { +const ORDERS_TABLE: MockTable = { id: 2, - name: 'teams', + name: 'orders', hasRls: true, columns: [ { name: 'id', type: 'uuid', isPrimary: true }, - { name: 'name', type: 'text' }, - { name: 'submission_id', type: 'uuid' }, + { name: 'product_id', type: 'uuid' }, + { name: 'quantity', type: 'int4' }, { name: 'created_at', type: 'timestamptz' }, ], rows: Array.from({ length: 100 }, (_, i) => ({ id: generateUUID(i + 5000), - name: `Team ${MOCK_NAMES[i % MOCK_NAMES.length]}`, - submission_id: generateUUID(i + 1000), + product_id: generateUUID(i + 1000), + quantity: String(Math.floor(seededRandom(i * 3) * 10) + 1), created_at: generateTimestamp(i + 50), })), } -const ALL_TABLES: MockTable[] = [SUBMISSIONS_TABLE, TEAMS_TABLE] +const ALL_TABLES: MockTable[] = [PRODUCTS_TABLE, ORDERS_TABLE] // ─── Mock Data Grid ───────────────────────────────────────────────────── @@ -447,7 +455,7 @@ function MockDataGrid({ table }: { table: MockTable }) { // ─── Table Editor Screen ──────────────────────────────────────────────── export function TableEditorScreen() { - const [selectedTableId, setSelectedTableId] = useState(SUBMISSIONS_TABLE.id) + const [selectedTableId, setSelectedTableId] = useState(PRODUCTS_TABLE.id) const [searchText, setSearchText] = useState('') const selectedTable = ALL_TABLES.find((t) => t.id === selectedTableId) ?? null diff --git a/packages/supa-mock/src/types.ts b/packages/supa-mock/src/types.ts index 02e87c59cbe..7d8f9698b64 100644 --- a/packages/supa-mock/src/types.ts +++ b/packages/supa-mock/src/types.ts @@ -1,5 +1,10 @@ import type { ComponentType, ReactNode } from 'react' +export interface SupaMockOrganization { + name: string + slug: string +} + export interface SupaMockProps { defaultScreen?: string className?: string @@ -7,6 +12,7 @@ export interface SupaMockProps { organizationName?: string organizationPlan?: string branchName?: string + organizations?: SupaMockOrganization[] } export interface MockRoute { @@ -45,6 +51,7 @@ export interface MockUser { export interface MockProjectContextType { project: MockProject + organizations: SupaMockOrganization[] } export interface MockUserContextType {