w-funnel/src/components/funnel/templates/InfoTemplate/InfoTemplate.tsx
dev.daminik00 0ceb254f4e hh
2025-09-29 06:10:56 +02:00

141 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useMemo } from "react";
import Image from "next/image";
import type { InfoScreenDefinition, DefaultTexts } from "@/lib/funnel/types";
import { TemplateLayout } from "../layouts/TemplateLayout";
import { cn } from "@/lib/utils";
interface InfoTemplateProps {
screen: InfoScreenDefinition;
onContinue: () => void;
canGoBack: boolean;
onBack: () => void;
screenProgress?: { current: number; total: number };
defaultTexts?: DefaultTexts;
}
export function InfoTemplate({
screen,
onContinue,
canGoBack,
onBack,
screenProgress,
defaultTexts,
}: InfoTemplateProps) {
const iconSizeClasses = useMemo(() => {
const size = screen.icon?.size ?? "xl";
switch (size) {
case "sm":
return "text-4xl";
case "md":
return "text-5xl";
case "lg":
return "text-6xl";
case "xl":
default:
return "text-8xl";
}
}, [screen.icon?.size]);
// Функция для проверки валидности URL
const isValidUrl = (value: string): boolean => {
if (!value || value.trim() === '') return false;
try {
new URL(value);
return true;
} catch {
// Проверяем относительные пути (начинаются с /) и API пути
return value.startsWith('/') || value.startsWith('/api/');
}
};
// Создаем иконку для передачи в childrenAboveTitle
const iconElement = screen.icon ? (
<div className={cn("mb-8", screen.icon.className)}>
{screen.icon.type === "emoji" ? (
<div className={cn(iconSizeClasses, "leading-none")}>
{screen.icon.value}
</div>
) : (screen.icon.value && isValidUrl(screen.icon.value)) ? (
<div className="relative">
<Image
src={screen.icon.value}
alt=""
width={
iconSizeClasses.includes("text-8xl") ? 128 :
iconSizeClasses.includes("text-6xl") ? 64 :
iconSizeClasses.includes("text-5xl") ? 48 : 36
}
height={
iconSizeClasses.includes("text-8xl") ? 128 :
iconSizeClasses.includes("text-6xl") ? 64 :
iconSizeClasses.includes("text-5xl") ? 48 : 36
}
className={cn("object-contain")}
unoptimized={screen.icon.value.startsWith('/api/images/')}
onError={(e) => {
console.error('Preview image load error:', screen.icon?.value, e);
}}
onLoad={() => {
console.log('Preview image loaded successfully:', screen.icon?.value);
}}
/>
{/* Fallback для проблемных изображений */}
<img
src={screen.icon.value}
alt=""
className={cn("absolute inset-0 object-contain opacity-0 hover:opacity-100")}
style={{
width: iconSizeClasses.includes("text-8xl") ? 128 :
iconSizeClasses.includes("text-6xl") ? 64 :
iconSizeClasses.includes("text-5xl") ? 48 : 36,
height: iconSizeClasses.includes("text-8xl") ? 128 :
iconSizeClasses.includes("text-6xl") ? 64 :
iconSizeClasses.includes("text-5xl") ? 48 : 36
}}
onError={(e) => {
console.error('Fallback image load error:', screen.icon?.value, e);
}}
onLoad={() => {
console.log('Fallback image loaded successfully:', screen.icon?.value);
}}
/>
</div>
) : (
<div className={cn(iconSizeClasses, "leading-none text-muted-foreground flex items-center justify-center")}>
📷
</div>
)}
</div>
) : null;
return (
<TemplateLayout
screen={screen}
canGoBack={canGoBack}
onBack={onBack}
screenProgress={screenProgress}
titleDefaults={{ font: "manrope", weight: "bold", align: "center" }}
subtitleDefaults={{ font: "inter", weight: "medium", color: "muted", align: "center" }}
actionButtonOptions={{
defaultText: defaultTexts?.nextButton || "Next",
disabled: false,
onClick: onContinue,
}}
childrenAboveTitle={iconElement}
>
{/* Пустые дети - весь контент теперь в заголовке, подзаголовке и иконке */}
<div className="w-full flex justify-center">
<div className={cn(
"w-full max-w-[320px] text-center",
screen.icon ? "mt-[30px]" : "mt-[60px]"
)}>
{/* Дополнительный контент если нужен */}
</div>
</div>
</TemplateLayout>
);
}