adding pilot
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
"start:azure": "npm run build && func host start"
|
"start:azure": "npm run build && func host start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@azure/data-tables": "^13.2.2",
|
||||||
"@azure/functions": "^1.0.3",
|
"@azure/functions": "^1.0.3",
|
||||||
"@nestjs/azure-database": "^3.0.0",
|
"@nestjs/azure-database": "^3.0.0",
|
||||||
"@nestjs/azure-func-http": "^0.10.0",
|
"@nestjs/azure-func-http": "^0.10.0",
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"@nestjs/passport": "^10.0.3",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"@noahspan/noahspan-modules": "^0.3.5",
|
"@noahspan/noahspan-modules": "^0.3.9",
|
||||||
"@schematics/angular": "^17.3.7",
|
"@schematics/angular": "^17.3.7",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { Controller, Get, Headers, Query } from '@nestjs/common';
|
||||||
Body,
|
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
Headers,
|
|
||||||
Post,
|
|
||||||
Query,
|
|
||||||
UnprocessableEntityException
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import {
|
import {
|
||||||
AppConfigService,
|
AppConfigService,
|
||||||
MsGraphService,
|
MsGraphService,
|
||||||
@@ -16,8 +8,6 @@ import { FeatureFlagValue } from '@azure/app-configuration';
|
|||||||
import { Public } from '@noahspan/noahspan-modules';
|
import { Public } from '@noahspan/noahspan-modules';
|
||||||
import { Person } from '@microsoft/microsoft-graph-types';
|
import { Person } from '@microsoft/microsoft-graph-types';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { PilotDTO } from './pilot/pilot.dto';
|
|
||||||
import { PilotService } from './pilot/pilot.service';
|
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { APP_GUARD } from '@nestjs/core';
|
import { APP_GUARD } from '@nestjs/core';
|
||||||
import { PilotModule } from './pilot/pilot.module';
|
import { PilotModule } from './pilot/pilot.module';
|
||||||
import { PilotController } from './pilot/pilot.controller';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -33,7 +32,7 @@ import { PilotController } from './pilot/pilot.controller';
|
|||||||
}),
|
}),
|
||||||
PilotModule
|
PilotModule
|
||||||
],
|
],
|
||||||
controllers: [AppController, PilotController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: APP_GUARD,
|
provide: APP_GUARD,
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
import { Body, Controller, Get, Post, Put, Query } from '@nestjs/common';
|
import {
|
||||||
import { Public } from '@noahspan/noahspan-modules';
|
Body,
|
||||||
import { PilotDTO } from './pilot.dto';
|
Controller,
|
||||||
import { Pilot } from './pilot.entity';
|
Get,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Query,
|
||||||
|
UnprocessableEntityException
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { PilotInfoService } from './info/pilot-info.service';
|
||||||
|
import { PilotInfoDto } from './info/pilot-info.dto';
|
||||||
|
|
||||||
@Controller('pilots')
|
@Controller('pilots')
|
||||||
export class PilotController {
|
export class PilotController {
|
||||||
constructor() {}
|
constructor(private readonly pilotInfoService: PilotInfoService) {}
|
||||||
|
|
||||||
@Public()
|
|
||||||
@Post()
|
@Post()
|
||||||
async createPilot(@Body() pilotDto: PilotDTO) {
|
async createPilot(@Body() pilotInfoData: PilotInfoDto): Promise<void> {
|
||||||
const pilot = new Pilot();
|
try {
|
||||||
Object.assign(pilot, pilotDto);
|
return await this.pilotInfoService.create(pilotInfoData);
|
||||||
|
} catch (error) {
|
||||||
console.log(pilot);
|
throw new UnprocessableEntityException(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { PilotController } from './pilot.controller';
|
import { PilotController } from './pilot.controller';
|
||||||
import { PilotService } from './pilot.service';
|
import { TableModule } from '@noahspan/noahspan-modules';
|
||||||
import { AzureTableStorageModule } from '@nestjs/azure-database';
|
import { PilotInfoService } from './info/pilot-info.service';
|
||||||
import { Pilot } from './pilot.entity';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
AzureTableStorageModule.forFeature(Pilot, {
|
TableModule.register({
|
||||||
table: 'Pilot',
|
accountName: process.env.AZURE_STORAGE_ACCOUNT_NAME,
|
||||||
createTableIfNotExists: true
|
accountKey: process.env.AZURE_STORAGE_ACCOUNT_KEY
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
controllers: [PilotController],
|
controllers: [PilotController],
|
||||||
providers: [PilotService]
|
providers: [PilotInfoService]
|
||||||
})
|
})
|
||||||
export class PilotModule {}
|
export class PilotModule {}
|
||||||
|
|||||||
@@ -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<Pilot>
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async create(pilot: Pilot): Promise<Pilot> {
|
|
||||||
return await this.pilotRepository.create(pilot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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<Profile>
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async find(rowKey: string): Promise<Profile> {
|
|
||||||
return await this.profileRepository.find(this.partitionKey, rowKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
async findAll(): Promise<Profile[]> {
|
|
||||||
return await this.profileRepository.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
async create(profile: Profile): Promise<Profile> {
|
|
||||||
return await this.profileRepository.create(profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
async update(rowKey: string, profile: Profile): Promise<Profile> {
|
|
||||||
return await this.profileRepository.update(
|
|
||||||
this.partitionKey,
|
|
||||||
rowKey,
|
|
||||||
profile
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete(rowKey: string): Promise<void> {
|
|
||||||
await this.profileRepository.delete(this.partitionKey, rowKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.5.2",
|
"@fortawesome/free-regular-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@noahspan/noahspan-components": "^0.5.9",
|
"@noahspan/noahspan-components": "^0.6.2",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"framer-motion": "^11.1.7",
|
"framer-motion": "^11.1.7",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
useForm,
|
useForm,
|
||||||
Controller,
|
Controller,
|
||||||
@@ -15,9 +15,11 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Option,
|
Option,
|
||||||
PeoplePicker,
|
PeoplePicker,
|
||||||
|
SaveIcon,
|
||||||
Select,
|
Select,
|
||||||
StateSelect,
|
StateSelect,
|
||||||
Typography
|
Typography,
|
||||||
|
XmarkIcon
|
||||||
} from '@noahspan/noahspan-components';
|
} from '@noahspan/noahspan-components';
|
||||||
import { IPilotFormProps } from './IPilotFormProps';
|
import { IPilotFormProps } from './IPilotFormProps';
|
||||||
import PilotFormCertificates from '../pilotFormCertificates/PilotFormCertificates';
|
import PilotFormCertificates from '../pilotFormCertificates/PilotFormCertificates';
|
||||||
@@ -28,8 +30,10 @@ import { useHttpClient } from '../../hooks/httpClient/UseHttpClient';
|
|||||||
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
|
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
|
||||||
import { IPilotFormCertificates } from '../pilotFormCertificates/IPilotFormCertificates';
|
import { IPilotFormCertificates } from '../pilotFormCertificates/IPilotFormCertificates';
|
||||||
import { IPilotFormEndorsements } from '../pilotFormEndorsements/IPilotFormEndorsements';
|
import { IPilotFormEndorsements } from '../pilotFormEndorsements/IPilotFormEndorsements';
|
||||||
|
import { EventMessage, EventPayload, EventType } from '@azure/msal-browser';
|
||||||
|
|
||||||
const PilotForm: React.FC<IPilotFormProps> = ({
|
const PilotForm: React.FC<IPilotFormProps> = ({
|
||||||
|
pilotId,
|
||||||
isDrawerOpen,
|
isDrawerOpen,
|
||||||
onOpenCloseDrawer
|
onOpenCloseDrawer
|
||||||
}: IPilotFormProps) => {
|
}: IPilotFormProps) => {
|
||||||
@@ -40,8 +44,14 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
const { getAccessToken } = useAccessToken();
|
const { getAccessToken } = useAccessToken();
|
||||||
const methods = useForm();
|
const methods = useForm();
|
||||||
|
|
||||||
const onSubmit = (data: unknown) => {
|
const handlePeoplePickerOnClick = (
|
||||||
console.log(data);
|
event: React.MouseEvent<HTMLDivElement>
|
||||||
|
) => {
|
||||||
|
const divElement: HTMLDivElement = event.target as HTMLDivElement;
|
||||||
|
|
||||||
|
methods.setValue('id', divElement.id);
|
||||||
|
methods.setValue('name', divElement.textContent);
|
||||||
|
setPeoplePickerResults([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePeoplePickerOnChange = async (
|
const handlePeoplePickerOnChange = async (
|
||||||
@@ -71,6 +81,21 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
open={isDrawerOpen}
|
open={isDrawerOpen}
|
||||||
@@ -83,10 +108,6 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
||||||
<DrawerBody>
|
<DrawerBody>
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-4 gap-4">
|
||||||
<div className="col-span-4">
|
|
||||||
<Typography variant="h5">Info</Typography>
|
|
||||||
<hr className="my-3" />
|
|
||||||
</div>
|
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
<Typography variant="h6">Name *</Typography>
|
<Typography variant="h6">Name *</Typography>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,6 +115,7 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
<Controller
|
<Controller
|
||||||
name="name"
|
name="name"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
|
rules={{ required: 'A name must be selected' }}
|
||||||
render={({ field: { disabled, value } }) => (
|
render={({ field: { disabled, value } }) => (
|
||||||
<PeoplePicker
|
<PeoplePicker
|
||||||
results={peoplePickerResults}
|
results={peoplePickerResults}
|
||||||
@@ -103,82 +125,120 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
className: 'before:content-none after:content-none'
|
className: 'before:content-none after:content-none'
|
||||||
},
|
},
|
||||||
onChange: (event) => handlePeoplePickerOnChange(event),
|
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
|
value: value
|
||||||
}}
|
}}
|
||||||
|
listItemProps={{
|
||||||
|
children: null,
|
||||||
|
onClick: handlePeoplePickerOnClick
|
||||||
|
}}
|
||||||
loading={isPeoplePickerLoading}
|
loading={isPeoplePickerLoading}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
<Typography variant="h6">Address</Typography>
|
<Typography variant="h6">Address *</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<Controller
|
<Controller
|
||||||
name="address"
|
name="address"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: true }}
|
rules={{ required: 'An address is required' }}
|
||||||
render={({ field }) => (
|
render={({ field: { disabled, onChange, value } }) => (
|
||||||
<Input
|
<Input
|
||||||
className="!border-t-blue-gray-200 focus:!border-t-gray-900"
|
className="!border-t-blue-gray-200 focus:!border-t-gray-900"
|
||||||
|
disabled={disabled}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
className: 'before:content-none after:content-none'
|
className: 'before:content-none after:content-none'
|
||||||
}}
|
}}
|
||||||
{...field}
|
error={methods.formState.errors.address ? true : false}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.address
|
||||||
|
? methods.formState.errors.address.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
<Typography variant="h6">City</Typography>
|
<Typography variant="h6">City *</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<Controller
|
<Controller
|
||||||
name="city"
|
name="city"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: true }}
|
rules={{ required: 'A city is required' }}
|
||||||
render={({ field }) => (
|
render={({ field: { disabled, onChange, value } }) => (
|
||||||
<Input
|
<Input
|
||||||
|
disabled={disabled}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
className: 'before:content-none after:content-none'
|
className: 'before:content-none after:content-none'
|
||||||
}}
|
}}
|
||||||
{...field}
|
error={methods.formState.errors.city ? true : false}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.city
|
||||||
|
? methods.formState.errors.city.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
<Typography variant="h6">State</Typography>
|
<Typography variant="h6">State *</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<Controller
|
<Controller
|
||||||
name="state"
|
name="state"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
render={({ field }) => (
|
rules={{ required: 'A state must be selected' }}
|
||||||
// <Input
|
render={({ field: { disabled, onChange, value } }) => (
|
||||||
// labelProps={{
|
<StateSelect
|
||||||
// className: 'before:content-none after:content-none'
|
disabled={disabled}
|
||||||
// }}
|
error={methods.formState.errors.state ? true : false}
|
||||||
// {...field}
|
helperText={
|
||||||
// />
|
methods.formState.errors.state
|
||||||
<StateSelect variant="outlined" />
|
? methods.formState.errors.state.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
<Typography variant="h6">Postal Code</Typography>
|
<Typography variant="h6">Postal Code *</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<Controller
|
<Controller
|
||||||
name="postalCode"
|
name="postalCode"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: true }}
|
rules={{ required: 'A postal code is required' }}
|
||||||
render={({ field }) => (
|
render={({ field: { disabled, onChange, value } }) => (
|
||||||
<Input
|
<Input
|
||||||
|
disabled={disabled}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
className: 'before:content-none after:content-none'
|
className: 'before:content-none after:content-none'
|
||||||
}}
|
}}
|
||||||
{...field}
|
error={methods.formState.errors.postalCode ? true : false}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.postalCode
|
||||||
|
? methods.formState.errors.postalCode.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -190,13 +250,26 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
<Controller
|
<Controller
|
||||||
name="email"
|
name="email"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: true }}
|
rules={{
|
||||||
render={({ field }) => (
|
pattern: {
|
||||||
|
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||||
|
message: 'Invalid email address'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
render={({ field: { disabled, onChange, value } }) => (
|
||||||
<Input
|
<Input
|
||||||
|
disabled={disabled}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
className: 'before:content-none after:content-none'
|
className: 'before:content-none after:content-none'
|
||||||
}}
|
}}
|
||||||
{...field}
|
error={methods.formState.errors.email ? true : false}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.email
|
||||||
|
? methods.formState.errors.email.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -208,120 +281,157 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
<Controller
|
<Controller
|
||||||
name="phone"
|
name="phone"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: true }}
|
rules={{
|
||||||
render={({ field }) => (
|
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 } }) => (
|
||||||
<Input
|
<Input
|
||||||
|
disabled={disabled}
|
||||||
labelProps={{
|
labelProps={{
|
||||||
className: 'before:content-none after:content-none'
|
className: 'before:content-none after:content-none'
|
||||||
}}
|
}}
|
||||||
{...field}
|
error={methods.formState.errors.phone ? true : false}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.phone
|
||||||
|
? methods.formState.errors.phone.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
{pilotId && (
|
||||||
<Typography variant="h6">Last Review</Typography>
|
<>
|
||||||
</div>
|
<div className="col-span-1">
|
||||||
<div className="col-span-3">
|
<Typography variant="h6">Last Review</Typography>
|
||||||
<Controller
|
</div>
|
||||||
name="lastReview"
|
<div className="col-span-3">
|
||||||
control={methods.control}
|
<Controller
|
||||||
render={({ field }) => {
|
name="lastReview"
|
||||||
return (
|
control={methods.control}
|
||||||
<DatePicker
|
render={({ field }) => {
|
||||||
handleDateChanged={(date: string) => {
|
return (
|
||||||
methods.setValue('lastReview', date);
|
<DatePicker
|
||||||
}}
|
handleDateChanged={(date: string) => {
|
||||||
inputProps={{
|
methods.setValue('lastReview', date);
|
||||||
value: field.value
|
}}
|
||||||
}}
|
inputProps={{
|
||||||
/>
|
value: field.value
|
||||||
);
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
);
|
||||||
</div>
|
}}
|
||||||
<div className="col-span-4">
|
/>
|
||||||
<Typography variant="h5">Certificates</Typography>
|
</div>
|
||||||
<hr className="my-3" />
|
</>
|
||||||
</div>
|
)}
|
||||||
<PilotFormCertificates certificates={[]} />
|
{pilotId && (
|
||||||
<div className="col-span-4">
|
<>
|
||||||
<Typography variant="h5">Endorsements</Typography>
|
<div className="col-span-4">
|
||||||
<hr className="my-3" />
|
<Typography variant="h5">Medical</Typography>
|
||||||
</div>
|
<hr className="my-3" />
|
||||||
<PilotFormEndorsements endorsements={[]} />
|
</div>
|
||||||
<div className="col-span-4">
|
<div className="col-span-1">
|
||||||
<Typography variant="h5">Medical</Typography>
|
<Typography variant="h6">Class</Typography>
|
||||||
<hr className="my-3" />
|
</div>
|
||||||
</div>
|
<div className="col-span-3">
|
||||||
<div className="col-span-1">
|
<Controller
|
||||||
<Typography variant="h6">Class</Typography>
|
name="medicalClass"
|
||||||
</div>
|
control={methods.control}
|
||||||
<div className="col-span-3">
|
render={({ field }) => {
|
||||||
<Controller
|
return (
|
||||||
name="medicalClass"
|
<Select
|
||||||
control={methods.control}
|
labelProps={{
|
||||||
render={({ field }) => {
|
className:
|
||||||
return (
|
'before:content-none after:content-none'
|
||||||
<Select
|
}}
|
||||||
labelProps={{
|
{...field}
|
||||||
className: 'before:content-none after:content-none'
|
>
|
||||||
}}
|
<Option key="first" value="First">
|
||||||
{...field}
|
First
|
||||||
>
|
</Option>
|
||||||
<Option key="first" value="First">
|
<Option key="second" value="Second">
|
||||||
First
|
Second
|
||||||
</Option>
|
</Option>
|
||||||
<Option key="second" value="Second">
|
<Option key="third" value="Third">
|
||||||
Second
|
Third
|
||||||
</Option>
|
</Option>
|
||||||
<Option key="third" value="Third">
|
<Option key="basicMed" value="Basic Med">
|
||||||
Third
|
Basic Med
|
||||||
</Option>
|
</Option>
|
||||||
<Option key="basicMed" value="Basic Med">
|
</Select>
|
||||||
Basic Med
|
);
|
||||||
</Option>
|
}}
|
||||||
</Select>
|
/>
|
||||||
);
|
</div>
|
||||||
}}
|
<div className="col-span-1">
|
||||||
/>
|
<Typography variant="h6">Expiration Date</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
<div className="col-span-3">
|
||||||
<Typography variant="h6">Expiration Date</Typography>
|
<Controller
|
||||||
</div>
|
name="medicalExpiration"
|
||||||
<div className="col-span-3">
|
control={methods.control}
|
||||||
<Controller
|
render={({ field }) => {
|
||||||
name="medicalExpiration"
|
return (
|
||||||
control={methods.control}
|
<DatePicker
|
||||||
render={({ field }) => {
|
handleDateChanged={(date: string) => {
|
||||||
return (
|
methods.setValue('medicalExpiration', date);
|
||||||
<DatePicker
|
}}
|
||||||
handleDateChanged={(date: string) => {
|
inputProps={{
|
||||||
methods.setValue('medicalExpiration', date);
|
value: field.value
|
||||||
}}
|
}}
|
||||||
inputProps={{
|
/>
|
||||||
value: field.value
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
</div>
|
||||||
}}
|
</>
|
||||||
/>
|
)}
|
||||||
</div>
|
{pilotId && (
|
||||||
|
<>
|
||||||
|
<div className="col-span-4">
|
||||||
|
<Typography variant="h5">Certificates</Typography>
|
||||||
|
<hr className="my-3" />
|
||||||
|
</div>
|
||||||
|
<PilotFormCertificates certificates={[]} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{pilotId && (
|
||||||
|
<>
|
||||||
|
<div className="col-span-4">
|
||||||
|
<Typography variant="h5">Endorsements</Typography>
|
||||||
|
<hr className="my-3" />
|
||||||
|
</div>
|
||||||
|
<PilotFormEndorsements endorsements={[]} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DrawerBody>
|
</DrawerBody>
|
||||||
<DrawerFooter>
|
<DrawerFooter>
|
||||||
<div className="flex gap-2 justify-end justify-self-center">
|
<div className="flex gap-2 justify-end justify-self-center pt-4">
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
|
className="flex items-center gap-3"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={onOpenCloseDrawer}
|
onClick={onOpenCloseDrawer}
|
||||||
data-testid="pilot-drawer-cancel-button"
|
data-testid="pilot-drawer-cancel-button"
|
||||||
>
|
>
|
||||||
|
<XmarkIcon size="lg" />
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button variant="filled" type="submit">
|
<Button
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
variant="filled"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<SaveIcon size="lg" />
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import PilotForm from '../pilotForm/PilotForm';
|
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<unknown> = () => {
|
const Pilots: React.FC<unknown> = () => {
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||||
@@ -10,10 +16,9 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
const [date, setDate] = useState();
|
const [date, setDate] = useState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto">
|
<Card>
|
||||||
<div className="px-6">
|
<div className="px-6">
|
||||||
<h1 role="heading">Pilots</h1>
|
<Typography variant="h3">Pilots</Typography>
|
||||||
<PlusIcon size="2xl" />
|
|
||||||
<Button
|
<Button
|
||||||
className="flex items-center gap-3"
|
className="flex items-center gap-3"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
@@ -24,19 +29,12 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
Add Pilot
|
Add Pilot
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<DatePicker
|
|
||||||
handleDateChanged={(date) => console.log(date)}
|
|
||||||
inputProps={{
|
|
||||||
value: date
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PilotForm
|
<PilotForm
|
||||||
isDrawerOpen={isDrawerOpen}
|
isDrawerOpen={isDrawerOpen}
|
||||||
onOpenCloseDrawer={onOpenCloseDrawer}
|
onOpenCloseDrawer={onOpenCloseDrawer}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -30,6 +30,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@azure/data-tables": "^13.2.2",
|
||||||
"@azure/functions": "^1.0.3",
|
"@azure/functions": "^1.0.3",
|
||||||
"@nestjs/azure-database": "^3.0.0",
|
"@nestjs/azure-database": "^3.0.0",
|
||||||
"@nestjs/azure-func-http": "^0.10.0",
|
"@nestjs/azure-func-http": "^0.10.0",
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"@nestjs/passport": "^10.0.3",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"@noahspan/noahspan-modules": "^0.3.5",
|
"@noahspan/noahspan-modules": "^0.3.9",
|
||||||
"@schematics/angular": "^17.3.7",
|
"@schematics/angular": "^17.3.7",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.5.2",
|
"@fortawesome/free-regular-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@noahspan/noahspan-components": "^0.5.9",
|
"@noahspan/noahspan-components": "^0.6.2",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"framer-motion": "^11.1.7",
|
"framer-motion": "^11.1.7",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
@@ -3385,9 +3386,9 @@
|
|||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@noahspan/noahspan-components": {
|
"node_modules/@noahspan/noahspan-components": {
|
||||||
"version": "0.5.9",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@noahspan/noahspan-components/-/noahspan-components-0.5.9.tgz",
|
"resolved": "https://registry.npmjs.org/@noahspan/noahspan-components/-/noahspan-components-0.6.2.tgz",
|
||||||
"integrity": "sha512-McUwtMGvs13h0Jq1R2vxcRWh4c41xSwK54NbdKJhPAKfZ9GRjwWHCtxhrh43XG4SeI4GJGlnLpAw4+D6UTsmaQ==",
|
"integrity": "sha512-ptdDx/HinOW5e9JnbVBGLotXV2W1gPYGu/qLId6ZEDLVVr26NBiMgg2bY6IP1pvKgeGz8xJYa7FQr3XZ5DgAsg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||||
@@ -3419,11 +3420,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@noahspan/noahspan-modules": {
|
"node_modules/@noahspan/noahspan-modules": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@noahspan/noahspan-modules/-/noahspan-modules-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@noahspan/noahspan-modules/-/noahspan-modules-0.3.9.tgz",
|
||||||
"integrity": "sha512-VUCEtJcFTcrVyROMDpP8a90LFez5VPzo//0pUdHlpV7KqIDPRPpp7z/RXkXoUBYM0g06kS2CD5t3ZtzyIewBNg==",
|
"integrity": "sha512-gRW+DiXQP+/0vHvEBi5yijQwwUnP+z5YxlSU7Q/bzm7YdUv5ecbZrz4is+BzH1S6Ctds9DeoQKtvb9xbAKwg8w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/app-configuration": "^1.6.0",
|
"@azure/app-configuration": "^1.6.0",
|
||||||
|
"@azure/data-tables": "^13.2.2",
|
||||||
"@azure/identity": "^4.2.0",
|
"@azure/identity": "^4.2.0",
|
||||||
"@azure/msal-node": "^2.9.2",
|
"@azure/msal-node": "^2.9.2",
|
||||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||||
|
|||||||
Reference in New Issue
Block a user