✨ Other user profile page
This commit is contained in:
parent
6a4449d93c
commit
c42e15cff0
@ -1,8 +1,11 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from 'next'
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
/* config options here */
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
};
|
images: {
|
||||||
|
domains: ['raw.sn.solsynth.dev', 'api.sn.solsynth.dev'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"@emotion/styled": "^11.14.0",
|
"@emotion/styled": "^11.14.0",
|
||||||
"@mui/icons-material": "^6.3.0",
|
"@mui/icons-material": "^6.3.0",
|
||||||
"@mui/material": "^6.3.0",
|
"@mui/material": "^6.3.0",
|
||||||
|
"@mui/x-charts": "^7.23.2",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"axios-case-converter": "^1.1.1",
|
"axios-case-converter": "^1.1.1",
|
||||||
"cookies-next": "^5.0.2",
|
"cookies-next": "^5.0.2",
|
||||||
|
114
src/pages/users/[name].tsx
Normal file
114
src/pages/users/[name].tsx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import { SnCheckInRecord } from '@/services/checkIn'
|
||||||
|
import { getAttachmentUrl, sni } from '@/services/network'
|
||||||
|
import { SnAccount } from '@/services/user'
|
||||||
|
import { Avatar, Box, Card, CardContent, Container, Grid2 as Grid, Typography } from '@mui/material'
|
||||||
|
import { LineChart } from '@mui/x-charts'
|
||||||
|
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
export const getServerSideProps = (async (context) => {
|
||||||
|
const name = context.params!.name as string
|
||||||
|
try {
|
||||||
|
const { data: user } = await sni.get<SnAccount>('/cgi/id/users/' + name)
|
||||||
|
const { data: checkIn } = await sni.get<{ data: SnCheckInRecord[] }>('/cgi/id/users/' + name + '/check-in', {
|
||||||
|
params: { take: 14 },
|
||||||
|
})
|
||||||
|
return { props: { user, checkIn: checkIn.data } }
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) satisfies GetServerSideProps<{ user: SnAccount; checkIn: SnCheckInRecord[] }>
|
||||||
|
|
||||||
|
export default function UserProfile({ user, checkIn }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{user.banner && (
|
||||||
|
<Box sx={{ aspectRatio: 16 / 5, position: 'relative' }}>
|
||||||
|
<Image src={getAttachmentUrl(user.banner)} alt="account banner" style={{ objectFit: 'cover' }} fill />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Container sx={{ mt: 4, px: 2 }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||||
|
{user && <Avatar src={getAttachmentUrl(user.avatar)} />}
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Typography fontWeight="bold">{user.nick}</Typography>
|
||||||
|
<Typography fontFamily="monospace" fontSize={13} lineHeight={1.2}>
|
||||||
|
@{user.name}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Grid container spacing={2} sx={{ mt: 3 }}>
|
||||||
|
<Grid size={8}>
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Fortune History
|
||||||
|
</Typography>
|
||||||
|
<LineChart
|
||||||
|
yAxis={[
|
||||||
|
{
|
||||||
|
data: [1, 2, 3, 4, 5],
|
||||||
|
tickMinStep: 1,
|
||||||
|
tickMaxStep: 1,
|
||||||
|
valueFormatter(value, _) {
|
||||||
|
const resultTierList = ['大凶', '凶', '中平', '吉', '大吉']
|
||||||
|
return resultTierList[value]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
xAxis={[
|
||||||
|
{
|
||||||
|
scaleType: 'time',
|
||||||
|
data: checkIn.map((c) => {
|
||||||
|
const og = new Date(c.createdAt)
|
||||||
|
og.setHours(0, 0, 0, 0)
|
||||||
|
return og
|
||||||
|
}),
|
||||||
|
valueFormatter(value, _) {
|
||||||
|
return new Date(value).toLocaleDateString('en-US', {
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
series={[
|
||||||
|
{
|
||||||
|
data: checkIn.map((c) => c.resultTier),
|
||||||
|
valueFormatter(value, _) {
|
||||||
|
const resultTierList = ['大凶', '凶', '中平', '吉', '大吉']
|
||||||
|
return resultTierList[value ?? 0]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
height={300}
|
||||||
|
margin={{ top: 16, bottom: 24 }}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={4}>
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Information
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="body2">
|
||||||
|
Born on {new Date(user.profile!.birthday!).toLocaleDateString()}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">Joined at {new Date(user.createdAt).toLocaleDateString()}</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,12 +1,40 @@
|
|||||||
import { checkAuthenticatedClient, redirectToLogin } from '@/services/auth'
|
import { checkAuthenticatedClient, redirectToLogin } from '@/services/auth'
|
||||||
import { Container } from '@mui/material'
|
import { useUserStore } from '@/services/user'
|
||||||
import { useRouter } from 'next/router'
|
import { Avatar, Box, Container, Typography } from '@mui/material'
|
||||||
|
import { getAttachmentUrl } from '@/services/network'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
export default function UserItself() {
|
export default function UserItself() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!checkAuthenticatedClient()) redirectToLogin()
|
if (!checkAuthenticatedClient()) redirectToLogin()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <Container></Container>
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{userStore.account && (
|
||||||
|
<Box sx={{ aspectRatio: 16 / 5, position: 'relative' }}>
|
||||||
|
<Image src={getAttachmentUrl(userStore.account!.banner)} alt="account banner" objectFit="cover" fill />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Container sx={{ mt: 5 }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||||
|
{userStore.account && <Avatar src={getAttachmentUrl(userStore.account!.avatar)} />}
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Typography fontWeight="bold">{userStore.account?.nick}</Typography>
|
||||||
|
<Typography fontFamily="monospace" fontSize={13}>
|
||||||
|
@{userStore.account?.name}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ mt: 5 }}></Box>
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
10
src/services/checkIn.ts
Normal file
10
src/services/checkIn.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface SnCheckInRecord {
|
||||||
|
id: number
|
||||||
|
createdAt: Date
|
||||||
|
updatedAt: Date
|
||||||
|
deletedAt?: Date | null
|
||||||
|
resultTier: number
|
||||||
|
resultExperience: number
|
||||||
|
resultModifiers: number[]
|
||||||
|
accountId: number
|
||||||
|
}
|
@ -2,7 +2,7 @@ import { create } from 'zustand'
|
|||||||
import { sni } from './network'
|
import { sni } from './network'
|
||||||
import { hasCookie } from 'cookies-next/client'
|
import { hasCookie } from 'cookies-next/client'
|
||||||
|
|
||||||
interface SnAccount {
|
export interface SnAccount {
|
||||||
id: number
|
id: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
@ -24,7 +24,7 @@ interface SnAccount {
|
|||||||
automatedId?: number | null
|
automatedId?: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SnAccountContact {
|
export interface SnAccountContact {
|
||||||
accountId: number
|
accountId: number
|
||||||
content: string
|
content: string
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
@ -37,7 +37,7 @@ interface SnAccountContact {
|
|||||||
verifiedAt?: Date | null
|
verifiedAt?: Date | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SnAccountProfile {
|
export interface SnAccountProfile {
|
||||||
id: number
|
id: number
|
||||||
accountId: number
|
accountId: number
|
||||||
birthday?: Date | null
|
birthday?: Date | null
|
||||||
@ -50,7 +50,7 @@ interface SnAccountProfile {
|
|||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SnAccountBadge {
|
export interface SnAccountBadge {
|
||||||
id: number
|
id: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
Loading…
Reference in New Issue
Block a user