Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Event key for the event you want to sell, for example ev_9f3a.
# Event key: the top of the event's page in the dashboard. It is not the pk_ key.
VITE_SEATLAYER_EVENT_KEY=ev_replace_me

# Publishable key for the same account. It is safe in browser code.
# Publishable key: Dashboard → Developer. It is safe in browser code.
# Never put a secret sk_ key in a front end bundle.
VITE_SEATLAYER_PUBLIC_KEY=pk_test_replace_me

Expand Down
375 changes: 133 additions & 242 deletions README.md

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions examples/hosted/collection.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>What's on</title>
</head>
<body>
<!--
Every upcoming public event in one workspace, on your page. Buyers pick a
date, then their seats. Only events you list publicly appear.
Replace <YOUR_WORKSPACE_ID> (ws_...) with your workspace id.
Optional: data-layout="list" or "calendar".
Try it live: https://seatlayer.io/demo/hosted/multi-date/
-->
<div data-seatlayer-collection="<YOUR_WORKSPACE_ID>">
<a class="slc-fallback" href="https://app.seatlayer.io/e/<YOUR_EVENT_KEY>">View tickets on SeatLayer</a>
</div>
<script src="https://app.seatlayer.io/sl-event-collection@0.js" defer></script>
</body>
</html>
26 changes: 26 additions & 0 deletions examples/hosted/event.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tickets</title>
</head>
<body>
<!--
Hosted Ticketing: one tag. SeatLayer runs the seat map, the payment through
your own gateway, and the tickets.
Replace <YOUR_EVENT_KEY> with the key from the event's page in the dashboard.
data-layout: picker (the seat map on your page), or card, banner, button.
Try it live: https://seatlayer.io/demo/hosted/paid-event/
-->
<div
data-seatlayer-event="<YOUR_EVENT_KEY>"
data-layout="picker"
data-checkout="hosted"
data-height="740px"
data-fallback-url="https://app.seatlayer.io/e/<YOUR_EVENT_KEY>">
<a href="https://app.seatlayer.io/e/<YOUR_EVENT_KEY>">Book tickets on SeatLayer</a>
</div>
<script src="https://app.seatlayer.io/sl-event-widget@0.js" defer></script>
</body>
</html>
18 changes: 18 additions & 0 deletions examples/hosted/performance-card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Performances</title>
</head>
<body>
<!--
A run of shows: a plain link card to the SeatLayer booking page, where
buyers choose the performance, then their seats. No script runs.
Try it live: https://seatlayer.io/demo/hosted/performance-groups/
-->
<div class="seatlayer-booking-card">
<a href="<YOUR_BOOKING_LINK>">Your show: choose a performance</a>
</div>
</body>
</html>
20 changes: 20 additions & 0 deletions examples/hosted/season-card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Season tickets</title>
</head>
<body>
<!--
A season card: a plain link to the SeatLayer booking page for your season.
No script runs. Style the card with your own CSS.
Use the booking link from the season's Sell tab in the dashboard.
Try it live: https://seatlayer.io/demo/hosted/season-ticket/
-->
<!-- SeatLayer season card -->
<div class="seatlayer-season-card">
<a href="https://app.seatlayer.io/s/<YOUR_SEASON_KEY>">Your season: book season tickets</a>
</div>
</body>
</html>
31 changes: 31 additions & 0 deletions src/hosted/BookingCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
interface BookingCardProps {
/** The booking link from your dashboard (a season or a run of shows). */
href: string;
title: string;
/** For example "3 performances: 6 Nov, 20 Nov and 4 Dec 2026". */
dates?: string;
/** For example "€120". */
fromPrice?: string;
/** For example "Book season tickets" or "Choose a performance". */
buttonLabel: string;
className?: string;
}

/**
* A plain link card for season tickets or a run of shows. No script runs: the
* button opens the SeatLayer booking page, which handles seats, payment and
* tickets. Style it with your own CSS.
* Try it live: https://seatlayer.io/demo/hosted/season-ticket/
*/
export function BookingCard({ href, title, dates, fromPrice, buttonLabel, className = "seatlayer-season-card" }: BookingCardProps) {
return (
<div className={className}>
<h3>{title}</h3>
{dates ? <p>{dates}</p> : null}
{fromPrice ? <p>From {fromPrice}</p> : null}
<a href={href} target="_blank" rel="noopener">
{buttonLabel}
</a>
</div>
);
}
37 changes: 37 additions & 0 deletions src/hosted/SeatLayerCollection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import { useEffect } from "react";
import { loadSeatLayerScript } from "./loadScript";

const COLLECTION_SRC = "https://app.seatlayer.io/sl-event-collection@0.js";

interface SeatLayerCollectionProps {
/** Your workspace id (ws_...), from the dashboard. */
workspaceId: string;
layout?: "grid" | "list" | "calendar";
/** Where buyers go if the script cannot load. */
fallbackUrl: string;
}

/**
* Every upcoming public event in one workspace, listed on your page. Buyers
* pick a date, then their seats. Only events you list publicly appear.
* Try it live: https://seatlayer.io/demo/hosted/multi-date/
*/
export function SeatLayerCollection({ workspaceId, layout = "grid", fallbackUrl }: SeatLayerCollectionProps) {
useEffect(() => {
loadSeatLayerScript(COLLECTION_SRC, "SeatLayerCollections");
}, [workspaceId]);

return (
<div
key={workspaceId}
data-seatlayer-collection={workspaceId}
data-layout={layout === "grid" ? undefined : layout}
>
<a className="slc-fallback" href={fallbackUrl}>
View tickets on SeatLayer
</a>
</div>
);
}
44 changes: 44 additions & 0 deletions src/hosted/SeatLayerEvent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { useEffect } from "react";
import { loadSeatLayerScript } from "./loadScript";

const WIDGET_SRC = "https://app.seatlayer.io/sl-event-widget@0.js";

interface SeatLayerEventProps {
/** Your event key, from the event's page in the dashboard. */
eventKey: string;
/**
* "picker" puts the seat map on your page. "card", "banner" and "button"
* show a smaller block that opens the seat map when clicked.
*/
layout?: "picker" | "card" | "banner" | "button";
/** CSS height of the seat map when layout is "picker". */
height?: string;
/** Where buyers go if the script cannot load: your event's booking page. */
fallbackUrl: string;
}

/**
* Hosted Ticketing in React: the same one tag as the HTML version. SeatLayer
* runs the seat map, the payment through your own gateway and the tickets.
* Try it live: https://seatlayer.io/demo/hosted/paid-event/
*/
export function SeatLayerEvent({ eventKey, layout = "picker", height = "740px", fallbackUrl }: SeatLayerEventProps) {
useEffect(() => {
loadSeatLayerScript(WIDGET_SRC, "SeatLayerEvents");
}, [eventKey]);

return (
<div
key={eventKey}
data-seatlayer-event={eventKey}
data-layout={layout}
data-checkout="hosted"
data-height={layout === "picker" ? height : undefined}
data-fallback-url={fallbackUrl}
>
<a href={fallbackUrl}>Book tickets on SeatLayer</a>
</div>
);
}
18 changes: 18 additions & 0 deletions src/hosted/loadScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Loads a SeatLayer embed script once per page. When the script is already on
* the page (a second embed, or a client-side route change), it asks the
* runtime to scan again so the new element is rendered too.
*/
export function loadSeatLayerScript(src: string, runtime: "SeatLayerEvents" | "SeatLayerCollections") {
if (typeof window === "undefined") return;
const loaded = (window as unknown as Record<string, { scan?: () => void } | undefined>)[runtime];
if (loaded?.scan) {
loaded.scan();
return;
}
if (document.querySelector(`script[src="${src}"]`)) return;
const script = document.createElement("script");
script.src = src;
script.defer = true;
document.body.appendChild(script);
}
20 changes: 0 additions & 20 deletions wrangler.jsonc

This file was deleted.

Loading