Merge Users and Teams Management.
This commit is contained in:
@@ -218,27 +218,8 @@ def admin_company():
|
||||
@admin_required
|
||||
@company_required
|
||||
def company_users():
|
||||
"""List all users in the company with detailed information"""
|
||||
users = User.query.filter_by(company_id=g.company.id).order_by(User.created_at.desc()).all()
|
||||
|
||||
# Calculate user statistics
|
||||
user_stats = {
|
||||
'total': len(users),
|
||||
'verified': len([u for u in users if u.is_verified]),
|
||||
'unverified': len([u for u in users if not u.is_verified]),
|
||||
'blocked': len([u for u in users if u.is_blocked]),
|
||||
'active': len([u for u in users if not u.is_blocked and u.is_verified]),
|
||||
'admins': len([u for u in users if u.role == Role.ADMIN]),
|
||||
'supervisors': len([u for u in users if u.role == Role.SUPERVISOR]),
|
||||
'team_leaders': len([u for u in users if u.role == Role.TEAM_LEADER]),
|
||||
'team_members': len([u for u in users if u.role == Role.TEAM_MEMBER]),
|
||||
}
|
||||
|
||||
return render_template('company_users.html',
|
||||
title='Company Users',
|
||||
company=g.company,
|
||||
users=users,
|
||||
stats=user_stats)
|
||||
"""Redirect to the unified organization management page"""
|
||||
return redirect(url_for('organization.admin_organization'))
|
||||
|
||||
|
||||
# Setup company route (separate from company blueprint due to different URL)
|
||||
|
||||
231
routes/organization.py
Normal file
231
routes/organization.py
Normal file
@@ -0,0 +1,231 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, g
|
||||
from models import db, User, Team, Role, Company
|
||||
from routes.auth import login_required, admin_required, company_required
|
||||
from sqlalchemy import or_
|
||||
|
||||
# Create the blueprint
|
||||
organization_bp = Blueprint('organization', __name__)
|
||||
|
||||
@organization_bp.route('/admin/organization')
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def admin_organization():
|
||||
"""Comprehensive organization management interface"""
|
||||
company = g.user.company
|
||||
|
||||
# Get all teams and users for the company
|
||||
teams = Team.query.filter_by(company_id=company.id).order_by(Team.name).all()
|
||||
users = User.query.filter_by(company_id=company.id).order_by(User.username).all()
|
||||
|
||||
return render_template('admin_organization.html',
|
||||
title='Organization Management',
|
||||
teams=teams,
|
||||
users=users,
|
||||
Role=Role)
|
||||
|
||||
@organization_bp.route('/api/organization/teams/<int:team_id>', methods=['GET', 'PUT', 'DELETE'])
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def api_team(team_id):
|
||||
"""API endpoint for team operations"""
|
||||
team = Team.query.filter_by(id=team_id, company_id=g.user.company_id).first_or_404()
|
||||
|
||||
if request.method == 'GET':
|
||||
return jsonify({
|
||||
'id': team.id,
|
||||
'name': team.name,
|
||||
'description': team.description,
|
||||
'members': [{'id': u.id, 'username': u.username} for u in team.users]
|
||||
})
|
||||
|
||||
elif request.method == 'PUT':
|
||||
data = request.get_json()
|
||||
team.name = data.get('name', team.name)
|
||||
team.description = data.get('description', team.description)
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': 'Team updated successfully'})
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'message': str(e)}), 400
|
||||
|
||||
elif request.method == 'DELETE':
|
||||
# Unassign all users from the team
|
||||
for user in team.users:
|
||||
user.team_id = None
|
||||
|
||||
db.session.delete(team)
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': 'Team deleted successfully'})
|
||||
|
||||
@organization_bp.route('/api/organization/teams', methods=['POST'])
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def api_create_team():
|
||||
"""API endpoint to create a new team"""
|
||||
data = request.get_json()
|
||||
|
||||
team = Team(
|
||||
name=data.get('name'),
|
||||
description=data.get('description'),
|
||||
company_id=g.user.company_id
|
||||
)
|
||||
|
||||
try:
|
||||
db.session.add(team)
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': 'Team created successfully', 'team_id': team.id})
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'message': str(e)}), 400
|
||||
|
||||
@organization_bp.route('/api/organization/users/<int:user_id>', methods=['GET', 'PUT', 'DELETE'])
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def api_user(user_id):
|
||||
"""API endpoint for user operations"""
|
||||
user = User.query.filter_by(id=user_id, company_id=g.user.company_id).first_or_404()
|
||||
|
||||
if request.method == 'GET':
|
||||
return jsonify({
|
||||
'id': user.id,
|
||||
'username': user.username,
|
||||
'email': user.email,
|
||||
'role': user.role.name if user.role else 'TEAM_MEMBER',
|
||||
'team_id': user.team_id,
|
||||
'is_blocked': user.is_blocked
|
||||
})
|
||||
|
||||
elif request.method == 'PUT':
|
||||
data = request.get_json()
|
||||
|
||||
# Update user fields
|
||||
if 'email' in data:
|
||||
user.email = data['email']
|
||||
if 'role' in data:
|
||||
user.role = Role[data['role']]
|
||||
if 'team_id' in data:
|
||||
user.team_id = data['team_id'] if data['team_id'] else None
|
||||
if 'is_blocked' in data:
|
||||
user.is_blocked = data['is_blocked']
|
||||
if 'password' in data and data['password']:
|
||||
user.set_password(data['password'])
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': 'User updated successfully'})
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'message': str(e)}), 400
|
||||
|
||||
elif request.method == 'DELETE':
|
||||
if user.id == g.user.id:
|
||||
return jsonify({'success': False, 'message': 'Cannot delete your own account'}), 400
|
||||
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': 'User deleted successfully'})
|
||||
|
||||
@organization_bp.route('/api/organization/users', methods=['POST'])
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def api_create_user():
|
||||
"""API endpoint to create a new user"""
|
||||
data = request.get_json()
|
||||
|
||||
# Check if username already exists
|
||||
if User.query.filter_by(username=data.get('username')).first():
|
||||
return jsonify({'success': False, 'message': 'Username already exists'}), 400
|
||||
|
||||
user = User(
|
||||
username=data.get('username'),
|
||||
email=data.get('email'),
|
||||
company_id=g.user.company_id,
|
||||
role=Role[data.get('role', 'TEAM_MEMBER')],
|
||||
team_id=data.get('team_id') if data.get('team_id') else None
|
||||
)
|
||||
user.set_password(data.get('password'))
|
||||
|
||||
try:
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': 'User created successfully', 'user_id': user.id})
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'message': str(e)}), 400
|
||||
|
||||
@organization_bp.route('/api/organization/users/<int:user_id>/toggle-status', methods=['POST'])
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def api_toggle_user_status(user_id):
|
||||
"""Toggle user active/blocked status"""
|
||||
user = User.query.filter_by(id=user_id, company_id=g.user.company_id).first_or_404()
|
||||
|
||||
if user.id == g.user.id:
|
||||
return jsonify({'success': False, 'message': 'Cannot block your own account'}), 400
|
||||
|
||||
user.is_blocked = not user.is_blocked
|
||||
db.session.commit()
|
||||
|
||||
status = 'blocked' if user.is_blocked else 'unblocked'
|
||||
return jsonify({'success': True, 'message': f'User {status} successfully'})
|
||||
|
||||
@organization_bp.route('/api/organization/users/<int:user_id>/assign-team', methods=['POST'])
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def api_assign_team(user_id):
|
||||
"""Assign user to a team"""
|
||||
user = User.query.filter_by(id=user_id, company_id=g.user.company_id).first_or_404()
|
||||
data = request.get_json()
|
||||
|
||||
team_id = data.get('team_id')
|
||||
if team_id:
|
||||
team = Team.query.filter_by(id=team_id, company_id=g.user.company_id).first_or_404()
|
||||
user.team_id = team.id
|
||||
else:
|
||||
user.team_id = None
|
||||
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': 'Team assignment updated'})
|
||||
|
||||
@organization_bp.route('/api/organization/search', methods=['GET'])
|
||||
@login_required
|
||||
@company_required
|
||||
@admin_required
|
||||
def api_organization_search():
|
||||
"""Search users and teams"""
|
||||
query = request.args.get('q', '').lower()
|
||||
|
||||
if not query:
|
||||
return jsonify({'users': [], 'teams': []})
|
||||
|
||||
# Search users
|
||||
users = User.query.filter(
|
||||
User.company_id == g.user.company_id,
|
||||
or_(
|
||||
User.username.ilike(f'%{query}%'),
|
||||
User.email.ilike(f'%{query}%')
|
||||
)
|
||||
).limit(10).all()
|
||||
|
||||
# Search teams
|
||||
teams = Team.query.filter(
|
||||
Team.company_id == g.user.company_id,
|
||||
or_(
|
||||
Team.name.ilike(f'%{query}%'),
|
||||
Team.description.ilike(f'%{query}%')
|
||||
)
|
||||
).limit(10).all()
|
||||
|
||||
return jsonify({
|
||||
'users': [{'id': u.id, 'username': u.username, 'email': u.email} for u in users],
|
||||
'teams': [{'id': t.id, 'name': t.name, 'description': t.description} for t in teams]
|
||||
})
|
||||
@@ -16,9 +16,8 @@ teams_bp = Blueprint('teams', __name__, url_prefix='/admin/teams')
|
||||
@admin_required
|
||||
@company_required
|
||||
def admin_teams():
|
||||
team_repo = TeamRepository()
|
||||
teams = team_repo.get_with_member_count(g.user.company_id)
|
||||
return render_template('admin_teams.html', title='Team Management', teams=teams)
|
||||
# Redirect to the new unified organization management page
|
||||
return redirect(url_for('organization.admin_organization'))
|
||||
|
||||
|
||||
@teams_bp.route('/create', methods=['GET', 'POST'])
|
||||
|
||||
@@ -38,9 +38,8 @@ def get_available_roles():
|
||||
@admin_required
|
||||
@company_required
|
||||
def admin_users():
|
||||
user_repo = UserRepository()
|
||||
users = user_repo.get_by_company(g.user.company_id)
|
||||
return render_template('admin_users.html', title='User Management', users=users)
|
||||
# Redirect to the new unified organization management page
|
||||
return redirect(url_for('organization.admin_organization'))
|
||||
|
||||
|
||||
@users_bp.route('/create', methods=['GET', 'POST'])
|
||||
|
||||
Reference in New Issue
Block a user