add save confirmation to admin page

fixes embed order not propagating to backend
This commit is contained in:
Pas 2025-10-24 16:39:07 -06:00
parent f25e055477
commit 71a3d91b2a
3 changed files with 176 additions and 10 deletions

View file

@ -0,0 +1,105 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { updateSettings } from "@/backend/accounts/settings";
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
import { useAuthStore } from "@/stores/auth";
import { usePreferencesStore } from "@/stores/preferences";
export function useEmbedOrderState() {
const account = useAuthStore((s) => s.account);
const backendUrl = useBackendUrl();
// Get current values from store
const embedOrder = usePreferencesStore((s) => s.embedOrder);
const enableEmbedOrder = usePreferencesStore((s) => s.enableEmbedOrder);
const disabledEmbeds = usePreferencesStore((s) => s.disabledEmbeds);
// Local state for tracking changes
const [localEmbedOrder, setLocalEmbedOrder] = useState(embedOrder);
const [localEnableEmbedOrder, setLocalEnableEmbedOrder] =
useState(enableEmbedOrder);
const [localDisabledEmbeds, setLocalDisabledEmbeds] =
useState(disabledEmbeds);
// Store setters
const setEmbedOrder = usePreferencesStore((s) => s.setEmbedOrder);
const setEnableEmbedOrder = usePreferencesStore((s) => s.setEnableEmbedOrder);
const setDisabledEmbeds = usePreferencesStore((s) => s.setDisabledEmbeds);
// Check if any changes have been made
const hasChanges = useMemo(() => {
return (
JSON.stringify(localEmbedOrder) !== JSON.stringify(embedOrder) ||
localEnableEmbedOrder !== enableEmbedOrder ||
JSON.stringify(localDisabledEmbeds) !== JSON.stringify(disabledEmbeds)
);
}, [
localEmbedOrder,
embedOrder,
localEnableEmbedOrder,
enableEmbedOrder,
localDisabledEmbeds,
disabledEmbeds,
]);
// Reset local state to match store
const reset = useCallback(() => {
setLocalEmbedOrder(embedOrder);
setLocalEnableEmbedOrder(enableEmbedOrder);
setLocalDisabledEmbeds(disabledEmbeds);
}, [embedOrder, enableEmbedOrder, disabledEmbeds]);
// Save changes to backend and update store
const saveChanges = useCallback(async () => {
if (!account || !backendUrl) return;
try {
await updateSettings(backendUrl, account, {
embedOrder: localEmbedOrder,
enableEmbedOrder: localEnableEmbedOrder,
disabledEmbeds: localDisabledEmbeds,
});
// Update the store with the new values
setEmbedOrder(localEmbedOrder);
setEnableEmbedOrder(localEnableEmbedOrder);
setDisabledEmbeds(localDisabledEmbeds);
} catch (error) {
console.error("Failed to save embed order settings:", error);
throw error;
}
}, [
account,
backendUrl,
localEmbedOrder,
localEnableEmbedOrder,
localDisabledEmbeds,
setEmbedOrder,
setEnableEmbedOrder,
setDisabledEmbeds,
]);
// Update local state when store changes
useEffect(() => {
setLocalEmbedOrder(embedOrder);
setLocalEnableEmbedOrder(enableEmbedOrder);
setLocalDisabledEmbeds(disabledEmbeds);
}, [embedOrder, enableEmbedOrder, disabledEmbeds]);
return {
// Current values
embedOrder: localEmbedOrder,
enableEmbedOrder: localEnableEmbedOrder,
disabledEmbeds: localDisabledEmbeds,
// Setters
setEmbedOrder: setLocalEmbedOrder,
setEnableEmbedOrder: setLocalEnableEmbedOrder,
setDisabledEmbeds: setLocalDisabledEmbeds,
// State management
hasChanges,
reset,
saveChanges,
};
}

View file

@ -1,5 +1,11 @@
import { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/buttons/Button";
import { ThinContainer } from "@/components/layout/ThinContainer";
import { Heading1, Paragraph } from "@/components/utils/Text";
import { Transition } from "@/components/utils/Transition";
import { useEmbedOrderState } from "@/hooks/useEmbedOrderState";
import { SubPageLayout } from "@/pages/layouts/SubPageLayout";
import { ConfigValuesPart } from "@/pages/parts/admin/ConfigValuesPart";
import { M3U8TestPart } from "@/pages/parts/admin/M3U8TestPart";
@ -10,6 +16,21 @@ import { BackendTestPart } from "../parts/admin/BackendTestPart";
import { EmbedOrderPart } from "../parts/admin/EmbedOrderPart";
export function AdminPage() {
const { t } = useTranslation();
const [isSaving, setIsSaving] = useState(false);
const embedOrderState = useEmbedOrderState();
const handleSaveChanges = useCallback(async () => {
setIsSaving(true);
try {
await embedOrderState.saveChanges();
} catch (error) {
console.error("Failed to save embed order changes:", error);
} finally {
setIsSaving(false);
}
}, [embedOrderState]);
return (
<SubPageLayout>
<ThinContainer>
@ -21,8 +42,40 @@ export function AdminPage() {
<WorkerTestPart />
<TMDBTestPart />
<M3U8TestPart />
<EmbedOrderPart />
<EmbedOrderPart
embedOrder={embedOrderState.embedOrder}
setEmbedOrder={embedOrderState.setEmbedOrder}
enableEmbedOrder={embedOrderState.enableEmbedOrder}
setEnableEmbedOrder={embedOrderState.setEnableEmbedOrder}
disabledEmbeds={embedOrderState.disabledEmbeds}
setDisabledEmbeds={embedOrderState.setDisabledEmbeds}
/>
</ThinContainer>
<Transition
animation="fade"
show={embedOrderState.hasChanges}
className="bg-settings-saveBar-background border-t border-settings-card-border/50 py-4 transition-opacity w-full fixed bottom-0 flex justify-between flex-col md:flex-row px-8 items-start md:items-center gap-3 z-[999]"
>
<p className="text-type-danger">{t("settings.unsaved")}</p>
<div className="space-x-3 w-full md:w-auto flex">
<Button
className="w-full md:w-auto"
theme="secondary"
onClick={embedOrderState.reset}
>
{t("settings.reset")}
</Button>
<Button
className="w-full md:w-auto"
theme="purple"
loading={isSaving}
onClick={handleSaveChanges}
>
{t("settings.save")}
</Button>
</div>
</Transition>
</SubPageLayout>
);
}

View file

@ -7,19 +7,27 @@ import { Button } from "@/components/buttons/Button";
import { Toggle } from "@/components/buttons/Toggle";
import { SortableListWithToggles } from "@/components/form/SortableListWithToggles";
import { Heading2 } from "@/components/utils/Text";
import { usePreferencesStore } from "@/stores/preferences";
export function EmbedOrderPart() {
interface EmbedOrderPartProps {
embedOrder: string[];
setEmbedOrder: (order: string[]) => void;
enableEmbedOrder: boolean;
setEnableEmbedOrder: (enabled: boolean) => void;
disabledEmbeds: string[];
setDisabledEmbeds: (disabled: string[]) => void;
}
export function EmbedOrderPart({
embedOrder,
setEmbedOrder,
enableEmbedOrder,
setEnableEmbedOrder,
disabledEmbeds,
setDisabledEmbeds,
}: EmbedOrderPartProps) {
const { t } = useTranslation();
const navigate = useNavigate();
const embedOrder = usePreferencesStore((s) => s.embedOrder);
const setEmbedOrder = usePreferencesStore((s) => s.setEmbedOrder);
const enableEmbedOrder = usePreferencesStore((s) => s.enableEmbedOrder);
const setEnableEmbedOrder = usePreferencesStore((s) => s.setEnableEmbedOrder);
const disabledEmbeds = usePreferencesStore((s) => s.disabledEmbeds);
const setDisabledEmbeds = usePreferencesStore((s) => s.setDisabledEmbeds);
const allEmbeds = getAllProviders().listEmbeds();
const embedItems = useMemo(() => {