✨ Supports release assets & installers
This commit is contained in:
parent
847151afe8
commit
bdcbb18592
@ -13,6 +13,7 @@
|
|||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@emotion/styled": "^11.14.0",
|
"@emotion/styled": "^11.14.0",
|
||||||
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@mui/icons-material": "^6.3.1",
|
"@mui/icons-material": "^6.3.1",
|
||||||
"@mui/material": "^6.3.1",
|
"@mui/material": "^6.3.1",
|
||||||
"@mui/material-nextjs": "^6.3.1",
|
"@mui/material-nextjs": "^6.3.1",
|
||||||
@ -37,7 +38,7 @@
|
|||||||
"remark-parse": "^11.0.0",
|
"remark-parse": "^11.0.0",
|
||||||
"remark-rehype": "^11.1.1",
|
"remark-rehype": "^11.1.1",
|
||||||
"sitemap": "^8.0.0",
|
"sitemap": "^8.0.0",
|
||||||
"solar-js-sdk": "^0.0.2",
|
"solar-js-sdk": "0.0.8",
|
||||||
"unified": "^11.0.5",
|
"unified": "^11.0.5",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"name": "LittleSheep",
|
"name": "LittleSheep",
|
||||||
"email": "littlesheep.code@hotmail.com"
|
"email": "littlesheep.code@hotmail.com"
|
||||||
},
|
},
|
||||||
"version": "0.0.5",
|
"version": "0.0.8",
|
||||||
"tsup": {
|
"tsup": {
|
||||||
"entry": [
|
"entry": [
|
||||||
"src/index.ts"
|
"src/index.ts"
|
||||||
|
@ -6,7 +6,8 @@ export interface MaRelease {
|
|||||||
version: string
|
version: string
|
||||||
type: number
|
type: number
|
||||||
channel: string
|
channel: string
|
||||||
assets: Record<string, any>
|
assets: Record<string, MaReleaseAsset>
|
||||||
|
installers: Record<string, MaReleaseInstaller>
|
||||||
product_id: number
|
product_id: number
|
||||||
meta: MaReleaseMeta
|
meta: MaReleaseMeta
|
||||||
}
|
}
|
||||||
@ -22,3 +23,19 @@ export interface MaReleaseMeta {
|
|||||||
attachments: string[]
|
attachments: string[]
|
||||||
release_id: number
|
release_id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MaReleaseAsset {
|
||||||
|
uri: string
|
||||||
|
contentType: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MaReleaseInstallerPatch {
|
||||||
|
action: string
|
||||||
|
glob: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MaReleaseInstaller {
|
||||||
|
workdir?: string
|
||||||
|
script?: string
|
||||||
|
patches: MaReleaseInstallerPatch[]
|
||||||
|
}
|
||||||
|
@ -11,15 +11,16 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Grid2 as Grid,
|
Grid2 as Grid,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
Card,
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { useRouter } from 'next-nprogress-bar'
|
import { useRouter } from 'next-nprogress-bar'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
|
import { MaProduct, MaRelease, MaReleaseAsset, MaReleaseInstaller, MaReleaseInstallerPatch } from 'solar-js-sdk'
|
||||||
|
import MonacoEditor from '@monaco-editor/react'
|
||||||
|
|
||||||
import ErrorIcon from '@mui/icons-material/Error'
|
import ErrorIcon from '@mui/icons-material/Error'
|
||||||
import CloseIcon from '@mui/icons-material/Close'
|
import CloseIcon from '@mui/icons-material/Close'
|
||||||
import { MaProduct } from 'solar-js-sdk'
|
|
||||||
import { version } from 'node:os'
|
|
||||||
|
|
||||||
export interface MatrixReleaseForm {
|
export interface MatrixReleaseForm {
|
||||||
version: string
|
version: string
|
||||||
@ -28,7 +29,8 @@ export interface MatrixReleaseForm {
|
|||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
content: string
|
content: string
|
||||||
assets: Record<string, any>
|
assets: Record<string, MaReleaseAsset>
|
||||||
|
installers: Record<string, MaReleaseInstaller>
|
||||||
attachments: string[]
|
attachments: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ export default function MaReleaseForm({
|
|||||||
onSubmit: (data: MatrixReleaseForm) => Promise<any>
|
onSubmit: (data: MatrixReleaseForm) => Promise<any>
|
||||||
onSuccess?: () => void
|
onSuccess?: () => void
|
||||||
parent: Partial<MaProduct>
|
parent: Partial<MaProduct>
|
||||||
defaultValue?: any
|
defaultValue?: MaRelease
|
||||||
}) {
|
}) {
|
||||||
const { handleSubmit, register } = useForm<MatrixReleaseForm>({
|
const { handleSubmit, register } = useForm<MatrixReleaseForm>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@ -56,17 +58,25 @@ export default function MaReleaseForm({
|
|||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (defaultValue) {
|
if (defaultValue?.assets) {
|
||||||
setAssets(Object.keys(defaultValue.assets).map((k) => ({ k, v: defaultValue.assets[k] })))
|
setAssets(Object.keys(defaultValue.assets).map((k) => ({ k, v: defaultValue.assets[k] })))
|
||||||
}
|
}
|
||||||
|
if (defaultValue?.installers) {
|
||||||
|
setInstallers(Object.keys(defaultValue.installers).map((k) => ({ k, v: defaultValue.installers[k] })))
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const [assets, setAssets] = useState<{ k: string; v: string }[]>([])
|
const [assets, setAssets] = useState<{ k: string; v: MaReleaseAsset }[]>([])
|
||||||
|
const [installers, setInstallers] = useState<{ k: string; v: MaReleaseInstaller }[]>([])
|
||||||
|
|
||||||
function addAsset() {
|
function addAsset() {
|
||||||
setAssets((val) => [...val, { k: '', v: '' }])
|
setAssets((val) => [...val, { k: '', v: { uri: '', contentType: '' } }])
|
||||||
|
}
|
||||||
|
|
||||||
|
function addInstaller() {
|
||||||
|
setInstallers((val) => [...val, { k: '', v: { workdir: '', script: '', patches: [] } }])
|
||||||
}
|
}
|
||||||
|
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
@ -83,7 +93,11 @@ export default function MaReleaseForm({
|
|||||||
async function submit(data: MatrixReleaseForm) {
|
async function submit(data: MatrixReleaseForm) {
|
||||||
try {
|
try {
|
||||||
setBusy(true)
|
setBusy(true)
|
||||||
await onSubmit({ ...data, assets: assets.reduce((a, { k, v }) => ({ ...a, [k]: v }), {}) })
|
await onSubmit({
|
||||||
|
...data,
|
||||||
|
assets: assets.reduce((a, { k, v }) => ({ ...a, [k]: v }), {}),
|
||||||
|
installers: installers.reduce((a, { k, v }) => ({ ...a, [k]: v }), {}),
|
||||||
|
})
|
||||||
callback()
|
callback()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.toString())
|
setError(err.toString())
|
||||||
@ -105,7 +119,12 @@ export default function MaReleaseForm({
|
|||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<InputLabel id="release-type">Type</InputLabel>
|
<InputLabel id="release-type">Type</InputLabel>
|
||||||
<Select labelId="release-type" label="Type" {...register('type', { required: true })}>
|
<Select
|
||||||
|
labelId="release-type"
|
||||||
|
label="Type"
|
||||||
|
defaultValue={defaultValue?.type}
|
||||||
|
{...register('type', { required: true })}
|
||||||
|
>
|
||||||
<MenuItem value={0}>Full Release</MenuItem>
|
<MenuItem value={0}>Full Release</MenuItem>
|
||||||
<MenuItem value={1}>Patch Release</MenuItem>
|
<MenuItem value={1}>Patch Release</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
@ -120,44 +139,64 @@ export default function MaReleaseForm({
|
|||||||
<TextField minRows={5} multiline label="Content" {...register('content')} />
|
<TextField minRows={5} multiline label="Content" {...register('content')} />
|
||||||
|
|
||||||
<Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
<Typography variant="subtitle1">Assets</Typography>
|
<Typography variant="h5">Assets</Typography>
|
||||||
|
|
||||||
{assets.map(({ k, v }, idx) => (
|
{assets.map(({ k, v }, idx) => (
|
||||||
<Grid container key={idx} spacing={2}>
|
<Card variant="outlined" key={idx}>
|
||||||
<Grid size={4}>
|
<Box sx={{ pl: 2, pr: 4, py: 2 }}>
|
||||||
<TextField
|
<Grid container spacing={2}>
|
||||||
label="Platform"
|
<Grid size={11}>
|
||||||
sx={{ width: '100%' }}
|
<TextField
|
||||||
value={k}
|
label="Platform"
|
||||||
onChange={(val) => {
|
sx={{ width: '100%' }}
|
||||||
setAssets((data) =>
|
value={k}
|
||||||
data.map((ele, index) => (index == idx ? { k: val.target.value, v: ele.v } : ele)),
|
onChange={(val) => {
|
||||||
)
|
setAssets((data) =>
|
||||||
}}
|
data.map((ele, index) => (index == idx ? { k: val.target.value, v: ele.v } : ele)),
|
||||||
/>
|
)
|
||||||
</Grid>
|
}}
|
||||||
<Grid size={7}>
|
/>
|
||||||
<TextField
|
</Grid>
|
||||||
label="URL"
|
<Grid size={1} sx={{ display: 'grid', placeItems: 'center' }}>
|
||||||
sx={{ width: '100%' }}
|
<IconButton
|
||||||
value={v}
|
onClick={() => {
|
||||||
onChange={(val) => {
|
setAssets((data) => data.filter((_, index) => index != idx))
|
||||||
setAssets((data) =>
|
}}
|
||||||
data.map((ele, index) => (index == idx ? { v: val.target.value, k: ele.k } : ele)),
|
>
|
||||||
)
|
<CloseIcon />
|
||||||
}}
|
</IconButton>
|
||||||
/>
|
</Grid>
|
||||||
</Grid>
|
<Grid size={8}>
|
||||||
<Grid size={1} sx={{ display: 'grid', placeItems: 'center' }}>
|
<TextField
|
||||||
<IconButton
|
label="URI"
|
||||||
onClick={() => {
|
sx={{ width: '100%' }}
|
||||||
setAssets((data) => data.filter((_, index) => index != idx))
|
value={v.uri}
|
||||||
}}
|
onChange={(val) => {
|
||||||
>
|
setAssets((data) =>
|
||||||
<CloseIcon />
|
data.map((ele, index) =>
|
||||||
</IconButton>
|
index == idx ? { v: { ...ele.v, uri: val.target.value }, k: ele.k } : ele,
|
||||||
</Grid>
|
),
|
||||||
</Grid>
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={4}>
|
||||||
|
<TextField
|
||||||
|
label="Content Type"
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
value={v.contentType}
|
||||||
|
onChange={(val) => {
|
||||||
|
setAssets((data) =>
|
||||||
|
data.map((ele, index) =>
|
||||||
|
index == idx ? { v: { ...ele.v, contentType: val.target.value }, k: ele.k } : ele,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
@ -167,6 +206,109 @@ export default function MaReleaseForm({
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ mt: 3, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
|
<Typography variant="h5">Installers</Typography>
|
||||||
|
|
||||||
|
{installers.map(({ k, v }, idx) => (
|
||||||
|
<Card variant="outlined" key={idx}>
|
||||||
|
<Box sx={{ pl: 2, pr: 4, py: 2 }}>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid size={4}>
|
||||||
|
<TextField
|
||||||
|
label="Platform"
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
value={k}
|
||||||
|
onChange={(val) => {
|
||||||
|
setInstallers((data) =>
|
||||||
|
data.map((ele, index) => (index == idx ? { k: val.target.value, v: ele.v } : ele)),
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={7}>
|
||||||
|
<TextField
|
||||||
|
label="Working Directory"
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
value={v.workdir}
|
||||||
|
onChange={(val) => {
|
||||||
|
setInstallers((data) =>
|
||||||
|
data.map((ele, index) =>
|
||||||
|
index == idx ? { k: ele.k, v: { ...ele.v, workdir: val.target.value } } : ele,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={1} sx={{ display: 'grid', placeItems: 'center' }}>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
setInstallers((data) => data.filter((_, index) => index != idx))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={12}>
|
||||||
|
<Typography variant="subtitle1" sx={{ mx: 1 }}>
|
||||||
|
Script
|
||||||
|
</Typography>
|
||||||
|
<Card variant="outlined">
|
||||||
|
<MonacoEditor
|
||||||
|
height="280px"
|
||||||
|
width="100%"
|
||||||
|
options={{ minimap: { enabled: false } }}
|
||||||
|
defaultValue={v.script}
|
||||||
|
onChange={(val) =>
|
||||||
|
setInstallers((data) =>
|
||||||
|
data.map((ele, index) => (index == idx ? { v: { ...ele.v, script: val }, k: ele.k } : ele)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={12}>
|
||||||
|
<Typography variant="subtitle1" sx={{ mx: 1 }}>
|
||||||
|
Patches
|
||||||
|
</Typography>
|
||||||
|
<Card variant="outlined">
|
||||||
|
<MonacoEditor
|
||||||
|
height="280px"
|
||||||
|
width="100%"
|
||||||
|
options={{ minimap: { enabled: false } }}
|
||||||
|
defaultValue={v.patches.map((p) => `${p.action}:${p.glob}`).join('\n')}
|
||||||
|
onChange={(val) =>
|
||||||
|
setInstallers((data) =>
|
||||||
|
data.map((ele, index) =>
|
||||||
|
index == idx
|
||||||
|
? {
|
||||||
|
v: {
|
||||||
|
...ele.v,
|
||||||
|
patches: val?.split('\n')?.map((p) => ({
|
||||||
|
action: p.split(':')[0],
|
||||||
|
glob: p.split(':')[1],
|
||||||
|
})) as MaReleaseInstallerPatch[],
|
||||||
|
},
|
||||||
|
k: ele.k,
|
||||||
|
}
|
||||||
|
: ele,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Button variant="outlined" onClick={addInstaller}>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ mt: 5 }} display="flex" gap={2}>
|
<Box sx={{ mt: 5 }} display="flex" gap={2}>
|
||||||
<Button variant="contained" type="submit" disabled={busy}>
|
<Button variant="contained" type="submit" disabled={busy}>
|
||||||
Submit
|
Submit
|
||||||
|
@ -34,46 +34,53 @@ export default function MatrixMarketplace() {
|
|||||||
if (!yes) return
|
if (!yes) return
|
||||||
|
|
||||||
await sni.delete('/cgi/ma/products/' + id)
|
await sni.delete('/cgi/ma/products/' + id)
|
||||||
window.location.reload()
|
await fetchProducts()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConsoleLayout>
|
<ConsoleLayout>
|
||||||
<Container sx={{ py: 16, display: 'flex', flexDirection: 'column', gap: 4 }}>
|
<Container sx={{ py: 16, display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||||
<Typography variant="h3" component="h1">
|
|
||||||
Matrix Marketplace
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Grid container columns={{ xs: 1, sm: 2, md: 3 }} spacing={4}>
|
|
||||||
{products.map((p) => (
|
|
||||||
<Grid size={1} key={p.id}>
|
|
||||||
<Card sx={{ width: '100%' }}>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="h5" gutterBottom>
|
|
||||||
{p.name}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1">{p.description}</Typography>
|
|
||||||
</CardContent>
|
|
||||||
<CardActions>
|
|
||||||
<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>
|
|
||||||
<Button size="small" color="error" onClick={() => deleteProduct(p.id)}>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<NextLink passHref href="/console/matrix/products/new">
|
<Typography variant="h3" component="h1">
|
||||||
<Button variant="contained">Create a product</Button>
|
Matrix Marketplace
|
||||||
</NextLink>
|
</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
The new way to release your app, implement version check and auto updating.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box display="flex" flexDirection="column" gap={2}>
|
||||||
|
<Box>
|
||||||
|
<NextLink passHref href="/console/matrix/products/new">
|
||||||
|
<Button variant="contained">Create a product</Button>
|
||||||
|
</NextLink>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Grid container columns={{ xs: 1, sm: 2, md: 3 }} spacing={4}>
|
||||||
|
{products.map((p) => (
|
||||||
|
<Grid size={1} key={p.id}>
|
||||||
|
<Card sx={{ width: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
{p.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1">{p.description}</Typography>
|
||||||
|
</CardContent>
|
||||||
|
<CardActions>
|
||||||
|
<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>
|
||||||
|
<Button size="small" color="error" onClick={() => deleteProduct(p.id)}>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Container>
|
||||||
</ConsoleLayout>
|
</ConsoleLayout>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
|
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
|
||||||
import { Box, Button, Container, Typography, Grid2 as Grid, Card, CardContent, CardActions } from '@mui/material'
|
import { Box, Button, Container, Typography, Grid2 as Grid, Card, CardContent, CardActions } from '@mui/material'
|
||||||
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
||||||
import { sni, MaProduct } from 'solar-js-sdk'
|
import { sni, MaProduct, MaRelease } from 'solar-js-sdk'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
import NextLink from 'next/link'
|
import NextLink from 'next/link'
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = (async (context) => {
|
export const getServerSideProps: GetServerSideProps = (async (context) => {
|
||||||
@ -9,28 +10,37 @@ export const getServerSideProps: GetServerSideProps = (async (context) => {
|
|||||||
|
|
||||||
const { data } = await sni.get<MaProduct>('/cgi/ma/products/' + id)
|
const { data } = await sni.get<MaProduct>('/cgi/ma/products/' + id)
|
||||||
|
|
||||||
const { data: resp } = await sni.get<{ data: any[] }>('/cgi/ma/products/' + id + '/releases', {
|
|
||||||
params: {
|
|
||||||
take: 10,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return getConsoleStaticProps({
|
return getConsoleStaticProps({
|
||||||
props: {
|
props: {
|
||||||
title: `Product "${data.name}"`,
|
title: `Product "${data.name}"`,
|
||||||
product: data,
|
product: data,
|
||||||
releases: resp.data,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}) satisfies GetServerSideProps<{ product: MaProduct; releases: any[] }>
|
}) satisfies GetServerSideProps<{ product: MaProduct }>
|
||||||
|
|
||||||
|
export default function ProductDetails({ product }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||||
|
const [releases, setReleases] = useState<MaRelease[]>([])
|
||||||
|
|
||||||
|
async function fetchReleases() {
|
||||||
|
const { data: resp } = await sni.get<{ data: MaRelease[] }>('/cgi/ma/products/' + product.id + '/releases', {
|
||||||
|
params: {
|
||||||
|
take: 10,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
setReleases(resp.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchReleases()
|
||||||
|
}, [])
|
||||||
|
|
||||||
export default function ProductDetails({ product, releases }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
|
||||||
async function deleteRelease(id: number) {
|
async function deleteRelease(id: number) {
|
||||||
const yes = confirm(`Are you sure you want to delete this release #${id}?`)
|
const yes = confirm(`Are you sure you want to delete this release #${id}?`)
|
||||||
if (!yes) return
|
if (!yes) return
|
||||||
|
|
||||||
await sni.delete('/cgi/ma/products/' + product.id + '/releases/' + id)
|
await sni.delete('/cgi/ma/products/' + product.id + '/releases/' + id)
|
||||||
window.location.reload()
|
await fetchReleases()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user