Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5dfe350
Added speakers to the hackathon (#40363)
CoolAssPuppy Nov 12, 2025
426c371
fix: correctly set entrypoint, import map path and static patterns fo…
laktek Nov 12, 2025
c5bf65b
feat: Create FDW for S3 Vectors buckets (#40206)
ivasilov Nov 12, 2025
ccbdde2
docs: Update connection string logic for Docker compatibility (#40288)
riderx Nov 12, 2025
1f6ce0d
Table Editor link to policies page use table name as search instead o…
joshenlim Nov 12, 2025
0f8f68b
chore: updates to self-hosted image versions (#39942)
kiwicopple Nov 12, 2025
8132f0d
[FE-2111] chore: adjust custom domains refetch interval (#40377)
alaister Nov 12, 2025
b6c57fb
Hide cmd k keyboard shortcut if disabled in account pref (#40332)
joshenlim Nov 12, 2025
1b81ebb
Fix: added PR reminder action for Dashboard team (#40357)
awaseem Nov 12, 2025
5be6a0c
Remove static destinations UI from ETL early access state (#40333)
joshenlim Nov 12, 2025
4be5756
fix: max export row count typo (#40380)
ignaciodob Nov 12, 2025
45e6a8e
fix: add image version tracking and changelog for self-hosted supabas…
aantti Nov 12, 2025
35fc021
fix: correctly generate migration insert statements for integrations …
pcnc Nov 12, 2025
cb045c9
Chore: add param routing on panels (#40184)
fsansalvadore Nov 12, 2025
8582c6a
fix: correct whatsmyip link and missing space (#39519)
jose-ledesma Nov 12, 2025
0382490
feat: update realtime report (#40252)
filipecabaco Nov 12, 2025
319c889
ci(ratchet): include filename in error message (#40387)
charislam Nov 12, 2025
23c7c8c
Fix: updated action to include state (#40385)
awaseem Nov 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/dashboard-pr-reminder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Dashboard PR Reminder

on:
schedule:
# Run at 10am Singapore Time (2am UTC)
- cron: '0 2 * * *'
# Run at 10am US Eastern Time (2pm UTC = 10am EDT / 9am EST)
- cron: '0 14 * * *'
workflow_dispatch: # Allow manual trigger for testing

permissions:
pull-requests: read
contents: read

jobs:
check-dashboard-prs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0

- name: Find Dashboard PRs older than 24 hours
id: find-prs
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const findStalePRs = require('./scripts/actions/find-stale-dashboard-prs.js');
return await findStalePRs({ github, context, core });

- name: Send Slack notification
if: fromJSON(steps.find-prs.outputs.count) > 0
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_DASHBOARD_WEBHOOK_URL }}
STALE_PRS_JSON: ${{ steps.find-prs.outputs.stale_prs }}
with:
script: |
const sendSlackNotification = require('./scripts/actions/send-slack-pr-notification.js');
const stalePRs = JSON.parse(process.env.STALE_PRS_JSON);
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
await sendSlackNotification(stalePRs, webhookUrl);

- name: No stale PRs found
if: fromJSON(steps.find-prs.outputs.count) == 0
run: |
echo "✓ No Dashboard PRs older than 24 hours found"
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,14 @@ const pageMap = [
},
remoteFile: 's3.md',
},
{
slug: 's3_vectors',
meta: {
title: 'AWS S3 Vectors',
dashboardIntegrationPath: 's3_vectors_wrapper',
},
remoteFile: 's3vectors.md',
},
{
slug: 'snowflake',
meta: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,10 @@ export const database: NavMenuConstant = {
name: 'Connecting to AWS S3',
url: '/guides/database/extensions/wrappers/s3' as `/${string}`,
},
{
name: 'Connecting to AWS S3 Vectors',
url: '/guides/database/extensions/wrappers/s3_vectors' as `/${string}`,
},
{
name: 'Connecting to BigQuery',
url: '/guides/database/extensions/wrappers/bigquery' as `/${string}`,
Expand Down
9 changes: 8 additions & 1 deletion apps/docs/content/guides/database/drizzle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ If you plan on solely using Drizzle instead of the Supabase Data API (PostgREST)

From the project [**Connect** panel](/dashboard/project/_?showConnect=true), copy the URI from the "Shared Pooler" option and save it as the `DATABASE_URL` environment variable. Remember to replace the password placeholder with your actual database password.

In local SUPABASE_DB_URL require to be adapted to work with Docker resolver

</StepHikeCompact.Details>

<StepHikeCompact.Code>
Expand All @@ -78,7 +80,12 @@ If you plan on solely using Drizzle instead of the Supabase Data API (PostgREST)
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'

const connectionString = process.env.DATABASE_URL
let connectionString = process.env.DATABASE_URL
if (host.includes('postgres:postgres@supabase_db_')) {
const url = URL.parse(host)!
url.hostname = url.hostname.split('_')[1]
connectionString = url.href
}

// Disable prefetch as it is not supported for "Transaction" pool mode
export const client = postgres(connectionString, { prepare: false })
Expand Down
264 changes: 263 additions & 1 deletion apps/studio/.github/eslint-rule-baselines.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/studio/components/grid/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import { SortPopover } from './sort/SortPopover'
export const MAX_EXPORT_ROW_COUNT = 500000
export const MAX_EXPORT_ROW_COUNT_MESSAGE = (
<>
Sorry! We're unable to support exporting row counts larger than $
Sorry! We're unable to support exporting row counts larger than{' '}
{MAX_EXPORT_ROW_COUNT.toLocaleString()} at the moment. Alternatively, you may consider using
<Link href={`${DOCS_URL}/reference/cli/supabase-db-dump`} target="_blank">
pg_dump
Expand Down
132 changes: 21 additions & 111 deletions apps/studio/components/interfaces/Database/ETL/ComingSoon.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
import { motion } from 'framer-motion'
import {
ArrowRight,
ArrowUpRight,
Circle,
Database,
MoreVertical,
Plus,
Search,
} from 'lucide-react'
import { ArrowRight, ArrowUpRight, Circle, Database, Plus } from 'lucide-react'
import { useTheme } from 'next-themes'
import Link from 'next/link'
import { useMemo } from 'react'
import ReactFlow, { Background, Handle, Position, ReactFlowProvider } from 'reactflow'
import 'reactflow/dist/style.css'

import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import Table from 'components/to-be-cleaned/Table'
import { BASE_PATH } from 'lib/constants'
import { Badge, Button, Card, CardContent, Input_Shadcn_ } from 'ui'
import { Badge, Button, Card, CardContent } from 'ui'
import { NODE_WIDTH } from '../../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants'

const STATIC_NODES = [
Expand Down Expand Up @@ -100,28 +89,25 @@ const ReplicationStaticMockup = ({ projectRef }: { projectRef: string }) => {
)

return (
<div className="relative border-t">
<div className="h-[500px] w-full relative">
<ReactFlow
fitView
fitViewOptions={{ minZoom: 0.9, maxZoom: 0.9 }}
className="instance-configuration"
zoomOnPinch={false}
zoomOnScroll={false}
nodesDraggable={true}
nodesConnectable={false}
zoomOnDoubleClick={false}
edgesFocusable={false}
edgesUpdatable={false}
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
proOptions={{ hideAttribution: true }}
>
<Background color={backgroundPatternColor} />
</ReactFlow>
</div>
<StaticDestinations />
<div className="relative border-t h-full w-full">
<ReactFlow
fitView
fitViewOptions={{ minZoom: 0.9, maxZoom: 0.9 }}
className="instance-configuration"
zoomOnPinch={false}
zoomOnScroll={false}
nodesDraggable={true}
nodesConnectable={false}
zoomOnDoubleClick={false}
edgesFocusable={false}
edgesUpdatable={false}
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
proOptions={{ hideAttribution: true }}
>
<Background color={backgroundPatternColor} />
</ReactFlow>
</div>
)
}
Expand Down Expand Up @@ -236,79 +222,3 @@ const CTANode = ({ projectRef }: { projectRef: string }) => {
</Card>
)
}

const StaticDestinations = () => {
const mockRows = [
{ name: 'BigQuery', tables: 4, lag: '55ms', status: 'Enabled' },
{ name: 'Iceberg', tables: 4, lag: '85ms', status: 'Enabled' },
{ name: 'US East', tables: 4, lag: '125ms', status: 'Enabled' },
]

return (
<>
<div className="flex flex-col bg-surface-100 px-6 py-6 border-t relative ">
<div className="z-10 bg-surface-300 w-full h-full absolute top-0 left-0 opacity-30" />

<ScaffoldContainer>
<ScaffoldSection className="!py-0">
<div className="col-span-12">
<div className="flex items-center justify-between">
<div className="relative w-52">
<Search
size={14}
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-foreground-lighter"
/>
<Input_Shadcn_
className="pl-9 bg-transparent h-8 pointer-events-none"
placeholder="Search..."
/>
</div>
<Button
disabled
type="primary"
icon={<Plus size={16} />}
className="flex items-center pointer-events-none"
>
New destination
</Button>
</div>
<Table
head={[
<Table.th key="name">Name</Table.th>,
<Table.th key="publication">Publication</Table.th>,
<Table.th key="lag">Lag</Table.th>,
<Table.th key="status">Status</Table.th>,
<Table.th key="actions"></Table.th>,
]}
className="mt-4"
body={mockRows.map((row, i) => (
<Table.tr key={i}>
<Table.td>{row.name}</Table.td>
<Table.td>
<span className="flex items-center gap-2">
<span className="font-bold">All</span>
<span className="text-sm text-foreground-lighter">{row.tables} tables</span>
</span>
</Table.td>
<Table.td>{row.lag}</Table.td>
<Table.td>
<span className="flex items-center gap-3">
<Circle size={10} className="bg-brand-500 stroke-none rounded-full" />
{row.status}
</span>
</Table.td>
<Table.td className="text-right">
<button className="p-1">
<MoreVertical size={18} />
</button>
</Table.td>
</Table.tr>
))}
/>
</div>
</ScaffoldSection>
</ScaffoldContainer>
</div>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ interface DeleteEnumeratedTypeModalProps {
visible: boolean
selectedEnumeratedType?: any
onClose: () => void
onDelete?: () => void
}

const DeleteEnumeratedTypeModal = ({
visible,
selectedEnumeratedType,
onClose,
onDelete,
}: DeleteEnumeratedTypeModalProps) => {
const { data: project } = useSelectedProjectQuery()
const { mutate: deleteEnumeratedType, isLoading: isDeleting } = useEnumeratedTypeDeleteMutation({
Expand All @@ -29,6 +31,7 @@ const DeleteEnumeratedTypeModal = ({
if (project?.connectionString === undefined)
return console.error('Project connectionString required')

onDelete?.()
deleteEnumeratedType({
projectRef: project?.ref,
connectionString: project?.connectionString,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Edit, MoreVertical, Search, Trash } from 'lucide-react'
import { useState } from 'react'
import { parseAsBoolean, useQueryState } from 'nuqs'
import { useRef, useState } from 'react'
import { toast } from 'sonner'

import AlertError from 'components/ui/AlertError'
import { DocsButton } from 'components/ui/DocsButton'
import SchemaSelector from 'components/ui/SchemaSelector'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import {
EnumeratedType,
useEnumeratedTypesQuery,
} from 'data/enumerated-types/enumerated-types-query'
import { useEnumeratedTypesQuery } from 'data/enumerated-types/enumerated-types-query'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { handleErrorOnDelete, useQueryStateWithSelect } from 'hooks/misc/useQueryStateWithSelect'
import { useIsProtectedSchema } from 'hooks/useProtectedSchemas'
import {
Button,
Expand All @@ -36,14 +36,33 @@ export const EnumeratedTypes = () => {
const { data: project } = useSelectedProjectQuery()
const [search, setSearch] = useState('')
const { selectedSchema, setSelectedSchema } = useQuerySchemaState()
const [showCreateTypePanel, setShowCreateTypePanel] = useState(false)
const [selectedTypeToEdit, setSelectedTypeToEdit] = useState<EnumeratedType>()
const [selectedTypeToDelete, setSelectedTypeToDelete] = useState<EnumeratedType>()
const deletingTypeIdRef = useRef<string | null>(null)

const { data, error, isLoading, isError, isSuccess } = useEnumeratedTypesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})

const [showCreateTypePanel, setShowCreateTypePanel] = useQueryState(
'new',
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
)

const { value: typeToEdit, setValue: setSelectedTypeIdToEdit } = useQueryStateWithSelect({
urlKey: 'edit',
select: (id) => (id ? data?.find((type) => type.id.toString() === id) : undefined),
enabled: !!data,
onError: () => toast.error(`Enumerated Type not found`),
})

const { value: typeToDelete, setValue: setSelectedTypeIdToDelete } = useQueryStateWithSelect({
urlKey: 'delete',
select: (id) => (id ? data?.find((type) => type.id.toString() === id) : undefined),
enabled: !!data,
onError: (_error, selectedId) =>
handleErrorOnDelete(deletingTypeIdRef, selectedId, `Enumerated Type not found`),
})

const enumeratedTypes = (data ?? []).filter((type) => type.enums.length > 0)
const filteredEnumeratedTypes =
search.length > 0
Expand Down Expand Up @@ -150,14 +169,14 @@ export const EnumeratedTypes = () => {
<DropdownMenuContent side="bottom" align="end" className="w-32">
<DropdownMenuItem
className="space-x-2"
onClick={() => setSelectedTypeToEdit(type)}
onClick={() => setSelectedTypeIdToEdit(type.id.toString())}
>
<Edit size={14} />
<p>Update type</p>
</DropdownMenuItem>
<DropdownMenuItem
className="space-x-2"
onClick={() => setSelectedTypeToDelete(type)}
onClick={() => setSelectedTypeIdToDelete(type.id.toString())}
>
<Trash size={14} />
<p>Delete type</p>
Expand All @@ -182,15 +201,20 @@ export const EnumeratedTypes = () => {
/>

<EditEnumeratedTypeSidePanel
visible={selectedTypeToEdit !== undefined}
selectedEnumeratedType={selectedTypeToEdit}
onClose={() => setSelectedTypeToEdit(undefined)}
visible={!!typeToEdit}
selectedEnumeratedType={typeToEdit}
onClose={() => setSelectedTypeIdToEdit(null)}
/>

<DeleteEnumeratedTypeModal
visible={selectedTypeToDelete !== undefined}
selectedEnumeratedType={selectedTypeToDelete}
onClose={() => setSelectedTypeToDelete(undefined)}
visible={!!typeToDelete}
selectedEnumeratedType={typeToDelete}
onClose={() => setSelectedTypeIdToDelete(null)}
onDelete={() => {
if (typeToDelete) {
deletingTypeIdRef.current = typeToDelete.id.toString()
}
}}
/>
</div>
)
Expand Down
Loading
Loading