do not merge to main, very buggy rough implementation of sorting between teams and sections. I have rewritten many areas.
309 lines
11 KiB
JavaScript
309 lines
11 KiB
JavaScript
jQuery.expr[':'].icontains = function(a, i, m) {
|
|
return jQuery(a).text().toUpperCase()
|
|
.indexOf(m[3].toUpperCase()) >= 0;
|
|
};
|
|
|
|
$(document).ready(() => {
|
|
|
|
teamsLoading(true); // show the loading icon
|
|
|
|
// Search functionality
|
|
var searchTimeout = null;
|
|
|
|
// Bind searching to when a key is lifted on the searchbar
|
|
$("#search").keyup(() => {
|
|
clearTimeout(searchTimeout);
|
|
teamsLoading(true);
|
|
|
|
searchTimeout = setTimeout(() => {
|
|
fetchAndLoadTeams(...getFilters());
|
|
}, 500)
|
|
});
|
|
|
|
// Bind searching to the search button
|
|
$("#searchButton").on("click", () => {
|
|
fetchAndLoadTeams(...getFilters());
|
|
});
|
|
|
|
// Bind searching to the sort buttons
|
|
$("#sortForm input").on("click", function() {
|
|
const name = $(this).attr("name");
|
|
localStorage.setItem(name, $(`input[name='${name}']:checked`, "#sortForm").val());
|
|
|
|
fetchAndLoadTeams(...getFilters());
|
|
});
|
|
|
|
// Load the last saved sort settings TODO: use local storage so it only saves on one reload
|
|
const sortTeamsValue = localStorage.getItem("sortGroups");
|
|
if (sortTeamsValue !== null) {
|
|
$("#sortForm input[name='sortGroups']").val([sortTeamsValue]);
|
|
}
|
|
|
|
const sortMembersValue = localStorage.getItem("sortMembers");
|
|
if (sortMembersValue !== null) {
|
|
$("#sortForm input[name='sortMembers']").val([sortMembersValue]);
|
|
}
|
|
|
|
// Customize form validation for the edit members form
|
|
$("#editMemberForm").validate({ errorClass: "text-danger mb-2" });
|
|
|
|
// Prevent dropdowns from closing when clicking inside
|
|
$('.dropdown-menu').on('hide.bs.dropdown', function (e) {
|
|
var target = $(e.clickEvent.target);
|
|
if(target.hasClass("keepopen") || target.parents(".keepopen").length){
|
|
return false; // returning false should stop the dropdown from hiding.
|
|
}else{
|
|
return true;
|
|
}
|
|
});
|
|
|
|
// load the teams with default filters
|
|
fetchAndLoadTeams(...getFilters());
|
|
|
|
});
|
|
|
|
/**
|
|
* Returns an array of search filters in this order [search, sortTeams, sortMembers]
|
|
*
|
|
* @returns {Array} The array of filters, each filter is a string.
|
|
*/
|
|
function getFilters() {
|
|
return [
|
|
$("#search").val(),
|
|
$("input[name='sortGroups']:checked", "#sortForm").val(),
|
|
$("input[name='sortMembers']:checked", "#sortForm").val()
|
|
]
|
|
}
|
|
|
|
function loadTeams(groups, highlightText="", groupType) {
|
|
$("#teamsContainer").html(""); // Clear the previous listed teams
|
|
|
|
if (groups.length < 1) {
|
|
$("#teamsNotFound").show();
|
|
}
|
|
|
|
groupType = groupType.toUpperCase();
|
|
|
|
// Iterate over and add each team
|
|
groups.forEach((group) => {
|
|
$("#teamsContainer").append(
|
|
`<div class='col-12 col-md-6 col-xl-4 mb-4'>
|
|
<div
|
|
class='team px-4 py-3 bg-body-tertiary bg-gradient rounded h-100 fluid-hover-zoom shadow-sm md-shadow-on-hover'
|
|
data-number='${group.name}'>
|
|
<h3>
|
|
<span class='fs-4'>
|
|
<span class='fs-6'>${groupType}</span>
|
|
${group.name}
|
|
</span>
|
|
<button class='btn btn-sm btn-light float-end border-0 me-1'>
|
|
<i class='bi bi-gear fs-6'></i>
|
|
</button>
|
|
</h3>
|
|
<ul class='list-unstyled team-members mt-3'>
|
|
</ul>
|
|
</div>
|
|
</div>`
|
|
);
|
|
|
|
// While we have the team, iterate over and add it's members
|
|
group.members.forEach((member) => {
|
|
const fullname = member.first + " " + member.last;
|
|
const oppositeGroupType = groupType === "SECTION"? "Team" : "Section";
|
|
const oppositeGroupName = groupType === "SECTION"? member.team : member.section;
|
|
|
|
$("#teamsContainer").find(".team-members").last().append(
|
|
`<li class='mb-3 rounded w-100 fluid-hover-zoom'>
|
|
<div
|
|
class='team-member d-flex'
|
|
data-first='${member.first}'
|
|
data-last='${member.last}'
|
|
data-member-id='${member.id}'
|
|
data-team-number='${member.team}'
|
|
data-peg-number='${member.peg}'
|
|
data-section='${member.section}'>
|
|
<div class='px-3 py-2 w-100'>
|
|
<div class='badge bg-secondary-subtle text-body fixed-badge' data-bs-title='Peg ${member.peg}' data-bs-toggle='tooltip'>
|
|
${member.peg}
|
|
</div>
|
|
<div class='badge bg-secondary-subtle text-body fixed-badge' data-bs-title='${oppositeGroupType} ${oppositeGroupName}' data-bs-toggle='tooltip'>
|
|
${oppositeGroupName}
|
|
</div>
|
|
<div class='d-inline-block ms-2 team-member-fullname'>
|
|
${fullname}
|
|
</div>
|
|
</div>
|
|
<button type='button' class='ms-auto btn btn-light border-0 fs-6 pencil-btn align-self-center me-1 force-contents-center'>
|
|
<i class='bi bi-pencil'></i>
|
|
</button>
|
|
</div>
|
|
</li>`
|
|
)
|
|
});
|
|
});
|
|
|
|
activateTooltips(); // activate the new tooltips
|
|
|
|
// Highlight all instances where the text matches the search critera
|
|
if (!isEmptyOrSpaces(highlightText)) {
|
|
$("#teamsContainer").find('.team-member-fullname').each(function() {
|
|
var regex = new RegExp(highlightText, 'gi');
|
|
$(this).html($(this).text().replace(regex, '<span class="text-company fw-bolder border-2 border-bottom border-company">$&</span>'));
|
|
});
|
|
}
|
|
|
|
// Bind edit team modal to the edit team button
|
|
$(".team > h3 > button").on("click", function() {
|
|
$(this).addClass("active");
|
|
$("#editTeamModal").off("hidden.bs.modal").on("hidden.bs.modal", function() {
|
|
$("#teamsContainer").find(".team").find("button.active").removeClass("active");
|
|
});
|
|
openEditTeamModal($(this).parent().parent().data("number"));
|
|
});
|
|
|
|
// Bind edit member modal to the edit member button
|
|
$(".team-member button").on("click", function() {
|
|
$(this).addClass("active");
|
|
$("#editMemberModal").off("hidden.bs.modal").on("hidden.bs.modal", function() {
|
|
$("#teamsContainer").find(".team").find("button.active").removeClass("active");
|
|
});
|
|
openEditMemberModal($(this).parent().data("member-id"));
|
|
});
|
|
|
|
// Bind new member/team buttons
|
|
$("#addTeam").on("click", () => {
|
|
openEditTeamModal(-1);
|
|
});
|
|
|
|
$("#addMember").on("click", () => {
|
|
openEditMemberModal(-1);
|
|
});
|
|
}
|
|
|
|
function fetchAndLoadTeams(search="", sortGroups="", sortMembers="") {
|
|
$.ajax({
|
|
url: getTeamsUrl,
|
|
type: "post",
|
|
data: {
|
|
"csrfmiddlewaretoken": csrfMiddlewareToken,
|
|
"search": search,
|
|
"sortGroups": sortGroups,
|
|
"sortMembers": sortMembers
|
|
},
|
|
error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); },
|
|
success: (result) => {
|
|
teamsLoading(false);
|
|
loadTeams(result.groups, search, result.sortGroups);
|
|
}
|
|
});
|
|
}
|
|
|
|
function teamsLoading(show=true) {
|
|
$("#teamsNotFound").hide()
|
|
|
|
if (show) {
|
|
$("#teamsContainer").hide();
|
|
$("#teamsLoadingSpinner").show();
|
|
return
|
|
}
|
|
|
|
$("#teamsContainer").show();
|
|
$("#teamsLoadingSpinner").hide();
|
|
}
|
|
|
|
function openEditTeamModal(teamNumber) {
|
|
|
|
// Team data
|
|
const team = $(`.team[data-number='${teamNumber}']`);
|
|
const section = team.data("section");
|
|
|
|
// Load data to form
|
|
$("#editTeamTitle").text(`Team ${teamNumber}`);
|
|
|
|
$("#editTeamNumber").val(teamNumber);
|
|
$('#editTeamSection').val(section);
|
|
|
|
$("#editTeamModal").modal("show");
|
|
}
|
|
|
|
function openEditMemberModal(memberId) {
|
|
|
|
var modalTitle = "Add Member";
|
|
var first = "";
|
|
var last = "";
|
|
var teamNumber = $(".team").map(function() { return $(this).data("number"); }).get(); // TODO: shorten this
|
|
var pegNumber = 0;
|
|
var firstPlaceholder = "Forename";
|
|
var lastPlaceholder = "Surname";
|
|
|
|
// Member data
|
|
if (memberId !== -1) {
|
|
const member = $(`.team-member[data-member-id='${memberId}']`);
|
|
first = member.data("first");
|
|
last = member.data("last");
|
|
teamNumber = member.data("team-number");
|
|
pegNumber = member.data("peg-number");
|
|
firstPlaceholder = first;
|
|
lastPlaceholder = last;
|
|
modalTitle = "Edit: " + first + " " + last
|
|
}
|
|
|
|
// Load teams as options
|
|
$("#editMemberTeam").html("");
|
|
$(".team").map(function() {
|
|
return $(this).data("number");
|
|
}).get().forEach((team) => {
|
|
$("#editMemberTeam").append($(`<option value='${team}'>Team ${team}</option>`));
|
|
});
|
|
|
|
// Load data to form
|
|
$("#editMemberName").text(modalTitle);
|
|
$("#editMemberFirstName").val(first);
|
|
$("#editMemberLastName").val(last);
|
|
$("#editMemberTeam").val(teamNumber);
|
|
$("#editMemberPeg").val(pegNumber);
|
|
|
|
$("#editMemberFirstName").attr("placeholder", firstPlaceholder);
|
|
$("#editMemberLastName").attr("placeholder", lastPlaceholder);
|
|
|
|
$("#editMemberModal").modal("show");
|
|
|
|
// Update the submit button
|
|
$("#saveEditModal").off("click").on("click", () => { saveEditMemberModal(memberId); });
|
|
}
|
|
|
|
function saveEditMemberModal(memberId) {
|
|
|
|
if (!$("#editMemberForm").valid()) {
|
|
return;
|
|
}
|
|
|
|
// Grab the updated data
|
|
const first = $("#editMemberFirstName").val();
|
|
const last = $("#editMemberLastName").val();
|
|
const teamNumber = $("#editMemberTeam").val();
|
|
const pegNumber = $("#editMemberPeg").val();
|
|
const search = $("#search").val();
|
|
|
|
teamsLoading(true);
|
|
|
|
$.ajax({
|
|
url: updateMemberUrl,
|
|
type: "post",
|
|
data: {
|
|
"csrfmiddlewaretoken": csrfMiddlewareToken,
|
|
"memberId": memberId,
|
|
"first": first,
|
|
"last": last,
|
|
"teamNumber": teamNumber,
|
|
"pegNumber": pegNumber,
|
|
"search": !isEmptyOrSpaces(search) ? search : null
|
|
},
|
|
error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); },
|
|
success: (result) => {
|
|
teamsLoading(false);
|
|
loadTeams(result.teams, search, result.sortGroups);
|
|
$("#editMemberModal").modal("hide");
|
|
}
|
|
});
|
|
} |