Add templates/activityCreateModel.html

This commit is contained in:
Ghassan Yusuf 2025-10-03 18:48:18 +03:00
parent fe38dfb1c0
commit 577e550130

View 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>