feat: add Config List page
This commit is contained in:
81
frontend/src/routes/ConfigList.tsx
Normal file
81
frontend/src/routes/ConfigList.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import Layout from '../components/Layout'
|
||||
import DataTable, { Column } from '../components/DataTable'
|
||||
import EntityForm from '../components/EntityForm'
|
||||
import Box from '@mui/material/Box'
|
||||
import Button from '@mui/material/Button'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import AddIcon from '@mui/icons-material/Add'
|
||||
import { configsApi } from '../api'
|
||||
|
||||
interface Config {
|
||||
id: number
|
||||
name: string
|
||||
description: string
|
||||
is_active: boolean
|
||||
created_at: string
|
||||
}
|
||||
|
||||
const COLUMNS: Column<Config>[] = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
{ key: 'description', label: 'Description' },
|
||||
{ key: 'is_active', label: 'Status', render: (r) => <Chip label={r.is_active ? 'Active' : 'Inactive'} color={r.is_active ? 'success' : 'default'} size="small" /> },
|
||||
{ key: 'created_at', label: 'Created', render: (r) => new Date(r.created_at).toLocaleDateString() },
|
||||
]
|
||||
|
||||
const FIELDS = [
|
||||
{ name: 'name', label: 'Name', required: true },
|
||||
{ name: 'description', label: 'Description' },
|
||||
]
|
||||
|
||||
export default function ConfigList() {
|
||||
const [configs, setConfigs] = useState<Config[]>([])
|
||||
const [formOpen, setFormOpen] = useState(false)
|
||||
const [editing, setEditing] = useState<Config | null>(null)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const load = () => configsApi.list().then((r) => setConfigs(r.data))
|
||||
useEffect(() => { load() }, [])
|
||||
|
||||
const handleSubmit = async (values: Record<string, unknown>) => {
|
||||
if (editing) {
|
||||
await configsApi.update(editing.id, values)
|
||||
} else {
|
||||
await configsApi.create(values)
|
||||
}
|
||||
setFormOpen(false)
|
||||
setEditing(null)
|
||||
load()
|
||||
}
|
||||
|
||||
const handleDelete = async (row: Config) => {
|
||||
if (!confirm(`Delete config "${row.name}"?`)) return
|
||||
await configsApi.delete(row.id)
|
||||
load()
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout title="Configurations">
|
||||
<Box sx={{ display: 'flex', justifyContent: 'flex-end', mb: 2 }}>
|
||||
<Button variant="contained" startIcon={<AddIcon />} onClick={() => { setEditing(null); setFormOpen(true) }}>
|
||||
New Config
|
||||
</Button>
|
||||
</Box>
|
||||
<DataTable
|
||||
columns={COLUMNS}
|
||||
rows={configs}
|
||||
onEdit={(row) => { navigate(`/configs/${row.id}`) }}
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
<EntityForm
|
||||
open={formOpen}
|
||||
title={editing ? 'Edit Config' : 'New Config'}
|
||||
fields={FIELDS}
|
||||
initialValues={editing ?? undefined}
|
||||
onClose={() => { setFormOpen(false); setEditing(null) }}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user