Unverified Commit 81ba242f authored by Lemon-miaow's avatar Lemon-miaow
Browse files

style: unify panel cards and form controls

parent 20994be9
Loading
Loading
Loading
Loading
+11 −0
Changes for CONTRIBUTING.md: 11 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -34,6 +34,17 @@ matching the actual UI labels. English and Chinese copy must retain the same
meaning and degree of formality. Update existing translation entries rather than
adding duplicate messages or components.

## Panel Visual Style

Reuse `PageHeader`, the `Card` family, and the shared form and button components.
Card titles, borders, corner radii, spacing, and save footers follow their shared
definitions; avoid redefining these styles in individual pages. Use white card
backgrounds in the light theme, restrained semantic colors, and 16px functional
icons. Use `CardFooter` for save actions and `Button` for interactive controls.
Tables, consoles, compact statistics, and status messages may retain spacing and
colors suited to their content. Preserve the shared page margins and bottom
spacing.

## Local Development

You can do most day-to-day development on macOS or Linux without a full cluster.
+1 −1
Changes for panel/src/components/DistributedNodes.tsx: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ export function DistributedNodes() {
  usePolling(nodes.reload, 10_000);
  return (
    <Card>
      <CardHeader className="flex-row items-center justify-between gap-3"><CardTitle>{t("nodes")}</CardTitle><Button variant="outline" size="sm" disabled={nodes.loading} onClick={nodes.reload}><RefreshCw />{t("nodes_reload")}</Button></CardHeader>
      <CardHeader className="flex-row flex-wrap items-center justify-between gap-3"><CardTitle>{t("nodes")}</CardTitle><Button variant="outline" size="sm" disabled={nodes.loading} onClick={nodes.reload}><RefreshCw />{t("nodes_reload")}</Button></CardHeader>
      <CardContent className="space-y-3">
      {nodes.loading && <p role="status" className="flex items-center gap-2 text-sm text-muted-foreground"><Loader2 className="h-4 w-4 animate-spin" />{t("nodes_loading")}</p>}
      {!nodes.loading && !nodes.error && nodes.data?.length === 0 && <p className="text-sm text-muted-foreground">{t("nodes_empty")}</p>}
+1 −1
Changes for panel/src/components/EditServerDialog.tsx: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -214,7 +214,7 @@ export function EditServerDialog({
        {/* Unified Sidebar Link/Card style trigger */}
        <button
          type="button"
          className="group flex w-full text-left items-center gap-3 rounded-lg border border-border bg-card p-4 transition-colors hover:border-primary/40 hover:bg-accent focus:outline-none"
          className="group flex w-full text-left items-center gap-3 rounded-xl border border-border bg-card p-4 transition-colors hover:border-primary/40 hover:bg-accent focus:outline-none"
        >
          <Settings className="h-5 w-5 shrink-0 text-primary" />
          <div className="min-w-0 flex-1 text-left">
+1 −1
Changes for panel/src/components/NodeControlPanel.tsx: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ export function NodeControlPanel({ distributed }: { distributed: boolean | null
  const showForm = distributed === true || distributed === false && enabling;
  const valid = confirmed && (action === "enable" || !!name.trim() && !!ssh.trim()) && (action === "approve" || !!ip.trim());
  return <Card>
    <CardHeader className="flex-row items-center justify-between gap-3"><CardTitle>{t("node_control_title")}</CardTitle><Button variant="outline" size="sm" disabled={tasks.loading} onClick={() => { tasks.reload(); progress.reload(); }}><RefreshCw />{t("node_control_reload")}</Button></CardHeader>
    <CardHeader className="flex-row flex-wrap items-center justify-between gap-3"><CardTitle>{t("node_control_title")}</CardTitle><Button variant="outline" size="sm" disabled={tasks.loading} onClick={() => { tasks.reload(); progress.reload(); }}><RefreshCw />{t("node_control_reload")}</Button></CardHeader>
    <CardContent className="space-y-5">
      <p className="text-sm leading-relaxed text-muted-foreground">{t("node_control_description")}</p>
      {tasks.loading && !tasks.data && <p role="status" className="flex items-center gap-2 text-sm text-muted-foreground"><Loader2 className="h-4 w-4 animate-spin" />{t("node_control_loading")}</p>}
+4 −4
Changes for panel/src/components/PlayerEntrySettings.tsx: 4 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
import { ArrowRight, DoorOpen, Globe, Loader2, LogIn, RefreshCw, Route, Save, Server, ShieldCheck } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
@@ -60,7 +60,7 @@ export function PlayerEntrySettings() {
    } finally { setSaving(false); }
  }
  return <Card>
    <CardHeader className="flex-row items-center justify-between gap-3"><CardTitle className="flex items-center gap-2"><Route className="h-4 w-4 text-primary" aria-hidden="true" />{t("entry_title")}</CardTitle><Button variant="outline" size="sm" disabled={dirty || saving || query.loading} onClick={() => { query.reload(); fleet.reload(); }}><RefreshCw />{t("platform_reload")}</Button></CardHeader>
    <CardHeader className="flex-row flex-wrap items-center justify-between gap-3"><CardTitle className="flex items-center gap-2"><Route className="h-4 w-4 text-primary" aria-hidden="true" />{t("entry_title")}</CardTitle><Button variant="outline" size="sm" disabled={dirty || saving || query.loading} onClick={() => { query.reload(); fleet.reload(); }}><RefreshCw />{t("platform_reload")}</Button></CardHeader>
    <CardContent className="space-y-6" aria-busy={query.loading}>
      <p className="text-sm text-muted-foreground">{t("entry_description")}</p>
      {query.loading && <p role="status" className="flex items-center gap-2 text-sm text-muted-foreground"><Loader2 className="h-4 w-4 animate-spin" />{t("platform_loading")}</p>}
@@ -90,8 +90,8 @@ export function PlayerEntrySettings() {
      {query.error != null && <MessageLine kind="error" message={humanizeError(query.error)} />}
      {fleet.error != null && <MessageLine kind="error" message={humanizeError(fleet.error)} />}
      {message && <MessageLine kind={message.kind} message={message.text} />}
      <div className="flex flex-wrap items-center justify-between gap-3 border-t border-border pt-4"><p className="text-xs text-muted-foreground">{t("entry_scope")}</p><div className="flex gap-2">{dirty && <Button variant="outline" disabled={saving} onClick={() => { if (saved) setDraft(saved); setMessage(null); }}>{t("platform_discard")}</Button>}<Button disabled={busy || !dirty || !valid} onClick={() => void save()}>{saving ? <Loader2 className="animate-spin" /> : <Save />}{t("entry_save")}</Button></div></div>
      {reauth.dialog}
    </CardContent>
    <CardFooter><p className="text-xs text-muted-foreground">{t("entry_scope")}</p><div className="flex gap-2">{dirty && <Button variant="outline" disabled={saving} onClick={() => { if (saved) setDraft(saved); setMessage(null); }}>{t("platform_discard")}</Button>}<Button disabled={busy || !dirty || !valid} onClick={() => void save()}>{saving ? <Loader2 className="animate-spin" /> : <Save />}{t("entry_save")}</Button></div></CardFooter>
    {reauth.dialog}
  </Card>;
}
Loading