141 lines
4.7 KiB
TypeScript
141 lines
4.7 KiB
TypeScript
"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>
|
||
);
|
||
}
|