recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
+570
View File
@@ -0,0 +1,570 @@
<?= $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() ?>