Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions apps/design-system/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ The design system _references_ components rather than housing them. That’s an
With that out of the way, there are several parts of this design system that need to be manually updated after components have been added or removed (from documentation). These include:

- `config/docs.ts`: list of components in the sidebar
- `content/docs`: where the actual component documentation `.mdx` file lives
- `content/docs`: the actual component documentation
- `registry/examples.ts`: list of example components
- `registry/default/example`: where the actual example component(s) live
- `registry/charts.ts`: Chart components
- `registry/fragments.ts`: Fragment components
- `registry/default/example`: the actual example components
- `registry/charts.ts`: chart components
- `registry/fragments.ts`: fragment components

You will need to rebuild the design system’s registry each time a new example component is added. In other words: whenever a new file enters `registry`, it needs to be rebuilt. You can do that via:
You may need to rebuild the design system’s registry. You can do that via:

```bash
cd apps/design-system
Expand Down
33 changes: 33 additions & 0 deletions apps/design-system/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2293,6 +2293,39 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"empty-state-missing-route": {
name: "empty-state-missing-route",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/empty-state-missing-route")),
source: "",
files: ["registry/default/example/empty-state-missing-route.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"empty-state-zero-items-table": {
name: "empty-state-zero-items-table",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/empty-state-zero-items-table")),
source: "",
files: ["registry/default/example/empty-state-zero-items-table.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"empty-state-initial-state": {
name: "empty-state-initial-state",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/empty-state-initial-state")),
source: "",
files: ["registry/default/example/empty-state-initial-state.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"chart-area-axes": {
name: "chart-area-axes",
type: "components:block",
Expand Down
27 changes: 5 additions & 22 deletions apps/design-system/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,6 @@ interface DocsConfig {
}

export const docsConfig: DocsConfig = {
// mainNav: [
// {
// title: 'Documentation',
// href: '/docs',
// },
// {
// title: 'Components',
// href: '/docs/components/accordion',
// },
// {
// title: 'Themes',
// href: '/themes',
// },
// {
// title: 'Examples',
// href: '/examples',
// },
// {
// title: 'Blocks',
// href: '/blocks',
// },
// ],
sidebarNav: [
{
title: 'Getting Started',
Expand Down Expand Up @@ -77,6 +55,11 @@ export const docsConfig: DocsConfig = {
href: '/docs/ui-patterns/navigation',
items: [],
},
{
title: 'Empty States',
href: '/docs/ui-patterns/empty-states',
items: [],
},
],
},
{
Expand Down
38 changes: 38 additions & 0 deletions apps/design-system/content/docs/ui-patterns/empty-states.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: Empty states
description: Convey the absence of data and provide clear instruction for what to do about it.
---

At a minimum, empty states convey the fact that there is nothing to list, perform, or display on the current page. They should also provide a clear call to action for the user to take.

## Missing route

Users may accidentally navigate to a non-existent dynamic route, such as a non-existent bucket in [Storage](https://supabase.com/dashboard/project/_/storage) or a non-existent table in the [Table Editor](https://supabase.com/dashboard/project/_/editor). In these cases, follow the pattern of a centered [Admonition](../fragments/admonition) as shown below..

<ComponentPreview name="empty-state-missing-route" peekCode wide />

## Zero results

Tabular information without results—or perhaps no data to begin with—should have an empty state that matches the larger presentation.

For instance, a [Table](../components/table) may just display a single row just like it would if it had data. Dulling the TableHead text color and removing the TableCell hover state can further reinforce the lack of usable data.

<ComponentPreview name="empty-state-zero-items-table" peekCode wide />

The treatment for other layouts, such as the list of users in [Authentication](https://supabase.com/dashboard/project/_/auth/users), should match their own general styling.

## Initial state

Perhaps the user has not yet created any data yet. They might be a feature for the first time. In these cases, the empty state should provide the briefest information about the lack of data, putting more focus on the value proposition and primary action.

<ComponentPreview name="empty-state-initial-state" peekCode wide />

Keep in mind that this empty state will likely appear after a visual loading state. Consider layout shift and button placement during and after the transition.

## Components

There is not yet a shared empty state UI component. The context and needs for each placement differ enough to warrant custom components for each placement. That said, we should aim to make these as consistent as possible over time. See the below examples that might share common logic in a future centralized component.

## External references

- [_Empty States_ on GitHub Primer](https://primer.style/product/ui-patterns/empty-states/)
25 changes: 18 additions & 7 deletions apps/design-system/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,27 @@ const nextConfig = {
return [
...(BASE_PATH.length
? [
{
source: '/',
destination: BASE_PATH,
basePath: false,
permanent: false,
},
]
{
source: '/',
destination: BASE_PATH,
basePath: false,
permanent: false,
},
]
: []),
]
},
// Turbopack configuration to handle .md files with raw-loader
// This mirrors the webpack configuration added by withContentlayer
// and ensures both bundlers can process content files properly
turbopack: {
rules: {
'*.md': {
loaders: ['raw-loader'],
as: '*.js',
},
},
},
}

export default withContentlayer(nextConfig)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Button } from 'ui'
import { Plus } from 'lucide-react'
import { BucketAdd } from 'icons'

export default function EmptyStateInitialState() {
return (
<aside className="border border-dashed w-full bg-surface-100 rounded-lg px-4 py-10 flex flex-col gap-y-4 items-center text-center gap-1 text-balance">
<div className="flex flex-col gap-3 items-center text-center">
<BucketAdd size={24} strokeWidth={1.5} className="text-foreground-muted" />
<div className="flex flex-col gap-1">
<h3>Create a vector bucket</h3>
<p className="text-foreground-light text-sm">
Store, index, and query your vector embeddings at scale.
</p>
</div>
</div>
<Button size="tiny" type="primary" className="w-fit" icon={<Plus size={14} />}>
Create bucket
</Button>
</aside>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Admonition } from 'ui-patterns/admonition'
import Link from 'next/link'
import { Button } from 'ui'

const bucketId = 'user_avatars'

export default function EmptyStateMissingRoute() {
return (
<div className="flex items-center justify-center w-full h-full">
<Admonition
type="default"
className="max-w-md"
title="Unable to find bucket"
description={`${bucketId ? `The bucket “${bucketId}”` : 'This bucket'} doesn’t seem to exist.`}
>
<Button asChild type="default" className="mt-2">
<Link href="/">Head back</Link>
</Button>
</Admonition>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Card, Table, TableBody, TableHead, TableHeader, TableRow, TableCell } from 'ui'

export default function EmptyStateZeroItemsTable() {
return (
<Card className="w-full">
<Table>
<TableHeader>
<TableRow>
<TableHead className="text-foreground-muted">Table name</TableHead>
<TableHead className="text-foreground-muted">Date created</TableHead>
<TableHead />
</TableRow>
</TableHeader>
<TableBody>
<TableRow className="[&>td]:hover:bg-inherit">
<TableCell colSpan={3}>
<p className="text-sm text-foreground">No tables yet</p>
<p className="text-sm text-foreground-lighter">Connect a table from your database</p>
</TableCell>
</TableRow>
</TableBody>
</Table>
</Card>
)
}
15 changes: 15 additions & 0 deletions apps/design-system/registry/examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1263,4 +1263,19 @@ export const examples: Registry = [
type: 'components:example',
files: ['example/logs-bar-chart.tsx'],
},
{
name: 'empty-state-missing-route',
type: 'components:example',
files: ['example/empty-state-missing-route.tsx'],
},
{
name: 'empty-state-zero-items-table',
type: 'components:example',
files: ['example/empty-state-zero-items-table.tsx'],
},
{
name: 'empty-state-initial-state',
type: 'components:example',
files: ['example/empty-state-initial-state.tsx'],
},
]
19 changes: 7 additions & 12 deletions apps/docs/content/guides/auth/social-login/auth-google.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -443,13 +443,13 @@ export default OneTapComponent

<TabPanel id="react-native" label="Expo React Native">

Unlike the OAuth flow which requires the use of a web browser, the native Sign in with Google flow on Android uses the [operating system's built-in functionalities](https://developers.google.com/android/reference/com/google/android/gms/auth/api/identity/package-summary) to prompt the user for consent. Note that native sign-in has been rebranded as _One Tap sign-in on Android_ by Google, which you should not confuse with _One Tap sign in for web_, as mentioned below.
Unlike the OAuth flow which requires the use of a web browser, the native Sign in with Google flow on Android uses the [Credential Manager library](https://developer.android.com/identity/sign-in/credential-manager-siwg) to prompt the user for consent.

When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that is then sent to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.
When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that you then send to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.

By default, Supabase Auth implements nonce validation during the authentication flow. This can be disabled in production under `Authentication > Providers > Google > Skip Nonce Check` in the Dashboard, or when developing locally by setting `auth.external.<provider>.skip_nonce_check`. Only disable this if your client libraries cannot properly handle nonce verification.

When working with Expo, you can use the [`react-native-google-signin/google-signin` library](https://github.com/react-native-google-signin/google-signin#expo-installation) library to obtain an ID token that you can pass to supabase-js [`signInWithIdToken` method](/docs/reference/javascript/auth-signinwithidtoken).
When working with Expo, you can use the [`@react-native-google-signin/google-signin` library](https://github.com/react-native-google-signin/google-signin#expo-installation) to obtain an ID token that you can pass to supabase-js [`signInWithIdToken` method](/docs/reference/javascript/auth-signinwithidtoken).

Follow the [Expo installation docs](https://react-native-google-signin.github.io/docs/setting-up/expo) for installation and configuration instructions. See the [supabase-js reference](/docs/reference/javascript/initializing?example=react-native-options-async-storage) for instructions on initializing the supabase-js client in React Native.

Expand All @@ -463,7 +463,6 @@ import { supabase } from '../utils/supabase'

export default function () {
GoogleSignin.configure({
scopes: ['https://www.googleapis.com/auth/drive.readonly'],
webClientId: 'YOUR CLIENT ID FROM GOOGLE CONSOLE',
})

Expand All @@ -474,20 +473,16 @@ export default function () {
onPress={async () => {
try {
await GoogleSignin.hasPlayServices()
const userInfo = await GoogleSignin.signIn()
if (userInfo.data.idToken) {
const response = await GoogleSignin.signIn()
if (isSuccessResponse(response)) {
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: userInfo.data.idToken,
token: response.data.idToken,
})
console.log(error, data)
} else {
throw new Error('no ID token present!')
}
} catch (error: any) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
} else if (error.code === statusCodes.IN_PROGRESS) {
if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
Expand Down
13 changes: 7 additions & 6 deletions apps/docs/content/guides/platform/regions.mdx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
---
title: Available regions
subtitle: Spin up Supabase projects in our global regions
---

The following regions are available for your Supabase projects.
Each Supabase project is deployed to one primary region. Choose the location closest to your users for the best performance.

## Smart region selection
## General regions

Smart Region Selection is a feature that allows you to select from a list of broader regions. Optimizations are made to ensure that the selected region has compute capacity availability.
For most projects, we recommend choosing a general region. Supabase will deploy your project to an available AWS region within that area based on current infrastructure capacity.

<SmartRegionsList />

Currently, smart region selection is not available for read replicas and project-related operations via the management API.
Note: General regions aren’t yet supported for read replicas or management via the API.

## AWS
## Specific regions

If you prefer, you can choose an exact AWS region for your project.

<RegionsList />
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
title = "Edge Function shutdown reasons explained"
topics = [ "functions" ]
keywords = [ "shutdown", "termination", "event loop", "wall clock", "cpu time", "memory", "early drop" ]
database_id = ""

[[errors]]
http_status_code = 546
Expand Down
31 changes: 31 additions & 0 deletions apps/docs/content/troubleshooting/email-template-not-updating.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title = "Email template not updating"
topics = [ "auth", "studio" ]
keywords = [ "email", "template" ]

[[errors]]
code = "templatemailer_template_body_parse_error"
---

If your email templates are not updated, check [Auth Logs](/dashboard/project/_/logs/auth-logs) for any errors when the email is sent. When there are errors, we will use a fallback template with valid syntax so it will not show any of your customizations.

Typical errors relate to invalid syntax involving variables. Refer to [the Email Templates docs](/docs/guides/auth/auth-email-templates#terminology) for the supported variables and syntax.

**Example error message**

```
"event": "templatemailer_template_body_parse_error",
"msg": "templatemailer: template type \"recovery\": template: https://api.supabase.com/platform/auth/PROJECT_ID/templates/recovery:2: function \"default\" not defined",
```

The error shows an issue with `default` which is used in the invalid syntax below.

```
{{ .Data.display_name | default: "test" }}
```

The final correct syntax after removing invalid syntax.

```
{{ .Data.display_name }}
```
2 changes: 1 addition & 1 deletion apps/docs/docs/ref/api/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ All API requests must be authenticated and made over HTTPS.

## Rate limits

The rate limit for Management API is 60 requests per one minute per user, and applies cumulatively across all requests made with your personal access tokens.
The rate limit for Management API is 120 requests per one minute per user, and applies cumulatively across all requests made with your personal access tokens.

If you exceed this limit, all Management API calls for the next minute will be blocked, resulting in a HTTP 429 response.

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/docs/ref/api/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ hideTitle: true

## Rate limits

The rate limit for Management API is 60 requests per one minute per user, and applies cumulatively across all requests made with your personal access tokens.
The rate limit for Management API is 120 requests per one minute per user, and applies cumulatively across all requests made with your personal access tokens.

If you exceed this limit, all Management API calls for the next minute will be blocked, resulting in a HTTP 429 response.

Expand Down
Loading
Loading