add insert lineup before and after

This commit is contained in:
2024-03-15 14:19:14 -05:00
parent dc17ca76ba
commit f2371c6b5a
12 changed files with 305 additions and 221 deletions

View File

@@ -96,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) => {
@@ -141,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)=>{

View File

@@ -3,7 +3,7 @@ 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},
@@ -17,7 +17,7 @@ exports.getTeams = async (req, res, 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);

View File

@@ -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))

View File

@@ -7017,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

View File

@@ -49,7 +49,6 @@ function initFlagsCheckboxes(){
)
).forEach((slot, i) => {
const flags = new Set(slot.querySelector("input[name*=flags]")?.value?.split(',')?.map(s=>s.trim())) || new Set()
console.log(slot, flags)
if (flags.has('DHd')) {
slot.querySelector('[name=flag-dhd]').checked = true;
}
@@ -106,44 +105,6 @@ function 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')
})
}
function refreshFlags(){
}
@@ -293,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,
@@ -309,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;
});
}
@@ -596,3 +568,86 @@ async function submitEmail () {
})
])
}
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)

View File

@@ -23,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
@@ -31,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}

View File

@@ -5,6 +5,8 @@ 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")
@@ -51,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}

View File

@@ -1,7 +1,7 @@
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')
@@ -10,46 +10,6 @@ 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,
(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();
}
const linksForEventSheet = async (req, res, next) => {
await Promise.all(req.promises)
const events = [...req.recent_events, req.event, ...req.upcoming_events]

View File

@@ -192,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 {

View File

@@ -1,6 +1,6 @@
{{>emailmodal}}
<div class="u-spaceAuto" id="event-lineup-{{event.id}}" data-event-lineup-id="{{event_lineup.id}}" data-event-id="{{event.id}}">
<form onsubmit="onSubmit(this,event)" action="#">
<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}}">
@@ -30,6 +30,16 @@
{{{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>
</div>
@@ -108,24 +118,7 @@
{{/each}}
</div>
</div>
</form>
</form>
</div>
</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();
initFlagsCheckboxes();
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>

View File

@@ -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>
@@ -26,3 +26,4 @@
</div>
</div>
</div>
</script>