Rebuild team/group bulk add as a searchable row formset
The old page gave every eligible member a table row, which a club with a hundred-plus members can't use, and its search was a GET round-trip that discarded anything already ticked. Now you add rows: pick a person from a searchable select, pick a position, fill in the details. The position decides the role. Position.staff_position already distinguishes them, so a staff position creates a StaffAssignment and anything else a TeamMembership -- no separate "player or staff?" control that could disagree with the position picked. Someone joining as both is two rows. Jersey number and captaincy exist only on TeamMembership, so a staff row rejects them and the row script greys them out, keyed off the data-staff marker PositionSelect stamps on staff options. All-or-nothing on submit: one bad row re-renders the page with every row still filled in and the offending field flagged, rather than saving the good rows and losing the rest -- a partial save costs far more when the rows were typed by hand. Cross-row checks no single row can see (the same person twice, two rows claiming one jersey) live on the formset's clean(); per-row checks live on the row form. Eligibility is still never trusted from the POST. Captain and alternate captain on one row is refused as self-contradictory, but how many captains a team may have is deliberately left alone: neither the model nor the single-add form constrains it, and inventing the rule in one entry path only would be bypassable by adding players one at a time. A test pins that absence so it reads as a decision rather than an oversight. Two implementation notes worth keeping: rows are cloned from the template's parsed content, not its innerHTML, because assigning "<tr>...</tr>" to a detached <div> silently drops it; and these tables deliberately skip the usual overflow-x-auto wrapper, which would make a scroll container that clips the picker's dropdown for every row but the first couple. Removing a row leaves TOTAL_FORMS alone -- Django reads a form whose fields are absent from the POST as an unchanged extra and skips it, which is safe, unlike re-indexing live inputs. management/tests.py also carries the setUpTestData rationalisation from the previous commit, which couldn't be split cleanly from the new bulk-add tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3572,6 +3572,12 @@
|
||||
.w-20 {
|
||||
width: calc(var(--spacing) * 20);
|
||||
}
|
||||
.w-24 {
|
||||
width: calc(var(--spacing) * 24);
|
||||
}
|
||||
.w-28 {
|
||||
width: calc(var(--spacing) * 28);
|
||||
}
|
||||
.w-60 {
|
||||
width: calc(var(--spacing) * 60);
|
||||
}
|
||||
@@ -4528,6 +4534,31 @@
|
||||
padding-inline: calc(var(--spacing) * 6);
|
||||
}
|
||||
}
|
||||
.md\:col-span-2 {
|
||||
@media (width >= 48rem) {
|
||||
grid-column: span 2 / span 2;
|
||||
}
|
||||
}
|
||||
.md\:col-span-4 {
|
||||
@media (width >= 48rem) {
|
||||
grid-column: span 4 / span 4;
|
||||
}
|
||||
}
|
||||
.md\:mb-2 {
|
||||
@media (width >= 48rem) {
|
||||
margin-bottom: calc(var(--spacing) * 2);
|
||||
}
|
||||
}
|
||||
.md\:grid {
|
||||
@media (width >= 48rem) {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
.md\:hidden {
|
||||
@media (width >= 48rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.md\:grid-cols-2 {
|
||||
@media (width >= 48rem) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@@ -4548,6 +4579,26 @@
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
.md\:grid-cols-\[minmax\(0\,1fr\)_2\.5rem\] {
|
||||
@media (width >= 48rem) {
|
||||
grid-template-columns: minmax(0,1fr) 2.5rem;
|
||||
}
|
||||
}
|
||||
.md\:grid-cols-\[minmax\(0\,2fr\)_minmax\(0\,2fr\)_7rem_2\.5rem\] {
|
||||
@media (width >= 48rem) {
|
||||
grid-template-columns: minmax(0,2fr) minmax(0,2fr) 7rem 2.5rem;
|
||||
}
|
||||
}
|
||||
.md\:items-start {
|
||||
@media (width >= 48rem) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
.md\:justify-center {
|
||||
@media (width >= 48rem) {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.lg\:block {
|
||||
@media (width >= 64rem) {
|
||||
display: block;
|
||||
|
||||
Reference in New Issue
Block a user