refactor: extract Installation Guides into block renderer components

Replace monolithic Connection.tsx (1076 lines) with modular architecture:
- 4 block renderers (cards, timeline, accordion, minimal) selectable
  via uiConfig.installationGuidesBlockType from RemnaWave config
- Color parser utility with 14 named colors + hex support
- InstallationGuide component handles platform/app selection and
  button rendering (subscriptionLink, copyButton, external)
- Remove classic step-based format (AppButton, AppStep, AppInfo)
- Add appConfig query invalidation on admin UUID change
This commit is contained in:
c0mrade
2026-02-05 20:07:07 +03:00
parent 5111b63f2e
commit 813f6e4449
12 changed files with 765 additions and 979 deletions

View File

@@ -0,0 +1,70 @@
import { useState } from 'react';
import { getColorGradient } from '@/utils/colorParser';
import { ThemeIcon } from './ThemeIcon';
import type { BlockRendererProps } from './types';
export function AccordionBlock({
blocks,
isMobile,
getLocalizedText,
getSvgHtml,
renderBlockButtons,
}: BlockRendererProps) {
const [openIndex, setOpenIndex] = useState<number | null>(0);
return (
<div className="space-y-2">
{blocks.map((block, index) => {
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan');
const isOpen = openIndex === index;
return (
<div
key={index}
className={`overflow-hidden rounded-2xl border bg-dark-800/50 transition-colors ${
isOpen ? 'border-accent-500/30' : 'border-dark-700/50'
}`}
>
{/* Control */}
<button
onClick={() => setOpenIndex(isOpen ? null : index)}
className="flex w-full items-center gap-3 p-4 text-left"
>
<ThemeIcon
getSvgHtml={getSvgHtml}
svgIconKey={block.svgIconKey}
gradientStyle={gradientStyle}
isMobile={isMobile}
/>
<span className="min-w-0 flex-1 truncate font-semibold text-dark-100">
{getLocalizedText(block.title)}
</span>
<svg
className={`h-[18px] w-[18px] shrink-0 text-dark-400 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Panel */}
<div
className={`overflow-hidden transition-all duration-200 ${
isOpen ? 'max-h-[600px] opacity-100' : 'max-h-0 opacity-0'
}`}
>
<div className="px-4 pb-4">
<p className="whitespace-pre-line text-sm leading-relaxed text-dark-400">
{getLocalizedText(block.description)}
</p>
{renderBlockButtons(block.buttons, 'light')}
</div>
</div>
</div>
);
})}
</div>
);
}