refactor: Extract admin blueprint Part 1 (Phase 6)
Moved 19 admin routes to blueprints/admin/: - Recommendations: 3 routes (list, approve, reject) - Users: 8 routes (list, add, toggle-admin, toggle-verified, update, assign-company, delete, reset-password) - Fees: 5 routes (list, generate, mark-paid, bulk-mark-paid, export) - Calendar admin: 3 routes (list, new, delete) Note: AI-parse routes (/api/admin/users/ai-parse, ai-create) remain in app.py. Aliases created for backward compatibility. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
55718ed211
commit
44268c5425
123
app.py
123
app.py
@ -1090,11 +1090,12 @@ def health_full():
|
||||
# See: blueprints/public/routes.py
|
||||
# ============================================================
|
||||
# RECOMMENDATIONS ADMIN ROUTES
|
||||
# MOVED TO: blueprints/admin/routes.py
|
||||
# ============================================================
|
||||
|
||||
@app.route('/admin/recommendations')
|
||||
@login_required
|
||||
def admin_recommendations():
|
||||
# @app.route('/admin/recommendations') # MOVED TO admin.admin_recommendations
|
||||
# @login_required
|
||||
def _old_admin_recommendations():
|
||||
"""Admin panel for recommendations moderation"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień do tej strony.', 'error')
|
||||
@ -1137,9 +1138,9 @@ def admin_recommendations():
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/recommendations/<int:recommendation_id>/approve', methods=['POST'])
|
||||
@login_required
|
||||
def admin_recommendation_approve(recommendation_id):
|
||||
# @app.route('/admin/recommendations/<int:recommendation_id>/approve', methods=['POST']) # MOVED TO admin.admin_recommendation_approve
|
||||
# @login_required
|
||||
def _old_admin_recommendation_approve(recommendation_id):
|
||||
"""Approve a recommendation"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1168,9 +1169,9 @@ def admin_recommendation_approve(recommendation_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/recommendations/<int:recommendation_id>/reject', methods=['POST'])
|
||||
@login_required
|
||||
def admin_recommendation_reject(recommendation_id):
|
||||
# @app.route('/admin/recommendations/<int:recommendation_id>/reject', methods=['POST']) # MOVED TO admin.admin_recommendation_reject
|
||||
# @login_required
|
||||
def _old_admin_recommendation_reject(recommendation_id):
|
||||
"""Reject a recommendation"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1204,11 +1205,13 @@ def admin_recommendation_reject(recommendation_id):
|
||||
|
||||
# ============================================================
|
||||
# USER MANAGEMENT ADMIN ROUTES
|
||||
# MOVED TO: blueprints/admin/routes.py
|
||||
# NOTE: AI-parse routes remain here (lines 1308-1591)
|
||||
# ============================================================
|
||||
|
||||
@app.route('/admin/users')
|
||||
@login_required
|
||||
def admin_users():
|
||||
# @app.route('/admin/users') # MOVED TO admin.admin_users
|
||||
# @login_required
|
||||
def _old_admin_users():
|
||||
"""Admin panel for user management"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień do tej strony.', 'error')
|
||||
@ -1243,9 +1246,9 @@ def admin_users():
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/users/add', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_add():
|
||||
# @app.route('/admin/users/add', methods=['POST']) # MOVED TO admin.admin_user_add
|
||||
# @login_required
|
||||
def _old_admin_user_add():
|
||||
"""Create a new user (admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1591,9 +1594,9 @@ def admin_users_bulk_create():
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/users/<int:user_id>/toggle-admin', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_toggle_admin(user_id):
|
||||
# @app.route('/admin/users/<int:user_id>/toggle-admin', methods=['POST']) # MOVED TO admin.admin_user_toggle_admin
|
||||
# @login_required
|
||||
def _old_admin_user_toggle_admin(user_id):
|
||||
"""Toggle admin status for a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1621,9 +1624,9 @@ def admin_user_toggle_admin(user_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/users/<int:user_id>/toggle-verified', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_toggle_verified(user_id):
|
||||
# @app.route('/admin/users/<int:user_id>/toggle-verified', methods=['POST']) # MOVED TO admin.admin_user_toggle_verified
|
||||
# @login_required
|
||||
def _old_admin_user_toggle_verified(user_id):
|
||||
"""Toggle verified status for a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1652,9 +1655,9 @@ def admin_user_toggle_verified(user_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/users/<int:user_id>/update', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_update(user_id):
|
||||
# @app.route('/admin/users/<int:user_id>/update', methods=['POST']) # MOVED TO admin.admin_user_update
|
||||
# @login_required
|
||||
def _old_admin_user_update(user_id):
|
||||
"""Update user data (name, email)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1707,9 +1710,9 @@ def admin_user_update(user_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/users/<int:user_id>/assign-company', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_assign_company(user_id):
|
||||
# @app.route('/admin/users/<int:user_id>/assign-company', methods=['POST']) # MOVED TO admin.admin_user_assign_company
|
||||
# @login_required
|
||||
def _old_admin_user_assign_company(user_id):
|
||||
"""Assign a company to a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1746,9 +1749,9 @@ def admin_user_assign_company(user_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/users/<int:user_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_delete(user_id):
|
||||
# @app.route('/admin/users/<int:user_id>/delete', methods=['POST']) # MOVED TO admin.admin_user_delete
|
||||
# @login_required
|
||||
def _old_admin_user_delete(user_id):
|
||||
"""Delete a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1776,9 +1779,9 @@ def admin_user_delete(user_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/users/<int:user_id>/reset-password', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_reset_password(user_id):
|
||||
# @app.route('/admin/users/<int:user_id>/reset-password', methods=['POST']) # MOVED TO admin.admin_user_reset_password
|
||||
# @login_required
|
||||
def _old_admin_user_reset_password(user_id):
|
||||
"""Generate password reset token for a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
@ -1812,18 +1815,20 @@ def admin_user_reset_password(user_id):
|
||||
|
||||
# ============================================================
|
||||
# MEMBERSHIP FEES ADMIN
|
||||
# MOVED TO: blueprints/admin/routes.py
|
||||
# ============================================================
|
||||
|
||||
MONTHS_PL = [
|
||||
# MONTHS_PL constant moved to blueprint
|
||||
_OLD_MONTHS_PL = [
|
||||
(1, 'Styczen'), (2, 'Luty'), (3, 'Marzec'), (4, 'Kwiecien'),
|
||||
(5, 'Maj'), (6, 'Czerwiec'), (7, 'Lipiec'), (8, 'Sierpien'),
|
||||
(9, 'Wrzesien'), (10, 'Pazdziernik'), (11, 'Listopad'), (12, 'Grudzien')
|
||||
]
|
||||
|
||||
|
||||
@app.route('/admin/fees')
|
||||
@login_required
|
||||
def admin_fees():
|
||||
# @app.route('/admin/fees') # MOVED TO admin.admin_fees
|
||||
# @login_required
|
||||
def _old_admin_fees():
|
||||
"""Admin panel for membership fee management"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnien do tej strony.', 'error')
|
||||
@ -1917,9 +1922,9 @@ def admin_fees():
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/fees/generate', methods=['POST'])
|
||||
@login_required
|
||||
def admin_fees_generate():
|
||||
# @app.route('/admin/fees/generate', methods=['POST']) # MOVED TO admin.admin_fees_generate
|
||||
# @login_required
|
||||
def _old_admin_fees_generate():
|
||||
"""Generate fee records for all companies for a given month"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnien'}), 403
|
||||
@ -1976,9 +1981,9 @@ def admin_fees_generate():
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/fees/<int:fee_id>/mark-paid', methods=['POST'])
|
||||
@login_required
|
||||
def admin_fees_mark_paid(fee_id):
|
||||
# @app.route('/admin/fees/<int:fee_id>/mark-paid', methods=['POST']) # MOVED TO admin.admin_fees_mark_paid
|
||||
# @login_required
|
||||
def _old_admin_fees_mark_paid(fee_id):
|
||||
"""Mark a fee as paid"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnien'}), 403
|
||||
@ -2025,9 +2030,9 @@ def admin_fees_mark_paid(fee_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/fees/bulk-mark-paid', methods=['POST'])
|
||||
@login_required
|
||||
def admin_fees_bulk_mark_paid():
|
||||
# @app.route('/admin/fees/bulk-mark-paid', methods=['POST']) # MOVED TO admin.admin_fees_bulk_mark_paid
|
||||
# @login_required
|
||||
def _old_admin_fees_bulk_mark_paid():
|
||||
"""Bulk mark fees as paid"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnien'}), 403
|
||||
@ -2064,9 +2069,9 @@ def admin_fees_bulk_mark_paid():
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/fees/export')
|
||||
@login_required
|
||||
def admin_fees_export():
|
||||
# @app.route('/admin/fees/export') # MOVED TO admin.admin_fees_export
|
||||
# @login_required
|
||||
def _old_admin_fees_export():
|
||||
"""Export fees to CSV"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnien.', 'error')
|
||||
@ -2130,10 +2135,10 @@ def admin_fees_export():
|
||||
# ============================================================
|
||||
# Routes: /kalendarz, /kalendarz/<id>, /kalendarz/<id>/rsvp
|
||||
|
||||
# Admin calendar routes remain here
|
||||
@app.route('/admin/kalendarz')
|
||||
@login_required
|
||||
def admin_calendar():
|
||||
# Admin calendar routes MOVED TO: blueprints/admin/routes.py
|
||||
# @app.route('/admin/kalendarz') # MOVED TO admin.admin_calendar
|
||||
# @login_required
|
||||
def _old_admin_calendar():
|
||||
"""Panel admin - zarządzanie wydarzeniami"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień.', 'error')
|
||||
@ -2147,9 +2152,9 @@ def admin_calendar():
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/admin/kalendarz/nowy', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def admin_calendar_new():
|
||||
# @app.route('/admin/kalendarz/nowy', methods=['GET', 'POST']) # MOVED TO admin.admin_calendar_new
|
||||
# @login_required
|
||||
def _old_admin_calendar_new():
|
||||
"""Dodaj nowe wydarzenie"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień.', 'error')
|
||||
@ -2199,9 +2204,9 @@ def admin_calendar_new():
|
||||
return render_template('calendar/admin_new.html')
|
||||
|
||||
|
||||
@app.route('/admin/kalendarz/<int:event_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
def admin_calendar_delete(event_id):
|
||||
# @app.route('/admin/kalendarz/<int:event_id>/delete', methods=['POST']) # MOVED TO admin.admin_calendar_delete
|
||||
# @login_required
|
||||
def _old_admin_calendar_delete(event_id):
|
||||
"""Usuń wydarzenie"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
@ -195,7 +195,45 @@ def register_blueprints(app):
|
||||
except Exception as e:
|
||||
logger.error(f"Error registering chat blueprint: {e}")
|
||||
|
||||
# Phase 6-10: Future blueprints will be added here
|
||||
# Phase 6: Admin blueprint (part 1: users, recommendations, fees, calendar)
|
||||
try:
|
||||
from blueprints.admin import bp as admin_bp
|
||||
app.register_blueprint(admin_bp)
|
||||
logger.info("Registered blueprint: admin")
|
||||
|
||||
# Create aliases for backward compatibility
|
||||
_create_endpoint_aliases(app, admin_bp, {
|
||||
# Recommendations
|
||||
'admin_recommendations': 'admin.admin_recommendations',
|
||||
'admin_recommendation_approve': 'admin.admin_recommendation_approve',
|
||||
'admin_recommendation_reject': 'admin.admin_recommendation_reject',
|
||||
# Users
|
||||
'admin_users': 'admin.admin_users',
|
||||
'admin_user_add': 'admin.admin_user_add',
|
||||
'admin_user_toggle_admin': 'admin.admin_user_toggle_admin',
|
||||
'admin_user_toggle_verified': 'admin.admin_user_toggle_verified',
|
||||
'admin_user_update': 'admin.admin_user_update',
|
||||
'admin_user_assign_company': 'admin.admin_user_assign_company',
|
||||
'admin_user_delete': 'admin.admin_user_delete',
|
||||
'admin_user_reset_password': 'admin.admin_user_reset_password',
|
||||
# Fees
|
||||
'admin_fees': 'admin.admin_fees',
|
||||
'admin_fees_generate': 'admin.admin_fees_generate',
|
||||
'admin_fees_mark_paid': 'admin.admin_fees_mark_paid',
|
||||
'admin_fees_bulk_mark_paid': 'admin.admin_fees_bulk_mark_paid',
|
||||
'admin_fees_export': 'admin.admin_fees_export',
|
||||
# Calendar
|
||||
'admin_calendar': 'admin.admin_calendar',
|
||||
'admin_calendar_new': 'admin.admin_calendar_new',
|
||||
'admin_calendar_delete': 'admin.admin_calendar_delete',
|
||||
})
|
||||
logger.info("Created admin endpoint aliases")
|
||||
except ImportError as e:
|
||||
logger.debug(f"Blueprint admin not yet available: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error registering admin blueprint: {e}")
|
||||
|
||||
# Phase 6 (continued) + Phase 7-10: Future blueprints will be added here
|
||||
|
||||
|
||||
def _create_endpoint_aliases(app, blueprint, aliases):
|
||||
|
||||
12
blueprints/admin/__init__.py
Normal file
12
blueprints/admin/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
"""
|
||||
Admin Blueprint
|
||||
===============
|
||||
|
||||
Admin panel routes: users, recommendations, fees, calendar, and more.
|
||||
"""
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
bp = Blueprint('admin', __name__, url_prefix='/admin')
|
||||
|
||||
from . import routes # noqa: E402, F401
|
||||
828
blueprints/admin/routes.py
Normal file
828
blueprints/admin/routes.py
Normal file
@ -0,0 +1,828 @@
|
||||
"""
|
||||
Admin Routes
|
||||
============
|
||||
|
||||
Admin panel: users, recommendations, fees, calendar management.
|
||||
"""
|
||||
|
||||
import os
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
import logging
|
||||
import secrets
|
||||
import string
|
||||
from io import StringIO
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from flask import render_template, request, redirect, url_for, flash, jsonify, Response
|
||||
from flask_login import login_required, current_user
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from . import bp
|
||||
from database import (
|
||||
SessionLocal, User, Company, CompanyRecommendation,
|
||||
MembershipFee, MembershipFeeConfig, NordaEvent, EventAttendee
|
||||
)
|
||||
import gemini_service
|
||||
|
||||
# Logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Polish month names for fees
|
||||
MONTHS_PL = [
|
||||
(1, 'Styczen'), (2, 'Luty'), (3, 'Marzec'), (4, 'Kwiecien'),
|
||||
(5, 'Maj'), (6, 'Czerwiec'), (7, 'Lipiec'), (8, 'Sierpien'),
|
||||
(9, 'Wrzesien'), (10, 'Pazdziernik'), (11, 'Listopad'), (12, 'Grudzien')
|
||||
]
|
||||
|
||||
|
||||
# ============================================================
|
||||
# RECOMMENDATIONS ADMIN ROUTES
|
||||
# ============================================================
|
||||
|
||||
@bp.route('/recommendations')
|
||||
@login_required
|
||||
def admin_recommendations():
|
||||
"""Admin panel for recommendations moderation"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień do tej strony.', 'error')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
recommendations = db.query(CompanyRecommendation).order_by(
|
||||
CompanyRecommendation.created_at.desc()
|
||||
).all()
|
||||
|
||||
pending_recommendations = db.query(CompanyRecommendation).filter(
|
||||
CompanyRecommendation.status == 'pending'
|
||||
).order_by(CompanyRecommendation.created_at.desc()).all()
|
||||
|
||||
total_recommendations = len(recommendations)
|
||||
pending_count = len(pending_recommendations)
|
||||
approved_count = db.query(CompanyRecommendation).filter(
|
||||
CompanyRecommendation.status == 'approved'
|
||||
).count()
|
||||
rejected_count = db.query(CompanyRecommendation).filter(
|
||||
CompanyRecommendation.status == 'rejected'
|
||||
).count()
|
||||
|
||||
logger.info(f"Admin {current_user.email} accessed recommendations panel - {pending_count} pending")
|
||||
|
||||
return render_template(
|
||||
'admin/recommendations.html',
|
||||
recommendations=recommendations,
|
||||
pending_recommendations=pending_recommendations,
|
||||
total_recommendations=total_recommendations,
|
||||
pending_count=pending_count,
|
||||
approved_count=approved_count,
|
||||
rejected_count=rejected_count
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/recommendations/<int:recommendation_id>/approve', methods=['POST'])
|
||||
@login_required
|
||||
def admin_recommendation_approve(recommendation_id):
|
||||
"""Approve a recommendation"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
recommendation = db.query(CompanyRecommendation).filter(
|
||||
CompanyRecommendation.id == recommendation_id
|
||||
).first()
|
||||
|
||||
if not recommendation:
|
||||
return jsonify({'success': False, 'error': 'Rekomendacja nie istnieje'}), 404
|
||||
|
||||
recommendation.status = 'approved'
|
||||
recommendation.moderated_by = current_user.id
|
||||
recommendation.moderated_at = datetime.utcnow()
|
||||
recommendation.rejection_reason = None
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Admin {current_user.email} approved recommendation #{recommendation_id}")
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Rekomendacja zatwierdzona'
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/recommendations/<int:recommendation_id>/reject', methods=['POST'])
|
||||
@login_required
|
||||
def admin_recommendation_reject(recommendation_id):
|
||||
"""Reject a recommendation"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
recommendation = db.query(CompanyRecommendation).filter(
|
||||
CompanyRecommendation.id == recommendation_id
|
||||
).first()
|
||||
|
||||
if not recommendation:
|
||||
return jsonify({'success': False, 'error': 'Rekomendacja nie istnieje'}), 404
|
||||
|
||||
rejection_reason = request.json.get('reason', '') if request.is_json else request.form.get('reason', '')
|
||||
|
||||
recommendation.status = 'rejected'
|
||||
recommendation.moderated_by = current_user.id
|
||||
recommendation.moderated_at = datetime.utcnow()
|
||||
recommendation.rejection_reason = rejection_reason.strip() if rejection_reason else None
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Admin {current_user.email} rejected recommendation #{recommendation_id}")
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Rekomendacja odrzucona'
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# USER MANAGEMENT ADMIN ROUTES
|
||||
# ============================================================
|
||||
|
||||
@bp.route('/users')
|
||||
@login_required
|
||||
def admin_users():
|
||||
"""Admin panel for user management"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień do tej strony.', 'error')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
users = db.query(User).order_by(User.created_at.desc()).all()
|
||||
companies = db.query(Company).order_by(Company.name).all()
|
||||
|
||||
total_users = len(users)
|
||||
admin_count = sum(1 for u in users if u.is_admin)
|
||||
verified_count = sum(1 for u in users if u.is_verified)
|
||||
unverified_count = total_users - verified_count
|
||||
|
||||
logger.info(f"Admin {current_user.email} accessed users panel - {total_users} users")
|
||||
|
||||
return render_template(
|
||||
'admin/users.html',
|
||||
users=users,
|
||||
companies=companies,
|
||||
total_users=total_users,
|
||||
admin_count=admin_count,
|
||||
verified_count=verified_count,
|
||||
unverified_count=unverified_count
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/users/add', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_add():
|
||||
"""Create a new user (admin only)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
data = request.get_json() or {}
|
||||
|
||||
email = data.get('email', '').strip().lower()
|
||||
if not email:
|
||||
return jsonify({'success': False, 'error': 'Email jest wymagany'}), 400
|
||||
|
||||
existing_user = db.query(User).filter(User.email == email).first()
|
||||
if existing_user:
|
||||
return jsonify({'success': False, 'error': 'Użytkownik z tym adresem email już istnieje'}), 400
|
||||
|
||||
password_chars = string.ascii_letters + string.digits + "!@#$%^&*"
|
||||
generated_password = ''.join(secrets.choice(password_chars) for _ in range(16))
|
||||
password_hash = generate_password_hash(generated_password, method='pbkdf2:sha256')
|
||||
|
||||
new_user = User(
|
||||
email=email,
|
||||
password_hash=password_hash,
|
||||
name=data.get('name', '').strip() or None,
|
||||
company_id=data.get('company_id') or None,
|
||||
is_admin=data.get('is_admin', False),
|
||||
is_verified=data.get('is_verified', True),
|
||||
is_active=True
|
||||
)
|
||||
|
||||
db.add(new_user)
|
||||
db.commit()
|
||||
db.refresh(new_user)
|
||||
|
||||
logger.info(f"Admin {current_user.email} created new user: {email} (ID: {new_user.id})")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'user_id': new_user.id,
|
||||
'generated_password': generated_password,
|
||||
'message': f'Użytkownik {email} został utworzony'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error creating user: {e}")
|
||||
return jsonify({'success': False, 'error': 'Błąd podczas tworzenia użytkownika'}), 500
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/toggle-admin', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_toggle_admin(user_id):
|
||||
"""Toggle admin status for a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
if user_id == current_user.id:
|
||||
return jsonify({'success': False, 'error': 'Nie możesz zmienić własnych uprawnień'}), 400
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404
|
||||
|
||||
user.is_admin = not user.is_admin
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Admin {current_user.email} {'granted' if user.is_admin else 'revoked'} admin for user {user.email}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'is_admin': user.is_admin,
|
||||
'message': f"{'Nadano' if user.is_admin else 'Odebrano'} uprawnienia admina"
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/toggle-verified', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_toggle_verified(user_id):
|
||||
"""Toggle verified status for a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404
|
||||
|
||||
user.is_verified = not user.is_verified
|
||||
if user.is_verified:
|
||||
user.verified_at = datetime.utcnow()
|
||||
else:
|
||||
user.verified_at = None
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Admin {current_user.email} {'verified' if user.is_verified else 'unverified'} user {user.email}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'is_verified': user.is_verified,
|
||||
'message': f"Użytkownik {'zweryfikowany' if user.is_verified else 'niezweryfikowany'}"
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/update', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_update(user_id):
|
||||
"""Update user data (name, email)"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
|
||||
if 'name' in data:
|
||||
user.name = data['name'].strip() if data['name'] else None
|
||||
|
||||
if 'email' in data:
|
||||
new_email = data['email'].strip().lower()
|
||||
if new_email and new_email != user.email:
|
||||
existing = db.query(User).filter(User.email == new_email, User.id != user_id).first()
|
||||
if existing:
|
||||
return jsonify({'success': False, 'error': 'Ten email jest już używany'}), 400
|
||||
user.email = new_email
|
||||
|
||||
if 'phone' in data:
|
||||
user.phone = data['phone'].strip() if data['phone'] else None
|
||||
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Admin {current_user.email} updated user {user.email}: name={user.name}, phone={user.phone}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'user': {
|
||||
'id': user.id,
|
||||
'name': user.name,
|
||||
'email': user.email,
|
||||
'phone': user.phone
|
||||
},
|
||||
'message': 'Dane użytkownika zaktualizowane'
|
||||
})
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error updating user {user_id}: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/assign-company', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_assign_company(user_id):
|
||||
"""Assign a company to a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
company_id = data.get('company_id')
|
||||
|
||||
if company_id:
|
||||
company = db.query(Company).filter(Company.id == company_id).first()
|
||||
if not company:
|
||||
return jsonify({'success': False, 'error': 'Firma nie znaleziona'}), 404
|
||||
user.company_id = company_id
|
||||
company_name = company.name
|
||||
else:
|
||||
user.company_id = None
|
||||
company_name = None
|
||||
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Admin {current_user.email} assigned company '{company_name}' to user {user.email}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'company_name': company_name,
|
||||
'message': f"Przypisano firmę: {company_name}" if company_name else "Odłączono od firmy"
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_delete(user_id):
|
||||
"""Delete a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
if user_id == current_user.id:
|
||||
return jsonify({'success': False, 'error': 'Nie możesz usunąć własnego konta'}), 400
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404
|
||||
|
||||
email = user.email
|
||||
db.delete(user)
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Admin {current_user.email} deleted user {email}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f"Użytkownik {email} został usunięty"
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/reset-password', methods=['POST'])
|
||||
@login_required
|
||||
def admin_user_reset_password(user_id):
|
||||
"""Generate password reset token for a user"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404
|
||||
|
||||
reset_token = secrets.token_urlsafe(32)
|
||||
user.reset_token = reset_token
|
||||
user.reset_token_expires = datetime.utcnow() + timedelta(hours=1)
|
||||
db.commit()
|
||||
|
||||
base_url = os.getenv('APP_URL', 'https://nordabiznes.pl')
|
||||
reset_url = f"{base_url}/reset-password/{reset_token}"
|
||||
|
||||
logger.info(f"Admin {current_user.email} generated reset token for user {user.email}: {reset_token[:8]}...")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'reset_url': reset_url,
|
||||
'message': f"Link do resetu hasła wygenerowany (ważny 1 godzinę)"
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# MEMBERSHIP FEES ADMIN
|
||||
# ============================================================
|
||||
|
||||
@bp.route('/fees')
|
||||
@login_required
|
||||
def admin_fees():
|
||||
"""Admin panel for membership fee management"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnien do tej strony.', 'error')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
from sqlalchemy import func, case
|
||||
|
||||
year = request.args.get('year', datetime.now().year, type=int)
|
||||
month = request.args.get('month', type=int)
|
||||
status_filter = request.args.get('status', '')
|
||||
|
||||
companies = db.query(Company).filter(Company.status == 'active').order_by(Company.name).all()
|
||||
|
||||
fee_query = db.query(MembershipFee).filter(MembershipFee.fee_year == year)
|
||||
if month:
|
||||
fee_query = fee_query.filter(MembershipFee.fee_month == month)
|
||||
|
||||
fees = {(f.company_id, f.fee_month): f for f in fee_query.all()}
|
||||
|
||||
companies_fees = []
|
||||
for company in companies:
|
||||
if month:
|
||||
fee = fees.get((company.id, month))
|
||||
companies_fees.append({
|
||||
'company': company,
|
||||
'fee': fee,
|
||||
'status': fee.status if fee else 'brak'
|
||||
})
|
||||
else:
|
||||
company_data = {'company': company, 'months': {}}
|
||||
for m in range(1, 13):
|
||||
fee = fees.get((company.id, m))
|
||||
company_data['months'][m] = fee
|
||||
companies_fees.append(company_data)
|
||||
|
||||
if status_filter and month:
|
||||
if status_filter == 'paid':
|
||||
companies_fees = [cf for cf in companies_fees if cf.get('status') == 'paid']
|
||||
elif status_filter == 'pending':
|
||||
companies_fees = [cf for cf in companies_fees if cf.get('status') in ('pending', 'brak')]
|
||||
elif status_filter == 'overdue':
|
||||
companies_fees = [cf for cf in companies_fees if cf.get('status') == 'overdue']
|
||||
|
||||
total_companies = len(companies)
|
||||
if month:
|
||||
month_fees = [cf.get('fee') for cf in companies_fees if cf.get('fee')]
|
||||
paid_count = sum(1 for f in month_fees if f and f.status == 'paid')
|
||||
pending_count = total_companies - paid_count
|
||||
total_due = sum(float(f.amount) for f in month_fees if f) if month_fees else Decimal(0)
|
||||
total_paid = sum(float(f.amount_paid or 0) for f in month_fees if f) if month_fees else Decimal(0)
|
||||
else:
|
||||
all_fees = list(fees.values())
|
||||
paid_count = sum(1 for f in all_fees if f.status == 'paid')
|
||||
pending_count = len(all_fees) - paid_count
|
||||
total_due = sum(float(f.amount) for f in all_fees) if all_fees else Decimal(0)
|
||||
total_paid = sum(float(f.amount_paid or 0) for f in all_fees) if all_fees else Decimal(0)
|
||||
|
||||
fee_config = db.query(MembershipFeeConfig).filter(
|
||||
MembershipFeeConfig.scope == 'global',
|
||||
MembershipFeeConfig.valid_until == None
|
||||
).first()
|
||||
default_fee = float(fee_config.monthly_amount) if fee_config else 100.00
|
||||
|
||||
return render_template(
|
||||
'admin/fees.html',
|
||||
companies_fees=companies_fees,
|
||||
year=year,
|
||||
month=month,
|
||||
status_filter=status_filter,
|
||||
total_companies=total_companies,
|
||||
paid_count=paid_count,
|
||||
pending_count=pending_count,
|
||||
total_due=total_due,
|
||||
total_paid=total_paid,
|
||||
default_fee=default_fee,
|
||||
years=list(range(2024, datetime.now().year + 2)),
|
||||
months=MONTHS_PL
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/fees/generate', methods=['POST'])
|
||||
@login_required
|
||||
def admin_fees_generate():
|
||||
"""Generate fee records for all companies for a given month"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnien'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
year = request.form.get('year', type=int)
|
||||
month = request.form.get('month', type=int)
|
||||
|
||||
if not year or not month:
|
||||
return jsonify({'success': False, 'error': 'Brak roku lub miesiaca'}), 400
|
||||
|
||||
fee_config = db.query(MembershipFeeConfig).filter(
|
||||
MembershipFeeConfig.scope == 'global',
|
||||
MembershipFeeConfig.valid_until == None
|
||||
).first()
|
||||
default_fee = fee_config.monthly_amount if fee_config else 100.00
|
||||
|
||||
companies = db.query(Company).filter(Company.status == 'active').all()
|
||||
|
||||
created = 0
|
||||
for company in companies:
|
||||
existing = db.query(MembershipFee).filter(
|
||||
MembershipFee.company_id == company.id,
|
||||
MembershipFee.fee_year == year,
|
||||
MembershipFee.fee_month == month
|
||||
).first()
|
||||
|
||||
if not existing:
|
||||
fee = MembershipFee(
|
||||
company_id=company.id,
|
||||
fee_year=year,
|
||||
fee_month=month,
|
||||
amount=default_fee,
|
||||
status='pending'
|
||||
)
|
||||
db.add(fee)
|
||||
created += 1
|
||||
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'Utworzono {created} rekordow skladek'
|
||||
})
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error generating fees: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/fees/<int:fee_id>/mark-paid', methods=['POST'])
|
||||
@login_required
|
||||
def admin_fees_mark_paid(fee_id):
|
||||
"""Mark a fee as paid"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnien'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
fee = db.query(MembershipFee).filter(MembershipFee.id == fee_id).first()
|
||||
if not fee:
|
||||
return jsonify({'success': False, 'error': 'Nie znaleziono skladki'}), 404
|
||||
|
||||
amount_paid = request.form.get('amount_paid', type=float)
|
||||
payment_date = request.form.get('payment_date')
|
||||
payment_method = request.form.get('payment_method', 'transfer')
|
||||
payment_reference = request.form.get('payment_reference', '')
|
||||
notes = request.form.get('notes', '')
|
||||
|
||||
fee.amount_paid = amount_paid or float(fee.amount)
|
||||
fee.payment_date = datetime.strptime(payment_date, '%Y-%m-%d').date() if payment_date else datetime.now().date()
|
||||
fee.payment_method = payment_method
|
||||
fee.payment_reference = payment_reference
|
||||
fee.notes = notes
|
||||
fee.recorded_by = current_user.id
|
||||
fee.recorded_at = datetime.now()
|
||||
|
||||
if fee.amount_paid >= float(fee.amount):
|
||||
fee.status = 'paid'
|
||||
elif fee.amount_paid > 0:
|
||||
fee.status = 'partial'
|
||||
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Skladka zostala zarejestrowana'
|
||||
})
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error marking fee as paid: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/fees/bulk-mark-paid', methods=['POST'])
|
||||
@login_required
|
||||
def admin_fees_bulk_mark_paid():
|
||||
"""Bulk mark fees as paid"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnien'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
fee_ids = request.form.getlist('fee_ids[]', type=int)
|
||||
|
||||
if not fee_ids:
|
||||
return jsonify({'success': False, 'error': 'Brak wybranych skladek'}), 400
|
||||
|
||||
updated = 0
|
||||
for fee_id in fee_ids:
|
||||
fee = db.query(MembershipFee).filter(MembershipFee.id == fee_id).first()
|
||||
if fee and fee.status != 'paid':
|
||||
fee.status = 'paid'
|
||||
fee.amount_paid = fee.amount
|
||||
fee.payment_date = datetime.now().date()
|
||||
fee.recorded_by = current_user.id
|
||||
fee.recorded_at = datetime.now()
|
||||
updated += 1
|
||||
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'Zaktualizowano {updated} rekordow'
|
||||
})
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error in bulk action: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/fees/export')
|
||||
@login_required
|
||||
def admin_fees_export():
|
||||
"""Export fees to CSV"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnien.', 'error')
|
||||
return redirect(url_for('.admin_fees'))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
year = request.args.get('year', datetime.now().year, type=int)
|
||||
month = request.args.get('month', type=int)
|
||||
|
||||
query = db.query(MembershipFee).join(Company).filter(
|
||||
MembershipFee.fee_year == year
|
||||
)
|
||||
|
||||
if month:
|
||||
query = query.filter(MembershipFee.fee_month == month)
|
||||
|
||||
fees = query.order_by(Company.name, MembershipFee.fee_month).all()
|
||||
|
||||
output = StringIO()
|
||||
writer = csv.writer(output)
|
||||
writer.writerow([
|
||||
'Firma', 'NIP', 'Rok', 'Miesiac', 'Kwota', 'Zaplacono',
|
||||
'Status', 'Data platnosci', 'Metoda', 'Referencja', 'Notatki'
|
||||
])
|
||||
|
||||
for fee in fees:
|
||||
writer.writerow([
|
||||
fee.company.name,
|
||||
fee.company.nip,
|
||||
fee.fee_year,
|
||||
fee.fee_month,
|
||||
fee.amount,
|
||||
fee.amount_paid,
|
||||
fee.status,
|
||||
fee.payment_date,
|
||||
fee.payment_method,
|
||||
fee.payment_reference,
|
||||
fee.notes
|
||||
])
|
||||
|
||||
output.seek(0)
|
||||
|
||||
return Response(
|
||||
output.getvalue(),
|
||||
mimetype='text/csv',
|
||||
headers={
|
||||
'Content-Disposition': f'attachment; filename=skladki_{year}_{month or "all"}.csv'
|
||||
}
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CALENDAR ADMIN ROUTES
|
||||
# ============================================================
|
||||
|
||||
@bp.route('/kalendarz')
|
||||
@login_required
|
||||
def admin_calendar():
|
||||
"""Panel admin - zarządzanie wydarzeniami"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień.', 'error')
|
||||
return redirect(url_for('calendar_index'))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
events = db.query(NordaEvent).order_by(NordaEvent.event_date.desc()).all()
|
||||
|
||||
return render_template('admin/calendar.html', events=events)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/kalendarz/nowy', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def admin_calendar_new():
|
||||
"""Dodaj nowe wydarzenie"""
|
||||
if not current_user.is_admin:
|
||||
flash('Brak uprawnień.', 'error')
|
||||
return redirect(url_for('calendar_index'))
|
||||
|
||||
if request.method == 'POST':
|
||||
db = SessionLocal()
|
||||
try:
|
||||
event = NordaEvent(
|
||||
title=request.form.get('title', '').strip(),
|
||||
description=request.form.get('description', '').strip(),
|
||||
event_date=datetime.strptime(request.form.get('event_date'), '%Y-%m-%d').date(),
|
||||
start_time=request.form.get('start_time') or None,
|
||||
end_time=request.form.get('end_time') or None,
|
||||
location=request.form.get('location', '').strip() or None,
|
||||
event_type=request.form.get('event_type', 'meeting'),
|
||||
max_participants=request.form.get('max_participants', type=int) or None,
|
||||
registration_required=request.form.get('registration_required') == 'on',
|
||||
is_public=request.form.get('is_public') == 'on',
|
||||
created_by=current_user.id
|
||||
)
|
||||
|
||||
db.add(event)
|
||||
db.commit()
|
||||
|
||||
flash('Wydarzenie zostało dodane.', 'success')
|
||||
return redirect(url_for('.admin_calendar'))
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
flash(f'Błąd: {str(e)}', 'error')
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return render_template('admin/calendar_form.html', event=None)
|
||||
|
||||
|
||||
@bp.route('/kalendarz/<int:event_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
def admin_calendar_delete(event_id):
|
||||
"""Usuń wydarzenie"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
event = db.query(NordaEvent).filter(NordaEvent.id == event_id).first()
|
||||
if not event:
|
||||
return jsonify({'success': False, 'error': 'Wydarzenie nie istnieje'}), 404
|
||||
|
||||
# Delete RSVPs first
|
||||
db.query(EventAttendee).filter(EventAttendee.event_id == event_id).delete()
|
||||
db.delete(event)
|
||||
db.commit()
|
||||
|
||||
return jsonify({'success': True, 'message': 'Wydarzenie usunięte'})
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error deleting event: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
db.close()
|
||||
Loading…
Reference in New Issue
Block a user