初步实现仪表盘界面布局
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
提取后刷新提取页套餐可用余量
|
提取后刷新提取页套餐可用余量
|
||||||
|
|
||||||
提取 ip 认证
|
提取时检查 IP 和实名状态
|
||||||
|
|
||||||
保存客户端信息时用 jwt 序列化
|
保存客户端信息时用 jwt 序列化
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"@radix-ui/react-radio-group": "^1.2.3",
|
"@radix-ui/react-radio-group": "^1.2.3",
|
||||||
"@radix-ui/react-select": "^2.1.6",
|
"@radix-ui/react-select": "^2.1.6",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
|
"@radix-ui/react-tabs": "^1.1.4",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"canvas": "^3.1.0",
|
"canvas": "^3.1.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
|
|||||||
102
pnpm-lock.yaml
generated
102
pnpm-lock.yaml
generated
@@ -38,6 +38,9 @@ importers:
|
|||||||
'@radix-ui/react-slot':
|
'@radix-ui/react-slot':
|
||||||
specifier: ^1.1.2
|
specifier: ^1.1.2
|
||||||
version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-tabs':
|
||||||
|
specifier: ^1.1.4
|
||||||
|
version: 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
'@tanstack/react-table':
|
'@tanstack/react-table':
|
||||||
specifier: ^8.21.2
|
specifier: ^8.21.2
|
||||||
version: 8.21.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 8.21.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
@@ -484,6 +487,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-collection@1.1.3':
|
||||||
|
resolution: {integrity: sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-compose-refs@1.1.1':
|
'@radix-ui/react-compose-refs@1.1.1':
|
||||||
resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==}
|
resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -542,6 +558,15 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-direction@1.1.1':
|
||||||
|
resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-dismissable-layer@1.1.5':
|
'@radix-ui/react-dismissable-layer@1.1.5':
|
||||||
resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==}
|
resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -799,6 +824,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-roving-focus@1.1.3':
|
||||||
|
resolution: {integrity: sha512-ufbpLUjZiOg4iYgb2hQrWXEPYX6jOLBbR27bDyAff5GYMRrCzcze8lukjuXVUQvJ6HZe8+oL+hhswDcjmcgVyg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-select@2.1.6':
|
'@radix-ui/react-select@2.1.6':
|
||||||
resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==}
|
resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -830,6 +868,19 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-tabs@1.1.4':
|
||||||
|
resolution: {integrity: sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-use-callback-ref@1.1.0':
|
'@radix-ui/react-use-callback-ref@1.1.0':
|
||||||
resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
|
resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2935,6 +2986,18 @@ snapshots:
|
|||||||
'@types/react': 19.0.10
|
'@types/react': 19.0.10
|
||||||
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-collection@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
'@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)':
|
'@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
@@ -2987,6 +3050,12 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.0.10
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-direction@1.1.1(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
'@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
'@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/primitive': 1.1.1
|
'@radix-ui/primitive': 1.1.1
|
||||||
@@ -3232,6 +3301,23 @@ snapshots:
|
|||||||
'@types/react': 19.0.10
|
'@types/react': 19.0.10
|
||||||
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-roving-focus@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.2
|
||||||
|
'@radix-ui/react-collection': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
'@radix-ui/react-select@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
'@radix-ui/react-select@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/number': 1.1.0
|
'@radix-ui/number': 1.1.0
|
||||||
@@ -3275,6 +3361,22 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.0.10
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-tabs@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.2
|
||||||
|
'@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-roving-focus': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
'@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
'@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|||||||
BIN
src/app/admin/(dashboard)/_assets/banner.webp
Normal file
BIN
src/app/admin/(dashboard)/_assets/banner.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
@@ -1,43 +1,104 @@
|
|||||||
import Page from '@/components/page'
|
import Page from '@/components/page'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import banner from './_assets/banner.webp'
|
||||||
|
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card'
|
||||||
|
import {Button} from '@/components/ui/button'
|
||||||
|
import {getProfile} from '@/actions/auth/auth'
|
||||||
|
import {redirect} from 'next/navigation'
|
||||||
|
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'
|
||||||
|
|
||||||
export type DashboardPageProps = {}
|
export type DashboardPageProps = {}
|
||||||
|
|
||||||
export default async function DashboardPage(props: DashboardPageProps) {
|
export default async function DashboardPage(props: DashboardPageProps) {
|
||||||
|
|
||||||
return (
|
const profile = await getProfile()
|
||||||
<Page mode={`blank`} className={`flex-auto grid grid-cols-4 grid-rows-4`}>
|
if (!profile) {
|
||||||
{/* banner */}
|
return redirect('/login')
|
||||||
<section className={`col-start-1 row-start-1 col-span-3 bg-red-200`}>
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page className={`flex-auto grid grid-cols-4 grid-rows-[150px_minmax(200px,1fr)_minmax(200px,1fr)_minmax(200px,1fr)]`}>
|
||||||
|
{/* banner */}
|
||||||
|
<section className={`col-start-1 row-start-1 col-span-3 relative rounded-lg overflow-hidden`}>
|
||||||
|
<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-primary font-medium`}>代理IP资源,先测后买</h3>
|
||||||
|
<p className={`text-primary font-medium`}>短效/长效/固定IP代理,一站式服务</p>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* 短效 */}
|
{/* 短效 */}
|
||||||
<section className={`col-start-1 row-start-2 bg-red-200`}>
|
<Card className={`col-start-1 row-start-2 py-4`}>
|
||||||
|
<CardHeader>
|
||||||
</section>
|
<CardTitle>短效动态套餐</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className={`flex gap-4`}>
|
||||||
|
<div className={`flex-1 flex flex-col items-stretch justify-center gap-2`}>
|
||||||
|
<h4 className={`text-lg`}>包时</h4>
|
||||||
|
<p className={`flex justify-between`}>
|
||||||
|
<span>套餐数量</span>
|
||||||
|
<span>todo</span>
|
||||||
|
</p>
|
||||||
|
<Button className={`h-9`}>去购买</Button>
|
||||||
|
</div>
|
||||||
|
<div className={`flex-1 flex flex-col items-stretch justify-center gap-2`}>
|
||||||
|
<h4 className={`text-lg`}>包量</h4>
|
||||||
|
<p className={`flex justify-between`}>
|
||||||
|
<span>套餐数量</span>
|
||||||
|
<span>todo</span>
|
||||||
|
</p>
|
||||||
|
<Button className={`h-9`}>去购买</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
{/* 长效 */}
|
{/* 长效 */}
|
||||||
<section className={`col-start-2 row-start-2 bg-red-200`}>
|
<Card className={`col-start-2 row-start-2`}>
|
||||||
|
<CardHeader>
|
||||||
</section>
|
<CardTitle>长效动态套餐</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
todo
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
{/* 固定 */}
|
{/* 固定 */}
|
||||||
<section className={`col-start-3 row-start-2 bg-red-200`}>
|
<Card className={`col-start-3 row-start-2 py-4`}>
|
||||||
|
<CardHeader className={`px-4`}>
|
||||||
</section>
|
<CardTitle>固定IP套餐</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className={`flex px-4 gap-4`}>
|
||||||
|
todo
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* 图表 */}
|
{/* 图表 */}
|
||||||
<section className={`col-start-1 row-start-3 col-span-3 row-span-2 bg-red-200`}>
|
<section className={`col-start-1 row-start-3 col-span-3 row-span-2`}>
|
||||||
|
<Tabs defaultValue={`dynamic`}>
|
||||||
|
<TabsList>
|
||||||
|
<TabsTrigger value={`dynamic`} className={`data-[state=active]:text-primary`}>动态 IP 套餐</TabsTrigger>
|
||||||
|
<TabsTrigger value={`static`} className={`data-[state=active]:text-primary`}>静态 IP 套餐</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value={`dynamic`}>
|
||||||
|
dynamic
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value={`static`}>
|
||||||
|
static
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* 信息 */}
|
{/* 信息 */}
|
||||||
<section className={`col-start-4 row-start-1 row-span-2 bg-red-200`}>
|
<Card className={`col-start-4 row-start-1 row-span-2`}>
|
||||||
|
<CardHeader>
|
||||||
</section>
|
<CardTitle>个人中心</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* 通知 */}
|
{/* 通知 */}
|
||||||
<section className={`col-start-4 row-start-3 row-span-2 bg-red-200`}>
|
<Card className={`col-start-4 row-start-3 row-span-2`}>
|
||||||
|
<CardHeader>
|
||||||
</section>
|
<CardTitle>待办事项</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
</Card>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,15 @@
|
|||||||
@plugin "tailwindcss-animate";
|
@plugin "tailwindcss-animate";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: oklch(1 0 0);
|
--idle: oklch(1 0 0);
|
||||||
--foreground: oklch(0.25 0 0);
|
--idle-muted: oklch(0.965 0 0);
|
||||||
--weak: oklch(0.5 0 0);
|
--idle-text: oklch(0.25 0 0);
|
||||||
|
--idle-desc: oklch(0.5 0 0);
|
||||||
|
|
||||||
--primary: oklch(0.65 0.16 265);
|
--primary: oklch(0.65 0.16 265);
|
||||||
|
--primary-muted: oklch(0.965 0.024 265);
|
||||||
--primary-text: oklch(1 0 0);
|
--primary-text: oklch(1 0 0);
|
||||||
--primary-weak: oklch(0.5 0 0);
|
--primary-desc: oklch(0.5 0 0);
|
||||||
|
|
||||||
--secondary: oklch(0.965 0 0);
|
--secondary: oklch(0.965 0 0);
|
||||||
--secondary-text: oklch(0.25 0 0);
|
--secondary-text: oklch(0.25 0 0);
|
||||||
@@ -29,9 +31,6 @@
|
|||||||
--card: oklch(0.985 0 0);
|
--card: oklch(0.985 0 0);
|
||||||
--card-text: oklch(0.25 0 0);
|
--card-text: oklch(0.25 0 0);
|
||||||
|
|
||||||
--muted: oklch(0.965 0 0);
|
|
||||||
--muted-text: oklch(0.25 0 0);
|
|
||||||
|
|
||||||
--popover: oklch(1 0 0);
|
--popover: oklch(1 0 0);
|
||||||
--popover-foreground: oklch(0.25 0 0);
|
--popover-foreground: oklch(0.25 0 0);
|
||||||
--border: oklch(0.928 0.006 264.531);
|
--border: oklch(0.928 0.006 264.531);
|
||||||
@@ -53,13 +52,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--idle);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--idle-text);
|
||||||
--color-weak: var(--weak);
|
--color-weak: var(--idle-desc);
|
||||||
|
--color-muted: var(--idle-muted);
|
||||||
|
--color-muted-foreground: var(--idle-text);
|
||||||
|
|
||||||
--color-primary: var(--primary);
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-muted: var(--primary-muted);
|
||||||
--color-primary-foreground: var(--primary-text);
|
--color-primary-foreground: var(--primary-text);
|
||||||
--color-primary-weak: var(--primary-weak);
|
--color-primary-weak: var(--primary-desc);
|
||||||
|
|
||||||
--color-secondary: var(--secondary);
|
--color-secondary: var(--secondary);
|
||||||
--color-secondary-foreground: var(--secondary-text);
|
--color-secondary-foreground: var(--secondary-text);
|
||||||
@@ -80,8 +82,6 @@
|
|||||||
--color-card-foreground: var(--card-text);
|
--color-card-foreground: var(--card-text);
|
||||||
--color-popover: var(--popover);
|
--color-popover: var(--popover);
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
--color-muted: var(--muted);
|
|
||||||
--color-muted-foreground: var(--muted-text);
|
|
||||||
--color-destructive: var(--fail);
|
--color-destructive: var(--fail);
|
||||||
--color-border: var(--border);
|
--color-border: var(--border);
|
||||||
--color-input: var(--input);
|
--color-input: var(--input);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
<div
|
<div
|
||||||
data-slot="card"
|
data-slot="card"
|
||||||
className={merge(
|
className={merge(
|
||||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
"bg-card text-card-foreground flex flex-col gap-4 rounded-lg py-4",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -20,7 +20,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
<div
|
<div
|
||||||
data-slot="card-header"
|
data-slot="card-header"
|
||||||
className={merge(
|
className={merge(
|
||||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-[data-slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-4 has-[data-slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -65,7 +65,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-content"
|
data-slot="card-content"
|
||||||
className={merge("px-6", className)}
|
className={merge("px-4", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
66
src/components/ui/tabs.tsx
Normal file
66
src/components/ui/tabs.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||||
|
|
||||||
|
import { merge } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Tabs({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Root
|
||||||
|
data-slot="tabs"
|
||||||
|
className={merge("flex flex-col gap-2", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsList({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.List
|
||||||
|
data-slot="tabs-list"
|
||||||
|
className={merge(
|
||||||
|
"bg-muted text-muted-foreground inline-flex h-10 w-fit items-center justify-center rounded-lg p-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsTrigger({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
data-slot="tabs-trigger"
|
||||||
|
className={merge(
|
||||||
|
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-4 text-sm font-medium whitespace-nowrap transition-[color] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsContent({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
data-slot="tabs-content"
|
||||||
|
className={merge("flex-1 outline-none", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||||
Reference in New Issue
Block a user