feat(panel): align my servers page search layout with fleet table
This commit is contained in:
3 files changed
+72
-64
No files matched your search
@@ -5,6 +5,8 @@
|
||||
"filter_status_all": "All statuses",
|
||||
"search_no_match": "No matching servers found.",
|
||||
"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_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.",
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
"filter_status_all": "全部状态",
|
||||
"search_no_match": "没有匹配的服务器。",
|
||||
"search_clear_btn": "清除筛选",
|
||||
"servers_count": "共 {{count}} 台",
|
||||
"servers_count_filtered": "显示 {{shown}} / {{total}} 台",
|
||||
"no_servers_linked": "暂无关联的服务器",
|
||||
"no_servers_hint": "认领一台你拥有访问权限的服务器,或联系管理员为你分配。",
|
||||
"my_servers_footer": "服务器及游戏类型由平台统一管理。认领后即可操作节点,原始集群配置不会暴露在此。",
|
||||
|
||||
@@ -45,71 +45,75 @@ export function MyServers() {
|
||||
const paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Server className="h-6 w-6 text-primary" />
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight">{t("my_servers_title")}</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("my_servers_subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<Server className="h-6 w-6 text-primary" />
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight">{t("my_servers_title")}</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("my_servers_subtitle")}
|
||||
</p>
|
||||
</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>
|
||||
|
||||
{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 />
|
||||
) : error ? (
|
||||
@@ -151,7 +155,7 @@ export function MyServers() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{filtered.length > 0 && (
|
||||
{totalPages > 1 && (
|
||||
<Pagination
|
||||
page={page}
|
||||
pageSize={PAGE_SIZE}
|
||||
@@ -159,6 +163,6 @@ export function MyServers() {
|
||||
onChange={(p) => { setPage(p); if (p > totalPages) setPage(totalPages); }}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user