feat(web): suggest tracks, my uploads and admin upload queue screens (M5.5)
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 3m4s
ci / ts / ts (packages + web) (push) Successful in 1m23s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 52s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m51s

- Suggest tracks: drag-and-drop of files and folders with relative
  paths, streaming SHA-256 in a Web Worker (hash-wasm), duplicate
  precheck, batch reservation and tus-js-client upload (8 MB chunks,
  4 in parallel, token refresh on 401, resume of failed files).
- My uploads: statuses, rejection reasons, withdraw, quota meter.
- Upload queue (admin): status tabs, possible-duplicate filter, batches,
  metadata and storage editing, bulk approve / reject with a reason,
  import retry, and the queue settings form.
- Sidebar section for moderators and admins; ru / en strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-20 17:46:18 +03:00
1 parent ca106e8eb1
commit a3f212d253
19 files changed
+2144 -14

No files matched your search

+10 -2
View File
@@ -20,8 +20,7 @@ import {
dislikeIcon,
settingsIcon,
storageFillIcon,
storageIcon,
} from '../shared/ui/icons';
storageIcon, inboxFillIcon, inboxIcon, myUploadsFillIcon, myUploadsIcon, uploadFillIcon, uploadIcon} from '../shared/ui/icons';
import { Avatar, Icon, Logo, Menu } from '../shared/ui/ui';
import { LanguageSwitch } from './LanguageSwitch';
import { PlayerProvider } from '../player/context';
@@ -35,6 +34,9 @@ type NavPath =
| '/admin/audit'
| '/admin/sources'
| '/admin/library'
| '/admin/uploads'
| '/uploads/new'
| '/uploads/mine'
| '/collection/history'
| '/collection/likes'
| '/collection/dislikes';
@@ -109,6 +111,12 @@ function Shell() {
<NavLink to="/collection/dislikes" label={t('nav.dislikes')} icon={dislikeIcon} activeIcon={dislikeFillIcon} />
{staff && (
<>
<div className={s.navSection}>{t('nav.uploads')}</div>
<NavLink to="/uploads/new" label={t('nav.suggest')} icon={uploadIcon} activeIcon={uploadFillIcon} />
<NavLink to="/uploads/mine" label={t('nav.my_uploads')} icon={myUploadsIcon} activeIcon={myUploadsFillIcon} />
{user.role === 'admin' && (
<NavLink to="/admin/uploads" label={t('nav.upload_queue')} icon={inboxIcon} activeIcon={inboxFillIcon} />
)}
<div className={s.navSection}>{t('nav.admin')}</div>
<NavLink to="/admin/users" label={t('nav.users')} icon={groupIcon} activeIcon={groupFillIcon} />
{user.role === 'admin' && (