Compare commits
12 Commits
6576d17539
...
fb648d477f
| Author | SHA1 | Date | |
|---|---|---|---|
|
fb648d477f
|
|||
|
6076a2801e
|
|||
|
b2b2dba352
|
|||
|
b9f9c8455f
|
|||
|
54dac93da5
|
|||
|
3a95ca4b74
|
|||
|
450499d9aa
|
|||
|
39d1a37043
|
|||
|
35d6eba599
|
|||
|
2f2f33ce74
|
|||
|
66c18479b3
|
|||
|
a505747b06
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
.env
|
.env
|
||||||
var
|
var
|
||||||
|
src/public/media/
|
||||||
|
|
||||||
|
|
||||||
#ide
|
#ide
|
||||||
/.nova
|
/.nova
|
||||||
|
|||||||
23
.vscode/launch.json
vendored
Normal file
23
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen",
|
||||||
|
"name": "nodemon",
|
||||||
|
"program": "dev",
|
||||||
|
"request": "launch",
|
||||||
|
"restart": true,
|
||||||
|
"runtimeExecutable": "nodemon",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"type": "node",
|
||||||
|
"env": {"NODE_ENV": "development"},
|
||||||
|
"preLaunchTask": "npm: scss"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
19
.vscode/tasks.json
vendored
Normal file
19
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "scss",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "npm: scss",
|
||||||
|
"detail": "sass src/scss/application.scss:public/css/application.css src/scss/eventsheet.scss:public/css/eventsheet.css"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
20
bin/www
20
bin/www
@@ -4,11 +4,14 @@
|
|||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var app = require("../src/app");
|
var {app} = require("../src/app");
|
||||||
var http = require("http");
|
var http = require("http");
|
||||||
var https = require("https");
|
var https = require("https");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var debug = require("debug")("https");
|
var debug = require("debug")("https");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get port from environment and store in Express.
|
* Get port from environment and store in Express.
|
||||||
@@ -25,6 +28,21 @@ const https_options = {
|
|||||||
cert: fs.readFileSync("certs/cert.pem"),
|
cert: fs.readFileSync("certs/cert.pem"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
// console.log(`starting livereload, watching ${path.join(__dirname, "../src/views")}`)
|
||||||
|
var livereload = require("livereload");
|
||||||
|
var connectLiveReload = require("connect-livereload");
|
||||||
|
|
||||||
|
const liveReloadServer = livereload.createServer({https: https_options, extraExts: ['pug']});
|
||||||
|
liveReloadServer.watch(path.join(__dirname, "../src/views"));
|
||||||
|
liveReloadServer.server.once("connection", () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
liveReloadServer.refresh("/");
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var server = https.createServer(https_options, app);
|
var server = https.createServer(https_options, app);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
2074
package-lock.json
generated
2074
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -23,19 +23,28 @@
|
|||||||
"url": "https://github.com/sponsors/anthonyscorrea"
|
"url": "https://github.com/sponsors/anthonyscorrea"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./bin/www"
|
"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"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@teamsnap/teamsnap-ui": "^3.12.3",
|
"@teamsnap/teamsnap-ui": "^3.12.3",
|
||||||
|
"better-sqlite3": "^9.4.1",
|
||||||
|
"better-sqlite3-session-store": "^0.1.0",
|
||||||
"bootstrap": "^5.3.1",
|
"bootstrap": "^5.3.1",
|
||||||
"bootstrap-icons": "^1.10.5",
|
"bootstrap-icons": "^1.10.5",
|
||||||
"connect-ensure-login": "^0.1.1",
|
"connect-ensure-login": "^0.1.1",
|
||||||
"cookie-parser": "~1.4.4",
|
"cookie-parser": "~1.4.4",
|
||||||
"csurf": "^1.11.0",
|
"cors": "^2.8.5",
|
||||||
|
"cors-anywhere": "^0.4.4",
|
||||||
|
"csrf-csrf": "^3.0.3",
|
||||||
"debug": "~2.6.9",
|
"debug": "~2.6.9",
|
||||||
"dotenv": "^8.6.0",
|
"dotenv": "^8.6.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
|
"handlebars-dateformat": "^1.1.3",
|
||||||
|
"hbs": "^4.2.0",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "~1.6.3",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
@@ -46,7 +55,16 @@
|
|||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"pug": "^3.0.2",
|
"pug": "^3.0.2",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"teamsnap.js": "^1.62.1",
|
"teamsnap.js": "github:anthonyscorrea/teamsnap-javascript-sdk#add-eventLineup-eventLineupEntry",
|
||||||
|
"tinymce": "^6.8.3",
|
||||||
|
"underscore": "^1.13.6",
|
||||||
"xhr2": "^0.2.1"
|
"xhr2": "^0.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"connect-livereload": "^0.6.1",
|
||||||
|
"install": "^0.13.0",
|
||||||
|
"livereload": "^0.9.3",
|
||||||
|
"nodemon": "^3.0.3",
|
||||||
|
"pug2hbs": "^1.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
134
src/app.js
134
src/app.js
@@ -1,32 +1,65 @@
|
|||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
var createError = require("http-errors");
|
const createError = require("http-errors");
|
||||||
var express = require("express");
|
const express = require("express");
|
||||||
var path = require("path");
|
const path = require("path");
|
||||||
var cookieParser = require("cookie-parser");
|
const cookieParser = require("cookie-parser");
|
||||||
var session = require("express-session");
|
const session = require("express-session");
|
||||||
var csrf = require("csurf");
|
const SqliteStore = require("better-sqlite3-session-store")(session)
|
||||||
var passport = require("passport");
|
var sqlite = require("better-sqlite3");
|
||||||
var logger = require("morgan");
|
const { generateToken } = require('./middlewares/csrf');
|
||||||
var bodyParser = require("body-parser");
|
const passport = require("passport");
|
||||||
|
const logger = require("morgan");
|
||||||
|
const bodyParser = require("body-parser");
|
||||||
global.XMLHttpRequest = require("xhr2");
|
global.XMLHttpRequest = require("xhr2");
|
||||||
var teamsnap = require("teamsnap.js");
|
// const teamsnap = require("./public/js/teamsnap");
|
||||||
var indexRouter = require("./routes/index");
|
const teamsnap = require("teamsnap.js/lib/teamsnap")
|
||||||
var authRouter = require("./routes/auth");
|
// import {teamsnap} from "teamsnap.js"
|
||||||
|
const indexRouter = require("./routes/index").router;
|
||||||
|
const authRouter = require("./routes/auth").router;
|
||||||
|
var hbs = require('hbs');
|
||||||
|
const { embeddedSvgFromPath } = require("./lib/utils");
|
||||||
|
const cors = require('cors');
|
||||||
|
const corsOptions = {
|
||||||
|
origin: false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// teamsnap.init(process.env["TEAMSNAP_CLIENT_ID"])
|
||||||
|
// console.log(teamsnap)
|
||||||
|
// teamsnap.teamsnap.init
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
// view engine setup
|
// view engine setup
|
||||||
app.set("views", path.join(__dirname, "views"));
|
app.set("views", path.join(__dirname, "views"));
|
||||||
app.set("view engine", "pug");
|
hbs.registerPartials(require("./routes/index").partials)
|
||||||
|
hbs.registerPartials(require("./controllers/event").partials)
|
||||||
|
hbs.registerPartials(require("./controllers/eventlineup").partials)
|
||||||
|
hbs.registerPartials(require("./controllers/eventsheet").partials)
|
||||||
|
|
||||||
|
hbs.registerHelper('dateFormat', require('handlebars-dateformat'));
|
||||||
|
hbs.registerHelper('section', (name, options) => {
|
||||||
|
if(!this._sections) this._sections = {};
|
||||||
|
this._sections[name] = options.fn(this);
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
hbs.registerHelper("embeddedSvgFromPath", require('./lib/utils').embeddedSvgFromPath)
|
||||||
|
hbs.registerHelper(require("./controllers/event").helpers)
|
||||||
|
hbs.registerHelper(require("./controllers/eventlineup").helpers)
|
||||||
|
hbs.registerHelper(require("./helpers/eventsheet"))
|
||||||
|
|
||||||
|
app.set("view engine", "hbs");
|
||||||
app.locals.pluralize = require("pluralize");
|
app.locals.pluralize = require("pluralize");
|
||||||
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
var connectLiveReload = require("connect-livereload");
|
||||||
|
app.use(connectLiveReload());
|
||||||
|
}
|
||||||
|
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded());
|
||||||
app.use(logger("dev"));
|
app.use(logger("dev"));
|
||||||
app.use(express.json());
|
app.use(cors(corsOptions))
|
||||||
app.use(express.urlencoded({ extended: false }));
|
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(express.static(path.join(__dirname, "public")));
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
app.use(
|
app.use(
|
||||||
@@ -39,25 +72,54 @@ app.use(
|
|||||||
path.join(__dirname, "../node_modules/@teamsnap/teamsnap-ui/dist/css")
|
path.join(__dirname, "../node_modules/@teamsnap/teamsnap-ui/dist/css")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
app.use(
|
||||||
|
"/teamsnap-ui/assets",
|
||||||
|
express.static(
|
||||||
|
path.join(__dirname, "../node_modules/@teamsnap/teamsnap-ui/src/assets")
|
||||||
|
)
|
||||||
|
);
|
||||||
app.use(
|
app.use(
|
||||||
"/font",
|
"/font",
|
||||||
express.static(path.join(__dirname, "../node_modules/bootstrap-icons/font"))
|
express.static(path.join(__dirname, "../node_modules/bootstrap-icons/font"))
|
||||||
);
|
);
|
||||||
|
app.use(
|
||||||
|
"/bootstrap-icons",
|
||||||
|
express.static(path.join(__dirname, "../node_modules/bootstrap-icons/icons"))
|
||||||
|
);
|
||||||
app.use(
|
app.use(
|
||||||
"/js",
|
"/js",
|
||||||
express.static(path.join(__dirname, "../node_modules/sortablejs"))
|
express.static(path.join(__dirname, "../node_modules/sortablejs"))
|
||||||
);
|
);
|
||||||
|
app.use(
|
||||||
|
"/js",
|
||||||
|
express.static(path.join(__dirname, "../node_modules/tinymce"))
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
"/js",
|
||||||
|
express.static(path.join(__dirname, "../node_modules/teamsnap.js/lib/"))
|
||||||
|
);
|
||||||
|
const db = new sqlite("./var/db/sessions.db");
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
|
store: new SqliteStore({
|
||||||
|
client: db,
|
||||||
|
expired: {
|
||||||
|
clear: true,
|
||||||
|
intervalMs: 900000 //ms = 15min
|
||||||
|
}
|
||||||
|
}),
|
||||||
teamsnap_token: "",
|
teamsnap_token: "",
|
||||||
current_team: "",
|
current_team: "",
|
||||||
secret: "keyboard cat",
|
secret: process.env['SECRET'],
|
||||||
resave: false, // don't save session if unmodified
|
resave: false, // don't save session if unmodified
|
||||||
saveUninitialized: false, // don't create session until something stored
|
saveUninitialized: false, // don't create session until something stored
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
app.use(csrf());
|
app.use(function (req, res, next) {
|
||||||
app.use(passport.authenticate("session"));
|
res.locals.csrfToken = generateToken;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
app.use(passport.authenticate("session", { failureRedirect: '/login', failureMessage: true }));
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
var msgs = req.session.messages || [];
|
var msgs = req.session.messages || [];
|
||||||
res.locals.messages = msgs;
|
res.locals.messages = msgs;
|
||||||
@@ -65,28 +127,40 @@ app.use(function (req, res, next) {
|
|||||||
req.session.messages = [];
|
req.session.messages = [];
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
app.use(function (req, res, next) {
|
|
||||||
res.locals.csrfToken = req.csrfToken();
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use("/", authRouter);
|
app.use("/", authRouter);
|
||||||
app.use("/", indexRouter);
|
app.use("/", indexRouter);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
app.use(require("./routes/team").router)
|
||||||
app.use(function (req, res, next) {
|
app.use(require("./routes/opponent").router)
|
||||||
next(createError(404));
|
app.use(require("./routes/event").router)
|
||||||
});
|
app.use(require("./routes/eventlineup").router)
|
||||||
|
app.use(require("./routes/eventsheet").router)
|
||||||
|
// app.use("/", indexRouter.team_router);
|
||||||
|
|
||||||
// error handler
|
// error handler
|
||||||
app.use(function (err, req, res, next) {
|
app.use(function (err, req, res, next) {
|
||||||
// set locals, only providing error in development
|
// set locals, only providing error in development
|
||||||
|
if (err) {
|
||||||
res.locals.message = err.message;
|
res.locals.message = err.message;
|
||||||
res.locals.error = req.app.get("env") === "development" ? err : {};
|
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||||
|
if (typeof err === 'string' || err instanceof String) {
|
||||||
|
err = {
|
||||||
|
message: err
|
||||||
|
}
|
||||||
|
}
|
||||||
console.log("error:", err);
|
console.log("error:", err);
|
||||||
// render the error page
|
// render the error page
|
||||||
res.status(err.status || 500);
|
res.status(err.status || 500).render("error", { title:"Error", layout: req.layout, message: err.message });
|
||||||
res.render("error", { message: err.message });
|
}
|
||||||
|
else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = app;
|
// catch 404 and forward to error handler
|
||||||
|
// app.use(function (req, res, next) {
|
||||||
|
// next(createError(404));
|
||||||
|
// });
|
||||||
|
|
||||||
|
module.exports = {app};
|
||||||
67
src/controllers/event.js
Normal file
67
src/controllers/event.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
tsUtils = require("../lib/utils");
|
||||||
|
const path = require('path');
|
||||||
|
const { teamsnapFailure, tsPromise } = require("../lib/utils");
|
||||||
|
|
||||||
|
|
||||||
|
exports.helpers = {
|
||||||
|
availability_percentage: (availabilitySummary, status) => {
|
||||||
|
attribute = {
|
||||||
|
going: "playerGoingCount",
|
||||||
|
notgoing: "playerNotGoingCount",
|
||||||
|
maybe: "playerMaybeCount",
|
||||||
|
unknown: "playerUnknownCount"
|
||||||
|
}[status.toLowerCase()]
|
||||||
|
return (availabilitySummary[attribute]/availabilitySummary.team.playerMemberCount)*100.0
|
||||||
|
},
|
||||||
|
isAway: (event) => {
|
||||||
|
return event.gameType == "Away";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.partials = path.join(__dirname, "../views/event/partials")
|
||||||
|
|
||||||
|
exports.getEvents = async (req, res, next) => {
|
||||||
|
const {user, team, layout} = req
|
||||||
|
const bulkLoadTypes = ["event", "availabilitySummary"]
|
||||||
|
req.promises.push(
|
||||||
|
tsPromise('bulkLoad', {teamId: team.id, types: bulkLoadTypes})
|
||||||
|
.then(items=>tsUtils.groupTeamsnapItems(items))
|
||||||
|
.then(items=>{
|
||||||
|
items.events.forEach((event) => {
|
||||||
|
event.link('availabilitySummary', items.availabilitySummaries.find(a=>a.eventId==event.id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
req.events = items.events;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team.id, req))
|
||||||
|
.catch((err) => {
|
||||||
|
teamsnapFailure(err,next)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const context = {
|
||||||
|
title: "Events",
|
||||||
|
user, team, layout,
|
||||||
|
events: req.events,
|
||||||
|
};
|
||||||
|
res.render("event/list", context);
|
||||||
|
} catch(e) {
|
||||||
|
next(e)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getEvent = async (req, res, next) => {
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
const {user, team, event, layout} = req
|
||||||
|
lineups = await req.event.loadItem('eventLineups')
|
||||||
|
event.link('availabilitySummary', req.availabilitySummary)
|
||||||
|
context = {
|
||||||
|
title: "Event",
|
||||||
|
user, team, event, layout,
|
||||||
|
availabilitySummary: req.availabilitySummary,
|
||||||
|
};
|
||||||
|
res.render("event/show", context);
|
||||||
|
};
|
||||||
183
src/controllers/eventlineup.js
Normal file
183
src/controllers/eventlineup.js
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
const path = require('path')
|
||||||
|
const fs = require('fs')
|
||||||
|
const {embeddedSvgFromPath, parsePositionLabel, compilePositionLabel} = 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) => flags?.join(","),
|
||||||
|
plus1: (i) => Number(i)+1,
|
||||||
|
positions: () => ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"],
|
||||||
|
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) => {
|
||||||
|
return label.replace(/(.*?)\s\[(.*?)\]/, "$1");
|
||||||
|
},
|
||||||
|
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 != "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 == "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.getEventLineup = async (req, res)=>{
|
||||||
|
// res.send(req.event_lineup)
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
const {user, team, members, event, layout, event_lineup, event_lineup_entries, availabilities, availabilitySummary, csrfToken} = req
|
||||||
|
attachBenchcoachPropertiesToMember(members, event_lineup_entries, availabilities)
|
||||||
|
members.sort(tsUtils.teamsnapMembersSortLineupAvailabilityLastName)
|
||||||
|
res.render("eventlineup/edit", {user, team, members, event, layout, event_lineup, event_lineup_entries, availabilitySummary, csrfToken})
|
||||||
|
}
|
||||||
|
|
||||||
|
attachBenchcoachPropertiesToMember = (members, event_lineup_entries, availabilities) => {
|
||||||
|
members.forEach((member)=> {
|
||||||
|
// I *think* this can be done with linking https://github.com/teamsnap/teamsnap-javascript-sdk/wiki/Persistence#linking
|
||||||
|
// here's an example:
|
||||||
|
// member.link('eventLineupEntry', event_lineup_entries.find(i=>i.id=members[1].id))
|
||||||
|
member.benchcoach = {}
|
||||||
|
// I don't really like this, but the member_id changes once a season is archived.
|
||||||
|
// 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 availability = availabilities.find(e=>e.memberId == member.id)
|
||||||
|
member.benchcoach.availability = availability
|
||||||
|
if (event_lineup_entry != null) {
|
||||||
|
// member.link('eventLineupEntry', event_lineup_entry)
|
||||||
|
member.benchcoach.eventLineupEntry = event_lineup_entry
|
||||||
|
const {positionLabelWithoutFlag, positionFlags} = parsePositionLabel(event_lineup_entry.label);
|
||||||
|
member.benchcoach.eventLineupEntry.positionLabelWithoutFlag = positionLabelWithoutFlag
|
||||||
|
member.benchcoach.eventLineupEntry.flags = positionFlags
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
member.benchcoach.eventLineupEntry = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
exports.attachBenchcoachPropertiesToMember = attachBenchcoachPropertiesToMember
|
||||||
|
|
||||||
|
exports.getEventLineupEmail = async (req, res)=>{
|
||||||
|
const {body} = req
|
||||||
|
if (body.memberId == null) {res.status(400).end();return}
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
const {user, team, members, event, layout, event_lineup, event_lineup_entries, availabilities, availabilitySummary} = req
|
||||||
|
const eventLineupEntries = req.event_lineup.eventLineupEntries
|
||||||
|
const {newEventLineupEntries} = processPostedEventLineupEntries(body, eventLineupEntries, event_lineup)
|
||||||
|
attachBenchcoachPropertiesToMember(members, newEventLineupEntries, availabilities)
|
||||||
|
members.sort(tsUtils.teamsnapMembersSortLineupAvailabilityLastName)
|
||||||
|
res.status(200).render("eventlineup/partials/email_table", {user, team, members, event, event_lineup, event_lineup_entries: newEventLineupEntries, availabilities, availabilitySummary})
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getEventLineupEntriesData = async (req, res)=>{
|
||||||
|
const {event_lineup, event_lineup_entries} = req
|
||||||
|
res.setHeader('Content-Type', 'application/json').send(JSON.stringify(req.event_lineup_entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.postEventLineup = async (req,res) => {
|
||||||
|
const {body} = req
|
||||||
|
if (body.memberId == null) {res.status(400).end();return}
|
||||||
|
await Promise.all(req.promises);
|
||||||
|
const eventLineupEntries = req.event_lineup.eventLineupEntries
|
||||||
|
const {newEventLineupEntries} = processPostedEventLineupEntries(body, eventLineupEntries, req.event_lineup)
|
||||||
|
newEventLineupEntries.forEach(e=>{
|
||||||
|
teamsnap.saveEventLineupEntry(e)
|
||||||
|
})
|
||||||
|
eventLineup = await teamsnap.loadEventLineups(req.params.event_id)
|
||||||
|
res.status(201).end()
|
||||||
|
}
|
||||||
|
|
||||||
|
const processPostedEventLineupEntries = (body, eventLineupEntries, eventLineup) => {
|
||||||
|
const newEventLineupEntries = []
|
||||||
|
|
||||||
|
body.memberId.forEach((memberId, i)=>{
|
||||||
|
const lineupEntryId = body.eventLineupEntryId[i]
|
||||||
|
const lineupEntryLabel = body.label[i]
|
||||||
|
const lineupEntrySequence = body.sequence[i]
|
||||||
|
const lineupEntryFlags = body.flags[i]
|
||||||
|
if (lineupEntryId != '') {
|
||||||
|
// Update lineup entry
|
||||||
|
const eventLineupEntry = eventLineupEntries.find((e)=>e.id==Number(lineupEntryId))
|
||||||
|
eventLineupEntry.sequence = lineupEntrySequence
|
||||||
|
eventLineupEntry.label = compilePositionLabel(lineupEntryLabel, lineupEntryFlags)
|
||||||
|
newEventLineupEntries.push(eventLineupEntry)
|
||||||
|
}
|
||||||
|
else if (lineupEntryLabel != '') {
|
||||||
|
// Create lineup entry
|
||||||
|
const eventLineupEntry = teamsnap.createEventLineupEntry()
|
||||||
|
eventLineupEntry.eventLineupId = eventLineup.id
|
||||||
|
eventLineupEntry.memberId = memberId
|
||||||
|
eventLineupEntry.sequence = lineupEntrySequence
|
||||||
|
eventLineupEntry.label = compilePositionLabel(lineupEntryLabel, lineupEntryFlags)
|
||||||
|
newEventLineupEntries.push(eventLineupEntry)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Skip lineup entry
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {newEventLineupEntries, eventLineupEntries}
|
||||||
|
}
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
utils = require("../lib/utils");
|
|
||||||
|
|
||||||
exports.getEvents = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap.bulkLoad(team_id, ["team", "event", "availabilitySummary"]).then(() => {
|
|
||||||
items = teamsnap.getAllItems();
|
|
||||||
context = {
|
|
||||||
title: "Events",
|
|
||||||
team: items.find((i) => i.type == "team" && i.id == team_id),
|
|
||||||
availabilitySummaries: items.filter((i) => i.type == "availabilitySummary"),
|
|
||||||
events: items.filter((i) => i.type == "event"),
|
|
||||||
};
|
|
||||||
res.render("events", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getEvent = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
event_id = req.params.event_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap.bulkLoad(team_id, ["team", "event", "availabilitySummary"]).then(() => {
|
|
||||||
items = teamsnap.getAllItems();
|
|
||||||
context = {
|
|
||||||
title: "Event",
|
|
||||||
team: items.find((i) => i.type == "team" && i.id == team_id),
|
|
||||||
availabilitySummary: items.find((i) => i.type == "availabilitySummary" && i.id == event_id),
|
|
||||||
event: items.find((i) => i.type == "event" && i.id == event_id),
|
|
||||||
};
|
|
||||||
res.render("event", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getLineup = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
event_id = req.params.event_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap
|
|
||||||
.bulkLoad(team_id, [
|
|
||||||
"team",
|
|
||||||
"member",
|
|
||||||
// "member_photos",
|
|
||||||
"event",
|
|
||||||
"opponent",
|
|
||||||
"availability_summary",
|
|
||||||
])
|
|
||||||
.then((items) => {
|
|
||||||
events = items.filter((i) => i.type == "event").sort((a, b) => a.startDate - b.startDate);
|
|
||||||
event = events.find((i) => i.id == event_id);
|
|
||||||
events_past = events.slice(
|
|
||||||
events.findIndex((e) => e == event) - 4,
|
|
||||||
events.findIndex((e) => e == event)
|
|
||||||
);
|
|
||||||
events_future = events.slice(events.findIndex((e) => e == event) + 1, events.findIndex((e) => e == event) + 5);
|
|
||||||
events = events_past.concat(event).concat(events_future);
|
|
||||||
})
|
|
||||||
.then((items) => {
|
|
||||||
return teamsnap.loadAvailabilities({
|
|
||||||
eventId: events.map((e) => e.id),
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return teamsnap.collections["eventLineups"]
|
|
||||||
.queryItems("search", {
|
|
||||||
eventId: events.map((e) => e.id),
|
|
||||||
})
|
|
||||||
.then((event_lineups) => {
|
|
||||||
return Promise.all(event_lineups.map((elu) => elu.loadItem("eventLineupEntries")));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
items = teamsnap.getAllItems();
|
|
||||||
events = items.filter((i) => i.type == "event");
|
|
||||||
current_event_index = events.findIndex((e) => e.id == event_id);
|
|
||||||
|
|
||||||
context = {
|
|
||||||
title: "Lineup",
|
|
||||||
team: items.find((e) => e.type == "team" && e.id == team_id),
|
|
||||||
team_id: req.params.team_id,
|
|
||||||
event_id: req.params.event_id,
|
|
||||||
current_event_index: current_event_index,
|
|
||||||
events: items.filter((a) => a.type == "event"),
|
|
||||||
availabilitySummaries: items.filter((i) => i.type == "availabilitySummary"),
|
|
||||||
availabilitySummary: items.filter((i) => i.type == "availabilitySummary" && i.eventId == event_id),
|
|
||||||
event: items.find((e) => e.type == "event" && e.id == event_id),
|
|
||||||
events_past: events_past,
|
|
||||||
events_future: events_future,
|
|
||||||
members: items.filter((a) => a.type == "member"),
|
|
||||||
availabilities: items.filter((i) => i.type == "availability").sort(utils.teamsnapAvailabilitiesSort),
|
|
||||||
all_lineup_entries: items.filter((i) => i.type == "eventLineupEntry"),
|
|
||||||
event_lineup_entries: items
|
|
||||||
.filter((i) => i.type == "eventLineupEntry" && i.eventId == event_id)
|
|
||||||
.sort((a, b) => a.sequence - b.sequence),
|
|
||||||
};
|
|
||||||
|
|
||||||
res.render("event-lineup", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getLineupCard = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
event_id = req.params.event_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap
|
|
||||||
.bulkLoad(team_id, [
|
|
||||||
"team",
|
|
||||||
"member",
|
|
||||||
// "member_photos",
|
|
||||||
"event",
|
|
||||||
"opponent",
|
|
||||||
"availability_summary",
|
|
||||||
])
|
|
||||||
.then((items) => {
|
|
||||||
events = items.filter((i) => i.type == "event").sort((a, b) => a.startDate - b.startDate);
|
|
||||||
event = events.find((i) => i.id == event_id);
|
|
||||||
events_past = events.slice(
|
|
||||||
events.findIndex((e) => e == event) - 4,
|
|
||||||
events.findIndex((e) => e == event)
|
|
||||||
);
|
|
||||||
events_future = events.slice(events.findIndex((e) => e == event) + 1, events.findIndex((e) => e == event) + 5);
|
|
||||||
events = events_past.concat(event).concat(events_future);
|
|
||||||
})
|
|
||||||
.then((items) => {
|
|
||||||
return teamsnap.loadAvailabilities({
|
|
||||||
eventId: events.map((e) => e.id),
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return teamsnap.collections["eventLineups"]
|
|
||||||
.queryItems("search", {
|
|
||||||
eventId: events.map((e) => e.id),
|
|
||||||
})
|
|
||||||
.then((event_lineups) => {
|
|
||||||
return Promise.all(event_lineups.map((elu) => elu.loadItem("eventLineupEntries")));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
items = teamsnap.getAllItems();
|
|
||||||
events = items.filter((i) => i.type == "event");
|
|
||||||
current_event_index = events.findIndex((e) => e.id == event_id);
|
|
||||||
|
|
||||||
context = {
|
|
||||||
title: "Gamecard",
|
|
||||||
team_id: req.params.team_id,
|
|
||||||
event_id: req.params.event_id,
|
|
||||||
current_event_index: current_event_index,
|
|
||||||
events: items.filter((a) => a.type == "event"),
|
|
||||||
availabilitySummaries: items.filter((i) => i.type == "availabilitySummary"),
|
|
||||||
event: items.find((e) => e.type == "event" && e.id == event_id),
|
|
||||||
events_past: events_past,
|
|
||||||
events_future: events_future,
|
|
||||||
members: items.filter((a) => a.type == "member"),
|
|
||||||
availabilities: items.filter((i) => i.type == "availability").sort(utils.teamsnapAvailabilitiesSort),
|
|
||||||
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]"))
|
|
||||||
.sort((a, b) => a.sequence - b.sequence),
|
|
||||||
event_lineup_entries: items
|
|
||||||
.filter((i) => i.type == "eventLineupEntry" && i.eventId == event_id)
|
|
||||||
.sort((a, b) => a.sequence - b.sequence),
|
|
||||||
};
|
|
||||||
|
|
||||||
res.render("event-lineupcard", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
94
src/controllers/eventsheet.js
Normal file
94
src/controllers/eventsheet.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
const tsUtils = require('../lib/utils')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
exports.partials = path.join(__dirname, "../views/eventsheet/partials")
|
||||||
|
|
||||||
|
exports.getEventSheet = async (req,res) =>{
|
||||||
|
req.promises.push(
|
||||||
|
teamsnap.loadOpponents(
|
||||||
|
req.team.id,
|
||||||
|
(err, opponents)=>{
|
||||||
|
if (err) console.log("error in route/opponent.js", err);
|
||||||
|
}
|
||||||
|
).then(opponents => {req.opponent=opponents.find(o=>o.id==req.event.opponentId);})
|
||||||
|
)
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
req.promises.push(
|
||||||
|
teamsnap.loadTeamMedia(req.team.id, (err, team_media)=>{
|
||||||
|
if (err) console.log("error in route/opponent.js", err);
|
||||||
|
})
|
||||||
|
.then(team_media => {
|
||||||
|
req.opponent_logo = team_media.find(tm=>tm.description==`opponent-logo-${req.event.opponentId}.png`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
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})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getLineupCard = (req, res, next) => {
|
||||||
|
team_id = req.params.team_id;
|
||||||
|
event_id = req.params.event_id;
|
||||||
|
teamsnap
|
||||||
|
.bulkLoad(team_id, [
|
||||||
|
"team",
|
||||||
|
"member",
|
||||||
|
// "member_photos",
|
||||||
|
"event",
|
||||||
|
"opponent",
|
||||||
|
"availabilitySummary",
|
||||||
|
])
|
||||||
|
.then((items) => {
|
||||||
|
events = items.filter((i) => i.type == "event").sort((a, b) => a.startDate - b.startDate);
|
||||||
|
event = events.find((i) => i.id == event_id);
|
||||||
|
events_past = events.slice(
|
||||||
|
events.findIndex((e) => e == event) - 4,
|
||||||
|
events.findIndex((e) => e == event)
|
||||||
|
);
|
||||||
|
events_future = events.slice(events.findIndex((e) => e == event) + 1, events.findIndex((e) => e == event) + 5);
|
||||||
|
events = events_past.concat(event).concat(events_future);
|
||||||
|
})
|
||||||
|
.then((items) => {
|
||||||
|
return teamsnap.loadAvailabilities({
|
||||||
|
eventId: events.map((e) => e.id),
|
||||||
|
}).catch(error => console.log("error in event.js"));
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return teamsnap.collections["eventLineups"]
|
||||||
|
.queryItems("search", {
|
||||||
|
eventId: events.map((e) => e.id),
|
||||||
|
})
|
||||||
|
.then((event_lineups) => {
|
||||||
|
return Promise.all(event_lineups.map((elu) => elu.loadItem("eventLineupEntries")));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
items = teamsnap.getAllItems();
|
||||||
|
events = items.filter((i) => i.type == "event");
|
||||||
|
current_event_index = events.findIndex((e) => e.id == event_id);
|
||||||
|
|
||||||
|
context = {
|
||||||
|
title: "Gamecard",
|
||||||
|
team_id: req.params.team_id,
|
||||||
|
event_id: req.params.event_id,
|
||||||
|
current_event_index: current_event_index,
|
||||||
|
events: items.filter((a) => a.type == "event"),
|
||||||
|
availabilitySummaries: items.filter((i) => i.type == "availabilitySummary"),
|
||||||
|
event: items.find((e) => e.type == "event" && e.id == event_id),
|
||||||
|
events_past: events_past,
|
||||||
|
events_future: events_future,
|
||||||
|
members: items.filter((a) => a.type == "member"),
|
||||||
|
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]"))
|
||||||
|
.sort((a, b) => a.sequence - b.sequence),
|
||||||
|
event_lineup_entries: items
|
||||||
|
.filter((i) => i.type == "eventLineupEntry" && i.eventId == event_id)
|
||||||
|
.sort((a, b) => a.sequence - b.sequence),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.render("event-lineupcard", context);
|
||||||
|
});
|
||||||
|
};
|
||||||
9
src/controllers/member.js
Normal file
9
src/controllers/member.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
exports.getMembers = (req, res, next) => {
|
||||||
|
const {members, team} = req
|
||||||
|
context = {
|
||||||
|
title: `Roster`,
|
||||||
|
team_id: team.id,
|
||||||
|
team, members,
|
||||||
|
};
|
||||||
|
res.render("members", context);
|
||||||
|
};
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
utils = require("../lib/utils");
|
|
||||||
|
|
||||||
exports.getMembers = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap.bulkLoad(team_id, ["team", "member"]).then(() => {
|
|
||||||
items = teamsnap.getAllItems();
|
|
||||||
context = {
|
|
||||||
title: `Roster`,
|
|
||||||
team_id: team_id,
|
|
||||||
team: items.find((i) => i.type == "team" && i.id == team_id),
|
|
||||||
members: items.filter((i) => i.type == "member" && i.teamId == team_id),
|
|
||||||
};
|
|
||||||
res.set("Content-Type", "text/html");
|
|
||||||
res.render("members", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
109
src/controllers/opponent.js
Normal file
109
src/controllers/opponent.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
|
||||||
|
|
||||||
|
exports.getOpponents = async (req, res, next) => {
|
||||||
|
const {user, team, layout, csrfToken} = req
|
||||||
|
opponents = await teamsnap.loadOpponents(team.id, ["event", "availabilitySummary"], (err, opponents)=>{
|
||||||
|
if (err) console.log('err in controllers/opponent.js')
|
||||||
|
|
||||||
|
else return opponents;
|
||||||
|
})
|
||||||
|
context = {
|
||||||
|
title: "Opponents",
|
||||||
|
user, team, layout, csrfToken,
|
||||||
|
opponents: opponents,
|
||||||
|
};
|
||||||
|
res.render("opponent/list", context);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.uploadOpponentLogoForm = (req, res, next) => {
|
||||||
|
opponent_id = req.params.opponent_id;
|
||||||
|
team_id = req.params.team_id;
|
||||||
|
res.set("Content-Type", "text/html");
|
||||||
|
res.render("upload-logo", {
|
||||||
|
title: "Upload Logo",
|
||||||
|
csrf_token: req.csrfToken(),
|
||||||
|
team_id: team_id,
|
||||||
|
opponent_id: opponent_id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.uploadOpponentLogo = (req, res, next) => {
|
||||||
|
opponent_id = req.body.opponent_id;
|
||||||
|
team_id = req.body.team_id;
|
||||||
|
member_id = req.user.id;
|
||||||
|
file = new File(req.file.buffer, `team-logo-${opponent_id}.png`, {
|
||||||
|
type: "image/png",
|
||||||
|
});
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
teamsnap
|
||||||
|
.loadCollections()
|
||||||
|
.then(() => {
|
||||||
|
return teamsnap.createTeamMedium({
|
||||||
|
file: file,
|
||||||
|
mediaFormat: "file",
|
||||||
|
memberId: member_id,
|
||||||
|
teamId: team_id,
|
||||||
|
teamMediaGroupId: "4927028",
|
||||||
|
description: `team-logo-${opponent_id}.png`,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((item) => {
|
||||||
|
return teamsnap.uploadTeamMedium(item);
|
||||||
|
})
|
||||||
|
.then((item) => {
|
||||||
|
res.send("Data Received: " + JSON.stringify(item));
|
||||||
|
})
|
||||||
|
.fail((err) => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getOpponent = async (req, res) => {
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
const {team, team_media_group, opponent, layout, opponent_logo, user, csrfToken} = req
|
||||||
|
context = {
|
||||||
|
team, team_media_group, opponent, layout, opponent_logo, user, csrfToken
|
||||||
|
// opponent_logo: items.find(
|
||||||
|
// (i) => i.type == "teamMedium" && i.description == `opponent-logo-${opponent_id}.png`
|
||||||
|
// ),
|
||||||
|
};
|
||||||
|
res.set("Content-Type", "text/html");
|
||||||
|
res.render("opponent/show", context);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.postOpponentLogo = async (req, res, next) => {
|
||||||
|
res.status('501').send('Not Implemented')
|
||||||
|
// await Promise.all(req.promises)
|
||||||
|
// const {team, opponent, user, body} = req
|
||||||
|
// const filename = `team-logo-${opponent.id}.png`
|
||||||
|
// file = new File(req.file.buffer, filename, {
|
||||||
|
// type: "image/png",
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const team_medium = await teamsnap.createTeamMedium(
|
||||||
|
// {
|
||||||
|
// file: file,
|
||||||
|
// memberId: user.id,
|
||||||
|
// teamId: team.id,
|
||||||
|
// teamMediaGroupId: body.teamMediaGroupId,
|
||||||
|
// description: filename,
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// await teamsnap.saveTeamMedium(team_medium)
|
||||||
|
// // await teamsnap.uploadTeamMedium(team_medium)
|
||||||
|
|
||||||
|
// const headers={'Authorization': `Bearer ${user.accessToken}`}
|
||||||
|
// // const url = teamsnap.collections.teamMedia.commands.uploadTeamMedium.href
|
||||||
|
// const url = teamsnap.collections.teamMedia.queries.search.href
|
||||||
|
// const response = await fetch(url+`?team_id=${team.id}`, {
|
||||||
|
// headers,
|
||||||
|
// method: 'get',
|
||||||
|
// // body:{team_id:team.id}
|
||||||
|
// // body: {
|
||||||
|
// // file: file,
|
||||||
|
// // member_id: user.id,
|
||||||
|
// // team_id: team.id,
|
||||||
|
// // team_media_group_id: body.teamMediaGroupId,
|
||||||
|
// // description: filename,
|
||||||
|
// // }
|
||||||
|
// })
|
||||||
|
// // await teamsnap
|
||||||
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
exports.getOpponents = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap.bulkLoad(team_id, ["team", "opponent"]).then((items) => {
|
|
||||||
res.set("Content-Type", "text/html");
|
|
||||||
res.render("opponents", {
|
|
||||||
title: "Opponents",
|
|
||||||
team: items.find((i) => i.type == "team" && i.id == team_id),
|
|
||||||
opponents: items.filter((i) => i.type == "opponent"),
|
|
||||||
team_id: team_id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.uploadOpponentLogoForm = (req, res, next) => {
|
|
||||||
opponent_id = req.params.opponent_id;
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
res.set("Content-Type", "text/html");
|
|
||||||
res.render("upload-logo", {
|
|
||||||
title: "Upload Logo",
|
|
||||||
csrf_token: req.csrfToken(),
|
|
||||||
team_id: team_id,
|
|
||||||
opponent_id: opponent_id,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.uploadOpponentLogo = (req, res, next) => {
|
|
||||||
opponent_id = req.body.opponent_id;
|
|
||||||
team_id = req.body.team_id;
|
|
||||||
member_id = req.user.id;
|
|
||||||
file = new File(req.file.buffer, `team-logo-${opponent_id}.png`, {
|
|
||||||
type: "image/png",
|
|
||||||
});
|
|
||||||
authTeamsnap(req.user);
|
|
||||||
teamsnap
|
|
||||||
.loadCollections()
|
|
||||||
.then(() => {
|
|
||||||
return teamsnap.createTeamMedium({
|
|
||||||
file: file,
|
|
||||||
mediaFormat: "file",
|
|
||||||
memberId: member_id,
|
|
||||||
teamId: team_id,
|
|
||||||
teamMediaGroupId: "4927028",
|
|
||||||
description: `team-logo-${opponent_id}.png`,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then((item) => {
|
|
||||||
return teamsnap.uploadTeamMedium(item);
|
|
||||||
})
|
|
||||||
.then((item) => {
|
|
||||||
res.send("Data Received: " + JSON.stringify(item));
|
|
||||||
})
|
|
||||||
.fail((err) => console.log(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getOpponent = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
opponent_id = req.params.opponent_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap
|
|
||||||
.bulkLoad(team_id, ["team", "opponent"])
|
|
||||||
.then(() => {
|
|
||||||
teamsnap.loadTeamMedia(team_id);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
items = teamsnap.getAllItems();
|
|
||||||
context = {
|
|
||||||
team: items.find((i) => i.type == "team" && i.id == team_id),
|
|
||||||
opponent: items.find((i) => i.type == "opponent" && i.id == opponent_id),
|
|
||||||
opponent_logo: items.find(
|
|
||||||
(i) => i.type == "teamMedium" && i.description == `opponent-logo-${opponent_id}.png`
|
|
||||||
),
|
|
||||||
team_id: team_id,
|
|
||||||
};
|
|
||||||
res.set("Content-Type", "text/html");
|
|
||||||
res.render("opponent", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
42
src/controllers/team.js
Normal file
42
src/controllers/team.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const { teamsnapCallback } = require("../lib/utils");
|
||||||
|
|
||||||
|
utils = require("../lib/utils");
|
||||||
|
|
||||||
|
exports.getTeams = async (req, res, next) => {
|
||||||
|
const {layout} = req
|
||||||
|
const {user_id} = req.params
|
||||||
|
promise = teamsnap.loadTeams({'userId':user_id},
|
||||||
|
(err, items) =>{
|
||||||
|
teamsnapCallback(err,items);
|
||||||
|
req.teams = items;
|
||||||
|
})
|
||||||
|
.then(() => tsUtils.teamsnapLog('loadTeams', types=['teams'], null, req))
|
||||||
|
.fail(
|
||||||
|
next
|
||||||
|
)
|
||||||
|
req.promises.push(promise)
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
try {
|
||||||
|
const context = { layout, title: "Teams", teams: req.teams.filter(t=>!t.isRetired) };
|
||||||
|
res.render("team/list", context);
|
||||||
|
} catch (e){
|
||||||
|
next(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getTeamHome = async (req, res, next) => {
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
const {user, team, team_preferences, upcoming_events, recent_events, layout} = req
|
||||||
|
|
||||||
|
try {
|
||||||
|
context = {
|
||||||
|
title: "Home",
|
||||||
|
layout, team, user, team_preferences, upcoming_events, recent_events
|
||||||
|
};
|
||||||
|
res.render("team/home", context);
|
||||||
|
} catch (e) {
|
||||||
|
next (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
utils = require("../lib/utils");
|
|
||||||
|
|
||||||
exports.getTeams = (req, res, next) => {
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap.loadTeams().then((teams) => {
|
|
||||||
context = { title: "Teams", teams: teams };
|
|
||||||
res.render("teams", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getTeamHome = (req, res, next) => {
|
|
||||||
team_id = req.params.team_id;
|
|
||||||
utils.initTeamsnap(req, res, () => {
|
|
||||||
teamsnap.loadTeam(team_id).then((team) => {
|
|
||||||
context = { title: "Home", team: team };
|
|
||||||
res.render("team", context);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
134
src/helpers/eventsheet.js
Normal file
134
src/helpers/eventsheet.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
const { parsePositionLabel, teamsnapMembersSortLineupAvailabilityLastName, teamsnapMembersSortAvailabilityLastName } = require('../lib/utils')
|
||||||
|
const {attachBenchcoachPropertiesToMember} = require('../controllers/eventlineup')
|
||||||
|
|
||||||
|
exports.offenseLineup = (number_of_slots, event_lineup_entries, members, options) => {
|
||||||
|
var results = ""
|
||||||
|
// const {event_lineup_entries, members} = options.data.root
|
||||||
|
|
||||||
|
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')){
|
||||||
|
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}`),
|
||||||
|
label: event_lineup_entry.label
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
results += options.fn({
|
||||||
|
sequence: i,
|
||||||
|
member: {},
|
||||||
|
label: ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.defenseLineup = (event_lineup_entries, members, options) => {
|
||||||
|
var results = ""
|
||||||
|
// const {event_lineup_entries, members} = options.data.root
|
||||||
|
const positions = ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "P"]
|
||||||
|
positions.forEach(position=>{
|
||||||
|
const event_lineup_entry = event_lineup_entries ? event_lineup_entries.find(e=>parsePositionLabel(e.label).positionLabelWithoutFlags == position) : null
|
||||||
|
if (event_lineup_entry) {
|
||||||
|
results += options.fn({position, eventLineupEntry:event_lineup_entry, member:members.find(member=> event_lineup_entry.memberId == member.id || event_lineup_entry.memberName == `${member.firstName} ${member.lastName}`)})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
results += options.fn({position, member:{}})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.rosterHistoryHeader = (options) => {
|
||||||
|
var results = ""
|
||||||
|
events = ["+1", "+2", "+3", "+4","-1","-2","-3","-4"]
|
||||||
|
|
||||||
|
events.forEach(event => {
|
||||||
|
const class_name = event.includes("+") ? "plus": "minus"
|
||||||
|
const past_or_future = event.includes("+") ? "future": "past"
|
||||||
|
const index = Number(event.replace("+","").replace("-",""))
|
||||||
|
results += options.fn({class:`today-${class_name}-${index} ${past_or_future}`, event})
|
||||||
|
})
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.rosterHistory = (event, event_lineup_entries, members, availabilities, options) => {
|
||||||
|
var results = ""
|
||||||
|
// 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.forEach(member=>{
|
||||||
|
const {firstName, lastName, jerseyNumber, benchcoach, position, id} = member
|
||||||
|
results += options.fn({
|
||||||
|
id, firstName, lastName, jerseyNumber, position, benchcoach
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
const positionGroups = {
|
||||||
|
"P":"P",
|
||||||
|
"IF":"IF",
|
||||||
|
"1B":"IF",
|
||||||
|
"2B":"IF",
|
||||||
|
"3B":"IF",
|
||||||
|
"SS":"IF",
|
||||||
|
"OF":"OF",
|
||||||
|
"LF":"OF",
|
||||||
|
"CF":"OF",
|
||||||
|
"RF":"OF",
|
||||||
|
"C":"C"
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.positionCapabilityFor = (member, position, options) => {
|
||||||
|
const member_positions = member.position.split(",").map(s=>s.trim())
|
||||||
|
const member_position_groups = new Set(member.position.split(",").map(s=>positionGroups[s.trim()]))
|
||||||
|
|
||||||
|
if (member_position_groups.has(position)){
|
||||||
|
return "\u2713"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.firstLetter = (s, options) => {
|
||||||
|
return s[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.repeat = (n, options) => {
|
||||||
|
var results = "";
|
||||||
|
[...Array(n).keys()].forEach(i => {
|
||||||
|
results += options.fn({index: i})
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.loopEvents = (events, options) => {
|
||||||
|
var results = "";
|
||||||
|
events.forEach(event => {
|
||||||
|
results += options.fn(event)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.timepointForMember = (member, timeline, event, options) => {
|
||||||
|
var results = ""
|
||||||
|
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 = ""
|
||||||
|
if (eventLineupEntry){
|
||||||
|
value = parsePositionLabel(eventLineupEntry.label).positionLabelWithoutFlags
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = availability.status[0]
|
||||||
|
}
|
||||||
|
return options.fn({availability: availability, eventLineupEntry: eventLineupEntry, value})
|
||||||
|
}
|
||||||
174
src/lib/utils.js
174
src/lib/utils.js
@@ -1,27 +1,54 @@
|
|||||||
exports.teamsnapAvailabilitiesSort = (a, b) => {
|
const path = require('path')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
exports.teamsnapMembersSortLineupAvailabilityLastName = (a, b) => {
|
||||||
status_code_sort = [
|
status_code_sort = [
|
||||||
teamsnap.AVAILABILITIES.YES,
|
teamsnap.AVAILABILITIES.YES,
|
||||||
teamsnap.AVAILABILITIES.MAYBE,
|
teamsnap.AVAILABILITIES.MAYBE,
|
||||||
teamsnap.AVAILABILITIES.NO,
|
teamsnap.AVAILABILITIES.NO,
|
||||||
teamsnap.AVAILABILITIES.NONE,
|
teamsnap.AVAILABILITIES.NONE,
|
||||||
];
|
];
|
||||||
a_sort = status_code_sort.indexOf(a.statusCode);
|
|
||||||
b_sort = status_code_sort.indexOf(b.statusCode);
|
if (a.benchcoach.eventLineupEntry != null && b.benchcoach.eventLineupEntry != null){
|
||||||
if (a_sort > b_sort) {
|
return a.benchcoach.eventLineupEntry.sequence - b.benchcoach.eventLineupEntry.sequence
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
if (a_sort < b_sort) {
|
else if (a.benchcoach.eventLineupEntry != null && b.benchcoach.eventLineupEntry == null){
|
||||||
return -1;
|
return -1
|
||||||
}
|
}
|
||||||
if (a_sort == b_sort) {
|
else if (a.benchcoach.eventLineupEntry == null && b.benchcoach.eventLineupEntry != null) {
|
||||||
if (a.member.lastName < b.member.lastName) {
|
return 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return teamsnapMembersSortAvailabilityLastName(a,b)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
teamsnapMembersSortAvailabilityLastName = (a, b) => {
|
||||||
|
status_code_sort = [
|
||||||
|
teamsnap.AVAILABILITIES.YES,
|
||||||
|
teamsnap.AVAILABILITIES.MAYBE,
|
||||||
|
teamsnap.AVAILABILITIES.NO,
|
||||||
|
teamsnap.AVAILABILITIES.NONE,
|
||||||
|
];
|
||||||
|
|
||||||
|
a_sort = status_code_sort.indexOf(a.benchcoach.availability?.statusCode);
|
||||||
|
b_sort = status_code_sort.indexOf(b.benchcoach.availability?.statusCode);
|
||||||
|
if (a_sort > b_sort) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a_sort < b_sort) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (a.member.lastName > b.member.lastName) {
|
if (a_sort == b_sort) {
|
||||||
|
if (a.lastName < b.lastName) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.lastName > b.lastName) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
exports.teamsnapMembersSortAvailabilityLastName = teamsnapMembersSortAvailabilityLastName
|
||||||
|
|
||||||
exports.initTeamsnap = (req, res, next) => {
|
exports.initTeamsnap = (req, res, next) => {
|
||||||
if (!teamsnap.isAuthed()) {
|
if (!teamsnap.isAuthed()) {
|
||||||
@@ -33,3 +60,128 @@ exports.initTeamsnap = (req, res, next) => {
|
|||||||
next(req, res, next);
|
next(req, res, next);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.teamsnapLog = (method, types, id, req, message="") => {
|
||||||
|
console.log(
|
||||||
|
'\x1b[33mTeamSnap:\x1b[0m',
|
||||||
|
`${method} for \x1b[33m\[${types}\]\x1b[0m on ${id}`,
|
||||||
|
`on url ${req.url}`,
|
||||||
|
`"${message}"`
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.tsPromise = (func_name, params) => {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
teamsnap.bulkLoad(
|
||||||
|
params,
|
||||||
|
(err, data) => {
|
||||||
|
console.log();
|
||||||
|
if (err !== null) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.teamsnapCallback = (err,items) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err.message);
|
||||||
|
throw new Error(err)
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.teamsnapFailure = (err, next) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err.message);
|
||||||
|
}
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPluralType = (type) =>{
|
||||||
|
// There are some instances where a type is not
|
||||||
|
// in the list of teamsnap.types, so a plural
|
||||||
|
// is not generated in the lookup. this is a
|
||||||
|
// kludge around that. (specifically availabilitySummary)
|
||||||
|
plural = teamsnap.getPluralType(type) || (function() {
|
||||||
|
switch (type.slice(-1)) {
|
||||||
|
case 'y':
|
||||||
|
return type.slice(0, -1) + 'ies';
|
||||||
|
case 's':
|
||||||
|
return type + 'es';
|
||||||
|
default:
|
||||||
|
return type + 's';
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return plural
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.groupTeamsnapItems = (items, types = [], params = {}) => {
|
||||||
|
const result = {};
|
||||||
|
items.forEach(item => {
|
||||||
|
const type = item.type
|
||||||
|
const type_plural = getPluralType(type)
|
||||||
|
if ((types.length > 0 && types.includes(type)) || (types.length == 0)) {
|
||||||
|
if (!result[type_plural]) result[type_plural] = []
|
||||||
|
result[type_plural].push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.embeddedSvgFromPath = (svg_path, additional_classes = "") => {
|
||||||
|
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"),
|
||||||
|
"/media":path.join(__dirname, "/../public/media")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(iconStaticPaths)) {
|
||||||
|
if (svg_path.startsWith(key)) {
|
||||||
|
svg_path = svg_path.replace(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const svg = fs.readFileSync(`${svg_path}`, 'utf8');
|
||||||
|
|
||||||
|
svgRegExWithClass = new RegExp(/<svg(.*)class="(.*?)"(.*)>/)
|
||||||
|
svgRegExWithoutClass = new RegExp(/<svg(.*?)>/)
|
||||||
|
|
||||||
|
if (svgRegExWithClass.test(svg)) {
|
||||||
|
return svg.replace(svgRegExWithClass, `<svg$1 class="$2 Icon ${additional_classes}"$3>`)
|
||||||
|
}
|
||||||
|
else if (svgRegExWithoutClass.test(svg)) {
|
||||||
|
return svg.replace(svgRegExWithoutClass, `<svg$1 class="Icon ${additional_classes}">`)
|
||||||
|
}
|
||||||
|
else return svg
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.parsePositionLabel = (label) => {
|
||||||
|
if (label == null) {
|
||||||
|
return {
|
||||||
|
positionLabelWithoutFlags: null,
|
||||||
|
positionFlags: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pattern = /(?<pos>[A-Z0-9]+)(?:\s\[(?<flags>.[A-z,]+)\])?/g
|
||||||
|
const {pos, flags} = pattern.exec(label)?.groups || {}
|
||||||
|
const positionLabelWithoutFlags= pos
|
||||||
|
const positionFlags = flags?.split(',').map(f=>f.trim()) || []
|
||||||
|
return {positionLabelWithoutFlags, positionFlags}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.compilePositionLabel = (label, flags) => {
|
||||||
|
if (flags == null || flags == '' || flags.lengh == 0) {
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const flags_set = new Set(flags.split(',').map(s=>s.trim()))
|
||||||
|
return `${label} [${Array.from(flags_set).sort().join(',')}]`
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/middlewares/bulkload.js
Normal file
49
src/middlewares/bulkload.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
exports.loadRecentAndUpcomingEvents = async (req, res, next) => {
|
||||||
|
const {team_id, event_id} = req.params
|
||||||
|
var subject_date = ""
|
||||||
|
if (event_id) {
|
||||||
|
const event = await teamsnap.loadEvents({id: event_id}).pop()
|
||||||
|
subject_date = event.startDate.toISOString().slice(0,10)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
subject_date = new Date().toISOString().slice(0,10)
|
||||||
|
}
|
||||||
|
req.promises.push(
|
||||||
|
teamsnap.bulkLoad({
|
||||||
|
teamId: team_id,
|
||||||
|
types: ["event", "availabilitySummary"],
|
||||||
|
scopeTo: "event",
|
||||||
|
event__startedAfter: subject_date,
|
||||||
|
event__pageSize: 4
|
||||||
|
})
|
||||||
|
.then(items => tsUtils.groupTeamsnapItems(items))
|
||||||
|
.then((items)=>{
|
||||||
|
req.upcoming_events=items.events || [];
|
||||||
|
const availabilitySummaries=items.availabilitySummaries;
|
||||||
|
req.upcoming_events.forEach((event) => {
|
||||||
|
event.link('availabilitySummary', availabilitySummaries.find(a=>a.eventId==event.id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
).fail(utils.teamsnapFailure)
|
||||||
|
)
|
||||||
|
req.promises.push(
|
||||||
|
teamsnap.bulkLoad({
|
||||||
|
teamId: team_id,
|
||||||
|
types: ["event", "availabilitySummary"],
|
||||||
|
scopeTo: "event",
|
||||||
|
event__startedBefore: subject_date,
|
||||||
|
event__pageSize: 4,
|
||||||
|
event__sortStartDate: "desc"
|
||||||
|
})
|
||||||
|
.then(items => tsUtils.groupTeamsnapItems(items))
|
||||||
|
.then((items)=>{
|
||||||
|
req.recent_events=items.events || [];
|
||||||
|
const availabilitySummaries=items.availabilitySummaries;
|
||||||
|
req.recent_events.forEach((event) => {
|
||||||
|
event.link('availabilitySummary', availabilitySummaries.find(a=>a.eventId==event.id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
).fail(utils.teamsnapFailure)
|
||||||
|
)
|
||||||
|
next();
|
||||||
|
}
|
||||||
18
src/middlewares/csrf.js
Normal file
18
src/middlewares/csrf.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const { doubleCsrf } = require('csrf-csrf');
|
||||||
|
|
||||||
|
const csrf = doubleCsrf({
|
||||||
|
getSecret: () => process.env.CSRF_SECRET,
|
||||||
|
getTokenFromRequest: req => {
|
||||||
|
return req.body.csrfToken
|
||||||
|
},
|
||||||
|
cookieName: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'development' ? '__benchcoach.x-csrf-token' : '_csrf',
|
||||||
|
cookieOptions: {
|
||||||
|
secure: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'development' // Enable for HTTPS in production
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
doubleCsrfProtection: csrf.doubleCsrfProtection,
|
||||||
|
generateToken: csrf.generateToken
|
||||||
|
};
|
||||||
7143
src/public/css/application.css
Normal file
7143
src/public/css/application.css
Normal file
File diff suppressed because it is too large
Load Diff
1
src/public/css/application.css.map
Normal file
1
src/public/css/application.css.map
Normal file
File diff suppressed because one or more lines are too long
791
src/public/css/eventsheet.css
Normal file
791
src/public/css/eventsheet.css
Normal file
@@ -0,0 +1,791 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Pacifico");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Oswald");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Graduate");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Inconsolata:wdth,wght@50..200,200..900&display=swap");
|
||||||
|
@import url("/font/helvetica-now/stylesheet.css");
|
||||||
|
@font-face {
|
||||||
|
font-family: "Futura Now";
|
||||||
|
src: url("/font/futura-now/futura-now.ttf") format("truetype");
|
||||||
|
font-weight: 125 900;
|
||||||
|
font-stretch: 50% 150%;
|
||||||
|
}
|
||||||
|
@page {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
/** For Print **/
|
||||||
|
@media print {
|
||||||
|
body .sheet {
|
||||||
|
size: B5;
|
||||||
|
padding: 0.2in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** For screen preview **/
|
||||||
|
@media screen {
|
||||||
|
body .sheet {
|
||||||
|
padding: 0.2in;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.sheet {
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 0.5mm 2mm rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Paper sizes **/
|
||||||
|
body.B5 .sheet {
|
||||||
|
width: 176mm;
|
||||||
|
height: 250mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.index-card .sheet {
|
||||||
|
width: 3.5in;
|
||||||
|
height: 5in;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-success: #b7e1cd;
|
||||||
|
--color-danger: #f4c7c3;
|
||||||
|
--color-neutral: #acc9fe;
|
||||||
|
--color-warning: rgb(249, 228, 180);
|
||||||
|
--color-grey-100: #f8f9fa;
|
||||||
|
--color-grey-200: #e9ecef;
|
||||||
|
--color-grey-300: #dee2e6;
|
||||||
|
--color-grey-400: #ced4da;
|
||||||
|
--color-grey-500: #adb5bd;
|
||||||
|
--color-grey-600: #6c757d;
|
||||||
|
--color-grey-700: #495057;
|
||||||
|
--color-grey-800: #343a40;
|
||||||
|
--color-grey-900: #212529;
|
||||||
|
--row-height: 14px;
|
||||||
|
--monospace-font: "Inconsolata", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Helvetica Now", "Helvetica", sans-serif;
|
||||||
|
position: relative;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
position: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
border-collapse: collapse;
|
||||||
|
empty-cells: show;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
width: 100%;
|
||||||
|
border: 0.5px solid black;
|
||||||
|
display: inline-table;
|
||||||
|
}
|
||||||
|
table tr {
|
||||||
|
border-bottom-width: 0.5px;
|
||||||
|
border-color: grey;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
table tr:nth-child(odd) {
|
||||||
|
background-color: rgba(242, 242, 242, 0.85);
|
||||||
|
}
|
||||||
|
table tr:nth-child(even) {
|
||||||
|
background-color: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
table tr th {
|
||||||
|
font-stretch: extra-condensed;
|
||||||
|
width: 1em;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
table tr td.player-name {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: 75%;
|
||||||
|
}
|
||||||
|
table tr td.position, table tr td.jersey-number {
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
width: 2ch;
|
||||||
|
text-align: right;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
table tr th, table tr td {
|
||||||
|
border-left: 0.5px solid grey;
|
||||||
|
border-right: 0.5px solid grey;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 2px 0 2px;
|
||||||
|
}
|
||||||
|
table tr th:empty::after, table tr td:empty::after {
|
||||||
|
content: " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.B5 > .eventsheet {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
column-gap: 0.2in;
|
||||||
|
row-gap: 0.2in;
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventsheet > section {
|
||||||
|
box-sizing: content-box;
|
||||||
|
overflow: hidden;
|
||||||
|
/* outline: 0.5px dashed lightgrey; */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventsheet > section > div {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: stretch;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card th {
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-dugout div.grid-container,
|
||||||
|
#lineup-card-dugout-blank div.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 60% auto;
|
||||||
|
grid-template-rows: fit-content(16px) auto;
|
||||||
|
grid-template-areas: "header header" "sarting-lineup-table substitution-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange div.grid-container,
|
||||||
|
#lineup-card-exchange-blank div.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
grid-template-rows: fit-content(16px) auto;
|
||||||
|
grid-template-areas: "header" "sarting-lineup-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange .substitution,
|
||||||
|
#lineup-card-exchange .homeaway,
|
||||||
|
#lineup-card-exchange-blank .substitution,
|
||||||
|
#lineup-card-exchange-blank .homeaway {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card div.grid-container > header {
|
||||||
|
grid-area: "header";
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card div.grid-container > header:empty::after {
|
||||||
|
content: " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
div.grid-container > .starting-lineup-table {
|
||||||
|
grid-area: "starting-lineup-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
div.grid-container > .substitution-table {
|
||||||
|
grid-area: "substitution-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
section.lineup-card {
|
||||||
|
counter-reset: lineup-sequence-counter 0;
|
||||||
|
}
|
||||||
|
section.lineup-card thead th {
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
section.lineup-card th.sequence {
|
||||||
|
counter-increment: lineup-sequence-counter 1;
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
font-size: inherit;
|
||||||
|
width: 2ch;
|
||||||
|
}
|
||||||
|
section.lineup-card th.sequence.counter::before {
|
||||||
|
content: counter(lineup-sequence-counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card table {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card td {
|
||||||
|
/* height: 34px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card td.substitution {
|
||||||
|
width: 8ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card td.substitution::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange tr,
|
||||||
|
#lineup-card-exchange-blank tr,
|
||||||
|
#lineup-card-dugout .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout-blank .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout .substitution-table tr:nth-child(odd) {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange tr,
|
||||||
|
#lineup-card-exchange-blank tr,
|
||||||
|
#lineup-card-dugout .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout-blank .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout .substitution-table tr:nth-child(even) {
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.substitution {
|
||||||
|
border-left: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange td.player-name {
|
||||||
|
font-stretch: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-dugout td.player-name {
|
||||||
|
width: 10ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card .position,
|
||||||
|
.lineup-card .jersey-number {
|
||||||
|
width: 2ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-dugout .position,
|
||||||
|
#lineup-card-dugout .jersey-number {
|
||||||
|
font-stretch: 75%;
|
||||||
|
padding-left: 2.5px;
|
||||||
|
padding-right: 2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card header {
|
||||||
|
font-size: inherit;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#todays-game > div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 110px auto;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
grid-template-areas: "offense defense" "footer footer";
|
||||||
|
}
|
||||||
|
#todays-game #offense-pane {
|
||||||
|
grid-area: offense;
|
||||||
|
}
|
||||||
|
#todays-game #defense-pane {
|
||||||
|
grid-area: defense;
|
||||||
|
}
|
||||||
|
#todays-game .footer {
|
||||||
|
/* height:var(--row-height); */
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
grid-area: footer;
|
||||||
|
/* border: 1px solid black; */
|
||||||
|
height: 100%;
|
||||||
|
border-right: 0.5px solid grey;
|
||||||
|
border-left: 0.5px solid grey;
|
||||||
|
}
|
||||||
|
#todays-game .footer table {
|
||||||
|
height: 100%;
|
||||||
|
outline: none;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
#todays-game .footer table tr {
|
||||||
|
background-color: white;
|
||||||
|
outline: none;
|
||||||
|
border-bottom: 0.5px solid var(--color-grey-500);
|
||||||
|
}
|
||||||
|
#todays-game .footer table tr :last-child {
|
||||||
|
background-color: white;
|
||||||
|
outline: none;
|
||||||
|
border-bottom-style: none;
|
||||||
|
}
|
||||||
|
#todays-game .footer table th {
|
||||||
|
text-align: left;
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
}
|
||||||
|
#todays-game .footer table td {
|
||||||
|
height: var(--row-height);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
#todays-game .footer table tdempty::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
#todays-game table.notes th {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
#todays-game table.notes td:empty::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#defense-card #defense-pane {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#defense-card .footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#defense-card .slot-set table {
|
||||||
|
font-size: 14px;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
#defense-card .slot-set.pos-c {
|
||||||
|
grid-area: 6/1/7/5 !important;
|
||||||
|
}
|
||||||
|
#defense-card .slot-set.pos-p {
|
||||||
|
justify-content: center !important;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: inherit !important;
|
||||||
|
grid-area: 5/1/6/5 !important;
|
||||||
|
}
|
||||||
|
#defense-card .slot-set.pos-p table {
|
||||||
|
width: 120px !important;
|
||||||
|
}
|
||||||
|
#defense-card .slot-set.pos-p table tbody > tr:last-child {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
#defense-pane {
|
||||||
|
position: relative;
|
||||||
|
grid-area: defense;
|
||||||
|
padding: 4px 4px 0px 4px; /* top right bottom left */
|
||||||
|
display: flex;
|
||||||
|
border-right: 0.5px solid grey;
|
||||||
|
border-bottom: 0.5px solid grey;
|
||||||
|
}
|
||||||
|
#defense-pane .field-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
grid-template-rows: repeat(6, 1fr);
|
||||||
|
grid-column-gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
#defense-pane img {
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-cf tr:first-child th.position:empty::after {
|
||||||
|
content: "cf";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-lf tr:first-child th.position:empty::after {
|
||||||
|
content: "lf";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-rf tr:first-child th.position:empty::after {
|
||||||
|
content: "rf";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-ss tr:first-child th.position:empty::after {
|
||||||
|
content: "ss";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-2b tr:first-child th.position:empty::after {
|
||||||
|
content: "2b";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-3b tr:first-child th.position:empty::after {
|
||||||
|
content: "3b";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-1b tr:first-child th.position:empty::after {
|
||||||
|
content: "1b";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-c tr:first-child th.position:empty::after {
|
||||||
|
content: "c";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-p tr:first-child th.position:empty::after {
|
||||||
|
content: "p";
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-cf {
|
||||||
|
justify-content: center;
|
||||||
|
grid-area: 1/1/2/5;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-lf {
|
||||||
|
justify-content: flex-start;
|
||||||
|
grid-area: 2/1/3/3;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-rf {
|
||||||
|
justify-content: flex-end;
|
||||||
|
grid-area: 2/3/3/5;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-ss {
|
||||||
|
justify-content: flex-end;
|
||||||
|
grid-area: 3/1/4/3;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-2b {
|
||||||
|
justify-content: flex-start;
|
||||||
|
grid-area: 3/3/4/5;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-3b {
|
||||||
|
justify-content: flex-start;
|
||||||
|
grid-area: 4/1/5/3;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-1b {
|
||||||
|
justify-content: flex-end;
|
||||||
|
grid-area: 4/3/5/5;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-c {
|
||||||
|
justify-content: center;
|
||||||
|
grid-area: 5/1/6/5;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-p {
|
||||||
|
align-items: end;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
grid-area: 6/1/7/5;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-p table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#defense-pane .slot-set.pos-p tr.substitute .position:empty::after {
|
||||||
|
content: "RP";
|
||||||
|
}
|
||||||
|
|
||||||
|
#offense-pane {
|
||||||
|
position: relative;
|
||||||
|
/* box-sizing: border-box; */
|
||||||
|
height: 100%;
|
||||||
|
border-bottom: 0.5px solid black;
|
||||||
|
counter-reset: lineup-sequence-counter 0;
|
||||||
|
/* outline: 0.5px solid black; */
|
||||||
|
}
|
||||||
|
#offense-pane table {
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
#offense-pane th.sequence {
|
||||||
|
counter-increment: lineup-sequence-counter 1;
|
||||||
|
}
|
||||||
|
#offense-pane th.sequence.counter::before {
|
||||||
|
content: counter(lineup-sequence-counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot-set table {
|
||||||
|
width: 77px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot-set.pos-slot-p table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-card .defense-slot-set {
|
||||||
|
width: 65px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-card .defense-slot-set .player-name {
|
||||||
|
font-stretch: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container .row {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #cadcf9;
|
||||||
|
font-family: "Oswald";
|
||||||
|
/* font-size: 8.8px; */
|
||||||
|
/* outline: 1px solid black; */
|
||||||
|
/* height: var(--row-height); */
|
||||||
|
width: auto;
|
||||||
|
text-align: center;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
border: 0.5px solid black;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-checkbox {
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-starting-lineup {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gametitle {
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: semi-condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homeaway {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-weight: bolder;
|
||||||
|
float: right;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-smalltext {
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statscell {
|
||||||
|
font-family: "m+1m";
|
||||||
|
text-align: center;
|
||||||
|
font-stretch: extra-condensed;
|
||||||
|
font-size: 9px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condensedNameCell {
|
||||||
|
width: 70px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-square {
|
||||||
|
height: var(--row-height);
|
||||||
|
width: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-square.narrow {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-mono {
|
||||||
|
font-family: "m+1m";
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-condensed {
|
||||||
|
font-stretch: condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-status-code-1 {
|
||||||
|
color: rgb(0, 85, 0);
|
||||||
|
background-color: #b7e1cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-status-code-0 {
|
||||||
|
color: rgb(170, 0, 0);
|
||||||
|
background-color: #f4c7c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.past.available-status-code-0,
|
||||||
|
.past.available-status-code-null {
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.past.available-status-code-1 {
|
||||||
|
color: inherit;
|
||||||
|
background-color: var(--color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.past.available-status-code-1.started {
|
||||||
|
color: inherit;
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-status-code-2 {
|
||||||
|
color: blue;
|
||||||
|
background-color: #acc9fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
#roster-and-history .player-name {
|
||||||
|
font-stretch: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.starting {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#roster-and-history > div > table {
|
||||||
|
/* font-size: 10.5px; */
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1em;
|
||||||
|
/* outline: 0.5px black; */
|
||||||
|
}
|
||||||
|
#roster-and-history td, #roster-and-history th {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
padding: 0.2em 0.1em 0.2em 0.1em; /* top right bottom left */
|
||||||
|
}
|
||||||
|
#roster-and-history td.availability-on-day, #roster-and-history th.availability-on-day {
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
font-stretch: 60%;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 0.8em;
|
||||||
|
min-width: 0.8em;
|
||||||
|
}
|
||||||
|
#roster-and-history td.availability-on-day.future, #roster-and-history td.availability-on-day.past, #roster-and-history th.availability-on-day.future, #roster-and-history th.availability-on-day.past {
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
#roster-and-history td.position-capability, #roster-and-history th.position-capability {
|
||||||
|
font-size: 8px;
|
||||||
|
font-stretch: 50%;
|
||||||
|
width: 5px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#roster-and-history td.player-stats, #roster-and-history th.player-stats {
|
||||||
|
display: none;
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
font-size: 1em;
|
||||||
|
font-stretch: 60%;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
#roster-and-history td.player-stats .delimiter,
|
||||||
|
#roster-and-history td.player-stats .decimal-point, #roster-and-history th.player-stats .delimiter,
|
||||||
|
#roster-and-history th.player-stats .decimal-point {
|
||||||
|
font-family: Helvetica Now;
|
||||||
|
font-stretch: expanded;
|
||||||
|
color: var(--color-grey-500);
|
||||||
|
}
|
||||||
|
#roster-and-history td.player-stats .decimal-point, #roster-and-history th.player-stats .decimal-point {
|
||||||
|
color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
#roster-and-history td.player-stats .delimiter, #roster-and-history th.player-stats .delimiter {
|
||||||
|
color: var(--color-grey-500);
|
||||||
|
}
|
||||||
|
#roster-and-history td.player-name {
|
||||||
|
color: black;
|
||||||
|
text-align: left;
|
||||||
|
font-stretch: 95%;
|
||||||
|
}
|
||||||
|
#roster-and-history td.jersey-number {
|
||||||
|
color: black;
|
||||||
|
border-left: 0.5px solid lightgrey;
|
||||||
|
}
|
||||||
|
#roster-and-history colgroup {
|
||||||
|
border-left-width: 1px;
|
||||||
|
border-left-style: solid;
|
||||||
|
border-left-color: black;
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-style: solid;
|
||||||
|
border-right-color: black;
|
||||||
|
}
|
||||||
|
#roster-and-history col.player-stats {
|
||||||
|
border: inherit;
|
||||||
|
}
|
||||||
|
#roster-and-history th {
|
||||||
|
background-color: #cadcf9;
|
||||||
|
color: black;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
#roster-and-history th.availability-on-day div {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
#roster-and-history thead > tr, #roster-and-history tfoot > tr {
|
||||||
|
border-bottom: solid black 1px;
|
||||||
|
}
|
||||||
|
#roster-and-history tbody {
|
||||||
|
border-bottom: solid black 1px;
|
||||||
|
}
|
||||||
|
#roster-and-history tr.border-top {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.position-capability:not(:empty) {
|
||||||
|
color: var(--color-grey-700);
|
||||||
|
background-color: var(--color-grey-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.is-present-checkbox {
|
||||||
|
font-size: 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
/* text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000,
|
||||||
|
1px 1px 0 #000; */
|
||||||
|
}
|
||||||
|
|
||||||
|
td.is-present-checkbox.available-status-code-0 > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.is-present-checkbox.available-status-code-None > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-number .label {
|
||||||
|
font-size: xx-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-number .label, .game-number .value {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#front-cover {
|
||||||
|
border: solid 1px black;
|
||||||
|
}
|
||||||
|
#front-cover Header {
|
||||||
|
font-family: "Helvetica Now";
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.5em;
|
||||||
|
background-color: #323669;
|
||||||
|
color: white;
|
||||||
|
display: inline-flex;
|
||||||
|
border: none;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
#front-cover .game-number, #front-cover .homeaway {
|
||||||
|
display: grid;
|
||||||
|
font-size: xx-large;
|
||||||
|
align-content: center;
|
||||||
|
width: 1.4em;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
#front-cover .title {
|
||||||
|
display: grid;
|
||||||
|
font-size: small;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
#front-cover .head-to-head {
|
||||||
|
padding: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
#front-cover .opponent, #front-cover .team {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: x-large;
|
||||||
|
align-items: center;
|
||||||
|
font-family: "Pacifico";
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
#front-cover .opponent img, #front-cover .team img {
|
||||||
|
height: 115px;
|
||||||
|
}
|
||||||
|
#front-cover .opponent div:has(.name), #front-cover .team div:has(.name) {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
#front-cover .opponent name {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
#front-cover .team name {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=eventsheet.css.map */
|
||||||
1
src/public/css/eventsheet.css.map
Normal file
1
src/public/css/eventsheet.css.map
Normal file
@@ -0,0 +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"}
|
||||||
@@ -1,661 +0,0 @@
|
|||||||
@import url("../css/paper.css");
|
|
||||||
@import url("../fonts/vera/bitstreamvera.css");
|
|
||||||
@import url("../fonts/verdana/verdanapro.css");
|
|
||||||
@import url("../fonts/m+1m/m+1m.css");
|
|
||||||
@import url("../fonts/helvetica-now/stylesheet.css");
|
|
||||||
@import url("../fonts/futura-now/stylesheet.css");
|
|
||||||
@import url("../fonts/inconsolata/stylesheet.css");
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--color-success: #b7e1cd;
|
|
||||||
--color-danger: #f4c7c3;
|
|
||||||
--color-neutral: #acc9fe;
|
|
||||||
--color-warning: rgb(249, 228, 180);
|
|
||||||
--color-grey-100: #f8f9fa;
|
|
||||||
--color-grey-200: #e9ecef;
|
|
||||||
--color-grey-300: #dee2e6;
|
|
||||||
--color-grey-400: #ced4da;
|
|
||||||
--color-grey-500: #adb5bd;
|
|
||||||
--color-grey-600: #6c757d;
|
|
||||||
--color-grey-700: #495057;
|
|
||||||
--color-grey-800: #343a40;
|
|
||||||
--color-grey-900: #212529;
|
|
||||||
--row-height: 14px;
|
|
||||||
--monospace-font: "Inconsolata";
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Helvetica Now";
|
|
||||||
position: relative;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
position: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
border-collapse: collapse;
|
|
||||||
empty-cells: show;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: hidden;
|
|
||||||
width: 100%;
|
|
||||||
border: 0.5px solid black;
|
|
||||||
display: inline-table;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-left {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
/* vertical-align: middle; */
|
|
||||||
/* line-height: 1.3em; */
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 0 2px 0 2px; /* top right bottom left */
|
|
||||||
}
|
|
||||||
|
|
||||||
tr {
|
|
||||||
border-bottom-width: 0.5px;
|
|
||||||
border-color: grey;
|
|
||||||
border-bottom-style: solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:last-child {
|
|
||||||
border-bottom-color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:first-child {
|
|
||||||
border-top-color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(odd) {
|
|
||||||
background-color: rgb(242, 242, 242, 0.85);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(even) {
|
|
||||||
background-color: rgb(256, 256, 256, 0.85);
|
|
||||||
}
|
|
||||||
|
|
||||||
td:not(:first-child) {
|
|
||||||
border-left: 0.5px solid grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
font-stretch: extra-condensed;
|
|
||||||
width: 1em;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:empty::after,
|
|
||||||
th:empty::after {
|
|
||||||
content: "\00a0";
|
|
||||||
}
|
|
||||||
|
|
||||||
td.player-name {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-stretch: 80%;
|
|
||||||
/* font-family: var(--monospace-font); */
|
|
||||||
}
|
|
||||||
|
|
||||||
td.position,
|
|
||||||
td.jersey-number {
|
|
||||||
font-family: var(--monospace-font);
|
|
||||||
width: 2ch;
|
|
||||||
text-align: right;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index-card .gamecard {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.B5 > .gamecard {
|
|
||||||
/* height: auto; */
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
grid-template-rows: 1fr 1fr;
|
|
||||||
/* padding: 0.15in; */
|
|
||||||
/* grid-gap: 0.25in 0.15in; */
|
|
||||||
|
|
||||||
/* background-image: url("../2023-G08-0523.png"); */
|
|
||||||
background-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gamecard > section {
|
|
||||||
/* margin: 0.07in; */
|
|
||||||
box-sizing: content-box;
|
|
||||||
/* border: 4px solid var(--color-grey-200); */
|
|
||||||
/* border-radius: 4px; */
|
|
||||||
overflow: hidden;
|
|
||||||
outline: 0.5px dashed lightgrey;
|
|
||||||
/* border-right: 1px dotted black; */
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gamecard > section > div {
|
|
||||||
margin: 0.15in;
|
|
||||||
outline: 0.5px solid black;
|
|
||||||
display: flex;
|
|
||||||
flex: 1; /* consumes all free space (taking full height) */
|
|
||||||
align-items: stretch;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-dugout div.grid-container,
|
|
||||||
#lineup-card-dugout-empty div.grid-container {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 60% auto;
|
|
||||||
grid-template-rows: fit-content(16px) auto;
|
|
||||||
grid-template-areas:
|
|
||||||
"header header"
|
|
||||||
"sarting-lineup-table substitution-table";
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-exchange div.grid-container,
|
|
||||||
#lineup-card-exchange-empty div.grid-container {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto;
|
|
||||||
grid-template-rows: fit-content(16px) auto;
|
|
||||||
grid-template-areas:
|
|
||||||
"header"
|
|
||||||
"sarting-lineup-table";
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card div.grid-container > .section-header {
|
|
||||||
grid-area: "header";
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card div.grid-container > .section-header:empty::after {
|
|
||||||
content: "\00a0";
|
|
||||||
}
|
|
||||||
|
|
||||||
div.grid-container > .starting-lineup-table {
|
|
||||||
grid-area: "starting-lineup-table";
|
|
||||||
}
|
|
||||||
|
|
||||||
div.grid-container > .substitution-table {
|
|
||||||
grid-area: "substitution-table";
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card thead th {
|
|
||||||
color: var(--color-grey-600);
|
|
||||||
font-size: 0.7em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card th.sequence {
|
|
||||||
color: var(--color-grey-600);
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card table {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card td {
|
|
||||||
height: 33.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-exchange tr,
|
|
||||||
#lineup-card-exchange-empty tr,
|
|
||||||
#lineup-card-dugout .starting-lineup-table tr,
|
|
||||||
#lineup-card-dugout .substitution-table tr:nth-child(odd) {
|
|
||||||
border-top: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-exchange tr,
|
|
||||||
#lineup-card-exchange-empty tr,
|
|
||||||
#lineup-card-dugout .starting-lineup-table tr,
|
|
||||||
#lineup-card-dugout .substitution-table tr:nth-child(even) {
|
|
||||||
border-bottom: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-exchange td.player-name {
|
|
||||||
font-stretch: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-dugout td.player-name {
|
|
||||||
width: 10ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-dugout td.substitution,
|
|
||||||
#lineup-card-dugout-empty td.substitution {
|
|
||||||
font-size: 11px;
|
|
||||||
height: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card .position,
|
|
||||||
.lineup-card .jersey-number {
|
|
||||||
width: 2ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lineup-card-dugout .position,
|
|
||||||
#lineup-card-dugout .jersey-number {
|
|
||||||
font-stretch: 75%;
|
|
||||||
padding-left: 2.5px;
|
|
||||||
padding-right: 2.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineup-card .section-header {
|
|
||||||
padding-left: 1px;
|
|
||||||
padding-right: 1px;
|
|
||||||
font-size: inherit;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-stretch: 85%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#todays-game > div {
|
|
||||||
display: grid;
|
|
||||||
/* gap: 0.5px; */
|
|
||||||
grid-template-columns: 110px auto;
|
|
||||||
grid-template-rows: calc(var(--row-height) * 1) auto auto;
|
|
||||||
grid-template-areas:
|
|
||||||
"header header"
|
|
||||||
"offense defense"
|
|
||||||
"footer footer";
|
|
||||||
}
|
|
||||||
|
|
||||||
#defense-pane {
|
|
||||||
position: relative;
|
|
||||||
/* box-sizing: border-box; */
|
|
||||||
padding: 4px 4px 0px 4px; /* top right bottom left */
|
|
||||||
display: flex;
|
|
||||||
grid-area: defense;
|
|
||||||
border-left: 0.5px solid black;
|
|
||||||
border-bottom: 0.5px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#offense-pane {
|
|
||||||
position: relative;
|
|
||||||
/* box-sizing: border-box; */
|
|
||||||
height: 100%;
|
|
||||||
grid-area: offense;
|
|
||||||
border-bottom: 0.5px solid black;
|
|
||||||
/* outline: 0.5px solid black; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#offense-pane table {
|
|
||||||
height: 100%;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#defense-pane .container {
|
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#defense-pane .pitching-container {
|
|
||||||
margin: auto 0 0 0; /* top right bottom left */
|
|
||||||
}
|
|
||||||
|
|
||||||
#defense-pane .field-container {
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: auto;
|
|
||||||
/* margin: top; */
|
|
||||||
width: 100%;
|
|
||||||
/* background: url("../baseball-diamond.svg"); */
|
|
||||||
background-size: 100%;
|
|
||||||
background-position: center 10px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
gap: 6px;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#defense-pane img {
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.defense-slot-set {
|
|
||||||
width: 77px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index-card .defense-slot-set {
|
|
||||||
width: 65px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index-card .defense-slot-set .player-name {
|
|
||||||
font-stretch: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pitching-container .defense-slot-set {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container .row {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
background-color: #cadcf9;
|
|
||||||
font-size: 8.8px;
|
|
||||||
/* outline: 1px solid black; */
|
|
||||||
/* height: var(--row-height); */
|
|
||||||
width: auto;
|
|
||||||
grid-area: header;
|
|
||||||
text-align: center;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
border-bottom: 0.5px solid black;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
/* height:var(--row-height); */
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
grid-area: footer;
|
|
||||||
/* border: 1px solid black; */
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer table {
|
|
||||||
height: 100%;
|
|
||||||
outline: none;
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer tr {
|
|
||||||
background-color: white;
|
|
||||||
outline: none;
|
|
||||||
border-bottom: 0.5px solid var(--color-grey-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer tr :last-child {
|
|
||||||
background-color: white;
|
|
||||||
outline: none;
|
|
||||||
border-bottom-style: none;
|
|
||||||
}
|
|
||||||
.footer th {
|
|
||||||
text-align: left;
|
|
||||||
color: var(--color-grey-600);
|
|
||||||
}
|
|
||||||
.footer td {
|
|
||||||
height: var(--row-height);
|
|
||||||
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer td:empty::after {
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-checkbox {
|
|
||||||
font-size: 0.75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.in-starting-lineup {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gametitle {
|
|
||||||
font-weight: bold;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-stretch: semi-condensed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.homeaway {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-stretch: normal;
|
|
||||||
font-weight: bolder;
|
|
||||||
float: right;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-smalltext {
|
|
||||||
font-stretch: condensed;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statscell {
|
|
||||||
font-family: "m+1m";
|
|
||||||
text-align: center;
|
|
||||||
font-stretch: extra-condensed;
|
|
||||||
font-size: 9px;
|
|
||||||
width: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.condensedNameCell {
|
|
||||||
width: 70px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-stretch: condensed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-square {
|
|
||||||
height: var(--row-height);
|
|
||||||
width: 14px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-square.narrow {
|
|
||||||
width: 10px;
|
|
||||||
}
|
|
||||||
.cell-mono {
|
|
||||||
font-family: "m+1m";
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-condensed {
|
|
||||||
font-stretch: condensed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.available-status-code-1 {
|
|
||||||
color: rgb(0, 85, 0);
|
|
||||||
background-color: #b7e1cd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.available-status-code-0 {
|
|
||||||
color: rgb(170, 0, 0);
|
|
||||||
background-color: #f4c7c3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.past.available-status-code-0,
|
|
||||||
.past.available-status-code-null {
|
|
||||||
color: var(--color-grey-600);
|
|
||||||
background-color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.past.available-status-code-1 {
|
|
||||||
color: inherit;
|
|
||||||
background-color: var(--color-warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
.past.available-status-code-1.started {
|
|
||||||
color: inherit;
|
|
||||||
background-color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.available-status-code-2 {
|
|
||||||
color: blue;
|
|
||||||
background-color: #acc9fe;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history .player-name,
|
|
||||||
#roster-and-history .jersey-number {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history .player-name {
|
|
||||||
font-stretch: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.starting {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history > div > table {
|
|
||||||
/* font-size: 10.5px; */
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1em;
|
|
||||||
/* outline: 0.5px black; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history td,
|
|
||||||
#roster-and-history th {
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
padding: 0.2em 0.1em 0.2em 0.1em; /* top right bottom left */
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history td.player-name {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history th {
|
|
||||||
background-color: #cadcf9;
|
|
||||||
color: black;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history thead > tr,
|
|
||||||
#roster-and-history tfoot > tr {
|
|
||||||
border-bottom: solid black 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history tbody {
|
|
||||||
border-bottom: solid black 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history td[id^="avail"][id$="today-plus-1"],
|
|
||||||
#roster-and-history .pitcher,
|
|
||||||
#roster-and-history .player-stats,
|
|
||||||
#roster-and-history td[id^="avail"][id$="today-minus-1"] {
|
|
||||||
border-left-width: 1px;
|
|
||||||
border-left-style: solid;
|
|
||||||
border-left-color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history td.jersey-number {
|
|
||||||
border-left: 0.5px solid lightgrey;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history td.today-minus-4 {
|
|
||||||
border-right: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history tr.border-top {
|
|
||||||
border-top: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history #today-availability {
|
|
||||||
font-stretch: normal;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.player-stats {
|
|
||||||
font-family: var(--monospace-font);
|
|
||||||
font-size: 1em;
|
|
||||||
font-stretch: 60%;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history td.position-capability,
|
|
||||||
th.position-capability {
|
|
||||||
font-size: 8px;
|
|
||||||
font-stretch: 50%;
|
|
||||||
width: 5px;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-and-history th.position-capability {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.position-capability:not(:empty) {
|
|
||||||
color: var(--color-grey-700);
|
|
||||||
background-color: var(--color-grey-200);
|
|
||||||
}
|
|
||||||
|
|
||||||
td.is-present-checkbox {
|
|
||||||
font-size: 0.5em;
|
|
||||||
text-align: center;
|
|
||||||
color: white;
|
|
||||||
/* text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000,
|
|
||||||
1px 1px 0 #000; */
|
|
||||||
}
|
|
||||||
|
|
||||||
td.is-present-checkbox.available-status-code-0 > span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.is-present-checkbox.available-status-code-None > span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.availability {
|
|
||||||
font-family: var(--monospace-font);
|
|
||||||
font-stretch: condensed;
|
|
||||||
text-align: center;
|
|
||||||
max-width: 0.8em;
|
|
||||||
min-width: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.availability.future,
|
|
||||||
.availability.past {
|
|
||||||
font-family: var(--monospace-font);
|
|
||||||
font-stretch: condensed;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 0.8em;
|
|
||||||
padding: 0.1em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-test .player-name {
|
|
||||||
font-weight: bold;
|
|
||||||
text-transform: uppercase;
|
|
||||||
grid-area: player-name;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-test .jersey-number {
|
|
||||||
font-weight: bolder;
|
|
||||||
font-stretch: extra-condensed;
|
|
||||||
grid-area: jersey-number;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-test .player-stats {
|
|
||||||
grid-area: player-stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
#roster-test .diamond {
|
|
||||||
grid-area: diamond;
|
|
||||||
overflow: hidden;
|
|
||||||
display: inline-block;
|
|
||||||
height: 10px;
|
|
||||||
/* height:10px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotate {
|
|
||||||
transform: rotate(270deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.delimiter,
|
|
||||||
.decimal-point {
|
|
||||||
font-family: Helvetica Now;
|
|
||||||
font-stretch: expanded;
|
|
||||||
color: var(--color-grey-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
th .decimal-point {
|
|
||||||
color: rgb(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
th .delimiter {
|
|
||||||
color: var(--color-grey-500);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,31 @@
|
|||||||
/* These styles are generated from project.scss. */
|
/* These styles are generated from project.scss. */
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
|
||||||
@import url("../fonts/helvetica-now/stylesheet.css");
|
@import url("../fonts/helvetica-now/stylesheet.css");
|
||||||
|
header.Header {
|
||||||
|
background: #323669;
|
||||||
|
padding: 8px 0;
|
||||||
|
box-shadow: 0 4px 0 rgba(0, 0, 25, 0.1);
|
||||||
|
border-bottom: 1px solid #d6d6d6;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.Header-bannerLogo, .Header-bannerTitle {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Header-bannerLogo img {
|
||||||
|
height: 36px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Header-bannerTitle {
|
||||||
|
font-family: "Helvetica", sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
color: white;
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
.alert-debug {
|
.alert-debug {
|
||||||
color: black;
|
color: black;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|||||||
565
src/public/js/eventlineup.js
Normal file
565
src/public/js/eventlineup.js
Normal file
@@ -0,0 +1,565 @@
|
|||||||
|
/* Project specific Javascript goes here. */
|
||||||
|
function onPositionSelectChange(elem) {
|
||||||
|
elem.querySelectorAll("option").forEach((option) => {
|
||||||
|
if (option.innerText.trim() == elem.value) {
|
||||||
|
option.setAttribute("selected", "selected");
|
||||||
|
} else {
|
||||||
|
option.removeAttribute("selected");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
colorPositions();
|
||||||
|
refreshLineup();
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePopup(el) {
|
||||||
|
el.querySelector(".Popup-container").classList.toggle("is-open");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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");
|
||||||
|
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')){
|
||||||
|
const flags = new Set(slot.querySelector("input[name*=flags]").value.split(',').map(s=>s.trim()))
|
||||||
|
flags.add('PO');flags.delete('')
|
||||||
|
slot.querySelector("input[name*=flags]").value = Array.from(flags).join(",");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const flags = new Set(slot.querySelector("input[name*=flags]").value.split(',').map(s=>s.trim()))
|
||||||
|
flags.delete('PO');flags.delete('')
|
||||||
|
slot.querySelector("input[name*=flags]").value = Array.from(flags).join(",");
|
||||||
|
}
|
||||||
|
if (slot.closest('.bench')){
|
||||||
|
slot.querySelector("input[name*=sequence]").value = '';
|
||||||
|
slot.querySelector("input[name*=label]").value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (bcLineup of document.querySelectorAll("[id^=event-lineup]")) {
|
||||||
|
options = {
|
||||||
|
animation: 150,
|
||||||
|
handle: ".Panel-cell:has(.drag-handle), .Panel-cell:has(.sequence)",
|
||||||
|
ghostClass: "ghost",
|
||||||
|
group: {
|
||||||
|
name: bcLineup.id,
|
||||||
|
put: [bcLineup.id],
|
||||||
|
pull: [bcLineup.id],
|
||||||
|
},
|
||||||
|
onAdd: function (/**Event*/ evt) {
|
||||||
|
console.log("added to lineup");
|
||||||
|
// Add to Lineup
|
||||||
|
var itemEl = evt.item; // dragged HTMLElement
|
||||||
|
|
||||||
|
refreshLineup();
|
||||||
|
},
|
||||||
|
onUpdate: function (/**Event*/ evt) {
|
||||||
|
console.log("update to lineup");
|
||||||
|
// var itemEl = evt.item; // dragged HTMLElement
|
||||||
|
// refresh_lineup_order(itemEl);
|
||||||
|
refreshLineup();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
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:[]}});
|
||||||
|
}
|
||||||
|
|
||||||
|
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) ')
|
||||||
|
Array.from(cells).forEach(cell=>{
|
||||||
|
cell.classList.add('u-hidden')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
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(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 emailModal(el, url) {
|
||||||
|
form = el.closest("form");
|
||||||
|
data = new FormData(form);
|
||||||
|
|
||||||
|
email_modal = document.querySelector("#modal");
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
headers: {
|
||||||
|
'CSRF-Token': data.get('_csrf')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.text();
|
||||||
|
} else {
|
||||||
|
return Promise.reject(response.text());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((lineup_table) => {
|
||||||
|
const email_textarea = document.querySelector('#email-editor')
|
||||||
|
const lineup_table_div = document.querySelector(".FieldGroup .lineup-email")
|
||||||
|
tinymce.activeEditor.setContent("Team,")
|
||||||
|
lineup_table_div.innerHTML = lineup_table
|
||||||
|
email_modal.classList.add("is-open");
|
||||||
|
// email_modal.querySelector(".Modal-body").innerHTML = html;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubmit(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");
|
||||||
|
data = new FormData(form);
|
||||||
|
console.log(form)
|
||||||
|
url = form.attributes.action.textContent;
|
||||||
|
teamsnap_icon.classList.add("u-hidden")
|
||||||
|
waiting_icon.classList.remove("u-hidden");
|
||||||
|
await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
headers: {
|
||||||
|
'CSRF-Token': data.get('_csrf')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
waiting_icon.classList.add("u-hidden");
|
||||||
|
if (response.ok) {
|
||||||
|
return response.text();
|
||||||
|
} else {
|
||||||
|
return Promise.reject(response.text());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((text) => {
|
||||||
|
event.submitter.blur()
|
||||||
|
waiting_icon.classList.add("u-hidden");
|
||||||
|
success_icon.classList.remove("u-hidden");
|
||||||
|
// success_icon.querySelector("span.message").innerHTML = text;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
event.submitter.blur()
|
||||||
|
waiting_icon.classList.add("u-hidden");
|
||||||
|
failure_icon.classList.remove("u-hidden");
|
||||||
|
console.log(error);
|
||||||
|
// success_icon.querySelector("span.message").innerHTML = error;
|
||||||
|
});
|
||||||
|
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) ')
|
||||||
|
Array.from(cells).forEach(cell=>{
|
||||||
|
cell.classList.toggle('u-hidden')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitEmail () {
|
||||||
|
// range=document.createRange();
|
||||||
|
// window.getSelection().removeAllRanges();
|
||||||
|
// // range.selectNode(document.querySelector('.Modal').querySelector('.Modal-body'));
|
||||||
|
// tinymce.activeEditor.selection.select(tinymce.activeEditor.getBody());
|
||||||
|
// // window.getSelection().addRange(range);
|
||||||
|
// document.execCommand('copy');
|
||||||
|
// window.getSelection().removeAllRanges();
|
||||||
|
const emailStyle = `
|
||||||
|
<style>.lineup-email {
|
||||||
|
font-family: "Helvetica", sans-serif;
|
||||||
|
}
|
||||||
|
.lineup-email .title-cell {
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #323669;
|
||||||
|
color: #fff;
|
||||||
|
padding: 2px 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.lineup-email .title-cell.out {
|
||||||
|
background-color: rgb(244, 199, 195);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.lineup-email .sequence-cell {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 1px 5px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.lineup-email .name-cell {
|
||||||
|
width: 200px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.lineup-email .position-label-cell {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Panel .Panel {
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
}</style>
|
||||||
|
`
|
||||||
|
html_content = emailStyle+tinymce.activeEditor.getContent()
|
||||||
|
console.log(html_content)
|
||||||
|
|
||||||
|
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'})
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
||||||
31
src/public/js/opponent.js
Normal file
31
src/public/js/opponent.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// THIS DOESN'T WORK, CORS ERRORS!!
|
||||||
|
// const form = document.querySelector("form[name=upload-opponent-logo]")
|
||||||
|
|
||||||
|
// form.querySelector("button").addEventListener('click', function() {
|
||||||
|
// form.requestSubmit();
|
||||||
|
// })
|
||||||
|
|
||||||
|
// form.addEventListener('submit', async function(e) {
|
||||||
|
// e.preventDefault()
|
||||||
|
// console.log(e.target)
|
||||||
|
// data = new FormData(e.target)
|
||||||
|
// // file = new File(data.file.buffer, data.filename, {
|
||||||
|
// // type: "image/png",
|
||||||
|
// // });
|
||||||
|
// teamsnap.TeamSnap("http://localhost:8080/https://apiv3.teamsnap.com")
|
||||||
|
// if (teamsnap.hasSession()) {
|
||||||
|
// const token = sessionStorage.getItem('teamsnap.authToken')
|
||||||
|
// teamsnap.auth(token);
|
||||||
|
// teamsnap.loadCollections(async function(err) {
|
||||||
|
// if (err) {
|
||||||
|
// console.log(err)
|
||||||
|
// alert('Error loading TeamSnap SDK');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// const team_medium = await teamsnap.createTeamMedium(data)
|
||||||
|
// await teamsnap.uploadTeamMedium(team_medium)
|
||||||
|
// console.log('Uploaded')
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// })
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 198 371" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5;">
|
|
||||||
<g transform="matrix(1.04762,0,0,1.04802,-58.5948,32.9153)">
|
|
||||||
<path d="M90,150L60,119.396C60,119.396 77.426,47.232 150,47.383C220.563,47.53 240.863,120.923 240.863,120.923L210,150" style="fill:none;stroke:rgb(13,202,242);stroke-opacity:0.42;stroke-width:4.17px;"/>
|
|
||||||
<g transform="matrix(1.97279,0,0,1.79916,-103.351,-30.936)">
|
|
||||||
<path d="M128.423,67.218L158.837,100.567L128.423,133.916L98.009,100.567L128.423,67.218Z" style="fill:rgb(13,202,242);fill-opacity:0.1;stroke:rgb(13,202,242);stroke-opacity:0.42;stroke-width:2.21px;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 17 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.8 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,11 +1,11 @@
|
|||||||
var express = require("express");
|
var express = require("express");
|
||||||
var passport = require("passport");
|
var passport = require("passport");
|
||||||
var TeamsnapStrategy = require("passport-teamsnap");
|
var TeamsnapStrategy = require("passport-teamsnap");
|
||||||
|
// const {teamsnap} = require("../app");
|
||||||
// Configure the TeamSnap strategy for use by Passport.
|
// Configure the TeamSnap strategy for use by Passport.
|
||||||
//
|
//
|
||||||
// OAuth 2.0-based strategies require a `verify` function which receives the
|
// OAuth 2.0-based strategies require a `verify` function which receives the
|
||||||
// credential (`accessToken`) for accessing the Facebook API on the user's
|
// credential (`accessToken`) for accessing the TeamSnap API on the user's
|
||||||
// behalf, along with the user's profile. The function must invoke `cb`
|
// behalf, along with the user's profile. The function must invoke `cb`
|
||||||
// with a user object, which will be set at `req.user` in route handlers after
|
// with a user object, which will be set at `req.user` in route handlers after
|
||||||
// authentication.
|
// authentication.
|
||||||
@@ -19,23 +19,21 @@ passport.use(
|
|||||||
passReqToCallback: true,
|
passReqToCallback: true,
|
||||||
scope: ["read", "write"],
|
scope: ["read", "write"],
|
||||||
},
|
},
|
||||||
function (req, accessToken, refreshToken, profile, done) {
|
async function (req, accessToken, refreshToken, profile, done) {
|
||||||
json = JSON.parse(profile._raw);
|
json = JSON.parse(profile._raw);
|
||||||
new_profile = { access_token: accessToken };
|
field_from_collection = (field_name) => {
|
||||||
new_profile["id"] = json.collection.items[0].data.filter(
|
return json.collection.items[0].data.filter(
|
||||||
(e) => e.name == "id"
|
(e) => e.name == field_name
|
||||||
)[0].value;
|
)[0].value;
|
||||||
new_profile["email"] = json.collection.items[0].data.filter(
|
}
|
||||||
(e) => e.name == "email"
|
const new_profile = { access_token: accessToken };
|
||||||
)[0].value;
|
new_profile["id"] = field_from_collection("id")
|
||||||
new_profile["first_name"] = json.collection.items[0].data.filter(
|
new_profile["email"] = field_from_collection("email")
|
||||||
(e) => e.name == "first_name"
|
new_profile["first_name"] = field_from_collection("first_name")
|
||||||
)[0].value;
|
new_profile["last_name"] = field_from_collection("last_name")
|
||||||
|
|
||||||
req.session.teamsnap_access_token = accessToken;
|
req.session.teamsnap_access_token = accessToken;
|
||||||
teamsnap.init(process.env["TEAMSNAP_CLIENT_ID"]);
|
await initTeamsnap(process.env["TEAMSNAP_CLIENT_ID"], accessToken)
|
||||||
teamsnap.auth(accessToken);
|
|
||||||
// teamsnap.enablePersistence();
|
|
||||||
return done(null, new_profile);
|
return done(null, new_profile);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -52,18 +50,24 @@ passport.use(
|
|||||||
// and deserialized.
|
// and deserialized.
|
||||||
passport.serializeUser(function (user, cb) {
|
passport.serializeUser(function (user, cb) {
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
console.log("L#51 serializing user id", user.id);
|
console.log("L#56 serializing user id", user.id);
|
||||||
cb(null, {
|
cb(null, {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.email,
|
username: user.email,
|
||||||
name: user.firstName,
|
email: user.email,
|
||||||
|
first_name: user.first_name,
|
||||||
|
last_name: user.last_name,
|
||||||
accessToken: user.access_token,
|
accessToken: user.access_token,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
passport.deserializeUser(function (user, cb) {
|
passport.deserializeUser(function (user, cb) {
|
||||||
process.nextTick(function () {
|
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)
|
||||||
|
}
|
||||||
return cb(null, user);
|
return cb(null, user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -83,7 +87,7 @@ router.get("/login", function (req, res, next) {
|
|||||||
returnTo = req.session.returnTo;
|
returnTo = req.session.returnTo;
|
||||||
// req.session.regenerate(); // this is not working right as of now...
|
// req.session.regenerate(); // this is not working right as of now...
|
||||||
req.session.returnTo = returnTo;
|
req.session.returnTo = returnTo;
|
||||||
res.render("login");
|
res.render("login", {layout:"layouts/main"});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* GET /login/federated/teamsnap
|
/* GET /login/federated/teamsnap
|
||||||
@@ -120,4 +124,28 @@ router.get(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = router;
|
const initTeamsnap = async (clientID, accessToken) => {
|
||||||
|
teamsnap.init(clientID);
|
||||||
|
teamsnap.auth(accessToken);
|
||||||
|
await teamsnap.loadCollections();
|
||||||
|
await teamsnap.enablePersistence();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ensureLoggedIn = (req, res, next) => {
|
||||||
|
if (!req.isAuthenticated()){
|
||||||
|
req.session.returnTo = req.originalUrl
|
||||||
|
res.redirect("/login");
|
||||||
|
// return next();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
req.user = req.session.passport.user
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get('/auth/teamsnap/session_storage', ensureLoggedIn, (req,res)=>{
|
||||||
|
res.status(200).json({"teamsnap.authToken":req.user.accessToken})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = {router, ensureLoggedIn};
|
||||||
|
|||||||
30
src/routes/event.js
Normal file
30
src/routes/event.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const eventsController = require("../controllers/event");
|
||||||
|
const router = express.Router();
|
||||||
|
const tsUtils = require("../lib/utils")
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
const loadEvent = (req,res,next) => {
|
||||||
|
const {team_id, event_id} = req.params;
|
||||||
|
const bulkLoadTypes = ["event", "availabilitySummary"]
|
||||||
|
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||||
|
req.promises.push(teamsnap.bulkLoad({teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:event_id}, null, tsUtils.teamsnapCallback)
|
||||||
|
.then(bulkLoadItems=>{
|
||||||
|
const items = tsUtils.groupTeamsnapItems(bulkLoadItems, bulkLoadTypes);
|
||||||
|
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||||
|
req.availabilitySummary = items.availabilitySummaries.find(e=>e.eventId==event_id);
|
||||||
|
req.event = items.events.find(e=>e.id==event_id);
|
||||||
|
}
|
||||||
|
))
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
module.exports = {router, loadEvent}
|
||||||
51
src/routes/eventlineup.js
Normal file
51
src/routes/eventlineup.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const eventsLineupController = require("../controllers/eventlineup");
|
||||||
|
const router = express.Router();
|
||||||
|
const tsUtils = require('../lib/utils')
|
||||||
|
const multer = require("multer");
|
||||||
|
const upload = multer()
|
||||||
|
const { doubleCsrfProtection } = require('../middlewares/csrf');
|
||||||
|
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
const loadEventLineup = (req,res,next) => {
|
||||||
|
const {team_id, event_id} = req.params
|
||||||
|
if (!req.event_lineup){
|
||||||
|
bulkLoadTypes = ['eventLineup', 'eventLineupEntry']
|
||||||
|
req.promises.push(teamsnap.bulkLoad({teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:event_id}, null, tsUtils.teamsnapCallback)
|
||||||
|
.then(items => tsUtils.groupTeamsnapItems(items, bulkLoadTypes))
|
||||||
|
.then(items => {
|
||||||
|
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||||
|
req.event_lineup = items.eventLineups.pop();
|
||||||
|
req.event_lineup_entries = items.eventLineupEntries?.sort((a,b)=>a.sequence-b.sequence) || [];
|
||||||
|
})
|
||||||
|
)
|
||||||
|
tsUtils.teamsnapLog('loadAvailabilites', types=['availabilities'], team_id, req);
|
||||||
|
req.promises.push(teamsnap.loadAvailabilities({eventId: event_id}).then(availabilities => req.availabilities = availabilities))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// const {event_lineup} = req
|
||||||
|
}
|
||||||
|
const {event_lineup} = req
|
||||||
|
// req.availabilitySummary = items.find((i) => i.type == "availabilitySummary" && i.id == event.id),
|
||||||
|
// req.event = items.find((i) => i.type == "event" && i.id == event_id)
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
router.use("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup", loadEventLineup)
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup", async (req,res) => {
|
||||||
|
await Promise.all(req.promises);
|
||||||
|
const {event_lineup} = req
|
||||||
|
res.redirect(`lineup/${event_lineup.id}`);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup_card", eventsController.getLineupCard);
|
||||||
|
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/entries", eventsLineupController.getEventLineupEntries)
|
||||||
|
|
||||||
|
module.exports = {router, loadEventLineup}
|
||||||
68
src/routes/eventsheet.js
Normal file
68
src/routes/eventsheet.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const eventsSheetController = require("../controllers/eventsheet");
|
||||||
|
const {loadEventLineup} = require("./eventlineup");
|
||||||
|
const {loadEvent} = require("./event");
|
||||||
|
const {loadRecentAndUpcomingEvents} = require("../middlewares/bulkload")
|
||||||
|
const router = express.Router();
|
||||||
|
const tsUtils = require('../lib/utils')
|
||||||
|
const multer = require("multer");
|
||||||
|
const upload = multer()
|
||||||
|
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
const loadEvents = async (req,res,next) => {
|
||||||
|
const {team_id, event_id} = req.params
|
||||||
|
req.timeline = {}
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
const {recent_events, upcoming_events} = req
|
||||||
|
const eventIds = [...recent_events.map(e=>e.id), event_id, ...upcoming_events.map(e=>e.id)]
|
||||||
|
// if (!req.event_lineup){
|
||||||
|
bulkLoadTypes = ['event','eventLineup', 'eventLineupEntry']
|
||||||
|
req.promises.push(teamsnap.bulkLoad({teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:eventIds}, null, tsUtils.teamsnapCallback)
|
||||||
|
.then(items => tsUtils.groupTeamsnapItems(items, bulkLoadTypes))
|
||||||
|
.then(items => {
|
||||||
|
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||||
|
req.timeline.events = items.events;
|
||||||
|
req.timeline.event_lineups = items.eventLineups;
|
||||||
|
req.timeline.event_lineup_entries = items.eventLineupEntries;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
tsUtils.teamsnapLog('loadAvailabilites', types=['availabilities'], team_id, req);
|
||||||
|
|
||||||
|
req.promises.push(
|
||||||
|
teamsnap.loadAvailabilities({eventId: eventIds}).then(availabilities => {
|
||||||
|
req.timeline.availabilities = availabilities
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// // const {event_lineup} = req
|
||||||
|
// }
|
||||||
|
const {event_lineup} = req
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
const linksForEventSheet = async (req, res, next) => {
|
||||||
|
await Promise.all(req.promises)
|
||||||
|
const events = [...req.recent_events, req.event, ...req.upcoming_events]
|
||||||
|
events.forEach((event) => {
|
||||||
|
console.log()
|
||||||
|
})
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
router.use("/:team_id([0-9]+)/event/:event_id([0-9]+)/sheet", loadEventLineup)
|
||||||
|
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/sheet", loadEventLineup, loadRecentAndUpcomingEvents, loadEvents, eventsSheetController.getEventSheet)
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/sheet", async (req,res) => {
|
||||||
|
await Promise.all(req.promises);
|
||||||
|
const {event_lineup} = req
|
||||||
|
res.redirect(`lineup/${event_lineup.id}/sheet`);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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}
|
||||||
@@ -1,28 +1,30 @@
|
|||||||
var express = require("express");
|
const express = require("express");
|
||||||
var ensureLogIn = require("connect-ensure-login").ensureLoggedIn;
|
const {ensureLoggedIn} = require("./auth")
|
||||||
var papaparse = require("papaparse");
|
|
||||||
|
|
||||||
var ensureLoggedIn = ensureLogIn();
|
|
||||||
|
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
|
|
||||||
var multer = require("multer");
|
var multer = require("multer");
|
||||||
const storage = multer.memoryStorage();
|
const storage = multer.memoryStorage();
|
||||||
const upload = multer({ storage: storage });
|
const upload = multer({ storage: storage });
|
||||||
const teamsController = require("../controllers/teams");
|
const path = require("path")
|
||||||
const eventsController = require("../controllers/events");
|
|
||||||
const membersController = require("../controllers/members");
|
|
||||||
const opponentsController = require("../controllers/opponents");
|
|
||||||
|
|
||||||
router.get("/", ensureLoggedIn, teamsController.getTeams);
|
partials = path.join(__dirname, "../views/partials")
|
||||||
router.get("/:team_id/home", ensureLoggedIn, teamsController.getTeamHome);
|
|
||||||
router.get("/:team_id/events", ensureLoggedIn, eventsController.getEvents);
|
|
||||||
router.get("/:team_id/event/:event_id", ensureLoggedIn, eventsController.getEvent);
|
|
||||||
router.get("/:team_id/event/:event_id/lineup", ensureLoggedIn, eventsController.getLineup);
|
|
||||||
router.get("/:team_id/event/:event_id/lineup_card", ensureLoggedIn, eventsController.getLineupCard);
|
|
||||||
router.get("/:team_id/members", ensureLoggedIn, membersController.getMembers);
|
|
||||||
router.get("/:team_id/opponents", ensureLoggedIn, opponentsController.getOpponents);
|
|
||||||
router.get("/:team_id/opponent/:opponent_id", ensureLoggedIn, opponentsController.getOpponent);
|
|
||||||
// router.get("/:team_id/opponent/:opponent_id/logo", ensureLoggedIn, opponentsController.getOpponentLogo);
|
|
||||||
|
|
||||||
module.exports = router;
|
const membersController = require("../controllers/member");
|
||||||
|
|
||||||
|
router.use("/", ensureLoggedIn, (req,res,next) => {req.layout="layouts/main";req.promises=[];next();})
|
||||||
|
|
||||||
|
router.get("/", (req,res,next) => {
|
||||||
|
if (!req.session.current_team){
|
||||||
|
res.redirect(`/user/${req.session.passport.user.id}/teams`)
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.redirect(`/${req.session.current_team}/home`)
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:team_id([0-9]+)/members", membersController.getMembers);
|
||||||
|
|
||||||
|
module.exports = {router, partials};
|
||||||
|
|||||||
38
src/routes/opponent.js
Normal file
38
src/routes/opponent.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const opponentsController = require("../controllers/opponent");
|
||||||
|
const {loadTeam} = require("./team")
|
||||||
|
var router = express.Router();
|
||||||
|
const multer = require("multer");
|
||||||
|
const upload = multer()
|
||||||
|
const { doubleCsrfProtection } = require('../middlewares/csrf');
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
const loadOpponent = (req,res,next) => {
|
||||||
|
const {opponent_id} = req.params;
|
||||||
|
const {team} = req
|
||||||
|
req.promises.push(
|
||||||
|
teamsnap.loadOpponents(team.id, (err, opponents)=>{
|
||||||
|
if (err) console.log("error in route/opponent.js", err);
|
||||||
|
}).then(opponents => {req.opponent=opponents.find(o=>o.id==opponent_id);})
|
||||||
|
)
|
||||||
|
|
||||||
|
req.promises.push(
|
||||||
|
teamsnap.loadTeamMedia(team.id, (err, team_media)=>{
|
||||||
|
if (err) console.log("error in route/opponent.js", err);
|
||||||
|
})
|
||||||
|
.then(team_media => {
|
||||||
|
req.opponent_logo = team_media.find(tm=>tm.description==`opponent-logo-${opponent_id}.png`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
router.use("/:team_id([0-9]+)/opponent/:opponent_id([0-9]+)", loadOpponent)
|
||||||
|
router.get("/:team_id([0-9]+)/opponents", opponentsController.getOpponents);
|
||||||
|
router.get("/:team_id([0-9]+)/opponent/:opponent_id([0-9]+)", doubleCsrfProtection, opponentsController.getOpponent);
|
||||||
|
router.post("/:team_id([0-9]+)/opponent/:opponent_id([0-9]+)/upload_logo", upload.single('file'), doubleCsrfProtection, opponentsController.postOpponentLogo);
|
||||||
|
// router.get("/:team_id([0-9]+)/opponent/:opponent_id/logo", ensureLoggedIn, opponentsController.getOpponentLogo);
|
||||||
|
|
||||||
|
module.exports = {router}
|
||||||
45
src/routes/team.js
Normal file
45
src/routes/team.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const teamsController = require("../controllers/team");
|
||||||
|
const {loadRecentAndUpcomingEvents} = require("../middlewares/bulkload")
|
||||||
|
const { load } = require("dotenv");
|
||||||
|
const router = express.Router();
|
||||||
|
const tsUtils = require('../lib/utils')
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
const loadTeam = async (req,res,next) => {
|
||||||
|
const {team_id} = req.params;
|
||||||
|
req.team = await teamsnap.loadTeam(team_id)
|
||||||
|
tsUtils.teamsnapLog('loadTeam', types=["team"], team_id, req);
|
||||||
|
const bulkLoadTypes = ['teamMediaGroup', 'teamPreferences', 'member'];
|
||||||
|
const items = tsUtils.groupTeamsnapItems(teamsnap.getAllItems(), bulkLoadTypes)
|
||||||
|
if (req.session.current_team_id == null || req.session.current_team_id != team_id || bulkLoadTypes.filter(t=> !items[t] || items[t].length==0).length > 0){
|
||||||
|
req.promises.push(teamsnap.bulkLoad(
|
||||||
|
team_id,
|
||||||
|
bulkLoadTypes,
|
||||||
|
tsUtils.teamsnapCallback
|
||||||
|
).then(bulkLoadItems=>{
|
||||||
|
const items = tsUtils.groupTeamsnapItems(bulkLoadItems, bulkLoadTypes)
|
||||||
|
req.members = items.members;
|
||||||
|
req.team_media_group = items.teamMediaGroups?.pop();
|
||||||
|
req.team_preferences = items.teamsPreferences.pop();
|
||||||
|
req.session.current_team_id = req.team.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
req.members = items.member;
|
||||||
|
req.team_media_group = items.teamMediaGroup.pop();
|
||||||
|
req.team_preferences = items.teamPreferences.pop();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
router.use("/:team_id([0-9]+)", loadTeam)
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
router.get('/user/:user_id/teams', teamsController.getTeams)
|
||||||
|
router.get("/:team_id([0-9]+)/home", loadRecentAndUpcomingEvents, teamsController.getTeamHome);
|
||||||
|
|
||||||
|
module.exports = {router, loadTeam}
|
||||||
350
src/scss/application.scss
Normal file
350
src/scss/application.scss
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
|
||||||
|
* vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||||
|
* compiled file so the styles you add here take precedence over styles defined in any other CSS
|
||||||
|
* files in this directory. Styles in this file should be added after the last require_* statement.
|
||||||
|
* It is generally better to create a new file per style scope.
|
||||||
|
*
|
||||||
|
*= require_tree .
|
||||||
|
*= require_self
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import "../../node_modules/@teamsnap/teamsnap-ui/src/css/teamsnap-ui.scss";
|
||||||
|
|
||||||
|
|
||||||
|
$color-success: #b7e1cd;
|
||||||
|
$color-danger: #f4c7c3;
|
||||||
|
$color-neutral: #acc9fe;
|
||||||
|
$color-warning: rgb(249, 228, 180);
|
||||||
|
$color-grey-100: #f8f9fa;
|
||||||
|
$color-grey-200: #e9ecef;
|
||||||
|
$color-grey-300: #dee2e6;
|
||||||
|
$color-grey-400: #ced4da;
|
||||||
|
$color-grey-500: #adb5bd;
|
||||||
|
$color-grey-600: #6c757d;
|
||||||
|
$color-grey-700: #495057;
|
||||||
|
$color-grey-800: #343a40;
|
||||||
|
$color-grey-900: #212529;
|
||||||
|
$row-height: 14px;
|
||||||
|
$monospace-font: "Inconsolata", monospace;
|
||||||
|
|
||||||
|
|
||||||
|
// Components
|
||||||
|
@import "components/progress";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bc-text-muted:#6c757d;
|
||||||
|
--bc-avail-color-yes: $cu-positive;
|
||||||
|
--bc-avail-color-no: $cu-negative;
|
||||||
|
--bc-avail-color-maybe:#113b63;
|
||||||
|
}
|
||||||
|
|
||||||
|
.availability-bar {
|
||||||
|
&.going {
|
||||||
|
background-color: $ts-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.not-going {
|
||||||
|
background-color: $ts-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.maybe {
|
||||||
|
background-color: $ts-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.unknown {
|
||||||
|
background-color: $ts-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Header {
|
||||||
|
background: #323669;
|
||||||
|
padding: 8px 0;
|
||||||
|
box-shadow: 0 4px 0 rgba(0, 0, 25, 0.1);
|
||||||
|
border-bottom: 1px solid #d6d6d6;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: rgb(246, 246, 246);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .u-padSm.u-border.u-borderRadiusLg.u-spaceEndsSm.u-maxWidthXs */
|
||||||
|
|
||||||
|
.event-card {
|
||||||
|
font-family: "Open Sans", Helvetica, sans-serif;
|
||||||
|
border: 1px solid #e6e6e6;
|
||||||
|
border-radius: 8px;
|
||||||
|
max-width: 480px;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: white;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-card-body {
|
||||||
|
padding: 8px 8px 8px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-card-body {
|
||||||
|
> .availability-bar {
|
||||||
|
margin: 4px;
|
||||||
|
|
||||||
|
&.fullwidth {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: -8px;
|
||||||
|
margin-left: -8px;
|
||||||
|
margin-right: -8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.availability-bar.fullwidth .progress {
|
||||||
|
margin-left: -8px;
|
||||||
|
margin-right: -8px;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-card {
|
||||||
|
.date, .location {
|
||||||
|
color: #7a7a7a;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent {}
|
||||||
|
|
||||||
|
.Button span {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-card-footer {
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 0px 0px 8px 8px;
|
||||||
|
background-color: rgb(251, 251, 251);
|
||||||
|
border-top: solid 1px rgb(214, 214, 214);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-card-footer div {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Header-bannerLogo, .Header-bannerTitle {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Header-bannerLogo img {
|
||||||
|
height: 36px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Header-bannerTitle {
|
||||||
|
font-family: "Helvetica", sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
color: white;
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.benchcoach-nav {
|
||||||
|
background-color: #323669;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
padding: 0.5em;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Panel-row {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.benchcoach-nav h3 {
|
||||||
|
font-family: "Helvetica", sans-serif;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: white;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-slot .Panel-cell {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[id^="event-lineup"] {
|
||||||
|
max-width: 576px;
|
||||||
|
counter-reset: lineup-sequence-counter 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-slot {
|
||||||
|
counter-increment: lineup-sequence-counter 1;
|
||||||
|
|
||||||
|
.Panel-cell {
|
||||||
|
&.Panel-cell--header {
|
||||||
|
background: rgba(256, 256, 256, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*="availability-status-code"]::before {
|
||||||
|
padding-right: 6px;
|
||||||
|
font-family: "bootstrap-icons";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-slot .availability-status-code-1 .icon {
|
||||||
|
color: $ts-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-slot .availability-status-code-2 .icon {
|
||||||
|
color: $ts-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-slot .availability-status-code-0 .icon {
|
||||||
|
color: $ts-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-slot {
|
||||||
|
.availability-status-code-nil .icon, .availability-status-code- .icon {
|
||||||
|
color: $ts-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li .availability-status-code- {
|
||||||
|
content: "\F50B";
|
||||||
|
color: var(--bs-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-slot {
|
||||||
|
line-height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
span {
|
||||||
|
margin:auto;
|
||||||
|
}
|
||||||
|
.lastname {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jerseynumber {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: light;
|
||||||
|
color: var(--bc-text-muted);
|
||||||
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
// &::before {
|
||||||
|
// content: "-";
|
||||||
|
// margin-right: 4px;
|
||||||
|
margin-left: 4px;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-right: 0.5ch;
|
||||||
|
}
|
||||||
|
.sequence {
|
||||||
|
width: 2.4ch;
|
||||||
|
text-align: right;
|
||||||
|
margin-right: 0.3ch;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: counter(lineup-sequence-counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
width: 2ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-select-box {
|
||||||
|
width: 11ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Panel-cell {
|
||||||
|
&:has(.sequence), &:has(.drag-handle), &:has(.position-select-box) {
|
||||||
|
flex: 0 0 0% !important;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Tooltip:after {
|
||||||
|
padding: 2px !important;
|
||||||
|
font-size: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px){
|
||||||
|
.Panel--full {
|
||||||
|
border-radius: 0;
|
||||||
|
margin-right: -16px;
|
||||||
|
margin-left: -16px;
|
||||||
|
border-right: none;
|
||||||
|
border-left: none;
|
||||||
|
}}
|
||||||
|
|
||||||
|
.lineup-email {
|
||||||
|
font-family: "Helvetica", sans-serif;
|
||||||
|
|
||||||
|
.title-cell {
|
||||||
|
font-weight:bold;
|
||||||
|
background-color:#323669;
|
||||||
|
color:#fff;
|
||||||
|
padding:2px 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
&.out {
|
||||||
|
background-color: rgb(244, 199, 195);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sequence-cell {
|
||||||
|
font-weight:bold;
|
||||||
|
padding: 1px 5px;
|
||||||
|
text-align: left
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-cell {
|
||||||
|
width:200px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-label-cell {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.Panel .Panel{
|
||||||
|
// padding: 0;
|
||||||
|
// border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
// border-top: 1px solid #d6d6d6;
|
||||||
|
// border-bottom: 1px solid #d6d6d6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
78
src/scss/components/_border-radius.scss
Normal file
78
src/scss/components/_border-radius.scss
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// stylelint-disable property-disallowed-list
|
||||||
|
// Single side border-radius
|
||||||
|
|
||||||
|
// Helper function to replace negative values with 0
|
||||||
|
@function valid-radius($radius) {
|
||||||
|
$return: ();
|
||||||
|
@each $value in $radius {
|
||||||
|
@if type-of($value) == number {
|
||||||
|
$return: append($return, max($value, 0));
|
||||||
|
} @else {
|
||||||
|
$return: append($return, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scss-docs-start border-radius-mixins
|
||||||
|
@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
@else if $fallback-border-radius != false {
|
||||||
|
border-radius: $fallback-border-radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-top-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-top-left-radius: valid-radius($radius);
|
||||||
|
border-top-right-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-end-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-top-right-radius: valid-radius($radius);
|
||||||
|
border-bottom-right-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-bottom-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-bottom-right-radius: valid-radius($radius);
|
||||||
|
border-bottom-left-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-start-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-top-left-radius: valid-radius($radius);
|
||||||
|
border-bottom-left-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-top-start-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-top-left-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-top-end-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-top-right-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-bottom-end-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-bottom-right-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-bottom-start-radius($radius: $border-radius) {
|
||||||
|
@if $enable-rounded {
|
||||||
|
border-bottom-left-radius: valid-radius($radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scss-docs-end border-radius-mixins
|
||||||
18
src/scss/components/_box-shadow.scss
Normal file
18
src/scss/components/_box-shadow.scss
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@mixin box-shadow($shadow...) {
|
||||||
|
@if $enable-shadows {
|
||||||
|
$result: ();
|
||||||
|
|
||||||
|
@each $value in $shadow {
|
||||||
|
@if $value != null {
|
||||||
|
$result: append($result, $value, "comma");
|
||||||
|
}
|
||||||
|
@if $value == none and length($shadow) > 1 {
|
||||||
|
@warn "The keyword 'none' must be used as a single argument.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (length($result) > 0) {
|
||||||
|
box-shadow: $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/scss/components/_progress.scss
Normal file
72
src/scss/components/_progress.scss
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
|
||||||
|
$progress-height: 1rem;
|
||||||
|
$progress-font-size: $tu-base-fontSize * .75;
|
||||||
|
$progress-bg: $color-grey-200;
|
||||||
|
$progress-border-radius: $border-radius-small;
|
||||||
|
$progress-box-shadow: $inset-box-shadow-small;
|
||||||
|
$progress-bar-color: $ts-white;
|
||||||
|
$progress-bar-bg: $ts-green;
|
||||||
|
$progress-bar-animation-timing: 1s linear infinite;
|
||||||
|
$progress-bar-transition: width .6s ease;
|
||||||
|
$prefix: "";
|
||||||
|
|
||||||
|
// Disable animation if transitions are disabled
|
||||||
|
|
||||||
|
// scss-docs-start progress-keyframes
|
||||||
|
// @if $enable-transitions {
|
||||||
|
// @keyframes progress-bar-stripes {
|
||||||
|
// 0% { background-position-x: $progress-height; }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// scss-docs-end progress-keyframes
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
// scss-docs-start progress-css-vars
|
||||||
|
--#{$prefix}progress-height: #{$progress-height};
|
||||||
|
// @include rfs($progress-font-size, --#{$prefix}progress-font-size);
|
||||||
|
--#{$prefix}progress-bg: #{$progress-bg};
|
||||||
|
--#{$prefix}progress-border-radius: #{$progress-border-radius};
|
||||||
|
--#{$prefix}progress-box-shadow: #{$progress-box-shadow};
|
||||||
|
--#{$prefix}progress-bar-color: #{$progress-bar-color};
|
||||||
|
--#{$prefix}progress-bar-bg: #{$progress-bar-bg};
|
||||||
|
--#{$prefix}progress-bar-transition: #{$progress-bar-transition};
|
||||||
|
// scss-docs-end progress-css-vars
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
height: var(--#{$prefix}progress-height);
|
||||||
|
overflow: hidden; // force rounded corners by cropping it
|
||||||
|
// @include font-size(var(--#{$prefix}progress-font-size));
|
||||||
|
background-color: var(--#{$prefix}progress-bg);
|
||||||
|
// @include border-radius(var(--#{$prefix}progress-border-radius));
|
||||||
|
// @include box-shadow(var(--#{$prefix}progress-box-shadow));
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--#{$prefix}progress-bar-color);
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
background-color: var(--#{$prefix}progress-bar-bg);
|
||||||
|
// @include transition(var(--#{$prefix}progress-bar-transition));
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar-striped {
|
||||||
|
// @include gradient-striped();
|
||||||
|
background-size: var(--#{$prefix}progress-height) var(--#{$prefix}progress-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @if $enable-transitions {
|
||||||
|
// .progress-bar-animated {
|
||||||
|
// animation: $progress-bar-animation-timing progress-bar-stripes;
|
||||||
|
|
||||||
|
// @if $enable-reduced-motion {
|
||||||
|
// @media (prefers-reduced-motion: reduce) {
|
||||||
|
// animation: none;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
902
src/scss/eventsheet.scss
Normal file
902
src/scss/eventsheet.scss
Normal file
@@ -0,0 +1,902 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Pacifico');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Oswald');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Graduate');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wdth,wght@50..200,200..900&display=swap');
|
||||||
|
@import url('/font/helvetica-now/stylesheet.css');
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Futura Now";
|
||||||
|
src: url("/font/futura-now/futura-now.ttf") format("truetype");
|
||||||
|
font-weight: 125 900;
|
||||||
|
font-stretch: 50% 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@page {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For Print **/
|
||||||
|
@media print {
|
||||||
|
body .sheet {
|
||||||
|
size: B5;
|
||||||
|
padding: 0.2in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For screen preview **/
|
||||||
|
@media screen {
|
||||||
|
body .sheet {
|
||||||
|
padding: 0.2in;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.sheet {
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 0.5mm 2mm rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Paper sizes **/
|
||||||
|
body.B5 .sheet {
|
||||||
|
width: 176mm;
|
||||||
|
height: 250mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.index-card .sheet {
|
||||||
|
width: 3.5in;
|
||||||
|
height: 5in;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-success: #b7e1cd;
|
||||||
|
--color-danger: #f4c7c3;
|
||||||
|
--color-neutral: #acc9fe;
|
||||||
|
--color-warning: rgb(249, 228, 180);
|
||||||
|
--color-grey-100: #f8f9fa;
|
||||||
|
--color-grey-200: #e9ecef;
|
||||||
|
--color-grey-300: #dee2e6;
|
||||||
|
--color-grey-400: #ced4da;
|
||||||
|
--color-grey-500: #adb5bd;
|
||||||
|
--color-grey-600: #6c757d;
|
||||||
|
--color-grey-700: #495057;
|
||||||
|
--color-grey-800: #343a40;
|
||||||
|
--color-grey-900: #212529;
|
||||||
|
--row-height: 14px;
|
||||||
|
--monospace-font: "Inconsolata", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Helvetica Now", "Helvetica", sans-serif;
|
||||||
|
position: relative;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
position: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
border-collapse: collapse;
|
||||||
|
empty-cells: show;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
width: 100%;
|
||||||
|
border: 0.5px solid black;
|
||||||
|
display: inline-table;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border-bottom-width: 0.5px;
|
||||||
|
border-color: grey;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
|
||||||
|
&:nth-child(odd) {
|
||||||
|
background-color: rgb(242, 242, 242, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(even) {
|
||||||
|
background-color: rgb(256, 256, 256, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-stretch: extra-condensed;
|
||||||
|
width: 1em;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
td {
|
||||||
|
&.player-name {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: 75%;
|
||||||
|
}
|
||||||
|
&.position, &.jersey-number {
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
width: 2ch;
|
||||||
|
text-align: right;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border-left: 0.5px solid grey;
|
||||||
|
border-right: 0.5px solid grey;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 2px 0 2px;
|
||||||
|
|
||||||
|
&:empty::after {
|
||||||
|
content: "\00a0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.B5 > .eventsheet {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
column-gap: 0.2in;
|
||||||
|
row-gap: 0.2in;
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventsheet > section {
|
||||||
|
box-sizing: content-box;
|
||||||
|
overflow: hidden;
|
||||||
|
/* outline: 0.5px dashed lightgrey; */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventsheet > section > div {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: stretch;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card th {
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-dugout div.grid-container,
|
||||||
|
#lineup-card-dugout-blank div.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 60% auto;
|
||||||
|
grid-template-rows: fit-content(16px) auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"header header"
|
||||||
|
"sarting-lineup-table substitution-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange div.grid-container,
|
||||||
|
#lineup-card-exchange-blank div.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
grid-template-rows: fit-content(16px) auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"header"
|
||||||
|
"sarting-lineup-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange .substitution,
|
||||||
|
#lineup-card-exchange .homeaway,
|
||||||
|
#lineup-card-exchange-blank .substitution,
|
||||||
|
#lineup-card-exchange-blank .homeaway {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card div.grid-container > header {
|
||||||
|
grid-area: "header";
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card div.grid-container > header:empty::after {
|
||||||
|
content: "\00a0";
|
||||||
|
}
|
||||||
|
|
||||||
|
div.grid-container > .starting-lineup-table {
|
||||||
|
grid-area: "starting-lineup-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
div.grid-container > .substitution-table {
|
||||||
|
grid-area: "substitution-table";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
section.lineup-card {
|
||||||
|
counter-reset: lineup-sequence-counter 0;
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
th.sequence {
|
||||||
|
counter-increment: lineup-sequence-counter 1;
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
font-size: inherit;
|
||||||
|
width: 2ch;
|
||||||
|
|
||||||
|
&.counter::before {
|
||||||
|
content: counter(lineup-sequence-counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.lineup-card table {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card td {
|
||||||
|
/* height: 34px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card td.substitution {
|
||||||
|
width: 8ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card td.substitution::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange tr,
|
||||||
|
#lineup-card-exchange-blank tr,
|
||||||
|
#lineup-card-dugout .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout-blank .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout .substitution-table tr:nth-child(odd) {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange tr,
|
||||||
|
#lineup-card-exchange-blank tr,
|
||||||
|
#lineup-card-dugout .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout-blank .starting-lineup-table tr,
|
||||||
|
#lineup-card-dugout .substitution-table tr:nth-child(even) {
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.substitution {
|
||||||
|
border-left: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-exchange td.player-name {
|
||||||
|
font-stretch: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-dugout td.player-name {
|
||||||
|
width: 10ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card .position,
|
||||||
|
.lineup-card .jersey-number {
|
||||||
|
width: 2ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineup-card-dugout .position,
|
||||||
|
#lineup-card-dugout .jersey-number {
|
||||||
|
font-stretch: 75%;
|
||||||
|
padding-left: 2.5px;
|
||||||
|
padding-right: 2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineup-card header {
|
||||||
|
font-size: inherit;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#todays-game {
|
||||||
|
> div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 110px auto;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"offense defense"
|
||||||
|
"footer footer";
|
||||||
|
}
|
||||||
|
|
||||||
|
#offense-pane {
|
||||||
|
grid-area: offense;
|
||||||
|
}
|
||||||
|
|
||||||
|
#defense-pane {
|
||||||
|
grid-area: defense;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
/* height:var(--row-height); */
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
grid-area: footer;
|
||||||
|
/* border: 1px solid black; */
|
||||||
|
height: 100%;
|
||||||
|
border-right: 0.5px solid grey;
|
||||||
|
border-left: 0.5px solid grey;
|
||||||
|
|
||||||
|
table {
|
||||||
|
height: 100%;
|
||||||
|
outline: none;
|
||||||
|
border-style: none;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
background-color: white;
|
||||||
|
outline: none;
|
||||||
|
border-bottom: 0.5px solid var(--color-grey-500);
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
background-color: white;
|
||||||
|
outline: none;
|
||||||
|
border-bottom-style: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
height: var(--row-height);
|
||||||
|
border: none;
|
||||||
|
&empty::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
&.notes {
|
||||||
|
th {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
td:empty::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#defense-card {
|
||||||
|
#defense-pane {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot-set {
|
||||||
|
table {
|
||||||
|
font-size: 14px;
|
||||||
|
width: 120px;
|
||||||
|
tbody > tr:last-child {
|
||||||
|
// display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-c {
|
||||||
|
grid-area: 6 / 1 / 7 / 5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-p {
|
||||||
|
table {
|
||||||
|
width: 120px !important;
|
||||||
|
tbody > tr:last-child {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
justify-content: center !important;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: inherit !important;
|
||||||
|
grid-area: 5 / 1 / 6 / 5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#defense-pane {
|
||||||
|
position: relative;
|
||||||
|
grid-area: defense;
|
||||||
|
padding: 4px 4px 0px 4px; /* top right bottom left */
|
||||||
|
display: flex;
|
||||||
|
border-right: 0.5px solid grey;
|
||||||
|
border-bottom: 0.5px solid grey;
|
||||||
|
|
||||||
|
.field-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
grid-template-rows: repeat(6, 1fr);
|
||||||
|
grid-column-gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot-set {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
$positions: "cf", "lf", "rf", "ss", "2b", "3b", "1b", "c", "p";
|
||||||
|
|
||||||
|
@each $position in $positions {
|
||||||
|
&.pos-#{$position} tr:first-child {
|
||||||
|
th.position:empty::after {
|
||||||
|
content: $position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-cf {
|
||||||
|
justify-content: center;
|
||||||
|
// align-items: start;
|
||||||
|
grid-area: 1 / 1 / 2 / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-lf {
|
||||||
|
justify-content: flex-start;
|
||||||
|
grid-area: 2 / 1 / 3 / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-rf {
|
||||||
|
justify-content: flex-end;
|
||||||
|
grid-area: 2 / 3 / 3 / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-ss {
|
||||||
|
justify-content: flex-end;
|
||||||
|
grid-area: 3 / 1 / 4 / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-2b {
|
||||||
|
justify-content: flex-start;
|
||||||
|
grid-area: 3 / 3 / 4 / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-3b {
|
||||||
|
justify-content: flex-start;
|
||||||
|
grid-area: 4 / 1 / 5 / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-1b {
|
||||||
|
justify-content: flex-end;
|
||||||
|
grid-area: 4 / 3 / 5 / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-c {
|
||||||
|
justify-content: center;
|
||||||
|
grid-area: 5 / 1 / 6 / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pos-p {
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
align-items: end;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
grid-area: 6 / 1 / 7 / 5;
|
||||||
|
tr.substitute .position:empty::after{
|
||||||
|
content: "RP";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#offense-pane {
|
||||||
|
position: relative;
|
||||||
|
/* box-sizing: border-box; */
|
||||||
|
height: 100%;
|
||||||
|
// grid-area: offense;
|
||||||
|
border-bottom: 0.5px solid black;
|
||||||
|
counter-reset: lineup-sequence-counter 0;
|
||||||
|
/* outline: 0.5px solid black; */
|
||||||
|
|
||||||
|
table {
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.sequence{
|
||||||
|
counter-increment: lineup-sequence-counter 1;
|
||||||
|
&.counter::before {
|
||||||
|
content: counter(lineup-sequence-counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.slot-set table {
|
||||||
|
width: 77px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot-set.pos-slot-p table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-card .defense-slot-set {
|
||||||
|
width: 65px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-card .defense-slot-set .player-name {
|
||||||
|
font-stretch: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container .row {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #cadcf9;
|
||||||
|
font-family: "Oswald";
|
||||||
|
/* font-size: 8.8px; */
|
||||||
|
/* outline: 1px solid black; */
|
||||||
|
/* height: var(--row-height); */
|
||||||
|
width: auto;
|
||||||
|
// grid-area: header;
|
||||||
|
text-align: center;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
border: 0.5px solid black;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.cell-checkbox {
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-starting-lineup {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gametitle {
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: semi-condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homeaway {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-weight: bolder;
|
||||||
|
float: right;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-smalltext {
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statscell {
|
||||||
|
font-family: "m+1m";
|
||||||
|
text-align: center;
|
||||||
|
font-stretch: extra-condensed;
|
||||||
|
font-size: 9px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condensedNameCell {
|
||||||
|
width: 70px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-square {
|
||||||
|
height: var(--row-height);
|
||||||
|
width: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-square.narrow {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
.cell-mono {
|
||||||
|
font-family: "m+1m";
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-condensed {
|
||||||
|
font-stretch: condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-status-code-1 {
|
||||||
|
color: rgb(0, 85, 0);
|
||||||
|
background-color: #b7e1cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-status-code-0 {
|
||||||
|
color: rgb(170, 0, 0);
|
||||||
|
background-color: #f4c7c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.past.available-status-code-0,
|
||||||
|
.past.available-status-code-null {
|
||||||
|
color: var(--color-grey-600);
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.past.available-status-code-1 {
|
||||||
|
color: inherit;
|
||||||
|
background-color: var(--color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.past.available-status-code-1.started {
|
||||||
|
color: inherit;
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-status-code-2 {
|
||||||
|
color: blue;
|
||||||
|
background-color: #acc9fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
#roster-and-history .player-name {
|
||||||
|
font-stretch: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.starting {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#roster-and-history {
|
||||||
|
// #today-availability {
|
||||||
|
// font-stretch: normal;
|
||||||
|
// text-transform: uppercase;
|
||||||
|
// font-size: 0.8em;
|
||||||
|
// }
|
||||||
|
> div > table {
|
||||||
|
/* font-size: 10.5px; */
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1em;
|
||||||
|
/* outline: 0.5px black; */
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
padding: 0.2em 0.1em 0.2em 0.1em; /* top right bottom left */
|
||||||
|
|
||||||
|
&.availability-on-day {
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
font-stretch: 60%;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 0.8em;
|
||||||
|
min-width: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.availability-on-day.future,
|
||||||
|
&.availability-on-day.past {
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.position-capability {
|
||||||
|
font-size: 8px;
|
||||||
|
font-stretch: 50%;
|
||||||
|
width: 5px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
&.player-stats {
|
||||||
|
display:none;
|
||||||
|
font-family: var(--monospace-font);
|
||||||
|
font-size: 1em;
|
||||||
|
font-stretch: 60%;
|
||||||
|
font-weight: 300;
|
||||||
|
|
||||||
|
.delimiter,
|
||||||
|
.decimal-point {
|
||||||
|
font-family: Helvetica Now;
|
||||||
|
font-stretch: expanded;
|
||||||
|
color: var(--color-grey-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.decimal-point {
|
||||||
|
color: rgb(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delimiter {
|
||||||
|
color: var(--color-grey-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td{
|
||||||
|
&.player-name {
|
||||||
|
color: black;
|
||||||
|
text-align: left;
|
||||||
|
font-stretch: 95%;
|
||||||
|
}
|
||||||
|
&.jersey-number {
|
||||||
|
color: black;
|
||||||
|
border-left: 0.5px solid lightgrey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
colgroup {
|
||||||
|
border-left-width: 1px;
|
||||||
|
border-left-style: solid;
|
||||||
|
border-left-color: black;
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-style: solid;
|
||||||
|
border-right-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
col.player-stats {
|
||||||
|
// visibility:collapse;
|
||||||
|
border: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #cadcf9;
|
||||||
|
color: black;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&.availability-on-day div {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// .position-capability {
|
||||||
|
// font-size: inherit;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
thead > tr, tfoot > tr {
|
||||||
|
border-bottom: solid black 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
border-bottom: solid black 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.border-top {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
td.position-capability:not(:empty) {
|
||||||
|
color: var(--color-grey-700);
|
||||||
|
background-color: var(--color-grey-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.is-present-checkbox {
|
||||||
|
font-size: 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
/* text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000,
|
||||||
|
1px 1px 0 #000; */
|
||||||
|
}
|
||||||
|
|
||||||
|
td.is-present-checkbox.available-status-code-0 > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.is-present-checkbox.available-status-code-None > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-number .label {
|
||||||
|
font-size: xx-small
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-number .label, .game-number .value{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#front-cover {
|
||||||
|
border: solid 1px black;
|
||||||
|
|
||||||
|
Header {
|
||||||
|
font-family: "Helvetica Now";
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.5em;
|
||||||
|
background-color: #323669;
|
||||||
|
color: white;
|
||||||
|
display: inline-flex;
|
||||||
|
border: none;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-number, .homeaway{
|
||||||
|
display: grid;
|
||||||
|
font-size: xx-large;
|
||||||
|
align-content: center;
|
||||||
|
width: 1.4em;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: grid;
|
||||||
|
font-size: small;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-to-head {
|
||||||
|
padding:5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent, .team {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: x-large;
|
||||||
|
align-items: center;
|
||||||
|
font-family: 'Pacifico';
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-grow:1
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent img, .team img{
|
||||||
|
height: 115px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent div:has(.name), .team div:has(.name){
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent name{
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team name{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
2
src/views/error.hbs
Normal file
2
src/views/error.hbs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<h1>Oops ...</h1>
|
||||||
|
<code>{{message}}</code>
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
html
|
|
||||||
head
|
|
||||||
body
|
|
||||||
h1 error
|
|
||||||
h2
|
|
||||||
error.status
|
|
||||||
pre
|
|
||||||
message #{message}
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
html
|
|
||||||
head
|
|
||||||
meta(charset='utf-8')
|
|
||||||
title #{event.formattedTitle}
|
|
||||||
link(rel='stylesheet' href='/css/gamecard.css')
|
|
||||||
|
|
||||||
body(class="B5")
|
|
||||||
input(name="team_id", type="hidden" value=`${team_id}`)
|
|
||||||
input(name="event_id", type="hidden" value=`${event_id}`)
|
|
||||||
#page-1.sheet.gamecard
|
|
||||||
section#todays-game
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
#todays-game-header.bar-left.event-title
|
|
||||||
| #{event.formattedTitle}
|
|
||||||
| #{event.startDate.toLocaleDateString("en-us",{weekday: "short", day: "numeric",month: "short"})}
|
|
||||||
| #{event.startDate.toLocaleTimeString("en-us",{hour: "numeric", minute: "2-digit"})}
|
|
||||||
.bar-right.homeaway #{event.gameType}
|
|
||||||
.bar-span.gametitle
|
|
||||||
#offense-pane.left
|
|
||||||
table#starting-lineup-offense
|
|
||||||
tbody
|
|
||||||
each _, i in Array(11)
|
|
||||||
- if (typeof(event_lineup_entries_offense[i]) !== 'undefined'){
|
|
||||||
tr
|
|
||||||
th(rowspan='2') #{i+1}
|
|
||||||
td(id=`offense-slot-${i}-name` class="player-name") #{event_lineup_entries_offense[i].member.lastName}
|
|
||||||
td(id=`offense-slot-${i}-jersey-number` class="jersey-number") #{event_lineup_entries_offense[i].member.jerseyNumber}
|
|
||||||
td(id=`offense-slot-${i}-position` class="position") #{event_lineup_entries_offense[i].label}
|
|
||||||
tr.substitute
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- } else {
|
|
||||||
tr
|
|
||||||
th(rowspan='2')
|
|
||||||
td(id=`offense-slot-${i}-name` class="player-name")
|
|
||||||
td(id=`offense-slot-${i}-jersey-number` class="jersey-number")
|
|
||||||
td(id=`offense-slot-${i}-position` class="position")
|
|
||||||
tr.substitute
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- }
|
|
||||||
|
|
||||||
#defense-pane.right
|
|
||||||
.container
|
|
||||||
.field-container
|
|
||||||
image(src='/media/baseball-diamond.svg')
|
|
||||||
.row(style='justify-content: center')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position CF
|
|
||||||
td#defense-slot-CF-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("CF")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: space-between')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position LF
|
|
||||||
td#defense-slot-LF-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("LF")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position RF
|
|
||||||
td#defense-slot-RF-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("RF")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: space-around')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position SS
|
|
||||||
td#defense-slot-SS-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("SS")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position 2B
|
|
||||||
td#defense-slot-2B-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("2B")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: space-between')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position 3B
|
|
||||||
td#defense-slot-3B-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("3B")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position 1B
|
|
||||||
td#defense-slot-1B-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("1B")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: center')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position C
|
|
||||||
td#defense-slot-C-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("C") && !lue.label.startsWith("CF") ) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.pitching-container
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position P
|
|
||||||
td#defense-slot-P-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("P")) || {"member":{}}).member.lastName}
|
|
||||||
td.jersey-number
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("P")) || {"member":{}}).member.jerseyNumber}
|
|
||||||
td.position
|
|
||||||
tr
|
|
||||||
th.position RP
|
|
||||||
td#defense-slot-RP1-name.player-name
|
|
||||||
td
|
|
||||||
td
|
|
||||||
tr
|
|
||||||
th.position RP
|
|
||||||
td#defense-slot-RP2-name.player-name
|
|
||||||
td
|
|
||||||
td
|
|
||||||
.footer
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th Notes
|
|
||||||
td
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
section#roster-and-history
|
|
||||||
div
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th#today-availability(colspan='3') Available (
|
|
||||||
| #{availabilitySummaries.find((e)=>e.id==event_id).playerGoingCount}|
|
|
||||||
| #{availabilitySummaries.find((e)=>e.id==event_id).playerMaybeCount}
|
|
||||||
| )
|
|
||||||
th.player-stats
|
|
||||||
span.decimal-point .
|
|
||||||
| AVG
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
| OBP
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
| SLG
|
|
||||||
span.delimiter :
|
|
||||||
| PA
|
|
||||||
th.position-capability.pitcher P
|
|
||||||
th.position-capability.catcher C
|
|
||||||
th.position-capability.infield I
|
|
||||||
th.position-capability.outfield O
|
|
||||||
each event_future, i in events_future
|
|
||||||
th(id=`avail-header-today-plus-${i+1}` class="availability future")
|
|
||||||
.rotate #{event_future.startDate.toLocaleDateString("en-us", {weekday: "short"})}
|
|
||||||
each event_past, i in events_past
|
|
||||||
th(id=`avail-header-today-minus-${i+1}` class="availability past")
|
|
||||||
.rotate #{event_past.startDate.toLocaleDateString("en-us", {weekday: "short"})}
|
|
||||||
tbody
|
|
||||||
each row, index in availabilities.filter((e)=>e.event.id==event_id && !e.member.isNonPlayer)
|
|
||||||
tr(id=`roster-history-slot-${index+1}` class=``)
|
|
||||||
td(class=`is-present-checkbox available-status-code-${row.statusCode}`)
|
|
||||||
span ■
|
|
||||||
td(
|
|
||||||
class=`
|
|
||||||
jersey-number
|
|
||||||
border-left
|
|
||||||
available-status-code-${row.statusCode}
|
|
||||||
${event_lineup_entries.find((lue)=>lue.member.id==row.member.id) !== undefined ? "starting" : ""}
|
|
||||||
`)
|
|
||||||
| #{row.member.jerseyNumber}
|
|
||||||
td(
|
|
||||||
class=`
|
|
||||||
player-name
|
|
||||||
available-status-code-${row.statusCode}
|
|
||||||
${event_lineup_entries.find((lue)=>lue.member.id==row.member.id) !== undefined ? "starting" : ""}
|
|
||||||
`)
|
|
||||||
| #{row.member.lastName}
|
|
||||||
td.player-stats.border-left.border-right
|
|
||||||
span.decimal-point .
|
|
||||||
span.avg 000
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
span.obp 000
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
span.slg 000
|
|
||||||
span.delimiter :
|
|
||||||
span.pa 00
|
|
||||||
td.position-capability.pitcher #{row.member.position.includes("P") ? "\u2713" : ""}
|
|
||||||
td.position-capability.catcher #{row.member.position.includes("C") ? "\u2713" : ""}
|
|
||||||
td.position-capability.infield #{row.member.position.includes("IF") ? "\u2713" : ""}
|
|
||||||
td.position-capability.outfield #{row.member.position.includes("OF") ? "\u2713" : ""}
|
|
||||||
- var future_availability
|
|
||||||
- var future_lineupEntry
|
|
||||||
each future_event, i in events_future
|
|
||||||
- future_availability = availabilities.find((el)=>el.eventId ==future_event.id && el.memberId==row.member.id)
|
|
||||||
- future_lineupEntry = all_lineup_entries.find((el)=>el.eventId ==future_event.id && el.member.id==row.member.id)
|
|
||||||
- console.log(future_availability)
|
|
||||||
td(id=`avail-${row.member}-today-plus-${i+1}` class=`
|
|
||||||
row
|
|
||||||
future
|
|
||||||
availability
|
|
||||||
available-status-code-${future_availability.statusCode}
|
|
||||||
`)
|
|
||||||
if future_lineupEntry
|
|
||||||
|#{future_lineupEntry.label.slice(0,2)}
|
|
||||||
else
|
|
||||||
|#{future_availability.status[0]}
|
|
||||||
- var past_availability
|
|
||||||
- var past_lineupEntry
|
|
||||||
each past_event, i in events_past
|
|
||||||
- past_availability = availabilities.find((el)=>el.eventId==past_event.id && el.memberId==row.memberId)
|
|
||||||
- past_lineupEntry = all_lineup_entries.find((el)=>el.event.id==past_event.id && el.member.id==row.member.id)
|
|
||||||
td(id=`avail-${row.member}-today-minus-${i+1}` class=`
|
|
||||||
row
|
|
||||||
past
|
|
||||||
availability
|
|
||||||
available-status-code-${past_availability.statusCode}
|
|
||||||
${past_lineupEntry ? "started" : ""}
|
|
||||||
`)
|
|
||||||
if past_lineupEntry
|
|
||||||
|#{past_lineupEntry.label.slice(0,2)}
|
|
||||||
else
|
|
||||||
|#{past_availability.status[0]}
|
|
||||||
tfoot
|
|
||||||
tr
|
|
||||||
th(colspan='3')
|
|
||||||
th
|
|
||||||
th(colspan='4')
|
|
||||||
each event_future, i in events_future
|
|
||||||
th(class=`availability future`)
|
|
||||||
.rotate #{availabilitySummaries.find((el)=>el.eventId == event_future.id).playerGoingCount}
|
|
||||||
th.today-minus-1
|
|
||||||
.rotate
|
|
||||||
th.today-minus-2
|
|
||||||
.rotate
|
|
||||||
th.today-minus-3
|
|
||||||
.rotate
|
|
||||||
th.today-minus-4
|
|
||||||
.rotate
|
|
||||||
section#lineup-card-dugout.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
.bar-left.event-title
|
|
||||||
| #{event.formattedTitle}
|
|
||||||
.bar-right.homeaway #{event.gameType}
|
|
||||||
.starting-lineup-table
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th(colspan='4') Starting
|
|
||||||
tbody
|
|
||||||
each i in [0,1,2,3,4,5,6,7,8,9,10]
|
|
||||||
- if (typeof(event_lineup_entries_offense[i]) !== 'undefined'){
|
|
||||||
tr
|
|
||||||
th.sequence.label #{event_lineup_entries_offense[i].sequence +1}
|
|
||||||
td.player-name #{event_lineup_entries_offense[i].member.lastName}
|
|
||||||
td.jersey-number #{event_lineup_entries_offense[i].member.jerseyNumber}
|
|
||||||
td.position #{event_lineup_entries_offense[i].label}
|
|
||||||
- } else {
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- }
|
|
||||||
|
|
||||||
.substitution-table
|
|
||||||
table(style='width: 100%')
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th Substitution
|
|
||||||
tbody
|
|
||||||
each i in [0,1,2,3,4,5,6,7,8,9,10,11]
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
section#lineup-card-exchange.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header.event-title #{event.formattedTitleForMultiTeam}
|
|
||||||
.starting-lineup-table
|
|
||||||
table.starting-lineup-table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th
|
|
||||||
th.player-name Name
|
|
||||||
th.jersey-number Num
|
|
||||||
th.position Pos
|
|
||||||
tbody
|
|
||||||
each _,i in Array(10)
|
|
||||||
- if (typeof(event_lineup_entries_offense[i]) !== 'undefined'){
|
|
||||||
tr
|
|
||||||
th.sequence.label #{event_lineup_entries_offense[i].sequence+1}
|
|
||||||
td.player-name #{event_lineup_entries_offense[i].member.lastName}
|
|
||||||
td.jersey-number #{event_lineup_entries_offense[i].member.jerseyNumber}
|
|
||||||
td.position #{event_lineup_entries_offense[i].label}
|
|
||||||
- } else {
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- }
|
|
||||||
#page-2.sheet.gamecard
|
|
||||||
section#back-cover
|
|
||||||
section#front-cover
|
|
||||||
div.grid-container
|
|
||||||
.section-header
|
|
||||||
.bar-right.homeaway #{event.gameType}
|
|
||||||
.event-title
|
|
||||||
| #{event.startDate.toLocaleDateString("en-us",{weekday: "long", day: "numeric",month: "short"})},
|
|
||||||
| #{event.startDate.toLocaleTimeString("en-us",{hour: "numeric", minute: "2-digit"})}
|
|
||||||
br
|
|
||||||
| #{event.locationName}
|
|
||||||
div.team
|
|
||||||
|#{event.team.name}
|
|
||||||
div.opponent
|
|
||||||
|#{event.opponent.name}
|
|
||||||
section#lineup-card-dugout-empty.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
.starting-lineup-table
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th(colspan='4') Starting
|
|
||||||
tbody
|
|
||||||
each _ in Array(12)
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
.substitution-table
|
|
||||||
table(style='width: 100%')
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th Substitution
|
|
||||||
tbody
|
|
||||||
each _ in Array(11)
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
section#lineup-card-exchange-empty.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
.starting-lineup-table
|
|
||||||
table.starting-lineup-table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th
|
|
||||||
th.player-name Name
|
|
||||||
th.jersey-number Num
|
|
||||||
th.position Pos
|
|
||||||
tbody
|
|
||||||
each _ in Array(12)
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
include mixin-lineup-slot.pug
|
|
||||||
include mixin-availability-progress-bar.pug
|
|
||||||
|
|
||||||
block append styles
|
|
||||||
link(rel='stylesheet' href='/css/lineup.css')
|
|
||||||
|
|
||||||
block pre-scripts
|
|
||||||
script(type='text/javascript', src='/js/Sortable.js')
|
|
||||||
|
|
||||||
block content
|
|
||||||
div(id=`event-lineup-${event.id}`).event-lineup
|
|
||||||
.Panel
|
|
||||||
.panel-header
|
|
||||||
.Panel-title #{event.formattedTitle}
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row
|
|
||||||
p.text-muted.mb-2 #{event.startDate}
|
|
||||||
p #{event.locationName}
|
|
||||||
+availability-progress-bar(availabilitySummary, team)
|
|
||||||
|
|
||||||
.Panel
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row.Panel-title.u-padXs
|
|
||||||
i.bi.bi-clipboard-check.me-1
|
|
||||||
span Starting Lineup
|
|
||||||
.Panel-row.Grid.Grid--fit.fw-bold.text-center.u-padXs
|
|
||||||
each pos in ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"]
|
|
||||||
.Grid-cell.position-status #{pos}
|
|
||||||
.slot-set
|
|
||||||
each lineup_entry, i in event_lineup_entries
|
|
||||||
if !lineup_entry.label.includes("PO")
|
|
||||||
+lineup-slot(lineup_entry, i)
|
|
||||||
.Panel
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row.Panel-title.u-padXs
|
|
||||||
i.bi.bi-clipboard-minus.me-1
|
|
||||||
span Starting Lineup (Position Only)
|
|
||||||
.slot-set
|
|
||||||
each lineup_entry, i in event_lineup_entries
|
|
||||||
if lineup_entry.label.includes("[PO]")
|
|
||||||
+lineup-slot(lineup_entry, i)
|
|
||||||
.Panel
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row.Panel-title.u-padXs
|
|
||||||
i.bi.bi-clipboard.me-1
|
|
||||||
span Bench
|
|
||||||
.slot-set
|
|
||||||
each availability, i in availabilities.filter((a)=>a.eventId==event_id && !context.event_lineup_entries.map((lue)=>lue.memberId).includes(a.memberId) && !a.member.isNonPlayer && a.statusCode!=0 && a.statusCode!==null)
|
|
||||||
+lineup-slot(availability, i)
|
|
||||||
|
|
||||||
.Panel
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row.Panel-title.u-padXs
|
|
||||||
i.bi.bi-clipboard-x.me-1
|
|
||||||
span Out
|
|
||||||
.slot-set
|
|
||||||
each availability, i in availabilities.filter((a)=>a.eventId==event_id && !context.event_lineup_entries.map((lue)=>lue.memberId).includes(a.memberId) && !a.member.isNonPlayer && (a.statusCode==0 || a.statusCode===null))
|
|
||||||
+lineup-slot(availability, i)
|
|
||||||
block scripts
|
|
||||||
script
|
|
||||||
include lineup.js
|
|
||||||
script.
|
|
||||||
colorPositions();
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
html
|
|
||||||
head
|
|
||||||
meta(charset='utf-8')
|
|
||||||
title #{event.formattedTitle}
|
|
||||||
link(rel='stylesheet' href='/css/gamecard.css')
|
|
||||||
|
|
||||||
body(class="B5")
|
|
||||||
input(name="team_id", type="hidden" value=`${team_id}`)
|
|
||||||
input(name="event_id", type="hidden" value=`${event_id}`)
|
|
||||||
#page-1.sheet.gamecard
|
|
||||||
section#todays-game
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
#todays-game-header.bar-left.event-title
|
|
||||||
| #{event.formattedTitle}
|
|
||||||
| #{event.startDate.toLocaleDateString("en-us",{weekday: "short", day: "numeric",month: "short"})}
|
|
||||||
| #{event.startDate.toLocaleTimeString("en-us",{hour: "numeric", minute: "2-digit"})}
|
|
||||||
.bar-right.homeaway #{event.gameType}
|
|
||||||
.bar-span.gametitle
|
|
||||||
#offense-pane.left
|
|
||||||
table#starting-lineup-offense
|
|
||||||
tbody
|
|
||||||
each _, i in Array(11)
|
|
||||||
- if (typeof(event_lineup_entries_offense[i]) !== 'undefined'){
|
|
||||||
tr
|
|
||||||
th(rowspan='2') #{i+1}
|
|
||||||
td(id=`offense-slot-${i}-name` class="player-name") #{event_lineup_entries_offense[i].member.lastName}
|
|
||||||
td(id=`offense-slot-${i}-jersey-number` class="jersey-number") #{event_lineup_entries_offense[i].member.jerseyNumber}
|
|
||||||
td(id=`offense-slot-${i}-position` class="position") #{event_lineup_entries_offense[i].label}
|
|
||||||
tr.substitute
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- } else {
|
|
||||||
tr
|
|
||||||
th(rowspan='2')
|
|
||||||
td(id=`offense-slot-${i}-name` class="player-name")
|
|
||||||
td(id=`offense-slot-${i}-jersey-number` class="jersey-number")
|
|
||||||
td(id=`offense-slot-${i}-position` class="position")
|
|
||||||
tr.substitute
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- }
|
|
||||||
|
|
||||||
#defense-pane.right
|
|
||||||
.container
|
|
||||||
.field-container
|
|
||||||
image(src='/media/baseball-diamond.svg')
|
|
||||||
.row(style='justify-content: center')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position CF
|
|
||||||
td#defense-slot-CF-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("CF")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: space-between')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position LF
|
|
||||||
td#defense-slot-LF-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("LF")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position RF
|
|
||||||
td#defense-slot-RF-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("RF")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: space-around')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position SS
|
|
||||||
td#defense-slot-SS-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("SS")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position 2B
|
|
||||||
td#defense-slot-2B-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("2B")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: space-between')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position 3B
|
|
||||||
td#defense-slot-3B-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("3B")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position 1B
|
|
||||||
td#defense-slot-1B-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("1B")) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.row(style='justify-content: center')
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position C
|
|
||||||
td#defense-slot-C-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("C") && !lue.label.startsWith("CF") ) || {"member":{}}).member.lastName}
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
tr
|
|
||||||
td(colspan='2')
|
|
||||||
.pitching-container
|
|
||||||
.defense-slot-set
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th.position P
|
|
||||||
td#defense-slot-P-name.player-name
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("P")) || {"member":{}}).member.lastName}
|
|
||||||
td.jersey-number
|
|
||||||
| #{(event_lineup_entries.find((lue)=>lue.label.startsWith("P")) || {"member":{}}).member.jerseyNumber}
|
|
||||||
td.position
|
|
||||||
tr
|
|
||||||
th.position RP
|
|
||||||
td#defense-slot-RP1-name.player-name
|
|
||||||
td
|
|
||||||
td
|
|
||||||
tr
|
|
||||||
th.position RP
|
|
||||||
td#defense-slot-RP2-name.player-name
|
|
||||||
td
|
|
||||||
td
|
|
||||||
.footer
|
|
||||||
table
|
|
||||||
tr
|
|
||||||
th Notes
|
|
||||||
td
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
section#roster-and-history
|
|
||||||
div
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th#today-availability(colspan='3') Available (
|
|
||||||
| #{availabilitySummaries.find((e)=>e.id==event_id).playerGoingCount}|
|
|
||||||
| #{availabilitySummaries.find((e)=>e.id==event_id).playerMaybeCount}
|
|
||||||
| )
|
|
||||||
th.player-stats
|
|
||||||
span.decimal-point .
|
|
||||||
| AVG
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
| OBP
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
| SLG
|
|
||||||
span.delimiter :
|
|
||||||
| PA
|
|
||||||
th.position-capability.pitcher P
|
|
||||||
th.position-capability.catcher C
|
|
||||||
th.position-capability.infield I
|
|
||||||
th.position-capability.outfield O
|
|
||||||
each event_future, i in events_future
|
|
||||||
th(id=`avail-header-today-plus-${i+1}` class="availability future")
|
|
||||||
.rotate #{event_future.startDate.toLocaleDateString("en-us", {weekday: "short"})}
|
|
||||||
each event_past, i in events_past
|
|
||||||
th(id=`avail-header-today-minus-${i+1}` class="availability past")
|
|
||||||
.rotate #{event_past.startDate.toLocaleDateString("en-us", {weekday: "short"})}
|
|
||||||
tbody
|
|
||||||
each row, index in availabilities.filter((e)=>e.event.id==event_id && !e.member.isNonPlayer)
|
|
||||||
tr(id=`roster-history-slot-${index+1}` class=``)
|
|
||||||
td(class=`is-present-checkbox available-status-code-${row.statusCode}`)
|
|
||||||
span ■
|
|
||||||
td(
|
|
||||||
class=`
|
|
||||||
jersey-number
|
|
||||||
border-left
|
|
||||||
available-status-code-${row.statusCode}
|
|
||||||
${event_lineup_entries.find((lue)=>lue.member.id==row.member.id) !== undefined ? "starting" : ""}
|
|
||||||
`)
|
|
||||||
| #{row.member.jerseyNumber}
|
|
||||||
td(
|
|
||||||
class=`
|
|
||||||
player-name
|
|
||||||
available-status-code-${row.statusCode}
|
|
||||||
${event_lineup_entries.find((lue)=>lue.member.id==row.member.id) !== undefined ? "starting" : ""}
|
|
||||||
`)
|
|
||||||
| #{row.member.lastName}
|
|
||||||
td.player-stats.border-left.border-right
|
|
||||||
span.decimal-point .
|
|
||||||
span.avg 000
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
span.obp 000
|
|
||||||
span.delimiter /
|
|
||||||
span.decimal-point .
|
|
||||||
span.slg 000
|
|
||||||
span.delimiter :
|
|
||||||
span.pa 00
|
|
||||||
td.position-capability.pitcher #{row.member.position.includes("P") ? "\u2713" : ""}
|
|
||||||
td.position-capability.catcher #{row.member.position.includes("C") ? "\u2713" : ""}
|
|
||||||
td.position-capability.infield #{row.member.position.includes("IF") ? "\u2713" : ""}
|
|
||||||
td.position-capability.outfield #{row.member.position.includes("OF") ? "\u2713" : ""}
|
|
||||||
- var future_availability
|
|
||||||
- var future_lineupEntry
|
|
||||||
each future_event, i in events_future
|
|
||||||
- future_availability = availabilities.find((el)=>el.eventId ==future_event.id && el.memberId==row.member.id)
|
|
||||||
- future_lineupEntry = all_lineup_entries.find((el)=>el.eventId ==future_event.id && el.member.id==row.member.id)
|
|
||||||
- console.log(future_availability)
|
|
||||||
td(id=`avail-${row.member}-today-plus-${i+1}` class=`
|
|
||||||
row
|
|
||||||
future
|
|
||||||
availability
|
|
||||||
available-status-code-${future_availability.statusCode}
|
|
||||||
`)
|
|
||||||
if future_lineupEntry
|
|
||||||
|#{future_lineupEntry.label.slice(0,2)}
|
|
||||||
else
|
|
||||||
|#{future_availability.status[0]}
|
|
||||||
- var past_availability
|
|
||||||
- var past_lineupEntry
|
|
||||||
each past_event, i in events_past
|
|
||||||
- past_availability = availabilities.find((el)=>el.eventId==past_event.id && el.memberId==row.memberId)
|
|
||||||
- past_lineupEntry = all_lineup_entries.find((el)=>el.event.id==past_event.id && el.member.id==row.member.id)
|
|
||||||
td(id=`avail-${row.member}-today-minus-${i+1}` class=`
|
|
||||||
row
|
|
||||||
past
|
|
||||||
availability
|
|
||||||
available-status-code-${past_availability.statusCode}
|
|
||||||
${past_lineupEntry ? "started" : ""}
|
|
||||||
`)
|
|
||||||
if past_lineupEntry
|
|
||||||
|#{past_lineupEntry.label.slice(0,2)}
|
|
||||||
else
|
|
||||||
|#{past_availability.status[0]}
|
|
||||||
tfoot
|
|
||||||
tr
|
|
||||||
th(colspan='3')
|
|
||||||
th
|
|
||||||
th(colspan='4')
|
|
||||||
each event_future, i in events_future
|
|
||||||
th(class=`availability future`)
|
|
||||||
.rotate #{availabilitySummaries.find((el)=>el.eventId == event_future.id).playerGoingCount}
|
|
||||||
th.today-minus-1
|
|
||||||
.rotate
|
|
||||||
th.today-minus-2
|
|
||||||
.rotate
|
|
||||||
th.today-minus-3
|
|
||||||
.rotate
|
|
||||||
th.today-minus-4
|
|
||||||
.rotate
|
|
||||||
section#lineup-card-dugout.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
.bar-left.event-title
|
|
||||||
| #{event.formattedTitle}
|
|
||||||
.bar-right.homeaway #{event.gameType}
|
|
||||||
.starting-lineup-table
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th(colspan='4') Starting
|
|
||||||
tbody
|
|
||||||
each i in [0,1,2,3,4,5,6,7,8,9,10]
|
|
||||||
- if (typeof(event_lineup_entries_offense[i]) !== 'undefined'){
|
|
||||||
tr
|
|
||||||
th.sequence.label #{event_lineup_entries_offense[i].sequence +1}
|
|
||||||
td.player-name #{event_lineup_entries_offense[i].member.lastName}
|
|
||||||
td.jersey-number #{event_lineup_entries_offense[i].member.jerseyNumber}
|
|
||||||
td.position #{event_lineup_entries_offense[i].label}
|
|
||||||
- } else {
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- }
|
|
||||||
|
|
||||||
.substitution-table
|
|
||||||
table(style='width: 100%')
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th Substitution
|
|
||||||
tbody
|
|
||||||
each i in [0,1,2,3,4,5,6,7,8,9,10,11]
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
section#lineup-card-exchange.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header.event-title #{event.formattedTitleForMultiTeam}
|
|
||||||
.starting-lineup-table
|
|
||||||
table.starting-lineup-table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th
|
|
||||||
th.player-name Name
|
|
||||||
th.jersey-number Num
|
|
||||||
th.position Pos
|
|
||||||
tbody
|
|
||||||
each _,i in Array(10)
|
|
||||||
- if (typeof(event_lineup_entries_offense[i]) !== 'undefined'){
|
|
||||||
tr
|
|
||||||
th.sequence.label #{event_lineup_entries_offense[i].sequence+1}
|
|
||||||
td.player-name #{event_lineup_entries_offense[i].member.lastName}
|
|
||||||
td.jersey-number #{event_lineup_entries_offense[i].member.jerseyNumber}
|
|
||||||
td.position #{event_lineup_entries_offense[i].label}
|
|
||||||
- } else {
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
td
|
|
||||||
- }
|
|
||||||
#page-2.sheet.gamecard
|
|
||||||
section#back-cover
|
|
||||||
section#front-cover
|
|
||||||
div.grid-container
|
|
||||||
.section-header
|
|
||||||
.bar-right.homeaway #{event.gameType}
|
|
||||||
.event-title
|
|
||||||
| #{event.startDate.toLocaleDateString("en-us",{weekday: "long", day: "numeric",month: "short"})},
|
|
||||||
| #{event.startDate.toLocaleTimeString("en-us",{hour: "numeric", minute: "2-digit"})}
|
|
||||||
br
|
|
||||||
| #{event.locationName}
|
|
||||||
div.team
|
|
||||||
|#{event.team.name}
|
|
||||||
div.opponent
|
|
||||||
|#{event.opponent.name}
|
|
||||||
section#lineup-card-dugout-empty.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
.starting-lineup-table
|
|
||||||
table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th(colspan='4') Starting
|
|
||||||
tbody
|
|
||||||
each _ in Array(12)
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
.substitution-table
|
|
||||||
table(style='width: 100%')
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th Substitution
|
|
||||||
tbody
|
|
||||||
each _ in Array(11)
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
tr
|
|
||||||
td.substitution
|
|
||||||
section#lineup-card-exchange-empty.lineup-card
|
|
||||||
.grid-container
|
|
||||||
.section-header
|
|
||||||
.starting-lineup-table
|
|
||||||
table.starting-lineup-table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th
|
|
||||||
th.player-name Name
|
|
||||||
th.jersey-number Num
|
|
||||||
th.position Pos
|
|
||||||
tbody
|
|
||||||
each _ in Array(12)
|
|
||||||
tr
|
|
||||||
th.sequence.label
|
|
||||||
td.player-name
|
|
||||||
td.jersey-number
|
|
||||||
td.position
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
include mixin-availability-progress-bar.pug
|
|
||||||
|
|
||||||
block content
|
|
||||||
.Panel
|
|
||||||
.Panel-header
|
|
||||||
h3.Panel-title #{event.formattedTitle}
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row
|
|
||||||
h6.card-text.text-muted.mb-2
|
|
||||||
|#{event.startDate}
|
|
||||||
br
|
|
||||||
|#{event.locationName}
|
|
||||||
.Panel-row
|
|
||||||
h4 Availability
|
|
||||||
+availability-progress-bar(availabilitySummary, team)
|
|
||||||
.Panel-row
|
|
||||||
div.d-flex
|
|
||||||
a(class="Button m-auto" href=`/${team_id}/event/${event.id}/lineup`)
|
|
||||||
i(class="bi bi-clipboard")
|
|
||||||
span.mx-1 Lineup
|
|
||||||
a(class="Button m-auto" href=`/${team_id}/event/${event.id}/lineup_card`)
|
|
||||||
i(class="bi bi-book")
|
|
||||||
span.mx-1 Game Card
|
|
||||||
a(class="Button m-auto" href=`https://go.teamsnap.com/${team_id}/schedule/view_game/${event.id}`)
|
|
||||||
i(class="bi bi-asterisk")
|
|
||||||
span.mx-1 TeamSnap
|
|
||||||
120
src/views/event/list.hbs
Normal file
120
src/views/event/list.hbs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<h1>
|
||||||
|
Schedule
|
||||||
|
</h1>
|
||||||
|
<div class="Panel">
|
||||||
|
<div class="Panel-body">
|
||||||
|
<div class="Panel-row Panel-row--withCells u-textDecorationNone">
|
||||||
|
<div class="Panel-cell u-size7of24 u-textLeft">
|
||||||
|
<h4 class="Panel-title">
|
||||||
|
Title
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-hidden u-xs-block">
|
||||||
|
<h4 class="Panel-title">
|
||||||
|
Date
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-hidden u-xs-block">
|
||||||
|
<h4 class="Panel-title">
|
||||||
|
Time
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-xs-hidden">
|
||||||
|
<h4 class="Panel-title">
|
||||||
|
When/Where
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size7of24 u-hidden u-xs-block">
|
||||||
|
<h4 class="Panel-title">
|
||||||
|
Location
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-borderLeft">
|
||||||
|
<h4 class="Panel-title">
|
||||||
|
...
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#each events as |event|}}
|
||||||
|
<div class="Panel-row Panel-row--withCells">
|
||||||
|
<div class="Panel-cell u-size7of24 u-textLeft">
|
||||||
|
<a href="event/{{this.id}}">
|
||||||
|
{{event.formattedTitle}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-textLeft u-hidden u-xs-block">
|
||||||
|
{{dateFormat event.startDate "ddd MMM D"}}
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-hidden u-xs-block">
|
||||||
|
{{dateFormat event.startDate "h:mm A"}}
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-xs-hidden">
|
||||||
|
{{dateFormat this.startDate "ddd MMM D, h:mm A"}} {{
|
||||||
|
event.locationName
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size7of24 u-hidden u-xs-block">
|
||||||
|
{{event.locationName}}
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-size3of24 u-borderLeft">
|
||||||
|
<button
|
||||||
|
class="Button Button--small Button--default Popup"
|
||||||
|
onclick='console.log(this);this.querySelector(".Popup-container").classList.toggle("is-open")'
|
||||||
|
;
|
||||||
|
>
|
||||||
|
...
|
||||||
|
<div
|
||||||
|
class="Popup-container Popup-container--up Popup-container--right"
|
||||||
|
>
|
||||||
|
<div class="Popup-content u-textDecorationNone">
|
||||||
|
<div class="Grid Grid--fit u-spaceXs">
|
||||||
|
<div class="Grid-cell u-sizeFit u-spaceXs">
|
||||||
|
<a href="url_for(event_path :team_id=>team.id, :event_id=>event.id)">
|
||||||
|
<span>{{{embeddedSvgFromPath "/bootstrap-icons/calendar.svg"}}}</span>
|
||||||
|
<span class="u-hidden u-xs-inline">Details</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="Grid-cell u-sizeFit u-spaceXs">
|
||||||
|
<a href="/{{team.id}}/event/{{event.id}}/lineup">
|
||||||
|
<span>{{{embeddedSvgFromPath "/bootstrap-icons/clipboard.svg"}}}</span>
|
||||||
|
<span class="u-hidden u-xs-inline">Lineup</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="Grid-cell u-sizeFit u-spaceXs">
|
||||||
|
<a 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>
|
||||||
|
</div>
|
||||||
|
<div class="Grid-cell u-sizeFit u-spaceXs">
|
||||||
|
<a 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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{!--
|
||||||
|
|
||||||
|
a.Button href=url_for(event_path :team_id=>team.id, :event_id=>event.id)
|
||||||
|
=embedded_svg "teamsnap-ui/assets/icons/schedule svg", class:"Icon"
|
||||||
|
| Details
|
||||||
|
.Grid-cell.u-sizeFit.u-spaceXs
|
||||||
|
a.Button href=url_for(event_lineup_path :team_id => team.id, :event_id => event.id)
|
||||||
|
=embedded_svg "bootstrap-icons/clipboard svg", class:"Icon"
|
||||||
|
| Lineup
|
||||||
|
.Grid-cell.u-sizeFit.u-spaceXs
|
||||||
|
a.Button href=url_for(event_lineup_card_path :team_id => team.id, :event_id => event.id)
|
||||||
|
=embedded_svg "bootstrap-icons/book svg", class:"Icon"
|
||||||
|
| Card
|
||||||
|
.Grid-cell.u-sizeFit.u-spaceXs
|
||||||
|
a.Button href="https://go teamsnap com/#{team id}/schedule/view_game/#{event id}"
|
||||||
|
=embedded_svg "bootstrap-icons/asterisk svg", class:"Icon"
|
||||||
|
| TeamSnap }} --}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
16
src/views/event/partials/availability_bar.hbs
Normal file
16
src/views/event/partials/availability_bar.hbs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="progress">
|
||||||
|
{{#if availabilitySummary}}
|
||||||
|
<div class="progress-bar availability-bar going" role="progressbar" style="width:{{availability_percentage availabilitySummary "going"}}%">
|
||||||
|
{{availabilitySummary.playerGoingCount}}
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar availability-bar maybe" role="progressbar" style="width:{{availability_percentage availabilitySummary "maybe"}}%">
|
||||||
|
{{availabilitySummary.playerMaybeCount}}
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar availability-bar not-going" role="progressbar" style="width:{{availability_percentage availabilitySummary "notgoing"}}%">
|
||||||
|
{{availabilitySummary.playerNotGoingCount}}
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar availability-bar unknown" role="progressbar" style="width:{{availability_percentage availabilitySummary "unknown"}}%">
|
||||||
|
{{availabilitySummary.playerUnknownCount}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
32
src/views/event/partials/event_panel.hbs
Normal file
32
src/views/event/partials/event_panel.hbs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<div class="Panel">
|
||||||
|
<div class="Panel-header u-padEndsSm">
|
||||||
|
<h3><a href="/{{team.id}}/event/{{event.id}}">{{event.formattedTitle}}</a></h3>
|
||||||
|
</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">
|
||||||
|
{{> availability_bar availabilitySummary=event.availabilitySummary}}
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
1
src/views/event/show.hbs
Normal file
1
src/views/event/show.hbs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{> event_panel event=event}}
|
||||||
129
src/views/eventlineup/edit.hbs
Normal file
129
src/views/eventlineup/edit.hbs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
{{>emailmodal}}
|
||||||
|
<div 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="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>
|
||||||
|
Save
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div class="Button Button--orange .u-padSidesXs Popup" onclick="togglePopup(this)">
|
||||||
|
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/caret-down.svg"}}}
|
||||||
|
<div class="Popup-container Popup-container--down Popup-container--right" style="width: 200px">
|
||||||
|
<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>
|
||||||
|
</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">
|
||||||
|
<span style="flex: 1 1 0%;">{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-x.svg"}}}Out</span>
|
||||||
|
<div class="Toggle">
|
||||||
|
<input class="Toggle-input" type="checkbox" id="enable-slots" onclick="toggleChildSlots(this);">
|
||||||
|
<label class="Toggle-label" for="availability-tab"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slot-set">
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInOut this)}}
|
||||||
|
{{> slot member=this}}
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
|
||||||
|
<script src="/js/eventlineup.js"></script>
|
||||||
|
<script src="/js/tinymce.min.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
colorPositions();
|
||||||
|
refreshLineup();
|
||||||
|
tinymce.init({
|
||||||
|
selector:"#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})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
70
src/views/eventlineup/email.hbs
Normal file
70
src/views/eventlineup/email.hbs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<div class="lineup-email">
|
||||||
|
<div>
|
||||||
|
<p>Team,</p>
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell" colSpan=3>
|
||||||
|
STARTING LINEUP
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInStartingLineup this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell">
|
||||||
|
{{plus1 this.benchcoach.eventLineupEntry.sequence}}
|
||||||
|
</td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell" colSpan=3>Starting (Pos. Only)</th>
|
||||||
|
</tr>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInPositionOnly this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell"></td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell" colSpan=3>Subs</th>
|
||||||
|
</tr>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInBench this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell">
|
||||||
|
{{availabilityStatusShort this.benchcoach.availability}}
|
||||||
|
</td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell out" colSpan=3>Out</th>
|
||||||
|
</tr>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInOut this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell">
|
||||||
|
{{availabilityStatusShort this.benchcoach.availability}}
|
||||||
|
</td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
62
src/views/eventlineup/partials/email_table.hbs
Normal file
62
src/views/eventlineup/partials/email_table.hbs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell" colSpan=3>
|
||||||
|
STARTING LINEUP
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInStartingLineup this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell">
|
||||||
|
{{plus1 this.benchcoach.eventLineupEntry.sequence}}
|
||||||
|
</td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell" colSpan=3>Starting (Pos. Only)</th>
|
||||||
|
</tr>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInPositionOnly this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell"></td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell" colSpan=3>Subs</th>
|
||||||
|
</tr>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInBench this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell">
|
||||||
|
{{availabilityStatusShort this.benchcoach.availability}}
|
||||||
|
</td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
<tr>
|
||||||
|
<th class="title-cell out" colSpan=3>Out</th>
|
||||||
|
</tr>
|
||||||
|
{{#each members}}
|
||||||
|
{{#if (isInOut this)}}
|
||||||
|
<tr>
|
||||||
|
<td class="sequence-cell">
|
||||||
|
{{availabilityStatusShort this.benchcoach.availability}}
|
||||||
|
</td>
|
||||||
|
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||||
|
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
28
src/views/eventlineup/partials/emailmodal.hbs
Normal file
28
src/views/eventlineup/partials/emailmodal.hbs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<div id="modal" class="Modal Modal--clickableBg">
|
||||||
|
<div class="Modal-bgDismiss" onclick="javascript:this.closest('.Modal').classList.toggle('is-open')"></div>
|
||||||
|
<div class="Modal-content">
|
||||||
|
<div onclick="javascript:this.closest('.Modal').classList.toggle('is-open')">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg" "Modal-iconDismiss"}}}</div>
|
||||||
|
<div class="Modal-header">
|
||||||
|
<div class="Modal-title">Email</div>
|
||||||
|
</div>
|
||||||
|
<div class="Modal-body">
|
||||||
|
<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 }})">
|
||||||
|
</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"></div>
|
||||||
|
</div>
|
||||||
|
<div class="FieldGroup">
|
||||||
|
<button class="Button" role="button" onclick="submitEmail();">Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
58
src/views/eventlineup/partials/slot.hbs
Normal file
58
src/views/eventlineup/partials/slot.hbs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<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}}">
|
||||||
|
<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="memberId" value="{{member.id}}">
|
||||||
|
<input type="hidden" name="lastName" value="{{member.lastName}}">
|
||||||
|
<input type="hidden" name="firstName" value="{{member.firstName}}">
|
||||||
|
<input type="hidden" name="jerseyNumber" value="{{member.jerseyNumber}}">
|
||||||
|
<input type="hidden" name="emailAddresses" value="{{member.emailAddresses}}">
|
||||||
|
<div class="Panel-row Panel-row--withCells Panel-row--parent">
|
||||||
|
<div
|
||||||
|
class="Panel-cell Panel-cell--header">
|
||||||
|
<div class="sequence u-textNoWrap u-fontSizeLg"></div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-padXs u-sizeFill">
|
||||||
|
<div
|
||||||
|
class="d-flex availability-status-code-{{
|
||||||
|
member.benchcoach.availability?.statusCode
|
||||||
|
}}"
|
||||||
|
>
|
||||||
|
<div class="u-flexInline u-fontSizeLg u-textNoWrap">
|
||||||
|
{{#if member.benchcoach.availability}}{{{avail_status_code_icon member.benchcoach.availability.statusCode}}}{{/if}}
|
||||||
|
<span class="lastname">
|
||||||
|
{{member.lastName}}
|
||||||
|
</span>
|
||||||
|
<span class="lastname u-hidden u-sm-inline">
|
||||||
|
, {{member.firstName}}
|
||||||
|
</span>
|
||||||
|
<span class="jerseynumber u-hidden u-sm-inline u-fontSizeSm">
|
||||||
|
#{{member.jerseyNumber}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-padXs u-sizeFit">
|
||||||
|
<div class="SelectBox position-selection">
|
||||||
|
<select name="positionLabelSelectBox" class="position-select-box SelectBox-options" onchange="onPositionSelectChange(this)" >
|
||||||
|
<option value="--">
|
||||||
|
--
|
||||||
|
</option>
|
||||||
|
{{#each (positions)}}
|
||||||
|
<option value="{{this}}" {{#if (comparePositionWithFlags this ../member.benchcoach.eventLineupEntry)}}selected{{/if}}>
|
||||||
|
{{this}}
|
||||||
|
</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell u-padSidesMd u-sizeFit">
|
||||||
|
<div class="drag-handle">
|
||||||
|
{{{embeddedSvgFromPath "/bootstrap-icons/grip-vertical.svg"}}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
0
src/views/eventlineup/show.hbs
Normal file
0
src/views/eventlineup/show.hbs
Normal file
@@ -1,21 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
include mixin-availability-progress-bar.pug
|
|
||||||
|
|
||||||
block content
|
|
||||||
.Panel
|
|
||||||
.Panel-header
|
|
||||||
.Panel-title Schedule
|
|
||||||
.Panel-body
|
|
||||||
each event in events
|
|
||||||
- var availabilitySummary = availabilitySummaries.find((a)=>a.eventId==event.id)
|
|
||||||
a(class="event list-group-item" href=`/${team_id}/event/${event.id}`).Panel-row--withCells
|
|
||||||
.Panel-cell
|
|
||||||
h4 #{event.formattedTitle}
|
|
||||||
+availability-progress-bar(availabilitySummary, team)
|
|
||||||
.Panel-cell
|
|
||||||
|#{event.startDate.toLocaleDateString("en-us",{weekday: "short", day: "numeric",month: "short"})}
|
|
||||||
.Panel-cell
|
|
||||||
|#{event.startDate.toLocaleTimeString("en-us",{hour: "numeric", minute: "2-digit"})}
|
|
||||||
.Panel-cell
|
|
||||||
|#{event.locationName}
|
|
||||||
|
|
||||||
22
src/views/eventsheet/partials/defense_pane.hbs
Normal file
22
src/views/eventsheet/partials/defense_pane.hbs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<div class="field-container">
|
||||||
|
<img src="/media/baseball-diamond.svg" />
|
||||||
|
{{#defenseLineup event_lineup_entries members}}
|
||||||
|
<div class="slot-set pos-{{this.position}}">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr class="slot">
|
||||||
|
<th class="position"></th>
|
||||||
|
<td class="player-name">{{this.member.lastName}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="slot substitute">
|
||||||
|
<th class="position"></th>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="slot substitute">
|
||||||
|
<th class="position"></th><td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{/defenseLineup}}
|
||||||
|
</div>
|
||||||
20
src/views/eventsheet/partials/offense_pane.hbs
Normal file
20
src/views/eventsheet/partials/offense_pane.hbs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<div class="slot-set">
|
||||||
|
{{!-- <% offensive_lineup_entries = by_member.select{|m,d| d[:event_lineup_entry] and d[:event_lineup_entry].label.exclude?("[PO]")}.sort_by{|m,d| d[:event_lineup_entry].sequence}.each_with_index do |(member, d), i| if i < 11%> --}}
|
||||||
|
{{#offenseLineup 11 event_lineup_entries members}}
|
||||||
|
<tr class="slot">
|
||||||
|
<th class="sequence counter" rowspan="2"></th>
|
||||||
|
<td class="player-name">{{this.member.lastName}}</td>
|
||||||
|
<td class="jersey-number">{{this.member.jerseyNumber}}</td>
|
||||||
|
<td class="position">{{positionLabelWithoutFlags this.label}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="slot substitute">
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{{/offenseLineup}}
|
||||||
|
</div>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
80
src/views/eventsheet/partials/roster_and_history.hbs
Normal file
80
src/views/eventsheet/partials/roster_and_history.hbs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<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="availability-on-day future"></colgroup>
|
||||||
|
<colgroup><col span="4" class="availability-on-day past"></colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="3" id="today-availability">
|
||||||
|
Available ({{availabilitySummary.playerGoingCount}}|{{availabilitySummary.playerMaybeCount}})
|
||||||
|
</th>
|
||||||
|
<th class="player-stats">
|
||||||
|
<span class="decimal-point">.</span>AVG
|
||||||
|
<span class="delimiter">/</span>
|
||||||
|
<span class="decimal-point">.</span>OBP
|
||||||
|
<span class="delimiter">/</span>
|
||||||
|
<span class="decimal-point">.</span>SLG
|
||||||
|
<span class="delimiter">:</span>PA
|
||||||
|
</th>
|
||||||
|
<th class="position-capability pitcher">P</th>
|
||||||
|
<th class="position-capability catcher">C</th>
|
||||||
|
<th class="position-capability infield">I</th>
|
||||||
|
<th class="position-capability outfield">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>
|
||||||
|
{{/loopEvents}}
|
||||||
|
{{#loopEvents recent_events}}
|
||||||
|
<th class="availability-on-day avail-today-minus-{{@index}}" 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}}">
|
||||||
|
<span>■</span>
|
||||||
|
</td>
|
||||||
|
<td class="jersey-number available-status-code-{{this.benchcoach.availability.statusCode}}{{#if (isStarting this)}} starting{{/if}}">
|
||||||
|
{{this.jerseyNumber}}
|
||||||
|
</td>
|
||||||
|
<td class="player-name available-status-code-{{this.benchcoach.availability.statusCode}}{{#if (isStarting this)}} starting{{/if}}">
|
||||||
|
{{this.lastName}}
|
||||||
|
</td>
|
||||||
|
<td class="player-stats border-left border-right">
|
||||||
|
<span class="decimal-point">.</span>
|
||||||
|
<span class="avg">000</span>
|
||||||
|
<span class="delimiter">/</span>
|
||||||
|
<span class="decimal-point">.</span>
|
||||||
|
<span class="obp">000</span>
|
||||||
|
<span class="delimiter">/</span>
|
||||||
|
<span class="decimal-point">.</span>
|
||||||
|
<span class="slg">000</span>
|
||||||
|
<span class="delimiter">:</span>
|
||||||
|
<span class="pa">00</span>
|
||||||
|
</td>
|
||||||
|
<td class="position-capability pitcher">{{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>
|
||||||
|
{{#loopEvents ../upcoming_events}}
|
||||||
|
{{#timepointForMember ../this ../../timeline this}}
|
||||||
|
<td class="availability-on-day future available-status-code-{{this.availability.statusCode}}">
|
||||||
|
{{this.value}}
|
||||||
|
</td>
|
||||||
|
{{/timepointForMember}}
|
||||||
|
{{/loopEvents}}
|
||||||
|
{{#loopEvents ../recent_events}}
|
||||||
|
{{#timepointForMember ../this ../../timeline this}}
|
||||||
|
<td class="availability-on-day past available-status-code-{{this.availability.statusCode}}">
|
||||||
|
{{this.value}}
|
||||||
|
</td>
|
||||||
|
{{/timepointForMember}}
|
||||||
|
{{/loopEvents}}
|
||||||
|
</tr>
|
||||||
|
{{/rosterHistory}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
254
src/views/eventsheet/sheet.hbs
Normal file
254
src/views/eventsheet/sheet.hbs
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
<link rel="stylesheet" href="/css/eventsheet.css">
|
||||||
|
|
||||||
|
<body class="B5">
|
||||||
|
<div class="sheet eventsheet" id="page-1">
|
||||||
|
<section id="defense-card">
|
||||||
|
<header>
|
||||||
|
<div class="event-title float-left">
|
||||||
|
{{event.formattedTitle}} – {{dateFormat event.startDate "ddd, MMM D h:mm A" }}
|
||||||
|
</div>
|
||||||
|
<div class="homeaway float-right">
|
||||||
|
{{event.gameType}}
|
||||||
|
</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 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="lineup-card" 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="lineup-card" 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" id="page-2">
|
||||||
|
<section id="defense-card" style="border:solid black;">
|
||||||
|
<div id="defense-pane">
|
||||||
|
{{> defense_pane}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="front-cover">
|
||||||
|
<header>
|
||||||
|
<div class="homeaway">
|
||||||
|
<span>{{firstLetter event.gameType}}</span>
|
||||||
|
</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>
|
||||||
|
</header>
|
||||||
|
<div style="display:block;max-height: 1em;background-color: lightgray;border-bottom: solid 2px black;">
|
||||||
|
</div>
|
||||||
|
<div class="head-to-head">
|
||||||
|
<div class="team">
|
||||||
|
<img src="{{team_preferences.links.teamLogo.href}}">
|
||||||
|
<div>
|
||||||
|
<span class="name">{{team.name}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{# if event.opponentName}}
|
||||||
|
<div>
|
||||||
|
<div style="text-align: center;font-family: 'Pacifico';">
|
||||||
|
<span>vs</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="opponent">
|
||||||
|
<div>
|
||||||
|
<span class="name">{{event.opponentName}}</span>
|
||||||
|
</div>
|
||||||
|
<img src="{{opponent_logo.mediumUrl}}">
|
||||||
|
{{!-- <%= ::Temple::Utils.escape_html((image_tag opponent_logos[event.opponent.id].medium_url)) %> --}}
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Final Score</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>{{team.name}}</th>
|
||||||
|
<th>{{event.opponentName}}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="height:5em;"></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="lineup-card" id="lineup-card-dugout-blank">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
|
|
||||||
block content
|
|
||||||
.row
|
|
||||||
.text-center.my-2
|
|
||||||
.row
|
|
||||||
h1
|
|
||||||
img.mx-auto(src="media/benchcoach.svg" style="width: 2.5em;")
|
|
||||||
.row
|
|
||||||
h1
|
|
||||||
strong
|
|
||||||
| Welcome to
|
|
||||||
span.text-nowrap BenchCoach
|
|
||||||
.text-center.lead.fst-italic.fw-light
|
|
||||||
| An assistant coach for TeamSnap
|
|
||||||
.row
|
|
||||||
.col.text-center
|
|
||||||
if req.user
|
|
||||||
ul.list-group
|
|
||||||
each team in teams
|
|
||||||
a(class='team list-group-item' href=`/${team.id}/home`) #{team.name} [#{team.seasonName}]
|
|
||||||
else
|
|
||||||
a.btn.btn-outline-primary(href="login")
|
|
||||||
| Login
|
|
||||||
33
src/views/layouts/main.hbs
Normal file
33
src/views/layouts/main.hbs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="stylesheet" href="/css/application.css">
|
||||||
|
{{#if style}}<link rel="stylesheet" href="/css/{{style}}">{{/if}}
|
||||||
|
<title>{{#if title}}{{title}}{{else}}BenchCoach{{/if}}</title>
|
||||||
|
<script>
|
||||||
|
async function setSession() {
|
||||||
|
const response = await fetch("/auth/teamsnap/session_storage")
|
||||||
|
const session_storage = await response.json();
|
||||||
|
for (const [key, value] of Object.entries(session_storage)) {
|
||||||
|
window.sessionStorage.setItem(key,value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSession();
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="bg-light">
|
||||||
|
<header class="Header">
|
||||||
|
{{> 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-padBottomMd u-xs-padEndsLg u-sm-padEndsXl">
|
||||||
|
{{{ body }}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -14,10 +14,7 @@ head
|
|||||||
|
|
||||||
body.bg-light
|
body.bg-light
|
||||||
block navbar
|
block navbar
|
||||||
.benchcoach-nav.u-flex.u-flexJustifyBetween.u-flexAlignItemsCenter
|
include navbar.pug
|
||||||
.u-flex.u-flexAlignItemsCenter
|
|
||||||
img(src='/media/benchcoach.svg' alt='TeamSnap Logo' width='30' height='30')
|
|
||||||
h3.u-padLeft.u-colorWhite.u-noMarginBottom BenchCoach
|
|
||||||
|
|
||||||
.u-spaceSm.u-md-spaceLg
|
.u-spaceSm.u-md-spaceLg
|
||||||
block content
|
block content
|
||||||
@@ -1,356 +0,0 @@
|
|||||||
/* Project specific Javascript goes here. */
|
|
||||||
function onPositionSelectChange(elem) {
|
|
||||||
elem.querySelectorAll("option").forEach((option) => {
|
|
||||||
if (option.innerText == elem.value) {
|
|
||||||
option.setAttribute("selected", "selected");
|
|
||||||
} else {
|
|
||||||
option.removeAttribute("selected");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
colorPositions();
|
|
||||||
}
|
|
||||||
|
|
||||||
function colorPositions() {
|
|
||||||
console.log("Coloring Positions");
|
|
||||||
for (bcLineup of document.getElementsByClassName("event-lineup")) {
|
|
||||||
selected_lineup_positions = Array.from(
|
|
||||||
bcLineup.querySelectorAll(
|
|
||||||
".Panel-row .SelectBox.position-selection option[selected='selected']"
|
|
||||||
)
|
|
||||||
).map((el) => el.value);
|
|
||||||
console.log(selected_lineup_positions);
|
|
||||||
for (position_status of bcLineup.querySelectorAll(".position-status")) {
|
|
||||||
for (class_name of ["text-danger", "text-warning", "text-success"]) {
|
|
||||||
if (position_status.classList.contains(class_name)) {
|
|
||||||
position_status.classList.remove(class_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
occurrences = selected_lineup_positions.filter(
|
|
||||||
(s) => s == position_status.innerText
|
|
||||||
).length;
|
|
||||||
|
|
||||||
if (occurrences == 1) {
|
|
||||||
position_status.classList.add("text-success");
|
|
||||||
} else if (occurrences > 1) {
|
|
||||||
position_status.classList.add("text-warning");
|
|
||||||
} else {
|
|
||||||
position_status.classList.add("text-danger");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function refresh_lineup_order(itemEl) {
|
|
||||||
let bcLineup = itemEl.closest(".benchcoach-lineup");
|
|
||||||
var player_rows = [];
|
|
||||||
for (tbody of bcLineup.querySelectorAll(
|
|
||||||
"[class*='tbody-benchcoach-starting']"
|
|
||||||
)) {
|
|
||||||
for (row of tbody.rows) {
|
|
||||||
player_rows.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < player_rows.length; i++) {
|
|
||||||
var player_order = player_rows[i].querySelector('[id^="sequence"]');
|
|
||||||
var form_element_order = player_rows[i].querySelector('[id$="sequence"]');
|
|
||||||
player_order.innerText = parseInt(player_rows[i].dataset.order);
|
|
||||||
player_rows[i].dataset.order = i;
|
|
||||||
form_element_order.value = i;
|
|
||||||
player_order.innerHTML = i + 1;
|
|
||||||
}
|
|
||||||
var player_rows = bcLineup.getElementsByClassName("tbody-benchcoach-bench")[0]
|
|
||||||
.rows;
|
|
||||||
for (let i = 0; i < player_rows.length; i++) {
|
|
||||||
var player_order = player_rows[i].querySelector('[id^="sequence"]');
|
|
||||||
var form_element_order = player_rows[i].querySelector('[id$="sequence"]');
|
|
||||||
player_rows[i].dataset.order = null;
|
|
||||||
form_element_order.value = null;
|
|
||||||
player_order.innerHTML = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendToClipboard(itemEl) {
|
|
||||||
let bcLineup = itemEl.closest(".benchcoach-lineup");
|
|
||||||
player_rows = bcLineup.querySelectorAll("[data-position=P]");
|
|
||||||
lineup_export = [];
|
|
||||||
if (player_rows.length > 0) {
|
|
||||||
lineup_export.push(player_rows[0].dataset.playerName);
|
|
||||||
lineup_export.push("", "");
|
|
||||||
} else {
|
|
||||||
lineup_export.push("", "", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
lineup_export.push("");
|
|
||||||
for (position of ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "DH"]) {
|
|
||||||
var player_rows = bcLineup.querySelectorAll(
|
|
||||||
`[data-position=${CSS.escape(position)}]`
|
|
||||||
);
|
|
||||||
if (player_rows.length > 0) {
|
|
||||||
lineup_export.push(player_rows[0].dataset.playerName);
|
|
||||||
} else {
|
|
||||||
lineup_export.push("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (position of ["EH"]) {
|
|
||||||
var player_rows = bcLineup.querySelectorAll(
|
|
||||||
`[data-position=${CSS.escape(position)}]`
|
|
||||||
);
|
|
||||||
for (var i = 0; i < 2; i++) {
|
|
||||||
if (i < player_rows.length) {
|
|
||||||
lineup_export.push(player_rows[i].dataset.playerName);
|
|
||||||
} else {
|
|
||||||
lineup_export.push("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (position of ["DR"]) {
|
|
||||||
let player_rows = bcLineup.querySelectorAll(
|
|
||||||
`[data-position=${CSS.escape(position)}]`
|
|
||||||
);
|
|
||||||
if (player_rows.length > 0) {
|
|
||||||
lineup_export.push(player_rows[0].dataset.playerName);
|
|
||||||
} else {
|
|
||||||
lineup_export.push("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lineup_export.push("");
|
|
||||||
lineup_export.push("", "");
|
|
||||||
lineup_export.push("");
|
|
||||||
|
|
||||||
for (var i = 0; i < 11; i++) {
|
|
||||||
let player_rows = bcLineup
|
|
||||||
.querySelector(".table-benchcoach-startinglineup")
|
|
||||||
.querySelectorAll(`[data-order=${CSS.escape(i)}]`);
|
|
||||||
if (player_rows.length > 0) {
|
|
||||||
lineup_export.push(player_rows[0].dataset.playerName);
|
|
||||||
} else {
|
|
||||||
lineup_export.push("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.dir(lineup_export);
|
|
||||||
var textArea = document.createElement("textarea");
|
|
||||||
textArea.value = lineup_export.join("\n");
|
|
||||||
|
|
||||||
// Avoid scrolling to bottom
|
|
||||||
textArea.style.top = "0";
|
|
||||||
textArea.style.left = "0";
|
|
||||||
textArea.style.position = "fixed";
|
|
||||||
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.focus();
|
|
||||||
textArea.select();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var successful = document.execCommand("copy");
|
|
||||||
var msg = successful ? "successful" : "unsuccessful";
|
|
||||||
console.log("Copying text command was " + msg);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Oops, unable to copy", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (bcLineup of document.querySelectorAll(".Panel:has(.lineup-slot)")) {
|
|
||||||
var startinglineup = new Sortable.create(
|
|
||||||
bcLineup.querySelector(".slot-set"),
|
|
||||||
{
|
|
||||||
animation: 150,
|
|
||||||
handle: ".drag-handle",
|
|
||||||
ghostClass: "ghost",
|
|
||||||
group: {
|
|
||||||
name: bcLineup.id,
|
|
||||||
put: [bcLineup.id],
|
|
||||||
pull: [bcLineup.id],
|
|
||||||
},
|
|
||||||
onAdd: function (/**Event*/ evt) {
|
|
||||||
// Add to Lineup
|
|
||||||
var itemEl = evt.item; // dragged HTMLElement
|
|
||||||
var player_order = itemEl.querySelector('[id^="sequence-member"]');
|
|
||||||
var player_available = itemEl.querySelector(
|
|
||||||
'[class^="member-availability-status"]'
|
|
||||||
);
|
|
||||||
refresh_lineup_order(itemEl);
|
|
||||||
if (player_order.classList.contains("d-none")) {
|
|
||||||
player_order.classList.remove("d-none");
|
|
||||||
}
|
|
||||||
// player_available.classList.add('d-none')
|
|
||||||
},
|
|
||||||
onUpdate: function (/**Event*/ evt) {
|
|
||||||
console.log("update to lineup");
|
|
||||||
var itemEl = evt.item; // dragged HTMLElement
|
|
||||||
refresh_lineup_order(itemEl);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
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(""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
27
src/views/login.hbs
Normal file
27
src/views/login.hbs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<div class="Grid Grid--fit Grid--withGutter u-max1200 u-flexExpandSides u-xs-size5of6 u-sm-size2of3 u-md-sizeFull u-padBottomMd u-xs-padEndsLg u-sm-padEndsXl">
|
||||||
|
<div class="Grid-cell u-size5of12">
|
||||||
|
<div class="Panel u-padLg u-spaceSidesAuto">
|
||||||
|
<h1 class="u-spaceSidesAuto u-spaceBottomLg">Sign in</h1>
|
||||||
|
<div>
|
||||||
|
<a class="Button Button--large Button--orange u-spaceSidesAuto" href="/login/federated/teamsnap">
|
||||||
|
{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}
|
||||||
|
<span>TeamSnap</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Grid-cell u-size7of12 u-textCenter">
|
||||||
|
<h1>
|
||||||
|
<img src="/media/benchcoach.svg" style="width: 2.5em;">
|
||||||
|
</h1>
|
||||||
|
<h1>
|
||||||
|
<strong>
|
||||||
|
Welcome to
|
||||||
|
<span class="text-nowrap">BenchCoach</span>
|
||||||
|
</strong>
|
||||||
|
</h1>
|
||||||
|
<div class="lead fst-italic fw-light">
|
||||||
|
An assistant coach for TeamSnap
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
|
|
||||||
block content
|
|
||||||
.Grid.Grid--fit.Grid--withGutter.u-max1200.u-flexExpandSides.u-xs-size5of6.u-sm-size2of3.u-md-sizeFull.u-padBottomMd.u-xs-padEndsLg.u-sm-padEndsXl
|
|
||||||
.Grid-cell.u-size5of12
|
|
||||||
.Panel.u-padLg.u-spaceSidesAuto
|
|
||||||
h1.u-spaceSidesAuto.u-spaceBottomLg Sign in
|
|
||||||
a(class="Button Button--large Button--orange" href="/login/federated/teamsnap").u-spaceSidesAuto
|
|
||||||
img(src="/media/teamsnap_star.svg").icon.u-spaceRightSm
|
|
||||||
span TeamSnap
|
|
||||||
.Grid-cell.u-size7of12.u-textCenter
|
|
||||||
h1
|
|
||||||
img(src="media/benchcoach.svg" style="width: 2.5em;")
|
|
||||||
h1
|
|
||||||
strong
|
|
||||||
| Welcome to
|
|
||||||
span.text-nowrap BenchCoach
|
|
||||||
.lead.fst-italic.fw-light
|
|
||||||
| An assistant coach for TeamSnap
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
|
|
||||||
block content
|
|
||||||
.Panel
|
|
||||||
.Panel-header
|
|
||||||
.Panel-title Roster
|
|
||||||
.Panel-body
|
|
||||||
each member in members
|
|
||||||
a(class="" href=`/${team_id}/member/${member.id}`).Panel-row--withCells
|
|
||||||
.Panel-cell
|
|
||||||
|#{member.firstName}
|
|
||||||
.Panel-cell
|
|
||||||
|#{member.lastName}
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
mixin availability-progress-bar(availabilitySummary, team)
|
|
||||||
.progress
|
|
||||||
div(class="progress-bar bg-success fw-bold" role="progressbar" style=`
|
|
||||||
width: ${((availabilitySummary.playerGoingCount/team.playerMemberCount)*100).toString() + "%"}`)
|
|
||||||
|#{availabilitySummary.playerGoingCount}
|
|
||||||
div(class="progress-bar bg-info fw-bold" role="progressbar" style=`
|
|
||||||
width: ${((availabilitySummary.playerMaybeCount/team.playerMemberCount)*100).toString() + "%"}`)
|
|
||||||
|#{availabilitySummary.playerMaybeCount}
|
|
||||||
div(class="progress-bar bg-danger fw-bold" role="progressbar" style=`
|
|
||||||
width: ${((availabilitySummary.playerNotGoingCount/team.playerMemberCount)*100).toString() + "%"}`)
|
|
||||||
|#{availabilitySummary.playerNotGoingCount}
|
|
||||||
div(class="progress-bar text-secondary fw-bold" role="progressbar" style=`
|
|
||||||
width: ${((availabilitySummary.playerUnknownCount/team.playerMemberCount)*100).toString() + "%"};
|
|
||||||
background-color: var(--bs-gray-200)`)
|
|
||||||
|#{availabilitySummary.playerUnknownCount}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
mixin lineup-slot(item, index)
|
|
||||||
-
|
|
||||||
if (item.type == "eventLineupEntry") {
|
|
||||||
var availability_status_code = item.availabilityStatusCode
|
|
||||||
} else if (item.type == "availability") {
|
|
||||||
var availability_status_code = item.statusCode
|
|
||||||
}
|
|
||||||
.Panel-row.Panel-row--withCells.lineup-slot
|
|
||||||
.Panel-cell.Panel-cell--header.u-padXs.u-size1of12
|
|
||||||
.u-flexAlignSelfCenter
|
|
||||||
|#{index+1}
|
|
||||||
div(class=`Panel-cell availability-status-code-${availability_status_code}`).u-padXs.u-size8of12
|
|
||||||
span.lastname #{item.member.lastName}
|
|
||||||
span.jerseynumber ##{item.member.jerseyNumber}
|
|
||||||
.Panel-cell.u-padXs.u-size2of12
|
|
||||||
.SelectBox.position-selection
|
|
||||||
select(onchange="onPositionSelectChange(this)").position-select-box.SelectBox-options
|
|
||||||
each pos in ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"]
|
|
||||||
option(selected=item.label==pos) #{pos}
|
|
||||||
.Panel-cell.u-padXs.u-flexAlignSelfCenter.u-size1of12
|
|
||||||
.drag-handle
|
|
||||||
i.bi.bi-grip-vertical.text-secondary.drag-handle
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
extends base
|
|
||||||
block content
|
|
||||||
.Panel
|
|
||||||
.Panel-header
|
|
||||||
.Panel-title #{opponent.name}
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row.Panel-row--withCells
|
|
||||||
.Panel-cell.Panel-cell--header ID
|
|
||||||
.panel-cell #{opponent.id}
|
|
||||||
Button(onclick=`navigator.clipboard.writeText("${opponent.id}");`).Button
|
|
||||||
i.bi.bi-clipboard.Icon
|
|
||||||
.Panel-row.Panel-row--withCells
|
|
||||||
.Panel-cell.Panel-cell--header Contact Name
|
|
||||||
.panel-cell #{opponent.contactsName}
|
|
||||||
.Panel-row.Panel-row--withCells
|
|
||||||
.Panel-cell.Panel-cell--header Contact Phone
|
|
||||||
.panel-cell #{opponent.contactsPhone}
|
|
||||||
.Panel-row.Panel-row--withCells
|
|
||||||
.Panel-cell.Panel-cell--header Contact Email
|
|
||||||
.panel-cell #{opponent.contactsEmail}
|
|
||||||
.Panel-row.Panel-row--withCells
|
|
||||||
.Panel-cell.Panel-cell--header Logo
|
|
||||||
.panel-cell
|
|
||||||
if opponent_logo
|
|
||||||
img(src=`${opponent_logo.mediumUrl}` width="64" height="64")
|
|
||||||
a.Button(target="_blank" rel="noopener noreferrer" href=`https://go.teamsnap.com/${team_id}/files/view/${opponent_logo.id}`).Button
|
|
||||||
i.bi.bi-asterisk.Icon
|
|
||||||
else
|
|
||||||
if team_media_group
|
|
||||||
a.Button(target="_blank" rel="noopener noreferrer" href=`https://go.teamsnap.com/${team_id}/files/list/${team_media_group.id}`) Upload
|
|
||||||
else
|
|
||||||
a.Button(target="_blank" rel="noopener noreferrer" href=`https://go.teamsnap.com/${team_id}/files/`) Upload
|
|
||||||
Button(onclick=`navigator.clipboard.writeText("opponent-logo-${opponent.id}.png");`).Button
|
|
||||||
i.bi.bi-clipboard.Icon
|
|
||||||
span Copy Filename
|
|
||||||
.Panel-row.Panel-row--withCells
|
|
||||||
.Panel-cell.Panel-cell--header Notes
|
|
||||||
.panel-cell #{opponent.Notes}
|
|
||||||
|
|
||||||
14
src/views/opponent/list.hbs
Normal file
14
src/views/opponent/list.hbs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<h1>
|
||||||
|
{{title}}
|
||||||
|
</h1>
|
||||||
|
<div class="Panel">
|
||||||
|
<div class="Panel-body">
|
||||||
|
{{#each opponents}}
|
||||||
|
<div class="Panel-row Panel-row--withCells u-textDecorationNone">
|
||||||
|
<a class="Panel-cell" href="opponent/{{this.id}}">
|
||||||
|
{{this.name}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
79
src/views/opponent/show.hbs
Normal file
79
src/views/opponent/show.hbs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<div class="Panel">
|
||||||
|
<div class="Panel-header">
|
||||||
|
<div class="Panel-title">
|
||||||
|
{{opponent.name}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-body">
|
||||||
|
<div class="Panel-row Panel-row--withCells">
|
||||||
|
<div class="Panel-cell Panel-cell--header">
|
||||||
|
ID
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell">
|
||||||
|
{{opponent.id}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-row Panel-row--withCells">
|
||||||
|
<div class="Panel-cell Panel-cell--header">
|
||||||
|
Contact Name
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell">
|
||||||
|
{{opponent.contactsName}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-row Panel-row--withCells">
|
||||||
|
<div class="Panel-cell Panel-cell--header">
|
||||||
|
Contact Phone
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell">
|
||||||
|
{{opponent.contactsPhone}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-row Panel-row--withCells">
|
||||||
|
<div class="Panel-cell Panel-cell--header">
|
||||||
|
Contact Email
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell">
|
||||||
|
{{opponent.contactsEmail}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-row Panel-row--withCells">
|
||||||
|
<div class="Panel-cell Panel-cell--header">
|
||||||
|
Logo
|
||||||
|
</div>
|
||||||
|
<div class="Panel-cell">
|
||||||
|
{{#if opponent_logo}}
|
||||||
|
<img src="{{opponent_logo.mediumUrl}}" width="64" height="64" />
|
||||||
|
{{else}}
|
||||||
|
<form name="upload-opponent-logo" action="{{opponent.id}}/upload_logo" method="post" enctype="multipart/form-data" hidden>
|
||||||
|
{{!-- THIS DOESN'T WORK XHR2 ERRORS (FORMDATA) --}}
|
||||||
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
<input type="hidden" name="csrfToken" value="{{csrfToken}}">
|
||||||
|
<input type="hidden" name="teamMediaGroupId" value="{{team_media_group.id}}">
|
||||||
|
<input type="hidden" name="memberId" value="{{member.id}}">
|
||||||
|
<input type="hidden" name="teamId" value="{{team.id}}">
|
||||||
|
<input type="hidden" name="description" value="team-logo-{{opponent.id}}.png">
|
||||||
|
<div class="FieldGroup">
|
||||||
|
<label for='file' class="FieldGroup-label">Select files</label>
|
||||||
|
<input id='file' name='file' type="file">
|
||||||
|
<button class="submit-btn Button" type="submit">Upload</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div
|
||||||
|
class="Button"
|
||||||
|
onclick="navigator.clipboard.writeText('opponent-logo-{{
|
||||||
|
opponent.id
|
||||||
|
}}.png');window.open('https://go.teamsnap.com/{{team.id}}/files/list/{{team_media_group.id}}');"
|
||||||
|
>
|
||||||
|
<img class="Icon" src="/bootstrap-icons/clipboard.svg" />
|
||||||
|
<span>
|
||||||
|
Copy Filename and Go to Teamsnap
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/js/teamsnap.min.js"></script>
|
||||||
|
<script src="/js/opponent.js"></script>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
extends base
|
|
||||||
|
|
||||||
block content
|
|
||||||
.Panel
|
|
||||||
.Panel-header
|
|
||||||
.Panel-title Opponents
|
|
||||||
.Panel-body
|
|
||||||
each opponent in opponents
|
|
||||||
.Panel-row
|
|
||||||
a(class='opponent' href=`/${team.id}/opponent/${opponent.id}`) #{opponent.name}
|
|
||||||
30
src/views/partials/navbar.hbs
Normal file
30
src/views/partials/navbar.hbs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<div class="Header-container Grid u-flexAlignItemsCenter">
|
||||||
|
<div class="Grid-cell u-sizeFill">
|
||||||
|
<div class="Header-banner Grid u-flexAlignItemsCenter">
|
||||||
|
<a href="/" class="Grid-cell u-sizeFit u-flexInline u-flexAlignItemsCenter u-textDecorationNone">
|
||||||
|
<div class="Header-bannerLogo">
|
||||||
|
<img class="logo" src="/media/benchcoach.svg" alt="BenchCoach Logo">
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="Grid-cell u-flexInline u-flexJustifyEnd u-sizeFill u-padSidesSm">
|
||||||
|
{{#if user}}
|
||||||
|
<div class="Popup">
|
||||||
|
<div class="Button Button--small Popup-toggle" onclick="this.closest('.Popup').querySelector('.Popup-container').classList.toggle('is-open')">
|
||||||
|
Account
|
||||||
|
</div>
|
||||||
|
<div class="Popup-container Popup-container--down Popup-container--right u-sizeFit">
|
||||||
|
<div class="Popup-content u-padXs u-sizeFit u-fontSizeSm">
|
||||||
|
<h6 class="h6 title u-textNoWrap u-fontSizeSm u-textSemiBold">{{user.first_name}} {{user.last_name}}</h6>
|
||||||
|
<div class="u-textNoWrap u-fontSizeSm">{{user.email}}</div>
|
||||||
|
<hr class="Divider u-spaceEndsNone">
|
||||||
|
<a href="/logout">Logout</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
25
src/views/partials/navbar.pug
Normal file
25
src/views/partials/navbar.pug
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
header.Header
|
||||||
|
.Header-container.Grid.u-flexAlignItemsCenter
|
||||||
|
.Grid-cell.u-sizeFill
|
||||||
|
.Header-banner.Grid.u-flexAlignItemsCenter
|
||||||
|
a.Grid-cell.u-sizeFit.u-flexInline.u-flexAlignItemsCenter.u-textDecorationNone href=url_for(root_path)
|
||||||
|
div.Header-bannerLogo
|
||||||
|
img(class='logo' src='/media/benchcoach.svg' alt='BenchCoach Logo')
|
||||||
|
span
|
||||||
|
- if defined?(team)
|
||||||
|
h1.Header-bannerTitle #{team.name}
|
||||||
|
- else
|
||||||
|
h1.Header-bannerTitle BenchCoach
|
||||||
|
.Grid-cell.u-flexInline.u-flexJustifyEnd.u-sizeFill.u-padSidesSm
|
||||||
|
- if @user
|
||||||
|
.Popup
|
||||||
|
.Button.Button--small.Popup-toggle onclick="this.closest('.Popup').querySelector('.Popup-container').classList.toggle('is-open')"
|
||||||
|
| Account
|
||||||
|
.Popup-container.Popup-container--down.Popup-container--right.u-sizeFit
|
||||||
|
.Popup-content.u-padXs.u-sizeFit.u-fontSizeSm
|
||||||
|
h6.title.u-textNoWrap.u-fontSizeSm.u-textSemiBold="#{@user['first_name']} #{@user['last_name']}"
|
||||||
|
.u-textNoWrap.u-fontSizeSm="#{@user['email']}"
|
||||||
|
hr.Divider.u-spaceEndsNone
|
||||||
|
a href=url_for(logout_path) Logout
|
||||||
|
|
||||||
|
.Grid-cell.u-sizeFit
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
|
|
||||||
block content
|
|
||||||
.Panel
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row
|
|
||||||
h2.Panel-title #{team.name}
|
|
||||||
p #{team.seasonName}
|
|
||||||
.Panel
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row
|
|
||||||
a(href=`/${team.id}/events`).u-fontSizeXl-
|
|
||||||
<svg viewBox="0 0 960 960" role="presentation" class="Icon">
|
|
||||||
<path d="M890 131c11.3 2.7 20.5 8.3 27.5 17s10.5 18.7 10.5 30v699c0 11.3-3.5 21.3-10.5 30s-16.2 14.3-27.5 17l-8 2c-4.7.7-11 1.7-19 3s-17.7 3-29 5-24.3 3.8-39 5.5-30.5 3.5-47.5 5.5-35.7 4-56 6-41.3 3.7-63 5-45.3 2.3-71 3-51.5 1-77.5 1-51.8-.3-77.5-1-49.3-1.7-71-3-42.7-3-63-5-39-4-56-6-32.8-3.8-47.5-5.5-27.7-3.5-39-5.5-21-3.7-29-5-14.3-2.3-19-3l-8-2c-11.3-2.7-20.5-8.3-27.5-17S32 888.3 32 877V178c0-11.3 3.5-21.3 10.5-30s16.2-14.3 27.5-17c.7 0 4.5-.7 11.5-2s17.8-3.2 32.5-5.5 30-4.5 46-6.5v43c0 23.3 8.3 42.5 25 57.5s36.7 22.5 60 22.5c-14-12.7-21-28.7-21-48V64c0-17.3 6.3-32.3 19-45s27.7-19 45-19 32.3 6.3 45 19 19 27.7 19 45v35c40-2 82.7-3 128-3 28.7 0 50 .3 64 1v63c0 23.3 8.3 42.5 25 57.5s36.7 22.5 60 22.5c-14-12.7-21-28.7-21-48V64c0-17.3 6.3-32.3 19-45s27.7-19 45-19 32.3 6.3 45 19 19 27.7 19 45v45c64 6.7 115.3 14 154 22zM480 869c122.7 0 240-9.3 352-28V384H128v457c112 18.7 229.3 28 352 28zm256-421v128H608V448h128zm-192 0v128H416V448h128zm0 320H416V640h128v128zm-192 0H224V640h128v128zm384 0H608V640h128v128z"></path>
|
|
||||||
</svg>
|
|
||||||
| Events
|
|
||||||
.Panel-row
|
|
||||||
a(href=`/${team.id}/members`)
|
|
||||||
<svg viewBox="0 0 960 960" class="Icon"><path d="M959 714c2 8 .7 16-4 24-5.3 7.3-12.3 11.7-21 13-9.3 4.7-66.7 8.3-172 11-28.7-60.7-73.3-109.7-134-147-17.3-10-39-19.7-65-29-6.7-3.3-12-5.7-16-7 31.3-49.3 52.3-97 63-143s13.5-84.3 8.5-115-15.2-58.3-30.5-83c-28-47.3-63.3-80.7-106-100 18.7-24 37.2-41.7 55.5-53S579 68 607 68c33.3 0 63.7 7.7 91 23s49.3 36.7 66 64c43.3 71.3 32.3 157.7-33 259-20 28.7-27.7 50.7-23 66 2 7.3 8 14.5 18 21.5s18.7 12.2 26 15.5 19.3 8.3 36 15c26 10.7 43.7 18.7 53 24 64.7 39.3 104 92 118 158zM352 892c-70 0-132-1-186-3s-90.2-4-108.5-6-28.8-3.3-31.5-4c-8.7-.7-15.7-5-21-13-4.7-6.7-6-14.7-4-24 14-66 53-118.7 117-158 8-4.7 25.7-12.7 53-24 16.7-6.7 28.7-11.7 36-15s16-8.5 26-15.5 16-14.2 18-21.5c4.7-14.7-2.7-36.7-22-66-65.3-102-76.7-188.3-34-259 16.7-27.3 38.8-48.7 66.5-64s57.8-23 90.5-23c33.3 0 63.8 7.7 91.5 23s49.8 36.7 66.5 64c42.7 70.7 31.3 157-34 259-19.3 29.3-26.7 51.3-22 66 2 7.3 8 14.5 18 21.5s18.7 12.2 26 15.5 19.3 8.3 36 15c27.3 11.3 45 19.3 53 24 63.3 39.3 102.7 92 118 158 2 8.7.3 16.7-5 24-5.3 8-12.3 12.3-21 13-2.7.7-13.3 2-32 4s-55 4-109 6-116 3-186 3z"></path></svg>
|
|
||||||
| Roster
|
|
||||||
.Panel-row
|
|
||||||
a(href=`/${team.id}/opponents`)
|
|
||||||
<svg viewBox="0 0 960 960" class="Icon"><path d="M959 628c2 6.7.7 13.7-4 21s-10.7 11.3-18 12l-7 1c-4.7.7-11.8 1.5-21.5 2.5s-21.5 2-35.5 3-33.2 1.8-57.5 2.5-50.8 1.3-79.5 2c-6-4.7-12.7-9.2-20-13.5s-15.2-9-23.5-14S678 635.7 674 633c-10.7-5.3-21-11.3-31-18 70-92.7 84-184.7 42-276-16.7-36-40-64-70-84s-62.7-32.3-98-37c0-23.3 5.3-47.3 16-72 12.7-28 32-48.7 58-62s53-20 81-20 55 6.7 81 20 45.3 34 58 62c32 70 16.3 141.7-47 215-19.3 22.7-18 46.3 4 71 6.7 8 26.2 21.3 58.5 40s55.2 32.7 68.5 42c7.3 5.3 13.8 10.2 19.5 14.5s10.5 9.3 14.5 15 7.2 10 9.5 13 4.7 8.2 7 15.5 3.8 12.3 4.5 15 2.2 9.2 4.5 19.5 3.8 17.5 4.5 21.5zm-673 5c-4 2.7-10.2 6.5-18.5 11.5s-16.2 9.7-23.5 14-14 8.8-20 13.5c-20.7 0-40.3-.3-59-1s-34.7-1.3-48-2-25.3-1.3-36-2-19.5-1.3-26.5-2-12.8-1.3-17.5-2-8.3-1-11-1l-3-1c-7.3-.7-13.3-4.7-18-12-4.7-6-6-13-4-21 1.3-4.7 3-11.8 5-21.5s3.3-16.2 4-19.5 2.2-8.5 4.5-15.5 4.7-12 7-15S27 549.3 31 544s8.7-10.3 14-15 12-9.7 20-15c14-10 37-24.3 69-43s51.3-31.7 58-39c22-24.7 23.3-48.3 4-71-63.3-73.3-79-145-47-215 12.7-28 32-48.7 58-62s53-20 81-20 55 6.7 81 20 45.3 34 58 62c10.7 24.7 16 48.7 16 72-80 11.3-136.3 51.7-169 121-41.3 91.3-27 183.3 43 276-2.7 2-5.7 4-9 6s-7.2 4.2-11.5 6.5-7.5 4.2-9.5 5.5h-1zm194 263c-28 0-54.5-.2-79.5-.5s-46.2-.8-63.5-1.5-33-1.5-47-2.5-25.5-1.8-34.5-2.5-16.5-1.3-22.5-2-10.3-1-13-1l-5-1c-7.3-.7-13.3-4.7-18-12-4.7-6-6-13-4-21 1.3-4.7 3-11.8 5-21.5s3.3-16.2 4-19.5 2.2-8.5 4.5-15.5 4.7-12 7-15 5.5-7.2 9.5-12.5 8.7-10.3 14-15 12-9.7 20-15c14-10 37-24.3 69-43s51.3-31.7 58-39c22-24.7 23.3-48.3 4-71-63.3-73.3-79-145-47-215 12.7-28 32-48.7 58-62s53-20 81-20 55 6.7 81 20 45.3 34 58 62c32 70 16.3 141.7-47 215-19.3 22.7-18 46.3 4 71 6.7 8 26.2 21.3 58.5 40s55.2 32.7 68.5 42c16.7 12 29 23.2 37 33.5s13.5 21.7 16.5 34S763 833.3 767 852c2 7.3.7 14.3-4 21-4.7 7.3-10.7 11.3-18 12l-5 1c-2.7 0-7 .3-13 1s-13.5 1.3-22.5 2-20.7 1.5-35 2.5-30.2 1.8-47.5 2.5-38.3 1.2-63 1.5-51 .5-79 .5z"></path></svg>
|
|
||||||
| Opponents
|
|
||||||
52
src/views/team/home.hbs
Normal file
52
src/views/team/home.hbs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<div class=" Grid u-spaceBottomSm">
|
||||||
|
<div class=" Grid-cell u-spaceNone u-sizeFit">
|
||||||
|
<img src="{{team_preferences.links.teamLogo.href}}", style="height: 64px;">
|
||||||
|
</div>
|
||||||
|
<div class=" Grid-cell u-sizeFill u-spaceNone">
|
||||||
|
<h2 class=" u-spaceNone">{{team.name}}</h2>
|
||||||
|
<p class=" u-spaceNone">{{team.season_name}}</p>
|
||||||
|
<small class=" u-spaceNone"><a href="{{team.leagueUrl}}">{{team.leagueName}}</a></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class=" Grid-cell u-sizeFit">
|
||||||
|
<div class=" u-flex u-flexAlignItemsCenter u-flexJustifyEnd">
|
||||||
|
<div class="ButtonGroup">
|
||||||
|
<a class="Button" href="schedule">
|
||||||
|
<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">
|
||||||
|
<img src ="/teamsnap-ui/assets/icons/roster.svg" class="Icon">
|
||||||
|
<span class="span u-hidden u-xs-inline">Roster</span>
|
||||||
|
</a>
|
||||||
|
<a class="Button" href="opponents">
|
||||||
|
<img src ="/teamsnap-ui/assets/icons/team.svg" class="Icon">
|
||||||
|
<span class="span u-hidden u-xs-inline">Opponents</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" Panel Panel--full">
|
||||||
|
<div class=" Panel-header">
|
||||||
|
<h2 class=" Panel-title">Upcoming Events</h2>
|
||||||
|
</div>
|
||||||
|
<div class=" Panel-body">
|
||||||
|
{{#each upcoming_events}}
|
||||||
|
{{>event_panel event=this}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" Panel Panel--full">
|
||||||
|
<div class=" Panel-header">
|
||||||
|
<h2 class=" Panel-title">Recent Events</h2>
|
||||||
|
</div>
|
||||||
|
<div class=" Panel-body">
|
||||||
|
{{#each recent_events}}
|
||||||
|
{{>event_panel event=this}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
49
src/views/team/list.hbs
Normal file
49
src/views/team/list.hbs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<div class=" Panel u-sm-size1of2 u-spaceSidesAuto u-spaceSm u-maxWidthSm">
|
||||||
|
<div class="Panel-header">
|
||||||
|
<h3 class="Panel-title">
|
||||||
|
My Teams
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="Panel-body">
|
||||||
|
{{#each teams}}
|
||||||
|
{{#unless this.isArchivedSeason}}
|
||||||
|
<a
|
||||||
|
class="Panel-row Panel-row--withCells u-textDecorationNone"
|
||||||
|
href="/{{this.id}}/home"
|
||||||
|
>
|
||||||
|
<div class=" Panel-cell u-flexGrow1 u-textNoWrap">
|
||||||
|
<b>
|
||||||
|
{{this.name}}
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
|
<div class=" Panel-cell u-fontSizeSm u-size1of3 u-textRight">
|
||||||
|
{{this.seasonName}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
<div class="Panel-header">
|
||||||
|
<h3 class="Panel-title">
|
||||||
|
Archived Seasons
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
{{#each teams}}
|
||||||
|
{{#if this.isArchivedSeason}}
|
||||||
|
<a
|
||||||
|
class="Panel-row Panel-row--withCells u-textDecorationNone"
|
||||||
|
href="/{{this.id}}/home"
|
||||||
|
>
|
||||||
|
<div class=" Panel-cell u-flexGrow1 u-textNoWrap">
|
||||||
|
<b>
|
||||||
|
{{this.name}}
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
|
<div class=" Panel-cell u-fontSizeSm u-size1of3 u-textRight">
|
||||||
|
{{this.seasonName}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
extends base.pug
|
|
||||||
|
|
||||||
block content
|
|
||||||
.row
|
|
||||||
.text-center.my-2
|
|
||||||
.row
|
|
||||||
h1
|
|
||||||
img.mx-auto(src="media/benchcoach.svg" style="width: 2.5em;")
|
|
||||||
.row
|
|
||||||
h1
|
|
||||||
strong
|
|
||||||
| Welcome to
|
|
||||||
span.text-nowrap BenchCoach
|
|
||||||
.text-center.lead.fst-italic.fw-light
|
|
||||||
| An assistant coach for TeamSnap
|
|
||||||
.row
|
|
||||||
.col.text-center
|
|
||||||
ul.list-group
|
|
||||||
each team in teams
|
|
||||||
a(class='team list-group-item' href=`/${team.id}/home`) #{team.name} [#{team.seasonName}]
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
html
|
|
||||||
head
|
|
||||||
meta(charset='utf-8')
|
|
||||||
meta(name='viewport' content='width=device-width, initial-scale=1')
|
|
||||||
title BenchCoach - Teams
|
|
||||||
link(rel='stylesheet' href='/css/bootstrap.min.css')
|
|
||||||
link(rel='stylesheet' href='/font/bootstrap-icons.min.css')
|
|
||||||
link(rel='stylesheet' href='/css/teamsnap-ui.css')
|
|
||||||
script(type='text/javascript', src='/js/teamsnap.js')
|
|
||||||
|
|
||||||
body
|
|
||||||
.container
|
|
||||||
.Panel
|
|
||||||
.Panel-header
|
|
||||||
.Panel-title Upload
|
|
||||||
.Panel-body
|
|
||||||
.Panel-row
|
|
||||||
.input-group
|
|
||||||
label(for='file') Select file
|
|
||||||
input(id='file' type="file")
|
|
||||||
input(id="csrf_token", type="hidden" name="csrf_token" value=`${csrf_token}`)
|
|
||||||
input(id='team_id', type="hidden", value=`${team_id}`)
|
|
||||||
input(id='opponent_id', type="hidden", value=`${opponent_id}`)
|
|
||||||
input(id='token', type="hidden", value=`${token}`)
|
|
||||||
input(id='clientId', type="hidden", value=`${clientId}`)
|
|
||||||
button(type='submit' onClick="submitForm()") Upload
|
|
||||||
|
|
||||||
script.
|
|
||||||
function submitForm(e) {
|
|
||||||
console.log(document.getElementById("opponent_id").value)
|
|
||||||
const file = document.getElementById("file");
|
|
||||||
const csrf_token = document.getElementById("csrf_token").value;
|
|
||||||
const team_id = document.getElementById("team_id").value;
|
|
||||||
const opponent_id = document.getElementById("opponent_id").value;
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("file", file.files[0]);
|
|
||||||
formData.append("team_id", team_id);
|
|
||||||
formData.append("opponent_id", opponent_id);
|
|
||||||
for (var key of formData.entries()) {
|
|
||||||
console.log(key[0] + ', ' + key[1]);
|
|
||||||
}
|
|
||||||
fetch(`/${team_id}/opponent/${opponent_id}/upload-logo`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
headers: {
|
|
||||||
"X-CSRF-Token": csrf_token,
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((res) => console.log(res))
|
|
||||||
.catch((err) => ("Error occured", err));
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadImageForm(e) {
|
|
||||||
const file = document.getElementById("file");
|
|
||||||
const csrf_token = document.getElementById("csrf_token").value;
|
|
||||||
const team_id = document.getElementById("team_id").value;
|
|
||||||
const opponent_id = document.getElementById("opponent_id").value;
|
|
||||||
const token = document.getElementById("token").value;
|
|
||||||
const clientId = document.getElementById("clientId").value;
|
|
||||||
const formData = new FormData();
|
|
||||||
teamsnap.init(clientId);
|
|
||||||
teamsnap.auth(token)
|
|
||||||
console.log(teamsnap.isAuthed())
|
|
||||||
teamsnap
|
|
||||||
.loadCollections()
|
|
||||||
.then(() => {
|
|
||||||
return teamsnap.createTeamMedium({
|
|
||||||
file: file.files[0],
|
|
||||||
mediaFormat: "file",
|
|
||||||
memberId: member_id,
|
|
||||||
teamId: team_id,
|
|
||||||
teamMediaGroupId: "4927028",
|
|
||||||
description: `team-logo-${opponent_id}.png`,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then((item) => {
|
|
||||||
return teamsnap.uploadTeamMedium(item);
|
|
||||||
})
|
|
||||||
.then((item) => {
|
|
||||||
res.send("Data Received: " + JSON.stringify(item));
|
|
||||||
})
|
|
||||||
.fail((err) => console.log(err));
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user