Passa al contenuto principale

Componenti Preact

Componenti Icone

Tutti i componenti icona sono ottimizzati con memo() per evitare re-render inutili:

resources/js/shadowDomWidgetPreact.jsx
const DefaultIcon = memo(() => (
<svg
width="30"
height="30"
viewBox="0 0 40 40"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="default-icon"
>
<rect width="40" height="40" rx="20" fill="#E97222" />
<path
d="M14.7611 29.1869C15.8673 29.1869 16.9443 28.9796 17.9922 28.5352C18.9818 28.1205 19.8551 27.4985 20.6119 26.7283C21.3687 25.9581 21.98 25.0399 22.3875 24.0327C22.8241 22.996 23.057 21.9 23.057 20.7447C23.057 19.6191 22.8241 18.5231 22.3875 17.4567C21.98 16.4495 21.3687 15.5609 20.6119 14.7907C19.8551 14.0206 18.9527 13.3985 17.9922 12.9838C16.9443 12.5395 15.8673 12.3025 14.7611 12.3025C13.6259 12.3025 12.5489 12.5395 11.5302 12.9838C10.5405 13.3985 9.63813 14.0206 8.88132 14.7907C8.1245 15.5609 7.51322 16.4792 7.10571 17.4567C6.66908 18.5231 6.46533 19.6191 6.46533 20.7447C6.46533 21.9 6.66908 22.996 7.10571 24.0327C7.51322 25.0399 8.1245 25.9581 8.88132 26.7283C9.63813 27.4985 10.5405 28.1205 11.5302 28.5352C12.5489 28.9796 13.6259 29.1869 14.7611 29.1869ZM14.7611 16.7458C16.9152 16.7458 18.6908 18.5527 18.6908 20.7447C18.6908 22.9663 16.9152 24.7437 14.7611 24.7437C12.578 24.7437 10.8316 22.9663 10.8316 20.7447C10.8316 18.5527 12.578 16.7458 14.7611 16.7458Z"
fill="white"
/>
<path
d="M24.5352 20.6157L32.5344 20.6363"
stroke="white"
stroke-width="3"
/>
<path
d="M23.884 25.0739L30.7687 28.4763"
stroke="white"
stroke-width="3"
/>
<path
d="M30.2566 11.4457L23.811 15.6454"
stroke="white"
stroke-width="3"
/>
</svg>
));

const CopyIcon = memo(() => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.8 18C17.9201 18 18.4802 18 18.908 17.782C19.2843 17.5903 19.5903 17.2843 19.782 16.908C20 16.4802 20 15.9201 20 14.8V5.2C20 4.0799 20 3.51984 19.782 3.09202C19.5903 2.71569 19.2843 2.40973 18.908 2.21799C18.4802 2 17.9201 2 16.8 2H11.2C10.0799 2 9.51984 2 9.09202 2.21799C8.71569 2.40973 8.40973 2.71569 8.21799 3.09202C8 3.51984 8 4.07989 8 5.2M7.2 22H12.8C13.9201 22 14.4802 22 14.908 21.782C15.2843 21.5903 15.5903 21.2843 15.782 20.908C16 20.4802 16 19.9201 16 18.8V9.2C16 8.07989 16 7.51984 15.782 7.09202C15.5903 6.71569 15.2843 6.40973 14.908 6.21799C14.4802 6 13.9201 6 12.8 6H7.2C6.0799 6 5.51984 6 5.09202 6.21799C4.71569 6.40973 4.40973 6.71569 4.21799 7.09202C4 7.51984 4 8.07989 4 9.2V18.8C4 19.9201 4 20.4802 4.21799 20.908C4.40973 21.2843 4.71569 21.5903 5.09202 21.782C5.51984 22 6.07989 22 7.2 22Z"
stroke="#7C7979"
stroke-width="2"
/>
</svg>
));

Componente ProductCard

Nuovo componente per la visualizzazione di prodotti con supporto per skeleton loading:

resources/js/shadowDomWidgetPreact.jsx
const ProductCard = memo(
({
id,
url,
addToCartUrl,
imageSrc,
imageAlt,
productTitle,
originalPrice,
discountPrice,
savings,
savingsPercentage,
variantsHtml,
isSkeleton = false,
language = "italian",
}) => {
const hasImage = Boolean(imageSrc);
const hasTitle = Boolean(productTitle);
const hasAnyPrice = Boolean(discountPrice || originalPrice || savings);

return (
<div className={`product-card ${isSkeleton ? "skeleton" : ""}`}>
<div className="product-image-container-wrapper">
<div className="product-image-container">
{hasImage ? (
<img
src={imageSrc}
alt={imageAlt}
className="product-image"
onError={(e) =>
(e.target.style.display = "none")
}
/>
) : (
<div className="skeleton-block skeleton-image" />
)}
</div>
</div>
<div className="product-content">
{hasTitle ? (
<h3 className="product-title">{productTitle}</h3>
) : (
<div className="skeleton-block skeleton-title" />
)}
<div className="product-price">
{discountPrice ? (
<span className="product-price-discount">
{discountPrice}
</span>
) : (
<div className="skeleton-block skeleton-price" />
)}
{originalPrice ? (
<span className="product-price-original">
{originalPrice}
</span>
) : hasAnyPrice ? null : (
<div className="skeleton-block skeleton-price small" />
)}
{savings ? (
<span
className="product-price-savings"
aria-label={`${__(
"product_price_savings_aria_label",
getLanguage(language) || "italian"
)} ${savings} (${savingsPercentage})`}
>
{savings} € ({savingsPercentage})
</span>
) : null}
</div>
{variantsHtml ? (
<div
className="product-variants"
dangerouslySetInnerHTML={{ __html: variantsHtml }}
/>
) : (
<div className="product-variants">
<div className="skeleton-block skeleton-variant" />
<div className="skeleton-block skeleton-variant short" />
</div>
)}
<div className="product-actions">
{addToCartUrl ? (
<a href={addToCartUrl} className="btn btn-primary">
{__(
"product_add_to_cart",
getLanguage(language) || "italian"
)}
</a>
) : (
<div className="btn btn-primary skeleton-block skeleton-button" />
)}
{url ? (
<a href={url} className="btn btn-outline">
{__(
"product_details",
getLanguage(language) || "italian"
)}
</a>
) : (
<div className="btn btn-outline skeleton-block skeleton-button" />
)}
</div>
</div>
</div>
);
}
);

Componente Message

Componente ottimizzato per la gestione dei messaggi con supporto per markdown e product cards:

resources/js/shadowDomWidgetPreact.jsx
const Message = memo(
({
type,
content,
textClass,
isBreathing = false,
language = "italian",
}) => {
const messageClass = `message ${type}-message ${textClass} ${
isBreathing ? "shimmer" : ""
}`;

if (type === "loading") {
return <LoadingIndicator textClass={textClass} />;
}

if (type === "system") {
return (
<div className={messageClass}>
<AssistantMarkdownContent
content={content}
textClass={textClass}
language={language}
/>
</div>
);
}

return (
<div
className={messageClass}
role={type === "user" ? "user" : "assistant"}
aria-label={
type === "user" ? "Your message" : "Assistant response"
}
>
{content}
</div>
);
}
);

Componente FAQ

Sistema FAQ migliorato con supporto per skeleton loading:

resources/js/shadowDomWidgetPreact.jsx
const FAQWrapper = memo(
({
questions,
onQuestionClick,
isRunning,
isInitial = false,
isSkeleton = false,
language = "italian",
}) => {
if (!isSkeleton && (!questions || questions.length === 0)) return null;

if (isSkeleton) {
return (
<div className={`faq-wrapper ${isInitial ? "initial" : ""}`}>
<p className="faq-title">{__("try_asking", language)}</p>
<div className="faq-container faq-skeleton">
<div className="skeleton-chip" />
<div className="skeleton-chip" />
<div className="skeleton-chip short" />
</div>
</div>
);
}

return (
<div className={`faq-wrapper ${isInitial ? "initial" : ""}`}>
<p className="faq-title">{__("try_asking", language)}</p>
<div className="faq-container">
{questions.map((question, index) => (
<FAQButton
key={index}
question={question}
onClick={onQuestionClick}
isRunning={isRunning}
/>
))}
</div>
</div>
);
}
);