Compare commits
80 Commits
9f9da4e191
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
87b2abbd80
|
|||
|
b0de3fb221
|
|||
|
149f0a411b
|
|||
|
e6d1d5a697
|
|||
|
bf9d0c1a78
|
|||
|
64fa16740b
|
|||
|
f273677ba7
|
|||
|
ca194d516d
|
|||
|
ed18389bb2
|
|||
|
b346710496
|
|||
|
6d1588e80f
|
|||
|
858cb24e3f
|
|||
|
196eb5f51d
|
|||
|
07446570c1
|
|||
|
a5c47ff9a7
|
|||
|
aa2ebf0b2e
|
|||
|
5f02ea4d5c
|
|||
|
39380eaf03
|
|||
|
cb131353dc
|
|||
|
e1c9a7b81b
|
|||
|
c495b265ee
|
|||
|
49864874fc
|
|||
|
e9fd60e619
|
|||
|
84da372330
|
|||
|
c696ffb4bc
|
|||
|
a06807b028
|
|||
|
07be459781
|
|||
|
769fa60196
|
|||
|
d377399c10
|
|||
|
fa50ab93dc
|
|||
|
73e3afcecc
|
|||
|
af9fa3bd9b
|
|||
|
7e803cc8e3
|
|||
|
339d4c7923
|
|||
|
58825b5bcd
|
|||
|
6b9e6734fe
|
|||
|
03a9ac3aae
|
|||
|
da159f2b13
|
|||
|
c2c192bdc6
|
|||
|
c2b1898b91
|
|||
|
4ee466e7cb
|
|||
|
6592f2eeae
|
|||
|
1f2ba45e54
|
|||
|
588c23ec3f
|
|||
|
1c3cafdcda
|
|||
|
fb0ca76c29
|
|||
|
bdb6a77371
|
|||
|
e4b981d676
|
|||
|
c4c0d0fb7d
|
|||
|
3695cd8975
|
|||
|
bcade85182
|
|||
|
d50f94acc8
|
|||
|
5e1facf24a
|
|||
|
cf01bf9fff
|
|||
|
83e722cdb9
|
|||
|
dd48aeca8d
|
|||
|
f421089eb9
|
|||
|
d484f8cfdf
|
|||
|
87735f76b5
|
|||
|
6d4600a858
|
|||
|
5c19a16f8b
|
|||
|
aea6a64b69
|
|||
|
b9ead6770a
|
|||
|
84cc1f651c
|
|||
|
a1cb6fcf0a
|
|||
|
e459a0688a
|
|||
|
8e98286f7c
|
|||
|
87fb835590
|
|||
|
1f897d2b1e
|
|||
|
8c1b325532
|
|||
|
4b56259b98
|
|||
|
16b1402c6f
|
|||
|
e4f6576847
|
|||
|
2df02e7452
|
|||
|
99d376af4c
|
|||
|
2b497a0227
|
|||
|
00b270e0f6
|
|||
|
2a2eb07823
|
|||
|
39e6c2b5af
|
|||
|
832fb654ec
|
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -4,10 +4,22 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach",
|
||||
"port": 9229,
|
||||
"request": "attach",
|
||||
"restart": true,
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"localRoot": "${workspaceFolder}/src",
|
||||
"remoteRoot": "/home/node/app/src"
|
||||
},
|
||||
{
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"name": "nodemon",
|
||||
"name": "nodemon (dev)",
|
||||
"program": "dev",
|
||||
"request": "launch",
|
||||
"restart": true,
|
||||
@@ -16,8 +28,8 @@
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"env": {"NODE_ENV": "development"},
|
||||
"preLaunchTask": "npm: scss"
|
||||
"env": {"NODE_ENV": "development", "DEBUG": "app"},
|
||||
"preLaunchTask": "npm: build-css"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
.vscode/tasks.json
vendored
16
.vscode/tasks.json
vendored
@@ -3,17 +3,21 @@
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "scss",
|
||||
"script": "watch-scss",
|
||||
"problemMatcher": [],
|
||||
"label": "npm: scss watch",
|
||||
"detail": "sass --watch src/scss/application.scss public/css/application.css src/scss/eventsheet.scss:public/css/eventsheet.css"
|
||||
"label": "npm: watch-scss",
|
||||
"detail": "npm run watch-css",
|
||||
"icon": {
|
||||
"id": "eye",
|
||||
"color": "terminal.ansiBlue"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "scss",
|
||||
"script": "build-css",
|
||||
"problemMatcher": [],
|
||||
"label": "npm: scss",
|
||||
"detail": "sass src/scss/application.scss:public/css/application.css src/scss/eventsheet.scss:public/css/eventsheet.css"
|
||||
"label": "npm: build-css",
|
||||
"detail": "npm build-css"
|
||||
}
|
||||
]
|
||||
}
|
||||
21
bin/www
21
bin/www
@@ -10,8 +10,7 @@ var https = require("https");
|
||||
var fs = require("fs");
|
||||
var debug = require("debug")("https");
|
||||
const path = require("path");
|
||||
|
||||
|
||||
var livereload = require("livereload");
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
@@ -19,30 +18,16 @@ const path = require("path");
|
||||
|
||||
var port = normalizePort(process.env.PORT || "3000");
|
||||
app.set("port", port);
|
||||
|
||||
var server = http.createServer(app);
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
/**
|
||||
* Create HTTPS server.
|
||||
*/
|
||||
const https_options = {
|
||||
key: fs.readFileSync("certs/key.pem"),
|
||||
cert: fs.readFileSync("certs/cert.pem"),
|
||||
};
|
||||
var livereload = require("livereload");
|
||||
|
||||
const liveReloadServer = livereload.createServer({https: https_options});
|
||||
const liveReloadServer = livereload.createServer({port:35729});
|
||||
liveReloadServer.watch(path.join(__dirname, "../src/views"));
|
||||
liveReloadServer.server.once("connection", () => {
|
||||
setTimeout(() => {
|
||||
liveReloadServer.refresh("/");
|
||||
}, 100);
|
||||
});
|
||||
|
||||
var server = https.createServer(https_options, app);
|
||||
}
|
||||
else {
|
||||
var server = http.createServer(app);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
{$DOMAIN} {
|
||||
reverse_proxy app:3000
|
||||
{
|
||||
{$LOG_LEVEL} # Set via environment variable
|
||||
}
|
||||
|
||||
localhost {
|
||||
# Development configuration
|
||||
@notProd {
|
||||
expression {env.ENVIRONMENT} == 'development'
|
||||
}
|
||||
handle @notProd {
|
||||
# Configuration that only applies when not in production
|
||||
reverse_proxy app-dev:3000
|
||||
}
|
||||
}
|
||||
|
||||
{$DOMAIN} {
|
||||
# Production configuration
|
||||
@prod {
|
||||
expression {env.ENVIRONMENT} == 'production'
|
||||
}
|
||||
handle @prod {
|
||||
# Configuration that only applies in production
|
||||
# header Strict-Transport-Security "max-age=31536000;"
|
||||
reverse_proxy app:3000
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,33 @@
|
||||
version: "3.3"
|
||||
|
||||
services:
|
||||
app:
|
||||
app: &app
|
||||
env_file:
|
||||
- .env
|
||||
build: .
|
||||
networks:
|
||||
- web
|
||||
profiles:
|
||||
- production
|
||||
expose:
|
||||
- 3000
|
||||
|
||||
app-dev:
|
||||
<<: *app
|
||||
ports:
|
||||
- 9229:9229 #debugger
|
||||
- 35729:35729 #livereload
|
||||
profiles:
|
||||
- development
|
||||
command: npm run dev
|
||||
volumes:
|
||||
- ./src:/home/node/app/src
|
||||
- ./package.json:/home/node/app/package.json
|
||||
- ./package-lock.json:/home/node/app/package-lock.json
|
||||
- ./certs:/home/node/app/certs
|
||||
- ./bin/www:/home/node/app/bin/www
|
||||
environment:
|
||||
DEBUG: "app"
|
||||
NODE_ENV: "development"
|
||||
|
||||
caddy:
|
||||
image: caddy
|
||||
ports:
|
||||
@@ -17,6 +35,8 @@ services:
|
||||
- 443:443
|
||||
volumes:
|
||||
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
networks:
|
||||
- web
|
||||
env_file:
|
||||
@@ -24,3 +44,7 @@ services:
|
||||
|
||||
networks:
|
||||
web:
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
71
package-lock.json
generated
71
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@teamsnap/teamsnap-ui": "^3.12.3",
|
||||
"better-sqlite3": "^9.4.1",
|
||||
"better-sqlite3": "^9.6.0",
|
||||
"better-sqlite3-session-store": "^0.1.0",
|
||||
"bootstrap": "^5.3.1",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
@@ -33,6 +33,7 @@
|
||||
"passport-teamsnap": "^1.1.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"pug": "^3.0.2",
|
||||
"sass": "^1.77.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"teamsnap.js": "github:anthonyscorrea/teamsnap-javascript-sdk#link-with-null-link",
|
||||
"tinymce": "^6.8.3",
|
||||
@@ -663,7 +664,6 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
@@ -813,9 +813,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/better-sqlite3": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.4.1.tgz",
|
||||
"integrity": "sha512-QpqiQeMI4WkE+dQ68zTMX5OzlPGc7lXIDP1iKUt4Omt9PdaVgzKYxHIJRIzt1E+RUBQoFmkip/IbvzyrxehAIg==",
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz",
|
||||
"integrity": "sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"bindings": "^1.5.0",
|
||||
@@ -834,7 +834,6 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -997,7 +996,6 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
@@ -1169,7 +1167,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
@@ -1618,9 +1615,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
|
||||
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -2040,7 +2037,6 @@
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -2195,7 +2191,6 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2304,7 +2299,6 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
@@ -2564,6 +2558,11 @@
|
||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
|
||||
"integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ=="
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
@@ -2632,7 +2631,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
@@ -2704,7 +2702,6 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -2722,7 +2719,6 @@
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
@@ -2745,7 +2741,6 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -3270,9 +3265,9 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node_modules/node-abi": {
|
||||
"version": "3.54.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz",
|
||||
"integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==",
|
||||
"version": "3.62.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz",
|
||||
"integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==",
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
@@ -3373,7 +3368,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -3667,7 +3661,6 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
@@ -3719,9 +3712,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
||||
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
|
||||
"integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.0",
|
||||
"expand-template": "^2.0.3",
|
||||
@@ -4078,7 +4071,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
@@ -4228,6 +4220,22 @@
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.77.2",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz",
|
||||
"integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==",
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"sass": "sass.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
@@ -4466,6 +4474,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
@@ -4735,7 +4751,6 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
||||
13
package.json
13
package.json
@@ -24,13 +24,17 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node ./bin/www",
|
||||
"dev": "nodemon . & npm run scss",
|
||||
"scss": "sass src/scss/application.scss:src/public/css/application.css src/scss/eventsheet.scss:src/public/css/eventsheet.css",
|
||||
"scss watch": "sass --watch src/scss/application.scss:src/public/css/application.css src/scss/eventsheet.scss:src/public/css/eventsheet.css"
|
||||
"dev": "nodemon --inspect=0.0.0.0 ./bin/www",
|
||||
"build-css": "sass src/scss:src/public/css",
|
||||
"watch-scss": "nodemon -e scss -x \"npm run build-css\""
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ext": "js,hbs,scss",
|
||||
"watch": ["src"]
|
||||
},
|
||||
"dependencies": {
|
||||
"@teamsnap/teamsnap-ui": "^3.12.3",
|
||||
"better-sqlite3": "^9.4.1",
|
||||
"better-sqlite3": "^9.6.0",
|
||||
"better-sqlite3-session-store": "^0.1.0",
|
||||
"bootstrap": "^5.3.1",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
@@ -54,6 +58,7 @@
|
||||
"passport-teamsnap": "^1.1.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"pug": "^3.0.2",
|
||||
"sass": "^1.77.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"teamsnap.js": "github:anthonyscorrea/teamsnap-javascript-sdk#link-with-null-link",
|
||||
"tinymce": "^6.8.3",
|
||||
|
||||
15
src/app.js
15
src/app.js
@@ -62,12 +62,14 @@ app.set("view engine", "hbs");
|
||||
app.locals.pluralize = require("pluralize");
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log('adding connectLiveReload')
|
||||
var connectLiveReload = require("connect-livereload");
|
||||
app.use(connectLiveReload());
|
||||
app.use("/scss", express.static(path.join(__dirname, "scss")));
|
||||
app.use(connectLiveReload({port: 35729, src:"http://localhost:35729/livereload.js?snipver=1"}));
|
||||
}
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded());
|
||||
app.use(bodyParser.urlencoded({extended: true }));
|
||||
app.use(logger("dev"));
|
||||
app.use(cors(corsOptions))
|
||||
app.use(cookieParser());
|
||||
@@ -118,8 +120,10 @@ app.use(
|
||||
intervalMs: 900000 //ms = 15min
|
||||
}
|
||||
}),
|
||||
cookie: { maxAge: 86400000 }, // value of maxAge is defined in milliseconds.
|
||||
teamsnap_token: "",
|
||||
current_team: "",
|
||||
csrfToken:"",
|
||||
secret: process.env['SECRET'],
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
@@ -169,9 +173,10 @@ app.use(function (err, req, res, next) {
|
||||
});
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
// app.use(function (req, res, next) {
|
||||
// next(createError(404));
|
||||
// });
|
||||
app.use(function (req, res, next) {
|
||||
// next(createError(404));
|
||||
res.status(404).send('not found')
|
||||
});
|
||||
|
||||
app.set('trust proxy')
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ exports.helpers = {
|
||||
|
||||
exports.partials = path.join(__dirname, "../views/event/partials")
|
||||
|
||||
exports.confirmModalAvailabilityReminders = async (req, res) => {
|
||||
res.status(200).render("event/partials/modal_availability_reminders")
|
||||
}
|
||||
|
||||
exports.getEvents = async (req, res, next) => {
|
||||
const {user, team, layout} = req
|
||||
const bulkLoadTypes = ["event", "availabilitySummary"]
|
||||
@@ -67,3 +71,53 @@ exports.getEvent = async (req, res, next) => {
|
||||
};
|
||||
res.render("event/show", context);
|
||||
};
|
||||
|
||||
exports.sendAvailabilityReminders = async (req,res,next) => {
|
||||
await Promise.all(req.promises)
|
||||
if (!req.body || ! (req.body.event_id && req.body.memberIds)) {
|
||||
res.status(400).send('Malformed post')
|
||||
}
|
||||
if (req.params.event_id != req.body.eventId) {
|
||||
// Load actual event. Do I want this to be an error? probably
|
||||
res.status(400).send('Event ID parameter does not match the POST body')
|
||||
return;
|
||||
}
|
||||
|
||||
const {event} = req
|
||||
const {eventId, memberIds} = req.body
|
||||
const sendingMember = req.members.find(m=>m.userId==req.user.id)
|
||||
try {
|
||||
const promise = teamsnap.sendAvailabilityReminders(event, sendingMember, memberIds)
|
||||
await promise
|
||||
.then (res.status(200).send('OK'))
|
||||
} catch (err) {
|
||||
res.status(500).send()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
exports.submitResetAvailabilities = async (req,res,next) => {
|
||||
await Promise.all(req.promises)
|
||||
if (!req.body || ! (req.body.event_id && req.body.memberIds)) {
|
||||
res.status(400).send('Malformed post')
|
||||
}
|
||||
|
||||
if (req.params.event_id != req.body.event_id) {
|
||||
// Load actual event. Do I want this to be an error? probably
|
||||
res.status(400).send('Event ID parameter does not match the POST body');
|
||||
return
|
||||
}
|
||||
|
||||
const {event_id, memberIds} = req.body
|
||||
const reset_promises = []
|
||||
|
||||
const availabilities = await teamsnap.loadAvailabilities({eventId: event_id}, teamsnapCallback);
|
||||
|
||||
availabilities.filter(availability =>memberIds.includes(availability.memberId.toString())).forEach( availability => {
|
||||
availability.statusCode = teamsnap.AVAILABILITIES.NONE
|
||||
const promise = teamsnap.saveAvailability(availability, teamsnapCallback)
|
||||
reset_promises.push(promise)
|
||||
})
|
||||
await Promise.all(reset_promises)
|
||||
.then(res.status(200).send('OK'))
|
||||
}
|
||||
@@ -1,99 +1,11 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const {embeddedSvgFromPath, parsePositionLabel, compilePositionLabel} = require("../lib/utils")
|
||||
const {groupTeamsnapItems, parsePositionLabel, compilePositionLabel, teamsnapCallback} = require("../lib/utils")
|
||||
const tsUtils = require('../lib/utils')
|
||||
const { loadEventLineupEntries } = require('teamsnap.js')
|
||||
|
||||
exports.partials = path.join(__dirname, "../views/eventlineup/partials")
|
||||
|
||||
const statusCodeIcons = {
|
||||
1: embeddedSvgFromPath("/teamsnap-ui/assets/icons/check.svg"),
|
||||
0: embeddedSvgFromPath("/teamsnap-ui/assets/icons/dismiss.svg"),
|
||||
2: embeddedSvgFromPath("/bootstrap-icons/question-lg.svg"),
|
||||
null: embeddedSvgFromPath("/bootstrap-icons/question.svg"),
|
||||
undefined: embeddedSvgFromPath("/bootstrap-icons/question-lg.svg")
|
||||
}
|
||||
|
||||
exports.helpers = {
|
||||
flagsString: (flags) => {
|
||||
return flags != null ? Array.from(flags).join(",") : ''
|
||||
},
|
||||
plus1: (i) => Number(i)+1,
|
||||
positions: () => ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH", "DR"],
|
||||
defense_positions: () => ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "P"],
|
||||
avail_status_code_icon: (status_code) => {
|
||||
const icon_classes = {
|
||||
1: "u-colorPositive",
|
||||
0: "u-colorNegative",
|
||||
2: "u-colorPrimary",
|
||||
null: "u-colorGrey",
|
||||
undefined: "u-colorGrey"
|
||||
}
|
||||
|
||||
const button_classes = {
|
||||
1: "Button--yes",
|
||||
0: "Button--no",
|
||||
2: "Button--maybe",
|
||||
null: "",
|
||||
undefined: ""
|
||||
}
|
||||
|
||||
return `<button class="Button Button--smallSquare ${button_classes[status_code]}" type="button"><span class="">${statusCodeIcons[status_code]}</span></button>`
|
||||
},
|
||||
positionLabelWithoutFlags: (label) => {
|
||||
const {positionLabelWithoutFlags} = parsePositionLabel(label);
|
||||
return positionLabelWithoutFlags
|
||||
},
|
||||
positionLabelWithoutPOFlag: (label) => {
|
||||
const {positionLabelWithoutFlags, positionFlags} = parsePositionLabel(label);
|
||||
positionFlags.delete('PO')
|
||||
return compilePositionLabel(positionLabelWithoutFlags, positionFlags)
|
||||
},
|
||||
positionFlags: (label)=> {
|
||||
const {positionFlags} = parsePositionLabel(label);
|
||||
return `[${Array.from(positionFlags).join(",")}]`
|
||||
},
|
||||
hasPositionFlags: (label) => {
|
||||
const {positionLabelWithoutFlags, positionFlags} = parsePositionLabel(label);
|
||||
return positionFlags.size > 0;
|
||||
},
|
||||
comparePositionWithFlags: (labelWithoutFlags, eventLineupEntry, options) => {
|
||||
labelWithFlags = eventLineupEntry?.label
|
||||
const {positionLabelWithoutFlags} = parsePositionLabel(labelWithFlags);
|
||||
return positionLabelWithoutFlags == labelWithoutFlags;
|
||||
},
|
||||
isStarting: (member) => {
|
||||
return (member.benchcoach?.eventLineupEntry != null);
|
||||
},
|
||||
isInStartingLineup: (member) => {
|
||||
if (member.benchcoach.eventLineupEntry == null || member.benchcoach.eventLineupEntry.label == '') return false;
|
||||
const {positionFlags} = parsePositionLabel(member.benchcoach.eventLineupEntry?.label);
|
||||
return (!positionFlags.has("PO"))
|
||||
},
|
||||
isInPositionOnly: (member) => {
|
||||
if (!member.benchcoach || member.benchcoach.eventLineupEntry == null) return false;
|
||||
const {positionFlags} = parsePositionLabel(member.benchcoach.eventLineupEntry?.label);
|
||||
return (member.benchcoach.eventLineupEntry != null && positionFlags.has("PO"))
|
||||
},
|
||||
isInBench: (member) => {
|
||||
if ((member.benchcoach.eventLineupEntry != null && member.benchcoach.eventLineupEntry.label != '') || member.isNonPlayer) return false;
|
||||
return (member.benchcoach.availability?.statusCode != 0 && member.benchcoach.availability?.statusCode != null)
|
||||
},
|
||||
isInOut: (member) => {
|
||||
if ((member.benchcoach.eventLineupEntry != null && member.benchcoach.eventLineupEntry.label != '') || member.isNonPlayer) return false;
|
||||
return (member.benchcoach.availability?.statusCode == 0 || member.benchcoach.availability?.statusCode == null)
|
||||
},
|
||||
availabilityStatusShort: (availability) => {
|
||||
const {YES, MAYBE, NO, NONE} = teamsnap.AVAILABILITIES
|
||||
const statusShortLookup = {}
|
||||
statusShortLookup[YES] = "YES"
|
||||
statusShortLookup[MAYBE] = "MAY"
|
||||
statusShortLookup[NO] = "NO"
|
||||
statusShortLookup[NONE] = "UNK"
|
||||
statusShortLookup[undefined] = "UNK"
|
||||
return (statusShortLookup[availability?.statusCode])
|
||||
}
|
||||
}
|
||||
exports.helpers = require('../helpers/eventlineup.js')
|
||||
|
||||
exports.getEventLineup = async (req, res)=>{
|
||||
await Promise.all(req.promises)
|
||||
@@ -105,7 +17,7 @@ exports.getEventLineup = async (req, res)=>{
|
||||
"/js/eventlineup.js",
|
||||
"/js/tinymce.min.js"
|
||||
]
|
||||
res.render("eventlineup/edit", {user, team, members, event, scripts, layout, event_lineup, event_lineup_entries, availabilitySummary, csrfToken})
|
||||
res.render("eventlineup/edit", {user, team, members, event, availabilities, scripts, layout, event_lineup, event_lineup_entries, availabilitySummary, csrfToken})
|
||||
}
|
||||
|
||||
exports.getAdjacentEventLineup = async (req, res) => {
|
||||
@@ -121,15 +33,19 @@ exports.getAdjacentEventLineup = async (req, res) => {
|
||||
} else {
|
||||
throw new Error('Index must be positive or negative number')
|
||||
}
|
||||
if (!event) {
|
||||
res.status(500).send()
|
||||
return
|
||||
}
|
||||
const availabilitySummary = event.availabilitySummary
|
||||
const event_lineup = req.timeline.event_lineups.find(i=>i.eventId==event.id)
|
||||
const event_lineup_entries = req.timeline.event_lineup_entries.filter(i=>i.eventId==event.id)
|
||||
const event_lineup = req.timeline.event_lineups?.find(i=>i.eventId==event.id)
|
||||
const event_lineup_entries = req.timeline.event_lineup_entries?.filter(i=>i.eventId==event.id)
|
||||
const availabilities = req.timeline.availabilities.filter(i=>i.eventId==event.id)
|
||||
attachBenchcoachPropertiesToMember(members, event_lineup_entries, availabilities)
|
||||
members.sort(tsUtils.teamsnapMembersSortLineupAvailabilityLastName)
|
||||
console.log()
|
||||
// res.status(200).send('Received')
|
||||
res.render("eventlineup/edit", {user, team, members, event, layout: null, event_lineup, event_lineup_entries, availabilitySummary, csrfToken})
|
||||
|
||||
res.render("eventlineup/edit", {user, team, members, event, layout: null, event_lineup, event_lineup_entries, availabilitySummary, availabilities, csrfToken})
|
||||
}
|
||||
|
||||
attachBenchcoachPropertiesToMember = (members, event_lineup_entries, availabilities) => {
|
||||
@@ -142,7 +58,7 @@ attachBenchcoachPropertiesToMember = (members, event_lineup_entries, availabilit
|
||||
// as far as I can tell, member_name should consistently be formulated from first and last name
|
||||
// perhaps could have some edge cases if first or last names change, but this *should be* exceedingly rare.
|
||||
const member_name = `${member.firstName} ${member.lastName}`
|
||||
const event_lineup_entry = event_lineup_entries.find(e=> e.memberId == member.id || e.memberName == member_name)
|
||||
const event_lineup_entry = event_lineup_entries?.find(e=> e.memberId == member.id || e.memberName == member_name)
|
||||
const availability = availabilities.find(e=>e.memberId == member.id)
|
||||
member.benchcoach.availability = availability
|
||||
if (event_lineup_entry != null) {
|
||||
@@ -172,6 +88,10 @@ exports.getEventLineupEmail = async (req, res)=>{
|
||||
res.status(200).render("eventlineup/partials/email_modal.hbs", {layout:null, user, team, members, event, event_lineup, event_lineup_entries: newEventLineupEntries, availabilities, availabilitySummary})
|
||||
}
|
||||
|
||||
exports.getAvailabilityRemindersModal = (req, res) => {
|
||||
res.status(200).render("eventlineup/partials/availability_reminder_modal.hbs")
|
||||
}
|
||||
|
||||
exports.getEventLineupEntries = async (req, res)=>{
|
||||
const {event_lineup, event_lineup_entries} = req
|
||||
res.setHeader('Content-Type', 'application/json').send(JSON.stringify(req.event_lineup_entries))
|
||||
@@ -189,14 +109,20 @@ exports.postEventLineup = async (req,res) => {
|
||||
const eventLineupEntries = req.event_lineup.eventLineupEntries
|
||||
const {newEventLineupEntries, deleteEventLineupEntries} = processPostedEventLineupEntries(body, eventLineupEntries, req.event_lineup)
|
||||
newEventLineupEntries.forEach(e=>{
|
||||
teamsnap.saveEventLineupEntry(e)
|
||||
teamsnap.saveEventLineupEntry(e, teamsnapCallback)
|
||||
})
|
||||
deleteEventLineupEntries.forEach(e=>{
|
||||
teamsnap.deleteEventLineupEntry(e)
|
||||
teamsnap.deleteEventLineupEntry(e, teamsnapCallback)
|
||||
})
|
||||
|
||||
eventLineup = await teamsnap.loadEventLineups(req.params.event_id)
|
||||
res.status(201).end()
|
||||
const bulk_items = await teamsnap.bulkLoad(
|
||||
{teamId: req.params.team_id, types: ['eventLineup', 'eventLineupEntry'], scopeTo:'event', event__id:req.params.event_id,},
|
||||
null,
|
||||
(err, items) => {teamsnapCallback(err, items, {req, source:"postEventLineup", method:'bulkLoad'})}
|
||||
)
|
||||
groupedReturnedItems = groupTeamsnapItems(bulk_items)
|
||||
returnedEventLineupEntries = groupedReturnedItems.eventLineupEntries
|
||||
res.status(201).end(JSON.stringify(returnedEventLineupEntries))
|
||||
}
|
||||
|
||||
const processPostedEventLineupEntries = (body, eventLineupEntries, eventLineup) => {
|
||||
@@ -210,10 +136,14 @@ const processPostedEventLineupEntries = (body, eventLineupEntries, eventLineup)
|
||||
const lineupEntryFlags = body.flags[i]
|
||||
if (lineupEntryId != '' && lineupEntryLabel != '') {
|
||||
// Update lineup entry
|
||||
const eventLineupEntry = eventLineupEntries.find((e)=>e.id==Number(lineupEntryId))
|
||||
eventLineupEntry.sequence = lineupEntrySequence
|
||||
eventLineupEntry.label = compilePositionLabel(lineupEntryLabel, lineupEntryFlags)
|
||||
newEventLineupEntries.push(eventLineupEntry)
|
||||
try {
|
||||
const eventLineupEntry = eventLineupEntries.find((e)=>e.id==Number(lineupEntryId))
|
||||
eventLineupEntry.sequence = lineupEntrySequence
|
||||
eventLineupEntry.label = compilePositionLabel(lineupEntryLabel, lineupEntryFlags)
|
||||
newEventLineupEntries.push(eventLineupEntry)
|
||||
} catch {
|
||||
console.log
|
||||
}
|
||||
}
|
||||
else if (lineupEntryId != '') {
|
||||
// Delete lineup entry
|
||||
@@ -235,3 +165,32 @@ const processPostedEventLineupEntries = (body, eventLineupEntries, eventLineup)
|
||||
})
|
||||
return {newEventLineupEntries, eventLineupEntries, deleteEventLineupEntries}
|
||||
}
|
||||
|
||||
exports.submitDeleteEventLineupEntries = async (req,res) => {
|
||||
await Promise.all(req.promises);
|
||||
const {event_lineup, event_lineup_entries} = req
|
||||
let event_id
|
||||
let memberIds
|
||||
|
||||
if (!req.body || ! (req.body.event_id && req.body.memberIds)) {
|
||||
res.status(400).send('Malformed post')
|
||||
} else if (req.params.event_id != req.body.event_id) {
|
||||
// Load actual event. Do I want this to be an error? probably
|
||||
res.status(400).send('Event ID parameter does not match the POST body');
|
||||
return
|
||||
} else {
|
||||
event_id = req.body.event_id
|
||||
memberIds = req.body.memberIds
|
||||
}
|
||||
|
||||
const deletion_promises = []
|
||||
|
||||
event_lineup_entries.filter(entry =>memberIds.includes(entry.memberId.toString())).forEach( entry => {
|
||||
const promise = teamsnap.deleteEventLineupEntry(entry, teamsnapCallback)
|
||||
deletion_promises.push(promise)
|
||||
})
|
||||
|
||||
await Promise.all(deletion_promises)
|
||||
.then(res.status(202).send('OK'))
|
||||
|
||||
}
|
||||
@@ -23,8 +23,14 @@ exports.getEventSheet = async (req,res) =>{
|
||||
)
|
||||
)
|
||||
await Promise.all(req.promises)
|
||||
const {sheet_size, sheet_layout} = req.query
|
||||
|
||||
const {user, team, team_preferences, members, event, event_lineup, event_lineup_entries, availabilities, availabilitySummary, timeline, recent_events, opponent_logo, upcoming_events} = req
|
||||
res.render('eventsheet/sheet', {user, team, team_preferences, members, event, event_lineup, event_lineup_entries, availabilities, availabilitySummary, timeline, recent_events, opponent_logo,upcoming_events})
|
||||
res.render('eventsheet/sheet', {sheet_size, sheet_layout, user, team, team_preferences, members, event, event_lineup, event_lineup_entries, availabilities, availabilitySummary, timeline, recent_events, opponent_logo,upcoming_events})
|
||||
}
|
||||
|
||||
exports.getEventSheetBlank = (req,res) => {
|
||||
res.render('eventsheet/sheet_blank')
|
||||
}
|
||||
|
||||
exports.getLineupCard = (req, res, next) => {
|
||||
@@ -82,7 +88,7 @@ exports.getLineupCard = (req, res, next) => {
|
||||
availabilities: items.filter((i) => i.type == "availability").sort(tsUtils.teamsnapMembersSortLineupAvailabilityLastName),
|
||||
all_lineup_entries: items.filter((i) => i.type == "eventLineupEntry"),
|
||||
event_lineup_entries_offense: items
|
||||
.filter((i) => i.type == "eventLineupEntry" && i.eventId == event_id && !i.label.includes("[PO]"))
|
||||
.filter((i) => i.type == "eventLineupEntry" && i.eventId == event_id && !i.label.has("[PO]"))
|
||||
.sort((a, b) => a.sequence - b.sequence),
|
||||
event_lineup_entries: items
|
||||
.filter((i) => i.type == "eventLineupEntry" && i.eventId == event_id)
|
||||
|
||||
119
src/helpers/eventlineup.js
Normal file
119
src/helpers/eventlineup.js
Normal file
@@ -0,0 +1,119 @@
|
||||
const {embeddedSvgFromPath, parsePositionLabel, compilePositionLabel} = require("../lib/utils")
|
||||
var hb = require('hbs').create();
|
||||
|
||||
const statusCodeIcons = {
|
||||
1: embeddedSvgFromPath("/teamsnap-ui/assets/icons/check.svg"),
|
||||
0: embeddedSvgFromPath("/teamsnap-ui/assets/icons/dismiss.svg"),
|
||||
2: embeddedSvgFromPath("/bootstrap-icons/question-lg.svg"),
|
||||
null: embeddedSvgFromPath("/bootstrap-icons/question.svg"),
|
||||
undefined: embeddedSvgFromPath("/bootstrap-icons/question-lg.svg")
|
||||
}
|
||||
|
||||
const statusCodeClasses = {
|
||||
1: "u-colorPositive",
|
||||
0: "u-colorNegative",
|
||||
2: "u-colorPrimary",
|
||||
null: "u-colorGrey",
|
||||
undefined: "u-colorGrey"
|
||||
}
|
||||
|
||||
const statusCodeButtonClasses = {
|
||||
1: "Button--yes",
|
||||
0: "Button--no",
|
||||
2: "Button--maybe",
|
||||
null: "",
|
||||
undefined: ""
|
||||
}
|
||||
exports.flagsString = (flags) => {
|
||||
return flags != null ? Array.from(flags).join(",") : ''
|
||||
};
|
||||
exports.plus1 = (i) => Number(i)+1;
|
||||
exports.positions = () => ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH", "DR"];
|
||||
exports.defense_positions = () => ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "P"];
|
||||
exports.avail_status_code_class = (status_code) => statusCodeButtonClasses[status_code];
|
||||
exports.avail_status_code_icon = (status_code) => statusCodeIcons[status_code];
|
||||
exports.positionLabelWithoutFlags = (label) => {
|
||||
const {positionLabelWithoutFlags} = parsePositionLabel(label);
|
||||
return positionLabelWithoutFlags
|
||||
};
|
||||
exports.positionLabelWithoutPOFlag = (label) => {
|
||||
const {positionLabelWithoutFlags, positionFlags} = parsePositionLabel(label);
|
||||
positionFlags.delete('PO')
|
||||
return compilePositionLabel(positionLabelWithoutFlags, positionFlags)
|
||||
};
|
||||
exports.positionFlags = (label)=> {
|
||||
const {positionFlags} = parsePositionLabel(label);
|
||||
return `[${Array.from(positionFlags).join(",")}]`
|
||||
};
|
||||
exports.hasPositionFlags = (label) => {
|
||||
const {positionLabelWithoutFlags, positionFlags} = parsePositionLabel(label);
|
||||
return positionFlags.size > 0;
|
||||
};
|
||||
exports.comparePositionWithFlags = (labelWithoutFlags, eventLineupEntry, options) => {
|
||||
labelWithFlags = eventLineupEntry?.label
|
||||
const {positionLabelWithoutFlags} = parsePositionLabel(labelWithFlags);
|
||||
return positionLabelWithoutFlags == labelWithoutFlags;
|
||||
};
|
||||
exports.isStarting = (member) => {
|
||||
return (member.benchcoach?.eventLineupEntry != null);
|
||||
};
|
||||
exports.isInStartingLineup = (member) => {
|
||||
if (member.benchcoach.eventLineupEntry == null || member.benchcoach.eventLineupEntry.label == '') return false;
|
||||
const {positionFlags} = parsePositionLabel(member.benchcoach.eventLineupEntry?.label);
|
||||
return (!positionFlags.has("PO"))
|
||||
};
|
||||
exports.isInPositionOnly = (member) => {
|
||||
if (!member.benchcoach || member.benchcoach.eventLineupEntry == null) return false;
|
||||
const {positionFlags} = parsePositionLabel(member.benchcoach.eventLineupEntry?.label);
|
||||
return (member.benchcoach.eventLineupEntry != null && positionFlags.has("PO"))
|
||||
};
|
||||
exports.isInBench = (member) => {
|
||||
if ((member.benchcoach.eventLineupEntry != null && member.benchcoach.eventLineupEntry.label != '') || member.isNonPlayer) return false;
|
||||
return (member.benchcoach.availability?.statusCode != 0 && member.benchcoach.availability?.statusCode != null)
|
||||
};
|
||||
exports. isInOut = (member) => {
|
||||
if ((member.benchcoach.eventLineupEntry != null && member.benchcoach.eventLineupEntry.label != '') || member.isNonPlayer) return false;
|
||||
return (member.benchcoach.availability?.statusCode == 0 || member.benchcoach.availability?.statusCode == null)
|
||||
};
|
||||
exports.availabilityStatusShort = (availability) => {
|
||||
const {YES, MAYBE, NO, NONE} = teamsnap.AVAILABILITIES
|
||||
const statusShortLookup = {}
|
||||
statusShortLookup[YES] = "YES"
|
||||
statusShortLookup[MAYBE] = "MAY"
|
||||
statusShortLookup[NO] = "NO"
|
||||
statusShortLookup[NONE] = "UNK"
|
||||
statusShortLookup[undefined] = "UNK"
|
||||
return (statusShortLookup[availability?.statusCode])
|
||||
};
|
||||
exports.filterNonPlayers = (members) => {
|
||||
return members.filter(m=>!m.isNonPlayer)
|
||||
};
|
||||
exports.joinMemberEmailAddresses = (members) => {
|
||||
return members.map(m=>m.emailAddresses.join(',')).join(',')
|
||||
}
|
||||
exports.loadSlots = (options) =>{
|
||||
var s = ""
|
||||
const {members, event_lineup, event_lineup_entries, event, availabilities} = options.data.root
|
||||
event_lineup_entries.forEach(eventLineupEntry =>{
|
||||
const availability = availabilities?.find(a=>a.memberId==eventLineupEntry.memberId)
|
||||
const member = members.find(m=>m.id==eventLineupEntry.memberId)
|
||||
const {positionFlags} = parsePositionLabel(eventLineupEntry.label)
|
||||
const initial_lineup_segment = `${positionFlags.has('PO') ? 'position-only' : 'starting'}`
|
||||
|
||||
s+=options.fn({eventLineupEntry, availability, member, event, initial_lineup_segment})
|
||||
})
|
||||
const players_without_lineup_entry = members.filter(
|
||||
member=>!event_lineup_entries.map(lue=>lue.memberId).includes(member.id) && !member.isNonPlayer
|
||||
)
|
||||
players_without_lineup_entry.forEach(member =>{
|
||||
const availability = availabilities?.find(a=>a.memberId==member.id)
|
||||
let initial_lineup_segment
|
||||
if (availability?.statusCode == 0 || availability?.statusCode == null) {
|
||||
initial_lineup_segment =`out`
|
||||
} else {
|
||||
initial_lineup_segment =`bench`
|
||||
}
|
||||
s+=options.fn({availability, member, event, initial_lineup_segment})
|
||||
})
|
||||
return s
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
const { parsePositionLabel, teamsnapMembersSortLineupAvailabilityLastName, teamsnapMembersSortAvailabilityLastName } = require('../lib/utils')
|
||||
const {attachBenchcoachPropertiesToMember} = require('../controllers/eventlineup')
|
||||
const Handlebars = require("handlebars");
|
||||
|
||||
exports.offenseLineup = (number_of_slots, event_lineup_entries, members, options) => {
|
||||
var results = ""
|
||||
@@ -7,7 +8,7 @@ exports.offenseLineup = (number_of_slots, event_lineup_entries, members, options
|
||||
|
||||
for (let i = 0; i < number_of_slots; i++){
|
||||
const event_lineup_entry = event_lineup_entries ? event_lineup_entries[i] : null
|
||||
if (event_lineup_entry && !parsePositionLabel(event_lineup_entry.label).positionFlags.includes('PO')){
|
||||
if (event_lineup_entry && !parsePositionLabel(event_lineup_entry.label).positionFlags.has('PO')){
|
||||
results += options.fn({
|
||||
sequence: event_lineup_entry.sequence,
|
||||
member: members.find(member=> event_lineup_entry.memberId == member.id || event_lineup_entry.memberName == `${member.firstName} ${member.lastName}`),
|
||||
@@ -60,7 +61,7 @@ exports.rosterHistory = (event, event_lineup_entries, members, availabilities, o
|
||||
// const {event, event_lineup_entries, members, availabilities} = options.data.root
|
||||
const players = members.filter(m=>!m.isNonPlayer)
|
||||
attachBenchcoachPropertiesToMember(players, event_lineup_entries ? event_lineup_entries.filter(i=>i.eventId==event.id) : [], availabilities.filter(i=>i.eventId==event.id))
|
||||
players.sort(teamsnapMembersSortAvailabilityLastName)
|
||||
players.sort(teamsnapMembersSortLineupAvailabilityLastName)
|
||||
|
||||
players.forEach(member=>{
|
||||
const {firstName, lastName, jerseyNumber, benchcoach, position, id} = member
|
||||
@@ -87,6 +88,9 @@ const positionGroups = {
|
||||
}
|
||||
|
||||
exports.positionCapabilityFor = (member, position, options) => {
|
||||
if (!member.position) {
|
||||
return ""
|
||||
}
|
||||
const member_positions = member.position.split(",").map(s=>s.trim())
|
||||
const member_position_groups = new Set(member.position.split(",").map(s=>positionGroups[s.trim()]))
|
||||
|
||||
@@ -112,8 +116,15 @@ exports.repeat = (n, options) => {
|
||||
|
||||
exports.loopEvents = (events, options) => {
|
||||
var results = "";
|
||||
events.forEach(event => {
|
||||
results += options.fn(event)
|
||||
if (options.data) {
|
||||
data = Handlebars.createFrame(options.data);
|
||||
}
|
||||
|
||||
events.forEach((event,i) => {
|
||||
if (data) {
|
||||
data.index = i;
|
||||
}
|
||||
results += options.fn(event, {data: data })
|
||||
}
|
||||
)
|
||||
return results;
|
||||
@@ -121,6 +132,9 @@ exports.loopEvents = (events, options) => {
|
||||
|
||||
exports.timepointForMember = (member, timeline, event, options) => {
|
||||
var results = ""
|
||||
if (options.data) {
|
||||
data = Handlebars.createFrame(options.data);
|
||||
}
|
||||
const availability = timeline.availabilities.find(a=>a.memberId==member.id && a.eventId==event.id)
|
||||
const eventLineupEntry = timeline.event_lineup_entries.find(a=>(a.memberId==member.id || a.memberName == `${member.firstName} ${member.lastName}`) && a.eventId==event.id)
|
||||
var value = ""
|
||||
@@ -128,7 +142,15 @@ exports.timepointForMember = (member, timeline, event, options) => {
|
||||
value = parsePositionLabel(eventLineupEntry.label).positionLabelWithoutFlags
|
||||
}
|
||||
else {
|
||||
value = availability.status[0]
|
||||
value = availability?.status[0]
|
||||
}
|
||||
return options.fn({availability: availability, eventLineupEntry: eventLineupEntry, value}, {data: data })
|
||||
}
|
||||
|
||||
exports.ifEquals = (testValue, targetValue, options) => {
|
||||
if (testValue === targetValue) {
|
||||
return options.fn();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
return options.fn({availability: availability, eventLineupEntry: eventLineupEntry, value})
|
||||
}
|
||||
@@ -50,17 +50,6 @@ teamsnapMembersSortAvailabilityLastName = (a, b) => {
|
||||
}
|
||||
exports.teamsnapMembersSortAvailabilityLastName = teamsnapMembersSortAvailabilityLastName
|
||||
|
||||
exports.initTeamsnap = (req, res, next) => {
|
||||
if (!teamsnap.isAuthed()) {
|
||||
teamsnap.init(process.env["TEAMSNAP_CLIENT_ID"]);
|
||||
teamsnap.auth(req.user.accessToken);
|
||||
}
|
||||
teamsnap.loadCollections((err) => {
|
||||
teamsnap.enablePersistence();
|
||||
next(req, res, next);
|
||||
});
|
||||
};
|
||||
|
||||
exports.teamsnapCallback = (err,result, d) => {
|
||||
if (Array.isArray(result)){
|
||||
types = new Set(result.map(i=>i.type))
|
||||
@@ -95,6 +84,10 @@ const getPluralType = (type) =>{
|
||||
// is not generated in the lookup. this is a
|
||||
// kludge around that. (specifically availabilitySummary)
|
||||
plural = teamsnap.getPluralType(type) || (function() {
|
||||
if (type === undefined){
|
||||
return type
|
||||
}
|
||||
|
||||
switch (type.slice(-1)) {
|
||||
case 'y':
|
||||
return type.slice(0, -1) + 'ies';
|
||||
@@ -120,7 +113,7 @@ exports.groupTeamsnapItems = (items, types = [], params = {}) => {
|
||||
return result;
|
||||
}
|
||||
|
||||
exports.embeddedSvgFromPath = (svg_path, additional_classes = "") => {
|
||||
exports.embeddedSvgFromPath = (svg_path, additional_classes, options) => {
|
||||
const iconStaticPaths = {
|
||||
"/teamsnap-ui/assets":path.join(__dirname, "/../../node_modules/@teamsnap/teamsnap-ui/src/assets"),
|
||||
"/bootstrap-icons":path.join(__dirname, "/../../node_modules/bootstrap-icons/icons"),
|
||||
@@ -133,6 +126,8 @@ exports.embeddedSvgFromPath = (svg_path, additional_classes = "") => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!options) {options=additional_classes; additional_classes=''}
|
||||
|
||||
const svg = fs.readFileSync(`${svg_path}`, 'utf8');
|
||||
|
||||
svgRegExWithClass = new RegExp(/<svg(.*)class="(.*?)"(.*)>/)
|
||||
@@ -167,7 +162,19 @@ exports.compilePositionLabel = (label, flags) => {
|
||||
return label
|
||||
}
|
||||
else {
|
||||
const flags_set = new Set(flags.split(',').map(s=>s.trim()))
|
||||
const flags_set = toFlagsSet(flags)
|
||||
return `${label} [${Array.from(flags_set).sort().join(',')}]`
|
||||
}
|
||||
}
|
||||
|
||||
function toFlagsSet(flags) {
|
||||
let flags_set
|
||||
if (typeof(flags) == 'string'){
|
||||
flags_set = new Set(flags.split(',').map(s=>s.trim()))
|
||||
} else if (flags.constructor === Array){
|
||||
flags_set = new Set(flags)
|
||||
} else if (flags.constructor === Set){
|
||||
flags_set = flags
|
||||
}
|
||||
return flags_set
|
||||
}
|
||||
@@ -1,25 +1,26 @@
|
||||
exports.loadRecentAndUpcomingEvents = async (req, res, next) => {
|
||||
const {team_id, event_id} = req.params
|
||||
const page_size = req.query.page_size ? Number(req.query.page_size) : 4
|
||||
var subject_date = ""
|
||||
var subject_date
|
||||
if (event_id) {
|
||||
const event = await teamsnap.loadEvents({id: event_id}).pop()
|
||||
subject_date = event.startDate.toISOString().slice(0,10)
|
||||
const new_date = new Date(event.startDate.getTime()+10000);
|
||||
subject_date = event.startDate
|
||||
}
|
||||
else {
|
||||
subject_date = new Date().toISOString().slice(0,10)
|
||||
subject_date = new Date()
|
||||
}
|
||||
req.promises.push(
|
||||
teamsnap.bulkLoad({
|
||||
teamId: team_id,
|
||||
types: ["event", "availabilitySummary"],
|
||||
scopeTo: "event",
|
||||
event__startedAfter: subject_date,
|
||||
event__pageSize: page_size + 1
|
||||
event__startedAfter: new Date(subject_date.getTime()+10000),
|
||||
event__pageSize: page_size
|
||||
})
|
||||
.then(items => tsUtils.groupTeamsnapItems(items))
|
||||
.then((items)=>{
|
||||
req.upcoming_events=items.events ? items.events.slice(1) : [];
|
||||
req.upcoming_events=items.events ? items.events : [];
|
||||
const availabilitySummaries=items.availabilitySummaries;
|
||||
req.upcoming_events.forEach((event) => {
|
||||
event.link('availabilitySummary', availabilitySummaries.find(a=>a.eventId==event.id))
|
||||
@@ -32,7 +33,7 @@ exports.loadRecentAndUpcomingEvents = async (req, res, next) => {
|
||||
teamId: team_id,
|
||||
types: ["event", "availabilitySummary"],
|
||||
scopeTo: "event",
|
||||
event__startedBefore: subject_date,
|
||||
event__startedBefore: new Date(subject_date.getTime()-10000),
|
||||
event__pageSize: page_size,
|
||||
event__sortStartDate: "desc"
|
||||
})
|
||||
|
||||
@@ -1004,21 +1004,21 @@ h6 {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.Button--blue {
|
||||
.Button--blue, button:has(+ .position-label-flags :checked) {
|
||||
background-color: #1A6BAF;
|
||||
border-color: #15568c;
|
||||
color: #ffffff;
|
||||
}
|
||||
.Button--blue:hover, .Button--blue:active, .Button--blue:focus {
|
||||
.Button--blue:hover, button:hover:has(+ .position-label-flags :checked), .Button--blue:active, button:active:has(+ .position-label-flags :checked), .Button--blue:focus, button:focus:has(+ .position-label-flags :checked) {
|
||||
background-color: #17609e;
|
||||
border-color: #134d7e;
|
||||
color: #ffffff;
|
||||
}
|
||||
.Button--blue.is-active {
|
||||
.Button--blue.is-active, button.is-active:has(+ .position-label-flags :checked) {
|
||||
background-color: #17609e;
|
||||
color: #ffffff;
|
||||
}
|
||||
.Button--blue.is-disabled, .Button--blue.is-disabled:hover, .Button--blue.is-disabled:active, .Button--blue:disabled, .Button--blue:disabled:hover, .Button--blue:disabled:active {
|
||||
.Button--blue.is-disabled, button.is-disabled:has(+ .position-label-flags :checked), .Button--blue.is-disabled:hover, button.is-disabled:hover:has(+ .position-label-flags :checked), .Button--blue.is-disabled:active, button.is-disabled:active:has(+ .position-label-flags :checked), .Button--blue:disabled, button:disabled:has(+ .position-label-flags :checked), .Button--blue:disabled:hover, button:disabled:hover:has(+ .position-label-flags :checked), .Button--blue:disabled:active, button:disabled:active:has(+ .position-label-flags :checked) {
|
||||
background-color: #1A6BAF;
|
||||
border-color: #15568c;
|
||||
color: #ffffff;
|
||||
@@ -2312,13 +2312,13 @@ input:-webkit-autofill:focus {
|
||||
}
|
||||
|
||||
.Popup-container::before {
|
||||
top: calc( 100% - 6px );
|
||||
top: calc(100% - 6px);
|
||||
border: solid #d6d6d6 1px;
|
||||
box-shadow: 0 0 2px rgba(56, 56, 56, 0.15);
|
||||
}
|
||||
|
||||
.Popup-container::after {
|
||||
top: calc( 100% - 7px );
|
||||
top: calc(100% - 7px);
|
||||
}
|
||||
|
||||
.Popup-content {
|
||||
@@ -2757,7 +2757,7 @@ input:-webkit-autofill:focus {
|
||||
|
||||
.StepNav-stepTitle {
|
||||
width: 150px;
|
||||
left: calc( 0px - 43px);
|
||||
left: calc(0px - 43px);
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
@@ -2875,7 +2875,7 @@ input:-webkit-autofill:focus {
|
||||
}
|
||||
.StepNav--small .StepNav-stepTitle {
|
||||
width: 150px;
|
||||
left: calc( 0px - 53px);
|
||||
left: calc(0px - 53px);
|
||||
padding-top: 2px;
|
||||
}
|
||||
.StepNav--small .StepNav-stepIcon {
|
||||
@@ -2910,7 +2910,7 @@ input:-webkit-autofill:focus {
|
||||
}
|
||||
.StepNav--xsmall .StepNav-stepTitle {
|
||||
width: 150px;
|
||||
left: calc( 0px - 62px);
|
||||
left: calc(0px - 62px);
|
||||
padding-top: 1px;
|
||||
}
|
||||
.StepNav--xsmall .StepNav-stepIcon {
|
||||
@@ -7014,7 +7014,7 @@ a.Panel-row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div[id^=event-lineup] {
|
||||
div.event-lineup {
|
||||
max-width: 576px;
|
||||
counter-reset: lineup-sequence-counter 0;
|
||||
margin-left: 8px;
|
||||
@@ -7093,9 +7093,23 @@ li .availability-status-code- {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div[id^=event-lineup] .Panel.position-only .Panel-cell:has(.sequence), div[id^=event-lineup] .Panel.bench .Panel-cell:has(.sequence), div[id^=event-lineup] .Panel.out .Panel-cell:has(.sequence) {
|
||||
div.event-lineup .lineup-segment:has(input.Toggle-input:not(:checked)).out .Panel-cell:has(.SelectBox),
|
||||
div.event-lineup .lineup-segment:has(input.Toggle-input:not(:checked)).out .Panel-cell:has(.drag-handle),
|
||||
div.event-lineup .lineup-segment:has(input.Toggle-input:not(:checked)).out button:has(+ .position-label-flags),
|
||||
div.event-lineup .lineup-segment:has(input.Toggle-input:not(:checked)).out button.addToStarting,
|
||||
div.event-lineup .lineup-segment:has(input.Toggle-input:not(:checked)).out button.addToBench {
|
||||
display: none;
|
||||
}
|
||||
div.event-lineup .lineup-segment.bench .Panel-cell:has(.sequence), div.event-lineup .lineup-segment.position-only .Panel-cell:has(.sequence), div.event-lineup .lineup-segment.out .Panel-cell:has(.sequence) {
|
||||
display: none;
|
||||
}
|
||||
div.event-lineup .lineup-segment.bench.bench button.addToBench, div.event-lineup .lineup-segment.position-only.bench button.addToBench, div.event-lineup .lineup-segment.out.bench button.addToBench {
|
||||
display: none;
|
||||
}
|
||||
div.event-lineup .lineup-segment.starting button.addToStarting, div.event-lineup .lineup-segment.position-only button.addToStarting {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.Tooltip:after {
|
||||
padding: 2px !important;
|
||||
font-size: inherit !important;
|
||||
@@ -7104,8 +7118,6 @@ div[id^=event-lineup] .Panel.position-only .Panel-cell:has(.sequence), div[id^=e
|
||||
@media (max-width: 480px) {
|
||||
.Panel--full {
|
||||
border-radius: 0;
|
||||
margin-right: -16px;
|
||||
margin-left: -16px;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
}
|
||||
@@ -7139,8 +7151,11 @@ div[id^=event-lineup] .Panel.position-only .Panel-cell:has(.sequence), div[id^=e
|
||||
}
|
||||
|
||||
.Panel .Panel {
|
||||
border: none;
|
||||
margin: 0;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.scroll-horizontal {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=application.css.map */
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../../scss/eventsheet.scss"],"names":[],"mappings":";AAAQ;AACA;AACA;AACA;AACA;AAER;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;AACA;EACE;IACE;IACA;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;EAEF;IACE;IACA;;;AAIJ;EACE;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAKA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAQR;EACE;;;AAGF;EACE;;;AAOF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;AACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;AAAA;EAEE;EACA;EACA;EACA,qBACE;;;AAIJ;AAAA;EAEE;EACA;EACA;EACA,qBACE;;;AAIJ;AAAA;AAAA;AAAA;EAIE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAKF;EACE;;AAEA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAQN;EACE;;;AAGF;AACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;AAAA;AAAA;AAAA;EAKE;;;AAGF;AAAA;AAAA;AAAA;AAAA;EAKE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAKA;EACE;EACA;EACA;EACA,qBACE;;AAIJ;EACE;;AAGF;EACE;;AAGF;AACE;EACA;EACA;EACA;AACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;;AACA;EACE;;AAQJ;EACE;EACA;EACA;;AAEF;EACE;;;AAON;EACE;;AAGF;EACE;;AAIA;EACE;EACA;;AAMF;EACE;;AAGF;EAOE;EACA;EACA;EACA;;AATA;EACE;;AACA;EACE;;;AAeV;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAMI;EACE,SALM;;AAIR;EACE,SALM;;AAIR;EACE,SALM;;AAIR;EACE,SALM;;AAIR;EACE,SALM;;AAIR;EACE,SALM;;AAIR;EACE,SALM;;AAIR;EACE,SALM;;AAIR;EACE,SALM;;AAUZ;EACE;EAEA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EAIE;EACA;EACA;;AALA;EACE;;AAKF;EACI;;;AASV;EACE;AACA;EACA;EAEA;EACA;AACA;;AAEA;EACE;EACA;;AAGF;EACE;;AACA;EACE;;;AAON;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;AACA;AACA;AACA;EACA;EAEA;EACA;EACA;EACA;EACA;;;AAKF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AASA;AACE;EACA;EACA;AACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACI;EACA;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;EAEE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAMN;EACE;EACA;EACA;;AAEF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;AAQJ;EACE;;AAGF;EACE;;AAGF;EACE;;;AAOJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;AACA;AAAA;;;AAIF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACI;EACA;EACA;EACA;;AAGJ;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE","file":"eventsheet.css"}
|
||||
{"version":3,"sourceRoot":"","sources":["../../scss/eventsheet.scss"],"names":[],"mappings":";AAAQ;AACA;AACA;AACA;AACA;AACA;AAER;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIF;AACA;EACE;IACE;;EAEF;IACE;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;EAEF;IACE;IACA;IACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;EAA+B;EAAc;;;AAC7C;EAA+B;EAAc;;;AAC7C;EAA+B;EAAc;;;AAE7C;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EACE;EACA;;AAEA;EACE;;AAMkB;EAChB;;AAGiB;EACjB;;;AAOR;EACE;;;AAGF;EACE;;;AAGF;EACE;;AACA;EACE;;AACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAMN;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;;AAIN;EACE;;;AAGF;EACE;EACE;EACA;EACA;EACA;;AACA;EACE;;;AAIN;EACE;EACE;EACA;EACA;EACA;;AACA;EACE;;;AAIN;EACE;EAEA;EAEA;;AAEA;EACE;EACA;EACA;EACA;;AACA;EACE;;AAIJ;EACE;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAMN;EACE;EACA;EACA;;AAGF;EAEE;;AAGF;AACE;;AACA;EACE;;AACA;EACE;;AAGJ;EACE;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAKF;EAEA;;AAIA;EACE;EACA;;AAEF;EACE;;AAKF;EACE;;AACA;EACE;;AAGJ;EACE;;AAGF;EACE;;;AAMJ;EACE;;AAEF;EACE;;AACA;EACE;;;AAOJ;EACE;EACA;EACA;EACA,qBACE;;AAIJ;EACE;;AAGF;EACE;;AAGF;AACE;EACA;EACA;EACA;AACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGE;EACA;EACA;EACA;;AAGA;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;;AACA;EACE;;AAQJ;EACE;EACA;EACA;;AAEF;EACE;;;AAON;EACE;;AAGF;EACE;;AAIA;EACE;EACA;;AAMF;EACE;;AAGF;EAOE;EACA;EACA;EACA;;AATA;EACE;;AACA;EACE;;;AAcV;EACE;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;;AAIA;EACE;;AAGF;EACE;EACA;EACA;;AAEE;EACE;;AAEM;EACN;;AAEF;EACA;EACA;EACA;;AAQA;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AA4BR;EACE;;AACA;EACA,SA/BM;;AAoCZ;EACE;EAEA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EAIE;EACA;EACA;;AALA;EACE;;AAKF;EACE;;AACA;EACE;;;AASV;EACE;AACA;EACA;EAEA;EACA;AACA;;AAEA;EACE;EACA;;AAGF;EACE;;AACA;EACE;;;AAMN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;;;AAGF;EACE;;;AAKF;EACE;;AAME;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EAEE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;AAEF;AACE;EACA;EACA;AACA;;AAIA;EACA;;AAKF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAOF;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACI;EACA;EACA;EACA;EACA;;AAEJ;EACE;;AACA;EACE;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;EAEE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAMN;EACE;EACA;EACA;;AAEF;EACE;;AAIJ;EACE;EACA;EACA;;AAEF;EACE;EACA;EACA;;AAGF;EAEE;;AAGF;EACE;EACA;;AAEA;EACE;AACA;EACA;EACA;EACA;;AAQJ;EACE;;AAGF;EACE;;AAGa;EACb;;;AAMF;EACE;;AAEF;EACE;;AAEF;EACE;;AAIA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAQE;EACA;;AARA;EACE;;AACA;EACE;EACA;;AAWJ;EACE;EACA;;AAQJ;EACE;;;AAKN;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;AACA;AAAA;;;AAIF;EACE;;;AAGF;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE","file":"eventsheet.css"}
|
||||
@@ -1,4 +1,42 @@
|
||||
/* Project specific Javascript goes here. */
|
||||
const lineupChangedEvent = new Event('bc:lineupChanged')
|
||||
|
||||
document.querySelectorAll('.event-lineup').forEach(lineup=>{
|
||||
lineup.addEventListener('bc:lineupChanged', (evt)=>{
|
||||
console.log(`lineup changed`, evt.target)
|
||||
const lineup = evt.target
|
||||
colorPositions(lineup)
|
||||
lineup.querySelectorAll(".lineup-slot").forEach((slot, i) => {
|
||||
const lineup_segment = determineLineupSegment(slot)
|
||||
if (lineup_segment != 'bench' && lineup_segment != 'out'){
|
||||
slot.querySelector("input[name=sequence]").value = i;
|
||||
} else {
|
||||
slot.querySelector("input[name=sequence]").value = null;
|
||||
}
|
||||
updateFlagInput(slot)
|
||||
updatePositionInput(slot)
|
||||
});
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
document.querySelectorAll('[data-control=popup]').forEach(popup_control=>{
|
||||
console.log(popup_control)
|
||||
popup_control.addEventListener('click', (evt)=>{
|
||||
const popup = evt.target.closest(".Popup")
|
||||
const to_open = popup.querySelector(".Popup-toggle").dataset.open
|
||||
popup.querySelectorAll(`[data-popup=${to_open}]`).forEach(popup_container => {
|
||||
console.log(evt, evt.target, popup, popup_container)
|
||||
popup_container.classList.toggle('is-open')
|
||||
evt.stopPropagation()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
document.querySelectorAll('.position-label-flags input[type="checkbox"]').forEach(flagCheckbox => {
|
||||
const lineup = flagCheckbox.closest('.event-lineup')
|
||||
flagCheckbox.addEventListener('click', ()=>{lineup.dispatchEvent(lineupChangedEvent)})
|
||||
})
|
||||
|
||||
function onPositionSelectChange(elem) {
|
||||
elem.querySelectorAll("option").forEach((option) => {
|
||||
if (option.innerText.trim() == elem.value) {
|
||||
@@ -7,251 +45,247 @@ function onPositionSelectChange(elem) {
|
||||
option.removeAttribute("selected");
|
||||
}
|
||||
});
|
||||
colorPositions();
|
||||
refreshLineup();
|
||||
const lineup = elem.closest('.event-lineup')
|
||||
lineup.dispatchEvent(lineupChangedEvent)
|
||||
elem
|
||||
}
|
||||
|
||||
function togglePopup(el) {
|
||||
el.querySelector(".Popup-container").classList.toggle("is-open");
|
||||
}
|
||||
function colorPositions(lineup) {
|
||||
const class_none = "u-colorNegative"
|
||||
const class_good = "u-colorPositive"
|
||||
const class_over = "u-colorHighlight"
|
||||
|
||||
function colorPositions() {
|
||||
for (bcLineup of document.querySelectorAll("[id^=event-lineup]")) {
|
||||
selected_lineup_positions = Array.from(
|
||||
bcLineup.querySelectorAll(".position-select-box option:checked")
|
||||
).map((el) => el.value);
|
||||
|
||||
for (position_status of bcLineup.querySelectorAll(".position-status")) {
|
||||
for (class_name of ["u-colorNegative", "u-colorHighlight", "u-colorPositive"]) {
|
||||
position_status.classList.remove(class_name);
|
||||
lineup.querySelectorAll('.position-status').forEach(
|
||||
position_status=>{
|
||||
position_status.classList.remove(class_over, class_good, class_none);
|
||||
const occurences = lineup.querySelectorAll(`.position-select-box option:checked[value="${position_status.dataset.value}"]`)
|
||||
switch (occurences.length){
|
||||
case 0:
|
||||
position_status.classList.add(class_none)
|
||||
break;
|
||||
case 1:
|
||||
position_status.classList.add(class_good)
|
||||
break;
|
||||
default:
|
||||
position_status.classList.add(class_over)
|
||||
break;
|
||||
}
|
||||
|
||||
occurrences = selected_lineup_positions.filter((s) => s == position_status.innerText).length;
|
||||
|
||||
if (occurrences == 1) {
|
||||
position_status.classList.add("u-colorPositive");
|
||||
} else if (occurrences > 1) {
|
||||
position_status.classList.add("u-colorHighlight");
|
||||
} else {
|
||||
position_status.classList.add("u-colorNegative");
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function initFlagsCheckboxes(){
|
||||
Array.from(document.querySelectorAll("[id^=event-lineup]")).forEach((bcLineup) => {
|
||||
Array.from(
|
||||
bcLineup.querySelectorAll(
|
||||
".starting .lineup-slot, \
|
||||
.position-only .lineup-slot, \
|
||||
.bench .lineup-slot"
|
||||
document.querySelectorAll(".lineup-slot").forEach(lineup_slot=>{
|
||||
const possible_flags = ['DHd', 'DRd']
|
||||
const flags_string = lineup_slot.querySelector("input[name=flags]")?.value
|
||||
const flags = flagSetFromString(flags_string)
|
||||
|
||||
possible_flags.forEach(flag=>{
|
||||
if (flags.has(flag)){
|
||||
lineup_slot.querySelector(`input[type=checkbox][name=${flag}]`).checked = true
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
const flagSetFromString = (s) => {
|
||||
if (!s) {return new Set()}
|
||||
const array = s.split(',').map(item=>item.trim())
|
||||
return new Set(array)
|
||||
}
|
||||
|
||||
const flagSetFromSlot = (slot) => {
|
||||
const inputs = slot.querySelectorAll('.position-label-flags input[type=checkbox]:checked')
|
||||
const set = new Set()
|
||||
inputs.forEach(i=>set.add(i.name))
|
||||
return set
|
||||
}
|
||||
|
||||
const flagSetToString = (set) => {
|
||||
return Array.from(set).join(",");
|
||||
}
|
||||
|
||||
const updateFlagInput = (slot) => {
|
||||
const flags = flagSetFromSlot(slot)
|
||||
const lineup_segment = slot.closest('.lineup-segment')
|
||||
lineup_segment.classList.contains('position-only') ? flags.add('PO') : flags.delete('PO')
|
||||
slot.querySelector('input[name="flags"]').value = flagSetToString(flags);
|
||||
}
|
||||
|
||||
const updatePositionInput = (slot) => {
|
||||
const selected_position = slot.querySelector(".position-select-box option:checked");
|
||||
const lineup_segment = slot.closest('.lineup-segment')
|
||||
|
||||
if (selected_position && selected_position.text != "--" && !lineup_segment.classList.contains('bench')) {
|
||||
slot.querySelector("input[name=label]").value = selected_position.text;
|
||||
} else {
|
||||
slot.querySelector("input[name=label]").value = null;
|
||||
}
|
||||
}
|
||||
|
||||
const determineLineupSegment = (slot) => {
|
||||
const lineup_segments = ['starting', 'position-only', 'bench', 'out']
|
||||
const lineup_segment = slot.closest('.lineup-segment')
|
||||
const classList = Array.from(lineup_segment.classList)
|
||||
const segments = classList.filter(c=>lineup_segments.includes(c))
|
||||
|
||||
if (segments.length == 1) {
|
||||
return segments[0]
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function openAvailabilityReminderModal (el, team_id, event_id) {
|
||||
const url = `/${team_id}/event/${event_id}/modal-confirm-availability-reminders/`
|
||||
const form = el.closest('form')
|
||||
const form_data = new FormData (form)
|
||||
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.text();
|
||||
} else {
|
||||
return Promise.reject(response.text());
|
||||
}
|
||||
})
|
||||
.then((html) => {
|
||||
const parser = new DOMParser()
|
||||
const modal = parser.parseFromString(html, 'text/html')
|
||||
const modal_node = modal.firstElementChild.querySelector('#modal')
|
||||
modal_node.classList.add('is-open')
|
||||
const modal_node_accept = modal.querySelector('Button[data-confirm=yes]')
|
||||
const checked = Array.from(el.querySelectorAll('input:checked')).map
|
||||
const body = document.querySelector('body')
|
||||
body.appendChild(modal_node)
|
||||
modal_node_accept.addEventListener(
|
||||
"click", ()=>{
|
||||
// const memberIds = form_data.getAll('memberId')
|
||||
const csrf_token = form_data.get('csrfToken')
|
||||
const selected_status_codes = Array.from(document.querySelectorAll('input:checked')).map(e=>e.value)
|
||||
const slots = Array.from(document.querySelectorAll('.lineup-slot')).filter(
|
||||
slot =>{
|
||||
const slot_status_code = slot.querySelector('input[name=availabilityStatusCode]').value
|
||||
return selected_status_codes.includes(slot_status_code)
|
||||
}
|
||||
)
|
||||
const memberIds = slots.map(
|
||||
slot => slot.querySelector('input[name=memberId]').value
|
||||
)
|
||||
|
||||
console.log("sending reminders", el, event_id, memberIds, csrf_token)
|
||||
sendAvailabilityReminder(el, event_id, memberIds, csrf_token)
|
||||
body.removeChild(modal_node)
|
||||
}
|
||||
)
|
||||
).forEach((slot, i) => {
|
||||
const flags = new Set(slot.querySelector("input[name*=flags]")?.value?.split(',')?.map(s=>s.trim())) || new Set()
|
||||
if (flags.has('DHd')) {
|
||||
slot.querySelector('[name=flag-dhd]').checked = true;
|
||||
}
|
||||
|
||||
if (flags.has('DRd') ) {
|
||||
slot.querySelector('[name=flag-drd]').checked = true;
|
||||
}
|
||||
})}
|
||||
)
|
||||
}
|
||||
|
||||
function refreshLineup() {
|
||||
Array.from(document.querySelectorAll("[id^=event-lineup]")).forEach((bcLineup) => {
|
||||
Array.from(
|
||||
bcLineup.querySelectorAll(
|
||||
".starting .lineup-slot, \
|
||||
.position-only .lineup-slot, \
|
||||
.bench .lineup-slot"
|
||||
)
|
||||
).forEach((slot, i) => {
|
||||
slot.querySelector("input[name*=sequence]").value = i;
|
||||
selected_position = slot.querySelector(".position-select-box option:checked");
|
||||
const flags = new Set(slot.querySelector("input[name*=flags]")?.value?.split(',')?.map(s=>s.trim())) || new Set()
|
||||
|
||||
if (slot.querySelector('[name=flag-dhd]').checked) {
|
||||
flags.add('DHd')
|
||||
} else {
|
||||
flags.delete('DHd')
|
||||
}
|
||||
|
||||
if (slot.querySelector('[name=flag-drd]').checked) {
|
||||
flags.add('DRd')
|
||||
} else {
|
||||
flags.delete('DRd')
|
||||
}
|
||||
|
||||
if (selected_position && selected_position.text != "--") {
|
||||
slot.querySelector("input[name*=label]").value = selected_position.text;
|
||||
} else {
|
||||
slot.querySelector("input[name*=label]").value = null;
|
||||
}
|
||||
if (slot.closest('.position-only')){
|
||||
flags.add('PO');flags.delete('')
|
||||
}
|
||||
else {
|
||||
flags.delete('PO');flags.delete('')
|
||||
}
|
||||
if (slot.closest('.bench')){
|
||||
slot.querySelector("input[name*=sequence]").value = '';
|
||||
slot.querySelector("input[name*=label]").value = '';
|
||||
}
|
||||
slot.querySelector("input[name*=flags]").value = Array.from(flags).join(",");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function refreshFlags(){
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function copyEmailTable(itemEl, subject, recipients) {
|
||||
// Create container for the HTML
|
||||
// [1]
|
||||
let bcLineup = itemEl.closest(".benchcoach-lineup");
|
||||
var container = document.createElement("div");
|
||||
var tbl = document.createElement("table");
|
||||
|
||||
let thead = tbl.createTHead();
|
||||
let thead_row = thead.insertRow();
|
||||
let thead_row_cell = thead_row.insertCell();
|
||||
thead_row_cell.appendChild(document.createElement("h3").appendChild(document.createTextNode("STARTING LINEUP")));
|
||||
thead_row_cell.colSpan = 3;
|
||||
thead_row_cell.classList.add("title-cell");
|
||||
var tbody = tbl.createTBody();
|
||||
for (row of bcLineup.querySelector(".table-benchcoach-startinglineup").rows) {
|
||||
let tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
cell.appendChild(document.createTextNode(parseInt(row.dataset.order) + 1));
|
||||
cell = tr.insertCell();
|
||||
cell.appendChild(document.createTextNode(row.dataset.playerName));
|
||||
cell.classList.add("name-cell");
|
||||
tr.insertCell().appendChild(document.createTextNode(row.dataset.position));
|
||||
}
|
||||
|
||||
if (bcLineup.querySelector(".table-benchcoach-startingpositionalonly").rows.length > 0) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.colSpan = 3;
|
||||
cell.appendChild(document.createTextNode("STARTING (POS. ONLY)"));
|
||||
cell.classList.add("title-cell");
|
||||
|
||||
for (row of bcLineup.querySelector(".table-benchcoach-startingpositionalonly").rows) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
cell.appendChild(document.createTextNode(""));
|
||||
cell = tr.insertCell();
|
||||
cell.appendChild(document.createTextNode(row.dataset.playerName));
|
||||
cell.classList.add("name-cell");
|
||||
tr.insertCell().appendChild(document.createTextNode(row.dataset.position));
|
||||
function confirmModal(el, prompt, fn, options) {
|
||||
const url = "/modal-confirm"
|
||||
const params = new URLSearchParams(prompt)
|
||||
url.search = params.toString()
|
||||
fetch(url+"?"+params.toString(), {method:"GET"})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.text();
|
||||
} else {
|
||||
return Promise.reject(response.text());
|
||||
}
|
||||
})
|
||||
.then((html) => {
|
||||
const parser = new DOMParser()
|
||||
const modal = parser.parseFromString(html, 'text/html')
|
||||
const modal_node = modal.firstElementChild.querySelector('#modal')
|
||||
modal_node.classList.add('is-open')
|
||||
const modal_node_accept = modal.querySelector('Button[data-confirm=yes]')
|
||||
const body = document.querySelector('body')
|
||||
body.appendChild(modal_node)
|
||||
modal_node_accept.addEventListener("click", ()=>{fn(modal_node, options)})
|
||||
})
|
||||
}
|
||||
|
||||
const toggleShowAndHideLoading = (el) => {
|
||||
console.log(el)
|
||||
el.querySelectorAll('.hideOnLoading').forEach((element)=>{
|
||||
element.classList.add('u-hidden')
|
||||
})
|
||||
el.querySelectorAll('.showOnLoading').forEach((element)=>{
|
||||
element.classList.remove('u-hidden')
|
||||
})
|
||||
}
|
||||
|
||||
const completeLoad = (el, success) => {
|
||||
el.querySelectorAll('.hideOnLoading, .showOnLoading, .showOnFailure, .showOnSuccess').forEach((element)=>{
|
||||
element.classList.add('u-hidden')
|
||||
})
|
||||
if (success) {
|
||||
el.querySelectorAll('.showOnSuccess').forEach((element) => {
|
||||
element.classList.remove('u-hidden')
|
||||
})
|
||||
} else {
|
||||
el.querySelectorAll('.showOnFailure').forEach((element) => {
|
||||
element.classList.remove('u-hidden')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (bcLineup.querySelector(".table-benchcoach-bench").rows.length > 0) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.colSpan = 3;
|
||||
cell.appendChild(document.createTextNode("SUBS"));
|
||||
cell.classList.add("title-cell");
|
||||
|
||||
for (row of bcLineup.querySelector(".table-benchcoach-bench").rows) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
availability_status = {
|
||||
None: "UNK",
|
||||
0: "NO",
|
||||
2: "MAY",
|
||||
1: "YES",
|
||||
}[row.dataset.availabilityStatuscode];
|
||||
cell.appendChild(document.createTextNode(availability_status));
|
||||
cell = tr.insertCell();
|
||||
cell.appendChild(document.createTextNode(row.dataset.playerName));
|
||||
cell.classList.add("name-cell");
|
||||
tr.insertCell().appendChild(document.createTextNode(""));
|
||||
function submitClearLineup(modal, options){
|
||||
console.log('clearing lineup...')
|
||||
toggleShowAndHideLoading(modal)
|
||||
const {team_id, event_id, event_lineup_id} = options
|
||||
const url = `/${team_id}/event/${event_id}/lineup/${event_lineup_id}/delete`
|
||||
const form = document.querySelector(`#event-lineup-${event_id} form`);
|
||||
const data = new FormData(form);
|
||||
const memberIds = data.getAll('memberId')
|
||||
console.log(url)
|
||||
fetch(url, {method:"POST", body: JSON.stringify({memberIds, event_id}), headers: {"Content-Type": "application/json"}})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
completeLoad(modal, true);
|
||||
return response.text();
|
||||
} else {
|
||||
completeLoad(modal, false);
|
||||
return Promise.reject(response.text());
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(()=>{
|
||||
setTimeout(function (){
|
||||
location.reload()
|
||||
}, 500)
|
||||
});//refresh page
|
||||
}
|
||||
|
||||
if (bcLineup.querySelector(".table-benchcoach-out").rows.length > 0) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.colSpan = 3;
|
||||
cell.appendChild(document.createTextNode("OUT"));
|
||||
cell.classList.add("title-cell");
|
||||
function submitResetAvailabilities(modal, options){
|
||||
const {team_id, event_id} = options
|
||||
toggleShowAndHideLoading(modal)
|
||||
const url = `/${team_id}/event/${event_id}/reset_availabilities`
|
||||
const form = document.querySelector(`#event-lineup-${event_id} form`);
|
||||
const data = new FormData(form);
|
||||
const memberIds = data.getAll('memberId')
|
||||
|
||||
for (row of bcLineup.querySelector(".table-benchcoach-out").rows) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
availability_status = {
|
||||
None: "UNK",
|
||||
0: "NO",
|
||||
1: "MAY",
|
||||
2: "YES",
|
||||
}[row.dataset.availabilityStatuscode];
|
||||
cell.appendChild(document.createTextNode(availability_status));
|
||||
tr.insertCell().appendChild(document.createTextNode(row.dataset.playerName));
|
||||
tr.insertCell().appendChild(document.createTextNode(""));
|
||||
console.log('submitting...', url)
|
||||
fetch(url, {method:"POST", body: JSON.stringify({memberIds, event_id}), headers: {"Content-Type": "application/json"}})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
completeLoad(modal, true);
|
||||
return response.text();
|
||||
} else {
|
||||
completeLoad(modal, false);
|
||||
return Promise.reject(response.text());
|
||||
}
|
||||
}
|
||||
|
||||
container.appendChild(tbl);
|
||||
for (cell of container.getElementsByClassName("title-cell")) {
|
||||
cell.setAttribute("style", "font-weight:bold;background-color:#323669;color:#fff;padding:2px 5px;");
|
||||
}
|
||||
|
||||
for (cell of container.getElementsByClassName("sequence-cell")) {
|
||||
cell.setAttribute("style", "font-weight:bold;padding:2px 5px;");
|
||||
}
|
||||
|
||||
for (cell of container.getElementsByClassName("name-cell")) {
|
||||
cell.setAttribute("style", "width:200px;");
|
||||
}
|
||||
|
||||
// Detect all style sheets of the page
|
||||
var activeSheets = Array.prototype.slice.call(document.styleSheets).filter(function (sheet) {
|
||||
return !sheet.disabled;
|
||||
});
|
||||
|
||||
// Mount the container to the DOM to make `contentWindow` available
|
||||
// [3]
|
||||
document.body.appendChild(container);
|
||||
|
||||
// Copy to clipboard
|
||||
// [4]
|
||||
window.getSelection().removeAllRanges();
|
||||
|
||||
var range = document.createRange();
|
||||
range.selectNode(container);
|
||||
window.getSelection().addRange(range);
|
||||
|
||||
// [5.1]
|
||||
document.execCommand("copy");
|
||||
|
||||
// [5.2]
|
||||
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = true;
|
||||
|
||||
// [5.3]
|
||||
// document.execCommand('copy')
|
||||
|
||||
// [5.4]
|
||||
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = false;
|
||||
|
||||
// Remove the container
|
||||
// [6]
|
||||
document.body.removeChild(container);
|
||||
subject_encoded = encodeURIComponent(subject);
|
||||
window.open("readdle-spark://compose?recipient=manager@chihounds.com&subject=" + subject + "&bcc=" + recipients);
|
||||
})
|
||||
.finally(()=>{
|
||||
setTimeout(function (){
|
||||
location.reload()
|
||||
}, 500)
|
||||
});//refresh page
|
||||
}
|
||||
|
||||
function emailModal(el, url) {
|
||||
form = el.closest("form");
|
||||
console.log(form)
|
||||
data = new FormData(form);
|
||||
|
||||
fetch(url, {
|
||||
@@ -277,32 +311,28 @@ function emailModal(el, url) {
|
||||
email_modal_node.classList.add('is-open')
|
||||
body.appendChild(email_modal_node)
|
||||
tinymce.init({
|
||||
selector:`#lineup-email-data-${data.get('event_lineup_id')} #email-editor`,
|
||||
selector:`textarea#email-editor`,
|
||||
content_css:"/css/application.css",
|
||||
plugins: 'image',
|
||||
menubar: false,
|
||||
toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | image',
|
||||
paste_data_images: true,
|
||||
statusbar:false})
|
||||
// tinymce.activeEditor.setContent("Team,")
|
||||
// lineup_table_div.innerHTML = lineup_table
|
||||
// email_modal.classList.add("is-open");
|
||||
// email_modal.querySelector(".Modal-body").innerHTML = html;
|
||||
tinymce.remove();
|
||||
});
|
||||
}
|
||||
|
||||
async function onSubmit(form, event) {
|
||||
async function submitEventLineup(form, event) {
|
||||
event.preventDefault();
|
||||
console.log(event)
|
||||
teamsnap_icon = document.querySelector("#teamsnap-icon");
|
||||
waiting_icon = document.querySelector("#waiting-icon");
|
||||
success_icon = document.querySelector("#success-icon");
|
||||
failure_icon = document.querySelector("#failure-icon");
|
||||
teamsnap_icon = form.querySelector("#teamsnap-icon");
|
||||
waiting_icon = form.querySelector("#waiting-icon");
|
||||
success_icon = form.querySelector("#success-icon");
|
||||
failure_icon = form.querySelector("#failure-icon");
|
||||
data = new FormData(form);
|
||||
console.log(form)
|
||||
url = form.attributes.action.textContent;
|
||||
teamsnap_icon.classList.add("u-hidden")
|
||||
waiting_icon.classList.remove("u-hidden");
|
||||
toggleShowAndHideLoading(form)
|
||||
await fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
@@ -311,8 +341,8 @@ async function onSubmit(form, event) {
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
waiting_icon.classList.add("u-hidden");
|
||||
if (response.ok) {
|
||||
|
||||
return response.text();
|
||||
} else {
|
||||
return Promise.reject(response.text());
|
||||
@@ -320,202 +350,22 @@ async function onSubmit(form, event) {
|
||||
})
|
||||
.then((text) => {
|
||||
event.submitter.blur()
|
||||
waiting_icon.classList.add("u-hidden");
|
||||
success_icon.classList.remove("u-hidden");
|
||||
// success_icon.querySelector("span.message").innerHTML = text;
|
||||
completeLoad(form, true)
|
||||
console.log(text);
|
||||
})
|
||||
.catch((error) => {
|
||||
event.submitter.blur()
|
||||
waiting_icon.classList.add("u-hidden");
|
||||
failure_icon.classList.remove("u-hidden");
|
||||
completeLoad(form, false)
|
||||
console.log(error);
|
||||
// success_icon.querySelector("span.message").innerHTML = error;
|
||||
});
|
||||
})
|
||||
.finally(()=>{location.reload()});//refresh page
|
||||
setTimeout(() => {
|
||||
[waiting_icon, success_icon, failure_icon].forEach(e=>e.classList.add('u-hidden'))
|
||||
teamsnap_icon.classList.remove('u-hidden')
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
function copyEmailTable(itemEl, subject, recipients) {
|
||||
// Create container for the HTML
|
||||
// [1]
|
||||
let bcLineup = itemEl.closest(".event-lineup");
|
||||
var container = document.createElement("div");
|
||||
var tbl = document.createElement("table");
|
||||
|
||||
let thead = tbl.createTHead();
|
||||
let thead_row = thead.insertRow();
|
||||
let thead_row_cell = thead_row.insertCell();
|
||||
thead_row_cell.appendChild(document.createElement("h3").appendChild(document.createTextNode("STARTING LINEUP")));
|
||||
thead_row_cell.colSpan = 3;
|
||||
thead_row_cell.classList.add("title-cell");
|
||||
var tbody = tbl.createTBody();
|
||||
|
||||
lineup_slots_starting = bcLineup.querySelectorAll(".starting .slot-set .lineup-slot");
|
||||
|
||||
for (node of lineup_slots_starting) {
|
||||
console.log("node", node);
|
||||
let tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
sequence = node.querySelector("input[name*='sequence']").value;
|
||||
console.log(sequence);
|
||||
cell.appendChild(document.createTextNode(parseInt(sequence) + 1));
|
||||
name = node.querySelector("div:has(.lastname)");
|
||||
cell = tr.insertCell();
|
||||
cell.appendChild(document.createTextNode(name.textContent));
|
||||
cell.classList.add("name-cell");
|
||||
position_label = node.querySelector("input[name*='label']").value;
|
||||
tr.insertCell().appendChild(document.createTextNode(position_label));
|
||||
}
|
||||
|
||||
lineup_slots_position_only = bcLineup.querySelector(".position-only .slot-set .lineup-slot");
|
||||
console.log("lineup slots position", lineup_slots_position_only);
|
||||
if (lineup_slots_position_only.length > 0) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.colSpan = 3;
|
||||
cell.appendChild(document.createTextNode("STARTING (POS. ONLY)"));
|
||||
cell.classList.add("title-cell");
|
||||
|
||||
for (node of lineup_slots_position_only) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
cell.appendChild(document.createTextNode(""));
|
||||
cell = tr.insertCell();
|
||||
name = node.querySelector("div:has(.lastname)");
|
||||
cell.appendChild(document.createTextNode(name.textCotent));
|
||||
cell.classList.add("name-cell");
|
||||
position_label = node.querySelector("input[name*='label']").value;
|
||||
tr.insertCell().appendChild(document.createTextNode(position_label));
|
||||
}
|
||||
}
|
||||
|
||||
lineup_slots_bench = bcLineup.querySelector(".bench .slot-set .lineup-slot");
|
||||
if (lineup_slots_bench > 0) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.colSpan = 3;
|
||||
cell.appendChild(document.createTextNode("SUBS"));
|
||||
cell.classList.add("title-cell");
|
||||
|
||||
for (node of lineup_slots_bench) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
|
||||
div_avail_code = node.querySelector("div[class*='availability-status-code']");
|
||||
if (div_with_avail_code.classList.includes("availability-status-code-1")) {
|
||||
cell.appendChild(document.createTextNode("YES"));
|
||||
} else if (div_with_avail_code.classList.includes("availability-status-code-2")) {
|
||||
cell.appendChild(document.createTextNode("MAY"));
|
||||
} else if (div_with_avail_code.classList.includes("availability-status-code-0")) {
|
||||
cell.appendChild(document.createTextNode("NO"));
|
||||
} else {
|
||||
cell.appendChild(document.createTextNode("UNK"));
|
||||
}
|
||||
cell = tr.insertCell();
|
||||
name = node.querySelector("div:has(.lastname)");
|
||||
cell.appendChild(document.createTextNode(name.textCotent));
|
||||
cell.classList.add("name-cell");
|
||||
tr.insertCell().appendChild(document.createTextNode(""));
|
||||
}
|
||||
}
|
||||
|
||||
lineup_slots_out = bcLineup.querySelector(".out .slot-set .lineup-slot");
|
||||
if (lineup_slots_out > 0) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.colSpan = 3;
|
||||
cell.appendChild(document.createTextNode("OUT"));
|
||||
cell.classList.add("title-cell");
|
||||
|
||||
for (node of lineup_slots_out) {
|
||||
var tr = tbody.insertRow();
|
||||
cell = tr.insertCell();
|
||||
cell.classList.add("sequence-cell");
|
||||
div_avail_code = node.querySelector("div[class*='availability-status-code']");
|
||||
if (div_with_avail_code.classList.includes("availability-status-code-1")) {
|
||||
cell.appendChild(document.createTextNode("YES"));
|
||||
} else if (div_with_avail_code.classList.includes("availability-status-code-2")) {
|
||||
cell.appendChild(document.createTextNode("MAY"));
|
||||
} else if (div_with_avail_code.classList.includes("availability-status-code-0")) {
|
||||
cell.appendChild(document.createTextNode("NO"));
|
||||
} else {
|
||||
cell.appendChild(document.createTextNode("UNK"));
|
||||
}
|
||||
cell = tr.insertCell();
|
||||
name = node.querySelector("div:has(.lastname)");
|
||||
cell.appendChild(document.createTextNode(name.textCotent));
|
||||
cell.classList.add("name-cell");
|
||||
tr.insertCell().appendChild(document.createTextNode(""));
|
||||
}
|
||||
}
|
||||
|
||||
container.appendChild(tbl);
|
||||
for (cell of container.getElementsByClassName("title-cell")) {
|
||||
cell.setAttribute("style", "font-weight:bold;background-color:#323669;color:#fff;padding:2px 5px;");
|
||||
}
|
||||
|
||||
for (cell of container.getElementsByClassName("sequence-cell")) {
|
||||
cell.setAttribute("style", "font-weight:bold;padding:2px 5px;");
|
||||
}
|
||||
|
||||
for (cell of container.getElementsByClassName("name-cell")) {
|
||||
cell.setAttribute("style", "width:200px;");
|
||||
}
|
||||
|
||||
// Detect all style sheets of the page
|
||||
var activeSheets = Array.prototype.slice.call(document.styleSheets).filter(function (sheet) {
|
||||
return !sheet.disabled;
|
||||
});
|
||||
|
||||
// Mount the container to the DOM to make `contentWindow` available
|
||||
// [3]
|
||||
document.body.appendChild(container);
|
||||
|
||||
// Copy to clipboard
|
||||
// [4]
|
||||
window.getSelection().removeAllRanges();
|
||||
|
||||
var range = document.createRange();
|
||||
range.selectNode(container);
|
||||
window.getSelection().addRange(range);
|
||||
|
||||
// [5.1]
|
||||
document.execCommand("copy");
|
||||
|
||||
// [5.2]
|
||||
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = true;
|
||||
|
||||
// [5.3]
|
||||
// document.execCommand('copy')
|
||||
|
||||
// [5.4]
|
||||
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = false;
|
||||
|
||||
// Remove the container
|
||||
// [6]
|
||||
document.body.removeChild(container);
|
||||
subject_encoded = encodeURIComponent(subject);
|
||||
window.open("readdle-spark://compose?recipient=manager@chihounds.com&subject=" + subject + "&bcc=" + recipients);
|
||||
}
|
||||
|
||||
function toggleChildSlots (element) {
|
||||
console.log(element);
|
||||
console.log(element.closest(".slot-set"))
|
||||
for (lineup_slot of document.querySelectorAll("[id^=lineup-out] .lineup-slot")) {
|
||||
console.log(lineup_slot)
|
||||
const cells = lineup_slot.querySelectorAll('.Panel-cell:has(.sequence), .Panel-cell:has(.drag-handle), .Panel-cell:has(.position-select-box), div.position-label-flags ')
|
||||
Array.from(cells).forEach(cell=>{
|
||||
cell.classList.toggle('u-hidden')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function submitEmail () {
|
||||
async function copyEmailTable (element) {
|
||||
// range=document.createRange();
|
||||
// window.getSelection().removeAllRanges();
|
||||
// // range.selectNode(document.querySelector('.Modal').querySelector('.Modal-body'));
|
||||
@@ -557,16 +407,63 @@ async function submitEmail () {
|
||||
margin: 0;
|
||||
}</style>
|
||||
`
|
||||
html_content = emailStyle+tinymce.activeEditor.getContent()
|
||||
console.log(html_content)
|
||||
// html_content = emailStyle+tinymce.activeEditor.getContent()
|
||||
// console.log(html_content)
|
||||
const table = element.closest('form').querySelector('.lineup-table table')
|
||||
|
||||
navigator.clipboard.write(
|
||||
[new ClipboardItem(
|
||||
{
|
||||
'text/plain': new Blob([tinymce.activeEditor.getContent({format: "text"})], {type: 'text/plain'}),
|
||||
'text/html': new Blob([html_content], {type: 'text/html'})
|
||||
})
|
||||
])
|
||||
// navigator.clipboard.write(
|
||||
// [new ClipboardItem(
|
||||
// {
|
||||
// // 'text/plain': new Blob([tinymce.activeEditor.getContent({format: "text"})], {type: 'text/plain'}),
|
||||
// 'text/plain': new Blob([table.innerText], {type: 'text/plain'}),
|
||||
// 'text/html': new Blob([emailStyle+table.outerHTML], {type: 'text/html'})
|
||||
// })
|
||||
// ])
|
||||
|
||||
window.getSelection().removeAllRanges();
|
||||
var range = document.createRange();
|
||||
range.selectNode(table);
|
||||
window.getSelection().addRange(range);
|
||||
document.execCommand("copy");
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
|
||||
moveToLineupSegment = (slot, segment_name) => {
|
||||
if (!slot.classList.contains('lineup-slot')) {
|
||||
slot = slot.closest('.lineup-slot')
|
||||
if (!slot) {return}
|
||||
}
|
||||
|
||||
const current_lineup_segment = slot.closest('.lineup-segment')
|
||||
|
||||
if (current_lineup_segment.classList.contains(segment_name)) {
|
||||
return
|
||||
}
|
||||
|
||||
const lineup = slot.closest('.event-lineup')
|
||||
const newParent = lineup.querySelector(`.lineup-segment.${segment_name} .slot-set`)
|
||||
newParent.append(slot)
|
||||
}
|
||||
|
||||
function initSlots () {
|
||||
document.querySelectorAll('.lineup-slot').forEach(slot=>{
|
||||
if (slot.dataset.initialLineupSegment) {
|
||||
moveToLineupSegment(slot, slot.dataset.initialLineupSegment)
|
||||
slot.removeAttribute('data-initial-lineup-segment')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addToStarting = (el) => {
|
||||
const slot = el.closest('.lineup-slot')
|
||||
|
||||
this.blur()
|
||||
}
|
||||
|
||||
removeToBench = (el) => {
|
||||
const slot = el.closest('.lineup-slot')
|
||||
|
||||
this.blue()
|
||||
}
|
||||
|
||||
function insertLineup(direction, teamId, eventId, element) {
|
||||
@@ -597,21 +494,38 @@ function insertLineup(direction, teamId, eventId, element) {
|
||||
.then((html) =>{
|
||||
const parser = new DOMParser();
|
||||
const new_lineup_doc = parser.parseFromString(html, 'text/html')
|
||||
const new_lineup_doc_node = new_lineup_doc.firstElementChild.querySelector('[id*=event-lineup]')
|
||||
const lineup_container = document.querySelector("#lineup-container")
|
||||
const new_lineup_doc_node = new_lineup_doc.firstElementChild.querySelector('.event-lineup')
|
||||
const main = document.querySelector("main")
|
||||
const new_csrf_token = new_lineup_doc.querySelector('form input[name=csrfToken]').value
|
||||
|
||||
direction > 0 ? main.appendChild(new_lineup_doc_node) : main.insertBefore(new_lineup_doc_node, element.closest('[id*=event-lineup]'))
|
||||
|
||||
main.classList.remove(...main.classList)
|
||||
main.classList.add('scroll-horizontal', 'u-spaceSidesSm', 'u-flex')
|
||||
|
||||
Array.from(document.querySelectorAll(".event-lineup")).forEach((bcLineup) => {
|
||||
// main.classList.remove('.u-max1200', 'u-flexExpandSides')
|
||||
bcLineup.classList.remove('u-spaceSidesNone', 'u-sm-spaceSidesAuto')
|
||||
}
|
||||
)
|
||||
|
||||
Array.from(document.querySelectorAll(".event-lineup .Panel")).forEach((bcLineupPanel) => {
|
||||
bcLineupPanel.classList.remove('Panel--full')
|
||||
})
|
||||
for (input of document.querySelectorAll("form input[name=csrfToken]")){
|
||||
input.value = new_csrf_token
|
||||
}
|
||||
|
||||
direction > 0 ? lineup_container.appendChild(new_lineup_doc_node) : lineup_container.insertBefore(new_lineup_doc_node, element.closest('[id*=event-lineup]'))
|
||||
initPage();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function initPage (){
|
||||
colorPositions();
|
||||
initSlots();
|
||||
initFlagsCheckboxes();
|
||||
refreshLineup();
|
||||
for (bcLineup of document.querySelectorAll("[id^=event-lineup]")) {
|
||||
for (bcLineup of document.querySelectorAll(".event-lineup")) {
|
||||
bcLineup.dispatchEvent(lineupChangedEvent)
|
||||
options = {
|
||||
animation: 150,
|
||||
handle: ".Panel-cell:has(.drag-handle), .Panel-cell:has(.sequence)",
|
||||
@@ -622,32 +536,77 @@ function initPage (){
|
||||
pull: [bcLineup.id],
|
||||
},
|
||||
onAdd: function (/**Event*/ evt) {
|
||||
console.log("added to lineup");
|
||||
// Add to Lineup
|
||||
var itemEl = evt.item; // dragged HTMLElement
|
||||
|
||||
refreshLineup();
|
||||
bcLineup.dispatchEvent(lineupChangedEvent)
|
||||
},
|
||||
onUpdate: function (/**Event*/ evt) {
|
||||
console.log("update to lineup");
|
||||
// var itemEl = evt.item; // dragged HTMLElement
|
||||
// refresh_lineup_order(itemEl);
|
||||
refreshLineup();
|
||||
bcLineup.dispatchEvent(lineupChangedEvent)
|
||||
},
|
||||
};
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-starting] .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-positiononly] .slot-set"), options);
|
||||
options["sort"] = false;
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-bench] .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-out] .slot-set"), {...options, group:{...options.group, put:[]}});
|
||||
new Sortable.create(bcLineup.querySelector(".lineup-segment.starting .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector(".lineup-segment.position-only .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector(".lineup-segment.bench .slot-set"), {...options, sort:false});
|
||||
new Sortable.create(bcLineup.querySelector(".lineup-segment.out .slot-set"), {...options, sort:false, group:{...options.group, put:[]}});
|
||||
}
|
||||
|
||||
for (lineup_slot of document.querySelectorAll("[id^=lineup-out] .lineup-slot")) {
|
||||
const cells = lineup_slot.querySelectorAll('.Panel-cell:has(.sequence), .Panel-cell:has(.drag-handle), .Panel-cell:has(.position-select-box), div.position-label-flags')
|
||||
Array.from(cells).forEach(cell=>{
|
||||
cell.classList.add('u-hidden')
|
||||
})
|
||||
// for (lineup_slot of document.querySelectorAll(".lineup-segment.out .lineup-slot")) {
|
||||
// const cells = lineup_slot.querySelectorAll('.Panel-cell:has(.sequence), .Panel-cell:has(.drag-handle), .Panel-cell:has(.position-select-box), button:has(+.position-label-flags)')
|
||||
// Array.from(cells).forEach(cell=>{
|
||||
// cell.classList.add('u-hidden')
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
function mailToLink(el, protocol) {
|
||||
const {to, bcc} = el.dataset
|
||||
const subject = document.getElementById('email-subject').value
|
||||
const email_body = document.getElementById('email-editor').value
|
||||
const url = `${protocol}://compose?recipient=${to}&bcc=${bcc}&subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(email_body)}`
|
||||
console.log(url)
|
||||
// location.href=`mailto:${to}${params}`
|
||||
const windowRef = window.open(url, '_blank');
|
||||
windowRef.focus();
|
||||
}
|
||||
|
||||
function sendAvailabilityReminder(element, eventId, memberIds, csrf_token) {
|
||||
const icon = element.querySelector('svg')
|
||||
const button_text = element.querySelector('span')
|
||||
icon.classList.toggle('u-hidden')
|
||||
button_text.classList.toggle('u-hidden')
|
||||
const loader = '<span class="PulseAnimation"><span class="PulseAnimation-dot"></span><span class="PulseAnimation-dot"></span><span class="PulseAnimation-dot"></span></span>'
|
||||
const loader_node = new DOMParser().parseFromString(loader, "text/html").firstChild.querySelector('span');
|
||||
element.appendChild(loader_node)
|
||||
element.blur();
|
||||
|
||||
const data = new FormData();
|
||||
const url = "../availability_reminders"
|
||||
data.append('eventId', eventId)
|
||||
for (var i = 0; i < memberIds.length; i++) {
|
||||
data.append('memberIds[]', memberIds[i]);
|
||||
}
|
||||
console.log(data)
|
||||
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
'CSRF-Token': csrf_token
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
console.log(response)
|
||||
return response.text();
|
||||
} else {
|
||||
return Promise.reject(response.text());
|
||||
}
|
||||
})
|
||||
.finally(()=>{
|
||||
loader_node.remove()
|
||||
icon.classList.toggle('u-hidden')
|
||||
button_text.classList.toggle('u-hidden')
|
||||
})
|
||||
|
||||
console.log(element, eventId, memberIds)
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initPage)
|
||||
@@ -1,6 +1,7 @@
|
||||
var express = require("express");
|
||||
var passport = require("passport");
|
||||
var TeamsnapStrategy = require("passport-teamsnap");
|
||||
const express = require("express");
|
||||
const passport = require("passport");
|
||||
const TeamsnapStrategy = require("passport-teamsnap");
|
||||
const {teamsnapCallback} = require('../lib/utils')
|
||||
// const {teamsnap} = require("../app");
|
||||
// Configure the TeamSnap strategy for use by Passport.
|
||||
//
|
||||
@@ -21,20 +22,17 @@ passport.use(
|
||||
proxy: true
|
||||
},
|
||||
async function (req, accessToken, refreshToken, profile, done) {
|
||||
json = JSON.parse(profile._raw);
|
||||
field_from_collection = (field_name) => {
|
||||
return json.collection.items[0].data.filter(
|
||||
(e) => e.name == field_name
|
||||
)[0].value;
|
||||
}
|
||||
const new_profile = { access_token: accessToken };
|
||||
new_profile["id"] = field_from_collection("id")
|
||||
new_profile["email"] = field_from_collection("email")
|
||||
new_profile["first_name"] = field_from_collection("first_name")
|
||||
new_profile["last_name"] = field_from_collection("last_name")
|
||||
// json = JSON.parse(profile._raw);
|
||||
const new_profile = {
|
||||
access_token: accessToken,
|
||||
};
|
||||
['id', 'email', 'first_name', 'last_name', 'managed_team_ids'].forEach(
|
||||
k => {
|
||||
new_profile[k] = profile.data[0].get(k)
|
||||
})
|
||||
|
||||
req.session.teamsnap_access_token = accessToken;
|
||||
await initTeamsnap(process.env["TEAMSNAP_CLIENT_ID"], accessToken)
|
||||
await initTeamsnap(accessToken)
|
||||
return done(null, new_profile);
|
||||
}
|
||||
)
|
||||
@@ -59,6 +57,7 @@ passport.serializeUser(function (user, cb) {
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
accessToken: user.access_token,
|
||||
managed_team_ids: user.managed_team_ids
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -66,10 +65,12 @@ passport.serializeUser(function (user, cb) {
|
||||
passport.deserializeUser(function (user, cb) {
|
||||
process.nextTick(async function () {
|
||||
console.log("L#68 deserializing user id", user.id);
|
||||
if (!teamsnap.isAuthed()){
|
||||
await initTeamsnap(process.env["TEAMSNAP_CLIENT_ID"], user.accessToken)
|
||||
try {
|
||||
await initTeamsnap(user.accessToken)
|
||||
return cb(null, user);
|
||||
} catch (err) {
|
||||
return cb(err)
|
||||
}
|
||||
return cb(null, user);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,9 +87,11 @@ var router = express.Router();
|
||||
router.get("/login", function (req, res, next) {
|
||||
// https://stackoverflow.com/a/73056806/20522015
|
||||
returnTo = req.session.returnTo;
|
||||
// req.session.regenerate(); // this is not working right as of now...
|
||||
req.session.returnTo = returnTo;
|
||||
res.render("login", {layout:"layouts/main"});
|
||||
if (req.user?.accessToken){
|
||||
res.redirect(returnTo || "/");
|
||||
} else {
|
||||
res.render("login", {layout:"layouts/main"});
|
||||
}
|
||||
});
|
||||
|
||||
/* GET /login/federated/teamsnap
|
||||
@@ -125,10 +128,9 @@ router.get(
|
||||
})
|
||||
);
|
||||
|
||||
const initTeamsnap = async (clientID, accessToken) => {
|
||||
teamsnap.init(clientID);
|
||||
teamsnap.auth(accessToken);
|
||||
await teamsnap.loadCollections();
|
||||
const initTeamsnap = async (accessToken) => {
|
||||
await teamsnap.auth(accessToken);
|
||||
await teamsnap.loadCollections(teamsnapCallback);
|
||||
await teamsnap.enablePersistence();
|
||||
}
|
||||
|
||||
@@ -138,7 +140,7 @@ const ensureLoggedIn = (req, res, next) => {
|
||||
res.redirect("/login");
|
||||
// return next();
|
||||
}
|
||||
else{
|
||||
else {
|
||||
req.user = req.session.passport.user
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ const eventsController = require("../controllers/event");
|
||||
const router = express.Router();
|
||||
const tsUtils = require("../lib/utils")
|
||||
const {teamsnapCallback} = require("../lib/utils")
|
||||
const multer = require("multer");
|
||||
const upload = multer()
|
||||
|
||||
// Middleware
|
||||
const loadEvent = (req,res,next) => {
|
||||
@@ -68,7 +70,9 @@ router.use("/:team_id([0-9]+)/event/:event_id([0-9]+)", loadEvent)
|
||||
// Routes
|
||||
router.get("/:team_id([0-9]+)/schedule", eventsController.getEvents);
|
||||
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)", eventsController.getEvent);
|
||||
// router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup", eventsController.getLineup);
|
||||
// router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup_card", eventsController.getLineupCard);
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/availability_reminders", upload.none(), eventsController.sendAvailabilityReminders)
|
||||
|
||||
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/modal-confirm-availability-reminders/", eventsController.confirmModalAvailabilityReminders)
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/reset_availabilities",upload.none(), eventsController.submitResetAvailabilities)
|
||||
|
||||
module.exports = {router, loadEvent, loadEvents}
|
||||
@@ -57,6 +57,7 @@ router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/adjacent", doubleCs
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/email", upload.none(), doubleCsrfProtection, eventsLineupController.getEventLineupEmail )
|
||||
router.get ("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)", upload.none(), doubleCsrfProtection, eventsLineupController.getEventLineup);
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)", upload.none(), doubleCsrfProtection, eventsLineupController.postEventLineup);
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/delete", upload.none(), eventsLineupController.submitDeleteEventLineupEntries);
|
||||
|
||||
|
||||
module.exports = {router, loadEventLineup}
|
||||
@@ -30,6 +30,8 @@ router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/sheet", async (req,res) =>
|
||||
}
|
||||
)
|
||||
|
||||
router.get("/lineup/sheet/blank", eventsSheetController.getEventSheetBlank )
|
||||
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/sheet", upload.none(), eventsSheetController.getEventSheet )
|
||||
|
||||
module.exports = {router}
|
||||
@@ -27,4 +27,10 @@ router.get("/", (req,res,next) => {
|
||||
|
||||
router.get("/:team_id([0-9]+)/members", membersController.getMembers);
|
||||
|
||||
router.get("/modal-confirm/", (req,res) => {
|
||||
const {title, body} = req.query
|
||||
res.render('modal_confirm', {title, body} )
|
||||
}
|
||||
)
|
||||
|
||||
module.exports = {router, partials};
|
||||
|
||||
@@ -189,7 +189,7 @@ a.Panel-row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div[id^="event-lineup"] {
|
||||
div.event-lineup {
|
||||
max-width: 576px;
|
||||
counter-reset: lineup-sequence-counter 0;
|
||||
margin-left: 8px;
|
||||
@@ -289,17 +289,31 @@ li .availability-status-code- {
|
||||
|
||||
}
|
||||
|
||||
div[id^="event-lineup"] .Panel {
|
||||
&.position-only .Panel-cell:has(.sequence), &.bench .Panel-cell:has(.sequence), &.out .Panel-cell:has(.sequence){
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.out {
|
||||
.Panel-cell {
|
||||
&:has(.sequence), .drag-handle, .SelectBox {
|
||||
// display: none;
|
||||
div.event-lineup {
|
||||
.lineup-segment {
|
||||
&:has(input.Toggle-input:not(:checked)) {
|
||||
&.out {
|
||||
.Panel-cell:has(.SelectBox),
|
||||
.Panel-cell:has(.drag-handle),
|
||||
button:has(+.position-label-flags),
|
||||
button.addToStarting,
|
||||
button.addToBench
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.bench, &.position-only, &.out {
|
||||
.Panel-cell:has(.sequence) {
|
||||
display: none;
|
||||
}
|
||||
&.bench button.addToBench {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.starting button.addToStarting, &.position-only button.addToStarting {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,8 +325,8 @@ div[id^="event-lineup"] .Panel {
|
||||
@media (max-width: 480px){
|
||||
.Panel--full {
|
||||
border-radius: 0;
|
||||
margin-right: -16px;
|
||||
margin-left: -16px;
|
||||
// margin-right: -16px;
|
||||
// margin-left: -16px;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
}}
|
||||
@@ -354,8 +368,16 @@ div[id^="event-lineup"] .Panel {
|
||||
.Panel .Panel{
|
||||
// padding: 0;
|
||||
// border-radius: 0;
|
||||
border: none;
|
||||
// border: none;
|
||||
// border-top: 1px solid #d6d6d6;
|
||||
// border-bottom: 1px solid #d6d6d6;
|
||||
margin: 0;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.scroll-horizontal {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
button:has(+.position-label-flags :checked) {
|
||||
@extend .Button--blue
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,21 +12,23 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class=" Panel-footer u-flex u-flexJustifyAround u-padSm">
|
||||
<a class="Button" href="/{{team.id}}/event/{{event.id}}">
|
||||
<span>{{{embeddedSvgFromPath "/bootstrap-icons/calendar.svg"}}}</span>
|
||||
<span class="u-hidden u-xs-inline">Details</span>
|
||||
</a>
|
||||
<a class="Button" href="/{{team.id}}/event/{{event.id}}/lineup">
|
||||
<span>{{{embeddedSvgFromPath "/bootstrap-icons/clipboard.svg"}}}</span>
|
||||
<span class="u-hidden u-xs-inline">Lineup</span>
|
||||
</a>
|
||||
<a class="Button" href="/{{team.id}}/event/{{event.id}}/sheet">
|
||||
<span>{{{embeddedSvgFromPath "/bootstrap-icons/file-earmark.svg"}}}</span>
|
||||
<span class="u-hidden u-xs-inline">Sheet</span>
|
||||
</a>
|
||||
<a class="Button" href="https://go.teamsnap.com/{{team.id}}/schedule/view_game/{{event.id}}">
|
||||
<span>{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}</span>
|
||||
<span class="u-hidden u-xs-inline">TeamSnap</span>
|
||||
</a>
|
||||
<div class="u-maxWidthXs">
|
||||
<a class="Button" href="/{{team.id}}/event/{{event.id}}">
|
||||
<span>{{{embeddedSvgFromPath "/bootstrap-icons/calendar.svg"}}}</span>
|
||||
<span class="u-hidden">Details</span>
|
||||
</a>
|
||||
<a class="Button" href="/{{team.id}}/event/{{event.id}}/lineup">
|
||||
<span>{{{embeddedSvgFromPath "/bootstrap-icons/clipboard.svg"}}}</span>
|
||||
<span class="u-hidden">Lineup</span>
|
||||
</a>
|
||||
<a class="Button" href="/{{team.id}}/event/{{event.id}}/sheet">
|
||||
<span>{{{embeddedSvgFromPath "/bootstrap-icons/file-earmark.svg"}}}</span>
|
||||
<span class="u-hidden">Sheet</span>
|
||||
</a>
|
||||
<a class="Button" href="https://go.teamsnap.com/{{team.id}}/schedule/view_game/{{event.id}}">
|
||||
<span>{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}</span>
|
||||
<span class="u-hidden">TeamSnap</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
38
src/views/event/partials/modal_availability_reminders.hbs
Normal file
38
src/views/event/partials/modal_availability_reminders.hbs
Normal file
@@ -0,0 +1,38 @@
|
||||
<div id="modal" class="Modal Modal--clickableBg">
|
||||
<div class="Modal-content">
|
||||
<div onclick="javascript:this.closest('.Modal').remove();">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg" "Modal-iconDismiss"}}}</div>
|
||||
<div class="Modal-header">
|
||||
<div class="Modal-title">Send Reminders</div>
|
||||
</div>
|
||||
<div class="Modal-body">
|
||||
<div class="u-padSidesMd">
|
||||
<strong>Send to players who have selected:</strong>
|
||||
<div class="u-spaceTopSm"><div class="Checkbox">
|
||||
<input class="Checkbox-input" type="checkbox" name="undecidedCheckBox" id="undecidedCheckBox" checked="" value>
|
||||
<label class="Checkbox-label" for="undecidedCheckBox">Undecided</label>
|
||||
</div>
|
||||
<div class="Checkbox">
|
||||
<input class="Checkbox-input" type="checkbox" name="maybeCheckbox" id="maybeCheckbox" value="2">
|
||||
<label class="Checkbox-label" for="maybeCheckbox">Maybe</label>
|
||||
</div>
|
||||
<div class="Checkbox">
|
||||
<input class="Checkbox-input" type="checkbox" name="attendingCheckbox" id="attendingCheckbox" value="1">
|
||||
<label class="Checkbox-label" for="attendingCheckbox">Attending</label>
|
||||
</div>
|
||||
<div class="Checkbox u-padBottomNone">
|
||||
<input class="Checkbox-input" type="checkbox" name="notAttendingCheckbox" id="notAttendingCheckbox" value="0">
|
||||
<label class="Checkbox-label" for="notAttendingCheckbox">Not Attending</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Modal-footer">
|
||||
<button class="Button Button--negative" role="button" type="button" onclick="javascript:this.closest('.Modal').remove();" data-confirm="cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="Button Button--primary" role="button" type="button" data-confirm="yes">
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
0
src/views/event/stats-importer.hbs
Normal file
0
src/views/event/stats-importer.hbs
Normal file
@@ -1,34 +1,34 @@
|
||||
<div class="u-flex" id="lineup-container">
|
||||
<div class="u-spaceSidessAuto" id="event-lineup-{{event.id}}" data-event-lineup-id="{{event_lineup.id}}" data-event-id="{{event.id}}">
|
||||
<form onsubmit="onSubmit(this,event)" action="#">
|
||||
<input type="hidden" name="event_lineup_id" value="{{event_lineup.id}}">
|
||||
{{!-- <input type="hidden" name="_csrf" value="{{csrfToken}}"> --}}
|
||||
<input type="hidden" name="csrfToken" value="{{csrfToken}}">
|
||||
<div class="Panel Panel--full">
|
||||
<div class="Panel-header u-padEndsSm">
|
||||
<h3 style="flex: 1 1 0%;">{{event.formattedTitle}}</h3>
|
||||
<div class="u-spaceSidesNone u-sm-spaceSidesAuto event-lineup" id="event-lineup-{{event.id}}" data-event-lineup-id="{{event_lineup.id}}" data-event-id="{{event.id}}">
|
||||
<form onsubmit="submitEventLineup(this,event)" action="/{{team.id}}/event/{{event.id}}/lineup/{{event_lineup.id}}">
|
||||
<input type="hidden" name="event_lineup_id" value="{{event_lineup.id}}">
|
||||
{{!-- <input type="hidden" name="_csrf" value="{{csrfToken}}"> --}}
|
||||
<input type="hidden" name="csrfToken" value="{{csrfToken}}">
|
||||
<div class="Panel Panel--full">
|
||||
<div class="Panel-header u-padEndsSm">
|
||||
<h3 style="flex: 1 1 0%;">{{event.formattedTitle}}</h3>
|
||||
<div class="Popup">
|
||||
<div class="ButtonGroup">
|
||||
<button class="Button Button--orange" type="submit" formmethod="post">
|
||||
<div>
|
||||
<span id="teamsnap-icon">{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}</span>
|
||||
<span id="waiting-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/loader.svg" "Icon--loader"}}}</span>
|
||||
<span id="success-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/check.svg"}}}</span>
|
||||
<span id="failure-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg"}}}</span>
|
||||
<span id="teamsnap-icon" class="hideOnLoading">{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}</span>
|
||||
<span id="waiting-icon" class="u-hidden showOnLoading">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/loader.svg" "Icon--loader"}}}</span>
|
||||
<span id="success-icon" class="u-hidden showOnSuccess">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/check.svg"}}}</span>
|
||||
<span id="failure-icon" class="u-hidden showOnFailure">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg"}}}</span>
|
||||
Save
|
||||
</div>
|
||||
</button>
|
||||
<div class="Button Button--orange .u-padSidesXs Popup" onclick="togglePopup(this)">
|
||||
<div class="Button Button--orange .u-padSidesXs Popup-toggle" data-control="popup" data-open="event-lineup-more-actions-{{event_lineup.id}}">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/caret-down.svg"}}}
|
||||
<div class="Popup-container Popup-container--down Popup-container--right" style="width: 200px">
|
||||
<div class="Popup-container Popup-container--down Popup-container--right" style="width: 200px" data-popup="event-lineup-more-actions-{{event_lineup.id}}">
|
||||
<div class="Popup-content u-textDecorationNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="emailModal(this, '{{event_lineup.id}}/email')">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/envelope.svg"}}}
|
||||
<span>Generate Email</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="../sheet">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/book.svg"}}}
|
||||
<span>Lineup Card</span>
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="/{{team.id}}/event/{{event.id}}/sheet">
|
||||
<span>{{{embeddedSvgFromPath "/bootstrap-icons/file-earmark.svg"}}}</span>
|
||||
<span class="u-hidden u-xs-inline">Game Sheet</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="insertLineup(1, {{team.id}}, {{event.id}}, this)">
|
||||
@@ -40,85 +40,93 @@
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/caret-left.svg"}}}
|
||||
<span>Insert previous lineup</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="openAvailabilityReminderModal(this, {{team.id}}, {{event.id}})">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/send.svg"}}}
|
||||
<span>Availability Reminders</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="confirmModal(this, {title:'Reset Availabilities',body:'Are sure you want to reset availabilities?'}, submitResetAvailabilities, {team_id:{{team.id}}, event_id:{{event.id}} })";>
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/refresh.svg"}}}
|
||||
<span>Reset All Availabilities</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="confirmModal(this, {title:'Clear Lineup',body:'Are sure you want to clear lineup?'}, submitClearLineup, {team_id:{{team.id}}, event_id:{{event.id}}, event_lineup_id:{{event_lineup.id}} })">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/trash.svg"}}}
|
||||
<span>Clear Lineup</span>
|
||||
</a>
|
||||
<div class="u-hidden">
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<span class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="console.log('not implemented yet')">
|
||||
<span>Publish</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" Panel-body u-padEndsSm">
|
||||
<div class=" u-padSidesSm">
|
||||
<div class="date">{{dateFormat event.startDate "ddd, MMM D h:mm A" }}</div>
|
||||
<div class="location">{{event.locationName}}</div>
|
||||
</div>
|
||||
<div class=" availability-bar fullwidth">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-starting-{{event.id}}" class="Panel u-maxWidthSm starting Panel--fullWidthMobile Panel--full">
|
||||
<div class="Panel-body">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
<i>{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}</i>
|
||||
<span>Starting Lineup</span>
|
||||
</div>
|
||||
<div class=" Panel-row Grid Grid--fit u-textBold u-textCenter u-padXs">
|
||||
{{#each (positions)}}
|
||||
<div class="Grid-cell position-status">{{this}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInStartingLineup this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-positiononly-{{event.id}}" class="Panel u-maxWidthSm position-only Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}
|
||||
<span>Position Only</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInPositionOnly this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-bench-{{event.id}}" class="Panel u-maxWidthSm bench Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-minus.svg"}}}
|
||||
<span>Bench</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInBench this)}}
|
||||
{{> slot member=this event=../event}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-out-{{event.id}}" class="Panel u-maxWidthSm out Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs u-flex">
|
||||
<div><span style="flex: 1 1 0%;">{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-x.svg"}}}Out</span></div>
|
||||
<div class="u-flexGrow1"></div>
|
||||
<div class="Toggle">
|
||||
<input class="Toggle-input" type="checkbox" id="enable-slots" onclick="toggleChildSlots(this);">
|
||||
<label class="Toggle-label" for="enable-slots"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInOut this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class=" Panel-body u-padEndsSm">
|
||||
<div class=" u-padSidesSm">
|
||||
<div class="date">{{dateFormat event.startDate "ddd, MMM D h:mm A" }}</div>
|
||||
<div class="location">{{event.locationName}}</div>
|
||||
</div>
|
||||
<div class="availability-bar fullwidth">
|
||||
{{> availability_bar availabilitySummary=availabilitySummary}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Panel u-maxWidthSm lineup-segment starting Panel--fullWidthMobile Panel--full">
|
||||
<div class="Panel-body">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
<i>{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}</i>
|
||||
<span>Starting Lineup</span>
|
||||
</div>
|
||||
<div class=" Panel-row Grid Grid--fit u-textBold u-textCenter u-padXs">
|
||||
{{#each (positions)}}
|
||||
<div class="Grid-cell position-status" data-value="{{this}}">{{this}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Panel u-maxWidthSm lineup-segment position-only Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}
|
||||
<span>Position Only</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="Panel u-maxWidthSm lineup-segment bench Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-minus.svg"}}}
|
||||
<span>Bench</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#loadSlots}}
|
||||
{{>slot member=member event_lineup=event_event_lineup availablity=availability}}
|
||||
{{/loadSlots}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="Panel u-maxWidthSm lineup-segment out Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs u-flex">
|
||||
<div><span style="flex: 1 1 0%;">{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-x.svg"}}}Out</span></div>
|
||||
<div class="u-flexGrow1"></div>
|
||||
<div class="Toggle">
|
||||
<input class="Toggle-input" type="checkbox" id="enable-slots">
|
||||
<label class="Toggle-label" for="enable-slots"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<div id="availability-reminder-modal-{{event.id}}" class="Modal Modal--clickableBg">
|
||||
<div class="Modal-content">
|
||||
<div onclick="javascript:this.closest('.Modal').remove();">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg" "Modal-iconDismiss"}}}</div>
|
||||
<div class="Modal-header">
|
||||
<div class="Modal-title">Send Reminders</div>
|
||||
</div>
|
||||
<div class="Modal-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,4 @@
|
||||
<div id="modal" class="Modal Modal--clickableBg">
|
||||
<div class="Modal-bgDismiss" onclick="javascript:this.closest('.Modal').remove();tinymce.remove();"></div>
|
||||
<div class="Modal-content">
|
||||
<div onclick="javascript:this.closest('.Modal').remove();tinymce.remove();">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg" "Modal-iconDismiss"}}}</div>
|
||||
<div class="Modal-header">
|
||||
@@ -9,21 +8,37 @@
|
||||
<form>
|
||||
<div class="FieldGroup">
|
||||
<label class="FieldGroup-label">Subject</label>
|
||||
<input class="Input" type="text" value="{{dateFormat event.startDate "ddd, MMM D, YYYY h:mm A" }}, {{ event.locationName }}, ({{#if (isAway event) }}@{{/if}}{{ event.opponentName }})">
|
||||
<input class="Input" id="email-subject" type="text" value="{{dateFormat event.startDate "ddd, MMM D, YYYY h:mm A" }}, {{ event.locationName }}, ({{#if (isAway event) }}@{{/if}}{{ event.opponentName }})">
|
||||
</div>
|
||||
<div class="FieldGroup">
|
||||
<label class="FieldGroup-label">Body</label>
|
||||
<textarea id="email-editor" class="Input"></textarea>
|
||||
</div>
|
||||
<div class="FieldGroup">
|
||||
<label class="FieldGroup-label">Lineup</label>
|
||||
<div class="lineup-email">{{>email_table}}</div>
|
||||
</div>
|
||||
<div class="FieldGroup">
|
||||
<button class="Button" role="button" onclick="submitEmail();">Submit</button>
|
||||
<label class="FieldGroup-label">
|
||||
Lineup
|
||||
<button class="Button Button--smallSquare" role="button" type="button" onclick="copyEmailTable(this)">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-fill.svg"}}}
|
||||
</button>
|
||||
</label>
|
||||
<div class="lineup-email lineup-table">{{>email_table}}</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="Modal-footer">
|
||||
<button class="Button" role="button" type="button" onclick="mailToLink(this, 'readdle-spark');",
|
||||
data-to="{{user.email}}"
|
||||
data-bcc="{{joinMemberEmailAddresses (filterNonPlayers members)}}">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/mail.svg"}}}
|
||||
Spark Mail
|
||||
</button>
|
||||
<button class="Button" role="button" type="button" onclick="mailToLink(this, 'mailto');",
|
||||
data-to="{{user.email}}"
|
||||
data-bcc="{{joinMemberEmailAddresses (filterNonPlayers members)}}">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/mail.svg"}}}
|
||||
Mail
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
@@ -1,4 +1,4 @@
|
||||
<table>
|
||||
<table class="lineup-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title-cell" colSpan=3>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="Panel-expandableRow lineup-slot">
|
||||
<input type="hidden" name="label" value="{{member.benchcoach.eventLineupEntry.label}}">
|
||||
<input type="hidden" name="flags" value="{{flagsString member.benchcoach.eventLineupEntry.flags}}">
|
||||
<input type="hidden" name="sequence" value="{{member.benchcoach.eventLineupEntry.sequence}}">
|
||||
<div class="Panel-expandableRow lineup-slot" data-initial-lineup-segment="{{initial_lineup_segment}}">
|
||||
<input type="hidden" name="label" value="{{eventLineupEntry.label}}">
|
||||
<input type="hidden" name="flags" value="{{flagsString eventLineupEntry.flags}}">
|
||||
<input type="hidden" name="sequence" value="{{eventLineupEntry.sequence}}">
|
||||
<input type="hidden" name="eventId" value="{{event.id}}">
|
||||
<input type="hidden" name="eventLineupEntryId" value="{{member.benchcoach.eventLineupEntry.id}}">
|
||||
<input type="hidden" name="availabilityStatusCode", value="{{member.benchcoach.availability?.statusCode}}">
|
||||
<input type="hidden" name="eventLineupEntryId" value="{{eventLineupEntry.id}}">
|
||||
<input type="hidden" name="availabilityStatusCode", value="{{#if availability}}{{availability.statusCode}}{{/if}}">
|
||||
<input type="hidden" name="memberId" value="{{member.id}}">
|
||||
<input type="hidden" name="lastName" value="{{member.lastName}}">
|
||||
<input type="hidden" name="firstName" value="{{member.firstName}}">
|
||||
@@ -17,12 +17,39 @@
|
||||
</div>
|
||||
<div class="Panel-cell u-padXs u-sizeFill u-flex">
|
||||
<div
|
||||
class="availability-status-code-{{
|
||||
member.benchcoach.availability?.statusCode
|
||||
class="Popup availability-status-code-{{
|
||||
availability?.statusCode
|
||||
}}"
|
||||
>
|
||||
{{#if member.benchcoach.availability}}{{{avail_status_code_icon member.benchcoach.availability.statusCode}}}{{/if}}
|
||||
{{#if availability}}
|
||||
{{#with availability}}
|
||||
<div class="Popup">
|
||||
<button class="Popup-toggle Button Button--smallSquare {{avail_status_code_class statusCode}}"
|
||||
type="button"
|
||||
data-control="popup"
|
||||
data-open="availablility-popup-{{eventId}}-{{memberId}}"
|
||||
>
|
||||
{{#if notes}}{{{embeddedSvgFromPath "/bootstrap-icons/asterisk.svg"}}}{{else}}{{{avail_status_code_icon statusCode}}}{{/if}}
|
||||
</button>
|
||||
<div class="Popup-container Popup-container--left" data-popup="availablility-popup-{{eventId}}-{{memberId}}">
|
||||
<div class="Popup-content u-padSm u-textCenter">
|
||||
<h3 class="u-spaceBottomSm">Availability</h3>
|
||||
{{#if notes}}
|
||||
<p class="u-textLeft">“ <i>{{notes}}</i> ”</p>
|
||||
{{else}}
|
||||
<p class="u-textLeft">No notes.</p>
|
||||
{{/if}}
|
||||
<button type="button" class="Button u-spaceTopSm" onclick="sendAvailabilityReminder(this, {{eventId}}, ['{{memberId}}'], {{csrfToken}})">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/send.svg"}}}
|
||||
<span>Send Reminder</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/with}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="u-fontSizeLg u-textNoWrap">
|
||||
<span class="lastname">
|
||||
{{member.lastName}}
|
||||
@@ -35,14 +62,27 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="u-flexGrow1"></div>
|
||||
<div class="position-label-flags">
|
||||
<div class="Checkbox Checkbox--inline">
|
||||
<input class="Checkbox-input" type="checkbox" name="flag-drd" id="flag-drd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}" onclick="refreshLineup()">
|
||||
<label class="Checkbox-label" for="flag-drd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}">DR<small>d</small></label>
|
||||
</div>
|
||||
<div class="Checkbox Checkbox--inline">
|
||||
<input class="Checkbox-input" type="checkbox" name="flag-dhd" id="flag-dhd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}" onclick="refreshLineup()">
|
||||
<label class="Checkbox-label" for="flag-dhd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}">DH<small>d</small></label>
|
||||
<button type="button" class="Button Button--smallSquare addToBench" onclick="moveToLineupSegment(this, 'bench');this.blur()">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg"}}}
|
||||
</button>
|
||||
<button type="button" class="Button Button--smallSquare addToStarting" onclick="moveToLineupSegment(this, 'starting');this.blur()">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/plus.svg"}}}
|
||||
</button>
|
||||
<div class="Popup">
|
||||
<button type="button" class="Popup-toggle Button Button--smallSquare" onclick="this.closest('div').querySelector('.Popup-container').classList.toggle('is-open');this.blur();" href="javascript:void(0)">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/three-dots.svg"}}}
|
||||
</button>
|
||||
<div class="Popup-container Popup-container--rightHang position-label-flags">
|
||||
<div class="Popup-content u-padSm u-textCenter">
|
||||
<div class="Checkbox Checkbox--inline">
|
||||
<input class="Checkbox-input" type="checkbox" name="DRd" id="flag-drd-{{member.id}}-{{eventLineupEntry.id}}">
|
||||
<label class="Checkbox-label" for="flag-drd-{{member.id}}-{{eventLineupEntry.id}}">DR<small>d</small></label>
|
||||
</div>
|
||||
<div class="Checkbox Checkbox--inline">
|
||||
<input class="Checkbox-input" type="checkbox" name="DHd" id="flag-dhd-{{member.id}}-{{eventLineupEntry.id}}">
|
||||
<label class="Checkbox-label" for="flag-dhd-{{member.id}}-{{eventLineupEntry.id}}">DH<small>d</small></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -53,7 +93,7 @@
|
||||
--
|
||||
</option>
|
||||
{{#each (positions)}}
|
||||
<option value="{{this}}" {{#if (comparePositionWithFlags this ../member.benchcoach.eventLineupEntry)}}selected{{/if}}>
|
||||
<option value="{{this}}" {{#if (comparePositionWithFlags this ../eventLineupEntry)}}selected{{/if}}>
|
||||
{{this}}
|
||||
</option>
|
||||
{{/each}}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="field-container">
|
||||
<img src="/media/baseball-diamond.svg" />
|
||||
{{{embeddedSvgFromPath "/media/baseball-diamond.svg" 'baseball-diamond'}}}
|
||||
{{#defenseLineup event_lineup_entries members}}
|
||||
<div class="slot-set pos-{{this.position}}">
|
||||
<table>
|
||||
<table class="striped">
|
||||
<tbody>
|
||||
<tr class="slot">
|
||||
<th class="position"></th>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<table>
|
||||
<colgroup><col span="3" class="player"></colgroup>
|
||||
{{!-- <colgroup><col span="0" class="player-stats"></colgroup> --}}
|
||||
<colgroup><col span="4" class="position-capability"></colgroup>
|
||||
{{!-- <colgroup><col span="4" class="player"></colgroup> --}}
|
||||
{{!-- <colgroup><col span="1" class="spacer"></colgroup> --}}
|
||||
{{!-- <colgroup><col span="1" class="player-stats"></colgroup> --}}
|
||||
{{!-- <colgroup><col span="4" class="position-capability"></colgroup>
|
||||
<colgroup><col span="4" class="availability-on-day future"></colgroup>
|
||||
<colgroup><col span="4" class="availability-on-day past"></colgroup>
|
||||
<colgroup><col span="4" class="availability-on-day past"></colgroup> --}}
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" id="today-availability">
|
||||
<th colspan="4" id="today-availability">
|
||||
Available ({{availabilitySummary.playerGoingCount}}|{{availabilitySummary.playerMaybeCount}})
|
||||
</th>
|
||||
<th class="spacer first-of-group last-of-group"></th>
|
||||
<th class="player-stats">
|
||||
<span class="decimal-point">.</span>AVG
|
||||
<span class="delimiter">/</span>
|
||||
@@ -17,34 +19,38 @@
|
||||
<span class="decimal-point">.</span>SLG
|
||||
<span class="delimiter">:</span>PA
|
||||
</th>
|
||||
<th class="position-capability pitcher">P</th>
|
||||
<th class="position-capability pitcher first-of-group">P</th>
|
||||
<th class="position-capability catcher">C</th>
|
||||
<th class="position-capability infield">I</th>
|
||||
<th class="position-capability outfield">O</th>
|
||||
<th class="position-capability outfield last-of-group">O</th>
|
||||
{{!-- <% for timepoint, i in timeline.select{|tp| tp[:comparison_to_selected]>0}.sort{|tp| -tp[:comparison_to_selected]}.each_with_index do%> --}}
|
||||
|
||||
{{#loopEvents upcoming_events}}
|
||||
<th class="availability-on-day avail-today-plus-{{@index}}" date="{{this.startDate}}"><div>{{dateFormat this.startDate "ddd" }}</div></th>
|
||||
<th class="availability-on-day avail-today-plus-{{@index}} {{#ifEquals @index 0}}first-of-group{{/ifEquals}}{{#ifEquals @index 3}}last-of-group{{/ifEquals}}" date="{{this.startDate}}"><div>{{dateFormat this.startDate "ddd" }}</div></th>
|
||||
{{/loopEvents}}
|
||||
{{#loopEvents recent_events}}
|
||||
<th class="availability-on-day avail-today-minus-{{@index}}" date="{{this.startDate}}"><div>{{dateFormat this.startDate "ddd" }}</div></th>
|
||||
<th class="availability-on-day avail-today-minus-{{@index}} {{#ifEquals @index 0}}first-of-group{{/ifEquals}}{{#ifEquals @index 3}}last-of-group{{/ifEquals}}" date="{{this.startDate}}"><div>{{dateFormat this.startDate "ddd" }}</div></th>
|
||||
{{/loopEvents}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{!-- <% by_member.select{|m,d| !m.is_non_player}.each_with_index do |(member, d), i|%> --}}
|
||||
{{#rosterHistory event event_lineup_entries members availabilities}}
|
||||
<tr id="roster-history-slot-<%= ::Temple::Utils.escape_html((i)) %>">
|
||||
<td class="is-present-checkbox available-status-code-{{this.benchcoach.availability.statusCode}}">
|
||||
<tr class="roster-history-slot{{#if (isStarting this)}} starting-today{{/if}}">
|
||||
<td class="is-present-checkbox available-status-code-{{this.benchcoach.availability.statusCode}} first-of-group">
|
||||
<span>■</span>
|
||||
</td>
|
||||
<td class="jersey-number available-status-code-{{this.benchcoach.availability.statusCode}}{{#if (isStarting this)}} starting{{/if}}">
|
||||
<td class="jersey-number available-status-code-{{this.benchcoach.availability.statusCode}}">
|
||||
{{this.jerseyNumber}}
|
||||
</td>
|
||||
<td class="player-name available-status-code-{{this.benchcoach.availability.statusCode}}{{#if (isStarting this)}} starting{{/if}}">
|
||||
<td class="player-name available-status-code-{{this.benchcoach.availability.statusCode}}">
|
||||
{{this.lastName}}
|
||||
</td>
|
||||
<td class="player-stats border-left border-right">
|
||||
<td class="position available-status-code-{{this.benchcoach.availability.statusCode}} last-of-group">
|
||||
<span>{{this.benchcoach.eventLineupEntry.label}}</span>
|
||||
</td>
|
||||
<td class="spacer"></td>
|
||||
<td class="player-stats first-of-group last-of-group">
|
||||
<span class="decimal-point">.</span>
|
||||
<span class="avg">000</span>
|
||||
<span class="delimiter">/</span>
|
||||
@@ -56,20 +62,20 @@
|
||||
<span class="delimiter">:</span>
|
||||
<span class="pa">00</span>
|
||||
</td>
|
||||
<td class="position-capability pitcher">{{positionCapabilityFor this "P"}}</td>
|
||||
<td class="position-capability pitcher first-of-group">{{positionCapabilityFor this "P"}}</td>
|
||||
<td class="position-capability catcher">{{positionCapabilityFor this "C"}}</td>
|
||||
<td class="position-capability infield">{{positionCapabilityFor this "IF"}}</td>
|
||||
<td class="position-capability outfield">{{positionCapabilityFor this "OF"}}</td>
|
||||
<td class="position-capability outfield last-of-group">{{positionCapabilityFor this "OF"}}</td>
|
||||
{{#loopEvents ../upcoming_events}}
|
||||
{{#timepointForMember ../this ../../timeline this}}
|
||||
<td class="availability-on-day future available-status-code-{{this.availability.statusCode}}">
|
||||
<td class="availability-on-day future available-status-code-{{this.availability.statusCode}} {{this.value}} {{#ifEquals @index 0}}first-of-group{{/ifEquals}}{{#ifEquals @index 3}}last-of-group{{/ifEquals}}">
|
||||
{{this.value}}
|
||||
</td>
|
||||
{{/timepointForMember}}
|
||||
{{/loopEvents}}
|
||||
{{#loopEvents ../recent_events}}
|
||||
{{#timepointForMember ../this ../../timeline this}}
|
||||
<td class="availability-on-day past available-status-code-{{this.availability.statusCode}}">
|
||||
<td class="availability-on-day past available-status-code-{{this.availability.statusCode}} {{this.value}} {{#ifEquals @index 0}}first-of-group{{/ifEquals}}{{#ifEquals @index 3}}last-of-group{{/ifEquals}}">
|
||||
{{this.value}}
|
||||
</td>
|
||||
{{/timepointForMember}}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<link rel="stylesheet" href="/css/eventsheet.css">
|
||||
|
||||
<body class="B5">
|
||||
<div class="sheet eventsheet" id="page-1">
|
||||
<section id="defense-card">
|
||||
<body class="{{#if sheet_size}}{{sheet_size}}{{else}}B5{{/if}}">
|
||||
<div class="sheet eventsheet {{#if sheet_layout}}{{sheet_layout}}{{else}}quarters{{/if}}" id="page-1">
|
||||
<section class="NE" id="defense-card">
|
||||
<header>
|
||||
<div class="event-title float-left">
|
||||
{{event.formattedTitle}} – {{dateFormat event.startDate "ddd, MMM D h:mm A" }}
|
||||
{{event.formattedTitle}}
|
||||
</div>
|
||||
<div class="homeaway float-right">
|
||||
{{event.gameType}}
|
||||
@@ -31,8 +31,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="roster-and-history">
|
||||
<div class="roster-and-history">
|
||||
<section class="SW" id="roster-and-history">
|
||||
<div class="container">
|
||||
{{> roster_and_history
|
||||
event=event
|
||||
event_lineup_entries=event_lineup_entries
|
||||
@@ -42,7 +42,7 @@
|
||||
}}
|
||||
</div>
|
||||
</section>
|
||||
<section class="lineup-card" id="lineup-card-dugout">
|
||||
<section class="NW lineup-card dugout" id="lineup-card-dugout">
|
||||
<header>
|
||||
<div class="float-left event-title">{{event.formattedTitle}}</div>
|
||||
<div class="float-right homeaway">{{event.gameType}}</div>
|
||||
@@ -80,9 +80,9 @@
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<section class="lineup-card" id="lineup-card-exchange">
|
||||
<section class="SE lineup-card exchange" id="lineup-card-exchange">
|
||||
<header>
|
||||
<div class="float-left event-title">{{event.formattedTitle}}</div>
|
||||
<div class="float-left event-title">{{event.formattedTitleForMultiTeam}}</div>
|
||||
<div class="float-right homeaway">{{event.gameType}}</div>
|
||||
</header>
|
||||
<div class="starting-lineup-table">
|
||||
@@ -119,24 +119,18 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="sheet eventsheet" id="page-2">
|
||||
<section id="defense-card" style="border:solid black;">
|
||||
<div id="defense-pane">
|
||||
{{> defense_pane}}
|
||||
</div>
|
||||
</section>
|
||||
<section id="front-cover">
|
||||
<div class="sheet eventsheet {{#if sheet_layout}}{{sheet_layout}}{{else}}quarters{{/if}}" id="page-2">
|
||||
<section class="SE" id="front-cover">
|
||||
<header>
|
||||
<div class="homeaway">
|
||||
<span>{{firstLetter event.gameType}}</span>
|
||||
<div class="game-number">
|
||||
{{event.label}}
|
||||
</div>
|
||||
<div class="title">
|
||||
<span class="date-time">{{dateFormat event.startDate "ddd, MMM D h:mm A" }}</span>
|
||||
<span class="location">{{event.locationName}}</span>
|
||||
</div>
|
||||
<div class="game-number">
|
||||
<span class="label">Game #</span>
|
||||
<span class="value">XX</span>
|
||||
<div class="homeaway">
|
||||
<span>{{firstLetter event.gameType}}</span>
|
||||
</div>
|
||||
</header>
|
||||
<div style="display:block;max-height: 1em;background-color: lightgray;border-bottom: solid 2px black;">
|
||||
@@ -149,17 +143,16 @@
|
||||
</div>
|
||||
</div>
|
||||
{{# if event.opponentName}}
|
||||
<div>
|
||||
<div style="text-align: center;font-family: 'Pacifico';">
|
||||
<div class="conjuction">
|
||||
<span>vs</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opponent">
|
||||
<div>
|
||||
<span class="name">{{event.opponentName}}</span>
|
||||
</div>
|
||||
{{#if opponent_logo.mediumUrl }}
|
||||
<img src="{{opponent_logo.mediumUrl}}">
|
||||
{{!-- <%= ::Temple::Utils.escape_html((image_tag opponent_logos[event.opponent.id].medium_url)) %> --}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="">
|
||||
<table>
|
||||
@@ -183,7 +176,70 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
</section>
|
||||
<section class="lineup-card" id="lineup-card-dugout-blank">
|
||||
<section class="NW blank" id="defense-card">
|
||||
<header>
|
||||
<div class="event-title float-left">
|
||||
|
||||
</div>
|
||||
<div class="homeaway float-right">
|
||||
|
||||
</div>
|
||||
</header>
|
||||
<div>
|
||||
<div id="defense-pane">
|
||||
{{> defense_pane event_lineup_entries=null members=null}}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<table class="notes">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
{{#repeat 3}}
|
||||
<tr>
|
||||
<td></td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="SW lineup-card exchange blank" id="lineup-card-exchange-blank">
|
||||
<header></header>
|
||||
<div class="starting-lineup-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4">
|
||||
Starting
|
||||
</th>
|
||||
<th class="substitution">
|
||||
Substitution
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#repeat 12}}
|
||||
<tr class="slot">
|
||||
<th class="sequence">
|
||||
</th>
|
||||
<td class="player-name">
|
||||
</td>
|
||||
<td class="jersey-number">
|
||||
</td>
|
||||
<td class="position">
|
||||
</td>
|
||||
<td class="substitution">
|
||||
</td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<section class="NE lineup-card dugout blank" id="lineup-card-dugout-blank">
|
||||
<header></header>
|
||||
<div class="starting-lineup-table">
|
||||
<table>
|
||||
<thead>
|
||||
@@ -217,38 +273,5 @@
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<section class="lineup-card" id="lineup-card-exchange-blank">
|
||||
<div class="starting-lineup-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4">
|
||||
Starting
|
||||
</th>
|
||||
<th class="substitution">
|
||||
Substitution
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#repeat 12}}
|
||||
<tr class="slot">
|
||||
<th class="sequence">
|
||||
</th>
|
||||
<td class="player-name">
|
||||
</td>
|
||||
<td class="jersey-number">
|
||||
</td>
|
||||
<td class="position">
|
||||
</td>
|
||||
<td class="substitution">
|
||||
</td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
248
src/views/eventsheet/sheet_blank.hbs
Normal file
248
src/views/eventsheet/sheet_blank.hbs
Normal file
@@ -0,0 +1,248 @@
|
||||
<link rel="stylesheet" href="/css/eventsheet.css">
|
||||
|
||||
<body class="{{page_size}} ">
|
||||
<div class="sheet eventsheet {{layout}}" id="page-1">
|
||||
<section class="NW" id="roster-and-history">
|
||||
<div class="roster-and-history">
|
||||
{{> roster_and_history
|
||||
event=event
|
||||
event_lineup_entries=event_lineup_entries
|
||||
members=members availabilities=availabilities
|
||||
recent_events=recent_events
|
||||
upcoming_events=upcoming_events
|
||||
}}
|
||||
</div>
|
||||
</section>
|
||||
<section class="NE blank" id="defense-card">
|
||||
<header>
|
||||
<div class="event-title float-left">
|
||||
</div>
|
||||
<div class="homeaway float-right">
|
||||
</div>
|
||||
</header>
|
||||
<div>
|
||||
<div id="defense-pane">
|
||||
{{> defense_pane event_lineup_entries=event_lineup_entries members=members}}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<table class="notes">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
{{#repeat 3}}
|
||||
<tr>
|
||||
<td></td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="SW lineup-card dugout blank" id="lineup-card-dugout">
|
||||
<header>
|
||||
<div class="float-left event-title">{{event.formattedTitle}}</div>
|
||||
<div class="float-right homeaway">{{event.gameType}}</div>
|
||||
</header>
|
||||
<div class="starting-lineup-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4">Starting</th>
|
||||
<th class="substitution">Substitution</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#offenseLineup 11 event_lineup_entries members}}
|
||||
<tr class="slot">
|
||||
<th class="sequence{{#if this.member.lastName}} counter{{/if}}"></th>
|
||||
<td class="player-name">{{this.member.lastName}}</td>
|
||||
<td class="jersey-number">{{this.member.jerseyNumber}}</td>
|
||||
<td class="position">{{this.label}}</td>
|
||||
<td class="substitution"></td>
|
||||
</tr>
|
||||
{{/offenseLineup }}
|
||||
{{#defenseLineup event_lineup_entries members}}
|
||||
<tr class="slot">
|
||||
{{#if (isInPositionOnly this.member)}}{{#if (comparePositionWithFlags "P" this.eventLineupEntry)}}
|
||||
<th class="sequence">PO</th>
|
||||
<td class="player-name">{{this.member.lastName}}</td>
|
||||
<td class="jersey-number">{{this.member.jerseyNumber}}</td>
|
||||
<td class="position">{{positionLabelWithoutFlags this.eventLineupEntry.label}}</td>
|
||||
<td class="substitution"></td>
|
||||
{{/if}}{{/if}}
|
||||
</tr>
|
||||
{{/defenseLineup}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<section class="SE lineup-card exchange blank" id="lineup-card-exchange">
|
||||
<header>
|
||||
<div class="float-left event-title">{{event.formattedTitle}}</div>
|
||||
<div class="float-right homeaway">{{event.gameType}}</div>
|
||||
</header>
|
||||
<div class="starting-lineup-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4">Starting</th>
|
||||
<th class="substitution">Substitution</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#offenseLineup 11 event_lineup_entries members}}
|
||||
<tr class="slot">
|
||||
<th class="sequence {{#if this.member.lastName}}counter{{/if}}"></th>
|
||||
<td class="player-name">{{this.member.lastName}}</td>
|
||||
<td class="jersey-number">{{this.member.jerseyNumber}}</td>
|
||||
<td class="position">{{this.label}}</td>
|
||||
<td class="substitution"></td>
|
||||
</tr>
|
||||
{{/offenseLineup}}
|
||||
{{#defenseLineup event_lineup_entries members}}
|
||||
<tr class="slot">
|
||||
{{#if (isInPositionOnly this.member)}}{{#if (comparePositionWithFlags "P" this.eventLineupEntry)}}
|
||||
<th class="sequence">PO</th>
|
||||
<td class="player-name">{{this.member.lastName}}</td>
|
||||
<td class="jersey-number">{{this.member.jerseyNumber}}</td>
|
||||
<td class="position">{{positionLabelWithoutFlags this.eventLineupEntry.label}}</td>
|
||||
<td class="substitution"></td>
|
||||
{{/if}}{{/if}}
|
||||
</tr>
|
||||
{{/defenseLineup}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="sheet eventsheet {{layout}}" id="page-2">
|
||||
<section class="NW blank" id="defense-card">
|
||||
<header>
|
||||
<div class="event-title float-left">
|
||||
|
||||
</div>
|
||||
<div class="homeaway float-right">
|
||||
|
||||
</div>
|
||||
</header>
|
||||
<div>
|
||||
<div id="defense-pane">
|
||||
{{> defense_pane event_lineup_entries=null members=null}}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<table class="notes">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
{{#repeat 3}}
|
||||
<tr>
|
||||
<td></td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="NE blank" id="defense-card">
|
||||
<header>
|
||||
<div class="event-title float-left">
|
||||
|
||||
</div>
|
||||
<div class="homeaway float-right">
|
||||
|
||||
</div>
|
||||
</header>
|
||||
<div>
|
||||
<div id="defense-pane">
|
||||
{{> defense_pane event_lineup_entries=null members=null}}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<table class="notes">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
{{#repeat 3}}
|
||||
<tr>
|
||||
<td></td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="SW lineup-card exchange blank" id="lineup-card-exchange-blank">
|
||||
<header></header>
|
||||
<div class="starting-lineup-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4">
|
||||
Starting
|
||||
</th>
|
||||
<th class="substitution">
|
||||
Substitution
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#repeat 12}}
|
||||
<tr class="slot">
|
||||
<th class="sequence">
|
||||
</th>
|
||||
<td class="player-name">
|
||||
</td>
|
||||
<td class="jersey-number">
|
||||
</td>
|
||||
<td class="position">
|
||||
</td>
|
||||
<td class="substitution">
|
||||
</td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<section class="SE lineup-card dugout blank" id="lineup-card-dugout-blank">
|
||||
<header></header>
|
||||
<div class="starting-lineup-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4">
|
||||
Starting
|
||||
</th>
|
||||
<th class="substitution">
|
||||
Substitution
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{!-- <% for i in (0...12) do%> --}}
|
||||
{{#repeat 12}}
|
||||
<tr class="slot">
|
||||
<th class="sequence">
|
||||
</th>
|
||||
<td class="player-name">
|
||||
</td>
|
||||
<td class="jersey-number">
|
||||
</td>
|
||||
<td class="position">
|
||||
</td>
|
||||
<td class="substitution">
|
||||
</td>
|
||||
</tr>
|
||||
{{/repeat}}
|
||||
{{!-- <% end %> --}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
@@ -21,15 +21,13 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<header class="u-spaceBottomMd">
|
||||
{{> navbar }}
|
||||
{{{_sections.header}}}
|
||||
</header>
|
||||
<div class="u-padSidesMd u-xs-padSidesLg">
|
||||
<div class="u-max1200 u-flexExpandSides u-xs-size5of6 u-sm-size2of3 u-md-sizeFull u-padEndsLg u-sm-padEndsXl">
|
||||
{{{ body }}}
|
||||
</div>
|
||||
</div>
|
||||
<main class="u-xs-spaceSidesMd">
|
||||
{{{ body }}}
|
||||
</main>
|
||||
</body>
|
||||
{{{script_tags scripts}}}
|
||||
</html>
|
||||
|
||||
26
src/views/modal_confirm.hbs
Normal file
26
src/views/modal_confirm.hbs
Normal file
@@ -0,0 +1,26 @@
|
||||
<div id="modal" class="Modal Modal--clickableBg">
|
||||
<div class="Modal-content">
|
||||
<div onclick="javascript:this.closest('.Modal').remove();">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg" "Modal-iconDismiss"}}}</div>
|
||||
<div class="Modal-header">
|
||||
<div class="Modal-title">{{title}}</div>
|
||||
</div>
|
||||
<div class="Modal-body">
|
||||
{{body}}
|
||||
</div>
|
||||
<div class="Modal-footer">
|
||||
<button class="Button Button--negative" role="button" type="button" onclick="javascript:this.closest('.Modal').remove();" data-confirm="cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="Button Button--primary" role="button" type="button" data-confirm="yes">
|
||||
<span class="hideOnLoading">Yes</span>
|
||||
<span id="success-icon" class="u-hidden showOnSuccess">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/check.svg"}}}</span>
|
||||
<span id="failure-icon" class="u-hidden showOnFailure">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg"}}}</span>
|
||||
<span class="PulseAnimation showOnLoading u-hidden">
|
||||
<span class="PulseAnimation-dot"></span>
|
||||
<span class="PulseAnimation-dot"></span>
|
||||
<span class="PulseAnimation-dot"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,6 @@
|
||||
<h1>
|
||||
{{title}}
|
||||
<hr class="Divider" />
|
||||
</h1>
|
||||
<div class="Panel">
|
||||
<div class="Panel-body">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<img src ="/teamsnap-ui/assets/icons/schedule.svg" class="Icon">
|
||||
<span class="span u-hidden u-xs-inline">Schedule</span>
|
||||
</a>
|
||||
<a class="Button" href="members">
|
||||
<a class="Button u-hidden" href="members">
|
||||
<img src ="/teamsnap-ui/assets/icons/roster.svg" class="Icon">
|
||||
<span class="span u-hidden u-xs-inline">Roster</span>
|
||||
</a>
|
||||
@@ -29,24 +29,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" Panel Panel--full">
|
||||
<div class=" Panel-header">
|
||||
<h2 class=" Panel-title">Upcoming Events</h2>
|
||||
</div>
|
||||
<div class=" Panel-body">
|
||||
|
||||
<h2 class=" ">Upcoming Events</h2>
|
||||
<hr class="Divider" />
|
||||
<div class="Grid Grid--withGutter">
|
||||
{{#each upcoming_events}}
|
||||
<div class="Grid-cell u-xs-size1of2 u-sm-size1of3">
|
||||
{{>event_panel event=this}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class=" Panel Panel--full">
|
||||
<div class=" Panel-header">
|
||||
<h2 class=" Panel-title">Recent Events</h2>
|
||||
</div>
|
||||
<div class=" Panel-body">
|
||||
<h2 class="">Recent Events</h2>
|
||||
<hr class="Divider" />
|
||||
<div class="Grid Grid--withGutter">
|
||||
{{#each recent_events}}
|
||||
<div class="Grid-cell u-xs-size1of2 u-sm-size1of3">
|
||||
{{>event_panel event=this}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
Reference in New Issue
Block a user