Loading panel/src/components/AppShell.tsx +2 −2 Changes for panel/src/components/AppShell.tsx: 2 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -121,8 +121,8 @@ export function AppShell() { <ThemeToggle /> </div> </header> <main className="flex-1 p-6"> <div className="mx-auto max-w-7xl space-y-7"> <main className="flex flex-1 flex-col p-6"> <div className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-6"> <Outlet /> </div> </main> Loading panel/src/components/Pagination.tsx 0 → 100644 +41 −0 Changes for panel/src/components/Pagination.tsx: 41 added lines, 0 removed lines. Original line number Diff line number Diff line import { ChevronLeft, ChevronRight } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; interface Props { page: number; pageSize: number; total: number; onChange: (page: number) => void; } export function Pagination({ page, pageSize, total, onChange }: Props) { const { t } = useTranslation("common"); const totalPages = Math.max(1, Math.ceil(total / pageSize)); return ( <div className="flex items-center justify-center gap-2 mt-auto"> <Button variant="outline" size="sm" disabled={page <= 1} onClick={() => onChange(page - 1)} > <ChevronLeft /> {t("pagination_prev")} </Button> <span className="text-xs text-muted-foreground min-w-[6rem] text-center"> {t("pagination_of", { current: page, total: totalPages })} </span> <Button variant="outline" size="sm" disabled={page >= totalPages} onClick={() => onChange(page + 1)} > {t("pagination_next")} <ChevronRight /> </Button> </div> ); } panel/src/i18n/resources/en-US/common.json +4 −1 Changes for panel/src/i18n/resources/en-US/common.json: 4 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -19,5 +19,8 @@ "lang_en": "EN", "lang_zh": "中文", "lang_toggle_hint": "Switch to {{lang}}", "toggle_theme": "Toggle theme" "toggle_theme": "Toggle theme", "pagination_prev": "Previous", "pagination_next": "Next", "pagination_of": "Page {{current}} of {{total}}" } panel/src/i18n/resources/zh-CN/common.json +4 −1 Changes for panel/src/i18n/resources/zh-CN/common.json: 4 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -19,5 +19,8 @@ "lang_en": "EN", "lang_zh": "中文", "lang_toggle_hint": "切换至 {{lang}}", "toggle_theme": "切换主题" "toggle_theme": "切换主题", "pagination_prev": "上一页", "pagination_next": "下一页", "pagination_of": "第 {{current}} / {{total}} 页" } panel/src/pages/MyServers.tsx +17 −4 Changes for panel/src/pages/MyServers.tsx: 17 added lines, 4 removed lines. Original line number Diff line number Diff line import { useState } from "react"; import { Server } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Card, CardContent } from "@/components/ui/card"; import { ServerCard } from "@/components/ServerCard"; import { Pagination } from "@/components/Pagination"; import { Loading, ErrorState, EmptyState } from "@/components/States"; import { api } from "@/lib/api"; import { useAsync, useConfig } from "@/lib/hooks"; // MyServers is the User-Side fleet: list / wake / stop / claim the servers the // caller owns or may claim. Creation lives on Admin-Side now (POST /servers is // admin-tier) — the platform provisions servers; users claim and operate them. const PAGE_SIZE = 12; export function MyServers() { const cfg = useConfig(); const { data, error, loading, reload } = useAsync(() => api.myServers(), []); const { t } = useTranslation("servers"); const servers = data ?? []; const [page, setPage] = useState(1); const totalPages = Math.max(1, Math.ceil(servers.length / PAGE_SIZE)); const paged = servers.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE); return ( <> Loading @@ -40,7 +44,7 @@ export function MyServers() { /> ) : ( <div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3"> {servers.map((s) => ( {paged.map((s) => ( <ServerCard key={s.name} server={s} cfg={cfg} onChanged={reload} /> ))} </div> Loading @@ -51,6 +55,15 @@ export function MyServers() { {t("my_servers_footer")} </CardContent> </Card> {servers.length > 0 && ( <Pagination page={page} pageSize={PAGE_SIZE} total={servers.length} onChange={(p) => { setPage(p); if (p > totalPages) setPage(totalPages); }} /> )} </> ); } Loading
panel/src/components/AppShell.tsx +2 −2 Changes for panel/src/components/AppShell.tsx: 2 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -121,8 +121,8 @@ export function AppShell() { <ThemeToggle /> </div> </header> <main className="flex-1 p-6"> <div className="mx-auto max-w-7xl space-y-7"> <main className="flex flex-1 flex-col p-6"> <div className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-6"> <Outlet /> </div> </main> Loading
panel/src/components/Pagination.tsx 0 → 100644 +41 −0 Changes for panel/src/components/Pagination.tsx: 41 added lines, 0 removed lines. Original line number Diff line number Diff line import { ChevronLeft, ChevronRight } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; interface Props { page: number; pageSize: number; total: number; onChange: (page: number) => void; } export function Pagination({ page, pageSize, total, onChange }: Props) { const { t } = useTranslation("common"); const totalPages = Math.max(1, Math.ceil(total / pageSize)); return ( <div className="flex items-center justify-center gap-2 mt-auto"> <Button variant="outline" size="sm" disabled={page <= 1} onClick={() => onChange(page - 1)} > <ChevronLeft /> {t("pagination_prev")} </Button> <span className="text-xs text-muted-foreground min-w-[6rem] text-center"> {t("pagination_of", { current: page, total: totalPages })} </span> <Button variant="outline" size="sm" disabled={page >= totalPages} onClick={() => onChange(page + 1)} > {t("pagination_next")} <ChevronRight /> </Button> </div> ); }
panel/src/i18n/resources/en-US/common.json +4 −1 Changes for panel/src/i18n/resources/en-US/common.json: 4 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -19,5 +19,8 @@ "lang_en": "EN", "lang_zh": "中文", "lang_toggle_hint": "Switch to {{lang}}", "toggle_theme": "Toggle theme" "toggle_theme": "Toggle theme", "pagination_prev": "Previous", "pagination_next": "Next", "pagination_of": "Page {{current}} of {{total}}" }
panel/src/i18n/resources/zh-CN/common.json +4 −1 Changes for panel/src/i18n/resources/zh-CN/common.json: 4 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -19,5 +19,8 @@ "lang_en": "EN", "lang_zh": "中文", "lang_toggle_hint": "切换至 {{lang}}", "toggle_theme": "切换主题" "toggle_theme": "切换主题", "pagination_prev": "上一页", "pagination_next": "下一页", "pagination_of": "第 {{current}} / {{total}} 页" }
panel/src/pages/MyServers.tsx +17 −4 Changes for panel/src/pages/MyServers.tsx: 17 added lines, 4 removed lines. Original line number Diff line number Diff line import { useState } from "react"; import { Server } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Card, CardContent } from "@/components/ui/card"; import { ServerCard } from "@/components/ServerCard"; import { Pagination } from "@/components/Pagination"; import { Loading, ErrorState, EmptyState } from "@/components/States"; import { api } from "@/lib/api"; import { useAsync, useConfig } from "@/lib/hooks"; // MyServers is the User-Side fleet: list / wake / stop / claim the servers the // caller owns or may claim. Creation lives on Admin-Side now (POST /servers is // admin-tier) — the platform provisions servers; users claim and operate them. const PAGE_SIZE = 12; export function MyServers() { const cfg = useConfig(); const { data, error, loading, reload } = useAsync(() => api.myServers(), []); const { t } = useTranslation("servers"); const servers = data ?? []; const [page, setPage] = useState(1); const totalPages = Math.max(1, Math.ceil(servers.length / PAGE_SIZE)); const paged = servers.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE); return ( <> Loading @@ -40,7 +44,7 @@ export function MyServers() { /> ) : ( <div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3"> {servers.map((s) => ( {paged.map((s) => ( <ServerCard key={s.name} server={s} cfg={cfg} onChanged={reload} /> ))} </div> Loading @@ -51,6 +55,15 @@ export function MyServers() { {t("my_servers_footer")} </CardContent> </Card> {servers.length > 0 && ( <Pagination page={page} pageSize={PAGE_SIZE} total={servers.length} onChange={(p) => { setPage(p); if (p > totalPages) setPage(totalPages); }} /> )} </> ); }