feat(servers): support claiming servers directly from ServersPage list

This commit is contained in:
Lemon-miaow committed 2026-07-03 02:01:06 +08:00
1 parent 73d6ec10d0
commit 5427bc7623
1 file changed
+28 -4
+28 -4
View File
@@ -12,6 +12,7 @@ import {
Users, Users,
AlertTriangle, AlertTriangle,
UserRound, UserRound,
Hand,
} from "lucide-react"; } from "lucide-react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
@@ -108,7 +109,7 @@ function Stat({
export function ServersPage() { export function ServersPage() {
const { t } = useTranslation(["ops", "servers"]); const { t } = useTranslation(["ops", "servers"]);
const { isAdmin } = useTier(); const { isAdmin, identity } = useTier();
const cfg = useConfig(); const cfg = useConfig();
const fetchFn = useMemo(() => (isAdmin ? api.fleet : api.myServers), [isAdmin]); const fetchFn = useMemo(() => (isAdmin ? api.fleet : api.myServers), [isAdmin]);
@@ -146,6 +147,8 @@ export function ServersPage() {
playersMax: s.playersMax, playersMax: s.playersMax,
owner: s.owner, owner: s.owner,
endpointAddress: s.endpointAddress, endpointAddress: s.endpointAddress,
claimable: !s.owner,
owned: s.owner === identity?.email,
})); }));
} else { } else {
return (data as ServerInfo[]).map((s) => ({ return (data as ServerInfo[]).map((s) => ({
@@ -162,7 +165,7 @@ export function ServersPage() {
owned: s.owned, owned: s.owned,
})); }));
} }
}, [data, isAdmin, t]); }, [data, isAdmin, t, identity]);
const stats = useMemo(() => { const stats = useMemo(() => {
const counts: Record<Phase, number> = { const counts: Record<Phase, number> = {
@@ -429,10 +432,10 @@ function ServerRow({
}) { }) {
const { t } = useTranslation("ops"); const { t } = useTranslation("ops");
const { t: ts } = useTranslation("servers"); const { t: ts } = useTranslation("servers");
const [busy, setBusy] = useState<null | "wake" | "stop">(null); const [busy, setBusy] = useState<null | "wake" | "stop" | "claim">(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
async function act(kind: "wake" | "stop", fn: () => Promise<unknown>) { async function act(kind: "wake" | "stop" | "claim", fn: () => Promise<unknown>) {
setBusy(kind); setBusy(kind);
setError(null); setError(null);
try { try {
@@ -514,6 +517,23 @@ function ServerRow({
</td> </td>
<td className="px-4 py-3 align-middle"> <td className="px-4 py-3 align-middle">
<div className="flex items-center justify-end gap-2"> <div className="flex items-center justify-end gap-2">
{server.claimable && !server.owned ? (
<>
<Button
size="sm"
variant="outline"
disabled={busy !== null}
onClick={() => act("claim", () => api.claim(server.name))}
className="text-primary hover:text-primary hover:bg-primary/5 border-primary/20"
>
<Hand /> {busy === "claim" ? ts("claiming") : ts("claim")}
</Button>
<Button size="sm" variant="outline" disabled>
<Terminal /> {ts("console")}
</Button>
</>
) : (
<>
{live ? ( {live ? (
<Button <Button
size="sm" size="sm"
@@ -533,11 +553,15 @@ function ServerRow({
<Play /> {busy === "wake" ? ts("waking") : ts("wake")} <Play /> {busy === "wake" ? ts("waking") : ts("wake")}
</Button> </Button>
)} )}
{(server.owned || isAdmin) && (
<Link to={`/servers/${server.name}`}> <Link to={`/servers/${server.name}`}>
<Button size="sm" variant="outline"> <Button size="sm" variant="outline">
<Terminal /> {ts("console")} <Terminal /> {ts("console")}
</Button> </Button>
</Link> </Link>
)}
</>
)}
</div> </div>
</td> </td>
</tr> </tr>