Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Claim Link - AstraPay</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Erica+One&display=swap" rel="stylesheet"> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| min-height: 100vh; | |
| background: linear-gradient(180deg, | |
| #0f0514 0%, | |
| #1a0a1a 15%, | |
| #2d1a3d 35%, | |
| #4a2a5a 50%, | |
| #3d2a4a 60%, | |
| #2d1a3d 70%, | |
| #1a0a1a 80%, | |
| #0f0514 85%, | |
| #000000 100% | |
| ); | |
| background-attachment: fixed; | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| color: #ffffff; | |
| line-height: 1.6; | |
| } | |
| .main-content { | |
| margin-top: 0; | |
| min-height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 2rem 0; | |
| } | |
| .container { | |
| text-align: center; | |
| max-width: 500px; | |
| padding: 2rem; | |
| } | |
| .title { | |
| font-size: 2.2rem; | |
| font-weight: normal; | |
| font-family: 'Erica One', cursive; | |
| margin-bottom: 1.5rem; | |
| letter-spacing: 0.02em; | |
| } | |
| .claim-card { | |
| background: rgba(255, 255, 255, 0.02); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 16px; | |
| padding: 2.5rem; | |
| margin-bottom: 2rem; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .claim-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | |
| } | |
| .amount { | |
| font-size: 3.2rem; | |
| font-weight: 700; | |
| background: linear-gradient(135deg, #ffffff 0%, #e0e0e0 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: 0.5rem; | |
| letter-spacing: -0.02em; | |
| } | |
| .status { | |
| font-size: 0.95rem; | |
| padding: 0.5rem 1.2rem; | |
| border-radius: 20px; | |
| display: inline-block; | |
| margin-bottom: 2rem; | |
| font-weight: 500; | |
| letter-spacing: 0.01em; | |
| } | |
| .status.pending { | |
| background: rgba(255, 193, 7, 0.15); | |
| color: #ffc107; | |
| border: 1px solid rgba(255, 193, 7, 0.3); | |
| } | |
| .status.verified { | |
| background: rgba(0, 255, 136, 0.15); | |
| color: #00ff88; | |
| border: 1px solid rgba(0, 255, 136, 0.3); | |
| } | |
| .status.claimed { | |
| background: rgba(100, 100, 255, 0.15); | |
| color: #6464ff; | |
| border: 1px solid rgba(100, 100, 255, 0.3); | |
| } | |
| .claim-button { | |
| background: linear-gradient(135deg, #ffffff 0%, #f8f8f8 100%); | |
| color: #000000; | |
| border: none; | |
| padding: 1rem 2.5rem; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| letter-spacing: 0.01em; | |
| box-shadow: 0 4px 20px rgba(255, 255, 255, 0.1); | |
| } | |
| .claim-button:hover:not(:disabled) { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 30px rgba(255, 255, 255, 0.2); | |
| } | |
| .claim-button:disabled { | |
| background: rgba(255, 255, 255, 0.3); | |
| color: rgba(255, 255, 255, 0.5); | |
| cursor: not-allowed; | |
| transform: none; | |
| box-shadow: none; | |
| } | |
| .info-text { | |
| font-size: 0.9rem; | |
| color: rgba(255, 255, 255, 0.7); | |
| margin-bottom: 1rem; | |
| } | |
| .back-link { | |
| color: rgba(255, 255, 255, 0.6); | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| margin-top: 2rem; | |
| display: inline-block; | |
| font-weight: 500; | |
| transition: color 0.3s ease; | |
| } | |
| .back-link:hover { | |
| color: rgba(255, 255, 255, 0.9); | |
| } | |
| .claim-modal { | |
| display: none; | |
| position: fixed; | |
| z-index: 1; | |
| left: 0; | |
| top: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.9); | |
| } | |
| .claim-modal-content { | |
| background-color: #1a1a1a; | |
| margin: 15% auto; | |
| padding: 2rem; | |
| border-radius: 12px; | |
| width: 90%; | |
| max-width: 500px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| text-align: center; | |
| } | |
| .claim-modal-title { | |
| font-family: 'Erica One', cursive; | |
| font-weight: normal; | |
| letter-spacing: 0.02em; | |
| color: #ffffff; | |
| font-size: 1.5rem; | |
| font-weight: bold; | |
| margin-bottom: 1.5rem; | |
| } | |
| .form-input { | |
| width: 100%; | |
| padding: 0.75rem; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 8px; | |
| background-color: rgba(255, 255, 255, 0.05); | |
| color: #ffffff; | |
| font-size: 1rem; | |
| margin: 1rem 0; | |
| box-sizing: border-box; | |
| } | |
| .form-input:focus { | |
| outline: none; | |
| border-color: rgba(255, 255, 255, 0.4); | |
| background-color: rgba(255, 255, 255, 0.1); | |
| } | |
| .claim-modal-buttons { | |
| display: flex; | |
| gap: 1rem; | |
| justify-content: center; | |
| margin-top: 2rem; | |
| } | |
| .btn-secondary { | |
| background-color: transparent; | |
| color: rgba(255, 255, 255, 0.9); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| padding: 0.75rem 1.5rem; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 1rem; | |
| transition: all 0.2s ease; | |
| } | |
| .btn-secondary:hover { | |
| background-color: rgba(255, 255, 255, 0.1); | |
| } | |
| .success-message { | |
| font-size: 1.2rem; | |
| color: #00ff88; | |
| font-weight: 500; | |
| margin-top: 1rem; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="main-content"> | |
| <div class="container"> | |
| <h1 class="title">AstraPay Claim</h1> | |
| <div class="claim-card"> | |
| <div class="amount">{{ claim_amount }} Astra{{ 's' if claim_amount > 1 else '' }}</div> | |
| {% if fee > 0 %} | |
| <div class="info-text" style="margin-bottom: 0.5rem; color: rgba(255, 255, 255, 0.6); font-size: 0.85rem;"> | |
| Fee: {{ fee }} Astra{{ 's' if fee > 1 else '' }} ({{ fee_percent }}%) | |
| </div> | |
| {% endif %} | |
| {% if not claimed %} | |
| <div class="info-text" style="margin-bottom: 1rem;"> | |
| Send {{ send_amount }} Astra{{ 's' if send_amount > 1 else '' }} to astrapay@astranova.org to verify | |
| </div> | |
| {% endif %} | |
| <div class="status {{ 'claimed' if claimed else ('verified' if verified else 'pending') }}"> | |
| {% if claimed %} | |
| Claimed | |
| {% elif verified %} | |
| Verified - Ready to Claim | |
| {% else %} | |
| Pending Verification | |
| {% endif %} | |
| </div> | |
| {% if not claimed %} | |
| {% if verified %} | |
| <button class="claim-button" onclick="openClaimModal()" style="margin-top: 1rem; display: flex; align-items: center; gap: 0.5rem; justify-content: center;"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="20 6 9 17 4 12"></polyline> | |
| </svg> | |
| Claim {{ claim_amount }} Astra{{ 's' if claim_amount > 1 else '' }} | |
| </button> | |
| {% else %} | |
| <div class="info-text" style="margin-top: 1rem;">Waiting for payment verification...</div> | |
| {% endif %} | |
| {% else %} | |
| <div class="success-message" style="margin-top: 1rem;">✓ Claimed{% if claimer_email %} by {{ claimer_email }}{% endif %}</div> | |
| {% endif %} | |
| </div> | |
| <a href="/" class="back-link">← Back to AstraPay</a> | |
| </div> | |
| <div id="claimModal" class="claim-modal"> | |
| <div class="claim-modal-content"> | |
| <div class="claim-modal-title">Claim Astras</div> | |
| <div style="color: #cccccc; margin-bottom: 1.5rem;"> | |
| Enter your AstraNova email to receive {{ claim_amount }} Astra{{ 's' if claim_amount > 1 else '' }} | |
| </div> | |
| <input type="email" id="claimer-email" class="form-input" placeholder="Enter your AstraNova email" required> | |
| <div class="claim-modal-buttons"> | |
| <button class="btn-secondary" onclick="closeClaimModal()" style="display: flex; align-items: center; gap: 0.5rem;"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="18" y1="6" x2="6" y2="18"></line> | |
| <line x1="6" y1="6" x2="18" y2="18"></line> | |
| </svg> | |
| Cancel | |
| </button> | |
| <button class="claim-button" onclick="processClaim()" style="display: flex; align-items: center; gap: 0.5rem;"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="20 6 9 17 4 12"></polyline> | |
| </svg> | |
| Claim | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let verificationInterval; | |
| {% if not verified and not claimed %} | |
| function startVerificationCheck() { | |
| verificationInterval = setInterval(async () => { | |
| try { | |
| const response = await fetch('/check-claim-verification/{{ claim_id }}'); | |
| const data = await response.json(); | |
| if (data.verified) { | |
| stopVerificationCheck(); | |
| location.reload(); | |
| } | |
| } catch (error) { | |
| console.log('Error checking verification:', error); | |
| } | |
| }, 2000); | |
| } | |
| function stopVerificationCheck() { | |
| if (verificationInterval) { | |
| clearInterval(verificationInterval); | |
| verificationInterval = null; | |
| } | |
| } | |
| startVerificationCheck(); | |
| window.onbeforeunload = function() { | |
| stopVerificationCheck(); | |
| }; | |
| {% endif %} | |
| function openClaimModal() { | |
| document.getElementById('claimModal').style.display = 'block'; | |
| } | |
| function closeClaimModal() { | |
| document.getElementById('claimModal').style.display = 'none'; | |
| document.getElementById('claimer-email').value = ''; | |
| } | |
| async function processClaim() { | |
| const email = document.getElementById('claimer-email').value.trim(); | |
| if (!email) { | |
| alert('Please enter your email address'); | |
| return; | |
| } | |
| const claimButton = document.querySelector('#claimModal .claim-button'); | |
| claimButton.disabled = true; | |
| claimButton.textContent = 'Claiming...'; | |
| try { | |
| const response = await fetch('/claim/{{ claim_id }}', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| email: email | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| document.querySelector('.claim-modal-content').innerHTML = ` | |
| <div class="claim-modal-title">Claim Successful!</div> | |
| <div class="success-message" style="margin-top: 1rem;"> | |
| ${data.message} | |
| </div> | |
| `; | |
| setTimeout(() => { | |
| location.reload(); | |
| }, 2000); | |
| } else { | |
| alert('Claim failed: ' + data.error); | |
| claimButton.disabled = false; | |
| claimButton.textContent = 'Claim'; | |
| } | |
| } catch (error) { | |
| alert('Claim failed: ' + error.message); | |
| claimButton.disabled = false; | |
| claimButton.textContent = 'Claim'; | |
| } | |
| } | |
| window.onclick = function(event) { | |
| const modal = document.getElementById('claimModal'); | |
| if (event.target == modal) { | |
| closeClaimModal(); | |
| } | |
| } | |
| </script> | |
| </div> | |
| </body> | |
| </html> | |