metacubexd/src/pages/Logs.tsx
2023-09-16 15:38:42 +08:00

174 lines
4.6 KiB
TypeScript

import { useI18n } from '@solid-primitives/i18n'
import { IconSettings } from '@tabler/icons-solidjs'
import {
ColumnDef,
createSolidTable,
flexRender,
getCoreRowModel,
} from '@tanstack/solid-table'
import { For, Index, createEffect, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge'
import { Button, LogsSettingsModal } from '~/components'
import { LOG_LEVEL, MODAL } from '~/constants'
import { logsTableSize, tableSizeClassName, useWsRequest } from '~/signals'
import { logLevel, logMaxRows } from '~/signals/config'
import { Log } from '~/types'
type LogWithSeq = Log & { seq: number }
export default () => {
const [t] = useI18n()
let seq = 1
const [search, setSearch] = createSignal('')
const [logs, setLogs] = createSignal<LogWithSeq[]>([])
const logsData = useWsRequest<Log>('logs', { level: logLevel() })
createEffect(() => {
const data = logsData()
if (!data) {
return
}
setLogs((logs) => [{ ...data, seq }, ...logs].slice(0, logMaxRows()))
seq++
})
const columns: ColumnDef<LogWithSeq>[] = [
{
header: t('sequence'),
accessorFn: (row) => row.seq,
},
{
header: t('type'),
cell: ({ row }) => {
const type = row.original.type as LOG_LEVEL
let className = ''
switch (type) {
case LOG_LEVEL.Error:
className = 'text-error'
break
case LOG_LEVEL.Warning:
className = 'text-warning'
break
case LOG_LEVEL.Info:
className = 'text-info'
break
case LOG_LEVEL.Debug:
className = 'text-success'
break
}
return <span class={className}>{`[${row.original.type}]`}</span>
},
},
{
header: t('payload'),
accessorFn: (row) => row.payload,
},
]
const table = createSolidTable({
get data() {
return search()
? logs().filter((log) =>
log.payload.toLowerCase().includes(search().toLowerCase()),
)
: logs()
},
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<div class="flex h-full flex-col gap-4 p-1">
<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')}
onInput={(e) => setSearch(e.target.value)}
/>
<Button
class="join-item btn-sm sm:btn-md"
onClick={() => {
const modal = document.querySelector(
`#${MODAL.LOGS_SETTINGS}`,
) as HTMLDialogElement | null
modal?.showModal()
}}
>
<IconSettings />
</Button>
</div>
<div class="overflow-x-auto whitespace-nowrap rounded-md bg-base-300">
<table
class={twMerge(
tableSizeClassName(logsTableSize()),
'table relative rounded-none',
)}
>
<thead class="sticky top-0 z-10">
<Index each={table.getHeaderGroups()}>
{(keyedHeaderGroup) => {
const headerGroup = keyedHeaderGroup()
return (
<tr>
<Index each={headerGroup.headers}>
{(keyedHeader) => {
const header = keyedHeader()
return (
<th class="bg-base-300">
<div>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</div>
</th>
)
}}
</Index>
</tr>
)
}}
</Index>
</thead>
<tbody>
<For each={table.getRowModel().rows}>
{(row) => (
<tr class="hover">
<For each={row.getVisibleCells()}>
{(cell) => (
<td>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
)}
</For>
</tr>
)}
</For>
</tbody>
</table>
</div>
<LogsSettingsModal />
</div>
)
}