diff --git a/package-lock.json b/package-lock.json index cd9f446..88b9621 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "qrcode.react": "^4.2.0", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-icons": "^5.6.0", "react-router-dom": "^7.14.1", "recharts": "^3.8.1" }, @@ -58,6 +59,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -267,31 +269,6 @@ "node": ">=6.9.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1034,6 +1011,7 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1086,6 +1064,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1222,6 +1201,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -1662,6 +1642,7 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2808,6 +2789,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2886,6 +2868,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -2895,6 +2878,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -2902,6 +2886,15 @@ "react": "^19.2.5" } }, + "node_modules/react-icons": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz", + "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "19.2.5", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz", @@ -2914,6 +2907,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -3000,7 +2994,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -3271,6 +3266,7 @@ "integrity": "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -3395,6 +3391,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 804154e..04b79e3 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "qrcode.react": "^4.2.0", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-icons": "^5.6.0", "react-router-dom": "^7.14.1", "recharts": "^3.8.1" }, diff --git a/src/App.css b/src/App.css index ec14b36..8859aa1 100644 --- a/src/App.css +++ b/src/App.css @@ -6,7 +6,7 @@ body { margin: 0; - font-family: 'Glacial Indifference'; + font-family: 'Glacial Indifference', Arial, sans-serif; background: linear-gradient(135deg, #0f1028, #1d1450, #2b145f); color: white; } @@ -53,8 +53,6 @@ body { width: 100%; padding: 16px 24px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); - display: flex; - justify-content: center; position: sticky; top: 0; backdrop-filter: blur(12px); @@ -63,21 +61,40 @@ body { .app-header-inner { width: 100%; - max-width: 980px; + max-width: 1180px; + margin: 0 auto; display: flex; justify-content: space-between; align-items: center; } .app-logo { - font-weight: 800; - font-size: 20px; - letter-spacing: -0.5px; - background: linear-gradient(135deg, #8b5cf6, #ec4899); + font-weight: 900; + font-size: 26px; + letter-spacing: -0.7px; + background: linear-gradient(135deg, #a855f7, #ec4899); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } +.header-actions { + display: flex; + gap: 14px; + align-items: center; +} + +.header-actions .button-primary, +.header-actions .secondary-button { + width: auto; + min-width: 150px; + margin-top: 0; + padding: 14px 22px; + font-size: 15px; + font-family: inherit; + line-height: 1; + white-space: nowrap; +} + .event-code { font-size: 14px; color: #c7c3f4; @@ -215,6 +232,9 @@ body { } + /* LOADING */ + + .loading-screen { min-height: 100vh; display: flex; @@ -572,6 +592,207 @@ body { } +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; +} + +.section-header p { + color: #c7c3f4; +} + +.section-header button, +.poll-form button, +.launch-poll-button { + border: none; + border-radius: 12px; + padding: 10px 14px; + background: linear-gradient(135deg, #8b5cf6, #ec4899); + color: white; + font-weight: 700; + cursor: pointer; +} + +.poll-form { + display: grid; + gap: 12px; + margin-top: 18px; +} + +.poll-form input { + width: 100%; + padding: 14px; + border-radius: 14px; + border: 1px solid rgba(199, 195, 244, 0.3); + background: rgba(255, 255, 255, 0.08); + color: white; +} + +.poll-form input::placeholder { + color: #c7c3f4; +} + +.live-poll-card, +.queued-poll-card { + margin-top: 14px; + padding: 16px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(199, 195, 244, 0.25); +} + +.live-poll-summary { + width: 100%; + display: flex; + align-items: center; + gap: 14px; + background: none; + border: none; + color: white; + text-align: left; + cursor: pointer; +} + +.poll-info { + flex: 1; +} + +.poll-info h3, +.queued-poll-info h3 { + margin: 0 0 6px; +} + +.poll-info p, +.queued-poll-info p { + margin: 0; + color: #c7c3f4; +} + +.active-badge { + background: rgba(34, 197, 94, 0.28); + color: #86efac; + padding: 6px 10px; + border-radius: 999px; + font-size: 13px; + font-weight: 700; +} + +.poll-arrow.open { + transform: rotate(90deg); +} + +.poll-results { + margin-top: 16px; +} + +.poll-result-row { + margin-top: 12px; +} + +.poll-result-label { + display: flex; + justify-content: space-between; + margin-bottom: 6px; +} + +.poll-result-bar { + height: 8px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.1); + overflow: hidden; +} + +.poll-result-fill { + height: 100%; + border-radius: 999px; + background: linear-gradient(135deg, #8b5cf6, #ec4899); +} + +.queued-poll-card { + display: flex; + justify-content: space-between; + align-items: center; +} + +/* FOOTER */ +.app-footer { + margin-top: 4rem; + padding: 2.5rem 1.5rem; + background: rgba(10, 10, 25, 0.95); + border-top: 1px solid rgba(255, 255, 255, 0.08); + text-align: center; +} + +.footer-brand h3 { + color: white; + margin-bottom: 0.4rem; +} + +.footer-brand p { + color: #b6b6d6; + font-size: 0.95rem; + margin-bottom: 1.8rem; +} + +.footer-socials { + display: flex; + justify-content: center; + gap: 1rem; + margin-bottom: 1.5rem; +} + +.footer-socials a { + width: 48px; + height: 48px; + border: 2px solid #ff4fa3; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + color: #ff4fa3; + font-size: 1.4rem; + transition: 0.2s ease; +} + +.footer-socials a:hover { + background: #ff4fa3; + color: white; + transform: translateY(-2px); +} + +.footer-links { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 1rem; +} + +.footer-links a { + color: #cfcfe7; + text-decoration: none; + font-size: 0.95rem; +} + +.footer-links a:hover { + color: #ff4fa3; +} + + +.attendee-poll-card h2 { + margin-top: 0; + margin-bottom: 18px; +} + +.attendee-poll-form { + display: grid; + gap: 12px; +} + +.option-button.selected { + border-color: #ec4899; + background: rgba(236, 72, 153, 0.22); .error-band { position: fixed; diff --git a/src/App.jsx b/src/App.jsx index 0fa3292..b05a891 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -9,6 +9,7 @@ import LoginPage from "./pages/LoginPage"; import CreateEventPage from "./pages/CreateEventPage"; import "./App.css"; import EventDetailsPage from "./pages/EventDetailsPage"; +import FacilitatorEventDetailsPage from "./pages/FacilitatorEventDetailsPage"; import ResetPasswordPage from "./pages/ResetPasswordPage"; import SignupPage from "./pages/SignupPage"; @@ -57,6 +58,8 @@ function App() { /> {/* EVENT DETAILS */} + } /> + } /> } diff --git a/src/components/polls/AttendeePollCard.jsx b/src/components/polls/AttendeePollCard.jsx new file mode 100644 index 0000000..96f2403 --- /dev/null +++ b/src/components/polls/AttendeePollCard.jsx @@ -0,0 +1,55 @@ +import { useState } from "react"; + +function AttendeePollCard({ + question = "How are you finding the session so far?", + options = ["Great", "Good", "Unsure"], +}) { + const [selectedOption, setSelectedOption] = useState(""); + const [hasSubmitted, setHasSubmitted] = useState(false); + + function handleSubmit(e) { + e.preventDefault(); + + if (!selectedOption) { + alert("Please select an answer"); + return; + } + + setHasSubmitted(true); + } + + return ( +
+

LIVE POLL

+ +

{question}

+ + {!hasSubmitted ? ( +
+ {options.map((option) => ( + + ))} + + +
+ ) : ( +

+ Thanks — your response has been submitted. +

+ )} +
+ ); +} + +export default AttendeePollCard; \ No newline at end of file diff --git a/src/components/shared/Footer.jsx b/src/components/shared/Footer.jsx index 8f92b90..c1b467f 100644 --- a/src/components/shared/Footer.jsx +++ b/src/components/shared/Footer.jsx @@ -1,7 +1,32 @@ +import { FaInstagram, FaLinkedin } from "react-icons/fa"; + function Footer() { return ( diff --git a/src/pages/AttendeeSessionPage.jsx b/src/pages/AttendeeSessionPage.jsx index 5e38ea8..eed8c2f 100644 --- a/src/pages/AttendeeSessionPage.jsx +++ b/src/pages/AttendeeSessionPage.jsx @@ -3,6 +3,10 @@ import { useParams } from "react-router-dom"; import ErrorAlert from "../components/shared/ErrorAlert"; import { getEventByCode } from "../services/events"; +// Interactive attendee session components +import LivePollCard from "../components/polls/AttendeePollCard"; +import QuestionForm from "../components/questions/QuestionForm"; +import EmailCaptureForm from "../components/email/EmailCaptureForm"; // Interactive attendee event components import QuestionForm from "../components/questions/QuestionForm"; import Footer from "../components/shared/Footer"; @@ -125,7 +129,7 @@ function AttendeeEventPage() { {/* Email capture for workshop slides */} - + {/* Global application footer */} diff --git a/src/pages/EventDetailsPage.jsx b/src/pages/EventDetailsPage.jsx index 8ef471b..d7ad7d0 100644 --- a/src/pages/EventDetailsPage.jsx +++ b/src/pages/EventDetailsPage.jsx @@ -140,6 +140,7 @@ function EventDetailsPage() { +