Files
alrahma_sunday_school/app/Views/user/register.php
T
2026-02-10 22:11:06 -05:00

571 lines
26 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?= $this->extend('layout/register_layout') ?>
<?= $this->section('content') ?>
<div class="registration-form container mt-5 mb-5">
<?php $errors = session()->getFlashdata('errors') ?? []; ?>
<?php $captcha_error = session()->getFlashdata('captcha_error') ?? ''; ?>
<form action="<?= base_url('/register') ?>" method="post" id="registrationForm">
<?= csrf_field() ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger" role="alert">
<?= session()->getFlashdata('error') ?>
</div>
<?php endif; ?>
<?php if (session()->getFlashdata('top_error')): ?>
<div class="alert alert-danger text-center">
<?= esc(session()->getFlashdata('top_error')) ?>
</div>
<?php endif; ?>
<div class="text-center mb-4">
<a href="<?= base_url('/') ?>">
<img src="<?= base_url('assets/images/alrahma_logo.png') ?>" alt=""
style="width: 180px; height: 120px;">
</a>
</div>
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Registration Form</h3>
<div class="alert alert-info" role="alert">
- This account is for parents/guardians to manage students. If you already registered in a prior year, please reuse your existing account.<br>
- If you cannot access your old email, reset your password.
</div>
<p class="text-center text-secondary mt-3 mb-2">
(All fields with * are required)
</p>
<br>
<div class="mb-3">
<input type="text" class="form-control item" id="firstname" name="firstname" placeholder="First Name*"
maxlength="50" required value="<?= old('firstname') ?>">
<div class="form-text text-muted d-none" id="firstname-hint">230 characters. Letters, spaces, and hyphens only.</div>
<?php if (isset($errors['firstname'])): ?>
<div class="text-danger"><?= $errors['firstname'] ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<input type="text" class="form-control item" id="lastname" name="lastname" placeholder="Last Name*"
maxlength="50" required value="<?= old('lastname') ?>">
<div class="form-text text-muted d-none" id="lastname-hint">230 characters. Letters, spaces, and hyphens only.</div>
<?php if (isset($errors['lastname'])): ?>
<div class="text-danger"><?= $errors['lastname'] ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<select id="gender" name="gender" class="form-select item" required>
<option value="" disabled selected>Select Gender*</option>
<option value="Male" <?= old('gender') == 'Male' ? 'selected' : '' ?>>Male</option>
<option value="Female" <?= old('gender') == 'Female' ? 'selected' : '' ?>>Female</option>
</select>
<?php if (isset($errors['gender'])): ?>
<div class="text-danger"><?= $errors['gender'] ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<input type="tel"
class="form-control item"
id="cellphone"
name="cellphone"
placeholder="Phone - Cell*"
maxlength="12"
required
title="Enter a valid 10-digit phone number (e.g., 123-456-7890)"
value="<?= old('cellphone') ?>">
<div class="form-text text-muted d-none" id="cellphone-hint">10-digit US phone number required.</div>
<?php if (isset($errors['cellphone'])): ?>
<div class="text-danger"><?= $errors['cellphone'] ?></div>
<?php endif; ?>
<div id="cellphone-error" class="text-danger"></div>
</div>
<div class="mb-3">
<input type="email" class="form-control item" id="email" name="email" placeholder="Email*"
maxlength="50" required value="<?= old('email') ?>">
<div class="form-text text-muted d-none" id="email-hint">Valid format (e.g. user@example.com), max 50 characters.</div>
<?php if (isset($errors['email'])): ?>
<div class="text-danger"><?= $errors['email'] ?></div>
<?php endif; ?>
<div id="email-error" class="text-danger"></div>
</div>
<div class="mb-3 position-relative">
<input type="email" class="form-control item" id="confirm_email" name="confirm_email"
placeholder="Confirm Email*" maxlength="50" required
value="<?= old('confirm_email') ?>"
onpaste="return false;" oncopy="return false;" oncut="return false;"
title="Copy/paste is disabled for accuracy">
<div class="form-text text-muted d-none" id="confirm_email-hint">Must match the email field. Copy/paste disabled.</div>
<?php if (isset($errors['confirm_email'])): ?>
<div class="text-danger"><?= $errors['confirm_email'] ?></div>
<?php endif; ?>
<div id="confirm-email-error" class="text-danger"></div>
<div class="invalid-feedback d-none" id="copy-warning-email">
Copy/paste is disabled for this field.
</div>
</div>
<div class="mb-3">
<input type="text" class="form-control item" id="address_street" name="address_street"
placeholder="Address - Street*" maxlength="50" required value="<?= old('address_street') ?>">
<div class="form-text text-muted d-none" id="address_street-hint">230 characters. Letters, digits, spaces, hyphens allowed.</div>
</div>
<div class="mb-3">
<input type="text" class="form-control item" id="apt" name="apt" placeholder="Apt" maxlength="15"
value="<?= old('apt') ?>">
<div class="form-text text-muted d-none" id="apt-hint">Optional. Max 15 characters.</div>
<div id="apt-error" class="text-danger small"></div>
</div>
<div class="mb-3">
<input type="text" class="form-control item" id="city" name="city" placeholder="City*" maxlength="15"
required value="<?= old('city') ?>">
<div class="form-text text-muted d-none" id="city-hint">215 characters. Letters and spaces allowed.</div>
<?php if (isset($errors['city'])): ?>
<div class="text-danger"><?= $errors['city'] ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<select id="state" name="state" class="form-select item" required>
<option value="" disabled selected>Select State*</option>
<?php
$states = [
'CT' => 'Connecticut',
'ME' => 'Maine',
'MA' => 'Massachusetts',
'NH' => 'New Hampshire',
'NY' => 'New York',
'RI' => 'Rhode Island',
'VT' => 'Vermont'
];
foreach ($states as $abbr => $name): ?>
<option value="<?= $abbr ?>" <?= old('state') == $abbr ? 'selected' : '' ?>><?= $name ?></option>
<?php endforeach; ?>
</select>
<?php if (isset($errors['state'])): ?>
<div class="text-danger"><?= $errors['state'] ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<input type="text" class="form-control item" id="zip" name="zip" placeholder="Zip*" maxlength="5"
pattern="\d{5}" title="Please enter a 5-digit zip code" inputmode="numeric" required
value="<?= old('zip') ?>">
<div class="form-text text-muted d-none" id="zip-hint">5-digit US ZIP code only.</div>
<?php if (isset($errors['zip'])): ?>
<div class="text-danger"><?= $errors['zip'] ?></div>
<?php endif; ?>
<div id="zip-error" class="text-danger"></div>
</div>
<!-- Parent/Guardian Checkbox -->
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" value="1" id="is_parent" name="is_parent" <?= old('is_parent') ? 'checked' : '' ?>>
<label class="form-check-label" for="is_parent">
Check this box if you are a Parent/Guardian
</label>
</div>
<!-- Second Parent Info Section -->
<div id="second-parent-section" style="display: none; border: 1px solid #ccc; padding: 15px; margin-top: 10px; border-radius: 5px;">
<h6>Second Parent/Guardian Information</h6>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" value="1"
id="no_second_parent_info" name="no_second_parent_info"
<?= old('no_second_parent_info') ? 'checked' : '' ?>>
<label class="form-check-label" for="no_second_parent_info">
Check this box if no second parent info is available
</label>
</div>
<div class="row" id="second-parent-fields">
<!-- First Name -->
<div class="mb-3">
<input type="text" class="form-control item" id="second_firstname" name="second_firstname"
placeholder="First Name*" maxlength="50" value="<?= old('second_firstname') ?>">
<div class="form-text text-muted d-none" id="second_firstname-hint">230 characters. Letters, spaces, and hyphens only.</div>
<?php if (isset($errors['second_firstname'])): ?>
<div class="text-danger"><?= $errors['second_firstname'] ?></div>
<?php endif; ?>
</div>
<!-- Last Name -->
<div class="mb-3">
<input type="text" class="form-control item" id="second_lastname" name="second_lastname"
placeholder="Last Name*" maxlength="50" value="<?= old('second_lastname') ?>">
<div class="form-text text-muted d-none" id="second_lastname-hint">230 characters. Letters, spaces, and hyphens only.</div>
<?php if (isset($errors['second_lastname'])): ?>
<div class="text-danger"><?= $errors['second_lastname'] ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<select id="second_gender" name="second_gender" class="form-select item">
<option value="" disabled selected>Select Gender*</option>
<option value="Male" <?= old('second_gender') == 'Male' ? 'selected' : '' ?>>Male</option>
<option value="Female" <?= old('second_gender') == 'Female' ? 'selected' : '' ?>>Female</option>
</select>
<?php if (isset($errors['second_gender'])): ?>
<div class="text-danger"><?= $errors['second_gender'] ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<input type="email" class="form-control item" id="second_email" name="second_email" placeholder="Email*"
maxlength="50" value="<?= old('second_email') ?>">
<div class="form-text text-muted d-none" id="second_email-hint">Valid format (e.g. user@example.com), max 50 characters.</div>
<?php if (isset($errors['second_email'])): ?>
<div class="text-danger"><?= $errors['second_email'] ?></div>
<?php endif; ?>
<div id="second_email-error" class="text-danger"></div>
</div>
<div class="mb-3">
<input type="text"
class="form-control item"
id="second_cellphone"
name="second_cellphone"
placeholder="Phone - Cell*"
maxlength="12"
title="Enter a valid 10-digit phone number (e.g., 123-456-7890"
value="<?= old('second_cellphone') ?>">
<div class="form-text text-muted d-none" id="second_cellphone-hint">10-digit US phone number required.</div>
<?php if (isset($errors['second_cellphone'])): ?>
<div class="text-danger"><?= $errors['second_cellphone'] ?></div>
<?php endif; ?>
<div id="second_cellphone-error" class="text-danger"></div>
</div>
</div>
</div>
<br>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" value="1" id="accept_school_policy"
name="accept_school_policy" required <?= old('accept_school_policy') ? 'checked' : '' ?>>
<label class="form-check-label mb-3 policy-label" for="accept_school_policy">
I have read and I accept all school policies
<!-- Use a button, not an <a>; keep it OUTSIDE the label -->
<button type="button" class="btn btn-link p-0 small text-success" id="openPolicyLink">
(Read school policies here)
</button>
</label>
</div>
<div class="mb-3">
<div class="text-center border p-2 bg-light item">
<?= strip_tags($captchaQuestion) ?>
</div>
</div>
<div class="mb-3 position-relative">
<input type="text"
class="form-control item <?= isset($errors['captcha']) ? 'is-invalid' : '' ?>"
id="captcha"
name="captcha"
placeholder="Enter the text above*"
maxlength="8"
autocomplete="off"
required
onpaste="return false;" oncopy="return false;" oncut="return false;"
title="Copy/paste is disabled for accuracy">
<!-- JS-based warning -->
<div id="captcha-warning" class="invalid-feedback d-none">
⚠️ Copying and pasting is disabled. Please manually type the answer.
</div>
<!-- Server-side error -->
<?php if (isset($errors['captcha'])): ?>
<div class="invalid-feedback d-block"><?= esc($errors['captcha']) ?></div>
<?php endif; ?>
</div>
<div class="mb-3 d-grid">
<button type="submit" class="btn btn-success item">Create Account</button>
<a href="/" class="btn btn-secondary item">Back to Home</a>
</div>
</form>
<!-- Policy Modal -->
<div class="modal fade" id="policyModal" tabindex="-1" aria-labelledby="policyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title text-success" id="policyModalLabel">School Policies</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<iframe src="<?= base_url('policy/school_policy') ?>" width="100%" height="400" frameborder="0"></iframe>
</div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
// ---- Elements ----
const form = document.getElementById('registrationForm');
const isParentCheckbox = document.getElementById('is_parent');
const secondParentSection = document.getElementById('second-parent-section');
const noSecondParentCheckbox = document.getElementById('no_second_parent_info');
const secondParentFieldsWrapper = document.getElementById('second-parent-fields');
// Policy button: open modal via JS only (no data attributes, no href)
const policyBtn = document.getElementById('openPolicyLink');
if (policyBtn) {
['click', 'mousedown', 'mouseup'].forEach(evt =>
policyBtn.addEventListener(evt, ev => {
ev.preventDefault();
ev.stopPropagation();
})
);
policyBtn.addEventListener('click', function() {
const modalEl = document.getElementById('policyModal');
if (modalEl && typeof bootstrap !== 'undefined' && bootstrap.Modal) {
new bootstrap.Modal(modalEl).show();
}
});
}
// Second-parent inputs we consider "required" when applicable
const secondRequiredIds = ['second_firstname', 'second_lastname', 'second_email', 'second_cellphone'];
const secondRequiredEls = secondRequiredIds.map(id => document.getElementById(id)).filter(Boolean);
// Phone inputs (yours + second parent)
const phoneInputs = document.querySelectorAll('input[type="tel"], #cellphone, #second_cellphone');
// ---- Helpers ----
function setSecondParentFieldsRequired(enable) {
secondRequiredEls.forEach(el => {
if (!el) return;
if (enable) {
el.setAttribute('required', 'required');
el.classList.add('second-required');
} else {
el.removeAttribute('required');
el.classList.remove('second-required');
el.classList.remove('is-invalid');
}
});
}
function enableDisableSecondParentFields(disable) {
const inputs = secondParentFieldsWrapper ? secondParentFieldsWrapper.querySelectorAll('input, select') : [];
inputs.forEach(el => {
el.disabled = disable;
if (disable) el.classList.remove('is-invalid');
});
}
function clearSecondParentFields() {
if (!secondParentFieldsWrapper) return;
secondParentFieldsWrapper.querySelectorAll('input').forEach(el => {
el.value = '';
});
// Clear inline error text if present
const errIds = ['second_cellphone-error', 'second_email-error'];
errIds.forEach(id => {
const e = document.getElementById(id);
if (e) e.textContent = '';
});
}
function showHideSecondParentSection(show) {
if (!secondParentSection) return;
secondParentSection.style.display = show ? 'block' : 'none';
}
// ---- Behaviors per your rules ----
// 1) Default: DO NOT auto-check "no_second_parent_info". Keep it unchecked.
if (noSecondParentCheckbox) noSecondParentCheckbox.checked = false;
// 2) Toggle logic when main "is_parent" changes
function onIsParentChanged() {
if (isParentCheckbox.checked) {
// Parent -> show section. Now whether we require fields depends on the "no info" switch.
showHideSecondParentSection(true);
if (noSecondParentCheckbox && noSecondParentCheckbox.checked) {
// No info: ignore second parent data
setSecondParentFieldsRequired(false);
enableDisableSecondParentFields(true);
clearSecondParentFields();
if (secondParentFieldsWrapper) secondParentFieldsWrapper.style.display = 'none';
} else {
// Info required
setSecondParentFieldsRequired(true);
enableDisableSecondParentFields(false);
if (secondParentFieldsWrapper) secondParentFieldsWrapper.style.display = 'block';
}
} else {
// Not a parent -> ignore second parent data completely
setSecondParentFieldsRequired(false);
enableDisableSecondParentFields(true);
clearSecondParentFields();
showHideSecondParentSection(false);
// IMPORTANT: do NOT auto-toggle the "no_second_parent_info" checkbox here.
}
}
// 3) Toggle logic when "no_second_parent_info" changes
function onNoSecondParentChanged() {
// If not parent, ignore entirely
if (!isParentCheckbox.checked) {
setSecondParentFieldsRequired(false);
enableDisableSecondParentFields(true);
clearSecondParentFields();
showHideSecondParentSection(false);
return;
}
// Parent is checked:
if (noSecondParentCheckbox.checked) {
// No info -> ignore second parent data
setSecondParentFieldsRequired(false);
enableDisableSecondParentFields(true);
clearSecondParentFields();
if (secondParentFieldsWrapper) secondParentFieldsWrapper.style.display = 'none';
} else {
// Require info
setSecondParentFieldsRequired(true);
enableDisableSecondParentFields(false);
if (secondParentFieldsWrapper) secondParentFieldsWrapper.style.display = 'block';
}
}
// ---- Phone formatting / validation ----
phoneInputs.forEach(input => {
input.addEventListener('input', function(e) {
const raw = e.target.value.replace(/\D/g, '').slice(0, 10);
let formatted = raw;
if (raw.length > 3) formatted = raw.slice(0, 3) + '-' + raw.slice(3);
if (raw.length > 6) formatted = formatted.slice(0, 7) + '-' + raw.slice(6);
e.target.value = formatted;
const errorElement = document.getElementById(input.id + '-error');
if (raw.length === 10 || input.disabled || !input.hasAttribute('required')) {
input.classList.remove('is-invalid');
if (errorElement) errorElement.textContent = '';
} else {
input.classList.add('is-invalid');
if (errorElement) errorElement.textContent = 'Please enter a valid 10-digit phone number.';
}
});
input.addEventListener('blur', function(e) {
const raw = e.target.value.replace(/\D/g, '');
const errorElement = document.getElementById(input.id + '-error');
if (raw.length !== 10 && !input.disabled && input.hasAttribute('required')) {
input.classList.add('is-invalid');
if (errorElement) errorElement.textContent = 'Please enter a valid 10-digit phone number.';
} else {
input.classList.remove('is-invalid');
if (errorElement) errorElement.textContent = '';
}
});
});
// ---- Event wiring ----
if (isParentCheckbox) isParentCheckbox.addEventListener('change', onIsParentChanged);
if (noSecondParentCheckbox) noSecondParentCheckbox.addEventListener('change', onNoSecondParentChanged);
// ---- Initial state ----
// Start with section hidden and inputs disabled, not required.
showHideSecondParentSection(false);
setSecondParentFieldsRequired(false);
enableDisableSecondParentFields(true);
// ---- Final pre-submit guard ----
if (form) {
form.addEventListener('submit', function(e) {
let ok = true;
// Rule 1: First, check Parent/Guardian
if (!isParentCheckbox.checked) {
// Not a parent -> ignore second parent data
setSecondParentFieldsRequired(false);
enableDisableSecondParentFields(true);
clearSecondParentFields();
showHideSecondParentSection(false);
} else {
// Is a parent
if (noSecondParentCheckbox && noSecondParentCheckbox.checked) {
// No second parent info -> ignore second parent data
setSecondParentFieldsRequired(false);
enableDisableSecondParentFields(true);
clearSecondParentFields();
} else {
// Require second parent info
setSecondParentFieldsRequired(true);
enableDisableSecondParentFields(false);
// Validate second required fields
for (const el of secondRequiredEls) {
if (!el) continue;
const val = (el.value || '').trim();
if (!val) {
el.classList.add('is-invalid');
ok = false;
} else {
el.classList.remove('is-invalid');
}
if (el.id === 'second_cellphone') {
const raw = val.replace(/\D/g, '');
if (raw.length !== 10) {
el.classList.add('is-invalid');
const err = document.getElementById('second_cellphone-error');
if (err) err.textContent = 'Please enter a valid 10-digit phone number.';
ok = false;
}
}
if (el.id === 'second_email') {
const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val);
if (!emailOk) {
el.classList.add('is-invalid');
const err = document.getElementById('second_email-error');
if (err) err.textContent = 'Please enter a valid email address.';
ok = false;
}
}
}
}
}
// Validate all required phone inputs (primary + any enabled second)
document.querySelectorAll('input[type="tel"]').forEach(input => {
const raw = (input.value || '').replace(/\D/g, '');
if (input.hasAttribute('required') && !input.disabled && raw.length !== 10) {
input.classList.add('is-invalid');
const err = document.getElementById(input.id + '-error');
if (err) err.textContent = 'Please enter a valid 10-digit phone number.';
ok = false;
}
});
if (!ok) {
e.preventDefault();
const firstError = document.querySelector('.is-invalid');
if (firstError) firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
}
});
</script>
<?= $this->endSection() ?>