Files
OmniRoute/src/shared/components/compression/ComboCompressionModeSelect.tsx
Diego Rodrigues de Sa e Souza 0f10225f1d feat(dashboard): add compression-mode selector to Context & Cache combos page (#6760) (#7219)
Extracts the routing-combo compression-mode dropdown (Default/Off/Lite/
Standard/Aggressive/Ultra) from the combo card into a shared
ComboCompressionModeSelect component, reused on both the combo card
(compact) and the Compression Combos page's "Assign to routing" list
under Context & Cache. Both surfaces persist through the existing
PUT /api/combos/{id} route -- no backend or schema change.
2026-07-17 10:40:05 -03:00

109 lines
3.6 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
export interface ComboCompressionModeSelectCombo {
id: string;
config?: { compressionMode?: string } | null;
compressionOverride?: string;
}
export interface ComboCompressionModeSelectProps {
combo: ComboCompressionModeSelectCombo;
disabled?: boolean;
title?: string;
className?: string;
onSaved?: (nextConfig: Record<string, unknown>) => void;
}
const OPTIONS: Array<{ value: string; label: string }> = [
{ value: "", label: "Default" },
{ value: "off", label: "Off" },
{ value: "lite", label: "Lite" },
{ value: "standard", label: "Standard" },
{ value: "aggressive", label: "Aggressive" },
{ value: "ultra", label: "Ultra" },
];
function getInitialCompressionMode(combo: ComboCompressionModeSelectCombo): string {
const hasRuntimeConfig = combo?.config && typeof combo.config === "object";
if (typeof combo?.config?.compressionMode === "string") {
return combo.config.compressionMode;
}
return hasRuntimeConfig ? "" : combo.compressionOverride || "";
}
// Extracted from src/app/(dashboard)/dashboard/combos/page.tsx so both the combo card
// and the Compression Combos page (#6760) can persist the same per-routing-combo
// compression-mode override through the same `PUT /api/combos/{id}` endpoint.
//
// Deliberately does NOT call `useTranslations` — this component is rendered from
// CompressionCombosPageClient.tsx, which avoids page-level `useTranslations` to
// prevent a documented production hydration regression. Callers that want localized
// labels pass a resolved `title` string; option labels stay literal English.
export function ComboCompressionModeSelect({
combo,
disabled,
title,
className,
onSaved,
}: ComboCompressionModeSelectProps) {
const initialCompressionMode = getInitialCompressionMode(combo);
const [compressionOverride, setCompressionOverride] = useState(initialCompressionMode);
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
setCompressionOverride(initialCompressionMode);
}, [initialCompressionMode]);
const handleChange = async (value: string) => {
setCompressionOverride(value);
setIsSaving(true);
const nextConfig: Record<string, unknown> = { ...(combo.config || {}) };
if (value) {
nextConfig.compressionMode = value;
} else {
delete nextConfig.compressionMode;
}
try {
const response = await fetch(`/api/combos/${combo.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ config: nextConfig }),
});
if (!response.ok) {
console.error("Failed to update compression override");
setCompressionOverride(initialCompressionMode);
return;
}
onSaved?.(nextConfig);
} catch (error) {
console.error("Error updating compression override:", error);
setCompressionOverride(initialCompressionMode);
} finally {
setIsSaving(false);
}
};
return (
<select
value={compressionOverride}
onChange={(e) => handleChange(e.target.value)}
disabled={disabled || isSaving}
className={
className ||
"text-xs py-1 px-2 rounded border border-black/10 dark:border-white/10 bg-surface text-text-main focus:border-primary focus:outline-none transition-colors disabled:opacity-50"
}
title={title}
>
{OPTIONS.map((option) => (
<option key={option.value} value={option.value} className="bg-surface text-text-main">
{option.label}
</option>
))}
</select>
);
}
export default ComboCompressionModeSelect;