| 1 | <!DOCTYPE html>
|
|---|
| 2 | <html lang="en">
|
|---|
| 3 | <head>
|
|---|
| 4 | <meta charset="UTF-8"/>
|
|---|
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|---|
| 6 | <title>Wedding RSVP</title>
|
|---|
| 7 | <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet"/>
|
|---|
| 8 | <style>
|
|---|
| 9 | :root {
|
|---|
| 10 | --cream:#fdf8f3; --cream-2:#f9f1e8;
|
|---|
| 11 | --blush-light:#fce8e8; --blush-mid:#e8949c;
|
|---|
| 12 | --rose:#d4606a; --rose-deep:#b84550;
|
|---|
| 13 | --gold:#c9a84c; --gold-dark:#9e7a28;
|
|---|
| 14 | --text-dark:#2c1f1f; --text-mid:#6b4f4f; --text-light:#a08080;
|
|---|
| 15 | --border:#ecddd0; --border-light:#f4ebe2;
|
|---|
| 16 | --green:#3a9e6b; --green-light:#e4f5ec;
|
|---|
| 17 | --red:#c94545; --red-light:#fdeaea;
|
|---|
| 18 | --shadow-lg:0 16px 40px rgba(44,31,31,.14),0 4px 12px rgba(44,31,31,.08);
|
|---|
| 19 | --radius-md:10px; --radius-lg:16px; --radius-xl:22px;
|
|---|
| 20 | --transition:200ms cubic-bezier(.4,0,.2,1);
|
|---|
| 21 | }
|
|---|
| 22 | *,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
|---|
| 23 | html,body{
|
|---|
| 24 | height:100%; font-family:'DM Sans',sans-serif;
|
|---|
| 25 | background:linear-gradient(135deg,#fdf8f3,#f9f1e8,#fce8e8);
|
|---|
| 26 | color:var(--text-dark); -webkit-font-smoothing:antialiased;
|
|---|
| 27 | display:flex; align-items:center; justify-content:center;
|
|---|
| 28 | min-height:100vh; padding:20px;
|
|---|
| 29 | }
|
|---|
| 30 |
|
|---|
| 31 | .rsvp-wrapper { width:100%; max-width:520px; }
|
|---|
| 32 |
|
|---|
| 33 | /* logo */
|
|---|
| 34 | .rsvp-logo {
|
|---|
| 35 | text-align:center; margin-bottom:24px;
|
|---|
| 36 | }
|
|---|
| 37 | .rsvp-logo .mark {
|
|---|
| 38 | width:48px; height:48px; margin:0 auto 10px;
|
|---|
| 39 | background:linear-gradient(135deg,var(--rose),var(--blush-mid));
|
|---|
| 40 | border-radius:12px;
|
|---|
| 41 | display:flex; align-items:center; justify-content:center;
|
|---|
| 42 | box-shadow:0 4px 16px rgba(212,96,106,.35);
|
|---|
| 43 | }
|
|---|
| 44 | .rsvp-logo .mark svg { width:24px; height:24px; }
|
|---|
| 45 | .rsvp-logo h1 {
|
|---|
| 46 | font-family:'Cormorant Garamond',serif;
|
|---|
| 47 | font-size:24px; font-weight:700;
|
|---|
| 48 | }
|
|---|
| 49 |
|
|---|
| 50 | /* card */
|
|---|
| 51 | .rsvp-card {
|
|---|
| 52 | background:#fff; border-radius:var(--radius-xl);
|
|---|
| 53 | border:1px solid var(--border-light);
|
|---|
| 54 | box-shadow:var(--shadow-lg); overflow:hidden;
|
|---|
| 55 | }
|
|---|
| 56 |
|
|---|
| 57 | /* header band */
|
|---|
| 58 | .rsvp-header {
|
|---|
| 59 | background:linear-gradient(135deg,var(--rose),var(--blush-mid));
|
|---|
| 60 | padding:28px 32px; text-align:center; color:#fff;
|
|---|
| 61 | }
|
|---|
| 62 | .rsvp-header .emoji { font-size:40px; margin-bottom:8px; }
|
|---|
| 63 | .rsvp-header h2 {
|
|---|
| 64 | font-family:'Cormorant Garamond',serif;
|
|---|
| 65 | font-size:26px; font-weight:700; margin-bottom:4px;
|
|---|
| 66 | }
|
|---|
| 67 | .rsvp-header p { font-size:14px; opacity:.9; }
|
|---|
| 68 |
|
|---|
| 69 | /* body */
|
|---|
| 70 | .rsvp-body { padding:32px; }
|
|---|
| 71 |
|
|---|
| 72 | .gold-rule {
|
|---|
| 73 | width:36px; height:2px;
|
|---|
| 74 | background:linear-gradient(90deg,var(--gold),transparent);
|
|---|
| 75 | border-radius:2px; margin:0 0 20px;
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | /* event info box */
|
|---|
| 79 | .event-box {
|
|---|
| 80 | background:var(--cream); border:1px solid var(--border-light);
|
|---|
| 81 | border-radius:var(--radius-lg); padding:18px 20px;
|
|---|
| 82 | margin-bottom:24px;
|
|---|
| 83 | }
|
|---|
| 84 | .event-box .ev-label {
|
|---|
| 85 | font-size:10px; font-weight:700; text-transform:uppercase;
|
|---|
| 86 | letter-spacing:.1em; color:var(--text-light); margin-bottom:6px;
|
|---|
| 87 | }
|
|---|
| 88 | .event-box .ev-type {
|
|---|
| 89 | font-family:'Cormorant Garamond',serif;
|
|---|
| 90 | font-size:22px; font-weight:700; color:var(--text-dark);
|
|---|
| 91 | margin-bottom:6px;
|
|---|
| 92 | }
|
|---|
| 93 | .event-box .ev-meta {
|
|---|
| 94 | font-size:13px; color:var(--text-mid);
|
|---|
| 95 | display:flex; gap:16px; flex-wrap:wrap;
|
|---|
| 96 | }
|
|---|
| 97 | .event-box .ev-meta span { display:flex; align-items:center; gap:5px; }
|
|---|
| 98 |
|
|---|
| 99 | /* guest greeting */
|
|---|
| 100 | .guest-greeting {
|
|---|
| 101 | font-size:15px; color:var(--text-mid);
|
|---|
| 102 | margin-bottom:24px; line-height:1.6;
|
|---|
| 103 | }
|
|---|
| 104 | .guest-greeting strong { color:var(--text-dark); }
|
|---|
| 105 |
|
|---|
| 106 | /* RSVP buttons */
|
|---|
| 107 | .rsvp-actions {
|
|---|
| 108 | display:grid; grid-template-columns:1fr 1fr; gap:14px;
|
|---|
| 109 | margin-bottom:20px;
|
|---|
| 110 | }
|
|---|
| 111 | .rsvp-btn {
|
|---|
| 112 | padding:16px 12px; border-radius:var(--radius-lg);
|
|---|
| 113 | border:2px solid transparent; cursor:pointer;
|
|---|
| 114 | font-family:'DM Sans',sans-serif; font-size:15px; font-weight:600;
|
|---|
| 115 | display:flex; flex-direction:column; align-items:center; gap:6px;
|
|---|
| 116 | transition:all var(--transition);
|
|---|
| 117 | }
|
|---|
| 118 | .rsvp-btn .btn-icon { font-size:28px; }
|
|---|
| 119 | .rsvp-btn-accept {
|
|---|
| 120 | background:var(--green-light); color:var(--green);
|
|---|
| 121 | border-color:#b2e0ca;
|
|---|
| 122 | }
|
|---|
| 123 | .rsvp-btn-accept:hover {
|
|---|
| 124 | background:var(--green); color:#fff; border-color:var(--green);
|
|---|
| 125 | transform:translateY(-2px); box-shadow:0 6px 20px rgba(58,158,107,.3);
|
|---|
| 126 | }
|
|---|
| 127 | .rsvp-btn-decline {
|
|---|
| 128 | background:var(--red-light); color:var(--red);
|
|---|
| 129 | border-color:#f5c2c2;
|
|---|
| 130 | }
|
|---|
| 131 | .rsvp-btn-decline:hover {
|
|---|
| 132 | background:var(--red); color:#fff; border-color:var(--red);
|
|---|
| 133 | transform:translateY(-2px); box-shadow:0 6px 20px rgba(201,69,69,.3);
|
|---|
| 134 | }
|
|---|
| 135 | .rsvp-btn:disabled {
|
|---|
| 136 | opacity:.5; cursor:not-allowed; transform:none !important;
|
|---|
| 137 | }
|
|---|
| 138 | .rsvp-btn.selected-accept {
|
|---|
| 139 | background:var(--green); color:#fff; border-color:var(--green);
|
|---|
| 140 | }
|
|---|
| 141 | .rsvp-btn.selected-decline {
|
|---|
| 142 | background:var(--red); color:#fff; border-color:var(--red);
|
|---|
| 143 | }
|
|---|
| 144 |
|
|---|
| 145 | /* result state */
|
|---|
| 146 | .rsvp-result {
|
|---|
| 147 | display:none; text-align:center; padding:24px;
|
|---|
| 148 | }
|
|---|
| 149 | .rsvp-result.show { display:block; }
|
|---|
| 150 | .rsvp-result .result-emoji { font-size:56px; margin-bottom:12px; }
|
|---|
| 151 | .rsvp-result h3 {
|
|---|
| 152 | font-family:'Cormorant Garamond',serif;
|
|---|
| 153 | font-size:24px; font-weight:700; margin-bottom:8px;
|
|---|
| 154 | }
|
|---|
| 155 | .rsvp-result p { font-size:14px; color:var(--text-mid); line-height:1.6; }
|
|---|
| 156 | .result-accepted h3 { color:var(--green); }
|
|---|
| 157 | .result-declined h3 { color:var(--red); }
|
|---|
| 158 |
|
|---|
| 159 | /* loading state */
|
|---|
| 160 | .rsvp-loading {
|
|---|
| 161 | display:none; text-align:center; padding:32px;
|
|---|
| 162 | }
|
|---|
| 163 | .rsvp-loading.show { display:block; }
|
|---|
| 164 | .spinner {
|
|---|
| 165 | width:36px; height:36px; margin:0 auto 12px;
|
|---|
| 166 | border:3px solid var(--border);
|
|---|
| 167 | border-top-color:var(--rose); border-radius:50%;
|
|---|
| 168 | animation:spin .7s linear infinite;
|
|---|
| 169 | }
|
|---|
| 170 | @keyframes spin { to { transform:rotate(360deg); } }
|
|---|
| 171 |
|
|---|
| 172 | /* error state */
|
|---|
| 173 | .rsvp-error {
|
|---|
| 174 | display:none; text-align:center; padding:24px;
|
|---|
| 175 | }
|
|---|
| 176 | .rsvp-error.show { display:block; }
|
|---|
| 177 | .rsvp-error .error-emoji { font-size:48px; margin-bottom:12px; }
|
|---|
| 178 | .rsvp-error h3 {
|
|---|
| 179 | font-family:'Cormorant Garamond',serif;
|
|---|
| 180 | font-size:22px; color:var(--red); margin-bottom:8px;
|
|---|
| 181 | }
|
|---|
| 182 | .rsvp-error p { font-size:13px; color:var(--text-mid); }
|
|---|
| 183 |
|
|---|
| 184 | /* footer */
|
|---|
| 185 | .rsvp-footer {
|
|---|
| 186 | background:var(--cream); border-top:1px solid var(--border-light);
|
|---|
| 187 | padding:14px 32px; text-align:center;
|
|---|
| 188 | font-size:12px; color:var(--text-light);
|
|---|
| 189 | }
|
|---|
| 190 |
|
|---|
| 191 | /* already responded badge */
|
|---|
| 192 | .already-badge {
|
|---|
| 193 | display:inline-flex; align-items:center; gap:6px;
|
|---|
| 194 | padding:6px 14px; border-radius:999px;
|
|---|
| 195 | font-size:12px; font-weight:600; margin-bottom:16px;
|
|---|
| 196 | }
|
|---|
| 197 | .already-accepted { background:var(--green-light); color:var(--green); }
|
|---|
| 198 | .already-declined { background:var(--red-light); color:var(--red); }
|
|---|
| 199 | .already-pending { background:#fff4e0; color:#c87f2a; }
|
|---|
| 200 | </style>
|
|---|
| 201 | </head>
|
|---|
| 202 | <body>
|
|---|
| 203 |
|
|---|
| 204 | <div class="rsvp-wrapper">
|
|---|
| 205 |
|
|---|
| 206 | <!-- Logo -->
|
|---|
| 207 | <div class="rsvp-logo">
|
|---|
| 208 | <div class="mark">
|
|---|
| 209 | <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|---|
| 210 | <path d="M12 21.593c-5.63-5.539-11-10.297-11-14.402C1 3.402 4.248 1 7.5 1 9.849 1 11.271 2.007 12 3.011 12.729 2.007 14.151 1 16.5 1 19.752 1 23 3.402 23 7.191c0 4.105-5.37 8.863-11 14.402z"/>
|
|---|
| 211 | </svg>
|
|---|
| 212 | </div>
|
|---|
| 213 | <h1>Wedding Planner</h1>
|
|---|
| 214 | </div>
|
|---|
| 215 |
|
|---|
| 216 | <!-- Card -->
|
|---|
| 217 | <div class="rsvp-card">
|
|---|
| 218 |
|
|---|
| 219 | <!-- Header -->
|
|---|
| 220 | <div class="rsvp-header">
|
|---|
| 221 | <div class="emoji">๐</div>
|
|---|
| 222 | <h2>Wedding Invitation</h2>
|
|---|
| 223 | <p>You have been invited โ please let us know if you can attend</p>
|
|---|
| 224 | </div>
|
|---|
| 225 |
|
|---|
| 226 | <!-- Loading -->
|
|---|
| 227 | <div class="rsvp-loading" id="loadingState">
|
|---|
| 228 | <div class="rsvp-body">
|
|---|
| 229 | <div class="spinner"></div>
|
|---|
| 230 | <p style="color:var(--text-light);font-size:14px;">Loading your invitation...</p>
|
|---|
| 231 | </div>
|
|---|
| 232 | </div>
|
|---|
| 233 |
|
|---|
| 234 | <!-- Error -->
|
|---|
| 235 | <div class="rsvp-error" id="errorState">
|
|---|
| 236 | <div class="rsvp-body">
|
|---|
| 237 | <div class="error-emoji">โ ๏ธ</div>
|
|---|
| 238 | <h3>Invalid Link</h3>
|
|---|
| 239 | <p id="errorMsg">This RSVP link is invalid or has expired. Please contact the wedding organizer.</p>
|
|---|
| 240 | </div>
|
|---|
| 241 | </div>
|
|---|
| 242 |
|
|---|
| 243 | <!-- Main RSVP form -->
|
|---|
| 244 | <div class="rsvp-body" id="rsvpForm" style="display:none;">
|
|---|
| 245 | <div class="gold-rule"></div>
|
|---|
| 246 |
|
|---|
| 247 | <!-- existing response badge -->
|
|---|
| 248 | <div id="alreadyBadge" style="display:none;"></div>
|
|---|
| 249 |
|
|---|
| 250 | <!-- guest greeting -->
|
|---|
| 251 | <div class="guest-greeting" id="guestGreeting"></div>
|
|---|
| 252 |
|
|---|
| 253 | <!-- event info -->
|
|---|
| 254 | <div class="event-box" id="eventBox"></div>
|
|---|
| 255 |
|
|---|
| 256 | <!-- action buttons -->
|
|---|
| 257 | <div class="rsvp-actions" id="rsvpActions">
|
|---|
| 258 | <button class="rsvp-btn rsvp-btn-accept" id="btnAccept" onclick="submitRsvp('accepted')">
|
|---|
| 259 | <span class="btn-icon">โ
</span>
|
|---|
| 260 | <span>I'll be there!</span>
|
|---|
| 261 | </button>
|
|---|
| 262 | <button class="rsvp-btn rsvp-btn-decline" id="btnDecline" onclick="submitRsvp('declined')">
|
|---|
| 263 | <span class="btn-icon">โ</span>
|
|---|
| 264 | <span>Can't make it</span>
|
|---|
| 265 | </button>
|
|---|
| 266 | </div>
|
|---|
| 267 |
|
|---|
| 268 | <p style="font-size:12px;color:var(--text-light);text-align:center;">
|
|---|
| 269 | You can change your response at any time by using the same link from your email.
|
|---|
| 270 | </p>
|
|---|
| 271 | </div>
|
|---|
| 272 |
|
|---|
| 273 | <!-- Result after submitting -->
|
|---|
| 274 | <div class="rsvp-result" id="rsvpResult">
|
|---|
| 275 | <div class="rsvp-body">
|
|---|
| 276 | <div class="result-emoji" id="resultEmoji"></div>
|
|---|
| 277 | <h3 id="resultTitle"></h3>
|
|---|
| 278 | <p id="resultMsg"></p>
|
|---|
| 279 | </div>
|
|---|
| 280 | </div>
|
|---|
| 281 |
|
|---|
| 282 | <div class="rsvp-footer">
|
|---|
| 283 | Wedding Planner ยท Sent with love ๐
|
|---|
| 284 | </div>
|
|---|
| 285 | </div>
|
|---|
| 286 |
|
|---|
| 287 | </div>
|
|---|
| 288 |
|
|---|
| 289 | <script>
|
|---|
| 290 | const API = 'http://localhost:3000';
|
|---|
| 291 |
|
|---|
| 292 | // โโ Parse URL params โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|---|
| 293 | const params = new URLSearchParams(window.location.search);
|
|---|
| 294 | const guestId = params.get('guest');
|
|---|
| 295 | const eventId = params.get('event');
|
|---|
| 296 | const token = params.get('token');
|
|---|
| 297 | const action = params.get('action'); // pre-filled from email link
|
|---|
| 298 |
|
|---|
| 299 | // โโ State โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|---|
| 300 | let guestData = null;
|
|---|
| 301 | let existingRsvp = null;
|
|---|
| 302 |
|
|---|
| 303 | // โโ On load โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|---|
| 304 | window.addEventListener('DOMContentLoaded', async () => {
|
|---|
| 305 | if (!guestId || !eventId || !token) {
|
|---|
| 306 | showError('Missing required parameters. Please use the link from your email.');
|
|---|
| 307 | return;
|
|---|
| 308 | }
|
|---|
| 309 | showLoading(true);
|
|---|
| 310 | await loadInvitation();
|
|---|
| 311 | });
|
|---|
| 312 |
|
|---|
| 313 | async function loadInvitation() {
|
|---|
| 314 | try {
|
|---|
| 315 | // Check for existing RSVP
|
|---|
| 316 | const rsvpRes = await fetch(`${API}/api/rsvp?guest_id=${guestId}&event_id=${eventId}`);
|
|---|
| 317 | if (rsvpRes.ok) {
|
|---|
| 318 | existingRsvp = await rsvpRes.json();
|
|---|
| 319 | }
|
|---|
| 320 |
|
|---|
| 321 | // We'll get guest + event info from the RSVP endpoint by doing a dry run
|
|---|
| 322 | // For now build the form and auto-submit if action is in URL
|
|---|
| 323 | showLoading(false);
|
|---|
| 324 | showForm();
|
|---|
| 325 |
|
|---|
| 326 | // Auto-submit if action came from email link
|
|---|
| 327 | if (action === 'accepted' || action === 'declined') {
|
|---|
| 328 | await submitRsvp(action, true);
|
|---|
| 329 | }
|
|---|
| 330 |
|
|---|
| 331 | } catch (err) {
|
|---|
| 332 | showError('Could not load invitation. Please check your connection.');
|
|---|
| 333 | }
|
|---|
| 334 | }
|
|---|
| 335 |
|
|---|
| 336 | // โโ Submit RSVP โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|---|
| 337 | async function submitRsvp(status, auto = false) {
|
|---|
| 338 | disableButtons();
|
|---|
| 339 | if (!auto) showLoading(true);
|
|---|
| 340 |
|
|---|
| 341 | try {
|
|---|
| 342 | const res = await fetch(`${API}/api/rsvp`, {
|
|---|
| 343 | method: 'POST',
|
|---|
| 344 | headers: { 'Content-Type': 'application/json' },
|
|---|
| 345 | body: JSON.stringify({
|
|---|
| 346 | guest_id: guestId,
|
|---|
| 347 | event_id: eventId,
|
|---|
| 348 | token,
|
|---|
| 349 | status
|
|---|
| 350 | })
|
|---|
| 351 | });
|
|---|
| 352 |
|
|---|
| 353 | const data = await res.json();
|
|---|
| 354 | showLoading(false);
|
|---|
| 355 |
|
|---|
| 356 | if (!res.ok) {
|
|---|
| 357 | showError(data.error || 'Could not submit your response.');
|
|---|
| 358 | return;
|
|---|
| 359 | }
|
|---|
| 360 |
|
|---|
| 361 | // Show result
|
|---|
| 362 | const guest = data.guest;
|
|---|
| 363 | const accepted = status === 'accepted';
|
|---|
| 364 | showResult({
|
|---|
| 365 | emoji: accepted ? '๐' : '๐ข',
|
|---|
| 366 | title: accepted ? 'See you there!' : 'We\'ll miss you!',
|
|---|
| 367 | msg: accepted
|
|---|
| 368 | ? `Thank you, ${guest ? guest.first_name : 'Guest'}! Your attendance for <strong>${guest ? guest.event_type : 'the event'}</strong> on <strong>${guest ? guest.date : ''}</strong> has been confirmed. We can't wait to celebrate with you! ๐ฅ`
|
|---|
| 369 | : `Thank you for letting us know, ${guest ? guest.first_name : 'Guest'}. We're sorry you won't be able to make it to <strong>${guest ? guest.event_type : 'the event'}</strong>, but we appreciate your response.`,
|
|---|
| 370 | type: accepted ? 'accepted' : 'declined'
|
|---|
| 371 | });
|
|---|
| 372 |
|
|---|
| 373 | } catch (err) {
|
|---|
| 374 | showLoading(false);
|
|---|
| 375 | showError('Connection error. Please try again.');
|
|---|
| 376 | }
|
|---|
| 377 | }
|
|---|
| 378 |
|
|---|
| 379 | // โโ UI Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|---|
| 380 | function showLoading(show) {
|
|---|
| 381 | document.getElementById('loadingState').classList.toggle('show', show);
|
|---|
| 382 | document.getElementById('rsvpForm').style.display = show ? 'none' : 'block';
|
|---|
| 383 | }
|
|---|
| 384 |
|
|---|
| 385 | function showForm() {
|
|---|
| 386 | document.getElementById('loadingState').classList.remove('show');
|
|---|
| 387 | document.getElementById('rsvpForm').style.display = 'block';
|
|---|
| 388 |
|
|---|
| 389 | // Show greeting
|
|---|
| 390 | document.getElementById('guestGreeting').innerHTML =
|
|---|
| 391 | `Dear <strong>Guest</strong>,<br>You have been invited to a special event. Please confirm your attendance below.`;
|
|---|
| 392 |
|
|---|
| 393 | // Show event info placeholder (will be filled after submit response)
|
|---|
| 394 | document.getElementById('eventBox').innerHTML = `
|
|---|
| 395 | <div class="ev-label">Event Details</div>
|
|---|
| 396 | <div class="ev-type">Wedding Event</div>
|
|---|
| 397 | <div class="ev-meta">
|
|---|
| 398 | <span>๐
Check your email for date details</span>
|
|---|
| 399 | </div>`;
|
|---|
| 400 |
|
|---|
| 401 | // Show existing response badge
|
|---|
| 402 | if (existingRsvp) {
|
|---|
| 403 | const badge = document.getElementById('alreadyBadge');
|
|---|
| 404 | badge.style.display = 'block';
|
|---|
| 405 | const cls = existingRsvp.status === 'accepted' ? 'already-accepted'
|
|---|
| 406 | : existingRsvp.status === 'declined' ? 'already-declined'
|
|---|
| 407 | : 'already-pending';
|
|---|
| 408 | const icon = existingRsvp.status === 'accepted' ? 'โ
'
|
|---|
| 409 | : existingRsvp.status === 'declined' ? 'โ' : 'โณ';
|
|---|
| 410 | badge.innerHTML = `
|
|---|
| 411 | <span class="already-badge ${cls}">
|
|---|
| 412 | ${icon} You previously responded: <strong>${existingRsvp.status}</strong>
|
|---|
| 413 | โ you can change your response below
|
|---|
| 414 | </span>`;
|
|---|
| 415 | }
|
|---|
| 416 | }
|
|---|
| 417 |
|
|---|
| 418 | function showError(msg) {
|
|---|
| 419 | document.getElementById('loadingState').classList.remove('show');
|
|---|
| 420 | document.getElementById('rsvpForm').style.display = 'none';
|
|---|
| 421 | document.getElementById('rsvpResult').classList.remove('show');
|
|---|
| 422 | const err = document.getElementById('errorState');
|
|---|
| 423 | err.classList.add('show');
|
|---|
| 424 | document.getElementById('errorMsg').textContent = msg;
|
|---|
| 425 | }
|
|---|
| 426 |
|
|---|
| 427 | function showResult({ emoji, title, msg, type }) {
|
|---|
| 428 | document.getElementById('rsvpForm').style.display = 'none';
|
|---|
| 429 | document.getElementById('loadingState').classList.remove('show');
|
|---|
| 430 | const result = document.getElementById('rsvpResult');
|
|---|
| 431 | result.className = `rsvp-result show result-${type}`;
|
|---|
| 432 | document.getElementById('resultEmoji').textContent = emoji;
|
|---|
| 433 | document.getElementById('resultTitle').textContent = title;
|
|---|
| 434 | document.getElementById('resultMsg').innerHTML = msg;
|
|---|
| 435 | }
|
|---|
| 436 |
|
|---|
| 437 | function disableButtons() {
|
|---|
| 438 | document.getElementById('btnAccept').disabled = true;
|
|---|
| 439 | document.getElementById('btnDecline').disabled = true;
|
|---|
| 440 | }
|
|---|
| 441 | </script>
|
|---|
| 442 | </body>
|
|---|
| 443 | </html>
|
|---|