add application forms
This commit is contained in:
parent
897544d20d
commit
5d32905110
6 changed files with 2480 additions and 2 deletions
456
functions/api/participant-application.ts
Normal file
456
functions/api/participant-application.ts
Normal file
|
|
@ -0,0 +1,456 @@
|
||||||
|
type Env = {
|
||||||
|
TURNSTILE_SECRET_KEY?: string;
|
||||||
|
RESEND_API_KEY?: string;
|
||||||
|
CONTACT_FROM_EMAIL?: string;
|
||||||
|
CONTACT_TO_EMAIL?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const jsonResponse = (data: Record<string, unknown>, status = 200) =>
|
||||||
|
new Response(JSON.stringify(data), {
|
||||||
|
status,
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchWithTimeout = async (
|
||||||
|
input: RequestInfo | URL,
|
||||||
|
init: RequestInit,
|
||||||
|
timeoutMs: number,
|
||||||
|
label: string,
|
||||||
|
) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
||||||
|
try {
|
||||||
|
return await fetch(input, { ...init, signal: controller.signal });
|
||||||
|
} catch (error) {
|
||||||
|
const message =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
|
throw new Error(`${label} request failed: ${message}`);
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const readTextField = (value: string | null) => (value ?? "").trim();
|
||||||
|
const toTitleCase = (value: string) =>
|
||||||
|
value
|
||||||
|
.replace(/_/g, " ")
|
||||||
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||||
|
|
||||||
|
const parseFormFields = async (request: Request) => {
|
||||||
|
const contentType = request.headers.get("content-type") ?? "";
|
||||||
|
const data = new Map<string, string[]>();
|
||||||
|
|
||||||
|
const appendValue = (key: string, value: string | null) => {
|
||||||
|
const normalized = readTextField(value);
|
||||||
|
if (!normalized) return;
|
||||||
|
data.set(key, [...(data.get(key) ?? []), normalized]);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (contentType.includes("application/x-www-form-urlencoded")) {
|
||||||
|
const bodyText = await request.text();
|
||||||
|
const params = new URLSearchParams(bodyText);
|
||||||
|
for (const [key, value] of params.entries()) {
|
||||||
|
appendValue(key, value);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = await request.formData();
|
||||||
|
for (const [key, value] of formData.entries()) {
|
||||||
|
appendValue(key, typeof value === "string" ? value : value.name);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatValue = (values: string[] | undefined) => (values ?? []).join(", ");
|
||||||
|
|
||||||
|
const labelMap: Record<string, string> = {
|
||||||
|
filled_by: "Who is filling in the form",
|
||||||
|
participant_first_names: "Participant first name(s)",
|
||||||
|
participant_last_name: "Participant last name",
|
||||||
|
preferred_name: "Preferred name or nickname",
|
||||||
|
preferred_pronouns: "Preferred pronouns",
|
||||||
|
date_of_birth: "Date of birth",
|
||||||
|
sex: "Sex",
|
||||||
|
languages_spoken: "Languages used every day",
|
||||||
|
address_line_1: "Address line 1",
|
||||||
|
address_line_2: "Address line 2",
|
||||||
|
postcode: "Postcode",
|
||||||
|
telephone: "Telephone",
|
||||||
|
mobile: "Mobile",
|
||||||
|
email: "Email",
|
||||||
|
activity_experience: "Previous activity experience",
|
||||||
|
allergies: "Allergies",
|
||||||
|
triggers: "Known triggers",
|
||||||
|
simple_instructions: "Would prefer simple instructions",
|
||||||
|
learning_disability: "Learning disability",
|
||||||
|
neurodiverse: "Neurodiverse",
|
||||||
|
sensitive_to_crowds: "Sensitive to strangers or crowds",
|
||||||
|
sensory_sensitivities: "Sensitive to light, temperature, noise or clothing",
|
||||||
|
anxiety_low_mood: "Anxiety, depression or low mood",
|
||||||
|
panic_attacks: "Panic attacks",
|
||||||
|
speech_difficulties: "Difficulty with speech or being understood",
|
||||||
|
communication_system: "Uses a communication system",
|
||||||
|
hearing_aid: "Hearing aid",
|
||||||
|
cochlear_implant: "Cochlear implant",
|
||||||
|
bsl: "Uses BSL",
|
||||||
|
blind: "Blind",
|
||||||
|
partially_sighted: "Partially sighted",
|
||||||
|
dexterity_difficulties: "Dexterity difficulties",
|
||||||
|
balance_problems: "Problems with balance",
|
||||||
|
weight_through_feet: "Can take weight through feet",
|
||||||
|
help_walking: "Needs help with walking",
|
||||||
|
steps_mounting_block: "Can walk up a few steps",
|
||||||
|
wheelchair_user: "Wheelchair user",
|
||||||
|
walking_aids: "Uses walking aids or supports",
|
||||||
|
prosthetic_limb: "Wears a prosthetic limb",
|
||||||
|
long_term_pain: "Long term pain",
|
||||||
|
breathing_stamina: "Breathing or stamina difficulties",
|
||||||
|
epilepsy: "Epilepsy or controlled by medication",
|
||||||
|
condition_details: "Disability or medical condition details",
|
||||||
|
participant_height: "Height",
|
||||||
|
height_unit: "Height unit",
|
||||||
|
participant_weight: "Weight",
|
||||||
|
weight_unit: "Weight unit",
|
||||||
|
interested_in_competing: "Interested in competing",
|
||||||
|
been_classified: "Has been classified",
|
||||||
|
classification_details: "Classification",
|
||||||
|
classification_date: "Classification date",
|
||||||
|
emergency_contact_name: "Emergency contact name and relationship",
|
||||||
|
emergency_contact_phone: "Emergency contact number",
|
||||||
|
emergency_contact_consent: "Emergency contact consent",
|
||||||
|
declaration_confirm: "Declaration confirmation",
|
||||||
|
provide_medical_details: "Agrees to provide further medical details if requested",
|
||||||
|
notify_changes: "Will notify RDA of changes",
|
||||||
|
risk_acknowledgement: "Accepts horse-related risk acknowledgement",
|
||||||
|
vaulting_acknowledgement: "Vaulting risk acknowledgement",
|
||||||
|
photos_consent: "Photo and video consent",
|
||||||
|
newsletter_consent: "Newsletter consent",
|
||||||
|
declaration_signature: "Signature name",
|
||||||
|
declaration_date: "Declaration date",
|
||||||
|
guardian_name: "Parent or legal guardian name",
|
||||||
|
guardian_relationship: "Relationship to applicant",
|
||||||
|
guardian_address_line_1: "Guardian address line 1",
|
||||||
|
guardian_address_line_2: "Guardian address line 2",
|
||||||
|
guardian_postcode: "Guardian postcode",
|
||||||
|
guardian_telephone: "Guardian telephone",
|
||||||
|
guardian_mobile: "Guardian mobile",
|
||||||
|
};
|
||||||
|
|
||||||
|
const requiredFields = [
|
||||||
|
"filled_by",
|
||||||
|
"participant_first_names",
|
||||||
|
"participant_last_name",
|
||||||
|
"date_of_birth",
|
||||||
|
"address_line_1",
|
||||||
|
"postcode",
|
||||||
|
"email",
|
||||||
|
"emergency_contact_name",
|
||||||
|
"emergency_contact_phone",
|
||||||
|
"declaration_confirm",
|
||||||
|
"photos_consent",
|
||||||
|
];
|
||||||
|
|
||||||
|
const sectionOrder = [
|
||||||
|
{
|
||||||
|
title: "Applicant",
|
||||||
|
fields: [
|
||||||
|
"filled_by",
|
||||||
|
"participant_first_names",
|
||||||
|
"participant_last_name",
|
||||||
|
"preferred_name",
|
||||||
|
"preferred_pronouns",
|
||||||
|
"date_of_birth",
|
||||||
|
"sex",
|
||||||
|
"languages_spoken",
|
||||||
|
"address_line_1",
|
||||||
|
"address_line_2",
|
||||||
|
"postcode",
|
||||||
|
"telephone",
|
||||||
|
"mobile",
|
||||||
|
"email",
|
||||||
|
"activity_experience",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Specific Information",
|
||||||
|
fields: [
|
||||||
|
"allergies",
|
||||||
|
"triggers",
|
||||||
|
"simple_instructions",
|
||||||
|
"learning_disability",
|
||||||
|
"neurodiverse",
|
||||||
|
"sensitive_to_crowds",
|
||||||
|
"sensory_sensitivities",
|
||||||
|
"anxiety_low_mood",
|
||||||
|
"panic_attacks",
|
||||||
|
"speech_difficulties",
|
||||||
|
"communication_system",
|
||||||
|
"hearing_aid",
|
||||||
|
"cochlear_implant",
|
||||||
|
"bsl",
|
||||||
|
"blind",
|
||||||
|
"partially_sighted",
|
||||||
|
"dexterity_difficulties",
|
||||||
|
"balance_problems",
|
||||||
|
"weight_through_feet",
|
||||||
|
"help_walking",
|
||||||
|
"steps_mounting_block",
|
||||||
|
"wheelchair_user",
|
||||||
|
"walking_aids",
|
||||||
|
"prosthetic_limb",
|
||||||
|
"long_term_pain",
|
||||||
|
"breathing_stamina",
|
||||||
|
"epilepsy",
|
||||||
|
"condition_details",
|
||||||
|
"participant_height",
|
||||||
|
"height_unit",
|
||||||
|
"participant_weight",
|
||||||
|
"weight_unit",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Additional Information",
|
||||||
|
fields: [
|
||||||
|
"interested_in_competing",
|
||||||
|
"been_classified",
|
||||||
|
"classification_details",
|
||||||
|
"classification_date",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Emergency Contact",
|
||||||
|
fields: [
|
||||||
|
"emergency_contact_name",
|
||||||
|
"emergency_contact_phone",
|
||||||
|
"emergency_contact_consent",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Declaration",
|
||||||
|
fields: [
|
||||||
|
"declaration_confirm",
|
||||||
|
"provide_medical_details",
|
||||||
|
"notify_changes",
|
||||||
|
"risk_acknowledgement",
|
||||||
|
"vaulting_acknowledgement",
|
||||||
|
"photos_consent",
|
||||||
|
"newsletter_consent",
|
||||||
|
"declaration_signature",
|
||||||
|
"declaration_date",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Parent or Legal Guardian",
|
||||||
|
fields: [
|
||||||
|
"guardian_name",
|
||||||
|
"guardian_relationship",
|
||||||
|
"guardian_address_line_1",
|
||||||
|
"guardian_address_line_2",
|
||||||
|
"guardian_postcode",
|
||||||
|
"guardian_telephone",
|
||||||
|
"guardian_mobile",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handlePost: PagesFunction<Env> = async ({ request, env }) => {
|
||||||
|
const fields = await parseFormFields(request);
|
||||||
|
|
||||||
|
const missingRequired = requiredFields.filter(
|
||||||
|
(field) => !formatValue(fields.get(field)),
|
||||||
|
);
|
||||||
|
if (missingRequired.length > 0) {
|
||||||
|
return jsonResponse(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
error: "Missing required fields.",
|
||||||
|
details: missingRequired.map((field) => labelMap[field] ?? field).join(", "),
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!env.TURNSTILE_SECRET_KEY) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Turnstile secret key not configured." },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnstileToken = formatValue(fields.get("cf-turnstile-response"));
|
||||||
|
if (!turnstileToken) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Turnstile verification failed." },
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyBody = new URLSearchParams({
|
||||||
|
secret: env.TURNSTILE_SECRET_KEY,
|
||||||
|
response: turnstileToken,
|
||||||
|
remoteip: request.headers.get("CF-Connecting-IP") ?? "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const verifyResponse = await fetchWithTimeout(
|
||||||
|
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
||||||
|
body: verifyBody,
|
||||||
|
},
|
||||||
|
8000,
|
||||||
|
"Turnstile",
|
||||||
|
);
|
||||||
|
const verifyResult = (await verifyResponse.json()) as {
|
||||||
|
success?: boolean;
|
||||||
|
"error-codes"?: string[];
|
||||||
|
messages?: string[];
|
||||||
|
hostname?: string;
|
||||||
|
action?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!verifyResult.success) {
|
||||||
|
const details = [
|
||||||
|
verifyResult["error-codes"]?.length
|
||||||
|
? `codes=${verifyResult["error-codes"].join(",")}`
|
||||||
|
: "",
|
||||||
|
verifyResult.messages?.length
|
||||||
|
? `messages=${verifyResult.messages.join(",")}`
|
||||||
|
: "",
|
||||||
|
verifyResult.hostname ? `hostname=${verifyResult.hostname}` : "",
|
||||||
|
verifyResult.action ? `action=${verifyResult.action}` : "",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" | ")
|
||||||
|
.slice(0, 500);
|
||||||
|
return jsonResponse(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
error: "Turnstile verification failed.",
|
||||||
|
details: details || undefined,
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!env.RESEND_API_KEY) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Email provider not configured." },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromEmail = env.CONTACT_FROM_EMAIL;
|
||||||
|
if (!fromEmail) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Sender email not configured." },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const toEmail = env.CONTACT_TO_EMAIL ?? "calum@muir.in";
|
||||||
|
const applicantName = [
|
||||||
|
formatValue(fields.get("participant_first_names")),
|
||||||
|
formatValue(fields.get("participant_last_name")),
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
const bodyText = sectionOrder
|
||||||
|
.map(({ title, fields: sectionFields }) => {
|
||||||
|
const lines = sectionFields
|
||||||
|
.map((field) => {
|
||||||
|
const value = formatValue(fields.get(field));
|
||||||
|
if (!value) return null;
|
||||||
|
return `${labelMap[field] ?? toTitleCase(field)}: ${value}`;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return lines ? `${title}\n${lines}` : "";
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
|
const extraFields = [...fields.keys()].filter(
|
||||||
|
(field) =>
|
||||||
|
field !== "cf-turnstile-response" &&
|
||||||
|
!sectionOrder.some((section) => section.fields.includes(field)),
|
||||||
|
);
|
||||||
|
const extraText = extraFields
|
||||||
|
.map((field) => {
|
||||||
|
const value = formatValue(fields.get(field));
|
||||||
|
return value ? `${labelMap[field] ?? toTitleCase(field)}: ${value}` : "";
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const emailPayload = {
|
||||||
|
from: fromEmail,
|
||||||
|
to: [toEmail],
|
||||||
|
reply_to: formatValue(fields.get("email")) || undefined,
|
||||||
|
subject: `New participant application${applicantName ? `: ${applicantName}` : ""}`,
|
||||||
|
text: [bodyText, extraText ? `Additional Fields\n${extraText}` : ""]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n\n"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetchWithTimeout(
|
||||||
|
"https://api.resend.com/emails",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${env.RESEND_API_KEY}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(emailPayload),
|
||||||
|
},
|
||||||
|
8000,
|
||||||
|
"Resend",
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
const contentType = response.headers.get("content-type") ?? "";
|
||||||
|
const errorText = contentType.includes("application/json")
|
||||||
|
? JSON.stringify(await response.json().catch(() => ({})))
|
||||||
|
: await response.text();
|
||||||
|
const errorCode = response.headers.get("x-ms-error-code");
|
||||||
|
const details = [
|
||||||
|
`${response.status} ${response.statusText}`.trim(),
|
||||||
|
errorCode ? `code=${errorCode}` : "",
|
||||||
|
errorText.trim(),
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" | ")
|
||||||
|
.slice(0, 500);
|
||||||
|
return jsonResponse(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
error: "Failed to send email.",
|
||||||
|
details,
|
||||||
|
},
|
||||||
|
502,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResponse({ ok: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const onRequest: PagesFunction<Env> = async (context) => {
|
||||||
|
try {
|
||||||
|
if (context.request.method !== "POST") {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Method not allowed." },
|
||||||
|
405,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return await handlePost(context);
|
||||||
|
} catch (error) {
|
||||||
|
const message =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Unhandled exception.", details: message },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
435
functions/api/volunteer-application.ts
Normal file
435
functions/api/volunteer-application.ts
Normal file
|
|
@ -0,0 +1,435 @@
|
||||||
|
type Env = {
|
||||||
|
TURNSTILE_SECRET_KEY?: string;
|
||||||
|
RESEND_API_KEY?: string;
|
||||||
|
CONTACT_FROM_EMAIL?: string;
|
||||||
|
CONTACT_TO_EMAIL?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const jsonResponse = (data: Record<string, unknown>, status = 200) =>
|
||||||
|
new Response(JSON.stringify(data), {
|
||||||
|
status,
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchWithTimeout = async (
|
||||||
|
input: RequestInfo | URL,
|
||||||
|
init: RequestInit,
|
||||||
|
timeoutMs: number,
|
||||||
|
label: string,
|
||||||
|
) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
||||||
|
try {
|
||||||
|
return await fetch(input, { ...init, signal: controller.signal });
|
||||||
|
} catch (error) {
|
||||||
|
const message =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
|
throw new Error(`${label} request failed: ${message}`);
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const readTextField = (value: string | null) => (value ?? "").trim();
|
||||||
|
const toTitleCase = (value: string) =>
|
||||||
|
value
|
||||||
|
.replace(/_/g, " ")
|
||||||
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||||
|
|
||||||
|
const parseFormFields = async (request: Request) => {
|
||||||
|
const contentType = request.headers.get("content-type") ?? "";
|
||||||
|
const data = new Map<string, string[]>();
|
||||||
|
|
||||||
|
const appendValue = (key: string, value: string | null) => {
|
||||||
|
const normalized = readTextField(value);
|
||||||
|
if (!normalized) return;
|
||||||
|
data.set(key, [...(data.get(key) ?? []), normalized]);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (contentType.includes("application/x-www-form-urlencoded")) {
|
||||||
|
const bodyText = await request.text();
|
||||||
|
const params = new URLSearchParams(bodyText);
|
||||||
|
for (const [key, value] of params.entries()) {
|
||||||
|
appendValue(key, value);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = await request.formData();
|
||||||
|
for (const [key, value] of formData.entries()) {
|
||||||
|
appendValue(key, typeof value === "string" ? value : value.name);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatValue = (values: string[] | undefined) => (values ?? []).join(", ");
|
||||||
|
|
||||||
|
const labelMap: Record<string, string> = {
|
||||||
|
group_name: "RDA Group Name",
|
||||||
|
charity_number: "Charity Number",
|
||||||
|
group_contact_name: "Group Contact Name",
|
||||||
|
group_contact_address: "Group Contact Address",
|
||||||
|
group_contact_email: "Group Contact Email",
|
||||||
|
group_contact_phone: "Group Contact Telephone",
|
||||||
|
volunteer_first_names: "Volunteer first name(s)",
|
||||||
|
volunteer_last_name: "Volunteer last name",
|
||||||
|
preferred_name: "Preferred name or nickname",
|
||||||
|
preferred_pronouns: "Preferred pronouns",
|
||||||
|
date_of_birth: "Date of birth",
|
||||||
|
sex: "Sex",
|
||||||
|
languages_spoken: "Languages used every day",
|
||||||
|
address_line_1: "Address line 1",
|
||||||
|
address_line_2: "Address line 2",
|
||||||
|
postcode: "Postcode",
|
||||||
|
telephone: "Telephone",
|
||||||
|
mobile: "Mobile",
|
||||||
|
email: "Email",
|
||||||
|
equine_experience: "Previous experience with equines",
|
||||||
|
disability_experience: "Experience volunteering or working with disabled people",
|
||||||
|
skills_qualifications: "Other skills and professional qualifications",
|
||||||
|
placement_considerations: "Placement considerations or support needs",
|
||||||
|
emergency_contact_name: "Emergency contact full name",
|
||||||
|
emergency_contact_relationship: "Emergency contact relationship",
|
||||||
|
emergency_contact_phone: "Emergency contact telephone number",
|
||||||
|
emergency_contact_consent: "Emergency contact consent",
|
||||||
|
reference_1_name: "Reference 1 full name",
|
||||||
|
reference_1_address: "Reference 1 address",
|
||||||
|
reference_1_email: "Reference 1 email",
|
||||||
|
reference_1_phone: "Reference 1 phone",
|
||||||
|
reference_2_name: "Reference 2 full name",
|
||||||
|
reference_2_address: "Reference 2 address",
|
||||||
|
reference_2_email: "Reference 2 email",
|
||||||
|
reference_2_phone: "Reference 2 phone",
|
||||||
|
declaration_truth: "Declaration of truth and accuracy",
|
||||||
|
declaration_notify_changes: "Will notify RDA of any changes",
|
||||||
|
declaration_risk: "Accepts activity risk and precautions",
|
||||||
|
declaration_codes: "Will adhere to the RDA Codes of Conduct",
|
||||||
|
declaration_horses: "Understands horses and ponies are unpredictable",
|
||||||
|
declaration_liability: "Accepts liability statement",
|
||||||
|
disclosure_consent: "Enhanced disclosure and safeguarding consent",
|
||||||
|
authority_checks: "Accepts authority and police record checks",
|
||||||
|
conviction_reporting: "Understands duty to report convictions involving children",
|
||||||
|
photos_consent: "Photo and video consent",
|
||||||
|
declaration_signature: "Signature name",
|
||||||
|
declaration_role: "Signer role",
|
||||||
|
declaration_date: "Declaration date",
|
||||||
|
guardian_name: "Parent or guardian name",
|
||||||
|
guardian_relationship: "Relationship to volunteer",
|
||||||
|
guardian_address_line_1: "Parent or guardian address line 1",
|
||||||
|
guardian_address_line_2: "Parent or guardian address line 2",
|
||||||
|
guardian_postcode: "Parent or guardian postcode",
|
||||||
|
guardian_telephone: "Parent or guardian telephone",
|
||||||
|
guardian_mobile: "Parent or guardian mobile",
|
||||||
|
};
|
||||||
|
|
||||||
|
const requiredFields = [
|
||||||
|
"volunteer_first_names",
|
||||||
|
"volunteer_last_name",
|
||||||
|
"date_of_birth",
|
||||||
|
"address_line_1",
|
||||||
|
"postcode",
|
||||||
|
"email",
|
||||||
|
"emergency_contact_name",
|
||||||
|
"emergency_contact_relationship",
|
||||||
|
"emergency_contact_phone",
|
||||||
|
"reference_1_name",
|
||||||
|
"reference_1_email",
|
||||||
|
"reference_2_name",
|
||||||
|
"reference_2_email",
|
||||||
|
"declaration_truth",
|
||||||
|
"photos_consent",
|
||||||
|
];
|
||||||
|
|
||||||
|
const sectionOrder = [
|
||||||
|
{
|
||||||
|
title: "Group Details",
|
||||||
|
fields: [
|
||||||
|
"group_name",
|
||||||
|
"charity_number",
|
||||||
|
"group_contact_name",
|
||||||
|
"group_contact_address",
|
||||||
|
"group_contact_email",
|
||||||
|
"group_contact_phone",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Volunteer Details",
|
||||||
|
fields: [
|
||||||
|
"volunteer_first_names",
|
||||||
|
"volunteer_last_name",
|
||||||
|
"preferred_name",
|
||||||
|
"preferred_pronouns",
|
||||||
|
"date_of_birth",
|
||||||
|
"sex",
|
||||||
|
"languages_spoken",
|
||||||
|
"address_line_1",
|
||||||
|
"address_line_2",
|
||||||
|
"postcode",
|
||||||
|
"telephone",
|
||||||
|
"mobile",
|
||||||
|
"email",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Specific Information",
|
||||||
|
fields: [
|
||||||
|
"equine_experience",
|
||||||
|
"disability_experience",
|
||||||
|
"skills_qualifications",
|
||||||
|
"placement_considerations",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Emergency Contact",
|
||||||
|
fields: [
|
||||||
|
"emergency_contact_name",
|
||||||
|
"emergency_contact_relationship",
|
||||||
|
"emergency_contact_phone",
|
||||||
|
"emergency_contact_consent",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "References",
|
||||||
|
fields: [
|
||||||
|
"reference_1_name",
|
||||||
|
"reference_1_address",
|
||||||
|
"reference_1_email",
|
||||||
|
"reference_1_phone",
|
||||||
|
"reference_2_name",
|
||||||
|
"reference_2_address",
|
||||||
|
"reference_2_email",
|
||||||
|
"reference_2_phone",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Declaration",
|
||||||
|
fields: [
|
||||||
|
"declaration_truth",
|
||||||
|
"declaration_notify_changes",
|
||||||
|
"declaration_risk",
|
||||||
|
"declaration_codes",
|
||||||
|
"declaration_horses",
|
||||||
|
"declaration_liability",
|
||||||
|
"disclosure_consent",
|
||||||
|
"authority_checks",
|
||||||
|
"conviction_reporting",
|
||||||
|
"photos_consent",
|
||||||
|
"declaration_signature",
|
||||||
|
"declaration_role",
|
||||||
|
"declaration_date",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Parent or Guardian",
|
||||||
|
fields: [
|
||||||
|
"guardian_name",
|
||||||
|
"guardian_relationship",
|
||||||
|
"guardian_address_line_1",
|
||||||
|
"guardian_address_line_2",
|
||||||
|
"guardian_postcode",
|
||||||
|
"guardian_telephone",
|
||||||
|
"guardian_mobile",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handlePost: PagesFunction<Env> = async ({ request, env }) => {
|
||||||
|
const fields = await parseFormFields(request);
|
||||||
|
|
||||||
|
const missingRequired = requiredFields.filter(
|
||||||
|
(field) => !formatValue(fields.get(field)),
|
||||||
|
);
|
||||||
|
if (missingRequired.length > 0) {
|
||||||
|
return jsonResponse(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
error: "Missing required fields.",
|
||||||
|
details: missingRequired.map((field) => labelMap[field] ?? field).join(", "),
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!env.TURNSTILE_SECRET_KEY) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Turnstile secret key not configured." },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnstileToken = formatValue(fields.get("cf-turnstile-response"));
|
||||||
|
if (!turnstileToken) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Turnstile verification failed." },
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyBody = new URLSearchParams({
|
||||||
|
secret: env.TURNSTILE_SECRET_KEY,
|
||||||
|
response: turnstileToken,
|
||||||
|
remoteip: request.headers.get("CF-Connecting-IP") ?? "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const verifyResponse = await fetchWithTimeout(
|
||||||
|
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
||||||
|
body: verifyBody,
|
||||||
|
},
|
||||||
|
8000,
|
||||||
|
"Turnstile",
|
||||||
|
);
|
||||||
|
const verifyResult = (await verifyResponse.json()) as {
|
||||||
|
success?: boolean;
|
||||||
|
"error-codes"?: string[];
|
||||||
|
messages?: string[];
|
||||||
|
hostname?: string;
|
||||||
|
action?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!verifyResult.success) {
|
||||||
|
const details = [
|
||||||
|
verifyResult["error-codes"]?.length
|
||||||
|
? `codes=${verifyResult["error-codes"].join(",")}`
|
||||||
|
: "",
|
||||||
|
verifyResult.messages?.length
|
||||||
|
? `messages=${verifyResult.messages.join(",")}`
|
||||||
|
: "",
|
||||||
|
verifyResult.hostname ? `hostname=${verifyResult.hostname}` : "",
|
||||||
|
verifyResult.action ? `action=${verifyResult.action}` : "",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" | ")
|
||||||
|
.slice(0, 500);
|
||||||
|
return jsonResponse(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
error: "Turnstile verification failed.",
|
||||||
|
details: details || undefined,
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!env.RESEND_API_KEY) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Email provider not configured." },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromEmail = env.CONTACT_FROM_EMAIL;
|
||||||
|
if (!fromEmail) {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Sender email not configured." },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const toEmail = env.CONTACT_TO_EMAIL ?? "calum@muir.in";
|
||||||
|
const volunteerName = [
|
||||||
|
formatValue(fields.get("volunteer_first_names")),
|
||||||
|
formatValue(fields.get("volunteer_last_name")),
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
const bodyText = sectionOrder
|
||||||
|
.map(({ title, fields: sectionFields }) => {
|
||||||
|
const lines = sectionFields
|
||||||
|
.map((field) => {
|
||||||
|
const value = formatValue(fields.get(field));
|
||||||
|
if (!value) return null;
|
||||||
|
return `${labelMap[field] ?? toTitleCase(field)}: ${value}`;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return lines ? `${title}\n${lines}` : "";
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
|
const extraFields = [...fields.keys()].filter(
|
||||||
|
(field) =>
|
||||||
|
field !== "cf-turnstile-response" &&
|
||||||
|
!sectionOrder.some((section) => section.fields.includes(field)),
|
||||||
|
);
|
||||||
|
const extraText = extraFields
|
||||||
|
.map((field) => {
|
||||||
|
const value = formatValue(fields.get(field));
|
||||||
|
return value ? `${labelMap[field] ?? toTitleCase(field)}: ${value}` : "";
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const emailPayload = {
|
||||||
|
from: fromEmail,
|
||||||
|
to: [toEmail],
|
||||||
|
reply_to: formatValue(fields.get("email")) || undefined,
|
||||||
|
subject: `New volunteer application${volunteerName ? `: ${volunteerName}` : ""}`,
|
||||||
|
text: [bodyText, extraText ? `Additional Fields\n${extraText}` : ""]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n\n"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetchWithTimeout(
|
||||||
|
"https://api.resend.com/emails",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${env.RESEND_API_KEY}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(emailPayload),
|
||||||
|
},
|
||||||
|
8000,
|
||||||
|
"Resend",
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
const contentType = response.headers.get("content-type") ?? "";
|
||||||
|
const errorText = contentType.includes("application/json")
|
||||||
|
? JSON.stringify(await response.json().catch(() => ({})))
|
||||||
|
: await response.text();
|
||||||
|
const errorCode = response.headers.get("x-ms-error-code");
|
||||||
|
const details = [
|
||||||
|
`${response.status} ${response.statusText}`.trim(),
|
||||||
|
errorCode ? `code=${errorCode}` : "",
|
||||||
|
errorText.trim(),
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" | ")
|
||||||
|
.slice(0, 500);
|
||||||
|
return jsonResponse(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
error: "Failed to send email.",
|
||||||
|
details,
|
||||||
|
},
|
||||||
|
502,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResponse({ ok: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const onRequest: PagesFunction<Env> = async (context) => {
|
||||||
|
try {
|
||||||
|
if (context.request.method !== "POST") {
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Method not allowed." },
|
||||||
|
405,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return await handlePost(context);
|
||||||
|
} catch (error) {
|
||||||
|
const message =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
|
return jsonResponse(
|
||||||
|
{ ok: false, error: "Unhandled exception.", details: message },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
70
public/application-form.js
Normal file
70
public/application-form.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
(() => {
|
||||||
|
const form = document.querySelector("[data-application-form]");
|
||||||
|
const message = document.querySelector("[data-form-message]");
|
||||||
|
const submitButton = document.querySelector("[data-form-submit]");
|
||||||
|
|
||||||
|
if (!form || !message || !submitButton) return;
|
||||||
|
|
||||||
|
form.addEventListener("submit", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (submitButton.disabled) return;
|
||||||
|
|
||||||
|
submitButton.disabled = true;
|
||||||
|
submitButton.textContent = "Submitting...";
|
||||||
|
message.textContent = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const body = new URLSearchParams();
|
||||||
|
for (const [key, value] of formData.entries()) {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
body.append(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(form.action, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let errorMessage = "Request failed";
|
||||||
|
const errorData = await response.json().catch(() => null);
|
||||||
|
if (errorData?.error) {
|
||||||
|
errorMessage = errorData.details
|
||||||
|
? `${errorData.error} (${errorData.details})`
|
||||||
|
: errorData.error;
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text().catch(() => "");
|
||||||
|
if (errorText.trim()) {
|
||||||
|
errorMessage = `${response.status} ${response.statusText}: ${errorText}`;
|
||||||
|
} else {
|
||||||
|
errorMessage = `${response.status} ${response.statusText}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.textContent =
|
||||||
|
"Thanks. Your participant application has been submitted.";
|
||||||
|
form.reset();
|
||||||
|
if (
|
||||||
|
window.turnstile &&
|
||||||
|
typeof window.turnstile.reset === "function"
|
||||||
|
) {
|
||||||
|
window.turnstile.reset();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.textContent =
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Sorry, something went wrong. Please try again.";
|
||||||
|
} finally {
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.textContent = "Submit application";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -29,12 +29,25 @@ const cardData = [
|
||||||
href: "/support-us",
|
href: "/support-us",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Contact",
|
title: "Contact Us",
|
||||||
imageUrl: "/contact-640.webp",
|
imageUrl: "/contact-640.webp",
|
||||||
imageSrcSet:
|
imageSrcSet:
|
||||||
"/contact-320.webp 320w, /contact-480.webp 480w, /contact-640.webp 640w, /contact-960.webp 960w",
|
"/contact-320.webp 320w, /contact-480.webp 480w, /contact-640.webp 640w, /contact-960.webp 960w",
|
||||||
href: "/contact",
|
href: "/contact",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Apply to Volunteer",
|
||||||
|
imageUrl: "/Vols5-2880w-1024x768.avif",
|
||||||
|
imageSrcSet: "/Vols5-2880w-1024x768.avif 1024w",
|
||||||
|
href: "/volunteer-application",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Apply to Participate",
|
||||||
|
imageUrl: "/Samantha-and-Connolly-960.webp",
|
||||||
|
imageSrcSet:
|
||||||
|
"/Samantha-and-Connolly-480.webp 480w, /Samantha-and-Connolly-640.webp 640w, /Samantha-and-Connolly-960.webp 960w",
|
||||||
|
href: "/participant-application",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -169,7 +182,7 @@ const cardData = [
|
||||||
srcset={
|
srcset={
|
||||||
card.imageSrcSet
|
card.imageSrcSet
|
||||||
}
|
}
|
||||||
sizes="(min-width: 640px) 33vw, 320px"
|
sizes="(min-width: 1024px) 20vw, (min-width: 640px) 50vw, 320px"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
896
src/pages/participant-application.astro
Normal file
896
src/pages/participant-application.astro
Normal file
|
|
@ -0,0 +1,896 @@
|
||||||
|
---
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
import SiteHeader from "../components/SiteHeader.astro";
|
||||||
|
import SiteFooter from "../components/SiteFooter.astro";
|
||||||
|
import { navigationItems } from "../lib/navigation";
|
||||||
|
|
||||||
|
const pageSeo = {
|
||||||
|
title: "Participant Application",
|
||||||
|
description:
|
||||||
|
"Apply to join Highland Group RDA with our participant application form. Share your details, support needs, and emergency contact information online.",
|
||||||
|
keywords:
|
||||||
|
"Highland Group RDA participant application, RDA application form, riding for the disabled application, Highland RDA participant form",
|
||||||
|
canonicalPath: "/participant-application",
|
||||||
|
};
|
||||||
|
|
||||||
|
const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
|
|
||||||
|
const inputClass =
|
||||||
|
"mt-2 w-full rounded-md border border-black/15 bg-white px-3 py-2 text-black placeholder:text-black/35";
|
||||||
|
const textareaClass = `${inputClass} min-h-[120px]`;
|
||||||
|
const selectClass = inputClass;
|
||||||
|
const legendClass =
|
||||||
|
"[font-family:'Merriweather',Helvetica] font-bold text-black text-xl sm:text-2xl";
|
||||||
|
const helpTextClass =
|
||||||
|
"mt-2 [font-family:'Public_Sans',Helvetica] font-semibold text-sm leading-[1.6] text-black/70";
|
||||||
|
|
||||||
|
const supportQuestions = [
|
||||||
|
["allergies", "Do you have any allergies we need to be aware of?"],
|
||||||
|
[
|
||||||
|
"triggers",
|
||||||
|
"Do you have any known triggers, physical, emotional or environmental?",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"simple_instructions",
|
||||||
|
"Would it help if we used very simple instructions?",
|
||||||
|
],
|
||||||
|
["learning_disability", "Do you have a learning disability?"],
|
||||||
|
["neurodiverse", "Are you neurodiverse?"],
|
||||||
|
[
|
||||||
|
"sensitive_to_crowds",
|
||||||
|
"Are you sensitive to strangers or crowds?",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"sensory_sensitivities",
|
||||||
|
"Are you sensitive to light, temperature, noise, clothing or hats?",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"anxiety_low_mood",
|
||||||
|
"Do you experience anxiety, depression or low mood?",
|
||||||
|
],
|
||||||
|
["panic_attacks", "Do you experience panic attacks?"],
|
||||||
|
[
|
||||||
|
"speech_difficulties",
|
||||||
|
"Do you have difficulty with speech or making yourself understood?",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"communication_system",
|
||||||
|
"Do you use a communication system such as Makaton, PECS or AAC?",
|
||||||
|
],
|
||||||
|
["hearing_aid", "Do you wear a hearing aid?"],
|
||||||
|
["cochlear_implant", "Do you have a cochlear implant?"],
|
||||||
|
["bsl", "Do you use BSL to communicate?"],
|
||||||
|
["blind", "Are you blind?"],
|
||||||
|
[
|
||||||
|
"partially_sighted",
|
||||||
|
"Are you partially sighted beyond what glasses or lenses correct?",
|
||||||
|
],
|
||||||
|
["dexterity_difficulties", "Do you have dexterity difficulties?"],
|
||||||
|
["balance_problems", "Do you have problems with balance?"],
|
||||||
|
[
|
||||||
|
"weight_through_feet",
|
||||||
|
"Can you take weight through your feet, for example sit to stand?",
|
||||||
|
],
|
||||||
|
["help_walking", "Do you need any help with walking?"],
|
||||||
|
[
|
||||||
|
"steps_mounting_block",
|
||||||
|
"Can you walk up a few steps, for example onto a mounting block?",
|
||||||
|
],
|
||||||
|
["wheelchair_user", "Are you a wheelchair user?"],
|
||||||
|
["walking_aids", "Do you use any walking aids or supports?"],
|
||||||
|
["prosthetic_limb", "Do you wear a prosthetic limb?"],
|
||||||
|
["long_term_pain", "Do you experience long-term pain?"],
|
||||||
|
[
|
||||||
|
"breathing_stamina",
|
||||||
|
"Do you have difficulties with breathing or stamina?",
|
||||||
|
],
|
||||||
|
["epilepsy", "Do you have epilepsy, and is it controlled by medication?"],
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout {...pageSeo}>
|
||||||
|
{
|
||||||
|
turnstileSiteKey && (
|
||||||
|
<Fragment slot="head">
|
||||||
|
<script
|
||||||
|
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||||
|
async
|
||||||
|
defer
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Fragment slot="head">
|
||||||
|
<script src="/application-form.js" defer data-cfasync="false"></script>
|
||||||
|
</Fragment>
|
||||||
|
<div class="flex min-h-screen flex-col items-center bg-[#e2e2e2]">
|
||||||
|
<main class="w-full">
|
||||||
|
<section class="mt-0 w-full max-w-[1200px] px-0 sm:px-8 mx-auto">
|
||||||
|
<div
|
||||||
|
class="relative w-full overflow-hidden bg-cover bg-[50%_30%] sm:rounded-[7px]"
|
||||||
|
style="background-image: url(/223152689_6028821187158318_7901235528269673744_n-1920w-1.webp);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-b from-black/70 via-black/45 to-black/30"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<SiteHeader
|
||||||
|
navigationItems={navigationItems}
|
||||||
|
theme="dark"
|
||||||
|
className="px-6 sm:px-4"
|
||||||
|
/>
|
||||||
|
<div class="relative px-6 pb-28 pt-6 sm:px-8 sm:pb-24 sm:pt-8">
|
||||||
|
<div
|
||||||
|
class="max-w-none rounded-[7px] bg-black/45 px-5 py-5 backdrop-blur-sm sm:max-w-[820px] sm:px-6 sm:py-6"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="[font-family:'Public_Sans',Helvetica] text-xs font-semibold uppercase tracking-[0.2em] text-white/90 sm:text-sm"
|
||||||
|
>
|
||||||
|
Participant application
|
||||||
|
</p>
|
||||||
|
<div class="mt-3 h-[2px] w-10 bg-white/70"></div>
|
||||||
|
<h1
|
||||||
|
class="mt-4 [font-family:'Merriweather',Helvetica] text-[34px] font-bold leading-[1.15] text-white sm:text-[44px]"
|
||||||
|
>
|
||||||
|
Apply to join Highland Group RDA
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
class="mt-4 max-w-[760px] [font-family:'Public_Sans',Helvetica] text-base font-semibold leading-[1.6] text-white sm:text-lg"
|
||||||
|
>
|
||||||
|
This online form follows the 2025 individual
|
||||||
|
participant application document and gathers the
|
||||||
|
details we need to understand support needs,
|
||||||
|
emergency contacts, and consent.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
class="absolute bottom-4 left-4 h-[48px] w-[190px] sm:bottom-6 sm:left-6 sm:h-[50px] sm:w-[240px]"
|
||||||
|
href="https://www.justgiving.com/charity/highlandgrouprda"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="h-full w-full object-contain"
|
||||||
|
alt="Donate via JustGiving"
|
||||||
|
src="/Button.webp"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="mt-0 w-full max-w-[1200px] px-0 sm:px-8 mx-auto">
|
||||||
|
<div class="bg-[#d6d6d6] px-6 py-6 sm:px-8 sm:py-8">
|
||||||
|
<div class="grid gap-6 lg:grid-cols-[0.75fr_1.25fr]">
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div class="bg-white/75 px-6 py-6">
|
||||||
|
<h2
|
||||||
|
class="[font-family:'Merriweather',Helvetica] text-2xl font-bold text-black sm:text-3xl"
|
||||||
|
>
|
||||||
|
Before you begin
|
||||||
|
</h2>
|
||||||
|
<p
|
||||||
|
class="mt-4 [font-family:'Public_Sans',Helvetica] text-base font-semibold leading-[1.7] text-black"
|
||||||
|
>
|
||||||
|
Please complete the form in full. The
|
||||||
|
information you provide will be kept
|
||||||
|
confidential and used only by Highland Group
|
||||||
|
RDA in line with the Data Protection Act
|
||||||
|
2018.
|
||||||
|
</p>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Required fields are marked with
|
||||||
|
<span class="text-black">*</span>. If a
|
||||||
|
question does not apply, leave it blank.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white/75 px-6 py-6">
|
||||||
|
<h2 class={legendClass}>Why we ask for this</h2>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Your details help us understand medical,
|
||||||
|
communication, and access needs so we can
|
||||||
|
match the right support, coach, and equine
|
||||||
|
safely.
|
||||||
|
</p>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Height and weight are used for suitable
|
||||||
|
equine matching in line with welfare
|
||||||
|
policies and are handled confidentially by
|
||||||
|
the group coach.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form
|
||||||
|
class="space-y-6"
|
||||||
|
action="/api/participant-application"
|
||||||
|
method="post"
|
||||||
|
data-application-form
|
||||||
|
>
|
||||||
|
{
|
||||||
|
!turnstileSiteKey && (
|
||||||
|
<p class="rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm font-semibold text-amber-900">
|
||||||
|
Turnstile is not configured. Set
|
||||||
|
`PUBLIC_TURNSTILE_SITE_KEY` and redeploy
|
||||||
|
to enable the form.
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>Part 1. Your Details</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Participant details and primary contact
|
||||||
|
information.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Who is filling in the form? *
|
||||||
|
</label>
|
||||||
|
<div class="mt-3 grid gap-3 sm:grid-cols-3">
|
||||||
|
{
|
||||||
|
[
|
||||||
|
"Participant",
|
||||||
|
"Parent",
|
||||||
|
"Guardian / Carer",
|
||||||
|
].map((option) => (
|
||||||
|
<label class="flex items-center gap-3 rounded-md border border-black/10 bg-white px-3 py-3 text-sm font-semibold text-black">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="filled_by"
|
||||||
|
value={option}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span>{option}</span>
|
||||||
|
</label>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Participant first name(s) *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="participant_first_names"
|
||||||
|
autocomplete="given-name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Last name *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="participant_last_name"
|
||||||
|
autocomplete="family-name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Preferred name or nickname
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="preferred_name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Preferred pronouns
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="preferred_pronouns"
|
||||||
|
placeholder="For example: she/her, they/them"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Date of birth *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="date"
|
||||||
|
name="date_of_birth"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Sex
|
||||||
|
</label>
|
||||||
|
<select class={selectClass} name="sex">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<option>Female</option>
|
||||||
|
<option>Male</option>
|
||||||
|
<option>I identify in another way</option>
|
||||||
|
<option>Prefer not to say</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
If you are not fluent in English,
|
||||||
|
which language(s) do you use every
|
||||||
|
day?
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="languages_spoken"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 1 *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="address_line_1"
|
||||||
|
autocomplete="address-line1"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 2
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="address_line_2"
|
||||||
|
autocomplete="address-line2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Postcode *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="postcode"
|
||||||
|
autocomplete="postal-code"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Telephone
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="tel"
|
||||||
|
name="telephone"
|
||||||
|
autocomplete="tel"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Mobile
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="tel"
|
||||||
|
name="mobile"
|
||||||
|
autocomplete="tel-national"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Email *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
autocomplete="email"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Previous experience of these activities
|
||||||
|
</label>
|
||||||
|
<div class="mt-3 grid gap-3 sm:grid-cols-3">
|
||||||
|
{
|
||||||
|
["Riding", "Carriage Driving", "Vaulting"].map(
|
||||||
|
(item) => (
|
||||||
|
<label class="flex items-center gap-3 rounded-md border border-black/10 bg-white px-3 py-3 text-sm font-semibold text-black">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="activity_experience"
|
||||||
|
value={item}
|
||||||
|
/>
|
||||||
|
<span>{item}</span>
|
||||||
|
</label>
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>
|
||||||
|
Part 2. Specific Information About You
|
||||||
|
</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Tick all that apply. We may contact you for
|
||||||
|
more information where needed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-3 sm:grid-cols-2">
|
||||||
|
{
|
||||||
|
supportQuestions.map(([name, label]) => (
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name={name}
|
||||||
|
value="Yes"
|
||||||
|
/>
|
||||||
|
<span>{label}</span>
|
||||||
|
</label>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Tell us about the disability or medical
|
||||||
|
condition and how it affects the
|
||||||
|
participant. Include medication or any
|
||||||
|
support we should know about.
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
class={textareaClass}
|
||||||
|
name="condition_details"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-[1fr_160px_1fr_160px]">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Height
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="participant_height"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Unit
|
||||||
|
</label>
|
||||||
|
<select class={selectClass} name="height_unit">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<option>cm</option>
|
||||||
|
<option>ft & in</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Weight
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="participant_weight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Unit
|
||||||
|
</label>
|
||||||
|
<select class={selectClass} name="weight_unit">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<option>kg</option>
|
||||||
|
<option>st & lb</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>
|
||||||
|
Part 3. Additional Information
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-6 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Interested in competing?
|
||||||
|
</label>
|
||||||
|
<div class="mt-3 flex gap-3">
|
||||||
|
{
|
||||||
|
["Yes", "No"].map((option) => (
|
||||||
|
<label class="flex items-center gap-3 rounded-md border border-black/10 bg-white px-4 py-3 text-sm font-semibold text-black">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="interested_in_competing"
|
||||||
|
value={option}
|
||||||
|
/>
|
||||||
|
<span>{option}</span>
|
||||||
|
</label>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Classification may not be relevant
|
||||||
|
for non-ridden activity.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Have you been classified?
|
||||||
|
</label>
|
||||||
|
<div class="mt-3 flex gap-3">
|
||||||
|
{
|
||||||
|
["Yes", "No"].map((option) => (
|
||||||
|
<label class="flex items-center gap-3 rounded-md border border-black/10 bg-white px-4 py-3 text-sm font-semibold text-black">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="been_classified"
|
||||||
|
value={option}
|
||||||
|
/>
|
||||||
|
<span>{option}</span>
|
||||||
|
</label>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
If yes, what is your classification?
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="classification_details"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Date given
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="date"
|
||||||
|
name="classification_date"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>
|
||||||
|
Emergency Contact Details
|
||||||
|
</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
We need to know who to contact if the
|
||||||
|
participant becomes unwell or is injured
|
||||||
|
during RDA activities.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Emergency contact name and
|
||||||
|
relationship *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="emergency_contact_name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Emergency contact number *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="tel"
|
||||||
|
name="emergency_contact_phone"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="mt-6 flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name="emergency_contact_consent"
|
||||||
|
value="Yes"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
I confirm that I have the consent of the
|
||||||
|
person named above to be contacted in an
|
||||||
|
emergency during the course of RDA
|
||||||
|
activities.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>
|
||||||
|
Part 4. Declaration
|
||||||
|
</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
These confirmations reflect the wording in
|
||||||
|
the application document.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 space-y-3">
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name="declaration_confirm"
|
||||||
|
value="Yes"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
I wish to join an RDA Group as a
|
||||||
|
participant and confirm that the
|
||||||
|
information provided on this form is
|
||||||
|
true and accurate to the best of my
|
||||||
|
knowledge. *
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name="provide_medical_details"
|
||||||
|
value="Yes"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
I agree to provide additional details
|
||||||
|
about medical conditions if
|
||||||
|
requested, and to obtain a medical
|
||||||
|
report where needed.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name="notify_changes"
|
||||||
|
value="Yes"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
I will notify RDA immediately of any
|
||||||
|
changes to the details or
|
||||||
|
information given on this form.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name="risk_acknowledgement"
|
||||||
|
value="Yes"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
I understand that horse-related
|
||||||
|
activities carry inherent risks and
|
||||||
|
agree to take reasonable precautions
|
||||||
|
and follow advice and instructions.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name="vaulting_acknowledgement"
|
||||||
|
value="Yes"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
For vaulting only, I understand the
|
||||||
|
specific risks involved in
|
||||||
|
participating on the barrel or horse
|
||||||
|
without a hat.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Photographs and videos consent *
|
||||||
|
</label>
|
||||||
|
<div class="mt-3 flex flex-wrap gap-3">
|
||||||
|
{
|
||||||
|
["Yes", "No"].map((option) => (
|
||||||
|
<label class="flex items-center gap-3 rounded-md border border-black/10 bg-white px-4 py-3 text-sm font-semibold text-black">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="photos_consent"
|
||||||
|
value={option}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span>{option}</span>
|
||||||
|
</label>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
This covers images used for training,
|
||||||
|
publicity, websites, social media,
|
||||||
|
newsletters, and marketing materials.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="mt-6 flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input
|
||||||
|
class="mt-1"
|
||||||
|
type="checkbox"
|
||||||
|
name="newsletter_consent"
|
||||||
|
value="Yes"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
I also consent to be sent RDA
|
||||||
|
newsletters and updates from the group.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Signature name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="declaration_signature"
|
||||||
|
placeholder="Participant / Parent / Guardian / Carer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Date
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="date"
|
||||||
|
name="declaration_date"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>
|
||||||
|
Part 5. Parent or Legal Guardian
|
||||||
|
</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Complete this section if the form is being
|
||||||
|
submitted by a parent or legal guardian, or
|
||||||
|
if the participant is under 18.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="guardian_name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Relationship to applicant
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="guardian_relationship"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 1
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="guardian_address_line_1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 2
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="guardian_address_line_2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Postcode
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="guardian_postcode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Telephone
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="tel"
|
||||||
|
name="guardian_telephone"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Mobile
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class={inputClass}
|
||||||
|
type="tel"
|
||||||
|
name="guardian_mobile"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="bg-white/75 px-6 py-6">
|
||||||
|
<div
|
||||||
|
class="cf-turnstile"
|
||||||
|
data-sitekey={turnstileSiteKey}
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="mt-4 text-sm font-semibold text-black"
|
||||||
|
data-form-message
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
class="mt-4 inline-flex items-center rounded-full border border-black/20 px-5 py-2 text-sm font-semibold uppercase tracking-wide text-black transition hover:bg-black hover:text-white"
|
||||||
|
type="submit"
|
||||||
|
data-form-submit
|
||||||
|
>
|
||||||
|
Submit application
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<SiteFooter fullBleedOnMobile />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
608
src/pages/volunteer-application.astro
Normal file
608
src/pages/volunteer-application.astro
Normal file
|
|
@ -0,0 +1,608 @@
|
||||||
|
---
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
import SiteHeader from "../components/SiteHeader.astro";
|
||||||
|
import SiteFooter from "../components/SiteFooter.astro";
|
||||||
|
import { navigationItems } from "../lib/navigation";
|
||||||
|
|
||||||
|
const pageSeo = {
|
||||||
|
title: "Volunteer Application",
|
||||||
|
description:
|
||||||
|
"Apply to volunteer with Highland Group RDA. Complete the volunteer application form with your details, experience, references, and declarations.",
|
||||||
|
keywords:
|
||||||
|
"Highland Group RDA volunteer application, RDA volunteer form, volunteer with horses Highlands, disabled riding charity volunteer",
|
||||||
|
canonicalPath: "/volunteer-application",
|
||||||
|
};
|
||||||
|
|
||||||
|
const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
|
|
||||||
|
const inputClass =
|
||||||
|
"mt-2 w-full rounded-md border border-black/15 bg-white px-3 py-2 text-black placeholder:text-black/35";
|
||||||
|
const textareaClass = `${inputClass} min-h-[120px]`;
|
||||||
|
const selectClass = inputClass;
|
||||||
|
const legendClass =
|
||||||
|
"[font-family:'Merriweather',Helvetica] font-bold text-black text-xl sm:text-2xl";
|
||||||
|
const helpTextClass =
|
||||||
|
"mt-2 [font-family:'Public_Sans',Helvetica] font-semibold text-sm leading-[1.6] text-black/70";
|
||||||
|
|
||||||
|
const groupDetails = [
|
||||||
|
["RDA Group Name", "Highland Group RDA"],
|
||||||
|
["Charity Number", "SC007357"],
|
||||||
|
["Group Contact Name", "Steve Byford"],
|
||||||
|
[
|
||||||
|
"Contact Address",
|
||||||
|
"Sandycroft, Reelig, Kirkhill, IV5 7PP",
|
||||||
|
],
|
||||||
|
["Contact Email", "info@highlandgrouprda.org.uk"],
|
||||||
|
["Contact Telephone", "07759 327690"],
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout {...pageSeo}>
|
||||||
|
{
|
||||||
|
turnstileSiteKey && (
|
||||||
|
<Fragment slot="head">
|
||||||
|
<script
|
||||||
|
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||||
|
async
|
||||||
|
defer
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Fragment slot="head">
|
||||||
|
<script src="/application-form.js" defer data-cfasync="false"></script>
|
||||||
|
</Fragment>
|
||||||
|
<div class="flex min-h-screen flex-col items-center bg-[#e2e2e2]">
|
||||||
|
<main class="w-full">
|
||||||
|
<section class="mt-0 w-full max-w-[1200px] px-0 sm:px-8 mx-auto">
|
||||||
|
<div
|
||||||
|
class="relative w-full overflow-hidden bg-cover bg-[50%_35%] sm:rounded-[7px]"
|
||||||
|
style="background-image: url(/274864973_7295359163837841_5927911936073070889_n-1920w.webp);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-b from-black/70 via-black/45 to-black/30"
|
||||||
|
></div>
|
||||||
|
<SiteHeader
|
||||||
|
navigationItems={navigationItems}
|
||||||
|
theme="dark"
|
||||||
|
className="px-6 sm:px-4"
|
||||||
|
/>
|
||||||
|
<div class="relative px-6 pb-28 pt-6 sm:px-8 sm:pb-24 sm:pt-8">
|
||||||
|
<div
|
||||||
|
class="max-w-none rounded-[7px] bg-black/45 px-5 py-5 backdrop-blur-sm sm:max-w-[820px] sm:px-6 sm:py-6"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="[font-family:'Public_Sans',Helvetica] text-xs font-semibold uppercase tracking-[0.2em] text-white/90 sm:text-sm"
|
||||||
|
>
|
||||||
|
Volunteer application
|
||||||
|
</p>
|
||||||
|
<div class="mt-3 h-[2px] w-10 bg-white/70"></div>
|
||||||
|
<h1
|
||||||
|
class="mt-4 [font-family:'Merriweather',Helvetica] text-[34px] font-bold leading-[1.15] text-white sm:text-[44px]"
|
||||||
|
>
|
||||||
|
Apply to volunteer with Highland Group RDA
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
class="mt-4 max-w-[760px] [font-family:'Public_Sans',Helvetica] text-base font-semibold leading-[1.6] text-white sm:text-lg"
|
||||||
|
>
|
||||||
|
This online form follows the 2026 volunteer
|
||||||
|
application document and gathers your details,
|
||||||
|
relevant experience, references, emergency
|
||||||
|
contact, and declarations.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
class="absolute bottom-4 left-4 h-[48px] w-[190px] sm:bottom-6 sm:left-6 sm:h-[50px] sm:w-[240px]"
|
||||||
|
href="https://www.justgiving.com/charity/highlandgrouprda"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="h-full w-full object-contain"
|
||||||
|
alt="Donate via JustGiving"
|
||||||
|
src="/Button.webp"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="mt-0 w-full max-w-[1200px] px-0 sm:px-8 mx-auto">
|
||||||
|
<div class="bg-[#d6d6d6] px-6 py-6 sm:px-8 sm:py-8">
|
||||||
|
<div class="grid gap-6 lg:grid-cols-[0.75fr_1.25fr]">
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div class="bg-white/75 px-6 py-6">
|
||||||
|
<h2
|
||||||
|
class="[font-family:'Merriweather',Helvetica] text-2xl font-bold text-black sm:text-3xl"
|
||||||
|
>
|
||||||
|
Group details
|
||||||
|
</h2>
|
||||||
|
<p
|
||||||
|
class="mt-4 [font-family:'Public_Sans',Helvetica] text-base font-semibold leading-[1.7] text-black"
|
||||||
|
>
|
||||||
|
This section is prefilled from the source
|
||||||
|
application document and identifies the RDA
|
||||||
|
group the form is being submitted to.
|
||||||
|
</p>
|
||||||
|
<dl class="mt-5 space-y-4">
|
||||||
|
{
|
||||||
|
groupDetails.map(([label, value]) => (
|
||||||
|
<div class="border-t border-black/10 pt-4 first:border-t-0 first:pt-0">
|
||||||
|
<dt class="text-xs font-semibold uppercase tracking-[0.18em] text-black/55">
|
||||||
|
{label}
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 [font-family:'Public_Sans',Helvetica] text-base font-semibold leading-[1.6] text-black">
|
||||||
|
{value}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white/75 px-6 py-6">
|
||||||
|
<h2 class={legendClass}>Before you begin</h2>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Please fill this in clearly and completely.
|
||||||
|
All information is treated as confidential
|
||||||
|
and used only for RDA volunteering
|
||||||
|
activities in line with the Data Protection
|
||||||
|
Act 2018.
|
||||||
|
</p>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
The form asks for two references from people
|
||||||
|
who are not related to you and have known
|
||||||
|
you for at least two years.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form
|
||||||
|
class="space-y-6"
|
||||||
|
action="/api/volunteer-application"
|
||||||
|
method="post"
|
||||||
|
data-application-form
|
||||||
|
>
|
||||||
|
<input type="hidden" name="group_name" value="Highland Group RDA" />
|
||||||
|
<input type="hidden" name="charity_number" value="SC007357" />
|
||||||
|
<input type="hidden" name="group_contact_name" value="Steve Byford" />
|
||||||
|
<input type="hidden" name="group_contact_address" value="Sandycroft, Reelig, Kirkhill, IV5 7PP" />
|
||||||
|
<input type="hidden" name="group_contact_email" value="info@highlandgrouprda.org.uk" />
|
||||||
|
<input type="hidden" name="group_contact_phone" value="07759 327690" />
|
||||||
|
|
||||||
|
{
|
||||||
|
!turnstileSiteKey && (
|
||||||
|
<p class="rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm font-semibold text-amber-900">
|
||||||
|
Turnstile is not configured. Set
|
||||||
|
`PUBLIC_TURNSTILE_SITE_KEY` and redeploy
|
||||||
|
to enable the form.
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>Part 1. Your Details</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Details of the volunteer applicant.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
First name(s) *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="volunteer_first_names" autocomplete="given-name" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Last name *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="volunteer_last_name" autocomplete="family-name" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Preferred name or nickname
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="preferred_name" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Preferred pronouns
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="preferred_pronouns" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Date of birth *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="date" name="date_of_birth" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Sex
|
||||||
|
</label>
|
||||||
|
<select class={selectClass} name="sex">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<option>Female</option>
|
||||||
|
<option>Male</option>
|
||||||
|
<option>I identify in another way</option>
|
||||||
|
<option>Prefer not to say</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
If you are not fluent in English, which language/s do you use on a daily basis?
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="languages_spoken" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 1 *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="address_line_1" autocomplete="address-line1" required />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 2
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="address_line_2" autocomplete="address-line2" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Postcode *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="postcode" autocomplete="postal-code" required />
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Telephone
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="tel" name="telephone" autocomplete="tel" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Mobile
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="tel" name="mobile" autocomplete="tel-national" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Email *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="email" name="email" autocomplete="email" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>
|
||||||
|
Part 2. Specific Information About You
|
||||||
|
</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
This section helps place you in a suitable
|
||||||
|
volunteering role and identify any support
|
||||||
|
needs.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 space-y-5">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Please tell us if you have any previous experience with equines.
|
||||||
|
</label>
|
||||||
|
<textarea class={textareaClass} name="equine_experience"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Please tell us about any experience volunteering or working with people with disabilities.
|
||||||
|
</label>
|
||||||
|
<textarea class={textareaClass} name="disability_experience"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Please tell us about any other skills and professional qualifications that may help.
|
||||||
|
</label>
|
||||||
|
<textarea class={textareaClass} name="skills_qualifications"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Is there any information we may need to consider when placing you as a volunteer to ensure you have a positive experience?
|
||||||
|
</label>
|
||||||
|
<textarea class={textareaClass} name="placement_considerations"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>
|
||||||
|
Part 3. Emergency Contact Details
|
||||||
|
</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
We need to know who to contact if you become
|
||||||
|
unwell or are injured while volunteering.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Full name *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="emergency_contact_name" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Relationship to you *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="emergency_contact_relationship" required />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Telephone number *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="tel" name="emergency_contact_phone" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="mt-6 flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="emergency_contact_consent" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I confirm I have consent of the individual listed above to be contacted in the case of an emergency during the course of RDA activities.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>References</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
Please provide two references. They should
|
||||||
|
not be related to you and should ideally
|
||||||
|
know you in a professional capacity.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-6">
|
||||||
|
<div class="rounded-[7px] border border-black/10 bg-white px-5 py-5">
|
||||||
|
<h3 class="[font-family:'Merriweather',Helvetica] text-lg font-bold text-black">
|
||||||
|
Reference 1
|
||||||
|
</h3>
|
||||||
|
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Full name *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="reference_1_name" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Phone
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="tel" name="reference_1_phone" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address
|
||||||
|
</label>
|
||||||
|
<textarea class={textareaClass} name="reference_1_address"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Email *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="email" name="reference_1_email" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-[7px] border border-black/10 bg-white px-5 py-5">
|
||||||
|
<h3 class="[font-family:'Merriweather',Helvetica] text-lg font-bold text-black">
|
||||||
|
Reference 2
|
||||||
|
</h3>
|
||||||
|
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Full name *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="reference_2_name" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Phone
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="tel" name="reference_2_phone" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address
|
||||||
|
</label>
|
||||||
|
<textarea class={textareaClass} name="reference_2_address"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Email *
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="email" name="reference_2_email" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>Part 4. Declaration</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
These declarations reflect the wording in
|
||||||
|
the volunteer application document.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 space-y-3">
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="declaration_truth" value="Yes" required />
|
||||||
|
<span>
|
||||||
|
I wish to apply to join an RDA Group as a volunteer, and confirm that all details given on this form are true and accurate to the best of my knowledge. *
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="declaration_notify_changes" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I will notify RDA immediately if any of the details or information provided on this form change in any way.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="declaration_risk" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I recognise that this activity involves risk and that I must take all reasonable precautions and follow advice properly given at all times.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="declaration_codes" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I confirm that I will adhere to the RDA Codes of Conduct.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="declaration_horses" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I understand that horses and ponies are unpredictable and may react in a way that could accidentally knock a volunteer.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="declaration_liability" value="Yes" />
|
||||||
|
<span>
|
||||||
|
In the absence of negligence on the part of the RDA Group or RDA UK, I understand and accept that no liability will attach to either party.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="disclosure_consent" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I consent to an enhanced disclosure check being made if applicable and will abide by the group’s policies and procedures, including safeguarding requirements.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="authority_checks" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I understand the Group reserves the right to make reference to Local Authority Social Services and Police Records to verify information given on this form.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-start gap-3 rounded-md border border-black/10 bg-white px-4 py-4 text-sm font-semibold leading-[1.5] text-black">
|
||||||
|
<input class="mt-1" type="checkbox" name="conviction_reporting" value="Yes" />
|
||||||
|
<span>
|
||||||
|
I understand it is the duty of all Group personnel, coaches and volunteers to report any conviction involving children.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Photographs and videos consent *
|
||||||
|
</label>
|
||||||
|
<div class="mt-3 flex flex-wrap gap-3">
|
||||||
|
{["Yes", "No"].map((option) => (
|
||||||
|
<label class="flex items-center gap-3 rounded-md border border-black/10 bg-white px-4 py-3 text-sm font-semibold text-black">
|
||||||
|
<input type="radio" name="photos_consent" value={option} required />
|
||||||
|
<span>{option}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-3">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Signature name
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="declaration_signature" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Role
|
||||||
|
</label>
|
||||||
|
<select class={selectClass} name="declaration_role">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<option>Volunteer</option>
|
||||||
|
<option>Parent</option>
|
||||||
|
<option>Guardian</option>
|
||||||
|
<option>Carer</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Date
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="date" name="declaration_date" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="bg-white/75 px-6 py-6">
|
||||||
|
<legend class={legendClass}>Parent or Guardian</legend>
|
||||||
|
<p class={helpTextClass}>
|
||||||
|
If you are under 18, this form must also be
|
||||||
|
signed by a parent or guardian.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-6 grid gap-4 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="guardian_name" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Relationship to volunteer
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="guardian_relationship" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 1
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="guardian_address_line_1" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Address line 2
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="guardian_address_line_2" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Postcode
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="text" name="guardian_postcode" />
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Telephone
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="tel" name="guardian_telephone" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-semibold text-black">
|
||||||
|
Mobile
|
||||||
|
</label>
|
||||||
|
<input class={inputClass} type="tel" name="guardian_mobile" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="bg-white/75 px-6 py-6">
|
||||||
|
<div class="cf-turnstile" data-sitekey={turnstileSiteKey}></div>
|
||||||
|
<p class="mt-4 text-sm font-semibold text-black" data-form-message></p>
|
||||||
|
<button
|
||||||
|
class="mt-4 inline-flex items-center rounded-full border border-black/20 px-5 py-2 text-sm font-semibold uppercase tracking-wide text-black transition hover:bg-black hover:text-white"
|
||||||
|
type="submit"
|
||||||
|
data-form-submit
|
||||||
|
>
|
||||||
|
Submit application
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<SiteFooter fullBleedOnMobile />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
Loading…
Reference in a new issue