From 9e1e69d99676c67a8b2868ce543a5f69ab30ecab Mon Sep 17 00:00:00 2001 From: Noah Spannbauer Date: Fri, 16 Aug 2024 19:23:54 -0500 Subject: [PATCH] adding pilot --- api/package.json | 3 +- api/src/app.controller.ts | 12 +- api/src/app.module.ts | 3 +- api/src/pilot/pilot.controller.ts | 29 +- api/src/pilot/pilot.dto.ts | 11 - api/src/pilot/pilot.entity.ts | 11 - api/src/pilot/pilot.module.ts | 13 +- api/src/pilot/pilot.service.ts | 17 - api/src/pilot/profile/profile.entity.ts | 18 -- api/src/pilot/profile/profile.service.ts | 37 --- app/package.json | 2 +- app/src/components/pilotForm/PilotForm.tsx | 360 ++++++++++++++------- app/src/components/pilots/Pilots.tsx | 22 +- package-lock.json | 18 +- 14 files changed, 284 insertions(+), 272 deletions(-) delete mode 100644 api/src/pilot/pilot.dto.ts delete mode 100644 api/src/pilot/pilot.entity.ts delete mode 100644 api/src/pilot/pilot.service.ts delete mode 100644 api/src/pilot/profile/profile.entity.ts delete mode 100644 api/src/pilot/profile/profile.service.ts diff --git a/api/package.json b/api/package.json index 99c8239..af1ee42 100644 --- a/api/package.json +++ b/api/package.json @@ -20,6 +20,7 @@ "start:azure": "npm run build && func host start" }, "dependencies": { + "@azure/data-tables": "^13.2.2", "@azure/functions": "^1.0.3", "@nestjs/azure-database": "^3.0.0", "@nestjs/azure-func-http": "^0.10.0", @@ -28,7 +29,7 @@ "@nestjs/core": "^10.0.0", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", - "@noahspan/noahspan-modules": "^0.3.5", + "@noahspan/noahspan-modules": "^0.3.9", "@schematics/angular": "^17.3.7", "dotenv": "^16.4.5", "reflect-metadata": "0.1.13", diff --git a/api/src/app.controller.ts b/api/src/app.controller.ts index 2a07472..22b27b5 100644 --- a/api/src/app.controller.ts +++ b/api/src/app.controller.ts @@ -1,12 +1,4 @@ -import { - Body, - Controller, - Get, - Headers, - Post, - Query, - UnprocessableEntityException -} from '@nestjs/common'; +import { Controller, Get, Headers, Query } from '@nestjs/common'; import { AppConfigService, MsGraphService, @@ -16,8 +8,6 @@ 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/app.module.ts b/api/src/app.module.ts index a942edc..1d94683 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -10,7 +10,6 @@ import { import { ConfigModule } from '@nestjs/config'; import { APP_GUARD } from '@nestjs/core'; import { PilotModule } from './pilot/pilot.module'; -import { PilotController } from './pilot/pilot.controller'; @Module({ imports: [ @@ -33,7 +32,7 @@ import { PilotController } from './pilot/pilot.controller'; }), PilotModule ], - controllers: [AppController, PilotController], + controllers: [AppController], providers: [ { provide: APP_GUARD, diff --git a/api/src/pilot/pilot.controller.ts b/api/src/pilot/pilot.controller.ts index 8c5f0a1..8776ced 100644 --- a/api/src/pilot/pilot.controller.ts +++ b/api/src/pilot/pilot.controller.ts @@ -1,18 +1,25 @@ -import { Body, Controller, Get, Post, Put, Query } from '@nestjs/common'; -import { Public } from '@noahspan/noahspan-modules'; -import { PilotDTO } from './pilot.dto'; -import { Pilot } from './pilot.entity'; +import { + Body, + Controller, + Get, + Post, + Put, + Query, + UnprocessableEntityException +} from '@nestjs/common'; +import { PilotInfoService } from './info/pilot-info.service'; +import { PilotInfoDto } from './info/pilot-info.dto'; @Controller('pilots') export class PilotController { - constructor() {} + constructor(private readonly pilotInfoService: PilotInfoService) {} - @Public() @Post() - async createPilot(@Body() pilotDto: PilotDTO) { - const pilot = new Pilot(); - Object.assign(pilot, pilotDto); - - console.log(pilot); + async createPilot(@Body() pilotInfoData: PilotInfoDto): Promise { + try { + return await this.pilotInfoService.create(pilotInfoData); + } catch (error) { + throw new UnprocessableEntityException(error); + } } } diff --git a/api/src/pilot/pilot.dto.ts b/api/src/pilot/pilot.dto.ts deleted file mode 100644 index 92071ba..0000000 --- a/api/src/pilot/pilot.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Certificate } from './certificate/certificate.entity'; -import { Endorsement } from './endorsement/endorsement.entity'; -import { Medical } from './medical/medical.entity'; -import { Profile } from './profile/profile.entity'; - -export class PilotDTO { - profile?: Profile; - medical?: Medical; - certificate?: Certificate; - endosement?: Endorsement; -} diff --git a/api/src/pilot/pilot.entity.ts b/api/src/pilot/pilot.entity.ts deleted file mode 100644 index 42a7346..0000000 --- a/api/src/pilot/pilot.entity.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Certificate } from './certificate/certificate.entity'; -import { Endorsement } from './endorsement/endorsement.entity'; -import { Medical } from './medical/medical.entity'; -import { Profile } from './profile/profile.entity'; - -export class Pilot { - profile: Profile; - medical: Medical; - certificates: Certificate; - endorsements: Endorsement; -} diff --git a/api/src/pilot/pilot.module.ts b/api/src/pilot/pilot.module.ts index 77a4bdf..60dc7dc 100644 --- a/api/src/pilot/pilot.module.ts +++ b/api/src/pilot/pilot.module.ts @@ -1,17 +1,16 @@ import { Module } from '@nestjs/common'; import { PilotController } from './pilot.controller'; -import { PilotService } from './pilot.service'; -import { AzureTableStorageModule } from '@nestjs/azure-database'; -import { Pilot } from './pilot.entity'; +import { TableModule } from '@noahspan/noahspan-modules'; +import { PilotInfoService } from './info/pilot-info.service'; @Module({ imports: [ - AzureTableStorageModule.forFeature(Pilot, { - table: 'Pilot', - createTableIfNotExists: true + TableModule.register({ + accountName: process.env.AZURE_STORAGE_ACCOUNT_NAME, + accountKey: process.env.AZURE_STORAGE_ACCOUNT_KEY }) ], controllers: [PilotController], - providers: [PilotService] + providers: [PilotInfoService] }) export class PilotModule {} diff --git a/api/src/pilot/pilot.service.ts b/api/src/pilot/pilot.service.ts deleted file mode 100644 index 9137848..0000000 --- a/api/src/pilot/pilot.service.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Repository, InjectRepository } from '@nestjs/azure-database'; -import { Medical } from './medical/medical.entity'; -import { Profile } from './profile/profile.entity'; -import { ProfileService } from './profile/profile.service'; -import { Pilot } from './pilot.entity'; - -@Injectable() -export class PilotService { - constructor( - @InjectRepository(Pilot) private readonly pilotRepository: Repository - ) {} - - async create(pilot: Pilot): Promise { - return await this.pilotRepository.create(pilot); - } -} diff --git a/api/src/pilot/profile/profile.entity.ts b/api/src/pilot/profile/profile.entity.ts deleted file mode 100644 index 28d6bf2..0000000 --- a/api/src/pilot/profile/profile.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - EntityDateTime, - EntityPartitionKey, - EntityRowKey, - EntityString -} from '@nestjs/azure-database'; - -@EntityPartitionKey('pilot') -@EntityRowKey('id') -export class Profile { - @EntityString() firstName: string; - @EntityString() lastName: string; - @EntityString() address: string; - @EntityString() city: string; - @EntityString() state: string; - @EntityString() postalCode: string; - @EntityDateTime() lastFlightReview: Date; -} diff --git a/api/src/pilot/profile/profile.service.ts b/api/src/pilot/profile/profile.service.ts deleted file mode 100644 index 8d5cc00..0000000 --- a/api/src/pilot/profile/profile.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Repository, InjectRepository } from '@nestjs/azure-database'; -import { Profile } from './profile.entity'; - -@Injectable() -export class ProfileService { - private readonly partitionKey: string = 'profile'; - - constructor( - @InjectRepository(Profile) - private readonly profileRepository: Repository - ) {} - - async find(rowKey: string): Promise { - return await this.profileRepository.find(this.partitionKey, rowKey); - } - - async findAll(): Promise { - return await this.profileRepository.findAll(); - } - - async create(profile: Profile): Promise { - return await this.profileRepository.create(profile); - } - - async update(rowKey: string, profile: Profile): Promise { - return await this.profileRepository.update( - this.partitionKey, - rowKey, - profile - ); - } - - async delete(rowKey: string): Promise { - await this.profileRepository.delete(this.partitionKey, rowKey); - } -} diff --git a/app/package.json b/app/package.json index 2c7f9cc..b5efc94 100644 --- a/app/package.json +++ b/app/package.json @@ -16,7 +16,7 @@ "@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.9", + "@noahspan/noahspan-components": "^0.6.2", "axios": "^1.7.2", "framer-motion": "^11.1.7", "react": "^18.2.0", diff --git a/app/src/components/pilotForm/PilotForm.tsx b/app/src/components/pilotForm/PilotForm.tsx index 0e5d7d9..f01b34b 100644 --- a/app/src/components/pilotForm/PilotForm.tsx +++ b/app/src/components/pilotForm/PilotForm.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useForm, Controller, @@ -15,9 +15,11 @@ import { Input, Option, PeoplePicker, + SaveIcon, Select, StateSelect, - Typography + Typography, + XmarkIcon } from '@noahspan/noahspan-components'; import { IPilotFormProps } from './IPilotFormProps'; import PilotFormCertificates from '../pilotFormCertificates/PilotFormCertificates'; @@ -28,8 +30,10 @@ import { useHttpClient } from '../../hooks/httpClient/UseHttpClient'; import { useAccessToken } from '../../hooks/accessToken/UseAcessToken'; import { IPilotFormCertificates } from '../pilotFormCertificates/IPilotFormCertificates'; import { IPilotFormEndorsements } from '../pilotFormEndorsements/IPilotFormEndorsements'; +import { EventMessage, EventPayload, EventType } from '@azure/msal-browser'; const PilotForm: React.FC = ({ + pilotId, isDrawerOpen, onOpenCloseDrawer }: IPilotFormProps) => { @@ -40,8 +44,14 @@ const PilotForm: React.FC = ({ const { getAccessToken } = useAccessToken(); const methods = useForm(); - const onSubmit = (data: unknown) => { - console.log(data); + const handlePeoplePickerOnClick = ( + event: React.MouseEvent + ) => { + const divElement: HTMLDivElement = event.target as HTMLDivElement; + + methods.setValue('id', divElement.id); + methods.setValue('name', divElement.textContent); + setPeoplePickerResults([]); }; const handlePeoplePickerOnChange = async ( @@ -71,6 +81,21 @@ const PilotForm: React.FC = ({ } }; + const onSubmit = async (data: unknown) => { + console.log(data); + + const accessToken: string = await getAccessToken(); + const response: AxiosResponse = await httpClient.post(`api/pilots`, data, { + headers: { + Authorization: accessToken + } + }); + }; + + useEffect(() => { + console.log(methods.formState.errors); + }, [methods.formState.errors]); + return ( = ({
-
- Info -
-
Name *
@@ -94,6 +115,7 @@ const PilotForm: React.FC = ({ ( = ({ className: 'before:content-none after:content-none' }, onChange: (event) => handlePeoplePickerOnChange(event), + error: methods.formState.errors.name ? true : false, + helperText: methods.formState.errors.name + ? methods.formState.errors.name.message?.toString() + : undefined, value: value }} + listItemProps={{ + children: null, + onClick: handlePeoplePickerOnClick + }} loading={isPeoplePickerLoading} /> )} />
- Address + Address *
( + rules={{ required: 'An address is required' }} + render={({ field: { disabled, onChange, value } }) => ( )} />
- City + City *
( + rules={{ required: 'A city is required' }} + render={({ field: { disabled, onChange, value } }) => ( )} />
- State + State *
( - // - + rules={{ required: 'A state must be selected' }} + render={({ field: { disabled, onChange, value } }) => ( + )} />
- Postal Code + Postal Code *
( + rules={{ required: 'A postal code is required' }} + render={({ field: { disabled, onChange, value } }) => ( )} /> @@ -190,13 +250,26 @@ const PilotForm: React.FC = ({ ( + rules={{ + pattern: { + value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, + message: 'Invalid email address' + } + }} + render={({ field: { disabled, onChange, value } }) => ( )} /> @@ -208,120 +281,157 @@ const PilotForm: React.FC = ({ ( + rules={{ + pattern: { + value: /^[0-9]{3}[-\s\.][0-9]{3}[-\s\.][0-9]{4}$/i, + message: 'Enter phone number as 123-456-7890' + } + }} + render={({ field: { disabled, onChange, value } }) => ( )} />
-
- Last Review -
-
- { - return ( - { - methods.setValue('lastReview', date); - }} - inputProps={{ - value: field.value - }} - /> - ); - }} - /> -
-
- Certificates -
-
- -
- Endorsements -
-
- -
- Medical -
-
-
- Class -
-
- { - return ( - - ); - }} - /> -
-
- Expiration Date -
-
- { - return ( - { - methods.setValue('medicalExpiration', date); - }} - inputProps={{ - value: field.value - }} - /> - ); - }} - /> -
+ {pilotId && ( + <> +
+ Last Review +
+
+ { + return ( + { + methods.setValue('lastReview', date); + }} + inputProps={{ + value: field.value + }} + /> + ); + }} + /> +
+ + )} + {pilotId && ( + <> +
+ Medical +
+
+
+ Class +
+
+ { + return ( + + ); + }} + /> +
+
+ Expiration Date +
+
+ { + return ( + { + methods.setValue('medicalExpiration', date); + }} + inputProps={{ + value: field.value + }} + /> + ); + }} + /> +
+ + )} + {pilotId && ( + <> +
+ Certificates +
+
+ + + )} + {pilotId && ( + <> +
+ Endorsements +
+
+ + + )}
-
+
-
diff --git a/app/src/components/pilots/Pilots.tsx b/app/src/components/pilots/Pilots.tsx index 5b050cb..cdc9328 100644 --- a/app/src/components/pilots/Pilots.tsx +++ b/app/src/components/pilots/Pilots.tsx @@ -1,6 +1,12 @@ import { useState } from 'react'; import PilotForm from '../pilotForm/PilotForm'; -import { Button, PlusIcon, DatePicker } from '@noahspan/noahspan-components'; +import { + Button, + Card, + PlusIcon, + DatePicker, + Typography +} from '@noahspan/noahspan-components'; const Pilots: React.FC = () => { const [isDrawerOpen, setIsDrawerOpen] = useState(false); @@ -10,10 +16,9 @@ const Pilots: React.FC = () => { const [date, setDate] = useState(); return ( -
+
-

Pilots

- + Pilots - console.log(date)} - inputProps={{ - value: date - }} - /> -
-
+ ); }; diff --git a/package-lock.json b/package-lock.json index 0188c38..ea8978d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@azure/data-tables": "^13.2.2", "@azure/functions": "^1.0.3", "@nestjs/azure-database": "^3.0.0", "@nestjs/azure-func-http": "^0.10.0", @@ -38,7 +39,7 @@ "@nestjs/core": "^10.0.0", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", - "@noahspan/noahspan-modules": "^0.3.5", + "@noahspan/noahspan-modules": "^0.3.9", "@schematics/angular": "^17.3.7", "dotenv": "^16.4.5", "reflect-metadata": "0.1.13", @@ -74,7 +75,7 @@ "@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.9", + "@noahspan/noahspan-components": "^0.6.2", "axios": "^1.7.2", "framer-motion": "^11.1.7", "react": "^18.2.0", @@ -3385,9 +3386,9 @@ "link": true }, "node_modules/@noahspan/noahspan-components": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@noahspan/noahspan-components/-/noahspan-components-0.5.9.tgz", - "integrity": "sha512-McUwtMGvs13h0Jq1R2vxcRWh4c41xSwK54NbdKJhPAKfZ9GRjwWHCtxhrh43XG4SeI4GJGlnLpAw4+D6UTsmaQ==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@noahspan/noahspan-components/-/noahspan-components-0.6.2.tgz", + "integrity": "sha512-ptdDx/HinOW5e9JnbVBGLotXV2W1gPYGu/qLId6ZEDLVVr26NBiMgg2bY6IP1pvKgeGz8xJYa7FQr3XZ5DgAsg==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.5.2", @@ -3419,11 +3420,12 @@ } }, "node_modules/@noahspan/noahspan-modules": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@noahspan/noahspan-modules/-/noahspan-modules-0.3.5.tgz", - "integrity": "sha512-VUCEtJcFTcrVyROMDpP8a90LFez5VPzo//0pUdHlpV7KqIDPRPpp7z/RXkXoUBYM0g06kS2CD5t3ZtzyIewBNg==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@noahspan/noahspan-modules/-/noahspan-modules-0.3.9.tgz", + "integrity": "sha512-gRW+DiXQP+/0vHvEBi5yijQwwUnP+z5YxlSU7Q/bzm7YdUv5ecbZrz4is+BzH1S6Ctds9DeoQKtvb9xbAKwg8w==", "dependencies": { "@azure/app-configuration": "^1.6.0", + "@azure/data-tables": "^13.2.2", "@azure/identity": "^4.2.0", "@azure/msal-node": "^2.9.2", "@microsoft/microsoft-graph-client": "^3.0.7",