fix: remove all non-ASCII characters that cause Windows encoding issues

- PIN input: removed literal bullet placeholder (was the reported gibberish)
- Search placeholders: ... -> ... (ASCII)
- Custom... period option in statement: uses … entity
- app.js/common.js/style.css/bar.js/cashier.js: en/em dashes in
  comments -> ASCII hyphens; Unicode minus sign U+2212 -> plain hyphen
- main.py: arrows and dashes in comments -> ASCII
- bar.html/cashier.html: standalone page titles and placeholders fixed

Intentional: pound sign in currency defaults stays as UTF-8 (served
with Content-Type: text/html; charset=utf-8 and correct HTTP headers).

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
This commit is contained in:
Claude 2026-05-31 03:48:14 +00:00
parent 59b49a3d3a
commit ca344e3762
No known key found for this signature in database
9 changed files with 31 additions and 31 deletions

24
main.py
View file

@ -1,5 +1,5 @@
"""
ClubLedger Store Credit Web App
ClubLedger - Store Credit Web App
Hard defaults live in CONFIG below; everything is overridable via the Admin UI.
"""
@ -96,7 +96,7 @@ DB_PATH = "clubledger.db"
STAFF_FILE = Path(__file__).parent / "staff.json"
static_dir = Path(__file__).parent / "static"
# In-memory sessions: token {user_id, name, role, expires}
# In-memory sessions: token -> {user_id, name, role, expires}
_sessions: dict = {}
# Cached settings merged from CONFIG + DB app_settings
@ -232,7 +232,7 @@ def migrate_db():
conn.execute("DROP TABLE _ledger_entries_old")
conn.execute("CREATE INDEX IF NOT EXISTS idx_ledger_member ON ledger_entries(member_id)")
# --- app_settings: rename allow_negative_balance overdraft_policy ---
# --- app_settings: rename allow_negative_balance -> overdraft_policy ---
row = conn.execute(
"SELECT value FROM app_settings WHERE key='allow_negative_balance'"
).fetchone()
@ -260,8 +260,8 @@ def seed_admin():
("Administrator", "admin", pw, "admin")
)
print("=" * 60)
print(" Default admin created username: admin password: admin")
print(" Change this immediately in the Admin Staff Accounts area.")
print(" Default admin created -> username: admin password: admin")
print(" Change this immediately in the Admin -> Staff Accounts area.")
print("=" * 60)
def refresh_settings():
@ -782,7 +782,7 @@ def _period_bounds(period: str, s: dict,
start = _local(fd); end = _local(td) + timedelta(days=1)
except ValueError:
start = _local(today); end = start + timedelta(days=1)
else: # fallback today
else: # fallback -> today
start = _local(today); end = start + timedelta(days=1)
fmt = "%Y-%m-%d %H:%M:%S"
@ -869,7 +869,7 @@ def transactions(member_id: int, limit: int = 50, offset: int = 0,
}
# ---------------------------------------------------------------------------
# Print views (no auth opened as new-tab popups)
# Print views (no auth - opened as new-tab popups)
# ---------------------------------------------------------------------------
def _print_size_script(s: dict):
@ -996,7 +996,7 @@ _STMT_PERIODS = [
("last_quarter", "Last quarter"),
("year", "This year"),
("last_year", "Last year"),
("custom", "Custom"),
("custom", "Custom…"),
]
def _stmt_period_selector(period: str, from_date: str, to_date: str) -> str:
@ -1287,7 +1287,7 @@ def remove_staff(name: str, user: dict = Depends(current_user)):
return {"staff": staff}
# ---------------------------------------------------------------------------
# Admin staff accounts
# Admin - staff accounts
# ---------------------------------------------------------------------------
@app.get("/admin/staff-accounts")
@ -1354,7 +1354,7 @@ def delete_staff_account(account_id: int, user: dict = Depends(admin_user)):
return {"ok": True}
# ---------------------------------------------------------------------------
# Admin app settings
# Admin - app settings
# ---------------------------------------------------------------------------
@app.get("/admin/settings")
@ -1379,7 +1379,7 @@ def update_admin_settings(body: AppSettingsUpdate, user: dict = Depends(admin_us
return _settings
# ---------------------------------------------------------------------------
# Admin logo upload
# Admin - logo upload
# ---------------------------------------------------------------------------
@app.post("/admin/logo")
@ -1400,7 +1400,7 @@ async def upload_logo(file: UploadFile = File(...), user: dict = Depends(admin_u
return {"url": url}
# ---------------------------------------------------------------------------
# Config (public loaded by frontend before login screen shows)
# Config (public - loaded by frontend before login screen shows)
# ---------------------------------------------------------------------------
@app.get("/config")

View file

@ -1,4 +1,4 @@
/* ClubLedger main SPA */
/* ClubLedger - main SPA */
let currentUser = null;
let cashierMember = null;
@ -7,7 +7,7 @@ let editMemberId = null;
let editAccountId = null;
// ---------------------------------------------------------------------------
// Boot check session, then either show login or start the app
// Boot - check session, then either show login or start the app
// ---------------------------------------------------------------------------
(async function boot() {
// Load config first so the login page shows the club name
@ -80,7 +80,7 @@ function populateTimezoneList() {
list.innerHTML = Intl.supportedValuesOf('timeZone')
.map(z => `<option value="${z}">`)
.join('');
} catch (e) { /* older browser plain text input still works */ }
} catch (e) { /* older browser -- plain text input still works */ }
}
async function startApp() {
@ -334,9 +334,9 @@ async function loadCashierStats() {
setCol('statsWithdrawals', 'statsWithdrawalsCount', d.withdrawals, 'stats-negative');
setCol('statsCharges', 'statsChargesCount', d.charges, 'stats-negative');
const netEl = document.getElementById('statsNet');
netEl.textContent = (d.net.negative ? '' : '+') + d.net.display;
netEl.textContent = (d.net.negative ? '-' : '+') + d.net.display;
netEl.className = 'stats-col-value ' + (d.net.negative ? 'stats-negative' : 'stats-positive');
} catch (e) { /* silently ignore widgets are non-critical */ }
} catch (e) { /* silently ignore -- widgets are non-critical */ }
}
// ---------------------------------------------------------------------------

View file

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bar ClubLedger</title>
<title>Bar - ClubLedger</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
@ -19,7 +19,7 @@
<div class="panel">
<h2>Charge Account</h2>
<div class="search-row">
<input type="text" id="barSearch" placeholder="Search member">
<input type="text" id="barSearch" placeholder="Search member...">
<button class="btn" onclick="barSearchMembers()">Search</button>
</div>
<div id="barMemberList" class="member-pick-list"></div>

View file

@ -1,4 +1,4 @@
/* ClubLedger bar page */
/* ClubLedger - bar page */
let barMember = null;

View file

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cashier ClubLedger</title>
<title>Cashier - ClubLedger</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
@ -41,7 +41,7 @@
<div class="panel">
<h2>Top Up Account</h2>
<div class="search-row">
<input type="text" id="cashierSearch" placeholder="Search member">
<input type="text" id="cashierSearch" placeholder="Search member...">
<button class="btn" onclick="cashierSearchMembers()">Search</button>
</div>
<div id="cashierMemberList" class="member-pick-list"></div>
@ -70,7 +70,7 @@
<div class="panel">
<h2>Members</h2>
<div class="search-row">
<input type="text" id="memberSearch" placeholder="Search name or number">
<input type="text" id="memberSearch" placeholder="Search name or number...">
<button class="btn" onclick="searchMembers()">Search</button>
</div>
<table id="memberTable" class="data-table">

View file

@ -1,4 +1,4 @@
/* ClubLedger cashier page */
/* ClubLedger - cashier page */
let cashierMember = null;

View file

@ -1,4 +1,4 @@
/* ClubLedger shared helpers */
/* ClubLedger - shared helpers */
let cfg = { currency_unit: 'pence', currency_symbol: '£', currency_divisor: 100, club_name: 'ClubLedger' };

View file

@ -81,7 +81,7 @@
<div class="panel">
<h2>Members</h2>
<div class="search-row">
<input type="text" id="memberSearch" placeholder="Search name or number">
<input type="text" id="memberSearch" placeholder="Search name or number...">
<button class="btn" onclick="searchMembers()">Search</button>
</div>
<table id="memberTable" class="data-table">
@ -154,7 +154,7 @@
<div class="panel">
<h2>Top Up Account</h2>
<div class="search-row">
<input type="text" id="cashierSearch" placeholder="Search member">
<input type="text" id="cashierSearch" placeholder="Search member...">
<button class="btn" onclick="cashierSearchMembers()">Search</button>
</div>
<div id="cashierMemberList" class="member-pick-list"></div>
@ -225,7 +225,7 @@
<div class="panel">
<h2>Charge Account</h2>
<div class="search-row">
<input type="text" id="barSearch" placeholder="Search member">
<input type="text" id="barSearch" placeholder="Search member...">
<button class="btn" onclick="barSearchMembers()">Search</button>
</div>
<div id="barMemberList" class="member-pick-list"></div>
@ -253,7 +253,7 @@
<div class="pin-charge-amount" id="pinAmount"></div>
<div class="pin-member-name" id="pinMember"></div>
<div class="pin-instruction">Please enter your PIN</div>
<input type="password" id="barPin" class="pin-input" placeholder="••••"
<input type="password" id="barPin" class="pin-input"
maxlength="20" autocomplete="off" inputmode="numeric"
onkeydown="if(event.key==='Enter') confirmCharge()">
<div id="pinMsg" class="msg"></div>

View file

@ -1,4 +1,4 @@
/* ClubLedger GitHub-style theme */
/* ClubLedger - GitHub-style theme */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
@ -43,7 +43,7 @@ nav {
z-index: 100;
}
/* On desktop the wrapper is invisible buttons behave as direct nav children */
/* On desktop the wrapper is invisible -- buttons behave as direct nav children */
.nav-tabs { display: contents; }
.brand {