Enhance channel and settings functionalities: introduce viewer settings in ChannelEndpoints, update SiteSettings to include channel number toggling, and refactor related data structures. Implement new endpoints for validating templates and diffing scheduling changes, improving overall user experience and configuration management.
This commit is contained in:
@@ -1,85 +1,108 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { HttpError } from '@/shared/api/client'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/shared/ui/card'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { toast } from '@/shared/ui/toast-store'
|
||||
import { getSiteSettings, updateSiteSettings } from './api'
|
||||
|
||||
export function SettingsPanel() {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
const [registrationEnabled, setRegistrationEnabled] = useState(false)
|
||||
const [preferredAudioLanguages, setPreferredAudioLanguages] = useState('')
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['admin', 'settings'],
|
||||
queryFn: getSiteSettings,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setRegistrationEnabled(data.registrationEnabled)
|
||||
setPreferredAudioLanguages(data.preferredAudioLanguages)
|
||||
}
|
||||
}, [data])
|
||||
|
||||
const save = useMutation({
|
||||
mutationFn: () => updateSiteSettings({ registrationEnabled, preferredAudioLanguages }),
|
||||
onSuccess: () => {
|
||||
toast.success(t('settings.saved'))
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin', 'settings'] })
|
||||
},
|
||||
onError: (error: unknown) =>
|
||||
toast.error(error instanceof HttpError ? error.detail : t('common.error')),
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<h2 className="crt-glow text-xl font-semibold">{t('admin.settings.title')}</h2>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t('admin.settings.registration')}</CardTitle>
|
||||
<CardDescription>{t('admin.settings.registrationHint')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={registrationEnabled}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setRegistrationEnabled(e.target.checked)}
|
||||
/>
|
||||
{t('admin.settings.registrationLabel')}
|
||||
</label>
|
||||
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<label className="text-sm font-medium" htmlFor="preferred-audio">
|
||||
{t('admin.settings.preferredAudio')}
|
||||
</label>
|
||||
<Input
|
||||
id="preferred-audio"
|
||||
className="max-w-xs"
|
||||
placeholder="rus, eng"
|
||||
value={preferredAudioLanguages}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setPreferredAudioLanguages(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t('admin.settings.preferredAudioHint')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button size="sm" disabled={isLoading || save.isPending} onClick={() => save.mutate()}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { HttpError } from '@/shared/api/client'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/shared/ui/card'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { toast } from '@/shared/ui/toast-store'
|
||||
import { getSiteSettings, updateSiteSettings } from './api'
|
||||
|
||||
export function SettingsPanel() {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
const [registrationEnabled, setRegistrationEnabled] = useState(false)
|
||||
const [preferredAudioLanguages, setPreferredAudioLanguages] = useState('')
|
||||
const [channelNumbersEnabled, setChannelNumbersEnabled] = useState(false)
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['admin', 'settings'],
|
||||
queryFn: getSiteSettings,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setRegistrationEnabled(data.registrationEnabled)
|
||||
setPreferredAudioLanguages(data.preferredAudioLanguages)
|
||||
setChannelNumbersEnabled(data.channelNumbersEnabled)
|
||||
}
|
||||
}, [data])
|
||||
|
||||
const save = useMutation({
|
||||
mutationFn: () =>
|
||||
updateSiteSettings({
|
||||
registrationEnabled,
|
||||
preferredAudioLanguages,
|
||||
channelNumbersEnabled,
|
||||
}),
|
||||
onSuccess: () => {
|
||||
toast.success(t('settings.saved'))
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin', 'settings'] })
|
||||
},
|
||||
onError: (error: unknown) =>
|
||||
toast.error(error instanceof HttpError ? error.detail : t('common.error')),
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<h2 className="crt-glow text-xl font-semibold">{t('admin.settings.title')}</h2>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t('admin.settings.registration')}</CardTitle>
|
||||
<CardDescription>{t('admin.settings.registrationHint')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={registrationEnabled}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setRegistrationEnabled(e.target.checked)}
|
||||
/>
|
||||
{t('admin.settings.registrationLabel')}
|
||||
</label>
|
||||
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<label className="text-sm font-medium" htmlFor="preferred-audio">
|
||||
{t('admin.settings.preferredAudio')}
|
||||
</label>
|
||||
<Input
|
||||
id="preferred-audio"
|
||||
className="max-w-xs"
|
||||
placeholder="rus, eng"
|
||||
value={preferredAudioLanguages}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setPreferredAudioLanguages(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t('admin.settings.preferredAudioHint')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label className="flex items-start gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-1"
|
||||
checked={channelNumbersEnabled}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setChannelNumbersEnabled(e.target.checked)}
|
||||
/>
|
||||
<span>
|
||||
{t('admin.settings.channelNumbers')}
|
||||
<span className="block text-xs text-muted-foreground">
|
||||
{t('admin.settings.channelNumbersHint')}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<Button size="sm" disabled={isLoading || save.isPending} onClick={() => save.mutate()}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user