fixed ux issue, switched to use timestamp_end, added utc timestamp display

This commit is contained in:
TzeYiing committed 2022-01-19 00:21:31 +08:00
1 parent bdd1601e9f
commit ce02f670c4
4 files changed
+58 -22

No files matched your search

@@ -13,6 +13,7 @@ import {
} from '@supabase/ui'
import { LogSearchCallback, LogTemplate } from '.'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
interface Props {
defaultSearchValue?: string
defaultFromValue?: string
@@ -26,6 +27,8 @@ interface Props {
onSelectTemplate: (template: LogTemplate) => void
}
dayjs.extend(utc)
/**
* Logs control panel header + wrapper
*/
@@ -43,7 +46,7 @@ const LogPanel: FC<Props> = ({
}) => {
const [search, setSearch] = useState('')
const [from, setFrom] = useState({ value: '', error: '' })
const [defaultTimestamp, setDefaultTimestamp] = useState(dayjs().toISOString())
const [defaultTimestamp, setDefaultTimestamp] = useState(dayjs().utc().toISOString())
// sync local state with provided default value
useEffect(() => {
if (search !== defaultSearchValue) {
@@ -52,7 +55,7 @@ const LogPanel: FC<Props> = ({
}, [defaultSearchValue])
useEffect(() => {
if (defaultFromValue && from.value !== defaultFromValue) {
if (from.value !== defaultFromValue) {
setFrom({ value: defaultFromValue, error: '' })
}
}, [defaultFromValue])
@@ -60,19 +63,21 @@ const LogPanel: FC<Props> = ({
const handleFromChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
if (value !== '' && isNaN(Date.parse(value))) {
setFrom({value, error: 'Invalid ISO 8601 timestamp' })
setFrom({ value, error: 'Invalid ISO 8601 timestamp' })
} else {
setFrom({ value, error: '' })
}
}
const handleFromReset = () => {
const value = dayjs().toISOString()
setFrom({ value, error: '' })
const handleFromReset = async () => {
setFrom({ value: '', error: '' })
const value = dayjs().utc().toISOString()
setDefaultTimestamp(value)
onSearch({ query: search, from: '' })
}
const handleSearch = () => onSearch({ query: search, from: from.value })
const showFromReset = from.value !== ''
return (
<div className="bg-panel-header-light dark:bg-panel-header-dark">
<div className="px-2 py-1 flex items-center justify-between w-full">
@@ -127,7 +132,7 @@ const LogPanel: FC<Props> = ({
<div className="flex items-center gap-x-4">
{!isCustomQuery && (
<>
<div className="flex flex-row gap-x-1">
<div className="flex flex-row">
<Dropdown
side="bottom"
align="end"
@@ -139,6 +144,7 @@ const LogPanel: FC<Props> = ({
value={from.value === '' ? defaultTimestamp : from.value}
onChange={handleFromChange}
error={from.error}
className="w-72"
actions={[
from.value && (
<IconX
@@ -163,11 +169,26 @@ const LogPanel: FC<Props> = ({
</Dropdown.Misc>
}
>
<Button as="span" type="outline" icon={<IconClock size="tiny" />}>
<Button
as="span"
size="tiny"
className={showFromReset ? '!rounded-r-none' : ''}
type={showFromReset ? 'outline' : 'text'}
icon={<IconClock size="tiny" />}
>
{from.value ? 'Custom' : 'Now'}
</Button>
</Dropdown>
<Button icon={<IconX />} title="Clear timestamp filter" />
{showFromReset && (
<Button
size="tiny"
className={showFromReset ? '!rounded-l-none' : ''}
icon={<IconX size="tiny" />}
type="outline"
title="Clear timestamp filter"
onClick={handleFromReset}
/>
)}
</div>
{/* wrap with form so that if user presses enter, the search value will submit automatically */}
<form
@@ -33,6 +33,7 @@ import useSWRInfinite from 'swr/infinite'
import { isUndefined } from 'lodash'
import Flag from 'components/ui/Flag/Flag'
import { useFlag } from 'hooks'
import dayjs from 'dayjs'
/**
* Acts as a container component for the entire log display
@@ -49,7 +50,7 @@ export const LogPage: NextPage = () => {
const logsQueryParamsSyncing = useFlag('logsQueryParamsSyncing')
const logsCustomSql = useFlag('logsCustomSql')
const router = useRouter()
const { ref, type, q, s, ts } = router.query
const { ref, type, q, s, te } = router.query
const [editorId, setEditorId] = useState<string>(uuidv4())
const [editorValue, setEditorValue] = useState('')
const [mode, setMode] = useState<'simple' | 'custom'>('simple')
@@ -82,9 +83,14 @@ export const LogPage: NextPage = () => {
mode: 'simple',
searchString: s as string,
})
} else {
setParams({ ...params, search_query: '', where: '', sql: '' })
}
if (ts) {
setParams({ ...params, timestamp_start: ts as string })
if (te) {
setParams({ ...params, timestamp_end: te as string })
} else {
setParams({ ...params, timestamp_end: '' })
}
}, [logsQueryParamsSyncing])
@@ -148,7 +154,14 @@ export const LogPage: NextPage = () => {
const handleRefresh = () => {
setLatestRefresh(new Date().toISOString())
setParams({ ...params, timestamp_start: '' })
setParams({ ...params, timestamp_end: '' })
router.push({
pathname: router.pathname,
query: {
...router.query,
te: undefined
},
})
setSize(1)
}
@@ -172,7 +185,7 @@ export const LogPage: NextPage = () => {
where: isSelectQuery ? '' : template.searchString,
sql: isSelectQuery ? template.searchString : '',
search_query: '',
timestamp_start: ''
timestamp_end: ''
}))
setEditorId(uuidv4())
}
@@ -191,15 +204,16 @@ export const LogPage: NextPage = () => {
...router.query,
q: editorValue,
s: undefined,
ts: undefined
te: undefined
},
})
}
const handleSearch: LogSearchCallback = ({ query, from }) => {
const unixMicro = dayjs(from).valueOf() * 1000
setParams((prev) => ({
...prev,
search_query: query || '',
timestamp_start: from,
timestamp_end: from ? String(unixMicro) : '' ,
where: '',
sql: '',
}))
@@ -210,7 +224,7 @@ export const LogPage: NextPage = () => {
...router.query,
q: undefined,
s: query || '',
ts: from
te: unixMicro
},
})
setEditorValue('')
@@ -227,7 +241,7 @@ export const LogPage: NextPage = () => {
onRefresh={handleRefresh}
onSearch={handleSearch}
defaultSearchValue={params.search_query}
defaultFromValue={params.timestamp_start}
defaultFromValue={params.timestamp_end ? dayjs(Number(params.timestamp_end)/1000).toISOString() : ''}
onCustomClick={handleModeToggle}
onSelectTemplate={onSelectTemplate}
/>
+1 -1
View File
@@ -101,7 +101,7 @@ test('timestamp from filter', async () => {
userEvent.click(set)
expect(mockFn).toBeCalled()
await screen.findByText('Custom')
await screen.findByTitle('Clear timestamp filter')
await screen.findByTitle(/Clear timestamp filter/)
})
test('custom query mode hides elements', async () => {
+4 -3
View File
@@ -274,21 +274,22 @@ test('q= query param will populate the query input', async () => {
expect(() => !screen.queryByDisplayValue(/someSearch/))
})
test('ts= query param will populate the timestamp from input', async () => {
test('te= query param will populate the timestamp from input', async () => {
// get time 20 mins before
const newDate = new Date()
newDate.setMinutes(new Date().getMinutes() - 20)
const isoString = newDate.toISOString()
const unixMicro = newDate.getTime() * 1000 //microseconds
useRouter.mockReturnValueOnce({
query: { ref: '123', type: 'api', ts: isoString },
query: { ref: '123', type: 'api', te: unixMicro },
push: jest.fn(),
})
render(<LogPage />)
await waitFor(() => {
expect(get).toHaveBeenCalledWith(
expect.stringContaining(`timestamp_start=${encodeURIComponent(isoString)}`)
expect.stringContaining(`timestamp_end=${encodeURIComponent(unixMicro)}`)
)
})
clickDropdown(await screen.findByText('Custom'))