diff --git a/src/food-market.web/src/components/Button.tsx b/src/food-market.web/src/components/Button.tsx index 47b8a4f..704a935 100644 --- a/src/food-market.web/src/components/Button.tsx +++ b/src/food-market.web/src/components/Button.tsx @@ -1,5 +1,6 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react' import { cn } from '@/lib/utils' +import { useReadOnly } from '@/lib/useReadOnly' type Variant = 'primary' | 'secondary' | 'ghost' | 'danger' type Size = 'sm' | 'md' @@ -7,6 +8,10 @@ type Size = 'sm' | 'md' interface ButtonProps extends ButtonHTMLAttributes { variant?: Variant size?: Size + /** Кнопка инициирует мутацию данных. В read-only режиме SuperAdmin'а + * (override без edit-mode) такие кнопки автоматически disabled с tooltip. + * Нав-кнопки/закрытие модалок/Cancel — оставлять без этого пропа. */ + mutating?: boolean children: ReactNode } @@ -22,10 +27,20 @@ const sizes: Record = { md: 'px-3.5 py-1.5 text-sm', } -export function Button({ variant = 'primary', size = 'md', className, children, ...rest }: ButtonProps) { +export function Button({ variant = 'primary', size = 'md', mutating, className, children, disabled, title, ...rest }: ButtonProps) { + const ro = useReadOnly() + // Variant primary/danger по умолчанию считаем мутирующими (Добавить/ + // Сохранить/Удалить/Создать почти всегда primary либо danger). Secondary/ + // ghost — нав-кнопки и Cancel — не блокируются. Явный mutating={false} + // нужен только в редких случаях когда primary не делает мутации (например + // «Применить фильтр», «Войти в режим…»). + const isMutating = mutating ?? (variant === 'primary' || variant === 'danger') + const blocked = isMutating && ro.readOnly return ( )} - diff --git a/src/food-market.web/src/lib/api.ts b/src/food-market.web/src/lib/api.ts index 5117022..c8692e7 100644 --- a/src/food-market.web/src/lib/api.ts +++ b/src/food-market.web/src/lib/api.ts @@ -32,12 +32,17 @@ export function getOrgOverride(): OrgOverride | null { return raw ? JSON.parse(raw) as OrgOverride : null } catch { return null } } -export function setOrgOverride(value: OrgOverride | null) { +export function setOrgOverride(value: OrgOverride | null, opts?: { redirectTo?: string }) { if (value) localStorage.setItem(ORG_OVERRIDE_KEY, JSON.stringify(value)) else { localStorage.removeItem(ORG_OVERRIDE_KEY); localStorage.removeItem(EDIT_MODE_KEY) } - // Силой обновляем все вкладки/страницы — кэш TanStack Query построен по - // tenant'у, нужен hard reload чтобы снять старые данные. - if (typeof window !== 'undefined') window.location.reload() + // Hard navigation чтобы (а) снести TanStack Query кэш, (б) гарантированно + // выйти из текущего layout'а в нужный (override → tenant /dashboard, + // выход → /super-admin/organizations). Без redirectTo — просто reload + // на ту же страницу (старое поведение, для совместимости). + if (typeof window !== 'undefined') { + if (opts?.redirectTo) window.location.assign(opts.redirectTo) + else window.location.reload() + } } const EDIT_MODE_KEY = 'superAdminEditMode' diff --git a/src/food-market.web/src/lib/useReadOnly.ts b/src/food-market.web/src/lib/useReadOnly.ts new file mode 100644 index 0000000..12d0621 --- /dev/null +++ b/src/food-market.web/src/lib/useReadOnly.ts @@ -0,0 +1,15 @@ +import { getOrgOverride, getEditMode } from '@/lib/api' + +/** SuperAdmin зашёл в режим «открыто как…» и edit-mode НЕ включён — + * любые мутации в UI должны быть отключены. Возвращает { readOnly, reason } + * чтобы кнопки могли показать tooltip и причину. */ +export function useReadOnly(): { readOnly: boolean; reason: string } { + const ov = getOrgOverride() + if (!ov) return { readOnly: false, reason: '' } + const edit = getEditMode() + if (edit && edit.expiresAt > Date.now()) return { readOnly: false, reason: '' } + return { + readOnly: true, + reason: 'Только просмотр. Включите редактирование в баннере сверху, чтобы менять данные.', + } +} diff --git a/src/food-market.web/src/pages/SuperAdminOrganizationsPage.tsx b/src/food-market.web/src/pages/SuperAdminOrganizationsPage.tsx index a9d5ee5..d060b5b 100644 --- a/src/food-market.web/src/pages/SuperAdminOrganizationsPage.tsx +++ b/src/food-market.web/src/pages/SuperAdminOrganizationsPage.tsx @@ -100,7 +100,7 @@ export function SuperAdminOrganizationsPage() {
e.stopPropagation()}> {!r.isArchived && (