✨ Add upload attachment ability into sdk
This commit is contained in:
166
src/components/matrix/MaReleaseForm.tsx
Normal file
166
src/components/matrix/MaReleaseForm.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import {
|
||||
Collapse,
|
||||
Alert,
|
||||
TextField,
|
||||
Button,
|
||||
Box,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Select,
|
||||
Typography,
|
||||
Grid2 as Grid,
|
||||
IconButton,
|
||||
} from '@mui/material'
|
||||
import { useRouter } from 'next-nprogress-bar'
|
||||
import { useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
|
||||
import ErrorIcon from '@mui/icons-material/Error'
|
||||
import CloseIcon from '@mui/icons-material/Close'
|
||||
import { MaProduct } from 'solar-js-sdk'
|
||||
|
||||
export interface MatrixReleaseForm {
|
||||
version: string
|
||||
type: number
|
||||
channel: string
|
||||
title: string
|
||||
description: string
|
||||
content: string
|
||||
assets: Record<string, any>
|
||||
attachments: string[]
|
||||
}
|
||||
|
||||
export default function MaReleaseForm({
|
||||
onSubmit,
|
||||
onSuccess,
|
||||
parent,
|
||||
defaultValue,
|
||||
}: {
|
||||
onSubmit: (data: MatrixReleaseForm) => Promise<any>
|
||||
onSuccess?: () => void
|
||||
parent: MaProduct
|
||||
defaultValue?: unknown
|
||||
}) {
|
||||
const { handleSubmit, register } = useForm<MatrixReleaseForm>({
|
||||
defaultValues: {},
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const [assets, setAssets] = useState<{ k: string; v: string }[]>([])
|
||||
|
||||
function addAsset() {
|
||||
setAssets((val) => [...val, { k: '', v: '' }])
|
||||
}
|
||||
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [busy, setBusy] = useState<boolean>(false)
|
||||
|
||||
function callback() {
|
||||
if (onSuccess) {
|
||||
onSuccess()
|
||||
} else {
|
||||
router.push(`/console/matrix/products/${parent.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function submit(data: MatrixReleaseForm) {
|
||||
try {
|
||||
setBusy(true)
|
||||
await onSubmit({ ...data, assets: assets.reduce((a, { k, v }) => ({ ...a, [k]: v }), {}) })
|
||||
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="Version" placeholder="Major.Minor.Patch" {...register('version', { required: true })} />
|
||||
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="release-type">Type</InputLabel>
|
||||
<Select labelId="release-type" label="Type" {...register('type', { required: true })}>
|
||||
<MenuItem value={0}>Full Release</MenuItem>
|
||||
<MenuItem value={1}>Patch Release</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<TextField label="Title" {...register('title')} />
|
||||
|
||||
<TextField label="Alias" {...register('channel')} />
|
||||
|
||||
<TextField minRows={3} maxRows={3} multiline label="Description" {...register('description')} />
|
||||
|
||||
<TextField minRows={5} multiline label="Content" {...register('content')} />
|
||||
|
||||
<Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Typography variant="subtitle1">Assets</Typography>
|
||||
|
||||
{assets.map(({ k, v }, idx) => (
|
||||
<Grid container key={idx} spacing={2}>
|
||||
<Grid size={4}>
|
||||
<TextField
|
||||
label="Platform"
|
||||
sx={{ width: '100%' }}
|
||||
value={k}
|
||||
onChange={(val) => {
|
||||
setAssets((data) =>
|
||||
data.map((ele, index) => (index == idx ? { k: val.target.value, v: ele.v } : ele)),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={7}>
|
||||
<TextField
|
||||
label="URL"
|
||||
sx={{ width: '100%' }}
|
||||
value={v}
|
||||
onChange={(val) => {
|
||||
setAssets((data) =>
|
||||
data.map((ele, index) => (index == idx ? { v: val.target.value, k: ele.k } : ele)),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={1} sx={{ display: 'grid', placeItems: 'center' }}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setAssets((data) => data.filter((_, index) => index != idx))
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
<Box>
|
||||
<Button variant="outlined" onClick={addAsset}>
|
||||
Add
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<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>
|
||||
)
|
||||
}
|
@@ -47,7 +47,9 @@ export default function MatrixMarketplace() {
|
||||
<Typography variant="body1">{p.description}</Typography>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button size="small">Details</Button>
|
||||
<NextLink passHref href={`/console/matrix/products/${p.id}`}>
|
||||
<Button size="small">Details</Button>
|
||||
</NextLink>
|
||||
<NextLink passHref href={`/console/matrix/products/${p.id}/edit`}>
|
||||
<Button size="small">Edit</Button>
|
||||
</NextLink>
|
||||
|
43
src/pages/console/matrix/products/[id]/index.tsx
Normal file
43
src/pages/console/matrix/products/[id]/index.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
|
||||
import { Box, Button, Container, Typography } from '@mui/material'
|
||||
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
||||
import { sni, MaProduct } from 'solar-js-sdk'
|
||||
import NextLink from 'next/link'
|
||||
|
||||
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: `Product "${data.name}"`,
|
||||
product: data,
|
||||
},
|
||||
})
|
||||
}) satisfies GetServerSideProps<{ product: MaProduct }>
|
||||
|
||||
export default function ProductDetails({ product }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||
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>
|
||||
|
||||
<Box display="flex" flexDirection="column" gap={2}>
|
||||
<Typography variant="h4" component="h2">
|
||||
Releases
|
||||
</Typography>
|
||||
|
||||
<NextLink passHref href={`/console/matrix/products/${product.id}/releases/new`}>
|
||||
<Button variant="contained">Create a release</Button>
|
||||
</NextLink>
|
||||
</Box>
|
||||
</Container>
|
||||
</ConsoleLayout>
|
||||
)
|
||||
}
|
40
src/pages/console/matrix/products/[id]/releases/new.tsx
Normal file
40
src/pages/console/matrix/products/[id]/releases/new.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
|
||||
import { Typography, Container, Box } from '@mui/material'
|
||||
import { MaProduct, sni } from 'solar-js-sdk'
|
||||
|
||||
import MaReleaseForm, { MatrixReleaseForm } from '@/components/matrix/MaReleaseForm'
|
||||
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
||||
|
||||
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: `New Release for "${data.name}"`,
|
||||
product: data,
|
||||
},
|
||||
})
|
||||
}) satisfies GetServerSideProps<{ product: MaProduct }>
|
||||
|
||||
export default function ReleaseNew({ product }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||
async function onSubmit(data: MatrixReleaseForm) {
|
||||
await sni.post(`/cgi/ma/products/${product.id}/releases`, data)
|
||||
}
|
||||
|
||||
return (
|
||||
<ConsoleLayout>
|
||||
<Container sx={{ py: 16, display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
<Box>
|
||||
<Typography variant="h3" component="h1">
|
||||
Create a release
|
||||
</Typography>
|
||||
<Typography variant="subtitle1">for {product.name}</Typography>
|
||||
</Box>
|
||||
|
||||
<MaReleaseForm onSubmit={onSubmit} parent={product} />
|
||||
</Container>
|
||||
</ConsoleLayout>
|
||||
)
|
||||
}
|
@@ -7,7 +7,7 @@ import MaProductForm, { MatrixProductForm } from '@/components/matrix/MaProductF
|
||||
export async function getStaticProps() {
|
||||
return getConsoleStaticProps({
|
||||
props: {
|
||||
title: 'Matrix',
|
||||
title: 'New Product',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user