""" routes/admin_shifts.py — Shift management CRUD routes. """ import logging from flask import Blueprint, render_template, request, redirect, url_for, flash, session, jsonify from models import ( get_all_shifts, get_shift_by_id, get_shift_assigned_users, get_shift_assigned_websites, create_shift, update_shift, delete_shift, get_all_users, get_all_websites, ) from utils.decorators import admin_required logger = logging.getLogger("routes.admin_shifts") admin_shifts_bp = Blueprint("admin_shifts", __name__, url_prefix="/admin/shifts") # MySQL DAYOFWEEK: 1=Sun 2=Mon … 7=Sat DAY_MAP = [ ("Mon", "2"), ("Tue", "3"), ("Wed", "4"), ("Thu", "5"), ("Fri", "6"), ("Sat", "7"), ("Sun", "1"), ] @admin_shifts_bp.route("/") @admin_required def shifts_list(): shifts = get_all_shifts() all_users = get_all_users() all_sites = get_all_websites() return render_template("admin/shifts.html", shifts=shifts, all_users=all_users, all_sites=all_sites, day_map=DAY_MAP) @admin_shifts_bp.route("//detail") @admin_required def detail(shift_id): shift = get_shift_by_id(shift_id) users = get_shift_assigned_users(shift_id) websites = get_shift_assigned_websites(shift_id) if not shift: return jsonify({"error": "Not found"}), 404 # Convert time objects to HH:MM strings for form inputs def _fmt(t): if t is None: return "" if hasattr(t, "seconds"): # timedelta from MySQL h, rem = divmod(int(t.total_seconds()), 3600) return f"{h:02d}:{rem // 60:02d}" return str(t)[:5] return jsonify({ "id": shift["id"], "name": shift["name"], "days_of_week": shift["days_of_week"], "start_time": _fmt(shift["start_time"]), "end_time": _fmt(shift["end_time"]), "note": shift["note"] or "", "is_active": shift["is_active"], "user_ids": [u["id"] for u in users], "website_ids": [w["id"] for w in websites], }) @admin_shifts_bp.route("/create", methods=["POST"]) @admin_required def create(): admin_id = session["user"]["id"] name = request.form.get("name", "").strip() days_of_week = "".join(request.form.getlist("days_of_week[]")) start_time = request.form.get("start_time", "08:00") end_time = request.form.get("end_time", "17:00") note = request.form.get("note", "").strip() user_ids = [int(x) for x in request.form.getlist("user_ids[]") if x] website_ids = [int(x) for x in request.form.getlist("website_ids[]") if x] if not name: flash("Shift name is required.", "danger") return redirect(url_for("admin_shifts.shifts_list")) try: create_shift(admin_id, name, days_of_week, start_time, end_time, note, user_ids, website_ids) flash(f"Shift '{name}' created successfully.", "success") logger.info(f"Shift '{name}' created by admin_id={admin_id}.") except Exception as e: logger.error(f"create_shift error: {e}") flash(f"Error creating shift: {e}", "danger") return redirect(url_for("admin_shifts.shifts_list")) @admin_shifts_bp.route("//edit", methods=["POST"]) @admin_required def edit(shift_id): admin_id = session["user"]["id"] name = request.form.get("name", "").strip() days_of_week = "".join(request.form.getlist("days_of_week[]")) start_time = request.form.get("start_time", "08:00") end_time = request.form.get("end_time", "17:00") note = request.form.get("note", "").strip() is_active = int(request.form.get("is_active", 1)) user_ids = [int(x) for x in request.form.getlist("user_ids[]") if x] website_ids = [int(x) for x in request.form.getlist("website_ids[]") if x] try: update_shift(admin_id, shift_id, name, days_of_week, start_time, end_time, note, is_active, user_ids, website_ids) flash(f"Shift '{name}' updated successfully.", "success") logger.info(f"Shift id={shift_id} updated by admin_id={admin_id}.") except Exception as e: logger.error(f"update_shift error: {e}") flash(f"Error updating shift: {e}", "danger") return redirect(url_for("admin_shifts.shifts_list")) @admin_shifts_bp.route("//delete", methods=["POST"]) @admin_required def delete(shift_id): admin_id = session["user"]["id"] try: delete_shift(admin_id, shift_id) flash("Shift deactivated successfully.", "success") logger.info(f"Shift id={shift_id} soft-deleted by admin_id={admin_id}.") except Exception as e: logger.error(f"delete_shift error: {e}") flash(f"Error deleting shift: {e}", "danger") return redirect(url_for("admin_shifts.shifts_list"))