Subversion Repositories SmartDukaan

Rev

Rev 36689 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 36689 Rev 36807
Line 1... Line 1...
1
<html>
1
<html>
2
<head>
2
<head>
3
    <meta charset="UTF-8">
3
    <meta charset="UTF-8">
4
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
4
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
5
    <title>Location Verification - SmartDukaan</title>
5
    <title>Store Verification - SmartDukaan</title>
6
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
6
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
7
    <style>
7
    <style>
8
        body {
8
        body {
9
            background: #f4f6f9;
9
            background: #f5f6f8;
10
            font-family: 'Segoe UI', Roboto, sans-serif;
10
            font-family: 'Segoe UI', Roboto, sans-serif;
-
 
11
            color: #2b2b2b;
11
        }
12
        }
12
 
13
 
13
        .geo-card {
-
 
14
            max-width: 480px;
14
        /* ---------- Top navbar ---------- */
15
            margin: 30px auto;
15
        .top-nav {
16
            background: #fff;
16
            background: #fff;
17
            border-radius: 14px;
17
            border-bottom: 1px solid #ececec;
18
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
18
            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
-
 
19
            padding: 14px 22px;
19
            overflow: hidden;
20
            display: flex;
-
 
21
            align-items: center;
-
 
22
            justify-content: space-between;
-
 
23
            position: sticky;
-
 
24
            top: 0;
-
 
25
            z-index: 10;
20
        }
26
        }
21
 
27
 
22
        .geo-header {
28
        .nav-home {
23
            background: linear-gradient(135deg, #f7931e 0%, #ed1c24 100%);
29
            display: inline-flex;
-
 
30
            align-items: center;
-
 
31
            gap: 8px;
24
            color: #fff;
32
            color: #2b2b2b;
-
 
33
            font-weight: 600;
25
            padding: 22px 24px;
34
            font-size: 15px;
26
            text-align: center;
35
            text-decoration: none;
27
        }
36
        }
28
 
37
 
-
 
38
        .nav-home:hover {
-
 
39
            color: #e1212a;
-
 
40
            text-decoration: none;
-
 
41
        }
-
 
42
 
29
        .geo-header .brand-row {
43
        .nav-brand {
30
            display: flex;
44
            display: inline-flex;
31
            align-items: center;
45
            align-items: center;
32
            justify-content: center;
46
            text-decoration: none;
33
            gap: 10px;
-
 
34
            margin-bottom: 6px;
-
 
35
        }
47
        }
36
 
48
 
37
        .geo-header .brand-mark {
49
        .nav-brand .brand-logo {
38
            width: 36px;
50
            height: 30px;
39
            height: 36px;
51
            width: auto;
40
            background: #fff;
52
            display: block;
41
            color: #ed1c24;
53
        }
-
 
54
 
42
            border-radius: 8px;
55
        .nav-help {
43
            display: inline-flex;
56
            display: inline-flex;
44
            align-items: center;
57
            align-items: center;
45
            justify-content: center;
58
            gap: 8px;
46
            font-weight: 800;
59
            font-size: 14px;
47
            font-size: 20px;
60
            color: #555;
48
            letter-spacing: -1px;
61
            white-space: nowrap;
49
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
-
 
50
        }
62
        }
51
 
63
 
52
        .geo-header h4 {
64
        .nav-call {
-
 
65
            display: inline-flex;
-
 
66
            align-items: center;
53
            margin: 0;
67
            gap: 7px;
-
 
68
            text-decoration: none;
-
 
69
            color: #555;
-
 
70
        }
-
 
71
 
-
 
72
        .nav-call:hover {
-
 
73
            text-decoration: none;
-
 
74
        }
-
 
75
 
-
 
76
        .nav-call .call-word {
-
 
77
            color: #555;
-
 
78
            font-weight: 600;
-
 
79
        }
-
 
80
 
-
 
81
        .nav-help .phone {
-
 
82
            color: #e1212a;
54
            font-weight: 700;
83
            font-weight: 700;
55
            letter-spacing: 0.3px;
-
 
56
        }
84
        }
57
 
85
 
-
 
86
        @media (max-width: 575px) {
58
        .geo-header p {
87
            .top-nav {
59
            margin: 8px 0 0;
88
                padding: 10px 12px;
-
 
89
            }
-
 
90
 
-
 
91
            .nav-brand .brand-logo {
-
 
92
                height: 22px;
-
 
93
            }
-
 
94
 
-
 
95
            .nav-help .q-label,
-
 
96
            .nav-call .call-word {
-
 
97
                display: none;
-
 
98
            }
-
 
99
 
-
 
100
            .nav-call {
60
            opacity: 0.95;
101
                gap: 5px;
-
 
102
            }
-
 
103
 
-
 
104
            .nav-help .phone {
61
            font-size: 13px;
105
                font-size: 13px;
-
 
106
            }
-
 
107
 
-
 
108
            .subhead {
-
 
109
                padding: 12px 14px 0;
-
 
110
            }
62
        }
111
        }
63
 
112
 
-
 
113
        /* ---------- Sub-header home button ---------- */
64
        .geo-body {
114
        .subhead {
65
            padding: 24px;
115
            padding: 16px 22px 0;
66
        }
116
        }
67
 
117
 
68
        .step {
118
        .btn-home {
69
            display: none;
119
            display: inline-flex;
-
 
120
            align-items: center;
-
 
121
            gap: 7px;
-
 
122
            background: #fff;
-
 
123
            color: #374151;
-
 
124
            border: 1px solid #e3e6ea;
-
 
125
            border-radius: 9px;
-
 
126
            padding: 8px 14px;
-
 
127
            font-size: 14px;
-
 
128
            font-weight: 600;
-
 
129
            text-decoration: none;
-
 
130
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
70
        }
131
        }
71
 
132
 
-
 
133
        .btn-home:hover {
-
 
134
            background: #f1f3f5;
-
 
135
            color: #374151;
-
 
136
            text-decoration: none;
-
 
137
        }
-
 
138
 
-
 
139
        /* ---------- Greeting ---------- */
72
        .step.active {
140
        .greeting {
-
 
141
            text-align: center;
-
 
142
            font-size: 19px;
-
 
143
            color: #2b2b2b;
-
 
144
            margin: 26px 16px 18px;
-
 
145
        }
-
 
146
 
-
 
147
        /* ---------- Card ---------- */
-
 
148
        .geo-card {
-
 
149
            max-width: 720px;
-
 
150
            margin: 0 auto 36px;
-
 
151
            background: #fff;
-
 
152
            border-radius: 14px;
-
 
153
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
-
 
154
            overflow: hidden;
-
 
155
        }
-
 
156
 
-
 
157
        .card-head {
-
 
158
            padding: 22px 28px 18px;
-
 
159
            border-bottom: 1px solid #eee;
-
 
160
        }
-
 
161
 
-
 
162
        .card-head h4 {
-
 
163
            margin: 0 0 6px;
-
 
164
            font-weight: 700;
-
 
165
            font-size: 22px;
-
 
166
        }
-
 
167
 
-
 
168
        .card-head p {
-
 
169
            margin: 0;
73
            display: block;
170
            color: #6b7280;
-
 
171
            font-size: 14px;
74
        }
172
        }
75
 
173
 
-
 
174
        .card-body-geo {
-
 
175
            padding: 26px 28px 28px;
-
 
176
        }
-
 
177
 
-
 
178
        @media (max-width: 575px) {
-
 
179
            .card-head, .card-body-geo {
-
 
180
                padding-left: 18px;
-
 
181
                padding-right: 18px;
-
 
182
            }
-
 
183
        }
-
 
184
 
-
 
185
        /* ---------- Step indicator + labels ---------- */
76
        .step-indicator {
186
        .step-indicator {
77
            display: flex;
187
            display: flex;
78
            justify-content: center;
188
            justify-content: center;
79
            gap: 8px;
189
            gap: 8px;
80
            margin-bottom: 20px;
190
            margin-bottom: 10px;
-
 
191
        }
-
 
192
 
-
 
193
        .step-caption {
-
 
194
            text-align: center;
-
 
195
            font-size: 13px;
-
 
196
            font-weight: 600;
-
 
197
            color: #8b93a1;
-
 
198
            margin-bottom: 24px;
81
        }
199
        }
82
 
200
 
83
        .step-dot {
201
        .step-dot {
84
            width: 10px;
202
            width: 28px;
85
            height: 10px;
203
            height: 5px;
86
            border-radius: 50%;
204
            border-radius: 3px;
87
            background: #ddd;
205
            background: #e3e6ea;
88
            transition: background 0.3s;
206
            transition: background 0.3s;
89
        }
207
        }
90
 
208
 
91
        .step-dot.done {
209
        .step-dot.done {
92
            background: #4caf50;
210
            background: #22c55e;
93
        }
211
        }
94
 
212
 
95
        .step-dot.current {
213
        .step-dot.current {
96
            background: #ed1c24;
214
            background: #e1212a;
97
        }
215
        }
98
 
216
 
99
        .btn-geo {
217
        .section-label {
-
 
218
            font-size: 12px;
-
 
219
            font-weight: 700;
-
 
220
            letter-spacing: 0.6px;
100
            background: linear-gradient(135deg, #f7931e 0%, #ed1c24 100%);
221
            text-transform: uppercase;
101
            color: #fff;
222
            color: #8b93a1;
-
 
223
            margin-bottom: 12px;
-
 
224
        }
-
 
225
 
-
 
226
        .section-label .step-no {
-
 
227
            color: #e1212a;
-
 
228
        }
-
 
229
 
-
 
230
        /* Hindi line shown right under its English counterpart */
-
 
231
        .hindi {
-
 
232
            display: block;
-
 
233
            margin-top: 6px;
-
 
234
        }
-
 
235
 
-
 
236
        /* divider between the two steps on the same page */
-
 
237
        .step-divider {
102
            border: none;
238
            border: none;
103
            border-radius: 8px;
239
            border-top: 1px solid #eef0f3;
104
            padding: 12px 24px;
240
            margin: 26px 0;
-
 
241
        }
-
 
242
 
105
            font-size: 16px;
243
        .step-section.locked {
106
            width: 100%;
244
            opacity: 0.6;
107
            cursor: pointer;
245
            pointer-events: none;
-
 
246
        }
-
 
247
 
-
 
248
        /* ---------- Form fields ---------- */
-
 
249
        .field-label {
108
            font-weight: 600;
250
            font-weight: 600;
-
 
251
            font-size: 15px;
109
            box-shadow: 0 2px 8px rgba(237, 28, 36, 0.25);
252
            margin-bottom: 8px;
-
 
253
            display: block;
110
        }
254
        }
111
 
255
 
112
        .btn-geo:hover {
256
        .field-label .req {
113
            filter: brightness(0.95);
257
            color: #e1212a;
114
            color: #fff;
258
            margin-left: 2px;
115
        }
259
        }
116
 
260
 
-
 
261
        .input-geo {
-
 
262
            width: 100%;
-
 
263
            background: #f1f3f5;
-
 
264
            border: 1px solid #f1f3f5;
-
 
265
            border-radius: 10px;
-
 
266
            padding: 13px 16px;
-
 
267
            font-size: 15px;
-
 
268
            transition: border-color 0.2s, background 0.2s;
-
 
269
        }
-
 
270
 
117
        .btn-geo:disabled {
271
        .input-geo:focus {
-
 
272
            outline: none;
118
            background: #b0bec5;
273
            background: #fff;
-
 
274
            border-color: #e1212a;
-
 
275
            box-shadow: 0 0 0 0.18rem rgba(225, 33, 42, 0.15);
-
 
276
        }
-
 
277
 
-
 
278
        .input-geo:disabled {
119
            cursor: not-allowed;
279
            color: #6b7280;
-
 
280
        }
-
 
281
 
-
 
282
        .mobile-row {
-
 
283
            position: relative;
120
        }
284
        }
121
 
285
 
-
 
286
        .mobile-row .input-geo {
-
 
287
            width: 100%;
-
 
288
        }
-
 
289
 
-
 
290
        /* reserve room on the right for the in-field badge once verified */
-
 
291
        .input-geo.field-verified {
-
 
292
            padding-right: 104px;
-
 
293
        }
-
 
294
 
122
        .loc-info {
295
        .verified-pill {
-
 
296
            position: absolute;
-
 
297
            right: 8px;
-
 
298
            top: 50%;
-
 
299
            transform: translateY(-50%);
-
 
300
            display: none;
-
 
301
            align-items: center;
-
 
302
            gap: 6px;
123
            background: #e8f5e9;
303
            background: #e8f7ee;
-
 
304
            color: #16a34a;
124
            border-radius: 8px;
305
            border-radius: 8px;
125
            padding: 12px;
306
            padding: 6px 12px;
126
            margin: 12px 0;
-
 
127
            font-size: 13px;
307
            font-size: 13px;
128
            color: #2e7d32;
308
            font-weight: 600;
-
 
309
            white-space: nowrap;
129
        }
310
        }
130
 
311
 
131
        .loc-info.error {
312
        .verified-pill.show {
132
            background: #fce4ec;
313
            display: inline-flex;
133
            color: #c62828;
314
            animation: badgeIn 0.4s ease both;
134
        }
315
        }
135
 
316
 
136
        .upload-area {
317
        @keyframes badgeIn {
137
            border: 2px dashed #b0bec5;
318
            from {
138
            border-radius: 8px;
319
                opacity: 0;
-
 
320
                transform: translateY(-50%) translateX(8px);
139
            padding: 24px;
321
            }
140
            text-align: center;
322
            to {
141
            cursor: pointer;
323
                opacity: 1;
142
            transition: border-color 0.3s;
324
                transform: translateY(-50%) translateX(0);
-
 
325
            }
143
        }
326
        }
144
 
327
 
-
 
328
        /* soft reveal used for Step 2 appearing */
145
        .upload-area:hover {
329
        @keyframes softReveal {
-
 
330
            from {
-
 
331
                opacity: 0;
-
 
332
                transform: translateY(10px);
-
 
333
            }
-
 
334
            to {
146
            border-color: #ed1c24;
335
                opacity: 1;
-
 
336
                transform: translateY(0);
-
 
337
            }
147
        }
338
        }
148
 
339
 
149
        .upload-area.uploaded {
340
        .reveal-soft {
150
            border-color: #4caf50;
-
 
151
            background: #e8f5e9;
341
            animation: softReveal 0.45s ease both;
152
        }
342
        }
153
 
343
 
154
        .preview-img {
-
 
155
            max-width: 100%;
344
        /* the tick draws itself once the badge appears */
156
            max-height: 200px;
345
        .verified-pill svg path {
157
            border-radius: 8px;
346
            stroke-dasharray: 24;
158
            margin-top: 12px;
347
            stroke-dashoffset: 24;
159
        }
348
        }
160
 
349
 
161
        .spinner {
-
 
162
            display: inline-block;
-
 
163
            width: 20px;
-
 
164
            height: 20px;
-
 
165
            border: 3px solid #fff;
-
 
166
            border-top-color: transparent;
350
        .verified-pill.show svg path {
167
            border-radius: 50%;
-
 
168
            animation: spin 0.8s linear infinite;
351
            animation: drawTick 0.45s ease-out 0.15s forwards;
169
        }
352
        }
170
 
353
 
171
        @keyframes spin {
354
        @keyframes drawTick {
172
            to {
355
            to {
173
                transform: rotate(360deg);
356
                stroke-dashoffset: 0;
-
 
357
            }
-
 
358
        }
-
 
359
 
-
 
360
        /* ---------- Success / thank-you ---------- */
-
 
361
        .success-wrap {
-
 
362
            min-height: 380px;
-
 
363
            display: flex;
-
 
364
            flex-direction: column;
-
 
365
            align-items: center;
-
 
366
            justify-content: center;
-
 
367
            gap: 30px;
-
 
368
            padding: 44px 24px;
-
 
369
            text-align: center;
-
 
370
        }
-
 
371
 
-
 
372
        /* circle draws a full 360, then the tick ticks left -> right; repeats 3x every 4s */
-
 
373
        .success-circle {
-
 
374
            stroke-dasharray: 151;
-
 
375
            stroke-dashoffset: 151;
-
 
376
            animation: drawCircle 4s ease-in-out 3 forwards;
-
 
377
        }
-
 
378
 
-
 
379
        .success-check {
-
 
380
            stroke-dasharray: 32;
-
 
381
            stroke-dashoffset: 32;
-
 
382
            animation: drawCheck 4s ease-in-out 3 forwards;
-
 
383
        }
-
 
384
 
-
 
385
        @keyframes drawCircle {
-
 
386
            0% {
-
 
387
                stroke-dashoffset: 151;
-
 
388
            }
-
 
389
            25% {
-
 
390
                stroke-dashoffset: 0;
174
            }
391
            }
-
 
392
            100% {
-
 
393
                stroke-dashoffset: 0;
-
 
394
            }
-
 
395
        }
-
 
396
 
-
 
397
        @keyframes drawCheck {
-
 
398
            0%, 25% {
-
 
399
                stroke-dashoffset: 32;
-
 
400
            }
-
 
401
            45% {
-
 
402
                stroke-dashoffset: 0;
-
 
403
            }
-
 
404
            100% {
-
 
405
                stroke-dashoffset: 0;
-
 
406
            }
-
 
407
        }
-
 
408
 
-
 
409
        .success-msg {
-
 
410
            margin: 0;
-
 
411
            font-size: 22px;
-
 
412
            font-weight: 700;
-
 
413
            color: #16a34a;
-
 
414
        }
-
 
415
 
-
 
416
        .success-msg-hi {
-
 
417
            margin: 6px 0 0;
-
 
418
            font-size: 16px;
-
 
419
            color: #4b5563;
-
 
420
        }
-
 
421
 
-
 
422
        /* ---------- Capture (combined location + photo) ---------- */
-
 
423
        .capture-help {
-
 
424
            color: #6b7280;
-
 
425
            font-size: 14px;
-
 
426
            margin: 0 0 16px;
175
        }
427
        }
176
 
428
 
-
 
429
        /* "Take Store Photo" + "Close" controls row */
177
        .form-control:focus {
430
        .camera-controls {
-
 
431
            display: flex;
-
 
432
            gap: 10px;
-
 
433
            align-items: stretch;
-
 
434
            margin-bottom: 18px;
-
 
435
        }
-
 
436
 
-
 
437
        .btn-camera-open {
-
 
438
            flex: 1;
-
 
439
            display: flex;
-
 
440
            flex-direction: row;
-
 
441
            align-items: center;
-
 
442
            justify-content: center;
-
 
443
            gap: 9px;
-
 
444
            background: #ef5a68;
-
 
445
            color: #fff;
-
 
446
            border: none;
178
            border-color: #ed1c24;
447
            border-radius: 12px;
-
 
448
            padding: 15px 18px;
-
 
449
            cursor: pointer;
179
            box-shadow: 0 0 0 0.2rem rgba(237, 28, 36, 0.20);
450
            box-shadow: 0 2px 10px rgba(239, 90, 104, 0.3);
-
 
451
        }
-
 
452
 
-
 
453
        .btn-camera-open .sep {
-
 
454
            opacity: 0.5;
-
 
455
            font-weight: 400;
-
 
456
        }
-
 
457
 
-
 
458
        .btn-camera-close {
-
 
459
            display: inline-flex;
-
 
460
            align-items: center;
-
 
461
            justify-content: center;
-
 
462
            gap: 6px;
-
 
463
            background: #fff;
-
 
464
            color: #455a64;
-
 
465
            border: 1px solid #cfd6dd;
-
 
466
            border-radius: 12px;
-
 
467
            padding: 0 22px;
-
 
468
            font-size: 15px;
-
 
469
            font-weight: 600;
-
 
470
            cursor: pointer;
-
 
471
            white-space: nowrap;
-
 
472
        }
-
 
473
 
-
 
474
        .btn-camera-close .close-x {
-
 
475
            display: none;
-
 
476
        }
-
 
477
 
-
 
478
        /* mobile: collapse Close to a compact icon-only ✕ — placed AFTER the
-
 
479
           base rules above so it wins the cascade on small screens */
-
 
480
        @media (max-width: 575px) {
-
 
481
            .btn-camera-close {
-
 
482
                padding: 0 14px;
-
 
483
            }
-
 
484
 
-
 
485
            .btn-camera-close .close-label {
-
 
486
                display: none;
-
 
487
            }
-
 
488
 
-
 
489
            .btn-camera-close .close-x {
-
 
490
                display: block;
-
 
491
            }
-
 
492
        }
-
 
493
 
-
 
494
        .btn-camera-close:hover {
-
 
495
            background: #f1f3f5;
-
 
496
        }
-
 
497
 
-
 
498
        .btn-camera-open:hover {
-
 
499
            filter: brightness(0.96);
-
 
500
            color: #fff;
-
 
501
        }
-
 
502
 
-
 
503
        .btn-camera-open .label-en {
-
 
504
            font-size: 15px;
-
 
505
            font-weight: 700;
-
 
506
            line-height: 1.2;
-
 
507
        }
-
 
508
 
-
 
509
        .btn-camera-open .label-hi {
-
 
510
            font-size: 14px;
-
 
511
            font-weight: 500;
-
 
512
            opacity: 0.95;
180
        }
513
        }
181
 
514
 
182
        /* Live camera view */
-
 
183
        .camera-wrap {
515
        .camera-wrap {
184
            position: relative;
516
            position: relative;
185
            background: #000;
517
            background: #000;
186
            border-radius: 10px;
518
            border-radius: 12px;
187
            overflow: hidden;
519
            overflow: hidden;
188
            aspect-ratio: 4 / 3;
520
            aspect-ratio: 4 / 3;
189
            margin-bottom: 12px;
521
            margin-bottom: 14px;
190
        }
522
        }
191
 
523
 
192
        .camera-wrap video,
524
        .camera-wrap video,
193
        .camera-wrap img {
525
        .camera-wrap img {
194
            width: 100%;
526
            width: 100%;
Line 203... Line 535...
203
            display: flex;
535
            display: flex;
204
            align-items: center;
536
            align-items: center;
205
            justify-content: center;
537
            justify-content: center;
206
            color: #fff;
538
            color: #fff;
207
            font-size: 13px;
539
            font-size: 13px;
208
            background: rgba(0, 0, 0, 0.5);
540
            background: rgba(0, 0, 0, 0.55);
209
            text-align: center;
541
            text-align: center;
210
            padding: 16px;
542
            padding: 16px;
211
        }
543
        }
212
 
544
 
-
 
545
        .capture-row {
-
 
546
            display: flex;
-
 
547
            align-items: center;
-
 
548
            justify-content: center;
-
 
549
            gap: 16px;
-
 
550
            margin-bottom: 16px;
-
 
551
        }
-
 
552
 
213
        .btn-capture {
553
        .btn-capture {
214
            background: #fff;
554
            background: #fff;
215
            color: #ed1c24;
555
            color: #e1212a;
216
            border: 3px solid #ed1c24;
556
            border: 3px solid #e1212a;
217
            border-radius: 50%;
557
            border-radius: 50%;
218
            width: 64px;
558
            width: 64px;
219
            height: 64px;
559
            height: 64px;
220
            display: inline-flex;
560
            display: inline-flex;
221
            align-items: center;
561
            align-items: center;
Line 231... Line 571...
231
 
571
 
232
        .btn-capture .inner {
572
        .btn-capture .inner {
233
            width: 48px;
573
            width: 48px;
234
            height: 48px;
574
            height: 48px;
235
            border-radius: 50%;
575
            border-radius: 50%;
236
            background: linear-gradient(135deg, #f7931e 0%, #ed1c24 100%);
576
            background: linear-gradient(135deg, #f7931e 0%, #e1212a 100%);
237
        }
-
 
238
 
-
 
239
        .capture-row {
-
 
240
            display: flex;
-
 
241
            align-items: center;
-
 
242
            justify-content: center;
-
 
243
            gap: 14px;
-
 
244
            margin: 8px 0 14px;
-
 
245
        }
577
        }
246
 
578
 
247
        .btn-retake {
579
        .btn-retake {
248
            background: transparent;
580
            background: transparent;
249
            border: 1px solid #b0bec5;
581
            border: 1px solid #b0bec5;
250
            color: #455a64;
582
            color: #455a64;
251
            border-radius: 8px;
583
            border-radius: 8px;
252
            padding: 8px 14px;
584
            padding: 9px 16px;
-
 
585
            cursor: pointer;
-
 
586
            font-size: 14px;
-
 
587
        }
-
 
588
 
-
 
589
        .capture-status {
-
 
590
            border-radius: 10px;
-
 
591
            padding: 12px 16px;
-
 
592
            font-size: 14px;
-
 
593
            margin-bottom: 18px;
-
 
594
            display: none;
-
 
595
        }
-
 
596
 
-
 
597
        .capture-status.ok {
-
 
598
            display: block;
-
 
599
            background: #e8f7ee;
-
 
600
            color: #16a34a;
-
 
601
        }
-
 
602
 
-
 
603
        .capture-status.err {
-
 
604
            display: block;
-
 
605
            background: #fce4ec;
-
 
606
            color: #c62828;
-
 
607
        }
-
 
608
 
-
 
609
        /* ---------- Buttons ---------- */
-
 
610
        .btn-primary-geo {
-
 
611
            background: #475569;
-
 
612
            color: #fff;
-
 
613
            border: none;
-
 
614
            border-radius: 10px;
-
 
615
            padding: 14px 24px;
-
 
616
            font-size: 16px;
-
 
617
            width: 100%;
-
 
618
            cursor: pointer;
-
 
619
            font-weight: 600;
-
 
620
            box-shadow: 0 2px 6px rgba(71, 85, 105, 0.20);
-
 
621
        }
-
 
622
 
-
 
623
        .btn-primary-geo:hover {
-
 
624
            background: #334155;
-
 
625
            color: #fff;
-
 
626
        }
-
 
627
 
-
 
628
        .btn-primary-geo:disabled {
-
 
629
            background: #b0bec5;
-
 
630
            box-shadow: none;
-
 
631
            cursor: not-allowed;
-
 
632
        }
-
 
633
 
-
 
634
        .btn-submit {
-
 
635
            background: #ef5a68;
-
 
636
            color: #fff;
-
 
637
            border: none;
-
 
638
            border-radius: 10px;
-
 
639
            padding: 15px 24px;
-
 
640
            font-size: 16px;
-
 
641
            width: 100%;
253
            cursor: pointer;
642
            cursor: pointer;
-
 
643
            font-weight: 700;
-
 
644
            box-shadow: 0 2px 10px rgba(239, 90, 104, 0.3);
-
 
645
        }
-
 
646
 
-
 
647
        .btn-submit:hover {
-
 
648
            filter: brightness(0.96);
-
 
649
            color: #fff;
-
 
650
        }
-
 
651
 
-
 
652
        .btn-submit:disabled {
-
 
653
            background: #f1a8b0;
-
 
654
            box-shadow: none;
-
 
655
            cursor: not-allowed;
-
 
656
        }
-
 
657
 
-
 
658
        .inline-err {
-
 
659
            background: #fce4ec;
-
 
660
            color: #c62828;
-
 
661
            border-radius: 8px;
-
 
662
            padding: 11px 14px;
-
 
663
            font-size: 14px;
-
 
664
            margin-bottom: 14px;
-
 
665
            display: none;
-
 
666
        }
-
 
667
 
-
 
668
        /* ---------- Consent ---------- */
-
 
669
        .consent-box {
-
 
670
            border: 1px solid #e3e6ea;
-
 
671
            background: #fafbfc;
-
 
672
            border-radius: 10px;
-
 
673
            padding: 16px 18px;
-
 
674
            margin-bottom: 22px;
-
 
675
            display: flex;
-
 
676
            gap: 12px;
-
 
677
            align-items: flex-start;
-
 
678
        }
-
 
679
 
-
 
680
        .consent-box input[type="checkbox"] {
-
 
681
            width: 18px;
-
 
682
            height: 18px;
-
 
683
            margin-top: 2px;
-
 
684
            flex-shrink: 0;
-
 
685
            cursor: pointer;
-
 
686
            accent-color: #e1212a;
-
 
687
        }
-
 
688
 
-
 
689
        .consent-box label {
-
 
690
            margin: 0;
-
 
691
            font-size: 13.5px;
-
 
692
            color: #4b5563;
-
 
693
            line-height: 1.5;
-
 
694
            cursor: pointer;
-
 
695
        }
-
 
696
 
-
 
697
        /* ---------- Spinner ---------- */
-
 
698
        .spinner {
-
 
699
            display: inline-block;
-
 
700
            width: 18px;
-
 
701
            height: 18px;
-
 
702
            border: 3px solid #fff;
-
 
703
            border-top-color: transparent;
-
 
704
            border-radius: 50%;
-
 
705
            animation: spin 0.8s linear infinite;
-
 
706
            vertical-align: middle;
-
 
707
        }
-
 
708
 
-
 
709
        @keyframes spin {
-
 
710
            to {
-
 
711
                transform: rotate(360deg);
-
 
712
            }
-
 
713
        }
-
 
714
 
-
 
715
        /* ---------- Footer ---------- */
-
 
716
        .footer-help {
-
 
717
            text-align: center;
-
 
718
            font-size: 14px;
-
 
719
            color: #4b5563;
-
 
720
            padding: 20px 16px 2px;
-
 
721
        }
-
 
722
 
-
 
723
        .footer-call {
-
 
724
            display: inline-flex;
-
 
725
            align-items: center;
-
 
726
            gap: 6px;
-
 
727
            color: #e1212a;
-
 
728
            font-weight: 700;
-
 
729
            text-decoration: none;
-
 
730
            margin-left: 6px;
-
 
731
        }
-
 
732
 
-
 
733
        .footer-call:hover {
-
 
734
            text-decoration: none;
-
 
735
        }
-
 
736
 
-
 
737
        .geo-footer {
-
 
738
            text-align: center;
-
 
739
            color: #9aa1ab;
254
            font-size: 13px;
740
            font-size: 13px;
-
 
741
            padding: 8px 16px 30px;
-
 
742
            line-height: 1.6;
255
        }
743
        }
256
    </style>
744
    </style>
257
</head>
745
</head>
258
<body>
746
<body>
259
 
747
 
-
 
748
<!-- ===== Top navbar ===== -->
-
 
749
<div class="top-nav">
-
 
750
    <a href="https://smartdukaan.com/" class="nav-brand">
-
 
751
        <img src="$rc.contextPath/resources/images/sd-logo.jpg" alt="SmartDukaan" class="brand-logo">
-
 
752
    </a>
-
 
753
    <span class="nav-help">
-
 
754
        <span class="q-label">Any questions?</span>
-
 
755
        <a href="tel:1800270273" class="nav-call">
-
 
756
            <span class="call-word">Call</span>
-
 
757
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#e1212a" stroke-width="2"
-
 
758
                 stroke-linecap="round" stroke-linejoin="round"><path
-
 
759
                    d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
-
 
760
            <span class="phone">1800 270 273</span>
-
 
761
        </a>
-
 
762
    </span>
-
 
763
</div>
-
 
764
 
-
 
765
<div class="subhead">
-
 
766
    <a href="https://smartdukaan.com/" class="btn-home">
-
 
767
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
-
 
768
             stroke-linecap="round" stroke-linejoin="round">
-
 
769
            <path d="M3 9.5L12 3l9 6.5"/>
-
 
770
            <path
-
 
771
                    d="M5 10v10h14V10"/>
-
 
772
            <path d="M9 20v-6h6v6"/>
-
 
773
        </svg>
-
 
774
        Home
-
 
775
    </a>
-
 
776
</div>
-
 
777
 
-
 
778
    #if($leadName && $leadName.trim() != "")
-
 
779
    <p class="greeting">Greetings, <strong>$leadName</strong>!</p>
-
 
780
    #else
-
 
781
    <p class="greeting">Greetings!</p>
-
 
782
    #end
-
 
783
 
260
<div class="geo-card">
784
<div class="geo-card">
261
    <div class="geo-header">
785
    <div class="card-head">
262
        <div class="brand-row">
-
 
263
            <span class="brand-mark">S</span>
-
 
264
            <h4>SmartDukaan</h4>
786
        <h4>Store Verification Form</h4>
265
        </div>
-
 
266
        <p>Location Verification</p>
787
        <p>Please provide your details for our Area Sales Manager to schedule a visit</p>
267
    </div>
788
    </div>
268
    <div class="geo-body">
789
    <div class="card-body-geo">
269
        <div class="step-indicator">
790
        <div class="step-indicator">
270
            <div class="step-dot current" id="dot1"></div>
791
            <div class="step-dot current" id="dot1"></div>
271
            <div class="step-dot" id="dot2"></div>
792
            <div class="step-dot" id="dot2"></div>
272
        </div>
793
        </div>
-
 
794
        <div class="step-caption" id="stepCaption">Step 1 of 2</div>
273
 
795
 
274
        <p class="text-center text-muted mb-3" style="font-size:14px;">
-
 
275
            Hello <strong>$!leadName</strong>, please verify your location.
-
 
276
        </p>
-
 
277
 
-
 
278
        <!-- STEP 1: Mobile Verification -->
796
        <!-- ===== STEP 1: Mobile verification (stays on the page) ===== -->
279
        <div class="step active" id="step1">
797
        <div class="step-section" id="step1Section">
280
            <h6 class="mb-3">Step 1: Verify Mobile Number</h6>
798
            <div class="section-label"><span class="step-no">Step 1.</span> Verify Mobile Number</div>
-
 
799
            <label class="field-label" for="mobileNumber">Mobile Number <span class="req">*</span></label>
281
            <div class="form-group">
800
            <div class="mobile-row">
282
                <input type="tel" class="form-control" id="mobileNumber" maxlength="10"
801
                <input type="tel" class="input-geo" id="mobileNumber" maxlength="10"
283
                       placeholder="Enter your registered mobile number" autocomplete="off">
802
                       placeholder="Enter 10-digit registered mobile number" autocomplete="off">
-
 
803
                <div class="verified-pill" id="mobileVerified">
-
 
804
                    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"
-
 
805
                         stroke-linecap="round" stroke-linejoin="round">
-
 
806
                        <path d="M4 12L9 17L20 6"/>
-
 
807
                    </svg>
-
 
808
                    Verified
-
 
809
                </div>
284
            </div>
810
            </div>
285
            <div id="mobileError" class="loc-info error" style="display:none;"></div>
811
            <div id="mobileError" class="inline-err mt-3"></div>
286
            <button class="btn btn-geo" id="btnVerifyMobile">
812
            <button class="btn-primary-geo mt-3" id="btnVerifyMobile">Verify &amp; Continue</button>
287
                Verify Mobile
-
 
288
            </button>
-
 
289
        </div>
813
        </div>
290
 
814
 
291
        <!-- STEP 2: Live camera capture (location is grabbed silently when photo is taken) -->
815
        <!-- ===== STEP 2: Single combined capture — photo + auto location ===== -->
-
 
816
        <div class="step-section" id="step2Section" style="display:none;">
292
        <div class="step" id="step2">
817
            <hr class="step-divider">
293
            <h6 class="mb-3">Step 2: Capture Your Store Photo</h6>
818
            <div class="section-label"><span class="step-no">Step 2.</span> Capture Your Store Photo</div>
294
            <p class="text-muted" style="font-size:13px;">
819
            <p class="capture-help">
-
 
820
                Tap the button below to open your camera and take a live photo of your store front.
295
                Stand at your store and tap the red button to take a live photo.
821
                Your location is recorded automatically with the photo.
296
                Your location will be recorded automatically.
822
                <span class="hindi">नीचे दिए गए बटन पर टैप करके कैमरा खोलें और अपनी दुकान के सामने की लाइव फोटो लें। आपकी लोकेशन फोटो के साथ अपने-आप रिकॉर्ड हो जाती है।</span>
297
            </p>
823
            </p>
298
 
824
 
299
            <div class="camera-wrap" id="cameraWrap">
825
            <div class="camera-controls">
300
                <video id="cameraVideo" autoplay playsinline muted></video>
826
                <button type="button" class="btn-camera-open" id="btnOpenCamera">
301
                <img id="photoPreview" style="display:none;" alt="Captured photo">
827
                    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
302
                <div class="cam-overlay" id="cameraOverlay">Starting camera...</div>
828
                         stroke-linecap="round" stroke-linejoin="round">
303
            </div>
829
                        <path
304
 
-
 
-
 
830
                                d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
305
            <div class="capture-row">
831
                        <circle
-
 
832
                                cx="12" cy="13" r="4"/>
-
 
833
                    </svg>
306
                <button type="button" class="btn-capture" id="btnCapture" disabled>
834
                    <span class="label-en">Take a Photo</span>
307
                    <span class="inner"></span>
835
                    <span class="sep">|</span>
-
 
836
                    <span class="label-hi">दुकान की फोटो लें</span>
308
                </button>
837
                </button>
309
                <button type="button" class="btn-retake" id="btnRetake" style="display:none;">
838
                <button type="button" class="btn-camera-close" id="btnCloseCamera" style="display:none;"
-
 
839
                        aria-label="Close camera">
-
 
840
                    <svg class="close-x" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
-
 
841
                         stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
-
 
842
                        <line x1="18" y1="6" x2="6"
-
 
843
                              y2="18"/>
-
 
844
                        <line x1="6" y1="6" x2="18" y2="18"/>
310
                    Retake
845
                    </svg>
-
 
846
                    <span class="close-label">Close</span>
311
                </button>
847
                </button>
312
            </div>
848
            </div>
313
 
849
 
-
 
850
            <div id="cameraStage" style="display:none;">
-
 
851
                <div class="camera-wrap" id="cameraWrap">
-
 
852
                    <video id="cameraVideo" autoplay playsinline muted></video>
-
 
853
                    <img id="photoPreview" alt="Captured photo" style="display:none;">
-
 
854
                    <div class="cam-overlay" id="cameraOverlay">Starting camera...</div>
-
 
855
                </div>
-
 
856
 
-
 
857
                <div class="capture-row">
-
 
858
                    <button type="button" class="btn-capture" id="btnCapture" disabled>
-
 
859
                        <span class="inner"></span>
-
 
860
                    </button>
-
 
861
                    <button type="button" class="btn-retake" id="btnRetake" style="display:none;">Retake</button>
-
 
862
                </div>
-
 
863
            </div>
-
 
864
 
-
 
865
            <div id="captureStatus" class="capture-status"></div>
-
 
866
 
-
 
867
            <!-- Consent -->
-
 
868
            <div class="consent-box">
-
 
869
                <input type="checkbox" id="consentCheck">
-
 
870
                <label for="consentCheck">
-
 
871
                    I acknowledge that the information provided above is accurate and I consent to SmartDukaan's
-
 
872
                    Area Sales Manager visiting my store at the location specified. I understand that this
-
 
873
                    information will be used for onboarding purposes only.
314
            <div id="captureStatus" class="loc-info" style="display:none;"></div>
874
                    <span class="hindi">मैं स्वीकार करता/करती हूँ कि ऊपर दी गई जानकारी सही है, और मैं SmartDukaan के एरिया सेल्स मैनेजर को बताए गए स्थान पर मेरी दुकान पर आने की सहमति देता/देती हूँ। मुझे पता है कि इस जानकारी का उपयोग केवल ऑनबोर्डिंग के लिए किया जाएगा।</span>
-
 
875
                </label>
-
 
876
            </div>
-
 
877
 
-
 
878
            <div id="submitError" class="inline-err"></div>
315
 
879
 
316
            <input type="hidden" id="latitude" value="">
880
            <input type="hidden" id="latitude" value="">
317
            <input type="hidden" id="longitude" value="">
881
            <input type="hidden" id="longitude" value="">
318
            <input type="hidden" id="imageDocumentId" value="0">
882
            <input type="hidden" id="imageDocumentId" value="0">
319
 
883
 
320
            <button class="btn btn-geo mt-2" id="btnSubmit" disabled>
884
            <button class="btn-submit" id="btnSubmit" disabled>Submit Details</button>
321
                Submit Location
-
 
322
            </button>
-
 
323
        </div>
885
        </div>
324
    </div>
886
    </div>
325
</div>
887
</div>
326
 
888
 
-
 
889
<div class="footer-help">
-
 
890
    Need help? Call
-
 
891
    <a href="tel:1800270273" class="footer-call">
-
 
892
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
-
 
893
             stroke-linecap="round" stroke-linejoin="round">
-
 
894
            <path
-
 
895
                    d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z"/>
-
 
896
        </svg>
-
 
897
        1800 270 273
-
 
898
    </a>
-
 
899
</div>
-
 
900
<div class="geo-footer">
-
 
901
    &copy; 2026 SmartDukaan. All rights reserved.<br>
-
 
902
    Transforming mobile retail across India
-
 
903
</div>
-
 
904
 
327
<!-- Hidden canvas used to grab a still frame from the live video stream -->
905
<!-- Hidden canvas used to grab a still frame from the live video stream -->
328
<canvas id="captureCanvas" style="display:none;"></canvas>
906
<canvas id="captureCanvas" style="display:none;"></canvas>
329
 
907
 
330
<input type="hidden" id="leadId" value="$leadId">
908
<input type="hidden" id="leadId" value="$leadId">
331
 
909
 
332
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
910
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
333
<script>
911
<script>
334
    var contextPath = "$rc.contextPath";
912
    var contextPath = "$rc.contextPath";
335
 
-
 
336
    var mediaStream = null; // live camera stream
913
    var mediaStream = null; // live camera stream
337
 
914
 
338
    function showStep(num) {
915
    function markStepsAfterVerify() {
339
        $('.step').removeClass('active');
916
        $('#dot1').removeClass('current').addClass('done');
340
        $('#step' + num).addClass('active');
917
        $('#dot2').addClass('current');
341
        for (var i = 1; i <= 2; i++) {
-
 
342
            var dot = $('#dot' + i);
-
 
343
            dot.removeClass('current done');
918
        $('#stepCaption').text('Step 2 of 2');
344
            if (i < num) dot.addClass('done');
-
 
345
            else if (i === num) dot.addClass('current');
-
 
346
        }
-
 
347
    }
919
    }
348
 
920
 
349
    // STEP 1: Verify Mobile
921
    // ===== STEP 1: Verify mobile against the lead record =====
350
    $('#btnVerifyMobile').click(function () {
922
    $('#btnVerifyMobile').click(function () {
351
        var mobile = $('#mobileNumber').val().trim();
923
        var mobile = $('#mobileNumber').val().trim();
352
        if (!mobile || mobile.length < 10) {
924
        if (!mobile || mobile.length < 10) {
353
            $('#mobileError').text('Please enter a valid 10-digit mobile number').show();
925
            $('#mobileError').text('Please enter a valid 10-digit mobile number').show();
354
            return;
926
            return;
Line 361... Line 933...
361
            url: contextPath + '/lead-geo/verify-mobile',
933
            url: contextPath + '/lead-geo/verify-mobile',
362
            method: 'POST',
934
            method: 'POST',
363
            data: {leadId: $('#leadId').val(), mobileNumber: mobile},
935
            data: {leadId: $('#leadId').val(), mobileNumber: mobile},
364
            success: function (resp) {
936
            success: function (resp) {
365
                if (resp.response && resp.response.verified) {
937
                if (resp.response && resp.response.verified) {
-
 
938
                    // Lock step 1, reveal step 2 on the same page
-
 
939
                    $('#mobileNumber').prop('disabled', true).addClass('field-verified');
366
                    showStep(2);
940
                    btn.hide();
-
 
941
                    $('#mobileVerified').addClass('show');
367
                    startCamera();
942
                    markStepsAfterVerify();
-
 
943
                    $('#step2Section').css('display', 'block').addClass('reveal-soft');
368
                } else {
944
                } else {
369
                    $('#mobileError').text('Verification failed. Please try again.').show();
945
                    $('#mobileError').text('Verification failed. Please try again.').show();
-
 
946
                    btn.prop('disabled', false).text('Verify & Continue');
370
                }
947
                }
371
            },
948
            },
372
            error: function (xhr) {
949
            error: function (xhr) {
373
                var msg = 'Verification failed';
950
                var msg = 'Verification failed';
374
                try {
951
                try {
375
                    msg = JSON.parse(xhr.responseText).message || msg;
952
                    msg = JSON.parse(xhr.responseText).message || msg;
376
                } catch (e) {
953
                } catch (e) {
377
                }
954
                }
378
                $('#mobileError').text(msg).show();
955
                $('#mobileError').text(msg).show();
379
            },
-
 
380
            complete: function () {
-
 
381
                btn.prop('disabled', false).text('Verify Mobile');
956
                btn.prop('disabled', false).text('Verify & Continue');
382
            }
957
            }
383
        });
958
        });
384
    });
959
    });
385
 
960
 
-
 
961
    $('#mobileNumber').keypress(function (e) {
-
 
962
        if (e.which === 13) $('#btnVerifyMobile').click();
-
 
963
    });
-
 
964
 
-
 
965
    // Open the camera only when the user taps "Take Store Photo".
-
 
966
    // The button stays visible and a "Close" button appears so the user
-
 
967
    // stays in control of the camera.
-
 
968
    $('#btnOpenCamera').click(function () {
-
 
969
        $('#cameraStage').show();
-
 
970
        $('#btnCloseCamera').show();
-
 
971
        startCamera();
-
 
972
    });
-
 
973
 
-
 
974
    $('#btnCloseCamera').click(function () {
-
 
975
        stopCamera();
-
 
976
        $('#cameraStage').hide();
-
 
977
        $(this).hide();
-
 
978
    });
-
 
979
 
386
    // STEP 2: Live camera — uses getUserMedia so only an in-page live capture
980
    // ===== STEP 2: Live camera — live capture only (no gallery picker). =====
387
    // is possible (no gallery picker, no file input). Geolocation is grabbed
981
    // Geolocation is grabbed silently at the instant the photo is captured,
388
    // silently when the user taps the capture button, so no separate step.
982
    // so location and photo are a single combined action.
389
    function startCamera() {
983
    function startCamera() {
390
        var overlay = $('#cameraOverlay');
984
        var overlay = $('#cameraOverlay').show().text('Starting camera...');
-
 
985
        $('#photoPreview').hide();
-
 
986
        $('#cameraVideo').show();
391
        if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
987
        if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
392
            overlay.text('Your browser does not support live camera. Please open this link in Chrome/Safari on your phone.');
988
            overlay.text('Your browser does not support live camera. Please open this link in Chrome/Safari on your phone.');
393
            return;
989
            return;
394
        }
990
        }
395
        overlay.text('Starting camera...');
-
 
396
        navigator.mediaDevices.getUserMedia({
991
        navigator.mediaDevices.getUserMedia({
397
            video: {facingMode: {ideal: 'environment'}}, // prefer rear camera
992
            video: {facingMode: {ideal: 'environment'}}, // prefer rear camera
398
            audio: false
993
            audio: false
399
        }).then(function (stream) {
994
        }).then(function (stream) {
400
            mediaStream = stream;
995
            mediaStream = stream;
Line 422... Line 1017...
422
            });
1017
            });
423
            mediaStream = null;
1018
            mediaStream = null;
424
        }
1019
        }
425
    }
1020
    }
426
 
1021
 
427
    // Capture frame + silently grab geolocation
1022
    // Capture frame + silently grab geolocation (single combined action)
428
    $('#btnCapture').click(function () {
1023
    $('#btnCapture').click(function () {
429
        var btn = $(this);
1024
        var btn = $(this);
430
        btn.prop('disabled', true);
1025
        btn.prop('disabled', true);
431
        $('#captureStatus').removeClass('error').text('Capturing photo and your location...').show();
1026
        $('#captureStatus').attr('class', 'capture-status ok').text('Capturing photo and your location...');
432
 
1027
 
433
        var video = document.getElementById('cameraVideo');
1028
        var video = document.getElementById('cameraVideo');
434
        var canvas = document.getElementById('captureCanvas');
1029
        var canvas = document.getElementById('captureCanvas');
435
        canvas.width = video.videoWidth;
1030
        canvas.width = video.videoWidth;
436
        canvas.height = video.videoHeight;
1031
        canvas.height = video.videoHeight;
437
        canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
1032
        canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
438
 
1033
 
439
        // Show the still preview, hide the live video, allow retake
1034
        // Show still preview, hide live video, allow retake
440
        var dataUrl = canvas.toDataURL('image/jpeg', 0.9);
1035
        var dataUrl = canvas.toDataURL('image/jpeg', 0.9);
441
        $('#photoPreview').attr('src', dataUrl).show();
1036
        $('#photoPreview').attr('src', dataUrl).show();
442
        $('#cameraVideo').hide();
1037
        $('#cameraVideo').hide();
443
        $('#btnRetake').show();
1038
        $('#btnRetake').show();
444
 
1039
 
445
        canvas.toBlob(function (blob) {
1040
        canvas.toBlob(function (blob) {
446
            if (!blob) {
1041
            if (!blob) {
447
                btn.prop('disabled', false);
1042
                btn.prop('disabled', false);
448
                $('#captureStatus').addClass('error').text('Could not capture photo. Try again.').show();
1043
                $('#captureStatus').attr('class', 'capture-status err').text('Could not capture photo. Try again.');
449
                return;
1044
                return;
450
            }
1045
            }
451
            // Upload photo + grab geolocation in parallel
1046
            // Upload photo + grab geolocation in parallel
452
            var uploadDone = uploadPhoto(blob);
1047
            var uploadDone = uploadPhoto(blob);
453
            var geoDone = captureGeo();
1048
            var geoDone = captureGeo();
454
            $.when(uploadDone, geoDone).done(function () {
1049
            $.when(uploadDone, geoDone).done(function () {
455
                $('#captureStatus').removeClass('error').text('Photo and location captured.').show();
1050
                $('#captureStatus').attr('class', 'capture-status ok')
456
                $('#btnSubmit').prop('disabled', false);
1051
                        .html('&#10004; Store photo and location captured.');
457
                stopCamera();
1052
                stopCamera();
-
 
1053
                refreshSubmitState();
458
            }).fail(function (msg) {
1054
            }).fail(function (msg) {
459
                btn.prop('disabled', false);
1055
                btn.prop('disabled', false);
460
                $('#captureStatus').addClass('error').text(msg || 'Capture failed. Try again.').show();
1056
                $('#captureStatus').attr('class', 'capture-status err').text(msg || 'Capture failed. Try again.');
461
            });
1057
            });
462
        }, 'image/jpeg', 0.9);
1058
        }, 'image/jpeg', 0.9);
463
    });
1059
    });
464
 
1060
 
465
    $('#btnRetake').click(function () {
1061
    $('#btnRetake').click(function () {
Line 467... Line 1063...
467
        $('#cameraVideo').show();
1063
        $('#cameraVideo').show();
468
        $(this).hide();
1064
        $(this).hide();
469
        $('#imageDocumentId').val('0');
1065
        $('#imageDocumentId').val('0');
470
        $('#latitude').val('');
1066
        $('#latitude').val('');
471
        $('#longitude').val('');
1067
        $('#longitude').val('');
472
        $('#btnSubmit').prop('disabled', true);
1068
        $('#captureStatus').attr('class', 'capture-status').hide();
473
        $('#captureStatus').hide();
1069
        refreshSubmitState();
474
        if (!mediaStream) startCamera();
1070
        if (!mediaStream) startCamera();
475
        else $('#btnCapture').prop('disabled', false);
1071
        else $('#btnCapture').prop('disabled', false);
476
    });
1072
    });
477
 
1073
 
478
    function uploadPhoto(blob) {
1074
    function uploadPhoto(blob) {
Line 522... Line 1118...
522
                {enableHighAccuracy: true, timeout: 15000, maximumAge: 0}
1118
                {enableHighAccuracy: true, timeout: 15000, maximumAge: 0}
523
        );
1119
        );
524
        return d.promise();
1120
        return d.promise();
525
    }
1121
    }
526
 
1122
 
-
 
1123
    // ===== Submit gating: photo + location + consent all required =====
-
 
1124
    function refreshSubmitState() {
-
 
1125
        var hasLocation = $('#latitude').val() !== '' && $('#longitude').val() !== '';
-
 
1126
        var hasImage = $('#imageDocumentId').val() !== '0';
-
 
1127
        var consented = $('#consentCheck').is(':checked');
-
 
1128
        $('#btnSubmit').prop('disabled', !(hasLocation && hasImage && consented));
-
 
1129
    }
-
 
1130
 
-
 
1131
    $('#consentCheck').change(refreshSubmitState);
-
 
1132
 
527
    // SUBMIT
1133
    // ===== Submit =====
528
    $('#btnSubmit').click(function () {
1134
    $('#btnSubmit').click(function () {
529
        var btn = $(this);
1135
        var btn = $(this);
530
        btn.prop('disabled', true).html('<span class="spinner"></span> Submitting...');
1136
        btn.prop('disabled', true).html('<span class="spinner"></span> Submitting...');
-
 
1137
        $('#submitError').hide();
531
 
1138
 
532
        $.ajax({
1139
        $.ajax({
533
            url: contextPath + '/lead-geo/submit',
1140
            url: contextPath + '/lead-geo/submit',
534
            method: 'POST',
1141
            method: 'POST',
535
            data: {
1142
            data: {
Line 538... Line 1145...
538
                latitude: $('#latitude').val(),
1145
                latitude: $('#latitude').val(),
539
                longitude: $('#longitude').val(),
1146
                longitude: $('#longitude').val(),
540
                imageDocumentId: $('#imageDocumentId').val()
1147
                imageDocumentId: $('#imageDocumentId').val()
541
            },
1148
            },
542
            success: function (resp) {
1149
            success: function (resp) {
543
                // Replace body with thank you (SVG check icon — no emoji to avoid charset issues)
1150
                $('.greeting').remove();
544
                $('.geo-body').html(
1151
                $('.geo-card').html(
545
                        '<div class="text-center py-4">' +
1152
                        '<div class="success-wrap">' +
546
                        '<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 24 24"' +
1153
                        '<svg class="success-svg" width="120" height="120" viewBox="0 0 52 52" fill="none">' +
547
                        ' fill="none" stroke="#22c55e" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"' +
1154
                        '<circle class="success-circle" cx="26" cy="26" r="24" fill="none" stroke="#22c55e"' +
548
                        ' style="display:block; margin: 0 auto;">' +
1155
                        ' stroke-width="3" stroke-linecap="round" transform="rotate(-90 26 26)"></circle>' +
549
                        '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline>' +
1156
                        '<path class="success-check" d="M16 27L23 34L37 19" fill="none" stroke="#22c55e" stroke-width="4"' +
-
 
1157
                        ' stroke-linecap="round" stroke-linejoin="round"></path>' +
550
                        '</svg>' +
1158
                        '</svg>' +
-
 
1159
                        '<div>' +
551
                        '<h5 class="mt-3">Thank You!</h5>' +
1160
                        '<h4 class="success-msg">Thank you for verification</h4>' +
552
                        '<p class="text-muted">Your location has been submitted successfully.<br>Our team will review it shortly.</p>' +
1161
                        '<p class="success-msg-hi">सत्यापन के लिए धन्यवाद</p>' +
-
 
1162
                        '</div>' +
553
                        '</div>'
1163
                        '</div>'
554
                );
1164
                );
555
                $('.step-indicator').remove();
-
 
556
                stopCamera();
1165
                stopCamera();
557
            },
1166
            },
558
            error: function () {
1167
            error: function () {
559
                btn.prop('disabled', false).text('Submit Location');
1168
                btn.prop('disabled', false).text('Submit Details');
560
                alert('Submission failed. Please try again.');
1169
                $('#submitError').text('Submission failed. Please try again.').show();
561
            }
1170
            }
562
        });
1171
        });
563
    });
1172
    });
564
 
-
 
565
    // Allow Enter key on mobile input
-
 
566
    $('#mobileNumber').keypress(function (e) {
-
 
567
        if (e.which === 13) $('#btnVerifyMobile').click();
-
 
568
    });
-
 
569
</script>
1173
</script>
570
</body>
1174
</body>
571
</html>
1175
</html>