Skip to content
Commerce block

Warranty Coverage Panel

A verdict for this serial number today, with exclusions given equal weight to cover and an extension offered only while it can still be bought.

259 lineslucide-reactAdded 13 Sept 2026
  • warranty
  • coverage
  • after-sale
  • claim
  • commerce

What's included

  • components/warranty-coverage-panel.tsx
  • Needs lucide-react

Works with

  • React
  • Next.js
  • Tailwind CSS
  • TypeScript

npx hoverlab add warranty-coverage-panel

Or over MCP, from your editor's agent — no account needed.

License

Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.

Was this useful?

Start a page with this section — add more, order them, and leave with the page source.

Preview

Warranty

Coverage is per unit, not per order. Pick the serial number on the underside of the product.

Covered until 12 March 2027

Kestrel sit-stand desk · serial KS-2291-8840 · bought 12 March 2025 · Standard · 2 years · 546 days remaining

Download the certificate

Covered

  • Motor, control box and handset failure
  • Frame welds, joints and structural parts
  • Gas lift, castors and tilt mechanism
  • Manufacturing defects in the desktop surface
  • Labour and parts on an approved repair

Not covered

  • Scratches, dents and finish wear from normal use
  • Damage from moving the unit without dismantling it
  • Units modified with third-party parts
  • Consumables — cable ties, felt pads, bulbs
  • Anything bought through an unauthorised reseller

This warranty is in addition to your statutory rights, which it does not affect. Nothing on this page shortens them.

Rendered live in your current theme — this is the same component whose source is below, not a screenshot of it.

Source

components/warranty-coverage-panel.tsx
'use client'

/**
 * <WarrantyCoveragePanel> — what is covered, what is not, and until when.
 *
 * Warranty screens fail in one consistent way: they state a duration and stop.
 * "2-year warranty" answers none of the three questions a customer opens the
 * page with — is my specific unit still in it, does it cover the thing that
 * has gone wrong, and what do I do now.
 *
 * SO THE PANEL IS BUILT AROUND A VERDICT
 *
 * A single line at the top says covered or not, for this serial number, today,
 * with the date it lapses. Everything below is the evidence. A customer who
 * reads only the first line has still been answered, which is the test any
 * status surface should pass.
 *
 * WHAT IS *NOT* COVERED IS AS PROMINENT AS WHAT IS
 *
 * Two columns, equal weight. Hiding exclusions behind a link is how a
 * warranty page turns into a complaint: the customer completes a claim, waits
 * four days and is refused for something that was always written down. Stating
 * it before the claim costs one column and saves the round trip.
 *
 * THE EXTENSION OFFER IS TIMED, NOT PERMANENT. It appears inside the window
 * where an extension can still be bought and disappears afterwards, rather
 * than sitting there permanently as an upsell — a lapsed warranty offering an
 * extension is an insult, not a conversion.
 *
 * DATES ARE RENDERED IN A FIXED LOCALE AND TIME ZONE. `toLocaleDateString`
 * with the browser's defaults produces one string on the server and another in
 * the client, React discards the subtree, and the block flickers. The strings
 * here are authored data for that reason — pass your own formatted values.
 *
 * ACCESSIBILITY: the verdict is a `role="status"` region rather than colour
 * alone; the two coverage lists are separate `<h3>`-headed lists so a screen
 * reader can jump between them; the serial-number picker is a labelled select
 * because most households own more than one of anything.
 */

import * as React from 'react'
import { CircleCheck, CircleX, FileText, ShieldCheck, Wrench } from 'lucide-react'

export interface WarrantyUnit {
  id: string
  product: string
  serial: string
  purchased: string
  expires: string
  /** Days left. Zero or less renders as lapsed. */
  daysLeft: number
  plan: string
}

export interface WarrantyCoveragePanelProps {
  units?: WarrantyUnit[]
  covered?: string[]
  excluded?: string[]
  /** Extension is offered only inside this many days of expiry. */
  extensionWindowDays?: number
  className?: string
}

const DEFAULT_UNITS: WarrantyUnit[] = [
  {
    id: 'u1',
    product: 'Kestrel sit-stand desk',
    serial: 'KS-2291-8840',
    purchased: '12 March 2025',
    expires: '12 March 2027',
    daysLeft: 546,
    plan: 'Standard · 2 years',
  },
  {
    id: 'u2',
    product: 'Meridian task chair',
    serial: 'MC-1187-2210',
    purchased: '4 November 2024',
    expires: '4 November 2026',
    daysLeft: 52,
    plan: 'Standard · 2 years',
  },
  {
    id: 'u3',
    product: 'Halo task lamp',
    serial: 'HL-0042-9917',
    purchased: '19 January 2023',
    expires: '19 January 2025',
    daysLeft: -602,
    plan: 'Standard · 2 years',
  },
]

const DEFAULT_COVERED = [
  'Motor, control box and handset failure',
  'Frame welds, joints and structural parts',
  'Gas lift, castors and tilt mechanism',
  'Manufacturing defects in the desktop surface',
  'Labour and parts on an approved repair',
]

const DEFAULT_EXCLUDED = [
  'Scratches, dents and finish wear from normal use',
  'Damage from moving the unit without dismantling it',
  'Units modified with third-party parts',
  'Consumables — cable ties, felt pads, bulbs',
  'Anything bought through an unauthorised reseller',
]

export function WarrantyCoveragePanel({
  units = DEFAULT_UNITS,
  covered = DEFAULT_COVERED,
  excluded = DEFAULT_EXCLUDED,
  extensionWindowDays = 90,
  className = '',
}: WarrantyCoveragePanelProps) {
  const uid = React.useId()
  const [unitId, setUnitId] = React.useState(units[0]?.id ?? '')

  const unit = units.find((u) => u.id === unitId) ?? units[0]
  if (!unit) return null

  const active = unit.daysLeft > 0
  // Offered only where it is still buyable — see the docblock.
  const canExtend = active && unit.daysLeft <= extensionWindowDays

  return (
    <section className={`bg-background px-4 py-10 sm:px-6 ${className}`}>
      <div className="mx-auto max-w-3xl">
        <div className="flex flex-wrap items-end justify-between gap-4">
          <div>
            <h2 className="text-xl font-bold tracking-tight">Warranty</h2>
            <p className="mt-1 text-sm text-muted-foreground">
              Coverage is per unit, not per order. Pick the serial number on the
              underside of the product.
            </p>
          </div>
          <div>
            <label htmlFor={`${uid}-unit`} className="sr-only">
              Choose a registered product
            </label>
            <select
              id={`${uid}-unit`}
              value={unitId}
              onChange={(e) => setUnitId(e.target.value)}
              className="rounded-lg border border-border bg-card px-3 py-2 text-sm outline-none focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-ring"
            >
              {units.map((u) => (
                <option key={u.id} value={u.id}>
                  {u.product} · {u.serial}
                </option>
              ))}
            </select>
          </div>
        </div>

        {/* The verdict. A customer who reads only this has been answered. */}
        <div
          role="status"
          className={`mt-5 rounded-2xl border p-5 ${
            active
              ? 'border-emerald-500/40 bg-emerald-500/5'
              : 'border-border bg-muted/40'
          }`}
        >
          <p className="flex items-center gap-2 text-base font-semibold">
            {active ? (
              <ShieldCheck aria-hidden className="h-5 w-5 text-emerald-600 dark:text-emerald-400" />
            ) : (
              <CircleX aria-hidden className="h-5 w-5 text-muted-foreground" />
            )}
            {active
              ? `Covered until ${unit.expires}`
              : `Cover lapsed on ${unit.expires}`}
          </p>
          <p className="mt-1.5 text-sm text-muted-foreground">
            {unit.product} · serial <span className="font-mono text-xs">{unit.serial}</span>{' '}
            · bought {unit.purchased} · {unit.plan}
            {active ? ` · ${unit.daysLeft} days remaining` : ''}
          </p>

          <div className="mt-4 flex flex-wrap gap-2">
            <button
              type="button"
              disabled={!active}
              className="inline-flex items-center gap-1.5 rounded-lg bg-primary px-3 py-2 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
            >
              <Wrench aria-hidden className="h-4 w-4" />
              Start a warranty claim
            </button>
            <a
              href="#"
              className="inline-flex items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-sm font-medium transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
            >
              <FileText aria-hidden className="h-4 w-4" />
              Download the certificate
            </a>
          </div>

          {canExtend ? (
            <p className="mt-4 rounded-lg border border-border bg-card p-3 text-xs text-muted-foreground">
              Cover ends in {unit.daysLeft} days. It can be extended by two years
              for £49 up until the expiry date — not afterwards.{' '}
              <a href="#" className="font-semibold text-primary underline-offset-2 hover:underline">
                Extend this unit
              </a>
            </p>
          ) : null}

          {!active ? (
            <p className="mt-4 rounded-lg border border-border bg-card p-3 text-xs text-muted-foreground">
              Out-of-warranty repairs are quoted before any work starts, and the
              quote is free.{' '}
              <a href="#" className="font-semibold text-primary underline-offset-2 hover:underline">
                Request a repair quote
              </a>
            </p>
          ) : null}
        </div>

        {/* Equal weight, deliberately. */}
        <div className="mt-6 grid gap-4 sm:grid-cols-2">
          <div className="min-w-0 rounded-xl border border-border bg-card p-4">
            <h3 data-stress-ignore className="text-sm font-semibold">Covered</h3>
            <ul className="mt-3 space-y-2">
              {covered.map((item) => (
                <li key={item} className="flex gap-2 text-sm text-muted-foreground">
                  <CircleCheck
                    aria-hidden
                    className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400"
                  />
                  <span className="min-w-0 flex-1 break-words">{item}</span>
                </li>
              ))}
            </ul>
          </div>

          <div className="min-w-0 rounded-xl border border-border bg-card p-4">
            <h3 data-stress-ignore className="text-sm font-semibold">Not covered</h3>
            <ul className="mt-3 space-y-2">
              {excluded.map((item) => (
                <li key={item} className="flex gap-2 text-sm text-muted-foreground">
                  <CircleX aria-hidden className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
                  <span className="min-w-0 flex-1 break-words">{item}</span>
                </li>
              ))}
            </ul>
          </div>
        </div>

        <p className="mt-4 text-xs text-muted-foreground">
          This warranty is in addition to your statutory rights, which it does
          not affect. Nothing on this page shortens them.
        </p>
      </div>
    </section>
  )
}

Before you paste

  • Styling is Tailwind utility classes on semantic tokens (bg-card, text-muted-foreground) — it inherits your theme instead of overriding it.
  • Install: npm i lucide-react
  • Every prop has a default, so it renders standalone before you wire it up.

Where it goes

Drop it at components/warranty-coverage-panel.tsx and import it where you need the section:

import { WarrantyCoveragePanel } from '@/components/warranty-coverage-panel'

Customize

1 of this block’s props are simple enough to drive from here. Change them and the block below re-renders — it is the same component whose source is above, not a mock of it. Everything else it accepts is in the table underneath.

Props

Read out of the component’s own type and signature, so this cannot drift from the source below. Every prop has a default — the component renders standalone before you pass it anything.

PropTypeDefault
unitsWarrantyUnit[]DEFAULT_UNITS
coveredstring[]DEFAULT_COVERED
excludedstring[]DEFAULT_EXCLUDED
extensionWindowDaysExtension is offered only inside this many days of expiry.number90
classNamestring''

Not using React?

The same block rendered once to markup, wrapped as a file your framework compiles. Tailwind classes are framework-agnostic, so the design transfers intact — the behaviour does not.

This block is interactive. The markup below is its initial state with the event handlers stripped — you will need to re-wire the behaviour in your framework.

warranty-coverage-panel.html
<!--
  Warranty Coverage Panel — markup from the Hoverlab catalog.

  This is the block rendered once to HTML and wrapped as a component
  file. It is not a port of the React source: the Tailwind classes carry
  the design, which is the part that took the work, and they are the same
  in every framework.

  This block is interactive in React and the handlers are NOT here.
  Buttons, toggles and menus render in their initial state and do
  nothing until you wire them up.
-->
<section class="bg-background px-4 py-10 sm:px-6 ">
  <div class="mx-auto max-w-3xl">
    <div class="flex flex-wrap items-end justify-between gap-4">
      <div>
        <h2 class="text-xl font-bold tracking-tight">Warranty</h2>
        <p class="mt-1 text-sm text-muted-foreground">Coverage is per unit, not per order. Pick the serial number on the underside of the product.</p>
      </div>
      <div>
        <label for="_R_0_-unit" class="sr-only">Choose a registered product</label>
        <select id="_R_0_-unit" class="rounded-lg border border-border bg-card px-3 py-2 text-sm outline-none focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-ring">
          <option value="u1" selected="">Kestrel sit-stand desk · KS-2291-8840</option>
          <option value="u2">Meridian task chair · MC-1187-2210</option>
          <option value="u3">Halo task lamp · HL-0042-9917</option>
        </select>
      </div>
    </div>
    <div role="status" class="mt-5 rounded-2xl border p-5 border-emerald-500/40 bg-emerald-500/5">
      <p class="flex items-center gap-2 text-base font-semibold"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-shield-check h-5 w-5 text-emerald-600 dark:text-emerald-400" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"></path><path d="m9 12 2 2 4-4"></path></svg>Covered until 12 March 2027</p>
      <p class="mt-1.5 text-sm text-muted-foreground">Kestrel sit-stand desk · serial <span class="font-mono text-xs">KS-2291-8840</span> · bought 12 March 2025 · Standard · 2 years · 546 days remaining</p>
      <div class="mt-4 flex flex-wrap gap-2">
        <button type="button" class="inline-flex items-center gap-1.5 rounded-lg bg-primary px-3 py-2 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wrench h-4 w-4" aria-hidden="true"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>Start a warranty claim</button>
        <a href="#" class="inline-flex items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-sm font-medium transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-text h-4 w-4" aria-hidden="true"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"></path><path d="M14 2v4a2 2 0 0 0 2 2h4"></path><path d="M10 9H8"></path><path d="M16 13H8"></path><path d="M16 17H8"></path></svg>Download the certificate</a>
      </div>
    </div>
    <div class="mt-6 grid gap-4 sm:grid-cols-2">
      <div class="min-w-0 rounded-xl border border-border bg-card p-4">
        <h3 data-stress-ignore="true" class="text-sm font-semibold">Covered</h3>
        <ul class="mt-3 space-y-2">
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check mt-0.5 h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m9 12 2 2 4-4"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Motor, control box and handset failure</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check mt-0.5 h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m9 12 2 2 4-4"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Frame welds, joints and structural parts</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check mt-0.5 h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m9 12 2 2 4-4"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Gas lift, castors and tilt mechanism</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check mt-0.5 h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m9 12 2 2 4-4"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Manufacturing defects in the desktop surface</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check mt-0.5 h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m9 12 2 2 4-4"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Labour and parts on an approved repair</span>
          </li>
        </ul>
      </div>
      <div class="min-w-0 rounded-xl border border-border bg-card p-4">
        <h3 data-stress-ignore="true" class="text-sm font-semibold">Not covered</h3>
        <ul class="mt-3 space-y-2">
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-x mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m15 9-6 6"></path>
              <path d="m9 9 6 6"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Scratches, dents and finish wear from normal use</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-x mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m15 9-6 6"></path>
              <path d="m9 9 6 6"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Damage from moving the unit without dismantling it</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-x mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m15 9-6 6"></path>
              <path d="m9 9 6 6"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Units modified with third-party parts</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-x mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m15 9-6 6"></path>
              <path d="m9 9 6 6"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Consumables — cable ties, felt pads, bulbs</span>
          </li>
          <li class="flex gap-2 text-sm text-muted-foreground">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-x mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true">
              <circle cx="12" cy="12" r="10"></circle>
              <path d="m15 9-6 6"></path>
              <path d="m9 9 6 6"></path>
            </svg>
            <span class="min-w-0 flex-1 break-words">Anything bought through an unauthorised reseller</span>
          </li>
        </ul>
      </div>
    </div>
    <p class="mt-4 text-xs text-muted-foreground">This warranty is in addition to your statutory rights, which it does not affect. Nothing on this page shortens them.</p>
  </div>
</section>
  • This is rendered HTML, not a translation of the React source. The Tailwind classes carry the design and work in any framework.
  • It is one frame: the component in its initial state, with no props applied beyond the defaults.
  • This block is interactive in React — toggles, menus or form state. None of that survives here; the markup is the closed/default state and the handlers are gone. Re-wire them in your own framework.
  • Requires Tailwind, and the design tokens the classes reference (bg-card, text-muted-foreground, and so on). The template ZIPs ship a globals.css that defines them.

What each framework gets across the whole catalog — effects convert properly; this rung is markup.

For AI

The component, its props, the design tokens it expects and the command that installs it — as one prompt. Paste it into Claude, Cursor, v0 or ChatGPT and what they build around it will match the rest of the catalog instead of inventing its own system.

See the prompt

8 more blocks in After-Sale Service

All of them free to read, copy and install — no account, no locked tiles, no watermarked preview. The whole catalog is open, and so are the API and the CLI.

Browse After-Sale Service

Shipping one commercially

Copying the code is free. Putting it in client work or a paid product is what Pro is for — the licence, not the access.

  • A commercial licence for everything in the catalog
  • Unlimited bundle exports, in Vue, Svelte and Tailwind
  • One payment — no subscription, nothing to renew
Pro — $79 once

More After-Sale Service blocks

View category
Open the full page for this block

Refund Request Form

Refunds are per line, not per order: a reason on each line that drives what is asked for next, and a running total itemising the restocking fee and the delivery rule.

After-Sale Service348 lines1 dep
Open the full page for this block

Refund Status Tracker

Where the money is and who is holding it - four stages, each naming the responsible party, including the bank stage every where-is-my-refund contact comes from.

After-Sale Service241 lines1 dep
Open the full page for this block

Refunds Overview Table

The operator queue, sorted oldest-open-first because recency is the wrong default, with blocked as a first-class status that names its blocker.

After-Sale Service277 lines1 dep
Open the full page for this block

Return or Exchange Picker

Refund, exchange or credit decided per line, with live stock on the variants and the price difference shown before the goods are posted rather than after.

After-Sale Service331 lines1 dep
Open the full page for this block

Return Label and Instructions

The screen between approved and posted: three drop-off methods with the thing that actually blocks each one, the packing rules that get returns refused, and a date rather than a duration.

After-Sale Service285 lines1 dep
Open the full page for this block

Service Repair Request

A repair is not a return - a symptom picker that filters the service routes, warranty resolved inside the form, and an estimate with its uncertainty stated.

After-Sale Service310 lines1 dep
Open the full page for this block

Review History

The reviews you wrote and the ones you owe, with editing that is honest about being marked as edited and moderation that names the rule it applied.

After-Sale Service291 lines1 dep
Open the full page for this block

Gift Card Balance Check

A balance checker built for the two ways it goes wrong - an empty card and an expired one - answering with a transaction history rather than a bare zero.

After-Sale Service254 lines1 dep