v0.12.0 - Add modular system architecture with user-based module access
- Add Modules and UserModules database tables - Create home page with module selection grid - Implement per-user module assignment in user management - Add route guards for module access control - Refactor navigation: login -> home -> modules, admin console via button - Add Font Awesome icons
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -10,6 +10,32 @@ def get_active_session(session_id):
|
||||
return None
|
||||
return sess
|
||||
|
||||
@counting_bp.route('/counts')
|
||||
@login_required
|
||||
def index():
|
||||
"""Counts module landing - show active sessions"""
|
||||
# Check if user has access to this module
|
||||
user_id = session.get('user_id')
|
||||
has_access = query_db('''
|
||||
SELECT 1 FROM UserModules um
|
||||
JOIN Modules m ON um.module_id = m.module_id
|
||||
WHERE um.user_id = ? AND m.module_key = 'counting' AND m.is_active = 1
|
||||
''', [user_id], one=True)
|
||||
|
||||
if not has_access:
|
||||
flash('You do not have access to this module', 'danger')
|
||||
return redirect(url_for('home'))
|
||||
|
||||
active_sessions = query_db('''
|
||||
SELECT session_id, session_name, session_type, created_timestamp
|
||||
FROM CountSessions
|
||||
WHERE status = 'active'
|
||||
ORDER BY created_timestamp DESC
|
||||
''')
|
||||
|
||||
return render_template('staff_dashboard.html', sessions=active_sessions)
|
||||
|
||||
|
||||
@counting_bp.route('/count/<int:session_id>')
|
||||
@login_required
|
||||
def count_session(session_id):
|
||||
@@ -19,7 +45,7 @@ def count_session(session_id):
|
||||
|
||||
if not sess:
|
||||
flash('Session not found or not active', 'danger')
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('counting.index'))
|
||||
|
||||
# Redirect to my_counts page (staff can manage multiple bins)
|
||||
return redirect(url_for('counting.my_counts', session_id=session_id))
|
||||
@@ -33,11 +59,11 @@ def my_counts(session_id):
|
||||
|
||||
if not sess:
|
||||
flash('Session not found', 'danger')
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('counting.index'))
|
||||
|
||||
if sess['status'] == 'archived':
|
||||
flash('This session has been archived', 'warning')
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('counting.index'))
|
||||
|
||||
# Get this user's active bins
|
||||
active_bins = query_db('''
|
||||
@@ -78,7 +104,7 @@ def start_bin_count(session_id):
|
||||
sess = get_active_session(session_id)
|
||||
if not sess:
|
||||
flash('Session not found or archived', 'warning')
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('counting.index'))
|
||||
if not sess['master_baseline_timestamp']:
|
||||
flash('Master File not uploaded. Please upload it before starting bins.', 'warning')
|
||||
return redirect(url_for('counting.my_counts', session_id=session_id))
|
||||
@@ -146,7 +172,7 @@ def count_location(session_id, location_count_id):
|
||||
sess = get_active_session(session_id)
|
||||
if not sess:
|
||||
flash('Session not found or archived', 'warning')
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('counting.index'))
|
||||
if not sess['master_baseline_timestamp']:
|
||||
flash('Master File not uploaded. Please upload it before starting bins.', 'warning')
|
||||
return redirect(url_for('counting.my_counts', session_id=session_id))
|
||||
|
||||
@@ -17,7 +17,10 @@ def manage_users():
|
||||
# Admins can only see staff
|
||||
users = query_db("SELECT * FROM Users WHERE role = 'staff' ORDER BY full_name")
|
||||
|
||||
return render_template('manage_users.html', users=users)
|
||||
# Get all active modules
|
||||
modules = query_db('SELECT * FROM Modules WHERE is_active = 1 ORDER BY display_order')
|
||||
|
||||
return render_template('manage_users.html', users=users, modules=modules)
|
||||
|
||||
|
||||
@users_bp.route('/settings/users/add', methods=['POST'])
|
||||
@@ -191,4 +194,44 @@ def delete_user(user_id):
|
||||
execute_db('UPDATE Users SET is_active = 0 WHERE user_id = ?', [user_id])
|
||||
return jsonify({'success': True, 'message': 'User deleted successfully'})
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'message': f'Error deleting user: {str(e)}'})
|
||||
return jsonify({'success': False, 'message': f'Error deleting user: {str(e)}'})
|
||||
|
||||
|
||||
@users_bp.route('/settings/users/<int:user_id>/modules', methods=['GET'])
|
||||
@role_required('owner', 'admin')
|
||||
def get_user_modules(user_id):
|
||||
"""Get modules assigned to a user"""
|
||||
modules = query_db('''
|
||||
SELECT module_id FROM UserModules WHERE user_id = ?
|
||||
''', [user_id])
|
||||
|
||||
module_ids = [m['module_id'] for m in modules]
|
||||
return jsonify({'success': True, 'module_ids': module_ids})
|
||||
|
||||
|
||||
@users_bp.route('/settings/users/<int:user_id>/modules', methods=['POST'])
|
||||
@role_required('owner', 'admin')
|
||||
def update_user_modules(user_id):
|
||||
"""Update modules assigned to a user"""
|
||||
data = request.get_json()
|
||||
module_ids = data.get('module_ids', [])
|
||||
|
||||
# Verify user exists
|
||||
user = query_db('SELECT user_id FROM Users WHERE user_id = ?', [user_id], one=True)
|
||||
if not user:
|
||||
return jsonify({'success': False, 'message': 'User not found'})
|
||||
|
||||
try:
|
||||
# Remove all current assignments
|
||||
execute_db('DELETE FROM UserModules WHERE user_id = ?', [user_id])
|
||||
|
||||
# Add new assignments
|
||||
for module_id in module_ids:
|
||||
execute_db('''
|
||||
INSERT INTO UserModules (user_id, module_id, granted_by)
|
||||
VALUES (?, ?, ?)
|
||||
''', [user_id, module_id, session['user_id']])
|
||||
|
||||
return jsonify({'success': True, 'message': 'Modules updated'})
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'message': str(e)})
|
||||
Reference in New Issue
Block a user