mdldfs
This commit is contained in:
1 parent
11031eb5a3
commit
123481d748
24 files changed
+29
-1587
No files matched your search
+2
-9
@@ -1,11 +1,4 @@
|
|||||||
# Deno
|
# Nix build result
|
||||||
node_modules/*
|
|
||||||
lib/*
|
|
||||||
css/*
|
|
||||||
/api/resultats.json
|
|
||||||
/index.html
|
|
||||||
/main-built.js
|
|
||||||
/api
|
|
||||||
result
|
result
|
||||||
|
|
||||||
# .NET
|
# .NET
|
||||||
@@ -16,4 +9,4 @@ obj/
|
|||||||
*.db
|
*.db
|
||||||
publish/
|
publish/
|
||||||
|
|
||||||
.direnv/
|
.direnv/
|
||||||
@@ -1,48 +1,39 @@
|
|||||||
# Site Web de [Balle Phénix](http://ballephenix.com)
|
# Site Web de [Balle Phénix](http://ballephenix.com)
|
||||||

|
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
## introduction
|
- **ASP.NET Core 9** — Razor Views (`Views/*.cshtml`), contrôleurs dans `Controllers/`
|
||||||
|
- **SQLite** par défaut (`phenix.db`), Postgres au choix via `DatabaseProvider`
|
||||||
|
- **Nix** — build et déploiement via `flake.nix` (service NixOS, domaine `ballephenix.com`)
|
||||||
|
- Les assets statiques vivent dans `static/` et sont servis par `app.config.WebRootPath = "static"`
|
||||||
|
|
||||||
Vous trouverez le code source du site web pour les Phénix ici. Le site web
|
## Développement
|
||||||
en lui-même est accessible au http://ballephenix.com ou au
|
|
||||||
https://ballephenix.gitlab.io/website/.
|
|
||||||
|
|
||||||
### Structure du site web.
|
|
||||||
|
|
||||||
Premièrement, mille excuses pour mes choix hétéroclites. J'ai décidé d'explorer
|
|
||||||
de nouvels technologies avec ce projet. En conséquence, le code est un peu
|
|
||||||
difficile à comprendre à première vue mais il est __tellement__ plus cool!
|
|
||||||
|
|
||||||
|
|
||||||
#### Choix
|
|
||||||
|
|
||||||
* mithril.js comme framework dans le fureteur
|
|
||||||
* require.js pour les dépendances
|
|
||||||
* uikit avec sass comme framework css
|
|
||||||
* gulp pour la compilation
|
|
||||||
* gitlab pour le deployment
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# English Version
|
|
||||||
|
|
||||||
All the source code is in english. The source code is for a static web
|
|
||||||
side for The softball league 'Balle Phénix'.
|
|
||||||
|
|
||||||
## How to build
|
|
||||||
|
|
||||||
No build, it uses [deno](https://deno.land)
|
|
||||||
|
|
||||||
# How to run it
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
deno run -A --unstable-vk server.ts
|
dotnet run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Base de données SQLite par défaut (`phenix.db`). La config se règle dans `appsettings.json`
|
||||||
|
(ou par variables d'environnement en prod, voir le module NixOS dans `flake.nix`).
|
||||||
|
|
||||||
## How to deploy the website
|
Pour peupler une instance locale depuis la prod:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./deploy
|
python dev/populate-db/populate.py --source https://ballephenix.com --target http://localhost:5000 --force
|
||||||
|
```
|
||||||
|
|
||||||
|
## Déploiement
|
||||||
|
|
||||||
|
Le build et le service NixOS sont décrits dans `flake.nix` (`packages.website` et
|
||||||
|
`nixosModules.x86_64-linux`). Le module déploie le binaire `Phenix` avec `static/`
|
||||||
|
et une base de données dans `/var/lib/phenix`.
|
||||||
|
|
||||||
|
## Outils d'administration
|
||||||
|
|
||||||
|
- `debut_saison.ts` — script Deno qui crée le calendrier de la saison: se connecte
|
||||||
|
via `GET /login` puis POST chaque partie à `/membre/new-game`. Mettre à jour les
|
||||||
|
identifiants en haut du fichier et les dates avant de le lancer.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
deno run -A debut_saison.ts
|
||||||
```
|
```
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"deploy": {
|
|
||||||
"project": "7a93d03e-eabe-4db1-b78e-5f1bbcb460b0",
|
|
||||||
"exclude": [
|
|
||||||
"**/node_modules"
|
|
||||||
],
|
|
||||||
"include": [],
|
|
||||||
"entrypoint": "server.ts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "4",
|
|
||||||
"specifiers": {
|
|
||||||
"jsr:@oak/commons@1": "1.0.0",
|
|
||||||
"jsr:@oak/oak@*": "17.1.4",
|
|
||||||
"jsr:@std/assert@1": "1.0.13",
|
|
||||||
"jsr:@std/bytes@1": "1.0.5",
|
|
||||||
"jsr:@std/crypto@1": "1.0.4",
|
|
||||||
"jsr:@std/encoding@1": "1.0.10",
|
|
||||||
"jsr:@std/encoding@^1.0.10": "1.0.10",
|
|
||||||
"jsr:@std/http@1": "1.0.15",
|
|
||||||
"jsr:@std/media-types@1": "1.1.0",
|
|
||||||
"jsr:@std/path@1": "1.0.9",
|
|
||||||
"npm:path-to-regexp@^6.3.0": "6.3.0"
|
|
||||||
},
|
|
||||||
"jsr": {
|
|
||||||
"@oak/commons@1.0.0": {
|
|
||||||
"integrity": "49805b55603c3627a9d6235c0655aa2b6222d3036b3a13ff0380c16368f607ac",
|
|
||||||
"dependencies": [
|
|
||||||
"jsr:@std/assert",
|
|
||||||
"jsr:@std/bytes",
|
|
||||||
"jsr:@std/crypto",
|
|
||||||
"jsr:@std/encoding@1",
|
|
||||||
"jsr:@std/http",
|
|
||||||
"jsr:@std/media-types"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@oak/oak@17.1.4": {
|
|
||||||
"integrity": "60530b582bf276ff741e39cc664026781aa08dd5f2bc5134d756cc427bf2c13e",
|
|
||||||
"dependencies": [
|
|
||||||
"jsr:@oak/commons",
|
|
||||||
"jsr:@std/assert",
|
|
||||||
"jsr:@std/bytes",
|
|
||||||
"jsr:@std/http",
|
|
||||||
"jsr:@std/media-types",
|
|
||||||
"jsr:@std/path",
|
|
||||||
"npm:path-to-regexp"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@std/assert@1.0.13": {
|
|
||||||
"integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29"
|
|
||||||
},
|
|
||||||
"@std/bytes@1.0.5": {
|
|
||||||
"integrity": "4465dd739d7963d964c809202ebea6d5c6b8e3829ef25c6a224290fbb8a1021e"
|
|
||||||
},
|
|
||||||
"@std/crypto@1.0.4": {
|
|
||||||
"integrity": "cee245c453bd5366207f4d8aa25ea3e9c86cecad2be3fefcaa6cb17203d79340"
|
|
||||||
},
|
|
||||||
"@std/encoding@1.0.10": {
|
|
||||||
"integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1"
|
|
||||||
},
|
|
||||||
"@std/http@1.0.15": {
|
|
||||||
"integrity": "435a4934b4e196e82a8233f724da525f7b7112f3566502f28815e94764c19159",
|
|
||||||
"dependencies": [
|
|
||||||
"jsr:@std/encoding@^1.0.10"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@std/media-types@1.1.0": {
|
|
||||||
"integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
|
|
||||||
},
|
|
||||||
"@std/path@1.0.9": {
|
|
||||||
"integrity": "260a49f11edd3db93dd38350bf9cd1b4d1366afa98e81b86167b4e3dd750129e"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"npm": {
|
|
||||||
"path-to-regexp@6.3.0": {
|
|
||||||
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"remote": {
|
|
||||||
"https://deno.land/std@0.205.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
|
|
||||||
"https://deno.land/std@0.205.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
|
|
||||||
"https://deno.land/std@0.205.0/html/entities.ts": "43f208131bb847e6e222c9dd50322066491da93ed945a93d778923ef7e975398",
|
|
||||||
"https://deno.land/std@0.205.0/html/mod.ts": "3f8c71781e32037ab63bd417759d15d31fb9606c6615c85dcabcc515986494ba",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/basename.ts": "0d978ff818f339cd3b1d09dc914881f4d15617432ae519c1b8fdc09ff8d3789a",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/common.ts": "9e4233b2eeb50f8b2ae10ecc2108f58583aea6fd3e8907827020282dc2b76143",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/dirname.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/format.ts": "11aa62e316dfbf22c126917f5e03ea5fe2ee707386555a8f513d27ad5756cf96",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/from_file_url.ts": "ef1bf3197d2efbf0297a2bdbf3a61d804b18f2bcce45548ae112313ec5be3c22",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/glob_to_reg_exp.ts": "5c3c2b79fc2294ec803d102bd9855c451c150021f452046312819fbb6d4dc156",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/is_glob.ts": "567dce5c6656bdedfc6b3ee6c0833e1e4db2b8dff6e62148e94a917f289c06ad",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/relative.ts": "1af19d787a2a84b8c534cc487424fe101f614982ae4851382c978ab2216186b4",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/strip_trailing_separators.ts": "7ffc7c287e97bdeeee31b155828686967f222cd73f9e5780bfe7dfb1b58c6c65",
|
|
||||||
"https://deno.land/std@0.205.0/path/_common/to_file_url.ts": "a8cdd1633bc9175b7eebd3613266d7c0b6ae0fb0cff24120b6092ac31662f9ae",
|
|
||||||
"https://deno.land/std@0.205.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
|
|
||||||
"https://deno.land/std@0.205.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2",
|
|
||||||
"https://deno.land/std@0.205.0/path/basename.ts": "04bb5ef3e86bba8a35603b8f3b69537112cdd19ce64b77f2522006da2977a5f3",
|
|
||||||
"https://deno.land/std@0.205.0/path/common.ts": "f4d061c7d0b95a65c2a1a52439edec393e906b40f1caf4604c389fae7caa80f5",
|
|
||||||
"https://deno.land/std@0.205.0/path/dirname.ts": "88a0a71c21debafc4da7a4cd44fd32e899462df458fbca152390887d41c40361",
|
|
||||||
"https://deno.land/std@0.205.0/path/extname.ts": "2da4e2490f3b48b7121d19fb4c91681a5e11bd6bd99df4f6f47d7a71bb6ecdf2",
|
|
||||||
"https://deno.land/std@0.205.0/path/format.ts": "3457530cc85d1b4bab175f9ae73998b34fd456c830d01883169af0681b8894fb",
|
|
||||||
"https://deno.land/std@0.205.0/path/from_file_url.ts": "e7fa233ea1dff9641e8d566153a24d95010110185a6f418dd2e32320926043f8",
|
|
||||||
"https://deno.land/std@0.205.0/path/glob.ts": "9c77cf47db1d786e2ebf66670824d03fd84ecc7c807cac24441eb9d5cb6a2986",
|
|
||||||
"https://deno.land/std@0.205.0/path/is_absolute.ts": "67232b41b860571c5b7537f4954c88d86ae2ba45e883ee37d3dec27b74909d13",
|
|
||||||
"https://deno.land/std@0.205.0/path/join.ts": "98d3d76c819af4a11a81d5ba2dbb319f1ce9d63fc2b615597d4bcfddd4a89a09",
|
|
||||||
"https://deno.land/std@0.205.0/path/mod.ts": "2d62a0a8b78a60e8e6f485d881bac6b61d58573b11cf585fb7c8fc50d9b20d80",
|
|
||||||
"https://deno.land/std@0.205.0/path/normalize.ts": "aa95be9a92c7bd4f9dc0ba51e942a1973e2b93d266cd74f5ca751c136d520b66",
|
|
||||||
"https://deno.land/std@0.205.0/path/parse.ts": "d87ff0deef3fb495bc0d862278ff96da5a06acf0625ca27769fc52ac0d3d6ece",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/basename.ts": "a630aeb8fd8e27356b1823b9dedd505e30085015407caa3396332752f6b8406a",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/dirname.ts": "f48c9c42cc670803b505478b7ef162c7cfa9d8e751b59d278b2ec59470531472",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/extname.ts": "ee7f6571a9c0a37f9218fbf510c440d1685a7c13082c348d701396cc795e0be0",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/format.ts": "b94876f77e61bfe1f147d5ccb46a920636cd3cef8be43df330f0052b03875968",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/from_file_url.ts": "b97287a83e6407ac27bdf3ab621db3fccbf1c27df0a1b1f20e1e1b5acf38a379",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/glob.ts": "86c3f06d1c98303613c74650961c3e24bdb871cde2a97c3ae7f0f6d4abbef445",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/is_absolute.ts": "159900a3422d11069d48395568217eb7fc105ceda2683d03d9b7c0f0769e01b8",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/mod.ts": "6bfa8a42d85345b12dbe8571028ca2c62d460b6ef968125e498602b43b6cf6b6",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/parse.ts": "199208f373dd93a792e9c585352bfc73a6293411bed6da6d3bc4f4ef90b04c8e",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/relative.ts": "e2f230608b0f083e6deaa06e063943e5accb3320c28aef8d87528fbb7fe6504c",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/resolve.ts": "51579d83159d5c719518c9ae50812a63959bbcb7561d79acbdb2c3682236e285",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/separator.ts": "0b6573b5f3269a3164d8edc9cefc33a02dd51003731c561008c8bb60220ebac1",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/to_file_url.ts": "08d43ea839ee75e9b8b1538376cfe95911070a655cd312bc9a00f88ef14967b6",
|
|
||||||
"https://deno.land/std@0.205.0/path/posix/to_namespaced_path.ts": "c9228a0e74fd37e76622cd7b142b8416663a9b87db643302fa0926b5a5c83bdc",
|
|
||||||
"https://deno.land/std@0.205.0/path/relative.ts": "23d45ede8b7ac464a8299663a43488aad6b561414e7cbbe4790775590db6349c",
|
|
||||||
"https://deno.land/std@0.205.0/path/resolve.ts": "5b184efc87155a0af9fa305ff68a109e28de9aee81fc3e77cd01380f19daf867",
|
|
||||||
"https://deno.land/std@0.205.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f",
|
|
||||||
"https://deno.land/std@0.205.0/path/to_file_url.ts": "edaafa089e0bce386e1b2d47afe7c72e379ff93b28a5829a5885e4b6c626d864",
|
|
||||||
"https://deno.land/std@0.205.0/path/to_namespaced_path.ts": "cf8734848aac3c7527d1689d2adf82132b1618eff3cc523a775068847416b22a",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/_util.ts": "f32b9444554c8863b9b4814025c700492a2b57ff2369d015360970a1b1099d54",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/basename.ts": "8a9dbf7353d50afbc5b221af36c02a72c2d1b2b5b9f7c65bf6a5a2a0baf88ad3",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/dirname.ts": "5c2aa541384bf0bd9aca821275d2a8690e8238fa846198ef5c7515ce31a01a94",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/extname.ts": "07f4fa1b40d06a827446b3e3bcc8d619c5546b079b8ed0c77040bbef716c7614",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/format.ts": "343019130d78f172a5c49fdc7e64686a7faf41553268961e7b6c92a6d6548edf",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/from_file_url.ts": "d53335c12b0725893d768be3ac6bf0112cc5b639d2deb0171b35988493b46199",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/glob.ts": "0286fb89ecd21db5cbf3b6c79e2b87c889b03f1311e66fb769e6b905d4142332",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/is_absolute.ts": "245b56b5f355ede8664bd7f080c910a97e2169972d23075554ae14d73722c53c",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/join.ts": "e6600bf88edeeef4e2276e155b8de1d5dec0435fd526ba2dc4d37986b2882f16",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/mod.ts": "c3d1a36fbf9f6db1320bcb4fbda8de011d25461be3497105e15cbea1e3726198",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/normalize.ts": "9deebbf40c81ef540b7b945d4ccd7a6a2c5a5992f791e6d3377043031e164e69",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/parse.ts": "120faf778fe1f22056f33ded069b68e12447668fcfa19540c0129561428d3ae5",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/relative.ts": "026855cd2c36c8f28f1df3c6fbd8f2449a2aa21f48797a74700c5d872b86d649",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/resolve.ts": "5ff441ab18a2346abadf778121128ee71bda4d0898513d4639a6ca04edca366b",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/separator.ts": "ae21f27015f10510ed1ac4a0ba9c4c9c967cbdd9d9e776a3e4967553c397bd5d",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/to_file_url.ts": "8e9ea9e1ff364aa06fa72999204229952d0a279dbb876b7b838b2b2fea55cce3",
|
|
||||||
"https://deno.land/std@0.205.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d",
|
|
||||||
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
|
|
||||||
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
|
|
||||||
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
|
|
||||||
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
|
|
||||||
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
|
|
||||||
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
|
|
||||||
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
|
|
||||||
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
|
|
||||||
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
|
|
||||||
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
|
|
||||||
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
|
|
||||||
"https://deno.land/std@0.84.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
|
|
||||||
"https://deno.land/std@0.84.0/_util/os.ts": "e282950a0eaa96760c0cf11e7463e66babd15ec9157d4c9ed49cc0925686f6a7",
|
|
||||||
"https://deno.land/std@0.84.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853",
|
|
||||||
"https://deno.land/std@0.84.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4",
|
|
||||||
"https://deno.land/std@0.84.0/path/_util.ts": "f4fa69aa3cbbd8568763bfc43c7236875015ba343602d8bafd332b4b4243681b",
|
|
||||||
"https://deno.land/std@0.84.0/path/common.ts": "eaf03d08b569e8a87e674e4e265e099f237472b6fd135b3cbeae5827035ea14a",
|
|
||||||
"https://deno.land/std@0.84.0/path/glob.ts": "4a524c1c9da3e79a9fdabdc6e850cd9e41bdf31e442856ffa19c5b123268ca95",
|
|
||||||
"https://deno.land/std@0.84.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12",
|
|
||||||
"https://deno.land/std@0.84.0/path/posix.ts": "1408f8ba482a4dc5fc0a7cd7be28bbbff9608d2b3b5ffdcf288ae1228d959add",
|
|
||||||
"https://deno.land/std@0.84.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
|
|
||||||
"https://deno.land/std@0.84.0/path/win32.ts": "6ca052f54500f00cd7a5172fde62900626ab620dcd5bdcf4e6f5695d001ddef6",
|
|
||||||
"https://deno.land/std@0.99.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
|
|
||||||
"https://deno.land/std@0.99.0/_util/os.ts": "e282950a0eaa96760c0cf11e7463e66babd15ec9157d4c9ed49cc0925686f6a7",
|
|
||||||
"https://deno.land/std@0.99.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853",
|
|
||||||
"https://deno.land/std@0.99.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4",
|
|
||||||
"https://deno.land/std@0.99.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b",
|
|
||||||
"https://deno.land/std@0.99.0/path/common.ts": "eaf03d08b569e8a87e674e4e265e099f237472b6fd135b3cbeae5827035ea14a",
|
|
||||||
"https://deno.land/std@0.99.0/path/glob.ts": "314ad9ff263b895795208cdd4d5e35a44618ca3c6dd155e226fb15d065008652",
|
|
||||||
"https://deno.land/std@0.99.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12",
|
|
||||||
"https://deno.land/std@0.99.0/path/posix.ts": "f56c3c99feb47f30a40ce9d252ef6f00296fa7c0fcb6dd81211bdb3b8b99ca3b",
|
|
||||||
"https://deno.land/std@0.99.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
|
|
||||||
"https://deno.land/std@0.99.0/path/win32.ts": "77f7b3604e0de40f3a7c698e8a79e7f601dc187035a1c21cb1e596666ce112f8",
|
|
||||||
"https://deno.land/x/media_types@v2.7.1/db.ts": "1d95aa3fc1fe076638550640609394f0e157b18b430800e419aa7ece5c151d9c",
|
|
||||||
"https://deno.land/x/media_types@v2.7.1/deps.ts": "cd4c31e4b5ed8c4d8c61bd2fb38a82d68d3fd2dcf6d43f22511cbe6d9d310e51",
|
|
||||||
"https://deno.land/x/media_types@v2.7.1/mod.ts": "d63583b978d32eff8b76e1ae5d83cba2fb27baa90cc1bcb0ad15a06122ea8c19",
|
|
||||||
"https://deno.land/x/static_files@1.1.6/mod.ts": "8a9bb05599e53864c54eb22931fe98507818517e0d69f26e099a1d8f4ef2b638",
|
|
||||||
"https://deno.land/x/static_files@1.1.6/src/deps.ts": "fb62be7a780339157bf692a619d6d02ea9662490deca275f488f9f9db917fc4a",
|
|
||||||
"https://deno.land/x/static_files@1.1.6/src/types.ts": "ef201288626294e9e1b8ee85cdfe2cdfb282e4d78af445df1726050dfa0c0988",
|
|
||||||
"https://deno.land/x/static_files@1.1.6/src/utils.ts": "9e6172dc97fed01e70a6b063c5afff40e20fe726b3bae0c5f0a962bad74b7107",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/deps.ts": "1fb4acd6b3b6261c308280b09e8feadac7b0fac1e01bd0e0f1e75527a2181a52",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/mod.ts": "ab690911075158ee3762bd7aefe209cdcb6ead7b492f14b0693d7025244db8e6",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/echo.ts": "f7c064fb6d34b29852f46f6e01583ed87656dcbbc5cae51c8f29198d6951d0cf",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/escape.ts": "8115ec84c84c13811f4a43263b8954e912df94df6565d6ff9ce7faea7daa50be",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/export.ts": "ef54811ed0205aaeef6ad09557c94b8ae98e1457234898da5a1c5461a7dcf424",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/for.ts": "6313e139a23ed17ca8b0852031f8d9e11aeb7520b228e68b9233418d25745414",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/function.ts": "cdf610a98493e4a093c53473abdad7d47cbcff40aa8362d280e39910620cb8d6",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/if.ts": "9de295f8675cacaec7866ad21068b6b089bfcadf72d3c62e1df50ca378c04279",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/import.ts": "406204ab9d785db206da5d5cbcffa9a111cef2a39a19574e630cdea89b0691c5",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/include.ts": "a9c65ce90354766220c0c9f7b21ea2db97bed99d9ffbe28f8a5ac035d488b163",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/js.ts": "2c50d60061e9c5b6280a145266e617181534b8767e91127d59d5cf5febdcfb03",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/layout.ts": "b9970b762ba73cf5605e63e585792803a48e326621c6cb2ec216502ba21a90cd",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/set.ts": "016235a906f99cbfab78d29dfdfe8ca0925db7a46458fd873adacd0d7c6ed82d",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/plugins/unescape.ts": "946512835a409815d6ad98ac4529440e9a368f42ca499791d0f25ee0ecc4c4d6",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/src/environment.ts": "9c28fbf05176297adb93fc688711125b295c69089554d4211cd38a5b04028ec5",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/src/loader.ts": "eb01b0dca7ea8bcdcdfd30b1f90f2fad28fb7f2cfd943900322a85bfaa86130c",
|
|
||||||
"https://deno.land/x/vento@v0.9.1/src/tokenizer.ts": "e9918ac58f941e048fe08cea10e65fb6e19c00fe7ed31fb050663445f11c6e9d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
exec deno run -A jsr:@deno/deployctl deploy --prod --project=phenix server.ts
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
import { Application } from "jsr:@oak/oak/application";
|
|
||||||
import { Router } from "jsr:@oak/oak/router";
|
|
||||||
import { GameService } from "./server/game-service.ts";
|
|
||||||
import { Game } from "./server/types.ts";
|
|
||||||
import staticFiles from "https://deno.land/x/static_files@1.1.6/mod.ts";
|
|
||||||
import vento from "https://deno.land/x/vento@v0.9.1/mod.ts";
|
|
||||||
import { LoginService } from "./server/login-service.ts";
|
|
||||||
|
|
||||||
const env = vento();
|
|
||||||
setInterval(() => env.cache.clear(), 1000);
|
|
||||||
const gameService = new GameService(await Deno.openKv());
|
|
||||||
const loginService = LoginService.officialLoginService();
|
|
||||||
|
|
||||||
const router = new Router();
|
|
||||||
|
|
||||||
router.get("/", async (ctx) => {
|
|
||||||
const template = await env.load("templates/home.vto");
|
|
||||||
const result = await template({ tab: "home" });
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/calendrier", async (ctx) => {
|
|
||||||
const template = await env.load("templates/calendrier.vto");
|
|
||||||
const result = await template({
|
|
||||||
tab: "calendrier",
|
|
||||||
});
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/nouvelles", async (ctx) => {
|
|
||||||
const template = await env.load("templates/nouvelles.vto");
|
|
||||||
const result = await template({ tab: "nouvelles" });
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/contact", async (ctx) => {
|
|
||||||
const template = await env.load("templates/contact.vto");
|
|
||||||
const result = await template({ tab: "contact" });
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/reglement", async (ctx) => {
|
|
||||||
const template = await env.load("templates/reglement.vto");
|
|
||||||
const result = await template({ tab: "reglement" });
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/login", async (ctx) => {
|
|
||||||
const username = ctx.request.url.searchParams.get("username") || "";
|
|
||||||
const password = ctx.request.url.searchParams.get("password") || "";
|
|
||||||
|
|
||||||
if (!(await loginService.validateCredentials(username, password))) {
|
|
||||||
ctx.response.status = 403;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await loginService.login(ctx);
|
|
||||||
ctx.response.redirect("/membre/games-editor/today");
|
|
||||||
});
|
|
||||||
|
|
||||||
const memberRouter = new Router();
|
|
||||||
|
|
||||||
memberRouter.use(async (ctx, next) => {
|
|
||||||
const isLoggedIn = await loginService.isLoggedIn(ctx);
|
|
||||||
if (ctx.request.url.pathname !== "/membre" && !isLoggedIn) {
|
|
||||||
ctx.response.redirect("/membre");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctx.params.isLoggedIn = isLoggedIn;
|
|
||||||
await next();
|
|
||||||
});
|
|
||||||
|
|
||||||
memberRouter.get("/", async (ctx) => {
|
|
||||||
const template = await env.load("templates/membre.vto");
|
|
||||||
const result = await template({
|
|
||||||
tab: "membre",
|
|
||||||
isLoggedIn: ctx.params.isLoggedIn,
|
|
||||||
});
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
function today(): Date {
|
|
||||||
const date = new Date().toLocaleDateString("fr-CA", {
|
|
||||||
timeZone: "America/New_York",
|
|
||||||
});
|
|
||||||
return new Date(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
memberRouter.get("/games-editor/today", async (ctx) => {
|
|
||||||
const template = await env.load("templates/partials/games-editor.vto");
|
|
||||||
const games = await gameService.getAllGames();
|
|
||||||
const lastGameDay = games
|
|
||||||
.map((g) => g.date)
|
|
||||||
.filter((d) => new Date(d) <= today())
|
|
||||||
.sort()
|
|
||||||
.reverse()[0];
|
|
||||||
|
|
||||||
const result = await template({
|
|
||||||
tab: "membre",
|
|
||||||
selectedYear: null,
|
|
||||||
isToday: true,
|
|
||||||
games: games.filter((g) => g.date === lastGameDay),
|
|
||||||
years: [...new Set(games.map((x) => x.year))].sort().reverse(),
|
|
||||||
});
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
memberRouter.get("/games-editor/:year", async (ctx) => {
|
|
||||||
const template = await env.load("templates/partials/games-editor.vto");
|
|
||||||
const games = await gameService.getAllGames();
|
|
||||||
const selectedYear = parseInt(ctx.params.year);
|
|
||||||
const result = await template({
|
|
||||||
tab: "membre",
|
|
||||||
selectedYear: selectedYear,
|
|
||||||
games: games.filter((g) => g.year === selectedYear),
|
|
||||||
years: [...new Set(games.map((x) => x.year))].sort().reverse(),
|
|
||||||
});
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
memberRouter.get("/new-game-editor", async (ctx) => {
|
|
||||||
const template = await env.load("templates/partials/new-games-editor.vto");
|
|
||||||
const result = await template({
|
|
||||||
tab: "membre",
|
|
||||||
});
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
memberRouter.post("/new-game", async (ctx) => {
|
|
||||||
const payload = await ctx.request.body.form();
|
|
||||||
const preGame = {
|
|
||||||
date: payload.get("game-date"),
|
|
||||||
time: payload.get("game-time"),
|
|
||||||
away: payload.get("away-team"),
|
|
||||||
home: payload.get("home-team"),
|
|
||||||
};
|
|
||||||
const [game, error] = await gameService.createNewGame(preGame);
|
|
||||||
|
|
||||||
if (error !== "") {
|
|
||||||
const template = await env.load("templates/partials/new-game-error.vto");
|
|
||||||
const result = await template({
|
|
||||||
tab: "membre",
|
|
||||||
error: error,
|
|
||||||
});
|
|
||||||
ctx.response.status = 500;
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const template = await env.load("templates/partials/single-game-editor.vto");
|
|
||||||
const result = await template({
|
|
||||||
tab: "membre",
|
|
||||||
game: game,
|
|
||||||
});
|
|
||||||
ctx.response.headers.set("game-id", game?.id ?? "");
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
memberRouter.delete("/games/:gameId", async (ctx) => {
|
|
||||||
if (!(await loginService.isLoggedIn(ctx))) {
|
|
||||||
ctx.response.redirect("/membre");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const success = await gameService.deleteGameById(ctx.params.gameId);
|
|
||||||
ctx.response.body = `${success}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
memberRouter.post("/games/:gameId", async (ctx) => {
|
|
||||||
if (!(await loginService.isLoggedIn(ctx))) {
|
|
||||||
ctx.response.redirect("/membre");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const payload = await ctx.request.body.form();
|
|
||||||
let preGame = {
|
|
||||||
awayScore: payload.get("away-score") ?? null,
|
|
||||||
homeScore: payload.get("home-score") ?? null,
|
|
||||||
rain: payload.get("game-type") === "rain",
|
|
||||||
practice: payload.get("game-type") === "practice",
|
|
||||||
};
|
|
||||||
|
|
||||||
preGame = Object.fromEntries(
|
|
||||||
Object.entries(preGame).filter(([_, v]) => v !== null),
|
|
||||||
) as Partial<Game>;
|
|
||||||
|
|
||||||
const success = await gameService.updateGame(ctx.params.gameId, preGame);
|
|
||||||
ctx.response.body = success
|
|
||||||
? `<i class="fa fa-floppy-o" aria-hidden="true"></i>`
|
|
||||||
: `<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>`;
|
|
||||||
});
|
|
||||||
|
|
||||||
memberRouter.post("/games/:gameId/switch", async (ctx) => {
|
|
||||||
if (!(await loginService.isLoggedIn(ctx))) {
|
|
||||||
ctx.response.redirect("/membre");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const success = await gameService.switchVisitors(ctx.params.gameId);
|
|
||||||
if (!success) {
|
|
||||||
ctx.response.body = `<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>`;
|
|
||||||
}
|
|
||||||
const template = await env.load("templates/partials/single-game-editor.vto");
|
|
||||||
const game = await gameService.getGameById(ctx.params.gameId);
|
|
||||||
const result = await template({
|
|
||||||
tab: "membre",
|
|
||||||
game: game,
|
|
||||||
});
|
|
||||||
ctx.response.headers.set("game-id", game?.id ?? "");
|
|
||||||
ctx.response.body = result.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
const apiRouter = new Router();
|
|
||||||
|
|
||||||
apiRouter.get("/results/:year.json", async (ctx) => {
|
|
||||||
ctx.response.json = true;
|
|
||||||
const games = await gameService.getAllGames();
|
|
||||||
ctx.response.body = games
|
|
||||||
.filter((g) => g.year == ctx.params.year)
|
|
||||||
.map((g) => {
|
|
||||||
g[g.home] = parseInt(g.homeScore);
|
|
||||||
g[g.away] = parseInt(g.awayScore);
|
|
||||||
return g;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
apiRouter.get("/available-years.json", async (ctx) => {
|
|
||||||
ctx.response.json = true;
|
|
||||||
const games = await gameService.getAllGames();
|
|
||||||
ctx.response.body = [...new Set(games.map((g) => g.year))];
|
|
||||||
});
|
|
||||||
|
|
||||||
router.use("/membre", memberRouter.routes());
|
|
||||||
router.use("/api", apiRouter.routes());
|
|
||||||
|
|
||||||
const app = new Application();
|
|
||||||
app.use(router.routes());
|
|
||||||
|
|
||||||
app.use(staticFiles("static", { brotli: true, fetch: true }));
|
|
||||||
|
|
||||||
await app.listen({ port: 8000 });
|
|
||||||
@@ -1,340 +0,0 @@
|
|||||||
import { Game, PreGame } from "./types.ts";
|
|
||||||
import {
|
|
||||||
assertEquals,
|
|
||||||
assertNotEquals,
|
|
||||||
} from "https://deno.land/std@0.224.0/assert/mod.ts";
|
|
||||||
|
|
||||||
type GamePrimaryKey = ["phenix", "games", string];
|
|
||||||
|
|
||||||
type date = [number, number, number];
|
|
||||||
type time = [number, number];
|
|
||||||
type DateIndex = ["phenix", "indexes", "game-date", ...date, ...time];
|
|
||||||
|
|
||||||
export class GameService {
|
|
||||||
readonly kv: Deno.Kv;
|
|
||||||
private readonly DATE_VALIDATOR =
|
|
||||||
/^(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})$/;
|
|
||||||
private readonly TIME_VALIDATOR = /^(?<hour>\d{1,2})(:|h)(?<minute>\d{1,2})$/;
|
|
||||||
private readonly VALID_TEAMS: string[] = [
|
|
||||||
"jaune",
|
|
||||||
"rouge",
|
|
||||||
"bleu",
|
|
||||||
"gris",
|
|
||||||
"vert",
|
|
||||||
"noir",
|
|
||||||
"orange",
|
|
||||||
"mauve",
|
|
||||||
];
|
|
||||||
|
|
||||||
constructor(kv: Deno.Kv) {
|
|
||||||
this.kv = kv;
|
|
||||||
}
|
|
||||||
|
|
||||||
private chronologicalGameOrder(game1: Game, game2: Game): number {
|
|
||||||
if (game1.date < game2.date) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (game1.date > game2.date) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (game1.time < game2.time) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (game1.time > game2.time) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAllGames(): Promise<Game[]> {
|
|
||||||
const ite = this.kv.list<Game>({ prefix: ["phenix", "games"] });
|
|
||||||
return (await Array.fromAsync(ite))
|
|
||||||
.map((e) => e.value)
|
|
||||||
.sort(this.chronologicalGameOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private gamePrimaryKey(id: string): GamePrimaryKey {
|
|
||||||
return ["phenix", "games", id];
|
|
||||||
}
|
|
||||||
|
|
||||||
private dateIndex(date: string, time: string): DateIndex | null {
|
|
||||||
let destructedDate = this.DATE_VALIDATOR.exec(date)?.groups;
|
|
||||||
let destructedTime = this.TIME_VALIDATOR.exec(time)?.groups;
|
|
||||||
|
|
||||||
if (!destructedDate || !destructedTime) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
"phenix",
|
|
||||||
"indexes",
|
|
||||||
"game-date",
|
|
||||||
parseInt(destructedDate.year),
|
|
||||||
parseInt(destructedDate.month),
|
|
||||||
parseInt(destructedDate.day),
|
|
||||||
parseInt(destructedTime.hour),
|
|
||||||
parseInt(destructedTime.minute),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private cleanTime(raw: string): string {
|
|
||||||
const groups = this.TIME_VALIDATOR.exec(raw)!.groups!;
|
|
||||||
return `${groups.hour.padStart(2, "0")}:${groups.minute.padStart(2, "0")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private cleanDate(raw: string): string {
|
|
||||||
const groups = this.DATE_VALIDATOR.exec(raw)!.groups!;
|
|
||||||
return `${groups.year}-${groups.month.padStart(2, "0")}-${groups.day.padStart(2, "0")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractYearFromDate(date: string): number {
|
|
||||||
return parseInt(this.DATE_VALIDATOR.exec(date)!.groups!.year);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createGame(preGame: PreGame): Game {
|
|
||||||
const ret = {
|
|
||||||
...preGame,
|
|
||||||
id: crypto.randomUUID(),
|
|
||||||
practice: false,
|
|
||||||
rain: false,
|
|
||||||
year: this.extractYearFromDate(preGame.date),
|
|
||||||
homeScore: null,
|
|
||||||
awayScore: null,
|
|
||||||
};
|
|
||||||
ret.time = this.cleanTime(ret.time);
|
|
||||||
ret.date = this.cleanDate(ret.date);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private validatePregate(preGame: PreGame): string {
|
|
||||||
if (!this.DATE_VALIDATOR.test(preGame.date)) {
|
|
||||||
return `Invalide date format ${preGame.date}`;
|
|
||||||
}
|
|
||||||
if (!this.TIME_VALIDATOR.test(preGame.time)) {
|
|
||||||
return `Invalide time format ${preGame.time}`;
|
|
||||||
}
|
|
||||||
if (!this.VALID_TEAMS.includes(preGame.away)) {
|
|
||||||
return `Invalide away format ${preGame.away}`;
|
|
||||||
}
|
|
||||||
if (!this.VALID_TEAMS.includes(preGame.home)) {
|
|
||||||
return `Invalide home format ${preGame.home}`;
|
|
||||||
}
|
|
||||||
if (preGame.home === preGame.away) {
|
|
||||||
return `Home and away teams must be different (received ${preGame.home} for both)`;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
async createNewGame(preGame: PreGame): Promise<[Game | null, string]> {
|
|
||||||
if (this.validatePregate(preGame) !== "") {
|
|
||||||
return [null, this.validatePregate(preGame)];
|
|
||||||
}
|
|
||||||
const game = this.createGame(preGame);
|
|
||||||
const dateIndex = this.dateIndex(game.date, game.time);
|
|
||||||
if (!dateIndex) {
|
|
||||||
return [null, "Cound not parse date or time"];
|
|
||||||
}
|
|
||||||
const precondition = await this.kv.get<string>(dateIndex);
|
|
||||||
if (precondition.value !== null) {
|
|
||||||
return [null, "game already exists " + precondition.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await this.kv
|
|
||||||
.atomic()
|
|
||||||
.check(precondition)
|
|
||||||
.set(this.gamePrimaryKey(game.id), game)
|
|
||||||
.set(dateIndex, game.id)
|
|
||||||
.commit();
|
|
||||||
if (!res.ok) {
|
|
||||||
return [null, "Transaction failed"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [game, ""];
|
|
||||||
}
|
|
||||||
|
|
||||||
async getGameById(id: string): Promise<Game | null> {
|
|
||||||
const res = await this.kv.get<Game>(this.gamePrimaryKey(id));
|
|
||||||
return res.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteGameById(id: string): Promise<boolean> {
|
|
||||||
const precond = await this.kv.get<Game>(this.gamePrimaryKey(id));
|
|
||||||
if (precond.value === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await this.kv
|
|
||||||
.atomic()
|
|
||||||
.check(precond)
|
|
||||||
.delete(this.gamePrimaryKey(precond.value.id))
|
|
||||||
.delete(this.dateIndex(precond.value.date, precond.value.time)!)
|
|
||||||
.commit();
|
|
||||||
return res.ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
async switchVisitors(id: string): Promise<boolean> {
|
|
||||||
const game = await this.getGameById(id);
|
|
||||||
if (game === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
game.id = "";
|
|
||||||
const [home, away] = [game.home, game.away];
|
|
||||||
const [homeScore, awayScore] = [game.homeScore, game.awayScore];
|
|
||||||
game.home = away;
|
|
||||||
game.away = home;
|
|
||||||
game.homeScore = awayScore;
|
|
||||||
game.awayScore = homeScore;
|
|
||||||
console.log(`game ${game}`);
|
|
||||||
|
|
||||||
return this.updateGame(id, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateGame(id: string, partial: Partial<Game>): Promise<boolean> {
|
|
||||||
delete partial.id;
|
|
||||||
const precond = await this.kv.get<Game>(this.gamePrimaryKey(id));
|
|
||||||
if (precond.value === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const res = await this.kv
|
|
||||||
.atomic()
|
|
||||||
.check(precond)
|
|
||||||
.delete(this.gamePrimaryKey(precond.value.id))
|
|
||||||
.delete(this.dateIndex(precond.value.date, precond.value.time)!)
|
|
||||||
.set(
|
|
||||||
this.dateIndex(precond.value.date, precond.value.time)!,
|
|
||||||
precond.value.id,
|
|
||||||
)
|
|
||||||
.set(this.gamePrimaryKey(precond.value.id), {
|
|
||||||
...precond.value,
|
|
||||||
...partial,
|
|
||||||
})
|
|
||||||
.commit();
|
|
||||||
return res.ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Deno.test("saving simple games", async (t) => {
|
|
||||||
const kv = await Deno.openKv();
|
|
||||||
const service = new GameService(kv);
|
|
||||||
|
|
||||||
async function deleteAllKeys() {
|
|
||||||
const keys = kv.list({ prefix: [] });
|
|
||||||
for await (const entry of keys) {
|
|
||||||
await kv.delete(entry.key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validePreGame(seed: number): PreGame {
|
|
||||||
let year = ((645 * seed + 162) % 10000) + 1;
|
|
||||||
let month = ((335 * seed + 71) % 12) + 1;
|
|
||||||
let day = ((72625 * seed + 176) % 28) + 1;
|
|
||||||
return {
|
|
||||||
date: `${(year + "").padStart(4, "0")}-${(month + "").padStart(2, "0")}-${(day + "").padStart(2, "0")}`,
|
|
||||||
time: "18:45",
|
|
||||||
away: "jaune",
|
|
||||||
home: "rouge",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step("creating games", async () => {
|
|
||||||
const preGame = validePreGame(7276);
|
|
||||||
const [game, error] = await service.createNewGame(preGame);
|
|
||||||
assertEquals(error, "");
|
|
||||||
const ret = await service.getGameById(game!.id);
|
|
||||||
assertEquals(game, ret);
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step("creating games with old date format", async () => {
|
|
||||||
const preGame = validePreGame(7276);
|
|
||||||
preGame.date = "2017-8-5";
|
|
||||||
const [game, error] = await service.createNewGame(preGame);
|
|
||||||
assertEquals(error, "");
|
|
||||||
assertEquals(game?.date, "2017-08-05");
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step("creating games with old time format", async () => {
|
|
||||||
const preGame = validePreGame(7276);
|
|
||||||
preGame.time = "9:00";
|
|
||||||
const [game, error] = await service.createNewGame(preGame);
|
|
||||||
assertEquals(error, "");
|
|
||||||
assertEquals(game?.time, "09:00");
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step(
|
|
||||||
"saving two games in the same timeslot should fail",
|
|
||||||
async () => {
|
|
||||||
const preGame = validePreGame(7276);
|
|
||||||
let [_, error] = await service.createNewGame(preGame);
|
|
||||||
assertEquals(error, "");
|
|
||||||
[_, error] = await service.createNewGame(preGame);
|
|
||||||
assertNotEquals(error, "");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step("saving games with bad fields", async () => {
|
|
||||||
let i = 0;
|
|
||||||
for (const key of Object.keys(validePreGame(7276))) {
|
|
||||||
let [_, error] = await service.createNewGame({
|
|
||||||
...validePreGame(7262 + i),
|
|
||||||
[key]: `Broken ${key}`,
|
|
||||||
});
|
|
||||||
assertNotEquals(error, "");
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await t.step("saving games with the same home and away teams", async () => {
|
|
||||||
const preGame = validePreGame(7276);
|
|
||||||
preGame.home = preGame.away;
|
|
||||||
let [_, error] = await service.createNewGame(preGame);
|
|
||||||
assertNotEquals(error, "");
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step("listing games", async () => {
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
await service.createNewGame(validePreGame(666 + i));
|
|
||||||
}
|
|
||||||
assertEquals((await service.getAllGames()).length, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step("deleting games", async () => {
|
|
||||||
let [game, error] = await service.createNewGame(validePreGame(8992));
|
|
||||||
await service.deleteGameById(game!.id);
|
|
||||||
assertEquals((await service.getAllGames()).length, 0);
|
|
||||||
|
|
||||||
[game, error] = await service.createNewGame(validePreGame(8992));
|
|
||||||
assertEquals(error, "");
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
|
|
||||||
await t.step("updating game scores", async () => {
|
|
||||||
let [game, _] = await service.createNewGame(validePreGame(8992));
|
|
||||||
const ret = await service.updateGame(game!.id, {
|
|
||||||
homeScore: 10,
|
|
||||||
awayScore: 32,
|
|
||||||
});
|
|
||||||
assertEquals(ret, true);
|
|
||||||
game = await service.getGameById(game!.id);
|
|
||||||
assertEquals(game!.homeScore, 10);
|
|
||||||
assertEquals(game!.awayScore, 32);
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAllKeys();
|
|
||||||
await kv.close();
|
|
||||||
});
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
import {
|
|
||||||
assert,
|
|
||||||
assertFalse,
|
|
||||||
} from "https://deno.land/std@0.224.0/assert/mod.ts";
|
|
||||||
|
|
||||||
type User = { username: string; passwordHashWithSalt: string };
|
|
||||||
|
|
||||||
const officialUsers: User[] = [
|
|
||||||
{
|
|
||||||
username: "marqueur",
|
|
||||||
passwordHashWithSalt:
|
|
||||||
"34373780cc542a1eddb6a08488ec149323b94d66025421acd98ffc20eb5055a0:6f2fa05777d45a2da9db79b7fd11e3bc69eb1807642ccb53d41a0b9eb71a3311",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
username: "comite",
|
|
||||||
passwordHashWithSalt:
|
|
||||||
"8649feff48a50f53448e59ce480db97b5549eef1d92c886c49b5a14ee22648e5:a0017b879c8834d2f34938e7a4fa3f4c7be8f70545b9bb22a28cf56ab2eb26d9",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export class LoginService {
|
|
||||||
private readonly users: User[];
|
|
||||||
private readonly sessionKey: string;
|
|
||||||
|
|
||||||
constructor(users: User[], sessionKey: string) {
|
|
||||||
this.users = users;
|
|
||||||
this.sessionKey = sessionKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static officialLoginService(): LoginService {
|
|
||||||
return new LoginService(
|
|
||||||
officialUsers,
|
|
||||||
Deno.env.get("SESSION_KEY") || "dev-only",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async hashMessage(message: string): Promise<string> {
|
|
||||||
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
|
|
||||||
const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // hash the message
|
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
|
|
||||||
const hashHex = hashArray
|
|
||||||
.map((b) => b.toString(16).padStart(2, "0"))
|
|
||||||
.join(""); // convert bytes to hex string
|
|
||||||
return hashHex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async shittyHmac(message: string, salt: string): Promise<string> {
|
|
||||||
const h1 = await this.hashMessage(message + salt);
|
|
||||||
const h2 = await this.hashMessage(salt + message);
|
|
||||||
return await this.hashMessage(h1 + h2);
|
|
||||||
}
|
|
||||||
|
|
||||||
async hashPassword(password: string): Promise<string> {
|
|
||||||
const salt = await this.hashMessage(`${Math.random()}`);
|
|
||||||
const hash = await this.shittyHmac(password, salt);
|
|
||||||
return `${salt}:${hash}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async verifyPassword(
|
|
||||||
password: string,
|
|
||||||
passwordHashWithSalt: string,
|
|
||||||
): Promise<boolean> {
|
|
||||||
const [passwordSalt, passwordHash] = passwordHashWithSalt.split(":");
|
|
||||||
return passwordHash === (await this.shittyHmac(password, passwordSalt));
|
|
||||||
}
|
|
||||||
|
|
||||||
async validateCredentials(
|
|
||||||
username: string,
|
|
||||||
password: string,
|
|
||||||
): Promise<boolean> {
|
|
||||||
for (const user of this.users) {
|
|
||||||
if (user.username === username) {
|
|
||||||
return this.verifyPassword(password, user.passwordHashWithSalt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async login(ctx: any): Promise<void> {
|
|
||||||
const nDays = 90;
|
|
||||||
const maxAgesInSecond = nDays * 24 * 3600;
|
|
||||||
const maxUnixEpoch = Math.floor(Date.now() / 1000) + maxAgesInSecond;
|
|
||||||
|
|
||||||
await ctx.cookies.set("phenix-session", `${maxUnixEpoch}`, {
|
|
||||||
maxAge: maxAgesInSecond,
|
|
||||||
});
|
|
||||||
await ctx.cookies.set(
|
|
||||||
"phenix-session-sign",
|
|
||||||
await this.shittyHmac(`${maxUnixEpoch}`, this.sessionKey),
|
|
||||||
{
|
|
||||||
maxAge: maxAgesInSecond,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async isLoggedIn(ctx: any): Promise<boolean> {
|
|
||||||
const expiration = await ctx.cookies.get("phenix-session");
|
|
||||||
const signature = await ctx.cookies.get("phenix-session-sign");
|
|
||||||
if (signature !== (await this.shittyHmac(expiration, this.sessionKey))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!expiration || !/^\d+$/.test(expiration)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return Date.now() < parseInt(expiration) * 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Deno.test("login service", async (t) => {
|
|
||||||
await t.step("hashing password", async () => {
|
|
||||||
const service = new LoginService([], "ss");
|
|
||||||
const passwordHashWithSalt = await service.hashPassword("qwerty");
|
|
||||||
assert(await service.verifyPassword("qwerty", passwordHashWithSalt));
|
|
||||||
assertFalse(await service.verifyPassword("ytrewq", passwordHashWithSalt));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
export type Game = {
|
|
||||||
id: string;
|
|
||||||
date: string;
|
|
||||||
time: string;
|
|
||||||
home: string;
|
|
||||||
away: string;
|
|
||||||
year: number;
|
|
||||||
homeScore: number | null;
|
|
||||||
awayScore: number | null;
|
|
||||||
practice: boolean;
|
|
||||||
rain: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PreGame = {
|
|
||||||
date: string;
|
|
||||||
time: string;
|
|
||||||
home: string;
|
|
||||||
away: string;
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{{ layout "templates/container.vto" }}
|
|
||||||
|
|
||||||
|
|
||||||
<div id="mithril-content"></div>
|
|
||||||
|
|
||||||
<script type=module src="scripts/calendar.js"></script>
|
|
||||||
|
|
||||||
{{ /layout }}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{{ layout "templates/container.vto" }}
|
|
||||||
|
|
||||||
<div class="uk-width-large-2-5 uk-width-medium-4-5 uk-width-small-1-1 uk-container-center">
|
|
||||||
<div class="uk-grid uk-margin">
|
|
||||||
<div class="uk-width-1-1">
|
|
||||||
<div style="height: 300px; width: 300px; background-size: 300px; background-image: url(images/logosansfond.JPG);" class="uk-cover-background uk-container-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="div uk-panel uk-panel-box uk-panel-box-secondary uk-container-center uk-margin">
|
|
||||||
<table class="uk-table">
|
|
||||||
<tr>
|
|
||||||
<td class="uk-text-right"><i class="fa fa-desktop"></i></td>
|
|
||||||
<td><a href="mailto:sportsphenixmtl@gmail.com">sportsphenixmtl@gmail.com</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="uk-text-right"><i class="fa fa-phone"></i></td>
|
|
||||||
<td> (514) 838-8950</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="uk-text-right"><i class="fa fa-envelope"></i></td>
|
|
||||||
<td>
|
|
||||||
<p> 10454 Sylvain-Garneau<br>Montréal, Québec H1C 0A2</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="uk-text-right"><i class="fa fa-gitlab"></i></td>
|
|
||||||
<td>
|
|
||||||
<p> Le site web est ouvert et accessible sur <a href="https://gitlab.com/ballephenix/website">gitlab.</a></p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ /layout }}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Balle Phenix</title>
|
|
||||||
|
|
||||||
<link rel="shortcut icon" href="/images/favicon.ico">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<script src="scripts/jquery-3.7.1.min.js"></script>
|
|
||||||
<script src="scripts/uikit.min.js"></script>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<script src="https://unpkg.com/htmx.org@2.0.0"></script>
|
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<nav class="uk-navbar uk-navbar-attached">
|
|
||||||
<ul class="uk-navbar-nav uk-visible-large uk-navbar-content uk-navbar-center">
|
|
||||||
<li class="uk-parent">
|
|
||||||
<div class="uk-navbar-brand"><a href="https://www.facebook.com/SportsPhenixMtl"><i class="fa fa-facebook-square"></i></a></div>
|
|
||||||
</li>
|
|
||||||
<li class="{{ if it.tab === 'home'}}uk-active{{ /if }}">
|
|
||||||
<a href="/">
|
|
||||||
<div><i class="fa fa-home"></i> Accueil</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="{{ if it.tab === 'nouvelles'}}uk-active{{ /if }}">
|
|
||||||
<a href="/nouvelles">
|
|
||||||
<div><i class="fa fa-newspaper-o"></i> Nouvelles</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="{{ if it.tab === 'calendrier'}}uk-active{{ /if }}">
|
|
||||||
<a href="/calendrier">
|
|
||||||
<div><i class="fa fa-calendar"></i> Calendrier</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="{{ if it.tab === 'reglement'}}uk-active{{ /if }}">
|
|
||||||
<a href="/reglement">
|
|
||||||
<div><i class="fa fa-legal"></i> Règlement</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="{{ if it.tab === 'membre'}}uk-active{{ /if }}">
|
|
||||||
<a href="/membre">
|
|
||||||
<div><i class="fa fa-lock"></i> Membre</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="{{ if it.tab === 'contact'}}uk-active{{ /if }}">
|
|
||||||
<a href="/contact">
|
|
||||||
<div><i class="fa fa-phone"></i> Nous Joindre</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul class="uk-navbar-nav uk-visible-medium">
|
|
||||||
<li class="uk-parent">
|
|
||||||
<div class="uk-navbar-brand"><a href="https://www.facebook.com/SportsPhenixMtl"><i class="fa fa-facebook-square"></i></a></div>
|
|
||||||
</li>
|
|
||||||
<li class="{{ if tab === 'home'}}uk-active{{ /if }}"><a href="/"><i class="fa fa-home"></i> Accueil </a></li>
|
|
||||||
<li class="{{ if tab === 'nouvelles'}}uk-active{{ /if }}"><a href="/nouvelles"><i class="fa fa-newspaper-o"></i> Nouvelles</a></li>
|
|
||||||
<li class="{{ if it.tab === 'calendrier'}}uk-active{{ /if }}"><a href="/calendrier"><i class="fa fa-calendar"></i> Calendrier</a></li>
|
|
||||||
<li class="{{ if it.tab === 'reglement'}}uk-active{{ /if }}"><a href="/reglement"><i class="fa fa-legal"></i> Règlement</a></li>
|
|
||||||
<li class="{{ if it.tab === 'membre'}}uk-active{{ /if }}"><a href="/membre"><i class="fa fa-lock"></i> Membre</a></li>
|
|
||||||
<li class="{{ if it.tab === 'contact'}}uk-active{{ /if }}"><a href="/contact"><i class="fa fa-phone"></i> Nous Joindre</a></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="uk-navbar-nav uk-visible-small">
|
|
||||||
<li class="uk-parent">
|
|
||||||
<div class="uk-navbar-brand"><a href="https://www.facebook.com/SportsPhenixMtl"><i class="fa fa-facebook-square"></i></a></div>
|
|
||||||
</li>
|
|
||||||
<li data-uk-dropdown="" aria-expanded="" class="uk-parent"><a><i class="fa fa-bars"></i> Menu</a>
|
|
||||||
<div class="uk-dropdown">
|
|
||||||
<ul class="uk-nav uk-nav-navbar uk-dropdown-navbar">
|
|
||||||
<li class="uk-active"><a href="/"><i class="fa fa-home"></i> Accueil</a></li>
|
|
||||||
<li class=""><a href="/nouvelles"><i class="fa fa-newspaper-o"></i> Nouvelles</a></li>
|
|
||||||
<li class=""><a href="/calendrier"><i class="fa fa-calendar"></i> Calendrier</a></li>
|
|
||||||
<li class=""><a href="/reglement"><i class="fa fa-legal"></i> Règlement</a></li>
|
|
||||||
<li class=""><a href="/membre"><i class="fa fa-lock"></i> Membre</a></li>
|
|
||||||
<li class=""><a href="/contact"><i class="fa fa-phone"></i> Nous Joindre</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{{ content }}
|
|
||||||
|
|
||||||
<footer class="uk-grid ph-footer" style="padding: 40px;">
|
|
||||||
<div class="uk-container uk-container-center uk-text-center">
|
|
||||||
<div class="uk-panel"><span class="uk-hidden-small">Ligue de balle les Phénix... pour le plaisir de jouer!</span>
|
|
||||||
<h4 class="uk-visible-small">Ligue de balle les Phénix</h4>
|
|
||||||
</div>
|
|
||||||
<ul class="uk-subnav uk-subnav-line uk-flex-center">
|
|
||||||
<li><a href="mailto:sportsphenixmtl@gmail.com">sportsphenixmtl@gmail.com</a></li>
|
|
||||||
</ul>
|
|
||||||
<h1><a href="https://www.facebook.com/SportsPhenixMtl"><i class="fa fa-facebook-square"></i></a></h1>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
|
|
||||||
{{ layout "templates/container.vto" }}
|
|
||||||
<script type="module" src="scripts/little-calendar.js"></script>
|
|
||||||
|
|
||||||
<div class="uk-grid">
|
|
||||||
<div class="uk-large-1-3 uk-medium-2-3 uk-container-center">
|
|
||||||
<div style="height:300px; width: 300px; background-size: 300px; background-image: url(images/logosansfond.JPG);" class="uk-cover-background uk-container-center uk-margin-left uk-margin-top"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-margin-top uk-margin-bottom uk-grid">
|
|
||||||
<div class="uk-width-large-3-5 uk-width-medium-4-5 uk-width-small-1-1 uk-container-center">
|
|
||||||
<div class="uk-grid uk-visible-large uk-grid-match">
|
|
||||||
<div class="uk-width-large-1-3 uk-width-medium-1-3">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge">
|
|
||||||
<i class="fa fa-newspaper-o" style="font-size: 1.7em;"></i>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{ include "templates/includes/home-news.vto" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-width-large-1-3 uk-width-medium-1-3">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-calendar" style="font-size: 1.7em;"></i></div>
|
|
||||||
<h3 class="uk-panel-title"><a href="#/calendrier" class="uk-link-muted">Calendrier</a></h3>
|
|
||||||
<div id="large-mithril-container"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-width-large-1-3 uk-width-medium-1-3">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-map-o" style="font-size: 1.7em;"></i></div>
|
|
||||||
<h3 class="uk-panel-title">Parc Walter-Stewart</h3>
|
|
||||||
{{ include "templates/includes/walter-stewart-map.vto" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- start -->
|
|
||||||
<div class="uk-visible-medium">
|
|
||||||
<div class="uk-grid uk-grid-match">
|
|
||||||
<div class="uk-width-medium-1-2">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-newspaper-o" style="font-size: 1.7em;"></i></div>
|
|
||||||
<div>
|
|
||||||
{{ include "templates/includes/home-news.vto" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-width-medium-1-2">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-calendar" style="font-size: 1.7em;"></i></div>
|
|
||||||
<h3 class="uk-panel-title"><a href="#/calendrier" class="uk-link-muted">Calendrier</a></h3>
|
|
||||||
<div id="medium-mithril-container"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-grid">
|
|
||||||
<div class="uk-width-medium-1-1">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-map-o" style="font-size: 1.7em;"></i></div>
|
|
||||||
<h3 class="uk-panel-title">Parc Walter-Stewart</h3>
|
|
||||||
{{ include "templates/includes/walter-stewart-map.vto" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- end -->
|
|
||||||
<div class="uk-visible-small">
|
|
||||||
<div class="uk-grid">
|
|
||||||
<div class="uk-width-small-1-1">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-newspaper-o" style="font-size: 1.7em;"></i></div>
|
|
||||||
<div>
|
|
||||||
{{ include "templates/includes/home-news.vto" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-grid">
|
|
||||||
<div class="uk-width-small-1-1">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-calendar" style="font-size: 1.7em;"></i></div>
|
|
||||||
<h3 class="uk-panel-title"><a href="#/calendrier" class="uk-link-muted">Calendrier</a></h3>
|
|
||||||
<div id="small-mithril-container">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-grid">
|
|
||||||
<div class="uk-width-small-1-1">
|
|
||||||
<div class="uk-panel uk-panel-box">
|
|
||||||
<div class="uk-panel-badge uk-badge"><i class="fa fa-map-o" style="font-size: 1.7em;"></i></div>
|
|
||||||
<h3 class="uk-panel-title">Parc Walter-Stewart</h3>
|
|
||||||
{{ include "templates/includes/walter-stewart-map.vto" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ /layout }}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
<div id="game-{{ game.id }}">
|
|
||||||
|
|
||||||
{{set prettyGameDate = new Date(game.date).toLocaleString("fr-CA", { month: 'long', year: 'numeric', day: 'numeric', timeZone: 'UTC'}) }}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3> {{ game.away }} vs {{ game.home }} | {{ prettyGameDate }} | {{ game.time }}</h3>
|
|
||||||
<form style="font-size: 1.1em;"
|
|
||||||
hx-trigger="input delay:250ms"
|
|
||||||
hx-post="/membre/games/{{game.id}}"
|
|
||||||
hx-target="#saving-result">
|
|
||||||
|
|
||||||
<div class="uk-form-row">
|
|
||||||
<label>
|
|
||||||
Points {{ game.away }} : <input type=number
|
|
||||||
min=0
|
|
||||||
name="away-score"
|
|
||||||
value="{{ game.awayScore || 0 }}"
|
|
||||||
{{if game.rain || game.practice }}disabled{{ /if}}>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-form-row">
|
|
||||||
<label>
|
|
||||||
Points {{ game.home }} : <input type=number
|
|
||||||
min=0
|
|
||||||
name="home-score"
|
|
||||||
value="{{ game.homeScore || 0 }}"
|
|
||||||
{{if game.rain || game.practice }}disabled{{ /if}}>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-form-row">
|
|
||||||
<label>
|
|
||||||
Partie reguliere <input type=radio
|
|
||||||
name="game-type"
|
|
||||||
value="regular-game"
|
|
||||||
{{if ! game.practice && ! game.rain }} checked {{/if}}>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Pluie <input type=radio
|
|
||||||
name="game-type"
|
|
||||||
value="rain"
|
|
||||||
{{if game.rain }}checked{{/if}}>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Pratique <input
|
|
||||||
type=radio
|
|
||||||
name="game-type"
|
|
||||||
value="practice"
|
|
||||||
{{if game.practice && ! game.rain }}checked{{/if}}>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="saving-result">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div style="text-align: right">
|
|
||||||
<button class="uk-button"
|
|
||||||
hx-post="/membre/games/{{game.id}}/switch"
|
|
||||||
hx-target="#game-{{ game.id }}"
|
|
||||||
>Inverser visiteur/receveur</button>
|
|
||||||
<button class="uk-button-danger"
|
|
||||||
hx-delete="/membre/games/{{game.id}}"
|
|
||||||
hx-confirm="Certain de vouloir supprimer la partie?"
|
|
||||||
hx-target="#game-{{ game.id }}"
|
|
||||||
hx-swap="delete"
|
|
||||||
>Supprimer</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<h3 class="uk-panel-title">
|
|
||||||
<a href="#/nouvelles" class="uk-link-muted">
|
|
||||||
<h2 id="pre-inscription">Pré-inscription</h2>
|
|
||||||
</a>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<p>Pré-inscription pour la balle-molle Phénix, ça ne coûte rien et c'est sans prix! <a href="https://forms.gle/Mv2wCVdbP83uiMdC7">https://forms.gle/Mv2wCVdbP83uiMdC7</a></p>
|
|
||||||
<p><strong><a href="#/nouvelles">lire la suite ...</a></strong></p>
|
|
||||||
</p>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{{ set selectedTeam = selectedTeam || "" }}
|
|
||||||
{{set isDisabled = isDisabled || false }}
|
|
||||||
{{set teams = ['jaune', 'rouge', 'bleu', 'gris', 'vert', 'noir', 'orange', 'mauve'] }}
|
|
||||||
|
|
||||||
|
|
||||||
<select name="{{- name -}}" class="uk-select" {{if isDisabled}} disabled {{ /if }}>
|
|
||||||
{{ for team of teams }}
|
|
||||||
<option name="{{team}}" {{if selectedTeam == team}} selected {{ /if }}>{{ team }}</option>
|
|
||||||
{{ /for }}
|
|
||||||
</select>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<div>
|
|
||||||
<iframe
|
|
||||||
style="border: 1px solid black; min-height: 300px;"
|
|
||||||
width="100%"
|
|
||||||
frameborder="0"
|
|
||||||
scrolling="no"
|
|
||||||
marginheight="0"
|
|
||||||
src="//www.openstreetmap.org/export/embed.html?bbox=-73.55936229228975%2C45.53102667048665%2C-73.55230271816255%2C45.53444249585723&layer=mapnik"; // "//www.openstreetmap.org/export/embed.html?bbox=-73.56355190277101%2C45.5294483312637%2C-73.54943275451662%2C45.53627996625677&layer=mapnik"; // "//www.openstreetmap.org/export/embed.html?bbox=-73.56355190277101%2C45.5294483312637%2C-73.54943275451662%2C45.53627996625677&layer=mapnik"
|
|
||||||
></iframe>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
<small>
|
|
||||||
<a href="http://www.openstreetmap.org/#map=17/45.53216/-73.55692">Voir la carte</a>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{{ layout "templates/container.vto" }}
|
|
||||||
|
|
||||||
<div id="content"
|
|
||||||
{{ if isLoggedIn }}
|
|
||||||
hx-get="/membre/games-editor/today"
|
|
||||||
hx-trigger="load"
|
|
||||||
hx-target="#content"
|
|
||||||
{{ /if }}
|
|
||||||
>
|
|
||||||
<div class="uk-grid uk-margin">
|
|
||||||
<div class="uk-width-1-1">
|
|
||||||
<div style="height: 250px; width: 250px; background-size: 250px; background-image: url(images/logosansfond.JPG);" class="uk-cover-background uk-container-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-panel uk-panel-box uk-panel-box-secondary uk-width-large-1-3 uk-container-center uk-margin">
|
|
||||||
<form action=""
|
|
||||||
class="uk-text-center"
|
|
||||||
|
|
||||||
hx-get="/login"
|
|
||||||
hx-target="#content">
|
|
||||||
<div class="uk-form-row"><label>Nom d'utilisateur <input type="text" name="username" class="uk-form-width-small"></label></div>
|
|
||||||
<div class="uk-form-row"><label>Mot de passe <input type="password" name="password" class="uk-form-width-small"></label></div>
|
|
||||||
<div class="uk-form-row"><button class="uk-button"><i class="fa fa-mars-double"></i> Pénétrez</button></div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{{ /layout }}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
{{ layout "templates/container.vto" }}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="uk-width-large-2-5 uk-width-medium-3-5 uk-width-small-1-1 uk-container-center">
|
|
||||||
<div>
|
|
||||||
<article class="uk-article uk-margin-">
|
|
||||||
<h1 class="uk-article-title">
|
|
||||||
<h2 id="pre-inscription">Pré-inscription</h2>
|
|
||||||
</h1>
|
|
||||||
<p class="uk-article-meta">
|
|
||||||
<p>Samedi 8 Août 2026</p>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
La pré-inscription pour la balle-molle Phénix est maintenant ouverte. Ça ne coûte rien et c'est sans prix!
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://forms.gle/Mv2wCVdbP83uiMdC7">https://forms.gle/Mv2wCVdbP83uiMdC7</a>
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-width-large-2-5 uk-width-medium-3-5 uk-width-small-1-1 uk-container-center">
|
|
||||||
<div>
|
|
||||||
<article class="uk-article uk-margin-">
|
|
||||||
<h1 class="uk-article-title">
|
|
||||||
<h2 id="saison-2025">Saison 2026</h2>
|
|
||||||
</h1>
|
|
||||||
<p class="uk-article-meta">
|
|
||||||
<p>Lundi 9 Mars 2026</p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
|
|
||||||
La saison de la balle annonce à grand pas et les inscriptions débuteront lundi prochain (16 mars 2026). Le coût de la saison sera le même que l'an passé: 155$ pour la saison + 20$ de dépôt remboursable pour le prêt du chandail.
|
|
||||||
|
|
||||||
</p><p>
|
|
||||||
Comme à l'habitude, nous jouerons au parc walter-stewart les jeudis soir. La ligue comptera quatre équipes pour un grand total de 44 joueurs.
|
|
||||||
</p><p>
|
|
||||||
Les inscriptions en présentiel auront lieu le vendredi 20 mars au bar fullum entre 18h00 et 21h00. Pour ceux qui veulle venir prendre une petite bière avec nous ou payer en argent compten.
|
|
||||||
</p><p>
|
|
||||||
On vous tiens au courrant!
|
|
||||||
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="uk-width-large-2-5 uk-width-medium-3-5 uk-width-small-1-1 uk-container-center">
|
|
||||||
<div>
|
|
||||||
<article class="uk-article uk-margin-">
|
|
||||||
<h1 class="uk-article-title">
|
|
||||||
<h2 id="saison-2025">Saison 2025</h2>
|
|
||||||
</h1>
|
|
||||||
<p class="uk-article-meta">
|
|
||||||
<p>Vendredi 25 Avril 2025</p>
|
|
||||||
</p>
|
|
||||||
<p>Bonne saison à tous et toutes!</p>
|
|
||||||
<p>N'oubliez pas de consulter la page facebook: <a href="https://www.facebook.com/SportsPhenixMtl/">https://www.facebook.com/SportsPhenixMtl/</a> pour toutes mises à jour, y compris les annulations due à la météo.</p>
|
|
||||||
<p>Votre Comité 2023</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ /layout }}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
{{set isToday = !!isToday }}
|
|
||||||
|
|
||||||
<div class="uk-width-large-2-3 uk-container-center uk-margin">
|
|
||||||
|
|
||||||
<div class="uk-margin" style="margin-top: 1em">
|
|
||||||
<ul class="uk-tab">
|
|
||||||
<li {{if isToday}} class="uk-active" {{ /if}}>
|
|
||||||
<a hx-get="/membre/games-editor/today"
|
|
||||||
hx-target="#content">
|
|
||||||
Aujourd'hui
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{ for year of years }}
|
|
||||||
<li {{if year === selectedYear}} class="uk-active" {{ /if}}>
|
|
||||||
<a hx-get="/membre/games-editor/{{year}}"
|
|
||||||
hx-target="#content">
|
|
||||||
Saison {{year}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{ /for }}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{{ for game of games }}
|
|
||||||
<div class="uk-panel uk-panel-box uk-panel-box-secondary uk-margin">
|
|
||||||
{{ include "templates/includes/game-editor.vto" {game} }}
|
|
||||||
</div>
|
|
||||||
{{ /for }}
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<button class="uk-button"
|
|
||||||
hx-get="/membre/new-game-editor"
|
|
||||||
hx-target="#content">
|
|
||||||
Nouvelle Partie
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<div class="uk-width-large-2-3 uk-container-center uk-margin">
|
|
||||||
|
|
||||||
<div class="uk-panel uk-panel-box uk-panel-box-secondary uk-margin">
|
|
||||||
<pre>{{ error }}</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<div class="uk-width-large-2-3 uk-container-center uk-margin">
|
|
||||||
|
|
||||||
<div class="uk-margin"></div>
|
|
||||||
|
|
||||||
<div class="uk-panel uk-panel-box uk-panel-box-secondary uk-margin">
|
|
||||||
<form hx-post="/membre/new-game"
|
|
||||||
hx-target="#created-game"
|
|
||||||
hx-swap="afterbegin">
|
|
||||||
<div class="uk-form-row" style="text-align: center; font-size: 2em;">
|
|
||||||
|
|
||||||
{{ include "templates/includes/team-selector.vto" {name: "away-team"} }}
|
|
||||||
<label>
|
|
||||||
Vs.
|
|
||||||
</label>
|
|
||||||
{{ include "templates/includes/team-selector.vto" {name: "home-team"}}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-form-row">
|
|
||||||
<label>
|
|
||||||
Date: <input type=date name="game-date" required>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-form-row">
|
|
||||||
<label>
|
|
||||||
Heure: <input type="time" name="game-time">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div style="text-align: right;">
|
|
||||||
<button class="uk-button">Creer</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="created-game"></div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<div class="uk-width-large-2-3 uk-container-center uk-margin">
|
|
||||||
|
|
||||||
<div class="uk-panel uk-panel-box uk-panel-box-secondary uk-margin">
|
|
||||||
{{ include "templates/includes/game-editor.vto" {game} }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
{{ layout "templates/container.vto" }}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="uk-margin-top uk-margin-bottom uk-grid">
|
|
||||||
<div class="uk-width-large-3-5 uk-width-medium-4-5 uk-width-large-1-1 uk-container-center">
|
|
||||||
<div class="uk-grid">
|
|
||||||
<div class="uk-width-large-1-3 uk-width-small-1-1">
|
|
||||||
<div style="height: 250px; width: 250px; background-size: 250px; background-image: url(images/logosansfond.JPG);" class="uk-cover-background uk-container-center"></div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-width-large-2-3 uk-width-small-1-1">
|
|
||||||
<div class="uk-panel uk-panel-box uk-panel-box-secondary">
|
|
||||||
<h2>Règlements 2021</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<h3><a href="files/regle_de_jeux_2021.pdf">Règles de jeu</a></h3>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h3><a href="files/regles_et_procedures_2021.pdf">Règles et procédures</a></h3>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h3><a href="files/charte_organisationnelle_2021.pdf">Charte Organisationnelle</a></h3>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h3><a href="files/formulaire_inscription_2021.pdf">Formulaire d'inscription</a></h3>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h3><a href="files/code_ethique_2021.pdf">Code d'éthique</a></h3>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h3><a href="http://www.asbm.ca/">Association des Sports de Balle à Montréal (ASBN)</a></h3>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="uk-panel uk-panel-box uk-panel-box-secondary">
|
|
||||||
<h2>Mission</h2>
|
|
||||||
<p>La ligue de balle Les Phénix a été fondée en 2004 afin d'offrir aux membres de la communauté gaie et lesbienne et à leurs amis hétéros l'opportunité de s'amuser et de pratiquer un sport dans une ambiance amicale et décontractée. L'ouverture d'esprit, la camaraderie et le plaisir de pratiquer un sport à l'extérieur caractérisent cette ligue.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{ /layout }}
|
|
||||||
Reference in new issue
Block a user