From 528094966749acea8bb5c5bea3a7c49c8882bbba Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 28 Aug 2026 03:12:29 +0200 Subject: [PATCH] feat(ui): migrate the canonical action-button surface onto + {choices.map((c, i) => ( - + ))} diff --git a/frontend/src/components/DownloadCenter.tsx b/frontend/src/components/DownloadCenter.tsx index 0fb569ac..46e5c9bb 100644 --- a/frontend/src/components/DownloadCenter.tsx +++ b/frontend/src/components/DownloadCenter.tsx @@ -31,6 +31,7 @@ import { useMe } from "../lib/useMe"; import { api, type DownloadJob } from "../lib/api"; import { invalidateDownloads } from "../lib/downloads"; import { inputCls } from "./ui/form"; +import { Button } from "./ui/Button"; import { DownloadRow, IconBtn, UsageBar } from "./downloadRow"; import { MetaEditModal, ErrorModal } from "./downloadModals"; import { AdminSystem } from "./downloadAdmin"; @@ -169,20 +170,24 @@ export default function DownloadCenter() { placeholder={t("downloads.page.addUrl")} className={inputCls} /> - - + {t("downloads.page.add")} +
{queue.map((job) => ( diff --git a/frontend/src/components/DownloadDialog.tsx b/frontend/src/components/DownloadDialog.tsx index 0fc3f72e..1c6d3eb4 100644 --- a/frontend/src/components/DownloadDialog.tsx +++ b/frontend/src/components/DownloadDialog.tsx @@ -3,6 +3,7 @@ import { qk } from "../lib/queryKeys"; import { useTranslation } from "react-i18next"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import Modal from "./Modal"; +import { Button } from "./ui/Button"; import { api } from "../lib/api"; import { invalidateDownloads } from "../lib/downloads"; @@ -89,19 +90,12 @@ export default function DownloadDialog({ />
- - + +
{manage && ( diff --git a/frontend/src/components/ErrorDialog.tsx b/frontend/src/components/ErrorDialog.tsx index 6ba217a6..5b8968bb 100644 --- a/frontend/src/components/ErrorDialog.tsx +++ b/frontend/src/components/ErrorDialog.tsx @@ -2,6 +2,7 @@ import { useSyncExternalStore } from "react"; import { useTranslation } from "react-i18next"; import { dismissError, getError, subscribeError } from "../lib/errorDialog"; import Modal from "./Modal"; +import { Button } from "./ui/Button"; // Renders the current app error (if any) as a modal the user must acknowledge. export default function ErrorDialog() { @@ -12,12 +13,7 @@ export default function ErrorDialog() {

{err.message}

- +
); diff --git a/frontend/src/components/PersonalViewsModal.tsx b/frontend/src/components/PersonalViewsModal.tsx index 2e85c361..2a66c37e 100644 --- a/frontend/src/components/PersonalViewsModal.tsx +++ b/frontend/src/components/PersonalViewsModal.tsx @@ -18,6 +18,7 @@ import { import { CSS } from "@dnd-kit/utilities"; import { coercePlexFilters, type FeedFilters, type PlexFilters, type SavedView } from "../lib/api"; import Modal from "./Modal"; +import { Button } from "./ui/Button"; import { useSavedViews, asFilters } from "./SavedViewsProvider"; import { useActiveFilters } from "../lib/useActiveFilters"; import { buildPlexActiveFilters } from "./usePlexActiveFilters"; @@ -202,14 +203,16 @@ export default function PersonalViewsModal() { placeholder={t("views.namePlaceholder")} className="min-w-0 flex-1 rounded-lg border border-border bg-surface px-2 py-1.5 text-sm focus:border-accent focus:outline-hidden focus:ring-2 focus:ring-accent/30" /> - + className="shrink-0" + />
@@ -272,13 +275,14 @@ export default function PersonalViewsModal() { )}
- +
{!formOpen ? ( - + ) : (
@@ -263,19 +261,17 @@ export default function ProfileEditor({ onClose }: { onClose: () => void }) { />
- - +
)} diff --git a/frontend/src/components/SettingsPanel.tsx b/frontend/src/components/SettingsPanel.tsx index 4b6f233b..f97ee5ab 100644 --- a/frontend/src/components/SettingsPanel.tsx +++ b/frontend/src/components/SettingsPanel.tsx @@ -11,6 +11,7 @@ import Avatar from "./Avatar"; import { notify, type NotifSettings } from "../lib/notifications"; import Tooltip from "./Tooltip"; import { inputCls, Section, SettingRow, Switch } from "./ui/form"; +import { Button } from "./ui/Button"; import { type SaveState } from "./ui/DraftSaveBar"; import { useConfirm } from "./ConfirmProvider"; import { useWizard } from "./WizardProvider"; @@ -472,13 +473,9 @@ function SignInMethods({ me }: { me: Me }) { className={inputCls} /> {err &&

{err}

} - + )} diff --git a/frontend/src/components/WatchPage.tsx b/frontend/src/components/WatchPage.tsx index d05325a3..6ff7b9c3 100644 --- a/frontend/src/components/WatchPage.tsx +++ b/frontend/src/components/WatchPage.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from "react"; import { Download, Link2, Lock, PlayCircle } from "lucide-react"; +import { Button } from "./ui/Button"; // Standalone, login-free video page for a shared /watch/ link. Rendered OUTSIDE the app // tree (no auth, no providers) — like the /privacy and /terms pages. Uses plain fetch and a tiny @@ -200,13 +201,16 @@ export default function WatchPage() { className="w-full rounded-lg bg-surface border border-border px-3 py-2 text-sm outline-hidden focus:ring-2 focus:ring-accent/40" /> {error &&
{error}
} - + )} diff --git a/frontend/src/components/downloadAdmin.tsx b/frontend/src/components/downloadAdmin.tsx index c0f4a965..c3f71898 100644 --- a/frontend/src/components/downloadAdmin.tsx +++ b/frontend/src/components/downloadAdmin.tsx @@ -8,6 +8,7 @@ import { useLiveQuery } from "../lib/useLiveQuery"; import { api } from "../lib/api"; import { formatBytes } from "../lib/format"; import { inputCls } from "./ui/form"; +import { Button } from "./ui/Button"; import { StatusPill, jobTitle } from "./downloadRow"; // The admin "System" tab — storage overview, per-user footprint, quota editor, and the global job @@ -110,19 +111,17 @@ function QuotaModal({ {t("downloads.admin.unlimited")}
- - +
diff --git a/frontend/src/components/downloadModals.tsx b/frontend/src/components/downloadModals.tsx index c73dfbd9..154d58da 100644 --- a/frontend/src/components/downloadModals.tsx +++ b/frontend/src/components/downloadModals.tsx @@ -6,6 +6,7 @@ import { Plus, Trash2 } from "lucide-react"; import Modal from "./Modal"; import { api, type DownloadJob } from "../lib/api"; import { inputCls } from "./ui/form"; +import { Button } from "./ui/Button"; import { IconBtn } from "./downloadRow"; // Per-item user modals split out of DownloadCenter (self-contained, prop-driven): rename/metadata @@ -101,13 +102,14 @@ export function MetaEditModal({ job, onClose }: { job: DownloadJob; onClose: ()

{t("downloads.edit.hint")}

- +
@@ -158,22 +160,20 @@ export function ErrorModal({ )}
{retryable && ( - + )} - +
diff --git a/frontend/src/components/ui/DraftSaveBar.tsx b/frontend/src/components/ui/DraftSaveBar.tsx index 2dd3dd5a..0cf39a5f 100644 --- a/frontend/src/components/ui/DraftSaveBar.tsx +++ b/frontend/src/components/ui/DraftSaveBar.tsx @@ -1,5 +1,6 @@ import { createPortal } from "react-dom"; import { Check, RotateCcw, Save } from "lucide-react"; +import { Button } from "./Button"; // The Save / Discard bar shown while an edited draft has unsaved changes (Settings prefs, // admin Configuration). Two visual variants share one state machine + markup: @@ -58,24 +59,27 @@ export function DraftSaveBar({ )}
- - +
);