ModelPageSection
apps/www/src/sections/ModelPageSection.astro · 140 lines · group Models (product catalog)
In the fleet
Used on 1 site built with this engine.
Samples 1

Station N3 — for several fields
The station that carries leaf wetness, wind and an expansion port, and still runs a full season on one battery. Mounts on a standard post with a single bolt.
Communication protocol
- Radio protocol
Error codes
For the full list of error codes and what they mean, click here
Highlights
Cloud or your own server
The same software, either in our cloud or inside your network.
A week of buffer
The gateway keeps reporting when the uplink is away.
Field-swappable
Sensors change by hand, with no tools and no service visit.
Distributors

Almara Agri

Kestrel Supply
Certification
Certificate of conformity
Every station carries a label with the certificate that applies to it. An example is shown here; the full list is published by the certification body.
For the full list of certificates click here


The technical specification lists the sensors, the ranges and the accuracy of each channel, together with the radio bands the station is approved for.

MDX of this sample
import BoltIcon from 'astro-heroicons/outline/Bolt.astro';
import SignalIcon from 'astro-heroicons/outline/Signal.astro';
import CloudIcon from 'astro-heroicons/outline/Cloud.astro';
<ModelPageSection
modelTitle="Station N3 — for several fields"
modelDescription="The station that carries leaf wetness, wind and an expansion port, and still runs a full season on one battery. Mounts on a standard post with a single bolt."
modelImgName="product-01.jpg"
certificatesText={['For the full list of certificates click', 'here']}
certificatesUrl="https://example.com/"
licenseSectionTitle="Certification"
licenseTitles={['Certificate of conformity']}
licenseContent={[
'Every station carries a label with the certificate that applies to it. An example is shown here; the full list is published by the certification body.',
]}
certificateImgNames={["document-01.png", "document-02.png"]}
techSpecContent={[
'The technical specification lists the sensors, the ranges and the accuracy of each channel, together with the radio bands the station is approved for.',
]}
techSpecImgNames={["document-03.png"]}
>
<AttachmentsSection title="Attachments">
<AttachmentItem fileName="Certificate" file="/downloads/certificate.pdf" downloadText="Download" />
<AttachmentItem fileName="Technical specification" file="/downloads/spec.pdf" downloadText="Download" />
</AttachmentsSection>
<AttachmentsSection title="Communication protocol">
<AttachmentItem fileName="Radio protocol" file="/downloads/protocol.pdf" downloadText="Download" />
</AttachmentsSection>
<ErrorCodesSection
title="Error codes"
clickText={['For the full list of error codes and what they mean, click ', 'here']}
linkPath="/api/#error-codes"
/>
<HighlightsSection title="Highlights">
<HighlightItem name="Cloud or your own server" description="The same software, either in our cloud or inside your network." IconImg={CloudIcon} />
<HighlightItem name="A week of buffer" description="The gateway keeps reporting when the uplink is away." IconImg={SignalIcon} />
<HighlightItem name="Field-swappable" description="Sensors change by hand, with no tools and no service visit." IconImg={BoltIcon} />
</HighlightsSection>
<DistributorsSection title="Distributors">
<DistributorItem title="Almara Agri" email="hello@almara.example" phone="+46 8 123 45 67" imageName="logo-02.png" />
<DistributorItem title="Kestrel Supply" email="sales@kestrel.example" phone="+47 22 12 34 56" imageName="logo-03.png" />
</DistributorsSection>
</ModelPageSection>Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| modelTitle | string | yes | ||
| modelDescription | string | yes | ||
| modelImgName | string | yes | ||
| faqs | FAQ[] | [] | ||
| licenseSectionTitle | string | yes | ||
| licenseTitles | string[] | yes | [] | |
| licenseContent | string[] | yes | [] | |
| certificatesText | string[] | yes | ||
| certificatesUrl | string | yes | ||
| certificateImgNames | string[] | yes | [] | |
| techSpecContent | string[] | yes | [] | |
| techSpecImgNames | string[] | yes | [] |
Source apps/www/src/sections/ModelPageSection.astro @ gitt.one
---
import { Image } from 'astro:assets';
import { resolveImage } from '../lib/images';
interface FAQ {
question: string;
answer: string;
}
interface Props {
modelTitle: string;
modelDescription: string;
modelImgName: string;
faqs?: FAQ[];
licenseSectionTitle: string;
licenseTitles: string[];
licenseContent: string[];
certificatesText: string[];
certificatesUrl: string;
certificateImgNames: string[];
techSpecContent: string[];
techSpecImgNames: string[];
}
const {
modelTitle,
modelDescription,
modelImgName,
faqs = [],
licenseSectionTitle,
licenseTitles = [],
licenseContent = [],
certificatesText,
certificatesUrl,
certificateImgNames = [],
techSpecContent = [],
techSpecImgNames = [],
} = Astro.props;
const modelImage = await resolveImage(modelImgName);
const certificateImages = await Promise.all(
certificateImgNames.map(async (imgName) => ({ img: await resolveImage(imgName), imgName })),
);
const techSpecImages = await Promise.all(
techSpecImgNames.map(async (imgName) => ({ img: await resolveImage(imgName), imgName })),
);
---
<div class="mx-auto px-4 py-16 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8">
<div class="lg:grid lg:grid-cols-7 lg:grid-rows-1 lg:gap-x-8 lg:gap-y-10 xl:gap-x-16">
<div class="lg:col-span-4 lg:row-end-1">
<div class="overflow-hidden rounded-lg bg-gray-100">
<Image
src={modelImage}
alt={modelImgName}
widths={[400, 840]}
class="object-cover object-center"
/>
</div>
</div>
<div class="mx-auto mt-14 max-w-2xl sm:mt-16 lg:col-span-3 lg:row-span-2 lg:row-end-2 lg:mt-0 lg:max-w-none">
<div class="flex flex-col-reverse">
<h1 class="text-2xl font-extrabold tracking-tight text-gray-900 sm:text-3xl">
{modelTitle}
</h1>
</div>
<p class="mt-6 text-gray-500">{modelDescription}</p>
<slot />
</div>
<div class="mx-auto mt-8 w-full max-w-2xl lg:col-span-4 lg:mt-0 lg:max-w-none">
<div class="border-b border-gray-200">
<h2 class="border-b-2 border-primary-600 py-6 text-sm font-medium whitespace-nowrap text-primary-600 inline-block -mb-px">
{licenseSectionTitle}
</h2>
</div>
<div class="mt-4 grid gap-4 text-sm text-gray-500">
{licenseTitles[0] && (
<h3 class="text-sm font-medium text-gray-900">{licenseTitles[0]}</h3>
)}
<div>
{licenseContent.map((paragraph, index) => (
<div class={index > 0 ? 'mt-4' : ''}>{paragraph}</div>
))}
<div class="mt-4">
{certificatesText[0]}{' '}
<a
href={certificatesUrl}
class="font-bold text-primary-600 underline hover:text-primary-500"
>
{certificatesText[1]}
</a>
</div>
</div>
<div class="grid sm:grid-cols-2">
{certificateImages.map(({ img, imgName }) => (
<Image src={img} alt={imgName} widths={[400, 840]} class="h-auto w-full" />
))}
</div>
</div>
{
(techSpecContent.length > 0 || techSpecImages.length > 0) && (
<div class="mt-8 grid gap-4 text-sm text-gray-500 sm:grid-cols-2">
{licenseTitles[1] && (
<h3 class="text-sm font-medium text-gray-900 sm:col-span-2">{licenseTitles[1]}</h3>
)}
{techSpecContent.map((paragraph) => (
<div>{paragraph}</div>
))}
<div class="mx-auto grid w-60 gap-4 sm:w-full sm:grid-cols-2">
{techSpecImages.map(({ img, imgName }) => (
<Image src={img} alt={imgName} widths={[400, 840]} class="h-auto w-full" />
))}
</div>
</div>
)
}
{
faqs.length > 0 && (
<div class="mt-8 text-sm text-gray-500">
<h3 class="font-medium text-gray-900">FAQ</h3>
{faqs.map((faq) => (
<dl>
<dt class="mt-8 font-medium text-gray-900">{faq.question}</dt>
<dd class="mt-2 max-w-none text-gray-500">
<p>{faq.answer}</p>
</dd>
</dl>
))}
</div>
)
}
</div>
</div>
</div>Source links point to engine-astro@38c294d9. Samples are demo content: the components are the real ones, the copy and the pictures are made up.