admin/logs: show status code on tablets
Massimo Melina committed
Jun 8, 2024 at 21:15 UTC
1fd8e979128979803c15ada8b732e7ead20653e9
5 files changed
+31
-27
admin/src/DataTable.ts
+17
-11
@@ -6,6 +6,7 @@ import { createElement as h, Fragment, ReactNode, useEffect, useMemo, useRef, us
6
import { newDialog, onlyTruthy } from '@hfs/shared'
7
import _ from 'lodash'
8
import { Center, Flex, useBreakpoint } from './mui'
9
+import { SxProps } from '@mui/system'
10
11
const ACTIONS = 'Actions'
12
@@ -13,7 +14,8 @@ export type DataTableColumn<R extends GridValidRowModel=any> = GridColDef<R> & {
14
hidden?: boolean
15
hideUnder?: Breakpoint | number
16
dialogHidden?: boolean
16
- mergeRender?: { other: string, override?: Partial<GridColDef<R>> } & BoxProps
17
+ sx?: SxProps
18
+ mergeRender?: { [other: string]: false | { override?: Partial<GridColDef<R>> } & BoxProps }
19
}
20
interface DataTableProps<R extends GridValidRowModel=any> extends Omit<DataGridProps<R>, 'columns'> {
21
columns: Array<DataTableColumn<R>>
@@ -32,9 +34,9 @@ export function DataTable({ columns, initialState={}, actions, actionsProps, ini
34
const apiRef = useGridApiRef()
35
const [actionsLength, setActionsLength] = useState(0)
36
const manipulatedColumns = useMemo(() => {
35
- const { localeText } = enUS.components.MuiDataGrid.defaultProps
37
+ const { localeText } = enUS.components.MuiDataGrid.defaultProps as any
38
const ret = columns.map(col => {
37
- const { type } = col
39
+ const { type, sx } = col
40
if (!type || type === 'string') // offer negated version of default string operators
41
col.filterOperators ??= getGridStringOperators().flatMap(op => op.value.includes('Empty') ? op : [ // isEmpty already has isNotEmpty
42
op,
@@ -49,23 +51,27 @@ export function DataTable({ columns, initialState={}, actions, actionsProps, ini
51
const res = op.getApplyFilterFnV7?.(item, col)
52
return res ? _.negate(res) : null
53
} },
52
- label: "(not) " + ((localeText as any)['filterOperator' + _.upperFirst(op.value)] || op.value)
54
+ label: "(not) " + (localeText['filterOperator' + _.upperFirst(op.value)] || op.value)
55
} satisfies typeof op
56
])
55
- const { mergeRender } = col
56
- if (!mergeRender)
57
+ if (!col.mergeRender)
58
return col
58
- const { other, override, ...props } = mergeRender
59
return {
60
...col,
61
originalRenderCell: col.renderCell || true,
62
renderCell(params: any) {
63
const { columns } = params.api.store.getSnapshot()
64
- const showOther = columns.columnVisibilityModel[other] === false
65
- return h(Box, { maxHeight: '100%', sx: { textWrap: 'wrap' } }, // wrap if necessary, but stay within the row
64
+ return h(Box, { maxHeight: '100%', sx: { textWrap: 'wrap', ...sx } }, // wrap if necessary, but stay within the row
65
col.renderCell ? col.renderCell(params) : params.formattedValue,
67
- showOther && h(Box, { ...compact && { lineHeight: '1em', fontSize: 'smaller' }, ...props },
68
- renderCell({ ...columns.lookup[other], ...override }, params.row) ) )
66
+ h(Flex, { fontSize: 'smaller', flexWrap: 'wrap', mt: '2px' }, // wrap, normally causing overflow/hiding, if it doesn't fit
67
+ ...onlyTruthy(_.map(col.mergeRender, (props, other) => {
68
+ if (!props || columns.columnVisibilityModel[other] !== false) return null
69
+ const { override, ...rest } = props
70
+ const rendered = renderCell({ ...columns.lookup[other], ...override }, params.row)
71
+ return rendered && h(Box, { ...rest, ...compact && { lineHeight: '1em' } }, rendered)
72
+ }))
73
+ )
74
+ )
75
}
76
}
77
})
admin/src/InstalledPlugins.ts
+2
-2
@@ -38,7 +38,7 @@ export default function InstalledPlugins({ updates }: { updates?: true }) {
38
minWidth: 150,
39
renderCell: renderName,
40
valueGetter({ row }) { return row.repo || row.id },
41
- mergeRender: { other: 'description', fontSize: 'x-small' }
41
+ mergeRender: { description: { fontSize: 'x-small' } }
42
},
43
{
44
field: 'version',
@@ -203,7 +203,7 @@ function UsernameField({ value, onChange, ...rest }: FieldProps<string>) {
203
204
export const descriptionField: DataTableColumn = {
205
field: 'description',
206
- mergeRender: { other: 'isTheme', sx: { float: 'left' } },
206
+ mergeRender: { isTheme: { sx: { float: 'left' } }} ,
207
}
208
209
export const themeField: DataTableColumn = {
admin/src/LogsPage.ts
+9
-10
@@ -178,7 +178,7 @@ function LogFile({ file, addToFooter, hidden }: { hidden?: boolean, file: string
178
field: 'msg',
179
headerName: "Message",
180
flex: 1,
181
- mergeRender: { other: 'k', override: { valueFormatter: ({ value }) => value !== 'log' && value } }
181
+ mergeRender: { k: { override: { valueFormatter: ({ value }) => value !== 'log' && value } } }
182
}
183
] : file === 'ips' ? [
184
tsColumn,
@@ -200,14 +200,12 @@ function LogFile({ file, addToFooter, hidden }: { hidden?: boolean, file: string
200
field: 'ip',
201
headerName: "Address",
202
flex: .6,
203
- minWidth: 100,
203
+ minWidth: 130,
204
maxWidth: 230,
205
mergeRender: {
206
- other: 'user',
207
- display: 'flex',
208
- justifyContent: 'space-between',
209
- gap: '.5em',
210
- override: { renderCell: ({ value, row }) => h(Fragment, {}, h('span', {}, value), h(Country, { code: row.extra?.country })) }
206
+ user: { display: 'flex', justifyContent: 'space-between', gap: '.5em', },
207
+ country: showCountry && {},
208
+ ua: {},
209
},
210
},
211
{
@@ -268,7 +266,8 @@ function LogFile({ file, addToFooter, hidden }: { hidden?: boolean, file: string
266
headerName: "URI",
267
flex: 2,
268
minWidth: 100,
271
- mergeRender: { other: 'method', fontSize: 'small' },
269
+ sx: { wordBreak: 'break-all' }, // be flexible, uri can be a mess
270
+ mergeRender: { method: {}, status: {} },
271
renderCell: ({ value, row }) => {
272
const [path, query] = splitAt('?', value).map(decodeURIComponent)
273
const ul = row.extra?.ul
@@ -319,11 +318,11 @@ export function agentIcons(agent: string | undefined) {
318
win: UW + '0/0a/Unofficial_Windows_logo_variant_-_2002%E2%80%932012_%28Multicolored%29.svg',
319
apple: UW + '7/74/Apple_logo_dark_grey.svg', // grey works for both themes
320
})
322
- return hTooltip(agent, undefined, h('span', { fontSize: '18px' }, browserIcon || short, ' ', osIcon) )
321
+ return hTooltip(agent, undefined, h('span', {}, browserIcon || short, ' ', osIcon) )
322
323
function icon(k: string, map: Dict<string>) {
324
const src = map[k]
326
- return src && h('img', { src, style: { height: '1em', verticalAlign: 'bottom', marginRight: '.2em' } })
325
+ return src && h('img', { src, style: { height: '1.3em', verticalAlign: 'bottom', marginRight: '.2em' } })
326
}
327
}
328
admin/src/MonitorPage.ts
+2
-3
@@ -31,7 +31,6 @@ function MoreInfo() {
31
if (status && connections)
32
Object.assign(status, connections)
33
const [allInfo, setAllInfo] = useState(false)
34
- const lg = useBreakpoint('lg')
34
const md = useBreakpoint('md')
35
const sm = useBreakpoint('sm')
36
const formatDuration = createDurationFormatter({ maxTokens: 2, skipZeroes: true })
@@ -132,7 +131,7 @@ function Connections() {
131
flex: 1,
132
maxWidth: 400,
133
renderCell: ({ row, value }) => ipForUrl(value) + ' :' + row.port,
135
- mergeRender: { other: 'user', fontSize: 'small' },
134
+ mergeRender: { user: { fontSize: 'small' } },
135
},
136
{
137
field: 'country',
@@ -184,7 +183,7 @@ function Connections() {
183
hideUnder: 'sm',
184
type: 'number',
185
renderCell: ({ value, row }) => formatSpeedK(Math.max(value||0, row.inSpeed||0) || undefined),
187
- mergeRender: { other: 'sent', fontSize: 'small', textAlign: 'right' }
186
+ mergeRender: { sent: { fontSize: 'small', textAlign: 'right' }}
187
},
188
{
189
field: 'sent',
admin/src/OnlinePlugins.ts
+1
-1
@@ -40,7 +40,7 @@ export default function OnlinePlugins() {
40
headerName: "name",
41
flex: 1,
42
renderCell: renderName,
43
- mergeRender: { other: 'description', fontSize: 'x-small' },
43
+ mergeRender: { description: { fontSize: 'x-small' } },
44
},
45
{
46
field: 'version',