mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 14:19:11 +03:00
design system documentation
This commit is contained in:
1 parent
6f610be8cd
commit
8ed0198f63
42 files changed
+1487
-204
No files matched your search
@@ -38,6 +38,39 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"PageContainer": {
|
||||
name: "PageContainer",
|
||||
type: "components:fragment",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/../../packages/ui-patterns/src/PageContainer")),
|
||||
source: "",
|
||||
files: ["registry/default//PageContainer/index.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"PageHeader": {
|
||||
name: "PageHeader",
|
||||
type: "components:fragment",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/../../packages/ui-patterns/src/PageHeader")),
|
||||
source: "",
|
||||
files: ["registry/default//PageHeader/index.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"PageSection": {
|
||||
name: "PageSection",
|
||||
type: "components:fragment",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/../../packages/ui-patterns/src/PageSection")),
|
||||
source: "",
|
||||
files: ["registry/default//PageSection/index.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"accordion-demo": {
|
||||
name: "accordion-demo",
|
||||
type: "components:example",
|
||||
@@ -1886,6 +1919,127 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-components-integrated": {
|
||||
name: "page-components-integrated",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-components-integrated")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-components-integrated.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-container-demo": {
|
||||
name: "page-container-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-container-demo")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-container-demo.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-layout-detail": {
|
||||
name: "page-layout-detail",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-layout-detail")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-layout-detail.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-layout-full-page": {
|
||||
name: "page-layout-full-page",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-layout-full-page")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-layout-full-page.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-layout-list": {
|
||||
name: "page-layout-list",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-layout-list")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-layout-list.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-layout-list-simple": {
|
||||
name: "page-layout-list-simple",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-layout-list-simple")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-layout-list-simple.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-layout-settings": {
|
||||
name: "page-layout-settings",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-layout-settings")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-layout-settings.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-header-demo": {
|
||||
name: "page-header-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-header-demo")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-header-demo.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-section-demo": {
|
||||
name: "page-section-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-section-demo")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-section-demo.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-section-horizontal": {
|
||||
name: "page-section-horizontal",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-section-horizontal")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-section-horizontal.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"page-section-with-aside": {
|
||||
name: "page-section-with-aside",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/page-section-with-aside")),
|
||||
source: "",
|
||||
files: ["registry/default/example/page-section-with-aside.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"inner-side-menu-demo": {
|
||||
name: "inner-side-menu-demo",
|
||||
type: "components:example",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '@/styles/globals.css'
|
||||
import '../../studio/styles/typography.scss'
|
||||
import type { Metadata } from 'next'
|
||||
import { ThemeProvider } from './Providers'
|
||||
import { SonnerToaster } from './SonnerToast'
|
||||
|
||||
@@ -120,11 +120,11 @@ export function ComponentPreview({
|
||||
)
|
||||
}, [Preview, align])
|
||||
|
||||
const wideClasses = wide ? '2xl:-ml-12 2xl:-mr-12' : ''
|
||||
const wideClasses = wide ? '2xl:-ml-32 2xl:-mr-32' : ''
|
||||
|
||||
if (peekCode) {
|
||||
return (
|
||||
<div className={cn('mt-4 mb-12', wideClasses)}>
|
||||
<div className={cn('@container mt-4 mb-12', wideClasses)}>
|
||||
<div
|
||||
className={cn(
|
||||
'relative rounded-tl-md rounded-tr-md border-t border-l border-r bg-studio'
|
||||
|
||||
@@ -55,6 +55,11 @@ export const docsConfig: DocsConfig = {
|
||||
href: '/docs/ui-patterns/navigation',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page',
|
||||
href: '/docs/ui-patterns/page',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Empty States',
|
||||
href: '/docs/ui-patterns/empty-states',
|
||||
@@ -85,6 +90,21 @@ export const docsConfig: DocsConfig = {
|
||||
href: '/docs/fragments/modal',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Container',
|
||||
href: '/docs/fragments/page-container',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Header',
|
||||
href: '/docs/fragments/page-header',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Section',
|
||||
href: '/docs/fragments/page-section',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Text Confirm Dialog',
|
||||
href: '/docs/fragments/text-confirm-dialog',
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Page Container
|
||||
description: A container component that provides consistent max-width and padding based on size variants.
|
||||
fragment: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="page-container-demo"
|
||||
description="Container with default size (1200px max-width)"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
```
|
||||
|
||||
```tsx
|
||||
<PageContainer size="default">
|
||||
{/* Page content */}
|
||||
</PageContainer>
|
||||
```
|
||||
|
||||
## Size Variants
|
||||
|
||||
The `PageContainer` supports four size variants:
|
||||
|
||||
- `small` - max-width: 768px
|
||||
- `default` - max-width: 1200px
|
||||
- `large` - max-width: 1600px
|
||||
- `full` - no max-width constraint
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: Page Header
|
||||
description: A compound component for building page headers with consistent structure.
|
||||
fragment: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="page-header-demo"
|
||||
description="Basic page header with title, description, and actions"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
## Sub-components
|
||||
|
||||
- `PageHeader.Root` - Root container with size variants (`default`, `small`, `large`, `full`)
|
||||
- `PageHeader.Breadcrumb` - Breadcrumb navigation wrapper
|
||||
- `PageHeader.Icon` - Icon container positioned left of title
|
||||
- `PageHeader.Summary` - Container for title and description
|
||||
- `PageHeader.Title` - Primary page heading (h1)
|
||||
- `PageHeader.Description` - Supporting text below title
|
||||
- `PageHeader.Aside` - Container for action buttons
|
||||
- `PageHeader.Footer` - Container for tab navigation (NavMenu)
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Page Section
|
||||
description: A compound component for organizing page content into distinct sections.
|
||||
fragment: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="page-section-demo"
|
||||
description="Basic page section with title, description, and content"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
## Sub-components
|
||||
|
||||
- `PageSection.Root` - Root container with orientation variants (`horizontal` or `vertical`)
|
||||
- `PageSection.Summary` - Container for section title and description
|
||||
- `PageSection.Title` - Section heading (h2)
|
||||
- `PageSection.Description` - Supporting text below section title
|
||||
- `PageSection.Aside` - Container for section-level actions
|
||||
- `PageSection.Content` - Container for the main section content
|
||||
|
||||
## Orientation Variants
|
||||
|
||||
- `vertical` - Content stacks vertically (default)
|
||||
- `horizontal` - Summary and content arranged horizontally on larger screens
|
||||
|
||||
## Examples
|
||||
|
||||
### Horizontal Orientation
|
||||
|
||||
<ComponentPreview
|
||||
name="page-section-horizontal"
|
||||
description="PageSection with horizontal layout - summary on left, content on right"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
### With Aside Actions
|
||||
|
||||
<ComponentPreview
|
||||
name="page-section-with-aside"
|
||||
description="PageSection with multiple action buttons in the Aside component"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Page
|
||||
description: Page components provide consistent structure and layout for page-level content.
|
||||
---
|
||||
|
||||
The Page pattern consists of three main components that work together to create consistent page layouts: `PageContainer`, `PageHeader`, and `PageSection`. These components provide a structured approach to building pages with consistent spacing, max-widths, and content organization.
|
||||
|
||||
## Layouts
|
||||
|
||||
### Settings
|
||||
|
||||
Settings pages are used for configuration and preference management. They follow a single-column layout with default widths to keep content focused and readable.
|
||||
|
||||
- Use `PageHeader` with `size="default"`
|
||||
- Use `PageContainer` with `size="default"`
|
||||
- Use `PageSection` for organizing settings into logical groups
|
||||
|
||||
<ComponentPreview
|
||||
name="page-layout-settings"
|
||||
description="Settings page layout with default widths"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
### List
|
||||
|
||||
List pages display collections of objects like tables, triggers, or functions. These pages use larger widths to accommodate wide content like data tables.
|
||||
|
||||
- Use `PageHeader` with `size="large"`
|
||||
- Use `PageContainer` with `size="large"`
|
||||
- Use `PageSection` to wrap list content
|
||||
|
||||
**Table and List Actions:**
|
||||
|
||||
- **With filters or search:** If the table has filters or search, place table actions aligned with the filters on the right side. Do not use `PageSection.Aside` or `PageHeader.Aside` for table actions when filters are present.
|
||||
- **Without filters:** For simple lists without filters or search, add primary list actions to `PageHeader.Aside` or `PageSection.Aside` as appropriate.
|
||||
|
||||
<ComponentPreview
|
||||
name="page-layout-list"
|
||||
description="List page layout with search and filters - actions aligned with search"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
<ComponentPreview
|
||||
name="page-layout-list-simple"
|
||||
description="Simple list page layout without filters - actions in PageHeader.Aside"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
### Data and Full Page Experiences
|
||||
|
||||
Full-page experiences like editors require maximum screen real estate. These pages use full-width containers to maximize available space.
|
||||
|
||||
- Use `PageHeader` with `size="full"`
|
||||
- Use `PageContainer` with `size="full"`
|
||||
- Content spans the full width of the viewport
|
||||
|
||||
### Detail Pages
|
||||
|
||||
Detail pages display dense or lengthy content split into multiple sections. The horizontal orientation allows for better information hierarchy and context.
|
||||
|
||||
- Use `PageHeader` with `size="large"`
|
||||
- Use `PageContainer` with `size="large"`
|
||||
- Use `PageSection` with `orientation="horizontal"` to show summary alongside content
|
||||
- Multiple sections can stack vertically with horizontal layouts within each
|
||||
|
||||
<ComponentPreview
|
||||
name="page-layout-detail"
|
||||
description="Detail page layout with horizontal sections for dense content"
|
||||
peekCode
|
||||
wide
|
||||
/>
|
||||
|
||||
## Components
|
||||
|
||||
- **[PageContainer](/docs/fragments/page-container)** - Container component providing consistent max-width and padding based on size variants
|
||||
- **[PageHeader](/docs/fragments/page-header)** - Compound component for building page headers with breadcrumbs, icons, titles, descriptions, actions, and navigation
|
||||
- **[PageSection](/docs/fragments/page-section)** - Compound component for organizing page content into distinct sections with title, description, and action areas
|
||||
@@ -51,6 +51,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@shikijs/compat": "^1.1.7",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@types/lodash.template": "4.5.0",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import { Button, Card, CardContent } from 'ui'
|
||||
|
||||
export default function PageComponentsIntegrated() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageHeader.Root size="large">
|
||||
<PageHeader.Summary>
|
||||
<PageHeader.Title>Edge Functions</PageHeader.Title>
|
||||
<PageHeader.Description>
|
||||
Manage and deploy serverless functions that run at the edge.
|
||||
</PageHeader.Description>
|
||||
</PageHeader.Summary>
|
||||
<PageHeader.Aside>
|
||||
<Button type="primary" size="small">
|
||||
Deploy Function
|
||||
</Button>
|
||||
</PageHeader.Aside>
|
||||
</PageHeader.Root>
|
||||
|
||||
<PageContainer size="large">
|
||||
<PageSection.Root>
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Function Details</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
Configure your function settings and view logs.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-foreground-light">
|
||||
This demonstrates how PageContainer, PageHeader, and PageSection work together to
|
||||
create a complete page layout.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</PageContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { Button } from 'ui'
|
||||
|
||||
export default function PageContainerDemo() {
|
||||
return (
|
||||
<div className="w-full space-y-4">
|
||||
<PageContainer size="small" className="bg-muted border rounded-lg p-4">
|
||||
<p>This is a page container with small size (768px max-width)</p>
|
||||
</PageContainer>
|
||||
<PageContainer size="default" className="bg-muted border rounded-lg p-4">
|
||||
<p>This is a page container with default size (1200px max-width)</p>
|
||||
</PageContainer>
|
||||
<PageContainer size="large" className="bg-muted border rounded-lg p-4">
|
||||
<p>This is a page container with large size (1600px max-width)</p>
|
||||
</PageContainer>
|
||||
<PageContainer size="full" className="bg-muted border rounded-lg p-4">
|
||||
<p>This is a page container with full size (no max-width)</p>
|
||||
</PageContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import {
|
||||
Button,
|
||||
NavMenu,
|
||||
NavMenuItem,
|
||||
BreadcrumbItem_Shadcn_ as BreadcrumbItem,
|
||||
BreadcrumbLink_Shadcn_ as BreadcrumbLink,
|
||||
BreadcrumbList_Shadcn_ as BreadcrumbList,
|
||||
BreadcrumbSeparator_Shadcn_ as BreadcrumbSeparator,
|
||||
} from 'ui'
|
||||
import { Database } from 'lucide-react'
|
||||
|
||||
export default function PageHeaderDemo() {
|
||||
const breadcrumbItems = [
|
||||
{ label: 'Project', href: '/project/demo' },
|
||||
{ label: 'Edge Functions', href: '/project/demo/functions' },
|
||||
]
|
||||
|
||||
const navigationItems = [
|
||||
{ label: 'Overview', href: '/project/demo/functions/demo-function' },
|
||||
{ label: 'Invocations', href: '/project/demo/functions/demo-function/invocations' },
|
||||
{ label: 'Logs', href: '/project/demo/functions/demo-function/logs' },
|
||||
{ label: 'Details', href: '/project/demo/functions/demo-function/details' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageHeader.Root>
|
||||
<PageHeader.Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
{breadcrumbItems.map((item, index) => (
|
||||
<React.Fragment key={item.label}>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={item.href}>{item.label}</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
{index < breadcrumbItems.length - 1 && <BreadcrumbSeparator />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
</PageHeader.Breadcrumb>
|
||||
<PageHeader.Icon>
|
||||
<Database className="w-5 h-5" />
|
||||
</PageHeader.Icon>
|
||||
<PageHeader.Summary>
|
||||
<PageHeader.Title>Demo Function</PageHeader.Title>
|
||||
<PageHeader.Description>
|
||||
Serverless functions that run at the edge with low latency and automatic scaling.
|
||||
</PageHeader.Description>
|
||||
</PageHeader.Summary>
|
||||
|
||||
<PageHeader.Aside>
|
||||
<Button type="default" size="small">
|
||||
Secondary
|
||||
</Button>
|
||||
<Button type="primary" size="small">
|
||||
Deploy Function
|
||||
</Button>
|
||||
</PageHeader.Aside>
|
||||
|
||||
<PageHeader.Footer>
|
||||
<NavMenu>
|
||||
{navigationItems.map((item) => (
|
||||
<NavMenuItem key={item.label}>
|
||||
<Link href={item.href}>{item.label}</Link>
|
||||
</NavMenuItem>
|
||||
))}
|
||||
</NavMenu>
|
||||
</PageHeader.Footer>
|
||||
</PageHeader.Root>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import { Button, Card, CardContent } from 'ui'
|
||||
|
||||
export default function PageLayoutDetail() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageHeader.Root size="large">
|
||||
<PageHeader.Summary>
|
||||
<PageHeader.Title>Billing</PageHeader.Title>
|
||||
<PageHeader.Description>
|
||||
Manage your organization's billing and subscription settings.
|
||||
</PageHeader.Description>
|
||||
</PageHeader.Summary>
|
||||
</PageHeader.Root>
|
||||
|
||||
<PageContainer size="large">
|
||||
<PageSection.Root orientation="horizontal">
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Subscription</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
View and manage your current subscription plan and billing cycle.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-xs text-foreground-lighter mb-1">Current Plan</p>
|
||||
<p className="text-sm font-medium">Pro Plan</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-foreground-lighter mb-1">Billing Cycle</p>
|
||||
<p className="text-sm">Monthly</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-foreground-lighter mb-1">Next Billing Date</p>
|
||||
<p className="text-sm">March 15, 2024</p>
|
||||
</div>
|
||||
<div className="pt-2">
|
||||
<Button type="default" size="small">
|
||||
Change Plan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
|
||||
<PageSection.Root orientation="horizontal">
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Cost Control</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
Set spending limits and alerts to manage your organization's costs.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-xs text-foreground-lighter mb-1">Monthly Spending Limit</p>
|
||||
<p className="text-sm">$500.00</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-foreground-lighter mb-1">Current Month Spend</p>
|
||||
<p className="text-sm">$234.50</p>
|
||||
</div>
|
||||
<div className="pt-2">
|
||||
<Button type="default" size="small">
|
||||
Configure Limits
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
|
||||
<PageSection.Root orientation="horizontal">
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Payment Methods</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
Manage payment methods and billing information for your organization.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-xs text-foreground-lighter mb-1">Primary Payment Method</p>
|
||||
<p className="text-sm">•••• •••• •••• 4242</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-foreground-lighter mb-1">Expires</p>
|
||||
<p className="text-sm">12/2025</p>
|
||||
</div>
|
||||
<div className="pt-2">
|
||||
<Button type="default" size="small">
|
||||
Update Payment Method
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</PageContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { Button } from 'ui'
|
||||
|
||||
export default function PageLayoutFullPage() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageHeader.Root size="full">
|
||||
<PageHeader.Summary>
|
||||
<PageHeader.Title>SQL Editor</PageHeader.Title>
|
||||
<PageHeader.Description>
|
||||
Write and execute SQL queries with syntax highlighting and auto-complete.
|
||||
</PageHeader.Description>
|
||||
</PageHeader.Summary>
|
||||
<PageHeader.Aside>
|
||||
<Button type="default" size="small">
|
||||
Format
|
||||
</Button>
|
||||
<Button type="primary" size="small">
|
||||
Run Query
|
||||
</Button>
|
||||
</PageHeader.Aside>
|
||||
</PageHeader.Root>
|
||||
|
||||
<PageContainer size="full">
|
||||
<div className="mt-4 border rounded-lg bg-background p-4 min-h-[400px]">
|
||||
<p className="text-sm text-foreground-light">
|
||||
Editor content takes full width for maximum screen real estate.
|
||||
</p>
|
||||
</div>
|
||||
</PageContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from 'ui'
|
||||
|
||||
export default function PageLayoutListSimple() {
|
||||
const items = [
|
||||
{ id: 1, name: 'Project Alpha', status: 'Active', members: 12 },
|
||||
{ id: 2, name: 'Project Beta', status: 'Active', members: 8 },
|
||||
{ id: 3, name: 'Project Gamma', status: 'Inactive', members: 5 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageHeader.Root size="large">
|
||||
<PageHeader.Summary>
|
||||
<PageHeader.Title>Projects</PageHeader.Title>
|
||||
<PageHeader.Description>
|
||||
Manage and view all your projects in one place.
|
||||
</PageHeader.Description>
|
||||
</PageHeader.Summary>
|
||||
<PageHeader.Aside>
|
||||
<Button type="primary" size="small">
|
||||
Create Project
|
||||
</Button>
|
||||
</PageHeader.Aside>
|
||||
</PageHeader.Root>
|
||||
|
||||
<PageContainer size="large">
|
||||
<PageSection.Root>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Members</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{items.map((item) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell className="font-medium">{item.name}</TableCell>
|
||||
<TableCell>{item.status}</TableCell>
|
||||
<TableCell>{item.members}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" size="small">
|
||||
Actions
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>Edit</DropdownMenuItem>
|
||||
<DropdownMenuItem>Duplicate</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="text-destructive">Delete</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</PageContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
Input,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from 'ui'
|
||||
import { Search } from 'lucide-react'
|
||||
|
||||
export default function PageLayoutList() {
|
||||
const functions = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'get_user_profile',
|
||||
arguments: 'user_id uuid',
|
||||
return_type: 'jsonb',
|
||||
security: 'Definer',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'update_user_settings',
|
||||
arguments: 'user_id uuid, settings jsonb',
|
||||
return_type: 'void',
|
||||
security: 'Invoker',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'calculate_total',
|
||||
arguments: 'amount numeric, tax_rate numeric',
|
||||
return_type: 'numeric',
|
||||
security: 'Definer',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageHeader.Root size="large">
|
||||
<PageHeader.Summary>
|
||||
<PageHeader.Title>Database Functions</PageHeader.Title>
|
||||
<PageHeader.Description>Manage your database functions</PageHeader.Description>
|
||||
</PageHeader.Summary>
|
||||
</PageHeader.Root>
|
||||
|
||||
<PageContainer size="large">
|
||||
<PageSection.Root>
|
||||
<PageSection.Content>
|
||||
<div className="w-full space-y-4">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-2 flex-wrap">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center gap-2">
|
||||
<Input
|
||||
placeholder="Search for a function"
|
||||
size="tiny"
|
||||
icon={<Search size={14} />}
|
||||
className="w-full lg:w-52"
|
||||
/>
|
||||
</div>
|
||||
<Button type="primary">Create a new function</Button>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<Table className="table-fixed overflow-x-auto">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead className="table-cell">Arguments</TableHead>
|
||||
<TableHead className="table-cell">Return type</TableHead>
|
||||
<TableHead className="table-cell w-[100px]">Security</TableHead>
|
||||
<TableHead className="w-1/6"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{functions.map((fn) => (
|
||||
<TableRow key={fn.id}>
|
||||
<TableCell className="font-medium">{fn.name}</TableCell>
|
||||
<TableCell>{fn.arguments}</TableCell>
|
||||
<TableCell>{fn.return_type}</TableCell>
|
||||
<TableCell>{fn.security}</TableCell>
|
||||
<TableCell>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" size="small">
|
||||
Actions
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>Edit</DropdownMenuItem>
|
||||
<DropdownMenuItem>Duplicate</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="text-destructive">
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</div>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</PageContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import * as z from 'zod'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
Form_Shadcn_,
|
||||
Input_Shadcn_,
|
||||
PrePostTab,
|
||||
Switch,
|
||||
} from 'ui'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
const RefreshTokenSchema = z.object({
|
||||
REFRESH_TOKEN_ROTATION_ENABLED: z.boolean(),
|
||||
SECURITY_REFRESH_TOKEN_REUSE_INTERVAL: z.coerce.number().min(0),
|
||||
})
|
||||
|
||||
const UserSessionsSchema = z.object({
|
||||
SESSIONS_TIMEBOX: z.coerce.number().min(0),
|
||||
SESSIONS_INACTIVITY_TIMEOUT: z.coerce.number().min(0),
|
||||
SESSIONS_SINGLE_PER_USER: z.boolean(),
|
||||
})
|
||||
|
||||
function HoursOrNeverText({ value }: { value: number }) {
|
||||
if (value === 0) {
|
||||
return 'never'
|
||||
} else if (value === 1) {
|
||||
return 'hour'
|
||||
} else {
|
||||
return 'hours'
|
||||
}
|
||||
}
|
||||
|
||||
export default function PageLayoutSettings() {
|
||||
const refreshTokenForm = useForm<z.infer<typeof RefreshTokenSchema>>({
|
||||
resolver: zodResolver(RefreshTokenSchema),
|
||||
defaultValues: {
|
||||
REFRESH_TOKEN_ROTATION_ENABLED: false,
|
||||
SECURITY_REFRESH_TOKEN_REUSE_INTERVAL: 10,
|
||||
},
|
||||
})
|
||||
|
||||
const userSessionsForm = useForm<z.infer<typeof UserSessionsSchema>>({
|
||||
resolver: zodResolver(UserSessionsSchema),
|
||||
defaultValues: {
|
||||
SESSIONS_TIMEBOX: 0,
|
||||
SESSIONS_INACTIVITY_TIMEOUT: 0,
|
||||
SESSIONS_SINGLE_PER_USER: false,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageHeader.Root size="default">
|
||||
<PageHeader.Summary>
|
||||
<PageHeader.Title>User Sessions</PageHeader.Title>
|
||||
<PageHeader.Description>
|
||||
Configure settings for user sessions and refresh tokens
|
||||
</PageHeader.Description>
|
||||
</PageHeader.Summary>
|
||||
</PageHeader.Root>
|
||||
|
||||
<PageContainer size="default">
|
||||
<PageSection.Root>
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Refresh Tokens</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
Configure refresh token rotation and security settings.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
<Form_Shadcn_ {...refreshTokenForm}>
|
||||
<form className="space-y-4">
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<FormField_Shadcn_
|
||||
control={refreshTokenForm.control}
|
||||
name="REFRESH_TOKEN_ROTATION_ENABLED"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Detect and revoke potentially compromised refresh tokens"
|
||||
description="Prevent replay attacks from potentially compromised refresh tokens."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={refreshTokenForm.control}
|
||||
name="SECURITY_REFRESH_TOKEN_REUSE_INTERVAL"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Refresh token reuse interval"
|
||||
description="Time interval where the same refresh token can be used multiple times to request for an access token. Recommendation: 10 seconds."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<PrePostTab postTab="seconds">
|
||||
<Input_Shadcn_ type="number" min={0} {...field} />
|
||||
</PrePostTab>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className="justify-end space-x-2">
|
||||
{refreshTokenForm.formState.isDirty && (
|
||||
<Button type="default" onClick={() => refreshTokenForm.reset()}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
disabled={!refreshTokenForm.formState.isDirty}
|
||||
>
|
||||
Save changes
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
|
||||
<PageSection.Root>
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>User Sessions</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
Configure session timeout and single session enforcement settings.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
<Form_Shadcn_ {...userSessionsForm}>
|
||||
<form className="space-y-4">
|
||||
<Card>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={userSessionsForm.control}
|
||||
name="SESSIONS_SINGLE_PER_USER"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Enforce single session per user"
|
||||
description="If enabled, all but a user's most recently active session will be terminated."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={userSessionsForm.control}
|
||||
name="SESSIONS_TIMEBOX"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Time-box user sessions"
|
||||
description="The amount of time before a user is forced to sign in again. Use 0 for never."
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<FormControl_Shadcn_>
|
||||
<PrePostTab postTab={<HoursOrNeverText value={field.value || 0} />}>
|
||||
<Input_Shadcn_ type="number" min={0} {...field} />
|
||||
</PrePostTab>
|
||||
</FormControl_Shadcn_>
|
||||
</div>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={userSessionsForm.control}
|
||||
name="SESSIONS_INACTIVITY_TIMEOUT"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Inactivity timeout"
|
||||
description="The amount of time a user needs to be inactive to be forced to sign in again. Use 0 for never."
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<FormControl_Shadcn_>
|
||||
<PrePostTab postTab={<HoursOrNeverText value={field.value || 0} />}>
|
||||
<Input_Shadcn_ type="number" {...field} />
|
||||
</PrePostTab>
|
||||
</FormControl_Shadcn_>
|
||||
</div>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
<CardFooter className="justify-end space-x-2">
|
||||
{userSessionsForm.formState.isDirty && (
|
||||
<Button type="default" onClick={() => userSessionsForm.reset()}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
disabled={!userSessionsForm.formState.isDirty}
|
||||
>
|
||||
Save changes
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</PageContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import { Button, Card, CardContent } from 'ui'
|
||||
|
||||
export default function PageSectionDemo() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageSection.Root>
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Section Title</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
This is a section with a title and description, plus optional actions.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Aside>
|
||||
<Button type="default" size="small">
|
||||
Action
|
||||
</Button>
|
||||
</PageSection.Aside>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-foreground-light">
|
||||
Section content goes here. This could be forms, tables, or any other content.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import { Card, CardContent } from 'ui'
|
||||
|
||||
export default function PageSectionHorizontal() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageSection.Root orientation="horizontal">
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Section Title</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
In horizontal orientation, the summary (title and description) appears on the left, with
|
||||
content arranged on the right on larger screens. This is useful for detailed sections
|
||||
where you want to maintain context while viewing content.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-foreground-light">
|
||||
The content area appears alongside the summary in a horizontal layout. On smaller
|
||||
screens, it will stack vertically.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import { Button, Card, CardContent } from 'ui'
|
||||
|
||||
export default function PageSectionWithAside() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<PageSection.Root>
|
||||
<PageSection.Summary>
|
||||
<PageSection.Title>Section Title</PageSection.Title>
|
||||
<PageSection.Description>
|
||||
This demonstrates PageSection with actions in the Aside component.
|
||||
</PageSection.Description>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Aside>
|
||||
<Button type="default" size="small">
|
||||
Secondary
|
||||
</Button>
|
||||
<Button type="primary" size="small">
|
||||
Primary Action
|
||||
</Button>
|
||||
</PageSection.Aside>
|
||||
<PageSection.Content>
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<p className="text-sm text-foreground-light">
|
||||
The Aside component positions actions horizontally aligned with the section summary,
|
||||
providing a clear separation between description and actions.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1078,6 +1078,56 @@ export const examples: Registry = [
|
||||
type: 'components:example',
|
||||
files: ['example/info-tooltip-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-components-integrated',
|
||||
type: 'components:example',
|
||||
files: ['example/page-components-integrated.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-container-demo',
|
||||
type: 'components:example',
|
||||
files: ['example/page-container-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-layout-detail',
|
||||
type: 'components:example',
|
||||
files: ['example/page-layout-detail.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-layout-full-page',
|
||||
type: 'components:example',
|
||||
files: ['example/page-layout-full-page.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-layout-list',
|
||||
type: 'components:example',
|
||||
files: ['example/page-layout-list.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-layout-settings',
|
||||
type: 'components:example',
|
||||
files: ['example/page-layout-settings.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-header-demo',
|
||||
type: 'components:example',
|
||||
files: ['example/page-header-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-section-demo',
|
||||
type: 'components:example',
|
||||
files: ['example/page-section-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-section-horizontal',
|
||||
type: 'components:example',
|
||||
files: ['example/page-section-horizontal.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'page-section-with-aside',
|
||||
type: 'components:example',
|
||||
files: ['example/page-section-with-aside.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'inner-side-menu-demo',
|
||||
type: 'components:example',
|
||||
|
||||
@@ -19,4 +19,22 @@ export const fragments: Registry = [
|
||||
files: ['/Dialogs/ConfirmDialog.tsx'],
|
||||
optionalPath: '/Dialogs',
|
||||
},
|
||||
{
|
||||
name: 'PageContainer',
|
||||
type: 'components:fragment',
|
||||
files: ['/PageContainer/index.tsx'],
|
||||
optionalPath: '/PageContainer',
|
||||
},
|
||||
{
|
||||
name: 'PageHeader',
|
||||
type: 'components:fragment',
|
||||
files: ['/PageHeader/index.tsx'],
|
||||
optionalPath: '/PageHeader',
|
||||
},
|
||||
{
|
||||
name: 'PageSection',
|
||||
type: 'components:fragment',
|
||||
files: ['/PageSection/index.tsx'],
|
||||
optionalPath: '/PageSection',
|
||||
},
|
||||
]
|
||||
@@ -207,17 +207,28 @@ export const Index: Record<string, any> = {
|
||||
// console.log('item', item)
|
||||
|
||||
let packagePath = ''
|
||||
let importPath = ''
|
||||
if (type === 'ui') {
|
||||
packagePath = `../../packages/ui/src/components/shadcn/ui`
|
||||
importPath = `${packagePath}/${item.name}`
|
||||
}
|
||||
if (type === 'fragment') {
|
||||
packagePath = `../../packages/ui-patterns/src${item.optionalPath}`
|
||||
// If the file is index.tsx, import from the directory directly (without appending component name)
|
||||
const firstFile = item.files[0] || ''
|
||||
if (firstFile.endsWith('/index.tsx')) {
|
||||
importPath = packagePath
|
||||
} else {
|
||||
importPath = `${packagePath}/${item.name}`
|
||||
}
|
||||
}
|
||||
if (type === 'example') {
|
||||
packagePath = `registry/${style.name}/${type}`
|
||||
importPath = `${packagePath}/${item.name}`
|
||||
}
|
||||
if (type === 'block') {
|
||||
packagePath = `registry/${style.name}/${type}`
|
||||
importPath = `${packagePath}/${item.name}`
|
||||
}
|
||||
|
||||
index += `
|
||||
@@ -225,7 +236,7 @@ export const Index: Record<string, any> = {
|
||||
name: "${item.name}",
|
||||
type: "${item.type}",
|
||||
registryDependencies: ${JSON.stringify(item.registryDependencies)},
|
||||
component: React.lazy(() => import("@/${packagePath}/${item.name}")),
|
||||
component: React.lazy(() => import("@/${importPath}")),
|
||||
source: "${sourceFilename}",
|
||||
files: [${resolveFiles.map((file) => `"${file}"`)}],
|
||||
category: "${item.category}",
|
||||
|
||||
@@ -10,6 +10,7 @@ module.exports = config({
|
||||
'./../../packages/ui/src/**/*.{tsx,ts,js}',
|
||||
'./../../packages/ui-patterns/src/**/*.{tsx,ts,js}',
|
||||
],
|
||||
plugins: [require('@tailwindcss/container-queries')],
|
||||
theme: {
|
||||
extend: {
|
||||
maxWidth: {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ExternalLink } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ResourceList } from 'components/ui/Resource/ResourceList'
|
||||
import { HorizontalShimmerWithIcon } from 'components/ui/Shimmers'
|
||||
|
||||
@@ -8,7 +8,7 @@ import { toast } from 'sonner'
|
||||
import { boolean, object, string } from 'yup'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { InlineLink } from 'components/ui/InlineLink'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
@@ -119,80 +119,112 @@ export const BasicAuthSettingsForm = () => {
|
||||
<PageSection.Title>User Signups</PageSection.Title>
|
||||
</PageSection.Summary>
|
||||
<PageSection.Content>
|
||||
{isError && (
|
||||
<AlertError
|
||||
error={authConfigError}
|
||||
subject="Failed to retrieve auth configuration for hooks"
|
||||
/>
|
||||
)}
|
||||
|
||||
{isError && (
|
||||
<AlertError
|
||||
error={authConfigError}
|
||||
subject="Failed to retrieve auth configuration for hooks"
|
||||
/>
|
||||
)}
|
||||
{isPermissionsLoaded && !canReadConfig && (
|
||||
<div className="mt-8">
|
||||
<NoPermission resourceText="view auth configuration settings" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isPermissionsLoaded && !canReadConfig && (
|
||||
<div className="mt-8">
|
||||
<NoPermission resourceText="view auth configuration settings" />
|
||||
</div>
|
||||
)}
|
||||
{isLoading && (
|
||||
<Card>
|
||||
<CardContent className="py-6">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-6">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-6">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-7">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-7"></CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<Card>
|
||||
<CardContent className="py-6">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-6">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-6">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-7">
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
<CardContent className="py-7"></CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{isSuccess && (
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<Card>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="DISABLE_SIGNUP"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Allow new users to sign up"
|
||||
description="If this is disabled, new users will not be able to sign up to your application"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
{showManualLinking && (
|
||||
{isSuccess && (
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<Card>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="SECURITY_MANUAL_LINKING_ENABLED"
|
||||
name="DISABLE_SIGNUP"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Allow manual linking"
|
||||
label="Allow new users to sign up"
|
||||
description="If this is disabled, new users will not be able to sign up to your application"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
{showManualLinking && (
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="SECURITY_MANUAL_LINKING_ENABLED"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Allow manual linking"
|
||||
description={
|
||||
<>
|
||||
Enable{' '}
|
||||
<InlineLink
|
||||
className="text-foreground-light hover:text-foreground"
|
||||
href={`${DOCS_URL}/guides/auth/auth-identity-linking#manual-linking-beta`}
|
||||
>
|
||||
manual linking APIs
|
||||
</InlineLink>{' '}
|
||||
for your project
|
||||
</>
|
||||
}
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
)}
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="EXTERNAL_ANONYMOUS_USERS_ENABLED"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Allow anonymous sign-ins"
|
||||
description={
|
||||
<>
|
||||
Enable{' '}
|
||||
<InlineLink
|
||||
className="text-foreground-light hover:text-foreground"
|
||||
href={`${DOCS_URL}/guides/auth/auth-identity-linking#manual-linking-beta`}
|
||||
href={`${DOCS_URL}/guides/auth/auth-anonymous`}
|
||||
>
|
||||
manual linking APIs
|
||||
anonymous sign-ins
|
||||
</InlineLink>{' '}
|
||||
for your project
|
||||
</>
|
||||
@@ -208,132 +240,99 @@ export const BasicAuthSettingsForm = () => {
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
)}
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="EXTERNAL_ANONYMOUS_USERS_ENABLED"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Allow anonymous sign-ins"
|
||||
description={
|
||||
<>
|
||||
Enable{' '}
|
||||
<InlineLink
|
||||
className="text-foreground-light hover:text-foreground"
|
||||
href={`${DOCS_URL}/guides/auth/auth-anonymous`}
|
||||
>
|
||||
anonymous sign-ins
|
||||
</InlineLink>{' '}
|
||||
for your project
|
||||
</>
|
||||
}
|
||||
|
||||
{form.watch('EXTERNAL_ANONYMOUS_USERS_ENABLED') && (
|
||||
<Alert_Shadcn_
|
||||
className="flex w-full items-center justify-between mt-4"
|
||||
variant="warning"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
{form.watch('EXTERNAL_ANONYMOUS_USERS_ENABLED') && (
|
||||
<Alert_Shadcn_
|
||||
className="flex w-full items-center justify-between mt-4"
|
||||
variant="warning"
|
||||
>
|
||||
<WarningIcon />
|
||||
<div>
|
||||
<AlertTitle_Shadcn_>
|
||||
Anonymous users will use the <code className="text-xs">authenticated</code>{' '}
|
||||
role when signing in
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="flex flex-col gap-y-3">
|
||||
<p>
|
||||
As a result, anonymous users will be subjected to RLS policies that apply
|
||||
to the <code className="text-xs">public</code> and{' '}
|
||||
<code className="text-xs">authenticated</code> roles. We strongly advise{' '}
|
||||
<Link
|
||||
href={`/project/${projectRef}/auth/policies`}
|
||||
className="text-foreground underline"
|
||||
>
|
||||
reviewing your RLS policies
|
||||
</Link>{' '}
|
||||
to ensure that access to your data is restricted where required.
|
||||
</p>
|
||||
<Button asChild type="default" className="w-min" icon={<ExternalLink />}>
|
||||
<Link href={`${DOCS_URL}/guides/auth/auth-anonymous#access-control`}>
|
||||
View access control docs
|
||||
</Link>
|
||||
</Button>
|
||||
</AlertDescription_Shadcn_>
|
||||
</div>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
|
||||
{!authConfig?.SECURITY_CAPTCHA_ENABLED &&
|
||||
form.watch('EXTERNAL_ANONYMOUS_USERS_ENABLED') && (
|
||||
<Alert_Shadcn_ className="mt-4">
|
||||
<WarningIcon />
|
||||
<AlertTitle_Shadcn_>
|
||||
We highly recommend{' '}
|
||||
<InlineLink href={`/project/${projectRef}/auth/protection`}>
|
||||
enabling captcha
|
||||
</InlineLink>{' '}
|
||||
for anonymous sign-ins
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
This will prevent potential abuse on sign-ins which may bloat your database
|
||||
and incur costs for monthly active users (MAU)
|
||||
</AlertDescription_Shadcn_>
|
||||
<div>
|
||||
<AlertTitle_Shadcn_>
|
||||
Anonymous users will use the{' '}
|
||||
<code className="text-xs">authenticated</code> role when signing in
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="flex flex-col gap-y-3">
|
||||
<p>
|
||||
As a result, anonymous users will be subjected to RLS policies that
|
||||
apply to the <code className="text-xs">public</code> and{' '}
|
||||
<code className="text-xs">authenticated</code> roles. We strongly advise{' '}
|
||||
<Link
|
||||
href={`/project/${projectRef}/auth/policies`}
|
||||
className="text-foreground underline"
|
||||
>
|
||||
reviewing your RLS policies
|
||||
</Link>{' '}
|
||||
to ensure that access to your data is restricted where required.
|
||||
</p>
|
||||
<Button asChild type="default" className="w-min" icon={<ExternalLink />}>
|
||||
<Link href={`${DOCS_URL}/guides/auth/auth-anonymous#access-control`}>
|
||||
View access control docs
|
||||
</Link>
|
||||
</Button>
|
||||
</AlertDescription_Shadcn_>
|
||||
</div>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="MAILER_AUTOCONFIRM"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Confirm email"
|
||||
description="Users will need to confirm their email address before signing in for the first time"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
|
||||
{!authConfig?.SECURITY_CAPTCHA_ENABLED &&
|
||||
form.watch('EXTERNAL_ANONYMOUS_USERS_ENABLED') && (
|
||||
<Alert_Shadcn_ className="mt-4">
|
||||
<WarningIcon />
|
||||
<AlertTitle_Shadcn_>
|
||||
We highly recommend{' '}
|
||||
<InlineLink href={`/project/${projectRef}/auth/protection`}>
|
||||
enabling captcha
|
||||
</InlineLink>{' '}
|
||||
for anonymous sign-ins
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
This will prevent potential abuse on sign-ins which may bloat your
|
||||
database and incur costs for monthly active users (MAU)
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="MAILER_AUTOCONFIRM"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Confirm email"
|
||||
description="Users will need to confirm their email address before signing in for the first time"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className="justify-end space-x-2">
|
||||
{form.formState.isDirty && (
|
||||
<Button type="default" onClick={() => form.reset()}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className="justify-end space-x-2">
|
||||
{form.formState.isDirty && (
|
||||
<Button type="default" onClick={() => form.reset()}>
|
||||
Cancel
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
disabled={!canUpdateConfig || isUpdatingConfig || !form.formState.isDirty}
|
||||
loading={isUpdatingConfig}
|
||||
>
|
||||
Save changes
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
disabled={!canUpdateConfig || isUpdatingConfig || !form.formState.isDirty}
|
||||
loading={isUpdatingConfig}
|
||||
>
|
||||
Save changes
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
)}
|
||||
</PageSection.Content>
|
||||
</PageSection.Root>
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { InlineLink } from 'components/ui/InlineLink'
|
||||
|
||||
+2
-2
@@ -51,8 +51,8 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
import CommandRender from '../CommandRender'
|
||||
import { INVOCATION_TABS } from './EdgeFunctionDetails.constants'
|
||||
import { generateCLICommands } from './EdgeFunctionDetails.utils'
|
||||
import { PageContainer } from 'components/ui/PageContainer'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z.string().min(0, 'Name is required'),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { BlobReader, BlobWriter, ZipWriter } from '@zip.js/zip.js'
|
||||
import { useParams } from 'common'
|
||||
import { useIsAPIDocsSidePanelEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { EdgeFunctionTesterSheet } from 'components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet'
|
||||
import { PageHeader } from 'components/ui/PageHeader'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { APIDocsButton } from 'components/ui/APIDocsButton'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
|
||||
@@ -7,8 +7,8 @@ import { AuthProvidersForm } from 'components/interfaces/Auth/AuthProvidersForm'
|
||||
import { BasicAuthSettingsForm } from 'components/interfaces/Auth/BasicAuthSettingsForm'
|
||||
import AuthLayout from 'components/layouts/AuthLayout/AuthLayout'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageContainer } from 'components/ui/PageContainer'
|
||||
import { PageHeader } from 'components/ui/PageHeader'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { NavMenu, NavMenuItem } from 'ui'
|
||||
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
|
||||
import { UnknownInterface } from 'components/ui/UnknownInterface'
|
||||
|
||||
@@ -8,8 +8,8 @@ import { useParams } from 'common'
|
||||
import { ThirdPartyAuthForm } from 'components/interfaces/Auth/ThirdPartyAuthForm'
|
||||
import AuthLayout from 'components/layouts/AuthLayout/AuthLayout'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageContainer } from 'components/ui/PageContainer'
|
||||
import { PageHeader } from 'components/ui/PageHeader'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { NavMenu, NavMenuItem } from 'ui'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
import { UnknownInterface } from 'components/ui/UnknownInterface'
|
||||
|
||||
@@ -8,7 +8,7 @@ import DatabaseBackupsNav from 'components/interfaces/Database/Backups/DatabaseB
|
||||
import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
|
||||
import { PageHeader } from 'components/ui/PageHeader'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
|
||||
@@ -9,8 +9,8 @@ import FunctionsList from 'components/interfaces/Database/Functions/FunctionsLis
|
||||
import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
|
||||
import { PageContainer } from 'components/ui/PageContainer'
|
||||
import { PageHeader } from 'components/ui/PageHeader'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
import { DatabaseFunction } from 'data/database-functions/database-functions-query'
|
||||
@@ -19,7 +19,7 @@ import { DOCS_URL } from 'lib/constants'
|
||||
import { useEditorPanelStateSnapshot } from 'state/editor-panel-state'
|
||||
import { useSidebarManagerSnapshot } from 'state/sidebar-manager-state'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
|
||||
const DatabaseFunctionsPage: NextPageWithLayout = () => {
|
||||
const [selectedFunction, setSelectedFunction] = useState<DatabaseFunction | undefined>()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { EdgeFunctionDetails } from 'components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import EdgeFunctionDetailsLayout from 'components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout'
|
||||
import { PageContainer } from 'components/ui/PageContainer'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
|
||||
const PageLayout: NextPageWithLayout = () => <EdgeFunctionDetails />
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useFlag } from 'common'
|
||||
import ReportWidget from 'components/interfaces/Reports/ReportWidget'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import EdgeFunctionDetailsLayout from 'components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout'
|
||||
import { PageContainer } from 'components/ui/PageContainer'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import AreaChart from 'components/ui/Charts/AreaChart'
|
||||
import StackedBarChart from 'components/ui/Charts/StackedBarChart'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
Button,
|
||||
WarningIcon,
|
||||
} from 'ui'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
|
||||
const CHART_INTERVALS: ChartIntervals[] = [
|
||||
{
|
||||
@@ -135,7 +135,7 @@ const PageLayout: NextPageWithLayout = () => {
|
||||
return (
|
||||
<PageContainer size="full">
|
||||
<PageSection.Root>
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<div className="flex flex-row items-center gap-2 mb-4">
|
||||
<div className="flex items-center">
|
||||
{CHART_INTERVALS.map((item, i) => {
|
||||
const classes = []
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
} from 'components/interfaces/Functions/FunctionsEmptyState'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import EdgeFunctionsLayout from 'components/layouts/EdgeFunctionsLayout/EdgeFunctionsLayout'
|
||||
import { PageContainer } from 'components/ui/PageContainer'
|
||||
import { PageHeader } from 'components/ui/PageHeader'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import { PageHeader } from 'ui-patterns/PageHeader'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
@@ -19,7 +19,7 @@ import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query'
|
||||
import { DOCS_URL, IS_PLATFORM } from 'lib/constants'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import { Button, Card, Table, TableBody, TableHead, TableHeader, TableRow } from 'ui'
|
||||
import { PageSection } from 'components/ui/PageSection'
|
||||
import { PageSection } from 'ui-patterns/PageSection'
|
||||
|
||||
const EdgeFunctionsPage: NextPageWithLayout = () => {
|
||||
const { ref } = useParams()
|
||||
|
||||
@@ -12,6 +12,9 @@ export * from './src/FilterBar'
|
||||
export * from './src/GlassPanel'
|
||||
export * from './src/InnerSideMenu'
|
||||
export * from './src/McpUrlBuilder'
|
||||
export * from './src/PageContainer'
|
||||
export * from './src/PageHeader'
|
||||
export * from './src/PageSection'
|
||||
export * from './src/PopupFrame'
|
||||
export * from './src/ShimmeringLoader'
|
||||
export * from './src/TimestampInfo'
|
||||
|
||||
@@ -318,6 +318,18 @@
|
||||
"import": "./src/McpUrlBuilder/index.ts",
|
||||
"types": "./src/McpUrlBuilder/index.ts"
|
||||
},
|
||||
"./PageContainer": {
|
||||
"import": "./src/PageContainer/index.tsx",
|
||||
"types": "./src/PageContainer/index.tsx"
|
||||
},
|
||||
"./PageHeader": {
|
||||
"import": "./src/PageHeader/index.tsx",
|
||||
"types": "./src/PageHeader/index.tsx"
|
||||
},
|
||||
"./PageSection": {
|
||||
"import": "./src/PageSection/index.tsx",
|
||||
"types": "./src/PageSection/index.tsx"
|
||||
},
|
||||
"./MobileSheetNav/MobileSheetNav": {
|
||||
"import": "./src/MobileSheetNav/MobileSheetNav.tsx",
|
||||
"types": "./src/MobileSheetNav/MobileSheetNav.tsx"
|
||||
|
||||
+1
@@ -51,3 +51,4 @@ export const PageContainer = forwardRef<HTMLDivElement, PageContainerProps>(
|
||||
)
|
||||
|
||||
PageContainer.displayName = 'PageContainer'
|
||||
|
||||
File renamed without changes.
+1
@@ -217,3 +217,4 @@ export const PageSection = Object.assign(PageSectionRoot, {
|
||||
Aside: PageSectionAside,
|
||||
Content: PageSectionContent,
|
||||
})
|
||||
|
||||
Generated
+3
@@ -320,6 +320,9 @@ importers:
|
||||
'@shikijs/compat':
|
||||
specifier: ^1.1.7
|
||||
version: 1.29.2
|
||||
'@tailwindcss/container-queries':
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1(tailwindcss@3.4.1)
|
||||
'@types/lodash.template':
|
||||
specifier: 4.5.0
|
||||
version: 4.5.0
|
||||
|
||||
Reference in new issue
Block a user