From ce02f670c4d5bfdf8f74b159c8fd65d6ff2b3ff4 Mon Sep 17 00:00:00 2001 From: TzeYiing Date: Wed, 19 Jan 2022 00:21:31 +0800 Subject: [PATCH] fixed ux issue, switched to use timestamp_end, added utc timestamp display --- .../interfaces/Settings/Logs/LogPanel.tsx | 39 ++++++++++++++----- .../project/[ref]/settings/logs/[type].tsx | 32 ++++++++++----- studio/tests/pages/projects/LogPanel.test.js | 2 +- studio/tests/pages/projects/logs.test.js | 7 ++-- 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/studio/components/interfaces/Settings/Logs/LogPanel.tsx b/studio/components/interfaces/Settings/Logs/LogPanel.tsx index 9241e4d14f9..3ea5e087aac 100644 --- a/studio/components/interfaces/Settings/Logs/LogPanel.tsx +++ b/studio/components/interfaces/Settings/Logs/LogPanel.tsx @@ -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 = ({ }) => { 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 = ({ }, [defaultSearchValue]) useEffect(() => { - if (defaultFromValue && from.value !== defaultFromValue) { + if (from.value !== defaultFromValue) { setFrom({ value: defaultFromValue, error: '' }) } }, [defaultFromValue]) @@ -60,19 +63,21 @@ const LogPanel: FC = ({ const handleFromChange = (e: React.ChangeEvent) => { 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 (
@@ -127,7 +132,7 @@ const LogPanel: FC = ({
{!isCustomQuery && ( <> -
+
= ({ value={from.value === '' ? defaultTimestamp : from.value} onChange={handleFromChange} error={from.error} + className="w-72" actions={[ from.value && ( = ({ } > - -
{/* wrap with form so that if user presses enter, the search value will submit automatically */}
{ 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(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} /> diff --git a/studio/tests/pages/projects/LogPanel.test.js b/studio/tests/pages/projects/LogPanel.test.js index fde58671497..712aae79dbe 100644 --- a/studio/tests/pages/projects/LogPanel.test.js +++ b/studio/tests/pages/projects/LogPanel.test.js @@ -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 () => { diff --git a/studio/tests/pages/projects/logs.test.js b/studio/tests/pages/projects/logs.test.js index a3aa2ee4757..804e36b1b32 100644 --- a/studio/tests/pages/projects/logs.test.js +++ b/studio/tests/pages/projects/logs.test.js @@ -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() await waitFor(() => { expect(get).toHaveBeenCalledWith( - expect.stringContaining(`timestamp_start=${encodeURIComponent(isoString)}`) + expect.stringContaining(`timestamp_end=${encodeURIComponent(unixMicro)}`) ) }) clickDropdown(await screen.findByText('Custom'))