# Run a deadline

Suppose you sell a concert shirt at a preorder price until a set time, then show the standard offer. Plandalf's promo supplies the countdown and active tier. You decide whether the deadline changes a price **inside one offer** or switches the button to a **different offer**.

## 1. Configure the sale

1. [Create a promo](https://plandalf.com/docs/product-guides/promos/create-promo) with the slug `concert-merch`. Set a fixed deadline, then add tiers labeled `Preorder` and `Standard`. These exact labels are used in the code below. [Tier setup](https://plandalf.com/docs/product-guides/promos/tiers) explains prices and windows.
2. For an HTML-only page, publish one `concert-shirt` offer and connect its prices to those promo tiers.
3. To switch checkouts in JavaScript or React, publish **two** offers: `concert-shirt-preorder` and `concert-shirt-standard`. Set each offer's price and completion behavior. The promo's active tier will choose which one the next click opens.
4. Test both offers separately before adding the countdown. Replace the example organization host with yours.

## 2. Put the countdown beside the buy button

<scalar-tabs default="HTML">
<scalar-tab title="HTML">

The browser bundle renders the countdown and applies the active promo tier price to the **same** offer. HTML attributes alone do not switch offer slugs.

```html title="merch.html"
<script defer
  src="https://your-org.plandalf.dev/js/plandalf-sdk.js"></script>
<div data-plandalf-promo="concert-merch"></div>
<button
  data-plandalf-present="concert-shirt"
  data-plandalf-apply-promo="concert-merch"
>Buy the shirt</button>
```

The declarative promo monitor may follow a redirect URL configured on an active tier. Check that tier's redirect setting if the page unexpectedly navigates.

</scalar-tab>
<scalar-tab title="JavaScript">

The promo handle resolves with the current tier and continues sending `tier-change` events. Disable its automatic redirects because this page selects the next offer itself.

```javascript title="merch.js"
import { Plandalf } from '@plandalf/sdk'

const sdk = new Plandalf({
  apiBase: 'https://your-org.plandalf.dev',
})
const button = document.querySelector('#buy-shirt')
let selectedOffer = null

const monitor = sdk.promo('concert-merch', {
  target: '#countdown', redirect: false,
})

function selectTier(label) {
  if (label === 'Preorder') {
    selectedOffer = 'concert-shirt-preorder'
    button.textContent = 'Preorder the shirt'
  } else if (label === 'Standard') {
    selectedOffer = 'concert-shirt-standard'
    button.textContent = 'Buy the shirt'
  } else {
    selectedOffer = null
    button.textContent = 'Merch unavailable'
  }
  button.disabled = !selectedOffer
}

void monitor.then(
  (promo) => selectTier(promo?.activeTier?.label),
  () => selectTier(null),
)
void (async () => {
  for await (const event of monitor.events) {
    if (event.type === 'tier-change') selectTier(event.to)
  }
})()

button.addEventListener('click', async () => {
  if (!selectedOffer) return
  const result = await sdk.present(selectedOffer)
  if (result.status === 'complete') {
    // Confirm the order on your server before promising shipment.
  }
})
// Call monitor.close() if this page is removed without navigation.
```

```html title="merch.html"
<div id="countdown"></div>
<button id="buy-shirt" disabled>Checking offer…</button>
```

</scalar-tab>
<scalar-tab title="React">

Subscribe to the same promo handle in an effect and close it when the component unmounts.

```tsx title="MerchPage.tsx"
import { useEffect, useRef, useState } from 'react'
import {
  PlandalfProvider, usePlandalf, usePresent,
} from '@plandalf/react'

function MerchButton() {
  const sdk = usePlandalf()
  const { present, presenting } = usePresent()
  const countdown = useRef<HTMLDivElement>(null)
  const [tier, setTier] = useState<string | null>()
  const offer = tier === 'Preorder' ? 'concert-shirt-preorder'
    : tier === 'Standard' ? 'concert-shirt-standard' : null

  useEffect(() => {
    if (!countdown.current) return
    let active = true
    const monitor = sdk.promo('concert-merch', {
      target: countdown.current, redirect: false,
    })
    void monitor.then(
      (promo) => {
        if (active) setTier(promo?.activeTier?.label ?? null)
      },
      () => { if (active) setTier(null) },
    )
    void (async () => {
      for await (const event of monitor.events) {
        if (active && event.type === 'tier-change') setTier(event.to)
      }
    })()
    return () => { active = false; monitor.close() }
  }, [sdk])

  async function buy() {
    if (!offer) return
    const result = await present(offer)
    if (result.status === 'complete') {
      // Confirm the order on your server before promising shipment.
    }
  }

  const label = tier === undefined ? 'Checking offer…'
    : tier === 'Preorder' ? 'Preorder the shirt'
    : offer ? 'Buy the shirt' : 'Merch unavailable'

  return <>
    <div ref={countdown} />
    <button disabled={!offer || presenting} onClick={buy}>
      {label}
    </button>
  </>
}

export function MerchPage() {
  return (
    <PlandalfProvider apiBase="https://your-org.plandalf.dev">
      <MerchButton />
    </PlandalfProvider>
  )
}
```

</scalar-tab>
</scalar-tabs>

## 3. Test both sides of the deadline

Preview the `Preorder` tier and confirm the countdown, checkout name, and price. Then test with the `Standard` tier active and confirm the **next** click opens the standard checkout. A change in tier does not replace a checkout that is already open. Before testing payment, confirm both offers and their integration are explicitly configured for sandbox use; a `mode: 'test'` override can fall back to live for public visitors. Complete a provider-supported sandbox purchase on each side and confirm the orders on your server.

The page switch does **not** disable an old preorder link. If late purchases must be rejected, enforce the cutoff in your checkout or server configuration. The [full event merch example](https://plandalf.com/docs/guides/event-merch-deadline) explains the two-offer pattern; [`promo()`](https://plandalf.com/docs/sdk/promo) and [`PromoHandle`](https://plandalf.com/docs/packages/sdk/referenced-types/PromoHandle) describe the live state and events.

Source: https://plandalf.com/docs/start/run-a-deadline
