mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
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:
70
src/components/connection/blocks/AccordionBlock.tsx
Normal file
70
src/components/connection/blocks/AccordionBlock.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user