feat(panel): align my servers page search layout with fleet table

This commit is contained in:
Lemon-miaow committed 2026-07-02 03:30:50 +08:00
1 parent fa7bab5ec1
commit 70d17a0d57
3 files changed
+72 -64

No files matched your search

@@ -5,6 +5,8 @@
"filter_status_all": "All statuses", "filter_status_all": "All statuses",
"search_no_match": "No matching servers found.", "search_no_match": "No matching servers found.",
"search_clear_btn": "Clear filters", "search_clear_btn": "Clear filters",
"servers_count": "{{count}} servers",
"servers_count_filtered": "Showing {{shown}} / {{total}} servers",
"no_servers_linked": "No servers linked to you", "no_servers_linked": "No servers linked to you",
"no_servers_hint": "Claim an unowned server you have access to, or ask an admin to provision one.", "no_servers_hint": "Claim an unowned server you have access to, or ask an admin to provision one.",
"my_servers_footer": "Servers and game types are provisioned and managed by the platform. You claim a node to operate it; raw cluster config is never exposed here.", "my_servers_footer": "Servers and game types are provisioned and managed by the platform. You claim a node to operate it; raw cluster config is never exposed here.",
@@ -5,6 +5,8 @@
"filter_status_all": "全部状态", "filter_status_all": "全部状态",
"search_no_match": "没有匹配的服务器。", "search_no_match": "没有匹配的服务器。",
"search_clear_btn": "清除筛选", "search_clear_btn": "清除筛选",
"servers_count": "共 {{count}} 台",
"servers_count_filtered": "显示 {{shown}} / {{total}} 台",
"no_servers_linked": "暂无关联的服务器", "no_servers_linked": "暂无关联的服务器",
"no_servers_hint": "认领一台你拥有访问权限的服务器,或联系管理员为你分配。", "no_servers_hint": "认领一台你拥有访问权限的服务器,或联系管理员为你分配。",
"my_servers_footer": "服务器及游戏类型由平台统一管理。认领后即可操作节点,原始集群配置不会暴露在此。", "my_servers_footer": "服务器及游戏类型由平台统一管理。认领后即可操作节点,原始集群配置不会暴露在此。",
+68 -64
View File
@@ -45,71 +45,75 @@ export function MyServers() {
const paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE); const paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
return ( return (
<> <div className="space-y-6">
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between"> <div className="flex items-center gap-3">
<div className="flex items-center gap-3"> <Server className="h-6 w-6 text-primary" />
<Server className="h-6 w-6 text-primary" /> <div>
<div> <h1 className="text-2xl font-semibold tracking-tight">{t("my_servers_title")}</h1>
<h1 className="text-2xl font-semibold tracking-tight">{t("my_servers_title")}</h1> <p className="text-sm text-muted-foreground">
<p className="text-sm text-muted-foreground"> {t("my_servers_subtitle")}
{t("my_servers_subtitle")} </p>
</p>
</div>
</div> </div>
{servers.length > 0 && cfg && (
<div className="flex flex-col gap-2 sm:flex-row sm:items-center w-full md:w-auto">
<div className="relative w-full sm:w-64">
<Search className="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder={t("search_placeholder")}
className="h-8 pl-8 pr-8 text-sm"
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
setPage(1);
}}
autoComplete="off"
spellCheck={false}
/>
{searchQuery && (
<button
type="button"
onClick={() => {
setSearchQuery("");
setPage(1);
}}
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
>
<X className="h-3.5 w-3.5" />
</button>
)}
</div>
<div className="w-full sm:w-36">
<Select
value={statusFilter}
onValueChange={(val) => {
setStatusFilter(val);
setPage(1);
}}
>
<SelectTrigger className="h-8 text-sm">
<SelectValue placeholder={t("filter_status_all")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">{t("filter_status_all")}</SelectItem>
<SelectItem value="Running">{t("phase_running")}</SelectItem>
<SelectItem value="Starting">{t("phase_starting")}</SelectItem>
<SelectItem value="Stopping">{t("phase_stopping")}</SelectItem>
<SelectItem value="Stopped">{t("phase_stopped")}</SelectItem>
<SelectItem value="Failed">{t("phase_failed")}</SelectItem>
</SelectContent>
</Select>
</div>
</div>
)}
</div> </div>
{servers.length > 0 && cfg && (
<div className="flex flex-wrap items-center gap-3">
<div className="relative min-w-[14rem] flex-1">
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder={t("search_placeholder")}
className="pl-9 pr-9"
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
setPage(1);
}}
autoComplete="off"
spellCheck={false}
/>
{searchQuery && (
<button
type="button"
onClick={() => {
setSearchQuery("");
setPage(1);
}}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
>
<X className="h-4 w-4" />
</button>
)}
</div>
<Select
value={statusFilter}
onValueChange={(val) => {
setStatusFilter(val);
setPage(1);
}}
>
<SelectTrigger className="w-44">
<SelectValue placeholder={t("filter_status_all")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">{t("filter_status_all")}</SelectItem>
<SelectItem value="Running">{t("phase_running")}</SelectItem>
<SelectItem value="Starting">{t("phase_starting")}</SelectItem>
<SelectItem value="Stopping">{t("phase_stopping")}</SelectItem>
<SelectItem value="Stopped">{t("phase_stopped")}</SelectItem>
<SelectItem value="Failed">{t("phase_failed")}</SelectItem>
</SelectContent>
</Select>
<span className="shrink-0 text-xs text-muted-foreground">
{filtered.length === servers.length
? t("servers_count", { count: servers.length })
: t("servers_count_filtered", {
shown: filtered.length,
total: servers.length,
})}
</span>
</div>
)}
{loading && !data ? ( {loading && !data ? (
<Loading /> <Loading />
) : error ? ( ) : error ? (
@@ -151,7 +155,7 @@ export function MyServers() {
</CardContent> </CardContent>
</Card> </Card>
{filtered.length > 0 && ( {totalPages > 1 && (
<Pagination <Pagination
page={page} page={page}
pageSize={PAGE_SIZE} pageSize={PAGE_SIZE}
@@ -159,6 +163,6 @@ export function MyServers() {
onChange={(p) => { setPage(p); if (p > totalPages) setPage(totalPages); }} onChange={(p) => { setPage(p); if (p > totalPages) setPage(totalPages); }}
/> />
)} )}
</> </div>
); );
} }