mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
chore(studio): table presentation improvements (#40224)
* fix table head * fix edge functions list * fix secret * access keys * policies * misc table fixes * publication height fix * fix-publications * table documentation * fix colspan * s3 fixes
This commit is contained in:
1 parent
277abeb1f1
commit
3b98a9844d
13 files changed
+97
-113
No files matched your search
@@ -1,44 +1,16 @@
|
||||
---
|
||||
title: Table
|
||||
description: A responsive table component.
|
||||
description: A responsive table component for basic data.
|
||||
component: true
|
||||
source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
Table presents basic tabular data presentation within a [Card](/docs/components/card).
|
||||
|
||||
<ComponentPreview name="table-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
Columns will naturally take the width of their content. Width specification is therefore not required unless you require columns to span a specific width. An example use-case may be when placing multiple Table instances on the one page, where columns on each table should optically align.
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx shadcn-ui@latest add table
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="table" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
`TableHead` includes a `whitespace-nowrap` utility class to prevent its text from wrapping. `TableCell` must have any custom width specified, as there are legitimate use cases for both wrapping (or truncated) text.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -59,7 +31,7 @@ import {
|
||||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||||
<TableHead>Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Card,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
@@ -14,74 +15,87 @@ const invoices = [
|
||||
invoice: 'INV001',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$250.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
paymentMethod: 'Credit card',
|
||||
description: 'Website design services',
|
||||
},
|
||||
{
|
||||
invoice: 'INV002',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$150.00',
|
||||
paymentMethod: 'PayPal',
|
||||
description: 'Monthly subscription fee',
|
||||
},
|
||||
{
|
||||
invoice: 'INV003',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$350.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
paymentMethod: 'Bank transfer',
|
||||
description: 'Consulting hours',
|
||||
},
|
||||
{
|
||||
invoice: 'INV004',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$450.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
paymentMethod: 'Credit card',
|
||||
description: 'Software license renewal',
|
||||
},
|
||||
{
|
||||
invoice: 'INV005',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$550.00',
|
||||
paymentMethod: 'PayPal',
|
||||
description: 'Custom development work',
|
||||
},
|
||||
{
|
||||
invoice: 'INV006',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$200.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
paymentMethod: 'Bank transfer',
|
||||
description: 'Hosting and maintenance',
|
||||
},
|
||||
{
|
||||
invoice: 'INV007',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$300.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
paymentMethod: 'Credit card',
|
||||
description: 'Training session package',
|
||||
},
|
||||
]
|
||||
|
||||
export default function TableDemo() {
|
||||
return (
|
||||
<Table>
|
||||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{invoices.map((invoice) => (
|
||||
<TableRow key={invoice.invoice}>
|
||||
<TableCell className="font-medium">{invoice.invoice}</TableCell>
|
||||
<TableCell>{invoice.paymentStatus}</TableCell>
|
||||
<TableCell>{invoice.paymentMethod}</TableCell>
|
||||
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
|
||||
<Card className="w-full pb-4">
|
||||
<Table>
|
||||
<TableCaption>A list of your recent invoices</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="hidden md:table-cell">Description</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={3}>Total</TableCell>
|
||||
<TableCell className="text-right">$2,500.00</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{invoices.map((invoice) => (
|
||||
<TableRow key={invoice.invoice}>
|
||||
<TableCell className="text-foreground">{invoice.invoice}</TableCell>
|
||||
<TableCell className="text-foreground-lighter">{invoice.paymentStatus}</TableCell>
|
||||
<TableCell className="text-foreground-lighter">{invoice.paymentMethod}</TableCell>
|
||||
<TableCell className="hidden md:table-cell text-foreground-muted">
|
||||
{invoice.description}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter className="border-t">
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>Total</TableCell>
|
||||
<TableCell className="text-right">$2,250.00</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -147,25 +147,27 @@ export const PublicationsList = () => {
|
||||
{isSuccess &&
|
||||
publications.map((x) => (
|
||||
<TableRow key={x.name}>
|
||||
<TableCell className="flex items-center gap-x-2 items-center">
|
||||
{x.name}
|
||||
{/* [Joshen] Making this tooltip very specific for these 2 publications */}
|
||||
{['supabase_realtime', 'supabase_realtime_messages_publication'].includes(
|
||||
x.name
|
||||
) && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Info size={14} className="text-foreground-light" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{x.name === 'supabase_realtime'
|
||||
? 'This publication is managed by Supabase and handles Postgres changes'
|
||||
: x.name === 'supabase_realtime_messages_publication'
|
||||
? 'This publication is managed by Supabase and handles broadcasts from the database'
|
||||
: undefined}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{x.name}
|
||||
{/* [Joshen] Making this tooltip very specific for these 2 publications */}
|
||||
{['supabase_realtime', 'supabase_realtime_messages_publication'].includes(
|
||||
x.name
|
||||
) && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Info size={14} className="text-foreground-light" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{x.name === 'supabase_realtime'
|
||||
? 'Managed by Supabase and handles Postgres changes'
|
||||
: x.name === 'supabase_realtime_messages_publication'
|
||||
? 'Managed by Supabase and handles broadcasts from the database'
|
||||
: undefined}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>{x.id}</TableCell>
|
||||
{publicationEvents.map((event) => (
|
||||
|
||||
@@ -78,8 +78,8 @@ export const PublicationsTableItem = ({
|
||||
return (
|
||||
<TableRow key={table.id}>
|
||||
<TableCell className="py-3 whitespace-nowrap">{table.name}</TableCell>
|
||||
<TableCell className="py-3 whitespace-nowrap">{table.schema}</TableCell>
|
||||
<TableCell className="py-3 hidden max-w-sm truncate whitespace-nowrap lg:table-cell">
|
||||
<TableCell className="py-3 whitespace-nowrap text-foreground-light">{table.schema}</TableCell>
|
||||
<TableCell className="py-3 whitespace-nowrap hidden lg:table-cell max-w-sm truncate text-foreground-light">
|
||||
{table.comment}
|
||||
</TableCell>
|
||||
<TableCell className="py-3">
|
||||
|
||||
@@ -102,7 +102,7 @@ export const PublicationsTables = () => {
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Schema</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
<TableHead className="hidden lg:table-cell">Description</TableHead>
|
||||
{/*
|
||||
We've disabled All tables toggle for publications.
|
||||
See https://github.com/supabase/supabase/pull/7233.
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { LOAD_TAB_FROM_CACHE_PARAM } from 'components/grid/SupabaseGrid.utils'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
|
||||
@@ -63,7 +64,6 @@ import {
|
||||
} from 'ui'
|
||||
import { ProtectedSchemaWarning } from '../ProtectedSchemaWarning'
|
||||
import { formatAllEntities } from './Tables.utils'
|
||||
import { LOAD_TAB_FROM_CACHE_PARAM } from 'components/grid/SupabaseGrid.utils'
|
||||
|
||||
interface TableListProps {
|
||||
onAddTable: () => void
|
||||
@@ -325,7 +325,7 @@ export const TableList = ({
|
||||
<TableHead key="size" className="hidden text-right xl:table-cell">
|
||||
Size (Estimated)
|
||||
</TableHead>
|
||||
<TableHead key="realtime" className="hidden xl:table-cell text-center">
|
||||
<TableHead key="realtime" className="hidden xl:table-cell text-right">
|
||||
Realtime Enabled
|
||||
</TableHead>
|
||||
<TableHead key="buttons"></TableHead>
|
||||
@@ -457,11 +457,11 @@ export const TableList = ({
|
||||
{(realtimePublication?.tables ?? []).find(
|
||||
(table) => table.id === x.id
|
||||
) ? (
|
||||
<div className="flex justify-center">
|
||||
<div className="flex justify-end">
|
||||
<Check size={18} strokeWidth={2} className="text-brand" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-center">
|
||||
<div className="flex justify-end">
|
||||
<X size={18} strokeWidth={2} className="text-foreground-lighter" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ const EdgeFunctionSecret = ({ secret, onSelectDelete }: EdgeFunctionSecretProps)
|
||||
displayAs="utc"
|
||||
utcTimestamp={secret.updated_at}
|
||||
labelFormat="DD MMM YYYY HH:mm:ss (ZZ)"
|
||||
className="!text-sm text-foreground-light"
|
||||
className="!text-sm text-foreground-light whitespace-nowrap"
|
||||
/>
|
||||
) : (
|
||||
'-'
|
||||
|
||||
@@ -37,9 +37,7 @@ export const EdgeFunctionsListItem = ({ function: item }: EdgeFunctionsListItemP
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-sm text-foreground">{item.name}</p>
|
||||
</div>
|
||||
<p className="text-sm text-foreground whitespace-nowrap">{item.name}</p>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="text-xs text-foreground-light flex gap-2 items-center truncate">
|
||||
@@ -80,7 +78,7 @@ export const EdgeFunctionsListItem = ({ function: item }: EdgeFunctionsListItemP
|
||||
</TableCell>
|
||||
<TableCell className="lg:table-cell">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-sm text-foreground-light">{dayjs(item.updated_at).fromNow()}</p>
|
||||
</div>
|
||||
|
||||
+7
-5
@@ -1,8 +1,8 @@
|
||||
import { PostgresPolicy } from '@supabase/postgres-meta'
|
||||
import { noop } from 'lodash'
|
||||
|
||||
import { Bucket } from 'data/storage/buckets-query'
|
||||
import { PolicyRow } from 'components/interfaces/Auth/Policies/PolicyTableRow/PolicyRow'
|
||||
import { Bucket } from 'data/storage/buckets-query'
|
||||
|
||||
import { Bucket as BucketIcon } from 'icons'
|
||||
import {
|
||||
@@ -40,11 +40,13 @@ export const StoragePoliciesBucketRow = ({
|
||||
}: StoragePoliciesBucketRowProps) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row w-full items-center justify-between gap-0 space-y-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<CardHeader className="flex flex-row w-full items-center justify-between gap-2 space-y-0">
|
||||
<div className="flex flex-1 min-w-0 items-center gap-3">
|
||||
<BucketIcon className="text-foreground-muted" size={16} strokeWidth={1.5} />
|
||||
<CardTitle>{label}</CardTitle>
|
||||
{bucket?.public && <Badge variant="warning">Public</Badge>}
|
||||
<div className="flex flex-1 min-w-0 items-center gap-1.5">
|
||||
<CardTitle className="truncate">{label}</CardTitle>
|
||||
{bucket?.public && <Badge variant="warning">Public</Badge>}
|
||||
</div>
|
||||
</div>
|
||||
<Button type="outline" onClick={() => onSelectPolicyAdd(bucket?.name, table)}>
|
||||
New policy
|
||||
|
||||
@@ -274,15 +274,9 @@ export const S3Connection = () => {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead key="description" className="min-w-40 lg:min-w-64">
|
||||
Description
|
||||
</TableHead>
|
||||
<TableHead key="access-key-id" className="min-w-64 w-full">
|
||||
Access key ID
|
||||
</TableHead>
|
||||
<TableHead key="created-at" className="min-w-48">
|
||||
Created at
|
||||
</TableHead>
|
||||
<TableHead key="description">Name</TableHead>
|
||||
<TableHead key="access-key-id">Key ID</TableHead>
|
||||
<TableHead key="created-at">Created at</TableHead>
|
||||
<TableHead key="actions" />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
@@ -53,7 +53,9 @@ export const StorageCredItem = ({
|
||||
<TableCell>
|
||||
<Input readOnly copy value={access_key} className="font-mono" />
|
||||
</TableCell>
|
||||
<TableCell className="text-foreground-lighter">{daysSince(created_at)}</TableCell>
|
||||
<TableCell className="text-foreground-lighter whitespace-nowrap">
|
||||
{daysSince(created_at)}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{canRemoveAccessKey && (
|
||||
<DropdownMenu>
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as React from 'react'
|
||||
import { cn } from '../../../lib/utils/cn'
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs bg-opacity-10',
|
||||
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs bg-opacity-10 whitespace-nowrap',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -68,7 +68,7 @@ const TableHead = React.forwardRef<
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'h-10 px-4 text-left align-middle heading-meta text-foreground-lighter [&:has([role=checkbox])]:pr-0',
|
||||
'h-10 px-4 text-left align-middle heading-meta whitespace-nowrap text-foreground-lighter [&:has([role=checkbox])]:pr-0',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in new issue
Block a user