Merge pull request #12187 from supabase/feat/add-autodocs-to-table-editor
feat/add-autodocs-to-table-editor
No files matched your search
@@ -305,6 +305,7 @@ export * from './src/components/Icon/icons/IconSun'
|
||||
export * from './src/components/Icon/icons/IconSunrise'
|
||||
export * from './src/components/Icon/icons/IconSunset'
|
||||
export * from './src/components/Icon/icons/IconTablet'
|
||||
export * from './src/components/Icon/icons/IconTable'
|
||||
export * from './src/components/Icon/icons/IconTag'
|
||||
export * from './src/components/Icon/icons/IconTarget'
|
||||
export * from './src/components/Icon/icons/IconTerminal'
|
||||
|
||||
@@ -234,6 +234,7 @@ export default {
|
||||
IconSunrise: 'src/components/Icon/icons/IconSunrise/index.tsx',
|
||||
IconSunset: 'src/components/Icon/icons/IconSunset/index.tsx',
|
||||
IconTablet: 'src/components/Icon/icons/IconTablet/index.tsx',
|
||||
IconTable: 'src/components/Icon/icons/IconTable/index.tsx',
|
||||
IconTag: 'src/components/Icon/icons/IconTag/index.tsx',
|
||||
IconTarget: 'src/components/Icon/icons/IconTarget/index.tsx',
|
||||
IconTerminal: 'src/components/Icon/icons/IconTerminal/index.tsx',
|
||||
@@ -284,5 +285,5 @@ export default {
|
||||
IconZapOff: 'src/components/Icon/icons/IconZapOff/index.tsx',
|
||||
IconZap: 'src/components/Icon/icons/IconZap/index.tsx',
|
||||
IconZoomIn: 'src/components/Icon/icons/IconZoomIn/index.tsx',
|
||||
IconZoomOut: 'src/components/Icon/icons/IconZoomOut/index.tsx'
|
||||
}
|
||||
IconZoomOut: 'src/components/Icon/icons/IconZoomOut/index.tsx',
|
||||
}
|
||||
@@ -939,6 +939,10 @@
|
||||
"name": "Tablet",
|
||||
"path": "react-feather/dist/icons/tablet"
|
||||
},
|
||||
"Table": {
|
||||
"name": "Table",
|
||||
"path": "react-feather/dist/icons/table"
|
||||
},
|
||||
"Tag": {
|
||||
"name": "Tag",
|
||||
"path": "react-feather/dist/icons/tag"
|
||||
|
||||
@@ -241,6 +241,7 @@ export const Sun: Icon = require(`react-feather/dist/icons/sun`)
|
||||
export const Sunrise: Icon = require(`react-feather/dist/icons/sunrise`)
|
||||
export const Sunset: Icon = require(`react-feather/dist/icons/sunset`)
|
||||
export const Tablet: Icon = require(`react-feather/dist/icons/tablet`)
|
||||
export const Table: Icon = require(`react-feather/dist/icons/table`)
|
||||
export const Tag: Icon = require(`react-feather/dist/icons/tag`)
|
||||
export const Target: Icon = require(`react-feather/dist/icons/target`)
|
||||
export const Terminal: Icon = require(`react-feather/dist/icons/terminal`)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// DO NOT EDIT
|
||||
// this file is generated by /internals/populate-icons script
|
||||
import * as React from 'react'
|
||||
// @ts-ignore
|
||||
import { Table } from 'react-feather'
|
||||
import IconBase from './../../IconBase'
|
||||
|
||||
function IconTable(props: any) {
|
||||
return <IconBase icon={Table} {...props} />
|
||||
}
|
||||
|
||||
export default IconTable
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default as IconTable } from './IconTable'
|
||||
@@ -234,6 +234,7 @@ export * as IconSun from './IconSun'
|
||||
export * as IconSunrise from './IconSunrise'
|
||||
export * as IconSunset from './IconSunset'
|
||||
export * as IconTablet from './IconTablet'
|
||||
export * as IconTable from './IconTable'
|
||||
export * as IconTag from './IconTag'
|
||||
export * as IconTarget from './IconTarget'
|
||||
export * as IconTerminal from './IconTerminal'
|
||||
|
||||
@@ -22,7 +22,7 @@ interface CustomProps {
|
||||
children?: React.ReactNode
|
||||
header?: string | React.ReactNode
|
||||
visible: boolean
|
||||
size?: 'medium' | 'large' | 'xlarge'
|
||||
size?: 'medium' | 'large' | 'xlarge' | 'xxlarge'
|
||||
loading?: boolean
|
||||
align?: 'right' | 'left'
|
||||
hideFooter?: boolean
|
||||
|
||||
@@ -998,6 +998,7 @@ export default {
|
||||
medium: `w-screen max-w-md h-full`,
|
||||
large: `w-screen max-w-2xl h-full`,
|
||||
xlarge: `w-screen max-w-3xl h-full`,
|
||||
xxlarge: `w-screen max-w-4xl h-full`,
|
||||
},
|
||||
align: {
|
||||
left: `
|
||||
|
||||
@@ -64,6 +64,15 @@ export interface SupabaseGridProps {
|
||||
*/
|
||||
onEditRow?: (row: SupaRow) => void
|
||||
onError?: (error: any) => void
|
||||
/**
|
||||
* Toggle api preview panel open
|
||||
*/
|
||||
apiPreviewPanelOpen?: boolean
|
||||
setApiPreviewPanelOpen?: () => void
|
||||
/**
|
||||
* Refresh the docs after a change is made to the table
|
||||
*/
|
||||
refreshDocs: () => void
|
||||
onExpandJSONEditor: (column: string, row: SupaRow) => void
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export type showApiKey = {
|
||||
key: string
|
||||
name: string
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import { FC, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { Button, IconDownload, IconExternalLink } from 'ui'
|
||||
import { API_ADMIN_URL } from 'lib/constants'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { useParams, useStore } from 'hooks'
|
||||
import CodeSnippet from 'components/to-be-cleaned/Docs/CodeSnippet'
|
||||
|
||||
interface Props {
|
||||
selectedLang: string
|
||||
}
|
||||
|
||||
export default function GeneratingTypes({ selectedLang }: Props) {
|
||||
const { ref } = useParams()
|
||||
const { ui } = useStore()
|
||||
const [isGeneratingTypes, setIsGeneratingTypes] = useState(false)
|
||||
|
||||
const generateTypes = async () => {
|
||||
setIsGeneratingTypes(true)
|
||||
const res = await get(`${API_ADMIN_URL}/projects/${ref}/types/typescript`)
|
||||
|
||||
if (!res.error) {
|
||||
let element = document.createElement('a')
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(res.types))
|
||||
element.setAttribute('download', 'supabase.ts')
|
||||
element.style.display = 'none'
|
||||
document.body.appendChild(element)
|
||||
element.click()
|
||||
document.body.removeChild(element)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully generated types! File is being downloaded`,
|
||||
})
|
||||
} else {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to generate types: ${res.error.message}`,
|
||||
error: res.error,
|
||||
})
|
||||
}
|
||||
|
||||
setIsGeneratingTypes(false)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<h2 className="doc-heading flex items-center justify-between">
|
||||
<span>Generating types</span>
|
||||
<Link href="https://supabase.com/docs/guides/database/api/generating-types">
|
||||
<a target="_blank">
|
||||
<Button type="default" icon={<IconExternalLink />}>
|
||||
Documentation
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</h2>
|
||||
<div className="doc-section">
|
||||
<article className="text ">
|
||||
<p>
|
||||
Supabase APIs are generated from your database, which means that we can use database
|
||||
introspection to generate type-safe API definitions.
|
||||
</p>
|
||||
<p>
|
||||
You can generate types from your database either through the{' '}
|
||||
<Link href="https://supabase.com/docs/guides/database/api/generating-types">
|
||||
Supabase CLI
|
||||
</Link>
|
||||
, or by downloading the types file via the button on the right and importing it in your
|
||||
application within <code>src/index.ts</code>.
|
||||
</p>
|
||||
</article>
|
||||
<article
|
||||
className={`code ${selectedLang === 'js' ? 'flex items-center justify-center' : ''}`}
|
||||
>
|
||||
<div className="grid gap-2">
|
||||
<p className="text-center">
|
||||
{selectedLang === 'js' && (
|
||||
<Button
|
||||
type="default"
|
||||
disabled={isGeneratingTypes}
|
||||
loading={isGeneratingTypes}
|
||||
icon={<IconDownload strokeWidth={1.5} />}
|
||||
onClick={generateTypes}
|
||||
>
|
||||
Generate and download types
|
||||
</Button>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-scale-1100 bg-scale-200 p-4 mt-2">
|
||||
Remember to re-generate and download this file as you make changes to your tables.
|
||||
</p>
|
||||
</div>
|
||||
<CodeSnippet selectedLang={selectedLang} snippet={localSnippets.cliLogin()} />
|
||||
<CodeSnippet
|
||||
selectedLang={selectedLang}
|
||||
snippet={localSnippets.generateTypes(ref ?? '')}
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const localSnippets = {
|
||||
cliLogin: () => ({
|
||||
title: 'Login via the CLI with your Personal Access Token',
|
||||
bash: {
|
||||
code: `
|
||||
npx supabase login
|
||||
`,
|
||||
},
|
||||
}),
|
||||
generateTypes: (ref: string) => ({
|
||||
title: 'Generate types',
|
||||
bash: {
|
||||
code: `
|
||||
npx supabase gen types typescript --project-id "${ref}" --schema public > types/supabase.ts
|
||||
`,
|
||||
},
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import { FC } from 'react'
|
||||
import { Button, Dropdown, IconKey } from 'ui'
|
||||
import { checkPermissions } from 'hooks'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { showApiKey } from 'components/interfaces/Docs/Docs.types'
|
||||
|
||||
const DEFAULT_KEY = { name: 'hide', key: 'SUPABASE_KEY' }
|
||||
|
||||
interface Props {
|
||||
selectedLang: string
|
||||
setSelectedLang: (selectedLang: string) => void
|
||||
showApiKey: showApiKey
|
||||
setShowApiKey: (showApiKey: showApiKey) => void
|
||||
apiKey: string | undefined
|
||||
autoApiService: any
|
||||
}
|
||||
|
||||
const LangSelector: FC<Props> = ({
|
||||
selectedLang,
|
||||
setSelectedLang,
|
||||
showApiKey,
|
||||
setShowApiKey,
|
||||
apiKey,
|
||||
autoApiService,
|
||||
}) => {
|
||||
const canReadServiceKey = checkPermissions(
|
||||
PermissionAction.READ,
|
||||
'service_api_keys.service_role_key'
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="p-1 w-1/2 ml-auto">
|
||||
<div className="z-0 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedLang('js')}
|
||||
className={`${
|
||||
selectedLang == 'js'
|
||||
? 'bg-scale-300 font-medium text-scale-1200 dark:bg-scale-200'
|
||||
: 'bg-scale-100 text-scale-900 dark:bg-scale-100'
|
||||
} relative inline-flex items-center border-r border-scale-200 p-1 px-2 text-sm transition hover:text-scale-1200 focus:outline-none`}
|
||||
>
|
||||
JavaScript
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedLang('bash')}
|
||||
className={`${
|
||||
selectedLang == 'bash'
|
||||
? 'bg-scale-300 font-medium text-scale-1200 dark:bg-scale-200'
|
||||
: 'bg-scale-100 text-scale-900 dark:bg-scale-100'
|
||||
} relative inline-flex items-center border-r border-scale-200 p-1 px-2 text-sm transition hover:text-scale-1200 focus:outline-none`}
|
||||
>
|
||||
Bash
|
||||
</button>
|
||||
{selectedLang == 'bash' && (
|
||||
<div className="flex">
|
||||
<div className="flex items-center gap-2 p-1 pl-2 text-xs text-scale-900">
|
||||
<IconKey size={12} strokeWidth={1.5} />
|
||||
<span>Project API key :</span>
|
||||
</div>
|
||||
<Dropdown
|
||||
align="end"
|
||||
side="bottom"
|
||||
className="cursor-pointer border-none bg-transparent p-0 pl-2 pr-8 text-sm text-scale-900"
|
||||
overlay={
|
||||
<>
|
||||
<Dropdown.Item key="hide" onClick={() => setShowApiKey(DEFAULT_KEY)}>
|
||||
hide
|
||||
</Dropdown.Item>
|
||||
{apiKey && (
|
||||
<Dropdown.Item
|
||||
key="anon"
|
||||
onClick={() =>
|
||||
setShowApiKey({
|
||||
key: apiKey,
|
||||
name: 'anon (public)',
|
||||
})
|
||||
}
|
||||
>
|
||||
anon (public)
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
{canReadServiceKey && (
|
||||
<Dropdown.Item
|
||||
key="service"
|
||||
onClick={() =>
|
||||
setShowApiKey({
|
||||
key: autoApiService.serviceApiKey,
|
||||
name: 'service_role (secret)',
|
||||
})
|
||||
}
|
||||
>
|
||||
service_role (secret)
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button type="default">{showApiKey.name}</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LangSelector
|
||||
@@ -2,6 +2,7 @@ import Snippets from 'components/to-be-cleaned/Docs/Snippets'
|
||||
import CodeSnippet from 'components/to-be-cleaned/Docs/CodeSnippet'
|
||||
import Param from 'components/to-be-cleaned/Docs/Param'
|
||||
import Description from 'components/to-be-cleaned/Docs/Description'
|
||||
import { IconTable } from 'ui'
|
||||
|
||||
const ResourceContent = ({
|
||||
autoApiService,
|
||||
@@ -29,12 +30,18 @@ const ResourceContent = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="text-scale-1200mt-0">
|
||||
<span className="px-6 py-2 text-2xl">{resourceId}</span>
|
||||
<h2 className="doc-section__table-name text-scale-1200 mt-0 flex items-center px-6 gap-2">
|
||||
<span className="bg-slate-300 dark:bg-slate-400 p-2 rounded-lg">
|
||||
<IconTable size="small" />
|
||||
</span>
|
||||
<span className="text-2xl font-bold">{resourceId}</span>
|
||||
</h2>
|
||||
|
||||
<div className="doc-section">
|
||||
<article className="text ">
|
||||
<article className="text">
|
||||
<label className="font-mono text-xs uppercase text-scale-900 inline-block mb-2">
|
||||
Description
|
||||
</label>
|
||||
<Description
|
||||
content={description}
|
||||
metadata={{ table: resourceId }}
|
||||
@@ -47,7 +54,7 @@ const ResourceContent = ({
|
||||
<div>
|
||||
{properties.map((x) => (
|
||||
<div className="doc-section py-4" key={x.id}>
|
||||
<article className="text">
|
||||
<div className="text">
|
||||
<Param
|
||||
key={x.id}
|
||||
name={x.id}
|
||||
@@ -61,8 +68,8 @@ const ResourceContent = ({
|
||||
}}
|
||||
onDesciptionUpdated={refreshDocs}
|
||||
/>
|
||||
</article>
|
||||
<article className="code">
|
||||
</div>
|
||||
<div className="code">
|
||||
<CodeSnippet
|
||||
selectedLang={selectedLang}
|
||||
snippet={Snippets.readColumns({
|
||||
@@ -73,7 +80,7 @@ const ResourceContent = ({
|
||||
columnName: x.id,
|
||||
})}
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -87,8 +94,8 @@ const ResourceContent = ({
|
||||
To read rows in <code>{resourceId}</code>, use the <code>select</code> method.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://supabase.com/docs/client/select" target="_blank">
|
||||
Learn more.
|
||||
<a href="https://supabase.com/docs/reference/javascript/select" target="_blank">
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</article>
|
||||
@@ -120,8 +127,11 @@ const ResourceContent = ({
|
||||
<h4 className="mt-0 text-white">Filtering</h4>
|
||||
<p>Supabase provides a wide range of filters.</p>
|
||||
<p>
|
||||
<a href="https://supabase.com/docs/client/using-filters" target="_blank">
|
||||
Learn more.
|
||||
<a
|
||||
href="https://supabase.com/docs/reference/javascript/using-filters"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</article>
|
||||
@@ -147,8 +157,8 @@ const ResourceContent = ({
|
||||
<code>insert</code> will also return the replaced values for UPSERT.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://supabase.com/docs/client/insert" target="_blank">
|
||||
Learn more.
|
||||
<a href="https://supabase.com/docs/reference/javascript/insert" target="_blank">
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</article>
|
||||
@@ -183,8 +193,8 @@ const ResourceContent = ({
|
||||
<code>update</code> will also return the replaced values for UPDATE.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://supabase.com/docs/client/update" target="_blank">
|
||||
Learn more.
|
||||
<a href="https://supabase.com/docs/reference/javascript/update" target="_blank">
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</article>
|
||||
@@ -207,8 +217,8 @@ const ResourceContent = ({
|
||||
default, so remember to specify your filters!
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://supabase.com/docs/client/delete" target="_blank">
|
||||
Learn more.
|
||||
<a href="https://supabase.com/docs/reference/javascript/delete" target="_blank">
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</article>
|
||||
@@ -230,8 +240,8 @@ const ResourceContent = ({
|
||||
users depending on Row Level Security (RLS) policies.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://supabase.com/docs/client/subscribe" target="_blank">
|
||||
Learn more.
|
||||
<a href="https://supabase.com/docs/reference/javascript/subscribe" target="_blank">
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</article>
|
||||
@@ -264,21 +274,6 @@ const ResourceContent = ({
|
||||
</article>
|
||||
</div>
|
||||
</>
|
||||
<>
|
||||
<h3 className="text-scale-1200 mt-4 px-6">Much more</h3>
|
||||
<div className="doc-section py-4">
|
||||
<article className="text ">
|
||||
<p>
|
||||
These docs are a work in progress! See our{' '}
|
||||
<a href="https://supabase.com/docs/" target="_blank">
|
||||
docs
|
||||
</a>{' '}
|
||||
for the additional functionality Supabase has to offer.
|
||||
</p>
|
||||
</article>
|
||||
<article className="code"></article>
|
||||
</div>
|
||||
</>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Button, IconAlertCircle, IconCode, IconLock } from 'ui'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { Button, IconAlertCircle, IconLock } from 'ui'
|
||||
import type { PostgresPolicy, PostgresTable } from '@supabase/postgres-meta'
|
||||
|
||||
import { useStore, checkPermissions } from 'hooks'
|
||||
@@ -9,9 +9,17 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
interface Props {
|
||||
table: PostgresTable
|
||||
apiPreviewPanelOpen: boolean
|
||||
setApiPreviewPanelOpen: (apiPreviewPanelOpen: boolean) => void
|
||||
refreshDocs: () => void
|
||||
}
|
||||
|
||||
const GridHeaderActions: FC<Props> = ({ table }) => {
|
||||
const GridHeaderActions: FC<Props> = ({
|
||||
table,
|
||||
apiPreviewPanelOpen,
|
||||
setApiPreviewPanelOpen,
|
||||
refreshDocs,
|
||||
}) => {
|
||||
const { ui, meta } = useStore()
|
||||
const projectRef = ui.selectedProject?.ref
|
||||
const policies = meta.policies.list((policy: PostgresPolicy) => policy.table_id === table.id)
|
||||
@@ -20,6 +28,24 @@ const GridHeaderActions: FC<Props> = ({ table }) => {
|
||||
!checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables') &&
|
||||
!checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'columns')
|
||||
|
||||
function handlePreviewToggle() {
|
||||
setApiPreviewPanelOpen(!apiPreviewPanelOpen)
|
||||
refreshDocs()
|
||||
}
|
||||
|
||||
const RenderAPIPreviewToggle = () => {
|
||||
return (
|
||||
<Button
|
||||
size="tiny"
|
||||
type="default"
|
||||
icon={<IconCode size={14} strokeWidth={2} />}
|
||||
onClick={handlePreviewToggle}
|
||||
>
|
||||
API Quickstart
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-3">
|
||||
{isReadOnly && (
|
||||
@@ -44,6 +70,9 @@ const GridHeaderActions: FC<Props> = ({ table }) => {
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)}
|
||||
<div className="mt-[1px]">
|
||||
<RenderAPIPreviewToggle />
|
||||
</div>
|
||||
<Link href={`/project/${projectRef}/auth/policies#${table.id}`}>
|
||||
<a>
|
||||
<Button
|
||||
|
||||
@@ -6,12 +6,20 @@ import type { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { SchemaView } from 'types'
|
||||
import { checkPermissions, useFlag, useStore } from 'hooks'
|
||||
import { checkPermissions, useFlag, useStore, useParams } from 'hooks'
|
||||
import GridHeaderActions from './GridHeaderActions'
|
||||
import NotFoundState from './NotFoundState'
|
||||
import SidePanelEditor from './SidePanelEditor'
|
||||
import { Dictionary, parseSupaTable, SupabaseGrid, SupabaseGridRef } from 'components/grid'
|
||||
import { IconBookOpen, SidePanel } from 'ui'
|
||||
import ActionBar from './SidePanelEditor/ActionBar'
|
||||
import { GeneralContent, ResourceContent } from '../Docs'
|
||||
import { useProjectApiQuery } from 'data/config/project-api-query'
|
||||
import { useProjectJsonSchemaQuery } from 'data/docs/project-json-schema-query'
|
||||
import { snakeToCamel } from 'lib/helpers'
|
||||
import { JsonEditValue } from './SidePanelEditor/RowEditor/RowEditor.types'
|
||||
import LangSelector from '../Docs/LangSelector'
|
||||
import GeneratingTypes from '../Docs/GeneratingTypes'
|
||||
|
||||
interface Props {
|
||||
/** Theme for the editor */
|
||||
@@ -61,11 +69,27 @@ const TableGridEditor: FC<Props> = ({
|
||||
}) => {
|
||||
const { meta, ui, vault } = useStore()
|
||||
const router = useRouter()
|
||||
const { ref: projectRef, page, id } = useParams()
|
||||
const gridRef = useRef<SupabaseGridRef>(null)
|
||||
const projectRef = ui.selectedProject?.ref
|
||||
|
||||
const tables = meta.tables.list()
|
||||
|
||||
const { data: settings } = useProjectApiQuery({ projectRef: projectRef })
|
||||
|
||||
const autoApiService = {
|
||||
...settings?.autoApiService,
|
||||
endpoint: `${settings?.autoApiService.protocol ?? 'https'}://${
|
||||
settings?.autoApiService.endpoint ?? '-'
|
||||
}`,
|
||||
}
|
||||
const DEFAULT_KEY = { name: 'hide', key: 'SUPABASE_KEY' }
|
||||
|
||||
const isVaultEnabled = useFlag('vaultExtension')
|
||||
const [encryptedColumns, setEncryptedColumns] = useState([])
|
||||
const [apiPreviewPanelOpen, setApiPreviewPanelOpen] = useState(false)
|
||||
|
||||
const [selectedLang, setSelectedLang] = useState<any>('js')
|
||||
const [showApiKey, setShowApiKey] = useState<any>(DEFAULT_KEY)
|
||||
|
||||
const isReadOnly =
|
||||
!checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables') &&
|
||||
@@ -76,6 +100,48 @@ const TableGridEditor: FC<Props> = ({
|
||||
setEncryptedColumns(columns)
|
||||
}
|
||||
|
||||
function getResourcesFromJsonSchema(value: any) {
|
||||
const { paths } = value || {}
|
||||
const functionPath = 'rpc/'
|
||||
let resources: any = {}
|
||||
|
||||
Object.entries(paths || []).forEach(([name, val]) => {
|
||||
let trimmed = name.slice(1)
|
||||
let id = trimmed.replace(functionPath, '')
|
||||
let displayName = id.replace(/_/g, ' ')
|
||||
let camelCase = snakeToCamel(id)
|
||||
let enriched = { id, displayName, camelCase }
|
||||
if (!trimmed.length) return
|
||||
else resources[id] = enriched
|
||||
})
|
||||
|
||||
return resources
|
||||
}
|
||||
|
||||
const apiService = settings?.autoApiService
|
||||
const anonKey = apiService?.service_api_keys.find((x) => x.name === 'anon key')
|
||||
? apiService.defaultApiKey
|
||||
: undefined
|
||||
const swaggerUrl = settings?.autoApiService?.restUrl
|
||||
|
||||
const {
|
||||
data: jsonSchema,
|
||||
error: jsonSchemaError,
|
||||
refetch,
|
||||
} = useProjectJsonSchemaQuery({
|
||||
projectRef,
|
||||
swaggerUrl,
|
||||
apiKey: anonKey,
|
||||
})
|
||||
|
||||
if (jsonSchemaError) console.log('jsonSchemaError', jsonSchemaError)
|
||||
|
||||
const resources = getResourcesFromJsonSchema(jsonSchema)
|
||||
|
||||
const refreshDocs = async () => {
|
||||
await refetch()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTable !== undefined && selectedTable.id !== undefined && isVaultEnabled) {
|
||||
getEncryptedColumns(selectedTable)
|
||||
@@ -83,7 +149,7 @@ const TableGridEditor: FC<Props> = ({
|
||||
}, [selectedTable?.id])
|
||||
|
||||
if (isUndefined(selectedTable)) {
|
||||
return <NotFoundState id={Number(router.query.id)} />
|
||||
return <NotFoundState id={Number(id)} />
|
||||
}
|
||||
|
||||
const tableId = selectedTable?.id
|
||||
@@ -177,8 +243,16 @@ const TableGridEditor: FC<Props> = ({
|
||||
editable={!isReadOnly && canUpdateTables && canEditViaTableEditor}
|
||||
schema={selectedTable.schema}
|
||||
table={gridTable}
|
||||
refreshDocs={refreshDocs}
|
||||
headerActions={
|
||||
canEditViaTableEditor && <GridHeaderActions table={selectedTable as PostgresTable} />
|
||||
canEditViaTableEditor && (
|
||||
<GridHeaderActions
|
||||
table={selectedTable as PostgresTable}
|
||||
apiPreviewPanelOpen={apiPreviewPanelOpen}
|
||||
setApiPreviewPanelOpen={setApiPreviewPanelOpen}
|
||||
refreshDocs={refreshDocs}
|
||||
/>
|
||||
)
|
||||
}
|
||||
onAddColumn={onAddColumn}
|
||||
onEditColumn={onSelectEditColumn}
|
||||
@@ -206,6 +280,82 @@ const TableGridEditor: FC<Props> = ({
|
||||
closePanel={onClosePanel}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SidePanel
|
||||
key="WrapperTableEditor"
|
||||
size="xxlarge"
|
||||
visible={apiPreviewPanelOpen}
|
||||
onCancel={() => setApiPreviewPanelOpen(false)}
|
||||
header={
|
||||
<span className="flex items-center gap-2">
|
||||
<IconBookOpen size="tiny" />
|
||||
API
|
||||
</span>
|
||||
}
|
||||
customFooter={
|
||||
<ActionBar
|
||||
backButtonLabel="Close"
|
||||
hideApply={true}
|
||||
formId="wrapper-table-editor-form"
|
||||
closePanel={() => setApiPreviewPanelOpen(false)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="Docs Docs--table-editor">
|
||||
<SidePanel.Content>
|
||||
{jsonSchemaError ? (
|
||||
<div className="p-6 mx-auto text-center sm:w-full md:w-3/4">
|
||||
<div className="text-scale-1000">
|
||||
<p>Error connecting to API</p>
|
||||
<p>{`${jsonSchemaError}`}</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{jsonSchema ? (
|
||||
<>
|
||||
<div className="sticky top-0 z-10 bg-scale-100 dark:bg-scale-300">
|
||||
<LangSelector
|
||||
selectedLang={selectedLang}
|
||||
setSelectedLang={setSelectedLang}
|
||||
showApiKey={showApiKey}
|
||||
setShowApiKey={setShowApiKey}
|
||||
apiKey={anonKey}
|
||||
autoApiService={autoApiService}
|
||||
/>
|
||||
</div>
|
||||
<GeneralContent
|
||||
autoApiService={autoApiService}
|
||||
selectedLang={selectedLang}
|
||||
showApiKey={true}
|
||||
page={page}
|
||||
/>
|
||||
|
||||
<GeneratingTypes selectedLang={selectedLang} />
|
||||
|
||||
{jsonSchema?.definitions && (
|
||||
<ResourceContent
|
||||
autoApiService={autoApiService}
|
||||
selectedLang={selectedLang}
|
||||
resourceId={tables.find((table) => table.id === Number(id))?.name}
|
||||
resources={resources}
|
||||
definitions={jsonSchema.definitions}
|
||||
paths={jsonSchema.paths}
|
||||
showApiKey={showApiKey.key}
|
||||
refreshDocs={refreshDocs}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="p-6 mx-auto text-center sm:w-full md:w-3/4">
|
||||
<h3 className="text-lg">Building docs ...</h3>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</SidePanel.Content>
|
||||
</div>
|
||||
</SidePanel>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import SimpleCodeBlock from 'components/to-be-cleaned/SimpleCodeBlock'
|
||||
const CodeSnippet = ({ selectedLang, snippet }) => {
|
||||
if (!snippet[selectedLang]) return null
|
||||
return (
|
||||
<>
|
||||
<div className="codeblock-container">
|
||||
<h4>{snippet.title}</h4>
|
||||
<SimpleCodeBlock className={snippet[selectedLang].language}>
|
||||
{snippet[selectedLang].code}
|
||||
</SimpleCodeBlock>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default CodeSnippet
|
||||
@@ -1,59 +0,0 @@
|
||||
import Snippets from '../Snippets'
|
||||
import CodeSnippet from '../CodeSnippet'
|
||||
|
||||
export default function Introduction({ autoApiService, selectedLang }) {
|
||||
return (
|
||||
<>
|
||||
<h2 className="doc-heading">Introduction</h2>
|
||||
<div className="doc-section">
|
||||
<article className="text ">
|
||||
<p>
|
||||
This API provides an easy way to integrate with your Postgres database. The API
|
||||
documentation below is specifically generated for your database.
|
||||
</p>
|
||||
<p>
|
||||
This is an <b>auto-generating</b> API, so as you make changes to your database, this
|
||||
documentation will change too.
|
||||
</p>
|
||||
<p>
|
||||
<b>Please note:</b> if you make changes to a field (column) name or type, the API
|
||||
interface for those fields will change correspondingly. Therefore, please make sure to
|
||||
update your API implementation accordingly whenever you make changes to your Supabase
|
||||
schema from the graphical interface.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<h2 className="doc-heading">API URL</h2>
|
||||
<div className="doc-section ">
|
||||
<article className="text ">
|
||||
<p>The API URL for your project.</p>
|
||||
</article>
|
||||
<article className="code">
|
||||
<CodeSnippet
|
||||
selectedLang={selectedLang}
|
||||
snippet={Snippets.endpoint(autoApiService.endpoint)}
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<h2 className="doc-heading">Client Libraries</h2>
|
||||
<div className="doc-section">
|
||||
<article className="text ">
|
||||
<p>Your API consists of both a RESTful interface and a Realtime interface.</p>
|
||||
<p>
|
||||
For interacting with the Realtime streams, we provide client libraries that handle the
|
||||
websockets.
|
||||
</p>
|
||||
</article>
|
||||
<article className="code">
|
||||
<CodeSnippet selectedLang={selectedLang} snippet={Snippets.install()} />
|
||||
<CodeSnippet
|
||||
selectedLang={selectedLang}
|
||||
snippet={Snippets.init(autoApiService.endpoint)}
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import Snippets from '../Snippets'
|
||||
import CodeSnippet from '../CodeSnippet'
|
||||
import Image from 'next/image'
|
||||
import { useStore } from 'hooks'
|
||||
import { AutoApiService } from 'data/config/project-api-query'
|
||||
|
||||
const libs = [
|
||||
{
|
||||
name: 'Javascript',
|
||||
url: 'https://supabase.com/docs/reference/javascript/introduction',
|
||||
icon: 'javascript',
|
||||
},
|
||||
{ name: 'Flutter', url: 'https://supabase.com/docs/reference/dart/introduction', icon: 'dart' },
|
||||
{
|
||||
name: 'Python',
|
||||
url: 'https://supabase.com/docs/reference/python/introduction',
|
||||
icon: 'python',
|
||||
},
|
||||
{ name: 'C#', url: 'https://supabase.com/docs/reference/csharp/introduction', icon: 'csharp' },
|
||||
]
|
||||
|
||||
interface Props {
|
||||
autoApiService: AutoApiService
|
||||
selectedLang: string
|
||||
}
|
||||
|
||||
export default function Introduction({ autoApiService, selectedLang }: Props) {
|
||||
const { ui } = useStore()
|
||||
const { isDarkTheme } = ui
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="doc-heading">Introduction</h2>
|
||||
<div className="doc-section doc-section--introduction">
|
||||
<article className="text ">
|
||||
<p>
|
||||
This API provides an easy way to integrate with your Postgres database. The API
|
||||
documentation below is specifically generated for your database.
|
||||
</p>
|
||||
<p>
|
||||
This is an <b>auto-generating</b> API, so as you make changes to your database, this
|
||||
documentation will change too.
|
||||
</p>
|
||||
<p>
|
||||
<b>Note:</b> if you make changes to a field (column) name or type, the API interface for
|
||||
those fields will change correspondingly. Therefore, please make sure to update your API
|
||||
implementation accordingly whenever you make changes to your Supabase schema from the
|
||||
graphical interface.
|
||||
</p>
|
||||
|
||||
<div className="not-prose mb-6">
|
||||
<p>Read the reference documentation:</p>
|
||||
<div className="flex items-center gap-4 mt-2">
|
||||
{libs.map((lib) => (
|
||||
<a
|
||||
href={lib.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="
|
||||
flex items-center gap-1 rounded-md px-3 py-1
|
||||
bg-scale-300 dark:bg-scale-500
|
||||
hover:bg-scale-500 hover:dark:bg-scale-700 transition-colors
|
||||
!text-scale-1100 hover:text-scale-1100
|
||||
"
|
||||
>
|
||||
<Image
|
||||
src={`/img/icons/reference-${isDarkTheme ? lib.icon : `${lib.icon}-light`}.svg`}
|
||||
width={16}
|
||||
height={16}
|
||||
alt={lib.name}
|
||||
/>
|
||||
{lib.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<h2 className="doc-heading">API URL</h2>
|
||||
<div className="doc-section ">
|
||||
<article className="text ">
|
||||
<p>The API URL for your project.</p>
|
||||
</article>
|
||||
<article className="code">
|
||||
<CodeSnippet
|
||||
selectedLang={selectedLang}
|
||||
snippet={Snippets.endpoint(autoApiService.endpoint)}
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<h2 className="doc-heading">Client Libraries</h2>
|
||||
<div className="doc-section doc-section--client-libraries">
|
||||
<article className="text">
|
||||
<p>Your API consists of both a RESTful interface and a Realtime interface.</p>
|
||||
<p>
|
||||
For interacting with the Realtime streams, we provide client libraries that handle the
|
||||
websockets.
|
||||
</p>
|
||||
</article>
|
||||
<article className="code">
|
||||
<CodeSnippet selectedLang={selectedLang} snippet={Snippets.install()} />
|
||||
<CodeSnippet
|
||||
selectedLang={selectedLang}
|
||||
snippet={Snippets.init(autoApiService.endpoint)}
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Button, IconDownload, IconExternalLink } from 'ui'
|
||||
import { useParams, useStore } from 'hooks'
|
||||
import { API_ADMIN_URL } from 'lib/constants'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { useParams } from 'hooks'
|
||||
import CodeSnippet from '../../CodeSnippet'
|
||||
import GeneratingTypes from 'components/interfaces/Docs/GeneratingTypes'
|
||||
|
||||
interface Props {
|
||||
selectedLang: string
|
||||
@@ -12,35 +10,6 @@ interface Props {
|
||||
|
||||
const Introduction: FC<Props> = ({ selectedLang }) => {
|
||||
const { ref } = useParams()
|
||||
const { ui } = useStore()
|
||||
const [isGeneratingTypes, setIsGeneratingTypes] = useState(false)
|
||||
|
||||
const generateTypes = async () => {
|
||||
setIsGeneratingTypes(true)
|
||||
const res = await get(`${API_ADMIN_URL}/projects/${ref}/types/typescript`)
|
||||
|
||||
if (!res.error) {
|
||||
let element = document.createElement('a')
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(res.types))
|
||||
element.setAttribute('download', 'supabase.ts')
|
||||
element.style.display = 'none'
|
||||
document.body.appendChild(element)
|
||||
element.click()
|
||||
document.body.removeChild(element)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully generated types! File is being downloaded`,
|
||||
})
|
||||
} else {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to generate types: ${res.error.message}`,
|
||||
error: res.error,
|
||||
})
|
||||
}
|
||||
|
||||
setIsGeneratingTypes(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -65,48 +34,7 @@ const Introduction: FC<Props> = ({ selectedLang }) => {
|
||||
<article className="code"></article>
|
||||
</div>
|
||||
|
||||
<h2 className="doc-heading flex items-center justify-between">
|
||||
<span>Generating types</span>
|
||||
<Link href="https://supabase.com/docs/guides/database/api/generating-types">
|
||||
<a target="_blank">
|
||||
<Button type="default" icon={<IconExternalLink />}>
|
||||
Documentation
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</h2>
|
||||
<div className="doc-section">
|
||||
<article className="text ">
|
||||
<p>
|
||||
Supabase APIs are generated from your database, which means that we can use database
|
||||
introspection to generate type-safe API definitions.
|
||||
</p>
|
||||
<p>
|
||||
You can generate types from your database either through the{' '}
|
||||
<Link href="https://supabase.com/docs/guides/database/api/generating-types">Supabase CLI</Link>,
|
||||
or by downloading the types file via the button on the right and importing it in your
|
||||
application within <code>src/index.ts</code>.
|
||||
</p>
|
||||
</article>
|
||||
<article className={`code ${selectedLang === 'js' ? 'flex items-center' : ''}`}>
|
||||
{selectedLang === 'js' && (
|
||||
<Button
|
||||
type="default"
|
||||
disabled={isGeneratingTypes}
|
||||
loading={isGeneratingTypes}
|
||||
icon={<IconDownload strokeWidth={1.5} />}
|
||||
onClick={generateTypes}
|
||||
>
|
||||
Generate and download types
|
||||
</Button>
|
||||
)}
|
||||
<CodeSnippet selectedLang={selectedLang} snippet={localSnippets.cliLogin()} />
|
||||
<CodeSnippet
|
||||
selectedLang={selectedLang}
|
||||
snippet={localSnippets.generateTypes(ref ?? '')}
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
<GeneratingTypes selectedLang={selectedLang} />
|
||||
|
||||
<h2 className="doc-heading">
|
||||
GraphQL <span className="lowercase">vs</span> Supabase
|
||||
@@ -141,22 +69,6 @@ const Introduction: FC<Props> = ({ selectedLang }) => {
|
||||
}
|
||||
|
||||
const localSnippets = {
|
||||
cliLogin: () => ({
|
||||
title: 'Login via the CLI with your Personal Access Token',
|
||||
bash: {
|
||||
code: `
|
||||
npx supabase login
|
||||
`,
|
||||
},
|
||||
}),
|
||||
generateTypes: (ref: string) => ({
|
||||
title: 'Generate types',
|
||||
bash: {
|
||||
code: `
|
||||
npx supabase gen types typescript --project-id "${ref}" --schema public > types/supabase.ts
|
||||
`,
|
||||
},
|
||||
}),
|
||||
withApollo: () => ({
|
||||
title: 'With Apollo GraphQL',
|
||||
bash: {
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Badge } from 'ui'
|
||||
import Description from './Description'
|
||||
|
||||
const Param = ({
|
||||
name,
|
||||
type,
|
||||
format,
|
||||
required,
|
||||
description,
|
||||
metadata = {},
|
||||
onDesciptionUpdated = () => {},
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-4 flex items-center justify-between ">
|
||||
<code className="inline-block px-2">{name}</code>
|
||||
<Badge color={required ? 'amber' : 'scale'}>{required ? 'Required' : 'Optional'}</Badge>
|
||||
</div>
|
||||
{format && format != type && (
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<label className="font-mono text-xs uppercase text-scale-900">Format:</label>
|
||||
<code className="px-2">{format}</code>
|
||||
{type}
|
||||
</div>
|
||||
)}
|
||||
{description !== false && (
|
||||
<>
|
||||
<label className="font-mono text-xs uppercase text-scale-900">Description</label>
|
||||
<Description content={description} metadata={metadata} onChange={onDesciptionUpdated} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Param
|
||||
@@ -0,0 +1,97 @@
|
||||
import { FC } from 'react'
|
||||
import { Badge, IconCode, IconDatabase } from 'ui'
|
||||
import Description from './Description'
|
||||
|
||||
function getColumnType(type: string, format: string) {
|
||||
// json and jsonb both have type=undefined, so check format instead
|
||||
if (type === undefined && (format === 'jsonb' || format === 'json')) return 'json'
|
||||
|
||||
switch (type) {
|
||||
case 'string':
|
||||
return 'string'
|
||||
case 'integer':
|
||||
return 'number'
|
||||
case 'json':
|
||||
return 'json'
|
||||
case 'number':
|
||||
return 'number'
|
||||
case 'boolean':
|
||||
return 'boolean'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
name: string
|
||||
type: string
|
||||
format: string
|
||||
required: boolean
|
||||
description: boolean
|
||||
metadata?: any
|
||||
onDesciptionUpdated?: () => void
|
||||
}
|
||||
const Param: FC<Props> = ({
|
||||
name,
|
||||
type,
|
||||
format,
|
||||
required,
|
||||
description,
|
||||
metadata = {},
|
||||
onDesciptionUpdated = () => {},
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-4 flex items-center justify-between ">
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="font-mono text-xs uppercase text-scale-900 min-w-[55px]">
|
||||
Column
|
||||
</label>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-md text-scale-1200 pb-0.5">{name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Badge color={required ? 'amber' : 'scale'}>{required ? 'Required' : 'Optional'}</Badge>
|
||||
</div>
|
||||
{format && (
|
||||
<div className="grid gap-2 mt-6">
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<label className="font-mono text-xs uppercase text-scale-900 min-w-[55px]">Type</label>
|
||||
<div>
|
||||
<span className="flex grow-0 bg-slate-300 px-2 py-0.5 rounded-md text-scale-1000">
|
||||
<span className="flex items-center gap-2 text-sm">
|
||||
<IconCode size="tiny" />
|
||||
<span>{getColumnType(type, format)}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<label className="font-mono text-xs uppercase text-scale-900 min-w-[55px]">
|
||||
Format
|
||||
</label>
|
||||
<div>
|
||||
<span className="flex grow-0 bg-slate-300 px-2 py-0.5 rounded-md text-scale-1000">
|
||||
<span className="flex items-center gap-2 text-sm">
|
||||
<IconDatabase size="tiny" />
|
||||
<span>{format}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{description !== false && (
|
||||
<div className="grid gap-2 mt-2">
|
||||
<label className="font-mono text-xs uppercase text-scale-900">Description</label>
|
||||
<Description content={description} metadata={metadata} onChange={onDesciptionUpdated} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Param
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button, Dropdown, IconKey } from 'ui'
|
||||
import { FC, createContext, useContext, useEffect, useState } from 'react'
|
||||
import { observer, useLocalObservable } from 'mobx-react-lite'
|
||||
|
||||
@@ -11,6 +10,7 @@ import { DocsLayout } from 'components/layouts'
|
||||
import { GeneralContent, ResourceContent, RpcContent } from 'components/interfaces/Docs'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useProjectJsonSchemaQuery } from 'data/docs/project-json-schema-query'
|
||||
import LangSelector from 'components/interfaces/Docs/LangSelector'
|
||||
|
||||
const PageContext = createContext(null)
|
||||
|
||||
@@ -79,11 +79,6 @@ const DocView: FC<any> = observer(({}) => {
|
||||
: undefined
|
||||
const swaggerUrl = data?.autoApiService?.restUrl
|
||||
|
||||
const canReadServiceKey = checkPermissions(
|
||||
PermissionAction.READ,
|
||||
'service_api_keys.service_role_key'
|
||||
)
|
||||
|
||||
const {
|
||||
data: jsonSchema,
|
||||
error: jsonSchemaError,
|
||||
@@ -104,7 +99,7 @@ const DocView: FC<any> = observer(({}) => {
|
||||
|
||||
if (error || jsonSchemaError)
|
||||
return (
|
||||
<div className="mx-auto p-6 text-center sm:w-full md:w-3/4">
|
||||
<div className="p-6 mx-auto text-center sm:w-full md:w-3/4">
|
||||
<p className="text-scale-1000">
|
||||
<p>Error connecting to API</p>
|
||||
<p>{`${error || jsonSchemaError}`}</p>
|
||||
@@ -113,7 +108,7 @@ const DocView: FC<any> = observer(({}) => {
|
||||
)
|
||||
if (!data || !jsonSchema || !PageState.jsonSchema)
|
||||
return (
|
||||
<div className="mx-auto p-6 text-center sm:w-full md:w-3/4">
|
||||
<div className="p-6 mx-auto text-center sm:w-full md:w-3/4">
|
||||
<h3 className="text-xl">Building docs ...</h3>
|
||||
</div>
|
||||
)
|
||||
@@ -129,81 +124,17 @@ const DocView: FC<any> = observer(({}) => {
|
||||
const PAGE_KEY: any = resource || rpc || page || 'index'
|
||||
|
||||
return (
|
||||
<div className="Docs h-full w-full overflow-y-auto" key={PAGE_KEY}>
|
||||
<div className="w-full h-full overflow-y-auto Docs Docs--api-page" key={PAGE_KEY}>
|
||||
<div className="Docs--inner-wrapper">
|
||||
<div className="sticky top-0 z-40 flex w-full flex-row-reverse ">
|
||||
<div className="bg-scale-100 dark:bg-scale-300" style={{ width: '50%' }}>
|
||||
<div className="z-0 flex ">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedLang('js')}
|
||||
className={`${
|
||||
selectedLang == 'js'
|
||||
? 'bg-scale-300 font-medium text-scale-1200 dark:bg-scale-200'
|
||||
: 'bg-scale-100 text-scale-900 dark:bg-scale-100'
|
||||
} relative inline-flex items-center border-r border-scale-200 p-1 px-2 text-sm transition hover:text-scale-1200 focus:outline-none`}
|
||||
>
|
||||
JavaScript
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedLang('bash')}
|
||||
className={`${
|
||||
selectedLang == 'bash'
|
||||
? 'bg-scale-300 font-medium text-scale-1200 dark:bg-scale-200'
|
||||
: 'bg-scale-100 text-scale-900 dark:bg-scale-100'
|
||||
} relative inline-flex items-center border-r border-scale-200 p-1 px-2 text-sm transition hover:text-scale-1200 focus:outline-none`}
|
||||
>
|
||||
Bash
|
||||
</button>
|
||||
{selectedLang == 'bash' && (
|
||||
<div className="flex">
|
||||
<div className="flex items-center gap-2 p-1 pl-2 text-xs text-scale-900">
|
||||
<IconKey size={12} strokeWidth={1.5} />
|
||||
<span>Project API key :</span>
|
||||
</div>
|
||||
<Dropdown
|
||||
align="end"
|
||||
side="bottom"
|
||||
className="cursor-pointer border-none bg-transparent p-0 pl-2 pr-8 text-sm text-scale-900"
|
||||
overlay={
|
||||
<>
|
||||
<Dropdown.Item key="hide" onClick={() => setShowApiKey(DEFAULT_KEY)}>
|
||||
hide
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="anon"
|
||||
onClick={() =>
|
||||
setShowApiKey({
|
||||
key: anonKey,
|
||||
name: 'anon (public)',
|
||||
})
|
||||
}
|
||||
>
|
||||
anon (public)
|
||||
</Dropdown.Item>
|
||||
{canReadServiceKey && (
|
||||
<Dropdown.Item
|
||||
key="service"
|
||||
onClick={() =>
|
||||
setShowApiKey({
|
||||
key: autoApiService.serviceApiKey,
|
||||
name: 'service_role (secret)',
|
||||
})
|
||||
}
|
||||
>
|
||||
service_role (secret)
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button type="default">{showApiKey.name}</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sticky top-0 z-40 flex flex-row-reverse w-full ">
|
||||
<LangSelector
|
||||
selectedLang={selectedLang}
|
||||
setSelectedLang={setSelectedLang}
|
||||
showApiKey={showApiKey}
|
||||
setShowApiKey={setShowApiKey}
|
||||
apiKey={anonKey}
|
||||
autoApiService={autoApiService}
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
{resource ? (
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="10" height="15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M.494 7.974h5v2h-5v-2Z" fill="url(#a)"/><path d="M.494 9.974h5v2h-5v-2Z" fill="url(#b)"/><path fill-rule="evenodd" clip-rule="evenodd" d="M2.682 3a2.318 2.318 0 1 1 4.636 0v1.682h2.155L9.79 5v9l-.318.318h-9L.154 14V5l.319-.318h2.209V3Zm.636 1.682h3.364V3a1.682 1.682 0 0 0-3.364 0v1.682Zm-2.527.636v2.338h4.703l.319.318v4l-.319.319H.791v1.389h8.363V5.318H.791Zm.022 6.338h4.363v-1.363H.813v1.363Zm0-2h4.363V8.293H.813v1.363Z" fill="url(#c)"/><defs><linearGradient id="a" x1="-1.666" y1=".682" x2="13.393" y2=".682" gradientUnits="userSpaceOnUse"><stop stop-color="#429B72"/><stop offset="1" stop-color="#333" stop-opacity=".6"/></linearGradient><linearGradient id="b" x1="-1.666" y1=".682" x2="13.393" y2=".682" gradientUnits="userSpaceOnUse"><stop stop-color="#429B72"/><stop offset="1" stop-color="#333" stop-opacity=".6"/></linearGradient><linearGradient id="c" x1="-1.666" y1=".682" x2="13.393" y2=".682" gradientUnits="userSpaceOnUse"><stop stop-color="#429B72"/><stop offset="1" stop-color="#333" stop-opacity=".6"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2202" height="2476"><defs><linearGradient id="b" x1="22.367%" x2="87.411%" y1="78.665%" y2="25.105%"><stop offset="0%" stop-color="#ADADAD" stop-opacity=".5"/><stop offset="100%" stop-opacity=".5"/></linearGradient><path id="a" d="M981.153 35.355c78.66-46.107 154.6-47.464 233.93-2.033 292.92 168.838 586.52 337.673 879.45 506.511 35.25 20.342 61.7 46.108 79.33 77.297l.546.938c17.94 31.001 26.574 67.962 26.574 108.909v1021.274c-.11 40.36-8.92 76.03-25.77 107.02-17.62 32.54-44.07 58.99-80.68 80.01-292.93 168.84-586.53 337-879.45 506.51-72.65 42.58-143.56 44.1-215.94 7.76-6.43-3.19-12.88-6.68-19.35-10.47-116.62-69.16-700.431-403.45-874.693-504.48-35.937-21.02-63.059-48.14-80.691-80.01l.756-.44C8.606 1823.221 0 1787.661 0 1747.461V727.656c0-82.723 34.58-146.462 106.456-187.823C280.039 438.801 864.523 103.841 981.153 35.355Zm117.98 470.575c-404.121 0-730.944 327.503-730.944 730.951 0 132.22 35.257 256.98 96.962 364.12 126.118 219.01 363.44 366.83 634.662 366.83 271.91 0 509.23-148.5 635.34-368.87l-318.01-183.75c-62.38 110.52-181.04 185.79-317.33 185.79-135.61 0-253.591-73.91-315.973-183.76-30.513-53.56-48.144-114.59-48.144-180.36 0-201.39 162.737-364.121 364.117-364.121 134.26 0 251.56 73.23 314.62 181.721l318.01-183.078c-126.79-218.333-362.76-365.473-633.31-365.473Zm720.1 558.041h-72.55v58.99h-58.31v72.56h58.31v87.47h-58.31v72.55h58.31v58.99h72.55l-.001-58.99h88.15l.001 58.99h72.55v-58.99h58.99v-72.55h-58.99v-87.47h58.99v-72.56h-58.99v-58.99h-72.55l-.001 58.99h-88.15l.001-58.99Zm88.149 131.55v87.47h-88.15v-87.47h88.15Z"/></defs><g fill="none" fill-rule="nonzero" transform="translate(.507 .109)"><use xlink:href="#a" fill="#4C4C4C"/><use xlink:href="#a" fill="url(#b)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 2500 2500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="M174.672,1866.26L173.916,1866.7C191.548,1898.57 218.67,1925.69 254.607,1946.71C428.869,2047.74 1012.68,2382.03 1129.3,2451.19C1135.77,2454.98 1142.22,2458.47 1148.65,2461.66C1221.03,2498 1291.94,2496.48 1364.59,2453.9C1657.51,2284.39 1951.11,2116.23 2244.04,1947.39C2280.65,1926.37 2307.1,1899.92 2324.72,1867.38C2341.57,1836.39 2350.38,1800.72 2350.49,1760.36C2350.49,1759.87 2350.49,1759.38 2350.49,1758.89L2350.49,739.086C2350.49,697.725 2341.68,660.432 2323.37,629.239L2323.37,629.241L2323.37,629.239C2305.74,598.05 2279.29,572.284 2244.04,551.942C1951.11,383.104 1657.51,214.269 1364.59,45.431C1285.26,0 1209.32,1.357 1130.66,47.464C1014.03,115.95 429.546,450.91 255.963,551.942C184.087,593.303 149.507,657.042 149.507,739.765L149.507,1759.57C149.507,1799.77 158.113,1835.33 174.672,1866.26Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M1566.65,1427.32C1504.27,1537.84 1385.61,1613.11 1249.32,1613.11C1113.71,1613.11 995.729,1539.2 933.347,1429.35C902.834,1375.79 885.203,1314.76 885.203,1248.99C885.203,1047.6 1047.94,884.869 1249.32,884.869C1383.58,884.869 1500.88,958.099 1563.94,1066.59L1881.95,883.512C1755.16,665.179 1519.19,518.039 1248.64,518.039C844.519,518.039 517.696,845.542 517.696,1248.99C517.696,1381.21 552.953,1505.97 614.658,1613.11C740.776,1832.12 978.098,1979.94 1249.32,1979.94C1521.23,1979.94 1758.55,1831.44 1884.66,1611.07L1566.65,1427.32ZM1968.74,1076.08L1896.19,1076.08L1896.19,1426.64L1968.74,1426.64L1968.74,1076.08ZM2129.44,1076.08L2056.89,1076.08L2056.89,1426.64L2129.44,1426.64L2129.44,1076.08Z" style="fill-rule:nonzero;"/>
|
||||
<g>
|
||||
<path d="M2188.43,1135.07L1837.88,1135.07L1837.88,1207.63L2188.43,1207.63L2188.43,1135.07ZM2188.43,1295.1L1837.88,1295.1L1837.88,1367.65L2188.43,1367.65L2188.43,1295.1Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.8209 10.9141L44.7971 10.9339L15.7344 39.9967L24.701 48.9743L32.5819 41.1376L62.8209 10.9141ZM45.3142 37.7255C45.0585 37.7136 44.7948 37.7015 44.6294 37.92L29.1317 53.4154L38.0337 62.2629L38.0234 62.273L44.394 68.649C44.4358 68.6861 44.4773 68.7276 44.5196 68.7698C44.6939 68.944 44.881 69.1309 45.151 69.0705C48.0938 69.0639 51.0367 69.065 53.9798 69.0661C56.9231 69.0672 59.8666 69.0683 62.8106 69.0617L47.1433 53.3803L62.8055 37.728L45.6203 37.7235C45.5247 37.7352 45.4201 37.7303 45.3142 37.7255Z" fill="url(#paint0_linear_1785_15060)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1785_15060" x1="15.7344" y1="69.0821" x2="72.627" y2="23.028" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#171717" stop-opacity="0.5"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 925 B |
@@ -0,0 +1,9 @@
|
||||
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.8209 10.9141L44.7971 10.9339L15.7344 39.9967L24.701 48.9743L32.5819 41.1376L62.8209 10.9141ZM45.3142 37.7255C45.0585 37.7136 44.7948 37.7015 44.6294 37.92L29.1317 53.4154L38.0337 62.2629L38.0234 62.273L44.394 68.649C44.4358 68.6861 44.4773 68.7276 44.5196 68.7698C44.6939 68.944 44.881 69.1309 45.151 69.0705C48.0938 69.0639 51.0367 69.065 53.9798 69.0661C56.9231 69.0672 59.8666 69.0683 62.8106 69.0617L47.1433 53.3803L62.8055 37.728L45.6203 37.7235C45.5247 37.7352 45.4201 37.7303 45.3142 37.7255Z" fill="url(#paint0_linear_1785_15060)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1785_15060" x1="15.7344" y1="69.0821" x2="72.627" y2="23.028" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EDEDED" stop-opacity="0.5"/>
|
||||
<stop offset="1" stop-color="white"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 944 B |
@@ -0,0 +1,9 @@
|
||||
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7305 13.2305H67.2695V66.7695H13.7305V13.2305ZM54.6588 58.1144C52.1773 58.1144 50.7743 56.8201 49.6959 55.0593L45.6073 57.4345C47.0843 60.3528 50.1029 62.5794 54.7753 62.5794C59.5538 62.5794 63.1121 60.0979 63.1121 55.5683C63.1121 51.3668 60.6986 49.498 56.4239 47.6649L55.1662 47.1261C53.0076 46.1913 52.0728 45.5803 52.0728 44.071C52.0728 42.8498 53.0068 41.915 54.4804 41.915C55.9251 41.915 56.8556 42.5243 57.7182 44.071L61.6359 41.5555C59.9788 38.6406 57.6791 37.5273 54.4804 37.5273C49.9873 37.5273 47.1124 40.3998 47.1124 44.173C47.1124 48.2691 49.5242 50.2068 53.1546 51.7534L54.4124 52.2931C56.7069 53.2967 58.0751 53.9077 58.0751 55.6329C58.0751 57.0725 56.7435 58.1144 54.6588 58.1144ZM35.1613 58.0829C33.4327 58.0829 32.7138 56.8974 31.9234 55.4952L27.8281 57.9746C29.0145 60.4854 31.3473 62.57 35.3754 62.57C39.8336 62.57 42.8879 60.199 42.8879 54.9896V37.8146H37.8569V54.9216C37.8569 57.4362 36.8142 58.0829 35.1613 58.0829Z" fill="url(#paint0_linear_1785_15055)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1785_15055" x1="13.7305" y1="66.7695" x2="67.2695" y2="13.2305" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#171717" stop-opacity="0.5"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7305 13.2305H67.2695V66.7695H13.7305V13.2305ZM54.6588 58.1144C52.1773 58.1144 50.7743 56.8201 49.6959 55.0593L45.6073 57.4345C47.0843 60.3528 50.1029 62.5794 54.7753 62.5794C59.5538 62.5794 63.1121 60.0979 63.1121 55.5683C63.1121 51.3668 60.6986 49.498 56.4239 47.6649L55.1662 47.1261C53.0076 46.1913 52.0728 45.5803 52.0728 44.071C52.0728 42.8498 53.0068 41.915 54.4804 41.915C55.9251 41.915 56.8556 42.5243 57.7182 44.071L61.6359 41.5555C59.9788 38.6406 57.6791 37.5273 54.4804 37.5273C49.9873 37.5273 47.1124 40.3998 47.1124 44.173C47.1124 48.2691 49.5242 50.2068 53.1546 51.7534L54.4124 52.2931C56.7069 53.2967 58.0751 53.9077 58.0751 55.6329C58.0751 57.0725 56.7435 58.1144 54.6588 58.1144ZM35.1613 58.0829C33.4327 58.0829 32.7138 56.8974 31.9234 55.4952L27.8281 57.9746C29.0145 60.4854 31.3473 62.57 35.3754 62.57C39.8336 62.57 42.8879 60.199 42.8879 54.9896V37.8146H37.8569V54.9216C37.8569 57.4362 36.8142 58.0829 35.1613 58.0829Z" fill="url(#paint0_linear_1785_15055)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1785_15055" x1="13.7305" y1="66.7695" x2="67.2695" y2="13.2305" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EDEDED" stop-opacity="0.5"/>
|
||||
<stop offset="1" stop-color="white"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.7668 10.4216C34.8343 10.0558 37.1841 9.84591 39.6447 9.83448C42.1052 9.82305 44.6707 10.01 47.1433 10.4216C51.0493 11.0726 54.3399 14.0028 54.3399 17.9034V31.6089C54.3399 35.628 51.1452 38.923 47.1433 38.923H32.7668C27.8856 38.923 23.7752 43.1136 23.7752 47.8643V54.4403H18.8264C14.6433 54.4403 12.2 51.4041 11.1768 47.143C9.79667 41.4184 9.85529 37.9973 11.1768 32.5148C12.3225 27.7318 15.9853 25.2175 20.1684 25.2175H25.5701H39.9634V23.389H25.5701V17.9034C25.5701 13.7499 26.676 11.4977 32.7668 10.4216ZM34.5617 16.9976C34.5617 15.4804 33.3476 14.2464 31.8609 14.2464C30.3688 14.2464 29.1601 15.4804 29.1601 16.9976C29.1601 18.5094 30.3688 19.732 31.8609 19.732C33.3476 19.732 34.5617 18.5094 34.5617 16.9976ZM56.1348 31.6092V25.2178H61.5365C65.7249 25.2178 67.6994 28.3508 68.7332 32.5151C70.1719 38.2988 70.2359 42.6346 68.7332 47.1432C67.2784 51.5227 65.7196 54.4405 61.5365 54.4405H54.3399H39.9634V56.269H54.3399V61.7546C54.3399 65.9081 50.7668 68.0195 47.1432 69.0687C41.6919 70.6505 37.3228 70.4083 32.7667 69.0687C28.962 67.9496 25.5701 65.6553 25.5701 61.7546V48.0491C25.5701 44.1054 28.8287 40.735 32.7667 40.735H47.1432C51.9338 40.735 56.1348 36.5644 56.1348 31.6092ZM50.7499 62.6605C50.7499 61.1486 49.5412 59.9261 48.0491 59.9261C46.5624 59.9261 45.3483 61.1486 45.3483 62.6605C45.3483 64.1777 46.5624 65.4116 48.0491 65.4116C49.5412 65.4116 50.7499 64.1777 50.7499 62.6605Z" fill="url(#paint0_linear_1785_15271)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1785_15271" x1="10.1636" y1="70.1661" x2="70.492" y2="10.4969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#171717" stop-opacity="0.5"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.7668 10.4216C34.8343 10.0558 37.1841 9.84591 39.6447 9.83448C42.1052 9.82305 44.6707 10.01 47.1433 10.4216C51.0493 11.0726 54.3399 14.0028 54.3399 17.9034V31.6089C54.3399 35.628 51.1452 38.923 47.1433 38.923H32.7668C27.8856 38.923 23.7752 43.1136 23.7752 47.8643V54.4403H18.8264C14.6433 54.4403 12.2 51.4041 11.1768 47.143C9.79667 41.4184 9.85529 37.9973 11.1768 32.5148C12.3225 27.7318 15.9853 25.2175 20.1684 25.2175H25.5701H39.9634V23.389H25.5701V17.9034C25.5701 13.7499 26.676 11.4977 32.7668 10.4216ZM34.5617 16.9976C34.5617 15.4804 33.3476 14.2464 31.8609 14.2464C30.3688 14.2464 29.1601 15.4804 29.1601 16.9976C29.1601 18.5094 30.3688 19.732 31.8609 19.732C33.3476 19.732 34.5617 18.5094 34.5617 16.9976ZM56.1348 31.6092V25.2178H61.5365C65.7249 25.2178 67.6994 28.3508 68.7332 32.5151C70.1719 38.2988 70.2359 42.6346 68.7332 47.1432C67.2784 51.5227 65.7196 54.4405 61.5365 54.4405H54.3399H39.9634V56.269H54.3399V61.7546C54.3399 65.9081 50.7668 68.0195 47.1432 69.0687C41.6919 70.6505 37.3228 70.4083 32.7667 69.0687C28.962 67.9496 25.5701 65.6553 25.5701 61.7546V48.0491C25.5701 44.1054 28.8287 40.735 32.7667 40.735H47.1432C51.9338 40.735 56.1348 36.5644 56.1348 31.6092ZM50.7499 62.6605C50.7499 61.1486 49.5412 59.9261 48.0491 59.9261C46.5624 59.9261 45.3483 61.1486 45.3483 62.6605C45.3483 64.1777 46.5624 65.4116 48.0491 65.4116C49.5412 65.4116 50.7499 64.1777 50.7499 62.6605Z" fill="url(#paint0_linear_1785_15271)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1785_15271" x1="10.1636" y1="70.1661" x2="70.492" y2="10.4969" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EDEDED" stop-opacity="0.5"/>
|
||||
<stop offset="1" stop-color="white"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -224,10 +224,40 @@
|
||||
@apply border-b flex border-scale-500;
|
||||
}
|
||||
|
||||
&.Docs--table-editor .doc-section--introduction {
|
||||
.text {
|
||||
width: 85%;
|
||||
}
|
||||
}
|
||||
|
||||
&.Docs--table-editor .doc-section--introduction {
|
||||
.text {
|
||||
width: 85%;
|
||||
}
|
||||
}
|
||||
|
||||
&.Docs--table-editor .doc-section--client-libraries {
|
||||
flex-direction: column;
|
||||
margin-bottom: 40px;
|
||||
|
||||
.text,
|
||||
.code {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
.code {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.doc-section article:first-child {
|
||||
@apply prose prose-docs prose-sm max-w-none;
|
||||
}
|
||||
|
||||
&.Docs--table-editor .doc-section__table-name {
|
||||
@apply mt-12;
|
||||
}
|
||||
|
||||
.text {
|
||||
@apply p-4 px-6 w-1/2;
|
||||
max-width: 50%;
|
||||
@@ -239,7 +269,7 @@
|
||||
a {
|
||||
@apply transition;
|
||||
}
|
||||
|
||||
|
||||
a:hover {
|
||||
@apply text-brand-1000;
|
||||
}
|
||||
@@ -270,6 +300,9 @@
|
||||
@apply text-brand-900;
|
||||
}
|
||||
}
|
||||
.codeblock-container + .codeblock-container {
|
||||
@apply mt-12;
|
||||
}
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
@@ -339,4 +372,4 @@
|
||||
label {
|
||||
@apply mt-1;
|
||||
}
|
||||
}
|
||||
}
|
||||