assets_js_controllers_base-gathering-form-controller.js
import { Controller } from "@hotwired/stimulus";
/**
* Base Gathering Form Controller
*
* Provides shared date validation logic for gathering forms.
* Extended by gathering-form-controller and gathering-clone-controller.
*
* Features:
* - Automatically defaults end date to start date when start date changes
* - Validates that end date is not before start date
* - Provides real-time feedback to users
*/
export class BaseGatheringFormController extends Controller {
// Define targets - elements this controller interacts with
static targets = ["startDate", "endDate", "submitButton"]
/**
* Connect function - runs when controller connects to DOM
*/
connect() {
// Set up initial validation when page loads
if (this.hasStartDateTarget && this.hasEndDateTarget) {
this.validateDates();
}
}
/**
* Handle start date changes
* Automatically updates end date to match start date if end date is empty or before start date
*/
startDateChanged(event) {
const startDate = this.startDateTarget.value;
const endDate = this.endDateTarget.value;
// If end date is empty or before start date, set it to start date
if (!endDate || endDate < startDate) {
this.endDateTarget.value = startDate;
}
// Validate dates
this.validateDates();
}
/**
* Handle end date changes
* Validates that end date is not before start date
*/
endDateChanged(event) {
this.validateDates();
}
/**
* Validate dates
* Ensures end date is on or after start date
*/
validateDates() {
if (!this.hasStartDateTarget || !this.hasEndDateTarget) {
return true;
}
const startDate = this.startDateTarget.value;
const endDate = this.endDateTarget.value;
// Clear any previous validation messages
this.clearValidationMessages();
if (startDate && endDate && endDate < startDate) {
// End date is before start date - show error
this.showValidationError(
this.endDateTarget,
'End date cannot be before start date'
);
// Disable submit button
if (this.hasSubmitButtonTarget) {
this.submitButtonTarget.disabled = true;
}
return false;
} else {
// Dates are valid - enable submit button
if (this.hasSubmitButtonTarget) {
this.submitButtonTarget.disabled = false;
}
return true;
}
}
/**
* Validate form before submission
*/
validateForm(event) {
if (!this.validateDates()) {
event.preventDefault();
return false;
}
return true;
}
/**
* Show validation error message
*/
showValidationError(element, message) {
// Add invalid class to element
element.classList.add('is-invalid');
// Create or update feedback element
let feedbackElement = element.parentElement.querySelector('.invalid-feedback');
if (!feedbackElement) {
feedbackElement = document.createElement('div');
feedbackElement.className = 'invalid-feedback';
element.parentElement.appendChild(feedbackElement);
}
feedbackElement.textContent = message;
feedbackElement.style.display = 'block';
}
/**
* Clear validation messages
*/
clearValidationMessages() {
// Remove invalid classes
if (this.hasStartDateTarget) {
this.startDateTarget.classList.remove('is-invalid');
}
if (this.hasEndDateTarget) {
this.endDateTarget.classList.remove('is-invalid');
}
// Remove feedback elements
const feedbackElements = this.element.querySelectorAll('.invalid-feedback');
feedbackElements.forEach(el => {
el.style.display = 'none';
});
}
}