Права, вход и passkeys, сессии, активность и опасная зона. Каталог пользователей вынесен в общее состояние макета, поэтому правки видны и в списке, и в профиле. Переходы: меню строки «Профиль», «Открыть профиль» в панели прав, хлебные крошки в шапке. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
import { createContext, useContext, type Dispatch, type SetStateAction } from 'react'
|
|
import type { AdminUser } from '@authportal/shared'
|
|
|
|
export type ChangeKind = 'add' | 'chg' | 'del'
|
|
|
|
export type RowMark = 'new' | 'dirty' | 'deleted'
|
|
|
|
export type PendingChange = {
|
|
id: string
|
|
kind: ChangeKind
|
|
title: string
|
|
/** Раздел, к которому относятся пометки строк («users», «app-links»). */
|
|
resource?: string
|
|
/** Пометки строк: «Не сохранено» (dirty/new) и «Будет удалено» (deleted). */
|
|
marks?: Record<string, RowMark>
|
|
/** Выполняется при «Сохранить»: окончательно применяет правку (например, убирает удалённую строку). */
|
|
commit?: () => void
|
|
/** Строки diff: «+ …», «− …», « … ». */
|
|
lines: string[]
|
|
/** Откатывает локальное состояние страницы при «Отменить». */
|
|
revert?: () => void
|
|
}
|
|
|
|
export type Density = 'comfortable' | 'compact'
|
|
export type Contrast = 'standard' | 'high'
|
|
|
|
export type PreviewStore = {
|
|
pending: PendingChange[]
|
|
/** Пометки строк раздела из накопленных правок. */
|
|
marksFor: (resource: string) => Record<string, RowMark>
|
|
addChange: (change: Omit<PendingChange, 'id'>) => void
|
|
discardAll: () => void
|
|
applyAll: () => void
|
|
paletteOpen: boolean
|
|
setPaletteOpen: (open: boolean) => void
|
|
/** Каталог пользователей: общий для списка и профиля. */
|
|
users: AdminUser[]
|
|
setUsers: Dispatch<SetStateAction<AdminUser[]>>
|
|
pins: string[]
|
|
togglePin: (id: string) => void
|
|
density: Density
|
|
setDensity: (v: Density) => void
|
|
contrast: Contrast
|
|
setContrast: (v: Contrast) => void
|
|
}
|
|
|
|
export const PreviewStoreContext = createContext<PreviewStore | null>(null)
|
|
|
|
export function usePreviewStore(): PreviewStore {
|
|
const ctx = useContext(PreviewStoreContext)
|
|
if (!ctx) throw new Error('PreviewProvider отсутствует')
|
|
return ctx
|
|
}
|