From d7ab35e844d844b2e9ca484cc9d603d4b2e81ee3 Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Tue, 19 Aug 2025 16:05:07 +1000 Subject: [PATCH] refinements --- .../interfaces/Home/ActivityStats.tsx | 84 ----- .../interfaces/Home/AdvisorSection.tsx | 7 - .../interfaces/Home/CustomReportSection.tsx | 344 ------------------ .../interfaces/Home/GettingStarted.tsx | 100 ----- .../interfaces/Home/GettingStartedSection.tsx | 93 ----- .../interfaces/Home/SortableSection.tsx | 38 -- .../components/interfaces/Home/TopSection.tsx | 98 ----- .../interfaces/HomeNew/ActivityStats.tsx | 45 ++- .../interfaces/HomeNew/AdvisorWidget.tsx | 10 +- .../interfaces/HomeNew/GettingStarted.tsx | 38 +- .../HomeNew/GettingStartedSection.tsx | 15 +- .../HomeNew/ProjectUsageSection.tsx | 13 +- .../interfaces/HomeNew/ServiceStatus.tsx | 22 +- .../interfaces/HomeNew/TopSection.tsx | 2 +- .../InstanceConfiguration.tsx | 196 +++++----- apps/studio/pages/project/[ref]/home.tsx | 17 +- .../getting-started/edge-functions-dark.svg | 76 ++++ 17 files changed, 269 insertions(+), 929 deletions(-) delete mode 100644 apps/studio/components/interfaces/Home/ActivityStats.tsx delete mode 100644 apps/studio/components/interfaces/Home/AdvisorSection.tsx delete mode 100644 apps/studio/components/interfaces/Home/CustomReportSection.tsx delete mode 100644 apps/studio/components/interfaces/Home/GettingStarted.tsx delete mode 100644 apps/studio/components/interfaces/Home/GettingStartedSection.tsx delete mode 100644 apps/studio/components/interfaces/Home/SortableSection.tsx delete mode 100644 apps/studio/components/interfaces/Home/TopSection.tsx create mode 100644 apps/studio/public/img/getting-started/edge-functions-dark.svg diff --git a/apps/studio/components/interfaces/Home/ActivityStats.tsx b/apps/studio/components/interfaces/Home/ActivityStats.tsx deleted file mode 100644 index 3b54810f9fb..00000000000 --- a/apps/studio/components/interfaces/Home/ActivityStats.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import dayjs from 'dayjs' -import Link from 'next/link' -import { useParams } from 'common' -import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' -import { useBranchesQuery } from 'data/branches/branches-query' -import { useMigrationsQuery } from 'data/database/migrations-query' -import { useBackupsQuery } from 'data/database/backups-query' -import { ServiceStatus } from './ServiceStatus' - -export default function ActivityStats() { - const { data: project } = useSelectedProjectQuery() - const { ref } = useParams() - - const { data: branchesData, isLoading: isLoadingBranches } = useBranchesQuery({ - projectRef: project?.parent_project_ref ?? project?.ref, - }) - const latestBranch = (branchesData ?? []) - .slice() - .sort( - (a, b) => - new Date(b.created_at ?? b.updated_at).valueOf() - - new Date(a.created_at ?? a.updated_at).valueOf() - )[0] - - const { data: migrationsData, isLoading: isLoadingMigrations } = useMigrationsQuery({ - projectRef: project?.ref, - connectionString: project?.connectionString, - }) - const latestMigration = (migrationsData ?? [])[0] - - const { data: backupsData, isLoading: isLoadingBackups } = useBackupsQuery({ - projectRef: project?.ref, - }) - const latestBackup = (backupsData?.backups ?? []) - .slice() - .sort((a, b) => new Date(b.inserted_at).valueOf() - new Date(a.inserted_at).valueOf())[0] - - return ( -
-
-
-

Status

-
- -
-
- -

Last branch

-

- {isLoadingBranches - ? 'Loading...' - : latestBranch?.created_at - ? dayjs(latestBranch.created_at).fromNow() - : latestBranch?.updated_at - ? dayjs(latestBranch.updated_at).fromNow() - : 'No branches'} -

- - -

Last migration

-

- {isLoadingMigrations - ? 'Loading...' - : latestMigration - ? dayjs(latestMigration.version, 'YYYYMMDDHHmmss').fromNow() - : 'No migrations'} -

- - -

Last backup

-

- {isLoadingBackups - ? 'Loading...' - : backupsData?.pitr_enabled - ? 'PITR enabled' - : latestBackup - ? dayjs(latestBackup.inserted_at).fromNow() - : 'No backups'} -

- -
-
- ) -} diff --git a/apps/studio/components/interfaces/Home/AdvisorSection.tsx b/apps/studio/components/interfaces/Home/AdvisorSection.tsx deleted file mode 100644 index c93cb53493c..00000000000 --- a/apps/studio/components/interfaces/Home/AdvisorSection.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { AdvisorWidget } from './AdvisorWidget' - -export default function AdvisorSection() { - return -} - - diff --git a/apps/studio/components/interfaces/Home/CustomReportSection.tsx b/apps/studio/components/interfaces/Home/CustomReportSection.tsx deleted file mode 100644 index f61d9c1597a..00000000000 --- a/apps/studio/components/interfaces/Home/CustomReportSection.tsx +++ /dev/null @@ -1,344 +0,0 @@ -import { Plus } from 'lucide-react' -import { useEffect, useState } from 'react' -import { useParams } from 'common' -import { DEFAULT_CHART_CONFIG } from 'components/ui/QueryBlock/QueryBlock' -import { AnalyticsInterval } from 'data/analytics/constants' -import { useContentQuery } from 'data/content/content-query' -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, - Command_Shadcn_, - CommandInput_Shadcn_, - CommandList_Shadcn_, - CommandEmpty_Shadcn_, - CommandGroup_Shadcn_, - CommandItem_Shadcn_, -} from 'ui' -import { Row } from 'ui-patterns' -import { ReportBlock } from 'components/interfaces/Reports/ReportBlock/ReportBlock' -import dayjs from 'dayjs' -import type { Dashboards } from 'types' -import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { useProfile } from 'lib/profile' -import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Content, useContentQuery as useReportQuery } from 'data/content/content-query' -import { useContentUpsertMutation } from 'data/content/content-upsert-mutation' -import { - DndContext, - DragEndEvent, - DragStartEvent, - PointerSensor, - useSensor, - useSensors, - closestCenter, -} from '@dnd-kit/core' -import { SortableContext, rectSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable' -import type { CSSProperties, ReactNode } from 'react' - -type CustomReportSectionProps = {} - -export default function CustomReportSection({}: CustomReportSectionProps) { - const startDate = dayjs().subtract(7, 'day').toISOString() - const endDate = dayjs().toISOString() - const [snippetSearch, setSnippetSearch] = useState('') - const { ref } = useParams() - const { profile } = useProfile() - - // Load first report - const { data: reportsData } = useReportQuery({ projectRef: ref, type: 'report', limit: 1 }) - const firstReport = reportsData?.content?.[0] as Content | undefined - const reportContent = firstReport?.content as Dashboards.Content | undefined - const [editableReport, setEditableReport] = useState( - reportContent - ) - - useEffect(() => { - if (reportContent) setEditableReport(reportContent) - }, [reportContent]) - - const canUpdateReport = useCheckPermissions(PermissionAction.UPDATE, 'user_content', { - resource: { - type: 'report', - visibility: (firstReport as any)?.visibility, - owner_id: (firstReport as any)?.owner_id, - }, - subject: { id: profile?.id }, - }) - - const { mutate: upsertContent } = useContentUpsertMutation() - - const persistReport = (updated: Dashboards.Content) => { - if (!ref || !firstReport) return - upsertContent({ projectRef: ref, payload: { ...firstReport, content: updated } }) - } - - const { data: snippetsData, isLoading: isLoadingSnippets } = useContentQuery({ - projectRef: ref, - type: 'sql', - name: snippetSearch.length === 0 ? undefined : snippetSearch, - }) - const snippets = (snippetsData?.content ?? []) as any[] - - // Drag and drop reordering for report blocks - const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } })) - - const handleDragStart = (_event: DragStartEvent) => {} - - const handleDragEnd = (event: DragEndEvent) => { - const { active, over } = event - if (!editableReport || !active || !over || active.id === over.id) return - const items = editableReport.layout.map((x: any) => String(x.id)) - const oldIndex = items.indexOf(String(active.id)) - const newIndex = items.indexOf(String(over.id)) - if (oldIndex === -1 || newIndex === -1) return - - // Reorder and recompute a simple 2-column grid like GridResize does (x,y with w=1,h=1) - const moved = arrayMove(editableReport.layout, oldIndex, newIndex) - const recomputed = moved.map((block: any, idx: number) => ({ - ...block, - x: idx % 2, - y: Math.floor(idx / 2), - w: 1, - h: 1, - })) - const updated = { ...editableReport, layout: recomputed } - setEditableReport(updated) - persistReport(updated) - } - - const addSnippetToReport = (snippet: any) => { - if (!editableReport) return - const current = [...editableReport.layout] - let x = 0 - let y: number | null = null - const chartsByY = current.reduce((acc: Record, item: any) => { - const key = String(item.y) - acc[key] = acc[key] ? [...acc[key], item] : [item] - return acc - }, {}) - const yValues = Object.keys(chartsByY) - if (yValues.length === 0) { - y = 0 - } else { - for (const yValue of yValues) { - const totalWidthTaken = chartsByY[yValue].reduce((a, b) => a + b.w, 0) - if (2 - totalWidthTaken >= 1) { - y = Number(yValue) - x = totalWidthTaken - break - } - } - if (y === null) { - y = Number(yValues[yValues.length - 1]) + 1 - } - } - current.push({ - x, - y: y as number, - w: 1, - h: 1, - id: snippet.id, - label: snippet.name, - attribute: `snippet_${snippet.id}` as Dashboards.ChartType, - provider: undefined as any, - chart_type: 'bar', - chartConfig: DEFAULT_CHART_CONFIG, - }) - const updated = { ...editableReport, layout: current } - setEditableReport(updated) - persistReport(updated) - } - - const handleRemoveChart = ({ metric }: { metric: { key: string } }) => { - if (!editableReport) return - const nextLayout = editableReport.layout.filter((x) => x.attribute !== (metric.key as any)) - const updated = { ...editableReport, layout: nextLayout } - setEditableReport(updated) - persistReport(updated) - } - - const handleUpdateChart = ( - id: string, - { chart, chartConfig }: { chart?: Partial; chartConfig?: Partial } - ) => { - if (!editableReport) return - const currentChart = editableReport.layout.find((x) => x.id === id) - if (!currentChart) return - const updatedChart: Dashboards.Chart = { ...currentChart, ...(chart ?? {}) } - if (chartConfig) { - updatedChart.chartConfig = { ...(currentChart.chartConfig ?? {}), ...chartConfig } - } - const updatedLayouts = editableReport.layout.map((x) => (x.id === id ? updatedChart : x)) - const updated = { ...editableReport, layout: updatedLayouts } - setEditableReport(updated) - persistReport(updated) - } - - return ( -
-
-

At a glance

- {canUpdateReport ? ( - - - - - - - - - {isLoadingSnippets ? ( - Loading… - ) : snippets.length === 0 ? ( - No snippets found - ) : null} - - {snippets.map((snippet: any) => ( - addSnippetToReport(snippet)} - > - {snippet.name} - - ))} - - - - - - ) : null} -
-
- {(() => { - const layout = editableReport?.layout ?? [] - if (layout.length === 0) { - return ( -
- {canUpdateReport ? ( - - - - - - - - - {isLoadingSnippets ? ( - Loading… - ) : snippets.length === 0 ? ( - No snippets found - ) : null} - - {snippets.map((snippet: any) => ( - addSnippetToReport(snippet)} - > - {snippet.name} - - ))} - - - - - - ) : ( -

No charts set up yet in report

- )} -
- ) - } - return ( - - String(x.id))} - strategy={rectSortingStrategy} - > - - {layout.map((item: any) => ( - - {({ attributes, listeners }: { attributes: any; listeners: any }) => ( -
- handleUpdateChart(item.id, config as any)} - dragAttributes={attributes} - dragListeners={listeners} - /> -
- )} -
- ))} -
-
-
- ) - })()} -
-
- ) -} - -function SortableReportBlock({ - id, - children, -}: { - id: string - children: - | ((args: { attributes: any; listeners: any; isDragging: boolean }) => ReactNode) - | ReactNode -}) { - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ - id, - }) - - const style: CSSProperties = { - transform: transform - ? `translate3d(${Math.round(transform.x)}px, ${Math.round(transform.y)}px, 0)` - : undefined, - transition, - } - - return ( -
- {typeof children === 'function' - ? (children as any)({ attributes, listeners, isDragging }) - : children} -
- ) -} diff --git a/apps/studio/components/interfaces/Home/GettingStarted.tsx b/apps/studio/components/interfaces/Home/GettingStarted.tsx deleted file mode 100644 index c0d3d16b12d..00000000000 --- a/apps/studio/components/interfaces/Home/GettingStarted.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import Link from 'next/link' -import { Check } from 'lucide-react' -import { cn, Button, Card, CardContent, CardHeader, CardTitle } from 'ui' -import { Row } from 'ui-patterns' - -export type GettingStartedAction = { - label: string - href?: string - onClick?: () => void - variant?: React.ComponentProps['type'] - icon?: React.ReactNode -} - -export type GettingStartedStep = { - key: string - status: 'complete' | 'incomplete' - imageUrl?: string - imageAlt?: string - title: string - description: string - actions: GettingStartedAction[] -} - -export interface GettingStartedProps { - steps: GettingStartedStep[] - className?: string -} - -export function GettingStarted({ steps, className }: GettingStartedProps) { - return ( -
-
-

Getting started

- -
- - {steps.map((step) => ( - - - - {step.status === 'complete' && } - {step.status} - - - {step.imageUrl ? ( - {step.imageAlt - ) : ( -
- )} - -
-

{step.title}

-

{step.description}

-
-
- {step.actions.map((action, i) => { - const content = ( - - ) - if (action.href) { - return ( - - ) - } - return ( - - {content} - - ) - })} -
-
- - ))} - -
- ) -} - -export default GettingStarted diff --git a/apps/studio/components/interfaces/Home/GettingStartedSection.tsx b/apps/studio/components/interfaces/Home/GettingStartedSection.tsx deleted file mode 100644 index 4590a413255..00000000000 --- a/apps/studio/components/interfaces/Home/GettingStartedSection.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import dayjs from 'dayjs' -import GettingStarted, { GettingStartedStep } from './GettingStarted' -import { useParams } from 'common' -import { useTablesQuery } from 'data/tables/tables-query' -import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query' -import { useReadReplicasQuery } from 'data/read-replicas/replicas-query' -import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' - -export default function GettingStartedSection() { - const { data: project } = useSelectedProjectQuery() - const { ref } = useParams() - - const { data: tablesData } = useTablesQuery({ - projectRef: project?.ref, - connectionString: project?.connectionString, - schema: 'public', - }) - const { data: functionsData } = useEdgeFunctionsQuery({ projectRef: project?.ref }) - const { data: replicasData } = useReadReplicasQuery({ projectRef: project?.ref }) - - const tablesCount = Math.max(0, tablesData?.length ?? 0) - const functionsCount = Math.max(0, functionsData?.length ?? 0) - const replicasCount = Math.max(0, (replicasData?.length ?? 1) - 1) - - return ( - 0 ? 'complete' : 'incomplete', - title: 'Design your database', - description: - 'Start building your app by creating tables with the table editor or describe what you want with the assistant', - imageUrl: '/img/getting-started/one.png', - actions: [ - { label: 'View tables', href: `/project/${ref}/editor`, variant: 'default' }, - { label: 'Do it for me', href: `/project/${ref}/sql/templates`, variant: 'default' }, - ], - }, - { - key: 'add-data', - status: 'incomplete', - title: 'Add some data', - description: - 'Use the table editor to create some sample data, or ask the Assistant to generate some data for you.', - imageUrl: '/img/getting-started/two.jpg', - actions: [ - { label: 'Table editor', href: `/project/${ref}/editor`, variant: 'default' }, - { label: 'Run SQL', href: `/project/${ref}/sql/new`, variant: 'default' }, - ], - }, - { - key: 'implement-auth', - status: 'incomplete', - title: 'Implement auth', - description: 'Set up authentication and manage users using Supabase Auth.', - imageUrl: '/img/getting-started/four.webp', - actions: [ - { label: 'Users', href: `/project/${ref}/auth/users`, variant: 'default' }, - { label: 'Auth settings', href: `/project/${ref}/auth/settings`, variant: 'default' }, - ], - }, - { - key: 'implement-storage', - status: 'incomplete', - title: 'Implement storage', - description: 'Create buckets and manage files with Supabase Storage.', - imageUrl: '/img/getting-started/three.webp', - actions: [ - { label: 'Buckets', href: `/project/${ref}/storage/buckets`, variant: 'default' }, - { label: 'Policies', href: `/project/${ref}/storage/policies`, variant: 'default' }, - ], - }, - { - key: 'connect-app', - status: 'incomplete', - title: 'Connect your app', - description: - 'Install a client library and connect to your Supabase project to start querying your data', - imageUrl: '/img/getting-started/one.png', - actions: [ - { label: 'Client libraries', href: `/project/${ref}/settings/api`, variant: 'default' }, - { label: 'Quickstarts', href: `/project/${ref}/sql/quickstarts`, variant: 'default' }, - ], - }, - ] satisfies GettingStartedStep[] - } - /> - ) -} - - diff --git a/apps/studio/components/interfaces/Home/SortableSection.tsx b/apps/studio/components/interfaces/Home/SortableSection.tsx deleted file mode 100644 index b028cdf49a9..00000000000 --- a/apps/studio/components/interfaces/Home/SortableSection.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { GripVertical } from 'lucide-react' -import { CSS } from '@dnd-kit/utilities' -import { useSortable } from '@dnd-kit/sortable' -import type { CSSProperties, ReactNode } from 'react' - -type SortableSectionProps = { - id: string - children: ReactNode -} - -export function SortableSection({ id, children }: SortableSectionProps) { - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ - id, - }) - - const style: CSSProperties = { - transform: transform - ? `translate3d(${Math.round(transform.x)}px, ${Math.round(transform.y)}px, 0)` - : undefined, - transition, - } - - return ( -
- -
{children}
-
- ) -} - -export default SortableSection diff --git a/apps/studio/components/interfaces/Home/TopSection.tsx b/apps/studio/components/interfaces/Home/TopSection.tsx deleted file mode 100644 index 211fe264756..00000000000 --- a/apps/studio/components/interfaces/Home/TopSection.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import Link from 'next/link' -import ActivityStats from 'components/interfaces/Home/ActivityStats' -import { ComputeBadgeWrapper } from 'components/ui/ComputeBadgeWrapper' -import { InlineLink } from 'components/ui/InlineLink' -import { ProjectUpgradeFailedBanner } from 'components/ui/ProjectUpgradeFailedBanner' -import InstanceConfiguration from 'components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration' -import { Badge, Tooltip, TooltipContent, TooltipTrigger } from 'ui' -import { ProjectPausedState } from 'components/layouts/ProjectLayout/PausedState/ProjectPausedState' - -interface TopSectionProps { - projectName: string - isMainBranch?: boolean - parentProject?: { ref?: string; name?: string } | null - isOrioleDb?: boolean - project: any - organization: any - projectRef?: string - isPaused: boolean -} - -const TopSection = ({ - projectName, - isMainBranch, - parentProject, - isOrioleDb, - project, - organization, - projectRef, - isPaused, -}: TopSectionProps) => { - return ( -
-
-
-
-
-
- {!isMainBranch && ( - - {parentProject?.name} - - )} -

{projectName}

-
-
- {isOrioleDb && ( - - - OrioleDB - - - This project is using Postgres with OrioleDB which is currently in preview and - not suitable for production workloads. View our{' '} - - documentation - {' '} - for all limitations. - - - )} - -
-
-
- -
-
-
- -
-
- -
-
- -
-
- - {isPaused && } -
-
- ) -} - -export default TopSection diff --git a/apps/studio/components/interfaces/HomeNew/ActivityStats.tsx b/apps/studio/components/interfaces/HomeNew/ActivityStats.tsx index 3b54810f9fb..cf52d44918f 100644 --- a/apps/studio/components/interfaces/HomeNew/ActivityStats.tsx +++ b/apps/studio/components/interfaces/HomeNew/ActivityStats.tsx @@ -5,6 +5,7 @@ import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { useBranchesQuery } from 'data/branches/branches-query' import { useMigrationsQuery } from 'data/database/migrations-query' import { useBackupsQuery } from 'data/database/backups-query' +import { Skeleton } from 'ui' import { ServiceStatus } from './ServiceStatus' export default function ActivityStats() { @@ -47,35 +48,41 @@ export default function ActivityStats() {

Last branch

- {isLoadingBranches - ? 'Loading...' - : latestBranch?.created_at - ? dayjs(latestBranch.created_at).fromNow() - : latestBranch?.updated_at - ? dayjs(latestBranch.updated_at).fromNow() - : 'No branches'} + {isLoadingBranches ? ( + + ) : latestBranch?.created_at ? ( + dayjs(latestBranch.created_at).fromNow() + ) : latestBranch?.updated_at ? ( + dayjs(latestBranch.updated_at).fromNow() + ) : ( + 'No branches' + )}

Last migration

- {isLoadingMigrations - ? 'Loading...' - : latestMigration - ? dayjs(latestMigration.version, 'YYYYMMDDHHmmss').fromNow() - : 'No migrations'} + {isLoadingMigrations ? ( + + ) : latestMigration ? ( + dayjs(latestMigration.version, 'YYYYMMDDHHmmss').fromNow() + ) : ( + 'No migrations' + )}

Last backup

- {isLoadingBackups - ? 'Loading...' - : backupsData?.pitr_enabled - ? 'PITR enabled' - : latestBackup - ? dayjs(latestBackup.inserted_at).fromNow() - : 'No backups'} + {isLoadingBackups ? ( + + ) : backupsData?.pitr_enabled ? ( + 'PITR enabled' + ) : latestBackup ? ( + dayjs(latestBackup.inserted_at).fromNow() + ) : ( + 'No backups' + )}

diff --git a/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx b/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx index 50144e8f0f2..9c3096b1105 100644 --- a/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx +++ b/apps/studio/components/interfaces/HomeNew/AdvisorWidget.tsx @@ -1,4 +1,4 @@ -import { Shield } from 'lucide-react' +import { BarChart, Shield } from 'lucide-react' import Link from 'next/link' import { useMemo } from 'react' @@ -118,10 +118,16 @@ export const AdvisorWidget = () => { const levelLabel = 'Error' return ( - + + {lint.categories[0] === 'SECURITY' ? ( + + ) : ( + + )} {lint.categories[0]} {steps.map((step) => ( - - - {step.status === 'complete' && } - {step.status} - - - {step.imageUrl ? ( - {step.imageAlt - ) : ( -
- )} - -
-

{step.title}

-

{step.description}

+ +
+ {step.icon &&
{step.icon}
} + {step.title}
+ + {step.status} + +
+ +

{step.description}

{step.actions.map((action, i) => { const content = ( diff --git a/apps/studio/components/interfaces/HomeNew/GettingStartedSection.tsx b/apps/studio/components/interfaces/HomeNew/GettingStartedSection.tsx index aa68b2003f7..80a4b340cca 100644 --- a/apps/studio/components/interfaces/HomeNew/GettingStartedSection.tsx +++ b/apps/studio/components/interfaces/HomeNew/GettingStartedSection.tsx @@ -2,6 +2,7 @@ import { useParams } from 'common' import { useTablesQuery } from 'data/tables/tables-query' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import GettingStarted, { GettingStartedStep } from './GettingStarted' +import { Code, Database, File, Table, User } from 'lucide-react' export default function GettingStartedSection() { const { data: project } = useSelectedProjectQuery() @@ -22,9 +23,9 @@ export default function GettingStartedSection() { key: 'design-db', status: tablesCount > 0 ? 'complete' : 'incomplete', title: 'Design your database', + icon: , description: - 'Start building your app by creating tables with the table editor or describe what you want with the assistant', - imageUrl: '/img/getting-started/one.png', + 'Start building your app by creating tables with the table editor, sql editor or describe what you want with the assistant', actions: [ { label: 'View tables', href: `/project/${ref}/editor`, variant: 'default' }, { label: 'Do it for me', href: `/project/${ref}/sql/templates`, variant: 'default' }, @@ -34,9 +35,9 @@ export default function GettingStartedSection() { key: 'add-data', status: 'incomplete', title: 'Add some data', + icon: , description: 'Use the table editor to create some sample data, or ask the Assistant to generate some data for you.', - imageUrl: '/img/getting-started/two.jpg', actions: [ { label: 'Table editor', href: `/project/${ref}/editor`, variant: 'default' }, { label: 'Run SQL', href: `/project/${ref}/sql/new`, variant: 'default' }, @@ -46,8 +47,8 @@ export default function GettingStartedSection() { key: 'implement-auth', status: 'incomplete', title: 'Implement auth', + icon: , description: 'Set up authentication and manage users using Supabase Auth.', - imageUrl: '/img/getting-started/four.webp', actions: [ { label: 'Users', href: `/project/${ref}/auth/users`, variant: 'default' }, { label: 'Auth settings', href: `/project/${ref}/auth/settings`, variant: 'default' }, @@ -57,8 +58,8 @@ export default function GettingStartedSection() { key: 'implement-storage', status: 'incomplete', title: 'Implement storage', + icon: , description: 'Create buckets and manage files with Supabase Storage.', - imageUrl: '/img/getting-started/three.webp', actions: [ { label: 'Buckets', href: `/project/${ref}/storage/buckets`, variant: 'default' }, { label: 'Policies', href: `/project/${ref}/storage/policies`, variant: 'default' }, @@ -68,9 +69,9 @@ export default function GettingStartedSection() { key: 'connect-app', status: 'incomplete', title: 'Connect your app', + icon: , description: 'Install a client library and connect to your Supabase project to start querying your data', - imageUrl: '/img/getting-started/one.png', actions: [ { label: 'Client libraries', href: `/project/${ref}/settings/api`, variant: 'default' }, { label: 'Quickstarts', href: `/project/${ref}/sql/quickstarts`, variant: 'default' }, @@ -79,4 +80,4 @@ export default function GettingStartedSection() { ]} /> ) -} \ No newline at end of file +} diff --git a/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx b/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx index cdaeb78f73e..a5a64c438ce 100644 --- a/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx +++ b/apps/studio/components/interfaces/HomeNew/ProjectUsageSection.tsx @@ -6,6 +6,7 @@ import { useProjectLogRequestsCountQuery } from 'data/analytics/project-log-requ import { useProjectLogStatsQuery } from 'data/analytics/project-log-stats-query' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import ProjectUsage from './ProjectUsage' +import ShimmeringLoader from 'components/ui/ShimmeringLoader' export const ProjectUsageSection = () => { const { data: project } = useSelectedProjectQuery() @@ -18,7 +19,17 @@ export const ProjectUsageSection = () => { }) if (isLoading || isLogsStatsLoading) { - return + return ( +
+ +
+ + + + +
+
+ ) } if (error) { diff --git a/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx b/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx index 9700835281f..1af7b3645df 100644 --- a/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx +++ b/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx @@ -19,6 +19,7 @@ import { PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_, + Skeleton, } from 'ui' const SERVICE_STATUS_THRESHOLD = 5 // minutes @@ -280,13 +281,15 @@ export const ServiceStatus = () => { const anyUnhealthy = services.some((service) => !service.isHealthy) const anyComingUp = services.some((service) => service.status === 'COMING_UP') - const overallStatusLabel = isLoadingChecks - ? 'Checking status' - : anyUnhealthy - ? 'Unhealthy' - : anyComingUp || isProjectNew - ? 'Coming up...' - : 'Healthy' + const overallStatusLabel = isLoadingChecks ? ( + + ) : anyUnhealthy ? ( + 'Unhealthy' + ) : anyComingUp || isProjectNew ? ( + 'Coming up...' + ) : ( + 'Healthy' + ) return ( @@ -294,9 +297,8 @@ export const ServiceStatus = () => {