diff --git a/.gitignore b/.gitignore index 30e9aa1..1db4877 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .env node_modules -/Input .windsurfrules \ No newline at end of file diff --git a/Input/Coding patterns preferences b/Input/Coding patterns preferences new file mode 100644 index 0000000..6e1490b --- /dev/null +++ b/Input/Coding patterns preferences @@ -0,0 +1,12 @@ +# Coding pattern preferences + +– Always prefer simple solutions +– Avoid duplication of code whenever possible, which means checking for other areas of the codebase that might already have similar code and functionality +– You are careful to only make changes that are requested or you are confident are well understood and related to the change being requested +– When fixing an issue or bug, do not introduce a new pattern or technology without first exhausting all options for the existing implementation. And if you finally do this, make sure to remove the old implementation afterwards so we don't have duplicate logic. +– Keep the codebase very clean and organized +– Avoid writing scripts in files if possible, especially if the script is likely only to be run once +– Avoid having files over 200–300 lines of code. Refactor at that point. +– Mocking data is only needed for tests, never mock data for dev or prod +– Never add stubbing or fake data patterns to code that affects the dev or prod environments +– Never overwrite my .env file without first asking and confirming diff --git a/Input/Sport Attendance Sheet MVP.md b/Input/Sport Attendance Sheet MVP.md new file mode 100644 index 0000000..35ab262 --- /dev/null +++ b/Input/Sport Attendance Sheet MVP.md @@ -0,0 +1,54 @@ +**Core Features:** + +1. **Attendance Table** + - A table with: + - Rows: Each game date (in DD/MM/YY format). + - Columns: Each player’s name (8 fixed names) and one column for a guest (labeled “Guest” or “Mystery”). + - Each cell (except for the date) is clickable to mark attendance (“Yes” or blank). + - The user should be able to adapt the name of the players. + +2. **Add/Edit Dates** + - Ability to add a new date (row) to the table. + +3. **Mark Attendance** + - Clicking a cell toggles attendance for that player/guest on that date. + - “Yes” means attending; blank means not attending. + - The user should be able to adapt the name of the guest. + - The user should be able to adapt the name of the players. + - The user should be able only to choose from Yes or blank to mark attendance. + +4. **Data Persistence** + - The table’s state (who is attending which date) is saved and loaded automatically (from a JSON file). + - No login or authentication needed (handled by your reverse proxy). + +5. **Guest/Mystery Player** + - The last column is always for a guest. You can leave the name as “Guest” or allow it to be edited per date. + +--- + +**What you don’t need for MVP:** +- User registration, login, or roles. +- Notifications, reminders, or analytics. +- Player management (names are fixed). +- Complex UI-just a simple table. + +--- + +**How it works (user flow):** +- User opens the web app. +- Sees a table with upcoming dates and player names. +- Clicks on their name under a date to mark themselves as attending (“Yes”). +- Guest attendance can also be marked. +- Data is saved automatically. + +--- + +**Technical outline:** +- **Frontend:** Simple HTML table, checkboxes or clickable cells, minimal CSS. +- **Backend:** JSON file for storing attendance. +- **No authentication logic needed** (handled at the proxy level). + +--- + +**Summary:** +You only need a simple, editable attendance table with dates as rows and player names (plus guest) as columns. Users can mark their attendance with a click. No login, no user management, just a fast and easy attendance tracker. \ No newline at end of file diff --git a/Input/Sport Attendance Sheet.xlsx b/Input/Sport Attendance Sheet.xlsx new file mode 100644 index 0000000..8a9847a Binary files /dev/null and b/Input/Sport Attendance Sheet.xlsx differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..a7ff33f --- /dev/null +++ b/app.py @@ -0,0 +1,46 @@ +from flask import Flask, render_template, request, jsonify +import json +import os + +app = Flask(__name__) +DATA_FILE = 'attendance_data.json' + +# Default player names +DEFAULT_PLAYERS = [ + "Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hannah" +] +DEFAULT_GUEST = "Guest" + +def load_data(): + if not os.path.exists(DATA_FILE): + data = { + "players": DEFAULT_PLAYERS, + "guest": DEFAULT_GUEST, + "dates": [], + "attendance": {} + } + with open(DATA_FILE, 'w') as f: + json.dump(data, f) + with open(DATA_FILE, 'r') as f: + return json.load(f) + +def save_data(data): + with open(DATA_FILE, 'w') as f: + json.dump(data, f, indent=2) + +@app.route('/') +def index(): + return render_template('index.html') + +@app.route('/api/data', methods=['GET']) +def get_data(): + return jsonify(load_data()) + +@app.route('/api/data', methods=['POST']) +def update_data(): + data = request.json + save_data(data) + return jsonify({"status": "success"}) + +if __name__ == '__main__': + app.run(debug=True) diff --git a/attendance_data.json b/attendance_data.json new file mode 100644 index 0000000..61ba1d1 --- /dev/null +++ b/attendance_data.json @@ -0,0 +1,21 @@ +{ + "attendance": { + "08/05/25|8": true, + "08/05/25|4": true + }, + "dates": [ + "08/05/25", + "5=5" + ], + "guest": "Guest", + "players": [ + "Alice", + "Bob", + "Charlie", + "David", + "Eve", + "Frank", + "Grace", + "Hannah" + ] +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +flask diff --git a/static/app.js b/static/app.js new file mode 100644 index 0000000..b5a104a --- /dev/null +++ b/static/app.js @@ -0,0 +1,98 @@ +let data = {}; + +function fetchData() { + fetch('/api/data').then(r => r.json()).then(d => { + data = d; + renderTable(); + }); +} + +function saveData() { + fetch('/api/data', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); +} + +function renderTable() { + const container = document.getElementById('attendance-table'); + container.innerHTML = ''; + const table = document.createElement('table'); + // Header row + const thead = document.createElement('thead'); + const headRow = document.createElement('tr'); + headRow.appendChild(document.createElement('th')).innerText = 'Date'; + data.players.forEach((name, i) => { + const th = document.createElement('th'); + const input = document.createElement('input'); + input.type = 'text'; + input.value = name; + input.onchange = e => { + data.players[i] = e.target.value; + saveData(); + renderTable(); + }; + th.appendChild(input); + headRow.appendChild(th); + }); + // Guest column + const guestTh = document.createElement('th'); + const guestInput = document.createElement('input'); + guestInput.type = 'text'; + guestInput.value = data.guest; + guestInput.onchange = e => { + data.guest = e.target.value; + saveData(); + renderTable(); + }; + guestTh.appendChild(guestInput); + headRow.appendChild(guestTh); + thead.appendChild(headRow); + table.appendChild(thead); + // Body rows + const tbody = document.createElement('tbody'); + data.dates.forEach((date, rowIdx) => { + const tr = document.createElement('tr'); + // Date cell + const dateTd = document.createElement('td'); + dateTd.innerText = date; + tr.appendChild(dateTd); + // Player attendance + [...data.players, data.guest].forEach((player, colIdx) => { + const td = document.createElement('td'); + td.className = 'clickable'; + const key = `${date}|${colIdx}`; + if (data.attendance[key]) { + td.innerText = 'Yes'; + td.classList.add('yes'); + } else { + td.innerText = ''; + } + td.onclick = () => { + if (data.attendance[key]) { + delete data.attendance[key]; + } else { + data.attendance[key] = true; + } + saveData(); + renderTable(); + }; + tr.appendChild(td); + }); + tbody.appendChild(tr); + }); + table.appendChild(tbody); + container.appendChild(table); +} + +document.getElementById('add-date').onclick = function() { + const date = prompt('Enter date (DD/MM/YY):'); + if (date && !data.dates.includes(date)) { + data.dates.push(date); + saveData(); + renderTable(); + } +}; + +window.onload = fetchData; diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..550750e --- /dev/null +++ b/templates/index.html @@ -0,0 +1,23 @@ + + + + + Sport Attendance Sheet + + + +

Sport Attendance Sheet

+
+ + + +