diff --git a/api/src/app.controller.ts b/api/src/app.controller.ts index 22b27b5..2a07472 100644 --- a/api/src/app.controller.ts +++ b/api/src/app.controller.ts @@ -1,4 +1,12 @@ -import { Controller, Get, Headers, Query } from '@nestjs/common'; +import { + Body, + Controller, + Get, + Headers, + Post, + Query, + UnprocessableEntityException +} from '@nestjs/common'; import { AppConfigService, MsGraphService, @@ -8,6 +16,8 @@ import { FeatureFlagValue } from '@azure/app-configuration'; import { Public } from '@noahspan/noahspan-modules'; import { Person } from '@microsoft/microsoft-graph-types'; import { AppService } from './app.service'; +import { PilotDTO } from './pilot/pilot.dto'; +import { PilotService } from './pilot/pilot.service'; @Controller() export class AppController { diff --git a/api/src/pilot/profile/profile.entity.ts b/api/src/pilot/profile/profile.entity.ts index a1a183c..28d6bf2 100644 --- a/api/src/pilot/profile/profile.entity.ts +++ b/api/src/pilot/profile/profile.entity.ts @@ -1,11 +1,18 @@ +import { + EntityDateTime, + EntityPartitionKey, + EntityRowKey, + EntityString +} from '@nestjs/azure-database'; + +@EntityPartitionKey('pilot') +@EntityRowKey('id') export class Profile { - partitionKey: string; - rowKey: string; - firstName: string; - lastName: string; - address: string; - city: string; - state: string; - postalCode: string; - lastFlightReview: Date; + @EntityString() firstName: string; + @EntityString() lastName: string; + @EntityString() address: string; + @EntityString() city: string; + @EntityString() state: string; + @EntityString() postalCode: string; + @EntityDateTime() lastFlightReview: Date; } diff --git a/app/package.json b/app/package.json index 1c43198..1719622 100644 --- a/app/package.json +++ b/app/package.json @@ -16,13 +16,16 @@ "@fortawesome/free-regular-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.2", - "@noahspan/noahspan-components": "^0.5.1", + "@hookform/resolvers": "^3.9.0", + "@noahspan/noahspan-components": "^0.5.6", "axios": "^1.7.2", "framer-motion": "^11.1.7", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.4", - "react-router-dom": "^6.23.0" + "react-router-dom": "^6.23.0", + "yup": "^1.4.0", + "zod": "^3.23.8" }, "devDependencies": { "@microsoft/microsoft-graph-types": "^2.40.0", diff --git a/app/src/components/pilotForm/PilotForm.tsx b/app/src/components/pilotForm/PilotForm.tsx index e846848..b588685 100644 --- a/app/src/components/pilotForm/PilotForm.tsx +++ b/app/src/components/pilotForm/PilotForm.tsx @@ -1,12 +1,7 @@ import { useState } from 'react'; import { useForm, Controller, FormProvider } from 'react-hook-form'; import { - Accordion, - AccordionBody, - AccordionHeader, Button, - ChevronDownIcon, - ChevronUpIcon, DatePicker, Drawer, DrawerBody, @@ -24,6 +19,40 @@ import { Person } from '@microsoft/microsoft-graph-types'; import { AxiosInstance, AxiosResponse } from 'axios'; import { useHttpClient } from '../../hooks/httpClient/UseHttpClient'; import { useAccessToken } from '../../hooks/accessToken/UseAcessToken'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { IPilotFormCertificates } from '../pilotFormCertificates/IPilotFormCertificates'; +import { IPilotFormEndorsements } from '../pilotFormEndorsements/IPilotFormEndorsements'; + +interface PilotFormSchema { + id: string; + name: string; + address: string; + city: string; + state: string; + postalCode: string; + email: string; + phone: string; + certificates: IPilotFormCertificates[]; + endorsements: IPilotFormEndorsements[]; + medicalClass: string; + medicalExpiration: string; +} + +const schema = z.object({ + id: z.string(), + name: z.string(), + address: z.string(), + city: z.string(), + state: z.string(), + postalCode: z.string(), + email: z.string(), + phone: z.string(), + // certificates: z.array(), + // endorsements: z.array(), + medicalClass: z.string(), + medicalExpiration: z.string() +}); interface PilotFormProps { isDrawerOpen: boolean; @@ -39,9 +68,11 @@ const PilotForm: React.FC = ({ const [isPeoplePickerLoading, setIsPeoplePickerLoading] = useState(false); const { getAccessToken } = useAccessToken(); - const methods = useForm(); + const methods = useForm({ + resolver: zodResolver(schema) + }); - const onSubmit = (data: unknown) => { + const onSubmit = (data: PilotFormSchema) => { console.log(data); }; @@ -120,6 +151,7 @@ const PilotForm: React.FC = ({ control={methods.control} render={({ field }) => ( = ({ )} /> +
+ Last Review +
+
+ { + return ( + { + methods.setValue('lastReview', date); + }} + inputProps={{ + value: field.value + }} + /> + ); + }} + /> +
Certificates
diff --git a/app/src/components/pilotFormCertificates/PilotFormCertificates.tsx b/app/src/components/pilotFormCertificates/PilotFormCertificates.tsx index 5e92893..fe1d7bc 100644 --- a/app/src/components/pilotFormCertificates/PilotFormCertificates.tsx +++ b/app/src/components/pilotFormCertificates/PilotFormCertificates.tsx @@ -6,7 +6,8 @@ import { Option, PlusIcon, Select, - TrashIcon + TrashIcon, + Typography } from '@noahspan/noahspan-components'; import { Controller, useFieldArray, useFormContext } from 'react-hook-form'; @@ -26,6 +27,20 @@ const PilotFormCertificates: React.FC = ({ return ( <> + {fields.length > 0 && ( + <> +
+ Type +
+
+ Number +
+
+ Date of Issue +
+
+ + )} {fields.map((field, index) => { return ( <> @@ -87,6 +102,7 @@ const PilotFormCertificates: React.FC = ({