first commit
This commit is contained in:
2
.env.sample
Normal file
2
.env.sample
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
TEAMSNAP_CLIENT_ID=
|
||||||
|
TEAMSNAP_CLIENT_SECRET=
|
||||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: anthonyscorrea
|
||||||
174
.gitignore
vendored
Normal file
174
.gitignore
vendored
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
.env
|
||||||
|
var
|
||||||
|
|
||||||
|
#ide
|
||||||
|
/.nova
|
||||||
|
|
||||||
|
# certs
|
||||||
|
certs/*.pem
|
||||||
|
|
||||||
|
# fonts
|
||||||
|
*.ttf
|
||||||
|
*.woff
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
https://github.com/github/gitignore/blob/main/Node.gitignore
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# MacOS
|
||||||
|
# https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
86
app.js
Normal file
86
app.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
|
var createError = require("http-errors");
|
||||||
|
var express = require("express");
|
||||||
|
var path = require("path");
|
||||||
|
var cookieParser = require("cookie-parser");
|
||||||
|
var session = require("express-session");
|
||||||
|
var csrf = require("csurf");
|
||||||
|
var passport = require("passport");
|
||||||
|
var logger = require("morgan");
|
||||||
|
global.XMLHttpRequest = require("xhr2");
|
||||||
|
var teamsnap = require("teamsnap.js");
|
||||||
|
|
||||||
|
var indexRouter = require("./routes/index");
|
||||||
|
var authRouter = require("./routes/auth");
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
// view engine setup
|
||||||
|
app.set("views", path.join(__dirname, "views"));
|
||||||
|
app.set("view engine", "pug");
|
||||||
|
|
||||||
|
app.locals.pluralize = require("pluralize");
|
||||||
|
|
||||||
|
app.use(logger("dev"));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
app.use(
|
||||||
|
"/css",
|
||||||
|
express.static(path.join(__dirname, "node_modules/bootstrap/dist/css"))
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
"/css",
|
||||||
|
express.static(
|
||||||
|
path.join(__dirname, "node_modules/@teamsnap/teamsnap-ui/dist/css")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
"/font",
|
||||||
|
express.static(path.join(__dirname, "node_modules/bootstrap-icons/font"))
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
session({
|
||||||
|
teamsnap_token: "",
|
||||||
|
current_team: "",
|
||||||
|
secret: "keyboard cat",
|
||||||
|
resave: false, // don't save session if unmodified
|
||||||
|
saveUninitialized: false, // don't create session until something stored
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.use(csrf());
|
||||||
|
app.use(passport.authenticate("session"));
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
var msgs = req.session.messages || [];
|
||||||
|
res.locals.messages = msgs;
|
||||||
|
res.locals.hasMessages = !!msgs.length;
|
||||||
|
req.session.messages = [];
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
res.locals.csrfToken = req.csrfToken();
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use("/", authRouter);
|
||||||
|
app.use("/", indexRouter);
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
next(createError(404));
|
||||||
|
});
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
// set locals, only providing error in development
|
||||||
|
res.locals.message = err.message;
|
||||||
|
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||||
|
console.log("error:", err);
|
||||||
|
// render the error page
|
||||||
|
res.status(err.status || 500);
|
||||||
|
res.render("error", { message: err.message });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
||||||
92
bin/www
Executable file
92
bin/www
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require("../app");
|
||||||
|
var http = require("http");
|
||||||
|
var https = require("https");
|
||||||
|
var fs = require("fs");
|
||||||
|
var debug = require("debug")("https");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || "3000");
|
||||||
|
app.set("port", port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTPS server.
|
||||||
|
*/
|
||||||
|
const https_options = {
|
||||||
|
key: fs.readFileSync("certs/key.pem"),
|
||||||
|
cert: fs.readFileSync("certs/cert.pem"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var server = https.createServer(https_options, app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on("error", onError);
|
||||||
|
server.on("listening", onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== "listen") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case "EACCES":
|
||||||
|
console.error(bind + " requires elevated privileges");
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case "EADDRINUSE":
|
||||||
|
console.error(bind + " is already in use");
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
|
||||||
|
debug("Listening on " + bind);
|
||||||
|
}
|
||||||
0
certs/.gitkeep
Normal file
0
certs/.gitkeep
Normal file
3566
package-lock.json
generated
Normal file
3566
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
50
package.json
Normal file
50
package.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "teamsnap-benchcoach",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "An assistant for teamsnap",
|
||||||
|
"keywords": [
|
||||||
|
"teamsnap"
|
||||||
|
],
|
||||||
|
"author": {
|
||||||
|
"name": "Tony Correa",
|
||||||
|
"email": "a@correa.co"
|
||||||
|
},
|
||||||
|
"homepage": "https://benchcoach.ascorrea.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/anthonyscorrea/teamsnap-benchcoach.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/anthonyscorrea/teamsnap-benchcoach/issues"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/anthonyscorrea"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./bin/www"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@teamsnap/teamsnap-ui": "^3.12.3",
|
||||||
|
"bootstrap": "^5.3.1",
|
||||||
|
"bootstrap-icons": "^1.10.5",
|
||||||
|
"connect-ensure-login": "^0.1.1",
|
||||||
|
"cookie-parser": "~1.4.4",
|
||||||
|
"csurf": "^1.11.0",
|
||||||
|
"debug": "~2.6.9",
|
||||||
|
"dotenv": "^8.6.0",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"express-session": "^1.17.2",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"mkdirp": "^1.0.4",
|
||||||
|
"morgan": "~1.9.1",
|
||||||
|
"papaparse": "^5.4.1",
|
||||||
|
"passport": "^0.6.0",
|
||||||
|
"passport-teamsnap": "^1.1.1",
|
||||||
|
"pluralize": "^8.0.0",
|
||||||
|
"pug": "^3.0.2",
|
||||||
|
"teamsnap.js": "^1.62.1",
|
||||||
|
"xhr2": "^0.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
661
public/css/gamecard.css
Normal file
661
public/css/gamecard.css
Normal file
@@ -0,0 +1,661 @@
|
|||||||
|
@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);
|
||||||
|
}
|
||||||
71
public/css/paper.css
Normal file
71
public/css/paper.css
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
@page {
|
||||||
|
margin: 0;
|
||||||
|
size: B5;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.sheet {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Paper sizes **/
|
||||||
|
body.B5 .sheet {
|
||||||
|
width: 182mm;
|
||||||
|
height: 257mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.index-card .sheet {
|
||||||
|
width: 3.5in;
|
||||||
|
height: 5in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For screen preview **/
|
||||||
|
@media screen {
|
||||||
|
body {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
.sheet {
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 0.5mm 2mm rgba(0, 0, 0, 0.3);
|
||||||
|
margin: 5mm auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fix for Chrome issue #273306 **/
|
||||||
|
@media print {
|
||||||
|
body.A3.landscape {
|
||||||
|
width: 420mm;
|
||||||
|
}
|
||||||
|
body.A3,
|
||||||
|
body.A4.landscape {
|
||||||
|
width: 297mm;
|
||||||
|
}
|
||||||
|
body.A4,
|
||||||
|
body.A5.landscape {
|
||||||
|
width: 210mm;
|
||||||
|
}
|
||||||
|
body.A5 {
|
||||||
|
width: 148mm;
|
||||||
|
}
|
||||||
|
body.B5 {
|
||||||
|
width: 182mm;
|
||||||
|
}
|
||||||
|
body.B5.landscape {
|
||||||
|
width: 257mm;
|
||||||
|
}
|
||||||
|
body.letter,
|
||||||
|
body.legal {
|
||||||
|
width: 216mm;
|
||||||
|
}
|
||||||
|
body.letter.landscape {
|
||||||
|
width: 280mm;
|
||||||
|
}
|
||||||
|
body.legal.landscape {
|
||||||
|
width: 357mm;
|
||||||
|
}
|
||||||
|
}
|
||||||
57
public/css/project.css
Normal file
57
public/css/project.css
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/* These styles are generated from project.scss. */
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
|
||||||
|
.alert-debug {
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
|
border-color: #d6e9c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
color: #b94a48;
|
||||||
|
background-color: #f2dede;
|
||||||
|
border-color: #eed3d7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-navbar {
|
||||||
|
background: #212529;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-teamsnap {
|
||||||
|
background-color: #ff8f00;
|
||||||
|
border-color: #cc7200;
|
||||||
|
color: #ffffff;
|
||||||
|
border-bottom-width: 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-family: "Open Sans", Helvetica, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 29px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 250ms ease-in-out;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-teamsnap:hover,
|
||||||
|
.btn-teamsnap:active,
|
||||||
|
.btn-teamsnap:focus {
|
||||||
|
background-color: #e68100;
|
||||||
|
border-color: #b86700;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-gamechanger {
|
||||||
|
color: #fff;
|
||||||
|
border-color: #1b73bc;
|
||||||
|
background-color: #1b73bc;
|
||||||
|
}
|
||||||
6
public/fonts/futura-now/stylesheet.css
Normal file
6
public/fonts/futura-now/stylesheet.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Futura Now";
|
||||||
|
src: url('futura-now.ttf') format('truetype');
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 900;
|
||||||
|
}
|
||||||
7
public/fonts/helvetica-now/stylesheet.css
Normal file
7
public/fonts/helvetica-now/stylesheet.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Helvetica Now";
|
||||||
|
src: url('helvetica-now.ttf') format('truetype');
|
||||||
|
font-weight: 125 900;
|
||||||
|
font-stretch: 50% 150%;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
4
public/fonts/inconsolata/stylesheet.css
Normal file
4
public/fonts/inconsolata/stylesheet.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "Inconsolata";
|
||||||
|
src: url("inconsolata-vf.ttf") format("truetype");
|
||||||
|
}
|
||||||
38
public/fonts/m+1m/m+1m.css
Normal file
38
public/fonts/m+1m/m+1m.css
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'm+1m';
|
||||||
|
src: url('mplus-1m-bold-webfont.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'm+1m';
|
||||||
|
src: url('mplus-1m-light-webfont.woff') format('woff');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'm+1m';
|
||||||
|
src: url('mplus-1m-medium-webfont.woff') format('woff');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'm+1m';
|
||||||
|
src: url('mplus-1m-regular-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'm+1m';
|
||||||
|
src: url('mplus-1m-thin-webfont.woff') format('woff');
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
68
public/fonts/vera/bitstreamvera.css
Normal file
68
public/fonts/vera/bitstreamvera.css
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans';
|
||||||
|
src: url('Vera-Bold-webfont.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans';
|
||||||
|
src: url('Vera-Bold-Italic-webfont.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: oblique;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans';
|
||||||
|
src: url('Vera-Italic-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: oblique;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans';
|
||||||
|
src: url('Vera-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono';
|
||||||
|
src: url('VeraMono-Bold-webfont.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono';
|
||||||
|
src: url('VeraMono-Bold-Italic-webfont.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono';
|
||||||
|
src: url('VeraMono-Italic-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono';
|
||||||
|
src: url('VeraMono-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono';
|
||||||
|
src: url('VeraMono-webfont.woff') format('woff');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
123
public/fonts/verdana/verdanapro.css
Normal file
123
public/fonts/verdana/verdanapro.css
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-Black.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-BlackItalic.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-Bold.ttf') format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-BoldItalic.ttf') format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondBlack.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondBlackItalic.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondBold.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondBoldItalic.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondItalic.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondLight.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondLightItalic.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondRegular.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondSemiBold.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-CondSemiBoldItalic.ttf') format('truetype');
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-Italic.ttf') format('truetype');
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-Light.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-LightItalic.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-Regular.ttf') format('truetype');
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-SemiBold.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'VerdanaPro';
|
||||||
|
src: url('VerdanaPro-SemiBoldItalic.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
115
public/js/load_from_teamsnap.js
Normal file
115
public/js/load_from_teamsnap.js
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
event_id = "292333461";
|
||||||
|
event_id_2 = "292333462";
|
||||||
|
team_id = "6882652";
|
||||||
|
|
||||||
|
function format_stat(number) {
|
||||||
|
const zeroPad = (num, places) => String(num).padStart(3, "0");
|
||||||
|
return zeroPad(Math.round(number * 1000), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function load_data_xxx() {
|
||||||
|
const event_id = document.querySelector('input[name="event_id"]').value;
|
||||||
|
const team_id = document.querySelector('input[name="team_id"]').value;
|
||||||
|
update_card(team_id, event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function update_card(team_id, event_id) {
|
||||||
|
fetch(`/${team_id}/event/${event_id}/gamecard/data`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then(function (items) {
|
||||||
|
console.log(items);
|
||||||
|
events = items.filter(function (item) {
|
||||||
|
return item.type == "event";
|
||||||
|
});
|
||||||
|
event_index = events.findIndex(function (e) {
|
||||||
|
return e.id == event_id;
|
||||||
|
});
|
||||||
|
event = events[event_index];
|
||||||
|
document.title = event.formattedTitle;
|
||||||
|
|
||||||
|
document.querySelectorAll(".event-title").forEach(function (element) {
|
||||||
|
element.innerText = event.formattedTitle;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(".event-label").forEach(function (element) {
|
||||||
|
element.innerText = event.label;
|
||||||
|
});
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll(".event-location-name")
|
||||||
|
.forEach(function (element) {
|
||||||
|
element.innerText = event.locationName;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(".opponent").forEach(function (element) {
|
||||||
|
element.innerText = event.opponentName;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(".homeaway").forEach(function (element) {
|
||||||
|
element.innerText = event.gameType;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(".event-date").forEach(function (element) {
|
||||||
|
element.innerText = new Date(event.startDate).toLocaleDateString(
|
||||||
|
"en-us",
|
||||||
|
{
|
||||||
|
weekday: "short",
|
||||||
|
day: "numeric",
|
||||||
|
// year: "numeric",
|
||||||
|
month: "short",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(".event-time").forEach(function (element) {
|
||||||
|
element.innerText = new Date(event.startDate).toLocaleTimeString(
|
||||||
|
"en-us",
|
||||||
|
{
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "2-digit",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("todays-game-header").innerText =
|
||||||
|
event.formattedTitle +
|
||||||
|
" - " +
|
||||||
|
new Date(event.startDate).toLocaleDateString("en-us", {
|
||||||
|
weekday: "short",
|
||||||
|
day: "numeric",
|
||||||
|
// year: "numeric",
|
||||||
|
month: "short",
|
||||||
|
}) +
|
||||||
|
" " +
|
||||||
|
new Date(event.startDate).toLocaleTimeString("en-us", {
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let j = -4; j < 5; j++) {
|
||||||
|
if (j < 0) {
|
||||||
|
plus_minus = "minus";
|
||||||
|
} else if (j > 0) {
|
||||||
|
plus_minus = "plus";
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
document.querySelector(
|
||||||
|
`th.today-${plus_minus}-${Math.abs(j)} div`
|
||||||
|
).textContent = new Date(
|
||||||
|
events[event_index + j].startDate
|
||||||
|
).toLocaleDateString("en-us", {
|
||||||
|
weekday: "short",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log({
|
||||||
|
0: events[event_index],
|
||||||
|
1: events[event_index + 1],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
6810
public/js/teamsnap.js
Normal file
6810
public/js/teamsnap.js
Normal file
File diff suppressed because it is too large
Load Diff
10
public/media/baseball-diamond.svg
Normal file
10
public/media/baseball-diamond.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?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>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
23
public/media/benchcoach.svg
Normal file
23
public/media/benchcoach.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 17 KiB |
128
routes/auth.js
Normal file
128
routes/auth.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
var express = require("express");
|
||||||
|
var passport = require("passport");
|
||||||
|
var TeamsnapStrategy = require("passport-teamsnap");
|
||||||
|
|
||||||
|
// Configure the TeamSnap strategy for use by Passport.
|
||||||
|
//
|
||||||
|
// OAuth 2.0-based strategies require a `verify` function which receives the
|
||||||
|
// credential (`accessToken`) for accessing the Facebook API on the user's
|
||||||
|
// 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
|
||||||
|
// authentication.
|
||||||
|
passport.use(
|
||||||
|
new TeamsnapStrategy(
|
||||||
|
{
|
||||||
|
apiVersion: "3",
|
||||||
|
clientID: process.env["TEAMSNAP_CLIENT_ID"],
|
||||||
|
clientSecret: process.env["TEAMSNAP_CLIENT_SECRET"],
|
||||||
|
callbackURL: "/auth/teamsnap/callback",
|
||||||
|
passReqToCallback: true,
|
||||||
|
},
|
||||||
|
function (req, accessToken, refreshToken, profile, done) {
|
||||||
|
json = JSON.parse(profile._raw);
|
||||||
|
new_profile = { access_token: accessToken };
|
||||||
|
new_profile["id"] = json.collection.items[0].data.filter(
|
||||||
|
(e) => e.name == "id"
|
||||||
|
)[0].value;
|
||||||
|
new_profile["email"] = json.collection.items[0].data.filter(
|
||||||
|
(e) => e.name == "email"
|
||||||
|
)[0].value;
|
||||||
|
new_profile["first_name"] = json.collection.items[0].data.filter(
|
||||||
|
(e) => e.name == "first_name"
|
||||||
|
)[0].value;
|
||||||
|
console.log("LI#35 session is ", req.session);
|
||||||
|
console.log("LI#35 session id is ", req.session.id);
|
||||||
|
req.session.teamsnap_access_token = accessToken;
|
||||||
|
teamsnap.init(process.env["TEAMSNAP_CLIENT_ID"]);
|
||||||
|
teamsnap.auth(accessToken);
|
||||||
|
// teamsnap.enablePersistence();
|
||||||
|
return done(null, new_profile);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Configure Passport authenticated session persistence.
|
||||||
|
//
|
||||||
|
// In order to restore authentication state across HTTP requests, Passport needs
|
||||||
|
// to serialize users into and deserialize users out of the session. In a
|
||||||
|
// production-quality application, this would typically be as simple as
|
||||||
|
// supplying the user ID when serializing, and querying the user record by ID
|
||||||
|
// from the database when deserializing. However, due to the fact that this
|
||||||
|
// example does not have a database, the complete Facebook profile is serialized
|
||||||
|
// and deserialized.
|
||||||
|
passport.serializeUser(function (user, cb) {
|
||||||
|
process.nextTick(function () {
|
||||||
|
console.log("L#51 serializing user id", user.id);
|
||||||
|
cb(null, {
|
||||||
|
id: user.id,
|
||||||
|
username: user.email,
|
||||||
|
name: user.firstName,
|
||||||
|
accessToken: user.access_token,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.deserializeUser(function (user, cb) {
|
||||||
|
process.nextTick(function () {
|
||||||
|
return cb(null, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET /login
|
||||||
|
*
|
||||||
|
* This route prompts the user to log in.
|
||||||
|
*
|
||||||
|
* The 'login' view renders an HTML page, which contain a button prompting the
|
||||||
|
* user to sign in with TeamSnap. When the user clicks this button, a request
|
||||||
|
* will be sent to the `GET /login/federated/teamsnap` route.
|
||||||
|
*/
|
||||||
|
router.get("/login", function (req, res, next) {
|
||||||
|
res.render("login");
|
||||||
|
});
|
||||||
|
|
||||||
|
/* GET /login/federated/teamsnap
|
||||||
|
*
|
||||||
|
* This route redirects the user to TeamSnap, where they will authenticate.
|
||||||
|
*
|
||||||
|
* Signing in with TeamSnap is implemented using OAuth 2.0. This route initiates
|
||||||
|
* an OAuth 2.0 flow by redirecting the user to TeamSnap's identity server.
|
||||||
|
* Once there, TeamSnap will authenticate the user
|
||||||
|
* and obtain their consent to release identity information to this app.
|
||||||
|
*
|
||||||
|
* Once TeamSnap has completed their interaction with the user, the user will be
|
||||||
|
* redirected back to the app.
|
||||||
|
*/
|
||||||
|
router.get("/login/federated/teamsnap", passport.authenticate("teamsnap"));
|
||||||
|
|
||||||
|
/*
|
||||||
|
This route completes the authentication sequence when TeamSnap redirects the
|
||||||
|
user back to the application. When a new user signs in, a user account is
|
||||||
|
automatically created and their TeamSnap account is linked. When an existing
|
||||||
|
user returns, they are signed in to their linked account.
|
||||||
|
*/
|
||||||
|
router.get(
|
||||||
|
"/auth/teamsnap",
|
||||||
|
passport.authenticate("teamsnap", function (err, user, info, status) {})
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get("/auth/teamsnap/callback", function (req, res, next) {
|
||||||
|
passport.authenticate("teamsnap", function (err, user, info, status) {
|
||||||
|
if (err) {
|
||||||
|
// do something with the error
|
||||||
|
console.error("error: ", err);
|
||||||
|
}
|
||||||
|
// success
|
||||||
|
console.log("L#105 user is ", user);
|
||||||
|
req.logIn(user, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.redirect("/");
|
||||||
|
});
|
||||||
|
})(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
390
routes/index.js
Normal file
390
routes/index.js
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
var express = require("express");
|
||||||
|
var ensureLogIn = require("connect-ensure-login").ensureLoggedIn;
|
||||||
|
var papaparse = require("papaparse");
|
||||||
|
|
||||||
|
var ensureLoggedIn = ensureLogIn();
|
||||||
|
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
function authTeamsnap(user) {
|
||||||
|
if (!teamsnap.isAuthed()) {
|
||||||
|
teamsnap.init(process.env["TEAMSNAP_CLIENT_ID"]);
|
||||||
|
teamsnap.auth(user.accessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function availabilitiesSort(a, b) {
|
||||||
|
status_code_sort = [
|
||||||
|
teamsnap.AVAILABILITIES.YES,
|
||||||
|
teamsnap.AVAILABILITIES.MAYBE,
|
||||||
|
teamsnap.AVAILABILITIES.NO,
|
||||||
|
teamsnap.AVAILABILITIES.NONE,
|
||||||
|
];
|
||||||
|
a_sort = status_code_sort.indexOf(a.statusCode);
|
||||||
|
b_sort = status_code_sort.indexOf(b.statusCode);
|
||||||
|
if (a_sort > b_sort) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a_sort < b_sort) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a_sort == b_sort) {
|
||||||
|
if (a.member.lastName < b.member.lastName) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.member.lastName > b.member.lastName) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetch_stats(resolve, reject) {
|
||||||
|
url =
|
||||||
|
"https://docs.google.com/spreadsheets/d/{sheet_id}/export?format=csv&gid={tab_id}";
|
||||||
|
papaparse.Papa.parse(url, {
|
||||||
|
download: true,
|
||||||
|
complete: function (results) {
|
||||||
|
results.data.forEach((row, i) => {
|
||||||
|
if (i == 0 || row[2] == "Totals" || row[2] == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d = {
|
||||||
|
first_name: row[3],
|
||||||
|
last_name: row[2],
|
||||||
|
jersey_number: row[1],
|
||||||
|
pa: row[5],
|
||||||
|
ab: row[6],
|
||||||
|
avg: row[20],
|
||||||
|
obp: row[21],
|
||||||
|
slg: row[22],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
resolve(d);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GET home page. */
|
||||||
|
router.get("/", ensureLoggedIn, function (req, res, next) {
|
||||||
|
if (req.user) {
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
teamsnap.loadCollections(function (err) {
|
||||||
|
if (err) {
|
||||||
|
alert("Error loading TeamSnap SDK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
teamsnap.loadTeams(function onTeamsLoad(err, teams) {
|
||||||
|
teams = teams.sort((a, b) => b.seasonName - a.seasonName);
|
||||||
|
res.render("home", { req: req, teams: teams });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.render("home", { req: req });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/teams",
|
||||||
|
ensureLoggedIn,
|
||||||
|
function (req, res, next) {
|
||||||
|
console.log("teamsnap authed?: ", teamsnap.isAuthed());
|
||||||
|
console.log("user is", req.user);
|
||||||
|
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
teamsnap.loadCollections(function (err) {
|
||||||
|
if (err) {
|
||||||
|
alert("Error loading TeamSnap SDK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
teamsnap.loadTeams(function onTeamsLoad(err, teams) {
|
||||||
|
teams = teams.sort((a, b) => b.seasonName - a.seasonName);
|
||||||
|
res.render("teams", { teams: teams });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
function (req, res, next) {
|
||||||
|
// res.send(`${me.firstName} ${me.lastName}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get("/:team_id([0-9]+)", ensureLoggedIn, function (req, res, next) {
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
team_id = req.params.team_id;
|
||||||
|
console.log("team_id", team_id);
|
||||||
|
teamsnap.loadCollections(function (err) {
|
||||||
|
if (err) {
|
||||||
|
alert("Error loading TeamSnap SDK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
teamsnap.enablePersistence();
|
||||||
|
|
||||||
|
teamsnap.bulkLoad(
|
||||||
|
team_id,
|
||||||
|
["team", "member", "event", "opponent", "availability_summary"],
|
||||||
|
function onBulkLoad(err, items) {
|
||||||
|
team = items.find((i) => (i.type == "team") & (i.id == team_id));
|
||||||
|
console.log(team);
|
||||||
|
res.set("Content-Type", "text/html");
|
||||||
|
res.render("team", { team: team });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:team_id/event/:event_id",
|
||||||
|
ensureLoggedIn,
|
||||||
|
function (req, res, next) {
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
var team_id = req.params.team_id;
|
||||||
|
var event_id = req.params.event_id;
|
||||||
|
teamsnap.loadCollections(function (err) {
|
||||||
|
console.log();
|
||||||
|
teamsnap.enablePersistence();
|
||||||
|
|
||||||
|
teamsnap.bulkLoad(
|
||||||
|
team_id,
|
||||||
|
["team", "event", "availabilitySummary"],
|
||||||
|
function (err, items) {
|
||||||
|
if (err) {
|
||||||
|
res.code = 500;
|
||||||
|
res.send(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
availabilitySummaries = items.filter(
|
||||||
|
(i) => i.type == "availabilitySummary" && i.id == event_id
|
||||||
|
);
|
||||||
|
events = items.filter((i) => i.type == "event" && i.id == event_id);
|
||||||
|
|
||||||
|
if (events) {
|
||||||
|
event = events[0];
|
||||||
|
availabilitySummary = availabilitySummaries[0];
|
||||||
|
console.log("A_S", availabilitySummaries);
|
||||||
|
res.render("event", {
|
||||||
|
event: event,
|
||||||
|
team_id: team_id,
|
||||||
|
team: items.find((i) => i.type == "team" && i.id == team_id),
|
||||||
|
availabilitySummary: availabilitySummary,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.code = 500;
|
||||||
|
res.send("error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:team_id/event/:event_id/gamecard",
|
||||||
|
ensureLoggedIn,
|
||||||
|
function (req, res, next) {
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
team_id = req.params.team_id;
|
||||||
|
event_id = req.params.event_id;
|
||||||
|
teamsnap.loadCollections((err) => {
|
||||||
|
teamsnap.enablePersistence();
|
||||||
|
var events;
|
||||||
|
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 = {
|
||||||
|
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(availabilitiesSort),
|
||||||
|
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("gamecard", context);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:team_id/event/:event_id/lineup",
|
||||||
|
ensureLoggedIn,
|
||||||
|
function (req, res, next) {
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
team_id = req.params.team_id;
|
||||||
|
event_id = req.params.event_id;
|
||||||
|
teamsnap.loadCollections((err) => {
|
||||||
|
teamsnap.enablePersistence();
|
||||||
|
var events;
|
||||||
|
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 = {
|
||||||
|
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(availabilitiesSort),
|
||||||
|
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("lineup", context);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get("/:team_id/events", ensureLoggedIn, function (req, res, next) {
|
||||||
|
authTeamsnap(req.user);
|
||||||
|
team_id = req.params.team_id;
|
||||||
|
event_id = req.params.event_id;
|
||||||
|
teamsnap.loadCollections(function (err) {
|
||||||
|
teamsnap
|
||||||
|
.bulkLoad(team_id, ["team", "event", "availability_summary"])
|
||||||
|
.then((items) => {
|
||||||
|
res.set("Content-Type", "text/html");
|
||||||
|
res.render("events", {
|
||||||
|
team: items.find((i) => i.type == "team" && i.id == team_id),
|
||||||
|
events: items.filter((i) => i.type == "event"),
|
||||||
|
availabilitySummaries: items.filter(
|
||||||
|
(i) => i.type == "availabilitySummary"
|
||||||
|
),
|
||||||
|
team_id: team_id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
8
views/error.pug
Normal file
8
views/error.pug
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
html
|
||||||
|
head
|
||||||
|
body
|
||||||
|
h1 error
|
||||||
|
h2
|
||||||
|
error.status
|
||||||
|
pre
|
||||||
|
message #{message}
|
||||||
46
views/event.pug
Normal file
46
views/event.pug
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
html
|
||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||||
|
title #{event.formattedTitle}
|
||||||
|
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')
|
||||||
|
body
|
||||||
|
.container
|
||||||
|
.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
|
||||||
|
.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}
|
||||||
|
hr
|
||||||
|
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}/gamecard`)
|
||||||
|
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
|
||||||
37
views/events.pug
Normal file
37
views/events.pug
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
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='/css/teamsnap-ui.css')
|
||||||
|
body
|
||||||
|
.container
|
||||||
|
.Panel
|
||||||
|
.Panel-header
|
||||||
|
.Panel-title Schedule
|
||||||
|
.Panel-body
|
||||||
|
each event in events
|
||||||
|
- var availabilitySummary = availabilitySummaries.find((a)=>a.eventId==event.id)
|
||||||
|
.Panel-row
|
||||||
|
a(class="event list-group-item" href=`/${team_id}/event/${event.id}`)
|
||||||
|
h4 #{event.formattedTitle}
|
||||||
|
p.small
|
||||||
|
| #{event.startDate.toLocaleDateString("en-us",{weekday: "short", day: "numeric",month: "short"})}
|
||||||
|
| #{event.startDate.toLocaleTimeString("en-us",{hour: "numeric", minute: "2-digit"})}
|
||||||
|
p #{event.locationName}
|
||||||
|
.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}
|
||||||
|
|
||||||
409
views/gamecard.pug
Normal file
409
views/gamecard.pug
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
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
|
||||||
29
views/home.pug
Normal file
29
views/home.pug
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||||
|
title BenchCoach - Home
|
||||||
|
link(rel='stylesheet' href='/css/bootstrap.min.css')
|
||||||
|
link(rel='stylesheet' href='/css/project.css')
|
||||||
|
body.bg-light
|
||||||
|
.container
|
||||||
|
.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}`) #{team.name} [#{team.seasonName}]
|
||||||
|
else
|
||||||
|
a.btn.btn-outline-primary(href="login")
|
||||||
|
| Login
|
||||||
139
views/lineup.pug
Normal file
139
views/lineup.pug
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
html
|
||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
title #{event.formattedTitle}
|
||||||
|
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')
|
||||||
|
|
||||||
|
body.bg-light
|
||||||
|
.container
|
||||||
|
div(style="max-width: 455px")
|
||||||
|
.Panel
|
||||||
|
.panel-header
|
||||||
|
.Panel-title #{event.formattedTitle}
|
||||||
|
.Panel-body
|
||||||
|
.Panel-row
|
||||||
|
p.text-muted.mb-2 #{event.startDate}
|
||||||
|
p #{event.locationName}
|
||||||
|
.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"]
|
||||||
|
if event_lineup_entries.map((lue)=>lue.label).includes(pos)
|
||||||
|
.Grid-cell.text-success
|
||||||
|
|#{pos}
|
||||||
|
else
|
||||||
|
.Grid-cell.text-danger
|
||||||
|
|#{pos}
|
||||||
|
each lineup_entry, i in event_lineup_entries_offense
|
||||||
|
.Panel-row.Panel-row--withCells
|
||||||
|
.Panel-cell.Panel-cell--header.u-padXs.u-size1of12
|
||||||
|
.u-flexAlignSelfCenter
|
||||||
|
|#{i+1}
|
||||||
|
.Panel-cell.u-padXs.u-size8of12.fw-bold.text-uppercase
|
||||||
|
if lineup_entry.availabilityStatusCode == 2
|
||||||
|
i.bi.bi-question-circle-fill.text-info.u-spaceRightXs
|
||||||
|
else if lineup_entry.availabilityStatusCode == 1
|
||||||
|
i.bi.bi-check-circle-fill.text-success.u-spaceRightXs
|
||||||
|
else if lineup_entry.availabilityStatusCode == 0
|
||||||
|
i.bi.bi-x-circle-fill.text-danger.u-spaceRightXs
|
||||||
|
else
|
||||||
|
i.bi.bi-question-circle.u-spaceRightXs
|
||||||
|
|#{lineup_entry.member.lastName}
|
||||||
|
.Panel-cell.u-padXs.u-size2of12
|
||||||
|
.SelectBox
|
||||||
|
select.SelectBox-options
|
||||||
|
each pos in ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"]
|
||||||
|
option(selected=lineup_entry.label==pos) #{pos}
|
||||||
|
.Panel-cell.u-padXs.u-flexAlignSelfCenter.u-size1of12
|
||||||
|
.drag-handle
|
||||||
|
i.bi.bi-grip-vertical.text-secondary
|
||||||
|
.Panel
|
||||||
|
.Panel-body
|
||||||
|
.Panel-row.Panel-title.u-padXs
|
||||||
|
i.bi.bi-clipboard-minus.me-1
|
||||||
|
span Starting Lineup (Position Only)
|
||||||
|
each lineup_entry, i in event_lineup_entries
|
||||||
|
if lineup_entry.label.includes("[PO]")
|
||||||
|
.Panel-row.Panel-row--withCells
|
||||||
|
.Panel-cell.Panel-cell--header.u-padXs.u-size1of12
|
||||||
|
.u-flexAlignSelfCenter
|
||||||
|
|#{i+1}
|
||||||
|
.Panel-cell.u-padXs.u-size8of12.fw-bold.text-uppercase
|
||||||
|
if lineup_entry.availabilityStatusCode == 2
|
||||||
|
i.bi.bi-question-circle-fill.text-info.u-spaceRightXs
|
||||||
|
else if lineup_entry.availabilityStatusCode == 1
|
||||||
|
i.bi.bi-check-circle-fill.text-success.u-spaceRightXs
|
||||||
|
else if lineup_entry.availabilityStatusCode == 0
|
||||||
|
i.bi.bi-x-circle-fill.text-danger.u-spaceRightXs
|
||||||
|
else
|
||||||
|
i.bi.bi-question-circle.u-spaceRightXs
|
||||||
|
|#{lineup_entry.member.lastName}
|
||||||
|
.Panel-cell.u-padXs.u-size2of12
|
||||||
|
.SelectBox
|
||||||
|
select.SelectBox-options
|
||||||
|
each pos in ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"]
|
||||||
|
option(selected=lineup_entry.label==pos) #{pos}
|
||||||
|
.Panel-cell.u-padXs.u-flexAlignSelfCenter.u-size1of12
|
||||||
|
.drag-handle
|
||||||
|
i.bi.bi-grip-vertical.text-secondary
|
||||||
|
.Panel
|
||||||
|
.Panel-body
|
||||||
|
.Panel-row.Panel-title.u-padXs
|
||||||
|
i.bi.bi-clipboard.me-1
|
||||||
|
span Bench
|
||||||
|
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)
|
||||||
|
.Panel-row.Panel-row--withCells
|
||||||
|
.Panel-cell.Panel-cell--header.u-padXs.u-size1of12
|
||||||
|
.u-flexAlignSelfCenter
|
||||||
|
.Panel-cell.u-padXs.u-size8of12.fw-bold.text-uppercase
|
||||||
|
if availability.statusCode == 2
|
||||||
|
i.bi.bi-question-circle-fill.text-info.u-spaceRightXs
|
||||||
|
else if availability.statusCode == 1
|
||||||
|
i.bi.bi-check-circle-fill.text-success.u-spaceRightXs
|
||||||
|
else if availability.statusCode == 0
|
||||||
|
i.bi.bi-x-circle-fill.text-danger.u-spaceRightXs
|
||||||
|
else
|
||||||
|
i.bi.bi-question-circle.u-spaceRightXs
|
||||||
|
|#{availability.member.lastName}
|
||||||
|
.Panel-cell.u-padXs.u-size2of12
|
||||||
|
.SelectBox
|
||||||
|
select.SelectBox-options
|
||||||
|
each pos in ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"]
|
||||||
|
option #{pos}
|
||||||
|
.Panel-cell.u-padXs.u-flexAlignSelfCenter.u-size1of12
|
||||||
|
.drag-handle
|
||||||
|
i.bi.bi-grip-vertical.text-secondary
|
||||||
|
|
||||||
|
.Panel
|
||||||
|
.Panel-body
|
||||||
|
.Panel-row.Panel-title.u-padXs
|
||||||
|
i.bi.bi-clipboard-x.me-1
|
||||||
|
span Out
|
||||||
|
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))
|
||||||
|
.Panel-row.Panel-row--withCells
|
||||||
|
.Panel-cell.Panel-cell--header.u-padXs.u-size1of12
|
||||||
|
.u-flexAlignSelfCenter
|
||||||
|
.Panel-cell.u-padXs.u-size8of12.fw-bold.text-uppercase
|
||||||
|
if availability.statusCode == 2
|
||||||
|
i.bi.bi-question-circle-fill.text-info.u-spaceRightXs
|
||||||
|
else if availability.statusCode == 1
|
||||||
|
i.bi.bi-check-circle-fill.text-success.u-spaceRightXs
|
||||||
|
else if availability.statusCode == 0
|
||||||
|
i.bi.bi-x-circle-fill.text-danger.u-spaceRightXs
|
||||||
|
else
|
||||||
|
i.bi.bi-question-circle.u-spaceRightXs
|
||||||
|
|#{availability.member.lastName}
|
||||||
|
.Panel-cell.u-padXs.u-size2of12
|
||||||
|
.SelectBox
|
||||||
|
select.SelectBox-options
|
||||||
|
each pos in ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"]
|
||||||
|
option #{pos}
|
||||||
|
.Panel-cell.u-padXs.u-flexAlignSelfCenter.u-size1of12
|
||||||
|
.drag-handle
|
||||||
|
i.bi.bi-grip-vertical.text-secondary
|
||||||
|
|
||||||
19
views/login.pug
Normal file
19
views/login.pug
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
html
|
||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||||
|
title BenchCoach - Login
|
||||||
|
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')
|
||||||
|
body
|
||||||
|
.u-padSidesMd.u-xs-padSidesLg
|
||||||
|
.Panel.u-padLg
|
||||||
|
h3 BenchCoach
|
||||||
|
p Sign in
|
||||||
|
|
||||||
|
a(class="Button Button--large Button--orange" href="/login/federated/teamsnap")
|
||||||
|
i(class="bi bi-asterisk")/
|
||||||
|
span Sign in with TeamSnap
|
||||||
|
|
||||||
|
|
||||||
17
views/team.pug
Normal file
17
views/team.pug
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
html
|
||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||||
|
title BenchCoach - #{team.name}
|
||||||
|
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')
|
||||||
|
|
||||||
|
body
|
||||||
|
.container
|
||||||
|
h2 #{team.name}
|
||||||
|
p #{team.seasonName}
|
||||||
|
hr
|
||||||
|
ul.list-group
|
||||||
|
a(class="list-group-item" href=`${team.id}/events`) Events
|
||||||
|
a(class="list-group-item" href=`${team.id}/roster`) Roster
|
||||||
14
views/teams.pug
Normal file
14
views/teams.pug
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
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')
|
||||||
|
|
||||||
|
body
|
||||||
|
ul
|
||||||
|
each team in teams
|
||||||
|
li
|
||||||
|
a(class='team' href=`/${team.id}`) #{team.name} [#{team.seasonName}]
|
||||||
Reference in New Issue
Block a user