metacubexd/src/pages/Logs.tsx

225 lines
6.2 KiB
TypeScript
Raw Normal View History

2023-09-17 12:37:10 +08:00
import { makePersisted } from '@solid-primitives/storage'
import {
IconSettings,
IconSortAscending,
IconSortDescending,
} from '@tabler/icons-solidjs'
import { rankItem } from '@tanstack/match-sorter-utils'
2023-09-01 22:07:02 +08:00
import {
ColumnDef,
2023-09-17 12:37:10 +08:00
FilterFn,
SortingState,
2023-09-01 22:07:02 +08:00
createSolidTable,
flexRender,
getCoreRowModel,
2023-09-17 12:37:10 +08:00
getFilteredRowModel,
getSortedRowModel,
2023-09-01 22:07:02 +08:00
} from '@tanstack/solid-table'
import { For, Index, createEffect, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge'
2023-09-15 23:43:55 +08:00
import { Button, LogsSettingsModal } from '~/components'
import { LOG_LEVEL } from '~/constants'
2023-09-22 17:14:35 +08:00
import { useI18n } from '~/i18n'
2023-09-15 23:43:55 +08:00
import { logsTableSize, tableSizeClassName, useWsRequest } from '~/signals'
import { logLevel, logMaxRows } from '~/signals/config'
2023-09-01 22:07:02 +08:00
import { Log } from '~/types'
type LogWithSeq = Log & { seq: number }
2023-08-24 04:20:53 +08:00
2023-09-17 12:37:10 +08:00
const fuzzyFilter: FilterFn<LogWithSeq> = (row, columnId, value, addMeta) => {
// Rank the item
const itemRank = rankItem(row.getValue(columnId), value)
// Store the itemRank info
addMeta({
itemRank,
})
// Return if the item should be filtered in/out
return itemRank.passed
}
export default () => {
let logsSettingsModalRef: HTMLDialogElement | undefined
const [t] = useI18n()
2023-09-10 19:03:26 +08:00
let seq = 1
2023-09-01 22:07:02 +08:00
const [logs, setLogs] = createSignal<LogWithSeq[]>([])
2023-08-24 04:20:53 +08:00
const logsData = useWsRequest<Log>('logs', { level: logLevel() })
2023-08-24 04:20:53 +08:00
createEffect(() => {
2023-09-06 19:33:07 +08:00
const data = logsData()
2023-08-24 04:20:53 +08:00
if (!data) {
return
}
2023-09-16 15:38:42 +08:00
setLogs((logs) => [{ ...data, seq }, ...logs].slice(0, logMaxRows()))
2023-09-01 22:07:02 +08:00
seq++
})
2023-09-17 12:37:10 +08:00
const [globalFilter, setGlobalFilter] = createSignal('')
const [sorting, setSorting] = makePersisted(createSignal<SortingState>([]), {
name: 'logsTableSorting',
storage: localStorage,
})
const columns: ColumnDef<LogWithSeq>[] = [
2023-09-01 22:07:02 +08:00
{
header: t('sequence'),
2023-09-01 22:07:02 +08:00
accessorFn: (row) => row.seq,
},
{
header: t('type'),
2023-09-17 12:37:10 +08:00
accessorFn: (row) => row.type,
2023-09-10 01:10:56 +08:00
cell: ({ row }) => {
const type = row.original.type as LOG_LEVEL
2023-09-10 01:10:56 +08:00
let className = ''
switch (type) {
case LOG_LEVEL.Error:
2023-09-10 01:10:56 +08:00
className = 'text-error'
break
case LOG_LEVEL.Warning:
2023-09-10 01:10:56 +08:00
className = 'text-warning'
break
case LOG_LEVEL.Info:
2023-09-10 01:10:56 +08:00
className = 'text-info'
break
case LOG_LEVEL.Debug:
className = 'text-success'
break
2023-09-10 01:10:56 +08:00
}
return <span class={className}>{`[${row.original.type}]`}</span>
},
2023-09-01 22:07:02 +08:00
},
{
header: t('payload'),
2023-09-01 22:07:02 +08:00
accessorFn: (row) => row.payload,
},
]
2023-09-01 22:07:02 +08:00
const table = createSolidTable({
2023-09-17 12:37:10 +08:00
filterFns: {
fuzzy: fuzzyFilter,
},
state: {
get globalFilter() {
return globalFilter()
},
get sorting() {
return sorting()
},
},
2023-09-01 22:07:02 +08:00
get data() {
2023-09-17 12:37:10 +08:00
return logs()
2023-09-01 22:07:02 +08:00
},
2023-09-17 12:37:10 +08:00
sortDescFirst: true,
columns,
2023-09-17 12:37:10 +08:00
onGlobalFilterChange: setGlobalFilter,
onSortingChange: setSorting,
globalFilterFn: fuzzyFilter,
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
2023-09-01 22:07:02 +08:00
getCoreRowModel: getCoreRowModel(),
2023-08-24 04:20:53 +08:00
})
return (
2023-09-17 12:40:24 +08:00
<div class="flex h-full flex-col gap-2">
2023-09-15 23:43:55 +08:00
<div class="join w-full">
<input
type="search"
class="input join-item input-primary input-sm flex-1 flex-shrink-0 sm:input-md"
placeholder={t('search')}
2023-09-17 12:37:10 +08:00
onInput={(e) => setGlobalFilter(e.target.value)}
2023-09-15 23:43:55 +08:00
/>
<Button
class="join-item btn-sm sm:btn-md"
onClick={() => logsSettingsModalRef?.showModal()}
2023-09-22 16:05:36 +08:00
icon={<IconSettings />}
/>
2023-09-15 23:43:55 +08:00
</div>
2023-08-24 04:20:53 +08:00
2023-09-05 00:20:19 +08:00
<div class="overflow-x-auto whitespace-nowrap rounded-md bg-base-300">
2023-09-06 03:08:18 +08:00
<table
class={twMerge(
2023-09-15 23:43:55 +08:00
tableSizeClassName(logsTableSize()),
'table relative rounded-none',
)}
2023-09-06 03:08:18 +08:00
>
2023-09-05 00:20:19 +08:00
<thead class="sticky top-0 z-10">
2023-09-10 19:03:26 +08:00
<Index each={table.getHeaderGroups()}>
{(keyedHeaderGroup) => {
const headerGroup = keyedHeaderGroup()
return (
<tr>
<Index each={headerGroup.headers}>
{(keyedHeader) => {
const header = keyedHeader()
return (
2023-09-17 12:37:10 +08:00
<th class="bg-base-200">
<div class="flex items-center">
<div
class={twMerge(
header.column.getCanSort() &&
'cursor-pointer select-none',
'flex-1',
)}
onClick={header.column.getToggleSortingHandler()}
>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</div>
{{
asc: <IconSortAscending />,
desc: <IconSortDescending />,
}[header.column.getIsSorted() as string] ?? null}
2023-09-10 19:03:26 +08:00
</div>
</th>
)
}}
</Index>
</tr>
)
}}
</Index>
2023-09-01 22:07:02 +08:00
</thead>
<tbody>
<For each={table.getRowModel().rows}>
{(row) => (
<tr class="hover:!bg-primary hover:text-primary-content">
2023-09-01 22:07:02 +08:00
<For each={row.getVisibleCells()}>
{(cell) => (
<td class="py-2">
2023-09-01 22:07:02 +08:00
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
)}
</For>
</tr>
)}
</For>
</tbody>
</table>
2023-08-24 04:20:53 +08:00
</div>
2023-09-15 23:43:55 +08:00
<LogsSettingsModal ref={(el) => (logsSettingsModalRef = el)} />
2023-08-24 04:20:53 +08:00
</div>
)
}