diff --git a/ordinis-admin-ui/src/components/workflow/SchemaDraftDrawer.tsx b/ordinis-admin-ui/src/components/workflow/SchemaDraftDrawer.tsx index bb9ff31..abe9ebd 100644 --- a/ordinis-admin-ui/src/components/workflow/SchemaDraftDrawer.tsx +++ b/ordinis-admin-ui/src/components/workflow/SchemaDraftDrawer.tsx @@ -1,5 +1,6 @@ import { useState } from 'react' import { useTranslation } from 'react-i18next' +import { useAuth } from 'react-oidc-context' import axios from 'axios' import { Badge, Button, Drawer, LoadingBlock, QueryErrorState, TextArea, toast } from '@/ui' import { @@ -241,6 +242,7 @@ export function SchemaDraftDrawer({ inline т.к. Drawer sheet flow-based. */} "). Иначе — short UUID (первые 8 chars) с full + // tooltip. Полное UUID показывать бессмысленно — это Keycloak sub claim, + // нечитаемый для оператора. + // + // TODO Phase 1d: backend endpoint /admin/users/{sub}/display чтобы для + // не-current-user тоже показывать username. Пока — best-effort: current + // user resolved через JWT profile claim, другие — UUID prefix. + const currentSub = auth.user?.profile?.sub + const isMe = currentSub && currentSub === draft.makerId + const myDisplay = + auth.user?.profile?.preferred_username || + auth.user?.profile?.name || + auth.user?.profile?.email + const authorDisplay = isMe + ? `Я${myDisplay ? ` • ${myDisplay}` : ''}` + : draft.makerId.substring(0, 8) return (
- {draft.makerId} + + {authorDisplay} + v{draft.branchedFromVersion} @@ -396,6 +421,7 @@ function DraftActions({ onEdit, disabled, hasCurrentVersion, + makerId, }: { status: SchemaDraftStatus onSubmitReview: () => void @@ -405,13 +431,21 @@ function DraftActions({ onEdit?: () => void disabled: boolean hasCurrentVersion: boolean + /** UUID maker'а draft'а — для gate "Отозвать" (только сам автор может). */ + makerId: string }) { const { t } = useTranslation() + const auth = useAuth() // Phase 1c RBAC stub: для review_pending reviewer-only действия скрываем // если нет approve role. Publish — отдельная role. Backend всё равно // защищает. Phase 1d intergrate'нет Keycloak realm_access.roles. const canReview = useCanReviewSchema() const canPublish = useCanPublishSchema() + // "Отозвать" — maker-only action (withdraw own draft). Backend всё равно + // 403'нет non-maker call, но скрытие на UI level убирает confusion: + // reviewer видел кнопку которая всегда fail'ила. Сравниваем JWT sub с makerId. + const currentSub = auth.user?.profile?.sub + const isMaker = Boolean(currentSub) && currentSub === makerId if (!isActiveSchemaDraft(status)) { return ( @@ -501,16 +535,18 @@ function DraftActions({ {t('workflow.schemaDraft.actions.publish', { defaultValue: 'Опубликовать' })} )} - + {isMaker && ( + + )} ) }