Rev 33845 | Rev 36024 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
<link href="https://cdn.datatables.net/fixedcolumns/3.3.0/css/fixedColumns.bootstrap.css" rel="stylesheet"/><style>.timeline-table {width: 100%;border-collapse: collapse;font-size: 12px;}.timeline-table thead th {background: #2c3e50;color: #fff;padding: 6px 5px;text-align: center;font-weight: 600;font-size: 11px;border: 1px solid #34495e;white-space: nowrap;}.timeline-table tbody td {padding: 5px 4px;text-align: center;border: 1px solid #e0e0e0;vertical-align: middle;white-space: nowrap;}.timeline-table tbody tr:nth-child(even) {background-color: #f8f9fa;}.timeline-table tbody tr:hover {background-color: #eaf2ff;}.status-badge {display: inline-block;padding: 2px 6px;border-radius: 10px;font-size: 10px;font-weight: 600;text-transform: uppercase;}.status-open {background: #d4edda;color: #155724;}.status-pending {background: #fff3cd;color: #856404;}.status-completed {background: #cce5ff;color: #004085;}.status-hold {background: #f8d7da;color: #721c24;}.status-rejected {background: #e2e3e5;color: #383d41;}.tl-cell {display: inline-flex;align-items: center;gap: 5px;}.tl-dot {height: 8px;width: 8px;border-radius: 50%;display: inline-block;flex-shrink: 0;}.tl-dot.completed {background-color: #28a745;}.tl-dot.delay {background-color: #dc3545;}.tl-dot.wip {background-color: #ffc107;}.tl-dot.not-started {background-color: #adb5bd;}.tl-date {font-size: 10px;color: #555;}.legend {display: inline-flex;gap: 14px;flex-wrap: wrap;margin: 0;}.legend-item {display: flex;align-items: center;gap: 5px;font-size: 11px;color: #555;}.dataTables_wrapper .top-controls {display: flex;align-items: center;justify-content: space-between;flex-wrap: nowrap;padding: 8px 0;}.dataTables_wrapper .top-controls .dataTables_length {flex-shrink: 0;}.dataTables_wrapper .top-controls .timeline-legend-row {flex: 1;display: flex;justify-content: center;}.dataTables_wrapper .top-controls .dataTables_filter {flex-shrink: 0;}.section-header {font-size: 11px;color: #95a5a6;text-transform: uppercase;letter-spacing: 0.5px;padding: 2px 0;}.timeline-table tbody tr {cursor: pointer;}.timeline-table tbody tr.row-selected,.DTFC_LeftBodyWrapper table tbody tr.row-selected {background-color: #d6eaf8 !important;}.ob-stepper {display: flex;align-items: center;justify-content: center;margin: 0 0 20px;padding: 0;}.ob-step {display: flex;align-items: center;}.ob-step-circle {width: 28px;height: 28px;border-radius: 50%;display: flex;align-items: center;justify-content: center;font-size: 11px;font-weight: 700;color: #fff;flex-shrink: 0;}.ob-step-circle.completed {background: #28a745;}.ob-step-circle.inProgress {background: #007bff;}.ob-step-circle.notStarted {background: #adb5bd;}.ob-step-line {width: 50px;height: 3px;}.ob-step-line.completed {background: #28a745;}.ob-step-line.inProgress {background: #007bff;}.ob-step-line.notStarted {background: #dee2e6;}.ob-step-label {font-size: 10px;text-align: center;margin-top: 4px;color: #555;}.ob-step-wrap {display: flex;flex-direction: column;align-items: center;}.ob-kv .row {padding: 4px 0;border-bottom: 1px solid #f0f0f0;}.ob-kv .text-muted {font-size: 12px;}.ob-kv .col-sm-8 {font-size: 12px;}.ob-badge-ok {display: inline-block;padding: 2px 8px;border-radius: 3px;background: #d4edda;color: #155724;font-size: 11px;}.ob-badge-fail {display: inline-block;padding: 2px 8px;border-radius: 3px;background: #f8d7da;color: #721c24;font-size: 11px;}.ob-badge-pending {display: inline-block;padding: 2px 8px;border-radius: 3px;background: #fff3cd;color: #856404;font-size: 11px;}.ob-badge-na {display: inline-block;padding: 2px 8px;border-radius: 3px;background: #e2e3e5;color: #6c757d;font-size: 11px;}a.ob-step-circle {text-decoration: none;color: #fff;cursor: pointer;transition: transform 0.15s, box-shadow 0.15s;}a.ob-step-circle:hover {transform: scale(1.2);box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.3);color: #fff;}.ob-action-bar {padding: 8px 10px;margin-bottom: 12px;background: #f8f9fa;border: 1px solid #e9ecef;border-radius: 4px;display: flex;flex-wrap: wrap;gap: 8px;align-items: center;}.ob-action-bar .btn-warning {font-size: 11px;font-weight: 600;}.ob-action-bar .ob-badge-ok {font-size: 11px;}.loi-header {border-bottom: 2px solid #9b59b6;}.onb-header {border-bottom: 2px solid #2980b9;}.store-header {border-bottom: 2px solid #27ae60;}</style><section class="wrapper"><div class="row"><div class="col-lg-12"><h3 class="page-header" style="color:#2c3e50;"><i class="icon_document_alt"></i> Partner Onboarding Timeline</h3><ol class="breadcrumb"><li><i class="fa fa-home"></i><a href="${rc.contextPath}/dashboard">Home</a></li><li><i class="icon_document_alt"></i>Onboarding Timeline</li></ol></div></div><div class="col-lg-12" style="overflow-x:auto;"><table class="timeline-table" id="storeTimeline"><thead><tr><th rowspan="2">ID</th><th rowspan="2">Partner</th><th rowspan="2">Code</th><th rowspan="2">Status</th><th colspan="4" style="background:#8e44ad;"><span class="section-header">LOI Process</span></th><th colspan="4" style="background:#2471a3;"><span class="section-header">Onboarding</span></th><th colspan="8" style="background:#1e8449;"><span class="section-header">Store Setup</span></th><th colspan="3" style="background:#d35400;"><span class="section-header">Launch</span></th></tr><tr><!-- LOI Process --><th style="background:#9b59b6;">LOI Form</th><th style="background:#9b59b6;">BM Approval</th><th style="background:#9b59b6;">Doc Approval</th><th style="background:#9b59b6;">Payment Approval</th><!-- Onboarding --><th style="background:#2980b9;">Onboarding</th><th style="background:#2980b9;">Verification</th><th style="background:#2980b9;">Store Code</th><th style="background:#2980b9;">Welcome Call</th><!-- Store Setup --><th style="background:#27ae60;">Recce</th><th style="background:#27ae60;">WOD</th><th style="background:#27ae60;">Fin Code</th><th style="background:#27ae60;">WOD-Fin Map</th><th style="background:#27ae60;">Branding</th><th style="background:#27ae60;">Full Stock</th><th style="background:#27ae60;">PO Creation</th><th style="background:#27ae60;">PO Approval</th><!-- Launch --><th style="background:#e67e22;">Billing</th><th style="background:#e67e22;">Inauguration</th><th style="background:#e67e22;">Training</th></tr></thead><tbody>#foreach($st in $storeTimelines)<tr><td><strong>$st.getOnboardingId()</strong></td><td style="text-align:left;max-width:120px;overflow:hidden;text-overflow:ellipsis;"title="$st.getOutletName() ($st.getCity())">$st.getOutletName() ($st.getCity())</td>#if($st.getCode())<td>$st.getCode()</td>#else<td style="color:#ccc;">-</td>#end<td>#set($statusLower = $st.getStatus().toLowerCase())#if($statusLower == "open")<span class="status-badge status-open">$st.getStatus()</span>#elseif($statusLower == "pending")<span class="status-badge status-pending">$st.getStatus()</span>#elseif($statusLower == "completed")<span class="status-badge status-completed">$st.getStatus()</span>#elseif($statusLower == "hold")<span class="status-badge status-hold">$st.getStatus()</span>#else<span class="status-badge status-rejected">$st.getStatus()</span>#end</td>#if($st.getObtm() && $st.getObtm().size() > 0)#foreach($obtm in $st.getObtm())#if($obtm.getStatus().toString() == "COMPLETED")<td><span class="tl-cell"><spanclass="tl-dot completed"></span>#if($obtm.getCompletedTimestamp())<spanclass="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span></td>#elseif($obtm.getStatus().toString() == "DELAY")<td style="background:#fff0f0;"><span class="tl-cell"><spanclass="tl-dot delay"></span>#if($obtm.getCompletedTimestamp())<spanclass="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span></td>#elseif($obtm.getStatus().toString() == "WIP")<td style="background:#fffde7;"><span class="tl-cell"><spanclass="tl-dot wip"></span>#if($obtm.getCompletedTimestamp())<spanclass="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span></td>#elseif($obtm.getStatus().toString() == "NOT_STARTED")<td><span class="tl-cell"><spanclass="tl-dot not-started"></span>#if($obtm.getCompletedTimestamp())<spanclass="tl-date">$obtm.getCompletedTimestamp().format($dateFormatter)</span>#end</span></td>#else<td style="color:#ccc;">-</td>#end#end#else#foreach($i in [1..19])<td style="color:#ccc;">-</td>#end#end</tr>#end</tbody></table></div><div id="timeline-detail-container" style="display:none; margin-top:15px;"><div class="panel panel-default"><div class="panel-heading"><button type="button" class="close" id="closeDetailPanel">×</button><h4 class="panel-title"><span id="detail-partner-name"></span><small id="detail-partner-code" class="text-muted" style="margin-left:10px;"></small></h4></div><div class="panel-body" id="timeline-detail-body"><div class="text-center"><i class="fa fa-spinner fa-spin"></i> Loading...</div></div></div></div></section><script type="text/javascript">$(document).ready(function () {var dtable = $('#storeTimeline').DataTable({scrollX: true,orderCellsTop: true,order: [[0, "desc"]],pageLength: 15,fixedColumns: {leftColumns: 4},dom: '<"top-controls"lf>rt<"bottom"ip>',language: {search: "Search Partners:",lengthMenu: "Show _MENU_ partners"}});// Insert legend between length and filter controlsvar legendHtml = '<div class="timeline-legend-row">' +'<div class="legend">' +'<div class="legend-item"><span class="tl-dot completed"></span> Completed</div>' +'<div class="legend-item"><span class="tl-dot wip"></span> In Progress</div>' +'<div class="legend-item"><span class="tl-dot delay"></span> Delayed</div>' +'<div class="legend-item"><span class="tl-dot not-started"></span> Not Started</div>' +'</div></div>';$('#storeTimeline_wrapper .dataTables_length').after(legendHtml);// Row click -> load detail panel$('#storeTimeline tbody').on('click', 'tr', function () {var $row = $(this);var onboardingId = $.trim($row.find('td:first').text());if (!onboardingId || isNaN(onboardingId)) return;// Highlight selected row (both main table and fixed-column clone)$('#storeTimeline tbody tr, .DTFC_LeftBodyWrapper table tbody tr').removeClass('row-selected');var rowIndex = dtable.row($row).index();$('#storeTimeline tbody tr').eq(rowIndex).addClass('row-selected');$('.DTFC_LeftBodyWrapper table tbody tr').eq(rowIndex).addClass('row-selected');var partnerName = $.trim($row.find('td:eq(1)').text());var partnerCode = $.trim($row.find('td:eq(2)').text());$('#detail-partner-name').text(partnerName);$('#detail-partner-code').text(partnerCode !== '-' ? partnerCode : '');$('#timeline-detail-body').html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> Loading...</div>');$('#storeTimeline_wrapper').hide();$('#timeline-detail-container').show();$.ajax({url: '${rc.contextPath}/partnerTimelineDetail',data: {onboardingId: onboardingId},success: function (html) {$('#timeline-detail-body').html(html);},error: function () {$('#timeline-detail-body').html('<div class="alert alert-danger">Failed to load details.</div>');}});$('html, body').animate({scrollTop: $('#timeline-detail-container').offset().top - 60}, 300);});// Close detail panel$(document).on('click', '#closeDetailPanel', function () {$('#timeline-detail-container').hide();$('#storeTimeline_wrapper').show();dtable.columns.adjust();$('#storeTimeline tbody tr, .DTFC_LeftBodyWrapper table tbody tr').removeClass('row-selected');});});</script>