Add templates/activityCreateModel.html
This commit is contained in:
parent
fe38dfb1c0
commit
577e550130
416
templates/activityCreateModel.html
Normal file
416
templates/activityCreateModel.html
Normal file
@ -0,0 +1,416 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Create Activities Modal (Final)</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
|
||||
<style>
|
||||
textarea.form-control {
|
||||
resize: vertical;
|
||||
}
|
||||
.btn-add-row {
|
||||
background-color: #6c757d;
|
||||
border-color: #6c757d;
|
||||
}
|
||||
.btn-add-row:hover {
|
||||
background-color: #5a6268;
|
||||
border-color: #545b62;
|
||||
}
|
||||
.form-check-label-toggle {
|
||||
font-weight: bold;
|
||||
margin-left: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.form-check-label-toggle i {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
/* Custom CSS to make the Enable switch green (success) when checked */
|
||||
#enableToggle:checked {
|
||||
background-color: #198754;
|
||||
border-color: #198754;
|
||||
}
|
||||
#enableToggle:checked:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
|
||||
}
|
||||
/* Style for schedule container when empty and invalid */
|
||||
.invalid-schedule {
|
||||
border: 1px solid var(--bs-danger);
|
||||
border-radius: var(--bs-border-radius);
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container py-5">
|
||||
<h1>Bootstrap Modal Preview</h1>
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createActivitiesModal">
|
||||
Open 'Create Activities' Modal
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
<div id="validationToast" class="toast text-bg-danger" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header text-bg-danger">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<strong class="me-auto">Validation Error</strong>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body text-white" id="toastMessage">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="createActivitiesModal" tabindex="-1" aria-labelledby="createActivitiesModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="createActivitiesModalLabel">
|
||||
<i class="bi bi-hand-index-thumb me-2"></i> Create Sports Activities
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body pb-3">
|
||||
<form id="activityForm" onsubmit="return validateForm(event)">
|
||||
|
||||
<div class="row mb-4 d-flex align-items-center">
|
||||
|
||||
<div class="col-4">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="durationToggle" checked>
|
||||
<label class="form-check-label form-check-label-toggle" for="durationToggle">
|
||||
<i class="bi bi-calendar-event"></i> Duration
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="sessionsToggle">
|
||||
<label class="form-check-label form-check-label-toggle" for="sessionsToggle">
|
||||
<i class="bi bi-ticket"></i> <span id="sessionableLabelText">Session</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="enableToggle" checked>
|
||||
<label class="form-check-label form-check-label-toggle" for="enableToggle">
|
||||
Enable
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="sessionsInputContainer" class="d-none">
|
||||
<div class="mb-3">
|
||||
<label for="countSessions" class="form-label">Count Sessions</label>
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="countSessions" placeholder="Count" min="1">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="#">1</a></li>
|
||||
<li><a class="dropdown-item" href="#">5</a></li>
|
||||
<li><a class="dropdown-item" href="#">10</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="activityTitle" class="form-label">Title</label>
|
||||
<input type="text" class="form-control" id="activityTitle" placeholder="Title">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="facilitiesSelect" class="form-label">Facility</label>
|
||||
<select class="form-select" id="facilitiesSelect">
|
||||
<option value="" selected disabled>Select facility</option>
|
||||
<option value="1">Gym</option>
|
||||
<option value="2">Pool</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="maxCapacityInput" class="form-label">Max Capacity</label>
|
||||
<input type="number" class="form-control" id="maxCapacityInput" placeholder="Capacity">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="trainerSelect" class="form-label">Trainer</label>
|
||||
<select class="form-select" id="trainerSelect">
|
||||
<option selected>Select Trainer</option>
|
||||
<option value="1">John Doe</option>
|
||||
<option value="2">Jane Smith</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label for="activityNotes" class="form-label">Notes</label>
|
||||
<textarea class="form-control" id="activityNotes" placeholder="Notes..." rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<label for="packageImage" class="form-label">Package Image</label>
|
||||
<div class="input-group">
|
||||
<input type="file" class="form-control d-none" id="packageImage">
|
||||
<button class="btn btn-outline-secondary rounded-end-0" type="button" onclick="document.getElementById('packageImage').click()">Browse...</button>
|
||||
<input type="text" class="form-control rounded-start-0" value="No file selected." readonly id="fileNameDisplay">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="mt-4 mb-3">Activity Schedule</h6>
|
||||
|
||||
<div id="scheduleHeaders" class="row fw-bold small text-secondary mb-1 d-none">
|
||||
<div class="col-4">Day</div>
|
||||
<div class="col-3">Start Time</div>
|
||||
<div class="col-3">End Time</div>
|
||||
<div class="col-2"></div>
|
||||
</div>
|
||||
|
||||
<div id="scheduleContainer" class="pb-2">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer d-flex justify-content-between border-0">
|
||||
<button type="button" class="btn btn-primary btn-add-row" onclick="addRow()">
|
||||
<i class="bi bi-plus"></i> Add Schedule
|
||||
</button>
|
||||
|
||||
<button type="submit" class="btn btn-primary" form="activityForm">Create</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Global element references
|
||||
const scheduleContainer = document.getElementById('scheduleContainer');
|
||||
const scheduleHeaders = document.getElementById('scheduleHeaders');
|
||||
const durationToggle = document.getElementById('durationToggle');
|
||||
const sessionsToggle = document.getElementById('sessionsToggle');
|
||||
const sessionsInput = document.getElementById('sessionsInputContainer');
|
||||
const countSessionsInput = document.getElementById('countSessions');
|
||||
const facilitiesSelect = document.getElementById('facilitiesSelect');
|
||||
const validationToast = new bootstrap.Toast(document.getElementById('validationToast'));
|
||||
const toastMessageElement = document.getElementById('toastMessage');
|
||||
|
||||
// Helper function to apply/remove red border
|
||||
function setInvalid(element, isInvalid) {
|
||||
if (isInvalid) {
|
||||
element.classList.add('is-invalid');
|
||||
// Special case for schedule container, apply custom border class
|
||||
if(element.id === 'scheduleContainer') {
|
||||
element.classList.add('invalid-schedule');
|
||||
}
|
||||
} else {
|
||||
element.classList.remove('is-invalid');
|
||||
if(element.id === 'scheduleContainer') {
|
||||
element.classList.remove('invalid-schedule');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// JS for file name display
|
||||
document.getElementById('packageImage').addEventListener('change', function() {
|
||||
var fileName = this.files.length > 0 ? this.files[0].name : 'No file selected.';
|
||||
document.getElementById('fileNameDisplay').value = fileName;
|
||||
});
|
||||
|
||||
function updateHeaderVisibility() {
|
||||
if (scheduleContainer.children.length > 0) {
|
||||
scheduleHeaders.classList.remove('d-none');
|
||||
setInvalid(scheduleContainer, false); // Clear schedule error on add/remove
|
||||
} else {
|
||||
scheduleHeaders.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
function updateSessionsInputVisibility() {
|
||||
if (sessionsToggle.checked) {
|
||||
sessionsInput.classList.remove('d-none');
|
||||
// Use the 'required' attribute for browser feedback, though JS handles the main logic
|
||||
countSessionsInput.setAttribute('required', 'required');
|
||||
} else {
|
||||
sessionsInput.classList.add('d-none');
|
||||
countSessionsInput.removeAttribute('required');
|
||||
// Clear any lingering red border if switch is turned off
|
||||
setInvalid(countSessionsInput, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce that at least one switch (Duration or Session) is always ON
|
||||
function enforceMinOneSwitch(event) {
|
||||
if (!durationToggle.checked && !sessionsToggle.checked) {
|
||||
if (event.target.id === 'durationToggle') {
|
||||
durationToggle.checked = true;
|
||||
} else if (event.target.id === 'sessionsToggle') {
|
||||
sessionsToggle.checked = true;
|
||||
}
|
||||
}
|
||||
updateSessionsInputVisibility();
|
||||
}
|
||||
|
||||
// ⭐ NEW: Form Validation Function with Highlighting and Toast
|
||||
function validateForm(event) {
|
||||
event.preventDefault(); // Prevent default submission first
|
||||
let isValid = true;
|
||||
let messages = [];
|
||||
|
||||
// Reset all error states
|
||||
setInvalid(countSessionsInput, false);
|
||||
setInvalid(facilitiesSelect, false);
|
||||
setInvalid(scheduleContainer, false);
|
||||
|
||||
// 1. Validate Session Count if Session is ON
|
||||
if (sessionsToggle.checked) {
|
||||
const count = parseInt(countSessionsInput.value);
|
||||
if (isNaN(count) || count <= 0) {
|
||||
isValid = false;
|
||||
messages.push("If 'Session' is enabled, 'Count Sessions' must be greater than 0.");
|
||||
setInvalid(countSessionsInput, true);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Validate Facility Selection
|
||||
if (facilitiesSelect.value === "") {
|
||||
isValid = false;
|
||||
messages.push("You must select a Facility.");
|
||||
setInvalid(facilitiesSelect, true);
|
||||
}
|
||||
|
||||
// 3. Validate Schedule (At least one row)
|
||||
if (scheduleContainer.children.length === 0) {
|
||||
isValid = false;
|
||||
messages.push("You must add at least one schedule row for the activity.");
|
||||
setInvalid(scheduleContainer, true);
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
// Show toast message with list of errors
|
||||
toastMessageElement.innerHTML = messages.map(msg => `• ${msg}`).join('<br>');
|
||||
validationToast.show();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If true, we would submit the form here (return true)
|
||||
// For demo purposes, we will just show a success alert or log:
|
||||
alert("Form is valid! Submitting...");
|
||||
// return true; // Uncomment this line to allow actual form submission
|
||||
|
||||
return false; // Keep it false for the demo to prevent page reload
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Set initial states
|
||||
document.getElementById('enableToggle').checked = true;
|
||||
durationToggle.checked = true;
|
||||
sessionsToggle.checked = false;
|
||||
|
||||
// Attach the switch constraint handler to both switches
|
||||
durationToggle.addEventListener('change', enforceMinOneSwitch);
|
||||
sessionsToggle.addEventListener('change', enforceMinOneSwitch);
|
||||
|
||||
// Attach listeners to clear red borders on interaction
|
||||
countSessionsInput.addEventListener('input', () => {
|
||||
const count = parseInt(countSessionsInput.value);
|
||||
if (count > 0) setInvalid(countSessionsInput, false);
|
||||
});
|
||||
facilitiesSelect.addEventListener('change', () => {
|
||||
if (facilitiesSelect.value !== "") setInvalid(facilitiesSelect, false);
|
||||
});
|
||||
|
||||
// Call update functions on load
|
||||
updateSessionsInputVisibility();
|
||||
updateHeaderVisibility();
|
||||
});
|
||||
|
||||
|
||||
// JS for Dynamic Schedule Rows
|
||||
let rowCount = 0;
|
||||
const daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
||||
|
||||
function addRow() {
|
||||
rowCount++;
|
||||
|
||||
const newRow = document.createElement('div');
|
||||
newRow.classList.add('row', 'mb-3', 'align-items-center');
|
||||
newRow.id = 'scheduleRow_' + rowCount;
|
||||
|
||||
// Column 1: Day Dropdown (col-4)
|
||||
const dayCol = document.createElement('div');
|
||||
dayCol.classList.add('col-4');
|
||||
dayCol.innerHTML = `
|
||||
<select class="form-select" name="day_${rowCount}" required>
|
||||
<option value="" selected disabled>Select Day</option>
|
||||
${daysOfWeek.map(day => `<option value="${day}">${day}</option>`).join('')}
|
||||
</select>
|
||||
`;
|
||||
|
||||
// Column 2: Start Time Input (col-3)
|
||||
const startTimeCol = document.createElement('div');
|
||||
startTimeCol.classList.add('col-3');
|
||||
startTimeCol.innerHTML = `
|
||||
<input type="time" class="form-control" name="start_time_${rowCount}" required>
|
||||
`;
|
||||
|
||||
// Column 3: End Time Input (col-3)
|
||||
const endTimeCol = document.createElement('div');
|
||||
endTimeCol.classList.add('col-3');
|
||||
endTimeCol.innerHTML = `
|
||||
<input type="time" class="form-control" name="end_time_${rowCount}" required>
|
||||
`;
|
||||
|
||||
// Column 4: Remove Button (col-2)
|
||||
const removeCol = document.createElement('div');
|
||||
removeCol.classList.add('col-2');
|
||||
removeCol.innerHTML = `
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick="removeRow('scheduleRow_${rowCount}')">
|
||||
<i class="bi bi-x"></i>
|
||||
</button>
|
||||
`;
|
||||
|
||||
newRow.appendChild(dayCol);
|
||||
newRow.appendChild(startTimeCol);
|
||||
newRow.appendChild(endTimeCol);
|
||||
newRow.appendChild(removeCol);
|
||||
|
||||
scheduleContainer.appendChild(newRow);
|
||||
|
||||
// Update visibility and clear schedule error
|
||||
updateHeaderVisibility();
|
||||
}
|
||||
|
||||
function removeRow(rowId) {
|
||||
const rowToRemove = document.getElementById(rowId);
|
||||
if (rowToRemove) {
|
||||
rowToRemove.remove();
|
||||
}
|
||||
// Update visibility and check if schedule is now empty
|
||||
updateHeaderVisibility();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user