Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion apps/studio/.env
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ NEXT_PUBLIC_GOTRUE_URL=$SUPABASE_PUBLIC_URL/auth/v1
NEXT_PUBLIC_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001

# CmdK / AI
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhndWloeHV6cWlid3hqbmlteGV2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzUwOTQ4MzUsImV4cCI6MTk5MDY3MDgzNX0.0PMlOxtKL4O9GGZuAP_Xl4f-Tut1qOnW4bNEmAtoB8w
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhndWloeHV6cWlid3hqbmlteGV2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTgzNzc1MTgsImV4cCI6MjAzMzk1MzUxOH0.aIqjQ9V7djMxYit-DT1fYNV_VWMHSqldh_18XfX2_BE
NEXT_PUBLIC_SUPABASE_URL=https://xguihxuzqibwxjnimxev.supabase.co

DOCKER_SOCKET_LOCATION=/var/run/docker.sock
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export const CreateBranchModal = () => {

const gitlessBranching = useIsBranching2Enabled()
const allowDataBranching = useFlag('allowDataBranching')
// [Joshen] This is meant to be short lived while we're figuring out how to control
// requests to this endpoint. Kill switch in case we need to stop the requests
const disableBackupsCheck = useFlag('disableBackupsCheckInCreatebranchmodal')

const isProPlanAndUp = selectedOrg?.plan?.id !== 'free'
const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
Expand All @@ -89,7 +92,13 @@ export const CreateBranchModal = () => {
useCheckGithubBranchValidity({
onError: () => {},
})
const { data: cloneBackups, error: cloneBackupsError } = useCloneBackupsQuery({ projectRef })
const { data: cloneBackups, error: cloneBackupsError } = useCloneBackupsQuery(
{ projectRef },
{
// [Joshen] Only trigger this request when the modal is opened
enabled: showCreateBranchModal && !disableBackupsCheck,
}
)
const targetVolumeSizeGb = cloneBackups?.target_volume_size_gb ?? 0
const noPhysicalBackups = cloneBackupsError?.message.startsWith(
'Physical backups need to be enabled'
Expand Down Expand Up @@ -358,13 +367,13 @@ export const CreateBranchModal = () => {
<TooltipTrigger>
<FormControl_Shadcn_>
<Switch
disabled={noPhysicalBackups}
disabled={!disableBackupsCheck && noPhysicalBackups}
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl_Shadcn_>
</TooltipTrigger>
{noPhysicalBackups && (
{!disableBackupsCheck && noPhysicalBackups && (
<TooltipContent side="bottom">
PITR is required for the project to clone data into the branch
</TooltipContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,10 +566,6 @@ export const EDGE_FUNCTION_REGIONS = [
key: 'us-west-1',
label: 'N. California',
},
{
key: 'ap-northeast-2',
label: 'Seoul',
},
{
key: 'us-west-2',
label: 'Oregon',
Expand All @@ -594,4 +590,4 @@ export const EDGE_FUNCTION_REGIONS = [
key: 'sa-east-1',
label: 'São Paulo',
},
]
] as const
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const ReportChartV2 = ({
const isAvailable =
report.availableIn === undefined || (orgPlanId && report.availableIn.includes(orgPlanId))

const canFetch = orgPlanId !== undefined
const canFetch = orgPlanId !== undefined && isAvailable

const {
data: queryResult,
Expand Down Expand Up @@ -83,7 +83,7 @@ export const ReportChartV2 = ({

const [chartStyle, setChartStyle] = useState<string>(report.defaultChartStyle)

if (!isAvailable && !isLoadingChart) {
if (!isAvailable) {
return <ReportChartUpsell report={report} orgSlug={org?.slug ?? ''} />
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import {
} from '@ui/components/shadcn/ui/select'
import { Button, cn } from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'

export type ComparisonOperator = '=' | '!=' | '>' | '>=' | '<' | '<='
import { z } from 'zod'

const OPERATOR_LABELS = {
'=': 'Equals',
Expand All @@ -24,15 +23,19 @@ const OPERATOR_LABELS = {
'!=': 'Not equal to',
} satisfies Record<ComparisonOperator, string>

export interface NumericFilter {
operator: ComparisonOperator
value: number
}
const comparisonOperatorSchema = z.enum(['=', '>=', '<=', '>', '<', '!='])
export type ComparisonOperator = z.infer<typeof comparisonOperatorSchema>

export const numericFilterSchema = z.object({
operator: comparisonOperatorSchema,
value: z.number(),
})
export type NumericFilter = z.infer<typeof numericFilterSchema>

interface ReportsNumericFilterProps {
label: string
value?: NumericFilter
onChange: (value: NumericFilter | undefined) => void
value: NumericFilter | null
onChange: (value: NumericFilter | null) => void
operators?: ComparisonOperator[]
defaultOperator?: ComparisonOperator
placeholder?: string
Expand All @@ -57,21 +60,16 @@ export const ReportsNumericFilter = ({
className,
}: ReportsNumericFilterProps) => {
const [open, setOpen] = useState(false)
const [tempValue, setTempValue] = useState<NumericFilter | undefined>(value)
const [tempValue, setTempValue] = useState<NumericFilter | null>(value)

const isActive = value !== undefined
const isActive = value !== null

useEffect(() => {
if (!open) {
setTempValue(value)
}
}, [open, value])

const handleClear = (e: React.MouseEvent) => {
e.stopPropagation()
onChange(undefined)
}

const handleApply = () => {
onChange(tempValue)
setOpen(false)
Expand All @@ -87,27 +85,26 @@ export const ReportsNumericFilter = ({
const handleOperatorChange = (operator: ComparisonOperator) => {
setTempValue({
operator,
value: tempValue?.value || 0,
value: tempValue?.value ?? 0,
})
}

const handleValueChange = (inputValue: string) => {
const numericValue = parseFloat(inputValue) || 0
setTempValue({
operator: tempValue?.operator || defaultOperator,
value: numericValue,
})
}

const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault()
handleApply()
if (inputValue === '') {
setTempValue(null)
} else {
const numericValue = parseFloat(inputValue)
if (!isNaN(numericValue)) {
setTempValue({
operator: tempValue?.operator ?? defaultOperator,
value: numericValue,
})
}
}
}

const handleClearAll = () => {
setTempValue(undefined)
setTempValue(null)
}

return (
Expand All @@ -133,13 +130,6 @@ export const ReportsNumericFilter = ({
</Button>
</PopoverTrigger>
<PopoverContent align="start" className="p-0 w-72" portal={true}>
<div className="p-2 border-b border-default flex items-center justify-between">
<span className="text-xs text-foreground">{label}</span>
<Button size="tiny" type="outline" onClick={handleClearAll}>
Clear
</Button>
</div>

<form
onSubmit={(e) => {
e.preventDefault()
Expand Down Expand Up @@ -175,7 +165,6 @@ export const ReportsNumericFilter = ({
placeholder={placeholder}
value={tempValue?.value || ''}
onChange={(e) => handleValueChange(e.target.value)}
onKeyDown={handleKeyDown}
min={min}
max={max}
step={step}
Expand All @@ -193,8 +182,8 @@ export const ReportsNumericFilter = ({
</form>

<div className="flex items-center justify-end gap-2 border-t border-default p-2">
<Button size="tiny" type="default" onClick={() => setOpen(false)} htmlType="button">
Cancel
<Button size="tiny" type="outline" onClick={handleClearAll}>
Clear
</Button>
<Button
loading={isLoading}
Expand Down
113 changes: 57 additions & 56 deletions apps/studio/components/interfaces/Reports/v2/ReportsSelectFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@ import { useEffect, useState } from 'react'
import { Checkbox } from '@ui/components/shadcn/ui/checkbox'
import { Label } from '@ui/components/shadcn/ui/label'
import { Popover, PopoverContent, PopoverTrigger } from '@ui/components/shadcn/ui/popover'
import { Button, cn } from 'ui'
import {
Button,
cn,
Command_Shadcn_ as Command,
CommandInput_Shadcn_ as CommandInput,
CommandItem_Shadcn_,
} from 'ui'
import { CommandList_Shadcn_ as CommandList, CommandEmpty_Shadcn_ as CommandEmpty } from 'ui'
import { z } from 'zod'
import { CommandGroup } from '@ui/components/shadcn/ui/command'

export interface ReportSelectOption {
key: string
label: React.ReactNode
value: string
description?: string
}

export interface SelectFilters {
[key: string]: boolean
}
export const selectFilterSchema = z.array(z.string())
export type SelectFilters = z.infer<typeof selectFilterSchema>

interface ReportsSelectFilterProps {
label: string
Expand All @@ -23,6 +31,7 @@ interface ReportsSelectFilterProps {
onChange: (value: SelectFilters) => void
isLoading?: boolean
className?: string
showSearch?: boolean
}

export const ReportsSelectFilter = ({
Expand All @@ -32,12 +41,12 @@ export const ReportsSelectFilter = ({
onChange,
isLoading = false,
className,
showSearch = false,
}: ReportsSelectFilterProps) => {
const [open, setOpen] = useState(false)
const [tempValue, setTempValue] = useState<SelectFilters>(value)

const selectedCount = Object.values(value).filter(Boolean).length
const isActive = selectedCount > 0
const isActive = tempValue.length > 0

useEffect(() => {
if (!open) {
Expand All @@ -51,7 +60,7 @@ export const ReportsSelectFilter = ({
}

const handleClearAll = () => {
setTempValue({})
setTempValue([])
}

const handleKeyDown = (e: React.KeyboardEvent) => {
Expand All @@ -75,60 +84,52 @@ export const ReportsSelectFilter = ({
>
<span>
{label}
{selectedCount > 0 && <span className="ml-1 text-xs">({selectedCount})</span>}
{tempValue.length > 0 && <span className="ml-1 text-xs">({tempValue.length})</span>}
</span>
</Button>
</PopoverTrigger>
<PopoverContent align="start" className="p-0 w-72" portal={true}>
<div className="p-2 border-b border-default">
<div className="flex items-center justify-between gap-2">
<span className="text-xs text-foreground">{label}</span>
<Button size="tiny" type="outline" onClick={handleClearAll} disabled={isLoading}>
Clear
</Button>
</div>
</div>

<div className="max-h-60 overflow-y-auto p-1" onKeyDown={handleKeyDown}>
{options.length === 0 ? (
<div className="p-4 text-center text-sm text-foreground-light">
{isLoading ? 'Loading options...' : 'No options available'}
</div>
) : (
options.map((option) => (
<Label
key={option.key}
htmlFor={`${label}-${option.key}`}
className={cn(
'flex items-center hover:bg-overlay-hover overflow-hidden px-1.5 py-1.5 rounded-sm gap-x-3',
'transition-all duration-150 ease-in-out cursor-pointer'
)}
>
<Checkbox
id={`${label}-${option.key}`}
checked={Boolean(tempValue[option.key])}
onCheckedChange={(checked) => {
setTempValue({
...tempValue,
[option.key]: Boolean(checked),
})
}}
onKeyDown={handleKeyDown}
/>
<div className="flex flex-col text-xs">
{option.label}
{option.description && (
<span className="text-foreground-lighter">{option.description}</span>
)}
</div>
</Label>
))
)}
</div>
<Command>
{showSearch && <CommandInput placeholder="Search..." />}
<CommandList>
<CommandEmpty>No options found.</CommandEmpty>
<CommandGroup>
{options.map((option) => (
<CommandItem_Shadcn_ key={option.value}>
<Label
key={option.value}
className={
'flex items-center overflow-hidden p-1 rounded-sm gap-x-3 w-full h-full'
}
>
<Checkbox
id={`${label}-${option.value}`}
checked={tempValue.includes(option.value)}
onCheckedChange={(checked) => {
setTempValue(
checked
? [...tempValue, option.value]
: tempValue.filter((x) => x !== option.value)
)
}}
onKeyDown={handleKeyDown}
/>
<div className="flex flex-col text-xs">
{option.label}
{option.description && (
<span className="text-foreground-lighter">{option.description}</span>
)}
</div>
</Label>
</CommandItem_Shadcn_>
))}
</CommandGroup>
</CommandList>
</Command>

<div className="flex items-center justify-end gap-2 border-t border-default p-2">
<Button size="tiny" type="default" onClick={() => setOpen(false)} htmlType="button">
Cancel
<Button size="tiny" type="outline" onClick={handleClearAll} disabled={isLoading}>
Clear
</Button>
<Button
loading={isLoading}
Expand Down
Loading
Loading