mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
## What kind of change does this PR introduce? Refactor. Follow-up to #49871. ## What is the current behavior? Branded (green) text still uses the `text-brand` classname while the colour comes from `--primary`. ## What is the new behavior? **Rename-only:** `text-brand` → `text-primary` across callsites and docs. Leftover `bg-brand` / `border-brand` alias to `brand-default`. No intentional colour changes in this PR. This better matches how we treat our green in other components and props, like `variant="primary"` for green buttons. ## To test On light mode: smoke-test that branded text still looks like #49871 (readable green, not the bright fill). - [Homepage](https://zone-www-dot-com-git-dnywh-depr-316-text-brand-de2380-supabase.vercel.app/): “Scale to millions” uses `text-primary` - [Docs homepage](https://docs-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/docs): branded links like “More on self-hosting” are still readable - [Typography docs](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/typography): documents `text-primary` - [Colour usage](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/color-usage): `text-primary` is visibly darker than `bg-brand-default` _on light mode_ - [Studio auth providers](https://studio-staging-git-dnywh-depr-316-text-brand-to-60fa6c-supabase.vercel.app/dashboard/project/_/auth/providers): enabled provider badge text readable; status dot stays bright green <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Style** * Updated branded text, link hover states, icons, badges, indicators, and highlighted content across the Design System, Docs, Studio, Learn, UI Library, and marketing experiences to use the primary theme color. * Updated syntax highlighting and table-of-contents styling for consistent primary-color presentation. * Refined brand color fallback behavior for bright fills and borders. * **Documentation** * Updated color-usage and typography guidance to recommend the primary text utility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
242 lines
8.3 KiB
TypeScript
242 lines
8.3 KiB
TypeScript
import { useFlag, useParams } from 'common'
|
|
import { BookOpen, Check, ChevronsUpDown, Copy, ExternalLink, List, X } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { useState } from 'react'
|
|
import { logConstants } from 'shared-data'
|
|
import {
|
|
Button,
|
|
cn,
|
|
Command,
|
|
CommandEmpty,
|
|
CommandGroup,
|
|
CommandInput,
|
|
CommandItem,
|
|
CommandList,
|
|
copyToClipboard,
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
SidePanel,
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from 'ui'
|
|
|
|
import { DocsButton } from '../DocsButton'
|
|
import { LOGS_EXPLORER_DOCS_URL } from '@/components/interfaces/Settings/Logs/Logs.constants'
|
|
import {
|
|
otelFieldsFromKeys,
|
|
toOtelFieldSchemas,
|
|
} from '@/components/interfaces/Settings/Logs/Logs.fieldReference'
|
|
import Table from '@/components/to-be-cleaned/Table'
|
|
import { useOtelLogKeysQuery } from '@/data/logs/otel-log-keys-query'
|
|
import { DOCS_URL } from '@/lib/constants'
|
|
|
|
export interface LogsExplorerHeaderProps {
|
|
subtitle?: string
|
|
}
|
|
|
|
const LogsExplorerHeader = ({ subtitle }: LogsExplorerHeaderProps) => {
|
|
const [showReference, setShowReference] = useState(false)
|
|
const [open, setOpen] = useState(false)
|
|
const useOtel = useFlag('otelLegacyLogs')
|
|
const schemas = useOtel ? toOtelFieldSchemas(logConstants.schemas) : logConstants.schemas
|
|
const [selectedRef, setSelectedRef] = useState(schemas[0]?.reference)
|
|
const selectedSchema = schemas.find((s) => s.reference === selectedRef) ?? schemas[0]
|
|
|
|
const { ref: projectRef } = useParams()
|
|
const { data: discoveredKeys, isPending: isLoadingKeys } = useOtelLogKeysQuery(
|
|
{ projectRef, source: selectedRef },
|
|
{ enabled: useOtel && showReference }
|
|
)
|
|
|
|
return (
|
|
<div className="flex flex-col md:flex-row md:items-center gap-4 md:gap-8 transition-all pb-6 justify-between">
|
|
<div className="flex flex-col md:flex-row md:items-center gap-3">
|
|
<div className="flex flex-row items-center gap-3">
|
|
<div className="flex h-6 w-6 items-center justify-center rounded-sm border border-brand-600 bg-brand-300 text-primary">
|
|
<List size={14} strokeWidth={3} />
|
|
</div>
|
|
|
|
<h1>Logs Explorer</h1>
|
|
</div>
|
|
{subtitle && <span className="text-2xl text-foreground-light">{subtitle}</span>}
|
|
</div>
|
|
<div className="flex flex-row gap-2">
|
|
<DocsButton href={LOGS_EXPLORER_DOCS_URL} />
|
|
|
|
<SidePanel
|
|
size="large"
|
|
header={
|
|
<div className="flex flex-row justify-between items-center">
|
|
<h3>Field Reference</h3>
|
|
<Button
|
|
variant="text"
|
|
className="px-1"
|
|
onClick={() => setShowReference(false)}
|
|
icon={<X size={18} strokeWidth={1.5} />}
|
|
/>
|
|
</div>
|
|
}
|
|
visible={showReference}
|
|
cancelText="Close"
|
|
onCancel={() => setShowReference(false)}
|
|
hideFooter
|
|
triggerElement={
|
|
<Button onClick={() => setShowReference(true)} icon={<BookOpen strokeWidth={1.5} />}>
|
|
<span>Field Reference</span>
|
|
</Button>
|
|
}
|
|
>
|
|
<SidePanel.Content>
|
|
<div className="pt-4 pb-2 space-y-1">
|
|
{useOtel ? (
|
|
<p className="text-sm">
|
|
The following table shows the fields available on each source. Nested fields live
|
|
in the <code className="text-xs">log_attributes</code> map and are read with{' '}
|
|
<code className="text-xs">log_attributes['key']</code> — no unnesting joins
|
|
needed.
|
|
</p>
|
|
) : (
|
|
<p className="text-sm">
|
|
The following table shows all the available paths that can be queried from each
|
|
respective source. Do note that to access nested keys, you would need to perform
|
|
the necessary{' '}
|
|
<Link
|
|
href={`${DOCS_URL}/guides/platform/logs#unnesting-arrays`}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="text-primary"
|
|
>
|
|
unnesting joins
|
|
<ExternalLink
|
|
size={14}
|
|
className="ml-1 inline translate-y-[-2px]"
|
|
strokeWidth={1.5}
|
|
/>
|
|
</Link>
|
|
</p>
|
|
)}
|
|
</div>
|
|
</SidePanel.Content>
|
|
<SidePanel.Separator />
|
|
<div className="px-4 pb-4 flex flex-col gap-4">
|
|
<Popover open={open} onOpenChange={setOpen}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
role="combobox"
|
|
size={'small'}
|
|
aria-expanded={open}
|
|
className="w-full justify-between"
|
|
iconRight={<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />}
|
|
>
|
|
{selectedSchema?.name ?? 'Select source...'}
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent className="p-0" sameWidthAsTrigger>
|
|
<Command>
|
|
<CommandInput placeholder="Search source..." />
|
|
<CommandList>
|
|
<CommandEmpty>No source found.</CommandEmpty>
|
|
<CommandGroup>
|
|
{schemas.map((schema) => (
|
|
<CommandItem
|
|
key={schema.reference}
|
|
value={schema.reference}
|
|
onSelect={() => {
|
|
setSelectedRef(schema.reference)
|
|
setOpen(false)
|
|
}}
|
|
>
|
|
<Check
|
|
className={cn(
|
|
'mr-2 h-4 w-4',
|
|
selectedSchema?.reference === schema.reference
|
|
? 'opacity-100'
|
|
: 'opacity-0'
|
|
)}
|
|
/>
|
|
{schema.name}
|
|
</CommandItem>
|
|
))}
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
{useOtel && isLoadingKeys ? (
|
|
<p className="text-sm text-foreground-light py-2">Loading fields…</p>
|
|
) : (
|
|
<Table
|
|
head={[
|
|
<Table.th className="text-xs p-2!" key="path">
|
|
Path
|
|
</Table.th>,
|
|
<Table.th key="type" className="text-xs p-2!">
|
|
Type
|
|
</Table.th>,
|
|
]}
|
|
body={(() => {
|
|
const fields = useOtel
|
|
? otelFieldsFromKeys(discoveredKeys ?? [])
|
|
: selectedSchema.fields
|
|
return fields.map((field) => <Field key={field.path} field={field} />)
|
|
})()}
|
|
/>
|
|
)}
|
|
</div>
|
|
</SidePanel>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default LogsExplorerHeader
|
|
|
|
const Field = ({
|
|
field,
|
|
}: {
|
|
field: {
|
|
path: string
|
|
type: string
|
|
}
|
|
}) => {
|
|
const [isCopied, setIsCopied] = useState(false)
|
|
|
|
return (
|
|
<Table.tr>
|
|
<Table.td
|
|
className="font-mono text-xs p-2! cursor-pointer hover:text-foreground transition flex items-center space-x-2"
|
|
onClick={() =>
|
|
copyToClipboard(field.path, () => {
|
|
setIsCopied(true)
|
|
setTimeout(() => setIsCopied(false), 3000)
|
|
})
|
|
}
|
|
>
|
|
<span>{field.path}</span>
|
|
{isCopied ? (
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<Check size={14} strokeWidth={3} className="text-primary" />
|
|
</TooltipTrigger>
|
|
<TooltipContent side="bottom" className="font-sans">
|
|
Copied
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
) : (
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<Copy size={14} />
|
|
</TooltipTrigger>
|
|
<TooltipContent side="bottom" className="font-sans">
|
|
Copy value
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
</Table.td>
|
|
<Table.td className="font-mono text-xs p-2!">{field.type}</Table.td>
|
|
</Table.tr>
|
|
)
|
|
}
|