GamerC0der commited on
Commit
53ee66f
·
verified ·
1 Parent(s): 9a2f9f8

Update templates/home.html

Browse files
Files changed (1) hide show
  1. templates/home.html +642 -94
templates/home.html CHANGED
@@ -12,38 +12,9 @@
12
  background-color: #000000;
13
  font-family: Arial, sans-serif;
14
  }
15
- .top-bar {
16
- position: fixed;
17
- top: 0;
18
- left: 0;
19
- right: 0;
20
- background-color: rgba(26, 26, 26, 0.95);
21
- backdrop-filter: blur(10px);
22
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
23
- padding: 1rem 2rem;
24
- display: flex;
25
- justify-content: center;
26
- gap: 2rem;
27
- z-index: 1000;
28
- }
29
- .top-bar a {
30
- color: #ffffff;
31
- text-decoration: none;
32
- font-size: 1.2rem;
33
- font-weight: 600;
34
- transition: color 0.3s ease;
35
- }
36
- .top-bar a:hover {
37
- color: rgba(255, 255, 255, 0.7);
38
- }
39
- .top-bar a.active {
40
- color: #ffffff;
41
- border-bottom: 2px solid #ffffff;
42
- padding-bottom: 0.25rem;
43
- }
44
  .main-content {
45
- margin-top: 80px;
46
- height: calc(100vh - 80px);
47
  display: flex;
48
  justify-content: center;
49
  align-items: center;
@@ -79,82 +50,230 @@
79
  .modal {
80
  display: none;
81
  position: fixed;
82
- z-index: 1;
83
  left: 0;
84
  top: 0;
85
  width: 100%;
86
  height: 100%;
87
  background-color: rgba(0, 0, 0, 0.8);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
  .modal-content {
90
  background-color: #1a1a1a;
91
- margin: 15% auto;
92
- padding: 2rem;
93
- border-radius: 8px;
94
  width: 90%;
95
  max-width: 500px;
96
- border: 1px solid #333;
 
 
 
 
 
 
97
  }
98
  .modal-header {
99
  color: #ffffff;
 
100
  margin-bottom: 1.5rem;
101
- font-size: 1.5rem;
102
- font-weight: bold;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  }
104
  .form-group {
105
- margin-bottom: 1.5rem;
106
  }
107
  .form-label {
108
  display: block;
109
- color: #ffffff;
110
  margin-bottom: 0.5rem;
111
- font-weight: bold;
 
112
  }
113
  .form-input {
114
  width: 100%;
115
- padding: 0.75rem;
116
- border: 1px solid #555;
117
- border-radius: 4px;
118
- background-color: #2a2a2a;
119
  color: #ffffff;
120
- font-size: 1rem;
 
 
121
  }
122
  .form-input:focus {
123
  outline: none;
124
- border-color: #ffffff;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  }
126
  .form-textarea {
127
  width: 100%;
128
- padding: 0.75rem;
129
- border: 1px solid #555;
130
- border-radius: 4px;
131
- background-color: #2a2a2a;
132
  color: #ffffff;
133
- font-size: 1rem;
134
  min-height: 100px;
135
  resize: vertical;
 
 
 
136
  }
137
  .form-textarea:focus {
138
  outline: none;
139
- border-color: #ffffff;
 
 
 
 
140
  }
141
  .modal-buttons {
142
  display: flex;
143
- gap: 1rem;
144
  justify-content: flex-end;
145
- margin-top: 2rem;
 
 
146
  }
147
  .btn-secondary {
148
- background-color: #555;
149
- color: #ffffff;
150
- border: none;
151
- padding: 0.75rem 1.5rem;
152
- border-radius: 4px;
153
  cursor: pointer;
154
- font-size: 1rem;
 
 
155
  }
156
  .btn-secondary:hover {
157
- background-color: #666;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  }
159
  .banner {
160
  background: rgba(255, 193, 7, 0.15);
@@ -170,55 +289,307 @@
170
  margin-left: auto;
171
  margin-right: auto;
172
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  </style>
174
  </head>
175
  <body>
176
- <div class="top-bar">
177
- <a href="/" class="active">AstraPay</a>
178
- <a href="/bank">AstraBank</a>
179
- </div>
180
  <div class="main-content">
181
  <div class="container">
182
  <h1 class="title">AstraPay</h1>
183
- <div class="banner">
184
- Payments May Reset Daily. Ensure your link works.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  </div>
186
- <button class="button" onclick="openModal()">Create Payment Link</button>
187
  </div>
188
 
189
  <div id="paymentModal" class="modal">
190
  <div class="modal-content">
 
 
 
 
 
191
  <div class="modal-header">Create Payment Link</div>
 
 
 
 
 
192
 
193
- <div class="form-group">
194
- <label class="form-label" for="astras">Number of Astras</label>
195
- <input type="number" id="astras" class="form-input" min="1" placeholder="Enter number of astras" required>
196
- </div>
197
 
198
- <div class="form-group">
199
- <label class="form-label" for="description">Description</label>
200
- <textarea id="description" class="form-textarea" placeholder="Enter payment description" required></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  </div>
 
 
202
 
203
- <div class="form-group">
204
- <label class="form-label" for="recipient-email">Send to User (Optional)</label>
205
- <input type="email" id="recipient-email" class="form-input" placeholder="Enter recipient email">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
- <div class="modal-buttons">
209
- <button class="btn-secondary" onclick="closeModal()">Cancel</button>
210
- <button class="button" onclick="createPaymentLink()">Create Link</button>
 
 
 
 
 
 
 
 
 
 
 
 
211
  </div>
212
  </div>
213
  </div>
214
 
 
 
215
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  function openModal() {
217
- document.getElementById('paymentModal').style.display = 'block';
 
 
 
 
 
218
  }
219
 
220
  function closeModal() {
221
- document.getElementById('paymentModal').style.display = 'none';
 
 
 
 
 
222
  document.getElementById('astras').value = '';
223
  document.getElementById('description').value = '';
224
  document.getElementById('recipient-email').value = '';
@@ -226,11 +597,31 @@
226
 
227
  async function createPaymentLink() {
228
  const astras = document.getElementById('astras').value;
229
- const description = document.getElementById('description').value;
230
  const recipientEmail = document.getElementById('recipient-email').value.trim();
231
 
232
- if (!astras || !description.trim()) {
233
- alert('Please fill in all required fields');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  return;
235
  }
236
 
@@ -241,30 +632,187 @@
241
  'Content-Type': 'application/json',
242
  },
243
  body: JSON.stringify({
244
- amount: parseInt(astras),
245
  description: description.trim(),
246
- recipient_email: recipientEmail || null
247
  })
248
  });
249
 
250
  const data = await response.json();
251
 
252
  if (response.ok) {
253
- window.location.href = data.payment_url;
 
 
 
254
  } else {
255
- alert('Error creating payment link: ' + data.error);
256
  }
257
  } catch (error) {
258
- alert('Error creating payment link: ' + error.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  }
260
  }
261
 
262
  window.onclick = function(event) {
263
- const modal = document.getElementById('paymentModal');
264
- if (event.target == modal) {
 
 
265
  closeModal();
266
  }
 
 
 
 
 
 
267
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  </script>
269
  </div>
270
  </body>
 
12
  background-color: #000000;
13
  font-family: Arial, sans-serif;
14
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  .main-content {
16
+ margin-top: 0;
17
+ height: 100vh;
18
  display: flex;
19
  justify-content: center;
20
  align-items: center;
 
50
  .modal {
51
  display: none;
52
  position: fixed;
53
+ z-index: 50;
54
  left: 0;
55
  top: 0;
56
  width: 100%;
57
  height: 100%;
58
  background-color: rgba(0, 0, 0, 0.8);
59
+ backdrop-filter: blur(4px);
60
+ -webkit-backdrop-filter: blur(4px);
61
+ animation: fadeIn 0.15s ease-out;
62
+ }
63
+ .modal.show {
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ }
68
+ @keyframes fadeIn {
69
+ from {
70
+ opacity: 0;
71
+ }
72
+ to {
73
+ opacity: 1;
74
+ }
75
+ }
76
+ @keyframes slideIn {
77
+ from {
78
+ opacity: 0;
79
+ transform: scale(0.96);
80
+ }
81
+ to {
82
+ opacity: 1;
83
+ transform: scale(1);
84
+ }
85
  }
86
  .modal-content {
87
  background-color: #1a1a1a;
88
+ position: relative;
89
+ padding: 0;
90
+ border-radius: 12px;
91
  width: 90%;
92
  max-width: 500px;
93
+ border: 1px solid rgba(255, 255, 255, 0.1);
94
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
95
+ animation: slideIn 0.2s ease-out;
96
+ display: flex;
97
+ flex-direction: column;
98
+ max-height: 90vh;
99
+ overflow: hidden;
100
  }
101
  .modal-header {
102
  color: #ffffff;
103
+ padding: 1.5rem 1.5rem 0 1.5rem;
104
  margin-bottom: 1.5rem;
105
+ font-size: 1.25rem;
106
+ font-weight: 600;
107
+ letter-spacing: -0.01em;
108
+ line-height: 1.4;
109
+ }
110
+ .modal-close {
111
+ position: absolute;
112
+ right: 1rem;
113
+ top: 1rem;
114
+ background: transparent;
115
+ border: none;
116
+ color: rgba(255, 255, 255, 0.7);
117
+ cursor: pointer;
118
+ padding: 0.5rem;
119
+ border-radius: 6px;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ transition: all 0.2s ease;
124
+ width: 32px;
125
+ height: 32px;
126
+ }
127
+ .modal-close:hover {
128
+ background-color: rgba(255, 255, 255, 0.1);
129
+ color: rgba(255, 255, 255, 1);
130
+ }
131
+ .modal-close:focus {
132
+ outline: 2px solid rgba(255, 255, 255, 0.5);
133
+ outline-offset: 2px;
134
+ }
135
+ .modal-close-icon {
136
+ width: 16px;
137
+ height: 16px;
138
+ stroke: currentColor;
139
+ stroke-width: 2;
140
+ fill: none;
141
+ }
142
+ .modal-body {
143
+ padding: 0 1.5rem 1.5rem 1.5rem;
144
+ overflow-y: auto;
145
  }
146
  .form-group {
147
+ margin-bottom: 1.25rem;
148
  }
149
  .form-label {
150
  display: block;
151
+ color: rgba(255, 255, 255, 0.9);
152
  margin-bottom: 0.5rem;
153
+ font-weight: 500;
154
+ font-size: 0.875rem;
155
  }
156
  .form-input {
157
  width: 100%;
158
+ padding: 0.625rem 0.875rem;
159
+ border: 1px solid rgba(255, 255, 255, 0.1);
160
+ border-radius: 6px;
161
+ background-color: rgba(255, 255, 255, 0.05);
162
  color: #ffffff;
163
+ font-size: 0.9375rem;
164
+ transition: all 0.2s ease;
165
+ box-sizing: border-box;
166
  }
167
  .form-input:focus {
168
  outline: none;
169
+ border-color: rgba(255, 255, 255, 0.3);
170
+ background-color: rgba(255, 255, 255, 0.08);
171
+ }
172
+ .form-input::placeholder {
173
+ color: rgba(255, 255, 255, 0.4);
174
+ }
175
+ .form-input-email {
176
+ width: 100%;
177
+ padding: 0.625rem 0.875rem;
178
+ border: 1px solid rgba(255, 255, 255, 0.15);
179
+ border-radius: 8px;
180
+ background-color: rgba(255, 255, 255, 0.06);
181
+ color: #ffffff;
182
+ font-size: 0.9375rem;
183
+ transition: all 0.3s ease;
184
+ box-sizing: border-box;
185
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
186
+ }
187
+ .form-input-email:focus {
188
+ outline: none;
189
+ border-color: rgba(255, 255, 255, 0.4);
190
+ background-color: rgba(255, 255, 255, 0.1);
191
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
192
+ }
193
+ .form-input-email::placeholder {
194
+ color: rgba(255, 255, 255, 0.5);
195
+ font-style: italic;
196
+ }
197
+ .form-input-email:hover:not(:focus) {
198
+ border-color: rgba(255, 255, 255, 0.25);
199
+ background-color: rgba(255, 255, 255, 0.08);
200
  }
201
  .form-textarea {
202
  width: 100%;
203
+ padding: 0.625rem 0.875rem;
204
+ border: 1px solid rgba(255, 255, 255, 0.1);
205
+ border-radius: 6px;
206
+ background-color: rgba(255, 255, 255, 0.05);
207
  color: #ffffff;
208
+ font-size: 0.9375rem;
209
  min-height: 100px;
210
  resize: vertical;
211
+ transition: all 0.2s ease;
212
+ box-sizing: border-box;
213
+ font-family: inherit;
214
  }
215
  .form-textarea:focus {
216
  outline: none;
217
+ border-color: rgba(255, 255, 255, 0.3);
218
+ background-color: rgba(255, 255, 255, 0.08);
219
+ }
220
+ .form-textarea::placeholder {
221
+ color: rgba(255, 255, 255, 0.4);
222
  }
223
  .modal-buttons {
224
  display: flex;
225
+ gap: 0.75rem;
226
  justify-content: flex-end;
227
+ margin-top: 1.5rem;
228
+ padding-top: 1.5rem;
229
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
230
  }
231
  .btn-secondary {
232
+ background-color: transparent;
233
+ color: rgba(255, 255, 255, 0.9);
234
+ border: 1px solid rgba(255, 255, 255, 0.2);
235
+ padding: 0.625rem 1.25rem;
236
+ border-radius: 6px;
237
  cursor: pointer;
238
+ font-size: 0.9375rem;
239
+ font-weight: 500;
240
+ transition: all 0.2s ease;
241
  }
242
  .btn-secondary:hover {
243
+ background-color: rgba(255, 255, 255, 0.1);
244
+ border-color: rgba(255, 255, 255, 0.3);
245
+ }
246
+ .button {
247
+ background-color: #ffffff;
248
+ color: #000000;
249
+ border: none;
250
+ padding: 0.625rem 1.25rem;
251
+ font-size: 0.9375rem;
252
+ font-weight: 500;
253
+ border-radius: 6px;
254
+ cursor: pointer;
255
+ transition: all 0.2s ease;
256
+ text-decoration: none;
257
+ display: inline-block;
258
+ }
259
+ .button:hover {
260
+ background-color: #f0f0f0;
261
+ transform: translateY(-1px);
262
+ box-shadow: 0 4px 12px rgba(255, 255, 255, 0.15);
263
+ }
264
+ .top-banner {
265
+ background: rgba(100, 100, 255, 0.15);
266
+ border: 1px solid rgba(100, 100, 255, 0.3);
267
+ border-radius: 8px;
268
+ padding: 0.75rem 1.5rem;
269
+ margin-bottom: 2rem;
270
+ color: #6464ff;
271
+ font-size: 0.9rem;
272
+ font-weight: 500;
273
+ text-align: center;
274
+ max-width: 500px;
275
+ margin-left: auto;
276
+ margin-right: auto;
277
  }
278
  .banner {
279
  background: rgba(255, 193, 7, 0.15);
 
289
  margin-left: auto;
290
  margin-right: auto;
291
  }
292
+ .toast-container {
293
+ position: fixed;
294
+ bottom: 1.5rem;
295
+ right: 1.5rem;
296
+ z-index: 100;
297
+ display: flex;
298
+ flex-direction: column;
299
+ gap: 0.75rem;
300
+ pointer-events: none;
301
+ }
302
+ .toast {
303
+ background-color: #1a1a1a;
304
+ color: #ffffff;
305
+ padding: 0.875rem 1rem;
306
+ border-radius: 8px;
307
+ border: 1px solid rgba(255, 255, 255, 0.1);
308
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
309
+ min-width: 300px;
310
+ max-width: 400px;
311
+ display: flex;
312
+ align-items: center;
313
+ gap: 0.75rem;
314
+ pointer-events: auto;
315
+ animation: toastSlideIn 0.3s ease-out;
316
+ font-size: 0.9375rem;
317
+ }
318
+ .toast.error {
319
+ border-color: rgba(239, 68, 68, 0.3);
320
+ background-color: rgba(239, 68, 68, 0.1);
321
+ }
322
+ .toast.success {
323
+ border-color: rgba(34, 197, 94, 0.3);
324
+ background-color: rgba(34, 197, 94, 0.1);
325
+ }
326
+ @keyframes toastSlideIn {
327
+ from {
328
+ opacity: 0;
329
+ transform: translateX(100%);
330
+ }
331
+ to {
332
+ opacity: 1;
333
+ transform: translateX(0);
334
+ }
335
+ }
336
+ @keyframes toastSlideOut {
337
+ from {
338
+ opacity: 1;
339
+ transform: translateX(0);
340
+ }
341
+ to {
342
+ opacity: 0;
343
+ transform: translateX(100%);
344
+ }
345
+ }
346
+ .toast.hiding {
347
+ animation: toastSlideOut 0.2s ease-in forwards;
348
+ }
349
+ .toast-icon {
350
+ flex-shrink: 0;
351
+ width: 20px;
352
+ height: 20px;
353
+ }
354
+ .toast-message {
355
+ flex: 1;
356
+ line-height: 1.5;
357
+ }
358
+ .toast-close {
359
+ background: transparent;
360
+ border: none;
361
+ color: rgba(255, 255, 255, 0.6);
362
+ cursor: pointer;
363
+ padding: 0.25rem;
364
+ border-radius: 4px;
365
+ display: flex;
366
+ align-items: center;
367
+ justify-content: center;
368
+ transition: all 0.2s ease;
369
+ flex-shrink: 0;
370
+ }
371
+ .toast-close:hover {
372
+ background-color: rgba(255, 255, 255, 0.1);
373
+ color: rgba(255, 255, 255, 1);
374
+ }
375
+ .toast-close-icon {
376
+ width: 16px;
377
+ height: 16px;
378
+ stroke: currentColor;
379
+ stroke-width: 2;
380
+ fill: none;
381
+ }
382
  </style>
383
  </head>
384
  <body>
 
 
 
 
385
  <div class="main-content">
386
  <div class="container">
387
  <h1 class="title">AstraPay</h1>
388
+ <div class="top-banner">
389
+ Contact Matthew on Slack if any issues occur.
390
+ </div>
391
+ <div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
392
+ <button class="button" onclick="openModal()" style="display: flex; align-items: center; gap: 0.5rem;">
393
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
394
+ <rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect>
395
+ <line x1="1" y1="10" x2="23" y2="10"></line>
396
+ </svg>
397
+ Create Payment Link
398
+ </button>
399
+ <button class="button" onclick="openClaimModal()" style="background-color: rgba(255, 255, 255, 0.9); display: flex; align-items: center; gap: 0.5rem;">
400
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
401
+ <polyline points="20 12 20 22 4 22 4 12"></polyline>
402
+ <rect x="2" y="7" width="20" height="5"></rect>
403
+ <line x1="12" y1="22" x2="12" y2="7"></line>
404
+ <path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"></path>
405
+ <path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"></path>
406
+ </svg>
407
+ Create Claim Link
408
+ </button>
409
+ <button class="button" onclick="openEnterIdModal()" style="background-color: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.3); color: #ffffff; display: flex; align-items: center; gap: 0.5rem;">
410
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
411
+ <path d="M21 21l-6-6m2-5a7 7 0 1 1-14 0 7 7 0 0 1 14 0z"></path>
412
+ </svg>
413
+ Enter ID
414
+ </button>
415
  </div>
 
416
  </div>
417
 
418
  <div id="paymentModal" class="modal">
419
  <div class="modal-content">
420
+ <button class="modal-close" onclick="closeModal()" aria-label="Close">
421
+ <svg class="modal-close-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
422
+ <path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/>
423
+ </svg>
424
+ </button>
425
  <div class="modal-header">Create Payment Link</div>
426
+ <div class="modal-body">
427
+ <div class="form-group">
428
+ <label class="form-label" for="astras">Number of Astras</label>
429
+ <input type="number" id="astras" class="form-input" min="1" max="10000" placeholder="Enter number of astras" required oninput="validateAstrasAmount(this)">
430
+ </div>
431
 
432
+ <div class="form-group">
433
+ <label class="form-label" for="description">Description</label>
434
+ <textarea id="description" class="form-textarea" placeholder="Enter payment description" required></textarea>
435
+ </div>
436
 
437
+ <div class="form-group">
438
+ <label class="form-label" for="recipient-email">Your AstraNova Email</label>
439
+ <input type="email" id="recipient-email" class="form-input-email" placeholder="Enter your AstraNova email" required>
440
+ </div>
441
+
442
+ <div class="modal-buttons">
443
+ <button class="btn-secondary" onclick="closeModal()" style="display: flex; align-items: center; gap: 0.5rem;">
444
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
445
+ <line x1="18" y1="6" x2="6" y2="18"></line>
446
+ <line x1="6" y1="6" x2="18" y2="18"></line>
447
+ </svg>
448
+ Cancel
449
+ </button>
450
+ <button class="button" onclick="createPaymentLink()" style="display: flex; align-items: center; gap: 0.5rem;">
451
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
452
+ <line x1="12" y1="5" x2="12" y2="19"></line>
453
+ <line x1="5" y1="12" x2="19" y2="12"></line>
454
+ </svg>
455
+ Create Link
456
+ </button>
457
+ </div>
458
  </div>
459
+ </div>
460
+ </div>
461
 
462
+ <div id="claimModal" class="modal">
463
+ <div class="modal-content">
464
+ <button class="modal-close" onclick="closeClaimModal()" aria-label="Close">
465
+ <svg class="modal-close-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
466
+ <path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/>
467
+ </svg>
468
+ </button>
469
+ <div class="modal-header">Create Claim Link</div>
470
+ <div class="modal-body">
471
+ <div class="form-group">
472
+ <label class="form-label" for="claim-astras">Number of Astras</label>
473
+ <input type="number" id="claim-astras" class="form-input" min="1" max="10000" placeholder="Enter number of astras" required oninput="validateClaimAstrasAmount(this)">
474
+ <div style="font-size: 0.8rem; color: rgba(255, 255, 255, 0.6); margin-top: 0.5rem;">
475
+ You'll send: <span id="claim-send-amount">-</span> Astras (+1%)<br>
476
+ Claimable: <span id="claim-claim-amount">-</span> Astras (-1%)
477
+ </div>
478
+ </div>
479
+
480
+ <div class="modal-buttons">
481
+ <button class="btn-secondary" onclick="closeClaimModal()" style="display: flex; align-items: center; gap: 0.5rem;">
482
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
483
+ <line x1="18" y1="6" x2="6" y2="18"></line>
484
+ <line x1="6" y1="6" x2="18" y2="18"></line>
485
+ </svg>
486
+ Cancel
487
+ </button>
488
+ <button class="button" onclick="createClaimLink()" style="display: flex; align-items: center; gap: 0.5rem;">
489
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
490
+ <polyline points="20 12 20 22 4 22 4 12"></polyline>
491
+ <rect x="2" y="7" width="20" height="5"></rect>
492
+ <line x1="12" y1="22" x2="12" y2="7"></line>
493
+ <path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"></path>
494
+ <path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"></path>
495
+ </svg>
496
+ Create Claim Link
497
+ </button>
498
+ </div>
499
  </div>
500
+ </div>
501
+ </div>
502
+
503
+ <div id="enterIdModal" class="modal">
504
+ <div class="modal-content">
505
+ <button class="modal-close" onclick="closeEnterIdModal()" aria-label="Close">
506
+ <svg class="modal-close-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
507
+ <path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/>
508
+ </svg>
509
+ </button>
510
+ <div class="modal-header">Enter Link ID</div>
511
+ <div class="modal-body">
512
+ <div class="form-group">
513
+ <label class="form-label" for="link-id">Enter 4-character code</label>
514
+ <input type="text" id="link-id" class="form-input" placeholder="e.g., A3B7" maxlength="4" style="text-transform: uppercase; font-size: 1.2rem; letter-spacing: 0.5rem; text-align: center;" required oninput="this.value = this.value.toUpperCase().replace(/[^A-Z0-9]/g, '')" onkeypress="if(event.key === 'Enter') goToLink()">
515
+ </div>
516
 
517
+ <div class="modal-buttons">
518
+ <button class="btn-secondary" onclick="closeEnterIdModal()" style="display: flex; align-items: center; gap: 0.5rem;">
519
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
520
+ <line x1="18" y1="6" x2="6" y2="18"></line>
521
+ <line x1="6" y1="6" x2="18" y2="18"></line>
522
+ </svg>
523
+ Cancel
524
+ </button>
525
+ <button class="button" onclick="goToLink()" style="display: flex; align-items: center; gap: 0.5rem;">
526
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
527
+ <path d="M5 12h14M12 5l7 7-7 7"></path>
528
+ </svg>
529
+ Go to Link
530
+ </button>
531
+ </div>
532
  </div>
533
  </div>
534
  </div>
535
 
536
+ <div id="toastContainer" class="toast-container"></div>
537
+
538
  <script>
539
+ function showToast(message, type = 'error') {
540
+ const container = document.getElementById('toastContainer');
541
+ const toast = document.createElement('div');
542
+ toast.className = `toast ${type}`;
543
+
544
+ const iconSvg = type === 'error'
545
+ ? '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
546
+ : '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
547
+
548
+ toast.innerHTML = `
549
+ ${iconSvg}
550
+ <span class="toast-message">${message}</span>
551
+ <button class="toast-close" onclick="this.parentElement.remove()" aria-label="Close">
552
+ <svg class="toast-close-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
553
+ <path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/>
554
+ </svg>
555
+ </button>
556
+ `;
557
+
558
+ container.appendChild(toast);
559
+
560
+ setTimeout(() => {
561
+ toast.classList.add('hiding');
562
+ setTimeout(() => {
563
+ if (toast.parentElement) {
564
+ toast.remove();
565
+ }
566
+ }, 200);
567
+ }, 4000);
568
+ }
569
+ function validateAstrasAmount(input) {
570
+ const value = parseInt(input.value);
571
+ if (!isNaN(value) && value > 10000) {
572
+ input.value = 10000;
573
+ showToast('Maximum 10,000 Astras per payment link');
574
+ }
575
+ }
576
+
577
  function openModal() {
578
+ const modal = document.getElementById('paymentModal');
579
+ modal.style.display = 'flex';
580
+ setTimeout(() => {
581
+ modal.classList.add('show');
582
+ }, 10);
583
+ document.body.style.overflow = 'hidden';
584
  }
585
 
586
  function closeModal() {
587
+ const modal = document.getElementById('paymentModal');
588
+ modal.classList.remove('show');
589
+ setTimeout(() => {
590
+ modal.style.display = 'none';
591
+ }, 200);
592
+ document.body.style.overflow = '';
593
  document.getElementById('astras').value = '';
594
  document.getElementById('description').value = '';
595
  document.getElementById('recipient-email').value = '';
 
597
 
598
  async function createPaymentLink() {
599
  const astras = document.getElementById('astras').value;
600
+ const description = document.getElementById('description').value.trim();
601
  const recipientEmail = document.getElementById('recipient-email').value.trim();
602
 
603
+ if (!astras) {
604
+ showToast('Please enter the number of Astras');
605
+ document.getElementById('astras').focus();
606
+ return;
607
+ }
608
+
609
+ const astrasAmount = parseInt(astras);
610
+ if (astrasAmount > 10000) {
611
+ showToast('Maximum 10,000 Astras per payment link');
612
+ document.getElementById('astras').focus();
613
+ return;
614
+ }
615
+
616
+ if (!description) {
617
+ showToast('Please enter a description');
618
+ document.getElementById('description').focus();
619
+ return;
620
+ }
621
+
622
+ if (!recipientEmail) {
623
+ showToast('Please enter your AstraNova email');
624
+ document.getElementById('recipient-email').focus();
625
  return;
626
  }
627
 
 
632
  'Content-Type': 'application/json',
633
  },
634
  body: JSON.stringify({
635
+ amount: astrasAmount,
636
  description: description.trim(),
637
+ recipient_email: recipientEmail
638
  })
639
  });
640
 
641
  const data = await response.json();
642
 
643
  if (response.ok) {
644
+ showToast('Payment link created successfully!', 'success');
645
+ setTimeout(() => {
646
+ window.location.href = data.payment_url;
647
+ }, 500);
648
  } else {
649
+ showToast('Error creating payment link: ' + data.error);
650
  }
651
  } catch (error) {
652
+ showToast('Error creating payment link: ' + error.message);
653
+ }
654
+ }
655
+
656
+
657
+ function openClaimModal() {
658
+ const modal = document.getElementById('claimModal');
659
+ modal.style.display = 'flex';
660
+ setTimeout(() => {
661
+ modal.classList.add('show');
662
+ }, 10);
663
+ document.body.style.overflow = 'hidden';
664
+ }
665
+
666
+ function closeClaimModal() {
667
+ const modal = document.getElementById('claimModal');
668
+ modal.classList.remove('show');
669
+ setTimeout(() => {
670
+ modal.style.display = 'none';
671
+ }, 200);
672
+ document.body.style.overflow = '';
673
+ document.getElementById('claim-astras').value = '';
674
+ document.getElementById('claim-send-amount').textContent = '-';
675
+ document.getElementById('claim-claim-amount').textContent = '-';
676
+ }
677
+
678
+ function validateClaimAstrasAmount(input) {
679
+ const value = parseInt(input.value);
680
+ if (!isNaN(value) && value > 0) {
681
+ const sendAmount = Math.max(1, Math.floor(value * 1.01));
682
+ const claimAmount = Math.max(1, Math.floor(value * 0.99));
683
+ document.getElementById('claim-send-amount').textContent = sendAmount;
684
+ document.getElementById('claim-claim-amount').textContent = claimAmount;
685
+ } else {
686
+ document.getElementById('claim-send-amount').textContent = '-';
687
+ document.getElementById('claim-claim-amount').textContent = '-';
688
+ }
689
+ if (!isNaN(value) && value > 10000) {
690
+ input.value = 10000;
691
+ showToast('Maximum 10,000 Astras per claim link');
692
+ }
693
+ }
694
+
695
+ async function createClaimLink() {
696
+ const astras = document.getElementById('claim-astras').value;
697
+
698
+ if (!astras) {
699
+ showToast('Please enter the number of Astras');
700
+ document.getElementById('claim-astras').focus();
701
+ return;
702
+ }
703
+
704
+ const astrasAmount = parseInt(astras);
705
+ if (astrasAmount > 10000) {
706
+ showToast('Maximum 10,000 Astras per claim link');
707
+ document.getElementById('claim-astras').focus();
708
+ return;
709
+ }
710
+
711
+ try {
712
+ const response = await fetch('/create-claim-link', {
713
+ method: 'POST',
714
+ headers: {
715
+ 'Content-Type': 'application/json',
716
+ },
717
+ body: JSON.stringify({
718
+ amount: astrasAmount
719
+ })
720
+ });
721
+
722
+ const data = await response.json();
723
+
724
+ if (response.ok) {
725
+ showToast('Claim link created successfully!', 'success');
726
+ setTimeout(() => {
727
+ window.location.href = data.claim_url;
728
+ }, 500);
729
+ } else {
730
+ showToast('Error creating claim link: ' + data.error);
731
+ }
732
+ } catch (error) {
733
+ showToast('Error creating claim link: ' + error.message);
734
+ }
735
+ }
736
+
737
+ function openEnterIdModal() {
738
+ const modal = document.getElementById('enterIdModal');
739
+ modal.style.display = 'flex';
740
+ setTimeout(() => {
741
+ modal.classList.add('show');
742
+ }, 10);
743
+ document.body.style.overflow = 'hidden';
744
+ document.getElementById('link-id').focus();
745
+ }
746
+
747
+ function closeEnterIdModal() {
748
+ const modal = document.getElementById('enterIdModal');
749
+ modal.classList.remove('show');
750
+ setTimeout(() => {
751
+ modal.style.display = 'none';
752
+ }, 200);
753
+ document.body.style.overflow = '';
754
+ document.getElementById('link-id').value = '';
755
+ }
756
+
757
+ async function goToLink() {
758
+ const linkId = document.getElementById('link-id').value.trim().toUpperCase();
759
+
760
+ if (!linkId || linkId.length !== 4) {
761
+ showToast('Please enter a valid 4-character code');
762
+ document.getElementById('link-id').focus();
763
+ return;
764
+ }
765
+
766
+ try {
767
+ const response = await fetch(`/check-link/${linkId}`);
768
+ const data = await response.json();
769
+
770
+ if (response.ok && data.exists) {
771
+ if (data.type === 'payment') {
772
+ window.location.href = `/pay/${linkId}`;
773
+ } else if (data.type === 'claim') {
774
+ window.location.href = `/claim/${linkId}`;
775
+ }
776
+ } else {
777
+ showToast('Link not found. Please check the code and try again.');
778
+ document.getElementById('link-id').focus();
779
+ }
780
+ } catch (error) {
781
+ showToast('Error checking link: ' + error.message);
782
  }
783
  }
784
 
785
  window.onclick = function(event) {
786
+ const paymentModal = document.getElementById('paymentModal');
787
+ const claimModal = document.getElementById('claimModal');
788
+ const enterIdModal = document.getElementById('enterIdModal');
789
+ if (event.target === paymentModal) {
790
  closeModal();
791
  }
792
+ if (event.target === claimModal) {
793
+ closeClaimModal();
794
+ }
795
+ if (event.target === enterIdModal) {
796
+ closeEnterIdModal();
797
+ }
798
  }
799
+
800
+ document.addEventListener('keydown', function(event) {
801
+ if (event.key === 'Escape') {
802
+ const paymentModal = document.getElementById('paymentModal');
803
+ const claimModal = document.getElementById('claimModal');
804
+ const enterIdModal = document.getElementById('enterIdModal');
805
+ if (paymentModal.classList.contains('show')) {
806
+ closeModal();
807
+ }
808
+ if (claimModal.classList.contains('show')) {
809
+ closeClaimModal();
810
+ }
811
+ if (enterIdModal.classList.contains('show')) {
812
+ closeEnterIdModal();
813
+ }
814
+ }
815
+ });
816
  </script>
817
  </div>
818
  </body>