From 2f705b1e220e6615adc12037bcb18e10b5cb5ac3 Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 23 Jan 2026 01:35:11 -0600 Subject: [PATCH] V1.0.0.4 - Refactor: Sessions.py --- app.py | 211 +----------------- .../__pycache__/sessions.cpython-313.pyc | Bin 0 -> 10041 bytes blueprints/sessions.py | 211 ++++++++++++++++++ templates/admin_dashboard.html | 26 ++- 4 files changed, 230 insertions(+), 218 deletions(-) create mode 100644 blueprints/__pycache__/sessions.cpython-313.pyc create mode 100644 blueprints/sessions.py diff --git a/app.py b/app.py index 9f7e5d6..fa18fa6 100644 --- a/app.py +++ b/app.py @@ -15,10 +15,12 @@ app = Flask(__name__) from db import query_db, execute_db, get_db from blueprints.data_imports import data_imports_bp from blueprints.users import users_bp +from blueprints.sessions import sessions_bp from utils import login_required, role_required app.register_blueprint(data_imports_bp) app.register_blueprint(users_bp) +app.register_blueprint(sessions_bp) # V1.0: Use environment variable for production, fallback to demo key for development app.secret_key = os.environ.get('SCANLOOK_SECRET_KEY', 'scanlook-demo-key-replace-for-production') @@ -121,215 +123,6 @@ def staff_mode(): return render_template('staff_dashboard.html', sessions=active_sessions, is_admin_mode=True) -# ==================== ROUTES: SESSION MANAGEMENT (ADMIN) ==================== - -@app.route('/session/create', methods=['GET', 'POST']) -@role_required('owner', 'admin') -def create_session(): - """Create new count session""" - if request.method == 'POST': - session_name = request.form.get('session_name', '').strip() - session_type = request.form.get('session_type') - - if not session_name: - flash('Session name is required', 'danger') - return redirect(url_for('create_session')) - - session_id = execute_db(''' - INSERT INTO CountSessions (session_name, session_type, created_by, branch) - VALUES (?, ?, ?, ?) - ''', [session_name, session_type, session['user_id'], 'Main']) - - flash(f'Session "{session_name}" created successfully!', 'success') - return redirect(url_for('session_detail', session_id=session_id)) - - return render_template('create_session.html') - - -@app.route('/session/') -@role_required('owner', 'admin') -def session_detail(session_id): - """Session detail and monitoring page""" - sess = query_db('SELECT * FROM CountSessions WHERE session_id = ?', [session_id], one=True) - - if not sess: - flash('Session not found', 'danger') - return redirect(url_for('dashboard')) - - # Get statistics - stats = query_db(''' - SELECT - COUNT(DISTINCT se.entry_id) FILTER (WHERE se.is_deleted = 0) as total_scans, - COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'match' AND se.duplicate_status = '00' AND se.is_deleted = 0 AND ABS(se.actual_weight - se.master_expected_weight) < 0.01) as matched, - COUNT(DISTINCT se.lot_number) FILTER (WHERE se.duplicate_status IN ('01', '03', '04') AND se.is_deleted = 0) as duplicates, - COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'match' AND se.duplicate_status = '00' AND se.is_deleted = 0 AND ABS(se.actual_weight - se.master_expected_weight) >= 0.01) as weight_discrepancy, - COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'wrong_location' AND se.is_deleted = 0) as wrong_location, - COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'ghost_lot' AND se.is_deleted = 0) as ghost_lots, - COUNT(DISTINCT ml.missing_id) as missing_lots - FROM CountSessions cs - LEFT JOIN ScanEntries se ON cs.session_id = se.session_id - LEFT JOIN MissingLots ml ON cs.session_id = ml.session_id - WHERE cs.session_id = ? - ''', [session_id], one=True) - - # Get location progress - locations = query_db(''' - SELECT lc.*, u.full_name as counter_name - FROM LocationCounts lc - LEFT JOIN Users u ON lc.counted_by = u.user_id - WHERE lc.session_id = ? - ORDER BY lc.status DESC, lc.location_name - ''', [session_id]) - - # Get active counters - active_counters = query_db(''' - SELECT DISTINCT u.full_name, lc.location_name, lc.start_timestamp - FROM LocationCounts lc - JOIN Users u ON lc.counted_by = u.user_id - WHERE lc.session_id = ? AND lc.status = 'in_progress' - ORDER BY lc.start_timestamp DESC - ''', [session_id]) - - return render_template('session_detail.html', - count_session=sess, - stats=stats, - locations=locations, - active_counters=active_counters) - - -@app.route('/session//status-details/') -@role_required('owner', 'admin') -def get_status_details(session_id, status): - """Get detailed breakdown for a specific status""" - - try: - if status == 'match': - # Matched lots (not duplicates) - JOIN with CURRENT for live data - items = query_db(''' - SELECT - se.*, - u.full_name as scanned_by_name, - bic.system_bin as current_system_location, - bic.system_quantity as current_system_weight - FROM ScanEntries se - JOIN Users u ON se.scanned_by = u.user_id - LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number - WHERE se.session_id = ? - AND se.master_status = 'match' - AND se.duplicate_status = '00' - AND se.is_deleted = 0 - ORDER BY se.scan_timestamp DESC - ''', [session_id]) - - elif status == 'duplicates': - # Duplicate lots (grouped by lot number) - JOIN with CURRENT - items = query_db(''' - SELECT - se.lot_number, - se.item, - se.description, - GROUP_CONCAT(DISTINCT se.scanned_location) as scanned_location, - SUM(se.actual_weight) as actual_weight, - se.master_expected_location, - se.master_expected_weight, - GROUP_CONCAT(DISTINCT u.full_name) as scanned_by_name, - MIN(se.scan_timestamp) as scan_timestamp, - bic.system_bin as current_system_location, - bic.system_quantity as current_system_weight - FROM ScanEntries se - JOIN Users u ON se.scanned_by = u.user_id - LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number - WHERE se.session_id = ? - AND se.duplicate_status IN ('01', '03', '04') - AND se.is_deleted = 0 - GROUP BY se.lot_number - ORDER BY se.lot_number - ''', [session_id]) - - elif status == 'wrong_location': - # Wrong location lots - JOIN with CURRENT - items = query_db(''' - SELECT - se.*, - u.full_name as scanned_by_name, - bic.system_bin as current_system_location, - bic.system_quantity as current_system_weight - FROM ScanEntries se - JOIN Users u ON se.scanned_by = u.user_id - LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number - WHERE se.session_id = ? - AND se.master_status = 'wrong_location' - AND se.is_deleted = 0 - ORDER BY se.scan_timestamp DESC - ''', [session_id]) - - elif status == 'weight_discrepancy': - # Weight discrepancies (right location, wrong weight) - JOIN with CURRENT - items = query_db(''' - SELECT - se.*, - u.full_name as scanned_by_name, - bic.system_bin as current_system_location, - bic.system_quantity as current_system_weight - FROM ScanEntries se - JOIN Users u ON se.scanned_by = u.user_id - LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number - WHERE se.session_id = ? - AND se.master_status = 'match' - AND se.duplicate_status = '00' - AND ABS(se.actual_weight - se.master_expected_weight) >= 0.01 - AND se.is_deleted = 0 - ORDER BY ABS(se.actual_weight - se.master_expected_weight) DESC - ''', [session_id]) - - elif status == 'ghost_lot': - # Ghost lots (not in master baseline) - JOIN with CURRENT - items = query_db(''' - SELECT - se.*, - u.full_name as scanned_by_name, - bic.system_bin as current_system_location, - bic.system_quantity as current_system_weight - FROM ScanEntries se - JOIN Users u ON se.scanned_by = u.user_id - LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number - WHERE se.session_id = ? - AND se.master_status = 'ghost_lot' - AND se.is_deleted = 0 - ORDER BY se.scan_timestamp DESC - ''', [session_id]) - - elif status == 'missing': - # Missing lots (in master but not scanned) - items = query_db(''' - SELECT - bim.lot_number, - bim.item, - bim.description, - bim.system_bin, - bim.system_quantity - FROM BaselineInventory_Master bim - WHERE bim.session_id = ? - AND bim.lot_number NOT IN ( - SELECT lot_number - FROM ScanEntries - WHERE session_id = ? AND is_deleted = 0 - ) - ORDER BY bim.system_bin, bim.lot_number - ''', [session_id, session_id]) - else: - return jsonify({'success': False, 'message': 'Invalid status'}) - - return jsonify({ - 'success': True, - 'items': [dict(item) for item in items] if items else [] - }) - - except Exception as e: - print(f"Error in get_status_details: {str(e)}") - return jsonify({'success': False, 'message': f'Error: {str(e)}'}) - - # ==================== ROUTES: COUNTING (STAFF) ==================== @app.route('/count/') diff --git a/blueprints/__pycache__/sessions.cpython-313.pyc b/blueprints/__pycache__/sessions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9aa4057349d3285d87c1a5913b99a3682bde5d9 GIT binary patch literal 10041 zcmeHNU2GKB6}~gO>;2#L4`u-qd~Hz8f?1moLhOL4ZPwrgyu{;8XzgY--W}VM?2mJ2 zHpJUBa{E&9Mx~}j6%Ra;HkEipZ68qblBa6jrGk!V(jOG5Z;Ono{=D?u`QINy5=E8T zFtTT7?mhRMbMKsU?sv}2W-#bOa7DLmWCfCzh5Txcmj%U5gf~ipBr6^wWJMxm zWW`+~x%5(zD6XX(FRdu<*Q8=WTv}7yk|0T9u@Gey4-6t}>FlE76W$UsC0T&$D5C^P zF(;%cPvDhRJo*T!93IjyhcZNvx{v6E)xZs%4iwN<*M94+NGnVjveSoUKprKrviXX%p&un^eZ4fUKzK~go*0Ftg{9G!| z;r%BEvHoZEgXc)#TVhsmp6A6v*{h4h{pCo%!5K@XOa|t-RLbSn`oO2OTidh@^|FG@ zi#hNPpV1;_%U$)8j;+Y~T+~nKLsr@}FH)S~ym`g3EXayWl8LxVJkU+r=!qIU3D6S_ zx`g^f)IsQ@BoTVr4JV2E87!r8I5WDy&84QM7B0*t6Y%K?>kErUOpz9J z^`zM9ni8tlkMsj8DpR@zr*-tmgFN9MzVrQeu5Pcod+$f~{PgV~y?r+_un`%!5xT`z zBQM^KjBiB7tC7jefrjpC^o82Jyz{HSqYqc z(A`@@j=n&x4TXC?7`PeTIB@*dQY9GQbUU`6W-j|4`H(ks#d~$|>YmELSS57w_Rc$= zv-iW1Ywijjx&7M4j+d(8*@|cOfqz$}XSnJ=TyY-W1l>`2ry0VILns}!9?Doo5Ac)> z(Y00s)oKmu3qF9SW|QX|yziAW@!7cPbYd2tz$fVz;3yV^a)-gaP?Yf!cx{&K zfK^evS=fjci#*Bl*T7^=kJJR2x77V7lc|}x{imk6x#=0uK@ws@L58hL%trCl^trkC zEZ%SE5EG?zR>%pmK-CzIVqU^>QRZ`L3Cvs?{PK+Ryd*;?kYrviNx*v`&&!#W0X#l) zimH??t>(lGY=bt6;bF78UM+Rd_yh-WE5>K!5~z1c5SLeEe2DVWMG0@M!sabx^}Z-R zj)!Bzht)Z$!iDUU=94SR=|U;LD3BIQ)ui(D4BkI5e0X3G4-6ks&(94+jS1GzKvlwI z@ISt;V_+%9!fMYUofRbrd#kWptUYOsA7hM7Hgky-3(M(T5ex-T*A~{Hu5CT#SEP$Q2l;X0O`^v?+I%jS7Xd-Sq^LK7vl+L*UoxGoMbOAtz31Xnb2yO%>!Vvy9DG|8 zB=7^AoWU6>W<_Q2I`9tb-sEp_IIn4Q4y5CJt`!GRxdjI;el$uusY)n+VhYm3U@j9o zFo;Vry5p;ugU(z9Mi3z=%rOV3Ge4(4ZxysiIF~WIEyYuSGbCK18UuHY7sV_v=Tb}u zA?7emTcERM0(6?TG-4)aPeD+e_!ecQ2dqEQFLCZ{LNU47zT#kAE3Wz7^qvsg&4lHxLfjcveGr(sd-Dr34jDq*?H zSW;~_)X4(KDqqz|r?P`x^25E}PTx@&M!!B=2gW!u!pyFLz7zWwTkKfQkHGk^HL zCs+yfU4P+@XHdmK*ZtKUgVpe%is#UyHWcixq2P8;Z5Q%)U*3K7xraT$VBdQD5k?(5 zYp89Prxs9qHiK;)k@e|EohSkjtPR`RBa!vwqjnVN0YG(c;NZR9efRbb-iyBQ(CZA0 z){xURO7?<+<-=P7tRc-V4{6IR4IS6+$1?WZkHQuecm<&KJ(zfk(euC%p!2t&3tU7)tO@AY zWN6+3kwGrRg=6+?&FX7Uu?FRF7&cPVuz3fS7%@i034dIGvzQq<^}dV#uuG6wt!D}Kp?R8XsD~TzF9Jt9sO+@z&y<-DhtLsITgX5yZG&9bR`m86 zN^CFUwiTS?&VBmt60?L(IC{|%6YVY!o)%=RSr1^nMMzLz&qApIFebq~mH;}5OJW9V zW~I1Pz{I}`c4P(w)y?3fN4%zX+7J89f%?tY7-fr#H2PC`SpclIxTYncEx9d<5RcaY zN9NOuVnN+BN(AaRa$0XS;z?_fw&8D-_<}6TYb|(a2&PHC8jb5W*e06BkW6=5b6zcX zB4ahIjckIKgq&CqrVDSv#EO8^C$%|ZP@isZz|?}Jz?9C)YG~5QMD|7`)DjHbM5qJt zCd{Sd^sVSk5L*x2w<52H!A(@=CZjK%wJlnEjIm~l&y+3k-S#BNpm>61TC3LDUO)#C zY;DWVtNhCGC4CjUY~#&R$?^Pp_NjV_p6H^bDOQT@%X&%;8p3Yej6H{xndi`Ri97C`{)w z9KU7y+vMSTn0Z1RP%VifaIgW(O`Dk6!^IY4N~k^+ zqxgJAX}7lez|>-M>TIrr@PB`5dS5%!)8JE+n?1hl*^>N=7d-XCTuW{g+|<>2o2X24 z|KftpdVt|zir1`&@!xss1z$TaFrWT^CHA`YDk}f%7d5p-F>k{gTY<~`WAXyZ;faX> zC9)FPt;OgxMz-4IqH0pwoH3qP-?czym~R@PrGu(`?X?vYo&g(LwNJxiX@H_+7OcWE zN&5B}?{8vC#~7tu^NmS!hDphq(&(1VjHFflxI))ZEmJ-rWLa;XsVsV!L9AiP8|GH0 z#Z);2**BkqmC?!{QI_;T?S_7oAxZE)Z_a3N(fm*Yb~XzoCt$!!@Gs3mm2Vx@eBWbS3Fe-^l|LzCdcGpm7PkU`Q>U!qN?#*XH z-M#O|YZ!&LU3t42+^hC?L;nG-|6sS;AA|l%WS|-xPzi!SuuCK8391BpRDy%m;6arj z3gZ9UgZm67RiXP|vsa({$!qVYe%F2EcK<_{V~1}OIokd37ToLq=U=*y z`~zyZf$mMj!ivyJZ~LJSGB^8vv2^2NCG`9q&$0XA?bnaqy!0FC_TgW@d26L|^z=sL zOf@|Hd(ZU0Yc4wKU()liwm+C~O^l%rJstGCvwLF1_hHOOuSeAOG2bbN<0H@T z{ecNxzI4@HW$;6`lX-U2f!IEJI$3kVEtnA-G{(iM%#fWt#*CWe!!;+|))pBX{Zy4X XZKwYx%On_c#2MzHQ*9x8LH+*#!e$0) literal 0 HcmV?d00001 diff --git a/blueprints/sessions.py b/blueprints/sessions.py new file mode 100644 index 0000000..6e6a7c6 --- /dev/null +++ b/blueprints/sessions.py @@ -0,0 +1,211 @@ +from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, session +from db import query_db, execute_db +from utils import role_required + +sessions_bp = Blueprint('sessions', __name__) + +@sessions_bp.route('/session/create', methods=['GET', 'POST']) +@role_required('owner', 'admin') +def create_session(): + """Create new count session""" + if request.method == 'POST': + session_name = request.form.get('session_name', '').strip() + session_type = request.form.get('session_type') + + if not session_name: + flash('Session name is required', 'danger') + return redirect(url_for('sessions.create_session')) + + session_id = execute_db(''' + INSERT INTO CountSessions (session_name, session_type, created_by, branch) + VALUES (?, ?, ?, ?) + ''', [session_name, session_type, session['user_id'], 'Main']) + + flash(f'Session "{session_name}" created successfully!', 'success') + return redirect(url_for('sessions.session_detail', session_id=session_id)) + + return render_template('create_session.html') + + +@sessions_bp.route('/session/') +@role_required('owner', 'admin') +def session_detail(session_id): + """Session detail and monitoring page""" + sess = query_db('SELECT * FROM CountSessions WHERE session_id = ?', [session_id], one=True) + + if not sess: + flash('Session not found', 'danger') + return redirect(url_for('dashboard')) + + # Get statistics + stats = query_db(''' + SELECT + COUNT(DISTINCT se.entry_id) FILTER (WHERE se.is_deleted = 0) as total_scans, + COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'match' AND se.duplicate_status = '00' AND se.is_deleted = 0 AND ABS(se.actual_weight - se.master_expected_weight) < 0.01) as matched, + COUNT(DISTINCT se.lot_number) FILTER (WHERE se.duplicate_status IN ('01', '03', '04') AND se.is_deleted = 0) as duplicates, + COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'match' AND se.duplicate_status = '00' AND se.is_deleted = 0 AND ABS(se.actual_weight - se.master_expected_weight) >= 0.01) as weight_discrepancy, + COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'wrong_location' AND se.is_deleted = 0) as wrong_location, + COUNT(DISTINCT se.entry_id) FILTER (WHERE se.master_status = 'ghost_lot' AND se.is_deleted = 0) as ghost_lots, + COUNT(DISTINCT ml.missing_id) as missing_lots + FROM CountSessions cs + LEFT JOIN ScanEntries se ON cs.session_id = se.session_id + LEFT JOIN MissingLots ml ON cs.session_id = ml.session_id + WHERE cs.session_id = ? + ''', [session_id], one=True) + + # Get location progress + locations = query_db(''' + SELECT lc.*, u.full_name as counter_name + FROM LocationCounts lc + LEFT JOIN Users u ON lc.counted_by = u.user_id + WHERE lc.session_id = ? + ORDER BY lc.status DESC, lc.location_name + ''', [session_id]) + + # Get active counters + active_counters = query_db(''' + SELECT DISTINCT u.full_name, lc.location_name, lc.start_timestamp + FROM LocationCounts lc + JOIN Users u ON lc.counted_by = u.user_id + WHERE lc.session_id = ? AND lc.status = 'in_progress' + ORDER BY lc.start_timestamp DESC + ''', [session_id]) + + return render_template('session_detail.html', + count_session=sess, + stats=stats, + locations=locations, + active_counters=active_counters) + + +@sessions_bp.route('/session//status-details/') +@role_required('owner', 'admin') +def get_status_details(session_id, status): + """Get detailed breakdown for a specific status""" + + try: + if status == 'match': + # Matched lots (not duplicates) - JOIN with CURRENT for live data + items = query_db(''' + SELECT + se.*, + u.full_name as scanned_by_name, + bic.system_bin as current_system_location, + bic.system_quantity as current_system_weight + FROM ScanEntries se + JOIN Users u ON se.scanned_by = u.user_id + LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number + WHERE se.session_id = ? + AND se.master_status = 'match' + AND se.duplicate_status = '00' + AND se.is_deleted = 0 + ORDER BY se.scan_timestamp DESC + ''', [session_id]) + + elif status == 'duplicates': + # Duplicate lots (grouped by lot number) - JOIN with CURRENT + items = query_db(''' + SELECT + se.lot_number, + se.item, + se.description, + GROUP_CONCAT(DISTINCT se.scanned_location) as scanned_location, + SUM(se.actual_weight) as actual_weight, + se.master_expected_location, + se.master_expected_weight, + GROUP_CONCAT(DISTINCT u.full_name) as scanned_by_name, + MIN(se.scan_timestamp) as scan_timestamp, + bic.system_bin as current_system_location, + bic.system_quantity as current_system_weight + FROM ScanEntries se + JOIN Users u ON se.scanned_by = u.user_id + LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number + WHERE se.session_id = ? + AND se.duplicate_status IN ('01', '03', '04') + AND se.is_deleted = 0 + GROUP BY se.lot_number + ORDER BY se.lot_number + ''', [session_id]) + + elif status == 'wrong_location': + # Wrong location lots - JOIN with CURRENT + items = query_db(''' + SELECT + se.*, + u.full_name as scanned_by_name, + bic.system_bin as current_system_location, + bic.system_quantity as current_system_weight + FROM ScanEntries se + JOIN Users u ON se.scanned_by = u.user_id + LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number + WHERE se.session_id = ? + AND se.master_status = 'wrong_location' + AND se.is_deleted = 0 + ORDER BY se.scan_timestamp DESC + ''', [session_id]) + + elif status == 'weight_discrepancy': + # Weight discrepancies (right location, wrong weight) - JOIN with CURRENT + items = query_db(''' + SELECT + se.*, + u.full_name as scanned_by_name, + bic.system_bin as current_system_location, + bic.system_quantity as current_system_weight + FROM ScanEntries se + JOIN Users u ON se.scanned_by = u.user_id + LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number + WHERE se.session_id = ? + AND se.master_status = 'match' + AND se.duplicate_status = '00' + AND ABS(se.actual_weight - se.master_expected_weight) >= 0.01 + AND se.is_deleted = 0 + ORDER BY ABS(se.actual_weight - se.master_expected_weight) DESC + ''', [session_id]) + + elif status == 'ghost_lot': + # Ghost lots (not in master baseline) - JOIN with CURRENT + items = query_db(''' + SELECT + se.*, + u.full_name as scanned_by_name, + bic.system_bin as current_system_location, + bic.system_quantity as current_system_weight + FROM ScanEntries se + JOIN Users u ON se.scanned_by = u.user_id + LEFT JOIN BaselineInventory_Current bic ON se.lot_number = bic.lot_number + WHERE se.session_id = ? + AND se.master_status = 'ghost_lot' + AND se.is_deleted = 0 + ORDER BY se.scan_timestamp DESC + ''', [session_id]) + + elif status == 'missing': + # Missing lots (in master but not scanned) + items = query_db(''' + SELECT + bim.lot_number, + bim.item, + bim.description, + bim.system_bin, + bim.system_quantity + FROM BaselineInventory_Master bim + WHERE bim.session_id = ? + AND bim.lot_number NOT IN ( + SELECT lot_number + FROM ScanEntries + WHERE session_id = ? AND is_deleted = 0 + ) + ORDER BY bim.system_bin, bim.lot_number + ''', [session_id, session_id]) + else: + return jsonify({'success': False, 'message': 'Invalid status'}) + + return jsonify({ + 'success': True, + 'items': [dict(item) for item in items] if items else [] + }) + + except Exception as e: + print(f"Error in get_status_details: {str(e)}") + return jsonify({'success': False, 'message': f'Error: {str(e)}'}) \ No newline at end of file diff --git a/templates/admin_dashboard.html b/templates/admin_dashboard.html index 4a38ea6..e4a6585 100644 --- a/templates/admin_dashboard.html +++ b/templates/admin_dashboard.html @@ -6,17 +6,25 @@
- - + +
+ +

Admin Dashboard

- + + New Session
@@ -59,7 +67,7 @@
@@ -71,7 +79,7 @@
📋

No Active Sessions

Create a new count session to get started

- + Create First Session