rework components
This commit is contained in:
+8
-8
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"navigation": {
|
||||
"platform": "Platforma",
|
||||
"demos": "Dema",
|
||||
"demos": "Demo",
|
||||
"features": "Funkcje",
|
||||
"faq": "FAQ",
|
||||
"contact": "Kontakt",
|
||||
@@ -79,9 +79,9 @@
|
||||
},
|
||||
"demos": {
|
||||
"desk": {
|
||||
"slug": "demo",
|
||||
"slug": "demo-biurko",
|
||||
"eyebrow": "Demo produktowe",
|
||||
"title": "Konfigurator 3D, który sprzedaje za Ciebie",
|
||||
"title": "Konfigurator - Biurko",
|
||||
"intro": "Pozwalaj klientom tworzyć własne biurka regulowane w czasie rzeczywistym.",
|
||||
"description": "Klient może zobaczyć swoje biurko w naturalnym oświetleniu, obrócić je o 360 stopni, powiększyć detale, zmienić dodatki i sprawdzić jak całość będzie wyglądała w jego przestrzeni.",
|
||||
"imageUrl": "https://backend.ultifide.com/uploads/offer/page-header-element/optimized/b64b9dddfb838bfafea9da0ee5c04c68.png?width=1000&height=1000",
|
||||
@@ -117,9 +117,9 @@
|
||||
}
|
||||
},
|
||||
"room": {
|
||||
"slug": "demo-room",
|
||||
"slug": "demo-pokoj",
|
||||
"eyebrow": "Demo wnętrzarskie",
|
||||
"title": "Konfigurator 3D, który sprzedaje całe wnętrza",
|
||||
"title": "Konfigurator - Wnętrze",
|
||||
"intro": "Twórz i prezentuj kompletne aranżacje pomieszczeń oraz meble w interaktywnym środowisku 3D.",
|
||||
"description": "Klient może zaprojektować pokój od podstaw, ustawić meble, zmienić kolory ścian, materiały, dodatki i zobaczyć wszystko w realistycznym oświetleniu.",
|
||||
"imageUrl": "https://backend.ultifide.com/uploads/offer/page-header-element/optimized/e78aac1388ff07761422e12272690878.png?width=1000&height=1000",
|
||||
@@ -155,12 +155,12 @@
|
||||
}
|
||||
},
|
||||
"door": {
|
||||
"slug": "demo-door",
|
||||
"slug": "demo-drzwi",
|
||||
"eyebrow": "Demo produktowe",
|
||||
"title": "Konfigurator 3D drzwi dla producentów i sprzedawców",
|
||||
"title": "Konfigurator - Drzwi",
|
||||
"intro": "Pozwól klientom dobrać model, kolor, przeszklenie, klamkę i detale drzwi w interaktywnym podglądzie 3D.",
|
||||
"description": "Konfigurator drzwi ułatwia sprzedaż produktów z wieloma wariantami. Klient widzi efekt wyborów od razu, a konfiguracja może zostać przekazana do koszyka, zapytania ofertowego albo systemu sprzedaży.",
|
||||
"imageUrl": "/demo-door-preview.svg",
|
||||
"imageUrl": "/demo-door-preview.png",
|
||||
"openLabel": "Otwórz konfigurator drzwi",
|
||||
"benefitsIntro": "Dla firm, które sprzedają drzwi, fronty, zabudowy i produkty wymagające precyzyjnego dopasowania wariantów.",
|
||||
"stats": [
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -1,31 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 420" fill="none">
|
||||
<rect width="640" height="420" rx="28" fill="#F9FAFE"/>
|
||||
<path d="M78 128c0-24 19-43 43-43h398c24 0 43 19 43 43v214H78V128Z" fill="#fff" stroke="#001C44" stroke-width="10"/>
|
||||
<path d="M78 128c0-24 19-43 43-43h398c24 0 43 19 43 43v34H78v-34Z" fill="#42A6FF" opacity=".72"/>
|
||||
<circle cx="120" cy="124" r="9" fill="#F9FAFE"/>
|
||||
<circle cx="151" cy="124" r="9" fill="#F9FAFE"/>
|
||||
<circle cx="182" cy="124" r="9" fill="#F9FAFE"/>
|
||||
<rect x="112" y="194" width="118" height="112" rx="14" fill="#E3F3FF"/>
|
||||
<path d="M145 281V211l53 13v70l-53-13Z" fill="#fff" stroke="#001C44" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M145 211l38-16 53 13-38 16-53-13Z" fill="#42A6FF" stroke="#001C44" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M198 224l38-16v70l-38 16v-70Z" fill="#DDF1FF" stroke="#001C44" stroke-width="8" stroke-linejoin="round"/>
|
||||
<circle cx="186" cy="253" r="5" fill="#001C44"/>
|
||||
<rect x="266" y="194" width="248" height="36" rx="18" fill="#DDF1FF"/>
|
||||
<rect x="266" y="251" width="204" height="22" rx="11" fill="#B9DFFF"/>
|
||||
<rect x="266" y="292" width="162" height="22" rx="11" fill="#B9DFFF"/>
|
||||
<circle cx="499" cy="292" r="43" fill="#42A6FF"/>
|
||||
<path d="M499 249a43 43 0 0 1 39 61l-39-18v-43Z" fill="#FF9F2F"/>
|
||||
<rect x="114" y="332" width="404" height="10" rx="5" fill="#001C44" opacity=".18"/>
|
||||
<path d="M413 344c0-36 29-65 65-65s65 29 65 65v31H413v-31Z" fill="#42A6FF" opacity=".22"/>
|
||||
<path d="M491 238c-6-17-1-35 12-45 18-14 45-8 57 14 8 14 8 32 1 46l-16 31-26-25c-13 1-24-7-28-21Z" fill="#001C44"/>
|
||||
<path d="M456 384v-72c0-16 13-29 29-29h22c16 0 29 13 29 29v72" fill="#2B7BB9"/>
|
||||
<path d="M455 328l-36 33" stroke="#2B7BB9" stroke-width="18" stroke-linecap="round"/>
|
||||
<path d="M535 327l34 30" stroke="#2B7BB9" stroke-width="18" stroke-linecap="round"/>
|
||||
<path d="M456 384h31l-3-62h-28v62Z" fill="#001C44"/>
|
||||
<path d="M505 384h31v-62h-28l-3 62Z" fill="#001C44"/>
|
||||
<circle cx="516" cy="230" r="22" fill="#FFCF9D"/>
|
||||
<rect x="71" y="284" width="104" height="104" rx="18" fill="#fff" stroke="#001C44" stroke-width="8"/>
|
||||
<path d="M101 349v-47l44 22-44 25Z" fill="#42A6FF" opacity=".28" stroke="#001C44" stroke-width="7" stroke-linejoin="round"/>
|
||||
<path d="M101 302l44 22 16-28-44-22-16 28Z" fill="#F9FAFE" stroke="#001C44" stroke-width="7" stroke-linejoin="round"/>
|
||||
<path d="M145 324l16-28v48l-16 28v-48Z" fill="#42A6FF" opacity=".5" stroke="#001C44" stroke-width="7" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,7 +1,8 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {DemoBrowser} from '@/components/DemoBrowser';
|
||||
import {DemoSubnav} from '@/components/DemoSubnav';
|
||||
import {SectionHeader} from '@/components/SectionHeader';
|
||||
import {demoContent} from '@/config/content';
|
||||
import {orderedDemos} from '@/config/content';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Dema konfiguratorów 3D | Ultifide',
|
||||
@@ -9,8 +10,6 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
export default function DemosPage() {
|
||||
const demos = [demoContent.desk, demoContent.room, demoContent.door];
|
||||
|
||||
return (
|
||||
<main>
|
||||
<section className="sectionBand sectionBand--light demosPage">
|
||||
@@ -20,7 +19,8 @@ export default function DemosPage() {
|
||||
title="Sprawdź konfiguratory 3D w jednym miejscu"
|
||||
description="Wybierz demo z listy, przetestuj je od razu w podglądzie, a potem przejdź do strony z opisem zastosowania i korzyści."
|
||||
/>
|
||||
<DemoBrowser demos={demos} />
|
||||
<DemoSubnav demos={orderedDemos} hrefType="browser" />
|
||||
<DemoBrowser demos={orderedDemos} />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {DemoPage} from '@/components/DemoPage';
|
||||
import {demoContent} from '@/config/content';
|
||||
import messages from '../../../../messages/pl.json';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: messages.metadata.deskTitle,
|
||||
description:
|
||||
'Poznaj konfigurator 3D, który zwiększa konwersję i pozwala klientom tworzyć własne biurka regulowane w czasie rzeczywistym.'
|
||||
};
|
||||
|
||||
export default function DeskDemoPage() {
|
||||
return <DemoPage demo={demoContent.desk} />;
|
||||
}
|
||||
@@ -1,14 +1,5 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {DemoPage} from '@/components/DemoPage';
|
||||
import {demoContent} from '@/config/content';
|
||||
import messages from '../../../../messages/pl.json';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: messages.metadata.doorTitle,
|
||||
description:
|
||||
'Poznaj konfigurator 3D drzwi, który pozwala klientom dobrać model, kolor, przeszklenie, klamkę i detale w czasie rzeczywistym.'
|
||||
};
|
||||
import {redirect} from 'next/navigation';
|
||||
|
||||
export default function DoorDemoPage() {
|
||||
return <DemoPage demo={demoContent.door} />;
|
||||
redirect('/pl/demo-drzwi');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {DemoPage} from '@/components/DemoPage';
|
||||
import {demoContent} from '@/config/content';
|
||||
import messages from '../../../../messages/pl.json';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: messages.metadata.doorTitle,
|
||||
description:
|
||||
'Poznaj konfigurator 3D drzwi, który pozwala klientom dobrać model, kolor, przeszklenie, klamkę i detale w czasie rzeczywistym.'
|
||||
};
|
||||
|
||||
export default function DoorDemoPage() {
|
||||
return <DemoPage demo={demoContent.door} />;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {DemoPage} from '@/components/DemoPage';
|
||||
import {demoContent} from '@/config/content';
|
||||
import messages from '../../../../messages/pl.json';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: messages.metadata.roomTitle,
|
||||
description:
|
||||
'Poznaj konfigurator 3D, który pozwala klientom projektować całe pomieszczenia i zestawy mebli w czasie rzeczywistym.'
|
||||
};
|
||||
|
||||
export default function RoomDemoPage() {
|
||||
return <DemoPage demo={demoContent.room} />;
|
||||
}
|
||||
@@ -1,14 +1,5 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {DemoPage} from '@/components/DemoPage';
|
||||
import {demoContent} from '@/config/content';
|
||||
import messages from '../../../../messages/pl.json';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: messages.metadata.roomTitle,
|
||||
description:
|
||||
'Poznaj konfigurator 3D, który pozwala klientom projektować całe pomieszczenia i zestawy mebli w czasie rzeczywistym.'
|
||||
};
|
||||
import {redirect} from 'next/navigation';
|
||||
|
||||
export default function RoomDemoPage() {
|
||||
return <DemoPage demo={demoContent.room} />;
|
||||
redirect('/pl/demo-pokoj');
|
||||
}
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {DemoPage} from '@/components/DemoPage';
|
||||
import {demoContent} from '@/config/content';
|
||||
import messages from '../../../../messages/pl.json';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: messages.metadata.deskTitle,
|
||||
description:
|
||||
'Poznaj konfigurator 3D, który zwiększa konwersję i pozwala klientom tworzyć własne biurka regulowane w czasie rzeczywistym.'
|
||||
};
|
||||
import {redirect} from 'next/navigation';
|
||||
|
||||
export default function DeskDemoPage() {
|
||||
return <DemoPage demo={demoContent.desk} />;
|
||||
redirect('/pl/demo-biurko');
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type {Metadata} from 'next';
|
||||
import {Poppins} from 'next/font/google';
|
||||
import localFont from 'next/font/local';
|
||||
import {NextIntlClientProvider} from 'next-intl';
|
||||
import {getMessages} from 'next-intl/server';
|
||||
import {notFound} from 'next/navigation';
|
||||
import {ContactSection} from '@/components/ContactSection';
|
||||
import {Footer} from '@/components/Footer';
|
||||
import {Header} from '@/components/Header';
|
||||
import {routing} from '@/i18n/routing';
|
||||
@@ -12,9 +13,24 @@ type LocaleLayoutProps = {
|
||||
params: Promise<{locale: string}>;
|
||||
};
|
||||
|
||||
const poppins = Poppins({
|
||||
subsets: ['latin', 'latin-ext'],
|
||||
weight: ['400', '600', '700'],
|
||||
const poppins = localFont({
|
||||
src: [
|
||||
{
|
||||
path: '../fonts/Poppins-Regular.ttf',
|
||||
weight: '400',
|
||||
style: 'normal'
|
||||
},
|
||||
{
|
||||
path: '../fonts/Poppins-SemiBold.ttf',
|
||||
weight: '600',
|
||||
style: 'normal'
|
||||
},
|
||||
{
|
||||
path: '../fonts/Poppins-Bold.ttf',
|
||||
weight: '700',
|
||||
style: 'normal'
|
||||
}
|
||||
],
|
||||
display: 'swap'
|
||||
});
|
||||
|
||||
@@ -44,6 +60,7 @@ export default async function LocaleLayout({children, params}: LocaleLayoutProps
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<Header />
|
||||
{children}
|
||||
<ContactSection />
|
||||
<Footer />
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
|
||||
+12
-15
@@ -7,8 +7,7 @@ import {Faq} from '@/components/Faq';
|
||||
import {HeroVisual} from '@/components/HeroVisual';
|
||||
import {SectionHeader} from '@/components/SectionHeader';
|
||||
import {Stats} from '@/components/Stats';
|
||||
import {contact} from '@/config/contact';
|
||||
import {demoContent, homeContent, marketingFaq} from '@/config/content';
|
||||
import {homeContent, marketingFaq, orderedDemos} from '@/config/content';
|
||||
import messages from '../../../messages/pl.json';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -20,8 +19,6 @@ const platformIcons = [Settings2, Workflow, Plug, ShoppingCart];
|
||||
const industryIcons = [Box, Layers3, CheckCircle2];
|
||||
|
||||
export default function HomePage() {
|
||||
const demos = [demoContent.desk, demoContent.room, demoContent.door];
|
||||
|
||||
return (
|
||||
<main>
|
||||
<section className="hero sectionBand sectionBand--light">
|
||||
@@ -32,7 +29,7 @@ export default function HomePage() {
|
||||
<p>{homeContent.hero.description}</p>
|
||||
<div className="buttonRow">
|
||||
<ButtonLink href="/pl/dema">{homeContent.hero.primaryCta}</ButtonLink>
|
||||
<ButtonLink href={contact.url} variant="secondary" isExternal>
|
||||
<ButtonLink href="#kontakt" variant="secondary">
|
||||
{homeContent.hero.secondaryCta}
|
||||
</ButtonLink>
|
||||
</div>
|
||||
@@ -41,17 +38,11 @@ export default function HomePage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="sectionBand statsBand">
|
||||
<div className="container">
|
||||
<Stats stats={homeContent.stats} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="dema" className="sectionBand">
|
||||
<section id="dema" className="sectionBand demosBand">
|
||||
<div className="container">
|
||||
<SectionHeader title={homeContent.demosTitle} description={homeContent.demosDescription} />
|
||||
<div className="demoCards">
|
||||
{demos.map(demo => (
|
||||
{orderedDemos.map(demo => (
|
||||
<article className="demoCard" key={demo.slug}>
|
||||
{demo.imageUrl ? (
|
||||
<Image src={demo.imageUrl} alt="" width={420} height={420} />
|
||||
@@ -93,7 +84,13 @@ export default function HomePage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="sectionBand">
|
||||
<section className="sectionBand statsBand">
|
||||
<div className="container">
|
||||
<Stats stats={homeContent.stats} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="sectionBand industriesBand">
|
||||
<div className="container">
|
||||
<SectionHeader title={homeContent.industriesTitle} />
|
||||
<div className="featureGrid featureGrid--three">
|
||||
@@ -151,7 +148,7 @@ export default function HomePage() {
|
||||
<div className="container finalCta">
|
||||
<h2>{homeContent.finalCta.title}</h2>
|
||||
<p>{homeContent.finalCta.description}</p>
|
||||
<ButtonLink href={contact.url} isExternal>
|
||||
<ButtonLink href="#kontakt">
|
||||
{homeContent.finalCta.button}
|
||||
</ButtonLink>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+237
-14
@@ -42,18 +42,21 @@ p {
|
||||
h1 {
|
||||
max-width: 780px;
|
||||
font-size: clamp(1.95rem, 3vw, 2.85rem);
|
||||
font-weight: 600;
|
||||
line-height: 1.14;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(2rem, 4vw, 3.35rem);
|
||||
font-weight: 600;
|
||||
line-height: 1.12;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: clamp(1.1rem, 2vw, 1.35rem);
|
||||
font-weight: 600;
|
||||
line-height: 1.28;
|
||||
}
|
||||
|
||||
@@ -87,6 +90,10 @@ p {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.sectionBand--compact {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.siteHeader {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@@ -160,7 +167,7 @@ p {
|
||||
border-radius: 6px;
|
||||
padding: 0 18px;
|
||||
border: 1px solid transparent;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
transition:
|
||||
background 160ms ease,
|
||||
@@ -168,17 +175,27 @@ p {
|
||||
border-color 160ms ease;
|
||||
}
|
||||
|
||||
.siteHeader__contact,
|
||||
.button--primary {
|
||||
background: $primary;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.siteHeader__contact:hover,
|
||||
.button--primary:hover {
|
||||
background: #208de8;
|
||||
}
|
||||
|
||||
.siteHeader__contact {
|
||||
border-color: rgba(0, 28, 68, 0.18);
|
||||
background: $white;
|
||||
color: $secondary;
|
||||
}
|
||||
|
||||
.siteHeader__contact:hover,
|
||||
.siteHeader__contact.isActive {
|
||||
border-color: $primary;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.button--secondary {
|
||||
border-color: rgba(0, 28, 68, 0.18);
|
||||
background: $white;
|
||||
@@ -243,7 +260,7 @@ p {
|
||||
.eyebrow {
|
||||
color: $primary;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@@ -370,7 +387,7 @@ p {
|
||||
|
||||
.heroVisual__panel > div:first-child {
|
||||
color: $secondary;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.heroVisual__option {
|
||||
@@ -451,7 +468,7 @@ p {
|
||||
border-bottom: 1px solid rgba(0, 28, 68, 0.1);
|
||||
color: $secondary;
|
||||
font-size: 0.92rem;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.demoFrame__bar > span {
|
||||
@@ -500,7 +517,7 @@ p {
|
||||
place-items: center;
|
||||
padding: 24px;
|
||||
color: $secondary;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -511,7 +528,7 @@ p {
|
||||
}
|
||||
|
||||
.statsBand {
|
||||
padding: clamp(28px, 4vw, 52px) 0;
|
||||
padding: clamp(34px, 4vw, 58px) 0 clamp(24px, 3vw, 42px);
|
||||
}
|
||||
|
||||
.statItem {
|
||||
@@ -607,6 +624,7 @@ p {
|
||||
}
|
||||
|
||||
.demoBrowser__item strong {
|
||||
font-weight: 600;
|
||||
font-size: 0.93rem;
|
||||
line-height: 1.32;
|
||||
}
|
||||
@@ -657,6 +675,14 @@ p {
|
||||
margin-bottom: 34px;
|
||||
}
|
||||
|
||||
.demosBand {
|
||||
padding-top: clamp(28px, 4vw, 52px);
|
||||
}
|
||||
|
||||
.industriesBand {
|
||||
padding-top: clamp(34px, 4vw, 56px);
|
||||
}
|
||||
|
||||
.demoCards,
|
||||
.featureGrid,
|
||||
.processGrid {
|
||||
@@ -710,7 +736,9 @@ p {
|
||||
|
||||
.demoCard > div {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr auto;
|
||||
gap: 14px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.demoCard p:not(.eyebrow),
|
||||
@@ -722,9 +750,98 @@ p {
|
||||
.demoCard a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
align-self: end;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
color: $primary;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.demoSubnav {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.demoSubnav a {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
min-height: 78px;
|
||||
align-content: center;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid rgba(0, 28, 68, 0.1);
|
||||
border-radius: 8px;
|
||||
background: $white;
|
||||
color: $secondary;
|
||||
font-size: 0.94rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.demoSubnav a span {
|
||||
color: $primary;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.demoSubnav a.isActive {
|
||||
border-color: rgba(66, 166, 255, 0.7);
|
||||
background: rgba(66, 166, 255, 0.09);
|
||||
box-shadow: 0 12px 30px rgba(0, 28, 68, 0.08);
|
||||
}
|
||||
|
||||
.demoSubnavBar {
|
||||
position: sticky;
|
||||
top: 78px;
|
||||
z-index: 19;
|
||||
border-bottom: 1px solid rgba(0, 28, 68, 0.1);
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.demoSubnavBar .demoSubnav {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.demoSubnavBar .demoSubnav a {
|
||||
min-height: 52px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
color: rgba(0, 28, 68, 0.78);
|
||||
font-size: 0.9rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.demoSubnavBar .demoSubnav a span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.demoSubnavBar .demoSubnav a.isActive {
|
||||
position: relative;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.demoSubnavBar .demoSubnav a.isActive::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 3px;
|
||||
border-radius: 999px 999px 0 0;
|
||||
background: $primary;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.splitSection {
|
||||
@@ -808,7 +925,7 @@ p {
|
||||
border-radius: 6px;
|
||||
background: $primary;
|
||||
color: $white;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.faqList {
|
||||
@@ -825,7 +942,7 @@ p {
|
||||
.faqList summary {
|
||||
cursor: pointer;
|
||||
padding: 20px 22px;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.faqList p {
|
||||
@@ -857,6 +974,92 @@ p {
|
||||
grid-template-columns: minmax(0, 1.1fr) minmax(300px, 0.9fr);
|
||||
}
|
||||
|
||||
.contactSection {
|
||||
scroll-margin-top: 88px;
|
||||
background: $tertiary;
|
||||
}
|
||||
|
||||
.contactSection__grid {
|
||||
display: grid;
|
||||
gap: clamp(18px, 3vw, 28px);
|
||||
}
|
||||
|
||||
.contactSection__intro {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(320px, 0.5fr);
|
||||
gap: clamp(28px, 5vw, 72px);
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.contactSection__intro > div:first-child {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.contactSection__grid h2 {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.contactSection__grid p:not(.eyebrow) {
|
||||
max-width: 640px;
|
||||
color: $gray-light;
|
||||
}
|
||||
|
||||
.contactSection__schedulerWrap {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.contactSection__scheduler {
|
||||
width: 100%;
|
||||
height: 555px;
|
||||
max-height: 555px;
|
||||
border: 1px solid rgba(0, 28, 68, 0.1);
|
||||
border-radius: 8px;
|
||||
background: $tertiary;
|
||||
}
|
||||
|
||||
.contactSection__details {
|
||||
display: grid;
|
||||
align-self: end;
|
||||
gap: 14px;
|
||||
padding: clamp(22px, 3vw, 30px);
|
||||
border: 1px solid rgba(0, 28, 68, 0.1);
|
||||
border-radius: 8px;
|
||||
background: $white;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.contactSection__details > div {
|
||||
display: grid;
|
||||
grid-template-columns: 24px 1fr;
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.contactSection__details svg {
|
||||
margin-top: 2px;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.contactSection__details span {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.contactSection__details strong,
|
||||
.contactSection__details a {
|
||||
color: $secondary;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.contactSection__fallback {
|
||||
justify-self: start;
|
||||
color: $primary;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: $secondary;
|
||||
color: $white;
|
||||
@@ -865,14 +1068,13 @@ p {
|
||||
|
||||
.footer__inner {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 0.8fr 0.9fr auto;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 32px;
|
||||
width: min(1180px, calc(100% - 40px));
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.footer__brand,
|
||||
.footer__nav,
|
||||
.footer__contact,
|
||||
.footer__social {
|
||||
display: grid;
|
||||
@@ -890,6 +1092,12 @@ p {
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
}
|
||||
|
||||
.footer__contact {
|
||||
justify-self: end;
|
||||
font-style: normal;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.footer__social {
|
||||
grid-auto-flow: column;
|
||||
}
|
||||
@@ -937,13 +1145,26 @@ p {
|
||||
|
||||
.siteHeader__mobileNav a {
|
||||
padding: 14px 0;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.demoSubnavBar {
|
||||
top: 68px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.demoSubnavBar .demoSubnav {
|
||||
width: max-content;
|
||||
min-width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.hero__grid,
|
||||
.demoHero__grid,
|
||||
.demoBrowser,
|
||||
.splitSection,
|
||||
.contactSection__grid,
|
||||
.contactSection__intro,
|
||||
.footer__inner {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -958,6 +1179,7 @@ p {
|
||||
|
||||
.featureGrid--three,
|
||||
.demoCards,
|
||||
.demoSubnav,
|
||||
.processGrid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
@@ -995,6 +1217,7 @@ p {
|
||||
|
||||
.statsGrid,
|
||||
.demoCards,
|
||||
.demoSubnav,
|
||||
.demoBrowser__list,
|
||||
.featureGrid,
|
||||
.featureGrid--three,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import {Mail, Phone} from 'lucide-react';
|
||||
import {contact} from '@/config/contact';
|
||||
|
||||
const schedulerUrl = 'https://42min.us/kubapyla';
|
||||
|
||||
function phoneHref(phone: string) {
|
||||
return `tel:${phone.replaceAll(' ', '')}`;
|
||||
}
|
||||
|
||||
export function ContactSection() {
|
||||
return (
|
||||
<section id="kontakt" className="contactSection sectionBand">
|
||||
<div className="container contactSection__grid">
|
||||
<div className="contactSection__intro">
|
||||
<div>
|
||||
<p className="eyebrow">Kontakt</p>
|
||||
<h2>Porozmawiajmy o konfiguratorze 3D</h2>
|
||||
<p>Opisz katalog produktów, zakres wariantów albo pierwszy pomysł na demo. Wrócimy z konkretną ścieżką wdrożenia.</p>
|
||||
</div>
|
||||
<address className="contactSection__details">
|
||||
<div>
|
||||
<Mail size={22} />
|
||||
<a href={`mailto:${contact.email}`}>{contact.email}</a>
|
||||
</div>
|
||||
<div>
|
||||
<Phone size={22} />
|
||||
<span>
|
||||
{contact.phones.map(phone => (
|
||||
<a href={phoneHref(phone)} key={phone}>
|
||||
{phone}
|
||||
</a>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
</address>
|
||||
</div>
|
||||
<div className="contactSection__schedulerWrap">
|
||||
<iframe
|
||||
className="contactSection__scheduler"
|
||||
src={schedulerUrl}
|
||||
title="Umów rozmowę z Ultifide"
|
||||
loading="lazy"
|
||||
referrerPolicy="strict-origin-when-cross-origin"
|
||||
/>
|
||||
<a className="contactSection__fallback" href={schedulerUrl} target="_blank" rel="noreferrer">
|
||||
Umów rozmowę online w kalendarzu
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import {ArrowRight, DoorOpen} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import {useRouter, useSearchParams} from 'next/navigation';
|
||||
import {useMemo, useState} from 'react';
|
||||
import {useMemo} from 'react';
|
||||
import {DemoFrame} from './DemoFrame';
|
||||
import type {DemoContent} from '@/types/content';
|
||||
|
||||
@@ -12,7 +12,7 @@ type DemoBrowserProps = {
|
||||
demos: DemoContent[];
|
||||
};
|
||||
|
||||
function isKnownDemo(slug: string | null, demos: DemoContent[]) {
|
||||
function isKnownDemo(slug: string | null, demos: DemoContent[]): slug is DemoContent['slug'] {
|
||||
return demos.some(demo => demo.slug === slug);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ export function DemoBrowser({demos}: DemoBrowserProps) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const requestedDemo = searchParams.get('demo');
|
||||
const initialSlug = isKnownDemo(requestedDemo, demos) ? requestedDemo : demos[0]?.slug;
|
||||
const [activeSlug, setActiveSlug] = useState(initialSlug);
|
||||
const activeSlug = isKnownDemo(requestedDemo, demos) ? requestedDemo : demos[0].slug;
|
||||
|
||||
const activeDemo = useMemo(
|
||||
() => demos.find(demo => demo.slug === activeSlug) ?? demos[0],
|
||||
@@ -29,7 +28,6 @@ export function DemoBrowser({demos}: DemoBrowserProps) {
|
||||
);
|
||||
|
||||
function selectDemo(slug: DemoContent['slug']) {
|
||||
setActiveSlug(slug);
|
||||
router.replace(`/pl/dema?demo=${slug}`, {scroll: false});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ import {CheckCircle2, DoorOpen} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import {ButtonLink} from './ButtonLink';
|
||||
import {DemoFrame} from './DemoFrame';
|
||||
import {DemoSubnav} from './DemoSubnav';
|
||||
import {Faq} from './Faq';
|
||||
import {SectionHeader} from './SectionHeader';
|
||||
import {Stats} from './Stats';
|
||||
import {contact} from '@/config/contact';
|
||||
import {orderedDemos} from '@/config/content';
|
||||
import type {DemoContent} from '@/types/content';
|
||||
|
||||
type DemoPageProps = {
|
||||
@@ -14,7 +15,14 @@ type DemoPageProps = {
|
||||
|
||||
export function DemoPage({demo}: DemoPageProps) {
|
||||
return (
|
||||
<main>
|
||||
<>
|
||||
<div className="demoSubnavBar">
|
||||
<div className="container">
|
||||
<DemoSubnav demos={orderedDemos} activeSlug={demo.slug} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<section className="demoHero sectionBand sectionBand--light">
|
||||
<div className="container demoHero__grid">
|
||||
<div>
|
||||
@@ -24,7 +32,7 @@ export function DemoPage({demo}: DemoPageProps) {
|
||||
<p>{demo.description}</p>
|
||||
<div className="buttonRow">
|
||||
<ButtonLink href="#podglad">Zobacz demo</ButtonLink>
|
||||
<ButtonLink href={contact.url} variant="secondary" isExternal>
|
||||
<ButtonLink href="#kontakt" variant="secondary">
|
||||
Porozmawiajmy
|
||||
</ButtonLink>
|
||||
</div>
|
||||
@@ -70,7 +78,7 @@ export function DemoPage({demo}: DemoPageProps) {
|
||||
<div className="container finalCta finalCta--dark">
|
||||
<h2>{demo.cta.title}</h2>
|
||||
<p>{demo.cta.description}</p>
|
||||
<ButtonLink href={contact.url} isExternal>
|
||||
<ButtonLink href="#kontakt">
|
||||
{demo.cta.button}
|
||||
</ButtonLink>
|
||||
</div>
|
||||
@@ -82,6 +90,7 @@ export function DemoPage({demo}: DemoPageProps) {
|
||||
<Faq items={demo.faq} />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import {usePathname, useSearchParams} from 'next/navigation';
|
||||
import type {DemoContent} from '@/types/content';
|
||||
|
||||
type DemoSubnavProps = {
|
||||
demos: DemoContent[];
|
||||
activeSlug?: DemoContent['slug'];
|
||||
hrefType?: 'page' | 'browser';
|
||||
};
|
||||
|
||||
export function DemoSubnav({demos, activeSlug, hrefType = 'page'}: DemoSubnavProps) {
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const selectedSlug = activeSlug ?? searchParams.get('demo') ?? demos[0]?.slug;
|
||||
|
||||
return (
|
||||
<nav className="demoSubnav" aria-label="Dema konfiguratorów">
|
||||
{demos.map(demo => {
|
||||
const isActive = selectedSlug === demo.slug || pathname === `/pl/${demo.slug}`;
|
||||
const href = hrefType === 'browser' ? `/pl/dema?demo=${demo.slug}` : `/pl/${demo.slug}`;
|
||||
|
||||
return (
|
||||
<Link className={isActive ? 'isActive' : undefined} href={href} key={demo.slug}>
|
||||
<span>{demo.eyebrow}</span>
|
||||
{demo.title}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -12,29 +12,11 @@ export function Footer() {
|
||||
</Link>
|
||||
<p>Copyright © 2026, Ultifide</p>
|
||||
</div>
|
||||
<nav className="footer__nav" aria-label="Nawigacja stopki">
|
||||
<Link href="/pl">Platforma</Link>
|
||||
<Link href="/pl/demo">Demo biurka</Link>
|
||||
<Link href="/pl/demo-room">Demo wnętrz</Link>
|
||||
<Link href="/pl/demo-door">Demo drzwi</Link>
|
||||
<a href={contact.url}>Kontakt</a>
|
||||
</nav>
|
||||
<address className="footer__contact">
|
||||
<strong>Contact Us</strong>
|
||||
{contact.address.map(line => (
|
||||
<span key={line}>{line}</span>
|
||||
))}
|
||||
<a href={`mailto:${contact.email}`}>{contact.email}</a>
|
||||
<a href={`tel:${contact.phone.replaceAll(' ', '')}`}>{contact.phone}</a>
|
||||
</address>
|
||||
<div className="footer__social">
|
||||
<a href={contact.linkedin} aria-label="LinkedIn Ultifide">
|
||||
in
|
||||
</a>
|
||||
<a href={contact.facebook} aria-label="Facebook Ultifide">
|
||||
f
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
+24
-23
@@ -4,15 +4,13 @@ import {Menu, X} from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {usePathname} from 'next/navigation';
|
||||
import {contact} from '@/config/contact';
|
||||
import {Logo} from './Logo';
|
||||
|
||||
const navItems = [
|
||||
{id: 'dema', label: 'Dema', href: '/pl#dema'},
|
||||
{id: 'platforma', label: 'Platforma', href: '/pl#platforma'},
|
||||
{id: 'dema', label: 'Dema', href: '/pl/dema'},
|
||||
{id: 'funkcje', label: 'Funkcje', href: '/pl#funkcje'},
|
||||
{id: 'faq', label: 'FAQ', href: '/pl#faq'},
|
||||
{id: 'kontakt', label: 'Kontakt', href: contact.url}
|
||||
{id: 'faq', label: 'FAQ', href: '/pl#faq'}
|
||||
];
|
||||
|
||||
export function Header() {
|
||||
@@ -25,33 +23,36 @@ export function Header() {
|
||||
return;
|
||||
}
|
||||
|
||||
const sections = ['platforma', 'dema', 'funkcje', 'faq']
|
||||
const sections = ['dema', 'platforma', 'funkcje', 'faq', 'kontakt']
|
||||
.map(id => document.getElementById(id))
|
||||
.filter((section): section is HTMLElement => Boolean(section));
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
entries => {
|
||||
const visibleEntry = entries
|
||||
.filter(entry => entry.isIntersecting)
|
||||
.sort((first, second) => second.intersectionRatio - first.intersectionRatio)[0];
|
||||
function updateActiveSection() {
|
||||
const activationLine = 120;
|
||||
const currentSection = sections.reduce<HTMLElement | null>((active, section) => {
|
||||
const sectionTop = section.getBoundingClientRect().top;
|
||||
|
||||
if (visibleEntry?.target.id) {
|
||||
setActiveSection(visibleEntry.target.id);
|
||||
if (sectionTop <= activationLine) {
|
||||
return section;
|
||||
}
|
||||
},
|
||||
{rootMargin: '-30% 0px -55% 0px', threshold: [0.1, 0.35, 0.6]}
|
||||
);
|
||||
|
||||
sections.forEach(section => observer.observe(section));
|
||||
return active;
|
||||
}, null);
|
||||
|
||||
return () => observer.disconnect();
|
||||
setActiveSection(currentSection?.id ?? 'platforma');
|
||||
}
|
||||
|
||||
updateActiveSection();
|
||||
window.addEventListener('scroll', updateActiveSection, {passive: true});
|
||||
window.addEventListener('resize', updateActiveSection);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', updateActiveSection);
|
||||
window.removeEventListener('resize', updateActiveSection);
|
||||
};
|
||||
}, [pathname]);
|
||||
|
||||
function isActive(itemId: string) {
|
||||
if (itemId === 'kontakt') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pathname === '/pl') {
|
||||
return itemId === activeSection;
|
||||
}
|
||||
@@ -76,8 +77,8 @@ export function Header() {
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
<a className="siteHeader__contact" href={contact.url}>
|
||||
Porozmawiajmy
|
||||
<a className={activeSection === 'kontakt' ? 'siteHeader__contact isActive' : 'siteHeader__contact'} href="#kontakt">
|
||||
Kontakt
|
||||
</a>
|
||||
<button
|
||||
className="siteHeader__menu"
|
||||
|
||||
@@ -44,10 +44,6 @@ export function HeroVisual() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="heroVisual__footer">
|
||||
<span>3D preview</span>
|
||||
<strong>API-ready</strong>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export const contact = {
|
||||
url: 'https://ultifide.com/contact',
|
||||
anchor: '#kontakt',
|
||||
email: 'contact@ultifide.com',
|
||||
phone: '+48 733 226 544',
|
||||
address: ['Cracow, Poland', 'ul. św. Wawrzyńca 19/2', '31-060 Kraków'],
|
||||
phones: ['+48 733 226 544', '+48 664 565 858'],
|
||||
address: ['Kraków, Polska', 'ul. św. Wawrzyńca 19/2', '31-060 Kraków'],
|
||||
linkedin: 'https://pl.linkedin.com/company/ultifide',
|
||||
facebook: 'https://facebook.com/ultifide'
|
||||
} as const;
|
||||
|
||||
+66
-3
@@ -21,9 +21,72 @@ function createDemoContent(demo: DemoMessages, iframeUrl: string): DemoContent {
|
||||
}
|
||||
|
||||
export const demoContent = {
|
||||
desk: createDemoContent(messages.demos.desk, demoUrls.desk),
|
||||
room: createDemoContent(messages.demos.room, demoUrls.room),
|
||||
door: createDemoContent(messages.demos.door, demoUrls.door)
|
||||
door: createDemoContent(messages.demos.door, demoUrls.door),
|
||||
desk: createDemoContent(messages.demos.desk, demoUrls.desk)
|
||||
} as const;
|
||||
|
||||
export const marketingFaq: FaqItem[] = [...demoContent.desk.faq, ...demoContent.room.faq];
|
||||
export const orderedDemos: DemoContent[] = [demoContent.room, demoContent.door, demoContent.desk];
|
||||
|
||||
export const marketingFaq: FaqItem[] = [
|
||||
{
|
||||
question: 'Czy możemy zacząć od jednego produktu?',
|
||||
answer:
|
||||
'Tak. V1 może obejmować jeden produkt, jedną kolekcję albo jedno demo, a potem rosnąć o kolejne warianty, reguły i integracje.'
|
||||
},
|
||||
{
|
||||
question: 'Czy konfigurator może być częścią mojego sklepu?',
|
||||
answer:
|
||||
'Tak. Może działać jako widżet, dedykowana podstrona, kreator zestawów, moduł koszyka albo element panelu B2B.'
|
||||
},
|
||||
{
|
||||
question: 'Jak wygląda integracja ze sprzedażą, CRM lub ERP?',
|
||||
answer:
|
||||
'Konfiguracja może trafiać do koszyka, formularza zapytania, CRM, ERP albo panelu B2B. Integrujemy przez API, webhooki i popularne platformy e-commerce.'
|
||||
},
|
||||
{
|
||||
question: 'Czy mogę dodać własne modele 3D, materiały i warianty?',
|
||||
answer:
|
||||
'Tak. Możemy pracować na dostarczonych modelach albo przygotować i zoptymalizować je pod konfigurator, a potem rozwijać katalog o nowe kolory, materiały, dodatki i akcesoria.'
|
||||
},
|
||||
{
|
||||
question: 'Czy konfigurator obsługuje zależności i reguły produktu?',
|
||||
answer:
|
||||
'Tak. Obsługujemy warunki, ograniczenia, wariantowe SKU, zgodność modułów i zależności między modelem, rozmiarem, kolorem, okuciami, dodatkami lub dostępnością.'
|
||||
},
|
||||
{
|
||||
question: 'Czy konfigurator działa na urządzeniach mobilnych?',
|
||||
answer:
|
||||
'Tak. Interfejs jest responsywny, a w wybranych wdrożeniach można dodać tryb AR.'
|
||||
},
|
||||
{
|
||||
question: 'Czy wygląd konfiguratora można dopasować do mojej marki?',
|
||||
answer:
|
||||
'Tak. UI, kolory, branding, układ opcji, sposób interakcji, modele 3D i środowisko sceny mogą wyglądać jak natywna część Twojego sklepu.'
|
||||
},
|
||||
{
|
||||
question: 'Czy konfigurator sprawdzi się dla mebli modułowych i całych kolekcji?',
|
||||
answer:
|
||||
'Tak. System można rozwijać o meble modułowe, zestawy, kolekcje aranżacyjne, nowe produkty i kolejne układy pomieszczeń bez przebudowy całości.'
|
||||
},
|
||||
{
|
||||
question: 'Czy można odwzorować całe pomieszczenia?',
|
||||
answer:
|
||||
'Tak. Możemy stworzyć pełne sceny 3D od pustego pokoju po gotowe aranżacje z konfigurowalnymi meblami, kolorami i materiałami.'
|
||||
},
|
||||
{
|
||||
question: 'Czy klient może zmieniać układ mebli i elementów?',
|
||||
answer:
|
||||
'Tak. Konfigurator może pozwalać na ustawianie elementów, zmianę układu, dobór dodatków i dopasowanie konfiguracji do przestrzeni.'
|
||||
},
|
||||
{
|
||||
question: 'Jakie są główne korzyści z wdrożenia konfiguratora 3D?',
|
||||
answer:
|
||||
'Konfigurator pozwala prezentować produkty w jakości premium, personalizować je w czasie rzeczywistym, ograniczać liczbę zwrotów i pytań oraz skracać proces decyzji zakupowej.'
|
||||
},
|
||||
{
|
||||
question: 'Czy konfigurator nadaje się do produktów takich jak drzwi, fronty lub zabudowy?',
|
||||
answer:
|
||||
'Tak. Sprawdza się przy produktach wymagających precyzyjnego dopasowania wariantów, takich jak drzwi, fronty, zabudowy, oświetlenie, wyposażenie i inne produkty custom.'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -3,4 +3,4 @@ $secondary: #001c44;
|
||||
$tertiary: #f9fafe;
|
||||
$white: #ffffff;
|
||||
$gray: rgba(157, 158, 180, 1);
|
||||
$gray-light: rgba(115, 130, 151, 1);
|
||||
$gray-light: rgba(63, 80, 104, 1);
|
||||
|
||||
@@ -19,7 +19,7 @@ export type FaqItem = {
|
||||
};
|
||||
|
||||
export type DemoContent = {
|
||||
slug: 'demo' | 'demo-room' | 'demo-door';
|
||||
slug: 'demo-pokoj' | 'demo-drzwi' | 'demo-biurko';
|
||||
title: string;
|
||||
eyebrow: string;
|
||||
intro: string;
|
||||
|
||||
Reference in New Issue
Block a user