Edit product

♻️ Extract form into a single widget
This commit is contained in:
LittleSheep 2025-01-10 19:57:10 +08:00
parent 9cc8cf999e
commit d8f51e305b
5 changed files with 134 additions and 57 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -0,0 +1,87 @@
import { Collapse, Alert, TextField, Button, Box } from '@mui/material'
import { useRouter } from 'next-nprogress-bar'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { MaProduct } from 'solar-js-sdk'
import ErrorIcon from '@mui/icons-material/Error'
export interface MatrixProductForm {
name: string
alias: string
description: string
introduction: string
}
export default function MaProductForm({
onSubmit,
onSuccess,
defaultValue,
}: {
onSubmit: (data: MatrixProductForm) => Promise<any>
onSuccess?: () => void
defaultValue?: MaProduct
}) {
const { handleSubmit, register } = useForm<MatrixProductForm>({
defaultValues: {
name: defaultValue?.name ?? '',
alias: defaultValue?.alias ?? '',
description: defaultValue?.description ?? '',
introduction: defaultValue?.meta?.introduction ?? '',
},
})
const router = useRouter()
const [error, setError] = useState<string | null>(null)
const [busy, setBusy] = useState<boolean>(false)
function callback() {
if (onSuccess) {
onSuccess()
} else {
router.push('/console/matrix')
}
}
async function submit(data: MatrixProductForm) {
try {
setBusy(true)
await onSubmit(data)
callback()
} catch (err: any) {
setError(err.toString())
} finally {
setBusy(false)
}
}
return (
<form onSubmit={handleSubmit(submit)}>
<Box display="flex" flexDirection="column" maxWidth="sm" gap={2.5}>
<Collapse in={!!error} sx={{ width: '100%' }}>
<Alert icon={<ErrorIcon fontSize="inherit" />} severity="error">
{error}
</Alert>
</Collapse>
<TextField label="Name" {...register('name')} />
<TextField label="Alias" {...register('alias')} />
<TextField minRows={3} maxRows={3} multiline label="Description" {...register('description')} />
<TextField minRows={5} multiline label="Introduction" {...register('introduction')} />
<Box sx={{ mt: 5 }} display="flex" gap={2}>
<Button variant="contained" type="submit" disabled={busy}>
Submit
</Button>
<Button onClick={callback} variant="outlined" disabled={busy}>
Cancel
</Button>
</Box>
</Box>
</form>
)
}

View File

@ -48,7 +48,9 @@ export default function MatrixMarketplace() {
</CardContent>
<CardActions>
<Button size="small">Details</Button>
<Button size="small">Edit</Button>
<NextLink passHref href={`/console/matrix/products/${p.id}/edit`}>
<Button size="small">Edit</Button>
</NextLink>
</CardActions>
</Card>
</Grid>

View File

@ -0,0 +1,39 @@
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
import { Typography, Container, Box } from '@mui/material'
import { MaProduct, sni } from 'solar-js-sdk'
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import MaProductForm, { MatrixProductForm } from '@/components/matrix/MaProductForm'
export const getServerSideProps: GetServerSideProps = (async (context) => {
const id = context.params!.id
const { data } = await sni.get<MaProduct>('/cgi/ma/products/' + id)
return getConsoleStaticProps({
props: {
title: `Edit Product "${data.name}"`,
product: data,
},
})
}) satisfies GetServerSideProps<{ product: MaProduct }>
export default function ProductEdit({ product }: InferGetServerSidePropsType<typeof getServerSideProps>) {
async function onSubmit(data: MatrixProductForm) {
await sni.put('/cgi/ma/products/' + product.id, data)
}
return (
<ConsoleLayout>
<Container sx={{ py: 16, display: 'flex', flexDirection: 'column', gap: 4 }}>
<Box>
<Typography variant="h3" component="h1">
Edit product
</Typography>
<Typography variant="subtitle1">{product.name}</Typography>
</Box>
<MaProductForm onSubmit={onSubmit} defaultValue={product} />
</Container>
</ConsoleLayout>
)
}

View File

@ -1,12 +1,8 @@
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
import { Typography, Container, Box, Button, TextField, Collapse, Alert } from '@mui/material'
import { useForm } from 'react-hook-form'
import { useState } from 'react'
import { useRouter } from 'next/router'
import { Typography, Container } from '@mui/material'
import { sni } from 'solar-js-sdk'
import NextLink from 'next/link'
import ErrorIcon from '@mui/icons-material/Error'
import MaProductForm, { MatrixProductForm } from '@/components/matrix/MaProductForm'
export async function getStaticProps() {
return getConsoleStaticProps({
@ -16,31 +12,9 @@ export async function getStaticProps() {
})
}
interface MatrixProductNewForm {
name: string
alias: string
description: string
introduction: string
}
export default function ProductNew() {
const { handleSubmit, register } = useForm<MatrixProductNewForm>()
const router = useRouter()
const [error, setError] = useState<string | null>(null)
const [busy, setBusy] = useState<boolean>(false)
async function onSubmit(data: any) {
try {
setBusy(true)
await sni.post('/cgi/ma/products', data)
router.push('/console/matrix')
} catch (err: any) {
setError(err.toString())
} finally {
setBusy(false)
}
async function onSubmit(data: MatrixProductForm) {
await sni.post('/cgi/ma/products', data)
}
return (
@ -50,32 +24,7 @@ export default function ProductNew() {
Create a product
</Typography>
<form onSubmit={handleSubmit(onSubmit)}>
<Box display="flex" flexDirection="column" maxWidth="sm" gap={2.5}>
<Collapse in={!!error} sx={{ width: '100%' }}>
<Alert icon={<ErrorIcon fontSize="inherit" />} severity="error">
{error}
</Alert>
</Collapse>
<TextField label="Name" {...register('name')} />
<TextField label="Alias" {...register('alias')} />
<TextField minRows={3} maxRows={3} multiline label="Description" {...register('description')} />
<TextField minRows={5} multiline label="Introduction" {...register('introduction')} />
<Box sx={{ mt: 5 }} display="flex" gap={2}>
<Button variant="contained" type="submit" disabled={busy}>
Create
</Button>
<NextLink passHref href="/console/matrix">
<Button disabled={busy}>Cancel</Button>
</NextLink>
</Box>
</Box>
</form>
<MaProductForm onSubmit={onSubmit} />
</Container>
</ConsoleLayout>
)