From c0c4897f2ef9eb9ae444bc05407c3cdf420c3461 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sat, 15 Nov 2025 15:48:50 +0100 Subject: Trying cards instead of a table --- index.html | 59 ++++++++++++++++++++++++----------------------------------- src/data.js | 29 ++++++++++++++++++++--------- src/style.css | 15 ++++++++------- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/index.html b/index.html index b839ede..5c31576 100644 --- a/index.html +++ b/index.html @@ -6,40 +6,33 @@ Match Up! -
+
-
+

- - - - - - - - - -
CourtTeam 1Team 2
+
-
- -
+
+ +
- +

Courts

-
+

Reserve courts for training or competition.

+

Regulars

-
+

Check in and check out club members.

+

Guests

-
+

Add guests to the pool.

+

Admin

Changes here are permanent and cannot be reverted.

- -
-
-
+
diff --git a/src/data.js b/src/data.js index 589b5a3..1d4576d 100644 --- a/src/data.js +++ b/src/data.js @@ -15,7 +15,7 @@ class Switch { } render() { let color = this.status ? "is-success" : "is-danger"; - let type = `class="button is-medium ${color}"`; + let type = `class="button ${color}"`; let id = this.description + "_" + this.id; return ``; } @@ -69,9 +69,15 @@ export class Team { this.playerTwo.print(); } render() { - let p1 = `${this.playerOne}`; - let p2 = `${this.playerTwo}`; - return p1 + p2; + + return `
+
+
+

${this.playerOne}

+

${this.playerTwo}

+
+
+
`; } } @@ -90,10 +96,15 @@ export class Game extends Switch { } render() { let blockedState = this.teamOne.playerOne === "RESERVED"; - let type = blockedState ? `class="is-dark"` : ``; - let court = `${ - this.id + 1 - }`; - return `${court}${this.teamOne.render()}${this.teamTwo.render()}`; + let placeHolder = `

Reserved

` + let type = blockedState ? placeHolder : this.teamOne.render() + this.teamTwo.render(); + return `
+
+

Court ${this.id + 1}

+
+
+ ${type} +
+
`; } } diff --git a/src/style.css b/src/style.css index 73550ca..cb46ed5 100644 --- a/src/style.css +++ b/src/style.css @@ -1,17 +1,18 @@ -.button-grid { +.grid-container { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(6, 1fr); grid-gap: 15px; } +.card { + height: 100%; +} #timer-display { text-align: center; - width: fit-content; -} -table { - font-size: 1.5em; + width: 100px; } + @media (max-width: 900px) { - .button-grid { + .grid-container { grid-template-columns: repeat(1, 1fr); } } \ No newline at end of file -- cgit v1.3.1 From 7cec8794b6616d294b6a0e8aab3b9d8b1dcdb6db Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sat, 15 Nov 2025 19:12:25 +0100 Subject: Added the change to Pico CSS --- .gitignore | 3 ++ index.html | 141 +++++++++++++++++++++++++----------------------------- package-lock.json | 7 +++ package.json | 1 + public/icon.png | Bin 1453 -> 0 bytes public/icon.svg | 5 ++ src/data.js | 58 +++++++++++----------- src/main.js | 25 ++++------ src/manage.js | 50 ++++++++++++------- src/style.css | 10 ++-- src/timer.js | 9 +++- 11 files changed, 163 insertions(+), 146 deletions(-) delete mode 100644 public/icon.png create mode 100644 public/icon.svg diff --git a/.gitignore b/.gitignore index bb4dc45..79c70fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Mac files +.DS_Store + # Data files *.csv diff --git a/index.html b/index.html index 5c31576..0b0e641 100644 --- a/index.html +++ b/index.html @@ -6,82 +6,73 @@ Match Up! -
-
-
- + + + - + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2e440f5..3c62e96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@fortawesome/fontawesome-free": "^7.1.0", + "@picocss/pico": "^2.1.1", "bulma": "^1.0.4", "notyf": "^3.10.0" }, @@ -92,6 +93,12 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@picocss/pico": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@picocss/pico/-/pico-2.1.1.tgz", + "integrity": "sha512-kIDugA7Ps4U+2BHxiNHmvgPIQDWPDU4IeU6TNRdvXQM1uZX+FibqDQT2xUOnnO2yq/LUHcwnGlu1hvf4KfXnMg==", + "license": "MIT" + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-beta.47", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.47.tgz", diff --git a/package.json b/package.json index 6e96392..5aafe98 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^7.1.0", + "@picocss/pico": "^2.1.1", "bulma": "^1.0.4", "notyf": "^3.10.0" } diff --git a/public/icon.png b/public/icon.png deleted file mode 100644 index 655915f..0000000 Binary files a/public/icon.png and /dev/null differ diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..6f796c9 --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/data.js b/src/data.js index 1d4576d..6abad81 100644 --- a/src/data.js +++ b/src/data.js @@ -14,10 +14,9 @@ class Switch { console.log(this.name + " is currently " + status); } render() { - let color = this.status ? "is-success" : "is-danger"; - let type = `class="button ${color}"`; + let color = this.status ? `` : `class="outline secondary"`; let id = this.description + "_" + this.id; - return ``; + return ``; } } @@ -27,14 +26,15 @@ export class Court extends Switch { } } -export class Player extends Switch { - constructor(id, status, level, games, name) { +export class Regular extends Switch { + constructor(id, status, level, games, firstName, lastName) { super(id, "Regular", status); + console.log(this.constructor.name); this.level = typeof level !== "undefined" ? level : 1; this.games = typeof games !== "undefined" ? games : 0; - if (typeof name !== "undefined") { - this.name = name; - } + this.firstName = typeof firstName !== "undefined" ? firstName : this.description; + this.lastName = typeof lastName !== "undefined" ? lastName : ( this.id + 1 ); + this.name = this.firstName + " " + this.lastName; } } @@ -54,10 +54,16 @@ export class Guest extends Switch { this.level = guestCategories[categoryId].level; this.games = typeof games !== "undefined" ? games : 0; const guestNumber = (id % this.maxCount) + 1; - this.name = guestCategories[categoryId].description + " " + guestNumber; + this.firstName = guestCategories[categoryId].description; + this.lastName = guestNumber; + this.name = this.firstName + " " + this.lastName; } } +function renderPlayerName(player) { + return `${player.firstName} ${player.lastName}`; +} + export class Team { constructor(playerOne, playerTwo) { this.playerOne = playerOne; @@ -69,15 +75,9 @@ export class Team { this.playerTwo.print(); } render() { - - return `
-
-
-

${this.playerOne}

-

${this.playerTwo}

-
-
-
`; + const p1 = renderPlayerName(this.playerOne); + const p2 = renderPlayerName(this.playerTwo); + return p1 + `

` + p2; } } @@ -88,23 +88,19 @@ export class Game extends Switch { this.teamTwo = teamTwo; } print() { - let teamOne = this.teamOne.playerOne + " and " + this.teamOne.playerTwo; - let teamTwo = this.teamTwo.playerOne + " and " + this.teamTwo.playerTwo; + const teamOne = this.teamOne.playerOne.firstName + " and " + this.teamOne.playerTwo.firstName; + const teamTwo = this.teamTwo.playerOne.firstName + " and " + this.teamTwo.playerTwo.firstName; console.log( "Game " + this.id + ": " + teamOne + " is playing against " + teamTwo ); } render() { - let blockedState = this.teamOne.playerOne === "RESERVED"; - let placeHolder = `

Reserved

` - let type = blockedState ? placeHolder : this.teamOne.render() + this.teamTwo.render(); - return `
-
-

Court ${this.id + 1}

-
-
- ${type} -
-
`; + const blockedState = this.teamOne.playerOne.firstName === "RESERVED"; + const playerState = this.teamOne.render() + "
"+ this.teamTwo.render(); + const type = blockedState ? `
Reserved

` : playerState; + return `
+

Court ${this.id + 1}


+ ${type} +
`; } } diff --git a/src/main.js b/src/main.js index ec33b18..6d8f35b 100644 --- a/src/main.js +++ b/src/main.js @@ -1,19 +1,11 @@ +import "@picocss/pico"; import "notyf/notyf.min.css"; -import "bulma/css/bulma.css"; import "@fortawesome/fontawesome-free/css/all.css"; import "./style.css"; -import { Notyf } from "notyf"; import { Timer } from "./timer"; -import { Team, Game } from "./data"; +import { Team, Game, Regular } from "./data"; import { Manager, CourtManager, RegularManager, GuestManager } from "./manage"; -export function getNotifier(seconds) { - return new Notyf({ - duration: seconds * milliSecond, - position: { x: "center", y: "top" }, - dismissible: true, - }); -} export class GameManager extends Manager { timer = new Timer(); @@ -31,7 +23,9 @@ export class GameManager extends Manager { if (typeof description === "undefined") { description = "---"; } - let team = new Team(description, description); + let player = new Regular(null, null, null, null, description, ""); + console.log(player); + let team = new Team(player, player); return new Game(i, true, team, team); } loadItem(data) { @@ -69,8 +63,8 @@ export class GameManager extends Manager { const startIndex = 4 * index; const endIndex = startIndex + 4; const courtPlayers = selectedPlayers.slice(startIndex, endIndex); - const teamOne = new Team(courtPlayers[0].name, courtPlayers[3].name); - const teamTwo = new Team(courtPlayers[1].name, courtPlayers[2].name); + const teamOne = new Team(courtPlayers[0], courtPlayers[3]); + const teamTwo = new Team(courtPlayers[1], courtPlayers[2]); const game = new Game(usedCourts[index].id, false, teamOne, teamTwo); this.data.push(game); } @@ -125,7 +119,6 @@ export class GameManager extends Manager { confirm() { let confirmedStatus = this.data[0].status; if (confirmedStatus) { - getNotifier().error("Create a proposal first!"); return; } console.log("Confirm the proposal for round " + this.round + "."); @@ -144,7 +137,7 @@ export class GameManager extends Manager { localStorage.removeItem(this.description); localStorage.removeItem(this.courts.description); localStorage.removeItem(this.guests.description); - // Clear the number of games played + this.regulars.reset(); document.getElementById("timer-stop").click(); window.location.reload(); } @@ -164,11 +157,9 @@ function addGameBehaviour(g) { }); document.getElementById("confirm").addEventListener("click", () => { g.confirm(); - getNotifier().success("Round confirmed!"); }); document.getElementById("reset").addEventListener("click", () => { g.reset(); - getNotifier().error("Session has been reset!"); }); } diff --git a/src/manage.js b/src/manage.js index 7449866..f8fe220 100644 --- a/src/manage.js +++ b/src/manage.js @@ -1,4 +1,4 @@ -import { Court, Guest, Player } from "./data"; +import { Court, Guest, Regular } from "./data"; export class Manager { constructor(description, maxCount) { @@ -49,8 +49,8 @@ export class Manager { p.addEventListener("click", () => { this.data[idx].toggle(); this.store(); - p.classList.toggle("is-success"); - p.classList.toggle("is-danger"); + p.classList.toggle("secondary"); + p.classList.toggle("outline"); }); } } @@ -94,8 +94,10 @@ function normalize(data) { } export class GuestManager extends Manager { - constructor() { - super("Guest", 18); + constructor(description, count) { + if ( typeof description === "undefined" ) { description = "Guest"; }; + if ( typeof count === "undefined" ) { count = 18; }; + super(description, count); } createItem(i) { return new Guest(i, randomStatus()); @@ -109,6 +111,13 @@ export class GuestManager extends Manager { active() { return normalize(super.active()); } + update(list) { + for (let index = 0; index < this.data.length; index++ ){ + if (this.data[index].name in list) { + this.data[index].games += 1; + } + } + } } export function randomLevel() { @@ -116,33 +125,38 @@ export function randomLevel() { return Math.floor(Math.random() * maxLevel + 1); } -export class RegularManager extends Manager { +export class RegularManager extends GuestManager { constructor() { super("Regular", 71); addLoadBehaviour(this); } createItem(i) { - return new Player(i, randomStatus(), randomLevel()); + return new Regular(i, randomStatus(), randomLevel()); } loadItem(data) { let id = Number(data.id); - let name = data.name; let level = Number(data.level); let status = Boolean(Number(data.status)); let games = Number(data.games); - return new Player(id, status, level, games, name); - } - active() { - return normalize(super.active()); + let firstName = data.firstName; + let lastName = data.lastName; + return new Regular(id, status, level, games, firstName, lastName); + } + reset() { + for (let index = 0; index < this.data.length; index++ ){ + this.data[index].games = 0; + this.data[index].status = false; + } } } function parseLineToPlayer(data) { - var info = data.split(","); - var id = Number(info[0].trim()); - var name = info[1].trim() + " " + info[2].trim(); - var level = Number(info[3].trim()); - return new Player(id, randomStatus(), level, 0, name); + const info = data.split(","); + const id = Number(info[0].trim()); + const firstName = info[1].trim(); + const lastName = info[2].trim(); + const level = Number(info[3].trim()); + return new Regular(id, randomStatus(), level, 0, firstName, lastName); } function parseCsv(data) { @@ -162,7 +176,7 @@ function parseCsv(data) { } function addLoadBehaviour(p) { - const f = document.querySelector("#player-load input[type=file]"); + const f = document.getElementById("player-load"); f.addEventListener("change", () => { const file = f.files[0]; let importData; diff --git a/src/style.css b/src/style.css index cb46ed5..197597b 100644 --- a/src/style.css +++ b/src/style.css @@ -3,16 +3,18 @@ grid-template-columns: repeat(6, 1fr); grid-gap: 15px; } -.card { - height: 100%; -} #timer-display { text-align: center; width: 100px; } +@media (min-width: 900px) { + #Regular_list button { + height: 100px; + } +} @media (max-width: 900px) { .grid-container { grid-template-columns: repeat(1, 1fr); } -} \ No newline at end of file +} diff --git a/src/timer.js b/src/timer.js index c00ffdc..9b0a8b9 100644 --- a/src/timer.js +++ b/src/timer.js @@ -1,3 +1,5 @@ +import { Notyf } from "notyf"; + const milliSecond = 1000; export class Timer { @@ -42,7 +44,12 @@ export class Timer { return; } p.reset(); - getNotifier().error("Round completed"); + const n = new Notyf({ + duration: 0, + position: { x: "center", y: "top" }, + dismissible: true, + }); + n.error("Round completed"); } start(fresh) { if (fresh) { -- cgit v1.3.1 From 4ff9794fa2b545134efe341ad012667c67f70d15 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sat, 15 Nov 2025 20:09:51 +0100 Subject: Added the changes to include the game counting --- index.html | 12 ++++++------ src/data.js | 32 +++++++++++++++++++------------- src/main.js | 28 ++++++++++++++++++++++------ src/manage.js | 8 +++++--- src/style.css | 4 +++- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/index.html b/index.html index 0b0e641..7a47a8d 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@
  • -
    +
    @@ -24,8 +24,8 @@
    -

    -
    +

    +
    @@ -44,21 +44,21 @@

    Courts

    Reserve courts for training or competition.

    -
    +

    Regulars

    Check in and check out club members.

    -
    +

    Guests

    Add guests to the pool.

    -
    +
    diff --git a/src/data.js b/src/data.js index 6abad81..3e133ee 100644 --- a/src/data.js +++ b/src/data.js @@ -1,9 +1,9 @@ class Switch { - constructor(id, description, status) { + constructor(id, status) { this.id = id; this.status = typeof status !== "undefined" ? status : false; - this.description = description; - this.name = description + " " + (id + 1); + this.description = this.constructor.name; + this.name = this.description + " " + (id + 1); } toggle() { this.status = !this.status; @@ -22,18 +22,18 @@ class Switch { export class Court extends Switch { constructor(id, status) { - super(id, "Court", status); + super(id, status); } } export class Regular extends Switch { constructor(id, status, level, games, firstName, lastName) { - super(id, "Regular", status); - console.log(this.constructor.name); + super(id, status); this.level = typeof level !== "undefined" ? level : 1; this.games = typeof games !== "undefined" ? games : 0; - this.firstName = typeof firstName !== "undefined" ? firstName : this.description; - this.lastName = typeof lastName !== "undefined" ? lastName : ( this.id + 1 ); + this.firstName = + typeof firstName !== "undefined" ? firstName : this.description; + this.lastName = typeof lastName !== "undefined" ? lastName : this.id + 1; this.name = this.firstName + " " + this.lastName; } } @@ -49,7 +49,7 @@ export class Guest extends Switch { categoryCount = 3; constructor(id, status, games) { - super(id, "Guest", status); + super(id, status); const categoryId = Math.floor(id / this.maxCount) || 0; this.level = guestCategories[categoryId].level; this.games = typeof games !== "undefined" ? games : 0; @@ -83,20 +83,26 @@ export class Team { export class Game extends Switch { constructor(id, status, teamOne, teamTwo) { - super(id, "Game", status); + super(id, status); this.teamOne = teamOne; this.teamTwo = teamTwo; } print() { - const teamOne = this.teamOne.playerOne.firstName + " and " + this.teamOne.playerTwo.firstName; - const teamTwo = this.teamTwo.playerOne.firstName + " and " + this.teamTwo.playerTwo.firstName; + const teamOne = + this.teamOne.playerOne.firstName + + " and " + + this.teamOne.playerTwo.firstName; + const teamTwo = + this.teamTwo.playerOne.firstName + + " and " + + this.teamTwo.playerTwo.firstName; console.log( "Game " + this.id + ": " + teamOne + " is playing against " + teamTwo ); } render() { const blockedState = this.teamOne.playerOne.firstName === "RESERVED"; - const playerState = this.teamOne.render() + "
    "+ this.teamTwo.render(); + const playerState = this.teamOne.render() + "
    " + this.teamTwo.render(); const type = blockedState ? `
    Reserved

    ` : playerState; return `

    Court ${this.id + 1}


    diff --git a/src/main.js b/src/main.js index 6d8f35b..3732e51 100644 --- a/src/main.js +++ b/src/main.js @@ -19,14 +19,17 @@ export class GameManager extends Manager { this.render(); addGameBehaviour(this); } - createItem(i, description) { + createItem(i, description, status) { if (typeof description === "undefined") { description = "---"; } + if (typeof status === "undefined") { + status = true; + } let player = new Regular(null, null, null, null, description, ""); console.log(player); let team = new Team(player, player); - return new Game(i, true, team, team); + return new Game(i, status, team, team); } loadItem(data) { let id = Number(data.id); @@ -37,21 +40,22 @@ export class GameManager extends Manager { } addBehaviour() { let confirmedStatus = this.data[0].status; + let description = confirmedStatus ? "Round " + this.round : "Proposal"; - document.getElementById("Game_description").innerHTML = description; + document.getElementById("Game-description").innerHTML = description; } addBlockedGames() { const inactiveCourts = this.courts.inactive(); console.log("Total reserved courts are " + inactiveCourts.length + "."); for (let court of inactiveCourts) { - this.data.push(this.createItem(court.id, "RESERVED")); + this.data.push(this.createItem(court.id, "RESERVED", false)); } } addInactiveGames(possibleMatches) { const activeCourts = this.courts.active(); const unusedCourts = activeCourts.slice(possibleMatches + 1); for (let court of unusedCourts) { - this.data.push(this.createItem(court.id)); + this.data.push(this.createItem(court.id, "---", false)); } } addActiveGames(possibleMatches) { @@ -92,7 +96,7 @@ export class GameManager extends Manager { p.print(); }); const shuffledPlayers = shuffle(activePlayers); - shuffledPlayers.sort((a, b) => a.totalPlayed - b.totalPlayed); + shuffledPlayers.sort((a, b) => a.games - b.games); const selectedPlayers = shuffledPlayers.slice(0, count); selectedPlayers.sort((a, b) => a.level - b.level); console.log("Selected players are:"); @@ -128,6 +132,18 @@ export class GameManager extends Manager { this.data[i].status = true; } // Increment the number of games played + let selectedPlayers = []; + for (let game of this.data) { + console.log(game); + if ( game.teamOne.playerOne.name !== "RESERVED" ){ + selectedPlayers.push(game.teamOne.playerOne.name); + selectedPlayers.push(game.teamOne.playerTwo.name); + selectedPlayers.push(game.teamTwo.playerOne.name); + selectedPlayers.push(game.teamTwo.playerTwo.name); + } + } + this.regulars.incrementGames(selectedPlayers); + this.guests.incrementGames(selectedPlayers); this.store(); this.render(); document.getElementById("timer-start").click(); diff --git a/src/manage.js b/src/manage.js index f8fe220..6ed58bd 100644 --- a/src/manage.js +++ b/src/manage.js @@ -38,7 +38,7 @@ export class Manager { return i.render(); }) .join(""); - let elementId = this.data[0].description + "_list"; + let elementId = this.data[0].description + "-list"; document.getElementById(elementId).innerHTML = renderedHTML; this.addBehaviour(); } @@ -111,12 +111,14 @@ export class GuestManager extends Manager { active() { return normalize(super.active()); } - update(list) { + incrementGames(list) { for (let index = 0; index < this.data.length; index++ ){ - if (this.data[index].name in list) { + if (list.includes(this.data[index].name)) { + console.log("Added game count to " + this.data[index].name); this.data[index].games += 1; } } + this.store(); } } diff --git a/src/style.css b/src/style.css index 197597b..d6ea4de 100644 --- a/src/style.css +++ b/src/style.css @@ -7,7 +7,9 @@ text-align: center; width: 100px; } - +#timer { + margin: 0; +} @media (min-width: 900px) { #Regular_list button { height: 100px; -- cgit v1.3.1 From fef09593445dcf3a56ee9074b88bde7b0a9f33f9 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sat, 15 Nov 2025 21:59:09 +0100 Subject: Added the changes to fix the bug with class name and changed the SVG color to match the buttons --- index.html | 44 +++++++++++++++++++++++++------------------- public/icon.svg | 4 ++-- src/data.js | 12 ++++++------ src/main.js | 6 ++---- src/manage.js | 16 ++++++++++++---- src/style.css | 6 +++++- 6 files changed, 52 insertions(+), 36 deletions(-) diff --git a/index.html b/index.html index 7a47a8d..b99eb3f 100644 --- a/index.html +++ b/index.html @@ -1,22 +1,26 @@ - + Match Up! + + + +
    -
    + +
    - -
    +
@@ -65,13 +69,15 @@

Admin

Changes here are permanent and cannot be reverted.

- - +
+ + +
+ - diff --git a/public/icon.svg b/public/icon.svg index 6f796c9..79fc328 100644 --- a/public/icon.svg +++ b/public/icon.svg @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/data.js b/src/data.js index 3e133ee..f1cf7c9 100644 --- a/src/data.js +++ b/src/data.js @@ -1,8 +1,8 @@ class Switch { - constructor(id, status) { + constructor(id, description, status) { this.id = id; this.status = typeof status !== "undefined" ? status : false; - this.description = this.constructor.name; + this.description = description; this.name = this.description + " " + (id + 1); } toggle() { @@ -22,13 +22,13 @@ class Switch { export class Court extends Switch { constructor(id, status) { - super(id, status); + super(id, "Court", status); } } export class Regular extends Switch { constructor(id, status, level, games, firstName, lastName) { - super(id, status); + super(id, "Regular", status); this.level = typeof level !== "undefined" ? level : 1; this.games = typeof games !== "undefined" ? games : 0; this.firstName = @@ -49,7 +49,7 @@ export class Guest extends Switch { categoryCount = 3; constructor(id, status, games) { - super(id, status); + super(id, "Guest", status); const categoryId = Math.floor(id / this.maxCount) || 0; this.level = guestCategories[categoryId].level; this.games = typeof games !== "undefined" ? games : 0; @@ -83,7 +83,7 @@ export class Team { export class Game extends Switch { constructor(id, status, teamOne, teamTwo) { - super(id, status); + super(id, "Game", status); this.teamOne = teamOne; this.teamTwo = teamTwo; } diff --git a/src/main.js b/src/main.js index 3732e51..654300a 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,4 @@ -import "@picocss/pico"; -import "notyf/notyf.min.css"; -import "@fortawesome/fontawesome-free/css/all.css"; -import "./style.css"; +import "./style.css" import { Timer } from "./timer"; import { Team, Game, Regular } from "./data"; import { Manager, CourtManager, RegularManager, GuestManager } from "./manage"; @@ -180,3 +177,4 @@ function addGameBehaviour(g) { } const g = new GameManager(); + diff --git a/src/manage.js b/src/manage.js index 6ed58bd..83a9b4c 100644 --- a/src/manage.js +++ b/src/manage.js @@ -95,8 +95,12 @@ function normalize(data) { export class GuestManager extends Manager { constructor(description, count) { - if ( typeof description === "undefined" ) { description = "Guest"; }; - if ( typeof count === "undefined" ) { count = 18; }; + if (typeof description === "undefined") { + description = "Guest"; + } + if (typeof count === "undefined") { + count = 18; + } super(description, count); } createItem(i) { @@ -112,7 +116,7 @@ export class GuestManager extends Manager { return normalize(super.active()); } incrementGames(list) { - for (let index = 0; index < this.data.length; index++ ){ + for (let index = 0; index < this.data.length; index++) { if (list.includes(this.data[index].name)) { console.log("Added game count to " + this.data[index].name); this.data[index].games += 1; @@ -145,7 +149,7 @@ export class RegularManager extends GuestManager { return new Regular(id, status, level, games, firstName, lastName); } reset() { - for (let index = 0; index < this.data.length; index++ ){ + for (let index = 0; index < this.data.length; index++) { this.data[index].games = 0; this.data[index].status = false; } @@ -177,6 +181,10 @@ function parseCsv(data) { return players; } +document.getElementById("button-player-load").addEventListener("click", () => { + document.getElementById("player-load").click(); +}); + function addLoadBehaviour(p) { const f = document.getElementById("player-load"); f.addEventListener("change", () => { diff --git a/src/style.css b/src/style.css index d6ea4de..357b043 100644 --- a/src/style.css +++ b/src/style.css @@ -1,3 +1,7 @@ +@import "@picocss/pico"; +@import "notyf/notyf.min.css"; +@import "@fortawesome/fontawesome-free/css/all.css"; + .grid-container { display: grid; grid-template-columns: repeat(6, 1fr); @@ -11,7 +15,7 @@ margin: 0; } @media (min-width: 900px) { - #Regular_list button { + #Regular-list button { height: 100px; } } -- cgit v1.3.1 From 0ce5b4d4ec3303f0fc1e04012e900e5998875981 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sat, 15 Nov 2025 23:59:54 +0100 Subject: Added random name generation and cleaned up the cards --- package-lock.json | 12 +++++++++++- package.json | 3 ++- src/data.js | 3 +-- src/manage.js | 25 ++++++++++++++++++------- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c62e96..b5f333e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "@fortawesome/fontawesome-free": "^7.1.0", "@picocss/pico": "^2.1.1", "bulma": "^1.0.4", - "notyf": "^3.10.0" + "notyf": "^3.10.0", + "unique-names-generator": "^4.7.1" }, "devDependencies": { "vite": "npm:rolldown-vite@7.2.2" @@ -808,6 +809,15 @@ "license": "0BSD", "optional": true }, + "node_modules/unique-names-generator": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz", + "integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/vite": { "name": "rolldown-vite", "version": "7.2.2", diff --git a/package.json b/package.json index 5aafe98..11d527b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@fortawesome/fontawesome-free": "^7.1.0", "@picocss/pico": "^2.1.1", "bulma": "^1.0.4", - "notyf": "^3.10.0" + "notyf": "^3.10.0", + "unique-names-generator": "^4.7.1" } } diff --git a/src/data.js b/src/data.js index f1cf7c9..0b0c9ab 100644 --- a/src/data.js +++ b/src/data.js @@ -105,8 +105,7 @@ export class Game extends Switch { const playerState = this.teamOne.render() + "
" + this.teamTwo.render(); const type = blockedState ? `
Reserved

` : playerState; return `
-

Court ${this.id + 1}


- ${type} +
Court ${this.id + 1}
${type}
`; } } diff --git a/src/manage.js b/src/manage.js index 83a9b4c..10a9896 100644 --- a/src/manage.js +++ b/src/manage.js @@ -1,4 +1,11 @@ import { Court, Guest, Regular } from "./data"; +import { uniqueNamesGenerator, names, adjectives } from 'unique-names-generator'; + +const config = { + dictionaries: [adjectives, names], + style: 'capital', + length: 2 +} export class Manager { constructor(description, maxCount) { @@ -137,15 +144,19 @@ export class RegularManager extends GuestManager { addLoadBehaviour(this); } createItem(i) { - return new Regular(i, randomStatus(), randomLevel()); + const name = uniqueNamesGenerator(config); + console.log(name); + const firstName = name.split("_")[0]; + const lastName = name.split("_")[1]; + return new Regular(i, randomStatus(), randomLevel(), 0, firstName, lastName); } loadItem(data) { - let id = Number(data.id); - let level = Number(data.level); - let status = Boolean(Number(data.status)); - let games = Number(data.games); - let firstName = data.firstName; - let lastName = data.lastName; + const id = Number(data.id); + const level = Number(data.level); + const status = Boolean(Number(data.status)); + const games = Number(data.games); + const firstName = data.firstName; + const lastName = data.lastName; return new Regular(id, status, level, games, firstName, lastName); } reset() { -- cgit v1.3.1 From 2e323bf7c20cc59a8d2017517b14d615344c5863 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sun, 16 Nov 2025 16:17:36 +0100 Subject: Changed the README --- README.md | 89 ++++++++++++++++++++++++--------------------------------- assets/docs.png | 3 ++ src/main.js | 2 -- 3 files changed, 40 insertions(+), 54 deletions(-) create mode 100644 assets/docs.png diff --git a/README.md b/README.md index 98e58f4..98662c4 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,64 @@ # Match Up -Match Up! is a flask web application to create pairings of players from a pool and assign to a limited number of slots. +Match Up! is a web application to create pairings of players from a pool and assign to a limited number of slots. It is [live](https://mu.karanj.com)! Here it is in action: -![Game Page](https://gitlab.com/KaranJayachandra/match_up/-/raw/main/docs/game_screen.png?ref_type=heads) - -![Player Page](https://gitlab.com/KaranJayachandra/match_up/-/raw/main/docs/player_screen.png?ref_type=heads) +![Game Page](https://gitlab.com/KaranJayachandra/match_up/-/raw/main/assets/docs.png?ref_type=heads) Some features of this application are: -- Propose pairings to create a round +- Activate and deactivate named players, guests and courts +- Propose pairings to each court from a set of players + - Considers player levels (optionally mingle players) + - Prioritize players that haven't played as many games - Timer to limit the time for each round -- Create pairings based on player level -- Prioritize players with fewer played games -- Blocking spaces that aren't available any more -- Blocking players that aren't available any more -- Regular and guest players What this application ***IS***: -- Simple: Easy to modify, extend and use +- Open: The source code is available for modifications and extension +- Private: All player data is stored in your browser via local storage What this application ***IS NOT***: -- Secure: This was designed to be used locally on a machine -- Performant: It was designed for use by a single admin user +- Secure: The data is available for anyone to observe via the browser console +- Performant: Works with a small number of named players (<3 MB) -## How to install +## Usage -With [python](https://realpython.com/installing-python/) installed, run the following command preferably in a seperate virtual environment. +- Select or deselect the courts available +- Check in and check out the regular players +- Optionally add guests to the game + - Beginner: Level 1 + - Novice: Level 3 + - Intermediate: Level 6 +- Choose how the pools should be created + - All levels (Disregard levels) + - 2 Levels (Players split into two categories) + - 3 Levels (Players split into three categories) + - Skill based (Consider all levels) +- Request a proposal and confirm to start a round +- Repeat as many times as needed +- Reset the session at the bottom of the page +- You can update the list of players by providing a CSV file. The format is show below: -### Using pip +|id|first_name|last_name|level| +|---|---|---|---| +|104|John|Doe|4| +|104|Jane|Doe|9| -```bash -pip install git+https://gitlab.com/KaranJayachandra/match_up.git -``` +Levels are defined between 1 to 10. -### How to complile from source +## Compilation -Please install [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [python](https://realpython.com/installing-python/) on your machine before proceeding further. Once installed, open the terminal to a folder of your choosing and run the following commands to setup the application. +Please install [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [node](https://nodejs.org/en) on your machine before proceeding further. + +Once installed, open the terminal to a folder of your choosing and run the following commands to setup the application. ```bash git clone https://gitlab.com/KaranJayachandra/match_up.git cd match_up -python -m venv .venv -.venv/Scripts/activate -pip install . -``` - -To run the application, just run: - -```bash -match_up -``` - -The application should be running now in 'localhost', just navigate there using a browser. Use the `--help` option to get an explanation of the application. - -## Startup Automation - -Create a powershell script with the following content in the folder with the code. - -```bash -.venv/Scripts/activate -pythonw match_up +npm install ``` -Create a task using Task Scheduler to run this script on start up. - -## Architectural Design Record - -This part of the README focuses on the motivation of why this application was built and contains background information. Please read on in case you want to know more about the technological choices made. - -This is a simple application build using [flask](https://flask.palletsprojects.com/en/3.0.x/), [htmx](https://htmx.org/) and [Bulma](https://bulma.io/). Flask acts as the backend that generates HTML responses using the wonderful [Jinja](https://jinja.palletsprojects.com/en/3.1.x/) templating language. Interactivity of the application is done using HTMX using simple GET and POST methods to the backend. The application is styled using the Bulma with its easy to used CSS classes. - -- Python was choose to prioritize delivery speed not for performance. More over, the sheer number of Python programmers allows for the application to be easily maintained by others. -- Flask was choosed as it more than enough for the features that I needed instead of Django. The application is to run locally on a laptop and doesn't really need to have complicated features. -- Due to a lack of frontend developers, the simplest UI stack was choosen. HTMX requires no need of JavaScript and was a natural choice. The website needed some basic styling and after looking at a few CSS frameworks, Bulma provided most of the styling. A toasting library, "Notyf" was also used for simple notifications to be sent via some custom javascript code for a simple timer. +To run a development server run `npm run dev` or run `npm run build` to create a optimized bundle of static content. I choose to serve this content via GitLab pages but you could choose to do so in any other server. diff --git a/assets/docs.png b/assets/docs.png new file mode 100644 index 0000000..a285f5d --- /dev/null +++ b/assets/docs.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15e4f5d832d0589d5054bcbfa61e011143051c20ed6150501ff98f2935d5e436 +size 332802 diff --git a/src/main.js b/src/main.js index 654300a..59f66d0 100644 --- a/src/main.js +++ b/src/main.js @@ -37,7 +37,6 @@ export class GameManager extends Manager { } addBehaviour() { let confirmedStatus = this.data[0].status; - let description = confirmedStatus ? "Round " + this.round : "Proposal"; document.getElementById("Game-description").innerHTML = description; } @@ -128,7 +127,6 @@ export class GameManager extends Manager { for (let i = 0; i < this.data.length; i++) { this.data[i].status = true; } - // Increment the number of games played let selectedPlayers = []; for (let game of this.data) { console.log(game); -- cgit v1.3.1