From f8662980af5847ee46ea476898551be943a2a74a Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Thu, 20 Feb 2025 16:07:39 -0800 Subject: [PATCH] feat: add form for submitting testimonials (#3) ### Explanation _No response_ ### Issue _No response_ ### Attestations - [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [ ] I have pinned the dependencies to a specific patch version. ### Style - [x] I have run the linter and resolved any errors. - [x] My pull request uses an appropriate title, matching the conventional commit standards. - [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [ ] My contribution adds new code, and I have added tests to cover it. - [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [ ] All new and existing tests pass locally with my changes. - [ ] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning Minor - My pull request introduces a new non-breaking feature. Reviewed-on: https://git.nhcarrigan.com/nhcarrigan/forms/pulls/3 Co-authored-by: Naomi Carrigan Co-committed-by: Naomi Carrigan --- client/src/app/api.service.ts | 21 ++++- client/src/app/app.routes.ts | 3 + .../testimonial/testimonial.component.css | 0 .../testimonial/testimonial.component.html | 12 +++ .../testimonial/testimonial.component.ts | 80 +++++++++++++++++++ client/src/app/home/home.component.html | 3 + client/src/app/review/review.component.html | 12 ++- client/src/app/review/review.component.ts | 9 ++- packages/types/src/forms/testimonial.ts | 13 +++ packages/types/src/index.ts | 2 + prisma/schema.prisma | 9 +++ .../src/handlers/submit/testimonialHandler.ts | 64 +++++++++++++++ server/src/interfaces/databasePath.ts | 3 +- server/src/modules/genericDataQueries.ts | 19 ++++- server/src/modules/validateBody.ts | 6 ++ server/src/routes/submit.ts | 11 +++ 16 files changed, 258 insertions(+), 9 deletions(-) create mode 100644 client/src/app/forms/testimonial/testimonial.component.css create mode 100644 client/src/app/forms/testimonial/testimonial.component.html create mode 100644 client/src/app/forms/testimonial/testimonial.component.ts create mode 100644 packages/types/src/forms/testimonial.ts create mode 100644 server/src/handlers/submit/testimonialHandler.ts diff --git a/client/src/app/api.service.ts b/client/src/app/api.service.ts index 3d7c009..07711ae 100644 --- a/client/src/app/api.service.ts +++ b/client/src/app/api.service.ts @@ -16,6 +16,7 @@ import type { SuccessResponse, Staff, DataResponse, + Testimonial, } from "@repo/types"; @Injectable({ @@ -142,6 +143,20 @@ export class ApiService { return response; } + public async submitTestimonial( + testimonial: Partial, + ): Promise { + const request = await fetch(`${this.url}/submit/testimonials`, { + body: JSON.stringify(testimonial), + headers: { + "Content-type": "application/json", + }, + method: "POST", + }); + const response = await request.json() as SuccessResponse | ErrorResponse; + return response; + } + public async getData( type: | "appeals" @@ -150,7 +165,8 @@ export class ApiService { | "events" | "meetings" | "mentorships" - | "staff", + | "staff" + | "testimonials", token: string, ): Promise { const request = await fetch(`${this.url}/list/${type}`, { @@ -172,7 +188,8 @@ export class ApiService { | "events" | "meetings" | "mentorships" - | "staff", + | "staff" + | "testimonials", id: string, token: string, ): Promise { diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts index 6127667..b5e237b 100644 --- a/client/src/app/app.routes.ts +++ b/client/src/app/app.routes.ts @@ -13,6 +13,8 @@ import { MeetingComponent } from "./forms/meeting/meeting.component.js"; import { MentorshipComponent } from "./forms/mentorship/mentorship.component.js"; import { StaffComponent } from "./forms/staff/staff.component.js"; +import { TestimonialComponent } + from "./forms/testimonial/testimonial.component.js"; import { HomeComponent } from "./home/home.component.js"; import { ReviewComponent } from "./review/review.component.js"; import type { Routes } from "@angular/router"; @@ -27,4 +29,5 @@ export const routes: Routes = [ { component: MentorshipComponent, path: "mentorship" }, { component: StaffComponent, path: "staff" }, { component: ReviewComponent, path: "review" }, + { component: TestimonialComponent, path: "testimonial" }, ]; diff --git a/client/src/app/forms/testimonial/testimonial.component.css b/client/src/app/forms/testimonial/testimonial.component.css new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/forms/testimonial/testimonial.component.html b/client/src/app/forms/testimonial/testimonial.component.html new file mode 100644 index 0000000..52c834e --- /dev/null +++ b/client/src/app/forms/testimonial/testimonial.component.html @@ -0,0 +1,12 @@ +

Testimonials

+

This form allows past clients and colleagues to submit reviews reflecting their positive experience with our work.

+

We will respond to these submissions when your testimonial is on our site.

+ + +
+ + + + +
+Back to home \ No newline at end of file diff --git a/client/src/app/forms/testimonial/testimonial.component.ts b/client/src/app/forms/testimonial/testimonial.component.ts new file mode 100644 index 0000000..cc6ed68 --- /dev/null +++ b/client/src/app/forms/testimonial/testimonial.component.ts @@ -0,0 +1,80 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; +import { FormControl, ReactiveFormsModule } from "@angular/forms"; +import { RouterModule } from "@angular/router"; +import { ApiService } from "../../api.service.js"; +import { ConsentComponent } from "../../consent/consent.component.js"; +import { ErrorComponent } from "../../error/error.component.js"; +import { MultiLineComponent } + from "../../inputs/multi-line/multi-line.component.js"; +import { SuccessComponent } from "../../success/success.component.js"; +import { UserinfoComponent } from "../../userinfo/userinfo.component.js"; + +@Component({ + imports: [ + RouterModule, + CommonModule, + ConsentComponent, + UserinfoComponent, + ErrorComponent, + ReactiveFormsModule, + MultiLineComponent, + SuccessComponent, + ], + selector: "app-testimonial", + styleUrl: "./testimonial.component.css", + templateUrl: "./testimonial.component.html", +}) +export class TestimonialComponent { + public loading = false; + public error = ""; + public success = false; + + public firstName = new FormControl(""); + public lastName = new FormControl(""); + public email = new FormControl(""); + public company = new FormControl(""); + + public content = new FormControl(""); + + public consent = new FormControl(false); + + public constructor(private readonly apiService: ApiService) {} + + public submit(event: MouseEvent): void { + this.error = ""; + const { form } = event.target as HTMLButtonElement; + const valid = form?.reportValidity(); + if (valid !== true) { + return; + } + this.loading = true; + + this.apiService. + submitTestimonial({ + consent: this.consent.value ?? false, + content: this.content.value ?? undefined, + email: this.email.value ?? undefined, + firstName: this.firstName.value ?? undefined, + lastName: this.lastName.value ?? undefined, + }). + then((response) => { + if ("error" in response) { + this.error = response.error; + this.loading = false; + } else { + this.error = ""; + this.success = true; + } + }). + catch(() => { + this.error = "An error occurred while submitting the form."; + this.loading = false; + }); + } +} diff --git a/client/src/app/home/home.component.html b/client/src/app/home/home.component.html index 3283f75..319abda 100644 --- a/client/src/app/home/home.component.html +++ b/client/src/app/home/home.component.html @@ -29,3 +29,6 @@ Mentorship Programme + + Testimonials + diff --git a/client/src/app/review/review.component.html b/client/src/app/review/review.component.html index 6684eac..7983999 100644 --- a/client/src/app/review/review.component.html +++ b/client/src/app/review/review.component.html @@ -58,11 +58,21 @@ > Staff Applications +

{{ view }}

{{ datum.email }}

-

{{ obj.key }}: {{ obj.value }}

+

+ {{ obj.key }}: {{ obj.value }} +