Compare commits
14 Commits
fb648d477f
...
f2371c6b5a
| Author | SHA1 | Date | |
|---|---|---|---|
|
f2371c6b5a
|
|||
|
dc17ca76ba
|
|||
|
d24b2a121e
|
|||
|
e4b4345cff
|
|||
|
dfab474f42
|
|||
|
053f6038f6
|
|||
|
cb69521875
|
|||
|
58c870ce7c
|
|||
|
61b6dc8a35
|
|||
|
b53c8c532e
|
|||
|
4f30021a99
|
|||
|
fda4e1c3cc
|
|||
|
a9fa89107e
|
|||
|
7efb083e1d
|
4
package-lock.json
generated
4
package-lock.json
generated
@@ -34,7 +34,7 @@
|
||||
"pluralize": "^8.0.0",
|
||||
"pug": "^3.0.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"teamsnap.js": "github:anthonyscorrea/teamsnap-javascript-sdk#add-eventLineup-eventLineupEntry",
|
||||
"teamsnap.js": "github:anthonyscorrea/teamsnap-javascript-sdk#link-with-null-link",
|
||||
"tinymce": "^6.8.3",
|
||||
"underscore": "^1.13.6",
|
||||
"xhr2": "^0.2.1"
|
||||
@@ -4708,7 +4708,7 @@
|
||||
},
|
||||
"node_modules/teamsnap.js": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "git+ssh://git@github.com/anthonyscorrea/teamsnap-javascript-sdk.git#67168be49492fe5a0331bbbeb33408a42c40ab9a",
|
||||
"resolved": "git+ssh://git@github.com/anthonyscorrea/teamsnap-javascript-sdk.git#cf747f97f030270615f6d293e734adb6fb1b5e86",
|
||||
"dependencies": {
|
||||
"form-data": "1.0.0-rc3",
|
||||
"xmlhttprequest": "1.8.0"
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"pluralize": "^8.0.0",
|
||||
"pug": "^3.0.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"teamsnap.js": "github:anthonyscorrea/teamsnap-javascript-sdk#add-eventLineup-eventLineupEntry",
|
||||
"teamsnap.js": "github:anthonyscorrea/teamsnap-javascript-sdk#link-with-null-link",
|
||||
"tinymce": "^6.8.3",
|
||||
"underscore": "^1.13.6",
|
||||
"xhr2": "^0.2.1"
|
||||
|
||||
14
src/app.js
14
src/app.js
@@ -43,6 +43,16 @@ hbs.registerHelper('section', (name, options) => {
|
||||
this._sections[name] = options.fn(this);
|
||||
return null;
|
||||
})
|
||||
hbs.registerHelper('script_tags', (scripts, options) => {
|
||||
if(!scripts) {
|
||||
return null;
|
||||
}
|
||||
var result = [];
|
||||
scripts.forEach((script)=>{
|
||||
result.push(`<script src="${script}"></script>`)
|
||||
})
|
||||
return result.join('\n');
|
||||
})
|
||||
hbs.registerHelper("embeddedSvgFromPath", require('./lib/utils').embeddedSvgFromPath)
|
||||
hbs.registerHelper(require("./controllers/event").helpers)
|
||||
hbs.registerHelper(require("./controllers/eventlineup").helpers)
|
||||
@@ -128,9 +138,9 @@ app.use(function (req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
app.use('/', require('./routes/meta').router);
|
||||
app.use("/", authRouter);
|
||||
app.use("/", indexRouter);
|
||||
|
||||
app.use(require("./routes/team").router)
|
||||
app.use(require("./routes/opponent").router)
|
||||
app.use(require("./routes/event").router)
|
||||
@@ -142,7 +152,7 @@ app.use(require("./routes/eventsheet").router)
|
||||
app.use(function (err, req, res, next) {
|
||||
// set locals, only providing error in development
|
||||
if (err) {
|
||||
res.locals.message = err.message;
|
||||
res.locals.message = req.app.get("env") === "development" ? err.message : "An error has occurred";
|
||||
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||
if (typeof err === 'string' || err instanceof String) {
|
||||
err = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
tsUtils = require("../lib/utils");
|
||||
const path = require('path');
|
||||
const { teamsnapFailure, tsPromise } = require("../lib/utils");
|
||||
const { teamsnapFailure, tsPromise, teamsnapCallback } = require("../lib/utils");
|
||||
const {promisify} = require('util')
|
||||
|
||||
|
||||
exports.helpers = {
|
||||
@@ -23,8 +24,12 @@ exports.partials = path.join(__dirname, "../views/event/partials")
|
||||
exports.getEvents = async (req, res, next) => {
|
||||
const {user, team, layout} = req
|
||||
const bulkLoadTypes = ["event", "availabilitySummary"]
|
||||
req.promises.push(
|
||||
tsPromise('bulkLoad', {teamId: team.id, types: bulkLoadTypes})
|
||||
// const tsPromiseBulkload = promisify(teamsnap.bulkLoad)
|
||||
const promise = teamsnap.bulkLoad(
|
||||
{teamId: team.id, types: bulkLoadTypes},
|
||||
undefined,
|
||||
(err,items) => {teamsnapCallback(err, items, {req, source: 'getEvents', method: 'bulkLoad'})}
|
||||
)
|
||||
.then(items=>tsUtils.groupTeamsnapItems(items))
|
||||
.then(items=>{
|
||||
items.events.forEach((event) => {
|
||||
@@ -34,13 +39,10 @@ exports.getEvents = async (req, res, next) => {
|
||||
req.events = items.events;
|
||||
}
|
||||
)
|
||||
.then(tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team.id, req))
|
||||
.catch((err) => {
|
||||
teamsnapFailure(err,next)
|
||||
})
|
||||
)
|
||||
await Promise.all(req.promises)
|
||||
|
||||
req.promises.push(promise)
|
||||
all = await Promise.all(req.promises)
|
||||
|
||||
try {
|
||||
const context = {
|
||||
title: "Events",
|
||||
|
||||
@@ -15,9 +15,11 @@ const statusCodeIcons = {
|
||||
}
|
||||
|
||||
exports.helpers = {
|
||||
flagsString: (flags) => flags?.join(","),
|
||||
flagsString: (flags) => {
|
||||
return flags != null ? Array.from(flags).join(",") : ''
|
||||
},
|
||||
plus1: (i) => Number(i)+1,
|
||||
positions: () => ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH"],
|
||||
positions: () => ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "EH", "DH", "DR"],
|
||||
defense_positions: () => ["C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "P"],
|
||||
avail_status_code_icon: (status_code) => {
|
||||
const icon_classes = {
|
||||
@@ -39,7 +41,21 @@ exports.helpers = {
|
||||
return `<button class="Button Button--smallSquare ${button_classes[status_code]}" type="button"><span class="">${statusCodeIcons[status_code]}</span></button>`
|
||||
},
|
||||
positionLabelWithoutFlags: (label) => {
|
||||
return label.replace(/(.*?)\s\[(.*?)\]/, "$1");
|
||||
const {positionLabelWithoutFlags} = parsePositionLabel(label);
|
||||
return positionLabelWithoutFlags
|
||||
},
|
||||
positionLabelWithoutPOFlag: (label) => {
|
||||
const {positionLabelWithoutFlags, positionFlags} = parsePositionLabel(label);
|
||||
positionFlags.delete('PO')
|
||||
return compilePositionLabel(positionLabelWithoutFlags, positionFlags)
|
||||
},
|
||||
positionFlags: (label)=> {
|
||||
const {positionFlags} = parsePositionLabel(label);
|
||||
return `[${Array.from(positionFlags).join(",")}]`
|
||||
},
|
||||
hasPositionFlags: (label) => {
|
||||
const {positionLabelWithoutFlags, positionFlags} = parsePositionLabel(label);
|
||||
return positionFlags.size > 0;
|
||||
},
|
||||
comparePositionWithFlags: (labelWithoutFlags, eventLineupEntry, options) => {
|
||||
labelWithFlags = eventLineupEntry?.label
|
||||
@@ -52,12 +68,12 @@ exports.helpers = {
|
||||
isInStartingLineup: (member) => {
|
||||
if (member.benchcoach.eventLineupEntry == null || member.benchcoach.eventLineupEntry.label == '') return false;
|
||||
const {positionFlags} = parsePositionLabel(member.benchcoach.eventLineupEntry?.label);
|
||||
return (positionFlags != "PO")
|
||||
return (!positionFlags.has("PO"))
|
||||
},
|
||||
isInPositionOnly: (member) => {
|
||||
if (!member.benchcoach || member.benchcoach.eventLineupEntry == null) return false;
|
||||
const {positionFlags} = parsePositionLabel(member.benchcoach.eventLineupEntry?.label);
|
||||
return (member.benchcoach.eventLineupEntry != null && positionFlags == "PO")
|
||||
return (member.benchcoach.eventLineupEntry != null && positionFlags.has("PO"))
|
||||
},
|
||||
isInBench: (member) => {
|
||||
if ((member.benchcoach.eventLineupEntry != null && member.benchcoach.eventLineupEntry.label != '') || member.isNonPlayer) return false;
|
||||
@@ -80,12 +96,40 @@ exports.helpers = {
|
||||
}
|
||||
|
||||
exports.getEventLineup = async (req, res)=>{
|
||||
// res.send(req.event_lineup)
|
||||
await Promise.all(req.promises)
|
||||
const {user, team, members, event, layout, event_lineup, event_lineup_entries, availabilities, availabilitySummary, csrfToken} = req
|
||||
attachBenchcoachPropertiesToMember(members, event_lineup_entries, availabilities)
|
||||
members.sort(tsUtils.teamsnapMembersSortLineupAvailabilityLastName)
|
||||
res.render("eventlineup/edit", {user, team, members, event, layout, event_lineup, event_lineup_entries, availabilitySummary, csrfToken})
|
||||
const scripts = [
|
||||
"https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js",
|
||||
"/js/eventlineup.js",
|
||||
"/js/tinymce.min.js"
|
||||
]
|
||||
res.render("eventlineup/edit", {user, team, members, event, scripts, layout, event_lineup, event_lineup_entries, availabilitySummary, csrfToken})
|
||||
}
|
||||
|
||||
exports.getAdjacentEventLineup = async (req, res) => {
|
||||
await Promise.all(req.promises)
|
||||
const index = Number(req.query.index)
|
||||
const {user, team, members, csrfToken} = req
|
||||
let event
|
||||
if (index > 0) {
|
||||
event = req.upcoming_events[index-1]
|
||||
}
|
||||
else if (index < 0){
|
||||
event = req.recent_events[Math.abs(index)-1]
|
||||
} else {
|
||||
throw new Error('Index must be positive or negative number')
|
||||
}
|
||||
const availabilitySummary = event.availabilitySummary
|
||||
const event_lineup = req.timeline.event_lineups.find(i=>i.eventId==event.id)
|
||||
const event_lineup_entries = req.timeline.event_lineup_entries.filter(i=>i.eventId==event.id)
|
||||
const availabilities = req.timeline.availabilities.filter(i=>i.eventId==event.id)
|
||||
attachBenchcoachPropertiesToMember(members, event_lineup_entries, availabilities)
|
||||
members.sort(tsUtils.teamsnapMembersSortLineupAvailabilityLastName)
|
||||
console.log()
|
||||
// res.status(200).send('Received')
|
||||
res.render("eventlineup/edit", {user, team, members, event, layout: null, event_lineup, event_lineup_entries, availabilitySummary, csrfToken})
|
||||
}
|
||||
|
||||
attachBenchcoachPropertiesToMember = (members, event_lineup_entries, availabilities) => {
|
||||
@@ -125,7 +169,7 @@ exports.getEventLineupEmail = async (req, res)=>{
|
||||
const {newEventLineupEntries} = processPostedEventLineupEntries(body, eventLineupEntries, event_lineup)
|
||||
attachBenchcoachPropertiesToMember(members, newEventLineupEntries, availabilities)
|
||||
members.sort(tsUtils.teamsnapMembersSortLineupAvailabilityLastName)
|
||||
res.status(200).render("eventlineup/partials/email_table", {user, team, members, event, event_lineup, event_lineup_entries: newEventLineupEntries, availabilities, availabilitySummary})
|
||||
res.status(200).render("eventlineup/partials/email_modal.hbs", {layout:null, user, team, members, event, event_lineup, event_lineup_entries: newEventLineupEntries, availabilities, availabilitySummary})
|
||||
}
|
||||
|
||||
exports.getEventLineupEntries = async (req, res)=>{
|
||||
@@ -143,29 +187,39 @@ exports.postEventLineup = async (req,res) => {
|
||||
if (body.memberId == null) {res.status(400).end();return}
|
||||
await Promise.all(req.promises);
|
||||
const eventLineupEntries = req.event_lineup.eventLineupEntries
|
||||
const {newEventLineupEntries} = processPostedEventLineupEntries(body, eventLineupEntries, req.event_lineup)
|
||||
const {newEventLineupEntries, deleteEventLineupEntries} = processPostedEventLineupEntries(body, eventLineupEntries, req.event_lineup)
|
||||
newEventLineupEntries.forEach(e=>{
|
||||
teamsnap.saveEventLineupEntry(e)
|
||||
})
|
||||
deleteEventLineupEntries.forEach(e=>{
|
||||
teamsnap.deleteEventLineupEntry(e)
|
||||
})
|
||||
|
||||
eventLineup = await teamsnap.loadEventLineups(req.params.event_id)
|
||||
res.status(201).end()
|
||||
}
|
||||
|
||||
const processPostedEventLineupEntries = (body, eventLineupEntries, eventLineup) => {
|
||||
const newEventLineupEntries = []
|
||||
const deleteEventLineupEntries = []
|
||||
|
||||
body.memberId.forEach((memberId, i)=>{
|
||||
const lineupEntryId = body.eventLineupEntryId[i]
|
||||
const lineupEntryLabel = body.label[i]
|
||||
const lineupEntrySequence = body.sequence[i]
|
||||
const lineupEntryFlags = body.flags[i]
|
||||
if (lineupEntryId != '') {
|
||||
if (lineupEntryId != '' && lineupEntryLabel != '') {
|
||||
// Update lineup entry
|
||||
const eventLineupEntry = eventLineupEntries.find((e)=>e.id==Number(lineupEntryId))
|
||||
eventLineupEntry.sequence = lineupEntrySequence
|
||||
eventLineupEntry.label = compilePositionLabel(lineupEntryLabel, lineupEntryFlags)
|
||||
newEventLineupEntries.push(eventLineupEntry)
|
||||
}
|
||||
else if (lineupEntryId != '') {
|
||||
// Delete lineup entry
|
||||
const eventLineupEntry = eventLineupEntries.find((e)=>e.id==Number(lineupEntryId))
|
||||
deleteEventLineupEntries.push(eventLineupEntry)
|
||||
}
|
||||
else if (lineupEntryLabel != '') {
|
||||
// Create lineup entry
|
||||
const eventLineupEntry = teamsnap.createEventLineupEntry()
|
||||
@@ -179,5 +233,5 @@ const processPostedEventLineupEntries = (body, eventLineupEntries, eventLineup)
|
||||
// Skip lineup entry
|
||||
}
|
||||
})
|
||||
return {newEventLineupEntries, eventLineupEntries}
|
||||
return {newEventLineupEntries, eventLineupEntries, deleteEventLineupEntries}
|
||||
}
|
||||
@@ -3,21 +3,21 @@ const { teamsnapCallback } = require("../lib/utils");
|
||||
utils = require("../lib/utils");
|
||||
|
||||
exports.getTeams = async (req, res, next) => {
|
||||
const {layout} = req
|
||||
const {layout, user} = req
|
||||
const {user_id} = req.params
|
||||
req.session.current_team_id = null
|
||||
promise = teamsnap.loadTeams({'userId':user_id},
|
||||
(err, items) =>{
|
||||
teamsnapCallback(err,items);
|
||||
req.teams = items;
|
||||
})
|
||||
.then(() => tsUtils.teamsnapLog('loadTeams', types=['teams'], null, req))
|
||||
.fail(
|
||||
next
|
||||
)
|
||||
req.promises.push(promise)
|
||||
await Promise.all(req.promises)
|
||||
try {
|
||||
const context = { layout, title: "Teams", teams: req.teams.filter(t=>!t.isRetired) };
|
||||
const context = { layout, title: "Teams", user, teams: req.teams.filter(t=>!t.isRetired) };
|
||||
res.render("team/list", context);
|
||||
} catch (e){
|
||||
next(e);
|
||||
|
||||
@@ -61,40 +61,25 @@ exports.initTeamsnap = (req, res, next) => {
|
||||
});
|
||||
};
|
||||
|
||||
exports.teamsnapLog = (method, types, id, req, message="") => {
|
||||
console.log(
|
||||
exports.teamsnapCallback = (err,result, d) => {
|
||||
if (Array.isArray(result)){
|
||||
types = new Set(result.map(i=>i.type))
|
||||
}
|
||||
else {
|
||||
types = [result?.type]
|
||||
}
|
||||
|
||||
if (d) {
|
||||
console.log(
|
||||
'\x1b[33mTeamSnap:\x1b[0m',
|
||||
`${method} for \x1b[33m\[${types}\]\x1b[0m on ${id}`,
|
||||
`on url ${req.url}`,
|
||||
`"${message}"`
|
||||
`${d.source} using ${d.method ? "teamsnap."+d.method : "?"} \x1b[33m\[${Array.from(types).join(", ")}\]\x1b[0m`
|
||||
)
|
||||
return;
|
||||
}
|
||||
|
||||
exports.tsPromise = (func_name, params) => {
|
||||
return new Promise(function(resolve, reject) {
|
||||
teamsnap.bulkLoad(
|
||||
params,
|
||||
(err, data) => {
|
||||
console.log();
|
||||
if (err !== null) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve(data);
|
||||
}
|
||||
}
|
||||
)
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
exports.teamsnapCallback = (err,items) => {
|
||||
if (err) {
|
||||
}
|
||||
if (err) {
|
||||
console.log(err.message);
|
||||
throw new Error(err)
|
||||
}
|
||||
return items;
|
||||
return result;
|
||||
}
|
||||
|
||||
exports.teamsnapFailure = (err, next) => {
|
||||
@@ -172,12 +157,13 @@ exports.parsePositionLabel = (label) => {
|
||||
const pattern = /(?<pos>[A-Z0-9]+)(?:\s\[(?<flags>.[A-z,]+)\])?/g
|
||||
const {pos, flags} = pattern.exec(label)?.groups || {}
|
||||
const positionLabelWithoutFlags= pos
|
||||
const positionFlags = flags?.split(',').map(f=>f.trim()) || []
|
||||
const positionFlags = new Set(flags?.split(',').map(f=>f.trim()) || [])
|
||||
|
||||
return {positionLabelWithoutFlags, positionFlags}
|
||||
}
|
||||
|
||||
exports.compilePositionLabel = (label, flags) => {
|
||||
if (flags == null || flags == '' || flags.lengh == 0) {
|
||||
if (flags == null || flags == '' || flags.size == 0) {
|
||||
return label
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
exports.loadRecentAndUpcomingEvents = async (req, res, next) => {
|
||||
const {team_id, event_id} = req.params
|
||||
const page_size = req.query.page_size ? Number(req.query.page_size) : 4
|
||||
var subject_date = ""
|
||||
if (event_id) {
|
||||
const event = await teamsnap.loadEvents({id: event_id}).pop()
|
||||
@@ -14,11 +15,11 @@ exports.loadRecentAndUpcomingEvents = async (req, res, next) => {
|
||||
types: ["event", "availabilitySummary"],
|
||||
scopeTo: "event",
|
||||
event__startedAfter: subject_date,
|
||||
event__pageSize: 4
|
||||
event__pageSize: page_size + 1
|
||||
})
|
||||
.then(items => tsUtils.groupTeamsnapItems(items))
|
||||
.then((items)=>{
|
||||
req.upcoming_events=items.events || [];
|
||||
req.upcoming_events=items.events ? items.events.slice(1) : [];
|
||||
const availabilitySummaries=items.availabilitySummaries;
|
||||
req.upcoming_events.forEach((event) => {
|
||||
event.link('availabilitySummary', availabilitySummaries.find(a=>a.eventId==event.id))
|
||||
@@ -32,7 +33,7 @@ exports.loadRecentAndUpcomingEvents = async (req, res, next) => {
|
||||
types: ["event", "availabilitySummary"],
|
||||
scopeTo: "event",
|
||||
event__startedBefore: subject_date,
|
||||
event__pageSize: 4,
|
||||
event__pageSize: page_size,
|
||||
event__sortStartDate: "desc"
|
||||
})
|
||||
.then(items => tsUtils.groupTeamsnapItems(items))
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*= require_tree .
|
||||
*= require_self
|
||||
*/
|
||||
@import url("/font/helvetica-now/stylesheet.css");
|
||||
@font-face {
|
||||
font-family: "MuseoSansRounded100Regular";
|
||||
src: url("https://teamsnap-ui.teamsnap.com/assets/fonts/museo/MuseoSansRounded-100-webfont.eot");
|
||||
@@ -6908,12 +6909,46 @@ input:-webkit-autofill:focus {
|
||||
background-color: #d6d6d6;
|
||||
}
|
||||
|
||||
header.Header {
|
||||
header {
|
||||
background: #323669;
|
||||
padding: 8px 0;
|
||||
box-shadow: 0 4px 0 rgba(0, 0, 25, 0.1);
|
||||
border-bottom: 1px solid #d6d6d6;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
header .Header-banner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
header .filler {
|
||||
flex-grow: 1;
|
||||
}
|
||||
header :has(> .Header-bannerLogo):has(> .Header-bannerTitle) {
|
||||
display: inline-flex;
|
||||
}
|
||||
header .Header-bannerLogo, header .Header-bannerTitle {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
header .Header-bannerLogo img {
|
||||
height: 36px;
|
||||
width: auto;
|
||||
}
|
||||
header .Header-bannerTitle {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.btn--Full {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -6970,44 +7005,10 @@ body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.Header-bannerLogo, .Header-bannerTitle {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.Header-bannerLogo img {
|
||||
height: 36px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.Header-bannerTitle {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.benchcoach-nav {
|
||||
background-color: #323669;
|
||||
margin-bottom: 2em;
|
||||
padding: 0.5em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
a.Panel-row {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.benchcoach-nav h3 {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
font-weight: bolder;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.lineup-slot .Panel-cell {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -7016,6 +7017,8 @@ a.Panel-row {
|
||||
div[id^=event-lineup] {
|
||||
max-width: 576px;
|
||||
counter-reset: lineup-sequence-counter 0;
|
||||
margin-left: 8px;
|
||||
margin-right: 9px;
|
||||
}
|
||||
|
||||
.lineup-slot {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -39,6 +39,27 @@ function colorPositions() {
|
||||
}
|
||||
}
|
||||
|
||||
function initFlagsCheckboxes(){
|
||||
Array.from(document.querySelectorAll("[id^=event-lineup]")).forEach((bcLineup) => {
|
||||
Array.from(
|
||||
bcLineup.querySelectorAll(
|
||||
".starting .lineup-slot, \
|
||||
.position-only .lineup-slot, \
|
||||
.bench .lineup-slot"
|
||||
)
|
||||
).forEach((slot, i) => {
|
||||
const flags = new Set(slot.querySelector("input[name*=flags]")?.value?.split(',')?.map(s=>s.trim())) || new Set()
|
||||
if (flags.has('DHd')) {
|
||||
slot.querySelector('[name=flag-dhd]').checked = true;
|
||||
}
|
||||
|
||||
if (flags.has('DRd') ) {
|
||||
slot.querySelector('[name=flag-drd]').checked = true;
|
||||
}
|
||||
})}
|
||||
)
|
||||
}
|
||||
|
||||
function refreshLineup() {
|
||||
Array.from(document.querySelectorAll("[id^=event-lineup]")).forEach((bcLineup) => {
|
||||
Array.from(
|
||||
@@ -50,67 +71,40 @@ function refreshLineup() {
|
||||
).forEach((slot, i) => {
|
||||
slot.querySelector("input[name*=sequence]").value = i;
|
||||
selected_position = slot.querySelector(".position-select-box option:checked");
|
||||
const flags = new Set(slot.querySelector("input[name*=flags]")?.value?.split(',')?.map(s=>s.trim())) || new Set()
|
||||
|
||||
if (slot.querySelector('[name=flag-dhd]').checked) {
|
||||
flags.add('DHd')
|
||||
} else {
|
||||
flags.delete('DHd')
|
||||
}
|
||||
|
||||
if (slot.querySelector('[name=flag-drd]').checked) {
|
||||
flags.add('DRd')
|
||||
} else {
|
||||
flags.delete('DRd')
|
||||
}
|
||||
|
||||
if (selected_position && selected_position.text != "--") {
|
||||
slot.querySelector("input[name*=label]").value = selected_position.text;
|
||||
} else {
|
||||
slot.querySelector("input[name*=label]").value = null;
|
||||
}
|
||||
if (slot.closest('.position-only')){
|
||||
const flags = new Set(slot.querySelector("input[name*=flags]").value.split(',').map(s=>s.trim()))
|
||||
flags.add('PO');flags.delete('')
|
||||
slot.querySelector("input[name*=flags]").value = Array.from(flags).join(",");
|
||||
}
|
||||
else {
|
||||
const flags = new Set(slot.querySelector("input[name*=flags]").value.split(',').map(s=>s.trim()))
|
||||
flags.delete('PO');flags.delete('')
|
||||
slot.querySelector("input[name*=flags]").value = Array.from(flags).join(",");
|
||||
}
|
||||
if (slot.closest('.bench')){
|
||||
slot.querySelector("input[name*=sequence]").value = '';
|
||||
slot.querySelector("input[name*=label]").value = '';
|
||||
}
|
||||
slot.querySelector("input[name*=flags]").value = Array.from(flags).join(",");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
for (bcLineup of document.querySelectorAll("[id^=event-lineup]")) {
|
||||
options = {
|
||||
animation: 150,
|
||||
handle: ".Panel-cell:has(.drag-handle), .Panel-cell:has(.sequence)",
|
||||
ghostClass: "ghost",
|
||||
group: {
|
||||
name: bcLineup.id,
|
||||
put: [bcLineup.id],
|
||||
pull: [bcLineup.id],
|
||||
},
|
||||
onAdd: function (/**Event*/ evt) {
|
||||
console.log("added to lineup");
|
||||
// Add to Lineup
|
||||
var itemEl = evt.item; // dragged HTMLElement
|
||||
|
||||
refreshLineup();
|
||||
},
|
||||
onUpdate: function (/**Event*/ evt) {
|
||||
console.log("update to lineup");
|
||||
// var itemEl = evt.item; // dragged HTMLElement
|
||||
// refresh_lineup_order(itemEl);
|
||||
refreshLineup();
|
||||
},
|
||||
};
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-starting] .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-positiononly] .slot-set"), options);
|
||||
options["sort"] = false;
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-bench] .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-out] .slot-set"), {...options, group:{...options.group, put:[]}});
|
||||
}
|
||||
|
||||
for (lineup_slot of document.querySelectorAll("[id^=lineup-out] .lineup-slot")) {
|
||||
const cells = lineup_slot.querySelectorAll('.Panel-cell:has(.sequence), .Panel-cell:has(.drag-handle), .Panel-cell:has(.position-select-box) ')
|
||||
Array.from(cells).forEach(cell=>{
|
||||
cell.classList.add('u-hidden')
|
||||
})
|
||||
}
|
||||
|
||||
function refreshFlags(){
|
||||
|
||||
}
|
||||
@@ -260,8 +254,6 @@ function emailModal(el, url) {
|
||||
form = el.closest("form");
|
||||
data = new FormData(form);
|
||||
|
||||
email_modal = document.querySelector("#modal");
|
||||
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
@@ -276,12 +268,25 @@ function emailModal(el, url) {
|
||||
return Promise.reject(response.text());
|
||||
}
|
||||
})
|
||||
.then((lineup_table) => {
|
||||
const email_textarea = document.querySelector('#email-editor')
|
||||
const lineup_table_div = document.querySelector(".FieldGroup .lineup-email")
|
||||
tinymce.activeEditor.setContent("Team,")
|
||||
lineup_table_div.innerHTML = lineup_table
|
||||
email_modal.classList.add("is-open");
|
||||
.then((html) => {
|
||||
const parser = new DOMParser()
|
||||
const email_modal = parser.parseFromString(html, 'text/html')
|
||||
const email_modal_node = email_modal.firstElementChild.querySelector('#modal')
|
||||
email_modal_node.setAttribute('id', `lineup-email-data-${data.get('event_lineup_id')}`)
|
||||
const body = document.querySelector('body')
|
||||
email_modal_node.classList.add('is-open')
|
||||
body.appendChild(email_modal_node)
|
||||
tinymce.init({
|
||||
selector:`#lineup-email-data-${data.get('event_lineup_id')} #email-editor`,
|
||||
content_css:"/css/application.css",
|
||||
plugins: 'image',
|
||||
menubar: false,
|
||||
toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | image',
|
||||
paste_data_images: true,
|
||||
statusbar:false})
|
||||
// tinymce.activeEditor.setContent("Team,")
|
||||
// lineup_table_div.innerHTML = lineup_table
|
||||
// email_modal.classList.add("is-open");
|
||||
// email_modal.querySelector(".Modal-body").innerHTML = html;
|
||||
});
|
||||
}
|
||||
@@ -503,7 +508,7 @@ function toggleChildSlots (element) {
|
||||
console.log(element.closest(".slot-set"))
|
||||
for (lineup_slot of document.querySelectorAll("[id^=lineup-out] .lineup-slot")) {
|
||||
console.log(lineup_slot)
|
||||
const cells = lineup_slot.querySelectorAll('.Panel-cell:has(.sequence), .Panel-cell:has(.drag-handle), .Panel-cell:has(.position-select-box) ')
|
||||
const cells = lineup_slot.querySelectorAll('.Panel-cell:has(.sequence), .Panel-cell:has(.drag-handle), .Panel-cell:has(.position-select-box), div.position-label-flags ')
|
||||
Array.from(cells).forEach(cell=>{
|
||||
cell.classList.toggle('u-hidden')
|
||||
})
|
||||
@@ -562,4 +567,87 @@ async function submitEmail () {
|
||||
'text/html': new Blob([html_content], {type: 'text/html'})
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
function insertLineup(direction, teamId, eventId, element) {
|
||||
const currentUrl = window.location.href;
|
||||
let search_params
|
||||
if (Number(direction) > 0) {
|
||||
search_params = new URLSearchParams({
|
||||
page_size:1,
|
||||
index: 1
|
||||
})
|
||||
} else if (Number(direction) < 0) {
|
||||
search_params = new URLSearchParams({
|
||||
page_size:1,
|
||||
index: -1
|
||||
})
|
||||
} else {throw new Error("Needs to be a negative number or a positive number")}
|
||||
|
||||
fetch(`/${teamId}/event/${eventId}/lineup/adjacent?`+search_params, {
|
||||
method: "GET"
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.text();
|
||||
} else {
|
||||
return Promise.reject(response.text());
|
||||
}
|
||||
})
|
||||
.then((html) =>{
|
||||
const parser = new DOMParser();
|
||||
const new_lineup_doc = parser.parseFromString(html, 'text/html')
|
||||
const new_lineup_doc_node = new_lineup_doc.firstElementChild.querySelector('[id*=event-lineup]')
|
||||
const lineup_container = document.querySelector("#lineup-container")
|
||||
|
||||
|
||||
direction > 0 ? lineup_container.appendChild(new_lineup_doc_node) : lineup_container.insertBefore(new_lineup_doc_node, element.closest('[id*=event-lineup]'))
|
||||
initPage();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function initPage (){
|
||||
colorPositions();
|
||||
initFlagsCheckboxes();
|
||||
refreshLineup();
|
||||
for (bcLineup of document.querySelectorAll("[id^=event-lineup]")) {
|
||||
options = {
|
||||
animation: 150,
|
||||
handle: ".Panel-cell:has(.drag-handle), .Panel-cell:has(.sequence)",
|
||||
ghostClass: "ghost",
|
||||
group: {
|
||||
name: bcLineup.id,
|
||||
put: [bcLineup.id],
|
||||
pull: [bcLineup.id],
|
||||
},
|
||||
onAdd: function (/**Event*/ evt) {
|
||||
console.log("added to lineup");
|
||||
// Add to Lineup
|
||||
var itemEl = evt.item; // dragged HTMLElement
|
||||
|
||||
refreshLineup();
|
||||
},
|
||||
onUpdate: function (/**Event*/ evt) {
|
||||
console.log("update to lineup");
|
||||
// var itemEl = evt.item; // dragged HTMLElement
|
||||
// refresh_lineup_order(itemEl);
|
||||
refreshLineup();
|
||||
},
|
||||
};
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-starting] .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-positiononly] .slot-set"), options);
|
||||
options["sort"] = false;
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-bench] .slot-set"), options);
|
||||
new Sortable.create(bcLineup.querySelector("[id^=lineup-out] .slot-set"), {...options, group:{...options.group, put:[]}});
|
||||
}
|
||||
|
||||
for (lineup_slot of document.querySelectorAll("[id^=lineup-out] .lineup-slot")) {
|
||||
const cells = lineup_slot.querySelectorAll('.Panel-cell:has(.sequence), .Panel-cell:has(.drag-handle), .Panel-cell:has(.position-select-box), div.position-label-flags')
|
||||
Array.from(cells).forEach(cell=>{
|
||||
cell.classList.add('u-hidden')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initPage)
|
||||
23
src/public/manifest.json
Normal file
23
src/public/manifest.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"short_name": "BenchCoach",
|
||||
"name": "BenchCoach: An assitant for TeamSnap",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/media/benchcoach.svg",
|
||||
"type": "image/svg+xml",
|
||||
"sizes": "800x800"
|
||||
},
|
||||
{
|
||||
"src": "/media/apple-touch-icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "120x120 180x180 167x167 152x152 80x80 120x120 58x58 87x87 76x76 114x114"
|
||||
}
|
||||
],
|
||||
"id": "/",
|
||||
"start_url": "/",
|
||||
"background_color": "#323669",
|
||||
"display": "standalone",
|
||||
"scope": "/",
|
||||
"theme_color": "#323669",
|
||||
"description": "An assitant for TeamSnap"
|
||||
}
|
||||
@@ -18,6 +18,7 @@ passport.use(
|
||||
callbackURL: "/auth/teamsnap/callback",
|
||||
passReqToCallback: true,
|
||||
scope: ["read", "write"],
|
||||
proxy: true
|
||||
},
|
||||
async function (req, accessToken, refreshToken, profile, done) {
|
||||
json = JSON.parse(profile._raw);
|
||||
@@ -143,9 +144,16 @@ const ensureLoggedIn = (req, res, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/auth/teamsnap/session_storage', ensureLoggedIn, (req,res)=>{
|
||||
res.status(200).json({"teamsnap.authToken":req.user.accessToken})
|
||||
router.get('/auth/teamsnap/session_storage', (req,res)=>{
|
||||
res.status(200).json({"teamsnap.authToken":req.user?.accessToken})
|
||||
}
|
||||
)
|
||||
|
||||
router.post('/logout', function(req, res, next){
|
||||
req.logout(function(err) {
|
||||
if (err) { return next(err); }
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = {router, ensureLoggedIn};
|
||||
|
||||
@@ -2,16 +2,20 @@ const express = require("express");
|
||||
const eventsController = require("../controllers/event");
|
||||
const router = express.Router();
|
||||
const tsUtils = require("../lib/utils")
|
||||
const {teamsnapCallback} = require("../lib/utils")
|
||||
|
||||
// Middleware
|
||||
const loadEvent = (req,res,next) => {
|
||||
const {team_id, event_id} = req.params;
|
||||
const bulkLoadTypes = ["event", "availabilitySummary"]
|
||||
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||
req.promises.push(teamsnap.bulkLoad({teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:event_id}, null, tsUtils.teamsnapCallback)
|
||||
req.promises.push(
|
||||
teamsnap.bulkLoad(
|
||||
{teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:event_id},
|
||||
null,
|
||||
(err, items) => {teamsnapCallback(err, items, {req, source:"loadEvent", method:'bulkLoad'})}
|
||||
)
|
||||
.then(bulkLoadItems=>{
|
||||
const items = tsUtils.groupTeamsnapItems(bulkLoadItems, bulkLoadTypes);
|
||||
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||
req.availabilitySummary = items.availabilitySummaries.find(e=>e.eventId==event_id);
|
||||
req.event = items.events.find(e=>e.id==event_id);
|
||||
}
|
||||
@@ -19,6 +23,46 @@ const loadEvent = (req,res,next) => {
|
||||
next();
|
||||
}
|
||||
|
||||
// Middleware
|
||||
const loadEvents = async (req,res,next) => {
|
||||
const {team_id, event_id} = req.params
|
||||
req.timeline = {}
|
||||
await Promise.all(req.promises)
|
||||
const {recent_events, upcoming_events} = req
|
||||
const eventIds = [...recent_events.map(e=>e.id), event_id, ...upcoming_events.map(e=>e.id)]
|
||||
// if (!req.event_lineup){
|
||||
bulkLoadTypes = ['event','eventLineup', 'eventLineupEntry']
|
||||
req.promises.push(
|
||||
teamsnap.bulkLoad(
|
||||
{teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:eventIds},
|
||||
null,
|
||||
(err, items) => {teamsnapCallback(err, items, {req, source:"loadEvents", method:'bulkLoad'})}
|
||||
)
|
||||
.then(items => tsUtils.groupTeamsnapItems(items, bulkLoadTypes))
|
||||
.then(items => {
|
||||
req.timeline.events = items.events;
|
||||
req.timeline.event_lineups = items.eventLineups;
|
||||
req.timeline.event_lineup_entries = items.eventLineupEntries;
|
||||
})
|
||||
)
|
||||
|
||||
req.promises.push(
|
||||
teamsnap.loadAvailabilities(
|
||||
{eventId: eventIds},
|
||||
(err, items) => {teamsnapCallback(err, items, {req, source:"loadEvents", method:'loadAvailabilities'})}
|
||||
).then(availabilities => {
|
||||
req.timeline.availabilities = availabilities
|
||||
}
|
||||
)
|
||||
)
|
||||
// }
|
||||
// else {
|
||||
// // const {event_lineup} = req
|
||||
// }
|
||||
const {event_lineup} = req
|
||||
next();
|
||||
}
|
||||
|
||||
router.use("/:team_id([0-9]+)/event/:event_id([0-9]+)", loadEvent)
|
||||
|
||||
// Routes
|
||||
@@ -27,4 +71,4 @@ router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)", eventsController.getEven
|
||||
// router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup", eventsController.getLineup);
|
||||
// router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup_card", eventsController.getLineupCard);
|
||||
|
||||
module.exports = {router, loadEvent}
|
||||
module.exports = {router, loadEvent, loadEvents}
|
||||
@@ -5,6 +5,9 @@ const tsUtils = require('../lib/utils')
|
||||
const multer = require("multer");
|
||||
const upload = multer()
|
||||
const { doubleCsrfProtection } = require('../middlewares/csrf');
|
||||
const {loadRecentAndUpcomingEvents} = require('../middlewares/bulkload')
|
||||
const {loadEvents} = require('./event')
|
||||
const {teamsnapCallback} = require("../lib/utils")
|
||||
|
||||
|
||||
// Middleware
|
||||
@@ -12,16 +15,24 @@ const loadEventLineup = (req,res,next) => {
|
||||
const {team_id, event_id} = req.params
|
||||
if (!req.event_lineup){
|
||||
bulkLoadTypes = ['eventLineup', 'eventLineupEntry']
|
||||
req.promises.push(teamsnap.bulkLoad({teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:event_id}, null, tsUtils.teamsnapCallback)
|
||||
req.promises.push(
|
||||
teamsnap.bulkLoad(
|
||||
{teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:event_id},
|
||||
null,
|
||||
(err, items) => {teamsnapCallback(err, items, {req, source:"loadEventLineup", method:'bulkLoad'})}
|
||||
)
|
||||
.then(items => tsUtils.groupTeamsnapItems(items, bulkLoadTypes))
|
||||
.then(items => {
|
||||
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||
req.event_lineup = items.eventLineups.pop();
|
||||
req.event_lineup_entries = items.eventLineupEntries?.sort((a,b)=>a.sequence-b.sequence) || [];
|
||||
})
|
||||
)
|
||||
tsUtils.teamsnapLog('loadAvailabilites', types=['availabilities'], team_id, req);
|
||||
req.promises.push(teamsnap.loadAvailabilities({eventId: event_id}).then(availabilities => req.availabilities = availabilities))
|
||||
req.promises.push(
|
||||
teamsnap.loadAvailabilities(
|
||||
{eventId: event_id},
|
||||
(err, items) => {teamsnapCallback(err, items, {req, source:"loadEventLineup", method:'loadAvailabilities'})}
|
||||
)
|
||||
.then(availabilities => req.availabilities = availabilities))
|
||||
}
|
||||
else {
|
||||
// const {event_lineup} = req
|
||||
@@ -42,10 +53,10 @@ router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup", async (req,res) =
|
||||
}
|
||||
)
|
||||
|
||||
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/adjacent", doubleCsrfProtection, loadRecentAndUpcomingEvents, loadEvents, eventsLineupController.getAdjacentEventLineup);
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/email", upload.none(), doubleCsrfProtection, eventsLineupController.getEventLineupEmail )
|
||||
router.get ("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)", upload.none(), doubleCsrfProtection, eventsLineupController.getEventLineup);
|
||||
router.post("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)", upload.none(), doubleCsrfProtection, eventsLineupController.postEventLineup);
|
||||
// router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup_card", eventsController.getLineupCard);
|
||||
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/entries", eventsLineupController.getEventLineupEntries)
|
||||
|
||||
|
||||
module.exports = {router, loadEventLineup}
|
||||
@@ -1,48 +1,15 @@
|
||||
const express = require("express");
|
||||
const eventsSheetController = require("../controllers/eventsheet");
|
||||
const {loadEventLineup} = require("./eventlineup");
|
||||
const {loadEvent} = require("./event");
|
||||
const {loadEvent, loadEvents} = require("./event");
|
||||
const {loadRecentAndUpcomingEvents} = require("../middlewares/bulkload")
|
||||
const router = express.Router();
|
||||
const tsUtils = require('../lib/utils')
|
||||
const {teamsnapCallback} = require('../lib/utils')
|
||||
const multer = require("multer");
|
||||
const upload = multer()
|
||||
|
||||
|
||||
// Middleware
|
||||
const loadEvents = async (req,res,next) => {
|
||||
const {team_id, event_id} = req.params
|
||||
req.timeline = {}
|
||||
await Promise.all(req.promises)
|
||||
const {recent_events, upcoming_events} = req
|
||||
const eventIds = [...recent_events.map(e=>e.id), event_id, ...upcoming_events.map(e=>e.id)]
|
||||
// if (!req.event_lineup){
|
||||
bulkLoadTypes = ['event','eventLineup', 'eventLineupEntry']
|
||||
req.promises.push(teamsnap.bulkLoad({teamId: team_id, types: bulkLoadTypes, scopeTo:'event', event__id:eventIds}, null, tsUtils.teamsnapCallback)
|
||||
.then(items => tsUtils.groupTeamsnapItems(items, bulkLoadTypes))
|
||||
.then(items => {
|
||||
tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req);
|
||||
req.timeline.events = items.events;
|
||||
req.timeline.event_lineups = items.eventLineups;
|
||||
req.timeline.event_lineup_entries = items.eventLineupEntries;
|
||||
})
|
||||
)
|
||||
tsUtils.teamsnapLog('loadAvailabilites', types=['availabilities'], team_id, req);
|
||||
|
||||
req.promises.push(
|
||||
teamsnap.loadAvailabilities({eventId: eventIds}).then(availabilities => {
|
||||
req.timeline.availabilities = availabilities
|
||||
}
|
||||
)
|
||||
)
|
||||
// }
|
||||
// else {
|
||||
// // const {event_lineup} = req
|
||||
// }
|
||||
const {event_lineup} = req
|
||||
next();
|
||||
}
|
||||
|
||||
const linksForEventSheet = async (req, res, next) => {
|
||||
await Promise.all(req.promises)
|
||||
const events = [...req.recent_events, req.event, ...req.upcoming_events]
|
||||
@@ -53,7 +20,7 @@ const linksForEventSheet = async (req, res, next) => {
|
||||
}
|
||||
|
||||
router.use("/:team_id([0-9]+)/event/:event_id([0-9]+)/sheet", loadEventLineup)
|
||||
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/sheet", loadEventLineup, loadRecentAndUpcomingEvents, loadEvents, eventsSheetController.getEventSheet)
|
||||
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/lineup/:event_lineup_id([0-9]+)/sheet", loadRecentAndUpcomingEvents, loadEvents, eventsSheetController.getEventSheet)
|
||||
|
||||
// Routes
|
||||
router.get("/:team_id([0-9]+)/event/:event_id([0-9]+)/sheet", async (req,res) => {
|
||||
|
||||
@@ -15,12 +15,12 @@ const membersController = require("../controllers/member");
|
||||
router.use("/", ensureLoggedIn, (req,res,next) => {req.layout="layouts/main";req.promises=[];next();})
|
||||
|
||||
router.get("/", (req,res,next) => {
|
||||
if (!req.session.current_team){
|
||||
if (!req.session.current_team_id){
|
||||
res.redirect(`/user/${req.session.passport.user.id}/teams`)
|
||||
next();
|
||||
}
|
||||
else {
|
||||
res.redirect(`/${req.session.current_team}/home`)
|
||||
res.redirect(`/${req.session.current_team_id}/home`)
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
6
src/routes/meta.js
Normal file
6
src/routes/meta.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/favicon.ico", (req, res, next) => res.status(204).end())
|
||||
|
||||
module.exports = {router}
|
||||
@@ -5,21 +5,25 @@ var router = express.Router();
|
||||
const multer = require("multer");
|
||||
const upload = multer()
|
||||
const { doubleCsrfProtection } = require('../middlewares/csrf');
|
||||
const {teamsnapCallback} = require('../lib/utils')
|
||||
|
||||
// Middleware
|
||||
const loadOpponent = (req,res,next) => {
|
||||
const {opponent_id} = req.params;
|
||||
const {team} = req
|
||||
req.promises.push(
|
||||
teamsnap.loadOpponents(team.id, (err, opponents)=>{
|
||||
if (err) console.log("error in route/opponent.js", err);
|
||||
}).then(opponents => {req.opponent=opponents.find(o=>o.id==opponent_id);})
|
||||
teamsnap.loadOpponents(
|
||||
team.id,
|
||||
(err, opponents) => {teamsnapCallback(err, opponents, {req, source:"loadOpponent", method:'loadOpponent'})}
|
||||
)
|
||||
.then(opponents => {req.opponent=opponents.find(o=>o.id==opponent_id);})
|
||||
)
|
||||
|
||||
req.promises.push(
|
||||
teamsnap.loadTeamMedia(team.id, (err, team_media)=>{
|
||||
if (err) console.log("error in route/opponent.js", err);
|
||||
})
|
||||
teamsnap.loadTeamMedia(
|
||||
team.id,
|
||||
(err, opponents) => {teamsnapCallback(err, opponents, {req, source:"loadOpponent", method:'teamMedia'})}
|
||||
)
|
||||
.then(team_media => {
|
||||
req.opponent_logo = team_media.find(tm=>tm.description==`opponent-logo-${opponent_id}.png`)
|
||||
}
|
||||
|
||||
@@ -4,28 +4,31 @@ const {loadRecentAndUpcomingEvents} = require("../middlewares/bulkload")
|
||||
const { load } = require("dotenv");
|
||||
const router = express.Router();
|
||||
const tsUtils = require('../lib/utils')
|
||||
const {teamsnapCallback} = require('../lib/utils')
|
||||
|
||||
// Middleware
|
||||
const loadTeam = async (req,res,next) => {
|
||||
const {team_id} = req.params;
|
||||
req.team = await teamsnap.loadTeam(team_id)
|
||||
tsUtils.teamsnapLog('loadTeam', types=["team"], team_id, req);
|
||||
req.team = await teamsnap.loadTeam(
|
||||
team_id,
|
||||
(err, result) => {teamsnapCallback(err, result, {req, source: 'loadTeam', method: 'loadTeam'})}
|
||||
)
|
||||
const bulkLoadTypes = ['teamMediaGroup', 'teamPreferences', 'member'];
|
||||
const items = tsUtils.groupTeamsnapItems(teamsnap.getAllItems(), bulkLoadTypes)
|
||||
if (req.session.current_team_id == null || req.session.current_team_id != team_id || bulkLoadTypes.filter(t=> !items[t] || items[t].length==0).length > 0){
|
||||
req.promises.push(teamsnap.bulkLoad(
|
||||
team_id,
|
||||
bulkLoadTypes,
|
||||
tsUtils.teamsnapCallback
|
||||
).then(bulkLoadItems=>{
|
||||
(err,items) => {teamsnapCallback(err, items, {req, source: 'loadTeam', method: 'bulkLoad'})}
|
||||
)
|
||||
.then(bulkLoadItems=>{
|
||||
const items = tsUtils.groupTeamsnapItems(bulkLoadItems, bulkLoadTypes)
|
||||
req.members = items.members;
|
||||
req.team_media_group = items.teamMediaGroups?.pop();
|
||||
req.team_preferences = items.teamsPreferences.pop();
|
||||
req.session.current_team_id = req.team.id
|
||||
req.session.current_team_id = req.team.id;
|
||||
}
|
||||
)
|
||||
.then(() => tsUtils.teamsnapLog('bulkLoad', types=bulkLoadTypes, team_id, req))
|
||||
)
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
@import "../../node_modules/@teamsnap/teamsnap-ui/src/css/teamsnap-ui.scss";
|
||||
@import url('/font/helvetica-now/stylesheet.css');
|
||||
|
||||
|
||||
$color-success: #b7e1cd;
|
||||
@@ -62,12 +63,54 @@ $monospace-font: "Inconsolata", monospace;
|
||||
}
|
||||
}
|
||||
|
||||
header.Header {
|
||||
header {
|
||||
background: #323669;
|
||||
padding: 8px 0;
|
||||
// margin: 0 0 16px 0;
|
||||
box-shadow: 0 4px 0 rgba(0, 0, 25, 0.1);
|
||||
border-bottom: 1px solid #d6d6d6;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
|
||||
.Header-banner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.filler {
|
||||
flex-grow:1,
|
||||
}
|
||||
|
||||
:has(>.Header-bannerLogo):has(>.Header-bannerTitle) {
|
||||
display: inline-flex
|
||||
}
|
||||
|
||||
.Header-bannerLogo, .Header-bannerTitle {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.Header-bannerLogo img {
|
||||
height: 36px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.Header-bannerTitle {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.btn--Full {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -137,44 +180,10 @@ body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.Header-bannerLogo, .Header-bannerTitle {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.Header-bannerLogo img {
|
||||
height: 36px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.Header-bannerTitle {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.benchcoach-nav {
|
||||
background-color: #323669;
|
||||
margin-bottom: 2em;
|
||||
padding: 0.5em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
a.Panel-row {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.benchcoach-nav h3 {
|
||||
font-family: "Helvetica", sans-serif;
|
||||
font-weight: bolder;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.lineup-slot .Panel-cell {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -183,6 +192,8 @@ a.Panel-row {
|
||||
div[id^="event-lineup"] {
|
||||
max-width: 576px;
|
||||
counter-reset: lineup-sequence-counter 0;
|
||||
margin-left: 8px;
|
||||
margin-right: 9px;
|
||||
}
|
||||
|
||||
.lineup-slot {
|
||||
|
||||
@@ -1,129 +1,124 @@
|
||||
{{>emailmodal}}
|
||||
<div id="event-lineup-{{event.id}}" data-event-lineup-id="{{event_lineup.id}}" data-event-id="{{event.id}}">
|
||||
<form onsubmit="onSubmit(this,event)" action="#">
|
||||
<input type="hidden" name="event_lineup_id" value="{{event_lineup.id}}">
|
||||
{{!-- <input type="hidden" name="_csrf" value="{{csrfToken}}"> --}}
|
||||
<input type="hidden" name="csrfToken" value="{{csrfToken}}">
|
||||
<div class="Panel Panel--full">
|
||||
<div class="Panel-header u-padEndsSm">
|
||||
<h3 style="flex: 1 1 0%;">{{event.formattedTitle}}</h3>
|
||||
<div class="ButtonGroup">
|
||||
<button class="Button Button--orange" type="submit" formmethod="post">
|
||||
<div>
|
||||
<span id="teamsnap-icon">{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}</span>
|
||||
<span id="waiting-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/loader.svg" "Icon--loader"}}}</span>
|
||||
<span id="success-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/check.svg"}}}</span>
|
||||
<span id="failure-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg"}}}</span>
|
||||
Save
|
||||
<div class="u-flex" id="lineup-container">
|
||||
<div class="u-spaceSidessAuto" id="event-lineup-{{event.id}}" data-event-lineup-id="{{event_lineup.id}}" data-event-id="{{event.id}}">
|
||||
<form onsubmit="onSubmit(this,event)" action="#">
|
||||
<input type="hidden" name="event_lineup_id" value="{{event_lineup.id}}">
|
||||
{{!-- <input type="hidden" name="_csrf" value="{{csrfToken}}"> --}}
|
||||
<input type="hidden" name="csrfToken" value="{{csrfToken}}">
|
||||
<div class="Panel Panel--full">
|
||||
<div class="Panel-header u-padEndsSm">
|
||||
<h3 style="flex: 1 1 0%;">{{event.formattedTitle}}</h3>
|
||||
<div class="ButtonGroup">
|
||||
<button class="Button Button--orange" type="submit" formmethod="post">
|
||||
<div>
|
||||
<span id="teamsnap-icon">{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}</span>
|
||||
<span id="waiting-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/loader.svg" "Icon--loader"}}}</span>
|
||||
<span id="success-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/check.svg"}}}</span>
|
||||
<span id="failure-icon" class="u-hidden">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg"}}}</span>
|
||||
Save
|
||||
</div>
|
||||
</button>
|
||||
<div class="Button Button--orange .u-padSidesXs Popup" onclick="togglePopup(this)">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/caret-down.svg"}}}
|
||||
<div class="Popup-container Popup-container--down Popup-container--right" style="width: 200px">
|
||||
<div class="Popup-content u-textDecorationNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="emailModal(this, '{{event_lineup.id}}/email')">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/envelope.svg"}}}
|
||||
<span>Generate Email</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="../sheet">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/book.svg"}}}
|
||||
<span>Lineup Card</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="insertLineup(1, {{team.id}}, {{event.id}}, this)">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/caret-right.svg"}}}
|
||||
<span>Insert next lineup</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="insertLineup(-1, {{team.id}}, {{event.id}}, this)">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/caret-left.svg"}}}
|
||||
<span>Insert previous lineup</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="Button Button--orange .u-padSidesXs Popup" onclick="togglePopup(this)">
|
||||
{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/caret-down.svg"}}}
|
||||
<div class="Popup-container Popup-container--down Popup-container--right" style="width: 200px">
|
||||
<div class="Popup-content u-textDecorationNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="javascript:void(0)" onclick="emailModal(this, '{{event_lineup.id}}/email')">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/envelope.svg"}}}
|
||||
<span>Generate Email</span>
|
||||
</a>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a class="u-padEndsSm u-padSidesMd u-textDecorationNone" href="../sheet">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/book.svg"}}}
|
||||
<span>Lineup Card</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" Panel-body u-padEndsSm">
|
||||
<div class=" u-padSidesSm">
|
||||
<div class="date">{{dateFormat event.startDate "ddd, MMM D h:mm A" }}</div>
|
||||
<div class="location">{{event.locationName}}</div>
|
||||
</div>
|
||||
<div class=" availability-bar fullwidth">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" Panel-body u-padEndsSm">
|
||||
<div class=" u-padSidesSm">
|
||||
<div class="date">{{dateFormat event.startDate "ddd, MMM D h:mm A" }}</div>
|
||||
<div class="location">{{event.locationName}}</div>
|
||||
<div id="lineup-starting-{{event.id}}" class="Panel u-maxWidthSm starting Panel--fullWidthMobile Panel--full">
|
||||
<div class="Panel-body">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
<i>{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}</i>
|
||||
<span>Starting Lineup</span>
|
||||
</div>
|
||||
<div class=" availability-bar fullwidth">
|
||||
|
||||
<div class=" Panel-row Grid Grid--fit u-textBold u-textCenter u-padXs">
|
||||
{{#each (positions)}}
|
||||
<div class="Grid-cell position-status">{{this}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInStartingLineup this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-starting-{{event.id}}" class="Panel u-maxWidthSm starting Panel--fullWidthMobile Panel--full">
|
||||
<div class="Panel-body">
|
||||
<div id="lineup-positiononly-{{event.id}}" class="Panel u-maxWidthSm position-only Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
<i>{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}</i>
|
||||
<span>Starting Lineup</span>
|
||||
</div>
|
||||
<div class=" Panel-row Grid Grid--fit u-textBold u-textCenter u-padXs">
|
||||
{{#each (positions)}}
|
||||
<div class="Grid-cell position-status">{{this}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInStartingLineup this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-positiononly-{{event.id}}" class="Panel u-maxWidthSm position-only Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}
|
||||
<span>Position Only</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInPositionOnly this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-bench-{{event.id}}" class="Panel u-maxWidthSm bench Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-minus.svg"}}}
|
||||
<span>Bench</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInBench this)}}
|
||||
{{> slot member=this event=../event}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-out-{{event.id}}" class="Panel u-maxWidthSm out Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
<span style="flex: 1 1 0%;">{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-x.svg"}}}Out</span>
|
||||
<div class="Toggle">
|
||||
<input class="Toggle-input" type="checkbox" id="enable-slots" onclick="toggleChildSlots(this);">
|
||||
<label class="Toggle-label" for="availability-tab"></label>
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-check.svg"}}}
|
||||
<span>Position Only</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInPositionOnly this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInOut this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div id="lineup-bench-{{event.id}}" class="Panel u-maxWidthSm bench Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs">
|
||||
{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-minus.svg"}}}
|
||||
<span>Bench</span>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInBench this)}}
|
||||
{{> slot member=this event=../event}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="lineup-out-{{event.id}}" class="Panel u-maxWidthSm out Panel--full">
|
||||
<div class="Panel-row Panel-title u-padXs u-flex">
|
||||
<div><span style="flex: 1 1 0%;">{{{embeddedSvgFromPath "/bootstrap-icons/clipboard-x.svg"}}}Out</span></div>
|
||||
<div class="u-flexGrow1"></div>
|
||||
<div class="Toggle">
|
||||
<input class="Toggle-input" type="checkbox" id="enable-slots" onclick="toggleChildSlots(this);">
|
||||
<label class="Toggle-label" for="enable-slots"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slot-set">
|
||||
{{#each members}}
|
||||
{{#if (isInOut this)}}
|
||||
{{> slot member=this}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
|
||||
<script src="/js/eventlineup.js"></script>
|
||||
<script src="/js/tinymce.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
colorPositions();
|
||||
refreshLineup();
|
||||
tinymce.init({
|
||||
selector:"#email-editor",
|
||||
content_css:"/css/application.css",
|
||||
plugins: 'image',
|
||||
menubar: false,
|
||||
toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | image',
|
||||
paste_data_images: true,
|
||||
statusbar:false})
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div id="modal" class="Modal Modal--clickableBg">
|
||||
<div class="Modal-bgDismiss" onclick="javascript:this.closest('.Modal').classList.toggle('is-open')"></div>
|
||||
<div class="Modal-bgDismiss" onclick="javascript:this.closest('.Modal').remove();tinymce.remove();"></div>
|
||||
<div class="Modal-content">
|
||||
<div onclick="javascript:this.closest('.Modal').classList.toggle('is-open')">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg" "Modal-iconDismiss"}}}</div>
|
||||
<div onclick="javascript:this.closest('.Modal').remove();tinymce.remove();">{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/dismiss.svg" "Modal-iconDismiss"}}}</div>
|
||||
<div class="Modal-header">
|
||||
<div class="Modal-title">Email</div>
|
||||
</div>
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
<div class="FieldGroup">
|
||||
<label class="FieldGroup-label">Lineup</label>
|
||||
<div class="lineup-email"></div>
|
||||
<div class="lineup-email">{{>email_table}}</div>
|
||||
</div>
|
||||
<div class="FieldGroup">
|
||||
<button class="Button" role="button" onclick="submitEmail();">Submit</button>
|
||||
@@ -25,4 +25,5 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
@@ -11,10 +11,10 @@
|
||||
{{#if (isInStartingLineup this)}}
|
||||
<tr>
|
||||
<td class="sequence-cell">
|
||||
{{plus1 this.benchcoach.eventLineupEntry.sequence}}
|
||||
{{plus1 this.benchcoach.eventLineupEntry.sequence}}{{#if (hasPositionFlags this.benchcoach.eventLineupEntry.label)}} {{positionFlags this.benchcoach.eventLineupEntry.label}}{{/if}}
|
||||
</td>
|
||||
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||
<td class="position-label-cell">{{positionLabelWithoutFlags this.benchcoach.eventLineupEntry.label}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
@@ -26,7 +26,7 @@
|
||||
<tr>
|
||||
<td class="sequence-cell"></td>
|
||||
<td class="name-cell">{{this.lastName}}, {{this.firstName}} – #{{this.jerseyNumber}}</td>
|
||||
<td class="position-label-cell">{{this.benchcoach.eventLineupEntry.label}}</td>
|
||||
<td class="position-label-cell">{{positionLabelWithoutPOFlag this.benchcoach.eventLineupEntry.label}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
class="Panel-cell Panel-cell--header">
|
||||
<div class="sequence u-textNoWrap u-fontSizeLg"></div>
|
||||
</div>
|
||||
<div class="Panel-cell u-padXs u-sizeFill">
|
||||
<div class="Panel-cell u-padXs u-sizeFill u-flex">
|
||||
<div
|
||||
class="d-flex availability-status-code-{{
|
||||
class="availability-status-code-{{
|
||||
member.benchcoach.availability?.statusCode
|
||||
}}"
|
||||
>
|
||||
<div class="u-flexInline u-fontSizeLg u-textNoWrap">
|
||||
{{#if member.benchcoach.availability}}{{{avail_status_code_icon member.benchcoach.availability.statusCode}}}{{/if}}
|
||||
{{#if member.benchcoach.availability}}{{{avail_status_code_icon member.benchcoach.availability.statusCode}}}{{/if}}
|
||||
</div>
|
||||
<div class="u-fontSizeLg u-textNoWrap">
|
||||
<span class="lastname">
|
||||
{{member.lastName}}
|
||||
</span>
|
||||
@@ -32,6 +33,16 @@
|
||||
<span class="jerseynumber u-hidden u-sm-inline u-fontSizeSm">
|
||||
#{{member.jerseyNumber}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="u-flexGrow1"></div>
|
||||
<div class="position-label-flags">
|
||||
<div class="Checkbox Checkbox--inline">
|
||||
<input class="Checkbox-input" type="checkbox" name="flag-drd" id="flag-drd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}" onclick="refreshLineup()">
|
||||
<label class="Checkbox-label" for="flag-drd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}">DR<small>d</small></label>
|
||||
</div>
|
||||
<div class="Checkbox Checkbox--inline">
|
||||
<input class="Checkbox-input" type="checkbox" name="flag-dhd" id="flag-dhd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}" onclick="refreshLineup()">
|
||||
<label class="Checkbox-label" for="flag-dhd-{{member.id}}-{{member.benchcoach.eventLineupEntry.id}}">DH<small>d</small></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
<th>{{event.opponentName}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="height:5em;"></td>
|
||||
<td style="height:5em;width: 50%;"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="stylesheet" href="/css/application.css">
|
||||
{{#if style}}<link rel="stylesheet" href="/css/{{style}}">{{/if}}
|
||||
<title>{{#if title}}{{title}}{{else}}BenchCoach{{/if}}</title>
|
||||
@@ -19,15 +20,16 @@
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="bg-light">
|
||||
<header class="Header">
|
||||
<body>
|
||||
<header>
|
||||
{{> navbar }}
|
||||
{{{_sections.header}}}
|
||||
</header>
|
||||
<div class="u-padSidesMd u-xs-padSidesLg">
|
||||
<div class="u-max1200 u-flexExpandSides u-xs-size5of6 u-sm-size2of3 u-md-sizeFull u-padBottomMd u-xs-padEndsLg u-sm-padEndsXl">
|
||||
<div class="u-max1200 u-flexExpandSides u-xs-size5of6 u-sm-size2of3 u-md-sizeFull u-padEndsLg u-sm-padEndsXl">
|
||||
{{{ body }}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
{{{script_tags scripts}}}
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
html
|
||||
head
|
||||
block meta
|
||||
meta(charset='utf-8')
|
||||
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||
title BenchCoach - #{title}
|
||||
block styles
|
||||
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')
|
||||
link(rel='stylesheet' href='/css/project.css')
|
||||
block pre-scripts
|
||||
script(type='text/javascript', src='/js/Sortable.js')
|
||||
|
||||
body.bg-light
|
||||
block navbar
|
||||
include navbar.pug
|
||||
|
||||
.u-spaceSm.u-md-spaceLg
|
||||
block content
|
||||
.Panel
|
||||
.Panel-header
|
||||
h2.Panel-title Panel
|
||||
.Panel-body
|
||||
.Panel-row Panel Row
|
||||
.Panel-row Roster
|
||||
.Panel-row Opponents
|
||||
|
||||
block footer
|
||||
.u-padMd.u-borderTop
|
||||
.u-max1200.u-flexExpandSides.Grid
|
||||
.Grid-cell.u-md-size1of2
|
||||
p.u-textCenter.u-md-textLeft
|
||||
| Copyright © 2023 BenchCoach. BenchCoach is not affiliated with TeamSnap, inc.
|
||||
.Grid-cell.u-md-size1of2
|
||||
p.u-textCenter.u-md-textRight
|
||||
|
||||
block scripts
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
<div class="Grid Grid--fit Grid--withGutter u-max1200 u-flexExpandSides u-xs-size5of6 u-sm-size2of3 u-md-sizeFull u-padBottomMd u-xs-padEndsLg u-sm-padEndsXl">
|
||||
<div class="Grid-cell u-size5of12">
|
||||
<div class="Panel u-padLg u-spaceSidesAuto">
|
||||
<h1 class="u-spaceSidesAuto u-spaceBottomLg">Sign in</h1>
|
||||
<div>
|
||||
<a class="Button Button--large Button--orange u-spaceSidesAuto" href="/login/federated/teamsnap">
|
||||
{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}
|
||||
<span>TeamSnap</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Grid-cell u-size7of12 u-textCenter">
|
||||
<h1>
|
||||
<img src="/media/benchcoach.svg" style="width: 2.5em;">
|
||||
</h1>
|
||||
<h1>
|
||||
<strong>
|
||||
Welcome to
|
||||
<span class="text-nowrap">BenchCoach</span>
|
||||
</strong>
|
||||
</h1>
|
||||
<div class="lead fst-italic fw-light">
|
||||
An assistant coach for TeamSnap
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Panel u-maxWidthXs u-padLg u-spaceSidesAuto">
|
||||
<h1 class="u-textCenter">Sign in</h1>
|
||||
<p class="u-spaceEndsMd">Sign into BenchCoach using your TeamSnap account</p>
|
||||
<a class="Button Button--large Button--orange u-spaceSidesAuto btn--Full" href="/login/federated/teamsnap">
|
||||
{{{embeddedSvgFromPath "/media/teamsnap_star.svg"}}}
|
||||
<span>TeamSnap</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
<div class="Header-container Grid u-flexAlignItemsCenter">
|
||||
<div class="Grid-cell u-sizeFill">
|
||||
<div class="Header-banner Grid u-flexAlignItemsCenter">
|
||||
<a href="/" class="Grid-cell u-sizeFit u-flexInline u-flexAlignItemsCenter u-textDecorationNone">
|
||||
<div class="Header-bannerLogo">
|
||||
<img class="logo" src="/media/benchcoach.svg" alt="BenchCoach Logo">
|
||||
</div>
|
||||
</a>
|
||||
<div class="Grid-cell u-flexInline u-flexJustifyEnd u-sizeFill u-padSidesSm">
|
||||
{{#if user}}
|
||||
<div class="Popup">
|
||||
<div class="Button Button--small Popup-toggle" onclick="this.closest('.Popup').querySelector('.Popup-container').classList.toggle('is-open')">
|
||||
Account
|
||||
</div>
|
||||
<div class="Popup-container Popup-container--down Popup-container--right u-sizeFit">
|
||||
<div class="Popup-content u-padXs u-sizeFit u-fontSizeSm">
|
||||
<h6 class="h6 title u-textNoWrap u-fontSizeSm u-textSemiBold">{{user.first_name}} {{user.last_name}}</h6>
|
||||
<div class="u-textNoWrap u-fontSizeSm">{{user.email}}</div>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<a href="/logout">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/if}}
|
||||
<div class="Header-banner">
|
||||
<a href="/" class="">
|
||||
<div class="Header-bannerLogo">
|
||||
<img class="logo" src="/media/benchcoach.svg" alt="BenchCoach Logo">
|
||||
</div>
|
||||
<div class="Header-bannerTitle">
|
||||
BenchCoach
|
||||
</div>
|
||||
</a>
|
||||
{{#if user}}
|
||||
<div class="filler"></div>
|
||||
<div class="u-padSidesSm u-spaceAuto">
|
||||
<div class="Popup">
|
||||
<div class="Button Button--small Popup-toggle" onclick="this.closest('.Popup').querySelector('.Popup-container').classList.toggle('is-open')">
|
||||
Account
|
||||
</div>
|
||||
<div class="Popup-container Popup-container--down Popup-container--right u-sizeFit">
|
||||
<div class="Popup-content u-padXs u-sizeFit u-fontSizeSm">
|
||||
<h6 class="h6 title u-textNoWrap u-fontSizeSm u-textSemiBold">{{user.first_name}} {{user.last_name}}</h6>
|
||||
<div class="u-textNoWrap u-fontSizeSm">{{user.email}}</div>
|
||||
<hr class="Divider u-spaceEndsNone">
|
||||
<div class="u-padBottomSm u-padTopSm">
|
||||
<a href="/user/{{user.id}}/teams" class="u-spaceBottomSm Button Button--small">
|
||||
<span>{{{embeddedSvgFromPath "/teamsnap-ui/assets/icons/team.svg"}}}</span>
|
||||
Teams
|
||||
</a>
|
||||
<form method="post" action="/logout">
|
||||
<button type="submit" name="logout" class="u-spaceBottomSm Button Button--small">
|
||||
Logout
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
header.Header
|
||||
.Header-container.Grid.u-flexAlignItemsCenter
|
||||
.Grid-cell.u-sizeFill
|
||||
.Header-banner.Grid.u-flexAlignItemsCenter
|
||||
a.Grid-cell.u-sizeFit.u-flexInline.u-flexAlignItemsCenter.u-textDecorationNone href=url_for(root_path)
|
||||
div.Header-bannerLogo
|
||||
img(class='logo' src='/media/benchcoach.svg' alt='BenchCoach Logo')
|
||||
span
|
||||
- if defined?(team)
|
||||
h1.Header-bannerTitle #{team.name}
|
||||
- else
|
||||
h1.Header-bannerTitle BenchCoach
|
||||
.Grid-cell.u-flexInline.u-flexJustifyEnd.u-sizeFill.u-padSidesSm
|
||||
- if @user
|
||||
.Popup
|
||||
.Button.Button--small.Popup-toggle onclick="this.closest('.Popup').querySelector('.Popup-container').classList.toggle('is-open')"
|
||||
| Account
|
||||
.Popup-container.Popup-container--down.Popup-container--right.u-sizeFit
|
||||
.Popup-content.u-padXs.u-sizeFit.u-fontSizeSm
|
||||
h6.title.u-textNoWrap.u-fontSizeSm.u-textSemiBold="#{@user['first_name']} #{@user['last_name']}"
|
||||
.u-textNoWrap.u-fontSizeSm="#{@user['email']}"
|
||||
hr.Divider.u-spaceEndsNone
|
||||
a href=url_for(logout_path) Logout
|
||||
|
||||
.Grid-cell.u-sizeFit
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class=" Grid-cell u-sizeFit">
|
||||
<div class=" Grid-cell u-sizeFit u-padTopSm u-padBottomSm">
|
||||
<div class=" u-flex u-flexAlignItemsCenter u-flexJustifyEnd">
|
||||
<div class="ButtonGroup">
|
||||
<a class="Button" href="schedule">
|
||||
|
||||
Reference in New Issue
Block a user