You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
13 KiB
359 lines
13 KiB
package templates
|
|
|
|
import (
|
|
"fmt"
|
|
"track-gopher/models"
|
|
)
|
|
|
|
templ Admin(groups []models.Group, racers []models.Racer) {
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Derby Race Admin</title>
|
|
<!-- Bootstrap CSS -->
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<script src="/static/js/htmx.min.js"></script>
|
|
<!-- Bootstrap JS Bundle with Popper -->
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
</head>
|
|
<body class="bg-light">
|
|
<div class="container py-4">
|
|
<header class="mb-4">
|
|
<h1 class="text-center">Derby Race Admin</h1>
|
|
<nav class="nav nav-pills nav-fill mt-3">
|
|
<a class="nav-link active" data-bs-toggle="tab" href="#groups">Groups</a>
|
|
<a class="nav-link" data-bs-toggle="tab" href="#racers">Racers</a>
|
|
<a class="nav-link" href="/">Race Timer</a>
|
|
<a class="nav-link" href="/register">Racer Registration</a>
|
|
<a class="nav-link" href="/heats">Race Heats</a>
|
|
</nav>
|
|
</header>
|
|
|
|
<div class="tab-content">
|
|
<div class="tab-pane fade show active" id="groups">
|
|
<div class="card mb-4">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">Groups</h5>
|
|
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addGroupModal">
|
|
Add Group
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Description</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
for _, group := range groups {
|
|
<tr>
|
|
<td>{ group.Name }</td>
|
|
<td>{ group.Description }</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary me-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#editGroupModal"
|
|
data-id={ fmt.Sprint(group.ID) }
|
|
data-name={ group.Name }
|
|
data-description={ group.Description }>
|
|
Edit
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#deleteGroupModal"
|
|
data-id={ fmt.Sprint(group.ID) }
|
|
data-name={ group.Name }>
|
|
Delete
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="racers">
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">Racers</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Car #</th>
|
|
<th>Weight</th>
|
|
<th>Group</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
for _, racer := range racers {
|
|
<tr>
|
|
<td>{ racer.FirstName } { racer.LastName }</td>
|
|
<td>{ racer.CarNumber }</td>
|
|
<td>{ fmt.Sprintf("%.1f", racer.CarWeight) } oz</td>
|
|
<td>{ racer.GroupName }</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary me-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#editRacerModal"
|
|
data-id={ fmt.Sprint(racer.ID) }
|
|
data-firstname={ racer.FirstName }
|
|
data-lastname={ racer.LastName }
|
|
data-carnumber={ racer.CarNumber }
|
|
data-carweight={ fmt.Sprintf("%.1f", racer.CarWeight) }
|
|
data-groupid={ fmt.Sprint(racer.GroupID) }>
|
|
Edit
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#deleteRacerModal"
|
|
data-id={ fmt.Sprint(racer.ID) }
|
|
data-name={ racer.FirstName + " " + racer.LastName }>
|
|
Delete
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Group Modal -->
|
|
<div class="modal fade" id="addGroupModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add Group</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form hx-post="/api/groups" hx-swap="none" hx-on::after-request="location.reload()">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="groupName" class="form-label">Name</label>
|
|
<input type="text" class="form-control" id="groupName" name="name" required/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="groupDescription" class="form-label">Description</label>
|
|
<textarea class="form-control" id="groupDescription" name="description" rows="3"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Group Modal -->
|
|
<div class="modal fade" id="editGroupModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit Group</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="editGroupForm" hx-put="/api/groups/0" hx-swap="none" hx-on::after-request="location.reload()">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="editGroupName" class="form-label">Name</label>
|
|
<input type="text" class="form-control" id="editGroupName" name="name" required/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="editGroupDescription" class="form-label">Description</label>
|
|
<textarea class="form-control" id="editGroupDescription" name="description" rows="3"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Group Modal -->
|
|
<div class="modal fade" id="deleteGroupModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Delete Group</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Are you sure you want to delete the group "<span id="deleteGroupName"></span>"?</p>
|
|
<p class="text-danger">This will also delete all racers in this group!</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" id="confirmDeleteGroup" class="btn btn-danger">Delete</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Racer Modal -->
|
|
<div class="modal fade" id="editRacerModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit Racer</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="editRacerForm" hx-put="/api/racers/0" hx-swap="none" hx-on::after-request="location.reload()">
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="editFirstName" class="form-label">First Name</label>
|
|
<input type="text" class="form-control" id="editFirstName" name="first_name" required/>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="editLastName" class="form-label">Last Name</label>
|
|
<input type="text" class="form-control" id="editLastName" name="last_name" required/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="editCarNumber" class="form-label">Car Number</label>
|
|
<input type="text" class="form-control" id="editCarNumber" name="car_number" required/>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="editCarWeight" class="form-label">Car Weight (oz)</label>
|
|
<input type="number" step="0.1" class="form-control" id="editCarWeight" name="car_weight" required/>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="editGroupID" class="form-label">Group</label>
|
|
<select class="form-select" id="editGroupID" name="group_id" required>
|
|
for _, group := range groups {
|
|
<option value={ fmt.Sprint(group.ID) }>{ group.Name }</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Racer Modal -->
|
|
<div class="modal fade" id="deleteRacerModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Delete Racer</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Are you sure you want to delete the racer "<span id="deleteRacerName"></span>"?</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" id="confirmDeleteRacer" class="btn btn-danger">Delete</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Handle edit group modal
|
|
document.getElementById('editGroupModal').addEventListener('show.bs.modal', function (event) {
|
|
const button = event.relatedTarget;
|
|
const id = button.getAttribute('data-id');
|
|
const name = button.getAttribute('data-name');
|
|
const description = button.getAttribute('data-description');
|
|
|
|
const form = document.getElementById('editGroupForm');
|
|
form.setAttribute('hx-put', `/api/groups/${id}`);
|
|
|
|
document.getElementById('editGroupName').value = name;
|
|
document.getElementById('editGroupDescription').value = description;
|
|
});
|
|
|
|
// Handle delete group modal
|
|
document.getElementById('deleteGroupModal').addEventListener('show.bs.modal', function (event) {
|
|
const button = event.relatedTarget;
|
|
const id = button.getAttribute('data-id');
|
|
const name = button.getAttribute('data-name');
|
|
|
|
document.getElementById('deleteGroupName').textContent = name;
|
|
|
|
document.getElementById('confirmDeleteGroup').onclick = function() {
|
|
fetch(`/api/groups/${id}`, { method: 'DELETE' })
|
|
.then(response => {
|
|
if (response.ok) {
|
|
location.reload();
|
|
} else {
|
|
alert('Failed to delete group');
|
|
}
|
|
});
|
|
};
|
|
});
|
|
|
|
// Handle edit racer modal
|
|
document.getElementById('editRacerModal').addEventListener('show.bs.modal', function (event) {
|
|
const button = event.relatedTarget;
|
|
const id = button.getAttribute('data-id');
|
|
const firstName = button.getAttribute('data-firstname');
|
|
const lastName = button.getAttribute('data-lastname');
|
|
const carNumber = button.getAttribute('data-carnumber');
|
|
const carWeight = button.getAttribute('data-carweight');
|
|
const groupId = button.getAttribute('data-groupid');
|
|
|
|
const form = document.getElementById('editRacerForm');
|
|
form.setAttribute('hx-put', `/api/racers/${id}`);
|
|
|
|
document.getElementById('editFirstName').value = firstName;
|
|
document.getElementById('editLastName').value = lastName;
|
|
document.getElementById('editCarNumber').value = carNumber;
|
|
document.getElementById('editCarWeight').value = carWeight;
|
|
document.getElementById('editGroupID').value = groupId;
|
|
});
|
|
|
|
// Handle delete racer modal
|
|
document.getElementById('deleteRacerModal').addEventListener('show.bs.modal', function (event) {
|
|
const button = event.relatedTarget;
|
|
const id = button.getAttribute('data-id');
|
|
const name = button.getAttribute('data-name');
|
|
|
|
document.getElementById('deleteRacerName').textContent = name;
|
|
|
|
document.getElementById('confirmDeleteRacer').onclick = function() {
|
|
fetch(`/api/racers/${id}`, { method: 'DELETE' })
|
|
.then(response => {
|
|
if (response.ok) {
|
|
location.reload();
|
|
} else {
|
|
alert('Failed to delete racer');
|
|
}
|
|
});
|
|
};
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
} |