diff options
| author | Dawid Rycerz <dawid@rycerz.xyz> | 2025-07-22 16:33:05 +0300 |
|---|---|---|
| committer | Dawid Rycerz <dawid@rycerz.xyz> | 2025-07-22 16:33:05 +0300 |
| commit | 6924fc24c8d7c4d9281492ff12de583ef26f8927 (patch) | |
| tree | 50fedb1340827f4626e66cb92e1efc8f6e281aad /src | |
| parent | 85a31139e7d11996da5de5d09ad3f821c60e232d (diff) | |
Add initial pricing
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/widgets/Pricing.astro | 126 | ||||
| -rw-r--r-- | src/pages/index.astro | 89 | ||||
| -rw-r--r-- | src/types.d.ts | 11 |
3 files changed, 161 insertions, 65 deletions
diff --git a/src/components/widgets/Pricing.astro b/src/components/widgets/Pricing.astro index 3f20b74..4ab5279 100644 --- a/src/components/widgets/Pricing.astro +++ b/src/components/widgets/Pricing.astro @@ -1,15 +1,15 @@ --- -import { Icon } from 'astro-icon/components'; -import Button from '~/components/ui/Button.astro'; +import type { Pricing as Props } from '~/types'; import Headline from '~/components/ui/Headline.astro'; import WidgetWrapper from '~/components/ui/WidgetWrapper.astro'; -import type { Pricing as Props } from '~/types'; +import Button from '~/components/ui/Button.astro'; const { - title = '', - subtitle = '', - tagline = '', - prices = [], + title = await Astro.slots.render('title'), + subtitle = await Astro.slots.render('subtitle'), + tagline, + items = [], + actions = [], id, isDark = false, @@ -18,66 +18,66 @@ const { } = Astro.props; --- -<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}> - <Headline title={title} subtitle={subtitle} tagline={tagline} /> - <div class="flex items-stretch justify-center"> - <div class="grid grid-cols-3 gap-4 dark:text-white sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3"> +<WidgetWrapper + id={id} + isDark={isDark} + containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} + bg={bg} +> + <Headline + title={title} + subtitle={subtitle} + tagline={tagline} + classes={{ + container: 'max-w-xl sm:mx-auto lg:max-w-2xl', + title: 'text-4xl md:text-5xl font-bold tracking-tighter mb-4 font-heading', + subtitle: 'max-w-3xl mx-auto sm:text-center text-xl text-muted dark:text-slate-400', + }} + /> + <div class="mt-12"> + <div class="row-gap-8 md:grid md:grid-cols-2 md:gap-8"> { - prices && - prices.map(({ title, subtitle, price, period, items, callToAction, hasRibbon = false, ribbonTitle }) => ( - <div class="col-span-3 mx-auto flex w-full sm:col-span-1 md:col-span-1 lg:col-span-1 xl:col-span-1 intersect-once motion-safe:md:intersect:animate-fade motion-safe:md:opacity-0 intersect-quarter"> - {price && period && ( - <div class="rounded-lg backdrop-blur border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900 shadow px-6 py-8 flex w-full max-w-sm flex-col justify-between text-center"> - {hasRibbon && ribbonTitle && ( - <div class="absolute right-[-5px] 2xl:right-[-8px] rtl:right-auto rtl:left-[-8px] rtl:2xl:left-[-10px] top-[-5px] 2xl:top-[-10px] z-[1] h-[100px] w-[100px] overflow-hidden text-right"> - <span class="absolute top-[19px] right-[-21px] rtl:right-auto rtl:left-[-21px] block w-full rotate-45 rtl:-rotate-45 bg-green-700 text-center text-[10px] font-bold uppercase leading-5 text-white shadow-[0_3px_10px_-5px_rgba(0,0,0,0.3)] before:absolute before:left-0 before:top-full before:z-[-1] before:border-[3px] before:border-r-transparent before:border-b-transparent before:border-l-green-800 before:border-t-green-800 before:content-[''] after:absolute after:right-0 after:top-full after:z-[-1] after:border-[3px] after:border-l-transparent after:border-b-transparent after:border-r-green-800 after:border-t-green-800 after:content-['']"> - {ribbonTitle} - </span> - </div> - )} - <div class="px-2 py-0"> - {title && ( - <h3 class="text-center text-xl font-semibold uppercase leading-6 tracking-wider mb-2">{title}</h3> - )} - {subtitle && <p class="font-light sm:text-lg text-gray-600 dark:text-slate-400">{subtitle}</p>} - <div class="my-8"> - <div class="flex items-center justify-center text-center mb-1"> - <span class="text-5xl">$</span> - <span class="text-6xl font-extrabold">{price}</span> - </div> - <span class="text-base leading-6 lowercase text-gray-600 dark:text-slate-400">{period}</span> - </div> - {items && ( - <ul class="my-8 md:my-10 space-y-2 text-left"> - {items.map( - ({ description, icon }) => - description && ( - <li class="mb-1.5 flex items-start space-x-3 leading-7"> - <div class="rounded-full bg-primary mt-1"> - <Icon name={icon ? icon : 'tabler:check'} class="w-5 h-5 font-bold p-1 text-white" /> - </div> - <span>{description}</span> - </li> - ) - )} - </ul> - )} - </div> - {callToAction && ( - <div class={`flex justify-center`}> - {typeof callToAction === 'string' ? ( - <Fragment set:html={callToAction} /> - ) : ( - callToAction && - callToAction.href && <Button {...(hasRibbon ? { variant: 'primary' } : {})} {...callToAction} /> - )} - </div> - )} - </div> + items && items.length > 0 && items.map((item, index) => ( + <div class={index % 2 === 0 ? "mb-6 md:mb-0" : ""}> + <div class="mb-6"> + {item.title && ( + <h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-4">{item.title}</h3> + )} + <hr class="mb-4"> + {item.entries && item.entries.length > 0 && ( + <table class="w-full border-collapse"> + <tbody> + {item.entries.map((entry) => ( + <tr class="h-12"> + <td class="w-1/2 h-12 px-4 text-muted dark:text-slate-400" set:html={entry.description}></td> + <td class="w-1/2 text-right h-12 px-4 text-muted dark:text-slate-400">{entry.price}</td> + </tr> + ))} + </tbody> + </table> )} </div> - )) + </div> + )) } </div> </div> + + <div class="mt-12 text-center"> + {await Astro.slots.render('disclaimer') && ( + <div class="text-xs text-muted dark:text-slate-400 mb-8" set:html={await Astro.slots.render('disclaimer')} /> + )} + + { + actions && actions.length > 0 && ( + <div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4 mt-6"> + {actions.map((action) => ( + <div class="flex w-full sm:w-auto"> + <Button {...action} class="w-full sm:mb-0" /> + </div> + ))} + </div> + ) + } + </div> </WidgetWrapper> diff --git a/src/pages/index.astro b/src/pages/index.astro index a40b445..cd79040 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -12,6 +12,7 @@ import FAQs from '~/components/widgets/FAQs.astro'; import Stats from '~/components/widgets/Stats.astro'; import CallToAction from '~/components/widgets/CallToAction.astro'; import CallToActionImage from '~/components/widgets/CallToActionImage.astro'; +import Pricing from '~/components/widgets/Pricing.astro'; const metadata = { title: 'CustomWorks – Detailing, wrapping, tuning | Profesjonalne usługi pielęgnacji samochodów – Bydgoszcz', @@ -133,7 +134,7 @@ const metadata = { </Fragment> </Content> - <!-- SEO2**************** --> + <!-- SEO2 **************** --> <Content isAfterContent @@ -171,6 +172,92 @@ const metadata = { </Fragment> </Content> + <!-- Cennik **************** --> + + <Pricing + title="Cennik" + id="pricing" + items={[ + { + title: 'Mycie', + entries: [ + { description: 'Mycie Detailingowe<br>Standard', price: 'od 100 zł' }, + { description: 'Mycie Detailingowe<br>Premium', price: 'od 180 zł' }, + ], + }, + { + title: 'Zabezpieczenie lakieru', + entries: [ + { description: 'Wosk', price: 'od 300 zł' }, + { description: 'Powłoka kwarcowa 12m.', price: 'od 500 zł' }, + { description: 'Powłoka ceramiczna 24m.', price: 'od 900 zł' }, + { description: 'Powłoka ceramiczna 36m.', price: 'od 1200 zł' }, + { description: 'Powłoka ceramiczna 60m.', price: 'od 2000 zł' }, + { description: 'Niewidzialna wycieraczka na przednią szybę', price: 'od 150 zł' }, + { description: 'Niewidzialna wycieraczka na wszystkie szyby', price: 'od 350 zł' }, + { description: 'Zabezpieczenie frontu felg powłoką ceramiczną', price: 'od 400 zł' }, + { description: 'Zabezpieczenie całych felg powłoką ceramiczną', price: 'od 800 zł' }, + ], + }, + { + title: 'Wnętrze', + entries: [ + { description: 'Podstawowe czyszczenie<br>+ mycie szyb', price: 'od 150 zł' }, + { description: 'Detailingowe czyszczenie wnętrza', price: 'od 300 zł' }, + { description: 'Pranie foteli', price: 'od 300 zł' }, + { description: 'Pranie premium<br>(wszystkie elementy + demontaż foteli)', price: 'od 700 zł' }, + { description: 'Czyszczenie skór', price: 'od 300 zł' }, + { description: 'Konserwacja skór', price: 'od 200 zł' }, + { description: 'Zabezpieczenie skór', price: 'od 250 zł' }, + ], + }, + { + title: 'Renowacja lakieru', + entries: [ + { description: 'One Step', price: 'od 500 zł' }, + { description: 'Dwuetapowa korekta lakieru (usuwa od 70% do 90% rys)', price: 'od 900 zł' }, + { description: 'Wieloetapowa korekta lakieru (usuwa od 90% do 99% rys)', price: 'od 1300 zł' }, + ], + }, + + { + title: 'Zabezpieczanie lakieru folią PPF', + entries: [ + { description: 'Pakiet „Full Body"', price: 'od 12000 zł' }, + { description: 'Pakiet „Full Front"', price: 'od 5000 zł' }, + { description: 'Pakiet „Mini"', price: 'od 700 zł' }, + { description: 'Reflektory', price: 'od 240 zł' }, + { description: 'Próg bagażnika', price: 'od 250 zł' }, + { description: 'Wnęki klamek', price: 'od 200 zł' }, + { description: 'Przyciemnianie lamp', price: 'od 250 zł' }, + ], + }, + { + title: 'Zmiana koloru folią', + entries: [ + { description: 'Całe auto', price: 'od 7000 zł' }, + { description: 'Dach', price: 'od 700 zł' }, + { description: 'Lusterka', price: 'od 400 zł' }, + { description: 'Dechroming', price: 'od 800 zł' }, + ], + }, + ]} + actions={[ + { + variant: 'primary', + text: 'Pobierz cennik PDF', + href: '/cennik_2023.pdf', + icon: 'tabler:download', + download: true, + }, + ]} + > + <Fragment slot="disclaimer"> + Podane ceny są cenami orientacyjnymi, dokładna wycena podawana jest po oględzinach pojazdu.<br><br> + Lista usług jest orientacyjna i ogólna, jeśli jesteś zainteresowany czymś nietypowym to zapraszam do kontaktu, postaram się pomóc. + </Fragment> + </Pricing> + <!-- Kontakt **************** --> <Steps2 diff --git a/src/types.d.ts b/src/types.d.ts index 3ea8a65..491ba89 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -228,8 +228,17 @@ export interface Stats extends Omit<Headline, 'classes'>, Widget { stats?: Array<Stat>; } +export interface PricingItem { + title?: string; + entries?: Array<{ + description?: string; + price?: string; + }>; +} + export interface Pricing extends Omit<Headline, 'classes'>, Widget { - prices?: Array<Price>; + items?: Array<PricingItem>; + actions?: Array<CallToAction>; } export interface Testimonials extends Omit<Headline, 'classes'>, Widget { |
