151 lines
4.8 KiB
Python
151 lines
4.8 KiB
Python
"""Flask-based ticketing system web application."""
|
|
|
|
from flask import Flask, redirect, render_template, request, url_for
|
|
|
|
from models import Ticket, TicketPriority, TicketStatus
|
|
from store import get_store
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
# ── helper ───────────────────────────────────────────────────────
|
|
|
|
def _priorities() -> list[tuple[str, str]]:
|
|
return [(p.value, p.name.replace("_", " ").title()) for p in TicketPriority]
|
|
|
|
|
|
def _statuses() -> list[tuple[str, str]]:
|
|
return [(s.value, s.name.replace("_", " ").title()) for s in TicketStatus]
|
|
|
|
|
|
def _ticket_from_form() -> Ticket:
|
|
return Ticket(
|
|
title=request.form.get("title", "").strip(),
|
|
description=request.form.get("description", "").strip(),
|
|
priority=TicketPriority(request.form.get("priority", "medium")),
|
|
status=TicketStatus(request.form.get("status", "open")),
|
|
assignee=request.form.get("assignee", "").strip(),
|
|
project=request.form.get("project", "").strip(),
|
|
)
|
|
|
|
|
|
# ── routes ───────────────────────────────────────────────────────
|
|
|
|
@app.route("/")
|
|
def index() -> str:
|
|
store = get_store()
|
|
status_filter = request.args.get("status", "")
|
|
priority_filter = request.args.get("priority", "")
|
|
project_filter = request.args.get("project", "")
|
|
search = request.args.get("search", "")
|
|
|
|
tickets = store.list_tickets(
|
|
status=status_filter or None,
|
|
priority=priority_filter or None,
|
|
search=search or None,
|
|
project=project_filter or None,
|
|
)
|
|
counts = store.count_by_status()
|
|
projects = store.get_projects()
|
|
|
|
return render_template(
|
|
"index.html",
|
|
tickets=tickets,
|
|
status_filter=status_filter,
|
|
priority_filter=priority_filter,
|
|
project_filter=project_filter,
|
|
search=search,
|
|
counts=counts,
|
|
priorities=_priorities(),
|
|
statuses=_statuses(),
|
|
projects=projects,
|
|
)
|
|
|
|
|
|
@app.route("/tickets/new", methods=["GET", "POST"])
|
|
def create_ticket() -> str:
|
|
if request.method == "POST":
|
|
ticket = _ticket_from_form()
|
|
if not ticket.title:
|
|
return render_template(
|
|
"create.html",
|
|
priorities=_priorities(),
|
|
statuses=_statuses(),
|
|
ticket=ticket,
|
|
error="Title is required.",
|
|
)
|
|
store = get_store()
|
|
store.create_ticket(ticket)
|
|
return redirect(url_for("index"))
|
|
|
|
return render_template(
|
|
"create.html",
|
|
priorities=_priorities(),
|
|
statuses=_statuses(),
|
|
ticket=None,
|
|
)
|
|
|
|
|
|
@app.route("/tickets/<int:ticket_id>")
|
|
def view_ticket(ticket_id: int) -> str:
|
|
store = get_store()
|
|
ticket = store.get_ticket(ticket_id)
|
|
if ticket is None:
|
|
return "Ticket not found", 404
|
|
return render_template(
|
|
"detail.html",
|
|
ticket=ticket,
|
|
priorities=_priorities(),
|
|
statuses=_statuses(),
|
|
)
|
|
|
|
|
|
@app.route("/tickets/<int:ticket_id>/edit", methods=["GET", "POST"])
|
|
def edit_ticket(ticket_id: int) -> str:
|
|
store = get_store()
|
|
ticket = store.get_ticket(ticket_id)
|
|
if ticket is None:
|
|
return "Ticket not found", 404
|
|
|
|
if request.method == "POST":
|
|
title = request.form.get("title", "").strip()
|
|
if not title:
|
|
return render_template(
|
|
"edit.html",
|
|
ticket=ticket,
|
|
priorities=_priorities(),
|
|
statuses=_statuses(),
|
|
error="Title is required.",
|
|
)
|
|
updates = {
|
|
"title": title,
|
|
"description": request.form.get("description", "").strip(),
|
|
"priority": TicketPriority(request.form.get("priority", "medium")),
|
|
"status": TicketStatus(request.form.get("status", "open")),
|
|
"assignee": request.form.get("assignee", "").strip(),
|
|
"project": request.form.get("project", "").strip(),
|
|
}
|
|
store.update_ticket(ticket_id, updates)
|
|
return redirect(url_for("view_ticket", ticket_id=ticket_id))
|
|
|
|
return render_template(
|
|
"edit.html",
|
|
ticket=ticket,
|
|
priorities=_priorities(),
|
|
statuses=_statuses(),
|
|
)
|
|
|
|
|
|
@app.route("/tickets/<int:ticket_id>/delete", methods=["POST"])
|
|
def delete_ticket(ticket_id: int) -> str:
|
|
store = get_store()
|
|
store.delete_ticket(ticket_id)
|
|
return redirect(url_for("index"))
|
|
|
|
|
|
# ── entrypoint ───────────────────────────────────────────────────
|
|
|
|
if __name__ == "__main__":
|
|
app.run(debug=True, host="0.0.0.0", port=5000)
|
|
|