mirror of
https://github.com/kbenestad/ClubLedger.git
synced 2026-06-18 09:44:33 +00:00
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:
parent
59b49a3d3a
commit
ca344e3762
9 changed files with 31 additions and 31 deletions
24
main.py
24
main.py
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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 */ }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* ClubLedger – bar page */
|
||||
/* ClubLedger - bar page */
|
||||
|
||||
let barMember = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* ClubLedger – cashier page */
|
||||
/* ClubLedger - cashier page */
|
||||
|
||||
let cashierMember = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* ClubLedger – shared helpers */
|
||||
/* ClubLedger - shared helpers */
|
||||
|
||||
let cfg = { currency_unit: 'pence', currency_symbol: '£', currency_divisor: 100, club_name: 'ClubLedger' };
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue