feat(schema-workflow): Phase 1c — CreateModal + /reviews extension + RBAC stub

This commit is contained in:
Александр Зимин
2026-05-12 11:20:29 +00:00
parent 00fe254ccc
commit ac3395f4de
6 changed files with 455 additions and 4 deletions
@@ -25,10 +25,11 @@ import {
TableRow,
TextInput,
} from '@/ui'
import { PlusIcon, PencilSimpleIcon, XCircleIcon, GearIcon, ClockCounterClockwiseIcon, TrashIcon, CheckCircleIcon, WarningIcon, XIcon, CaretLeftIcon, CaretRightIcon, DotsThreeVerticalIcon } from '@phosphor-icons/react'
import { PlusIcon, PencilSimpleIcon, XCircleIcon, GearIcon, ClockCounterClockwiseIcon, TrashIcon, CheckCircleIcon, WarningIcon, XIcon, CaretLeftIcon, CaretRightIcon, DotsThreeVerticalIcon, GitBranchIcon } from '@phosphor-icons/react'
import {
useAudit,
useChangelog,
useDictActiveSchemaDraft,
useDictionaryDetail,
useDictPendingDrafts,
useRecordRaw,
@@ -53,6 +54,7 @@ import { AoiPickerDialog, type AoiResult } from '@/components/geo/AoiPickerDialo
import { TimeTravelModal } from '@/components/timetravel/TimeTravelModal'
import { ChangelogDiffModal } from '@/components/changelog/ChangelogDiffModal'
import { kindBadgeVariant, kindIcon, kindLabel, isVersioningKind } from '@/components/changelog/kind-meta'
import { CreateSchemaDraftModal } from '@/components/workflow/CreateSchemaDraftModal'
import { nowIsoLocal } from '@/lib/dates'
import { recordDisplayName } from '@/lib/locales'
import { SCOPE_BORDER_TOP } from '@/lib/scope-style'
@@ -132,6 +134,14 @@ function DictionaryDetail() {
const { t } = useTranslation()
const detailQuery = useDictionaryDetail(name)
const canMutate = useCanMutate()
// Phase 1c: show «Create draft» button only когда approvalRequired=true
// и нет active draft'а (иначе banner предложит open existing). Live (без
// approval flag) словари редактируются напрямую через EditorDialog —
// создавать draft не нужно.
const activeSchemaDraftQuery = useDictActiveSchemaDraft(name)
const shouldOfferCreateDraft =
detailQuery.data?.approvalRequired === true &&
!activeSchemaDraftQuery.data
// AOI: bbox из URL → bbox-AOI rendered initially; polygon AOI живёт
// только в local state (не сериализуем в URL — слишком длинно).
@@ -194,6 +204,7 @@ function DictionaryDetail() {
total: 0,
})
const [schemaEditOpen, setSchemaEditOpen] = useState(false)
const [createDraftOpen, setCreateDraftOpen] = useState(false)
const [historyKey, setHistoryKey] = useState<string | undefined>(undefined)
const [aoiOpen, setAoiOpen] = useState(false)
const [timeTravelOpen, setTimeTravelOpen] = useState(false)
@@ -703,6 +714,21 @@ function DictionaryDetail() {
actions={
canMutate ? (
<>
{/* approval-required dict без active draft: предлагаем
создать черновик. Direct edit через GearIcon оставляем
для админов, но primary path — через workflow. */}
{shouldOfferCreateDraft && (
<IconButton
type="button"
label={t('createDraft.openButton', {
defaultValue: 'Создать черновик схемы',
})}
variant="default"
icon={<GitBranchIcon weight="regular" />}
disabled={!detailQuery.data}
onClick={() => setCreateDraftOpen(true)}
/>
)}
<IconButton
type="button"
label={t('schema.action.editSchema')}
@@ -1214,6 +1240,14 @@ function DictionaryDetail() {
/>
)}
{detailQuery.data && (
<CreateSchemaDraftModal
open={createDraftOpen}
onClose={() => setCreateDraftOpen(false)}
detail={detailQuery.data}
/>
)}
<RecordHistoryDrawer
open={Boolean(historyKey)}
onClose={() => setHistoryKey(undefined)}