From 426f381da25f5a9d2656cea29f2673a419b55fbd Mon Sep 17 00:00:00 2001 From: Corban-Lee <77944149+XordK@users.noreply.github.com> Date: Mon, 15 May 2023 00:22:17 +0100 Subject: [PATCH] Anglers page refactor pt.1 --- src/mainapp/admin.py | 10 +- src/mainapp/fixtures/members_fixture.json | 1039 +---------------- .../commands/create_members_fixture.py | 8 +- .../management/commands/create_superuser.py | 28 + src/mainapp/migrations/0001_initial.py | 10 +- src/mainapp/models.py | 37 +- src/mainapp/teams_demo_test.py | 11 - src/mainapp/templates/anglers.html | 147 +++ src/mainapp/templates/index.html | 2 +- src/mainapp/templates/modals/angler.html | 83 ++ src/mainapp/templates/modals/section.html | 47 + src/mainapp/templates/modals/team.html | 47 + src/mainapp/templates/teams.html | 16 +- src/mainapp/urls.py | 7 +- src/mainapp/views.py | 320 ++++- src/static/css/mainapp/anglers.css | 0 src/static/js/custom.js | 13 +- src/static/js/mainapp/anglers.js | 199 ++++ src/static/js/teams.js | 275 ++++- 19 files changed, 1165 insertions(+), 1134 deletions(-) create mode 100644 src/mainapp/management/commands/create_superuser.py delete mode 100644 src/mainapp/teams_demo_test.py create mode 100644 src/mainapp/templates/anglers.html create mode 100644 src/mainapp/templates/modals/angler.html create mode 100644 src/mainapp/templates/modals/section.html create mode 100644 src/mainapp/templates/modals/team.html create mode 100644 src/static/css/mainapp/anglers.css create mode 100644 src/static/js/mainapp/anglers.js diff --git a/src/mainapp/admin.py b/src/mainapp/admin.py index 9ba3b88..cce404d 100644 --- a/src/mainapp/admin.py +++ b/src/mainapp/admin.py @@ -28,14 +28,14 @@ class MemberAdmin(admin.ModelAdmin): class TeamAdmin(admin.ModelAdmin): """Admin model for the Team model.""" - readonly_fields = ("name",) - list_display = ("name",) - search_fields = ("name",) + # readonly_fields = ("id", "number",) + list_display = ("id", "number",) + search_fields = ("number",) @admin.register(Section) class SectionAdmin(admin.ModelAdmin): """Admin model for the Section model.""" - list_display = ("name",) - search_fields = ("name",) + list_display = ("id", "character",) + search_fields = ("character",) diff --git a/src/mainapp/fixtures/members_fixture.json b/src/mainapp/fixtures/members_fixture.json index 6fe915f..d2aa5a7 100644 --- a/src/mainapp/fixtures/members_fixture.json +++ b/src/mainapp/fixtures/members_fixture.json @@ -3,297 +3,88 @@ "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Ronald", - "last_name": "Williams", + "first_name": "Duane", + "last_name": "Hamilton", "team": 1, - "section": 83, - "peg_number": 118 + "section": 3, + "peg_number": 2 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Amanda", - "last_name": "Campbell", + "first_name": "Stacey", + "last_name": "Strunk", "team": 1, - "section": 12, - "peg_number": 119 + "section": 10, + "peg_number": 3 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Sherita", - "last_name": "Horne", + "first_name": "Leah", + "last_name": "Bishop", "team": 1, - "section": 135, - "peg_number": 120 + "section": 6, + "peg_number": 4 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Malik", - "last_name": "Edmunds", - "team": 1, - "section": 102, - "peg_number": 121 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Anne", - "last_name": "Gridley", - "team": 1, - "section": 123, - "peg_number": 122 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Clara", - "last_name": "Mayfield", - "team": 1, - "section": 146, - "peg_number": 123 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Santo", - "last_name": "Ross", + "first_name": "Frances", + "last_name": "Day", "team": 2, - "section": 43, - "peg_number": 124 + "section": 16, + "peg_number": 5 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Sara", - "last_name": "Kjelland", + "first_name": "Hector", + "last_name": "Story", "team": 2, - "section": 130, - "peg_number": 125 + "section": 8, + "peg_number": 6 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Elizabeth", - "last_name": "Cochran", + "first_name": "Deborah", + "last_name": "Groseclose", "team": 2, - "section": 128, - "peg_number": 126 + "section": 11, + "peg_number": 7 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Donald", - "last_name": "Shannon", - "team": 3, - "section": 37, - "peg_number": 127 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Casey", - "last_name": "Kane", + "first_name": "Elaine", + "last_name": "Clarke", "team": 3, "section": 10, - "peg_number": 128 + "peg_number": 8 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Jim", - "last_name": "Bagley", + "first_name": "Minnie", + "last_name": "Stigall", "team": 3, - "section": 40, - "peg_number": 129 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Jennifer", - "last_name": "Harter", - "team": 4, - "section": 85, - "peg_number": 130 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Scott", - "last_name": "Nicolosi", - "team": 4, - "section": 8, - "peg_number": 131 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Alberto", - "last_name": "Lanford", - "team": 4, - "section": 153, - "peg_number": 132 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Andrew", - "last_name": "Bond", - "team": 5, - "section": 81, - "peg_number": 133 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Owen", - "last_name": "Stimmell", - "team": 5, - "section": 5, - "peg_number": 134 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Rosie", - "last_name": "Canales", - "team": 5, - "section": 136, - "peg_number": 135 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Paul", - "last_name": "Howell", - "team": 6, - "section": 99, - "peg_number": 136 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Franklin", - "last_name": "Crawford", - "team": 6, - "section": 28, - "peg_number": 137 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Erwin", - "last_name": "Golding", - "team": 6, - "section": 129, - "peg_number": 138 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Jesse", - "last_name": "Soto", - "team": 7, - "section": 101, - "peg_number": 139 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Robert", - "last_name": "Alway", - "team": 7, - "section": 65, - "peg_number": 140 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Donald", - "last_name": "Tillman", - "team": 7, - "section": 155, - "peg_number": 141 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Philip", - "last_name": "Koehler", - "team": 7, - "section": 58, - "peg_number": 142 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Phillip", - "last_name": "Bishop", - "team": 8, - "section": 38, - "peg_number": 143 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Timothy", - "last_name": "Heller", - "team": 8, - "section": 98, - "peg_number": 144 + "section": 7, + "peg_number": 9 } }, { @@ -301,384 +92,21 @@ "pk": null, "fields": { "first_name": "Dennis", - "last_name": "Elliott", - "team": 8, - "section": 152, - "peg_number": 145 + "last_name": "Stolar", + "team": 3, + "section": 14, + "peg_number": 10 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Louis", - "last_name": "Huang", - "team": 9, - "section": 15, - "peg_number": 146 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Susan", - "last_name": "Martinez", - "team": 9, - "section": 25, - "peg_number": 147 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Margaret", - "last_name": "Myles", - "team": 9, - "section": 57, - "peg_number": 148 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Wendell", - "last_name": "Morrison", - "team": 9, - "section": 154, - "peg_number": 149 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Gregory", - "last_name": "Oliver", - "team": 10, - "section": 126, - "peg_number": 150 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Raymond", - "last_name": "Gentry", - "team": 10, - "section": 157, - "peg_number": 151 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Andy", - "last_name": "Mckinney", - "team": 10, - "section": 148, - "peg_number": 152 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Kathy", - "last_name": "Pacheco", - "team": 11, - "section": 104, - "peg_number": 153 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "James", - "last_name": "Jones", - "team": 11, - "section": 80, - "peg_number": 154 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Sandra", - "last_name": "Payne", - "team": 11, - "section": 125, - "peg_number": 155 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Lynn", - "last_name": "Snider", - "team": 11, - "section": 98, - "peg_number": 156 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Joseph", - "last_name": "Lewis", - "team": 12, - "section": 48, - "peg_number": 157 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Carrie", - "last_name": "Broussard", - "team": 12, - "section": 86, - "peg_number": 158 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Leora", - "last_name": "Yeaton", - "team": 12, - "section": 118, - "peg_number": 159 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "John", - "last_name": "Walker", - "team": 13, - "section": 137, - "peg_number": 160 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Bart", - "last_name": "Simmons", - "team": 13, - "section": 80, - "peg_number": 161 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Anthony", - "last_name": "Delacruz", - "team": 13, - "section": 73, - "peg_number": 162 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Autumn", - "last_name": "Scott", - "team": 14, - "section": 39, - "peg_number": 163 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Candace", - "last_name": "Olds", - "team": 14, - "section": 98, - "peg_number": 164 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Sara", - "last_name": "Huerta", - "team": 14, - "section": 126, - "peg_number": 165 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Earl", - "last_name": "Bryan", - "team": 14, - "section": 117, - "peg_number": 166 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Max", - "last_name": "Coon", - "team": 15, - "section": 138, - "peg_number": 167 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Brooke", - "last_name": "Pizzo", - "team": 15, - "section": 151, - "peg_number": 168 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Terry", - "last_name": "Lawrence", - "team": 15, - "section": 8, - "peg_number": 169 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Jacqueline", - "last_name": "Brown", - "team": 16, - "section": 54, - "peg_number": 170 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Ervin", - "last_name": "Gervasio", - "team": 16, - "section": 114, - "peg_number": 171 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Anthony", - "last_name": "Herrera", - "team": 16, - "section": 82, - "peg_number": 172 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Sheila", - "last_name": "Hewitt", - "team": 17, - "section": 107, - "peg_number": 173 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Jill", - "last_name": "Weisman", - "team": 17, - "section": 150, - "peg_number": 174 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Mary", - "last_name": "Williams", - "team": 17, - "section": 143, - "peg_number": 175 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Janice", - "last_name": "Ali", - "team": 18, - "section": 9, - "peg_number": 176 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Scott", - "last_name": "Hancock", - "team": 18, - "section": 39, - "peg_number": 177 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Emily", - "last_name": "Marconis", - "team": 18, - "section": 69, - "peg_number": 178 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Martha", - "last_name": "Johnson", - "team": 19, - "section": 88, - "peg_number": 179 + "first_name": "Darlene", + "last_name": "Beckman", + "team": 4, + "section": 5, + "peg_number": 11 } }, { @@ -686,395 +114,54 @@ "pk": null, "fields": { "first_name": "Charles", - "last_name": "Ober", - "team": 19, - "section": 91, - "peg_number": 180 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Kristy", - "last_name": "Mayo", - "team": 19, - "section": 102, - "peg_number": 181 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Mabel", - "last_name": "Caraballo", - "team": 20, - "section": 129, - "peg_number": 182 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Shelley", - "last_name": "Gardea", - "team": 20, - "section": 13, - "peg_number": 183 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Kristina", - "last_name": "Goad", - "team": 20, - "section": 91, - "peg_number": 184 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Sammie", - "last_name": "Reed", - "team": 21, - "section": 31, - "peg_number": 185 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Frank", - "last_name": "Wilson", - "team": 21, - "section": 119, - "peg_number": 186 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Hazel", - "last_name": "Orosco", - "team": 21, - "section": 101, - "peg_number": 187 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Jorge", - "last_name": "Dangelo", - "team": 22, - "section": 88, - "peg_number": 188 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Richard", - "last_name": "Broach", - "team": 22, - "section": 60, - "peg_number": 189 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "William", - "last_name": "Mcclellan", - "team": 22, - "section": 94, - "peg_number": 190 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Warren", - "last_name": "Camara", - "team": 23, - "section": 35, - "peg_number": 191 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Gabriela", - "last_name": "Davis", - "team": 23, - "section": 32, - "peg_number": 192 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Rebecca", - "last_name": "Foster", - "team": 23, - "section": 83, - "peg_number": 193 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Ann", - "last_name": "Parks", - "team": 24, - "section": 36, - "peg_number": 194 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Melinda", - "last_name": "Gardner", - "team": 24, - "section": 141, - "peg_number": 195 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Paula", - "last_name": "Flagge", - "team": 24, - "section": 138, - "peg_number": 196 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Delores", - "last_name": "Jenkins", - "team": 25, - "section": 68, - "peg_number": 197 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Amanda", - "last_name": "Zamora", - "team": 25, - "section": 45, - "peg_number": 198 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Grace", - "last_name": "Hill", - "team": 25, - "section": 144, - "peg_number": 199 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Ronnie", - "last_name": "Hornick", - "team": 26, - "section": 115, - "peg_number": 200 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Rebecca", - "last_name": "Lilly", - "team": 26, - "section": 65, - "peg_number": 201 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Gena", - "last_name": "Hernandez", - "team": 26, - "section": 74, - "peg_number": 202 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Anthony", - "last_name": "Mccauley", - "team": 27, - "section": 103, - "peg_number": 203 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Evelyn", - "last_name": "Woodman", - "team": 27, - "section": 59, - "peg_number": 204 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Julie", - "last_name": "Senecal", - "team": 27, - "section": 105, - "peg_number": 205 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Karen", - "last_name": "Smith", - "team": 27, - "section": 89, - "peg_number": 206 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Anna", - "last_name": "Ritz", - "team": 28, - "section": 80, - "peg_number": 207 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Chris", - "last_name": "Peachey", - "team": 28, - "section": 157, - "peg_number": 208 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Guadalupe", - "last_name": "Cannon", - "team": 28, - "section": 82, - "peg_number": 209 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Holly", - "last_name": "Solano", - "team": 29, - "section": 36, - "peg_number": 210 - } - }, - { - "model": "mainapp.member", - "pk": null, - "fields": { - "first_name": "Alice", - "last_name": "Prahl", - "team": 29, + "last_name": "Boone", + "team": 4, "section": 12, - "peg_number": 211 + "peg_number": 12 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Robert", - "last_name": "Foster", - "team": 29, - "section": 147, - "peg_number": 212 + "first_name": "Cecil", + "last_name": "Lamus", + "team": 4, + "section": 13, + "peg_number": 13 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Rick", - "last_name": "Washington", - "team": 30, - "section": 57, - "peg_number": 213 + "first_name": "Morton", + "last_name": "Quinn", + "team": 5, + "section": 14, + "peg_number": 14 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Frieda", - "last_name": "Hoover", - "team": 30, - "section": 80, - "peg_number": 214 + "first_name": "Stuart", + "last_name": "Bristol", + "team": 5, + "section": 15, + "peg_number": 15 } }, { "model": "mainapp.member", "pk": null, "fields": { - "first_name": "Clara", - "last_name": "Moore", - "team": 30, - "section": 33, - "peg_number": 215 + "first_name": "Jeffrey", + "last_name": "Ferretti", + "team": 5, + "section": 10, + "peg_number": 16 } } ] \ No newline at end of file diff --git a/src/mainapp/management/commands/create_members_fixture.py b/src/mainapp/management/commands/create_members_fixture.py index 27ab6bb..9179635 100644 --- a/src/mainapp/management/commands/create_members_fixture.py +++ b/src/mainapp/management/commands/create_members_fixture.py @@ -49,10 +49,10 @@ class Command(BaseCommand): team=team ) - if not section.is_joinable(member): - member.delete() - i += 1 - continue + # if not section.is_joinable(member): + # member.delete() + # i += 1 + # continue new_members.append(member) print(member) diff --git a/src/mainapp/management/commands/create_superuser.py b/src/mainapp/management/commands/create_superuser.py new file mode 100644 index 0000000..6fd45be --- /dev/null +++ b/src/mainapp/management/commands/create_superuser.py @@ -0,0 +1,28 @@ +from django.contrib.auth.management.commands import createsuperuser +from django.core.management import CommandError + + +class Command(createsuperuser.Command): + help = 'Crate a superuser, and allow password to be provided' + + def add_arguments(self, parser): + super(Command, self).add_arguments(parser) + parser.add_argument( + '--password', dest='password', default=None, + help='Specifies the password for the superuser.', + ) + + def handle(self, *args, **options): + password = options.get('password') + username = options.get('username') + database = options.get('database') + + if password and not username: + raise CommandError("--username is required if specifying --password") + + super(Command, self).handle(*args, **options) + + if password: + user = self.UserModel._default_manager.db_manager(database).get(username=username) + user.set_password(password) + user.save() \ No newline at end of file diff --git a/src/mainapp/migrations/0001_initial.py b/src/mainapp/migrations/0001_initial.py index ed59721..ab3aa24 100644 --- a/src/mainapp/migrations/0001_initial.py +++ b/src/mainapp/migrations/0001_initial.py @@ -1,8 +1,7 @@ -# Generated by Django 4.1.5 on 2023-05-10 08:30 +# Generated by Django 4.1.5 on 2023-05-12 09:13 from django.db import migrations, models import django.db.models.deletion -import mainapp.models class Migration(migrations.Migration): @@ -17,13 +16,14 @@ class Migration(migrations.Migration): name='Section', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=3, unique=True)), + ('character', models.CharField(max_length=3, unique=True)), ], ), migrations.CreateModel( name='Team', fields=[ - ('name', mainapp.models.ReusableAutoField(default=mainapp.models.ReusableAutoField.get_default, editable=False, primary_key=True, serialize=False)), + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.PositiveIntegerField()), ], ), migrations.CreateModel( @@ -33,7 +33,7 @@ class Migration(migrations.Migration): ('first_name', models.CharField(max_length=255)), ('last_name', models.CharField(max_length=255)), ('peg_number', models.PositiveIntegerField(null=True, unique=True)), - ('section', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='mainapp.section')), + ('section', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='mainapp.section')), ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='mainapp.team')), ], ), diff --git a/src/mainapp/models.py b/src/mainapp/models.py index a74288c..4a4ecae 100644 --- a/src/mainapp/models.py +++ b/src/mainapp/models.py @@ -1,12 +1,8 @@ """Models for the mainapp.""" -from string import ascii_uppercase - from django.db import models from django.core.exceptions import ValidationError -from django.db import models - class ReusableAutoField(models.PositiveIntegerField): """A django auto field that can reuse deleted primary keys.""" @@ -98,7 +94,6 @@ class SectionValidator: return value - class SectionManager(models.Manager): @staticmethod @@ -153,25 +148,16 @@ class Section(models.Model): """Represents a fishing area. Members can be assigned to a section, but no 2 teammates can be in the same or adjacent section.""" - name = models.CharField(max_length=3, unique=True, null=False) + character = models.CharField(max_length=3, unique=True, null=False) objects = SectionManager() def clean(self): super().clean() - - validator = SectionValidator(max_value="ZZZ") - if not validator.is_valid(self.name): - raise ValidationError("Invalid section value.") - - self.name = self.name.upper() + self.character = self.character.upper() def __str__(self) -> str: - return self.name - - @property - def get_members(self): - return Member.objects.filter(section=self) + return self.character class Member(models.Model): @@ -195,28 +181,17 @@ class Member(models.Model): self.peg_number = min (peg_numbers) def __str__(self): - return f"{self.first_name} {self.last_name} (team {self.team.name}) [section {self.section.name}]" + return f"{self.first_name} {self.last_name} (team {self.team.number}) [section {self.section.character}]" @property def fullname(self) -> str: return f"{self.first_name} {self.last_name}" -class TeamManager(models.Manager): - pass - - class Team(models.Model): """Represents a team""" - name = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False) - - objects = TeamManager() + number = models.PositiveIntegerField(unique=True, null=False, blank=False) def __str__(self): - return f"Team {self.name}" - - @property - def get_members(self) -> list[Member]: - return Member.objects.filter(team=self) - + return f"Team {self.number}" diff --git a/src/mainapp/teams_demo_test.py b/src/mainapp/teams_demo_test.py deleted file mode 100644 index 0f95ba5..0000000 --- a/src/mainapp/teams_demo_test.py +++ /dev/null @@ -1,11 +0,0 @@ -import names -from string import ascii_lowercase - -created_teams = [] -for char in ascii_lowercase: - created_teams.append({ - "identifier": char.upper(), - "members": [{ - "name": names.get_full_name() - } for i in range(9)] - }) diff --git a/src/mainapp/templates/anglers.html b/src/mainapp/templates/anglers.html new file mode 100644 index 0000000..a20e0c2 --- /dev/null +++ b/src/mainapp/templates/anglers.html @@ -0,0 +1,147 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %} + Manage Anglers | +{% endblock title %} + +{% block style %} + + +{% endblock style %} + +{% block content %} + + +
+
+ Back + +
+ + +
+ + +
+
+

+ Manage Anglers +
+

+
+
+
+ + + +
+
+
+ + + + + +
+
+ + + +
+ + + + + + + + + + +
+ + + {% include "modals/section.html" %} + + + + {% include "modals/team.html" %} + + + + {% include "modals/angler.html" %} + + +{% endblock content %} + +{% block scripts %} + + + + + +{% endblock scripts %} \ No newline at end of file diff --git a/src/mainapp/templates/index.html b/src/mainapp/templates/index.html index d586ac6..cb9c353 100644 --- a/src/mainapp/templates/index.html +++ b/src/mainapp/templates/index.html @@ -14,7 +14,7 @@
- + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/src/mainapp/templates/modals/section.html b/src/mainapp/templates/modals/section.html new file mode 100644 index 0000000..fbd52a2 --- /dev/null +++ b/src/mainapp/templates/modals/section.html @@ -0,0 +1,47 @@ + + \ No newline at end of file diff --git a/src/mainapp/templates/modals/team.html b/src/mainapp/templates/modals/team.html new file mode 100644 index 0000000..8135dcc --- /dev/null +++ b/src/mainapp/templates/modals/team.html @@ -0,0 +1,47 @@ + + \ No newline at end of file diff --git a/src/mainapp/templates/teams.html b/src/mainapp/templates/teams.html index ee4b7aa..aa09c4d 100644 --- a/src/mainapp/templates/teams.html +++ b/src/mainapp/templates/teams.html @@ -50,11 +50,11 @@

Sort groups by

- +
- +
@@ -78,8 +78,7 @@ - -
+
Loading... @@ -234,10 +233,11 @@ diff --git a/src/mainapp/urls.py b/src/mainapp/urls.py index 7da889d..4f35f3b 100644 --- a/src/mainapp/urls.py +++ b/src/mainapp/urls.py @@ -8,9 +8,12 @@ urlpatterns = [ path('members/', views.teams, name='members'), # path('bulk-peg/', views.bulk_create_pegs, name='bulk-peg'), - path('get-teams/', views.get_teams, name='get-teams'), + path('get-angler-data/', views.get_angler_page_data, name='get-angler-data'), path('update-member/', views.update_member, name='update-member'), path('update-section/', views.update_section, name='update-section'), path('update-team/', views.update_team, name='update-team'), - path("get-next-identifier/", views.get_next_identifier, name='get-next-identifier') + path("get-next-identifier/", views.get_next_identifier, name='get-next-identifier'), + + # Rewrite + path('anglers/', views.ManageAnglersView.as_view(), name='anglers'), ] \ No newline at end of file diff --git a/src/mainapp/views.py b/src/mainapp/views.py index cdecbb9..70abaa6 100644 --- a/src/mainapp/views.py +++ b/src/mainapp/views.py @@ -6,6 +6,9 @@ from django.shortcuts import render, redirect from django.http import JsonResponse from django.db.models import Q, Case, When, Value, IntegerField from django.db.utils import IntegrityError +from django.views import View +from django.views.decorators.http import require_GET, require_POST +from django.http import HttpRequest from .models import Team, Member, Section, SectionManager, ReusableAutoField, SectionValidator @@ -38,7 +41,246 @@ def name_sort_key(section): else: return (1, section.name[:-1], section.name[-1]) -def get_teams(request, **kwargs): + +class ManageAnglersView(View): + """View for the Manage Anglers page.""" + + template_name = "anglers.html" + + def get(self, request: HttpRequest, *args, **kwargs) -> HttpRequest: + """Handle GET requests to the Manage Anglers page. + + Args: + request (HttpRequest): The HttpRequest object, contains GET data. + + Returns: + HttpRequest: A render of the Manage Anglers page. + """ + + return render(request, self.template_name) + + def post(self, request: HttpRequest, *args, **kwargs) -> JsonResponse: + """Handle POST requests to the Manage Anglers page. + + Args: + request (HttpRequest): The HttpRequest object, contains POST data. + + Returns: + JsonResponse: Contains the result of the action. + """ + + tasks = request.POST.getlist("tasks[]") + + data = {} + for task in tasks: + data.update(self.handle_task(request, task)) + + return JsonResponse(data) + + def handle_task(self, request, task: str) -> dict[str, str]: + """Handle a task. + + Args: + request (HttpRequest): HttpRequest object, contains POST data. + task (str): The task to handle. + + Raises: + ValueError: The task is invalid. + + Returns: + dict[str, str]: The result of the task. + """ + + # Format is {key = ACTION-TASK_NAME: value = HANDLER_FUNCTION} + task_handlers = { + "update-team": self.update_team, + "update-section": self.update_section, + "update-angler": self.update_angler, + "get-teams": self.get_teams, + "get-sections": self.get_sections, + "get-anglers": self.get_anglers, + } + + handler = task_handlers.get(task) + if not handler: + raise ValueError(f"Invalid task: {task}") + + return handler(request) + + def update_team(self, request) -> dict[str]: + """Update a team, returns a dictionary of the new team's data.""" + + result = {"form_errors": {}, "team": None} + team_id = request.POST.get("id") + team_number = request.POST.get("number") + + if not (team_id and team_number): + raise ValueError("Team ID or Team Number is missing or empty") + + if team_id == "-1": + team = Team(number=team_number) + else: + team = Team.objects.get(id=team_id) + team.number = team_number + + try: + team.save() + result["team"] = {"id": team.id, "number": team.number} + except IntegrityError: + result["form_errors"]["#editTeamNumberError"] = "A Team with this number already exists" + + return result + + def update_section(self, request) -> dict[str]: + """Update a section, returns a dictionary of the new section's data.""" + + result = {"form_errors": {}, "section": None} + section_id = request.POST.get("id") + section_character = request.POST.get("character") + + if not (section_id and section_character): + raise ValueError("Section ID or Section Character is missing or empty") + + if section_id == "-1": + section = Section(character=section_character) + else: + section = Section.objects.get(id=section_id) + section.character = section_character + + try: + section.save() + result["section"] = {"id": section.id, "character": section.character} + except IntegrityError: + result["form_errors"]["#editSectionNameError"] = "A Section with this character already exists" + + return result + + def update_angler(self, request) -> dict[str]: + """Update an Angler, returns a dictionary of the new angler's data.""" + + result = {"form_errors": {}, "angler": None} + angler_id = request.POST.get("angler_id") + forename = request.POST.get("forename") + surname = request.POST.get("surname") + peg_number = request.POST.get("peg_number") + team_id = request.POST.get("team_id") + section_id = request.POST.get("section_id") + + if not angler_id: + raise ValueError("Invalid angler ID") + + team = Team.objects.get(id=team_id) + section = Section.objects.get(id=section_id) + + if angler_id == "-1": + angler = Member( + first_name=forename, + last_name=surname, + peg_number=peg_number, + team=team, + section=section + ) + else: + angler = Member.objects.get(id=angler_id) + angler.first_name = forename + angler.last_name = surname + angler.peg_number = peg_number + angler.team = team + angler.section = section + + angler.save() + result["angler"] = { + "id": id, + "forename": forename, + "surname": surname, + "peg_number": peg_number, + "team_id": team_id, + "section_id": section_id, + "team_number": angler.team.number, + "section_character": angler.section.character + } + + return result + + def get_teams(self, request) -> dict[str]: + """Returns a dictionary of all teams.""" + + search = request.GET.get("search") + teams = Team.objects.order_by("number").all() + + # Search works by exluding teams that do not contain members with the search term in their names. + if search: + search_terms = search.split() + members = Member.objects.filter(reduce(lambda x, y: x & y, [ + Q(first_name__icontains=term) | Q(last_name__icontains=term) + for term in search_terms + ])) + teams = teams.filter(members__in=members).distinct() + + return {"teams": [{"id": team.id, "number": team.number} for team in teams]} + + def get_sections(self, request) -> dict[str]: + """Returns a dictionary of all sections.""" + + search = request.GET.get("search") + sections = Section.objects.order_by("character").all() + + if search: + search_terms = search.split() + members = Member.objects.filter(reduce(lambda x, y: x & y, [ + Q(first_name__icontains=term) | Q(last_name__icontains=term) + for term in search_terms + ])) + sections = sections.filter(members__in=members).distinct() + + return {"sections": [{"id": section.id, "character": section.character} for section in sections]} + + def get_anglers(self, request) -> dict[str]: + """Returns a dictionary of all anglers.""" + + search = request.GET.get("search") + anglers = Member.objects.order_by("first_name").all() + + if search: + search_terms = search.split() + anglers = anglers.filter(reduce(lambda x, y: x & y, [ + Q(first_name__icontains=term) | Q(last_name__icontains=term) + for term in search_terms + ])).distinct() + + return { + "anglers": [ + { + "id": angler.id, + "first_name": angler.first_name, + "last_name": angler.last_name, + "peg_number": angler.peg_number, + "team_id": angler.team.id, + "section_id": angler.section.id, + "team_number": angler.team.number, + "section_character": angler.section.character + } + for angler in anglers + ] + } + + + + + + + + + + + + + + + + + +def get_angler_page_data(request, **kwargs): """Returns a JsonResponse containing a dictionary with a k/v pair for a list of teams. Args: @@ -54,44 +296,44 @@ def get_teams(request, **kwargs): sort_groups = request.POST.get("sortGroups") or "team" sort_members = request.POST.get("sortMembers") or "peg_number" - group_object = Team if sort_groups == "team" else Section - groups = group_object.objects.order_by("name").all() + teams = Team.objects.order_by("number").all() + sections = Section.objects.order_by("character").all() - # Filter out teams that don't contain members being searched for if search: search_terms = search.split() members = Member.objects.filter( reduce( - lambda x, y: x | y, + lambda x, y: x & y, ## changed to AND from OR to fix bug with whitespace searches [ Q(first_name__icontains=term) | Q(last_name__icontains=term) for term in search_terms ] ) ) - groups = groups.filter(members__in=members).distinct() + teams = teams.filter(members__in=members).distinct() + sections = sections.filter(members__in=members).distinct() - if sort_groups == "section": - groups = sorted([group for group in groups], key=name_sort_key) - - # Create a dictionary for the data that is JSON safe - response_data = {"groups": []} - for group in groups: - group_data = { - "name": group.name, - "members": [ - { - "first": member.first_name, - "last": member.last_name, - "id": member.id, - "peg": member.peg_number, - "team": member.team.name if member.team else None, - "section": member.section.name if member.section else None - } - for member in group.members.order_by(sort_members).all() - ] - } - response_data["groups"].append(group_data) + response_data = { + "teams": [ + {"id": team.id, "number": team.number} + for team in teams + ], + "sections": [ + {"id": sec.id, "character": sec.character} + for sec in sections + ], + "anglers": [ + { + "id": member.id, + "first": member.first_name, + "last": member.last_name, + "peg": member.peg_number, + "team_id": member.team.id if member.team else None, + "section_id": member.section.id if member.section else None + } + for member in Member.objects.order_by(sort_members).all() + ] + } response_data["sortGroups"] = sort_groups response_data["sortMembers"] = sort_members @@ -101,6 +343,10 @@ def get_teams(request, **kwargs): return JsonResponse(response_data) + + + + def update_member(request): """Update a member. Returns a JsonResponse with the updated teams.""" @@ -126,7 +372,7 @@ def update_member(request): member.save() - return get_teams(request) + return get_angler_page_data(request) def update_section(request): """Update a section, returns JsonResponse with updated teams data.""" @@ -139,26 +385,26 @@ def update_section(request): validator = SectionValidator() if not validator.is_valid(section_name): - json_response = get_teams(request, form_errors={ + json_response = get_angler_page_data(request, form_errors={ "editSectionName": "This is an invalid section" }) return json_response if section_id == "-1": - section = Section(name=section_name) + section = Section(character=section_name) else: section = Section.objects.get(id=section_id) - section.name = section_name + section.character = section_name try: section.save() except IntegrityError: - json_response = get_teams(request, form_errors={ + json_response = get_angler_page_data(request, form_errors={ "editSectionName": "A Section with this character already exists" }) return json_response - return get_teams(request) # returns jsonresponse with new details + return get_angler_page_data(request) # returns jsonresponse with new details def update_team(request): """Update a team, returns a JsonResponse with updated teams data.""" @@ -171,18 +417,18 @@ def update_team(request): try: if team_id == "-1": - team = Team.objects.create(name=team_number) + team = Team.objects.create(number=team_number) else: team = Team.objects.get(id=team_id) - team.name = team_number + team.number = team_number team.save() except IntegrityError as error: - json_response = get_teams(request, form_errors={ + json_response = get_angler_page_data(request, form_errors={ "editTeamNumber": "A Team with this number already exists" }) return json_response - return get_teams(request) + return get_angler_page_data(request) def get_next_peg() -> int: pass diff --git a/src/static/css/mainapp/anglers.css b/src/static/css/mainapp/anglers.css new file mode 100644 index 0000000..e69de29 diff --git a/src/static/js/custom.js b/src/static/js/custom.js index c0aa691..f77b435 100644 --- a/src/static/js/custom.js +++ b/src/static/js/custom.js @@ -7,6 +7,8 @@ $(document).ready(() => { activateTooltips(); pageLoadMotionEffects(); + + $("select").select2({theme: "bootstrap-5", minimumResultsForSearch: -1}) }); @@ -46,4 +48,13 @@ function setMotionEffects(enable) { else { $(".fluid-hover-zoom").addClass("fluid-hover-zoom-off").removeClass("fluid-hover-zoom"); } -} \ No newline at end of file +} + +jQuery.expr[':'].icontains = function(a, i, m) { + return jQuery(a).text().toUpperCase() + .indexOf(m[3].toUpperCase()) >= 0; +}; + +const findByKey = (array, key, value) => { + return array.find(item => item[key] === value); +} diff --git a/src/static/js/mainapp/anglers.js b/src/static/js/mainapp/anglers.js new file mode 100644 index 0000000..10ad73b --- /dev/null +++ b/src/static/js/mainapp/anglers.js @@ -0,0 +1,199 @@ + +$(document).ready(() => { + + toggleLoading(true); + + // Search Bar Functionality + + searchTimeout = null; + + $("#search").on("input", () => { + clearTimeout(searchTimeout); + toggleLoading(true); + + searchTimeout = setTimeout(() => { + fetchAndLoadGroups(); + }, 500) + }); + + $("#searchButton").on("click", () => { + fetchAndLoadGroups(); + }); + + $("#sortForm input").on("click", function() { + const name = $(this).attr("name"); + localStorage.setItem(name, $(`input[name='${name}']:checked`, "#sortForm").val()); + + fetchAndLoadGroups(); + }); + + // Modals + + $("#addAngler").on("click", () => { + addAngler(-1); + }); + + $("#addTeam").on("click", () => { + addTeam(-1); + }); + + $("#addSection").on("click", () => { + addSection(-1); + }); + + fetchAndLoadGroups(); + +}); + +function fetchAndLoadGroups() { + toggleLoading(true); + + const showGroups = localStorage.getItem("showGroups"); + const sortAnglers = localStorage.getItem("sortAnglers"); + const task = showGroups === "teams" ? "get-teams" : "get-sections"; + + $.ajax({ + type: "POST", + url: pageUrl, + data: { + csrfmiddlewaretoken: csrfMiddlewareToken, + tasks: [task, "get-anglers"], + sortAnglers: sortAnglers, + search: $("#search").val() + }, + error: (error) => { + console.error(error); + }, + success: (data) => { + loadGroups(data); + } + }); + + toggleLoading(false); +} + +function groupTemplate(group, groupType, oppositeGroupType) { + groupValue = groupType === "teams" ? group.number : group.character + + return ` +
+
+

+ + ${groupType.toUpperCase()} + ${groupValue} + + +

+
    +
+
+
+ ` +} + +function anglerTemplate(angler, oppositeGroupType) { + const fullname = angler.first_name + " " + angler.last_name; + const oppositeGroupValue = oppositeGroupType === "teams"? angler.team_number : angler.section_character; + + var oppositeGroup = oppositeGroupType.slice(0, -1); + oppositeGroup = oppositeGroup.charAt(0).toUpperCase() + oppositeGroup.slice(1); + + return ` +
  • +
    +
    +
    + ${angler.peg_number} +
    +
    + ${oppositeGroupValue} +
    +
    + ${fullname} +
    +
    + +
    +
  • + ` +} + +function loadGroups(data) { + const showGroups = localStorage.getItem("showGroups"); + const groupType = showGroups === "teams" ? "teams" : "sections"; + const oppositeGroupType = showGroups === "teams" ? "sections" : "teams"; + const groups = data[groupType]; + + $("#groups").html(""); + + groups.forEach((group) => { + $("#groups").append(groupTemplate(group, groupType, oppositeGroupType)); + data.anglers.forEach((angler) => { + if (angler.team_id === group.id || angler.section_id === group.id) { + $("#groups").find(".anglers").last().append( + anglerTemplate(angler, oppositeGroupType) + ); + } + }); + }); + + activateTooltips(); +} + +/** + * Shows a loading icon, hides groups and error alerts. + * + * @param {Boolean} loading - Wether or not to show the loading icon + */ +function toggleLoading(loading) { + if (loading) { + $("#groups").hide(); + $("#notFound").hide(); + $("#loadingSpinner").show(); + } + else { + $("#groups").show(); + $("#loadingSpinner").hide(); + } +} + +/** + * Open the modal for adding/editing an angler + * + * @param {Number} anglerId - ID of the angler, if -1 will create a new angler + */ +function addAngler(anglerId) { + $("#anglerModal").modal("show"); +} + +/** + * Open the modal for adding/editing a team + * + * @param {Number} teamId - ID of the team, if -1 will create a new team + */ +function addTeam(teamId) { + $("#teamModal").modal("show"); +} + +/** + * Open the modal for adding/editing a section + * + * @param {Number} sectionId - ID of the section, if -1 will create a new section + */ +function addSection(sectionId) { + $("#sectionModal").modal("show"); +} diff --git a/src/static/js/teams.js b/src/static/js/teams.js index 2cc6795..5470f44 100644 --- a/src/static/js/teams.js +++ b/src/static/js/teams.js @@ -3,6 +3,14 @@ jQuery.expr[':'].icontains = function(a, i, m) { .indexOf(m[3].toUpperCase()) >= 0; }; +const findByKey = (array, key, value) => { + return array.find(item => item[key] === value); +} + +var globalTeamsList = []; +var globalSectionsList = []; +var globalAnglersList = []; + $(document).ready(() => { teamsLoading(true); // show the loading icon @@ -30,7 +38,7 @@ $(document).ready(() => { const name = $(this).attr("name"); localStorage.setItem(name, $(`input[name='${name}']:checked`, "#sortForm").val()); - fetchAndLoadTeams(...getFilters()); + fetchAndLoadPageData(); }); // Load the last saved sort settings TODO: use local storage so it only saves on one reload @@ -93,7 +101,9 @@ $(document).ready(() => { $("select").select2({theme: "bootstrap-5", minimumResultsForSearch: -1}) // load the teams with default filters - fetchAndLoadTeams(...getFilters()); + fetchAndLoadPageData(); + + // fetchAndLoadTeams(...getFilters()); }); @@ -121,10 +131,12 @@ function loadTeams(groups, highlightText="", groupType) { // Iterate over and add each team groups.forEach((group) => { + const oppositeGroupType = groupType === "SECTION"? "Team" : "Section"; + $("#teamsContainer").append( `

    @@ -144,7 +156,7 @@ function loadTeams(groups, highlightText="", groupType) { // 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( @@ -204,19 +216,6 @@ function loadTeams(groups, highlightText="", groupType) { }); openEditMemberModal($(this).parent().data("member-id")); }); - - // Bind new member/team buttons - $("#addTeam").on("click", () => { - openEditTeamModal(-1); - }); - - $("#addMember").on("click", () => { - openEditMemberModal(-1); - }); - - $("#addSection").on("click", () => { - openEditSectionModal(-1); - }); } function fetchAndLoadTeams(search="", sortGroups="", sortMembers="") { @@ -237,16 +236,17 @@ function fetchAndLoadTeams(search="", sortGroups="", sortMembers="") { }); } + function teamsLoading(show=true) { $("#teamsNotFound").hide() if (show) { - $("#teamsContainer").hide(); + $("#groupsContainer").hide(); $("#teamsLoadingSpinner").show(); return } - $("#teamsContainer").show(); + $("#groupsContainer").show(); $("#teamsLoadingSpinner").hide(); } @@ -267,32 +267,38 @@ function openEditSectionModal(sectionId) { teamsLoading(true); const [search, sortGroups, sortMembers] = getFilters(); + const getGroupTask = sortGroups === "teams" ? "get-teams": "get-sections" + const loadFunction = sortGroups === "teams" ? displayTeams : displaySections; + $.ajax({ - url: updateSectionUrl, + url: pageUrl, type: "post", data: { "csrfmiddlewaretoken": csrfMiddlewareToken, - "sectionId": sectionId, - "sectionName": $("#editSectionName").val(), + "tasks": ["update-section", getGroupTask, "get-anglers"], + "id": sectionId, + "character": $("#editSectionName").val().toUpperCase(), "search": search, - "sortGroups": sortGroups, - "sortMembers": sortMembers }, error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); }, success: (result) => { - teamsLoading(false); - loadTeams(result.groups, search, result.sortGroups); + if (Object.keys(result.form_errors).length > 0) { + for (const errorId in result.form_errors) { + $(errorId).text(result.form_errors[errorId]).show(); + } + return; + } - if (!result.form_errors) { - $("#editSectionModal").modal("hide"); - } - else { - $("#editSectionName").focus(); - $("#editSectionNameError").text(result.form_errors.editSectionName).show(); - } + alert(JSON.stringify(result)); + alert(sortGroups); + loadFunction(result[sortGroups], result.anglers); + + $("#editSectionModal").modal("hide"); } }); + + teamsLoading(false); }); } @@ -310,27 +316,28 @@ function openEditTeamModal(teamId) { type: "post", data: { "csrfmiddlewaretoken": csrfMiddlewareToken, - "teamId": teamId, - "teamNumber": $("#editTeamNumber").val(), + "tasks": ["update-team"], + "id": teamId, + "number": $("#editTeamNumber").val(), "search": search, - "sortGroups": sortGroups, - "sortMembers": sortMembers }, error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); }, success: (result) => { - teamsLoading(false); - loadTeams(result.groups, search, result.sortGroups); + if (Object.keys(result.form_errors).length > 0) { + alert(JSON.stringify(result)); + for (const errorId in result.form_errors) { + $(errorId).focus(); + $(errorId).text(result.form_errors[errorId]).show(); + } + return; + } - if (!result.form_errors) { - $("#editTeamModal").modal("hide"); - } - else { - $("#editTeamNumber").focus(); - $("#editTeamNumberError").text(result.form_errors.editTeamNumber).show(); - } + $("#editTeamModal").modal("hide"); } }); + + teamsLoading(false); }); } @@ -356,13 +363,17 @@ function openEditMemberModal(memberId) { // modalTitle = "Edit: " + first + " " + last // } - // // Load teams as options - // $("#editMemberTeam").html(""); - // $(".team").map(function() { - // return $(this).data("number"); - // }).get().forEach((team) => { - // $("#editMemberTeam").append($(``)); - // }); + // Load teams as options + $("#editMemberTeam").html(""); + globalTeamsList.forEach((team) => { + $("#editMemberTeam").append(``); + }); + + + $("#editMemberSection").html(""); + globalSectionsList.forEach((section) => { + $("#editMemberSection").append(``); + }); // // Load data to form // $("#editMemberName").text(modalTitle); @@ -382,6 +393,7 @@ function openEditMemberModal(memberId) { }); } + function saveEditMemberModal(memberId) { if (!$("#editMemberForm").valid()) { @@ -416,4 +428,161 @@ function saveEditMemberModal(memberId) { $("#editMemberModal").modal("hide"); } }); +} + + + + + + + + + + +// REWRITE BEYOND THIS POINT + +$(document).ready(() => { + + + // Bind new member/team buttons + $("#addTeam").on("click", () => { + openEditTeamModal(-1); + }); + + $("#addMember").on("click", () => { + openEditMemberModal(-1); + }); + + $("#addSection").on("click", () => { + openEditSectionModal(-1); + }); +}); + +function fetchAndLoadPageData() { + + teamsLoading(true); + + const [search, sortGroups, sortMembers] = getFilters(); + + $.ajax({ + url: getPageDataUrl, + type: "post", + data: { + "csrfmiddlewaretoken": csrfMiddlewareToken, + "search": search, + "sortGroups": sortGroups, + "sortMembers": sortMembers + }, + error: (xhr, textStatus, errorThrown) => { + alert(xhr + " " + textStatus + " " + errorThrown); + }, + success: (result) => { + loadPageData(result.teams, result.sections, result.anglers, result.sortGroups); + } + }); + + teamsLoading(false); +} + +function loadPageData(teams, sections, anglers, displayGroups) { + + $("#groupsContainer").html(""); + + globalTeamsList = teams; + globalSectionsList = sections; + globalAnglersList = anglers; + + if (displayGroups === "section") { + // displaySections(); + } + else if (displayGroups === "team") { + displayTeams(); + } + else { + throw new Error(`invalid display groups '${displayGroups}'`); + } + + activateTooltips(); +} + + +function displaySections(sections, anglers) { + + sections.forEach((section) => { + $("#groupsContainer").append(getGroupTemplate(section.id, "section", section.character)); + + anglers.forEach((angler) => { + if (angler.section_id != section.id) { return; } + + const team = findByKey(globalTeamsList, "id", angler.team_id).number; + $("#groupsContainer").find(".anglers").last().append(getAnglerTemplate(angler, "Team", team)); + }); + }); +} + +function displayTeams() { + + globalTeamsList.forEach((team) => { + $("#groupsContainer").append(getGroupTemplate(team.id, "team", team.number)); + + globalAnglersList.forEach((angler) => { + if (angler.team_id != team.id) { return; } + + const section = findByKey(globalSectionsList, "id", angler.section_id).character; + $("#groupsContainer").find(".anglers").last().append(getAnglerTemplate(angler, "Section", section)); + }); + }); +} + +function getGroupTemplate(groupId, groupType, groupName) { + return` +
    +
    +

    + + ${groupType.toUpperCase()} + ${groupName} + + +

    +
      +
    +
    +
    + `; +} + +function getAnglerTemplate(angler, oppositeGroupType, oppositeGroupName) { + const fullname = angler.first + " " + angler.last; + return ` +
  • +
    +
    +
    + ${angler.peg} +
    +
    + ${oppositeGroupName} +
    +
    + ${fullname} +
    +
    + +
    +
  • + `; } \ No newline at end of file