refactor(reviews): interaction state polish — 5 UX fixes
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { humanizeBulkReason } from './bulkReason'
|
||||
|
||||
describe('humanizeBulkReason', () => {
|
||||
it('maps self_approve_forbidden to russian title', () => {
|
||||
expect(humanizeBulkReason('self_approve_forbidden')).toBe(
|
||||
'Нельзя одобрить свой draft (maker-checker)',
|
||||
)
|
||||
})
|
||||
|
||||
it('maps draft_not_pending to russian title', () => {
|
||||
expect(humanizeBulkReason('draft_not_pending')).toBe(
|
||||
'Draft уже не pending — обнови очередь',
|
||||
)
|
||||
})
|
||||
|
||||
it('maps forbidden_role to russian title', () => {
|
||||
expect(humanizeBulkReason('forbidden_role')).toBe(
|
||||
'Недостаточно прав (нужна reviewer role)',
|
||||
)
|
||||
})
|
||||
|
||||
it('maps reject_reason_required to russian title', () => {
|
||||
expect(humanizeBulkReason('reject_reason_required')).toBe(
|
||||
'Reject требует comment (compliance)',
|
||||
)
|
||||
})
|
||||
|
||||
it('maps self_reject_forbidden to russian title', () => {
|
||||
expect(humanizeBulkReason('self_reject_forbidden')).toBe(
|
||||
'Нельзя отклонить свой draft',
|
||||
)
|
||||
})
|
||||
|
||||
it('returns unknown reason as-is (backend message fallback)', () => {
|
||||
expect(humanizeBulkReason('Some raw backend message')).toBe(
|
||||
'Some raw backend message',
|
||||
)
|
||||
})
|
||||
|
||||
it('returns empty string for empty input', () => {
|
||||
expect(humanizeBulkReason('')).toBe('')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Bulk-action failure reason → human-readable label.
|
||||
*
|
||||
* <p>Lives in `src/lib` (а не в `routes/reviews.tsx`) потому что bulk runner
|
||||
* captures axios errors как plain string `reason` (либо `response.data.code`,
|
||||
* либо `error.message`) и хранит в `BulkResult.failed[].reason`. Раньше
|
||||
* reviewer видел сырой code типа `self_approve_forbidden` в bulk result
|
||||
* alert'е — теперь маппит на russain title из {@code extractReviewError}
|
||||
* mapping (single source of truth).
|
||||
*
|
||||
* <p>Если reason нет в known codes — возвращает as-is (backend message
|
||||
* вероятно уже human-readable, либо unknown server error который оставляем
|
||||
* сырым чтобы reviewer мог скопировать в bug report).
|
||||
*
|
||||
* <p>Surfaced during TODO 2 (`/plan-design-review` interaction state pass
|
||||
* 2026-05-14). Pure function — unit tested через `bulkReason.test.ts`.
|
||||
*/
|
||||
export function humanizeBulkReason(reason: string): string {
|
||||
const known: Record<string, string> = {
|
||||
self_approve_forbidden: 'Нельзя одобрить свой draft (maker-checker)',
|
||||
self_reject_forbidden: 'Нельзя отклонить свой draft',
|
||||
draft_not_pending: 'Draft уже не pending — обнови очередь',
|
||||
forbidden_role: 'Недостаточно прав (нужна reviewer role)',
|
||||
reject_reason_required: 'Reject требует comment (compliance)',
|
||||
}
|
||||
return known[reason] ?? reason
|
||||
}
|
||||
Reference in New Issue
Block a user