@@ -273,6 +273,83 @@ foreach ($coc as $row) {
273273 $ code = $ row ->getData ('country_code ' );
274274 $ ordersByCountry [] = array ('country ' => isset ($ cnames [$ code ]) ? $ cnames [$ code ] : $ code , 'count ' => (int )$ row ->getData ('order_count ' ));
275275}
276+
277+ // =====================================================================
278+ // DATA — Learner panel (My Classes)
279+ // =====================================================================
280+ $ _learnerCourses = array ();
281+ $ _learnerCurrent = 0 ; $ _learnerUpcoming = 0 ; $ _learnerPast = 0 ;
282+ $ _loggedInEmail = '' ;
283+ try {
284+ $ _loggedInUser = Mage::getSingleton ('admin/session ' )->getUser ();
285+ $ _loggedInEmail = $ _loggedInUser ? strtolower ($ _loggedInUser ->getEmail ()) : '' ;
286+ } catch (Exception $ e ) {}
287+
288+ if ($ _loggedInEmail ) {
289+ $ _now = date ('Y-m-d ' );
290+ $ _resource = Mage::getSingleton ('core/resource ' );
291+ $ _read = $ _resource ->getConnection ('core_read ' );
292+ $ _orderTable = $ _resource ->getTableName ('sales/order ' );
293+ $ _itemTable = $ _resource ->getTableName ('sales/order_item ' );
294+ $ _prodTable = $ _resource ->getTableName ('catalog/product ' );
295+ $ _eav_dt = $ _resource ->getTableName ('catalog_product_entity_datetime ' );
296+ $ _eav_vc = $ _resource ->getTableName ('catalog_product_entity_varchar ' );
297+
298+ // Get attribute IDs
299+ $ _nameAttrId = (int ) $ _read ->fetchOne ("SELECT attribute_id FROM eav_attribute WHERE attribute_code='name' AND entity_type_id=4 " );
300+ $ _startAttrId = (int ) $ _read ->fetchOne ("SELECT attribute_id FROM eav_attribute WHERE attribute_code='news_from_date' AND entity_type_id=4 " );
301+ $ _endAttrId = (int ) $ _read ->fetchOne ("SELECT attribute_id FROM eav_attribute WHERE attribute_code='news_to_date' AND entity_type_id=4 " );
302+
303+ $ _sql = "SELECT DISTINCT oi.product_id, oi.sku, oi.name as item_name,
304+ o.state as order_state,
305+ pn.value as product_name,
306+ sd.value as start_date,
307+ ed.value as end_date
308+ FROM {$ _orderTable } o
309+ JOIN {$ _itemTable } oi ON o.entity_id = oi.order_id
310+ LEFT JOIN {$ _eav_vc } pn ON oi.product_id = pn.entity_id AND pn.attribute_id = {$ _nameAttrId } AND pn.store_id = 0
311+ LEFT JOIN {$ _eav_dt } sd ON oi.product_id = sd.entity_id AND sd.attribute_id = {$ _startAttrId } AND sd.store_id = 0
312+ LEFT JOIN {$ _eav_dt } ed ON oi.product_id = ed.entity_id AND ed.attribute_id = {$ _endAttrId } AND ed.store_id = 0
313+ WHERE LOWER(o.customer_email) = ?
314+ AND o.state NOT IN ('canceled','closed')
315+ ORDER BY sd.value DESC " ;
316+
317+ $ _rows = $ _read ->fetchAll ($ _sql , array ($ _loggedInEmail ));
318+ foreach ($ _rows as $ _r ) {
319+ $ _start = $ _r ['start_date ' ] ? date ('Y-m-d ' , strtotime ($ _r ['start_date ' ])) : null ;
320+ $ _end = $ _r ['end_date ' ] ? date ('Y-m-d ' , strtotime ($ _r ['end_date ' ])) : null ;
321+
322+ $ _status = 'Confirmed ' ;
323+ if (in_array ($ _r ['order_state ' ], array ('pending ' ,'holded ' ,'new ' ))) $ _status = 'Pending ' ;
324+
325+ $ _timeStatus = 'past ' ;
326+ if ($ _start && $ _end ) {
327+ if ($ _now >= $ _start && $ _now <= $ _end ) { $ _timeStatus = 'current ' ; $ _learnerCurrent ++; }
328+ elseif ($ _now < $ _start ) { $ _timeStatus = 'upcoming ' ; $ _learnerUpcoming ++; }
329+ else { $ _learnerPast ++; }
330+ } else { $ _learnerPast ++; }
331+
332+ // Get product image
333+ $ _imgUrl = '' ;
334+ try {
335+ $ _prod = Mage::getModel ('catalog/product ' )->load ($ _r ['product_id ' ]);
336+ if ($ _prod ->getImage () && $ _prod ->getImage () !== 'no_selection ' ) {
337+ $ _imgUrl = (string ) Mage::helper ('catalog/image ' )->init ($ _prod , 'image ' )->resize (400 , 250 );
338+ }
339+ } catch (Exception $ e ) {}
340+
341+ $ _learnerCourses [] = array (
342+ 'name ' => $ _r ['product_name ' ] ?: $ _r ['item_name ' ],
343+ 'sku ' => $ _r ['sku ' ],
344+ 'start_date ' => $ _start ? date ('j M Y ' , strtotime ($ _start )) : '— ' ,
345+ 'end_date ' => $ _end ? date ('j M Y ' , strtotime ($ _end )) : '— ' ,
346+ 'status ' => $ _status ,
347+ 'time_status ' => $ _timeStatus ,
348+ 'image ' => $ _imgUrl ,
349+ 'order_state ' => $ _r ['order_state ' ],
350+ );
351+ }
352+ }
276353?>
277354
278355<style>
@@ -615,42 +692,143 @@ foreach ($coc as $row) {
615692</div>
616693</div>
617694
618- <?php /* ===== PANEL 4: Learner / Trainer ===== */ ?>
695+ <?php /* ===== PANEL 4: Learner My Classes ===== */ ?>
619696<div id="dash-panel-learner" style="display:none;">
620- <div class="lms-kpi-cards">
621- <div class="lms-kpi-card lms-current"><div class="lms-kpi-value">0</div><div class="lms-kpi-label">Current Classes</div></div>
622- <div class="lms-kpi-card lms-upcoming"><div class="lms-kpi-value">0</div><div class="lms-kpi-label">Upcoming Classes</div></div>
623- <div class="lms-kpi-card lms-past"><div class="lms-kpi-value">0</div><div class="lms-kpi-label">Past Classes</div></div>
624- </div>
625- <div class="lms-tabs">
626- <button class="lms-tab active">All Classes</button>
627- <button class="lms-tab">Current Classes</button>
628- <button class="lms-tab">Upcoming Classes</button>
629- <button class="lms-tab">Past Classes</button>
630- </div>
631- <div class="lms-search">
632- <svg class="lms-search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
633- <input type="text" class="lms-search-input" placeholder="Search by course code, title, or run ID..." />
634- </div>
635- <div class="lms-empty">
636- <div class="lms-empty-icon"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg></div>
637- <div class="lms-empty-title">No enrolled courses found</div>
638- <div class="lms-empty-sub">You haven't enrolled in any courses yet</div>
697+ <style>
698+ .lmc-wrap{padding:0}
699+ .lmc-title{font-size:24px;font-weight:700;color:var(--t1);margin:0 0 20px}
700+ .lmc-kpi{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px}
701+ .lmc-kpi-card{background:#131d2e;border:1px solid var(--b1);border-radius:12px;padding:24px;text-align:center}
702+ .lmc-kpi-num{font-size:36px;font-weight:700;line-height:1.1;margin-bottom:4px}
703+ .lmc-kpi-num.green{color:#22c55e}.lmc-kpi-num.blue{color:#3b82f6}.lmc-kpi-num.gray{color:#94a3b8}
704+ .lmc-kpi-lbl{color:var(--t4);font-size:13px}
705+ .lmc-tabs{display:flex;gap:8px;margin-bottom:16px}
706+ .lmc-tab{padding:8px 18px;border-radius:20px;border:none;font-size:13px;font-weight:500;cursor:pointer;background:#1e293b;color:#94a3b8}
707+ .lmc-tab.active{background:#3b82f6;color:#fff}
708+ .lmc-search{position:relative;margin-bottom:24px}
709+ .lmc-search input{width:100%;padding:10px 16px 10px 40px;background:#1e293b;border:1px solid #334155;border-radius:10px;color:#f1f5f9;font-size:14px;outline:none;box-sizing:border-box}
710+ .lmc-search input:focus{border-color:#60a5fa}
711+ .lmc-search svg{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:#64748b}
712+ .lmc-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
713+ .lmc-card{background:#131d2e;border:1px solid #1e293b;border-radius:14px;overflow:hidden;transition:transform .15s,box-shadow .15s}
714+ .lmc-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,.3)}
715+ .lmc-card-img{width:100%;aspect-ratio:16/9;object-fit:cover;background:#0f172a;display:block}
716+ .lmc-card-img-placeholder{width:100%;aspect-ratio:16/9;background:linear-gradient(135deg,#1e3a5f,#0f172a);display:flex;align-items:center;justify-content:center;color:#334155}
717+ .lmc-card-body{padding:16px}
718+ .lmc-card-title{font-size:15px;font-weight:600;color:#f1f5f9;margin:0 0 10px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
719+ .lmc-card-row{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
720+ .lmc-card-label{color:#64748b;font-size:12px}
721+ .lmc-card-value{color:#cbd5e1;font-size:12px;font-weight:500}
722+ .lmc-badge{padding:2px 10px;border-radius:12px;font-size:11px;font-weight:600}
723+ .lmc-badge-wsq{background:rgba(59,130,246,.15);color:#60a5fa;border:1px solid rgba(59,130,246,.3)}
724+ .lmc-badge-confirmed{background:rgba(34,197,94,.12);color:#22c55e;border:1px solid rgba(34,197,94,.3)}
725+ .lmc-badge-pending{background:rgba(245,158,11,.12);color:#f59e0b;border:1px solid rgba(245,158,11,.3)}
726+ .lmc-badge-cancelled{background:rgba(239,68,68,.12);color:#ef4444;border:1px solid rgba(239,68,68,.3)}
727+ .lmc-card-footer{padding:12px 16px;border-top:1px solid #1e293b;display:flex;justify-content:space-between;align-items:center}
728+ .lmc-card-footer a{color:#06b6d4;font-size:13px;font-weight:600;text-decoration:none}
729+ .lmc-card-footer a:hover{color:#22d3ee}
730+ .lmc-empty{text-align:center;padding:60px 20px;color:#64748b}
731+ .lmc-empty svg{margin-bottom:12px;opacity:.5}
732+ @media(max-width:1024px){.lmc-grid{grid-template-columns:repeat(2,1fr)}}
733+ @media(max-width:640px){.lmc-grid{grid-template-columns:1fr}}
734+ </style>
735+ <div class="lmc-wrap">
736+ <h2 class="lmc-title">My Classes</h2>
737+ <div class="lmc-kpi">
738+ <div class="lmc-kpi-card"><div class="lmc-kpi-num green"><?php echo $ _learnerCurrent ?> </div><div class="lmc-kpi-lbl">Current Classes</div></div>
739+ <div class="lmc-kpi-card"><div class="lmc-kpi-num blue"><?php echo $ _learnerUpcoming ?> </div><div class="lmc-kpi-lbl">Upcoming Classes</div></div>
740+ <div class="lmc-kpi-card"><div class="lmc-kpi-num gray"><?php echo $ _learnerPast ?> </div><div class="lmc-kpi-lbl">Past Classes</div></div>
741+ </div>
742+ <div class="lmc-tabs">
743+ <button class="lmc-tab active" onclick="lmcFilter('all',this)">All Classes</button>
744+ <button class="lmc-tab" onclick="lmcFilter('current',this)">Current Classes</button>
745+ <button class="lmc-tab" onclick="lmcFilter('upcoming',this)">Upcoming Classes</button>
746+ <button class="lmc-tab" onclick="lmcFilter('past',this)">Past Classes</button>
747+ </div>
748+ <div class="lmc-search">
749+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
750+ <input type="text" id="lmc-search" placeholder="Search by course code, title, or run ID..." oninput="lmcSearch(this.value)" />
751+ </div>
752+
753+ <?php if (empty ($ _learnerCourses )): ?>
754+ <div class="lmc-empty">
755+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
756+ <div style="font-size:16px;font-weight:600;color:#94a3b8;margin-bottom:4px;">No enrolled courses found</div>
757+ <div>You haven't enrolled in any courses yet</div>
758+ </div>
759+ <?php else : ?>
760+ <div class="lmc-grid" id="lmc-grid">
761+ <?php foreach ($ _learnerCourses as $ _c ): ?>
762+ <div class="lmc-card" data-time="<?php echo $ _c ['time_status ' ] ?> " data-search="<?php echo strtolower ($ _c ['name ' ] . ' ' . $ _c ['sku ' ]) ?> ">
763+ <?php if ($ _c ['image ' ]): ?>
764+ <img class="lmc-card-img" src="<?php echo $ _c ['image ' ] ?> " alt="" />
765+ <?php else : ?>
766+ <div class="lmc-card-img-placeholder">
767+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
768+ </div>
769+ <?php endif ; ?>
770+ <div class="lmc-card-body">
771+ <div class="lmc-card-title"><?php echo htmlspecialchars ($ _c ['name ' ]) ?> </div>
772+ <div class="lmc-card-row">
773+ <span class="lmc-card-label">Course Code</span>
774+ <span class="lmc-card-value"><?php echo htmlspecialchars ($ _c ['sku ' ]) ?> </span>
775+ </div>
776+ <div class="lmc-card-row">
777+ <span class="lmc-card-label">Course Type</span>
778+ <span class="lmc-badge lmc-badge-wsq">WSQ</span>
779+ </div>
780+ <div class="lmc-card-row">
781+ <span class="lmc-card-label">Class Status</span>
782+ <span class="lmc-badge lmc-badge-<?php echo strtolower ($ _c ['status ' ]) ?> "><?php echo $ _c ['status ' ] ?> </span>
783+ </div>
784+ <div class="lmc-card-row">
785+ <span class="lmc-card-label">Start Date</span>
786+ <span class="lmc-card-value"><?php echo $ _c ['start_date ' ] ?> </span>
787+ </div>
788+ <div class="lmc-card-row">
789+ <span class="lmc-card-label">End Date</span>
790+ <span class="lmc-card-value"><?php echo $ _c ['end_date ' ] ?> </span>
791+ </div>
792+ </div>
793+ <div class="lmc-card-footer">
794+ <a href="#">View Course</a>
795+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#06b6d4" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="m10 8 6 4-6 4z" fill="#06b6d4"/></svg>
796+ </div>
797+ </div>
798+ <?php endforeach ; ?>
799+ </div>
800+ <?php endif ; ?>
639801 </div>
802+ <script type="text/javascript">
803+ function lmcFilter(type, btn) {
804+ var tabs = document.querySelectorAll('.lmc-tab');
805+ for (var i = 0; i < tabs.length; i++) tabs[i].classList.remove('active');
806+ btn.classList.add('active');
807+ var cards = document.querySelectorAll('.lmc-card');
808+ for (var i = 0; i < cards.length; i++) {
809+ cards[i].style.display = (type === 'all' || cards[i].getAttribute('data-time') === type) ? '' : 'none';
810+ }
811+ }
812+ function lmcSearch(q) {
813+ q = q.toLowerCase();
814+ var cards = document.querySelectorAll('.lmc-card');
815+ for (var i = 0; i < cards.length; i++) {
816+ cards[i].style.display = (!q || cards[i].getAttribute('data-search').indexOf(q) >= 0) ? '' : 'none';
817+ }
818+ }
819+ </script>
640820</div>
641821
642822<?php /* ===== Role switcher JS ===== */ ?>
643823<script type="text/javascript">
644824(function(){
645825 function showPanel(role) {
646- // Temporarily show admin dashboard (charts) for all roles
647- // TODO: restore per-role dashboards once role UIs are configured
648826 var panels = {
649827 'Admin': 'dash-panel-superadmin',
650828 'Marketing': 'dash-panel-superadmin',
651829 'Training Provider': 'dash-panel-superadmin',
652- 'Learner': 'dash-panel-superadmin ',
653- 'Trainer': 'dash-panel-superadmin '
830+ 'Learner': 'dash-panel-learner ',
831+ 'Trainer': 'dash-panel-learner '
654832 };
655833 var all = ['dash-panel-admin','dash-panel-superadmin','dash-panel-trainingprovider','dash-panel-learner'];
656834 for (var i=0; i<all.length; i++) {
0 commit comments