Skip to content
8 changes: 4 additions & 4 deletions apps/studio/components/grid/SupabaseGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { useTableEditorStateSnapshot } from 'state/table-editor'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'

import { Shortcuts } from './components/common/Shortcuts'
import Footer from './components/footer/Footer'
import { Footer } from './components/footer/Footer'
import { Grid } from './components/grid/Grid'
import Header, { HeaderProps } from './components/header/Header'
import { Header, HeaderProps } from './components/header/Header'
import { RowContextMenu } from './components/menu'
import { GridProps } from './types'

Expand Down Expand Up @@ -84,7 +84,7 @@ export const SupabaseGrid = ({
return (
<DndProvider backend={HTML5Backend} context={window}>
<div className="sb-grid h-full flex flex-col">
<Header customHeader={customHeader} />
<Header customHeader={customHeader} isRefetching={isRefetching} />

{children || (
<>
Expand All @@ -99,7 +99,7 @@ export const SupabaseGrid = ({
filters={filters}
onApplyFilters={onApplyFilters}
/>
<Footer isRefetching={isRefetching} />
<Footer />
<Shortcuts gridRef={gridRef} rows={rows} />
</>
)}
Expand Down
13 changes: 1 addition & 12 deletions apps/studio/components/grid/components/footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
import { isTableLike, isViewLike } from 'data/table-editor/table-editor-types'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useUrlState } from 'hooks/ui/useUrlState'
import RefreshButton from '../header/RefreshButton'
import { Pagination } from './pagination'

export interface FooterProps {
isRefetching?: boolean
}

const Footer = ({ isRefetching }: FooterProps) => {
export const Footer = () => {
const { id: _id } = useParams()
const id = _id ? Number(_id) : undefined
const { data: project } = useSelectedProjectQuery()
Expand Down Expand Up @@ -41,10 +36,6 @@ const Footer = ({ isRefetching }: FooterProps) => {
{selectedView === 'data' && <Pagination />}

<div className="ml-auto flex items-center gap-x-2">
{entity && selectedView === 'data' && (
<RefreshButton tableId={entity.id} isRefetching={isRefetching} />
)}

{(isViewSelected || isTableSelected) && (
<TwoOptionToggle
width={75}
Expand All @@ -58,5 +49,3 @@ const Footer = ({ isRefetching }: FooterProps) => {
</GridFooter>
)
}

export default Footer
9 changes: 4 additions & 5 deletions apps/studio/components/grid/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { toast } from 'sonner'
import { useParams } from 'common'
import { useTableFilter } from 'components/grid/hooks/useTableFilter'
import { useTableSort } from 'components/grid/hooks/useTableSort'
import GridHeaderActions from 'components/interfaces/TableGridEditor/GridHeaderActions'
import { GridHeaderActions } from 'components/interfaces/TableGridEditor/GridHeaderActions'
import { formatTableRowsToSQL } from 'components/interfaces/TableGridEditor/TableEntity.utils'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query'
Expand Down Expand Up @@ -56,9 +56,10 @@ export const MAX_EXPORT_ROW_COUNT_MESSAGE = (

export type HeaderProps = {
customHeader: ReactNode
isRefetching: boolean
}

const Header = ({ customHeader }: HeaderProps) => {
export const Header = ({ customHeader, isRefetching }: HeaderProps) => {
const snap = useTableEditorTableStateSnapshot()

return (
Expand All @@ -71,14 +72,12 @@ const Header = ({ customHeader }: HeaderProps) => {
) : (
<DefaultHeader />
)}
<GridHeaderActions table={snap.originalTable} />
<GridHeaderActions table={snap.originalTable} isRefetching={isRefetching} />
</div>
</div>
)
}

export default Header

const DefaultHeader = () => {
const { ref: projectRef } = useParams()
const { data: org } = useSelectedOrganizationQuery()
Expand Down
22 changes: 13 additions & 9 deletions apps/studio/components/grid/components/header/RefreshButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { useQueryClient } from '@tanstack/react-query'
import { RefreshCw } from 'lucide-react'

import { useParams } from 'common'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { tableRowKeys } from 'data/table-rows/keys'
import { Button } from 'ui'

export type RefreshButtonProps = {
tableId?: number
isRefetching?: boolean
}

const RefreshButton = ({ tableId, isRefetching }: RefreshButtonProps) => {
export const RefreshButton = ({ tableId, isRefetching }: RefreshButtonProps) => {
const { ref } = useParams()
const queryClient = useQueryClient()
const queryKey = tableRowKeys.tableRowsAndCount(ref, tableId)
Expand All @@ -20,14 +20,18 @@ const RefreshButton = ({ tableId, isRefetching }: RefreshButtonProps) => {
}

return (
<Button
type="text"
<ButtonTooltip
type="outline"
loading={isRefetching}
icon={<RefreshCw className="text-foreground-muted" strokeWidth={1.5} />}
icon={<RefreshCw />}
onClick={() => onClick()}
>
Refresh
</Button>
className="w-7 h-7 p-0"
tooltip={{
content: {
side: 'bottom',
text: 'Refresh table data',
},
}}
/>
)
}
export default RefreshButton
2 changes: 1 addition & 1 deletion apps/studio/components/interfaces/Auth/Users/UsersV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { toast } from 'sonner'
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useIsAPIDocsSidePanelEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import AlertError from 'components/ui/AlertError'
import APIDocsButton from 'components/ui/APIDocsButton'
import { APIDocsButton } from 'components/ui/APIDocsButton'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { FilterPopover } from 'components/ui/FilterPopover'
import { FormHeader } from 'components/ui/Forms/FormHeader'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ import {
Input_Shadcn_,
Label_Shadcn_ as Label,
Switch,
Tooltip,
TooltipContent,
TooltipTrigger,
cn,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
Expand Down Expand Up @@ -92,7 +89,11 @@ export const CreateBranchModal = () => {
useCheckGithubBranchValidity({
onError: () => {},
})
const { data: cloneBackups, error: cloneBackupsError } = useCloneBackupsQuery(
const {
data: cloneBackups,
error: cloneBackupsError,
isLoading: isLoadingCloneBackups,
} = useCloneBackupsQuery(
{ projectRef },
{
// [Joshen] Only trigger this request when the modal is opened
Expand Down Expand Up @@ -359,26 +360,29 @@ export const CreateBranchModal = () => {
name="withData"
render={({ field }) => (
<FormItemLayout
label="Include data"
label={
<>
<Label className="mr-2">Include data</Label>
{!disableBackupsCheck && (isLoadingCloneBackups || noPhysicalBackups) && (
<Badge variant="warning" size="small">
Requires PITR
</Badge>
)}
</>
}
layout="flex-row-reverse"
className="[&>div>label]:mb-1"
description="Clone production data into this branch"
>
<Tooltip>
<TooltipTrigger>
<FormControl_Shadcn_>
<Switch
disabled={!disableBackupsCheck && noPhysicalBackups}
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl_Shadcn_>
</TooltipTrigger>
{!disableBackupsCheck && noPhysicalBackups && (
<TooltipContent side="bottom">
PITR is required for the project to clone data into the branch
</TooltipContent>
)}
</Tooltip>
<FormControl_Shadcn_>
<Switch
disabled={
!disableBackupsCheck && (isLoadingCloneBackups || noPhysicalBackups)
}
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl_Shadcn_>
</FormItemLayout>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { PricingMetric } from 'data/analytics/org-daily-stats-query'
import type { OrgSubscription } from 'data/subscriptions/types'
import type { OrgUsageResponse } from 'data/usage/org-usage-query'
import { formatCurrency } from 'lib/helpers'
import { ChevronRight } from 'lucide-react'
import { useMemo } from 'react'
import { Button, HoverCard, HoverCardContent, HoverCardTrigger } from 'ui'
import { Button, cn, HoverCard, HoverCardContent, HoverCardTrigger } from 'ui'
import { billingMetricUnit, formatUsage } from '../helpers'
import { Metric, USAGE_APPROACHING_THRESHOLD } from './BillingBreakdown.constants'
import { ChevronRight } from 'lucide-react'

export interface BillingMetricProps {
idx: number
Expand All @@ -17,14 +17,16 @@ export interface BillingMetricProps {
usage: OrgUsageResponse
subscription: OrgSubscription
relativeToSubscription: boolean
className?: string
}

const BillingMetric = ({
export const BillingMetric = ({
slug,
metric,
usage,
subscription,
relativeToSubscription,
className,
}: BillingMetricProps) => {
const usageMeta = usage.usages.find((x) => x.metric === metric.key)

Expand Down Expand Up @@ -79,31 +81,41 @@ const BillingMetric = ({
return (
<HoverCard openDelay={50} closeDelay={200}>
<HoverCardTrigger asChild>
<div className="flex items-center justify-between">
<Link href={`/org/${slug}/usage#${metric.anchor}`} className="block w-full group">
{metric.anchor ? (
<div className={cn('flex items-center justify-between', className)}>
{metric.anchor ? (
<Link href={`/org/${slug}/usage#${metric.anchor}`} className="block w-full group">
<div className="group flex items-center gap-1">
<p className="text-sm text-foreground-light group-hover:text-foreground transition cursor-pointer">
{metric.name}
</p>
{usageMeta.available_in_plan && (
<span className="transition inline-block group-hover:transform group-hover:translate-x-0.5">
<span className="text-foreground-muted transition inline-block group-hover:transform group-hover:translate-x-0.5">
<ChevronRight strokeWidth={1.5} size={16} className="transition" />
</span>
)}
</div>
) : (
<p className="text-xs text-foreground-light flex space-x-1">{metric.name}</p>
)}
<span className="text-sm">{usageLabel}</span>&nbsp;
{relativeToSubscription && usageMeta.cost && usageMeta.cost > 0 ? (
<span className="text-sm" translate="no">
({formatCurrency(usageMeta.cost)})
</span>
) : usageMeta.available_in_plan && !usageMeta.unlimited && relativeToSubscription ? (
<span className="text-sm">{percentageLabel}</span>
) : null}
</Link>
<span className="text-sm">{usageLabel}</span>&nbsp;
{relativeToSubscription && usageMeta.cost && usageMeta.cost > 0 ? (
<span className="text-sm" translate="no">
({formatCurrency(usageMeta.cost)})
</span>
) : usageMeta.available_in_plan && !usageMeta.unlimited && relativeToSubscription ? (
<span className="text-sm">{percentageLabel}</span>
) : null}
</Link>
) : (
<div className="block w-full">
<p className="text-sm text-foreground-light flex space-x-1">{metric.name}</p>
<span className="text-sm">{usageLabel}</span>&nbsp;
{relativeToSubscription && usageMeta.cost && usageMeta.cost > 0 ? (
<span className="text-sm" translate="no">
({formatCurrency(usageMeta.cost)})
</span>
) : usageMeta.available_in_plan && !usageMeta.unlimited && relativeToSubscription ? (
<span className="text-sm">{percentageLabel}</span>
) : null}
</div>
)}

{usageMeta.available_in_plan ? (
<div>
Expand Down Expand Up @@ -156,7 +168,7 @@ const BillingMetric = ({
</div>
</HoverCardTrigger>
{usageMeta.available_in_plan && (
<HoverCardContent side="bottom" align="center" className="w-[500px]" animate="slide-in">
<HoverCardContent side="bottom" align="end" className="w-[500px]" animate="slide-in">
<div className="text-sm">
<p className="font-medium" translate="no">
{usageMeta.unit_price_desc}
Expand Down Expand Up @@ -229,5 +241,3 @@ const BillingMetric = ({
</HoverCard>
)
}

export default BillingMetric
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,27 @@ import Link from 'next/link'
import { ComputeUsageMetric, PricingMetric } from 'data/analytics/org-daily-stats-query'
import type { OrgUsageResponse } from 'data/usage/org-usage-query'
import { formatCurrency } from 'lib/helpers'
import { ChevronRight } from 'lucide-react'
import { useMemo } from 'react'
import { HoverCard, HoverCardContent, HoverCardTrigger } from 'ui'
import { formatUsage } from '../helpers'
import { Metric } from './BillingBreakdown.constants'
import { ChevronRight } from 'lucide-react'
import { HoverCard, HoverCardContent, HoverCardTrigger } from 'ui'

export interface ComputeMetricProps {
slug?: string
metric: Metric
usage: OrgUsageResponse
relativeToSubscription: boolean
className?: string
}

const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeMetricProps) => {
export const ComputeMetric = ({
slug,
metric,
usage,
relativeToSubscription,
className,
}: ComputeMetricProps) => {
const usageMeta = usage.usages.find((x) => x.metric === metric.key)

const usageLabel = useMemo(() => {
Expand All @@ -37,8 +44,8 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM

return (
<HoverCard openDelay={50} closeDelay={200}>
<HoverCardTrigger>
<div>
<HoverCardTrigger asChild>
<div className={className}>
<Link href={`/org/${slug}/usage#${metric.anchor}`}>
<div className="group flex items-center space-x-2">
<p className="text-sm text-foreground-light group-hover:text-foreground transition cursor-pointer">
Expand All @@ -55,7 +62,7 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM
) : null}
</div>
</HoverCardTrigger>
<HoverCardContent side="bottom" align="center" className="w-[500px]" animate="slide-in">
<HoverCardContent side="bottom" align="end" className="w-[500px]" animate="slide-in">
<div className="text-sm text-foreground space-y-2">
<p className="font-medium" translate="no">
{usageMeta?.unit_price_desc}
Expand Down Expand Up @@ -126,5 +133,3 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM
</HoverCard>
)
}

export default ComputeMetric
Loading
Loading