diff --git a/.env.example b/.env.example index 4d9c8ae..75d97cf 100644 --- a/.env.example +++ b/.env.example @@ -6,3 +6,7 @@ VITE_PUBLIC_WEB_BASE=http://localhost:5175 # Production (see .env.production — used automatically by `npm run build`) # VITE_API_BASE_URL=https://api.eventbuzz.live/api # VITE_PUBLIC_WEB_BASE=https://web.eventbuzz.live + +# Landing page demo checkout (public ticket catalog) +VITE_DEMO_EVENT_ID=6a620657ef2449311c51fd6b +VITE_DEMO_TICKET_ID=6a6b37ba9ce2326fe3e591f6 diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..10df967 --- /dev/null +++ b/.env.production @@ -0,0 +1,4 @@ +VITE_API_BASE_URL=https://api.eventbuzz.live/api +VITE_PUBLIC_WEB_BASE=https://web.eventbuzz.live +VITE_DEMO_EVENT_ID=6a620657ef2449311c51fd6b +VITE_DEMO_TICKET_ID=6a6b37ba9ce2326fe3e591f6 diff --git a/deploy.sh b/deploy.sh index 616bb6c..e7be5c5 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,181 +1,3 @@ #!/usr/bin/env bash -# -# EventBuzz production deploy -# -# Usage: -# ./deploy.sh # backend + dashboard + visitor website + public web -# ./deploy.sh backend -# ./deploy.sh frontend -# ./deploy.sh website -# ./deploy.sh web -# ./deploy.sh all -# -# Requirements: ssh, rsync, npm, aws CLI (profile stratawiztech) -# -set -euo pipefail - -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -SSH_KEY="${SSH_KEY:-$HOME/Documents/Keys/ravimac}" -SSH_HOST="${SSH_HOST:-root@168.144.70.247}" -BACKEND_REMOTE="${BACKEND_REMOTE:-/var/www/backend}" -AWS_PROFILE="${AWS_PROFILE:-stratawiztech}" - -FRONTEND_DIR="$ROOT/frontend" -EVENTBUZZ_DIR="$ROOT/eventbuzz" -WEB_DIR="$ROOT/web" -BACKEND_DIR="$ROOT/backend" - -S3_CONSOLE="s3://eventbuzzlive/console/" -S3_WEBSITE="s3://eventbuzzlive/website/" -S3_WEB="s3://eventbuzzlive/web/" - -# CloudFront distributions (console dashboard + visitor website + public web) -CF_CONSOLE="${CF_CONSOLE:-E1QQM75X0JF0IU}" -CF_WEBSITE="${CF_WEBSITE:-E3ORUQT7269RIM}" -CF_WEB="${CF_WEB:-ETZS6Z42DG7I6}" - -log() { printf '\n\033[1;36m==>\033[0m %s\n' "$*"; } -die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } - -ssh_cmd() { - ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new "$SSH_HOST" "$@" -} - -rsync_ssh() { - rsync -avz -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new" "$@" -} - -invalidate_cloudfront() { - local distribution_id="$1" - local label="$2" - log "CloudFront invalidation ($label) → $distribution_id /*" - aws cloudfront create-invalidation \ - --distribution-id "$distribution_id" \ - --paths "/*" \ - --profile "$AWS_PROFILE" \ - --output text \ - --query 'Invalidation.Id' -} - -# Ensure dashboard / visitor builds use production API URLs (not localhost). -apply_production_env() { - log "Applying production env for dashboard (frontend/.env.production)" - if [[ ! -f "$FRONTEND_DIR/.env.production" ]]; then - die "Missing $FRONTEND_DIR/.env.production — create it with production VITE_* URLs." - fi - # If a local .env exists, show what we're overriding for clarity - if [[ -f "$FRONTEND_DIR/.env" ]]; then - grep -E '^VITE_API_URL=' "$FRONTEND_DIR/.env.production" || true - fi - - log "Applying production env for visitor site (eventbuzz/.env.production)" - if [[ ! -f "$EVENTBUZZ_DIR/.env.production" ]]; then - die "Missing $EVENTBUZZ_DIR/.env.production — create it with production VITE_* URLs." - fi - grep -E '^VITE_API_BASE_URL=' "$EVENTBUZZ_DIR/.env.production" || true - - log "Applying production env for public web (web/.env.production)" - if [[ ! -f "$WEB_DIR/.env.production" ]]; then - die "Missing $WEB_DIR/.env.production — create it with production VITE_* URLs." - fi - grep -E '^VITE_(API_BASE_URL|PUBLIC_WEB_BASE)=' "$WEB_DIR/.env.production" || true -} - -deploy_backend() { - log "Deploying backend → $SSH_HOST:$BACKEND_REMOTE/" - [[ -d "$BACKEND_DIR/src" ]] || die "Backend src folder not found at $BACKEND_DIR/src" - [[ -f "$BACKEND_DIR/package.json" ]] || die "Backend package.json not found" - [[ -f "$SSH_KEY" ]] || die "SSH key not found: $SSH_KEY" - - rsync_ssh --delete \ - "$BACKEND_DIR/src/" \ - "$SSH_HOST:$BACKEND_REMOTE/src/" - - log "Syncing package.json + package-lock.json" - rsync_ssh \ - "$BACKEND_DIR/package.json" \ - "$BACKEND_DIR/package-lock.json" \ - "$SSH_HOST:$BACKEND_REMOTE/" - - log "Installing dependencies + reloading API on server (pm2)" - ssh_cmd "cd '$BACKEND_REMOTE' && npm ci --omit=dev && (pm2 reload all || pm2 restart all || echo 'pm2 reload skipped — run manually on server')" -} - -deploy_frontend() { - log "Building dashboard (console) for production" - apply_production_env - cd "$FRONTEND_DIR" - npm run build - - log "Uploading dashboard dist → $S3_CONSOLE" - aws s3 rm "$S3_CONSOLE" --recursive --profile "$AWS_PROFILE" - aws s3 sync "$FRONTEND_DIR/dist" "$S3_CONSOLE" --profile "$AWS_PROFILE" - - invalidate_cloudfront "$CF_CONSOLE" "console" - - log "Dashboard deploy complete" -} - -deploy_website() { - log "Building visitor website for production" - apply_production_env - cd "$EVENTBUZZ_DIR" - npm run build - - log "Uploading visitor site dist → $S3_WEBSITE" - aws s3 rm "$S3_WEBSITE" --recursive --profile "$AWS_PROFILE" - aws s3 sync "$EVENTBUZZ_DIR/dist" "$S3_WEBSITE" --profile "$AWS_PROFILE" - - invalidate_cloudfront "$CF_WEBSITE" "website" - - log "Visitor website deploy complete" -} - -deploy_web() { - log "Building public web app for production" - apply_production_env - cd "$WEB_DIR" - npm run build - - log "Uploading public web dist → $S3_WEB" - aws s3 rm "$S3_WEB" --recursive --profile "$AWS_PROFILE" - aws s3 sync "$WEB_DIR/dist" "$S3_WEB" --profile "$AWS_PROFILE" - - invalidate_cloudfront "$CF_WEB" "web" - - log "Public web deploy complete" -} - -usage() { - cat < - EventBuzz + EventBuzz — Live events, ticketing & engagement +
diff --git a/package-lock.json b/package-lock.json index e6d4bea..3943f8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,19 @@ "name": "eventbuzz-web", "version": "0.0.1", "dependencies": { + "bootstrap": "^5.3.8", "bootstrap-icons": "^1.13.1", "flatpickr": "^4.6.13", + "qrcode": "^1.5.4", "react": "^18.3.1", + "react-bootstrap": "^2.10.10", "react-dom": "^18.3.1", + "react-icons": "^5.7.0", + "react-player": "^3.4.0", "react-router-dom": "^6.28.0" }, "devDependencies": { + "@types/qrcode": "^1.5.6", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.4", @@ -256,6 +262,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", @@ -746,6 +761,33 @@ "node": ">=18" } }, + "node_modules/@internationalized/date": { + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.3.tgz", + "integrity": "sha512-fuLX+3ZKLsxI73y8b01EG/WjHb6gE6weCqlfawPO27kBWGMh9G1yH6Csv1uU7/cac9H2GHmOMt6CjmuQ1aia4Q==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.7.tgz", + "integrity": "sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/string": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.10.tgz", + "integrity": "sha512-PDx6//vHSpRnHfxqMqto11zQvhsaU74O3mKv2F/0eicGZcl9NLjQmGlbHz/LsJh5tLKp4A4L7ZVTzN1/MmMTvA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -796,6 +838,111 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mux/mux-data-google-ima": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@mux/mux-data-google-ima/-/mux-data-google-ima-0.3.17.tgz", + "integrity": "sha512-4wpH6dYybyZhqLn9qGn/+67Z8MZnQRAdqTFEEZw2bx61M9q01uPYYHxd8qwOnYtUGEeafsdTwVHVxKHGD3oc1A==", + "license": "MIT", + "dependencies": { + "mux-embed": "5.18.1" + } + }, + "node_modules/@mux/mux-player": { + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/@mux/mux-player/-/mux-player-3.13.2.tgz", + "integrity": "sha512-qYOYK4/vAr0VEs5SoC8KRieKm3VSgXJpyuERvOIDYvomIVGVpeqGzhjeDeZ7XKQBH3swg0RdAT3kZaTWXe5s9Q==", + "license": "MIT", + "dependencies": { + "@mux/mux-video": "0.31.2", + "@mux/playback-core": "0.35.2", + "media-chrome": "~4.19.0", + "player.style": "^0.3.0" + } + }, + "node_modules/@mux/mux-player-react": { + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/@mux/mux-player-react/-/mux-player-react-3.13.2.tgz", + "integrity": "sha512-yjAmiqVPYTKi88LAuHxSwKiYrZUDy1ofUYjrDP91mvPfz8BfTvUanzpyE8+a6pRIZFp55rmiAnWyjWKuqSGASw==", + "license": "MIT", + "dependencies": { + "@mux/mux-player": "3.13.2", + "@mux/playback-core": "0.35.2", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^17.0.0-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0", + "react": "^17.0.2 || ^17.0.0-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0", + "react-dom": "^17.0.2 || ^17.0.2-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@mux/mux-video": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/@mux/mux-video/-/mux-video-0.31.2.tgz", + "integrity": "sha512-waahxlrat1PpRzcY2pwMQJ5Gyhos+D6iRyKnYzF0wBf4SESfBtunSBj3EuKuljypm8bamh9gWnlA0LSzm5Pz1w==", + "license": "MIT", + "dependencies": { + "@mux/mux-data-google-ima": "^0.3.4", + "@mux/playback-core": "0.35.2", + "@types/google_interactive_media_ads_types": "^3.697.0", + "castable-video": "~1.1.13", + "custom-media-element": "~1.4.6", + "media-tracks": "~0.3.5" + } + }, + "node_modules/@mux/playback-core": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@mux/playback-core/-/playback-core-0.35.2.tgz", + "integrity": "sha512-AsMV0LMwJm9T1ig6BBYSddeMlkzz+2RGcd6dkOgYk+L5Hx477ovbTxJZJ/vTW+rZjNWHcG3BvgNbSLgiubyZhw==", + "license": "MIT", + "dependencies": { + "hls.js": "~1.6.15", + "mux-embed": "^5.16.1" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.10.1.tgz", + "integrity": "sha512-jn038/ZYmu6DpfXJ6r2U9zFFppjbc9wnApPJSCxao2RZVEqep4YyoniHSy8qv6V21/xyS4IV7W9a+X2jOjSuag==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "^3.48.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.36.1.tgz", + "integrity": "sha512-AzsuD9OfxTOZMMvTRhlN3oHBwOmFN7tDh27LzqmHt4+uOgPhJT7ZM7/kVs/8/o0WxayMUIk3hBmCFRHv1FUoag==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@remix-run/router": { "version": "1.23.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz", @@ -805,6 +952,60 @@ "node": ">=14.0.0" } }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.4.tgz", + "integrity": "sha512-N4C7haUc3vn4LTwVUPlkJN8Ach/+yIMvRuTVIhjilNHqegY60SGLrzud6errOMNJwSnmYFnt1J0H/k8FE3A4KA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@popperjs/core": "^2.11.8", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.5.0", + "@types/warning": "^3.0.3", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.4", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/@restart/hooks": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.1.tgz", + "integrity": "sha512-EMoH04NHS1pbn07iLTjIjgttuqb7qu4+/EyhAx27MHpoENcB2ZdSsLTNxmKD+WEPnZigo62Qc8zjGnNxoSE/5Q==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -1162,6 +1363,138 @@ "win32" ] }, + "node_modules/@svta/cml-608": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@svta/cml-608/-/cml-608-1.0.2.tgz", + "integrity": "sha512-ZEJ68330gcLKfvVv6Qifr1HR7+GldDUxzkjqSbqRK7jHtHSLSV1JAyDwlYe8+C7ABuY1bp8MpgJ4/Gg+jl+pLQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@svta/cml-cmcd": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@svta/cml-cmcd/-/cml-cmcd-2.3.2.tgz", + "integrity": "sha512-SKBBjLmci0WK8HMjuv+36tVIMktonoOoxsXblOFZmB+ePPV2zjRMTD+2ZmE/1VEPJkKHENyhSjSHgJyeOlvZ1A==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-structured-field-values": "1.1.3", + "@svta/cml-utils": "1.5.0" + } + }, + "node_modules/@svta/cml-cmsd": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@svta/cml-cmsd/-/cml-cmsd-1.0.6.tgz", + "integrity": "sha512-LUORV6bb0TbU4rSC2HoPqUCix1igLrXkRQXWiIyJo2OMzb14kAK/1jsW0mzY6up6w1GrjKQcjc6OwqJdo/zd/g==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-cta": "1.0.6", + "@svta/cml-structured-field-values": "1.1.3", + "@svta/cml-utils": "1.5.0" + } + }, + "node_modules/@svta/cml-cta": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@svta/cml-cta/-/cml-cta-1.0.6.tgz", + "integrity": "sha512-l13/4myTX1EbRd38nY8J1umVY5UdR/nZO6CeKqVLXnMMIayg7/fu0TueZckjTA9Loal55MGK1xbHnXVjz7OUUw==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-structured-field-values": "1.1.3", + "@svta/cml-utils": "1.5.0" + } + }, + "node_modules/@svta/cml-dash": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@svta/cml-dash/-/cml-dash-1.0.6.tgz", + "integrity": "sha512-4XtHYlPrzL/dRe/8XmRQoLnTo9S86tISgrl67eUqKl5MtNTpZBYTncuPrspslPZPZROBBWNrBuepYfYUtU9CKA==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-utils": "1.5.0" + } + }, + "node_modules/@svta/cml-id3": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@svta/cml-id3/-/cml-id3-1.0.6.tgz", + "integrity": "sha512-63j8gkAnPOmOBWlp0hIZPvsIioZttdbg6/TgwITqMYbSLYVJ+6QGa/UtIP0I84NsfstANw6QdCn7i8SS08kn1A==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-utils": "1.5.0" + } + }, + "node_modules/@svta/cml-request": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@svta/cml-request/-/cml-request-1.0.12.tgz", + "integrity": "sha512-4sJvnnoNpq58j2mCGP8k+MF6wVy/qa4gbt6kfT1dPIKmn3mPxw+JVfilhcWsUi+peK2yCZxOJJYyHj1cAcQE1w==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-cmcd": "2.3.2", + "@svta/cml-utils": "1.5.0", + "@svta/cml-xml": "1.1.4" + } + }, + "node_modules/@svta/cml-structured-field-values": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@svta/cml-structured-field-values/-/cml-structured-field-values-1.1.3.tgz", + "integrity": "sha512-XqLzQOTznz6kh/nsSh8dy1kV7GQjL4csG+2U5EvLGhAqNuNUczbVg5EAsDobKDVg8xhdthdXx2UuwM+ZHQCxSg==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-utils": "1.5.0" + } + }, + "node_modules/@svta/cml-utils": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@svta/cml-utils/-/cml-utils-1.5.0.tgz", + "integrity": "sha512-JMqclD7Akd+GSJiuaYNUHOP2wNtf/nauKeszlYeivSHfi0Lp3pmSW5PXDvJ2dO4aPmmSOQbF0ztTsW9Vcs2Whw==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/@svta/cml-xml": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@svta/cml-xml/-/cml-xml-1.1.4.tgz", + "integrity": "sha512-jbixqjiJIc16SGxylHwiOzO+DuhkGfuP+fJ9AHeVJKdFDKnabgfCDpnp6dvZpZnjMj4nHvzVtuUV7RISPIwYXw==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@svta/cml-utils": "1.5.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1214,18 +1547,42 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/google_interactive_media_ads_types": { + "version": "3.697.1", + "resolved": "https://registry.npmjs.org/@types/google_interactive_media_ads_types/-/google_interactive_media_ads_types-3.697.1.tgz", + "integrity": "sha512-gRjJoPKQRjjTuySHqZe8Pnu6a5B0bKbTUD2ioDLPSWlEuBaAc2p1mzUzy42SCeWkyYffo2Mnv+blUGuvVvx3dQ==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "dev": true, "license": "MIT" }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/react": { "version": "18.3.31", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", - "dev": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1242,6 +1599,31 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/warning": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.4.tgz", + "integrity": "sha512-CqN8MnISMwQbLJXO3doBAV4Yw9hx9/Pyr2rZ78+NfaCnhyRA/nKrpyk6E7mKw17ZOaQdLpK9GiUjrqLzBlN3sg==", + "license": "MIT" + }, + "node_modules/@vimeo/player": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/@vimeo/player/-/player-2.29.0.tgz", + "integrity": "sha512-9JjvjeqUndb9otCCFd0/+2ESsLk7VkDE6sxOBy9iy2ukezuQbplVRi+g9g59yAurKofbmTi/KcKxBGO/22zWRw==", + "license": "MIT", + "dependencies": { + "native-promise-only": "0.8.1", + "weakmap-polyfill": "2.0.4" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", @@ -1263,6 +1645,42 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.10.38", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", @@ -1276,6 +1694,35 @@ "node": ">=6.0.0" } }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/bootstrap-icons": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", @@ -1326,6 +1773,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001799", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", @@ -1347,6 +1803,80 @@ ], "license": "CC-BY-4.0" }, + "node_modules/castable-video": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/castable-video/-/castable-video-1.1.16.tgz", + "integrity": "sha512-wBhe2dZu2afhewL3EaGgVYTyDsa9HvNhY98clMZkNzDrLelOValSrTaoMos9YX7PPBCrgpd1j6YmNyyI2Vbq3w==", + "license": "MIT", + "dependencies": { + "custom-media-element": "~1.4.6" + } + }, + "node_modules/ce-la-react": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ce-la-react/-/ce-la-react-0.3.2.tgz", + "integrity": "sha512-QJ6k4lOD/btI08xG8jBPxRCGXvCnusGGkTsiXk0u3NqUu/W+BXRnFD4PYjwtqh8AWmGa5LDbGk0fLQsqr0nSMA==", + "license": "BSD-3-Clause", + "peerDependencies": { + "react": ">=17.0.0" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cloudflare-video-element": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/cloudflare-video-element/-/cloudflare-video-element-1.3.5.tgz", + "integrity": "sha512-zj9gjJa6xW8MNrfc4oKuwgGS0njRLpOlQjdifbuNxvy8k4Y3pKCyKCMG2XIsjd2iQGhgjS57b1P5VWdJlxcXBw==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/codem-isoboxer": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/codem-isoboxer/-/codem-isoboxer-0.3.10.tgz", + "integrity": "sha512-eNk3TRV+xQMJ1PEj0FQGY8KD4m0GPxT487XJ+Iftm7mVa9WpPFDMWqPt+46buiP5j5Wzqe5oMIhqBcAeKfygSA==", + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1358,9 +1888,50 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, + "node_modules/custom-media-element": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/custom-media-element/-/custom-media-element-1.4.6.tgz", + "integrity": "sha512-/HRYqJOa1ob5ik4q7FIJVYxTJCFs/FL3+cQPAJjUf2uiqrDEzbTgB315gQ2rG8oK3w094W9m5tcB8S5Qah+caA==", + "license": "MIT" + }, + "node_modules/dash-video-element": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/dash-video-element/-/dash-video-element-0.3.2.tgz", + "integrity": "sha512-eN1IqgtTAbq4zVkbt82BDwQtybQ6WOXyQU5HbftUSnqo+SHAPbZCif1Y7uViAhgvuEPvZtbiXDiacvvTeGxc/g==", + "license": "MIT", + "dependencies": { + "custom-media-element": "^1.4.6", + "dashjs": "^5.0.3", + "media-tracks": "^0.3.5" + } + }, + "node_modules/dashjs": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dashjs/-/dashjs-5.2.0.tgz", + "integrity": "sha512-2W2KHFN53Sk7+rtdnIfSUK/3Oov+hraMTeVZwDOTSCNKM1cQtFiJdblNzRMnl59a/QDseG7JW+PC9mh/B+CMcg==", + "license": "BSD-3-Clause", + "dependencies": { + "@svta/cml-608": "1.0.2", + "@svta/cml-cmcd": "2.3.2", + "@svta/cml-cmsd": "1.0.6", + "@svta/cml-dash": "1.0.6", + "@svta/cml-id3": "1.0.6", + "@svta/cml-request": "1.0.12", + "@svta/cml-xml": "1.1.4", + "bcp-47-match": "^2.0.3", + "codem-isoboxer": "0.3.10", + "fast-deep-equal": "3.1.3", + "html-entities": "^2.6.0", + "imsc": "^1.1.5", + "localforage": "^1.10.0", + "path-browserify": "^1.0.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1379,6 +1950,40 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.378", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz", @@ -1386,6 +1991,12 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", @@ -1438,6 +2049,12 @@ "node": ">=6" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1456,6 +2073,19 @@ } } }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/flatpickr": { "version": "4.6.13", "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", @@ -1487,6 +2117,81 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/hls-video-element": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/hls-video-element/-/hls-video-element-1.5.11.tgz", + "integrity": "sha512-tJJ65/52CDxj8XFyIve6zT9nVVdUIc6mqvKR25X0ycPKHk07rpjp4xxVteeCefDUBSf/tFLhlICFmn3KWj37xA==", + "license": "MIT", + "dependencies": { + "custom-media-element": "^1.4.6", + "hls.js": "^1.6.5", + "media-tracks": "^0.3.5" + } + }, + "node_modules/hls.js": { + "version": "1.6.17", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.17.tgz", + "integrity": "sha512-NUplVGVuc1hSPwdB/9/cbRkUmLrYi75/hqiXKdA+l300pJNxDu96R7jRb2imDzWJqIUF4I5ThmAdp9GvOCXsuQ==", + "license": "Apache-2.0" + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/imsc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/imsc/-/imsc-1.1.5.tgz", + "integrity": "sha512-V8je+CGkcvGhgl2C1GlhqFFiUOIEdwXbXLiu1Fcubvvbo+g9inauqT3l0pNYXGoLPBj3jxtZz9t+wCopMkwadQ==", + "license": "BSD-2-Clause", + "dependencies": { + "sax": "1.2.1" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1519,6 +2224,36 @@ "node": ">=6" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "license": "Apache-2.0", + "dependencies": { + "lie": "3.1.1" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1541,6 +2276,27 @@ "yallist": "^3.0.2" } }, + "node_modules/media-chrome": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/media-chrome/-/media-chrome-4.19.2.tgz", + "integrity": "sha512-4ai1ITN8wBhwugQcRgqe3tN0z6OSKGOXqHLNrS04MgKFfsLqu6Dm8MPq02pI9Y9ZKoXtFjIl85jOryIW9es3BA==", + "license": "MIT", + "dependencies": { + "ce-la-react": "^0.3.2" + } + }, + "node_modules/media-played-ranges-mixin": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/media-played-ranges-mixin/-/media-played-ranges-mixin-0.1.0.tgz", + "integrity": "sha512-zTsvkleu5sAyTsPVxDI+KUbCwy/lXwHgOPi3ER9S3lhtAWhGTQH6qxvfrVMym1cvoLU36SPbVr6Qe8Zxyc0WpA==", + "license": "MIT" + }, + "node_modules/media-tracks": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/media-tracks/-/media-tracks-0.3.5.tgz", + "integrity": "sha512-l54rkKXlLBt3ob3zOLWHcnjvwUmX5bNEZ70igyapOZZC9imzqBmq1oz8p2roiV04KhjblFIi2hetLPF1oYVLRA==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1548,6 +2304,12 @@ "dev": true, "license": "MIT" }, + "node_modules/mux-embed": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/mux-embed/-/mux-embed-5.18.1.tgz", + "integrity": "sha512-ePsHjiEKY+FgrSBiMmaF+LOtTQSSBWv/1zqpREQFN96JE93xlsArT/MEi30yKOE06MgjOlL70YI750molu3y7g==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.15", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", @@ -1567,6 +2329,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==", + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.49", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.49.tgz", @@ -1577,6 +2345,66 @@ "node": ">=18" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1597,6 +2425,31 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/player.style": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/player.style/-/player.style-0.3.4.tgz", + "integrity": "sha512-5O9bkbq0APQIkhptyZwp0gUgheCeImGPFo4hvPF2M5xBmgsUYzsUPHCfMye2xyeRE1zvQBKtziaC3jPgnHE1tw==", + "license": "MIT", + "workspaces": [ + ".", + "site", + "examples/*", + "scripts/*", + "themes/*" + ], + "dependencies": { + "media-chrome": "~4.19.0" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", @@ -1626,6 +2479,47 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "license": "MIT", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -1638,6 +2532,58 @@ "node": ">=0.10.0" } }, + "node_modules/react-aria": { + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.51.0.tgz", + "integrity": "sha512-AyWLw0XR38cFPwBu/ErgGaVrc5dupLEKmRlMXTGvFKOtbaGRQ2+yQJkjVhpdHhoRhU4+G+tJDFeHDTS8tK3bfQ==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.3", + "@internationalized/number": "^3.6.7", + "@internationalized/string": "^3.2.10", + "@react-types/shared": "^3.36.1", + "@swc/helpers": "^0.5.0", + "aria-hidden": "^1.2.3", + "clsx": "^2.0.0", + "react-stately": "3.49.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/react-bootstrap": { + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.10.tgz", + "integrity": "sha512-gMckKUqn8aK/vCnfwoBpBVFUGT9SVQxwsYrp9yDHt0arXMamxALerliKBxr1TPbntirK/HGrUAHYbAeQTa9GHQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.9.4", + "@types/prop-types": "^15.7.12", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -1651,6 +2597,50 @@ "react": "^18.3.1" } }, + "node_modules/react-icons": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", + "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-player": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-3.4.0.tgz", + "integrity": "sha512-QpQSHXtnMBKjQVNeaCYMtTVcynWQ0DDDhz/FJu1OR9PHLC1Aih94UqNstywzSHbJ6Oc7lI8/7kDDqcIvyTI6zQ==", + "license": "MIT", + "dependencies": { + "@mux/mux-player-react": "^3.8.0", + "cloudflare-video-element": "^1.3.4", + "dash-video-element": "^0.3.0", + "hls-video-element": "^1.5.9", + "spotify-audio-element": "^1.0.3", + "tiktok-video-element": "^0.1.1", + "twitch-video-element": "^0.1.5", + "vimeo-video-element": "^1.6.1", + "wistia-video-element": "^1.3.5", + "youtube-video-element": "^1.8.0" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18 || ^19", + "react": "^17.0.2 || ^18 || ^19", + "react-dom": "^17.0.2 || ^18 || ^19" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -1693,6 +2683,54 @@ "react-dom": ">=16.8" } }, + "node_modules/react-stately": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.49.0.tgz", + "integrity": "sha512-13iNq2KzBrRAzxRc+n53hgROfIistiYY/sPtIhCw1qUB7/kmo+X1xEU2uiS5zcCIrc55AUPwoHqOIIpKWSwB9A==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.3", + "@internationalized/number": "^3.6.7", + "@internationalized/string": "^3.2.10", + "@react-types/shared": "^3.36.1", + "@swc/helpers": "^0.5.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/rollup": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", @@ -1738,6 +2776,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -1757,6 +2801,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1767,6 +2817,50 @@ "node": ">=0.10.0" } }, + "node_modules/spotify-audio-element": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spotify-audio-element/-/spotify-audio-element-1.0.4.tgz", + "integrity": "sha512-QdKrJPkYCzaNwwz2vN2eDGyoW0KmQFmnwVprB41mpMzj4qujbqr6pegEchQeTn0b5PceKiLoVu0pp2QDpTcWnw==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/super-media-element": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/super-media-element/-/super-media-element-1.4.2.tgz", + "integrity": "sha512-9pP/CVNp4NF2MNlRzLwQkjiTgKKe9WYXrLh9+8QokWmMxz+zt2mf1utkWLco26IuA3AfVcTb//qtlTIjY3VHxA==", + "license": "MIT" + }, + "node_modules/tiktok-video-element": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tiktok-video-element/-/tiktok-video-element-0.1.2.tgz", + "integrity": "sha512-w6TboLm236XJKKiIXIhCbYCnUxbixBbaAoty0etaEAZ/2kHkVIdfZdv2oouMU/HGMsWCHI/VjQ3wU3MJ+s192Q==", + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -1784,6 +2878,18 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/twitch-video-element": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/twitch-video-element/-/twitch-video-element-0.1.6.tgz", + "integrity": "sha512-X7l8gy+DEFKJ/EztUwaVnAYwQN9fUJxPkOVJj2sE62sGvGU4DNLyvmOsmVulM+8Plc5dMg6hYIMNRAPaH+39Uw==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1798,6 +2904,28 @@ "node": ">=14.17" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -1829,6 +2957,25 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vimeo-video-element": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/vimeo-video-element/-/vimeo-video-element-1.7.2.tgz", + "integrity": "sha512-7QM7fvSZvTTSq4igxBuO6Gc+0u3Exgk4IaLNixVzilCPzHEf7SN8b6YLXSM5QCs0ineTJI4XjiUCSoIbabHvwg==", + "license": "MIT", + "dependencies": { + "@vimeo/player": "2.29.0", + "media-played-ranges-mixin": "^0.1.0" + } + }, "node_modules/vite": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", @@ -1904,12 +3051,109 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/weakmap-polyfill": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/weakmap-polyfill/-/weakmap-polyfill-2.0.4.tgz", + "integrity": "sha512-ZzxBf288iALJseijWelmECm/1x7ZwQn3sMYIkDr2VvZp7r6SEKuT8D0O9Wiq6L9Nl5mazrOMcmiZE/2NCenaxw==", + "license": "MIT", + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wistia-video-element": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/wistia-video-element/-/wistia-video-element-1.4.0.tgz", + "integrity": "sha512-udI8/yiMZ+KIGwYK1qNOFiXl+s/wffiY+XkwTXlBFICZylFalOS0QYEQqYOmuBsm3jNfGUS4O50tLuN7Ejqm3A==", + "license": "MIT", + "dependencies": { + "super-media-element": "~1.4.2" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/youtube-video-element": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/youtube-video-element/-/youtube-video-element-1.9.0.tgz", + "integrity": "sha512-Hh0dbQM+FVlUaYUbpYkZNUvdKxTNcSNvTGzkQKYShltnX+LRHEp2eYvC2Zm43eU8Np+CBZuoNR2i+seCYzzAyg==", + "license": "MIT", + "dependencies": { + "media-played-ranges-mixin": "^0.1.0" + } } } } diff --git a/package.json b/package.json index 3536389..2180c96 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,19 @@ "preview": "vite preview --port 5175" }, "dependencies": { + "bootstrap": "^5.3.8", "bootstrap-icons": "^1.13.1", "flatpickr": "^4.6.13", + "qrcode": "^1.5.4", "react": "^18.3.1", + "react-bootstrap": "^2.10.10", "react-dom": "^18.3.1", + "react-icons": "^5.7.0", + "react-player": "^3.4.0", "react-router-dom": "^6.28.0" }, "devDependencies": { + "@types/qrcode": "^1.5.6", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.4", diff --git a/src/App.tsx b/src/App.tsx index bbe020f..3beae8d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,5 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { LandingPage } from './pages/LandingPage'; import { EventMicrositePage } from './pages/EventMicrositePage'; import { RegisterPage } from './pages/RegisterPage'; import { FormPage } from './pages/FormPage'; @@ -8,6 +9,7 @@ export default function App() { return ( + } /> } /> } /> } /> diff --git a/src/api/microsite.ts b/src/api/microsite.ts index 9edcbb2..e289741 100644 --- a/src/api/microsite.ts +++ b/src/api/microsite.ts @@ -5,7 +5,7 @@ import type { PublicMicrositePayload, RegistrationInfo } from '../types'; export const micrositeApi = { get(eventId: string): Promise { //Real API call - return apiGet(`/public/events/${eventId}/microsite`); + return apiGet(`/public/events/${eventId}/defaultsite`); //Mock: Testing data // return Promise.resolve({ diff --git a/src/api/publicEventTickets.ts b/src/api/publicEventTickets.ts new file mode 100644 index 0000000..8ec7a19 --- /dev/null +++ b/src/api/publicEventTickets.ts @@ -0,0 +1,163 @@ +import { apiGet, apiPost } from './client'; + +export type PublicTicketCatalogItem = { + _id: string; + name: string; + description?: string; + keyPoints?: string[]; + basePriceMinor: number; + currency: string; + maxPerOrder?: number | null; + requiresBillingState?: boolean; +}; + +export type TicketQuote = { + ticketId: string; + quantity: number; + billingStateCode: string | null; + baseMinor: number; + taxMinor: number; + totalMinor: number; + platformFeeMinor?: number; + ticketName?: string; + eventName?: string; + taxLines: { label: string; rateBps: number; amountMinor: number }[]; + currency: string; +}; + +export type PublicCheckoutOptions = { + onlineAvailable: boolean; + onlineGateways?: Array<{ + id: string; + provider: string; + label: string; + isActive: boolean; + isPrimary: boolean; + testMode: boolean; + ticketCheckoutSupported: boolean; + }>; + manualUpi: { + manualQrMode: 'upload' | 'dynamic'; + eventName: string; + upiQrImageUrl: string; + upiId: string; + payeeName: string; + instructions: string; + platformFeeInr: number; + } | null; +}; + +export type CheckoutOrderResponse = { + keyId: string; + orderId: string; + amount: number; + currency: string; + transactionId: string; + checkoutSource: string; +}; + +export type TicketAttendeePayload = { + firstName: string; + lastName: string; + email: string; + phone?: string; + organization?: string; + designation?: string; + displayName?: string; + formData?: Record; +}; + +export type PublicEventSummary = { + name: string; + subtitle?: string; + startDate?: string; + endDate?: string; + location?: string; + locationType?: string; + image?: string; +}; + +export function loadRazorpayScript(): Promise { + if (typeof window === 'undefined') return Promise.reject(new Error('No window')); + const w = window as unknown as { Razorpay?: unknown }; + if (w.Razorpay) return Promise.resolve(); + return new Promise((resolve, reject) => { + const s = document.createElement('script'); + s.src = 'https://checkout.razorpay.com/v1/checkout.js'; + s.async = true; + s.onload = () => resolve(); + s.onerror = () => reject(new Error('Failed to load Razorpay')); + document.body.appendChild(s); + }); +} + +export const publicEventTicketsApi = { + getEventSummary(eventId: string): Promise { + return apiGet(`/public/events/${eventId}/summary`); + }, + + listTickets(eventId: string): Promise { + return apiGet<{ tickets: PublicTicketCatalogItem[] }>(`/public/events/${eventId}/tickets`).then( + (data) => data.tickets ?? [], + ); + }, + + checkoutOptions(eventId: string): Promise { + return apiGet(`/public/events/${eventId}/tickets/checkout-options`); + }, + + quote(eventId: string, payload: { ticketId: string; quantity: number; billingStateCode?: string }): Promise { + return apiPost(`/public/events/${eventId}/tickets/quote`, payload); + }, + + manualQuote( + eventId: string, + payload: { ticketId: string; quantity: number; billingStateCode?: string }, + ): Promise { + return apiPost(`/public/events/${eventId}/tickets/manual-quote`, payload); + }, + + checkout( + eventId: string, + payload: { + ticketId: string; + quantity: number; + billingStateCode?: string; + purchaserEmail: string; + sourceFormId?: string; + attendees: TicketAttendeePayload[]; + expectedTotalMinor: number; + idempotencyKey?: string; + orgGatewayId?: string; + }, + ): Promise { + return apiPost(`/public/events/${eventId}/tickets/checkout`, payload); + }, + + verifyPayment( + eventId: string, + payload: { orderId: string; paymentId: string; signature: string }, + ): Promise<{ verified: boolean }> { + return apiPost<{ verified: boolean }>(`/public/events/${eventId}/payment/verify`, payload); + }, + + manualSubmit( + eventId: string, + payload: { + ticketId: string; + quantity: number; + billingStateCode?: string; + purchaserEmail: string; + sourceFormId?: string; + attendees: TicketAttendeePayload[]; + expectedTotalMinor: number; + screenshotDataUrl: string; + buyerNote?: string; + }, + ): Promise<{ orderId: string; message: string }> { + return apiPost<{ orderId: string; message: string }>( + `/public/events/${eventId}/tickets/manual-submit`, + payload, + ); + }, +}; diff --git a/src/assets/logo-white.png b/src/assets/logo-white.png new file mode 100644 index 0000000..3c05926 Binary files /dev/null and b/src/assets/logo-white.png differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..d5afd28 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/assets/main_banner.png b/src/assets/main_banner.png new file mode 100644 index 0000000..3ea8dc1 Binary files /dev/null and b/src/assets/main_banner.png differ diff --git a/src/components/forms/FormShell.tsx b/src/components/forms/FormShell.tsx index deca403..ab1901e 100644 --- a/src/components/forms/FormShell.tsx +++ b/src/components/forms/FormShell.tsx @@ -6,6 +6,8 @@ import { DynamicFormFields, buildSubmitData, validateFormFields } from './Dynami import { FormCaptchaField, validateCaptchaAnswer } from './FormCaptchaField'; import { assignUniqueFieldNames } from '../../utils/formFieldNames'; import { ensureRegistrationCoreFields } from '../../utils/registrationFormFields'; +import { RegistrationTicketStep } from './RegistrationTicketStep'; +import { buildTicketAttendeeFromForm } from '../../utils/registrationTicketAttendee'; type Props = { eventId: string; @@ -25,11 +27,15 @@ export function FormShell({ eventId, formId, titleOverride }: Props) { const [submitting, setSubmitting] = useState(false); const [submitError, setSubmitError] = useState(null); const [done, setDone] = useState(false); + const [doneMessage, setDoneMessage] = useState(null); + const [step, setStep] = useState<1 | 2>(1); const [captchaAnswer, setCaptchaAnswer] = useState(''); const [captchaToken, setCaptchaToken] = useState(''); const [captchaMode, setCaptchaMode] = useState<'server' | 'local'>('server'); const [localCaptchaAnswer, setLocalCaptchaAnswer] = useState(null); + const ticketsEnabled = !!(form?.settings?.paymentEnabled && (form.tickets?.length || 0) > 0); + const handleCaptchaModeChange = useCallback((mode: 'server' | 'local', localAnswer: string | null) => { setCaptchaMode(mode); setLocalCaptchaAnswer(localAnswer); @@ -69,6 +75,12 @@ export function FormShell({ eventId, formId, titleOverride }: Props) { setSubmitError(captchaErr); return; } + + if (ticketsEnabled) { + setStep(2); + return; + } + setSubmitting(true); try { await formsApi.submit(formId, { @@ -103,7 +115,10 @@ export function FormShell({ eventId, formId, titleOverride }: Props) { } if (done) { - const msg = form.settings?.thankYouMessage?.trim() || 'Thank you for your response.'; + const msg = + doneMessage + || form.settings?.thankYouMessage?.trim() + || 'Thank you for your response.'; const ev = form.event; return (
@@ -126,6 +141,11 @@ export function FormShell({ eventId, formId, titleOverride }: Props) { const ev = form.event; const pageTitle = titleOverride || form.name; + const attendee = buildTicketAttendeeFromForm( + form.fields || [], + values, + buildSubmitData(form.fields || [], values), + ); return (
@@ -136,26 +156,46 @@ export function FormShell({ eventId, formId, titleOverride }: Props) { {form.description ?

{form.description}

:
} -
void onSubmit(e)}> - setValues((prev) => ({ ...prev, [key]: v }))} - onFileNamesChange={(key, name) => setFileNames((prev) => ({ ...prev, [key]: name }))} + {step === 1 ? ( + void onSubmit(e)}> + {ticketsEnabled ?

Step 1 of 2 — Your details

: null} + setValues((prev) => ({ ...prev, [key]: v }))} + onFileNamesChange={(key, name) => setFileNames((prev) => ({ ...prev, [key]: name }))} + /> + + {submitError ?

{submitError}

: null} + + + ) : ( + setStep(1)} + onPaid={() => { + setDoneMessage(null); + setDone(true); + }} + onPendingReview={(message) => { + setDoneMessage(message); + setDone(true); + }} /> - - {submitError ?

{submitError}

: null} - - + )}
); diff --git a/src/components/forms/PublicFormEventHeader.tsx b/src/components/forms/PublicFormEventHeader.tsx index 088b995..b6fa99e 100644 --- a/src/components/forms/PublicFormEventHeader.tsx +++ b/src/components/forms/PublicFormEventHeader.tsx @@ -64,7 +64,7 @@ export function PublicFormEventHeader({ const showLoc = d.showVenue && shouldShowLocation(event); const embedded = variant === 'embedded'; - const showBanner = d.showLogo && !!event.imageUrl && !event.logoUrl; + const showBanner = d.showBanner && !!event.imageUrl; const showLogo = d.showLogo && !!event.logoUrl; const showTitle = d.showTitle && !!event.name; const showSubtitle = d.showTitle && !!event.subtitle?.trim(); @@ -91,14 +91,14 @@ export function PublicFormEventHeader({ return (
+ {showLogo ? ( + {event.name} + ) : null} {showBanner ? (
) : null} - {showLogo ? ( - {event.name} - ) : null} {showTitle || showSubtitle || metaItems.length > 0 ? (
{showTitle ?

{event.name}

: null} diff --git a/src/components/forms/RegistrationTicketStep.tsx b/src/components/forms/RegistrationTicketStep.tsx new file mode 100644 index 0000000..2fdabc3 --- /dev/null +++ b/src/components/forms/RegistrationTicketStep.tsx @@ -0,0 +1,406 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import QRCode from 'qrcode'; +import { + loadRazorpayScript, + publicEventTicketsApi, + type PublicTicketCatalogItem, + type TicketAttendeePayload, + type TicketQuote, +} from '../../api/publicEventTickets'; +import { formatTicketPrice, INDIAN_STATES } from '../../utils/registrationTicketAttendee'; +import { buildUpiPayUri } from '../../utils/upiPayUri'; + +const PAYMENT_CANCELLED_MESSAGE = 'Payment cancelled'; + +type ManualUpi = NonNullable< + Awaited>['manualUpi'] +>; + +type Props = { + eventId: string; + formId: string; + eventName?: string; + tickets: PublicTicketCatalogItem[]; + attendee: TicketAttendeePayload; + onBack: () => void; + onPaid: () => void; + onPendingReview: (message: string) => void; +}; + +export function RegistrationTicketStep({ + eventId, + formId, + eventName, + tickets, + attendee, + onBack, + onPaid, + onPendingReview, +}: Props) { + const [ticketId, setTicketId] = useState(tickets[0]?._id || ''); + const [billingState, setBillingState] = useState(''); + const [quote, setQuote] = useState(null); + const [quoteError, setQuoteError] = useState(null); + const [payError, setPayError] = useState(null); + const [busy, setBusy] = useState(false); + const [checkoutChannel, setCheckoutChannel] = useState<'online' | 'manual'>('online'); + const [onlineAvailable, setOnlineAvailable] = useState(false); + const [manualUpi, setManualUpi] = useState(null); + const [screenshotDataUrl, setScreenshotDataUrl] = useState(''); + const [buyerNote, setBuyerNote] = useState(''); + const [dynamicQrDataUrl, setDynamicQrDataUrl] = useState(null); + + const selected = useMemo(() => tickets.find((t) => t._id === ticketId) || null, [tickets, ticketId]); + const payMode = useMemo(() => { + if (onlineAvailable && manualUpi) return 'both'; + if (onlineAvailable) return 'online'; + if (manualUpi) return 'manual'; + return 'none'; + }, [onlineAvailable, manualUpi]); + + const isManualCheckout = payMode === 'manual' || (payMode === 'both' && checkoutChannel === 'manual'); + + useEffect(() => { + let cancelled = false; + void publicEventTicketsApi + .checkoutOptions(eventId) + .then((opts) => { + if (cancelled) return; + setOnlineAvailable(!!opts.onlineAvailable); + setManualUpi(opts.manualUpi || null); + if (!opts.onlineAvailable && opts.manualUpi) setCheckoutChannel('manual'); + }) + .catch(() => { + if (!cancelled) { + setOnlineAvailable(false); + setManualUpi(null); + } + }); + return () => { + cancelled = true; + }; + }, [eventId]); + + useEffect(() => { + if (!ticketId || !selected) { + setQuote(null); + return; + } + if (selected.requiresBillingState && !billingState) { + setQuote(null); + setQuoteError(null); + return; + } + let cancelled = false; + setQuoteError(null); + const run = async () => { + try { + const q = isManualCheckout + ? await publicEventTicketsApi.manualQuote(eventId, { + ticketId, + quantity: 1, + billingStateCode: billingState || undefined, + }) + : await publicEventTicketsApi.quote(eventId, { + ticketId, + quantity: 1, + billingStateCode: billingState || undefined, + }); + if (!cancelled) setQuote(q); + } catch (e) { + if (!cancelled) { + setQuote(null); + setQuoteError(e instanceof Error ? e.message : 'Could not load price'); + } + } + }; + void run(); + return () => { + cancelled = true; + }; + }, [eventId, ticketId, billingState, selected, isManualCheckout]); + + useEffect(() => { + if (!isManualCheckout || !manualUpi || manualUpi.manualQrMode !== 'dynamic') { + setDynamicQrDataUrl(null); + return; + } + const pa = manualUpi.upiId?.trim(); + const evName = (manualUpi.eventName || eventName || '').trim(); + const quoteTotal = quote?.totalMinor; + if (!pa || !selected?.name || !evName || quoteTotal == null) { + setDynamicQrDataUrl(null); + return; + } + const pn = `${selected.name} · ${evName}`.slice(0, 99); + const am = (quoteTotal / 100).toFixed(2); + const uri = buildUpiPayUri({ pa, pn, am }); + if (!uri) { + setDynamicQrDataUrl(null); + return; + } + let cancelled = false; + void QRCode.toDataURL(uri, { margin: 1, width: 280, errorCorrectionLevel: 'M' }).then((url) => { + if (!cancelled) setDynamicQrDataUrl(url); + }); + return () => { + cancelled = true; + }; + }, [isManualCheckout, manualUpi, selected?.name, eventName, quote?.totalMinor]); + + const onScreenshot = useCallback((file: File | null) => { + if (!file) { + setScreenshotDataUrl(''); + return; + } + const reader = new FileReader(); + reader.onload = () => setScreenshotDataUrl(typeof reader.result === 'string' ? reader.result : ''); + reader.readAsDataURL(file); + }, []); + + const pay = async () => { + setPayError(null); + if (!selected || !quote) { + setPayError('Select a ticket and wait for the price.'); + return; + } + if (selected.requiresBillingState && !billingState) { + setPayError('Select your billing state.'); + return; + } + if (!attendee.email || !attendee.firstName || !attendee.lastName) { + setPayError('Registration details are incomplete. Go back and fill required fields.'); + return; + } + if (payMode === 'none') { + setPayError('Payment is not available for this event yet.'); + return; + } + if (isManualCheckout && !screenshotDataUrl) { + setPayError('Upload a payment screenshot to continue.'); + return; + } + + setBusy(true); + try { + const attendees = [attendee]; + const pe = attendee.email; + if (isManualCheckout) { + const res = await publicEventTicketsApi.manualSubmit(eventId, { + ticketId, + quantity: 1, + billingStateCode: billingState || undefined, + purchaserEmail: pe, + sourceFormId: formId, + expectedTotalMinor: quote.totalMinor, + screenshotDataUrl, + buyerNote: buyerNote.trim() || undefined, + attendees, + }); + onPendingReview(res.message || 'Submitted for review.'); + return; + } + + const order = await publicEventTicketsApi.checkout(eventId, { + ticketId, + quantity: 1, + billingStateCode: billingState || undefined, + purchaserEmail: pe, + sourceFormId: formId, + expectedTotalMinor: quote.totalMinor, + idempotencyKey: `reg-${formId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, + attendees, + }); + + await loadRazorpayScript(); + type RazorpayResponse = { razorpay_order_id: string; razorpay_payment_id: string; razorpay_signature: string }; + type RazorpayConstructor = new (opts: { + key: string; + amount: number; + currency: string; + order_id: string; + name: string; + description: string; + handler: (response: RazorpayResponse) => void; + modal?: { ondismiss?: () => void }; + theme?: { color?: string }; + prefill?: { email?: string; name?: string }; + }) => { open: () => void; on: (ev: string, fn: (r: { error: { description?: string } }) => void) => void }; + + const Rzp = (window as unknown as { Razorpay: RazorpayConstructor }).Razorpay; + await new Promise((resolve, reject) => { + let invoked = false; + const rzp = new Rzp({ + key: order.keyId, + amount: order.amount, + currency: order.currency, + order_id: order.orderId, + name: eventName || 'Event', + description: selected.name, + handler: (response: RazorpayResponse) => { + invoked = true; + void publicEventTicketsApi + .verifyPayment(eventId, { + orderId: response.razorpay_order_id, + paymentId: response.razorpay_payment_id, + signature: response.razorpay_signature, + }) + .then(() => resolve()) + .catch((e) => reject(e instanceof Error ? e : new Error('Verification failed'))); + }, + modal: { + ondismiss: () => { + if (!invoked) reject(new Error(PAYMENT_CANCELLED_MESSAGE)); + }, + }, + theme: { color: '#0f172a' }, + prefill: { + email: pe, + name: `${attendee.firstName} ${attendee.lastName}`.trim(), + }, + }); + rzp.on('payment.failed', (response: { error: { description?: string } }) => { + reject(new Error(response.error?.description || 'Payment failed')); + }); + rzp.open(); + }); + onPaid(); + } catch (e) { + const msg = e instanceof Error ? e.message : 'Checkout failed'; + if (msg !== PAYMENT_CANCELLED_MESSAGE) setPayError(msg); + } finally { + setBusy(false); + } + }; + + if (!tickets.length) { + return ( +
+

No tickets are available for this form.

+ +
+ ); + } + + return ( +
+

Step 2 of 2 — Choose your ticket

+
+ {tickets.map((t) => { + const active = t._id === ticketId; + return ( + + ); + })} +
+ + {selected?.requiresBillingState ? ( + + ) : null} + + {payMode === 'both' ? ( +
+ + +
+ ) : null} + + {isManualCheckout && manualUpi ? ( +
+ {manualUpi.upiId ?

UPI ID: {manualUpi.upiId}

: null} + {manualUpi.instructions ?

{manualUpi.instructions}

: null} +
+ {manualUpi.manualQrMode === 'dynamic' ? ( + dynamicQrDataUrl ? ( + UPI QR + ) : ( +

Select a ticket to generate the UPI QR with the exact amount.

+ ) + ) : manualUpi.upiQrImageUrl ? ( + UPI QR + ) : null} +
+ + +
+ ) : null} + + {quoteError ?

{quoteError}

: null} + {quote ? ( +

+ Total: {formatTicketPrice(quote.totalMinor, quote.currency)} + {quote.taxMinor > 0 ? ( + (incl. tax {formatTicketPrice(quote.taxMinor, quote.currency)}) + ) : null} +

+ ) : null} + {payError ?

{payError}

: null} + +
+ + +
+
+ ); +} diff --git a/src/components/microsite/AboutSection.tsx b/src/components/microsite/AboutSection.tsx new file mode 100644 index 0000000..5b963b1 --- /dev/null +++ b/src/components/microsite/AboutSection.tsx @@ -0,0 +1,243 @@ +import { Card, Button, Container } from 'react-bootstrap'; +import { BsFillPersonFill } from 'react-icons/bs'; +import { FaFacebook, FaInstagram, FaLinkedin, FaRegCalendarAlt } from 'react-icons/fa'; +import { FaLocationDot, FaXTwitter } from 'react-icons/fa6'; +import { useState, type ReactNode } from 'react'; +import ReactPlayer from 'react-player'; +import { str } from './sectionUtils'; +import type { MicrositeAboutConfig, MicrositeSectionEnvelope } from '../../types'; +import type { MicrositeSectionContext as Ctx } from './sectionUtils'; + +export type AboutSectionProps = { + section: MicrositeSectionEnvelope<'about', MicrositeAboutConfig>; + ctx: Ctx; +}; + +function socialIcon(name: string): ReactNode | null { + switch (name.trim().toLowerCase()) { + case 'facebook': + return ; + case 'instagram': + return ; + case 'x': + case 'twitter': + return ; + case 'linkedin': + return ; + default: + return null; + } +} + +function AboutMedia({ mediaType, mediaUrl, alt }: { mediaType: string; mediaUrl: string; alt: string }) { + const type = mediaType.trim().toLowerCase() || 'video'; + + if (type === 'image') { + return ( + {alt} + ); + } + + // Default / "video" + return ( + + ); +} + +export function AboutSection({ section, ctx }: AboutSectionProps) { + const c = section.config; + const body = str(c.body) || str(c.lead); + const bullets = Array.isArray(c.bullets) ? (c.bullets as string[]) : []; + const socialLinks = ctx.data.socialLinks ?? []; + + const eventName = ctx.data.event?.name || ''; + const eventDate = ctx.data.event?.date || ''; + const eventLocation = ctx.data.event?.location || ''; + const organizer = ctx.data.event?.organizerName || ctx.data.event?.organizer || 'EventBuzz'; + const mediaUrl = str(c.mediaUrl); + const mediaType = str(c.mediaType, 'video').toLowerCase(); + + // Other sections are available on the shared payload via ctx + const registerConfig = ctx.data.register_links?.config; + const ticketsConfig = ctx.data.tickets?.config; + + const registerTitle = str(registerConfig?.title, 'Register Now'); + const registerText = str(registerConfig?.text, 'Welcome! To join the event, please register below.'); + const registerLabel = str(registerConfig?.primaryLabel, 'Register Now'); + const registerUrl = + ctx.registrationUrl || str(registerConfig?.primaryUrl) || '#'; + + const ticketsTitle = str(ticketsConfig?.title, 'Get Tickets'); + const ticketsText = str(ticketsConfig?.text); + const ticketsLabel = str(ticketsConfig?.primaryLabel, 'Get Tickets'); + const ticketsUrl = + ctx.registrationUrl || str(ticketsConfig?.primaryUrl) || registerUrl; + const ticketTiers = Array.isArray(ticketsConfig?.tiers) ? ticketsConfig.tiers : []; + + const tierKey = (tier: { id?: string; name?: string }, index: number) => + str(tier.id) || str(tier.name) || String(index); + + const [selectedTicketKey, setSelectedTicketKey] = useState(() => + ticketTiers.length ? tierKey(ticketTiers[0]!, 0) : '', + ); + + return ( +
+ + + +
+
+ {mediaUrl ? ( +
+ +
+ ) : null} +

{eventName}

+ + {body ?

{body}

: null} + + {bullets.length > 0 ? ( +
    + {bullets.map((b, i) => ( +
  • {b}
  • + ))} +
+ ) : null} + + {socialLinks.length > 0 ? ( +
+ + Follow us:{' '} + +
+ {socialLinks.map((link, i) => { + const icon = socialIcon(link.name); + if (!icon || !link.handleUrl) return null; + return ( + + {icon} + + ); + })} +
+
+ ) : null} +
+ +
+
+
+
+
+
+ +
+
+ Organized by + {organizer} +
+
+
+ +
+
+
+ +
+
+ Date + {eventDate} +
+
+
+ +
+
+
+ +
+
+ Location + {eventLocation} +
+
+
+
+ + {(registerConfig || ticketsConfig) ?
: null} + + {registerConfig ? ( + + +
{registerTitle}
+
+ + {registerText ?
{registerText}
: null} + +
+
+ ) : null} + + {ticketsConfig ? ( + + +
{ticketsTitle}
+
+ + {ticketsText ?
{ticketsText}
: null} +
+ {ticketTiers.map((tier, i) => { + const key = tierKey(tier, i); + const isSelected = selectedTicketKey === key; + return ( + + ); + })} + +
+
+
+ ) : null} +
+
+
+
+
+
+
+ ); +} diff --git a/src/components/microsite/AgendaSection.tsx b/src/components/microsite/AgendaSection.tsx new file mode 100644 index 0000000..37ee5c4 --- /dev/null +++ b/src/components/microsite/AgendaSection.tsx @@ -0,0 +1,148 @@ +import { useMemo, useState } from 'react'; +import type { AgendaItem, MicrositeAgendaConfig, MicrositeSectionEnvelope } from '../../types'; +import type { MicrositeSectionContext } from './sectionUtils'; +import { str } from './sectionUtils'; +import { IoChevronDownOutline, IoChevronUpOutline } from 'react-icons/io5'; + +export type AgendaSectionProps = { + section: MicrositeSectionEnvelope<'agenda', MicrositeAgendaConfig>; + ctx: MicrositeSectionContext; +}; + +export function AgendaSection({ section, ctx }: AgendaSectionProps) { + const title = str(section.config.title, 'Schedule'); + + const list = useMemo((): AgendaItem[] => { + const items = Array.isArray(section.config.agenda) ? section.config.agenda : []; + if (items.length) return items; + return [ + { + title: 'Session 1', + time: '9:00 AM', + track: 'Main', + date: '', + }, + ]; + }, [section.config.agenda]); + + const dates = useMemo(() => { + const d = list.map((item) => item.date).filter(Boolean) as string[]; + return Array.from(new Set(d)); + }, [list]); + + const [selectedDate, setSelectedDate] = useState(dates.length ? dates[0]! : ''); + const [expandedIndex, setExpandedIndex] = useState(null); + + const filteredList = dates.length ? list.filter((item) => item.date === selectedDate) : list; + + return ( +
+
+

{title}

+ +
+ {dates.map((date) => ( + + ))} +
+ +
+ {filteredList.map((item, index) => { + const isExpanded = expandedIndex === index; + const speakers = Array.isArray(item.speakers) ? item.speakers.filter((s) => str(s.name)) : []; + const hasSpeakers = speakers.length > 0; + + return ( +
+ + + {isExpanded && ( +
+ {item.description ? ( +
+

{item.description}

+
+ ) : null} + + {hasSpeakers ? ( +
+

+ {speakers.length > 1 ? 'Speakers' : 'Speaker'} +

+
+ {speakers.map((speaker, si) => ( +
+ {speaker.photoUrl ? ( + {str(speaker.name)} + ) : null} +
{str(speaker.name)}
+
+ ))} +
+
+ ) : null} +
+ )} +
+ ); + })} +
+ +
+ {ctx.registrationUrl ? ( + + Register + + ) : null} +
+
+ + +
+ ); +} diff --git a/src/components/microsite/BannerSection.tsx b/src/components/microsite/BannerSection.tsx new file mode 100644 index 0000000..9f8ffde --- /dev/null +++ b/src/components/microsite/BannerSection.tsx @@ -0,0 +1,27 @@ +import { Container } from 'react-bootstrap'; +import type { MicrositeBannerConfig, MicrositeSectionEnvelope } from '../../types'; +import type { MicrositeSectionContext as Ctx } from './sectionUtils'; +import { str } from './sectionUtils'; + +export type BannerSectionProps = { + section: MicrositeSectionEnvelope<'banner', MicrositeBannerConfig>; + ctx: Ctx; +}; + +export function BannerSection({ section }: BannerSectionProps) { + const c = section.config; + const bgImage = str(c.backgroundImage); + + return ( +
+ +
+ {bgImage ?
: null} +
+
+
+
+
+ ); +} + diff --git a/src/components/microsite/ContactSection.tsx b/src/components/microsite/ContactSection.tsx new file mode 100644 index 0000000..d37dcab --- /dev/null +++ b/src/components/microsite/ContactSection.tsx @@ -0,0 +1,106 @@ +import { FaEnvelope, FaPhoneAlt } from 'react-icons/fa'; +import { FaLocationDot } from 'react-icons/fa6'; +import { Card, Col, Container, Row } from 'react-bootstrap'; +import type { MicrositeContactConfig, MicrositeSectionEnvelope } from '../../types'; +import type { MicrositeSectionContext as Ctx } from './sectionUtils'; +import { str } from './sectionUtils'; + +export type ContactSectionProps = { + section: MicrositeSectionEnvelope<'contact', MicrositeContactConfig>; + ctx: Ctx; +}; + +export function ContactSection({ section, ctx }: ContactSectionProps) { + const c = section.config; + const title = str(c.title, 'Contact'); + const text = str(c.text); + const email = str(c.email); + const phone = str(c.phone); + const address = str(c.address); + const eventLocation = str(ctx.data.event.location); + const safeEventLocation = encodeURIComponent( + // "1600 Amphitheatre Parkway, Mountain View, CA" + eventLocation + ); + + + return ( +
+ + + + + +
{title}
+
+ + {text ?

{text}

: null} +
+ {email ? ( +
+
+ +
+
+ Email + + {email} + +
+
+ ) : null} + + {phone ? ( +
+
+ +
+
+ Phone + + {phone} + +
+
+ ) : null} + + {address ? ( +
+
+ +
+
+ Address + {address} +
+
+ ) : null} +
+
+
+ + + + + +
Location
+
+ + {eventLocation ?
{eventLocation}
: null} +
+