Subversion Repositories SmartDukaan

Rev

Rev 33845 | Rev 36024 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
29061 tejbeer 1
<link href="https://cdn.datatables.net/fixedcolumns/3.3.0/css/fixedColumns.bootstrap.css" rel="stylesheet"/>
2
<style>
35971 aman 3
	.timeline-table {
4
		width: 100%;
5
		border-collapse: collapse;
6
		font-size: 12px;
7
	}
8
 
9
	.timeline-table thead th {
10
		background: #2c3e50;
11
		color: #fff;
12
		padding: 6px 5px;
13
		text-align: center;
14
		font-weight: 600;
15
		font-size: 11px;
16
		border: 1px solid #34495e;
17
		white-space: nowrap;
18
	}
19
 
20
	.timeline-table tbody td {
21
		padding: 5px 4px;
22
		text-align: center;
23
		border: 1px solid #e0e0e0;
24
		vertical-align: middle;
25
		white-space: nowrap;
26
	}
27
 
28
	.timeline-table tbody tr:nth-child(even) {
29
		background-color: #f8f9fa;
30
	}
31
 
32
	.timeline-table tbody tr:hover {
33
		background-color: #eaf2ff;
34
	}
35
 
36
	.status-badge {
37
		display: inline-block;
38
		padding: 2px 6px;
39
		border-radius: 10px;
40
		font-size: 10px;
41
		font-weight: 600;
42
		text-transform: uppercase;
43
	}
44
 
45
	.status-open {
46
		background: #d4edda;
47
		color: #155724;
48
	}
49
 
50
	.status-pending {
51
		background: #fff3cd;
52
		color: #856404;
53
	}
54
 
55
	.status-completed {
56
		background: #cce5ff;
57
		color: #004085;
58
	}
59
 
60
	.status-hold {
61
		background: #f8d7da;
62
		color: #721c24;
63
	}
64
 
65
	.status-rejected {
66
		background: #e2e3e5;
67
		color: #383d41;
68
	}
69
 
70
	.tl-cell {
71
		display: inline-flex;
72
		align-items: center;
73
		gap: 5px;
74
	}
75
 
76
	.tl-dot {
77
		height: 8px;
78
		width: 8px;
79
		border-radius: 50%;
80
		display: inline-block;
81
		flex-shrink: 0;
82
	}
83
 
84
	.tl-dot.completed {
85
		background-color: #28a745;
86
	}
87
 
88
	.tl-dot.delay {
89
		background-color: #dc3545;
90
	}
91
 
92
	.tl-dot.wip {
93
		background-color: #ffc107;
94
	}
95
 
96
	.tl-dot.not-started {
97
		background-color: #adb5bd;
98
	}
99
 
100
	.tl-date {
101
		font-size: 10px;
102
		color: #555;
103
	}
104
 
105
	.legend {
106
		display: inline-flex;
107
		gap: 14px;
108
		flex-wrap: wrap;
109
		margin: 0;
110
	}
111
 
112
	.legend-item {
113
		display: flex;
114
		align-items: center;
115
		gap: 5px;
116
		font-size: 11px;
117
		color: #555;
118
	}
119
 
120
	.dataTables_wrapper .top-controls {
121
		display: flex;
122
		align-items: center;
123
		justify-content: space-between;
124
		flex-wrap: nowrap;
125
		padding: 8px 0;
126
	}
127
 
128
	.dataTables_wrapper .top-controls .dataTables_length {
129
		flex-shrink: 0;
130
	}
131
 
132
	.dataTables_wrapper .top-controls .timeline-legend-row {
133
		flex: 1;
134
		display: flex;
135
		justify-content: center;
136
	}
137
 
138
	.dataTables_wrapper .top-controls .dataTables_filter {
139
		flex-shrink: 0;
29061 tejbeer 140
}
141
 
35971 aman 142
	.section-header {
143
		font-size: 11px;
144
		color: #95a5a6;
145
		text-transform: uppercase;
146
		letter-spacing: 0.5px;
147
		padding: 2px 0;
148
	}
29061 tejbeer 149
 
35971 aman 150
	.timeline-table tbody tr {
151
		cursor: pointer;
152
	}
33845 tejus.loha 153
 
35971 aman 154
	.timeline-table tbody tr.row-selected,
155
	.DTFC_LeftBodyWrapper table tbody tr.row-selected {
156
		background-color: #d6eaf8 !important;
157
	}
32550 amit.gupta 158
 
35971 aman 159
	.ob-stepper {
160
		display: flex;
161
		align-items: center;
162
		justify-content: center;
163
		margin: 0 0 20px;
164
		padding: 0;
165
	}
32550 amit.gupta 166
 
35971 aman 167
	.ob-step {
168
		display: flex;
169
		align-items: center;
170
	}
29061 tejbeer 171
 
35971 aman 172
	.ob-step-circle {
173
		width: 28px;
174
		height: 28px;
175
		border-radius: 50%;
176
		display: flex;
177
		align-items: center;
178
		justify-content: center;
179
		font-size: 11px;
180
		font-weight: 700;
181
		color: #fff;
182
		flex-shrink: 0;
183
	}
184
 
185
	.ob-step-circle.completed {
186
		background: #28a745;
187
	}
188
 
189
	.ob-step-circle.inProgress {
190
		background: #007bff;
191
	}
192
 
193
	.ob-step-circle.notStarted {
194
		background: #adb5bd;
195
	}
196
 
197
	.ob-step-line {
198
		width: 50px;
199
		height: 3px;
200
	}
201
 
202
	.ob-step-line.completed {
203
		background: #28a745;
204
	}
205
 
206
	.ob-step-line.inProgress {
207
		background: #007bff;
208
	}
209
 
210
	.ob-step-line.notStarted {
211
		background: #dee2e6;
212
	}
213
 
214
	.ob-step-label {
215
		font-size: 10px;
216
		text-align: center;
217
		margin-top: 4px;
218
		color: #555;
219
	}
220
 
221
	.ob-step-wrap {
222
		display: flex;
223
		flex-direction: column;
224
		align-items: center;
225
	}
226
 
227
	.ob-kv .row {
228
		padding: 4px 0;
229
		border-bottom: 1px solid #f0f0f0;
230
	}
231
 
232
	.ob-kv .text-muted {
233
		font-size: 12px;
234
	}
235
 
236
	.ob-kv .col-sm-8 {
237
		font-size: 12px;
238
	}
239
 
240
	.ob-badge-ok {
241
		display: inline-block;
242
		padding: 2px 8px;
243
		border-radius: 3px;
244
		background: #d4edda;
245
		color: #155724;
246
		font-size: 11px;
247
	}
248
 
249
	.ob-badge-fail {
250
		display: inline-block;
251
		padding: 2px 8px;
252
		border-radius: 3px;
253
		background: #f8d7da;
254
		color: #721c24;
255
		font-size: 11px;
256
	}
257
 
258
	.ob-badge-pending {
259
		display: inline-block;
260
		padding: 2px 8px;
261
		border-radius: 3px;
262
		background: #fff3cd;
263
		color: #856404;
264
		font-size: 11px;
265
	}
266
 
267
	.ob-badge-na {
268
		display: inline-block;
269
		padding: 2px 8px;
270
		border-radius: 3px;
271
		background: #e2e3e5;
272
		color: #6c757d;
273
		font-size: 11px;
274
	}
275
 
276
	a.ob-step-circle {
277
		text-decoration: none;
278
		color: #fff;
279
		cursor: pointer;
280
		transition: transform 0.15s, box-shadow 0.15s;
281
	}
282
 
283
	a.ob-step-circle:hover {
284
		transform: scale(1.2);
285
		box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.3);
286
		color: #fff;
287
	}
288
 
289
	.ob-action-bar {
290
		padding: 8px 10px;
291
		margin-bottom: 12px;
292
		background: #f8f9fa;
293
		border: 1px solid #e9ecef;
294
		border-radius: 4px;
295
		display: flex;
296
		flex-wrap: wrap;
297
		gap: 8px;
298
		align-items: center;
299
	}
300
 
301
	.ob-action-bar .btn-warning {
302
		font-size: 11px;
303
		font-weight: 600;
304
	}
305
 
306
	.ob-action-bar .ob-badge-ok {
307
		font-size: 11px;
308
	}
309
 
310
	.loi-header {
311
		border-bottom: 2px solid #9b59b6;
312
	}
313
 
314
	.onb-header {
315
		border-bottom: 2px solid #2980b9;
316
	}
317
 
318
	.store-header {
319
		border-bottom: 2px solid #27ae60;
320
	}
321
</style>
322
 
323
<section class="wrapper">
324
	<div class="row">
325
		<div class="col-lg-12">
326
			<h3 class="page-header" style="color:#2c3e50;">
327
				<i class="icon_document_alt"></i> Partner Onboarding Timeline
328
			</h3>
329
			<ol class="breadcrumb">
330
				<li><i class="fa fa-home"></i><a href="${rc.contextPath}/dashboard">Home</a></li>
331
				<li><i class="icon_document_alt"></i>Onboarding Timeline</li>
332
			</ol>
333
		</div>
334
	</div>
335
 
336
	<div class="col-lg-12" style="overflow-x:auto;">
337
		<table class="timeline-table" id="storeTimeline">
338
			<thead>
339
			<tr>
340
				<th rowspan="2">ID</th>
341
				<th rowspan="2">Partner</th>
342
				<th rowspan="2">Code</th>
343
				<th rowspan="2">Status</th>
344
				<th colspan="4" style="background:#8e44ad;"><span class="section-header">LOI Process</span></th>
345
				<th colspan="4" style="background:#2471a3;"><span class="section-header">Onboarding</span></th>
346
				<th colspan="8" style="background:#1e8449;"><span class="section-header">Store Setup</span></th>
347
				<th colspan="3" style="background:#d35400;"><span class="section-header">Launch</span></th>
348
			</tr>
349
			<tr>
350
				<!-- LOI Process -->
351
				<th style="background:#9b59b6;">LOI Form</th>
352
				<th style="background:#9b59b6;">BM Approval</th>
353
				<th style="background:#9b59b6;">Doc Approval</th>
354
				<th style="background:#9b59b6;">Payment Approval</th>
355
				<!-- Onboarding -->
356
				<th style="background:#2980b9;">Onboarding</th>
357
				<th style="background:#2980b9;">Verification</th>
358
				<th style="background:#2980b9;">Store Code</th>
359
				<th style="background:#2980b9;">Welcome Call</th>
360
				<!-- Store Setup -->
361
				<th style="background:#27ae60;">Recce</th>
362
				<th style="background:#27ae60;">WOD</th>
363
				<th style="background:#27ae60;">Fin Code</th>
364
				<th style="background:#27ae60;">WOD-Fin Map</th>
365
				<th style="background:#27ae60;">Branding</th>
366
				<th style="background:#27ae60;">Full Stock</th>
367
				<th style="background:#27ae60;">PO Creation</th>
368
				<th style="background:#27ae60;">PO Approval</th>
369
				<!-- Launch -->
370
				<th style="background:#e67e22;">Billing</th>
371
				<th style="background:#e67e22;">Inauguration</th>
372
				<th style="background:#e67e22;">Training</th>
373
			</tr>
374
			</thead>
375
			<tbody>
376
				#foreach($st in $storeTimelines)
377
				<tr>
378
					<td><strong>$st.getOnboardingId()</strong></td>
379
					<td style="text-align:left;max-width:120px;overflow:hidden;text-overflow:ellipsis;"
380
						title="$st.getOutletName() ($st.getCity())">$st.getOutletName() ($st.getCity())
381
					</td>
382
					#if($st.getCode())
383
						<td>$st.getCode()</td>
384
					#else
385
						<td style="color:#ccc;">-</td>
386
					#end
387
					<td>
388
						#set($statusLower = $st.getStatus().toLowerCase())
389
						#if($statusLower == "open")
390
							<span class="status-badge status-open">$st.getStatus()</span>
391
						#elseif($statusLower == "pending")
392
							<span class="status-badge status-pending">$st.getStatus()</span>
393
						#elseif($statusLower == "completed")
394
							<span class="status-badge status-completed">$st.getStatus()</span>
395
						#elseif($statusLower == "hold")
396
							<span class="status-badge status-hold">$st.getStatus()</span>
397
						#else
398
							<span class="status-badge status-rejected">$st.getStatus()</span>
399
						#end
400
					</td>
401
					#if($st.getObtm() && $st.getObtm().size() > 0)
402
						#foreach($obtm in $st.getObtm())
403
							#if($obtm.getStatus().toString() == "COMPLETED")
404
								<td><span class="tl-cell"><span
405
										class="tl-dot completed"></span>#if($obtm.getCompletedTimestamp())<span
406
										class="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span>
407
								</td>
408
							#elseif($obtm.getStatus().toString() == "DELAY")
409
								<td style="background:#fff0f0;"><span class="tl-cell"><span
410
										class="tl-dot delay"></span>#if($obtm.getCompletedTimestamp())<span
411
										class="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span>
412
								</td>
413
							#elseif($obtm.getStatus().toString() == "WIP")
414
								<td style="background:#fffde7;"><span class="tl-cell"><span
415
										class="tl-dot wip"></span>#if($obtm.getCompletedTimestamp())<span
416
										class="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span>
417
								</td>
418
							#elseif($obtm.getStatus().toString() == "NOT_STARTED")
419
								<td><span class="tl-cell"><span
420
										class="tl-dot not-started"></span>#if($obtm.getCompletedTimestamp())<span
421
										class="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span>
422
								</td>
423
							#else
424
								<td style="color:#ccc;">-</td>
425
							#end
426
						#end
427
					#else
428
						#foreach($i in [1..19])
429
							<td style="color:#ccc;">-</td>
430
						#end
431
					#end
432
				</tr>
433
				#end
434
			</tbody>
435
		</table>
436
	</div>
437
 
438
	<div id="timeline-detail-container" style="display:none; margin-top:15px;">
439
		<div class="panel panel-default">
440
			<div class="panel-heading">
441
				<button type="button" class="close" id="closeDetailPanel">&times;</button>
442
				<h4 class="panel-title">
443
					<span id="detail-partner-name"></span>
444
					<small id="detail-partner-code" class="text-muted" style="margin-left:10px;"></small>
445
				</h4>
446
			</div>
447
			<div class="panel-body" id="timeline-detail-body">
448
				<div class="text-center"><i class="fa fa-spinner fa-spin"></i> Loading...</div>
449
			</div>
450
		</div>
451
	</div>
29061 tejbeer 452
</section>
453
 
454
<script type="text/javascript">
35971 aman 455
	$(document).ready(function () {
456
		var dtable = $('#storeTimeline').DataTable({
457
			scrollX: true,
458
			orderCellsTop: true,
459
			order: [[0, "desc"]],
460
			pageLength: 15,
461
			fixedColumns: {
462
				leftColumns: 4
463
			},
464
			dom: '<"top-controls"lf>rt<"bottom"ip>',
465
			language: {
466
				search: "Search Partners:",
467
				lengthMenu: "Show _MENU_ partners"
468
			}
469
		});
29061 tejbeer 470
 
35971 aman 471
		// Insert legend between length and filter controls
472
		var legendHtml = '<div class="timeline-legend-row">' +
473
				'<div class="legend">' +
474
				'<div class="legend-item"><span class="tl-dot completed"></span> Completed</div>' +
475
				'<div class="legend-item"><span class="tl-dot wip"></span> In Progress</div>' +
476
				'<div class="legend-item"><span class="tl-dot delay"></span> Delayed</div>' +
477
				'<div class="legend-item"><span class="tl-dot not-started"></span> Not Started</div>' +
478
				'</div></div>';
479
		$('#storeTimeline_wrapper .dataTables_length').after(legendHtml);
29061 tejbeer 480
 
35971 aman 481
		// Row click -> load detail panel
482
		$('#storeTimeline tbody').on('click', 'tr', function () {
483
			var $row = $(this);
484
			var onboardingId = $.trim($row.find('td:first').text());
485
			if (!onboardingId || isNaN(onboardingId)) return;
486
 
487
			// Highlight selected row (both main table and fixed-column clone)
488
			$('#storeTimeline tbody tr, .DTFC_LeftBodyWrapper table tbody tr').removeClass('row-selected');
489
			var rowIndex = dtable.row($row).index();
490
			$('#storeTimeline tbody tr').eq(rowIndex).addClass('row-selected');
491
			$('.DTFC_LeftBodyWrapper table tbody tr').eq(rowIndex).addClass('row-selected');
492
 
493
			var partnerName = $.trim($row.find('td:eq(1)').text());
494
			var partnerCode = $.trim($row.find('td:eq(2)').text());
495
 
496
			$('#detail-partner-name').text(partnerName);
497
			$('#detail-partner-code').text(partnerCode !== '-' ? partnerCode : '');
498
			$('#timeline-detail-body').html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> Loading...</div>');
499
			$('#storeTimeline_wrapper').hide();
500
			$('#timeline-detail-container').show();
501
 
502
			$.ajax({
503
				url: '${rc.contextPath}/partnerTimelineDetail',
504
				data: {onboardingId: onboardingId},
505
				success: function (html) {
506
					$('#timeline-detail-body').html(html);
507
				},
508
				error: function () {
509
					$('#timeline-detail-body').html('<div class="alert alert-danger">Failed to load details.</div>');
510
				}
511
			});
512
 
513
			$('html, body').animate({scrollTop: $('#timeline-detail-container').offset().top - 60}, 300);
514
		});
515
 
516
		// Close detail panel
517
		$(document).on('click', '#closeDetailPanel', function () {
518
			$('#timeline-detail-container').hide();
519
			$('#storeTimeline_wrapper').show();
520
			dtable.columns.adjust();
521
			$('#storeTimeline tbody tr, .DTFC_LeftBodyWrapper table tbody tr').removeClass('row-selected');
522
		});
523
	});
524
</script>