mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 21:59:16 +03:00
fix datepicker helper state
This commit is contained in:
1 parent
f6d8f52e96
commit
4150bbe2cc
7 files changed
+81
-79
No files matched your search
@@ -12,18 +12,21 @@ interface ReportsDatetimeHelper extends DatetimeHelper {
|
||||
|
||||
export const REPORTS_DATEPICKER_HELPERS: ReportsDatetimeHelper[] = [
|
||||
{
|
||||
id: 'last_10_minutes',
|
||||
text: 'Last 10 minutes',
|
||||
calcFrom: () => dayjs().subtract(10, 'minute').toISOString(),
|
||||
calcTo: () => dayjs().toISOString(),
|
||||
availableIn: ['team', 'enterprise'],
|
||||
},
|
||||
{
|
||||
id: 'last_30_minutes',
|
||||
text: 'Last 30 minutes',
|
||||
calcFrom: () => dayjs().subtract(30, 'minute').toISOString(),
|
||||
calcTo: () => dayjs().toISOString(),
|
||||
availableIn: ['team', 'enterprise'],
|
||||
},
|
||||
{
|
||||
id: 'last_60_minutes',
|
||||
text: 'Last 60 minutes',
|
||||
calcFrom: () => dayjs().subtract(1, 'hour').startOf('day').toISOString(),
|
||||
calcTo: () => dayjs().toISOString(),
|
||||
@@ -31,30 +34,35 @@ export const REPORTS_DATEPICKER_HELPERS: ReportsDatetimeHelper[] = [
|
||||
availableIn: ['free', 'pro', 'team', 'enterprise'],
|
||||
},
|
||||
{
|
||||
id: 'last_3_hours',
|
||||
text: 'Last 3 hours',
|
||||
calcFrom: () => dayjs().subtract(3, 'hour').startOf('day').toISOString(),
|
||||
calcTo: () => dayjs().toISOString(),
|
||||
availableIn: ['free', 'pro', 'team', 'enterprise'],
|
||||
},
|
||||
{
|
||||
id: 'last_24_hours',
|
||||
text: 'Last 24 hours',
|
||||
calcFrom: () => dayjs().subtract(1, 'day').startOf('day').toISOString(),
|
||||
calcTo: () => dayjs().toISOString(),
|
||||
availableIn: ['free', 'pro', 'team', 'enterprise'],
|
||||
},
|
||||
{
|
||||
id: 'last_7_days',
|
||||
text: 'Last 7 days',
|
||||
calcFrom: () => dayjs().subtract(7, 'day').startOf('day').toISOString(),
|
||||
calcTo: () => '',
|
||||
availableIn: ['pro', 'team', 'enterprise'],
|
||||
},
|
||||
{
|
||||
id: 'last_14_days',
|
||||
text: 'Last 14 days',
|
||||
calcFrom: () => dayjs().subtract(14, 'day').startOf('day').toISOString(),
|
||||
calcTo: () => '',
|
||||
availableIn: ['team', 'enterprise'],
|
||||
},
|
||||
{
|
||||
id: 'last_28_days',
|
||||
text: 'Last 28 days',
|
||||
calcFrom: () => dayjs().subtract(28, 'day').startOf('day').toISOString(),
|
||||
calcTo: () => '',
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Button, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_
|
||||
import { LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD } from './Logs.constants'
|
||||
import type { DatetimeHelper } from './Logs.types'
|
||||
import { copyToClipboard } from 'lib/helpers'
|
||||
import { useLogsUrlState } from 'hooks/analytics/useLogsUrlState'
|
||||
|
||||
export type DatePickerValue = {
|
||||
to: string
|
||||
@@ -20,22 +21,32 @@ export type DatePickerValue = {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
value: DatePickerValue
|
||||
onSubmit: (args: DatePickerValue) => void
|
||||
helpers: DatetimeHelper[]
|
||||
}
|
||||
|
||||
export const LogsDatePicker = ({ onSubmit, helpers, value }: PropsWithChildren<Props>) => {
|
||||
export const LogsDatePicker = ({ helpers }: PropsWithChildren<Props>) => {
|
||||
const { timestampStart, timestampEnd, setTimeRange, selectedHelperId, setSelectedHelperId } =
|
||||
useLogsUrlState()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedHelperId) {
|
||||
const helper = helpers.find((h) => h.id === selectedHelperId)
|
||||
if (helper) {
|
||||
setTimeRange(helper.calcFrom(), helper.calcTo())
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedHelperId])
|
||||
|
||||
// Reset the state when the popover closes
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setStartDate(value.from ? new Date(value.from) : null)
|
||||
setEndDate(value.to ? new Date(value.to) : new Date())
|
||||
setStartDate(timestampStart ? new Date(timestampStart) : null)
|
||||
setEndDate(timestampEnd ? new Date(timestampEnd) : new Date())
|
||||
|
||||
const fromDate = value.from ? new Date(value.from) : null
|
||||
const toDate = value.to ? new Date(value.to) : null
|
||||
const fromDate = timestampStart ? new Date(timestampStart) : null
|
||||
const toDate = timestampEnd ? new Date(timestampEnd) : null
|
||||
|
||||
setStartTime({
|
||||
HH: fromDate?.getHours().toString().padStart(2, '0') || '00',
|
||||
@@ -54,24 +65,23 @@ export const LogsDatePicker = ({ onSubmit, helpers, value }: PropsWithChildren<P
|
||||
ss: toDate?.getSeconds().toString().padStart(2, '0') || nowSS,
|
||||
})
|
||||
}
|
||||
}, [open, value])
|
||||
}, [open, timestampStart, timestampEnd])
|
||||
|
||||
const handleHelperChange = (newValue: string) => {
|
||||
const selectedHelper = helpers.find((h) => h.text === newValue)
|
||||
if (onSubmit && selectedHelper) {
|
||||
onSubmit({
|
||||
to: selectedHelper.calcTo(),
|
||||
from: selectedHelper.calcFrom(),
|
||||
isHelper: true,
|
||||
text: selectedHelper.text,
|
||||
})
|
||||
if (selectedHelper) {
|
||||
setTimeRange(selectedHelper.calcFrom(), selectedHelper.calcTo())
|
||||
setSelectedHelperId(selectedHelper.id)
|
||||
}
|
||||
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const [startDate, setStartDate] = useState<Date | null>(value.from ? new Date(value.from) : null)
|
||||
const [endDate, setEndDate] = useState<Date | null>(value.to ? new Date(value.to) : new Date())
|
||||
const [startDate, setStartDate] = useState<Date | null>(
|
||||
timestampStart ? new Date(timestampStart) : null
|
||||
)
|
||||
const [endDate, setEndDate] = useState<Date | null>(
|
||||
timestampEnd ? new Date(timestampEnd) : new Date()
|
||||
)
|
||||
|
||||
const [startTime, setStartTime] = useState({
|
||||
HH: startDate?.getHours().toString() || '00',
|
||||
@@ -99,12 +109,8 @@ export const LogsDatePicker = ({ onSubmit, helpers, value }: PropsWithChildren<P
|
||||
const finalFrom = new Date(from.setHours(+startTime.HH, +startTime.mm, +startTime.ss))
|
||||
const finalTo = new Date(to.setHours(+endTime.HH, +endTime.mm, +endTime.ss))
|
||||
|
||||
onSubmit({
|
||||
from: finalFrom.toISOString(),
|
||||
to: finalTo.toISOString(),
|
||||
isHelper: false,
|
||||
})
|
||||
|
||||
setTimeRange(finalFrom.toISOString(), finalTo.toISOString())
|
||||
setSelectedHelperId('')
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
@@ -209,13 +215,17 @@ export const LogsDatePicker = ({ onSubmit, helpers, value }: PropsWithChildren<P
|
||||
Math.abs(dayjs(startDate).diff(dayjs(endDate), 'days')) >
|
||||
LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD - 1
|
||||
|
||||
const selectedHelper = helpers.find((h) => h.id === selectedHelperId)
|
||||
|
||||
return (
|
||||
<Popover_Shadcn_ open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger_Shadcn_ asChild>
|
||||
<Button type="default" icon={<Clock size={12} />}>
|
||||
{value.isHelper
|
||||
? value.text
|
||||
: `${dayjs(value.from).format('DD MMM, HH:mm')} - ${dayjs(value.to || new Date()).format('DD MMM, HH:mm')}`}
|
||||
{selectedHelper
|
||||
? selectedHelper.text
|
||||
: timestampStart && timestampEnd
|
||||
? `${dayjs(timestampStart).format('DD MMM, HH:mm')} - ${dayjs(timestampEnd).format('DD MMM, HH:mm')}`
|
||||
: helpers[0]?.text}
|
||||
</Button>
|
||||
</PopoverTrigger_Shadcn_>
|
||||
<PopoverContent_Shadcn_
|
||||
@@ -226,7 +236,10 @@ export const LogsDatePicker = ({ onSubmit, helpers, value }: PropsWithChildren<P
|
||||
>
|
||||
<RadioGroup
|
||||
onValueChange={handleHelperChange}
|
||||
value={value.isHelper ? value.text : ''}
|
||||
value={(() => {
|
||||
const selected = helpers.find((h) => h.id === selectedHelperId)
|
||||
return selected ? selected.text : ''
|
||||
})()}
|
||||
className="border-r p-2 flex flex-col gap-px"
|
||||
>
|
||||
{helpers.map((helper) => (
|
||||
|
||||
@@ -672,42 +672,50 @@ export const LOGS_TAILWIND_CLASSES = {
|
||||
|
||||
export const PREVIEWER_DATEPICKER_HELPERS: DatetimeHelper[] = [
|
||||
{
|
||||
id: 'last_15_minutes',
|
||||
text: 'Last 15 minutes',
|
||||
calcFrom: () => dayjs().subtract(15, 'minute').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_30_minutes',
|
||||
text: 'Last 30 minutes',
|
||||
calcFrom: () => dayjs().subtract(30, 'minute').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_hour',
|
||||
text: 'Last hour',
|
||||
calcFrom: () => dayjs().subtract(1, 'hour').toISOString(),
|
||||
calcTo: () => '',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: 'last_3_hours',
|
||||
text: 'Last 3 hours',
|
||||
calcFrom: () => dayjs().subtract(3, 'hour').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_24_hours',
|
||||
text: 'Last 24 hours',
|
||||
calcFrom: () => dayjs().subtract(1, 'day').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_2_days',
|
||||
text: 'Last 2 days',
|
||||
calcFrom: () => dayjs().subtract(2, 'day').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_3_days',
|
||||
text: 'Last 3 days',
|
||||
calcFrom: () => dayjs().subtract(3, 'day').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_5_days',
|
||||
text: 'Last 5 days',
|
||||
calcFrom: () => dayjs().subtract(5, 'day').toISOString(),
|
||||
calcTo: () => '',
|
||||
@@ -715,27 +723,32 @@ export const PREVIEWER_DATEPICKER_HELPERS: DatetimeHelper[] = [
|
||||
]
|
||||
export const EXPLORER_DATEPICKER_HELPERS: DatetimeHelper[] = [
|
||||
{
|
||||
id: 'last_hour',
|
||||
text: 'Last hour',
|
||||
calcFrom: () => dayjs().subtract(1, 'hour').toISOString(),
|
||||
calcTo: () => '',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: 'last_3_hours',
|
||||
text: 'Last 3 hours',
|
||||
calcFrom: () => dayjs().subtract(3, 'hour').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_24_hours',
|
||||
text: 'Last 24 hours',
|
||||
calcFrom: () => dayjs().subtract(1, 'day').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_3_days',
|
||||
text: 'Last 3 days',
|
||||
calcFrom: () => dayjs().subtract(3, 'day').toISOString(),
|
||||
calcTo: () => '',
|
||||
},
|
||||
{
|
||||
id: 'last_7_days',
|
||||
text: 'Last 7 days',
|
||||
calcFrom: () => dayjs().subtract(7, 'day').toISOString(),
|
||||
calcTo: () => '',
|
||||
|
||||
@@ -138,6 +138,7 @@ export type Override = {
|
||||
}
|
||||
|
||||
export interface DatetimeHelper {
|
||||
id: string
|
||||
text: string
|
||||
calcTo: () => string
|
||||
calcFrom: () => string
|
||||
|
||||
@@ -27,23 +27,21 @@ import {
|
||||
LOGS_SOURCE_DESCRIPTION,
|
||||
LogsTableName,
|
||||
} from './Logs.constants'
|
||||
import { DatePickerValue, LogsDatePicker } from './Logs.DatePickers'
|
||||
import { LogsDatePicker } from './Logs.DatePickers'
|
||||
import { LogsWarning, LogTemplate, WarehouseCollection } from './Logs.types'
|
||||
import { WarehouseQueryTemplate } from './Warehouse.utils'
|
||||
import { useLogsUrlState } from 'hooks/analytics/useLogsUrlState'
|
||||
|
||||
export type SourceType = 'logs' | 'warehouse'
|
||||
export interface LogsQueryPanelProps {
|
||||
templates?: LogTemplate[]
|
||||
warehouseTemplates?: WarehouseQueryTemplate[]
|
||||
defaultFrom: string
|
||||
defaultTo: string
|
||||
warnings: LogsWarning[]
|
||||
warehouseCollections: WarehouseCollection[]
|
||||
dataSource: SourceType
|
||||
onSelectTemplate: (template: LogTemplate) => void
|
||||
onSelectWarehouseTemplate: (template: WarehouseQueryTemplate) => void
|
||||
onSelectSource: (source: string) => void
|
||||
onDateChange: (value: DatePickerValue) => void
|
||||
onDataSourceChange: (sourceType: SourceType) => void
|
||||
}
|
||||
|
||||
@@ -59,18 +57,16 @@ function DropdownMenuItemContent({ name, desc }: { name: ReactNode; desc?: strin
|
||||
const LogsQueryPanel = ({
|
||||
templates = [],
|
||||
warehouseTemplates = [],
|
||||
defaultFrom,
|
||||
defaultTo,
|
||||
warnings,
|
||||
warehouseCollections,
|
||||
dataSource,
|
||||
onSelectTemplate,
|
||||
onSelectWarehouseTemplate,
|
||||
onSelectSource,
|
||||
onDateChange,
|
||||
onDataSourceChange,
|
||||
}: LogsQueryPanelProps) => {
|
||||
const [showReference, setShowReference] = useState(false)
|
||||
const { timestampStart, timestampEnd, setTimeRange } = useLogsUrlState()
|
||||
|
||||
const {
|
||||
projectAuthAll: authEnabled,
|
||||
@@ -91,27 +87,6 @@ const LogsQueryPanel = ({
|
||||
})
|
||||
.map(([, value]) => value)
|
||||
|
||||
function getDefaultDatePickerValue() {
|
||||
if (defaultFrom && defaultTo) {
|
||||
return {
|
||||
to: defaultTo,
|
||||
from: defaultFrom,
|
||||
text: `${dayjs(defaultFrom).format('DD MMM, HH:mm')} - ${dayjs(defaultTo).format('DD MMM, HH:mm')}`,
|
||||
isHelper: false,
|
||||
}
|
||||
}
|
||||
return {
|
||||
to: EXPLORER_DATEPICKER_HELPERS[0].calcTo(),
|
||||
from: EXPLORER_DATEPICKER_HELPERS[0].calcFrom(),
|
||||
text: EXPLORER_DATEPICKER_HELPERS[0].text,
|
||||
isHelper: true,
|
||||
}
|
||||
}
|
||||
|
||||
const [selectedDatePickerValue, setSelectedDatePickerValue] = useState<DatePickerValue>(
|
||||
getDefaultDatePickerValue()
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="border-b bg-surface-100">
|
||||
<div className="flex w-full items-center justify-between px-4 md:px-5 py-2 overflow-x-scroll no-scrollbar">
|
||||
@@ -222,16 +197,7 @@ const LogsQueryPanel = ({
|
||||
</DropdownMenu>
|
||||
)}
|
||||
|
||||
{dataSource === 'logs' && (
|
||||
<LogsDatePicker
|
||||
value={selectedDatePickerValue}
|
||||
onSubmit={(value) => {
|
||||
setSelectedDatePickerValue(value)
|
||||
onDateChange(value)
|
||||
}}
|
||||
helpers={EXPLORER_DATEPICKER_HELPERS}
|
||||
/>
|
||||
)}
|
||||
{dataSource === 'logs' && <LogsDatePicker helpers={EXPLORER_DATEPICKER_HELPERS} />}
|
||||
|
||||
<div
|
||||
data-testid="log-explorer-warnings"
|
||||
|
||||
@@ -8,6 +8,7 @@ interface LogsUrlState {
|
||||
timestampEnd: string
|
||||
selectedLogId: string | null
|
||||
filters: Filters
|
||||
selectedHelperId: string
|
||||
}
|
||||
|
||||
const defaultState: LogsUrlState = {
|
||||
@@ -16,6 +17,7 @@ const defaultState: LogsUrlState = {
|
||||
timestampEnd: '',
|
||||
selectedLogId: null,
|
||||
filters: {},
|
||||
selectedHelperId: '',
|
||||
}
|
||||
|
||||
const stringWithDefault = (defaultValue: string) =>
|
||||
@@ -59,6 +61,10 @@ export function useLogsUrlState() {
|
||||
'f',
|
||||
jsonWithDefault<Filters>(defaultState.filters)
|
||||
)
|
||||
const [selectedHelperIdValue, setSelectedHelperIdValue] = useQueryState(
|
||||
'helper',
|
||||
stringWithDefault(defaultState.selectedHelperId)
|
||||
)
|
||||
|
||||
// Ensure we never return null values
|
||||
const search = searchValue || defaultState.search
|
||||
@@ -66,6 +72,7 @@ export function useLogsUrlState() {
|
||||
const timestampEnd = timestampEndValue || defaultState.timestampEnd
|
||||
const selectedLogId = selectedLogIdValue || null
|
||||
const filters = filtersValue || defaultState.filters
|
||||
const selectedHelperId = selectedHelperIdValue || defaultState.selectedHelperId
|
||||
|
||||
const setSearch = (value: string) => {
|
||||
setSearchValue(value || defaultState.search)
|
||||
@@ -88,12 +95,17 @@ export function useLogsUrlState() {
|
||||
setFilters({ ...filters, [key]: value })
|
||||
}
|
||||
|
||||
const setSelectedHelperId = (value: string) => {
|
||||
setSelectedHelperIdValue(value || defaultState.selectedHelperId)
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
setSearch(defaultState.search)
|
||||
setTimestampStartValue(defaultState.timestampStart)
|
||||
setTimestampEndValue(defaultState.timestampEnd)
|
||||
setSelectedLogIdValue('')
|
||||
setFiltersValue(defaultState.filters)
|
||||
setSelectedHelperIdValue(defaultState.selectedHelperId)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -103,6 +115,7 @@ export function useLogsUrlState() {
|
||||
timestampEnd,
|
||||
selectedLogId,
|
||||
filters,
|
||||
selectedHelperId,
|
||||
|
||||
// Setters
|
||||
setSearch,
|
||||
@@ -110,6 +123,7 @@ export function useLogsUrlState() {
|
||||
setSelectedLogId,
|
||||
setFilters,
|
||||
updateFilter,
|
||||
setSelectedHelperId,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
@@ -313,16 +313,6 @@ export const LogsExplorerPage: NextPageWithLayout = () => {
|
||||
setSaveModalOpen(!saveModalOpen)
|
||||
}
|
||||
|
||||
const handleDateChange = ({ to, from }: DatePickerToFrom) => {
|
||||
const shouldShowUpgradePrompt = maybeShowUpgradePrompt(from, organization?.plan?.id)
|
||||
|
||||
if (shouldShowUpgradePrompt) {
|
||||
setShowUpgradePrompt(!showUpgradePrompt)
|
||||
} else {
|
||||
setTimeRange(from || '', to || '')
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (warehouseError) toast.error(warehouseError.message)
|
||||
}, [warehouseError])
|
||||
@@ -379,9 +369,6 @@ export const LogsExplorerPage: NextPageWithLayout = () => {
|
||||
>
|
||||
<ResizablePanel collapsible minSize={5}>
|
||||
<LogsQueryPanel
|
||||
defaultFrom={timestampStart || ''}
|
||||
defaultTo={timestampEnd || ''}
|
||||
onDateChange={handleDateChange}
|
||||
onSelectSource={handleInsertSource}
|
||||
templates={TEMPLATES.filter((template) => template.mode === 'custom')}
|
||||
warehouseCollections={warehouseCollections || []}
|
||||
|
||||
Reference in new issue
Block a user