feat: build juozas auto site
This commit is contained in:
128
src/pages/automobiliai/[slug].astro
Normal file
128
src/pages/automobiliai/[slug].astro
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
import { getImage } from 'astro:assets';
|
||||
import { getCollection, type CollectionEntry } from 'astro:content';
|
||||
import CarCard from '../../components/CarCard.astro';
|
||||
import CarGallery from '../../components/CarGallery.astro';
|
||||
import ContactButtons from '../../components/ContactButtons.astro';
|
||||
import Footer from '../../components/Footer.astro';
|
||||
import Header from '../../components/Header.astro';
|
||||
import JsonLd from '../../components/JsonLd.astro';
|
||||
import SpecStrip from '../../components/SpecStrip.astro';
|
||||
import { lt } from '../../i18n/lt';
|
||||
import { EMAIL, PHONE_DISPLAY } from '../../lib/contact';
|
||||
import { excerpt, formatEngineSize, formatMileage, formatPower, formatPrice } from '../../lib/format';
|
||||
import { vehicleJsonLd } from '../../lib/jsonLd';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { site } from '../../site';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const cars = await getCollection('cars');
|
||||
return cars.map((car) => ({ params: { slug: car.slug }, props: { car } }));
|
||||
}
|
||||
|
||||
const { car } = Astro.props as { car: CollectionEntry<'cars'> };
|
||||
const allCars = await getCollection('cars');
|
||||
const similarCars = allCars.filter((item) => item.id !== car.id && !item.data.sold).slice(0, 3);
|
||||
const { Content } = await car.render();
|
||||
const carName = `${car.data.make} ${car.data.model}`;
|
||||
const title = `${car.data.year} ${car.data.make} ${car.data.model} — ${formatPrice(car.data.price)} — Juozas Auto`;
|
||||
const ogTitle = `${car.data.year} ${car.data.make} ${car.data.model} — ${formatPrice(car.data.price)}`;
|
||||
const description = excerpt(car.body);
|
||||
const canonicalPath = `/automobiliai/${car.slug}`;
|
||||
const canonicalUrl = new URL(canonicalPath, site.url).toString();
|
||||
const ogImage = await getImage({ src: car.data.photos[0], width: 1200, height: 630, format: 'jpg' });
|
||||
const ogImageUrl = new URL(ogImage.src, site.url).toString();
|
||||
const specs = [
|
||||
[lt.labels.year, String(car.data.year)],
|
||||
[lt.labels.mileage, formatMileage(car.data.mileage)],
|
||||
[lt.labels.fuel, car.data.fuel],
|
||||
[lt.labels.gearbox, car.data.transmission],
|
||||
[lt.labels.bodyType, car.data.bodyType],
|
||||
[lt.labels.color, car.data.color],
|
||||
car.data.drivetrain ? [lt.labels.drivetrain, car.data.drivetrain] : null,
|
||||
car.data.power ? [lt.labels.power, formatPower(car.data.power)] : null,
|
||||
car.data.engineSize ? [lt.labels.engineSize, formatEngineSize(car.data.engineSize)] : null,
|
||||
car.data.firstRegistration ? [lt.labels.firstRegistration, car.data.firstRegistration] : null,
|
||||
car.data.vin ? [lt.labels.vin, car.data.vin] : null,
|
||||
].filter(Boolean) as [string, string][];
|
||||
|
||||
const jsonLd = vehicleJsonLd({ car: car.data, canonicalUrl, imageUrl: ogImageUrl });
|
||||
---
|
||||
|
||||
<BaseLayout title={title} ogTitle={ogTitle} description={description} canonicalPath={canonicalPath} image={ogImageUrl} type="product">
|
||||
<Header />
|
||||
<JsonLd data={jsonLd} />
|
||||
<main class="mx-auto max-w-7xl px-5 pb-8 sm:px-8 lg:px-10">
|
||||
<div class="grid min-w-0 gap-10 lg:grid-cols-[minmax(0,1.08fr)_minmax(380px,0.72fr)] lg:items-start">
|
||||
<div class="min-w-0 lg:sticky lg:top-6">
|
||||
<CarGallery photos={car.data.photos} title={carName} />
|
||||
</div>
|
||||
|
||||
<aside class="w-full max-w-full min-w-0 overflow-hidden lg:sticky lg:top-6">
|
||||
<p class="text-sm text-muted">{car.data.year} · {car.data.city}</p>
|
||||
<h1 class="mt-2 text-2xl font-semibold tracking-[-0.06em] text-ink sm:text-3xl">{carName}</h1>
|
||||
<p class="mt-5 text-3xl font-semibold tracking-[-0.07em] text-burgundy-700 tabular">{formatPrice(car.data.price)}</p>
|
||||
<div class="mt-6 lg:hidden">
|
||||
<ContactButtons make={car.data.make} model={car.data.model} compact />
|
||||
</div>
|
||||
<div class="mt-7">
|
||||
<SpecStrip year={car.data.year} mileage={car.data.mileage} fuel={car.data.fuel} transmission={car.data.transmission} />
|
||||
</div>
|
||||
<div class="mt-7 hidden lg:block">
|
||||
<ContactButtons make={car.data.make} model={car.data.model} />
|
||||
<p class="mt-4 text-sm text-muted">Tiesioginis kontaktas: {PHONE_DISPLAY}, {EMAIL}</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 grid gap-12 lg:grid-cols-[minmax(0,1fr)_380px]">
|
||||
<article class="prose-lite measure">
|
||||
<h2 class="mb-5 text-2xl font-semibold tracking-[-0.05em] text-ink">{lt.sections.description}</h2>
|
||||
<Content />
|
||||
</article>
|
||||
|
||||
<section aria-labelledby="specs-heading">
|
||||
<h2 id="specs-heading" class="text-2xl font-semibold tracking-[-0.05em]">{lt.sections.specs}</h2>
|
||||
<dl class="mt-5 divide-y divide-line rounded-[1.75rem] border border-line bg-paper">
|
||||
{specs.map(([label, value]) => (
|
||||
<div class="grid grid-cols-[1fr_1.25fr] gap-4 px-5 py-4 text-sm">
|
||||
<dt class="text-muted">{label}</dt>
|
||||
<dd class="font-semibold text-ink tabular">{value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="mt-16 rounded-[2rem] bg-wash p-6 sm:p-8" aria-labelledby="location-heading">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.18em] text-muted">{lt.sections.location}</p>
|
||||
<h2 id="location-heading" class="mt-2 text-2xl font-semibold tracking-[-0.05em]">{car.data.city}</h2>
|
||||
<p class="mt-3 max-w-2xl text-muted">Apžiūra derinama telefonu. Tikslus adresas pateikiamas susitarus dėl laiko.</p>
|
||||
</section>
|
||||
|
||||
{similarCars.length > 0 && (
|
||||
<section class="mt-20" aria-labelledby="similar-heading">
|
||||
<h2 id="similar-heading" class="text-2xl font-semibold tracking-[-0.05em]">{lt.sections.similar}</h2>
|
||||
<div class="mt-7 grid gap-x-7 gap-y-12 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{similarCars.map((item) => <CarCard car={item} />)}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</main>
|
||||
|
||||
<div class="fixed inset-x-0 bottom-0 z-40 translate-y-full border-t border-line bg-paper/95 px-4 pb-[max(1rem,env(safe-area-inset-bottom))] pt-3 shadow-soft lg:hidden" data-sticky-contact>
|
||||
<ContactButtons make={car.data.make} model={car.data.model} compact />
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</BaseLayout>
|
||||
|
||||
<script is:inline>
|
||||
const sticky = document.querySelector('[data-sticky-contact]');
|
||||
if (sticky) {
|
||||
const toggle = () => sticky.classList.toggle('translate-y-full', window.scrollY < 200);
|
||||
sticky.classList.add('transition-transform', 'duration-300', 'ease-out-quart');
|
||||
toggle();
|
||||
window.addEventListener('scroll', toggle, { passive: true });
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user