Normalize line endings to LF via .gitattributes

Репозиторий хранил фронтенд в CRLF, а часть бэкенда — вперемешку, хотя CI и Docker-сборка
работают под Linux. Прибиваем LF атрибутом `* text=auto eol=lf` и разово нормализуем дерево,
чтобы форматтеры не переписывали файлы целиком на каждом прогоне.

Коммит чисто механический: изменений содержимого нет, только концы строк.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-07-27 01:37:33 +03:00
co-authored by Claude Opus 5
parent 9d1c6d2fc3
commit 0442056367
109 changed files with 13469 additions and 13459 deletions
@@ -1,110 +1,110 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { qk } from '@/shared/api/query-keys'
import { useApiError } from '@/shared/lib/use-api-error'
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: qk.settings.all,
queryFn: getSiteSettings,
})
useEffect(() => {
if (data) {
setRegistrationEnabled(data.registrationEnabled)
setPreferredAudioLanguages(data.preferredAudioLanguages)
setChannelNumbersEnabled(data.channelNumbersEnabled)
}
}, [data])
const onError = useApiError()
const save = useMutation({
mutationFn: () =>
updateSiteSettings({
registrationEnabled,
preferredAudioLanguages,
channelNumbersEnabled,
}),
onSuccess: () => {
toast.success(t('settings.saved'))
void queryClient.invalidateQueries({ queryKey: qk.settings.all })
},
onError,
})
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>
)
}
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { qk } from '@/shared/api/query-keys'
import { useApiError } from '@/shared/lib/use-api-error'
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: qk.settings.all,
queryFn: getSiteSettings,
})
useEffect(() => {
if (data) {
setRegistrationEnabled(data.registrationEnabled)
setPreferredAudioLanguages(data.preferredAudioLanguages)
setChannelNumbersEnabled(data.channelNumbersEnabled)
}
}, [data])
const onError = useApiError()
const save = useMutation({
mutationFn: () =>
updateSiteSettings({
registrationEnabled,
preferredAudioLanguages,
channelNumbersEnabled,
}),
onSuccess: () => {
toast.success(t('settings.saved'))
void queryClient.invalidateQueries({ queryKey: qk.settings.all })
},
onError,
})
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>
)
}