feat: implmement feedback card and subcription section component
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import { clx } from "@medusajs/ui"
|
||||
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
|
||||
export default async function VtFeedbackCard({
|
||||
nodes,
|
||||
context,
|
||||
}: {
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = nodes.config ?? {}
|
||||
|
||||
const title: string = props.title ?? ""
|
||||
const items = props.items ?? []
|
||||
|
||||
const classes = {
|
||||
container: props.className ?? "",
|
||||
title: props.titleClassName ?? "text-[#003F31] text-[28px] font-semibold mb-10",
|
||||
grid: props.gridClassName ?? "grid grid-cols-1 small:grid-cols-2 xl:grid-cols-4 gap-6",
|
||||
card: props.cardClassName ?? "rounded-2xl overflow-hidden bg-[#CFECD9]",
|
||||
image: props.imageClassName ?? "w-full h-[260px] object-cover",
|
||||
content: props.contentClassName ?? "p-6",
|
||||
name: props.nameClassName ?? "text-[#003F31] text-[20px] font-bold",
|
||||
subtitle: props.subtitleClassName ?? "mt-1 text-[#003f31b3] text-[14px]",
|
||||
quote: props.quoteClassName ?? "mt-4 text-[#003F31] text-[16px]",
|
||||
cta: props.ctaClassName ?? "mt-6 inline-flex items-center justify-center bg-[#FCEE56] text-[#0D382E] px-6 py-2 rounded-full font-bold",
|
||||
}
|
||||
|
||||
if (!items || items.length === 0) return null
|
||||
|
||||
const renderCard = (entry: any, idx: number) => {
|
||||
const imageEl = entry.imageSrc ? (
|
||||
<img
|
||||
src={entry.imageSrc}
|
||||
alt={entry.imageAlt ?? entry.name ?? `feedback-card-${idx}`}
|
||||
className={classes.image}
|
||||
/>
|
||||
) : null
|
||||
|
||||
const ctaEl = entry.cta?.href ? (
|
||||
<LocalizedClientLink href={entry.cta.href} className={clx(classes.cta, entry.cta?.className)}>
|
||||
{entry.cta.label ?? "Mehr erfahren"}
|
||||
</LocalizedClientLink>
|
||||
) : entry.cta?.label ? (
|
||||
<button className={clx(classes.cta, entry.cta?.className)}>{entry.cta.label}</button>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<div className={clx(classes.card, entry.className)} key={`vt-feedback-card-${idx}`}>
|
||||
{imageEl}
|
||||
<div className={clx(classes.content)}>
|
||||
{entry.name && <div className={clx(classes.name, entry.nameClassName)}>{entry.name}</div>}
|
||||
{entry.subtitle && (
|
||||
<div className={clx(classes.subtitle, entry.subtitleClassName)}>{entry.subtitle}</div>
|
||||
)}
|
||||
{entry.quote && <div className={clx(classes.quote, entry.quoteClassName)}>{entry.quote}</div>}
|
||||
{ctaEl}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={classes.container}>
|
||||
{title && <h2 className={classes.title}>{title}</h2>}
|
||||
<div className={classes.grid}>{items.map((it: any, idx: number) => renderCard(it, idx))}</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
"use client"
|
||||
import { Button } from "@medusajs/ui"
|
||||
import { clx } from "@medusajs/ui"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
import React, { useState } from "react"
|
||||
|
||||
interface BenefitItem {
|
||||
icon?: string
|
||||
imgSrc?: string
|
||||
title?: string
|
||||
description?: string
|
||||
className?: string
|
||||
iconClassName?: string
|
||||
titleClassName?: string
|
||||
descriptionClassName?: string
|
||||
}
|
||||
|
||||
export default function VtSubcription({
|
||||
nodes,
|
||||
context,
|
||||
}: {
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = nodes.config ?? {}
|
||||
|
||||
const [firstName, setFirstName] = useState("")
|
||||
const [email, setEmail] = useState("")
|
||||
const [accepted, setAccepted] = useState(false)
|
||||
const [submitted, setSubmitted] = useState(false)
|
||||
|
||||
const classes = {
|
||||
container: props.className ?? "content-container",
|
||||
card: props.cardClassName ?? "rounded-2xl bg-[#CFECD9] p-8 small:p-12",
|
||||
title:
|
||||
props.titleClassName ??
|
||||
"text-[#003F31] text-[28px] font-bold text-center",
|
||||
description:
|
||||
props.descriptionClassName ?? "mt-2 text-center text-[#003F31]",
|
||||
highlight: props.highlightClassName ?? "font-bold",
|
||||
form: props.formClassName ?? "mt-8 flex flex-col gap-6",
|
||||
fields: props.fieldsClassName ?? "grid grid-cols-1 small:grid-cols-2 gap-4",
|
||||
input:
|
||||
props.inputClassName ??
|
||||
"h-[52px] rounded-md border border-[#003F31]/40 px-4 bg-transparent text-[#003F31]",
|
||||
checkboxRow: props.checkboxRowClassName ?? "flex items-center gap-3",
|
||||
checkbox:
|
||||
props.checkboxClassName ??
|
||||
"w-5 h-5 rounded-md border border-[#003F31]/60",
|
||||
checkboxLabel: props.checkboxLabelClassName ?? "text-[#003F31] text-[16px]",
|
||||
subtextClass: props.subtextClassName ?? "text-[#003F31]",
|
||||
submit:
|
||||
props.submitClassName ?? "",
|
||||
success: props.successClassName ?? "mt-4 text-center text-[#003F31]",
|
||||
benefits:
|
||||
props.benefitsClassName ??
|
||||
"mt-8 grid grid-cols-1 small:grid-cols-3 gap-8",
|
||||
benefitItem:
|
||||
props.benefitItemClassName ??
|
||||
"flex flex-col items-center text-center gap-3",
|
||||
benefitIcon:
|
||||
props.benefitIconClassName ??
|
||||
"w-12 h-12 rounded-full bg-[#003F31] text-white flex items-center justify-center",
|
||||
benefitTitle: props.benefitTitleClassName ?? "text-[#003F31] font-semibold",
|
||||
benefitDesc: props.benefitDescClassName ?? "text-[#003F31] opacity-80",
|
||||
}
|
||||
|
||||
const submitConfig = props.cta ?? {}
|
||||
const policyLabel: string =
|
||||
props.policyLabel ?? "Ich habe die DSGVO gelesen und akzeptiere sie."
|
||||
const firstNameField = props.firstName ?? null
|
||||
const emailField = props.email ?? null
|
||||
|
||||
const onSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (!accepted) return
|
||||
setSubmitted(true)
|
||||
console.log("subscription_submit", { firstName, email, accepted })
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={classes.container}>
|
||||
<div className={classes.card}>
|
||||
{props.title && <h2 className={classes.title}>{props.title}</h2>}
|
||||
{props.description && (
|
||||
<p className={classes.description}>
|
||||
{props.descriptionPrefix}{" "}
|
||||
<span className={classes.highlight}>
|
||||
{props.descriptionHighlight}
|
||||
</span>{" "}
|
||||
{props.descriptionSuffix}
|
||||
</p>
|
||||
)}
|
||||
{Array.isArray(props.benefits) && props.benefits.length > 0 && (
|
||||
<div className={classes.benefits}>
|
||||
{props.benefits.map((b: BenefitItem, i: number) => (
|
||||
<div
|
||||
key={`benefit-${i}`}
|
||||
className={clx(classes.benefitItem, b.className)}
|
||||
>
|
||||
{b.imgSrc ? (
|
||||
<img
|
||||
src={b.imgSrc}
|
||||
alt={b.title ?? `benefit-${i}`}
|
||||
className={clx(classes.benefitIcon, b.iconClassName)}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className={clx(classes.benefitIcon, b.iconClassName)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{b.icon ?? ""}
|
||||
</div>
|
||||
)}
|
||||
{b.title && (
|
||||
<div className={clx(classes.benefitTitle, b.titleClassName)}>
|
||||
{b.title}
|
||||
</div>
|
||||
)}
|
||||
{b.description && (
|
||||
<div
|
||||
className={clx(classes.benefitDesc, b.descriptionClassName)}
|
||||
>
|
||||
{b.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{props.subtext && (
|
||||
<p
|
||||
className={clx(
|
||||
"mt-2 text-center", classes?.subtextClass ?? "text-[#003F31]"
|
||||
)}
|
||||
>
|
||||
{props.subtext}
|
||||
</p>
|
||||
)}
|
||||
<form className={classes.form} onSubmit={onSubmit}>
|
||||
<div className={classes.fields}>
|
||||
{firstNameField && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={firstNameField.placeholder ?? "Vorname"}
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
className={clx(classes.input, firstNameField.className)}
|
||||
/>
|
||||
)}
|
||||
{emailField && (
|
||||
<input
|
||||
type="email"
|
||||
placeholder={emailField.placeholder ?? "E-Mail-Adresse"}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className={clx(classes.input, emailField.className)}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{props.newCheckboxRowClassName && (
|
||||
<label className={classes.checkboxRow}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={accepted}
|
||||
onChange={(e) => setAccepted(e.target.checked)}
|
||||
className={classes.checkbox}
|
||||
/>
|
||||
<span className={classes.checkboxLabel}>{policyLabel}</span>
|
||||
</label>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className={clx(classes.submit, submitConfig.className)}
|
||||
>
|
||||
{submitConfig.label ?? "Anmelden"}
|
||||
</button>
|
||||
</form>
|
||||
{submitted && (
|
||||
<div className={classes.success}>
|
||||
{props.successMessage ??
|
||||
"Danke! Prüfe deine E-Mails für den Rabattcode."}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -29,6 +29,8 @@ import VtFeaturedProducts from "@modules/home/components/vt-featured-products"
|
||||
import VtCategoryHighlight from "@modules/home/components/vt-category-highlight"
|
||||
import VtBrand from "@modules/home/components/vt-brand"
|
||||
import VtFeedback from "@modules/home/components/vt-feedback"
|
||||
import VtFeedbackCard from "@modules/home/components/vt-feedback-card"
|
||||
import VtSubcription from "@modules/home/components/vt-subcription"
|
||||
|
||||
type ComponentConfig = Record<string, any>
|
||||
|
||||
@@ -107,6 +109,8 @@ export const componentMap: Record<string, ComponentRenderer> = {
|
||||
VtCategoryHighlight: nodesContextRenderer(VtCategoryHighlight),
|
||||
VtBrand: nodesContextRenderer(VtBrand),
|
||||
VtFeedback: nodesContextRenderer(VtFeedback),
|
||||
VtFeedbackCard: nodesContextRenderer(VtFeedbackCard),
|
||||
VtSubcription: nodesContextRenderer(VtSubcription),
|
||||
}
|
||||
|
||||
export type ComponentName = keyof typeof componentMap
|
||||
|
||||
Reference in New Issue
Block a user