mirror of
https://github.com/supabase/supabase.git
synced 2026-10-13 21:29:08 +03:00
chore: explorer error message updated. time range buttons updated
This commit is contained in:
1 parent
0c2b5ea011
commit
2eeb1c8fce
6 files changed
+192
-84
No files matched your search
@@ -16,9 +16,10 @@ interface Props {
|
||||
log: LogData | null
|
||||
onClose: () => void
|
||||
queryType?: QueryType
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const LogSelection: FC<Props> = ({ log, onClose, queryType }) => {
|
||||
const LogSelection: FC<Props> = ({ log, onClose, queryType, isLoading }) => {
|
||||
const Formatter = () => {
|
||||
switch (queryType) {
|
||||
case 'api':
|
||||
@@ -59,13 +60,13 @@ const LogSelection: FC<Props> = ({ log, onClose, queryType }) => {
|
||||
className={[
|
||||
'relative h-full flex flex-col flex-grow border border-l',
|
||||
'border-panel-border-light dark:border-panel-border-dark',
|
||||
'bg-gray-200',
|
||||
'bg-gray-200 overflow-y-scroll',
|
||||
].join(' ')}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
`transition-all
|
||||
bg-scale-200 absolute w-full h-full text-center flex-col gap-2 flex items-center justify-center ` +
|
||||
`overflow-y-scroll transition-all
|
||||
bg-scale-200 absolute w-full h-full text-center flex-col gap-2 flex items-center justify-center opacity-0 ` +
|
||||
(log ? 'opacity-0 z-0' : 'opacity-100 z-10')
|
||||
}
|
||||
>
|
||||
@@ -73,7 +74,7 @@ const LogSelection: FC<Props> = ({ log, onClose, queryType }) => {
|
||||
className={
|
||||
`transition-all
|
||||
duration-500
|
||||
delay-200
|
||||
delay-300
|
||||
w-full
|
||||
flex
|
||||
flex-col
|
||||
@@ -81,7 +82,8 @@ const LogSelection: FC<Props> = ({ log, onClose, queryType }) => {
|
||||
items-center
|
||||
gap-6
|
||||
max-w-sm
|
||||
text-center ` + (log ? 'mt-0 opacity-0 scale-95' : 'mt-8 opacity-100 scale-100')
|
||||
text-center scale-95 opacity-0 ` +
|
||||
(log || isLoading ? 'mt-0 opacity-0 scale-95' : 'mt-8 opacity-100 scale-100')
|
||||
}
|
||||
>
|
||||
<div className="relative border border-scale-600 dark:border-scale-400 w-32 h-4 rounded px-2 flex items-center">
|
||||
@@ -112,23 +114,21 @@ const LogSelection: FC<Props> = ({ log, onClose, queryType }) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full grow">
|
||||
<div
|
||||
className="
|
||||
relative
|
||||
flex-grow
|
||||
h-px
|
||||
bg-scale-300
|
||||
"
|
||||
>
|
||||
<div
|
||||
className="
|
||||
flex-grow overflow-y-auto
|
||||
bg-scale-300
|
||||
space-y-6
|
||||
py-4
|
||||
"
|
||||
className="transition absolute cursor-pointer top-6 right-6 text-scale-900 hover:text-scale-1200"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="transition absolute cursor-pointer top-5 right-5 text-scale-900 hover:text-scale-1200"
|
||||
onClick={onClose}
|
||||
>
|
||||
<IconX size={14} strokeWidth={2} />
|
||||
</div>
|
||||
<div className="flex flex-col space-y-6">{log && <Formatter />}</div>
|
||||
<IconX size={14} strokeWidth={2} />
|
||||
</div>
|
||||
<div className="bg-scale-300 py-8 flex flex-col space-y-6">{log && <Formatter />}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { useEffect, useState, useMemo } from 'react'
|
||||
import { Button, IconDownloadCloud, IconEye, IconEyeOff, Typography } from '@supabase/ui'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
IconDownloadCloud,
|
||||
IconEye,
|
||||
IconEyeOff,
|
||||
IconLoader,
|
||||
Input,
|
||||
Typography,
|
||||
} from '@supabase/ui'
|
||||
import DataGrid from '@supabase/react-data-grid'
|
||||
|
||||
import LogSelection from './LogSelection'
|
||||
@@ -16,6 +25,8 @@ interface Props {
|
||||
queryType?: QueryType
|
||||
onHistogramToggle?: () => void
|
||||
isHistogramShowing?: boolean
|
||||
isLoading?: boolean
|
||||
error?: any
|
||||
}
|
||||
type LogMap = { [id: string]: LogData }
|
||||
|
||||
@@ -24,7 +35,14 @@ type LogMap = { [id: string]: LogData }
|
||||
*
|
||||
* When in custom data display mode, the side panel will not open when focusing on logs.
|
||||
*/
|
||||
const LogTable = ({ data = [], queryType, onHistogramToggle, isHistogramShowing }: Props) => {
|
||||
const LogTable = ({
|
||||
data = [],
|
||||
queryType,
|
||||
onHistogramToggle,
|
||||
isHistogramShowing,
|
||||
isLoading,
|
||||
error,
|
||||
}: Props) => {
|
||||
const [focusedLog, setFocusedLog] = useState<LogData | null>(null)
|
||||
const columnNames = Object.keys(data[0] || {})
|
||||
const hasId = columnNames.includes('id')
|
||||
@@ -183,7 +201,7 @@ const LogTable = ({ data = [], queryType, onHistogramToggle, isHistogramShowing
|
||||
return (
|
||||
<>
|
||||
<section
|
||||
className={'flex flex-1 flex-col ' + (!queryType ? 'shadow-lg' : '')}
|
||||
className={'flex flex-1 flex-col ' + (!queryType ? 'shadow-lg' : '')}
|
||||
style={{ maxHeight }}
|
||||
>
|
||||
{!queryType && (
|
||||
@@ -197,6 +215,7 @@ const LogTable = ({ data = [], queryType, onHistogramToggle, isHistogramShowing
|
||||
border-l
|
||||
border-r
|
||||
|
||||
|
||||
flex items-center justify-between
|
||||
px-5 py-2
|
||||
"
|
||||
@@ -228,7 +247,9 @@ const LogTable = ({ data = [], queryType, onHistogramToggle, isHistogramShowing
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={'flex flex-row h-full ' + (!queryType ? 'border-l border-r' : '')}>
|
||||
<div
|
||||
className={'flex flex-row flex-grow h-full ' + (!queryType ? 'border-l border-r' : '')}
|
||||
>
|
||||
<DataGrid
|
||||
style={{ height: '100%' }}
|
||||
className={`
|
||||
@@ -242,11 +263,77 @@ const LogTable = ({ data = [], queryType, onHistogramToggle, isHistogramShowing
|
||||
setFocusedLog(data[rowIdx] as LogData)
|
||||
}}
|
||||
noRowsFallback={
|
||||
<div className="p-4">
|
||||
<Typography.Text type="secondary" small className="font-mono">
|
||||
No data returned from query
|
||||
</Typography.Text>
|
||||
</div>
|
||||
!isLoading ? (
|
||||
<>
|
||||
<div className="py-4 w-full h-full flex-col space-y-12">
|
||||
<div
|
||||
className={`transition-all
|
||||
duration-500
|
||||
delay-200
|
||||
|
||||
flex
|
||||
flex-col
|
||||
items-center
|
||||
|
||||
gap-6
|
||||
text-center
|
||||
mt-16
|
||||
opacity-100
|
||||
scale-100
|
||||
|
||||
justify-center
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="relative border border-scale-600 border-dashed dark:border-scale-400 w-32 h-4 rounded px-2 flex items-center"></div>
|
||||
<div className="relative border border-scale-600 border-dashed dark:border-scale-400 w-32 h-4 rounded px-2 flex items-center">
|
||||
<div className="absolute right-1 -bottom-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 px-5">
|
||||
<h3 className="text-lg text-scale-1200">No results</h3>
|
||||
<p className="text-sm text-scale-900">
|
||||
Try another search, or adjusting the filters
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<div className="flex justify-center px-5">
|
||||
<Alert
|
||||
variant="danger"
|
||||
title="Sorry! An error occured when fetching data."
|
||||
withIcon
|
||||
className="max-w-xl"
|
||||
>
|
||||
<Input.TextArea
|
||||
size="small"
|
||||
value={JSON.stringify(error, null, 2)}
|
||||
borderless
|
||||
className="font-mono w-full mt-4"
|
||||
copy
|
||||
rows={12}
|
||||
/>
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
columns={columns as any}
|
||||
rowClass={(r) => {
|
||||
@@ -269,19 +356,24 @@ const LogTable = ({ data = [], queryType, onHistogramToggle, isHistogramShowing
|
||||
}}
|
||||
onRowClick={(r) => setFocusedLog(r)}
|
||||
/>
|
||||
{/* {focusedLog && ( */}
|
||||
<div
|
||||
className={
|
||||
queryType ? 'w-1/2 flex flex-col' : focusedLog ? 'w-1/2 flex flex-col' : 'w-0 hidden'
|
||||
}
|
||||
>
|
||||
<LogSelection
|
||||
onClose={() => setFocusedLog(null)}
|
||||
log={focusedLog}
|
||||
queryType={queryType}
|
||||
/>
|
||||
</div>
|
||||
{/* )} */}
|
||||
{logDataRows.length > 0 ? (
|
||||
<div
|
||||
className={
|
||||
queryType
|
||||
? 'w-1/2 flex flex-col'
|
||||
: focusedLog
|
||||
? 'w-1/2 flex flex-col'
|
||||
: 'w-0 hidden'
|
||||
}
|
||||
>
|
||||
<LogSelection
|
||||
isLoading={isLoading}
|
||||
onClose={() => setFocusedLog(null)}
|
||||
log={focusedLog}
|
||||
queryType={queryType}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
||||
@@ -166,7 +166,9 @@ export const LogsPreviewer: React.FC<Props> = ({ projectRef, queryType, override
|
||||
<div
|
||||
className={
|
||||
'transition-all duration-500 ' +
|
||||
(showChart && !isLoading ? 'opacity-100 h-48 pt-4 mb-4' : 'opacity-0 h-0')
|
||||
(showChart && !isLoading && logData.length > 0
|
||||
? 'opacity-100 h-48 pt-4 mb-4'
|
||||
: 'opacity-0 h-0')
|
||||
}
|
||||
>
|
||||
<div className={condensedLayout ? 'px-4' : ''}>
|
||||
@@ -182,6 +184,7 @@ export const LogsPreviewer: React.FC<Props> = ({ projectRef, queryType, override
|
||||
<ShimmerLine active={isLoading} />
|
||||
<LoadingOpacity active={isLoading}>
|
||||
<LogTable
|
||||
isLoading={isLoading}
|
||||
data={logData}
|
||||
queryType={queryType}
|
||||
isHistogramShowing={showChart}
|
||||
|
||||
@@ -102,6 +102,7 @@ const PreviewFilterPanel: FC<Props> = ({
|
||||
</div>
|
||||
}
|
||||
loading={isLoading}
|
||||
disabled={isLoading}
|
||||
onClick={onRefresh}
|
||||
>
|
||||
Refresh
|
||||
|
||||
@@ -119,25 +119,32 @@ const PageLayout = () => {
|
||||
|
||||
return (
|
||||
<FunctionLayout>
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-6 py-2">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<Dropdown
|
||||
side="bottom"
|
||||
align="start"
|
||||
overlay={
|
||||
<Dropdown.RadioGroup value={interval} onChange={setInterval}>
|
||||
{CHART_INTERVALS.map((i) => (
|
||||
<Dropdown.Radio key={i.key} value={i.key}>
|
||||
{i.label}
|
||||
</Dropdown.Radio>
|
||||
))}
|
||||
</Dropdown.RadioGroup>
|
||||
}
|
||||
>
|
||||
<Button as="span" type="default" iconRight={<IconChevronDown />}>
|
||||
{selectedInterval.label}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<div className="flex items-center">
|
||||
{CHART_INTERVALS.map((item, i) => {
|
||||
const classes = []
|
||||
|
||||
if (i === 0) {
|
||||
classes.push('rounded-tr-none rounded-br-none')
|
||||
} else if (i === CHART_INTERVALS.length - 1) {
|
||||
classes.push('rounded-tl-none rounded-bl-none')
|
||||
} else {
|
||||
classes.push('rounded-none')
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={`function-filter-${i}`}
|
||||
type={interval === item.key ? 'secondary' : 'default'}
|
||||
onClick={() => setInterval(item.key)}
|
||||
className={classes.join(' ')}
|
||||
>
|
||||
{item.label}
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<span className="text-scale-1000 text-xs">
|
||||
Statistics for past {selectedInterval.label}
|
||||
@@ -170,7 +177,7 @@ const PageLayout = () => {
|
||||
/>
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
<Panel key="auth-chart">
|
||||
<Panel key="invocation-chart">
|
||||
<Panel.Content className="space-y-4">
|
||||
<ChartHandler
|
||||
label="Invocations"
|
||||
@@ -191,6 +198,31 @@ const PageLayout = () => {
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
</div>
|
||||
<Panel key="error-chart">
|
||||
<Panel.Content className="space-y-4">
|
||||
<ChartHandler
|
||||
defaultChartStyle="line"
|
||||
label="Error count"
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
attribute={'min_execution_time'}
|
||||
provider="log-stats"
|
||||
// interval="1d"
|
||||
hideChartType
|
||||
customDateFormat={datetimeFormat}
|
||||
data={charts}
|
||||
isLoading={!charts.data && !error ? true : false}
|
||||
highlightedValue={calculateHighlightedValue(
|
||||
data?.result,
|
||||
'min_execution_time',
|
||||
{
|
||||
sum: true,
|
||||
}
|
||||
)}
|
||||
onBarClick={(v) => handleBarClick(v, '/auth')}
|
||||
/>
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'
|
||||
import { NextPage } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Typography, IconAlertCircle, Card, Input } from '@supabase/ui'
|
||||
import { Typography, IconAlertCircle, Card, Input, Alert } from '@supabase/ui'
|
||||
import { withAuth } from 'hooks'
|
||||
import CodeEditor from 'components/ui/CodeEditor'
|
||||
import {
|
||||
@@ -88,32 +88,12 @@ export const LogsExplorerPage: NextPage = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-grow relative pb-8">
|
||||
<LoadingOpacity active={isLoading}>
|
||||
<div className="flex flex-grow h-full">
|
||||
<LogTable data={logData} />
|
||||
<LogTable data={logData} error={error} />
|
||||
</div>
|
||||
</LoadingOpacity>
|
||||
|
||||
{error && (
|
||||
<div className="flex w-full h-full justify-center items-center mx-auto">
|
||||
<Card className="flex flex-col gap-y-2 w-2/5 bg-scale-400">
|
||||
<div className="flex flex-row gap-x-2 py-2">
|
||||
<IconAlertCircle size={16} />
|
||||
<Typography.Text type="secondary">
|
||||
Sorry! An error occured when fetching data.
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Input.TextArea
|
||||
label="Error Messages"
|
||||
value={JSON.stringify(error, null, 2)}
|
||||
borderless
|
||||
className=" border-t-2 border-scale-800 pt-2 font-mono"
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</LogsExplorerLayout>
|
||||
|
||||
Reference in new issue
Block a user