@gofranz/formshive-submit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/test.html ADDED
@@ -0,0 +1,1074 @@
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>Formshive Submit Library Test</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ max-width: 1200px;
11
+ margin: 0 auto;
12
+ padding: 20px;
13
+ background-color: #f5f5f5;
14
+ }
15
+
16
+ .container {
17
+ background: white;
18
+ padding: 30px;
19
+ border-radius: 8px;
20
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
21
+ margin-bottom: 20px;
22
+ }
23
+
24
+ h1, h2 {
25
+ color: #333;
26
+ }
27
+
28
+ .form-section {
29
+ margin-bottom: 40px;
30
+ padding: 20px;
31
+ border: 1px solid #e0e0e0;
32
+ border-radius: 5px;
33
+ }
34
+
35
+ .form-group {
36
+ margin-bottom: 15px;
37
+ }
38
+
39
+ label {
40
+ display: block;
41
+ margin-bottom: 5px;
42
+ font-weight: 500;
43
+ color: #555;
44
+ }
45
+
46
+ input, textarea, select {
47
+ width: 100%;
48
+ max-width: 400px;
49
+ padding: 10px;
50
+ border: 1px solid #ddd;
51
+ border-radius: 4px;
52
+ font-size: 14px;
53
+ }
54
+
55
+ button {
56
+ background-color: #007bff;
57
+ color: white;
58
+ padding: 10px 20px;
59
+ border: none;
60
+ border-radius: 4px;
61
+ cursor: pointer;
62
+ font-size: 14px;
63
+ margin-right: 10px;
64
+ margin-bottom: 10px;
65
+ }
66
+
67
+ button:hover {
68
+ background-color: #0056b3;
69
+ }
70
+
71
+ button:disabled {
72
+ background-color: #ccc;
73
+ cursor: not-allowed;
74
+ }
75
+
76
+ .progress {
77
+ width: 100%;
78
+ max-width: 400px;
79
+ height: 20px;
80
+ background-color: #f0f0f0;
81
+ border-radius: 10px;
82
+ overflow: hidden;
83
+ margin: 10px 0;
84
+ }
85
+
86
+ .progress-bar {
87
+ height: 100%;
88
+ background-color: #007bff;
89
+ width: 0%;
90
+ transition: width 0.3s ease;
91
+ }
92
+
93
+ .log {
94
+ background-color: #f8f9fa;
95
+ border: 1px solid #e9ecef;
96
+ border-radius: 4px;
97
+ padding: 15px;
98
+ max-height: 300px;
99
+ overflow-y: auto;
100
+ font-family: 'Courier New', monospace;
101
+ font-size: 12px;
102
+ white-space: pre-wrap;
103
+ margin-top: 10px;
104
+ }
105
+
106
+ .status {
107
+ padding: 10px 15px;
108
+ border-radius: 4px;
109
+ margin: 10px 0;
110
+ font-weight: 500;
111
+ }
112
+
113
+ .status.success {
114
+ background-color: #d4edda;
115
+ color: #155724;
116
+ border: 1px solid #c3e6cb;
117
+ }
118
+
119
+ .status.error {
120
+ background-color: #f8d7da;
121
+ color: #721c24;
122
+ border: 1px solid #f5c6cb;
123
+ }
124
+
125
+ .status.info {
126
+ background-color: #cce7ff;
127
+ color: #004085;
128
+ border: 1px solid #99d3ff;
129
+ }
130
+
131
+ .tabs {
132
+ display: flex;
133
+ border-bottom: 1px solid #ddd;
134
+ margin-bottom: 20px;
135
+ }
136
+
137
+ .tab {
138
+ padding: 10px 20px;
139
+ cursor: pointer;
140
+ border: none;
141
+ background: none;
142
+ border-bottom: 3px solid transparent;
143
+ }
144
+
145
+ .tab.active {
146
+ border-bottom-color: #007bff;
147
+ color: #007bff;
148
+ font-weight: 500;
149
+ }
150
+
151
+ .tab-content {
152
+ display: none;
153
+ }
154
+
155
+ .tab-content.active {
156
+ display: block;
157
+ }
158
+
159
+ .code-block {
160
+ background-color: #f1f3f4;
161
+ padding: 15px;
162
+ border-radius: 4px;
163
+ font-family: 'Courier New', monospace;
164
+ font-size: 13px;
165
+ overflow-x: auto;
166
+ margin: 10px 0;
167
+ }
168
+
169
+ .field-error {
170
+ color: #dc3545;
171
+ background-color: #f8d7da;
172
+ border: 1px solid #f5c6cb;
173
+ border-radius: 3px;
174
+ padding: 6px 10px;
175
+ margin-top: 5px;
176
+ font-size: 12px;
177
+ display: block;
178
+ }
179
+
180
+ .input-error {
181
+ border-color: #dc3545 !important;
182
+ background-color: #fff5f5;
183
+ }
184
+
185
+ .input-success {
186
+ border-color: #28a745;
187
+ background-color: #f8fff8;
188
+ }
189
+ </style>
190
+ </head>
191
+ <body>
192
+ <div class="container">
193
+ <h1>Formshive Submit Library Test Page</h1>
194
+ <p>This page demonstrates various features of the Formshive Submit library.</p>
195
+
196
+ <div class="tabs">
197
+ <button class="tab active" onclick="showTab('basic')">Basic Form</button>
198
+ <button class="tab" onclick="showTab('files')">File Upload</button>
199
+ <button class="tab" onclick="showTab('custom')">Custom Config</button>
200
+ <button class="tab" onclick="showTab('validation')">Field Validation</button>
201
+ <button class="tab" onclick="showTab('axios')">With Axios</button>
202
+ </div>
203
+
204
+ <!-- Basic Form Tab -->
205
+ <div id="basic" class="tab-content active">
206
+ <div class="form-section">
207
+ <h2>Basic Form Submission</h2>
208
+ <p>Simple form submission with default settings.</p>
209
+
210
+ <form id="basicForm">
211
+ <div class="form-group">
212
+ <label for="formId1">Form ID:</label>
213
+ <input type="text" id="formId1" value="test-form-id" required>
214
+ </div>
215
+
216
+ <div class="form-group">
217
+ <label for="name1">Name:</label>
218
+ <input type="text" id="name1" value="John Doe" required>
219
+ </div>
220
+
221
+ <div class="form-group">
222
+ <label for="email1">Email:</label>
223
+ <input type="email" id="email1" value="john@example.com" required>
224
+ </div>
225
+
226
+ <div class="form-group">
227
+ <label for="message1">Message:</label>
228
+ <textarea id="message1" rows="4">Hello from Formshive Submit library!</textarea>
229
+ </div>
230
+
231
+ <button type="submit">Submit Form</button>
232
+ <button type="button" onclick="clearLog('basicLog')">Clear Log</button>
233
+ </form>
234
+
235
+ <div id="basicStatus"></div>
236
+ <div id="basicLog" class="log"></div>
237
+ </div>
238
+ </div>
239
+
240
+ <!-- File Upload Tab -->
241
+ <div id="files" class="tab-content">
242
+ <div class="form-section">
243
+ <h2>Form with File Upload</h2>
244
+ <p>Form submission with file uploads and progress tracking.</p>
245
+
246
+ <form id="fileForm">
247
+ <div class="form-group">
248
+ <label for="formId2">Form ID:</label>
249
+ <input type="text" id="formId2" value="file-form-id" required>
250
+ </div>
251
+
252
+ <div class="form-group">
253
+ <label for="name2">Name:</label>
254
+ <input type="text" id="name2" value="Jane Smith" required>
255
+ </div>
256
+
257
+ <div class="form-group">
258
+ <label for="files2">Files:</label>
259
+ <input type="file" id="files2" multiple accept=".txt,.pdf,.jpg,.png">
260
+ </div>
261
+
262
+ <div class="form-group">
263
+ <label for="description2">Description:</label>
264
+ <textarea id="description2" rows="3">These are test files.</textarea>
265
+ </div>
266
+
267
+ <button type="submit">Submit with Files</button>
268
+ <button type="button" onclick="clearLog('fileLog')">Clear Log</button>
269
+ </form>
270
+
271
+ <div class="progress" style="display: none;" id="fileProgress">
272
+ <div class="progress-bar" id="fileProgressBar"></div>
273
+ </div>
274
+ <div id="fileProgressText"></div>
275
+
276
+ <div id="fileStatus"></div>
277
+ <div id="fileLog" class="log"></div>
278
+ </div>
279
+
280
+ <!-- Second File Upload Form -->
281
+ <div class="form-section">
282
+ <h2>Single File Upload Form</h2>
283
+ <p>Another form demonstrating single file upload with different validation rules.</p>
284
+
285
+ <form id="singleFileForm">
286
+ <div class="form-group">
287
+ <label for="formId2b">Form ID:</label>
288
+ <input type="text" id="formId2b" value="single-file-form-id" required>
289
+ </div>
290
+
291
+ <div class="form-group">
292
+ <label for="email2b">Email:</label>
293
+ <input type="email" id="email2b" value="user@example.com" required>
294
+ </div>
295
+
296
+ <div class="form-group">
297
+ <label for="category2b">Category:</label>
298
+ <select id="category2b" required>
299
+ <option value="">Select category...</option>
300
+ <option value="document">Document</option>
301
+ <option value="image">Image</option>
302
+ <option value="video">Video</option>
303
+ <option value="other">Other</option>
304
+ </select>
305
+ </div>
306
+
307
+ <div class="form-group">
308
+ <label for="singleFile">Upload File (max 5MB):</label>
309
+ <input type="file" id="singleFile" accept=".pdf,.doc,.docx,.jpg,.jpeg,.png,.mp4,.mov" required>
310
+ <small style="color: #666; font-size: 12px;">Allowed: PDF, Word docs, Images, Videos</small>
311
+ </div>
312
+
313
+ <div class="form-group">
314
+ <label for="comments2b">Comments (optional):</label>
315
+ <textarea id="comments2b" rows="3" placeholder="Add any additional comments..."></textarea>
316
+ </div>
317
+
318
+ <button type="submit">Upload Single File</button>
319
+ <button type="button" onclick="clearLog('singleFileLog')">Clear Log</button>
320
+ </form>
321
+
322
+ <div class="progress" style="display: none;" id="singleFileProgress">
323
+ <div class="progress-bar" id="singleFileProgressBar"></div>
324
+ </div>
325
+ <div id="singleFileProgressText"></div>
326
+
327
+ <div id="singleFileStatus"></div>
328
+ <div id="singleFileLog" class="log"></div>
329
+ </div>
330
+ </div>
331
+
332
+ <!-- Field Validation Tab -->
333
+ <div id="validation" class="tab-content">
334
+ <div class="form-section">
335
+ <h2>Field Validation Error Handling</h2>
336
+ <p>This form demonstrates how to handle structured field validation errors from Formshive.</p>
337
+
338
+ <form id="validationForm">
339
+ <div class="form-group">
340
+ <label for="formId4">Form ID:</label>
341
+ <input type="text" id="formId4" value="validation-test-form" required>
342
+ </div>
343
+
344
+ <div class="form-group">
345
+ <label for="email4">Email:</label>
346
+ <input type="email" id="email4" value="invalid-email" required>
347
+ <div id="email4-error" class="field-error" style="display: none;"></div>
348
+ </div>
349
+
350
+ <div class="form-group">
351
+ <label for="phone4">Phone:</label>
352
+ <input type="tel" id="phone4" value="123" required>
353
+ <div id="phone4-error" class="field-error" style="display: none;"></div>
354
+ </div>
355
+
356
+ <div class="form-group">
357
+ <label for="age4">Age:</label>
358
+ <input type="number" id="age4" value="999" min="18" max="100" required>
359
+ <div id="age4-error" class="field-error" style="display: none;"></div>
360
+ </div>
361
+
362
+ <div class="form-group">
363
+ <label for="website4">Website:</label>
364
+ <input type="url" id="website4" value="not-a-url" placeholder="https://example.com">
365
+ <div id="website4-error" class="field-error" style="display: none;"></div>
366
+ </div>
367
+
368
+ <div class="form-group">
369
+ <label for="message4">Message (min 10 characters):</label>
370
+ <textarea id="message4" rows="3" required>Hi</textarea>
371
+ <div id="message4-error" class="field-error" style="display: none;"></div>
372
+ </div>
373
+
374
+ <button type="submit">Submit (Expect Validation Errors)</button>
375
+ <button type="button" onclick="clearValidationErrors()">Clear Errors</button>
376
+ <button type="button" onclick="clearLog('validationLog')">Clear Log</button>
377
+ </form>
378
+
379
+ <div id="validationStatus"></div>
380
+ <div id="validationLog" class="log"></div>
381
+
382
+ <div style="margin-top: 20px;">
383
+ <h3>Field Error Helpers Demo</h3>
384
+ <div id="fieldErrorHelpers" style="background: #f5f5f5; padding: 15px; border-radius: 4px;">
385
+ <p><strong>Field Error Information:</strong></p>
386
+ <div id="errorSummary">Submit form to see field validation errors</div>
387
+ <div id="errorDetails" style="margin-top: 10px;"></div>
388
+ </div>
389
+ </div>
390
+ </div>
391
+ </div>
392
+
393
+ <!-- Custom Config Tab -->
394
+ <div id="custom" class="tab-content">
395
+ <div class="form-section">
396
+ <h2>Custom Configuration</h2>
397
+ <p>Form submission with custom retry settings and callbacks.</p>
398
+
399
+ <form id="customForm">
400
+ <div class="form-group">
401
+ <label for="formId3">Form ID:</label>
402
+ <input type="text" id="formId3" value="custom-form-id" required>
403
+ </div>
404
+
405
+ <div class="form-group">
406
+ <label for="endpoint3">Endpoint:</label>
407
+ <input type="url" id="endpoint3" value="https://api.formshive.com/v1" required>
408
+ </div>
409
+
410
+ <div class="form-group">
411
+ <label for="maxRetries3">Max Retries:</label>
412
+ <select id="maxRetries3">
413
+ <option value="1">1</option>
414
+ <option value="3" selected>3</option>
415
+ <option value="5">5</option>
416
+ </select>
417
+ </div>
418
+
419
+ <div class="form-group">
420
+ <label for="baseDelay3">Base Delay (ms):</label>
421
+ <input type="number" id="baseDelay3" value="1000" min="100" max="10000">
422
+ </div>
423
+
424
+ <div class="form-group">
425
+ <label for="subject3">Subject:</label>
426
+ <input type="text" id="subject3" value="Custom Configuration Test">
427
+ </div>
428
+
429
+ <div class="form-group">
430
+ <label for="content3">Content:</label>
431
+ <textarea id="content3" rows="4">Testing custom retry configuration and callbacks.</textarea>
432
+ </div>
433
+
434
+ <button type="submit">Submit with Custom Config</button>
435
+ <button type="button" onclick="clearLog('customLog')">Clear Log</button>
436
+ </form>
437
+
438
+ <div id="customStatus"></div>
439
+ <div id="customLog" class="log"></div>
440
+ </div>
441
+ </div>
442
+
443
+ <!-- Axios Tab -->
444
+ <div id="axios" class="tab-content">
445
+ <div class="form-section">
446
+ <h2>Using Axios HTTP Client</h2>
447
+ <p>Form submission using Axios instead of fetch (requires axios to be loaded).</p>
448
+
449
+ <form id="axiosForm">
450
+ <div class="form-group">
451
+ <label for="formId4">Form ID:</label>
452
+ <input type="text" id="formId4" value="axios-form-id" required>
453
+ </div>
454
+
455
+ <div class="form-group">
456
+ <label for="title4">Title:</label>
457
+ <input type="text" id="title4" value="Axios Test" required>
458
+ </div>
459
+
460
+ <div class="form-group">
461
+ <label for="body4">Body:</label>
462
+ <textarea id="body4" rows="4">This form was submitted using Axios HTTP client.</textarea>
463
+ </div>
464
+
465
+ <button type="submit">Submit with Axios</button>
466
+ <button type="button" onclick="clearLog('axiosLog')">Clear Log</button>
467
+ </form>
468
+
469
+ <div id="axiosStatus"></div>
470
+ <div id="axiosLog" class="log"></div>
471
+ </div>
472
+ </div>
473
+
474
+ <!-- Documentation -->
475
+ <div class="container">
476
+ <h2>Usage Examples</h2>
477
+
478
+ <h3>Basic Usage</h3>
479
+ <div class="code-block">
480
+ // Simple form submission
481
+ FormshiveSubmit.submitFormSimple('your-form-id', {
482
+ name: 'John Doe',
483
+ email: 'john@example.com',
484
+ message: 'Hello world!'
485
+ }).then(response => {
486
+ console.log('Success:', response);
487
+ }).catch(error => {
488
+ console.error('Error:', error);
489
+ });
490
+ </div>
491
+
492
+ <h3>With File Upload</h3>
493
+ <div class="code-block">
494
+ // Form data with files
495
+ const formData = new FormData();
496
+ formData.append('name', 'Jane Smith');
497
+ formData.append('file', fileInput.files[0]);
498
+
499
+ FormshiveSubmit.submitForm({
500
+ formId: 'file-form-id',
501
+ data: formData,
502
+ files: {
503
+ maxFileSize: 5 * 1024 * 1024, // 5MB
504
+ allowedTypes: ['image/jpeg', 'application/pdf'],
505
+ trackProgress: true
506
+ },
507
+ callbacks: {
508
+ onProgress: (percent, loaded, total) => {
509
+ console.log(`Upload progress: ${percent}%`);
510
+ updateProgressBar(percent);
511
+ },
512
+ onSuccess: (response) => {
513
+ console.log('File uploaded!', response);
514
+ },
515
+ onError: (error) => {
516
+ if (error.code === 'FILE_TOO_LARGE') {
517
+ alert('File is too large!');
518
+ }
519
+ }
520
+ }
521
+ });
522
+ </div>
523
+
524
+ <h3>Field Validation Error Handling</h3>
525
+ <div class="code-block">
526
+ try {
527
+ const response = await FormshiveSubmit.submitForm({
528
+ formId: 'your-form-id',
529
+ data: { email: 'invalid-email', phone: '123' }
530
+ });
531
+ } catch (error) {
532
+ // Check if this is a field validation error
533
+ if (FormshiveSubmit.isFieldValidationError(error)) {
534
+ // Get all field errors
535
+ const fieldErrors = FormshiveSubmit.getFieldErrors(error);
536
+
537
+ // Display errors next to form fields
538
+ fieldErrors.forEach(fieldError => {
539
+ const input = document.getElementById(fieldError.field);
540
+ const errorDiv = document.getElementById(fieldError.field + '-error');
541
+
542
+ if (input) input.classList.add('error');
543
+ if (errorDiv) {
544
+ errorDiv.textContent = fieldError.message;
545
+ errorDiv.style.display = 'block';
546
+ }
547
+ });
548
+
549
+ // Or use field error helpers for easier UI integration
550
+ const helpers = FormshiveSubmit.createFieldErrorHelpers(error);
551
+
552
+ console.log('Email has error:', helpers.hasError('email'));
553
+ console.log('Email error message:', helpers.getMessage('email'));
554
+ console.log('Phone error CSS class:', helpers.getFieldClass('phone'));
555
+ }
556
+ }
557
+ </div>
558
+
559
+ <h3>Custom Retry Configuration</h3>
560
+ <div class="code-block">
561
+ FormshiveSubmit.submitForm({
562
+ formId: 'your-form-id',
563
+ data: { message: 'Hello' },
564
+ retry: {
565
+ maxAttempts: 5,
566
+ baseDelay: 2000,
567
+ maxDelay: 30000
568
+ },
569
+ callbacks: {
570
+ onRetry: (attempt, maxAttempts) => {
571
+ console.log(`Retry ${attempt}/${maxAttempts}`);
572
+ }
573
+ }
574
+ });
575
+ </div>
576
+ </div>
577
+ </div>
578
+
579
+ <!-- Include the library (update path as needed) -->
580
+ <script src="formshive-submit.min.js"></script>
581
+ <!-- Optional: Include Axios for axios example -->
582
+ <script src="https://cdn.jsdelivr.net/npm/axios@1.6.0/dist/axios.min.js"></script>
583
+
584
+ <script>
585
+ // Tab switching functionality
586
+ function showTab(tabName) {
587
+ // Hide all tab contents
588
+ const tabContents = document.querySelectorAll('.tab-content');
589
+ tabContents.forEach(content => content.classList.remove('active'));
590
+
591
+ // Remove active class from all tabs
592
+ const tabs = document.querySelectorAll('.tab');
593
+ tabs.forEach(tab => tab.classList.remove('active'));
594
+
595
+ // Show selected tab content and mark tab as active
596
+ document.getElementById(tabName).classList.add('active');
597
+ event.target.classList.add('active');
598
+ }
599
+
600
+ // Utility functions
601
+ function log(elementId, message, type = 'info') {
602
+ const logElement = document.getElementById(elementId);
603
+ const timestamp = new Date().toLocaleTimeString();
604
+ const logMessage = `[${timestamp}] ${message}\n`;
605
+ logElement.textContent += logMessage;
606
+ logElement.scrollTop = logElement.scrollHeight;
607
+ }
608
+
609
+ function clearLog(elementId) {
610
+ document.getElementById(elementId).textContent = '';
611
+ }
612
+
613
+ function showStatus(elementId, message, type = 'info') {
614
+ const statusElement = document.getElementById(elementId);
615
+ statusElement.innerHTML = `<div class="status ${type}">${message}</div>`;
616
+ }
617
+
618
+ function updateProgress(percent, loaded, total, progressBarId = 'fileProgressBar', progressTextId = 'fileProgressText', progressContainerId = 'fileProgress') {
619
+ const progressBar = document.getElementById(progressBarId);
620
+ const progressText = document.getElementById(progressTextId);
621
+ const progressContainer = document.getElementById(progressContainerId);
622
+
623
+ progressContainer.style.display = 'block';
624
+ progressBar.style.width = percent + '%';
625
+ progressText.textContent = `Upload progress: ${percent}% (${formatBytes(loaded)} / ${formatBytes(total)})`;
626
+
627
+ if (percent === 100) {
628
+ setTimeout(() => {
629
+ progressContainer.style.display = 'none';
630
+ progressText.textContent = '';
631
+ }, 2000);
632
+ }
633
+ }
634
+
635
+ function formatBytes(bytes) {
636
+ if (bytes === 0) return '0 Bytes';
637
+ const k = 1024;
638
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
639
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
640
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
641
+ }
642
+
643
+ // Form submissions
644
+ document.getElementById('basicForm').addEventListener('submit', async (e) => {
645
+ e.preventDefault();
646
+
647
+ const formId = document.getElementById('formId1').value;
648
+ const data = {
649
+ name: document.getElementById('name1').value,
650
+ email: document.getElementById('email1').value,
651
+ message: document.getElementById('message1').value
652
+ };
653
+
654
+ log('basicLog', `Starting basic form submission...`);
655
+ log('basicLog', `Form ID: ${formId}`);
656
+ log('basicLog', `Data: ${JSON.stringify(data, null, 2)}`);
657
+
658
+ try {
659
+ const response = await FormshiveSubmit.submitFormSimple(formId, data, {
660
+ debug: true,
661
+ callbacks: {
662
+ onStart: () => log('basicLog', 'Submission started'),
663
+ onRetry: (attempt, maxAttempts, error) => {
664
+ log('basicLog', `Retry ${attempt}/${maxAttempts}: ${error.message}`);
665
+ }
666
+ }
667
+ });
668
+
669
+ log('basicLog', 'SUCCESS: Form submitted successfully');
670
+ log('basicLog', `Response: ${JSON.stringify(response, null, 2)}`);
671
+ showStatus('basicStatus', 'Form submitted successfully!', 'success');
672
+
673
+ } catch (error) {
674
+ log('basicLog', `ERROR: ${error.message}`);
675
+ log('basicLog', `Error code: ${error.code}`);
676
+ log('basicLog', `Status code: ${error.statusCode}`);
677
+ showStatus('basicStatus', `Error: ${error.message}`, 'error');
678
+ }
679
+ });
680
+
681
+ document.getElementById('fileForm').addEventListener('submit', async (e) => {
682
+ e.preventDefault();
683
+
684
+ const formId = document.getElementById('formId2').value;
685
+ const name = document.getElementById('name2').value;
686
+ const description = document.getElementById('description2').value;
687
+ const files = document.getElementById('files2').files;
688
+
689
+ // Create FormData with files
690
+ const formData = new FormData();
691
+ formData.append('name', name);
692
+ formData.append('description', description);
693
+
694
+ for (let i = 0; i < files.length; i++) {
695
+ formData.append(`file_${i}`, files[i]);
696
+ }
697
+
698
+ log('fileLog', `Starting file form submission...`);
699
+ log('fileLog', `Form ID: ${formId}`);
700
+ log('fileLog', `Files: ${files.length} files selected`);
701
+
702
+ try {
703
+ const response = await FormshiveSubmit.submitForm({
704
+ formId: formId,
705
+ data: formData,
706
+ debug: true,
707
+ files: {
708
+ maxFileSize: 10 * 1024 * 1024, // 10MB
709
+ trackProgress: true
710
+ },
711
+ callbacks: {
712
+ onStart: () => log('fileLog', 'File upload started'),
713
+ onProgress: (percent, loaded, total) => {
714
+ log('fileLog', `Upload progress: ${percent}%`);
715
+ updateProgress(percent, loaded, total, 'fileProgressBar', 'fileProgressText', 'fileProgress');
716
+ },
717
+ onRetry: (attempt, maxAttempts, error) => {
718
+ log('fileLog', `Retry ${attempt}/${maxAttempts}: ${error.message}`);
719
+ }
720
+ }
721
+ });
722
+
723
+ log('fileLog', 'SUCCESS: Files uploaded successfully');
724
+ log('fileLog', `Response: ${JSON.stringify(response, null, 2)}`);
725
+ showStatus('fileStatus', 'Files uploaded successfully!', 'success');
726
+
727
+ } catch (error) {
728
+ log('fileLog', `ERROR: ${error.message}`);
729
+ log('fileLog', `Error code: ${error.code}`);
730
+ showStatus('fileStatus', `Error: ${error.message}`, 'error');
731
+ }
732
+ });
733
+
734
+ document.getElementById('singleFileForm').addEventListener('submit', async (e) => {
735
+ e.preventDefault();
736
+
737
+ const formId = document.getElementById('formId2b').value;
738
+ const email = document.getElementById('email2b').value;
739
+ const category = document.getElementById('category2b').value;
740
+ const comments = document.getElementById('comments2b').value;
741
+ const fileInput = document.getElementById('singleFile');
742
+ const file = fileInput.files[0];
743
+
744
+ if (!file) {
745
+ log('singleFileLog', 'ERROR: No file selected');
746
+ showStatus('singleFileStatus', 'Please select a file to upload', 'error');
747
+ return;
748
+ }
749
+
750
+ // Create FormData with single file
751
+ const formData = new FormData();
752
+ formData.append('email', email);
753
+ formData.append('category', category);
754
+ formData.append('comments', comments);
755
+ formData.append('file', file);
756
+
757
+ log('singleFileLog', `Starting single file upload...`);
758
+ log('singleFileLog', `Form ID: ${formId}`);
759
+ log('singleFileLog', `File: ${file.name} (${formatBytes(file.size)})`);
760
+ log('singleFileLog', `File type: ${file.type}`);
761
+ log('singleFileLog', `Category: ${category}`);
762
+
763
+ try {
764
+ const response = await FormshiveSubmit.submitForm({
765
+ formId: formId,
766
+ data: formData,
767
+ debug: true,
768
+ files: {
769
+ maxFileSize: 5 * 1024 * 1024, // 5MB limit
770
+ allowedTypes: [
771
+ 'application/pdf',
772
+ 'application/msword',
773
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
774
+ 'image/jpeg',
775
+ 'image/jpg',
776
+ 'image/png',
777
+ 'video/mp4',
778
+ 'video/quicktime'
779
+ ],
780
+ trackProgress: true
781
+ },
782
+ callbacks: {
783
+ onStart: () => log('singleFileLog', 'Single file upload started'),
784
+ onProgress: (percent, loaded, total) => {
785
+ log('singleFileLog', `Upload progress: ${percent}%`);
786
+ updateProgress(percent, loaded, total, 'singleFileProgressBar', 'singleFileProgressText', 'singleFileProgress');
787
+ },
788
+ onRetry: (attempt, maxAttempts, error) => {
789
+ log('singleFileLog', `Retry ${attempt}/${maxAttempts}: ${error.message}`);
790
+ }
791
+ }
792
+ });
793
+
794
+ log('singleFileLog', 'SUCCESS: File uploaded successfully');
795
+ log('singleFileLog', `Response: ${JSON.stringify(response, null, 2)}`);
796
+ showStatus('singleFileStatus', `File "${file.name}" uploaded successfully!`, 'success');
797
+
798
+ // Reset form
799
+ document.getElementById('singleFileForm').reset();
800
+ document.getElementById('category2b').value = '';
801
+
802
+ } catch (error) {
803
+ log('singleFileLog', `ERROR: ${error.message}`);
804
+ log('singleFileLog', `Error code: ${error.code}`);
805
+
806
+ // Show user-friendly error message based on error code
807
+ let userMessage = error.message;
808
+ if (error.code === 'FILE_TOO_LARGE') {
809
+ userMessage = `File "${file.name}" is too large. Maximum size is 5MB.`;
810
+ } else if (error.code === 'INVALID_FILE_TYPE') {
811
+ userMessage = `File "${file.name}" has an unsupported format. Please use PDF, Word docs, images, or videos.`;
812
+ }
813
+
814
+ showStatus('singleFileStatus', userMessage, 'error');
815
+ }
816
+ });
817
+
818
+ document.getElementById('customForm').addEventListener('submit', async (e) => {
819
+ e.preventDefault();
820
+
821
+ const formId = document.getElementById('formId3').value;
822
+ const endpoint = document.getElementById('endpoint3').value;
823
+ const maxRetries = parseInt(document.getElementById('maxRetries3').value);
824
+ const baseDelay = parseInt(document.getElementById('baseDelay3').value);
825
+
826
+ const data = {
827
+ subject: document.getElementById('subject3').value,
828
+ content: document.getElementById('content3').value
829
+ };
830
+
831
+ log('customLog', `Starting custom form submission...`);
832
+ log('customLog', `Form ID: ${formId}`);
833
+ log('customLog', `Endpoint: ${endpoint}`);
834
+ log('customLog', `Retry config: max=${maxRetries}, delay=${baseDelay}ms`);
835
+
836
+ try {
837
+ const response = await FormshiveSubmit.submitForm({
838
+ formId: formId,
839
+ data: data,
840
+ endpoint: endpoint,
841
+ debug: true,
842
+ retry: {
843
+ maxAttempts: maxRetries,
844
+ baseDelay: baseDelay,
845
+ enableJitter: true
846
+ },
847
+ callbacks: {
848
+ onStart: () => log('customLog', 'Custom submission started'),
849
+ onRetry: (attempt, maxAttempts, error) => {
850
+ log('customLog', `Retry ${attempt}/${maxAttempts}: ${error.message}`);
851
+ showStatus('customStatus', `Retrying... (${attempt}/${maxAttempts})`, 'info');
852
+ }
853
+ }
854
+ });
855
+
856
+ log('customLog', 'SUCCESS: Custom form submitted successfully');
857
+ log('customLog', `Response: ${JSON.stringify(response, null, 2)}`);
858
+ showStatus('customStatus', 'Form submitted successfully with custom config!', 'success');
859
+
860
+ } catch (error) {
861
+ log('customLog', `ERROR: ${error.message}`);
862
+ log('customLog', `Final attempt: ${error.attempt}`);
863
+ showStatus('customStatus', `Error: ${error.message}`, 'error');
864
+ }
865
+ });
866
+
867
+ document.getElementById('axiosForm').addEventListener('submit', async (e) => {
868
+ e.preventDefault();
869
+
870
+ // Check if axios is available
871
+ if (typeof axios === 'undefined') {
872
+ log('axiosLog', 'ERROR: Axios is not loaded');
873
+ showStatus('axiosStatus', 'Error: Axios library not found. Please include Axios.', 'error');
874
+ return;
875
+ }
876
+
877
+ const formId = document.getElementById('formId4').value;
878
+ const data = {
879
+ title: document.getElementById('title4').value,
880
+ body: document.getElementById('body4').value
881
+ };
882
+
883
+ log('axiosLog', `Starting Axios form submission...`);
884
+ log('axiosLog', `Form ID: ${formId}`);
885
+ log('axiosLog', `Using Axios HTTP client`);
886
+
887
+ try {
888
+ const response = await FormshiveSubmit.submitForm({
889
+ formId: formId,
890
+ data: data,
891
+ httpClient: 'axios', // Use axios instead of fetch
892
+ debug: true,
893
+ callbacks: {
894
+ onStart: () => log('axiosLog', 'Axios submission started'),
895
+ onRetry: (attempt, maxAttempts, error) => {
896
+ log('axiosLog', `Retry ${attempt}/${maxAttempts}: ${error.message}`);
897
+ }
898
+ }
899
+ });
900
+
901
+ log('axiosLog', 'SUCCESS: Form submitted successfully with Axios');
902
+ log('axiosLog', `Response: ${JSON.stringify(response, null, 2)}`);
903
+ showStatus('axiosStatus', 'Form submitted successfully with Axios!', 'success');
904
+
905
+ } catch (error) {
906
+ log('axiosLog', `ERROR: ${error.message}`);
907
+ log('axiosLog', `Error code: ${error.code}`);
908
+ showStatus('axiosStatus', `Error: ${error.message}`, 'error');
909
+ }
910
+ });
911
+
912
+ document.getElementById('validationForm').addEventListener('submit', async (e) => {
913
+ e.preventDefault();
914
+
915
+ // Clear any existing errors first
916
+ clearValidationErrors();
917
+
918
+ const formId = document.getElementById('formId4').value;
919
+ const data = {
920
+ email: document.getElementById('email4').value,
921
+ phone: document.getElementById('phone4').value,
922
+ age: document.getElementById('age4').value,
923
+ website: document.getElementById('website4').value,
924
+ message: document.getElementById('message4').value
925
+ };
926
+
927
+ log('validationLog', `Starting validation test submission...`);
928
+ log('validationLog', `Form ID: ${formId}`);
929
+ log('validationLog', `Data: ${JSON.stringify(data, null, 2)}`);
930
+
931
+ try {
932
+ const response = await FormshiveSubmit.submitForm({
933
+ formId: formId,
934
+ data: data,
935
+ debug: true,
936
+ callbacks: {
937
+ onStart: () => log('validationLog', 'Validation test started'),
938
+ onError: (error) => {
939
+ // This callback will be called, but we also handle it in the catch block
940
+ log('validationLog', 'Error callback triggered');
941
+ }
942
+ }
943
+ });
944
+
945
+ log('validationLog', 'SUCCESS: Validation test submitted successfully');
946
+ log('validationLog', `Response: ${JSON.stringify(response, null, 2)}`);
947
+ showStatus('validationStatus', 'Form submitted successfully!', 'success');
948
+
949
+ // Clear field errors and update demo
950
+ clearValidationErrors();
951
+ updateFieldErrorDemo(null);
952
+
953
+ } catch (error) {
954
+ log('validationLog', `ERROR: ${error.message}`);
955
+ log('validationLog', `Error code: ${error.code}`);
956
+ log('validationLog', `Status code: ${error.statusCode}`);
957
+
958
+ // Check if this is a field validation error
959
+ if (FormshiveSubmit.isFieldValidationError(error)) {
960
+ log('validationLog', 'FIELD VALIDATION ERRORS DETECTED:');
961
+
962
+ const fieldErrors = FormshiveSubmit.getFieldErrors(error);
963
+ fieldErrors.forEach(fieldError => {
964
+ log('validationLog', ` - ${fieldError.field}: ${fieldError.message} (code: ${fieldError.code})`);
965
+ });
966
+
967
+ // Show field errors in the form
968
+ showFieldValidationErrors(error);
969
+
970
+ // Update field error demo
971
+ updateFieldErrorDemo(error);
972
+
973
+ const summary = FormshiveSubmit.getValidationErrorSummary ?
974
+ FormshiveSubmit.getValidationErrorSummary(error) :
975
+ `Validation failed for ${fieldErrors.length} fields`;
976
+
977
+ showStatus('validationStatus', summary, 'error');
978
+ } else {
979
+ showStatus('validationStatus', `Error: ${error.message}`, 'error');
980
+ updateFieldErrorDemo(null);
981
+ }
982
+ }
983
+ });
984
+
985
+ // Validation error handling functions
986
+ function clearValidationErrors() {
987
+ const form = document.getElementById('validationForm');
988
+ const inputs = form.querySelectorAll('input, textarea');
989
+
990
+ // Remove error classes and hide error messages
991
+ inputs.forEach(input => {
992
+ input.classList.remove('input-error');
993
+ const errorDiv = document.getElementById(input.id + '-error');
994
+ if (errorDiv) {
995
+ errorDiv.style.display = 'none';
996
+ errorDiv.textContent = '';
997
+ }
998
+ });
999
+ }
1000
+
1001
+ function showFieldValidationErrors(error) {
1002
+ const fieldErrors = FormshiveSubmit.getFieldErrors(error);
1003
+
1004
+ fieldErrors.forEach(fieldError => {
1005
+ const input = document.getElementById(fieldError.field + '4'); // Add '4' suffix for validation form
1006
+ const errorDiv = document.getElementById(fieldError.field + '4-error');
1007
+
1008
+ if (input) {
1009
+ input.classList.add('input-error');
1010
+ }
1011
+
1012
+ if (errorDiv) {
1013
+ errorDiv.textContent = fieldError.message;
1014
+ errorDiv.style.display = 'block';
1015
+ }
1016
+ });
1017
+ }
1018
+
1019
+ function updateFieldErrorDemo(error) {
1020
+ const errorSummary = document.getElementById('errorSummary');
1021
+ const errorDetails = document.getElementById('errorDetails');
1022
+
1023
+ if (!error || !FormshiveSubmit.isFieldValidationError(error)) {
1024
+ errorSummary.innerHTML = 'Submit form to see field validation errors';
1025
+ errorDetails.innerHTML = '';
1026
+ return;
1027
+ }
1028
+
1029
+ // Get field error helpers
1030
+ const helpers = FormshiveSubmit.createFieldErrorHelpers(error, 'input-error');
1031
+
1032
+ // Show summary
1033
+ const summary = FormshiveSubmit.getValidationErrorSummary ?
1034
+ FormshiveSubmit.getValidationErrorSummary(error) :
1035
+ 'Field validation errors occurred';
1036
+ errorSummary.innerHTML = `<strong>Error Summary:</strong> ${summary}`;
1037
+
1038
+ // Show detailed error information
1039
+ const fieldErrors = FormshiveSubmit.getFieldErrors(error);
1040
+ let detailsHtml = '<div style="margin-top: 10px;"><strong>Field Details:</strong></div>';
1041
+
1042
+ ['email', 'phone', 'age', 'website', 'message'].forEach(fieldName => {
1043
+ const hasError = helpers.hasError(fieldName);
1044
+ const message = helpers.getMessage(fieldName);
1045
+ const statusIcon = hasError ? '❌' : '✅';
1046
+ const statusText = hasError ? `Error: ${message}` : 'Valid';
1047
+
1048
+ detailsHtml += `<div style="padding: 4px 0;">
1049
+ <strong>${fieldName}:</strong> ${statusIcon} ${statusText}
1050
+ </div>`;
1051
+ });
1052
+
1053
+ detailsHtml += '<div style="margin-top: 10px;"><strong>Available Helper Methods:</strong></div>';
1054
+ detailsHtml += '<code style="font-size: 11px; background: #f8f9fa; padding: 8px; display: block; margin-top: 5px;">';
1055
+ detailsHtml += `helpers.hasError('email'): ${helpers.hasError('email')}<br>`;
1056
+ detailsHtml += `helpers.getMessage('phone'): "${helpers.getMessage('phone')}"<br>`;
1057
+ detailsHtml += `helpers.getFieldClass('age'): "${helpers.getFieldClass('age')}"`;
1058
+ detailsHtml += '</code>';
1059
+
1060
+ errorDetails.innerHTML = detailsHtml;
1061
+ }
1062
+
1063
+ // Initialize
1064
+ document.addEventListener('DOMContentLoaded', () => {
1065
+ log('basicLog', 'Formshive Submit Test Page loaded');
1066
+ log('basicLog', `Library version: ${FormshiveSubmit ? 'Loaded' : 'Not found'}`);
1067
+
1068
+ if (typeof FormshiveSubmit !== 'undefined') {
1069
+ log('basicLog', 'Available methods: ' + Object.keys(FormshiveSubmit).join(', '));
1070
+ }
1071
+ });
1072
+ </script>
1073
+ </body>
1074
+ </html>