187 lines
6.3 KiB
TypeScript
187 lines
6.3 KiB
TypeScript
import Page from '@/components/page'
|
||
import Image from 'next/image'
|
||
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card'
|
||
import {format} from 'date-fns'
|
||
import {merge} from '@/lib/utils'
|
||
import banner from './_assets/banner.webp'
|
||
import {listInitialization} from '@/actions/dashboard'
|
||
import Charts from './_client/charts'
|
||
import UserCenter from './_client/userCenter'
|
||
import soon from './_assets/coming-soon.svg'
|
||
import mask from './_assets/Mask group.webp'
|
||
import {ExtraResp} from '@/lib/api'
|
||
|
||
export type DashboardPageProps = {}
|
||
|
||
export default async function DashboardPage(props: DashboardPageProps) {
|
||
const resp = await listInitialization()
|
||
if (!resp.success) {
|
||
return (
|
||
<div className="col-start-4 row-start-3 row-span-2 flex justify-center items-center">
|
||
初始化数据失败
|
||
</div>
|
||
)
|
||
}
|
||
const initData = resp.data
|
||
|
||
return (
|
||
<Page className={merge(
|
||
`flex-auto grid`,
|
||
`grid-cols-1`,
|
||
`md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_300px]`,
|
||
`md:grid-rows-[150px_200px_minmax(150px,1fr)_minmax(150px,1fr)]`,
|
||
)}
|
||
>
|
||
{/* banner */}
|
||
<section className="md:row-start-1 md:col-start-1 md:col-span-3 relative md:rounded-lg overflow-hidden hidden md:block">
|
||
<Image src={banner} alt="banner image" className="w-full h-full inset-0 absolute object-cover "/>
|
||
<div className="flex flex-col absolute inset-0 justify-center px-8 gap-1">
|
||
<h3 className="text-2xl text-balance text-primary font-medium">代理IP资源,先测后买</h3>
|
||
<p className="text-primary text-balance font-medium">短效/长效/固定IP代理,一站式服务</p>
|
||
</div>
|
||
</section>
|
||
|
||
{/* 磁贴集 */}
|
||
{initData && (
|
||
<Pins {...initData.free}/>
|
||
)}
|
||
|
||
{/* 图表 */}
|
||
<div className="col-start-1 row-start-3 col-span-3 row-span-2 hidden md:block"><Charts initialData={initData.usage}/></div>
|
||
|
||
{/* 信息 */}
|
||
<div className=" md:col-start-4 md:row-start-1 md:row-span-2">
|
||
<UserCenter/>
|
||
</div>
|
||
|
||
{/* 通知 */}
|
||
<div className="md:col-start-4 md:row-start-3 md:row-span-2">
|
||
{initData && (
|
||
<Announcements list={initData.anno.list}/>
|
||
)}
|
||
</div>
|
||
|
||
</Page>
|
||
)
|
||
}
|
||
type DashboardPinsProps = ExtraResp<typeof listInitialization>['free']
|
||
|
||
function Pins(props: DashboardPinsProps) {
|
||
return (
|
||
<div className="flex md:row-start-2 md:col-start-1 md:col-span-3 gap-4 max-md:flex-col">
|
||
{/* 短效 */}
|
||
<Card className="flex-1">
|
||
<CardHeader>
|
||
<CardTitle>
|
||
<Image src={mask} alt="Mask group" priority/>
|
||
短效动态套餐
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="flex-auto flex flex-col gap-2">
|
||
<div className="flex-1 flex items-center justify-between">
|
||
<h4>包时</h4>
|
||
<p className="flex flex-col items-end">
|
||
<span className="text-sm text-weak">当日可提取数量</span>
|
||
<span className="text-sm">{props.short.resource_daily_free_sum}</span>
|
||
</p>
|
||
</div>
|
||
<div className="border-b"></div>
|
||
<div className="flex-1 flex items-center justify-between">
|
||
<h4 className="text-balance">包量</h4>
|
||
<p className="flex flex-col items-end">
|
||
<span className="text-sm text-weak">剩余可提取数量</span>
|
||
<span className="text-sm">{props.short.resource_quota_sum}</span>
|
||
</p>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* 长效 */}
|
||
<Card className="flex-1">
|
||
<CardHeader>
|
||
<CardTitle>
|
||
<Image src={mask} alt="Mask group" priority/>
|
||
长效动态套餐
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="flex-auto flex flex-col gap-2">
|
||
<div className="flex-1 flex items-center justify-between">
|
||
<h4 className="text-balance">包时</h4>
|
||
<p className="flex flex-col items-end">
|
||
<span className="text-sm text-weak" >当日可提取数量</span>
|
||
<span className="text-sm">{props.long.resource_daily_free_sum}</span>
|
||
</p>
|
||
</div>
|
||
<div className="border-b"></div>
|
||
<div className="flex-1 flex items-center justify-between">
|
||
<h4 className="text-balance">包量</h4>
|
||
<p className="flex flex-col items-end">
|
||
<span className="text-sm text-weak">剩余可提取数量</span>
|
||
<span className="text-sm">{props.long.resource_quota_sum}</span>
|
||
</p>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* 固定 */}
|
||
<Card className="flex-1 py-4 h-full hidden xl:block ">
|
||
<CardHeader className="px-4">
|
||
<CardTitle>固定IP套餐</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="flex-auto flex flex-col gap-2 items-center justify-center">
|
||
<Image alt="coming soon" src={soon}/>
|
||
<p>敬请期待</p>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
</div>
|
||
)
|
||
}
|
||
|
||
type Props = {
|
||
list: {
|
||
id: number
|
||
title: string
|
||
created_at: Date
|
||
} []
|
||
}
|
||
function Announcements(props: Props) {
|
||
return (
|
||
<Card className="h-full">
|
||
<CardHeader>
|
||
<div className="flex justify-between gap-2">
|
||
<CardTitle>公告</CardTitle>
|
||
{/* <Button
|
||
theme="text"
|
||
className="text-sm text-primary font-medium hover:text-primary bg-transparent border-none p-0 cursor-pointer"
|
||
>
|
||
查看更多
|
||
</Button> */}
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent className="flex-auto p-0">
|
||
{!props.list.length
|
||
? (
|
||
<div className="flex flex-col items-center justify-center gap-8 ">
|
||
<Image alt="coming soon" src={soon}/>
|
||
<p>暂无公告</p>
|
||
</div>
|
||
)
|
||
: props.list.map(item => (
|
||
<div
|
||
key={item.id}
|
||
className={merge(
|
||
`transition-colors duration-150 ease-in-out`,
|
||
`flex flex-col gap-1 px-4 py-2`,
|
||
`hover:bg-muted cursor-pointer`,
|
||
)}
|
||
>
|
||
<h4>{item.title}</h4>
|
||
<p className="text-sm text-weak">{format(item.created_at, 'yyyy-MM-dd HH:mm')}</p>
|
||
</div>
|
||
))}
|
||
</CardContent>
|
||
</Card>
|
||
)
|
||
}
|