feat: open contact modal from vendor card

This commit is contained in:
Zotov Ivan Yuryevich
2026-04-21 22:19:29 +03:00
parent c9a199da91
commit 83330e9fd7
9 changed files with 57 additions and 12 deletions

View File

@@ -27,6 +27,7 @@ export interface ContactFormValues {
export interface ContactFormProps { export interface ContactFormProps {
visibleFields?: readonly ContactFormField[] visibleFields?: readonly ContactFormField[]
prefillValues?: Partial<ContactFormValues> prefillValues?: Partial<ContactFormValues>
submissionSource?: string
submitLabel?: string submitLabel?: string
className?: string className?: string
idPrefix?: string idPrefix?: string
@@ -42,6 +43,7 @@ function withPrefix(prefix: string, fieldName: string): string {
export function ContactForm({ export function ContactForm({
visibleFields = DEFAULT_VISIBLE_FIELDS, visibleFields = DEFAULT_VISIBLE_FIELDS,
prefillValues, prefillValues,
submissionSource,
submitLabel = 'Отправить заявку', submitLabel = 'Отправить заявку',
className = 'space-y-5', className = 'space-y-5',
idPrefix = 'contact-form', idPrefix = 'contact-form',
@@ -54,6 +56,7 @@ export function ContactForm({
return ( return (
<form className={className} onSubmit={onSubmit}> <form className={className} onSubmit={onSubmit}>
{submissionSource ? <input type="hidden" name="submissionSource" value={submissionSource} /> : null}
{showNameCompanyRow && ( {showNameCompanyRow && (
<div className="grid md:grid-cols-2 gap-5"> <div className="grid md:grid-cols-2 gap-5">
{fields.has('name') && ( {fields.has('name') && (

View File

@@ -12,6 +12,7 @@ interface ContactModalProps {
contacts: readonly SiteConfigContact[] contacts: readonly SiteConfigContact[]
formVisibleFields?: readonly ContactFormField[] formVisibleFields?: readonly ContactFormField[]
formPrefillValues?: Partial<ContactFormValues> formPrefillValues?: Partial<ContactFormValues>
submissionSource?: string
} }
const DEFAULT_MODAL_VISIBLE_FIELDS: readonly ContactFormField[] = [ const DEFAULT_MODAL_VISIBLE_FIELDS: readonly ContactFormField[] = [
@@ -31,6 +32,7 @@ export function ContactModal({
contacts, contacts,
formVisibleFields = DEFAULT_MODAL_VISIBLE_FIELDS, formVisibleFields = DEFAULT_MODAL_VISIBLE_FIELDS,
formPrefillValues, formPrefillValues,
submissionSource,
}: ContactModalProps) { }: ContactModalProps) {
useEffect(() => { useEffect(() => {
if (isOpen) { if (isOpen) {
@@ -74,6 +76,7 @@ export function ContactModal({
idPrefix="contact-modal-form" idPrefix="contact-modal-form"
visibleFields={formVisibleFields} visibleFields={formVisibleFields}
prefillValues={formPrefillValues} prefillValues={formPrefillValues}
submissionSource={submissionSource}
/> />
<div className="mt-8 pt-8 border-t border-gray-100"> <div className="mt-8 pt-8 border-t border-gray-100">

View File

@@ -2,9 +2,10 @@ interface VendorCardProps {
name?: string name?: string
description?: string description?: string
products?: string[] products?: string[]
onLearnMore?: (vendorName?: string) => void
} }
export function VendorCard({ name, description, products }: VendorCardProps) { export function VendorCard({ name, description, products, onLearnMore }: VendorCardProps) {
return ( return (
<div className="bg-white rounded-2xl p-8 hover:shadow-lg transition-all border border-gray-100 group flex flex-col"> <div className="bg-white rounded-2xl p-8 hover:shadow-lg transition-all border border-gray-100 group flex flex-col">
<div className="space-y-4 flex-1 flex flex-col"> <div className="space-y-4 flex-1 flex flex-col">
@@ -29,7 +30,11 @@ export function VendorCard({ name, description, products }: VendorCardProps) {
</div> </div>
)} )}
<button className="mt-auto w-full py-3 bg-blue-50 text-blue-600 rounded-xl hover:bg-blue-600 hover:text-white transition-all"> <button
type="button"
className="mt-auto w-full py-3 bg-blue-50 text-blue-600 rounded-xl hover:bg-blue-600 hover:text-white transition-all cursor-pointer"
onClick={() => onLearnMore?.(name)}
>
Узнать больше Узнать больше
</button> </button>
</div> </div>

View File

@@ -76,7 +76,7 @@ const vendors = (category.vendors?.filter(v => v != null) ?? []).map(vendor => (
<Layout> <Layout>
<div class="min-h-screen bg-white w-full overflow-x-hidden"> <div class="min-h-screen bg-white w-full overflow-x-hidden">
<Header client:load contacts={contacts} solutions={solutionCategories} services={serviceCategories} /> <Header client:load contacts={contacts} solutions={solutionCategories} services={serviceCategories} />
<CategoryView client:load badge={badge} title={title} description={description} vendors={vendors} /> <CategoryView client:load badge={badge} title={title} description={description} vendors={vendors} contacts={contacts} />
<Footer client:load contacts={contacts} solutionCategories={solutionCategories} /> <Footer client:load contacts={contacts} solutionCategories={solutionCategories} />
</div> </div>
</Layout> </Layout>

View File

@@ -6,9 +6,10 @@ export interface ContactProps {
contacts: readonly SiteConfigContact[] contacts: readonly SiteConfigContact[]
formVisibleFields?: readonly ContactFormField[] formVisibleFields?: readonly ContactFormField[]
formPrefillValues?: Partial<ContactFormValues> formPrefillValues?: Partial<ContactFormValues>
submissionSource?: string
} }
export function Contact({ contacts, formVisibleFields, formPrefillValues }: ContactProps) { export function Contact({ contacts, formVisibleFields, formPrefillValues, submissionSource }: ContactProps) {
return ( return (
<section className="py-4 bg-gray-50"> <section className="py-4 bg-gray-50">
<div className="max-w-225 mx-auto px-6"> <div className="max-w-225 mx-auto px-6">
@@ -24,6 +25,7 @@ export function Contact({ contacts, formVisibleFields, formPrefillValues }: Cont
idPrefix="contact-section-form" idPrefix="contact-section-form"
visibleFields={formVisibleFields} visibleFields={formVisibleFields}
prefillValues={formPrefillValues} prefillValues={formPrefillValues}
submissionSource={submissionSource}
/> />
</div> </div>

View File

@@ -218,7 +218,12 @@ export function Header({ contacts = [], solutions = [], services = [] }: HeaderP
</div> </div>
)} )}
</nav> </nav>
<ContactModal isOpen={isContactModalOpen} onClose={() => setIsContactModalOpen(false)} contacts={contacts} /> <ContactModal
isOpen={isContactModalOpen}
onClose={() => setIsContactModalOpen(false)}
contacts={contacts}
submissionSource="header_modal"
/>
</header> </header>
) )
} }

View File

@@ -8,15 +8,22 @@ interface Vendor {
interface VendorsProps { interface VendorsProps {
vendors?: Vendor[] vendors?: Vendor[]
onVendorLearnMore?: (vendorName?: string) => void
} }
export function Vendors({ vendors }: VendorsProps) { export function Vendors({ vendors, onVendorLearnMore }: VendorsProps) {
if (vendors && vendors.length > 0) { if (vendors && vendors.length > 0) {
return ( return (
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16 py-20"> <div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16 py-20">
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{vendors.map(({ name, description, products }, index) => ( {vendors.map(({ name, description, products }, index) => (
<VendorCard key={index} name={name} description={description} products={products} /> <VendorCard
key={index}
name={name}
description={description}
products={products}
onLearnMore={onVendorLearnMore}
/>
))} ))}
</div> </div>
</div> </div>

View File

@@ -1,18 +1,33 @@
import type { ComponentProps } from 'react' import { useState, type ComponentProps } from 'react'
import { CategoryPageHero } from '../sections/CategoryPageHero' import { CategoryPageHero } from '../sections/CategoryPageHero'
import { Vendors } from '../sections/Vendors' import { Vendors } from '../sections/Vendors'
import { ContactModal } from '../components/ContactModal'
import type { SiteConfigContact } from '../lib/cms/contact/types'
export type CategoryViewProps = { export type CategoryViewProps = {
title?: string title?: string
description?: string description?: string
badge?: string badge?: string
contacts?: readonly SiteConfigContact[]
} & ComponentProps<typeof Vendors> } & ComponentProps<typeof Vendors>
export function CategoryView({ title, description, vendors = [], badge }: CategoryViewProps) { export function CategoryView({ title, description, vendors = [], badge, contacts = [] }: CategoryViewProps) {
const [isContactModalOpen, setIsContactModalOpen] = useState(false)
const handleVendorLearnMore = () => {
setIsContactModalOpen(true)
}
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<CategoryPageHero badge={badge} title={title} description={description} /> <CategoryPageHero badge={badge} title={title} description={description} />
<Vendors vendors={vendors} /> <Vendors vendors={vendors} onVendorLearnMore={handleVendorLearnMore} />
<ContactModal
isOpen={isContactModalOpen}
onClose={() => setIsContactModalOpen(false)}
contacts={contacts}
submissionSource="vendor_card_modal"
/>
</div> </div>
) )
} }

View File

@@ -85,9 +85,14 @@ export function HomePage({ homePage, contacts = [] }: HomePageProps) {
<About data={about} /> <About data={about} />
</div> </div>
<div id="contact" style={{ scrollMarginTop: HEADER_OFFSET_PX }}> <div id="contact" style={{ scrollMarginTop: HEADER_OFFSET_PX }}>
<Contact contacts={contacts} /> <Contact contacts={contacts} submissionSource="home_contact_section" />
</div> </div>
<ContactModal isOpen={contactOpen} onClose={() => setContactOpen(false)} contacts={contacts} /> <ContactModal
isOpen={contactOpen}
onClose={() => setContactOpen(false)}
contacts={contacts}
submissionSource="home_modal"
/>
</> </>
) )
} }