SeatSquirrel
Embedding

Self-Hosting Guide

Deploy and configure the self-hosted version of SeatSquirrel Designer and Picker

Overview

The self-hosted version of SeatSquirrel provides a complete, standalone seat map solution that you can deploy on your own infrastructure. It includes both the Designer (for creating layouts) and Picker (for seat selection) as static files that can be served from any web server.

Key Benefits:

  • Complete control over your infrastructure
  • No external dependencies on SeatSquirrel servers
  • Data stays entirely within your systems
  • Same SDK API as the hosted version

Self-Hosting Access

Contact us for access to the self-hosted version.

Distribution Contents

The self-hosted package contains a directory with the following structure:

── designer.html          # Designer application entry point
── picker.html            # Picker application entry point
── sdk/
   ├── designer-sdk.js    # Designer embedding SDK
   └── picker-sdk.js      # Picker embedding SDK
── assets/
   ├── *.js               # Bundled JavaScript modules
   └── *.css              # Bundled stylesheets

Files Overview

FilePurpose
designer.htmlThe layout designer application
picker.htmlThe seat selection/booking application
assets/*Required JS/CSS bundles (auto-loaded by HTML files)
sdk/designer-sdk.jsJavaScript SDK for embedding the Designer, called from your webpage
sdk/picker-sdk.jsJavaScript SDK for embedding the Picker, called from your webpage

Deployment

The self-hosted version can be deployed to any static hosting platform or web server capable of serving static files (nginx, Apache, Caddy, etc.).

Static Hosting Guide

For detailed deployment instructions for various platforms (Netlify, Vercel, Cloudflare Pages, AWS S3, nginx, etc.), see the official Vite Static Deployment Guide.

Example: nginx Configuration

server {
    listen 80;
    server_name seatsquirrel.yourdomain.com;
    root /var/www/seatsquirrel;
    index designer.html;

    location / {
        try_files $uri $uri/ /designer.html;
    }

    # Cache static assets
    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Example: Caddy Configuration

seatsquirrel.yourdomain.com {
    root * /var/www/seatsquirrel
    file_server
    try_files {path} /designer.html
}

Example: Running locally with Serve

  1. Install Serve: pnpm install -g serve
  2. Serve the files with serve from inside the directory: serve

This will start the server on port 3000.

Configuration

Domain Verification

The self-hosted version uses the SEATSQUIRREL_APPROVED_DOMAINS environment variable to control which domains are allowed to embed the Designer and Picker. This is a security measure to prevent unauthorized embedding.

Important

Set the SEATSQUIRREL_APPROVED_DOMAINS environment variable before your application loads. The exact method depends on your deployment platform. For example, in a .env file:

SEATSQUIRREL_APPROVED_DOMAINS=https://example.com,https://www.example.com,http://localhost:3000

Format: Comma-separated list of full URLs with protocol

# Example: Allow embedding from these domains
SEATSQUIRREL_APPROVED_DOMAINS=https://example.com,https://www.example.com,http://localhost:3000

Important Notes:

  • Include the full URL with protocol (https:// or http://)
  • Include the port for localhost: http://localhost:3000
  • No trailing slashes
  • Separate multiple domains with commas

Using the SDKs

The self-hosted SDKs work identically to the hosted version. You must set the baseUrl option to point to your self-hosted deployment in the SDK constructor.

Designer SDK

<!-- Include the SDK from your self-hosted location -->
<script src="https://seatsquirrel.yourdomain.com/sdk/designer-sdk.js"></script>

<div id="designer-container" style="width: 100%; height: 700px;"></div>

<script>
  const designer = new SeatSquirrel.Designer({
    container: '#designer-container',
    // Point to your self-hosted deployment
    baseUrl: 'https://seatsquirrel.yourdomain.com',

    onReady: function () {
      console.log('Designer is ready');
    },

    onExport: function (layoutData) {
      // Save the layout to your database
      console.log('Layout exported:', layoutData);
      saveToYourDatabase(layoutData);
    },

    onLayoutChanged: function (data) {
      console.log('Has unsaved changes:', data.hasChanges);
    },

    onError: function (error) {
      console.error('Designer error:', error);
    },
  });
</script>

Picker SDK

<!-- Include the SDK from your self-hosted location -->
<script src="https://seatsquirrel.yourdomain.com/sdk/picker-sdk.js"></script>

<div id="picker-container" style="width: 100%; height: 600px;"></div>

<script>
  const picker = new SeatSquirrel.StatelessPicker({
    container: '#picker-container',
    // Point to your self-hosted deployment
    baseUrl: 'https://seatsquirrel.yourdomain.com',

    onReady: function () {
      // Load your layout data from your database
      picker.loadLayout(layoutDataFromYourDatabase);
      picker.setAvailability(availabilityFromYourDatabase);
    },

    onSelectionChange: function (data) {
      console.log('Selected:', data.selections);
      console.log('Total:', data.totals.amount);
    },

    onComplete: function (data) {
      // Handle checkout - process the selections
      console.log('Checkout requested:', data);
      processCheckout(data.selections, data.totals);
    },

    onError: function (error) {
      console.error('Picker error:', error);
    },
  });
</script>

Complete Example

Here's a complete HTML page demonstrating both the Designer and Picker:

<!DOCTYPE html>
<html>
  <head>
    <title>SeatSquirrel Self-Hosted Demo</title>
    <style>
      .container {
        width: 100%;
        height: 700px;
        border: 1px solid #e5e7eb;
        border-radius: 8px;
      }
      .toolbar {
        display: flex;
        gap: 10px;
        margin-bottom: 20px;
      }
      button {
        padding: 10px 20px;
        border-radius: 6px;
        border: none;
        cursor: pointer;
      }
      .primary {
        background: #3b82f6;
        color: white;
      }
      .secondary {
        background: #e5e7eb;
        color: #374151;
      }
      #status {
        margin-left: 10px;
        font-weight: 600;
      }
    </style>
  </head>
  <body>
    <h1>Venue Layout Designer</h1>

    <div class="toolbar">
      <button id="save-btn" class="primary">Save Layout</button>
      <button id="load-btn" class="secondary">Load Layout</button>
      <button id="clear-btn" class="secondary">Clear</button>
      <span id="status"></span>
    </div>

    <div id="designer-container" class="container"></div>

    <!-- Load SDK from your self-hosted location -->
    <script src="https://seatsquirrel.yourdomain.com/sdk/designer-sdk.js"></script>

    <script>
      // Replace with your self-hosted URL
      const SEATSQUIRREL_URL = 'https://seatsquirrel.yourdomain.com';

      let currentLayoutId = null;

      const designer = new SeatSquirrel.Designer({
        container: '#designer-container',
        baseUrl: SEATSQUIRREL_URL,

        onReady: function () {
          document.getElementById('status').textContent = 'Ready';
        },

        onExport: async function (layoutData) {
          // Save to your backend
          const response = await fetch('/api/layouts', {
            method: currentLayoutId ? 'PUT' : 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              id: currentLayoutId,
              data: layoutData,
            }),
          });

          const result = await response.json();
          currentLayoutId = result.id;

          document.getElementById('status').textContent = 'Saved!';
          setTimeout(() => {
            document.getElementById('status').textContent = '';
          }, 2000);
        },

        onLayoutChanged: function (data) {
          if (data.hasChanges) {
            document.getElementById('status').textContent = 'Unsaved changes';
          }
        },

        onError: function (error) {
          alert('Error: ' + error.message);
        },
      });

      // Save button - triggers onExport callback
      document.getElementById('save-btn').addEventListener('click', () => {
        designer.getLayout();
      });

      // Load button
      document.getElementById('load-btn').addEventListener('click', async () => {
        const layoutId = prompt('Enter layout ID:');
        if (!layoutId) return;

        const response = await fetch(`/api/layouts/${layoutId}`);
        const layoutData = await response.json();

        designer.loadLayout(layoutData);
        currentLayoutId = layoutId;
      });

      // Clear button
      document.getElementById('clear-btn').addEventListener('click', () => {
        if (confirm('Clear the current layout?')) {
          designer.clearLayout();
          currentLayoutId = null;
        }
      });
    </script>
  </body>
</html>

SDK API Reference

The self-hosted SDKs have the same API as the hosted version. For complete documentation on all methods, callbacks, and data structures, see:

Key Callbacks

SDKCallbackPurpose
DesigneronExport(layoutData)Receive layout data when user exports or getLayout() is called
DesigneronLayoutChanged(data)Track unsaved changes
PickeronSelectionChange(data)Receive seat/area selections in real-time
PickeronComplete(data)Handle checkout when user clicks the checkout button

Data Flow

  1. Designer: Create/edit layouts → onExport callback → Save to your database
  2. Picker: Load layout from database → loadLayout() → User selects seats → onComplete → Process checkout

Troubleshooting

Assets Not Loading

Problem: Designer/Picker page shows blank or broken

Solutions:

  1. Ensure all files from dist/app/ are deployed
  2. Verify assets are served from the root path (/assets/...)
  3. Check browser console for 404 errors
  4. Ensure correct MIME types for .js and .css files

"Access Denied" Error

Problem: Embedding fails with "Access Denied"

Solutions:

  1. Check APPROVED_DOMAINS includes your embedding domain
  2. Include the full URL with protocol: https://example.com
  3. Include port for localhost: http://localhost:3000
  4. Check browser console for specific error details

SDK Methods Not Working

Problem: Methods like loadLayout() don't work

Solutions:

  1. Wait for onReady callback before calling methods
  2. Verify baseUrl points to your self-hosted deployment
  3. Check that the container element exists and has a height
  4. Check browser console for errors

Support

For issues specific to the self-hosted version, check:

  1. Browser console for JavaScript errors
  2. Network tab for failed requests
  3. Server logs for access/error information

For SDK API questions, refer to the Designer or Picker embedding documentation.