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

sample
product-01.jpg

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.

Attachments

Communication 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.

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
document-01.pngdocument-02.png
The technical specification lists the sensors, the ranges and the accuracy of each channel, together with the radio bands the station is approved for.
document-03.png
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

NameTypeRequiredDefaultDescription
modelTitlestringyes
modelDescriptionstringyes
modelImgNamestringyes
faqsFAQ[][]
licenseSectionTitlestringyes
licenseTitlesstring[]yes[]
licenseContentstring[]yes[]
certificatesTextstring[]yes
certificatesUrlstringyes
certificateImgNamesstring[]yes[]
techSpecContentstring[]yes[]
techSpecImgNamesstring[]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.