From 078fc9d756d9847d115a0f060791b8aed5d95547 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Wed, 27 Nov 2024 20:25:19 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(billing):=20Update=20Billing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/admin/app/dashboard/page.tsx | 754 +----------------- apps/admin/app/dashboard/payment/page.tsx | 53 +- apps/admin/components/billing.tsx | 60 ++ .../admin/components/dashboard/statistics.tsx | 703 ++++++++++++++++ 4 files changed, 771 insertions(+), 799 deletions(-) create mode 100644 apps/admin/components/billing.tsx create mode 100644 apps/admin/components/dashboard/statistics.tsx diff --git a/apps/admin/app/dashboard/page.tsx b/apps/admin/app/dashboard/page.tsx index 34fd6db..e91e3d6 100644 --- a/apps/admin/app/dashboard/page.tsx +++ b/apps/admin/app/dashboard/page.tsx @@ -1,757 +1,11 @@ -'use client'; - -import { Icon } from '@iconify/react'; -import { formatBytes, unitConversion } from '@repo/ui/utils'; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from '@shadcn/ui/card'; -import { - ChartConfig, - ChartContainer, - ChartLegend, - ChartLegendContent, - ChartTooltip, - ChartTooltipContent, -} from '@shadcn/ui/chart'; -import { - Area, - AreaChart, - Bar, - BarChart, - CartesianGrid, - Label, - LabelList, - Pie, - PieChart, - XAxis, - YAxis, -} from '@shadcn/ui/lib/recharts'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@shadcn/ui/select'; -import { Separator } from '@shadcn/ui/separator'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@shadcn/ui/tabs'; -import { useLocale } from 'next-intl'; -import { useState } from 'react'; - -const UserStatisticsConfig = { - register: { - label: '注册', - color: 'hsl(var(--chart-1))', - }, - new_purchase: { - label: '新购', - color: 'hsl(var(--chart-2))', - }, - repurchase: { - label: '复购', - color: 'hsl(var(--chart-3))', - }, -} satisfies ChartConfig; - -const IncomeStatisticsConfig = { - new_purchase: { - label: '新购', - color: 'hsl(var(--chart-1))', - }, - repurchase: { - label: '复购', - color: 'hsl(var(--chart-2))', - }, -}; - -// Sample data - replace with actual data -const trafficData = { - nodes: { - today: [ - { name: 'Node 1', traffic: 1000, type: 'Trojan', address: '127.0.0.1:443' }, - { name: 'Node 2', traffic: 800, type: 'Trojan', address: '127.0.0.1:444' }, - { name: 'Node 3', traffic: 600, type: 'Trojan', address: '127.0.0.1:445' }, - { name: 'Node 4', traffic: 400, type: 'Trojan', address: '127.0.0.1:446' }, - { name: 'Node 5', traffic: 200, type: 'Trojan', address: '127.0.0.1:447' }, - { name: 'Node 6', traffic: 1000, type: 'Trojan', address: '127.0.0.1:443' }, - { name: 'Node 7', traffic: 800, type: 'Trojan', address: '127.0.0.1:444' }, - { name: 'Node 8', traffic: 600, type: 'Trojan', address: '127.0.0.1:445' }, - { name: 'Node 9', traffic: 400, type: 'Trojan', address: '127.0.0.1:446' }, - { name: 'Node 10', traffic: 200, type: 'Trojan', address: '127.0.0.1:447' }, - ], - yesterday: [ - { name: 'Node 1', traffic: 900, type: 'Trojan', address: '127.0.0.1:443' }, - { name: 'Node 2', traffic: 750, type: 'Trojan', address: '127.0.0.1:444' }, - { name: 'Node 3', traffic: 550, type: 'Trojan', address: '127.0.0.1:445' }, - { name: 'Node 4', traffic: 350, type: 'Trojan', address: '127.0.0.1:446' }, - { name: 'Node 5', traffic: 150, type: 'Trojan', address: '127.0.0.1:447' }, - ], - }, - users: { - today: [ - { name: 'olivia.martin@email.com', traffic: 100, email: 'olivia.martin@email.com' }, - { name: 'jackson.lee@email.com', traffic: 90, email: 'jackson.lee@email.com' }, - { name: 'isabella.nguyen@email.com', traffic: 80, email: 'isabella.nguyen@email.com' }, - { name: 'william.chen@email.com', traffic: 70, email: 'william.chen@email.com' }, - { name: 'sophia.rodriguez@email.com', traffic: 60, email: 'sophia.rodriguez@email.com' }, - { name: 'olivia.martin@email.com', traffic: 100, email: 'olivia.martin@email.com' }, - { name: 'jackson.lee@email.com', traffic: 90, email: 'jackson.lee@email.com' }, - { name: 'isabella.nguyen@email.com', traffic: 80, email: 'isabella.nguyen@email.com' }, - { name: 'william.chen@email.com', traffic: 70, email: 'william.chen@email.com' }, - { name: 'sophia.rodriguez@email.com', traffic: 60, email: 'sophia.rodriguez@email.com' }, - ], - yesterday: [ - { name: 'olivia.martin@email.com', traffic: 95, email: 'olivia.martin@email.com' }, - { name: 'jackson.lee@email.com', traffic: 85, email: 'jackson.lee@email.com' }, - { name: 'isabella.nguyen@email.com', traffic: 75, email: 'isabella.nguyen@email.com' }, - { name: 'william.chen@email.com', traffic: 65, email: 'william.chen@email.com' }, - { name: 'sophia.rodriguez@email.com', traffic: 55, email: 'sophia.rodriguez@email.com' }, - ], - }, -}; +import Billing from '@/components/billing'; +import Statistics from '@/components/dashboard/statistics'; export default function Dashboard() { - const locale = useLocale(); - - const [dataType, setDataType] = useState('nodes'); - const [timeFrame, setTimeFrame] = useState('today'); - - const currentData = trafficData[dataType][timeFrame]; - return (
-
- {[ - { - title: '在线IP数', - value: '666', - icon: 'uil:network-wired', - onClick: () => console.log('在线IP数 clicked'), - }, - { - title: '在线节点数', - value: '99', - icon: 'uil:server-network', - onClick: () => console.log('在线节点数 clicked'), - }, - { - title: '离线节点数', - value: '1', - icon: 'uil:server-network-alt', - onClick: () => console.log('离线节点数 clicked'), - }, - { - title: '待处理工单', - value: '1', - icon: 'uil:clipboard-notes', - onClick: () => console.log('待处理工单 clicked'), - }, - { - title: '今日上传流量', - value: formatBytes(99999999999999), - icon: 'uil:arrow-up', - onClick: () => console.log('今日上传流量 clicked'), - }, - { - title: '今日下载流量', - value: formatBytes(99999999999999), - icon: 'uil:arrow-down', - onClick: () => console.log('今日下载流量 clicked'), - }, - { - title: '总上传流量', - value: formatBytes(99999999999999), - icon: 'uil:cloud-upload', - onClick: () => console.log('总上传流量 clicked'), - }, - { - title: '总下载流量', - value: formatBytes(99999999999999), - icon: 'uil:cloud-download', - onClick: () => console.log('总下载流量 clicked'), - }, - ].map((item, index) => ( - - - {item.title} - - - - - -
{item.value}
-
-
- ))} -
-
- - - - 收入统计 - - 今日 - 本月 - 总计 - - - - - - - } /> - } /> - - - - - - -
-
-
总收入
-
6,666
-
- -
-
- {IncomeStatisticsConfig.new_purchase.label} -
-
123
-
- -
-
- {IncomeStatisticsConfig.repurchase.label} -
-
456
-
-
-
-
- - - - - - { - return new Date(value).toLocaleDateString(locale, { - month: 'short', - day: 'numeric', - }); - }} - /> - - - } /> - } /> - - - - -
-
-
总收入
-
654,321
-
- -
-
- {IncomeStatisticsConfig.new_purchase.label} -
-
123
-
- -
-
- {IncomeStatisticsConfig.repurchase.label} -
-
456
-
-
-
-
- - - - - - { - return new Date(value).toLocaleDateString(locale, { - month: 'short', - }); - }} - /> - } - /> - - - } /> - - - - -
-
-
总收入
-
987,654,321
-
-
-
-
-
-
- - - - 用户统计 - - 今日 - 本月 - 总计 - - - - - - - } /> - } /> - - - - - - -
-
-
- {UserStatisticsConfig.register.label} -
-
789
-
- -
-
- {UserStatisticsConfig.new_purchase.label} -
-
123
-
- -
-
- {UserStatisticsConfig.repurchase.label} -
-
456
-
-
-
-
- - - - - - { - return new Date(value).toLocaleDateString(locale, { - month: 'short', - day: 'numeric', - }); - }} - /> - - - - } /> - } /> - - - - -
-
-
- {UserStatisticsConfig.register.label} -
-
789
-
- -
-
- {UserStatisticsConfig.new_purchase.label} -
-
123
-
- -
-
- {UserStatisticsConfig.repurchase.label} -
-
456
-
-
-
-
- - - - - - { - return new Date(value).toLocaleDateString(locale, { - month: 'short', - }); - }} - /> - } - /> - - - - } /> - - - - -
-
-
- {UserStatisticsConfig.register.label} -
-
987,654,321
-
-
-
-
-
-
- - - 流量排行 - - - 今日 - 昨日 - - - - -
-

{dataType === 'nodes' ? '节点流量' : '用户流量'}

- -
- - - - formatBytes(unitConversion('gbToBytes', value) || 0)} - /> - String(index + 1)} - /> - - dataType === 'nodes' ? `节点: ${label}` : `用户: ${label}` - } - /> - } - /> - - - - - -
-
-
-
- {[ - { - title: '广告位1', - value: '广告内容1', - onClick: () => console.log('广告位1 clicked'), - }, - { - title: '广告位2', - value: '广告内容2', - onClick: () => console.log('广告位2 clicked'), - }, - { - title: '广告位3', - value: '广告内容3', - onClick: () => console.log('广告位3 clicked'), - }, - { - title: '广告位4', - value: '广告内容4', - onClick: () => console.log('广告位4 clicked'), - }, - { - title: '广告位5', - value: '广告内容5', - onClick: () => console.log('广告位5 clicked'), - }, - { - title: '广告位6', - value: '广告内容6', - onClick: () => console.log('广告位6 clicked'), - }, - { - title: '广告位7', - value: '广告内容7', - onClick: () => console.log('广告位7 clicked'), - }, - { - title: '广告位8', - value: '广告内容8', - onClick: () => console.log('广告位8 clicked'), - }, - ].map((item, index) => ( - - - {item.title} - - -
{item.value}
-
-
- ))} -
+ +
); } diff --git a/apps/admin/app/dashboard/payment/page.tsx b/apps/admin/app/dashboard/payment/page.tsx index 0861778..4ba3a51 100644 --- a/apps/admin/app/dashboard/payment/page.tsx +++ b/apps/admin/app/dashboard/payment/page.tsx @@ -1,19 +1,11 @@ -import { PinContainer } from '@shadcn/ui/3d-pin'; -import { Label } from '@shadcn/ui/label'; -import { Separator } from '@shadcn/ui/separator'; +import Billing from '@/components/billing'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@shadcn/ui/tabs'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@shadcn/ui/tooltip'; -import { getTranslations } from 'next-intl/server'; -import Image from 'next/legacy/image'; -import Link from 'next/link'; import AlipayF2F from './alipayf2f'; import Epay from './epay'; import StripeAlipay from './stripe-alipay'; import StripeWeChatPay from './stripe-wechat-pay'; export default async function Page() { - const response = await (await fetch('https://pay.ppanel.dev/')).json(); - const t = await getTranslations('payment'); return ( <> @@ -36,46 +28,9 @@ export default async function Page() { - {response?.list?.length > 0 && ( - - -
- - {response.list?.map((item) => { - return ( - - - - - {item.name} - - - - -
-

- {item.name} -

-
- {item.description} -
-
- -
-
-
-
-
- ); - })} -
-
- )} +
+ +
); } diff --git a/apps/admin/components/billing.tsx b/apps/admin/components/billing.tsx new file mode 100644 index 0000000..a08ba0b --- /dev/null +++ b/apps/admin/components/billing.tsx @@ -0,0 +1,60 @@ +import { Avatar, AvatarFallback, AvatarImage } from '@shadcn/ui/avatar'; +import { Card, CardDescription, CardHeader, CardTitle } from '@shadcn/ui/card'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@shadcn/ui/tooltip'; +import { getLocale } from 'next-intl/server'; +import Image from 'next/legacy/image'; +import Link from 'next/link'; + +const BASE_URL = 'https://cdn.jsdelivr.net/gh/perfect-panel/ppanel-assets/billing/index.json'; + +interface BillingProps { + type: 'dashboard' | 'payment'; +} +export default async function Billing({ type }: BillingProps) { + const locale = await getLocale(); + const response = await fetch(BASE_URL, { cache: 'no-store' }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + + if (data[type].length === 0) return null; + return ( + +

+ {locale === 'en-US' ? 'Advertisement' : '广告合作'} + + {locale === 'en-US' + ? 'Ad revenue helps PPanel continue to release updates' + : '广告收入有助于 PPanel 继续发布更新'} + +

+
+ {data[type].map((item, index) => ( + + + + + + + + {item.title} + +
+ {item.title} + {item.description} +
+
+
+ +
+ +

{item.description}

+ +
+
+ ))} +
+
+ ); +} diff --git a/apps/admin/components/dashboard/statistics.tsx b/apps/admin/components/dashboard/statistics.tsx new file mode 100644 index 0000000..2266201 --- /dev/null +++ b/apps/admin/components/dashboard/statistics.tsx @@ -0,0 +1,703 @@ +'use client'; + +import { Icon } from '@iconify/react'; +import { formatBytes, unitConversion } from '@repo/ui/utils'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@shadcn/ui/card'; +import { + ChartConfig, + ChartContainer, + ChartLegend, + ChartLegendContent, + ChartTooltip, + ChartTooltipContent, +} from '@shadcn/ui/chart'; +import { + Area, + AreaChart, + Bar, + BarChart, + CartesianGrid, + Label, + LabelList, + Pie, + PieChart, + XAxis, + YAxis, +} from '@shadcn/ui/lib/recharts'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@shadcn/ui/select'; +import { Separator } from '@shadcn/ui/separator'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@shadcn/ui/tabs'; +import { useLocale } from 'next-intl'; +import { useState } from 'react'; + +const UserStatisticsConfig = { + register: { + label: '注册', + color: 'hsl(var(--chart-1))', + }, + new_purchase: { + label: '新购', + color: 'hsl(var(--chart-2))', + }, + repurchase: { + label: '复购', + color: 'hsl(var(--chart-3))', + }, +} satisfies ChartConfig; + +const IncomeStatisticsConfig = { + new_purchase: { + label: '新购', + color: 'hsl(var(--chart-1))', + }, + repurchase: { + label: '复购', + color: 'hsl(var(--chart-2))', + }, +}; + +// Sample data - replace with actual data +const trafficData = { + nodes: { + today: [ + { name: 'Node 1', traffic: 1000, type: 'Trojan', address: '127.0.0.1:443' }, + { name: 'Node 2', traffic: 800, type: 'Trojan', address: '127.0.0.1:444' }, + { name: 'Node 3', traffic: 600, type: 'Trojan', address: '127.0.0.1:445' }, + { name: 'Node 4', traffic: 400, type: 'Trojan', address: '127.0.0.1:446' }, + { name: 'Node 5', traffic: 200, type: 'Trojan', address: '127.0.0.1:447' }, + { name: 'Node 6', traffic: 1000, type: 'Trojan', address: '127.0.0.1:443' }, + { name: 'Node 7', traffic: 800, type: 'Trojan', address: '127.0.0.1:444' }, + { name: 'Node 8', traffic: 600, type: 'Trojan', address: '127.0.0.1:445' }, + { name: 'Node 9', traffic: 400, type: 'Trojan', address: '127.0.0.1:446' }, + { name: 'Node 10', traffic: 200, type: 'Trojan', address: '127.0.0.1:447' }, + ], + yesterday: [ + { name: 'Node 1', traffic: 900, type: 'Trojan', address: '127.0.0.1:443' }, + { name: 'Node 2', traffic: 750, type: 'Trojan', address: '127.0.0.1:444' }, + { name: 'Node 3', traffic: 550, type: 'Trojan', address: '127.0.0.1:445' }, + { name: 'Node 4', traffic: 350, type: 'Trojan', address: '127.0.0.1:446' }, + { name: 'Node 5', traffic: 150, type: 'Trojan', address: '127.0.0.1:447' }, + ], + }, + users: { + today: [ + { name: 'olivia.martin@email.com', traffic: 100, email: 'olivia.martin@email.com' }, + { name: 'jackson.lee@email.com', traffic: 90, email: 'jackson.lee@email.com' }, + { name: 'isabella.nguyen@email.com', traffic: 80, email: 'isabella.nguyen@email.com' }, + { name: 'william.chen@email.com', traffic: 70, email: 'william.chen@email.com' }, + { name: 'sophia.rodriguez@email.com', traffic: 60, email: 'sophia.rodriguez@email.com' }, + { name: 'olivia.martin@email.com', traffic: 100, email: 'olivia.martin@email.com' }, + { name: 'jackson.lee@email.com', traffic: 90, email: 'jackson.lee@email.com' }, + { name: 'isabella.nguyen@email.com', traffic: 80, email: 'isabella.nguyen@email.com' }, + { name: 'william.chen@email.com', traffic: 70, email: 'william.chen@email.com' }, + { name: 'sophia.rodriguez@email.com', traffic: 60, email: 'sophia.rodriguez@email.com' }, + ], + yesterday: [ + { name: 'olivia.martin@email.com', traffic: 95, email: 'olivia.martin@email.com' }, + { name: 'jackson.lee@email.com', traffic: 85, email: 'jackson.lee@email.com' }, + { name: 'isabella.nguyen@email.com', traffic: 75, email: 'isabella.nguyen@email.com' }, + { name: 'william.chen@email.com', traffic: 65, email: 'william.chen@email.com' }, + { name: 'sophia.rodriguez@email.com', traffic: 55, email: 'sophia.rodriguez@email.com' }, + ], + }, +}; + +export default function Statistics() { + const locale = useLocale(); + + const [dataType, setDataType] = useState('nodes'); + const [timeFrame, setTimeFrame] = useState('today'); + + const currentData = trafficData[dataType][timeFrame]; + return ( + <> +
+ {[ + { + title: '在线IP数', + value: '666', + icon: 'uil:network-wired', + onClick: () => console.log('在线IP数 clicked'), + }, + { + title: '在线节点数', + value: '99', + icon: 'uil:server-network', + onClick: () => console.log('在线节点数 clicked'), + }, + { + title: '离线节点数', + value: '1', + icon: 'uil:server-network-alt', + onClick: () => console.log('离线节点数 clicked'), + }, + { + title: '待处理工单', + value: '1', + icon: 'uil:clipboard-notes', + onClick: () => console.log('待处理工单 clicked'), + }, + { + title: '今日上传流量', + value: formatBytes(99999999999999), + icon: 'uil:arrow-up', + onClick: () => console.log('今日上传流量 clicked'), + }, + { + title: '今日下载流量', + value: formatBytes(99999999999999), + icon: 'uil:arrow-down', + onClick: () => console.log('今日下载流量 clicked'), + }, + { + title: '总上传流量', + value: formatBytes(99999999999999), + icon: 'uil:cloud-upload', + onClick: () => console.log('总上传流量 clicked'), + }, + { + title: '总下载流量', + value: formatBytes(99999999999999), + icon: 'uil:cloud-download', + onClick: () => console.log('总下载流量 clicked'), + }, + ].map((item, index) => ( + + + {item.title} + + + + + +
{item.value}
+
+
+ ))} +
+
+ + + + 收入统计 + + 今日 + 本月 + 总计 + + + + + + + } /> + } /> + + + + + + +
+
+
总收入
+
6,666
+
+ +
+
+ {IncomeStatisticsConfig.new_purchase.label} +
+
123
+
+ +
+
+ {IncomeStatisticsConfig.repurchase.label} +
+
456
+
+
+
+
+ + + + + + { + return new Date(value).toLocaleDateString(locale, { + month: 'short', + day: 'numeric', + }); + }} + /> + + + } /> + } /> + + + + +
+
+
总收入
+
654,321
+
+ +
+
+ {IncomeStatisticsConfig.new_purchase.label} +
+
123
+
+ +
+
+ {IncomeStatisticsConfig.repurchase.label} +
+
456
+
+
+
+
+ + + + + + { + return new Date(value).toLocaleDateString(locale, { + month: 'short', + }); + }} + /> + } + /> + + + } /> + + + + +
+
+
总收入
+
987,654,321
+
+
+
+
+
+
+ + + + 用户统计 + + 今日 + 本月 + 总计 + + + + + + + } /> + } /> + + + + + + +
+
+
+ {UserStatisticsConfig.register.label} +
+
789
+
+ +
+
+ {UserStatisticsConfig.new_purchase.label} +
+
123
+
+ +
+
+ {UserStatisticsConfig.repurchase.label} +
+
456
+
+
+
+
+ + + + + + { + return new Date(value).toLocaleDateString(locale, { + month: 'short', + day: 'numeric', + }); + }} + /> + + + + } /> + } /> + + + + +
+
+
+ {UserStatisticsConfig.register.label} +
+
789
+
+ +
+
+ {UserStatisticsConfig.new_purchase.label} +
+
123
+
+ +
+
+ {UserStatisticsConfig.repurchase.label} +
+
456
+
+
+
+
+ + + + + + { + return new Date(value).toLocaleDateString(locale, { + month: 'short', + }); + }} + /> + } + /> + + + + } /> + + + + +
+
+
+ {UserStatisticsConfig.register.label} +
+
987,654,321
+
+
+
+
+
+
+ + + 流量排行 + + + 今日 + 昨日 + + + + +
+

{dataType === 'nodes' ? '节点流量' : '用户流量'}

+ +
+ + + + formatBytes(unitConversion('gbToBytes', value) || 0)} + /> + String(index + 1)} + /> + + dataType === 'nodes' ? `节点: ${label}` : `用户: ${label}` + } + /> + } + /> + + + + + +
+
+
+ + ); +}