Skip to content

Commit f1eb1dc

Browse files
feat(studio): log drains list brush up (supabase#40020)
* feat: log drains card A little more control over our card and isolated so we can include pricing information * feat: separate vote link * feat: get the right page layout components * feat: tidy up table * feat: quick pick dropdown * feat: prettify name of destination * fix: checks for button and header * feat: rename http endpoint to custom * chore: copy update and remove github icon on vote * feat: add confirmation modal for when adding log drains * feat: sort table by name We dont have create_at for log drains so sorting the table by names * chore: sort by id instead * fix: mobile scaling of table * chore: clean up commented elements
1 parent 8b65716 commit f1eb1dc

File tree

6 files changed

+264
-134
lines changed

6 files changed

+264
-134
lines changed

apps/studio/components/interfaces/LogDrains/LogDrains.constants.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type LogDrainType = components['schemas']['CreateBackendParamsOpenapi']['
1313
export const LOG_DRAIN_TYPES = [
1414
{
1515
value: 'webhook',
16-
name: 'HTTP Endpoint',
16+
name: 'Custom Endpoint',
1717
description: 'Forward logs as a POST request to a custom HTTP endpoint',
1818
icon: <BracesIcon {...iconProps} />,
1919
},

apps/studio/components/interfaces/LogDrains/LogDrains.tsx

Lines changed: 87 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import { MoreHorizontal, Pencil, TrashIcon } from 'lucide-react'
2-
import { useState } from 'react'
2+
import React, { useState } from 'react'
33
import { toast } from 'sonner'
4-
54
import { useFlag, useParams } from 'common'
65
import AlertError from 'components/ui/AlertError'
7-
import CardButton from 'components/ui/CardButton'
8-
import Panel from 'components/ui/Panel'
96
import { useDeleteLogDrainMutation } from 'data/log-drains/delete-log-drain-mutation'
107
import { LogDrainData, useLogDrainsQuery } from 'data/log-drains/log-drains-query'
118
import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan'
@@ -21,11 +18,15 @@ import {
2118
TableHead,
2219
TableHeader,
2320
TableRow,
21+
Card,
22+
cn,
2423
} from 'ui'
2524
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
2625
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
2726
import { LOG_DRAIN_TYPES, LogDrainType } from './LogDrains.constants'
2827
import { LogDrainsEmpty } from './LogDrainsEmpty'
28+
import { LogDrainsCard } from './LogDrainsCard'
29+
import { VoteLink } from './VoteLink'
2930

3031
export function LogDrains({
3132
onNewDrainClick,
@@ -81,19 +82,23 @@ export function LogDrains({
8182

8283
if (!isLoading && !hasLogDrains) {
8384
return (
84-
<div className="grid lg:grid-cols-2 gap-3">
85-
{LOG_DRAIN_TYPES.filter((t) => t.value !== 'sentry' || sentryEnabled).map((src) => (
86-
<CardButton
87-
key={src.value}
88-
title={src.name}
89-
description={src.description}
90-
icon={src.icon}
91-
onClick={() => {
92-
onNewDrainClick(src.value)
93-
}}
94-
/>
95-
))}
96-
</div>
85+
<>
86+
<div className="grid lg:grid-cols-2 gap-4">
87+
{LOG_DRAIN_TYPES.filter((t) => t.value !== 'sentry' || sentryEnabled).map((src) => (
88+
<LogDrainsCard
89+
key={src.value}
90+
title={src.name}
91+
description={src.description}
92+
icon={src.icon}
93+
rightLabel="Additional $60"
94+
onClick={() => {
95+
onNewDrainClick(src.value)
96+
}}
97+
/>
98+
))}
99+
</div>
100+
<VoteLink />
101+
</>
97102
)
98103
}
99104

@@ -103,63 +108,80 @@ export function LogDrains({
103108

104109
return (
105110
<>
106-
<Panel className="">
111+
<Card>
107112
<Table>
108113
<TableHeader>
109114
<TableRow>
110115
<TableHead className="max-w-[200px]">Name</TableHead>
111-
<TableHead>Description</TableHead>
112-
<TableHead className="text-right">Destination</TableHead>
116+
<TableHead className="w-96">Description</TableHead>
117+
<TableHead className="w-48">Destination</TableHead>
113118
<TableHead className="text-right">
114119
<div className="sr-only">Actions</div>
115120
</TableHead>
116121
</TableRow>
117122
</TableHeader>
118123
<TableBody>
119-
{logDrains?.map((drain) => (
120-
<TableRow key={drain.id}>
121-
<TableCell className="font-medium truncate max-w-72" title={drain.name}>
122-
{drain.name}
123-
</TableCell>
124-
<TableCell
125-
className="text-foreground-light truncate max-w-72"
126-
title={drain.description}
127-
>
128-
{drain.description}
129-
</TableCell>
130-
<TableCell className="text-right font-mono">{drain.type}</TableCell>
131-
<TableCell className="text-right">
132-
<DropdownMenu>
133-
<DropdownMenuTrigger asChild>
134-
<Button
135-
type="text"
136-
className="px-1 opacity-50 hover:opacity-100 !bg-transparent"
137-
icon={<MoreHorizontal />}
138-
/>
139-
</DropdownMenuTrigger>
140-
<DropdownMenuContent className="max-w-[140px]" align="end">
141-
<DropdownMenuItem
142-
onClick={() => {
143-
onUpdateDrainClick(drain)
144-
}}
145-
>
146-
<Pencil className="h-4 w-4 mr-2" />
147-
Update
148-
</DropdownMenuItem>
149-
<DropdownMenuItem
150-
onClick={() => {
151-
setSelectedLogDrain(drain)
152-
setIsDeleteModalOpen(true)
153-
}}
154-
>
155-
<TrashIcon className="h-4 w-4 mr-2" />
156-
Delete
157-
</DropdownMenuItem>
158-
</DropdownMenuContent>
159-
</DropdownMenu>
160-
</TableCell>
161-
</TableRow>
162-
))}
124+
{logDrains
125+
?.slice()
126+
.sort((a, b) => b.id - a.id)
127+
.map((drain) => (
128+
<TableRow key={drain.id}>
129+
<TableCell className="font-medium truncate max-w-72" title={drain.name}>
130+
{drain.name}
131+
</TableCell>
132+
<TableCell
133+
className={cn(
134+
'truncate max-w-96',
135+
drain.description ? 'text-foreground-light' : 'text-foreground-muted'
136+
)}
137+
title={drain.description}
138+
>
139+
{drain.description || '-'}
140+
</TableCell>
141+
<TableCell className="text-foreground-light">
142+
<div className="flex items-center gap-2">
143+
{React.cloneElement(
144+
LOG_DRAIN_TYPES.find((t) => t.value === drain.type)
145+
?.icon as React.ReactElement,
146+
{ width: 16, height: 16 }
147+
)}
148+
<span className="truncate max-w-40">
149+
{LOG_DRAIN_TYPES.find((t) => t.value === drain.type)?.name}
150+
</span>
151+
</div>
152+
</TableCell>
153+
<TableCell className="text-right">
154+
<DropdownMenu>
155+
<DropdownMenuTrigger asChild>
156+
<Button
157+
type="text"
158+
className="px-1 opacity-50 hover:opacity-100 !bg-transparent flex-shrink-0"
159+
icon={<MoreHorizontal />}
160+
/>
161+
</DropdownMenuTrigger>
162+
<DropdownMenuContent className="max-w-[140px]" align="end">
163+
<DropdownMenuItem
164+
onClick={() => {
165+
onUpdateDrainClick(drain)
166+
}}
167+
>
168+
<Pencil className="h-4 w-4 mr-2" />
169+
Update
170+
</DropdownMenuItem>
171+
<DropdownMenuItem
172+
onClick={() => {
173+
setSelectedLogDrain(drain)
174+
setIsDeleteModalOpen(true)
175+
}}
176+
>
177+
<TrashIcon className="h-4 w-4 mr-2" />
178+
Delete
179+
</DropdownMenuItem>
180+
</DropdownMenuContent>
181+
</DropdownMenu>
182+
</TableCell>
183+
</TableRow>
184+
))}
163185
</TableBody>
164186

165187
<ConfirmationModal
@@ -183,7 +205,7 @@ export function LogDrains({
183205
</div>
184206
</ConfirmationModal>
185207
</Table>
186-
</Panel>
208+
</Card>
187209
</>
188210
)
189211
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Card } from 'ui'
2+
3+
interface LogDrainsCardProps {
4+
title: string
5+
description: string
6+
rightLabel?: string
7+
icon: React.ReactNode
8+
onClick: () => void
9+
}
10+
11+
export const LogDrainsCard = ({
12+
title,
13+
description,
14+
rightLabel,
15+
icon,
16+
onClick,
17+
}: LogDrainsCardProps) => {
18+
return (
19+
<button className="w-full h-full text-left" onClick={onClick}>
20+
<Card className="p-6 cursor-pointer hover:bg-surface-200 hover:border-strong transition-colors h-48">
21+
<div className="flex flex-col gap-5">
22+
<div className="flex items-start justify-between w-full">
23+
<div>{icon}</div>
24+
<div className="text-xs text-foreground-light">{rightLabel}</div>
25+
</div>
26+
<div className="flex flex-col gap-1">
27+
<h1 className="text-base">{title}</h1>
28+
<p className="text-sm text-foreground-light leading-relaxed">{description}</p>
29+
</div>
30+
</div>
31+
</Card>
32+
</button>
33+
)
34+
}

apps/studio/components/interfaces/LogDrains/LogDrainsEmpty.tsx

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { Button, Card, cn } from 'ui'
22
import Link from 'next/link'
33
import { AnimatedLogos } from './AnimatedLogos'
4-
import Image from 'next/image'
5-
import { BASE_PATH } from 'lib/constants'
64
import { UpgradePlanButton } from 'components/ui/UpgradePlanButton'
5+
import { VoteLink } from './VoteLink'
76

87
export const LogDrainsEmpty = () => {
98
const items = [
109
{
1110
step: 1,
12-
title: 'Log drain pricing',
11+
title: 'Pricing',
1312
description:
1413
'Log Drains are available as a project Add-On for all Team and Enterprise users. Each Log Drain costs $60 per month.',
1514
label: 'See our pricing',
@@ -19,14 +18,14 @@ export const LogDrainsEmpty = () => {
1918
step: 2,
2019
title: 'Connect to your drain',
2120
description:
22-
'We offer support for multiple destinations including HTTPS, Datadog, Loki and Sentry.',
21+
'We offer support for multiple destinations including Datadog, Loki, Sentry or a custom endpoint.',
2322
label: 'Read our documentation',
2423
link: 'https://supabase.com/docs/guides/telemetry/log-drains',
2524
},
2625
]
2726

2827
return (
29-
<div className="flex grow h-full pt-16">
28+
<div className="flex grow h-full">
3029
<div className="flex grow items-center justify-center p-12 @container">
3130
<div className="w-full max-w-4xl flex flex-col items-center gap-0">
3231
<div className="text-center mb-12">
@@ -61,25 +60,7 @@ export const LogDrainsEmpty = () => {
6160
</div>
6261
))}
6362
</Card>
64-
<div className="flex items-center justify-center gap-1.5 text-sm">
65-
<Image
66-
className={cn('dark:invert text-muted')}
67-
src={`${BASE_PATH}/img/icons/github-icon.svg`}
68-
width={16}
69-
height={16}
70-
alt="GitHub icon"
71-
/>
72-
<p className="text-foreground-light">
73-
Don't see your preferred drain?{' '}
74-
<Link
75-
href="https://github.com/orgs/supabase/discussions/28324?sort=top"
76-
className="text-foreground underline underline-offset-2 decoration-foreground-muted hover:decoration-foreground transition-all"
77-
target="_blank"
78-
>
79-
Vote here
80-
</Link>
81-
</p>
82-
</div>
63+
<VoteLink />
8364
</div>
8465
</div>
8566
</div>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Link from 'next/link'
2+
3+
export const VoteLink = () => {
4+
return (
5+
<div className="flex items-center justify-center gap-1.5 text-sm">
6+
<p className="text-foreground-light">
7+
Don't see your preferred drain?{' '}
8+
<Link
9+
href="https://github.com/orgs/supabase/discussions/28324?sort=top"
10+
className="text-foreground underline underline-offset-2 decoration-foreground-muted hover:decoration-foreground transition-all"
11+
target="_blank"
12+
>
13+
Vote here
14+
</Link>
15+
</p>
16+
</div>
17+
)
18+
}

0 commit comments

Comments
 (0)