From f3680a70200d716411b6219a2218c20c29b91e83 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Sat, 21 Dec 2024 00:10:09 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(announcement):=20Popup=20and?= =?UTF-8?q?=20pinned?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/app/dashboard/announcement/page.tsx | 49 ++- apps/admin/locales/cs-CZ/announcement.json | 2 + apps/admin/locales/de-DE/announcement.json | 2 + apps/admin/locales/en-US/announcement.json | 2 + apps/admin/locales/es-ES/announcement.json | 2 + apps/admin/locales/es-MX/announcement.json | 2 + apps/admin/locales/fi-FI/announcement.json | 2 + apps/admin/locales/fr-FR/announcement.json | 2 + apps/admin/locales/hi-IN/announcement.json | 2 + apps/admin/locales/hu-HU/announcement.json | 2 + apps/admin/locales/ja-JP/announcement.json | 2 + apps/admin/locales/ko-KR/announcement.json | 2 + apps/admin/locales/no-NO/announcement.json | 2 + apps/admin/locales/pl-PL/announcement.json | 2 + apps/admin/locales/pt-BR/announcement.json | 2 + apps/admin/locales/ro-RO/announcement.json | 2 + apps/admin/locales/ru-RU/announcement.json | 2 + apps/admin/locales/th-TH/announcement.json | 2 + apps/admin/locales/tr-TR/announcement.json | 2 + apps/admin/locales/uk-UA/announcement.json | 2 + apps/admin/locales/vi-VN/announcement.json | 2 + apps/admin/locales/zh-CN/announcement.json | 4 +- apps/admin/locales/zh-HK/announcement.json | 2 + apps/admin/openapi2ts.config.ts | 6 +- apps/admin/services/admin/announcement.ts | 15 - apps/admin/services/admin/typings.d.ts | 11 +- apps/admin/services/common/typings.d.ts | 6 +- .../(main)/(user)/dashboard/announcemnet.tsx | 36 --- .../app/(main)/(user)/dashboard/content.tsx | 281 +++++++++++++++++ .../user/app/(main)/(user)/dashboard/page.tsx | 283 +----------------- apps/user/app/(main)/(user)/layout.tsx | 3 + apps/user/components/announcement/index.tsx | 62 ++++ apps/user/openapi2ts.config.ts | 6 +- apps/user/services/common/typings.d.ts | 6 +- apps/user/services/user/typings.d.ts | 6 +- 35 files changed, 465 insertions(+), 351 deletions(-) delete mode 100644 apps/user/app/(main)/(user)/dashboard/announcemnet.tsx create mode 100644 apps/user/app/(main)/(user)/dashboard/content.tsx create mode 100644 apps/user/components/announcement/index.tsx diff --git a/apps/admin/app/dashboard/announcement/page.tsx b/apps/admin/app/dashboard/announcement/page.tsx index 652eca7..ded74d8 100644 --- a/apps/admin/app/dashboard/announcement/page.tsx +++ b/apps/admin/app/dashboard/announcement/page.tsx @@ -5,7 +5,6 @@ import { deleteAnnouncement, getAnnouncementList, updateAnnouncement, - updateAnnouncementEnable, } from '@/services/admin/announcement'; import { ConfirmButton } from '@repo/ui/confirm-button'; import { format } from '@shadcn/ui/lib/date-fns'; @@ -51,16 +50,52 @@ export default function Page() { }} columns={[ { - accessorKey: 'enable', - header: t('enable'), + accessorKey: 'show', + header: t('show'), cell: ({ row }) => { return ( { - await updateAnnouncementEnable({ - id: row.original.id, - enable: checked, + await updateAnnouncement({ + ...row.original, + show: checked, + }); + ref.current?.refresh(); + }} + /> + ); + }, + }, + { + accessorKey: 'pinned', + header: t('pinned'), + cell: ({ row }) => { + return ( + { + await updateAnnouncement({ + ...row.original, + pinned: checked, + }); + ref.current?.refresh(); + }} + /> + ); + }, + }, + { + accessorKey: 'popup', + header: t('popup'), + cell: ({ row }) => { + return ( + { + await updateAnnouncement({ + ...row.original, + popup: checked, }); ref.current?.refresh(); }} diff --git a/apps/admin/locales/cs-CZ/announcement.json b/apps/admin/locales/cs-CZ/announcement.json index bccfc24..0e56cbd 100644 --- a/apps/admin/locales/cs-CZ/announcement.json +++ b/apps/admin/locales/cs-CZ/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Zadejte" }, "hide": "skrýt", + "pinned": "Připnuto", + "popup": "vyskakovací okno", "show": "Zobrazit", "title": "Titul", "updateSuccess": "Aktualizace byla úspěšná", diff --git a/apps/admin/locales/de-DE/announcement.json b/apps/admin/locales/de-DE/announcement.json index 407ba6c..96ccff4 100644 --- a/apps/admin/locales/de-DE/announcement.json +++ b/apps/admin/locales/de-DE/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Bitte eingeben" }, "hide": "Verbergen", + "pinned": "Angeheftet", + "popup": "Popup", "show": "Anzeigen", "title": "Titel", "updateSuccess": "Aktualisierung erfolgreich", diff --git a/apps/admin/locales/en-US/announcement.json b/apps/admin/locales/en-US/announcement.json index 9d83367..49edeb6 100644 --- a/apps/admin/locales/en-US/announcement.json +++ b/apps/admin/locales/en-US/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Please enter" }, "hide": "Hide", + "pinned": "Pinned", + "popup": "Popup", "show": "Show", "title": "Title", "updateSuccess": "Update Success", diff --git a/apps/admin/locales/es-ES/announcement.json b/apps/admin/locales/es-ES/announcement.json index 0bb79f6..7596fd6 100644 --- a/apps/admin/locales/es-ES/announcement.json +++ b/apps/admin/locales/es-ES/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Por favor, introduce" }, "hide": "Ocultar", + "pinned": "Fijado", + "popup": "Ventana emergente", "show": "mostrar", "title": "Título", "updateSuccess": "Actualización exitosa", diff --git a/apps/admin/locales/es-MX/announcement.json b/apps/admin/locales/es-MX/announcement.json index adf114a..269354c 100644 --- a/apps/admin/locales/es-MX/announcement.json +++ b/apps/admin/locales/es-MX/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Por favor, ingresa" }, "hide": "Ocultar", + "pinned": "Fijado", + "popup": "ventana emergente", "show": "Mostrar", "title": "Título", "updateSuccess": "Actualización exitosa", diff --git a/apps/admin/locales/fi-FI/announcement.json b/apps/admin/locales/fi-FI/announcement.json index 55710c1..7fdfc6c 100644 --- a/apps/admin/locales/fi-FI/announcement.json +++ b/apps/admin/locales/fi-FI/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Syötä" }, "hide": "piilota", + "pinned": "Kiinnitetty", + "popup": "ponnahdusikkuna", "show": "Näytä", "title": "Otsikko", "updateSuccess": "Päivitys onnistui", diff --git a/apps/admin/locales/fr-FR/announcement.json b/apps/admin/locales/fr-FR/announcement.json index e42b4b8..98a3ba3 100644 --- a/apps/admin/locales/fr-FR/announcement.json +++ b/apps/admin/locales/fr-FR/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Veuillez entrer" }, "hide": "Masquer", + "pinned": "Épinglé", + "popup": "Fenêtre contextuelle", "show": "afficher", "title": "Titre", "updateSuccess": "Mise à jour réussie", diff --git a/apps/admin/locales/hi-IN/announcement.json b/apps/admin/locales/hi-IN/announcement.json index 9cffbc9..29ab06b 100644 --- a/apps/admin/locales/hi-IN/announcement.json +++ b/apps/admin/locales/hi-IN/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "कृपया दर्ज करें" }, "hide": "छिपाएं", + "pinned": "पिन किया हुआ", + "popup": "पॉपअप", "show": "प्रदर्शित करें", "title": "शीर्षक", "updateSuccess": "अपडेट सफल", diff --git a/apps/admin/locales/hu-HU/announcement.json b/apps/admin/locales/hu-HU/announcement.json index b4cb26c..7364f4d 100644 --- a/apps/admin/locales/hu-HU/announcement.json +++ b/apps/admin/locales/hu-HU/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Kérjük, adja meg" }, "hide": "elrejt", + "pinned": "Kiemelt", + "popup": "Felugró ablak", "show": "Megjelenítés", "title": "Cím", "updateSuccess": "Sikeres frissítés", diff --git a/apps/admin/locales/ja-JP/announcement.json b/apps/admin/locales/ja-JP/announcement.json index 0ebe27c..eca7fab 100644 --- a/apps/admin/locales/ja-JP/announcement.json +++ b/apps/admin/locales/ja-JP/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "入力してください" }, "hide": "隠す", + "pinned": "固定", + "popup": "ポップアップ", "show": "表示", "title": "タイトル", "updateSuccess": "更新が成功しました", diff --git a/apps/admin/locales/ko-KR/announcement.json b/apps/admin/locales/ko-KR/announcement.json index 3017bad..051a6ec 100644 --- a/apps/admin/locales/ko-KR/announcement.json +++ b/apps/admin/locales/ko-KR/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "입력하세요" }, "hide": "숨기기", + "pinned": "고정", + "popup": "팝업", "show": "표시", "title": "제목", "updateSuccess": "업데이트 성공", diff --git a/apps/admin/locales/no-NO/announcement.json b/apps/admin/locales/no-NO/announcement.json index a776353..b8bd9a4 100644 --- a/apps/admin/locales/no-NO/announcement.json +++ b/apps/admin/locales/no-NO/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Vennligst skriv inn" }, "hide": "skjul", + "pinned": "Festet", + "popup": "Popup", "show": "Vis", "title": "Tittel", "updateSuccess": "Oppdatering vellykket", diff --git a/apps/admin/locales/pl-PL/announcement.json b/apps/admin/locales/pl-PL/announcement.json index e8e1cd6..0e08403 100644 --- a/apps/admin/locales/pl-PL/announcement.json +++ b/apps/admin/locales/pl-PL/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Wprowadź" }, "hide": "ukryj", + "pinned": "Przypięte", + "popup": "wyskakujące okienko", "show": "Pokaż", "title": "Tytuł", "updateSuccess": "Aktualizacja zakończona pomyślnie", diff --git a/apps/admin/locales/pt-BR/announcement.json b/apps/admin/locales/pt-BR/announcement.json index 31eb0ed..da169c1 100644 --- a/apps/admin/locales/pt-BR/announcement.json +++ b/apps/admin/locales/pt-BR/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Por favor, insira" }, "hide": "ocultar", + "pinned": "Fixado", + "popup": "Janela pop-up", "show": "mostrar", "title": "Título", "updateSuccess": "Atualização bem-sucedida", diff --git a/apps/admin/locales/ro-RO/announcement.json b/apps/admin/locales/ro-RO/announcement.json index c8b42c3..94b77b8 100644 --- a/apps/admin/locales/ro-RO/announcement.json +++ b/apps/admin/locales/ro-RO/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Introduceți" }, "hide": "ascunde", + "pinned": "Fixat", + "popup": "fereastră pop-up", "show": "afișare", "title": "Titlu", "updateSuccess": "Actualizare reușită", diff --git a/apps/admin/locales/ru-RU/announcement.json b/apps/admin/locales/ru-RU/announcement.json index 5567049..01b7d8c 100644 --- a/apps/admin/locales/ru-RU/announcement.json +++ b/apps/admin/locales/ru-RU/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Пожалуйста, введите" }, "hide": "скрыть", + "pinned": "Закреплено", + "popup": "всплывающее окно", "show": "Показать", "title": "Заголовок", "updateSuccess": "Обновление успешно", diff --git a/apps/admin/locales/th-TH/announcement.json b/apps/admin/locales/th-TH/announcement.json index ab0f135..a733209 100644 --- a/apps/admin/locales/th-TH/announcement.json +++ b/apps/admin/locales/th-TH/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "กรุณาใส่" }, "hide": "ซ่อน", + "pinned": "ปักหมุด", + "popup": "ป๊อปอัพ", "show": "แสดง", "title": "หัวข้อ", "updateSuccess": "อัปเดตสำเร็จ", diff --git a/apps/admin/locales/tr-TR/announcement.json b/apps/admin/locales/tr-TR/announcement.json index 97752d5..2bc1d73 100644 --- a/apps/admin/locales/tr-TR/announcement.json +++ b/apps/admin/locales/tr-TR/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Lütfen giriniz" }, "hide": "gizle", + "pinned": "Sabitlenmiş", + "popup": "açılır pencere", "show": "göster", "title": "Başlık", "updateSuccess": "Güncelleme başarılı", diff --git a/apps/admin/locales/uk-UA/announcement.json b/apps/admin/locales/uk-UA/announcement.json index 1650484..bebcaf2 100644 --- a/apps/admin/locales/uk-UA/announcement.json +++ b/apps/admin/locales/uk-UA/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Будь ласка, введіть" }, "hide": "приховати", + "pinned": "Закріплено", + "popup": "спливаюче вікно", "show": "Показати", "title": "Заголовок", "updateSuccess": "Оновлення успішне", diff --git a/apps/admin/locales/vi-VN/announcement.json b/apps/admin/locales/vi-VN/announcement.json index 0e29a7c..e928c53 100644 --- a/apps/admin/locales/vi-VN/announcement.json +++ b/apps/admin/locales/vi-VN/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "Vui lòng nhập" }, "hide": "Ẩn", + "pinned": "Ghim", + "popup": "Cửa sổ bật lên", "show": "Hiển thị", "title": "Tiêu đề", "updateSuccess": "Cập nhật thành công", diff --git a/apps/admin/locales/zh-CN/announcement.json b/apps/admin/locales/zh-CN/announcement.json index eb3eb9b..736a0e2 100644 --- a/apps/admin/locales/zh-CN/announcement.json +++ b/apps/admin/locales/zh-CN/announcement.json @@ -13,7 +13,7 @@ "deleteSuccess": "删除成功", "edit": "编辑", "editAnnouncement": "编辑公告", - "enable": "启用", + "enable": "是否显示", "form": { "cancel": "取消", "confirm": "确认", @@ -23,6 +23,8 @@ "titlePlaceholder": "请输入" }, "hide": "隐藏", + "pinned": "置顶", + "popup": "弹窗", "show": "显示", "title": "标题", "updateSuccess": "更新成功", diff --git a/apps/admin/locales/zh-HK/announcement.json b/apps/admin/locales/zh-HK/announcement.json index 3c62a63..81c90bc 100644 --- a/apps/admin/locales/zh-HK/announcement.json +++ b/apps/admin/locales/zh-HK/announcement.json @@ -23,6 +23,8 @@ "titlePlaceholder": "請輸入" }, "hide": "隱藏", + "pinned": "置頂", + "popup": "彈窗", "show": "顯示", "title": "標題", "updateSuccess": "更新成功", diff --git a/apps/admin/openapi2ts.config.ts b/apps/admin/openapi2ts.config.ts index 84c6a3c..2288ec7 100644 --- a/apps/admin/openapi2ts.config.ts +++ b/apps/admin/openapi2ts.config.ts @@ -1,13 +1,15 @@ const config = [ { requestLibPath: "import request from '@/utils/request';", - schemaPath: 'https://docs.ppanel.dev/swagger/common.json', + schemaPath: + 'https://raw.githubusercontent.com/perfect-panel/ppanel-docs/refs/heads/main/public/swagger/common.json', serversPath: './services', projectName: 'common', }, { requestLibPath: "import request from '@/utils/request';", - schemaPath: 'https://docs.ppanel.dev/swagger/admin.json', + schemaPath: + 'https://raw.githubusercontent.com/perfect-panel/ppanel-docs/refs/heads/main/public/swagger/admin.json', serversPath: './services', projectName: 'admin', }, diff --git a/apps/admin/services/admin/announcement.ts b/apps/admin/services/admin/announcement.ts index 4f87a03..bc1e079 100644 --- a/apps/admin/services/admin/announcement.ts +++ b/apps/admin/services/admin/announcement.ts @@ -62,21 +62,6 @@ export async function getAnnouncement( }); } -/** Update announcement enable PUT /v1/admin/announcement/enable */ -export async function updateAnnouncementEnable( - body: API.UpdateAnnouncementEnableRequest, - options?: { [key: string]: any }, -) { - return request('/v1/admin/announcement/enable', { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - data: body, - ...(options || {}), - }); -} - /** Get announcement list GET /v1/admin/announcement/list */ export async function getAnnouncementList( // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) diff --git a/apps/admin/services/admin/typings.d.ts b/apps/admin/services/admin/typings.d.ts index 3b812cc..1af2005 100644 --- a/apps/admin/services/admin/typings.d.ts +++ b/apps/admin/services/admin/typings.d.ts @@ -11,8 +11,9 @@ declare namespace API { id: number; title: string; content: string; - enable: boolean; - type: number; + show: boolean; + pinned: boolean; + popup: boolean; created_at: number; updated_at: number; }; @@ -82,7 +83,6 @@ declare namespace API { type CreateAnnouncementRequest = { title: string; content: string; - type: number; }; type CreateApplicationRequest = { @@ -823,8 +823,9 @@ declare namespace API { id: number; title: string; content: string; - enable: boolean; - type: number; + show: boolean; + pinned: boolean; + popup: boolean; }; type UpdateApplicationRequest = { diff --git a/apps/admin/services/common/typings.d.ts b/apps/admin/services/common/typings.d.ts index 2bb8e0d..052f974 100644 --- a/apps/admin/services/common/typings.d.ts +++ b/apps/admin/services/common/typings.d.ts @@ -3,8 +3,9 @@ declare namespace API { id: number; title: string; content: string; - enable: boolean; - type: number; + show: boolean; + pinned: boolean; + popup: boolean; created_at: number; updated_at: number; }; @@ -321,6 +322,7 @@ declare namespace API { server: number[]; show: boolean; sell: boolean; + sort: number; created_at: number; updated_at: number; }; diff --git a/apps/user/app/(main)/(user)/dashboard/announcemnet.tsx b/apps/user/app/(main)/(user)/dashboard/announcemnet.tsx deleted file mode 100644 index b78b666..0000000 --- a/apps/user/app/(main)/(user)/dashboard/announcemnet.tsx +++ /dev/null @@ -1,36 +0,0 @@ -'use client'; - -import { Empty } from '@/components/empty'; -import { queryAnnouncement } from '@/services/user/announcement'; -import { Icon } from '@iconify/react'; -import { Markdown } from '@repo/ui/markdown'; -import { Card } from '@shadcn/ui/card'; -import { useQuery } from '@tanstack/react-query'; -import { useTranslations } from 'next-intl'; - -export default function Announcement() { - const t = useTranslations('dashboard'); - - const { data } = useQuery({ - queryKey: ['queryAnnouncement', 1], - queryFn: async () => { - const { data } = await queryAnnouncement({ - page: 1, - size: 1, - }); - return (data.data?.announcements?.[0] as API.Announcement) || {}; - }, - }); - - return ( - <> -

- - {t('latestAnnouncement')} -

- - {data?.content ? {data?.content} : } - - - ); -} diff --git a/apps/user/app/(main)/(user)/dashboard/content.tsx b/apps/user/app/(main)/(user)/dashboard/content.tsx new file mode 100644 index 0000000..8ff1e83 --- /dev/null +++ b/apps/user/app/(main)/(user)/dashboard/content.tsx @@ -0,0 +1,281 @@ +'use client'; + +import { Display } from '@/components/display'; +import { queryApplicationConfig } from '@/services/user/subscribe'; +import { queryUserSubscribe } from '@/services/user/user'; +import { Icon } from '@iconify/react'; +import { getNextResetDate, isBrowser } from '@repo/ui/utils'; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@shadcn/ui/accordion'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@shadcn/ui/alert-dialog'; +import { Button } from '@shadcn/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@shadcn/ui/card'; +import { differenceInDays } from '@shadcn/ui/lib/date-fns'; +import { toast } from '@shadcn/ui/lib/sonner'; +import { Separator } from '@shadcn/ui/separator'; +import { Tabs, TabsList, TabsTrigger } from '@shadcn/ui/tabs'; +import { useQuery } from '@tanstack/react-query'; +import { useTranslations } from 'next-intl'; +import Image from 'next/image'; +import Link from 'next/link'; +import { QRCodeCanvas } from 'qrcode.react'; +import { useState } from 'react'; + +import useGlobalStore from '@/config/use-global'; +import { getStat } from '@/services/common/common'; +import { getPlatform } from '@/utils/common'; +import CopyToClipboard from 'react-copy-to-clipboard'; +import Renewal from '../order/renewal'; +import ResetTraffic from '../order/reset-traffic'; +import Subscribe from '../subscribe/page'; + +export default function Content() { + const t = useTranslations('dashboard'); + const { getUserSubscribe, getAppSubLink } = useGlobalStore(); + + const [protocol, setProtocol] = useState(''); + + const { data: userSubscribe = [] } = useQuery({ + queryKey: ['queryUserSubscribe'], + queryFn: async () => { + const { data } = await queryUserSubscribe(); + return data.data?.list || []; + }, + }); + const { data: application } = useQuery({ + queryKey: ['queryApplicationConfig'], + queryFn: async () => { + const { data } = await queryApplicationConfig(); + return data.data as API.ApplicationResponse; + }, + }); + const [platform, setPlatform] = useState(getPlatform()); + + const { data } = useQuery({ + queryKey: ['getStat'], + queryFn: async () => { + const { data } = await getStat({ + skipErrorHandler: true, + }); + return data.data; + }, + refetchOnWindowFocus: false, + }); + + return ( + <> + {userSubscribe.length ? ( + <> +

+ + {t('mySubscriptions')} +

+
+ setPlatform(value as keyof API.ApplicationResponse)} + className='w-full max-w-full md:w-auto' + > + + {application && + Object.keys(application)?.map((item) => ( + + {item} + + ))} + + + {data?.protocol && data?.protocol.length > 1 && ( + + + {['all', ...(data?.protocol || [])].map((item) => ( + + {item} + + ))} + + + )} +
+ {userSubscribe.map((item) => ( + + + {item.subscribe.name} +
+ + + + + + + {t('prompt')} + + {t('confirmResetSubscription')} + + + + {t('cancel')} + toast.success(t('resetSuccess'))}> + {t('confirm')} + + + + + + +
+
+ +
    +
  • + {t('used')} + + + +
  • +
  • + {t('totalTraffic')} + + + +
  • +
  • + {t('nextResetDays')} + + {differenceInDays(getNextResetDate(item.start_time), new Date()) || + t('unknown')} + +
  • +
  • + {t('expirationDays')} + + {differenceInDays(new Date(item.expire_time), new Date()) || t('unknown')} + +
  • +
+ + + {getUserSubscribe(item.token, protocol)?.map((url, index) => ( + + +
+ + {t('subscriptionUrl')} {index + 1} + + { + if (result) { + toast.success(t('copySuccess')); + } + }} + > + { + e.stopPropagation(); + }} + > + + {t('copy')} + + +
+
+ +
+ {application && + application[platform]?.map((app) => ( +
+ {app.name} + {app.icon && ( + {app.name} + )} +
+ + { + const href = getAppSubLink(app.subscribe_type, url); + if (isBrowser() && href) { + window.location.href = href; + } else if (result) { + toast.success( + <> +

{t('copySuccess')}

+

{t('manualImportMessage')}

+ , + ); + } + }} + > + +
+
+
+ ))} +
+ {t('qrCode')} + + {t('scanToSubscribe')} +
+
+
+
+ ))} +
+
+
+ ))} + + ) : ( + <> +

+ + {t('purchaseSubscription')} +

+ + + )} + + ); +} diff --git a/apps/user/app/(main)/(user)/dashboard/page.tsx b/apps/user/app/(main)/(user)/dashboard/page.tsx index f7a884b..32f025f 100644 --- a/apps/user/app/(main)/(user)/dashboard/page.tsx +++ b/apps/user/app/(main)/(user)/dashboard/page.tsx @@ -1,283 +1,12 @@ -'use client'; - -import { Display } from '@/components/display'; -import { queryApplicationConfig } from '@/services/user/subscribe'; -import { queryUserSubscribe } from '@/services/user/user'; -import { Icon } from '@iconify/react'; -import { getNextResetDate, isBrowser } from '@repo/ui/utils'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@shadcn/ui/accordion'; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from '@shadcn/ui/alert-dialog'; -import { Button } from '@shadcn/ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from '@shadcn/ui/card'; -import { differenceInDays } from '@shadcn/ui/lib/date-fns'; -import { toast } from '@shadcn/ui/lib/sonner'; -import { Separator } from '@shadcn/ui/separator'; -import { Tabs, TabsList, TabsTrigger } from '@shadcn/ui/tabs'; -import { useQuery } from '@tanstack/react-query'; -import { useTranslations } from 'next-intl'; -import Image from 'next/image'; -import Link from 'next/link'; -import { QRCodeCanvas } from 'qrcode.react'; -import { useState } from 'react'; - -import useGlobalStore from '@/config/use-global'; -import { getStat } from '@/services/common/common'; -import { getPlatform } from '@/utils/common'; -import CopyToClipboard from 'react-copy-to-clipboard'; -import Renewal from '../order/renewal'; -import ResetTraffic from '../order/reset-traffic'; -import Subscribe from '../subscribe/page'; -import Announcement from './announcemnet'; - -export default function Page() { - const t = useTranslations('dashboard'); - const { getUserSubscribe, getAppSubLink } = useGlobalStore(); - - const [protocol, setProtocol] = useState(''); - - const { data: userSubscribe = [] } = useQuery({ - queryKey: ['queryUserSubscribe'], - queryFn: async () => { - const { data } = await queryUserSubscribe(); - return data.data?.list || []; - }, - }); - const { data: application } = useQuery({ - queryKey: ['queryApplicationConfig'], - queryFn: async () => { - const { data } = await queryApplicationConfig(); - return data.data as API.ApplicationResponse; - }, - }); - const [platform, setPlatform] = useState(getPlatform()); - - const { data } = useQuery({ - queryKey: ['getStat'], - queryFn: async () => { - const { data } = await getStat({ - skipErrorHandler: true, - }); - return data.data; - }, - refetchOnWindowFocus: false, - }); +import Announcement from '@/components/announcement'; +import { cookies } from 'next/headers'; +import Content from './content'; +export default async function Page() { return (
- - {userSubscribe.length ? ( - <> -

- - {t('mySubscriptions')} -

-
- setPlatform(value as keyof API.ApplicationResponse)} - className='w-full max-w-full md:w-auto' - > - - {application && - Object.keys(application)?.map((item) => ( - - {item} - - ))} - - - {data?.protocol && data?.protocol.length > 1 && ( - - - {['all', ...(data?.protocol || [])].map((item) => ( - - {item} - - ))} - - - )} -
- {userSubscribe.map((item) => ( - - - {item.subscribe.name} -
- - - - - - - {t('prompt')} - - {t('confirmResetSubscription')} - - - - {t('cancel')} - toast.success(t('resetSuccess'))}> - {t('confirm')} - - - - - - -
-
- -
    -
  • - {t('used')} - - - -
  • -
  • - {t('totalTraffic')} - - - -
  • -
  • - {t('nextResetDays')} - - {differenceInDays(getNextResetDate(item.start_time), new Date()) || - t('unknown')} - -
  • -
  • - {t('expirationDays')} - - {differenceInDays(new Date(item.expire_time), new Date()) || t('unknown')} - -
  • -
- - - {getUserSubscribe(item.token, protocol)?.map((url, index) => ( - - -
- - {t('subscriptionUrl')} {index + 1} - - { - if (result) { - toast.success(t('copySuccess')); - } - }} - > - { - e.stopPropagation(); - }} - > - - {t('copy')} - - -
-
- -
- {application && - application[platform]?.map((app) => ( -
- {app.name} - {app.icon && ( - {app.name} - )} -
- - { - const href = getAppSubLink(app.subscribe_type, url); - if (isBrowser() && href) { - window.location.href = href; - } else if (result) { - toast.success( - <> -

{t('copySuccess')}

-

{t('manualImportMessage')}

- , - ); - } - }} - > - -
-
-
- ))} -
- {t('qrCode')} - - {t('scanToSubscribe')} -
-
-
-
- ))} -
-
-
- ))} - - ) : ( - <> -

- - {t('purchaseSubscription')} -

- - - )} + +
); } diff --git a/apps/user/app/(main)/(user)/layout.tsx b/apps/user/app/(main)/(user)/layout.tsx index f908775..f70f2cf 100644 --- a/apps/user/app/(main)/(user)/layout.tsx +++ b/apps/user/app/(main)/(user)/layout.tsx @@ -1,4 +1,6 @@ +import Announcement from '@/components/announcement'; import { SidebarInset, SidebarProvider } from '@shadcn/ui/sidebar'; +import { cookies } from 'next/headers'; import { SidebarLeft } from './sidebar-left'; import { SidebarRight } from './sidebar-right'; @@ -8,6 +10,7 @@ export default async function DashboardLayout({ children }: { children: React.Re {children} + ); } diff --git a/apps/user/components/announcement/index.tsx b/apps/user/components/announcement/index.tsx new file mode 100644 index 0000000..4dc6abf --- /dev/null +++ b/apps/user/components/announcement/index.tsx @@ -0,0 +1,62 @@ +import { queryAnnouncement } from '@/services/user/announcement'; +import { Icon } from '@iconify/react'; +import { Markdown } from '@repo/ui/markdown'; +import { Card } from '@shadcn/ui/card'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@shadcn/ui/dialog'; +import { getTranslations } from 'next-intl/server'; +import { Empty } from '../empty'; + +export default async function Announcement({ + type, + Authorization, +}: { + type: 'popup' | 'pinned'; + Authorization?: string; +}) { + let data; + try { + data = await queryAnnouncement( + { + page: 1, + size: 50, + }, + { + skipErrorHandler: true, + Authorization, + }, + ).then((res) => { + return res.data.data?.announcements.find((item) => item[type]); + }); + } catch (error) { + /* empty */ + } + if (!data) return null; + + const t = await getTranslations('dashboard'); + + if (type === 'popup') { + return ( + + + + {data?.title} + + {data?.content} + + + ); + } + if (type === 'pinned') { + return ( + <> +

+ + {t('latestAnnouncement')} +

+ + {data?.content ? {data?.content} : } + + + ); + } +} diff --git a/apps/user/openapi2ts.config.ts b/apps/user/openapi2ts.config.ts index b34bf7f..07075cc 100644 --- a/apps/user/openapi2ts.config.ts +++ b/apps/user/openapi2ts.config.ts @@ -1,13 +1,15 @@ const config = [ { requestLibPath: "import request from '@/utils/request';", - schemaPath: 'https://docs.ppanel.dev/swagger/common.json', + schemaPath: + 'https://raw.githubusercontent.com/perfect-panel/ppanel-docs/refs/heads/main/public/swagger/common.json', serversPath: './services', projectName: 'common', }, { requestLibPath: "import request from '@/utils/request';", - schemaPath: 'https://docs.ppanel.dev/swagger/user.json', + schemaPath: + 'https://raw.githubusercontent.com/perfect-panel/ppanel-docs/refs/heads/main/public/swagger/user.json', serversPath: './services', projectName: 'user', }, diff --git a/apps/user/services/common/typings.d.ts b/apps/user/services/common/typings.d.ts index 2bb8e0d..052f974 100644 --- a/apps/user/services/common/typings.d.ts +++ b/apps/user/services/common/typings.d.ts @@ -3,8 +3,9 @@ declare namespace API { id: number; title: string; content: string; - enable: boolean; - type: number; + show: boolean; + pinned: boolean; + popup: boolean; created_at: number; updated_at: number; }; @@ -321,6 +322,7 @@ declare namespace API { server: number[]; show: boolean; sell: boolean; + sort: number; created_at: number; updated_at: number; }; diff --git a/apps/user/services/user/typings.d.ts b/apps/user/services/user/typings.d.ts index 5e1a02c..4c69f5b 100644 --- a/apps/user/services/user/typings.d.ts +++ b/apps/user/services/user/typings.d.ts @@ -3,8 +3,9 @@ declare namespace API { id: number; title: string; content: string; - enable: boolean; - type: number; + show: boolean; + pinned: boolean; + popup: boolean; created_at: number; updated_at: number; }; @@ -460,6 +461,7 @@ declare namespace API { server: number[]; show: boolean; sell: boolean; + sort: number; created_at: number; updated_at: number; };