Able to set product icon and previews

This commit is contained in:
LittleSheep 2025-01-11 15:51:17 +08:00
parent 7e0dff9f0f
commit 45c871cb06
2 changed files with 93 additions and 45 deletions

View File

@ -1,6 +1,6 @@
import { Collapse, Alert, TextField, Button, Box } from '@mui/material'
import { useRouter } from 'next-nprogress-bar'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { MaProduct } from 'solar-js-sdk'
@ -11,6 +11,9 @@ export interface MatrixProductForm {
alias: string
description: string
introduction: string
icon: string
previews: string[]
tags: string[]
}
export default function MaProductForm({
@ -28,11 +31,24 @@ export default function MaProductForm({
alias: defaultValue?.alias ?? '',
description: defaultValue?.description ?? '',
introduction: defaultValue?.meta?.introduction ?? '',
icon: defaultValue?.icon ?? '',
},
})
const router = useRouter()
const [previews, setPreviews] = useState<string[]>([])
const [tags, setTags] = useState<string[]>([])
useEffect(() => {
if (defaultValue?.previews) {
setPreviews(defaultValue.previews)
}
if (defaultValue?.tags) {
setTags(defaultValue.tags)
}
}, [])
const [error, setError] = useState<string | null>(null)
const [busy, setBusy] = useState<boolean>(false)
@ -47,7 +63,11 @@ export default function MaProductForm({
async function submit(data: MatrixProductForm) {
try {
setBusy(true)
await onSubmit(data)
await onSubmit({
...data,
previews,
tags,
})
callback()
} catch (err: any) {
setError(err.toString())
@ -65,10 +85,26 @@ export default function MaProductForm({
</Alert>
</Collapse>
<TextField label="Icon" placeholder="Image URL or Attachment RID" {...register('icon')} />
<TextField
label="Previews"
placeholder="Comma separated, Image URL or Attachment RID, the first one will be used as the banner"
value={previews.join(',')}
onChange={(val) => setPreviews(val.target.value.split(',').map((v) => v.trim()))}
/>
<TextField label="Name" {...register('name')} />
<TextField label="Alias" {...register('alias')} />
<TextField
label="Tags"
placeholder="Comma separated"
value={tags.join(',')}
onChange={(val) => setTags(val.target.value.split(',').map((v) => v.trim()))}
/>
<TextField minRows={3} maxRows={3} multiline label="Description" {...register('description')} />
<TextField minRows={5} multiline label="Introduction" {...register('introduction')} />

View File

@ -1,9 +1,10 @@
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
import { Box, Button, Container, Typography, Grid2 as Grid, Card, CardContent, CardActions } from '@mui/material'
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import { sni, MaProduct, MaRelease } from 'solar-js-sdk'
import { sni, MaProduct, MaRelease, getAttachmentUrl } from 'solar-js-sdk'
import { useEffect, useState } from 'react'
import NextLink from 'next/link'
import Image from 'next/image'
export const getServerSideProps: GetServerSideProps = (async (context) => {
const id = context.params!.id
@ -45,52 +46,63 @@ export default function ProductDetails({ product }: InferGetServerSidePropsType<
return (
<ConsoleLayout>
<Container sx={{ py: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
<Box maxWidth="sm">
<Typography variant="h3" component="h1">
{product.name}
</Typography>
<Typography variant="subtitle1">{product.description}</Typography>
</Box>
<>
{product.previews && (
<img
src={getAttachmentUrl(product.previews[0])}
alt={product.name}
style={{ objectFit: 'cover', aspectRatio: 16 / 5 }}
className='border-b border-1'
/>
)}
<Box display="flex" flexDirection="column" gap={2}>
<Typography variant="h4" component="h2">
Releases
</Typography>
<Container sx={{ pt: (product.previews ? 8 : 16), pb: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
<Box maxWidth="sm">
<Typography variant="h3" component="h1">
{product.name}
</Typography>
<Typography variant="subtitle1">{product.description}</Typography>
</Box>
<NextLink passHref href={`/console/matrix/products/${product.id}/releases/new`}>
<Button variant="contained">Create a release</Button>
</NextLink>
<Box display="flex" flexDirection="column" gap={2}>
<Typography variant="h4" component="h2">
Releases
</Typography>
<Grid container columns={{ xs: 1, sm: 2, md: 3 }} spacing={2}>
{releases.map((r: any) => (
<Grid size={1} key={r.id}>
<Card>
<CardContent>
<Typography variant="caption">{r.version}</Typography>
<Typography variant="h5" component="h2">
{r.meta.title}
</Typography>
<Typography variant="body1" gutterBottom>
{r.type == 0 ? 'Full Release' : 'Patch Release'}
</Typography>
<NextLink passHref href={`/console/matrix/products/${product.id}/releases/new`}>
<Button variant="contained">Create a release</Button>
</NextLink>
<Typography variant="body1">{r.meta.description}</Typography>
</CardContent>
<CardActions>
<NextLink passHref href={`/console/matrix/products/${r.productId}/releases/${r.id}/edit`}>
<Button size="small">Edit</Button>
</NextLink>
<Button size="small" color="error" onClick={() => deleteRelease(r.id)}>
Delete
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Box>
</Container>
<Grid container columns={{ xs: 1, sm: 2, md: 3 }} spacing={2}>
{releases.map((r: any) => (
<Grid size={1} key={r.id}>
<Card>
<CardContent>
<Typography variant="caption">{r.version}</Typography>
<Typography variant="h5" component="h2">
{r.meta.title}
</Typography>
<Typography variant="body1" gutterBottom>
{r.type == 0 ? 'Full Release' : 'Patch Release'}
</Typography>
<Typography variant="body1">{r.meta.description}</Typography>
</CardContent>
<CardActions>
<NextLink passHref href={`/console/matrix/products/${r.productId}/releases/${r.id}/edit`}>
<Button size="small">Edit</Button>
</NextLink>
<Button size="small" color="error" onClick={() => deleteRelease(r.id)}>
Delete
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Box>
</Container>
</>
</ConsoleLayout>
)
}