From eb75437708ae123a48e6f22d51e96412ca7c7470 Mon Sep 17 00:00:00 2001 From: mrleo1nid <43583385+mrleo1nid@users.noreply.github.com> Date: Thu, 29 Oct 2026 10:44:10 +0300 Subject: [PATCH] fix(uploads): separate withdrawn proposals from default filters --- apps/web/e2e/upload-review.spec.ts | 21 +++++++++++++++++++- apps/web/src/pages/uploads/MyUploadsPage.tsx | 5 +++-- packages/i18n/locales/en/admin.json | 1 + packages/i18n/locales/ru/admin.json | 1 + 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/web/e2e/upload-review.spec.ts b/apps/web/e2e/upload-review.spec.ts index e892c29..aa3fefa 100644 --- a/apps/web/e2e/upload-review.spec.ts +++ b/apps/web/e2e/upload-review.spec.ts @@ -16,6 +16,8 @@ async function setup(page: Page, scenario: 'queue' | 'template' | 'mine') { { ...file, id: 'visible', original_name: 'visible.flac', target_source_name: source.name, target_rel_path: 'Artist/Album/01 - Song.flac' }, { ...file, id: 'approved', original_name: 'approved.flac', status: 'approved' }, { ...file, id: 'hidden', original_name: 'hidden.flac', status: 'processing' }, + { ...file, id: 'withdrawn', original_name: 'withdrawn.flac', metadata: null, status: 'withdrawn' }, + { ...file, id: 'rejected', original_name: 'rejected.flac', metadata: null, status: 'rejected' }, ]; const missing = { ...file, id: 'missing', original_name: 'missing.flac', target_source_id: null, target_source_name: null, target_rel_path: null, status_reason: 'uploads.no_target' }; let failOnce = true; @@ -47,7 +49,9 @@ async function setup(page: Page, scenario: 'queue' | 'template' | 'mine') { document.documentElement.dataset.batchPages = String(Number(document.documentElement.dataset.batchPages ?? 0) + 1); return json({ items: [url.searchParams.has('cursor') ? files[2] : files[0]], next_cursor: url.searchParams.has('cursor') ? null : 'next' }); } - return json({ items: files.slice(0, 2), next_cursor: 'more' }); + const statuses = url.searchParams.getAll('status'); + const visible = [files[0], files[1], files[3], files[4]].filter((f) => !statuses.length || statuses.includes(f.status)); + return json({ items: visible, next_cursor: 'more' }); } if (scenario === 'mine' && req.method === 'DELETE' && url.pathname.startsWith('/api/v1/uploads/files/')) { const id = url.pathname.split('/').pop()!; @@ -112,3 +116,18 @@ test('selects a whole own batch across pages and reports partial withdrawal fail await expect(page.locator('html')).toHaveAttribute('data-deleted', '["visible","hidden","hidden"]'); await expect(page.getByRole('region', { name: 'Массовый отзыв предложений' })).toHaveCount(0); }); + +test('keeps withdrawn proposals in their own filter', async ({ page }) => { + await setup(page, 'mine'); + await expect(page.getByText('visible.flac', { exact: true })).toBeVisible(); + await expect(page.getByText('withdrawn.flac', { exact: true })).toHaveCount(0); + await page.getByRole('button', { name: 'Отозванные', exact: true }).click(); + await expect(page.getByText('withdrawn.flac', { exact: true }).first()).toBeVisible(); + await expect(page.getByText('visible.flac', { exact: true })).toHaveCount(0); + await page.getByRole('button', { name: 'Не приняты', exact: true }).click(); + await expect(page.getByText('rejected.flac', { exact: true }).first()).toBeVisible(); + await expect(page.getByText('withdrawn.flac', { exact: true })).toHaveCount(0); + await page.getByRole('button', { name: 'Все', exact: true }).click(); + await expect(page.getByText('visible.flac', { exact: true })).toBeVisible(); + await expect(page.getByText('withdrawn.flac', { exact: true })).toHaveCount(0); +}); diff --git a/apps/web/src/pages/uploads/MyUploadsPage.tsx b/apps/web/src/pages/uploads/MyUploadsPage.tsx index 232c8d5..37899ae 100644 --- a/apps/web/src/pages/uploads/MyUploadsPage.tsx +++ b/apps/web/src/pages/uploads/MyUploadsPage.tsx @@ -14,10 +14,11 @@ import { MOVING, QuotaMeter, UploadStatusBadge, useReasonText, type UploadFile, import u from './uploads.module.css'; const FILTERS = { - all: undefined, + all: ['uploading', 'processing', 'pending_review', 'approved', 'importing', 'imported', 'import_failed', 'duplicate', 'invalid', 'rejected', 'expired'], pending: ['uploading', 'processing', 'pending_review', 'approved', 'importing', 'import_failed'], imported: ['imported'], - declined: ['rejected', 'duplicate', 'invalid', 'withdrawn', 'expired'], + declined: ['rejected', 'duplicate', 'invalid', 'expired'], + withdrawn: ['withdrawn'], } satisfies Record; type Filter = keyof typeof FILTERS; diff --git a/packages/i18n/locales/en/admin.json b/packages/i18n/locales/en/admin.json index 4db8c55..1aa6ac2 100644 --- a/packages/i18n/locales/en/admin.json +++ b/packages/i18n/locales/en/admin.json @@ -291,6 +291,7 @@ "filter_pending": "In progress", "filter_imported": "Added", "filter_declined": "Not accepted", + "filter_withdrawn": "Withdrawn", "col_file": "Track", "col_status": "Status", "col_size": "Size", diff --git a/packages/i18n/locales/ru/admin.json b/packages/i18n/locales/ru/admin.json index 9bd9cb9..ec76887 100644 --- a/packages/i18n/locales/ru/admin.json +++ b/packages/i18n/locales/ru/admin.json @@ -291,6 +291,7 @@ "filter_pending": "В работе", "filter_imported": "Добавлены", "filter_declined": "Не приняты", + "filter_withdrawn": "Отозванные", "col_file": "Трек", "col_status": "Статус", "col_size": "Размер",