refactor(frontend): extract shared fmtSize, remove duplicate ModEntry, use shared ConfigEditor

- Extract fmtSize to src/utils/format.ts (was duplicated in Mods.tsx and Status.tsx)
- Remove duplicate ModEntry interface from ModlistEditor.tsx, import from types
- Refactor pages/ConfigEditor.tsx to use shared components/ConfigEditor.tsx
- Add enabled option to queries with undefined params to prevent premature fetches
This commit is contained in:
MrFastwind
2026-07-23 20:15:19 +02:00
parent 6e628a0bba
commit f4d98830dc
4 changed files with 19 additions and 43 deletions
+9 -19
View File
@@ -2,12 +2,16 @@ import { useParams, Link } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { configsApi } from '../api/client' import { configsApi } from '../api/client'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import Editor from '@monaco-editor/react' import ConfigEditorComponent from '../components/ConfigEditor'
export default function ConfigEditor() { export default function ConfigEditor() {
const { name } = useParams<{ name: string }>() const { name } = useParams<{ name: string }>()
const qc = useQueryClient() const qc = useQueryClient()
const { data: content, isLoading } = useQuery({ queryKey: ['config', name], queryFn: () => configsApi.get(name!) }) const { data: content, isLoading } = useQuery({
queryKey: ['config', name],
queryFn: () => configsApi.get(name!),
enabled: !!name,
})
const [value, setValue] = useState('') const [value, setValue] = useState('')
const [dirty, setDirty] = useState(false) const [dirty, setDirty] = useState(false)
@@ -18,8 +22,8 @@ export default function ConfigEditor() {
onSuccess: () => { qc.invalidateQueries({ queryKey: ['config', name] }); setDirty(false) }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['config', name] }); setDirty(false) },
}) })
if (isLoading) return <p className="text-neutral-500">Loading...</p>
if (!name) return <p className="text-red-400">No config specified</p> if (!name) return <p className="text-red-400">No config specified</p>
if (isLoading) return <p className="text-neutral-500">Loading...</p>
return ( return (
<div> <div>
@@ -33,24 +37,10 @@ export default function ConfigEditor() {
</button> </button>
</div> </div>
<div className="border border-neutral-800 rounded-lg overflow-hidden"> <ConfigEditorComponent
<Editor
height="600px"
language="plaintext"
value={value} value={value}
onChange={v => { setValue(v ?? ''); setDirty(true) }} onChange={v => { setValue(v); setDirty(true) }}
theme="vs-dark"
options={{
minimap: { enabled: false },
fontSize: 13,
fontFamily: '"JetBrains Mono", "Fira Code", monospace',
lineNumbers: 'on',
scrollBeyondLastLine: false,
automaticLayout: true,
tabSize: 2,
}}
/> />
</div> </div>
</div>
) )
} }
+6 -7
View File
@@ -3,17 +3,16 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { modlistsApi, steamcmdApi, logsApi } from '../api/client' import { modlistsApi, steamcmdApi, logsApi } from '../api/client'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import LiveTerminal from '../components/LiveTerminal' import LiveTerminal from '../components/LiveTerminal'
import type { ModEntry } from '../types'
interface ModEntry {
id: string
name: string
enabled: boolean
}
export default function ModlistEditor() { export default function ModlistEditor() {
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const qc = useQueryClient() const qc = useQueryClient()
const { data: modlist, isLoading } = useQuery({ queryKey: ['modlist', id], queryFn: () => modlistsApi.get(id!) }) const { data: modlist, isLoading } = useQuery({
queryKey: ['modlist', id],
queryFn: () => modlistsApi.get(id!),
enabled: !!id,
})
const [name, setName] = useState('') const [name, setName] = useState('')
const [mods, setMods] = useState<ModEntry[]>([]) const [mods, setMods] = useState<ModEntry[]>([])
const [dirty, setDirty] = useState(false) const [dirty, setDirty] = useState(false)
+1 -8
View File
@@ -1,14 +1,7 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { modsApi } from '../api/client' import { modsApi } from '../api/client'
import { useState } from 'react' import { useState } from 'react'
import { fmtSize } from '../utils/format'
function fmtSize(bytes: number): string {
if (bytes === 0) return '0 B'
const units = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(1024))
const v = bytes / Math.pow(1024, i)
return `${v.toFixed(i > 0 ? 1 : 0)} ${units[i]}`
}
export default function Mods() { export default function Mods() {
const qc = useQueryClient() const qc = useQueryClient()
+1 -7
View File
@@ -1,13 +1,7 @@
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { settingsApi } from '../api/client' import { settingsApi } from '../api/client'
import { fmtSize } from '../utils/format'
function fmtSize(bytes: number): string {
if (bytes === 0) return '0 B'
const units = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1)
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + units[i]
}
function fmtPercent(p: number): string { function fmtPercent(p: number): string {
return p.toFixed(1) + '%' return p.toFixed(1) + '%'