From 9921e6151545600971edfc4761f33bbeb39372ba Mon Sep 17 00:00:00 2001 From: SowinskiBraeden Date: Sat, 27 Jun 2026 14:30:52 -0700 Subject: [PATCH] seperate delete from archive functions --- config.py | 2 +- league_repositories.py | 10 +++ routes/account.py | 49 ++++++++++++-- routes/leagues.py | 23 +++++++ static/css/style.css | 11 +++ templates/account_settings.html | 115 +++++++++++++++++++++++--------- templates/league_settings.html | 38 +++++++++++ 7 files changed, 208 insertions(+), 40 deletions(-) diff --git a/config.py b/config.py index d4555e2..5aff018 100644 --- a/config.py +++ b/config.py @@ -9,7 +9,7 @@ DATA_PATH = BASE_DIR / "data" / "entries.csv" DEFAULT_DATABASE_URL = f"sqlite:///{BASE_DIR / 'data' / 'boker-dev.sqlite3'}" ELIGIBLE_MIN_SESSIONS = 3 -APP_VERSION = "2.5.20" +APP_VERSION = "2.5.21" def load_local_env(env_path: Path) -> None: diff --git a/league_repositories.py b/league_repositories.py index dbc53e2..9f4e5ab 100644 --- a/league_repositories.py +++ b/league_repositories.py @@ -420,6 +420,16 @@ def delete_season(season: Season) -> None: db.session.delete(season) +def delete_league(league: League) -> None: + from db_models import LedgerEvent, LeagueMembership, Player, PokerSession, Season + LedgerEvent.query.filter_by(league_id=league.id).delete(synchronize_session=False) + PokerSession.query.filter_by(league_id=league.id).delete(synchronize_session=False) + Player.query.filter_by(league_id=league.id).delete(synchronize_session=False) + Season.query.filter_by(league_id=league.id).delete(synchronize_session=False) + LeagueMembership.query.filter_by(league_id=league.id).delete(synchronize_session=False) + db.session.delete(league) + + def auto_assign_sessions_to_seasons(league_id: str) -> int: """Assign unassigned sessions to seasons based on date ranges. diff --git a/routes/account.py b/routes/account.py index d82fb22..3511698 100644 --- a/routes/account.py +++ b/routes/account.py @@ -352,6 +352,42 @@ def update_password(): return redirect(url_for("account.settings")) +@account_bp.post("/disable") +@login_required +def disable_account(): + if not db_ready(): + flash("Account database is not available.", "error") + return redirect(url_for("account.settings")) + + from db_models import League, User, utc_now + + user = db.session.get(User, current_user_id()) + if user is None: + flash("User not found.", "error") + return redirect(url_for("account.settings")) + + confirm = request.form.get("confirm", "").strip() + current_password = request.form.get("current_password", "") + + if confirm != "DISABLE": + flash("Confirmation text did not match.", "error") + return redirect(url_for("account.settings")) + + if not verify_password(user.password_hash, current_password): + flash("Password is incorrect.", "error") + return redirect(url_for("account.settings")) + + owned_leagues = League.query.filter_by(created_by_user_id=user.id, archived_at=None).all() + for league in owned_leagues: + league.archived_at = utc_now() + + user.disabled_at = utc_now() + db.session.commit() + log_user_out() + flash("Your account has been disabled.", "success") + return redirect(url_for("public.home")) + + @account_bp.post("/delete") @login_required def delete_account(): @@ -359,7 +395,8 @@ def delete_account(): flash("Account database is not available.", "error") return redirect(url_for("account.settings")) - from db_models import League, User, utc_now + from db_models import League, LeagueMembership, User, utc_now + from league_repositories import delete_league user = db.session.get(User, current_user_id()) if user is None: @@ -377,14 +414,14 @@ def delete_account(): flash("Password is incorrect.", "error") return redirect(url_for("account.settings")) - owned_leagues = League.query.filter_by(created_by_user_id=user.id, archived_at=None).all() - for league in owned_leagues: - league.archived_at = utc_now() + for league in League.query.filter_by(created_by_user_id=user.id).all(): + delete_league(league) - user.disabled_at = utc_now() + LeagueMembership.query.filter_by(user_id=user.id).delete(synchronize_session=False) + db.session.delete(user) db.session.commit() log_user_out() - flash("Your account has been deleted.", "success") + flash("Your account and all data have been permanently deleted.", "success") return redirect(url_for("public.home")) diff --git a/routes/leagues.py b/routes/leagues.py index 0a828fc..804121f 100644 --- a/routes/leagues.py +++ b/routes/leagues.py @@ -1603,6 +1603,29 @@ def archive_league(league_ref: str): return redirect(url_for("leagues.index")) +@leagues_bp.post("/l//delete") +@login_required +def delete_league_route(league_ref: str): + if not db_ready(): + flash("League database is not available.", "error") + return redirect(url_for("public.home")) + + from league_repositories import delete_league + + league = require_league(league_ref, {"owner"}) + confirm_name = request.form.get("confirm_name", "").strip() + + if confirm_name != league.name: + flash("League name did not match. Deletion cancelled.", "error") + return redirect(url_for("leagues.league_settings", league_ref=league_ref)) + + league_name = league.name + delete_league(league) + db.session.commit() + flash(f'"{league_name}" and all its data have been permanently deleted.', "success") + return redirect(url_for("leagues.index")) + + @leagues_bp.get("/l//ledger/export") @login_required def export_ledger_csv(league_ref: str): diff --git a/static/css/style.css b/static/css/style.css index 48d7c58..1710c1a 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -865,6 +865,17 @@ fieldset[disabled] button { box-shadow: 0 0 0 3px var(--accent-a22); } +/* Custom chevron for all themed selects */ +.form-card select, +.field-select, +.session-filter-select { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2 4l4 4 4-4' stroke='%23999' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + padding-right: 30px; + cursor: pointer; +} + /* ================================================================ TABLE ================================================================ */ diff --git a/templates/account_settings.html b/templates/account_settings.html index fb4eddc..323fa2d 100644 --- a/templates/account_settings.html +++ b/templates/account_settings.html @@ -67,16 +67,66 @@
- Delete account -

Permanently disables your account and archives all leagues you own. This cannot be undone.

+ Disable account +

Disables your account and archives all leagues you own. Your data is preserved — you can sign up again with the same email to restore access.

- + +
+
+
+ Delete account +

Permanently deletes your account and all leagues, sessions, and ledger data you own. This cannot be undone.

+
+
+ + + + +
+
+ Delete league +

Permanently deletes this league and all players, sessions, and ledger data. This cannot be undone.

+
+ +
@@ -231,6 +238,37 @@ + +