fix teacher, parent and admin pages
This commit is contained in:
@@ -1,42 +1,145 @@
|
||||
import { type FormEvent, useEffect, useRef, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { requestForgotPassword } from '../api/passwordFlow'
|
||||
|
||||
/** Mirrors CodeIgniter `Views/user/forgot_password.php` shell; reset flow remains on the Laravel site. */
|
||||
/** Mirrors CodeIgniter `user/forgot_password.php` + confirmation modal from `password_reset_confirmation.php`. */
|
||||
export function ForgotPasswordPage() {
|
||||
const [email, setEmail] = useState('')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [confirmEmail, setConfirmEmail] = useState<string | null>(null)
|
||||
const confirmModalRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!confirmEmail) return
|
||||
const el = confirmModalRef.current
|
||||
const B = (
|
||||
window as unknown as {
|
||||
bootstrap?: { Modal: { getOrCreateInstance: (n: HTMLElement) => { show: () => void } } }
|
||||
}
|
||||
).bootstrap
|
||||
if (el && B?.Modal?.getOrCreateInstance) {
|
||||
B.Modal.getOrCreateInstance(el).show()
|
||||
}
|
||||
}, [confirmEmail])
|
||||
|
||||
async function onSubmit(e: FormEvent) {
|
||||
e.preventDefault()
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
await requestForgotPassword(email)
|
||||
setConfirmEmail(email.trim())
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Request failed.')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ci-auth-bg flex-grow-1 py-4">
|
||||
<div className="registration-form ci-parity container mt-5 mb-5 text-center">
|
||||
<div className="text-center mb-4">
|
||||
<Link to="/">
|
||||
<img
|
||||
src="/alrahma_logo.png"
|
||||
alt=""
|
||||
style={{ width: 180, height: 120, objectFit: 'contain' }}
|
||||
onError={(ev) => {
|
||||
;(ev.target as HTMLImageElement).style.display = 'none'
|
||||
}}
|
||||
<div className="registration-form ci-parity container mt-5 mb-5">
|
||||
<form
|
||||
className="text-start mx-auto"
|
||||
style={{ maxWidth: 600 }}
|
||||
onSubmit={(e) => void onSubmit(e)}
|
||||
noValidate
|
||||
>
|
||||
<div className="text-center mb-4">
|
||||
<Link to="/">
|
||||
<img
|
||||
src="/alrahma_logo.png"
|
||||
alt=""
|
||||
style={{ width: 180, height: 120, objectFit: 'contain' }}
|
||||
onError={(ev) => {
|
||||
;(ev.target as HTMLImageElement).style.display = 'none'
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<h3 className="text-center text-success mb-4" style={{ fontFamily: 'Arial, sans-serif' }}>
|
||||
Reset Your Password
|
||||
</h3>
|
||||
|
||||
<div className="centered-paragraph mb-4 text-center text-muted">
|
||||
<p className="mb-1">Please enter the email address you used to register with us.</p>
|
||||
<p className="mb-0">We'll send you a link to reset your password.</p>
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
<div className="alert alert-danger text-center" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label visually-hidden" htmlFor="forgot-email">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
className="form-control item"
|
||||
id="forgot-email"
|
||||
name="email"
|
||||
maxLength={50}
|
||||
placeholder="Email Address"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
autoComplete="email"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-center text-success mb-4" style={{ fontFamily: 'Arial, sans-serif' }}>
|
||||
Reset Your Password
|
||||
</h3>
|
||||
<div className="d-grid mb-3">
|
||||
<button type="submit" className="btn btn-success item" disabled={loading}>
|
||||
{loading ? 'Sending…' : 'Reset Password'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto mb-4 text-muted" style={{ maxWidth: 560 }}>
|
||||
<p>Password reset requests are handled through the school's main website.</p>
|
||||
<p className="mb-0">
|
||||
If this SPA is only the API client, open the legacy site or contact the office for a
|
||||
reset link.
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="mb-0">Don't have an account?</p>
|
||||
<Link className="d-inline-block mt-1" to="/register">
|
||||
Register now
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="d-grid gap-2" style={{ maxWidth: 400, margin: '0 auto' }}>
|
||||
<Link className="btn btn-success item" to="/login">
|
||||
Back to Sign in
|
||||
</Link>
|
||||
<Link className="btn btn-secondary item" to="/register">
|
||||
Register now
|
||||
</Link>
|
||||
<div
|
||||
className="modal fade"
|
||||
id="emailConfirmModal"
|
||||
tabIndex={-1}
|
||||
ref={confirmModalRef}
|
||||
aria-labelledby="emailConfirmLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div className="modal-content rounded-4 shadow border-0" style={{ maxWidth: 600, margin: 'auto' }}>
|
||||
<div className="modal-body text-center">
|
||||
<img
|
||||
src="/alrahma_logo.png"
|
||||
alt=""
|
||||
style={{ width: 180, height: 120, objectFit: 'contain' }}
|
||||
onError={(ev) => {
|
||||
;(ev.target as HTMLImageElement).style.display = 'none'
|
||||
}}
|
||||
/>
|
||||
<h5 className="modal-title text-success" id="emailConfirmLabel">
|
||||
Check Your Email
|
||||
</h5>
|
||||
<p className="lead mt-3 mb-2">
|
||||
A link to reset the password has been sent to this email:
|
||||
<br />
|
||||
<strong>{confirmEmail ?? ''}</strong>
|
||||
</p>
|
||||
<p className="mb-0">Please check your inbox and follow the instructions to reset the password.</p>
|
||||
<Link className="btn btn-success mt-3" to="/login">
|
||||
Return to Login
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user