From a8abe3f3d8bb0a15591e4a0f4d3a62684c3a5587 Mon Sep 17 00:00:00 2001 From: Hannah Date: Mon, 13 Jul 2026 23:45:25 -0600 Subject: [PATCH] vbs registration --- Client/src/app/app-routing.module.ts | 5 + Client/src/app/app.module.ts | 15 +- .../app/components/home/home.component.html | 15 +- .../src/app/components/home/home.component.ts | 7 +- .../secondary-page.component.css | 8 + .../secondary-page.component.html | 4 +- .../secondary-page.component.ts | 6 +- .../vbs-page/vbs-page.component.css | 100 +++++++ .../vbs-page/vbs-page.component.html | 216 +++++++++++++++ .../components/vbs-page/vbs-page.component.ts | 253 ++++++++++++++++++ Client/src/app/constants/urls.ts | 1 + Client/src/app/services/email.service.ts | 7 +- Server/src/routes/api/api.js | 2 + Server/src/routes/api/vbs.js | 181 +++++++++++++ 14 files changed, 805 insertions(+), 15 deletions(-) create mode 100644 Client/src/app/components/vbs-page/vbs-page.component.css create mode 100644 Client/src/app/components/vbs-page/vbs-page.component.html create mode 100644 Client/src/app/components/vbs-page/vbs-page.component.ts create mode 100644 Server/src/routes/api/vbs.js diff --git a/Client/src/app/app-routing.module.ts b/Client/src/app/app-routing.module.ts index c981293..bff4782 100644 --- a/Client/src/app/app-routing.module.ts +++ b/Client/src/app/app-routing.module.ts @@ -22,6 +22,7 @@ import { ImageComponent } from './components/image/image.component'; import { VideoComponent } from './components/video/video.component'; import { FCCPermitPageComponent } from './components/fcc-permit-page/fcc-permit-page.component'; import { BalloonComponent } from './components/balloon/balloon.component'; +import { VbsPageComponent } from './components/vbs-page/vbs-page.component'; const routes = [ @@ -120,6 +121,10 @@ const routes = { path: 'balloon', component: BalloonComponent + }, + { + path: 'vbs', + component: VbsPageComponent } ] diff --git a/Client/src/app/app.module.ts b/Client/src/app/app.module.ts index 1d95d26..e9b7207 100644 --- a/Client/src/app/app.module.ts +++ b/Client/src/app/app.module.ts @@ -18,7 +18,10 @@ import { MatButtonModule, MatOptionModule, MatRadioModule, MatAutocompleteModule, - MatCheckboxModule} from '@angular/material'; + MatCheckboxModule, + MatDatepickerModule, + MatNativeDateModule, + MatCardModule} from '@angular/material'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import 'hammerjs'; @@ -86,7 +89,7 @@ import { VideoComponent } from './components/video/video.component'; import { YoutubeListService } from './components/youtube-list/youtube-list-service'; import { VideoItemComponent } from './components/video-item/video-item.component'; import { YoutubePopupComponent } from './components/popups/youtube-popup/youtube-popup.component'; - +import { VbsPageComponent } from './components/vbs-page/vbs-page.component'; @@ -143,7 +146,8 @@ import { YoutubePopupComponent } from './components/popups/youtube-popup/youtube YoutubeListComponent, VideoComponent, VideoItemComponent, - YoutubePopupComponent + YoutubePopupComponent, + VbsPageComponent ], imports: [ BrowserModule, @@ -162,7 +166,10 @@ import { YoutubePopupComponent } from './components/popups/youtube-popup/youtube MatDialogModule, MatAutocompleteModule, MatRadioModule, - MatCheckboxModule + MatCheckboxModule, + MatCardModule, + MatDatepickerModule, + MatNativeDateModule ], providers: [LoginService,PrintService,UserService,GoogleAnalyticsService,SermonService,TransactionService,EventService,EmailService,MissionarySupportService,YoutubeListService,WindowRefService], entryComponents: [AddSermonPopupComponent, diff --git a/Client/src/app/components/home/home.component.html b/Client/src/app/components/home/home.component.html index f960af5..5fd4f29 100644 --- a/Client/src/app/components/home/home.component.html +++ b/Client/src/app/components/home/home.component.html @@ -40,10 +40,19 @@
-
-
- +
+
+ + +
+ Click To Register! +
+
+
+
+   +
diff --git a/Client/src/app/components/home/home.component.ts b/Client/src/app/components/home/home.component.ts index 604f036..9514918 100644 --- a/Client/src/app/components/home/home.component.ts +++ b/Client/src/app/components/home/home.component.ts @@ -16,7 +16,7 @@ export class HomeComponent { backgroundTop: string = "0px"; public get showVGC() : boolean { - +return false; let maxDate = new Date(2024,7,15,11); // August 15th 2018 -- Set the month one month behind since JavaScript dates are 0 based let now = new Date(); if (now.getFullYear() > maxDate.getFullYear()) return false; @@ -32,7 +32,7 @@ export class HomeComponent { return true; } - + public get showSpecial() : boolean { let maxDate = new Date(2026,5,26,11); // September 18th 2018 -- Set the month one month behind since JavaScript dates are 0 based @@ -63,7 +63,8 @@ export class HomeComponent { return true; } public get showVBS(): boolean { - let maxDate = new Date(2019,7,22); // August 22nd 2018 -- Set the month one month behind since JavaScript dates are 0 based + return false; + let maxDate = new Date(2026,7,13); // August 22nd 2018 -- Set the month one month behind since JavaScript dates are 0 based let now = new Date(); if (now.getFullYear() > maxDate.getFullYear()) return false; if (now.getFullYear() == maxDate.getFullYear()) { diff --git a/Client/src/app/components/secondary-page/secondary-page.component.css b/Client/src/app/components/secondary-page/secondary-page.component.css index 80b1602..8c5017c 100644 --- a/Client/src/app/components/secondary-page/secondary-page.component.css +++ b/Client/src/app/components/secondary-page/secondary-page.component.css @@ -12,6 +12,10 @@ padding: 20px 10px 10px 10px; } +.width100{ + width:100%; +} + .side-bar{ width: 30%; display: table-cell; @@ -27,6 +31,10 @@ vertical-align: top; } +.display-none { + display: none; +} + @media(max-width:800px){ .side-bar{ display: block; diff --git a/Client/src/app/components/secondary-page/secondary-page.component.html b/Client/src/app/components/secondary-page/secondary-page.component.html index f21bde6..8564fe4 100644 --- a/Client/src/app/components/secondary-page/secondary-page.component.html +++ b/Client/src/app/components/secondary-page/secondary-page.component.html @@ -1,9 +1,9 @@
-
+
diff --git a/Client/src/app/components/secondary-page/secondary-page.component.ts b/Client/src/app/components/secondary-page/secondary-page.component.ts index 9f2d400..ac89748 100644 --- a/Client/src/app/components/secondary-page/secondary-page.component.ts +++ b/Client/src/app/components/secondary-page/secondary-page.component.ts @@ -7,9 +7,11 @@ import { Component, Input, OnInit } from '@angular/core'; }) export class SecondaryPageComponent implements OnInit { @Input() - public hideSideBarOnMobile: boolean; + public hideSideBarOnMobile: boolean = true; @Input() - public fixedSideBar: boolean; + public fixedSideBar: boolean = true; + @Input() + public hideAlways: boolean = true; constructor(){ diff --git a/Client/src/app/components/vbs-page/vbs-page.component.css b/Client/src/app/components/vbs-page/vbs-page.component.css new file mode 100644 index 0000000..de10736 --- /dev/null +++ b/Client/src/app/components/vbs-page/vbs-page.component.css @@ -0,0 +1,100 @@ +.w-50 { + width: 50%; +} + +.w-100 { + width: 100%; +} + +.section-header{ + font-size: 1.2em; + font-weight: bold; + border-bottom: 1px solid gray; + margin-bottom: 10px; +} + +.center { + text-align: center; +} + +.form-row-1-col { + margin-left: 10px; + margin-right: 10px; +} + +.form-row-2-col { + margin-left: 10px; + margin-right: 10px; + gap: 10px; + display: flex; +} + +@media(max-width:600px){ + .form-row-2-col { + flex-direction: column; + } +} + +.form-container { + width: 800px; + margin-left: auto; + margin-right: auto; +} + +@media(max-width:800px){ + .form-container { + width: auto; + margin-left: 10px; + margin-right: 10px; + } +} + +.error { + color: red; +} + +mat-label { + font-weight: bold; + overflow-wrap: break-word; +} + +label { + display: flex; + flex-direction: column; + font-size: 1.15rem; + margin-top: 35px; + color: black; + font-weight: 400; + font-family: Arial, Helvetica, sans-serif +} + +ol > li { + margin-left: 20px; +} + +mat-label { + font-size: 1.15rem; + color: black; + font-weight: 400; +} + +label.sub { + margin-top: 0px; + font-size: .85rem; +} + +mat-radio-group { + display: flex; + flex-direction: row; + gap: 15px; +} + +mat-radio-button { + display: flex; + flex-direction: row; + margin: 15px 0; +} + +.hidden { + display: none; +} \ No newline at end of file diff --git a/Client/src/app/components/vbs-page/vbs-page.component.html b/Client/src/app/components/vbs-page/vbs-page.component.html new file mode 100644 index 0000000..3f9fa19 --- /dev/null +++ b/Client/src/app/components/vbs-page/vbs-page.component.html @@ -0,0 +1,216 @@ + + +
+
+

+ Vacation Bible School Registration +

+
+
+

Student Information

+
+ + First Name + + Please enter student first name + + + + Last Name + + Please enter student last name + +
+
+ + Grade + + Please enter student grade + + + Birthday + + + + Please enter student birthday + +
+
+
+ + + Yes + No + +
+ + Allergies + + Please enter student allergies + +
+
+
+ + + Yes + No + +
+ + Dietary Restrictions + + Please enter student dietary restrictions + +
+
+
+ + + Yes + No + +
+
+
+
+ + + Yes + No + +
+
+
+ +
+
+ +
+

Parent/Guardian Information

+
+ + First Name + + Please enter parent/guardian name + + + + Last Name + + Please enter parent/guardian name + +
+
+ + Address Line 1 + + Please enter parent/guardian address + + + + Address Line 2 + + +
+
+ + City + + Please enter parent/guardian city + + + + State * + + + -- Select a State -- + + + + {{ state.name }} + + + Please enter parent/guardian state + + + + + Zip + + Please enter parent/guardian zip + +
+
+ + Home Phone + + + + + Mobile Phone + + Please enter parent/guardian mobile phone (must be 10 digits) + +
+
+ + Work Phone + + + + + Email + + Please enter parent/guardian email + +
+
+ + +
+
+ +
+

Emergency Contact Information

+
+ Same as Parent/Guardian +
+
+ + First Name + + + + + Last Name + + + + + Phone + + +
+ + +
+ + +
+
+ +
+

Complete!

+

{{form.get('firstName').value}} has been registered!

+
+ +
+
+ +
+
+ +
+ +
\ No newline at end of file diff --git a/Client/src/app/components/vbs-page/vbs-page.component.ts b/Client/src/app/components/vbs-page/vbs-page.component.ts new file mode 100644 index 0000000..4e5b90c --- /dev/null +++ b/Client/src/app/components/vbs-page/vbs-page.component.ts @@ -0,0 +1,253 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormBuilder, Validators, FormArray, FormControl, ValidatorFn, ValidationErrors } from '@angular/forms'; +import { MissionarySupportService } from 'src/app/services/missionary-support-service'; +import { MatDialog, MatDialogConfig } from '@angular/material'; +import { OkPopupComponent } from '../popups/ok-popup/ok-popup.component'; +import { EmailService } from 'src/app/services/email.service'; + +@Component({ + selector: 'app-vbs-page', + templateUrl: './vbs-page.component.html', + styleUrls: ['./vbs-page.component.css'] +}) +export class VbsPageComponent implements OnInit { + + form: FormGroup; + submitButtonText: string = 'Submit'; + submitButtonDisabled: boolean = false; + errorMessages: string[] = []; + public students: Student[] = []; + public studentStep: boolean = true; + public parentStep: boolean = false; + public emergencyStep: boolean = false; + public completeStep: boolean = false; + public submitting: boolean = false; + + constructor(private formBuilder: FormBuilder, private matDialog: MatDialog, private emailService: EmailService, private missionarySupportService: MissionarySupportService) { + //this.setupForm(); +this.form = this.formBuilder.group({ + firstName: ['', [Validators.required]], + lastName: ['', [Validators.required]], + grade: ['', [Validators.required]], + dob: [null, [Validators.required]], + hasAllergies: [null, [Validators.required]], + allergies: ['', [Validators.required]], + hasDietaryRestrictions: [null, [Validators.required]], + dietaryRestrictions: ['', [Validators.required]], + attendedInPast: [null, [Validators.required]], + ridesBus: [null, [Validators.required]], + parentFirstName: ['', [Validators.required]], + parentLastName: ['', [Validators.required]], + parentHomePhone: ['', []], + parentMobilePhone: ['', [Validators.required]], + parentWorkPhone: ['', []], + parentEmail: ['', [Validators.required]], + address1: ['', [Validators.required]], + address2: ['', [Validators.required]], + city: ['', [Validators.required]], + state: ['', [Validators.required]], + zip: ['', [Validators.required]], + emergencySameAsParent: ['', []], + emergencyFirstName: ['', []], + emergencyLastName: ['', []], + emergencyPhone: ['', []], + hp: ['.', [Validators.required]] + }); + + } + + ngOnInit(): void { + + } + + hasErrors(formControlName: string): boolean { + let res = this.form.get(formControlName) && typeof this.form.get(formControlName).errors !== 'undefined'; + if (res === true) { + res = this.form.get(formControlName).dirty || (this.form.get(formControlName).touched && this.form.get(formControlName).valid === false); + } + return res; + } + + studentComplete(): boolean { + var firstName = this.form.get('firstName'); + var lastName = this.form.get('lastName'); + var grade = this.form.get('grade'); + var dob = this.form.get('dob'); + var hasAllergies = this.form.get('hasAllergies'); + var hasDietaryRestrictions = this.form.get('hasDietaryRestrictions'); + var allergies = this.form.get('allergies'); + var dietaryRestrictions = this.form.get('dietaryRestrictions'); + var attendedInPast = this.form.get('attendedInPast'); + var ridesBus = this.form.get('ridesBus'); + + var hasFirst = firstName && firstName.value && firstName.value.length > 2; + var hasLast = lastName && lastName.value && lastName.value.length > 2; + var hasGrade = grade && grade.value && grade.value > 0; + var hasDob = dob && dob.value != null; + var hasAllergiesComplete = hasAllergies && hasAllergies.value != null; + var hasDietaryRestrictionsComplete = hasDietaryRestrictions && hasDietaryRestrictions.value != null; + var allergiesComplete = allergies && allergies.value && allergies.value.length > 2; + var dietaryRestrictionsComplete = dietaryRestrictions && dietaryRestrictions.value && dietaryRestrictions.value.length > 2; + var hasAttendedInPast = attendedInPast && attendedInPast.value != null; + var hasRidesBus = ridesBus && ridesBus.value != null; + + var validAllergies = hasAllergiesComplete && ((hasAllergies.value === 'true' && allergiesComplete) || (hasAllergies.value === 'false')); + var validDietaryRestrictions = hasDietaryRestrictionsComplete && ((hasDietaryRestrictions.value === 'true' && dietaryRestrictionsComplete) || (hasDietaryRestrictions.value === 'false')); + + return hasFirst && hasLast && hasGrade && hasDob && validAllergies && validDietaryRestrictions && hasRidesBus && hasAttendedInPast; + } + + parentComplete(): boolean { + var first = this.form.get('parentFirstName'); + var last = this.form.get('parentLastName'); + var mobilePhone = this.form.get('parentMobilePhone'); + var email = this.form.get('parentEmail'); + var address1 = this.form.get('address1'); + var city = this.form.get('city'); + var state = this.form.get('state'); + var zip = this.form.get('zip'); + + var hasFirst = first && first.value && first.value.length > 1; + var hasLast = last && last.value && last.value.length > 1; + var hasMobilePhone = mobilePhone && mobilePhone.value && mobilePhone.value.length > 9; + var hasEmail = email && email.value && this.isValidEmail(email.value); + var hasAddress1 = address1 && address1.value && address1.value.length > 2; + var hasCity = city && city.value && city.value.length > 2; + var hasState = state && state.value && state.value.length > 0; + var hasZip = zip && zip.value && zip.value.length > 2; + + return hasFirst && hasLast && hasMobilePhone && hasEmail && hasAddress1 && hasCity && hasState && hasZip; + } + + emergencyComplete(): boolean { + var same = this.form.get('emergencySameAsParent'); + var first = this.form.get('emergencyFirstName'); + var last = this.form.get('emergencyLastName'); + var phone = this.form.get('emergencyPhone'); + + var isSame = same && same.value && same.value === true; + var hasFirst = first && first.value && first.value.length > 2; + var hasLast = last && last.value && last.value.length > 2; + var hasPhone = phone && phone.value && phone.value.length > 9; + + if (isSame) { + return true; + } + + return hasFirst && hasLast && hasPhone; + } + + isValidEmail(email: string) { + // The official regex standard used by most frontend validation engines + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; + return emailRegex.test(email); +} + + gotoStudentStep() { + this.studentStep = true; + this.parentStep = false; + this.emergencyStep = false; + this.completeStep = false; + } + + gotoParentStep() { + this.studentStep = false; + this.parentStep = true; + this.emergencyStep = false; + this.completeStep = false; + } + + gotoEmergencyStep() { + this.studentStep = false; + this.parentStep = false; + this.emergencyStep = true; + this.completeStep = false; + } + + gotoCompleteStep() { + this.studentStep = false; + this.parentStep = false; + this.emergencyStep = false; + this.completeStep = true; + this.submitting = false; + } + + submit() { + if (this.submitting === true) { + return; + } + this.submitting = true; + this.emailService.sendVbsEmail(this.form.value) + .subscribe( + success => {this.gotoCompleteStep()}, + error => {this.submitError();}); + } + + private submitError(){ + this.submitting = false; + console.error("error"); + let opts = new MatDialogConfig; + opts.data = { title:'Error','message':'Error submitting registration form. Please try again or contact us at (406) 494 - 5028.' }; + let popup = this.matDialog.open(OkPopupComponent,opts); + this.submitButtonText = "Submit"; + this.submitButtonDisabled = false; + } + + registerAnotherStudent() { + this.completeStep = false; + this.studentStep = true; + this.form.get('firstName').reset(); + this.form.get('lastName').reset(); + this.form.get('grade').reset(); + this.form.get('dob').reset(); + this.form.get('hasAllergies').reset(); + this.form.get('allergies').reset(); + this.form.get('hasDietaryRestrictions').reset(); + this.form.get('dietaryRestrictions').reset(); + this.form.get('attendedInPast').reset(); + this.form.get('ridesBus').reset(); + } + + public stateList = [ + { code: 'AL', name: 'Alabama' }, { code: 'AK', name: 'Alaska' }, { code: 'AZ', name: 'Arizona' }, + { code: 'AR', name: 'Arkansas' }, { code: 'CA', name: 'California' }, { code: 'CO', name: 'Colorado' }, + { code: 'CT', name: 'Connecticut' }, { code: 'DE', name: 'Delaware' }, { code: 'FL', name: 'Florida' }, + { code: 'GA', name: 'Georgia' }, { code: 'HI', name: 'Hawaii' }, { code: 'ID', name: 'Idaho' }, + { code: 'IL', name: 'Illinois' }, { code: 'IN', name: 'Indiana' }, { code: 'IA', name: 'Iowa' }, + { code: 'KS', name: 'Kansas' }, { code: 'KY', name: 'Kentucky' }, { code: 'LA', name: 'Louisiana' }, + { code: 'ME', name: 'Maine' }, { code: 'MD', name: 'Maryland' }, { code: 'MA', name: 'Massachusetts' }, + { code: 'MI', name: 'Michigan' }, { code: 'MN', name: 'Minnesota' }, { code: 'MS', name: 'Mississippi' }, + { code: 'MO', name: 'Missouri' }, { code: 'MT', name: 'Montana' }, { code: 'NE', name: 'Nebraska' }, + { code: 'NV', name: 'Nevada' }, { code: 'NH', name: 'New Hampshire' }, { code: 'NJ', name: 'New Jersey' }, + { code: 'NM', name: 'New Mexico' }, { code: 'NY', name: 'New York' }, { code: 'NC', name: 'North Carolina' }, + { code: 'ND', name: 'North Dakota' }, { code: 'OH', name: 'Ohio' }, { code: 'OK', name: 'Oklahoma' }, + { code: 'OR', name: 'Oregon' }, { code: 'PA', name: 'Pennsylvania' }, { code: 'RI', name: 'Rhode Island' }, + { code: 'SC', name: 'South Carolina' }, { code: 'SD', name: 'South Dakota' }, { code: 'TN', name: 'Tennessee' }, + { code: 'TX', name: 'Texas' }, { code: 'UT', name: 'Utah' }, { code: 'VT', name: 'Vermont' }, + { code: 'VA', name: 'Virginia' }, { code: 'WA', name: 'Washington' }, { code: 'WV', name: 'West Virginia' }, + { code: 'WI', name: 'Wisconsin' }, { code: 'WY', name: 'Wyoming' } +]; +} + +class Student { + public firstName: string; + public lastName: string; + public grade: number; + public birthday: string; + public allergies: string; + public dietaryRestrictions: string; + public attendedInPast: boolean; + public ridesBus: boolean; + public guardian: Guardian + public emergencyContact: EmergencyContact; +} + +class Guardian { + public firstName: string; + public lastName: string; +} + +class EmergencyContact { + public firstName: string; + public lastName: string; +} \ No newline at end of file diff --git a/Client/src/app/constants/urls.ts b/Client/src/app/constants/urls.ts index ebb2acb..f72fbf1 100644 --- a/Client/src/app/constants/urls.ts +++ b/Client/src/app/constants/urls.ts @@ -20,4 +20,5 @@ export const USER_GET_ALL_URL = environment.baseUrl + "/api2/users/a"; export const LOGIN_URL = environment.baseUrl + '/api2/login'; export const LOGIN_VALIDATE_TOKEN = ''; export const EMAIL_URL = environment.baseUrl + "/api2/email"; +export const VBS_REGISTRATION_URL = environment.baseUrl + "/api2/vbs"; export const RANDOM_VERSE_URL = "//www.kingjamesbibleonline.org/popular-bible-verses-widget.php"; diff --git a/Client/src/app/services/email.service.ts b/Client/src/app/services/email.service.ts index 1f0582f..6643f83 100644 --- a/Client/src/app/services/email.service.ts +++ b/Client/src/app/services/email.service.ts @@ -3,7 +3,7 @@ import { catchError } from 'rxjs/operators'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { EMAIL_URL } from '../constants/urls'; +import { EMAIL_URL, VBS_REGISTRATION_URL } from '../constants/urls'; @Injectable() @@ -32,6 +32,11 @@ export class EmailService { } + sendVbsEmail(formData) { + return this.httpClient.post(VBS_REGISTRATION_URL, formData, {withCredentials:true}) + .pipe(catchError(this.handleError)); + } + private handleError(error: HttpErrorResponse) { if (error.error instanceof ErrorEvent) { // A client-side or network error occurred. Handle it accordingly. diff --git a/Server/src/routes/api/api.js b/Server/src/routes/api/api.js index 10a210b..20e7140 100644 --- a/Server/src/routes/api/api.js +++ b/Server/src/routes/api/api.js @@ -19,6 +19,8 @@ router.use("/events", require("./events")); router.use("/login", require("./login")); router.use("/email", require("./email")); router.use("/transactions", require("./transactions")); +router.use("/vbs", require("./vbs")); + router.use('/share',require('./share')); diff --git a/Server/src/routes/api/vbs.js b/Server/src/routes/api/vbs.js new file mode 100644 index 0000000..dcd195a --- /dev/null +++ b/Server/src/routes/api/vbs.js @@ -0,0 +1,181 @@ +var express = require('express'); +var router = express.Router(); +const nodemailer = require('nodemailer'); + +let transporter = nodemailer.createTransport({ + host: 'smtp.ionos.com', + port: 587, + secure: false, + auth:{ + user: 'mail@ofbbutte.com', + pass: '@2014OfbPwd' + } + +}); + +router.get("/",function(req,res){ + res.status(200).json({"message":"Hello World"}); + return; +}); + +router.post("/", function(req,res){ + console.log(req.body); + //This is the honeypot field + //If it has something in it then we know it was filled out by a bot + if (!req.body.hp || req.body.hp != '.'){ + res.status(200).json({"status":200,"message":"Success!"}); + return; + } + + let mailOptions = { + from: 'donotreply@ofbbutte.com', + to: 'djabsher@gmail.com', + subject: 'OFB - VBS Registration' + req.body.firstName, + html: generateRegistrationEmailHTML(req.body) + }; + transporter.sendMail(mailOptions,(error, info) =>{ + if (error){ + console.log(error); + res.status(400).json({"status":400,"message":"There was an error","error":error.response}); + } else { + res.status(200).json({"status":200,"message":"Success"}); + } + }); + +}); + +/** + * Generates a sectioned HTML email string for the registration form. + * Displays all fields even if they are empty. + * @param {Object} data - The value object from this.form.value + * @returns {string} Ready-to-send HTML string + */ +function generateRegistrationEmailHTML(data) { + + // Helper to cleanly format boolean true/false fields + const formatBool = (val) => { + if (val === true || val === 'true') return 'Yes'; + if (val === false || val === 'false') return 'No'; + return val; + }; + + // Helper to ensure we don't render literal 'null' or 'undefined' text + const sanitizeValue = (val) => { + if (val === null || val === undefined) return ''; + return String(val).trim(); + }; + + // Define the layout sections and human-readable labels + const sections = [ + { + title: "Student Information", + fields: [ + { label: "Student Name", value: `${sanitizeValue(data.firstName)} ${sanitizeValue(data.lastName)}`.trim() }, + { label: "Grade", value: sanitizeValue(data.grade) }, + { label: "Date of Birth", value: sanitizeValue(data.dob) }, + { label: "Has Allergies?", value: sanitizeValue(formatBool(data.hasAllergies)) }, + { label: "Allergies", value: sanitizeValue(data.allergies) }, + { label: "Has Dietary Restrictions?", value: sanitizeValue(formatBool(data.hasDietaryRestrictions)) }, + { label: "Dietary Restrictions", value: sanitizeValue(data.dietaryRestrictions) }, + { label: "Attended in Past?", value: sanitizeValue(formatBool(data.attendedInPast)) }, + { label: "Rides Bus?", value: sanitizeValue(formatBool(data.ridesBus)) } + ] + }, + { + title: "Parent / Guardian Contact", + fields: [ + { label: "Parent Name", value: `${sanitizeValue(data.parentFirstName)} ${sanitizeValue(data.parentLastName)}`.trim() }, + { label: "Email", value: sanitizeValue(data.parentEmail) }, + { label: "Mobile Phone", value: sanitizeValue(data.parentMobilePhone) }, + { label: "Home Phone", value: sanitizeValue(data.parentHomePhone) }, + { label: "Work Phone", value: sanitizeValue(data.parentWorkPhone) }, + { label: "Address", value: `${sanitizeValue(data.address1)}${data.address2 ? ', ' + sanitizeValue(data.address2) : ''}, ${sanitizeValue(data.city)}, ${sanitizeValue(data.state)} ${sanitizeValue(data.zip)}`.trim().replace(/^,|,$/g, '') } + ] + }, + { + title: "Emergency Contact", + fields: [ + { label: "Same as Parent?", value: sanitizeValue(formatBool(data.emergencySameAsParent)) }, + { label: "Emergency Name", value: `${sanitizeValue(data.emergencyFirstName)} ${sanitizeValue(data.emergencyLastName)}`.trim() }, + { label: "Emergency Phone", value: sanitizeValue(data.emergencyPhone) } + ] + } + ]; + + // Map the sections array into HTML tables (No filtering out fields!) + const htmlContent = sections.map(section => { + const rows = section.fields.map(field => ` + + + ${field.label} + + + ${field.value} + + + `).join(''); + + return ` +

+ ${section.title} +

+ + + ${rows} + +
+ `; + }).join(''); + + return ` + + + + + + New Registration Submission + + + + + + +
+ + + + + + + + + + + + + + + + + +
+

+ New Registration Submitted +

+
+

+ A new form submission has been received. +

+ + ${htmlContent} + +
+ This email was automatically generated and sent from the registration system. +
+
+ + + `; +} + +module.exports = router; \ No newline at end of file