feat(control-center): adapt ModulesPage styling, rollout stages, and RU strings
This commit is contained in:
parent
41aa560d38
commit
5115517403
1 changed files with 119 additions and 89 deletions
|
|
@ -5,10 +5,12 @@ import {
|
||||||
Pause,
|
Pause,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
Sliders,
|
Sliders,
|
||||||
|
Sparkles,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useKccStore, useOperatorRole } from '../store/kccStore';
|
import { useKccStore, useOperatorRole } from '../store/kccStore';
|
||||||
import { StatusBadge } from '../components/StatusBadge';
|
import { StatusBadge } from '../components/StatusBadge';
|
||||||
import { RolloutStage } from '../contracts';
|
import { RolloutStage } from '../contracts';
|
||||||
|
import { RU_ROLLOUT_STAGE, RU_MODULE_CATEGORY } from '../locales/ru';
|
||||||
|
|
||||||
export const ModulesPage: React.FC = () => {
|
export const ModulesPage: React.FC = () => {
|
||||||
const modules = useKccStore((state) => state.modules);
|
const modules = useKccStore((state) => state.modules);
|
||||||
|
|
@ -31,10 +33,11 @@ export const ModulesPage: React.FC = () => {
|
||||||
|
|
||||||
const handleStageChange = (stage: RolloutStage, percentage: number) => {
|
const handleStageChange = (stage: RolloutStage, percentage: number) => {
|
||||||
if (!activeRollout) return;
|
if (!activeRollout) return;
|
||||||
|
const stageLabel = RU_ROLLOUT_STAGE[stage] || stage;
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
title: `Смена фазы раскатки: ${activeRollout.moduleName}`,
|
title: `Смена фазы раскатки: ${activeRollout.moduleName}`,
|
||||||
actionName: `rollout.stage_${stage}`,
|
actionName: `rollout.stage_${stage}`,
|
||||||
impactSummary: `Конвейер раскатки будет переведен в состояние «${stage}» (${percentage}% трафика).`,
|
impactSummary: `Конвейер раскатки будет переведен в состояние «${stageLabel}» (${percentage}% трафика).`,
|
||||||
targetType: 'rollout',
|
targetType: 'rollout',
|
||||||
targetId: activeRollout.rolloutId,
|
targetId: activeRollout.rolloutId,
|
||||||
targetName: activeRollout.moduleName,
|
targetName: activeRollout.moduleName,
|
||||||
|
|
@ -68,34 +71,39 @@ export const ModulesPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-4 md:space-y-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 border-b border-zinc-800 pb-4">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 border-b border-krona-border pb-4">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-zinc-100 flex items-center gap-2">
|
<h1 className="text-lg md:text-xl font-bold text-krona-primary flex items-center gap-2">
|
||||||
<Package className="w-5 h-5 text-amber-400" />
|
<Package className="w-5 h-5 text-amber-500 shrink-0" />
|
||||||
<span>Каталог модулей и Rollout конвейер</span>
|
<span>Каталог модулей и Rollout конвейер</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-xs text-zinc-400 mt-0.5">
|
<p className="text-xs text-krona-secondary mt-0.5">
|
||||||
Управление манифестами, compatibility core/schema, пилотными организациями и фазовыми релизами
|
Управление версиями модулей, совместимостью с ядром, пилотными группами и канареечным развертыванием
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-xs text-zinc-400">
|
<div className="flex items-center gap-2 text-xs text-krona-secondary">
|
||||||
Всего модулей: <span className="font-bold text-zinc-100">{modules.length}</span> • Активных раскаток:{' '}
|
<span className="px-2.5 py-1 rounded-md bg-krona-workspace border border-krona-border">
|
||||||
<span className="font-bold text-amber-400">{rollouts.length}</span>
|
Всего модулей: <strong className="text-krona-primary">{modules.length}</strong>
|
||||||
|
</span>
|
||||||
|
<span className="px-2.5 py-1 rounded-md bg-amber-500/10 border border-amber-500/30 text-amber-500 font-semibold">
|
||||||
|
Активных раскаток: <strong>{rollouts.length}</strong>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Grid: Module Selector + Module Detail & Rollout Manager */}
|
{/* Grid: Module Selector + Module Detail & Rollout Manager */}
|
||||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 md:gap-6">
|
||||||
{/* Left Column: Modules Catalog List */}
|
{/* Left Column: Modules Catalog List */}
|
||||||
<div className="rounded-xl border border-zinc-800 bg-[#091a13] p-4 space-y-3">
|
<div className="rounded-xl border border-krona-border bg-krona-surface p-3 md:p-4 space-y-3 shadow-xs">
|
||||||
<div className="text-xs font-semibold text-zinc-400 uppercase tracking-wider mb-1">
|
<div className="text-xs font-semibold text-krona-secondary uppercase tracking-wider px-1">
|
||||||
Каталог системных модулей
|
Каталог системных модулей
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
{/* Module list with min 44px items */}
|
||||||
|
<div className="space-y-2 max-h-[480px] lg:max-h-none overflow-y-auto">
|
||||||
{modules.map((m) => {
|
{modules.map((m) => {
|
||||||
const isSelected = m.moduleId === selectedModuleId;
|
const isSelected = m.moduleId === selectedModuleId;
|
||||||
const hasRollout = rollouts.some((r) => r.moduleId === m.moduleId);
|
const hasRollout = rollouts.some((r) => r.moduleId === m.moduleId);
|
||||||
|
|
@ -104,24 +112,24 @@ export const ModulesPage: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
key={m.moduleId}
|
key={m.moduleId}
|
||||||
onClick={() => setSelectedModuleId(m.moduleId)}
|
onClick={() => setSelectedModuleId(m.moduleId)}
|
||||||
className={`p-3 rounded-lg border text-xs cursor-pointer transition-colors space-y-1.5 ${
|
className={`min-h-[44px] p-3 rounded-lg border text-xs cursor-pointer transition-colors space-y-1.5 ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-amber-500/15 border-amber-500/40 text-zinc-100'
|
? 'bg-amber-500/15 border-amber-500/40 text-krona-primary shadow-xs'
|
||||||
: 'bg-zinc-950/60 border-zinc-800 text-zinc-400 hover:bg-zinc-800/50 hover:text-zinc-200'
|
: 'bg-krona-workspace border-krona-border text-krona-secondary hover:bg-krona-surface hover:text-krona-primary'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="font-bold text-zinc-100">{m.name}</span>
|
<span className="font-bold text-krona-primary">{m.name}</span>
|
||||||
<span className="text-[10px] font-mono px-1.5 py-0.5 rounded-xs bg-zinc-900 border border-zinc-700 text-amber-300">
|
<span className="text-[10px] font-mono px-1.5 py-0.5 rounded-md bg-krona-surface border border-krona-border text-amber-500 font-semibold">
|
||||||
v{m.latestVersion}
|
v{m.latestVersion}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[11px] text-zinc-400 line-clamp-1">{m.description}</p>
|
<p className="text-[11px] text-krona-secondary line-clamp-1">{m.description}</p>
|
||||||
<div className="flex justify-between items-center text-[10px] text-zinc-500 pt-1">
|
<div className="flex justify-between items-center text-[10px] text-krona-secondary/80 pt-1">
|
||||||
<span>Тенантов: {m.activeTenantsCount} • Инстансов: {m.instancesCount}</span>
|
<span>Организаций: {m.activeTenantsCount} • Установок: {m.instancesCount}</span>
|
||||||
{hasRollout && (
|
{hasRollout && (
|
||||||
<span className="px-1.5 py-0.5 rounded-xs bg-indigo-950 text-indigo-300 border border-indigo-700 font-bold uppercase">
|
<span className="px-1.5 py-0.5 rounded-md bg-amber-500/15 text-amber-600 dark:text-amber-300 border border-amber-500/30 font-bold uppercase text-[9px]">
|
||||||
Rollout
|
Раскатка
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -132,97 +140,115 @@ export const ModulesPage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right 2 Columns: Module Details & Rollout Controller */}
|
{/* Right 2 Columns: Module Details & Rollout Controller */}
|
||||||
<div className="xl:col-span-2 space-y-6">
|
<div className="lg:col-span-2 space-y-4 md:space-y-6">
|
||||||
{selectedModule && (
|
{selectedModule && (
|
||||||
<>
|
<>
|
||||||
{/* Module Header Card */}
|
{/* Module Header Card */}
|
||||||
<div className="rounded-xl border border-zinc-800 bg-[#0a1c15] p-5 space-y-4">
|
<div className="rounded-xl border border-krona-border bg-krona-surface p-4 md:p-5 space-y-4 shadow-xs">
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex flex-wrap items-center gap-2 mb-1">
|
||||||
<span className="font-mono text-xs text-amber-400 font-bold">{selectedModule.code}</span>
|
<span className="font-mono text-xs text-amber-500 font-bold">{selectedModule.code}</span>
|
||||||
<span className="text-zinc-600">•</span>
|
<span className="text-krona-secondary">•</span>
|
||||||
<span className="text-xs text-zinc-400">Категория: {selectedModule.category}</span>
|
<span className="text-xs text-krona-secondary">
|
||||||
|
Категория: {RU_MODULE_CATEGORY[selectedModule.category] || selectedModule.category}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-lg font-bold text-zinc-100">{selectedModule.name}</h2>
|
<h2 className="text-base md:text-lg font-bold text-krona-primary">{selectedModule.name}</h2>
|
||||||
<p className="text-xs text-zinc-300 mt-1">{selectedModule.description}</p>
|
<p className="text-xs text-krona-secondary mt-1">{selectedModule.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-right">
|
<div className="text-left sm:text-right shrink-0">
|
||||||
<span className="text-xs text-zinc-400">Текущая стабильная версия:</span>
|
<span className="text-xs text-krona-secondary">Текущая версия:</span>
|
||||||
<div className="font-mono font-bold text-lg text-emerald-400">v{selectedModule.latestVersion}</div>
|
<div className="font-mono font-bold text-lg text-emerald-600 dark:text-emerald-400">
|
||||||
|
v{selectedModule.latestVersion}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 gap-3 text-xs pt-3 border-t border-zinc-800">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2 text-xs pt-3 border-t border-krona-border">
|
||||||
<div className="p-2.5 rounded-lg bg-zinc-950/70 border border-zinc-800">
|
<div className="p-2.5 rounded-lg bg-krona-workspace border border-krona-border">
|
||||||
<div className="text-zinc-400 mb-0.5">Канал релиза:</div>
|
<div className="text-krona-secondary mb-0.5">Канал релиза:</div>
|
||||||
<div className="font-semibold text-zinc-200 uppercase">{selectedModule.rolloutChannel}</div>
|
<div className="font-semibold text-krona-primary uppercase">{selectedModule.rolloutChannel}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2.5 rounded-lg bg-zinc-950/70 border border-zinc-800">
|
<div className="p-2.5 rounded-lg bg-krona-workspace border border-krona-border">
|
||||||
<div className="text-zinc-400 mb-0.5">Манифест:</div>
|
<div className="text-krona-secondary mb-0.5">Манифест:</div>
|
||||||
<div className="font-semibold text-zinc-200 font-mono">v{selectedModule.manifestVersion}</div>
|
<div className="font-semibold text-krona-primary font-mono">v{selectedModule.manifestVersion}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2.5 rounded-lg bg-zinc-950/70 border border-zinc-800">
|
<div className="p-2.5 rounded-lg bg-krona-workspace border border-krona-border">
|
||||||
<div className="text-zinc-400 mb-0.5">Зависимости:</div>
|
<div className="text-krona-secondary mb-0.5">Зависимости:</div>
|
||||||
<div className="font-semibold text-zinc-200">
|
<div className="font-semibold text-krona-primary truncate">
|
||||||
{selectedModule.dependencies.length > 0 ? selectedModule.dependencies.join(', ') : 'Нет (Базовый)'}
|
{selectedModule.dependencies.length > 0 ? selectedModule.dependencies.join(', ') : 'Базовый'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Rollout Control Panel (Сценарий 8) */}
|
{/* Rollout Control Panel */}
|
||||||
<div className="rounded-xl border border-zinc-800 bg-[#091a13] p-5 space-y-4">
|
<div className="rounded-xl border border-krona-border bg-krona-surface p-4 md:p-5 space-y-4 shadow-xs">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Sliders className="w-4 h-4 text-amber-400" />
|
<Sliders className="w-4 h-4 text-amber-500" />
|
||||||
<h3 className="text-sm font-bold text-zinc-200">Управление раскаткой (Rollout Pipeline)</h3>
|
<h3 className="text-sm font-bold text-krona-primary">
|
||||||
|
Управление конвейером раскатки (Rollout)
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
{activeRollout && <StatusBadge status={activeRollout.stage} size="sm" />}
|
{activeRollout && (
|
||||||
|
<StatusBadge
|
||||||
|
status={activeRollout.stage}
|
||||||
|
size="sm"
|
||||||
|
customLabel={RU_ROLLOUT_STAGE[activeRollout.stage] || activeRollout.stage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activeRollout ? (
|
{activeRollout ? (
|
||||||
<div className="space-y-4 text-xs">
|
<div className="space-y-4 text-xs">
|
||||||
{/* Progress visualizer */}
|
{/* Progress visualizer */}
|
||||||
<div className="p-4 rounded-xl bg-zinc-950 border border-zinc-800 space-y-3">
|
<div className="p-4 rounded-xl bg-krona-workspace border border-krona-border space-y-3">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="font-semibold text-zinc-200">
|
<span className="font-semibold text-krona-primary">
|
||||||
Целевая версия: <span className="font-mono text-amber-300">v{activeRollout.targetVersion}</span>
|
Целевая версия:{' '}
|
||||||
|
<span className="font-mono text-amber-500 font-bold">v{activeRollout.targetVersion}</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="font-bold text-amber-400 font-mono text-sm">{activeRollout.percentage}%</span>
|
<span className="font-bold text-amber-500 font-mono text-sm">{activeRollout.percentage}%</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full bg-zinc-800 h-2 rounded-full overflow-hidden">
|
<div className="w-full bg-krona-border h-2 rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full bg-amber-400 transition-all duration-300"
|
className="h-full bg-amber-500 transition-all duration-300 rounded-full"
|
||||||
style={{ width: `${activeRollout.percentage}%` }}
|
style={{ width: `${activeRollout.percentage}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between text-[11px] text-zinc-400">
|
<div className="flex flex-wrap justify-between gap-2 text-[11px] text-krona-secondary">
|
||||||
<span>Стадия: <strong className="text-zinc-200 uppercase">{activeRollout.stage}</strong></span>
|
<span>
|
||||||
|
Стадия:{' '}
|
||||||
|
<strong className="text-krona-primary">
|
||||||
|
{RU_ROLLOUT_STAGE[activeRollout.stage] || activeRollout.stage}
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
<span>Пилотных организаций: {activeRollout.pilotTenantIds.length}</span>
|
<span>Пилотных организаций: {activeRollout.pilotTenantIds.length}</span>
|
||||||
<span>Старт: {new Date(activeRollout.startedAt).toLocaleDateString('ru-RU')}</span>
|
<span>Старт: {new Date(activeRollout.startedAt).toLocaleDateString('ru-RU')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Rollout Action Buttons */}
|
{/* Rollout Action Buttons with min 44px touch targets */}
|
||||||
<div className="flex flex-wrap gap-2 pt-2">
|
<div className="flex flex-wrap gap-2 pt-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!permissions.canTriggerRollouts || activeRollout.stage === 'pilot'}
|
disabled={!permissions.canTriggerRollouts || activeRollout.stage === 'pilot'}
|
||||||
onClick={() => handleStageChange('pilot', 15)}
|
onClick={() => handleStageChange('pilot', 15)}
|
||||||
className="px-3 py-1.5 rounded-lg bg-zinc-900 border border-zinc-700 text-zinc-300 hover:bg-zinc-800 text-xs font-medium disabled:opacity-40 transition-colors"
|
className="min-h-[44px] px-3.5 py-2 rounded-lg bg-krona-workspace border border-krona-border text-krona-primary hover:bg-krona-surface text-xs font-medium disabled:opacity-40 transition-colors shadow-xs"
|
||||||
>
|
>
|
||||||
Фаза 1: Pilot (15%)
|
Фаза 1: Пилот (15%)
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!permissions.canTriggerRollouts || activeRollout.stage === 'percentage'}
|
disabled={!permissions.canTriggerRollouts || activeRollout.stage === 'percentage'}
|
||||||
onClick={() => handleStageChange('percentage', 50)}
|
onClick={() => handleStageChange('percentage', 50)}
|
||||||
className="px-3 py-1.5 rounded-lg bg-zinc-900 border border-zinc-700 text-zinc-300 hover:bg-zinc-800 text-xs font-medium disabled:opacity-40 transition-colors"
|
className="min-h-[44px] px-3.5 py-2 rounded-lg bg-krona-workspace border border-krona-border text-krona-primary hover:bg-krona-surface text-xs font-medium disabled:opacity-40 transition-colors shadow-xs"
|
||||||
>
|
>
|
||||||
Фаза 2: 50% организаций
|
Фаза 2: 50% организаций
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -231,7 +257,7 @@ export const ModulesPage: React.FC = () => {
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!permissions.canTriggerRollouts || activeRollout.stage === 'general_availability'}
|
disabled={!permissions.canTriggerRollouts || activeRollout.stage === 'general_availability'}
|
||||||
onClick={() => handleStageChange('general_availability', 100)}
|
onClick={() => handleStageChange('general_availability', 100)}
|
||||||
className="px-3 py-1.5 rounded-lg bg-emerald-950 text-emerald-300 border border-emerald-700 hover:bg-emerald-900 text-xs font-bold disabled:opacity-40 transition-colors"
|
className="min-h-[44px] px-4 py-2 rounded-lg bg-emerald-500/15 text-emerald-600 dark:text-emerald-300 border border-emerald-500/40 hover:bg-emerald-500/25 text-xs font-bold disabled:opacity-40 transition-colors shadow-xs"
|
||||||
>
|
>
|
||||||
Фаза 3: Полный релиз (100% GA)
|
Фаза 3: Полный релиз (100% GA)
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -241,19 +267,19 @@ export const ModulesPage: React.FC = () => {
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!permissions.canPauseRollbackRollouts}
|
disabled={!permissions.canPauseRollbackRollouts}
|
||||||
onClick={() => handleStageChange('paused', activeRollout.percentage)}
|
onClick={() => handleStageChange('paused', activeRollout.percentage)}
|
||||||
className="px-3 py-1.5 rounded-lg bg-amber-950/70 text-amber-300 border border-amber-700 hover:bg-amber-900/60 text-xs font-bold disabled:opacity-40 transition-colors flex items-center gap-1.5"
|
className="min-h-[44px] px-3.5 py-2 rounded-lg bg-amber-500/15 text-amber-600 dark:text-amber-300 border border-amber-500/40 hover:bg-amber-500/25 text-xs font-bold disabled:opacity-40 transition-colors flex items-center gap-1.5 shadow-xs"
|
||||||
>
|
>
|
||||||
<Pause className="w-3.5 h-3.5" />
|
<Pause className="w-4 h-4" />
|
||||||
<span>Поставить на паузу</span>
|
<span>На паузу</span>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!permissions.canTriggerRollouts}
|
disabled={!permissions.canTriggerRollouts}
|
||||||
onClick={() => handleStageChange('pilot', activeRollout.percentage)}
|
onClick={() => handleStageChange('pilot', activeRollout.percentage)}
|
||||||
className="px-3 py-1.5 rounded-lg bg-amber-500 text-zinc-950 font-bold text-xs hover:bg-amber-400 transition-colors flex items-center gap-1.5"
|
className="min-h-[44px] px-3.5 py-2 rounded-lg bg-amber-500 text-zinc-950 font-bold text-xs hover:bg-amber-400 transition-colors flex items-center gap-1.5 shadow-xs"
|
||||||
>
|
>
|
||||||
<Play className="w-3.5 h-3.5" />
|
<Play className="w-4 h-4" />
|
||||||
<span>Снять с паузы</span>
|
<span>Снять с паузы</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
@ -262,47 +288,50 @@ export const ModulesPage: React.FC = () => {
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!permissions.canPauseRollbackRollouts || activeRollout.stage === 'rolled_back'}
|
disabled={!permissions.canPauseRollbackRollouts || activeRollout.stage === 'rolled_back'}
|
||||||
onClick={() => handleStageChange('rolled_back', 0)}
|
onClick={() => handleStageChange('rolled_back', 0)}
|
||||||
className="px-3 py-1.5 rounded-lg bg-rose-950 text-rose-300 border border-rose-700 hover:bg-rose-900 text-xs font-bold disabled:opacity-40 transition-colors flex items-center gap-1.5"
|
className="min-h-[44px] px-3.5 py-2 rounded-lg bg-rose-500/15 text-rose-600 dark:text-rose-300 border border-rose-500/40 hover:bg-rose-500/25 text-xs font-bold disabled:opacity-40 transition-colors flex items-center gap-1.5 shadow-xs"
|
||||||
>
|
>
|
||||||
<RotateCcw className="w-3.5 h-3.5" />
|
<RotateCcw className="w-4 h-4" />
|
||||||
<span>Rollback (Откат)</span>
|
<span>Откат (Rollback)</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="p-4 rounded-xl bg-zinc-950 border border-zinc-800 text-xs space-y-3">
|
<div className="p-4 rounded-xl bg-krona-workspace border border-krona-border text-xs space-y-3">
|
||||||
<p className="text-zinc-400">
|
<p className="text-krona-secondary">
|
||||||
Для модуля <strong className="text-zinc-200">{selectedModule.name}</strong> нет активных раскаток.
|
Для модуля <strong className="text-krona-primary">{selectedModule.name}</strong> нет активных раскаток.
|
||||||
Вы можете запустить пилотный релиз новой версии.
|
Вы можете запустить пилотный релиз новой версии для проверки совместимости.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!permissions.canTriggerRollouts}
|
disabled={!permissions.canTriggerRollouts}
|
||||||
onClick={handleCreateRollout}
|
onClick={handleCreateRollout}
|
||||||
className="px-4 py-2 rounded-lg bg-amber-500 hover:bg-amber-400 text-zinc-950 font-bold text-xs transition-colors flex items-center gap-1.5 disabled:opacity-40"
|
className="min-h-[44px] px-4 py-2.5 rounded-lg bg-amber-500 hover:bg-amber-400 text-zinc-950 font-bold text-xs transition-colors flex items-center gap-2 disabled:opacity-40 shadow-xs"
|
||||||
>
|
>
|
||||||
<Play className="w-3.5 h-3.5" />
|
<Play className="w-4 h-4" />
|
||||||
<span>Запустить пилотный Rollout</span>
|
<span>Запустить пилотную раскатку</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Release History & Deployments */}
|
{/* Release History & Deployments */}
|
||||||
<div className="rounded-xl border border-zinc-800 bg-[#0a1c15] p-5 space-y-3 text-xs">
|
<div className="rounded-xl border border-krona-border bg-krona-surface p-4 md:p-5 space-y-3 text-xs shadow-xs">
|
||||||
<h3 className="text-sm font-bold text-zinc-200">История релизов и совместимость (Compatibility)</h3>
|
<h3 className="text-sm font-bold text-krona-primary flex items-center gap-1.5">
|
||||||
|
<Sparkles className="w-4 h-4 text-amber-500" />
|
||||||
|
<span>История релизов и совместимость</span>
|
||||||
|
</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{selectedModule.releaseHistory.map((rel) => (
|
{selectedModule.releaseHistory.map((rel) => (
|
||||||
<div key={rel.version} className="p-3 rounded-lg bg-zinc-950 border border-zinc-800 space-y-1">
|
<div key={rel.version} className="p-3 rounded-lg bg-krona-workspace border border-krona-border space-y-1">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="font-bold text-amber-300 font-mono">v{rel.version}</span>
|
<span className="font-bold text-amber-500 font-mono">v{rel.version}</span>
|
||||||
<span className="text-[11px] text-zinc-500">
|
<span className="text-[11px] text-krona-secondary">
|
||||||
{new Date(rel.releasedAt).toLocaleDateString('ru-RU')}
|
{new Date(rel.releasedAt).toLocaleDateString('ru-RU')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-zinc-300 text-[11px]">{rel.changelog}</p>
|
<p className="text-krona-primary text-[11px]">{rel.changelog}</p>
|
||||||
<div className="text-[10px] text-zinc-400 font-mono pt-1">
|
<div className="text-[10px] text-krona-secondary font-mono pt-1">
|
||||||
Совместимость с Core: {rel.compatibleCore}
|
Совместимость с ядром: {rel.compatibleCore}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -315,3 +344,4 @@ export const ModulesPage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue