Finalisation of teams/member model. Added testing script.

This commit is contained in:
Corban-Lee 2023-05-07 23:20:34 +01:00
parent 8afd3fa905
commit 77b8c2fc2d
11 changed files with 696 additions and 244 deletions

23
create_dummy_data.bat Normal file
View File

@ -0,0 +1,23 @@
@echo off
echo The purpose of this file is to populate the database with dummy data for testing.
set /p confirm=Do you want to continue? [y/n]
if /i "%confirm%"=="y" (
cd /d %~dp0
if exist src\db.sqlite3 (
del src\db.sqlite3
)
call venv\Scripts\activate.bat
python src/manage.py migrate
python src/manage.py create_teams_fixture 10
python src/manage.py loaddata teams_fixture
python src/manage.py create_members_fixture 3
python src/manage.py loaddata members_fixture
deactivate
) else (
echo Exiting script...
)

View File

@ -2,7 +2,7 @@
from django.contrib import admin
from .models import Peg, Member, Team, Section
from .models import Peg, Member, Team
@admin.register(Peg)
@ -33,9 +33,9 @@ class TeamAdmin(admin.ModelAdmin):
search_fields = ("team_number",)
@admin.register(Section)
class SectionAdmin(admin.ModelAdmin):
"""Admin model for the Section model."""
# @admin.register(Section)
# class SectionAdmin(admin.ModelAdmin):
# """Admin model for the Section model."""
list_display = ("character",)
search_fields = ("character",)
# list_display = ("character",)
# search_fields = ("character",)

View File

@ -0,0 +1,302 @@
[
{
"model": "mainapp.member",
"pk": 1,
"fields": {
"first_name": "Michael",
"last_name": "Jewett",
"team": 1,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 2,
"fields": {
"first_name": "Robert",
"last_name": "Kearns",
"team": 1,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 3,
"fields": {
"first_name": "Jack",
"last_name": "Colon",
"team": 1,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 4,
"fields": {
"first_name": "Nina",
"last_name": "Freeman",
"team": 2,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 5,
"fields": {
"first_name": "Josephine",
"last_name": "Mcdonald",
"team": 2,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 6,
"fields": {
"first_name": "Don",
"last_name": "Wrape",
"team": 2,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 7,
"fields": {
"first_name": "Audrey",
"last_name": "Mcguire",
"team": 3,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 8,
"fields": {
"first_name": "Morris",
"last_name": "Delker",
"team": 3,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 9,
"fields": {
"first_name": "Keith",
"last_name": "Cline",
"team": 3,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 10,
"fields": {
"first_name": "Vincent",
"last_name": "Alconcel",
"team": 4,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 11,
"fields": {
"first_name": "Leroy",
"last_name": "Gibson",
"team": 4,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 12,
"fields": {
"first_name": "Ronald",
"last_name": "Ross",
"team": 4,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 13,
"fields": {
"first_name": "Dennis",
"last_name": "Thompson",
"team": 5,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 14,
"fields": {
"first_name": "Clarence",
"last_name": "Nieto",
"team": 5,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 15,
"fields": {
"first_name": "Trena",
"last_name": "Robbins",
"team": 5,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 16,
"fields": {
"first_name": "Karen",
"last_name": "Vessell",
"team": 6,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 17,
"fields": {
"first_name": "Herb",
"last_name": "Mcgowan",
"team": 6,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 18,
"fields": {
"first_name": "Jimmie",
"last_name": "Rittenhouse",
"team": 6,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 19,
"fields": {
"first_name": "Lewis",
"last_name": "Queener",
"team": 7,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 20,
"fields": {
"first_name": "Nancy",
"last_name": "Merritt",
"team": 7,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 21,
"fields": {
"first_name": "Elaine",
"last_name": "Foss",
"team": 7,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 22,
"fields": {
"first_name": "Natasha",
"last_name": "Perkins",
"team": 8,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 23,
"fields": {
"first_name": "Dean",
"last_name": "Carroll",
"team": 8,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 24,
"fields": {
"first_name": "Lauren",
"last_name": "Carter",
"team": 8,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 25,
"fields": {
"first_name": "Robert",
"last_name": "Orr",
"team": 9,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 26,
"fields": {
"first_name": "Deborah",
"last_name": "Johnson",
"team": 9,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 27,
"fields": {
"first_name": "Helen",
"last_name": "Henry",
"team": 9,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 28,
"fields": {
"first_name": "Linda",
"last_name": "Armstrong",
"team": 10,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 29,
"fields": {
"first_name": "Tricia",
"last_name": "Charles",
"team": 10,
"peg_number": null
}
},
{
"model": "mainapp.member",
"pk": 30,
"fields": {
"first_name": "Cortney",
"last_name": "Hogan",
"team": 10,
"peg_number": null
}
}
]

View File

@ -0,0 +1,72 @@
[
{
"model": "mainapp.team",
"pk": 1,
"fields": {
"section_letter": "A"
}
},
{
"model": "mainapp.team",
"pk": 2,
"fields": {
"section_letter": "B"
}
},
{
"model": "mainapp.team",
"pk": 3,
"fields": {
"section_letter": "C"
}
},
{
"model": "mainapp.team",
"pk": 4,
"fields": {
"section_letter": "D"
}
},
{
"model": "mainapp.team",
"pk": 5,
"fields": {
"section_letter": "E"
}
},
{
"model": "mainapp.team",
"pk": 6,
"fields": {
"section_letter": "F"
}
},
{
"model": "mainapp.team",
"pk": 7,
"fields": {
"section_letter": "G"
}
},
{
"model": "mainapp.team",
"pk": 8,
"fields": {
"section_letter": "H"
}
},
{
"model": "mainapp.team",
"pk": 9,
"fields": {
"section_letter": "J"
}
},
{
"model": "mainapp.team",
"pk": 10,
"fields": {
"section_letter": "K"
}
}
]

View File

@ -30,25 +30,6 @@ class Command(BaseCommand):
self.stdout.write(self.style.SUCCESS(f"Created {num_members} members."))
# create a team fixture file
teams_fixture = []
for team in teams:
team_fixture = {
"model": "mainapp.team",
"pk": team.pk,
"fields": {
"team_number": team.team_number,
"section": team.section_id
}
}
teams_fixture.append(team_fixture)
with open("src/mainapp/fixtures/teams_fixture.json", "w") as f:
f.write(json.dumps(teams_fixture, indent=2))
self.stdout.write(self.style.SUCCESS("Created teams_fixture.json."))
# create a members fixture file
members_fixture = []
for member in members:
@ -58,7 +39,8 @@ class Command(BaseCommand):
"fields": {
"first_name": member.first_name,
"last_name": member.last_name,
"team": member.team_id
"team": member.team_id,
"peg_number": member.peg_number
}
}
members_fixture.append(member_fixture)

View File

@ -1,6 +1,13 @@
import json
from string import ascii_uppercase
from django.core.management.base import BaseCommand
from mainapp.models import Team
import random
from django.db.utils import IntegrityError
from mainapp.models import Team, BLOCKED_SECTION_LETTERS
for char in BLOCKED_SECTION_LETTERS:
ascii_uppercase = ascii_uppercase.replace(char.upper(), "")
class Command(BaseCommand):
help = "Creates a fixture file for Team objects"
@ -10,28 +17,49 @@ class Command(BaseCommand):
def handle(self, *args, **options):
num_teams = options["num_teams"]
limit = len(ascii_uppercase)
if num_teams > limit:
self.stdout.write(self.style.ERROR(f"Number of teams is too large [{num_teams}/{limit}]."))
return
# this code is so shit
# reminder to please rewrite this please
teams = []
for i in range(num_teams):
team = Team.objects.create()
iteration = 0
errors = 0
while iteration < num_teams:
try:
team = Team.objects.create(section_letter=ascii_uppercase[iteration])
except IntegrityError as err:
self.stdout.write(self.style.ERROR(err))
errors += 1
if errors > limit:
break # heavy nesting: not good
iteration += 1
teams.append(team)
if not teams: # TODO: error message
self.stdout.write(self.style.ERROR(f"Couldn't make teams -> ask corban because im too lazy to write an error message right now."))
return
for team in teams:
self.stdout.write(self.style.SUCCESS(f"Created team {team.team_number}"))
filename = f"src/mainapp/fixtures/teams_{num_teams}.json"
with open(filename, "w") as f:
self.stdout.write(f"Writing fixture to {filename}")
f.write("[\n")
for i, team in enumerate(teams):
f.write(" {\n")
f.write(f' "model": "mainapp.team",\n')
f.write(f' "pk": {team.team_number},\n')
f.write(' "fields": {}\n')
f.write(" }")
if i < len(teams) - 1:
f.write(",")
f.write("\n")
f.write("]\n")
filename = f"src/mainapp/fixtures/teams_fixture.json"
self.stdout.write(self.style.SUCCESS("Fixture created successfully"))
teams_fixture = []
for team in teams:
team_fixture = {
"model": "mainapp.team",
"pk": team.pk,
"fields": {
"section_letter": team.section_letter
}
}
teams_fixture.append(team_fixture)
with open(filename, "w") as f:
f.write(json.dumps(teams_fixture, indent=2))
self.stdout.write(self.style.SUCCESS("Created teams_fixture.json."))

View File

@ -1,4 +1,4 @@
# Generated by Django 4.1.5 on 2023-05-06 15:53
# Generated by Django 4.1.5 on 2023-05-07 21:57
from django.db import migrations, models
import django.db.models.deletion
@ -19,17 +19,11 @@ class Migration(migrations.Migration):
('peg_number', mainapp.models.ReusableAutoField(default=mainapp.models.ReusableAutoField.get_default, editable=False, primary_key=True, serialize=False)),
],
),
migrations.CreateModel(
name='Section',
fields=[
('character', models.CharField(max_length=1, primary_key=True, serialize=False, validators=[mainapp.models.validate_section_character])),
],
),
migrations.CreateModel(
name='Team',
fields=[
('team_number', mainapp.models.ReusableAutoField(default=mainapp.models.ReusableAutoField.get_default, editable=False, primary_key=True, serialize=False)),
('section', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mainapp.section')),
('section_letter', models.CharField(choices=[('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D'), ('E', 'E'), ('F', 'F'), ('G', 'G'), ('H', 'H'), ('I', 'I'), ('J', 'J'), ('K', 'K'), ('L', 'L'), ('M', 'M'), ('N', 'N'), ('O', 'O'), ('P', 'P'), ('Q', 'Q'), ('R', 'R'), ('S', 'S'), ('T', 'T'), ('U', 'U'), ('V', 'V'), ('W', 'W'), ('X', 'X'), ('Y', 'Y'), ('Z', 'Z')], max_length=1, unique=True)),
],
),
migrations.CreateModel(
@ -38,7 +32,8 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=255)),
('last_name', models.CharField(max_length=255)),
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mainapp.team', validators=[mainapp.models.validate_team_size])),
('peg_number', models.PositiveIntegerField(null=True, unique=True)),
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='mainapp.team', validators=[mainapp.models.validate_team_size])),
],
),
]

View File

@ -1,13 +1,14 @@
from string import ascii_uppercase
from django.db import models
from django.core.exceptions import ValidationError
# products/models.py
from django.db import models
class ReusableAutoField(models.PositiveIntegerField):
"""A django auto field that can reuse deleted primary keys"""
def get_next_available_id(self, model_cls, using=None):
"""
Returns the next available id for the given model class.
@ -50,8 +51,20 @@ class Member(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True, blank=True, validators=(validate_team_size,), related_name='members')
peg_number = models.PositiveIntegerField(null=True, editable=True, unique=True)
# peg = models.OneToOneField("Peg", on_delete=models.SET_NULL, null=True, blank=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# If the peg_number field is not set, we assign it the smallest
# available positive integer, excluding any used values.
if not self.peg_number:
used_peg_numbers = Member.objects.exclude(id=self.id).exclude(peg_number=None).values_list("peg_number", flat=True)
peg_numbers = set(range(1, Member.objects.count() + 1)) - set(used_peg_numbers)
if peg_numbers:
self.peg_number = min (peg_numbers)
def __str__(self):
return f"{self.first_name} {self.last_name} (team {self.team.team_number}))"
@ -60,11 +73,16 @@ class Member(models.Model):
return f"{self.first_name} {self.last_name}"
BLOCKED_SECTION_LETTERS = ["i"] # lowercase only, sections cannot be any of these letters#
class Team(models.Model):
"""Represents a team"""
team_number = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False)
section = models.OneToOneField("Section", on_delete=models.SET_NULL, null=True, blank=True)
section_letter = models.CharField(max_length=1, unique=True, choices=[
(char, char) for char in ascii_uppercase if char.lower() not in BLOCKED_SECTION_LETTERS
])
def __str__(self):
return f"Team {self.team_number}"
@ -84,17 +102,17 @@ def validate_section_character(value):
raise ValidationError(f"The character <{value}> is a prohibited character.")
class Section(models.Model):
"""Represents a section of the scoreboard"""
# class Section(models.Model):
# """Represents a section of the scoreboard"""
# character field stores a single character but doesnt allow for 'I' or 'i'
character = models.CharField(primary_key=True, max_length=1, validators=(validate_section_character, ))
# # character field stores a single character but doesnt allow for 'I' or 'i'
# character = models.CharField(primary_key=True, max_length=1, validators=(validate_section_character, ))
def clean(self):
self.character = self.character.upper()
# def clean(self):
# self.character = self.character.upper()
def __str__(self):
return f"Section {self.character}"
# def __str__(self):
# return f"Section {self.character}"
#class Scoreboard(models.Model):

View File

@ -12,7 +12,7 @@
<div class="container my-4 p-4 pb-0 bg-body-secondary rounded">
<div class="row mb-4">
<div class="col-md-6 col-xl-8">
<h3 class="mb-0">Teams &amp; Members</h3>
<h3>Teams &amp; Members</h3>
</div>
<div class="col-md-6 col-xl-4">
<div class="input-group mb-4 mb-md-0">
@ -38,7 +38,7 @@
<!-- Edit Modal -->
<div class="modal fade" id="editMemberModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title fs-5" id="editMemberName">Member Name Here</h3>
@ -69,179 +69,12 @@
</div>
{% endblock content %}
{% load static %}
{% block scripts %}
<script type="text/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
fetchAndLoadTeams(); // load the teams
var searchTimeout = null;
$("#search").keyup(() => {
clearTimeout(searchTimeout);
teamsLoading(true);
searchTimeout = setTimeout(() => {
fetchAndLoadTeams($("#search").val());
}, 500)
});
$("#searchButton").on("click", () => {
fetchAndLoadTeams($("#search").val());
});
})
/**
* Returns bool if the string is empty or only contains whitespace
*
* **/
function isEmptyOrSpaces(string) {
return string === null || string.match(/^ *$/) !== null;
}
function loadTeams(teams, highlightText="") {
$("#teamsContainer").html(""); // Clear the previous listed teams
if (teams.length < 1) {
$("#teamsNotFound").show();
}
// Iterate over and add each team
teams.forEach((team, index) => {
$("#teamsContainer").append(
`<div class='col-12 col-md-6 col-xl-4 mb-4'>
<div
class='team p-4 bg-body-tertiary rounded h-100 fluid-hover-zoom shadow-sm shadow-on-hover'
data-number='${team.team_number}'
>
<h4>${team.team_number}</h4>
<ul class='list-unstyled ul-cols-2 team-members'>
</ul>
</div>
</div>`
);
// Whilimage.pnge we have the team, iterate over and add it's members
team.members.forEach((member) => {
const fullname = member.first + " " + member.last;
$("#teamsContainer").find(".team-members").last().append(
`<li>
<span
type='button'
class='team-member'
data-first='${member.first}'
data-last='${member.last}'
data-member-id='${member.id}'
data-team-number='${member.team}'
>
${fullname}
</span>
</li>`
);
});
});
// Highlight all instances where the text matches the search critera
if (!isEmptyOrSpaces(highlightText)) {
$("#teamsContainer").find(`.team-member:icontains('${highlightText}')`).addClass("border-bottom text-primary fw-bold");
}
$(".team-member").on("click", function() {
openEditModal($(this).data("member-id"));
});
}
function fetchAndLoadTeams(search="") {
$.ajax({
url: "{% url 'get-teams' %}",
type: "post",
data: {
"csrfmiddlewaretoken": "{{ csrf_token }}",
"search": !isEmptyOrSpaces(search) ? search : null
},
error: (xhr, textStatus, errorThrown) => { alert(errorThrown); },
success: (result) => {
teamsLoading(false);
loadTeams(result.teams, search);
}
});
}
function teamsLoading(show=true) {
$("#teamsNotFound").hide()
if (show) {
$("#teamsContainer").hide();
$("#teamsLoadingSpinner").show();
return
}
$("#teamsContainer").show();
$("#teamsLoadingSpinner").hide();
}
function openEditModal(memberId) {
// Member data
const member = $(`.team-member[data-member-id='${memberId}']`);
const first = member.data("first");
const last = member.data("last");
const teamNumber = member.data("team-number");
// Load teams as options
$("#editMemberTeam").html("");
$(".team").map(function() {
return $(this).data("number");
}).get().forEach((team) => {
$("#editMemberTeam").append($(`<option value='${team}'>${team}</option>`));
});
// Load data to form
$("#editMemberName").text(first + " " + last);
$("#editMemberFirstName").val(first);
$("#editMemberLastName").val(last);
$("#editMemberTeam").val(teamNumber);
$("#editMemberModal").modal("show");
// Update the submit button
$("#saveEditModal").off("click").on("click", () => { saveEditModal(memberId); });
}
function saveEditModal(memberId) {
// Grab the updated data
const first = $("#editMemberFirstName").val();
const last = $("#editMemberLastName").val();
const teamNumber = $("#editMemberTeam").val();
const search = $("#search").val();
teamsLoading(true);
$.ajax({
url: "{% url 'update-member' %}",
type: "post",
data: {
"csrfmiddlewaretoken": "{{ csrf_token }}",
"memberId": memberId,
"first": first,
"last": last,
"teamNumber": teamNumber,
"search": !isEmptyOrSpaces(search) ? search : null
},
error: (xhr, textStatus, errorThrown) => { alert(errorThrown); },
success: (result) => {
teamsLoading(false);
loadTeams(result.teams, search);
$("#editMemberModal").modal("hide");
}
});
}
const getTeamsUrl = "{% url 'get-teams' %}";
const updateMemberUrl = "{% url 'update-member' %}";
const csrfMiddlewareToken = "{{ csrf_token }}";
</script>
<script src="{% static 'js/teams.js' %}"></script>
{% endblock scripts %}

View File

@ -4,7 +4,7 @@ from functools import reduce
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.db.models import Q
from django.db.models import Q, Case, When, Value, IntegerField
from .models import Peg, Team, Member
@ -58,17 +58,23 @@ def get_teams(request):
]
)
)
teams = teams.filter(members__in=members)
teams = teams.filter(members__in=members).distinct()
# Create a dictionary for the data that is JSON safe
response_data = {"teams": []}
for team in teams:
team_data = {
"team_number": team.team_number,
"section": team.section.name if team.section else None,
"section_letter": team.section_letter if team.section_letter else None,
"members": [
{"first": member.first_name, "last": member.last_name, "id": member.id, "team": team.team_number}
for member in team.members.all()
{
"first": member.first_name,
"last": member.last_name,
"id": member.id,
"team": team.team_number,
"peg": member.peg_number
}
for member in team.members.order_by("peg_number").all()
]
}
response_data["teams"].append(team_data)

193
src/static/js/teams.js Normal file
View File

@ -0,0 +1,193 @@
jQuery.expr[':'].icontains = function(a, i, m) {
return jQuery(a).text().toUpperCase()
.indexOf(m[3].toUpperCase()) >= 0;
};
$(document).ready(() => {
// Enable all tooltips on the page
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
teamsLoading(true); // show the loading icon
fetchAndLoadTeams(); // load the teams
var searchTimeout = null;
$("#search").keyup(() => {
clearTimeout(searchTimeout);
teamsLoading(true);
searchTimeout = setTimeout(() => {
fetchAndLoadTeams($("#search").val());
}, 500)
});
$("#searchButton").on("click", () => {
fetchAndLoadTeams($("#search").val());
});
})
/**
* Returns bool if the string is empty or only contains whitespace
*
* **/
function isEmptyOrSpaces(string) {
return string === null || string.match(/^ *$/) !== null;
}
function loadTeams(teams, highlightText="") {
$("#teamsContainer").html(""); // Clear the previous listed teams
if (teams.length < 1) {
$("#teamsNotFound").show();
}
// Iterate over and add each team
teams.forEach((team) => {
$("#teamsContainer").append(
`<div class='col-12 col-md-6 col-xl-4 mb-4'>
<div
class='team p-4 bg-body-tertiary rounded h-100 fluid-hover-zoom shadow-sm shadow-on-hover'
data-number='${team.team_number}'>
<h4>
<span class='fs-6'>TEAM</span>
${team.team_number}
<span class='fs-5 text-secondary'>
<span class='fs-6'>SECTION</span>
${team.section_letter}
</span>
</h4>
<ul class='list-unstyled team-members mt-3'>
</ul>
</div>
</div>`
);
// Whilimage.pnge we have the team, iterate over and add it's members
team.members.forEach((member) => {
const fullname = member.first + " " + member.last;
$("#teamsContainer").find(".team-members").last().append(
`<li class='bg-body-secondary mb-3 rounded w-100'>
<div
type='button'
class='team-member px-3 py-2 d-flex'
data-first='${member.first}'
data-last='${member.last}'
data-member-id='${member.id}'
data-team-number='${member.team}'>
<span class='text-secondary me-2'>
${member.peg}
</span>
<span class='team-member-fullname'>
${fullname}
</span>
<span class='ms-auto text-secondary'>
<i class='bi bi-pencil'></i>
</span>
</div>
</li>`
);
});
});
// 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-primary fw-bold">$&</span>'));
});
}
$(".team-member").on("click", function() {
openEditModal($(this).data("member-id"));
});
}
function fetchAndLoadTeams(search="") {
$.ajax({
url: getTeamsUrl,
type: "post",
data: {
"csrfmiddlewaretoken": csrfMiddlewareToken,
"search": !isEmptyOrSpaces(search) ? search : null
},
error: (xhr, textStatus, errorThrown) => { alert(errorThrown); },
success: (result) => {
teamsLoading(false);
loadTeams(result.teams, search);
}
});
}
function teamsLoading(show=true) {
$("#teamsNotFound").hide()
if (show) {
$("#teamsContainer").hide();
$("#teamsLoadingSpinner").show();
return
}
$("#teamsContainer").show();
$("#teamsLoadingSpinner").hide();
}
function openEditModal(memberId) {
// Member data
const member = $(`.team-member[data-member-id='${memberId}']`);
const first = member.data("first");
const last = member.data("last");
const teamNumber = member.data("team-number");
// Load teams as options
$("#editMemberTeam").html("");
$(".team").map(function() {
return $(this).data("number");
}).get().forEach((team) => {
$("#editMemberTeam").append($(`<option value='${team}'>${team}</option>`));
});
// Load data to form
$("#editMemberName").text(first + " " + last);
$("#editMemberFirstName").val(first);
$("#editMemberLastName").val(last);
$("#editMemberTeam").val(teamNumber);
$("#editMemberModal").modal("show");
// Update the submit button
$("#saveEditModal").off("click").on("click", () => { saveEditModal(memberId); });
}
function saveEditModal(memberId) {
// Grab the updated data
const first = $("#editMemberFirstName").val();
const last = $("#editMemberLastName").val();
const teamNumber = $("#editMemberTeam").val();
const search = $("#search").val();
teamsLoading(true);
$.ajax({
url: updateMemberUrl,
type: "post",
data: {
"csrfmiddlewaretoken": csrfMiddlewareToken,
"memberId": memberId,
"first": first,
"last": last,
"teamNumber": teamNumber,
"search": !isEmptyOrSpaces(search) ? search : null
},
error: (xhr, textStatus, errorThrown) => { alert(errorThrown); },
success: (result) => {
teamsLoading(false);
loadTeams(result.teams, search);
$("#editMemberModal").modal("hide");
}
});
}