Update README.md with additional SonarCloud badges and improve CI workflow for coverage reporting
ci / build-backend (push) Successful in 2m34s
ci / build-frontend (push) Successful in 41s
ci / tests (push) Successful in 2m55s
ci / sonar (push) Successful in 5m21s

Enhanced the README.md file by adding new SonarCloud badges for coverage, bugs, code smells, security rating, and maintainability rating. Updated the CI workflow to remove coverage collection from the test step, as it is now handled by SonarCloud, streamlining the process and ensuring accurate badge representation.
This commit is contained in:
Leonid Pershin
2026-07-27 03:19:18 +03:00
parent 464355434b
commit 790d01b587
22 changed files with 2891 additions and 99 deletions
@@ -3,6 +3,7 @@ import { ChevronDown, ChevronRight, Folder } from 'lucide-react'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { listShows } from '@/features/admin/shows/api'
import { ShowPicker } from '@/features/admin/shows/ShowPicker'
import { qk } from '@/shared/api/query-keys'
import type { ManualInboxFileDto } from '@/shared/api/types'
import { useApiError } from '@/shared/lib/use-api-error'
@@ -18,7 +19,6 @@ import {
} from '@/shared/ui/dialog'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { toast } from '@/shared/ui/toast-store'
import { importManualInbox, listManualInbox } from './api'
import { compareParsed, formatSeasonEpisode, isValidRegex, parseEpisodeName } from './episode-parse'
@@ -221,24 +221,15 @@ export function ManualInboxDialog({ onClose }: Readonly<{ onClose: () => void }>
{/* Шоу — первое решение в этом диалоге: остальные поля лишь помогают разложить файлы. */}
<div className="flex flex-col gap-1.5">
<Label>{t('admin.media.manualShow')}</Label>
<Select
<ShowPicker
value={showId}
onValueChange={(value) => {
shows={shows}
placeholder={t('admin.media.manualPickShow')}
onChange={(value) => {
setShowPicked(true)
setShowId(value)
}}
>
<SelectTrigger>
<SelectValue placeholder={t('admin.media.manualPickShow')} />
</SelectTrigger>
<SelectContent>
{(shows ?? []).map((show) => (
<SelectItem key={show.id} value={show.id}>
{show.name}
</SelectItem>
))}
</SelectContent>
</Select>
/>
{autoDetected && (
<p className="text-xs text-muted-foreground">{t('admin.media.manualDetected')}</p>
)}
@@ -14,15 +14,12 @@ import {
} from '@/shared/ui/dialog'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { listShows } from '@/features/admin/shows/api'
import { ShowPicker } from '@/features/admin/shows/ShowPicker'
import { compareParsed, formatSeasonEpisode, isValidRegex, parseEpisodeName } from './episode-parse'
import { matchShowByName } from './match-show'
import { useUploadStore } from './upload-store'
/** Radix Select запрещает пустое значение — под «в библиотеку» используем спец-значение. */
const LIBRARY_VALUE = '__library__'
export function UploadToShowDialog({
files,
onClose,
@@ -119,19 +116,16 @@ export function UploadToShowDialog({
<span className="text-muted-foreground">
{t('admin.media.toShowMatched', { matched: matchedCount, total: files.length })}
</span>
<Select value="" onValueChange={(v) => applyToAll(v === LIBRARY_VALUE ? '' : v)}>
<SelectTrigger className="h-8 w-52">
<SelectValue placeholder={t('admin.media.applyToAll')} />
</SelectTrigger>
<SelectContent>
<SelectItem value={LIBRARY_VALUE}>{t('admin.media.toShowLibrary')}</SelectItem>
{shows?.map((s) => (
<SelectItem key={s.id} value={s.id}>
{s.name}
</SelectItem>
))}
</SelectContent>
</Select>
{/* Значение не храним: выбор здесь — это действие «проставить всем», а не состояние,
поэтому на кнопке всегда её собственная подпись. */}
<ShowPicker
value=""
shows={shows}
noneLabel={t('admin.media.toShowLibrary')}
placeholder={t('admin.media.applyToAll')}
className="h-8 w-52"
onChange={applyToAll}
/>
</div>
</div>
<ul className="crt-panel max-h-72 divide-y divide-border overflow-y-auto rounded-md text-sm">
@@ -148,26 +142,13 @@ export function UploadToShowDialog({
<span className="min-w-0 flex-1 truncate" title={p.name}>
{p.name}
</span>
<Select
value={current || LIBRARY_VALUE}
onValueChange={(v) =>
setOverrides((o) => ({ ...o, [p.name]: v === LIBRARY_VALUE ? '' : v }))
}
>
<SelectTrigger className="h-8 w-56 shrink-0 whitespace-nowrap [&>span]:truncate">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value={LIBRARY_VALUE}>
{t('admin.media.toShowLibrary')}
</SelectItem>
{shows?.map((s) => (
<SelectItem key={s.id} value={s.id}>
{s.name}
</SelectItem>
))}
</SelectContent>
</Select>
<ShowPicker
value={current}
shows={shows}
noneLabel={t('admin.media.toShowLibrary')}
className="h-8 w-56 shrink-0"
onChange={(showId) => setOverrides((o) => ({ ...o, [p.name]: showId }))}
/>
</li>
)
})}