diff --git a/src/components/admin/AdminFeaturesPanel.tsx b/src/components/admin/AdminFeaturesPanel.tsx index 96fc72c..575c875 100644 --- a/src/components/admin/AdminFeaturesPanel.tsx +++ b/src/components/admin/AdminFeaturesPanel.tsx @@ -7,7 +7,6 @@ import toast from 'react-hot-toast'; import { ChevronUpDownIcon, CheckIcon } from '@/components/icons/Icons'; import { Badge, - EditableRow, Section, ToggleRow, buttonClass, @@ -57,7 +56,6 @@ export function AdminFeaturesPanel() { }); const [draft, setDraft] = useState>({}); const [dirty, setDirty] = useState>(new Set()); - const [editingChangelogUrl, setEditingChangelogUrl] = useState(false); const { providers: sharedProviders } = useSharedProviders(); useEffect(() => { @@ -144,14 +142,10 @@ export function AdminFeaturesPanel() { providerType: entry.providerType, })); }, [sharedProviders]); - const valueFor = (key: string) => - Object.prototype.hasOwnProperty.call(draft, key) - ? draft[key] - : data?.values?.[key]; const currentProviderId = - typeof valueFor('defaultTtsProvider') === 'string' - ? (valueFor('defaultTtsProvider') as string) + typeof draft.defaultTtsProvider === 'string' + ? draft.defaultTtsProvider : ''; const currentSharedEntry: SharedProviderEntry | undefined = sharedProviders.find( (p) => p.slug === currentProviderId, @@ -165,7 +159,7 @@ export function AdminFeaturesPanel() { } as ProviderOption : fallbackShared; const selectedProviderOption = effectiveSelectedProvider; - const showTtsDailyLimitInputs = !Boolean(valueFor('disableTtsRateLimit')); + const shouldRenderRateLimitInputs = draft.disableTtsRateLimit === false; const handleProviderChange = (opt: ProviderOption) => { updateDraft('defaultTtsProvider', opt.id); @@ -256,7 +250,7 @@ export function AdminFeaturesPanel() { { if (!checked) { const ok = confirm( @@ -272,7 +266,7 @@ export function AdminFeaturesPanel() { updateDraft('enableTtsProvidersTab', checked)} right={renderSource('enableTtsProvidersTab')} variant="flat" @@ -280,7 +274,7 @@ export function AdminFeaturesPanel() { updateDraft('showAllProviderModels', checked)} right={renderSource('showAllProviderModels')} variant="flat" @@ -288,82 +282,71 @@ export function AdminFeaturesPanel() { updateDraft('disableTtsRateLimit', checked)} right={renderSource('disableTtsRateLimit')} variant="flat" /> - -
-
-
-
- - {renderSource('ttsDailyLimitAnonymous')} -
- updatePositiveIntDraft('ttsDailyLimitAnonymous', event.target.value)} - /> + {shouldRenderRateLimitInputs ? ( +
+
+
+ + {renderSource('ttsDailyLimitAnonymous')}
-
-
- - {renderSource('ttsDailyLimitAuthenticated')} -
- updatePositiveIntDraft('ttsDailyLimitAuthenticated', event.target.value)} - /> + updatePositiveIntDraft('ttsDailyLimitAnonymous', event.target.value)} + /> +
+
+
+ + {renderSource('ttsDailyLimitAuthenticated')}
-
-
- - {renderSource('ttsIpDailyLimitAnonymous')} -
- updatePositiveIntDraft('ttsIpDailyLimitAnonymous', event.target.value)} - /> + updatePositiveIntDraft('ttsDailyLimitAuthenticated', event.target.value)} + /> +
+
+
+ + {renderSource('ttsIpDailyLimitAnonymous')}
-
-
- - {renderSource('ttsIpDailyLimitAuthenticated')} -
- updatePositiveIntDraft('ttsIpDailyLimitAuthenticated', event.target.value)} - /> + updatePositiveIntDraft('ttsIpDailyLimitAnonymous', event.target.value)} + /> +
+
+
+ + {renderSource('ttsIpDailyLimitAuthenticated')}
+ updatePositiveIntDraft('ttsIpDailyLimitAuthenticated', event.target.value)} + />
- + ) : null}
Feature Flags} > - +
+
+
+

Changelog feed URL

+

+ Public URL to the changelog manifest JSON used by Settings. +

+
+
{renderSource('changelogFeedUrl')}
+
updateDraft('changelogFeedUrl', event.target.value)} placeholder="https://docs.openreader.richardr.dev/changelog/manifest.json" /> - +
updateDraft('enableUserSignups', checked)} right={renderSource('enableUserSignups')} variant="flat" @@ -399,7 +383,7 @@ export function AdminFeaturesPanel() { updateDraft('enableAudiobookExport', checked)} right={renderSource('enableAudiobookExport')} variant="flat" @@ -407,7 +391,7 @@ export function AdminFeaturesPanel() { updateDraft('enableDocxConversion', checked)} right={renderSource('enableDocxConversion')} variant="flat" @@ -415,7 +399,7 @@ export function AdminFeaturesPanel() { updateDraft('enableDestructiveDeleteActions', checked)} right={renderSource('enableDestructiveDeleteActions')} variant="flat" diff --git a/src/components/admin/AdminProvidersPanel.tsx b/src/components/admin/AdminProvidersPanel.tsx index 88ad1b9..b53b15f 100644 --- a/src/components/admin/AdminProvidersPanel.tsx +++ b/src/components/admin/AdminProvidersPanel.tsx @@ -57,6 +57,7 @@ interface FormState { const providerDefaultModel = defaultModelForProviderType; const ADMIN_PROVIDERS_QUERY_KEY = ['admin-providers'] as const; const ADMIN_SETTINGS_QUERY_KEY = ['admin-settings'] as const; +const ADMIN_DEFAULT_PROVIDER_QUERY_KEY = ['admin-settings', 'default-provider-slug'] as const; async function fetchDefaultProviderSlug(): Promise { const res = await fetch('/api/admin/settings'); @@ -160,7 +161,7 @@ export function AdminProvidersPanel() { data: defaultProviderSlug = '', error: defaultProviderError, } = useQuery({ - queryKey: ADMIN_SETTINGS_QUERY_KEY, + queryKey: ADMIN_DEFAULT_PROVIDER_QUERY_KEY, queryFn: fetchDefaultProviderSlug, }); @@ -183,6 +184,7 @@ export function AdminProvidersPanel() { await Promise.all([ queryClient.invalidateQueries({ queryKey: ADMIN_PROVIDERS_QUERY_KEY }), queryClient.invalidateQueries({ queryKey: ADMIN_SETTINGS_QUERY_KEY }), + queryClient.invalidateQueries({ queryKey: ADMIN_DEFAULT_PROVIDER_QUERY_KEY }), ]); }, onError: (mutationError) => { @@ -198,6 +200,7 @@ export function AdminProvidersPanel() { await Promise.all([ queryClient.invalidateQueries({ queryKey: ADMIN_PROVIDERS_QUERY_KEY }), queryClient.invalidateQueries({ queryKey: ADMIN_SETTINGS_QUERY_KEY }), + queryClient.invalidateQueries({ queryKey: ADMIN_DEFAULT_PROVIDER_QUERY_KEY }), ]); }, onError: (mutationError) => { @@ -212,6 +215,7 @@ export function AdminProvidersPanel() { await Promise.all([ queryClient.invalidateQueries({ queryKey: ADMIN_PROVIDERS_QUERY_KEY }), queryClient.invalidateQueries({ queryKey: ADMIN_SETTINGS_QUERY_KEY }), + queryClient.invalidateQueries({ queryKey: ADMIN_DEFAULT_PROVIDER_QUERY_KEY }), ]); }, onError: (mutationError) => { @@ -223,7 +227,10 @@ export function AdminProvidersPanel() { mutationFn: patchDefaultProviderSlug, onSuccess: async () => { toast.success('Default provider updated'); - await queryClient.invalidateQueries({ queryKey: ADMIN_SETTINGS_QUERY_KEY }); + await Promise.all([ + queryClient.invalidateQueries({ queryKey: ADMIN_SETTINGS_QUERY_KEY }), + queryClient.invalidateQueries({ queryKey: ADMIN_DEFAULT_PROVIDER_QUERY_KEY }), + ]); }, onError: (mutationError) => { console.error('[AdminProvidersPanel] set default failed:', mutationError); diff --git a/src/components/formPrimitives.tsx b/src/components/formPrimitives.tsx index d332594..6095591 100644 --- a/src/components/formPrimitives.tsx +++ b/src/components/formPrimitives.tsx @@ -1,8 +1,6 @@ 'use client'; -import { Fragment, useId, type ReactNode } from 'react'; -import { Transition } from '@headlessui/react'; -import { EditIcon } from '@/components/icons/Icons'; +import { useId, type ReactNode } from 'react'; /** * Shared compact form primitives used by settings-like surfaces across @@ -241,65 +239,6 @@ export function ToggleRow({ ); } -export function EditableRow({ - label, - description, - value, - expanded, - onExpandedChange, - right, - children, - editLabel = 'Edit', -}: { - label: string; - description: string; - value: ReactNode; - expanded: boolean; - onExpandedChange: (expanded: boolean) => void; - right?: ReactNode; - children: ReactNode; - editLabel?: string; -}) { - const rowClass = - 'px-0.5 pt-1 pb-2 border-b border-offbase last:border-b-0 transition-transform duration-200 ease-out hover:scale-[1.003]'; - return ( -
-
-
- {label} - {description} - {value} -
- {right ?
{right}
: null} - -
- -
- {children} -
-
-
- ); -} - export function CheckItem({ label, checked, diff --git a/src/components/icons/Icons.tsx b/src/components/icons/Icons.tsx index a780481..74c6e8f 100644 --- a/src/components/icons/Icons.tsx +++ b/src/components/icons/Icons.tsx @@ -193,27 +193,6 @@ export function PlusIcon(props: React.SVGProps) { ); } -export function EditIcon(props: React.SVGProps) { - return ( - - - - - ); -} - export function UploadIcon(props: React.SVGProps) { return (