pilot view
This commit is contained in:
@@ -74,7 +74,7 @@ export class AppController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('userProfile')
|
@Get('userProfile')
|
||||||
async getUserProfile(@Headers() headers: any) {
|
async getUserProfile(@Headers() headers: any): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const graphToken: string = await this.msGraphService.getMsGraphAuth(
|
const graphToken: string = await this.msGraphService.getMsGraphAuth(
|
||||||
headers.authorization.replace('Bearer ', ''),
|
headers.authorization.replace('Bearer ', ''),
|
||||||
@@ -94,12 +94,12 @@ export class AppController {
|
|||||||
async searchUsers(
|
async searchUsers(
|
||||||
@Headers() headers: any,
|
@Headers() headers: any,
|
||||||
@Query('search') search: any
|
@Query('search') search: any
|
||||||
): Promise<Person[]> {
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const accessToken: string = headers.authorization.replace('Bearer ', '');
|
const accessToken: string = headers.authorization.replace('Bearer ', '');
|
||||||
const personSearchResults: Person[] =
|
const personSearchResults: any[] =
|
||||||
await this.appService.getPersonSearchResults(accessToken, search);
|
await this.appService.getPersonSearchResults(accessToken, search);
|
||||||
console.log(personSearchResults);
|
|
||||||
return personSearchResults;
|
return personSearchResults;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { MsGraphService, MsGraphClient } from '@noahspan/noahspan-modules';
|
import { MsGraphService, MsGraphClient } from '@noahspan/noahspan-modules';
|
||||||
import { Person } from '@microsoft/microsoft-graph-types';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppService {
|
export class AppService {
|
||||||
@@ -13,7 +12,7 @@ export class AppService {
|
|||||||
async getPersonSearchResults(
|
async getPersonSearchResults(
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
search: string
|
search: string
|
||||||
): Promise<Person[]> {
|
): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const graphToken: string = await this.msGraphService.getMsGraphAuth(
|
const graphToken: string = await this.msGraphService.getMsGraphAuth(
|
||||||
accessToken,
|
accessToken,
|
||||||
@@ -22,12 +21,16 @@ export class AppService {
|
|||||||
const client: MsGraphClient =
|
const client: MsGraphClient =
|
||||||
await this.msGraphService.getMsGraphClientDelegated(graphToken);
|
await this.msGraphService.getMsGraphClientDelegated(graphToken);
|
||||||
const results: any = await client
|
const results: any = await client
|
||||||
.api(`me/people/?$search=${search}`)
|
.api('users')
|
||||||
|
.header('ConsistencyLevel', 'eventual')
|
||||||
|
.search(`"displayName:${search}"`)
|
||||||
|
.orderby('displayName')
|
||||||
|
.select(['displayName', 'userPrincipalName'])
|
||||||
.get();
|
.get();
|
||||||
let personResults: Person[];
|
let personResults: any[];
|
||||||
|
|
||||||
if (results.value) {
|
if (results.value) {
|
||||||
personResults = results.value.filter((result: Person) => {
|
personResults = results.value.filter((result: any) => {
|
||||||
if (result.userPrincipalName !== null) {
|
if (result.userPrincipalName !== null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,43 @@ export class PilotInfoService {
|
|||||||
|
|
||||||
constructor(private readonly tableService: TableService) {}
|
constructor(private readonly tableService: TableService) {}
|
||||||
|
|
||||||
// async find(rowKey: string): Promise<PilotInfoEntity> {
|
async find(pilotId: string): Promise<PilotInfoEntity> {
|
||||||
// return await this.pilotInfoRepository.find(this.partitionKey, rowKey);
|
try {
|
||||||
// }
|
const client: TableClient =
|
||||||
|
await this.tableService.getTableClient('Pilots');
|
||||||
|
const entities = await client.listEntities({
|
||||||
|
queryOptions: {
|
||||||
|
filter: odata`PartitionKey eq 'pilot' and RowKey eq '${pilotId}'`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let pilot: PilotInfoEntity;
|
||||||
|
console.log(entities);
|
||||||
|
for await (const entity of entities) {
|
||||||
|
pilot = {
|
||||||
|
partitionKey: entity.partitionKey,
|
||||||
|
rowKey: entity.rowKey,
|
||||||
|
id: entity.id.toString(),
|
||||||
|
name: entity.name.toString(),
|
||||||
|
address: entity.address.toString(),
|
||||||
|
city: entity.city.toString(),
|
||||||
|
state: entity.state.toString(),
|
||||||
|
postalCode: entity.postalCode.toString(),
|
||||||
|
email: entity.email.toString(),
|
||||||
|
phone: entity.phone.toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return pilot;
|
||||||
|
} catch (error) {
|
||||||
|
const restError: RestError = error as RestError;
|
||||||
|
|
||||||
|
throw new CustomError(
|
||||||
|
restError.details['odataError']['message']['value'],
|
||||||
|
restError.details['odataError']['code'],
|
||||||
|
restError.statusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async findAll(): Promise<PilotInfoEntity[]> {
|
async findAll(): Promise<PilotInfoEntity[]> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -8,7 +8,29 @@ export class PilotInterceptor implements NestInterceptor {
|
|||||||
const token = authHeader && authHeader.split(' ')[1];
|
const token = authHeader && authHeader.split(' ')[1];
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return handler.handle().pipe(map((data) => data));
|
return handler.handle().pipe(
|
||||||
|
map((data) => {
|
||||||
|
if (data.length) {
|
||||||
|
const pilots = data.map((pilot) => {
|
||||||
|
return {
|
||||||
|
partitionKey: pilot.partitionKey,
|
||||||
|
rowKey: pilot.rowKey,
|
||||||
|
id: pilot.id,
|
||||||
|
name: pilot.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return pilots;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
partitionKey: data.partitionKey,
|
||||||
|
rowKey: data.rowKey,
|
||||||
|
id: data.id,
|
||||||
|
name: data.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.handle().pipe(map((data) => data));
|
return handler.handle().pipe(map((data) => data));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get,
|
||||||
HttpException,
|
HttpException,
|
||||||
|
Param,
|
||||||
Post,
|
Post,
|
||||||
UseInterceptors
|
UseInterceptors
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
@@ -18,6 +19,25 @@ import { Public } from '@noahspan/noahspan-modules';
|
|||||||
export class PilotController {
|
export class PilotController {
|
||||||
constructor(private readonly pilotInfoService: PilotInfoService) {}
|
constructor(private readonly pilotInfoService: PilotInfoService) {}
|
||||||
|
|
||||||
|
@Get(':pilotId')
|
||||||
|
@Public()
|
||||||
|
@UseInterceptors(PilotInterceptor)
|
||||||
|
async find(@Param() params: any): Promise<PilotInfoEntity> {
|
||||||
|
try {
|
||||||
|
const pilot: PilotInfoEntity = await this.pilotInfoService.find(
|
||||||
|
params.pilotId
|
||||||
|
);
|
||||||
|
|
||||||
|
return pilot;
|
||||||
|
} catch (error) {
|
||||||
|
const customError = error as CustomError;
|
||||||
|
|
||||||
|
throw new HttpException(customError.message, customError.statusCode, {
|
||||||
|
cause: customError.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Public()
|
@Public()
|
||||||
@UseInterceptors(PilotInterceptor)
|
@UseInterceptors(PilotInterceptor)
|
||||||
|
|||||||
@@ -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.6.8",
|
"@noahspan/noahspan-components": "^0.7.0",
|
||||||
"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,5 +1,8 @@
|
|||||||
|
import { PilotFormMode } from './PilotForm';
|
||||||
|
|
||||||
export interface IPilotFormProps {
|
export interface IPilotFormProps {
|
||||||
pilotId?: string;
|
|
||||||
isDrawerOpen: boolean;
|
isDrawerOpen: boolean;
|
||||||
onOpenCloseDrawer: () => void;
|
mode: PilotFormMode;
|
||||||
|
onOpenClose: (mode: PilotFormMode) => void;
|
||||||
|
pilotId?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,60 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import {
|
import { useForm, Controller, FormProvider } from 'react-hook-form';
|
||||||
useForm,
|
|
||||||
Controller,
|
|
||||||
FormProvider,
|
|
||||||
FieldValues
|
|
||||||
} from 'react-hook-form';
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DatePicker,
|
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerBody,
|
DrawerBody,
|
||||||
DrawerHeader,
|
DrawerHeader,
|
||||||
DrawerFooter,
|
DrawerFooter,
|
||||||
Input,
|
Input,
|
||||||
Option,
|
|
||||||
PeoplePicker,
|
PeoplePicker,
|
||||||
SaveIcon,
|
SaveIcon,
|
||||||
Select,
|
|
||||||
StateSelect,
|
StateSelect,
|
||||||
Typography,
|
Typography,
|
||||||
XmarkIcon
|
XmarkIcon
|
||||||
} from '@noahspan/noahspan-components';
|
} from '@noahspan/noahspan-components';
|
||||||
import { IPilotFormProps } from './IPilotFormProps';
|
import { IPilotFormProps } from './IPilotFormProps';
|
||||||
import PilotFormCertificates from '../pilotFormCertificates/PilotFormCertificates';
|
import axios, { AxiosInstance, AxiosResponse } from 'axios';
|
||||||
import PilotFormEndorsements from '../pilotFormEndorsements/PilotFormEndorsements';
|
|
||||||
import { Person } from '@microsoft/microsoft-graph-types';
|
|
||||||
import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
|
|
||||||
import { useHttpClient } from '../../hooks/httpClient/UseHttpClient';
|
import { useHttpClient } from '../../hooks/httpClient/UseHttpClient';
|
||||||
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
|
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
|
||||||
import { IPilotFormCertificates } from '../pilotFormCertificates/IPilotFormCertificates';
|
import { useIsAuthenticated } from '@azure/msal-react';
|
||||||
import { IPilotFormEndorsements } from '../pilotFormEndorsements/IPilotFormEndorsements';
|
|
||||||
import { EventMessage, EventPayload, EventType } from '@azure/msal-browser';
|
export enum PilotFormMode {
|
||||||
|
ADD = 'ADD',
|
||||||
|
EDIT = 'EDIT',
|
||||||
|
VIEW = 'VIEW',
|
||||||
|
CANCEL = 'CANCEL'
|
||||||
|
}
|
||||||
|
|
||||||
const PilotForm: React.FC<IPilotFormProps> = ({
|
const PilotForm: React.FC<IPilotFormProps> = ({
|
||||||
pilotId,
|
pilotId,
|
||||||
isDrawerOpen,
|
isDrawerOpen,
|
||||||
onOpenCloseDrawer
|
mode,
|
||||||
|
onOpenClose
|
||||||
}: IPilotFormProps) => {
|
}: IPilotFormProps) => {
|
||||||
const httpClient: AxiosInstance = useHttpClient();
|
const httpClient: AxiosInstance = useHttpClient();
|
||||||
const [peoplePickerResults, setPeoplePickerResults] = useState<Person[]>([]);
|
const [peoplePickerResults, setPeoplePickerResults] = useState<any[]>([]);
|
||||||
const [isPeoplePickerLoading, setIsPeoplePickerLoading] =
|
const [isPeoplePickerLoading, setIsPeoplePickerLoading] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const { getAccessToken } = useAccessToken();
|
const { getAccessToken } = useAccessToken();
|
||||||
const methods = useForm();
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
const defaultValues = {
|
||||||
|
partitionKey: '',
|
||||||
|
rowKey: '',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
address: '',
|
||||||
|
city: '',
|
||||||
|
state: '',
|
||||||
|
postalCode: '',
|
||||||
|
email: '',
|
||||||
|
phone: ''
|
||||||
|
};
|
||||||
|
const methods = useForm({
|
||||||
|
defaultValues: defaultValues
|
||||||
|
});
|
||||||
|
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
||||||
|
|
||||||
const handlePeoplePickerOnClick = (
|
const handlePeoplePickerOnClick = (
|
||||||
event: React.MouseEvent<HTMLDivElement>
|
event: React.MouseEvent<HTMLDivElement>
|
||||||
@@ -82,6 +93,11 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
methods.reset(defaultValues);
|
||||||
|
onOpenClose(PilotFormMode.CANCEL);
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async (data: unknown) => {
|
const onSubmit = async (data: unknown) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -111,8 +127,38 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(methods.formState.errors);
|
if (mode === PilotFormMode.VIEW) {
|
||||||
}, [methods.formState.errors]);
|
setIsDisabled(true);
|
||||||
|
}
|
||||||
|
}, [mode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getPilot = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const config = isAuthenticated
|
||||||
|
? { headers: { Authorization: await getAccessToken() } }
|
||||||
|
: {};
|
||||||
|
const response: AxiosResponse = await httpClient.get(
|
||||||
|
`api/pilots/${pilotId}`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
const pilot = response.data;
|
||||||
|
console.log(pilot);
|
||||||
|
// methods.setValue('blah', pilot.value)
|
||||||
|
methods.reset(pilot);
|
||||||
|
console.log(methods.getValues());
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (pilotId) {
|
||||||
|
getPilot();
|
||||||
|
}
|
||||||
|
}, [pilotId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
@@ -122,7 +168,7 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
data-testid="pilot-drawer"
|
data-testid="pilot-drawer"
|
||||||
>
|
>
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<DrawerHeader text="Add Pilot" onClose={onOpenCloseDrawer} />
|
<DrawerHeader text="Add Pilot" onClose={onCancel} />
|
||||||
<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">
|
||||||
@@ -134,11 +180,11 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
rules={{ required: 'A name must be selected' }}
|
rules={{ required: 'A name must be selected' }}
|
||||||
render={({ field: { disabled, value } }) => (
|
render={({ field: { value } }) => (
|
||||||
<PeoplePicker
|
<PeoplePicker
|
||||||
results={peoplePickerResults}
|
results={peoplePickerResults}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
disabled: disabled,
|
disabled: isDisabled,
|
||||||
labelProps: {
|
labelProps: {
|
||||||
className: 'before:content-none after:content-none'
|
className: 'before:content-none after:content-none'
|
||||||
},
|
},
|
||||||
@@ -159,178 +205,209 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
{isAuthenticated && (
|
||||||
<Typography variant="h6">Address *</Typography>
|
<>
|
||||||
</div>
|
<div className="col-span-1">
|
||||||
<div className="col-span-3">
|
<Typography variant="h6">Address *</Typography>
|
||||||
<Controller
|
</div>
|
||||||
name="address"
|
<div className="col-span-3">
|
||||||
control={methods.control}
|
<Controller
|
||||||
rules={{ required: 'An address is required' }}
|
name="address"
|
||||||
render={({ field: { disabled, onChange, value } }) => (
|
control={methods.control}
|
||||||
<Input
|
rules={{ required: 'An address is required' }}
|
||||||
className="!border-t-blue-gray-200 focus:!border-t-gray-900"
|
render={({ field: { onChange, value } }) => (
|
||||||
disabled={disabled}
|
<Input
|
||||||
labelProps={{
|
className="!border-t-blue-gray-200 focus:!border-t-gray-900"
|
||||||
className: 'before:content-none after:content-none'
|
disabled={isDisabled}
|
||||||
|
labelProps={{
|
||||||
|
className: 'before:content-none after:content-none'
|
||||||
|
}}
|
||||||
|
error={
|
||||||
|
methods.formState.errors.address ? true : false
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.address
|
||||||
|
? methods.formState.errors.address.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
data-testid="pilot-form-address-input"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isAuthenticated && (
|
||||||
|
<>
|
||||||
|
<div className="col-span-1">
|
||||||
|
<Typography variant="h6">City *</Typography>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-3">
|
||||||
|
<Controller
|
||||||
|
name="city"
|
||||||
|
control={methods.control}
|
||||||
|
rules={{ required: 'A city is required' }}
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<Input
|
||||||
|
disabled={isDisabled}
|
||||||
|
labelProps={{
|
||||||
|
className: 'before:content-none after:content-none'
|
||||||
|
}}
|
||||||
|
error={methods.formState.errors.city ? true : false}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.city
|
||||||
|
? methods.formState.errors.city.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
data-testid="pilot-form-city-input"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isAuthenticated && (
|
||||||
|
<>
|
||||||
|
<div className="col-span-1">
|
||||||
|
<Typography variant="h6">State *</Typography>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-3">
|
||||||
|
<Controller
|
||||||
|
name="state"
|
||||||
|
control={methods.control}
|
||||||
|
rules={{ required: 'A state must be selected' }}
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<StateSelect
|
||||||
|
disabled={isDisabled}
|
||||||
|
labelProps={{
|
||||||
|
className: 'before:content-none after:content-none'
|
||||||
|
}}
|
||||||
|
error={methods.formState.errors.state ? true : false}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.state
|
||||||
|
? methods.formState.errors.state.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
variant="outlined"
|
||||||
|
data-testid="pilot-form-state-dropdown"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isAuthenticated && (
|
||||||
|
<>
|
||||||
|
<div className="col-span-1">
|
||||||
|
<Typography variant="h6">Postal Code *</Typography>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-3">
|
||||||
|
<Controller
|
||||||
|
name="postalCode"
|
||||||
|
control={methods.control}
|
||||||
|
rules={{ required: 'A postal code is required' }}
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<Input
|
||||||
|
disabled={isDisabled}
|
||||||
|
labelProps={{
|
||||||
|
className: 'before:content-none after:content-none'
|
||||||
|
}}
|
||||||
|
error={
|
||||||
|
methods.formState.errors.postalCode ? true : false
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
methods.formState.errors.postalCode
|
||||||
|
? methods.formState.errors.postalCode.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
data-testid="pilot-form-postal-code-input"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isAuthenticated && (
|
||||||
|
<>
|
||||||
|
<div className="col-span-1">
|
||||||
|
<Typography variant="h6">Email</Typography>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-3">
|
||||||
|
<Controller
|
||||||
|
name="email"
|
||||||
|
control={methods.control}
|
||||||
|
rules={{
|
||||||
|
pattern: {
|
||||||
|
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||||
|
message: 'Invalid email address'
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
error={methods.formState.errors.address ? true : false}
|
render={({ field: { onChange, value } }) => (
|
||||||
helperText={
|
<Input
|
||||||
methods.formState.errors.address
|
disabled={isDisabled}
|
||||||
? methods.formState.errors.address.message?.toString()
|
labelProps={{
|
||||||
: undefined
|
className: 'before:content-none after:content-none'
|
||||||
}
|
}}
|
||||||
onChange={onChange}
|
error={methods.formState.errors.email ? true : false}
|
||||||
value={value}
|
helperText={
|
||||||
data-testid="pilot-form-address-input"
|
methods.formState.errors.email
|
||||||
|
? methods.formState.errors.email.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
data-testid="pilot-form-email-input"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
/>
|
</>
|
||||||
</div>
|
)}
|
||||||
<div className="col-span-1">
|
{isAuthenticated && (
|
||||||
<Typography variant="h6">City *</Typography>
|
<>
|
||||||
</div>
|
<div className="col-span-1">
|
||||||
<div className="col-span-3">
|
<Typography variant="h6">Phone Number</Typography>
|
||||||
<Controller
|
</div>
|
||||||
name="city"
|
<div className="col-span-3">
|
||||||
control={methods.control}
|
<Controller
|
||||||
rules={{ required: 'A city is required' }}
|
name="phone"
|
||||||
render={({ field: { disabled, onChange, value } }) => (
|
control={methods.control}
|
||||||
<Input
|
rules={{
|
||||||
disabled={disabled}
|
pattern: {
|
||||||
labelProps={{
|
value: /^[0-9]{3}[-\s\.][0-9]{3}[-\s\.][0-9]{4}$/i,
|
||||||
className: 'before:content-none after:content-none'
|
message: 'Enter phone number as 123-456-7890'
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
error={methods.formState.errors.city ? true : false}
|
render={({ field: { onChange, value } }) => (
|
||||||
helperText={
|
<Input
|
||||||
methods.formState.errors.city
|
disabled={isDisabled}
|
||||||
? methods.formState.errors.city.message?.toString()
|
labelProps={{
|
||||||
: undefined
|
className: 'before:content-none after:content-none'
|
||||||
}
|
}}
|
||||||
onChange={onChange}
|
error={methods.formState.errors.phone ? true : false}
|
||||||
value={value}
|
helperText={
|
||||||
data-testid="pilot-form-city-input"
|
methods.formState.errors.phone
|
||||||
|
? methods.formState.errors.phone.message?.toString()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
data-testid="pilot-form-phone-input"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
/>
|
</>
|
||||||
</div>
|
)}
|
||||||
<div className="col-span-1">
|
{/* {pilotId && (
|
||||||
<Typography variant="h6">State *</Typography>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-3">
|
|
||||||
<Controller
|
|
||||||
name="state"
|
|
||||||
control={methods.control}
|
|
||||||
rules={{ required: 'A state must be selected' }}
|
|
||||||
render={({ field: { disabled, onChange, value } }) => (
|
|
||||||
<StateSelect
|
|
||||||
disabled={disabled}
|
|
||||||
error={methods.formState.errors.state ? true : false}
|
|
||||||
helperText={
|
|
||||||
methods.formState.errors.state
|
|
||||||
? methods.formState.errors.state.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onChange={onChange}
|
|
||||||
value={value}
|
|
||||||
variant="outlined"
|
|
||||||
data-testid="pilot-form-state-dropdown"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-1">
|
|
||||||
<Typography variant="h6">Postal Code *</Typography>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-3">
|
|
||||||
<Controller
|
|
||||||
name="postalCode"
|
|
||||||
control={methods.control}
|
|
||||||
rules={{ required: 'A postal code is required' }}
|
|
||||||
render={({ field: { disabled, onChange, value } }) => (
|
|
||||||
<Input
|
|
||||||
disabled={disabled}
|
|
||||||
labelProps={{
|
|
||||||
className: 'before:content-none after:content-none'
|
|
||||||
}}
|
|
||||||
error={methods.formState.errors.postalCode ? true : false}
|
|
||||||
helperText={
|
|
||||||
methods.formState.errors.postalCode
|
|
||||||
? methods.formState.errors.postalCode.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onChange={onChange}
|
|
||||||
value={value}
|
|
||||||
data-testid="pilot-form-postal-code-input"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-1">
|
|
||||||
<Typography variant="h6">Email</Typography>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-3">
|
|
||||||
<Controller
|
|
||||||
name="email"
|
|
||||||
control={methods.control}
|
|
||||||
rules={{
|
|
||||||
pattern: {
|
|
||||||
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
|
||||||
message: 'Invalid email address'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
render={({ field: { disabled, onChange, value } }) => (
|
|
||||||
<Input
|
|
||||||
disabled={disabled}
|
|
||||||
labelProps={{
|
|
||||||
className: 'before:content-none after:content-none'
|
|
||||||
}}
|
|
||||||
error={methods.formState.errors.email ? true : false}
|
|
||||||
helperText={
|
|
||||||
methods.formState.errors.email
|
|
||||||
? methods.formState.errors.email.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onChange={onChange}
|
|
||||||
value={value}
|
|
||||||
data-testid="pilot-form-email-input"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-1">
|
|
||||||
<Typography variant="h6">Phone Number</Typography>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-3">
|
|
||||||
<Controller
|
|
||||||
name="phone"
|
|
||||||
control={methods.control}
|
|
||||||
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 } }) => (
|
|
||||||
<Input
|
|
||||||
disabled={disabled}
|
|
||||||
labelProps={{
|
|
||||||
className: 'before:content-none after:content-none'
|
|
||||||
}}
|
|
||||||
error={methods.formState.errors.phone ? true : false}
|
|
||||||
helperText={
|
|
||||||
methods.formState.errors.phone
|
|
||||||
? methods.formState.errors.phone.message?.toString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onChange={onChange}
|
|
||||||
value={value}
|
|
||||||
data-testid="pilot-form-phone-input"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{pilotId && (
|
|
||||||
<>
|
<>
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
<Typography variant="h6">Last Review</Typography>
|
<Typography variant="h6">Last Review</Typography>
|
||||||
@@ -354,8 +431,8 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)} */}
|
||||||
{pilotId && (
|
{/* {pilotId && (
|
||||||
<>
|
<>
|
||||||
<div className="col-span-4">
|
<div className="col-span-4">
|
||||||
<Typography variant="h5">Medical</Typography>
|
<Typography variant="h5">Medical</Typography>
|
||||||
@@ -434,35 +511,41 @@ const PilotForm: React.FC<IPilotFormProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<PilotFormEndorsements endorsements={[]} />
|
<PilotFormEndorsements endorsements={[]} />
|
||||||
</>
|
</>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</DrawerBody>
|
</DrawerBody>
|
||||||
<DrawerFooter>
|
<DrawerFooter>
|
||||||
<div className="flex gap-2 justify-end justify-self-center pt-4">
|
<>
|
||||||
<div>
|
{mode !== PilotFormMode.VIEW && (
|
||||||
<Button
|
<div className="flex gap-2 justify-end justify-self-center pt-4">
|
||||||
className="flex items-center gap-3"
|
<div>
|
||||||
variant="outlined"
|
<Button
|
||||||
onClick={onOpenCloseDrawer}
|
className="flex items-center gap-3"
|
||||||
data-testid="pilot-cancel-button"
|
disabled={isDisabled}
|
||||||
>
|
variant="outlined"
|
||||||
<XmarkIcon size="lg" />
|
onClick={onCancel}
|
||||||
Cancel
|
data-testid="pilot-cancel-button"
|
||||||
</Button>
|
>
|
||||||
</div>
|
<XmarkIcon size="lg" />
|
||||||
<div>
|
Cancel
|
||||||
<Button
|
</Button>
|
||||||
className="flex items-center gap-3"
|
</div>
|
||||||
loading={isLoading}
|
<div>
|
||||||
variant="filled"
|
<Button
|
||||||
type="submit"
|
className="flex items-center gap-3"
|
||||||
data-testid="pilot-save-button"
|
disabled={isDisabled}
|
||||||
>
|
loading={isLoading}
|
||||||
<SaveIcon size="lg" />
|
variant="filled"
|
||||||
Save
|
type="submit"
|
||||||
</Button>
|
data-testid="pilot-save-button"
|
||||||
</div>
|
>
|
||||||
</div>
|
<SaveIcon size="lg" />
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
</DrawerFooter>
|
</DrawerFooter>
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
|||||||
@@ -19,14 +19,34 @@ import {
|
|||||||
import { useHttpClient } from '../../hooks/httpClient/UseHttpClient';
|
import { useHttpClient } from '../../hooks/httpClient/UseHttpClient';
|
||||||
import { AxiosInstance, AxiosResponse } from 'axios';
|
import { AxiosInstance, AxiosResponse } from 'axios';
|
||||||
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
|
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
|
||||||
|
import { useIsAuthenticated } from '@azure/msal-react';
|
||||||
|
import { PilotFormMode } from '../pilotForm/PilotForm';
|
||||||
|
|
||||||
const Pilots: React.FC<unknown> = () => {
|
const Pilots: React.FC<unknown> = () => {
|
||||||
const httpClient: AxiosInstance = useHttpClient();
|
const httpClient: AxiosInstance = useHttpClient();
|
||||||
|
const isAuthenticated = useIsAuthenticated();
|
||||||
const { getAccessToken } = useAccessToken();
|
const { getAccessToken } = useAccessToken();
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||||
|
const [pilotFormMode, setPilotFormMode] = useState<PilotFormMode>(
|
||||||
|
PilotFormMode.CANCEL
|
||||||
|
);
|
||||||
|
const [selectedPilotId, setSelectedPilotId] = useState<string | undefined>();
|
||||||
const [pilots, setPilots] = useState<Pilot[]>([]);
|
const [pilots, setPilots] = useState<Pilot[]>([]);
|
||||||
const onOpenCloseDrawer = () => {
|
const onOpenClosePilotForm = (mode: PilotFormMode, pilotId?: string) => {
|
||||||
setIsDrawerOpen(!isDrawerOpen);
|
switch (mode) {
|
||||||
|
case PilotFormMode.ADD:
|
||||||
|
case PilotFormMode.EDIT:
|
||||||
|
case PilotFormMode.VIEW:
|
||||||
|
setPilotFormMode(mode);
|
||||||
|
setSelectedPilotId(pilotId);
|
||||||
|
setIsDrawerOpen(true);
|
||||||
|
break;
|
||||||
|
case PilotFormMode.CANCEL:
|
||||||
|
setPilotFormMode(mode);
|
||||||
|
setSelectedPilotId(undefined);
|
||||||
|
setIsDrawerOpen(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type Pilot = {
|
type Pilot = {
|
||||||
@@ -40,54 +60,66 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
{
|
{
|
||||||
accessorKey: 'name',
|
accessorKey: 'name',
|
||||||
header: 'Name'
|
header: 'Name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
header: 'Actions',
|
||||||
|
cellProps: {
|
||||||
|
className: 'text-end'
|
||||||
|
},
|
||||||
|
cell: (info: any) => {
|
||||||
|
const pilotId = info.row.original.rowKey;
|
||||||
|
return (
|
||||||
|
<Menu placement="bottom-end">
|
||||||
|
<MenuHandler>
|
||||||
|
<div>
|
||||||
|
<IconButton variant="text">
|
||||||
|
<EllipsisVerticalIcon size="xl" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</MenuHandler>
|
||||||
|
<MenuList>
|
||||||
|
<MenuItem
|
||||||
|
className="flex gap-3"
|
||||||
|
onClick={() =>
|
||||||
|
onOpenClosePilotForm(PilotFormMode.EDIT, pilotId)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<PenIcon size="lg" />
|
||||||
|
Edit
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
className="flex gap-3"
|
||||||
|
onClick={() =>
|
||||||
|
onOpenClosePilotForm(PilotFormMode.VIEW, pilotId)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<EyeIcon size="lg" />
|
||||||
|
View
|
||||||
|
</MenuItem>
|
||||||
|
<hr className="my-3" />
|
||||||
|
<MenuItem className="flex gap-3">
|
||||||
|
<TrashIcon size="lg" />
|
||||||
|
Delete
|
||||||
|
</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableSorting: false
|
||||||
}
|
}
|
||||||
// {
|
|
||||||
// id: 'actions',
|
|
||||||
// header: 'Actions',
|
|
||||||
// cellProps: {
|
|
||||||
// className: 'text-center'
|
|
||||||
// },
|
|
||||||
// cell: () => {
|
|
||||||
// return (
|
|
||||||
// <Menu placement="bottom-end">
|
|
||||||
// <MenuHandler>
|
|
||||||
// <div>
|
|
||||||
// <IconButton variant="text">
|
|
||||||
// <EllipsisVerticalIcon size="xl" />
|
|
||||||
// </IconButton>
|
|
||||||
// </div>
|
|
||||||
// </MenuHandler>
|
|
||||||
// <MenuList>
|
|
||||||
// <MenuItem className="flex gap-3">
|
|
||||||
// <PenIcon size="lg" />
|
|
||||||
// Edit
|
|
||||||
// </MenuItem>
|
|
||||||
// <MenuItem className="flex gap-3">
|
|
||||||
// <EyeIcon size="lg" />
|
|
||||||
// View
|
|
||||||
// </MenuItem>
|
|
||||||
// <hr className="my-3" />
|
|
||||||
// <MenuItem className="flex gap-3">
|
|
||||||
// <TrashIcon size="lg" />
|
|
||||||
// Delete
|
|
||||||
// </MenuItem>
|
|
||||||
// </MenuList>
|
|
||||||
// </Menu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// enableSorting: false
|
|
||||||
// }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getPilots = async () => {
|
const getPilots = async () => {
|
||||||
try {
|
try {
|
||||||
const accessToken: string = await getAccessToken();
|
const config = isAuthenticated
|
||||||
const response: AxiosResponse = await httpClient.get(`api/pilots`, {
|
? { headers: { Authorization: await getAccessToken() } }
|
||||||
headers: {
|
: {};
|
||||||
Authorization: accessToken
|
const response: AxiosResponse = await httpClient.get(
|
||||||
}
|
`api/pilots`,
|
||||||
});
|
config
|
||||||
|
);
|
||||||
console.log(response.data);
|
console.log(response.data);
|
||||||
setPilots(response.data);
|
setPilots(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -108,7 +140,7 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
<Button
|
<Button
|
||||||
className="flex justify-center gap-3"
|
className="flex justify-center gap-3"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
onClick={onOpenCloseDrawer}
|
onClick={() => onOpenClosePilotForm(PilotFormMode.ADD)}
|
||||||
data-testid="pilot-add-button"
|
data-testid="pilot-add-button"
|
||||||
>
|
>
|
||||||
<PlusIcon size="lg" />
|
<PlusIcon size="lg" />
|
||||||
@@ -119,7 +151,9 @@ const Pilots: React.FC<unknown> = () => {
|
|||||||
</div>
|
</div>
|
||||||
<PilotForm
|
<PilotForm
|
||||||
isDrawerOpen={isDrawerOpen}
|
isDrawerOpen={isDrawerOpen}
|
||||||
onOpenCloseDrawer={onOpenCloseDrawer}
|
mode={pilotFormMode}
|
||||||
|
onOpenClose={(mode) => onOpenClosePilotForm(mode)}
|
||||||
|
pilotId={selectedPilotId}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ISiteNavProps } from './ISiteNavProps';
|
|||||||
// import { Link as ReactRouterLink } from 'react-router-dom';
|
// import { Link as ReactRouterLink } from 'react-router-dom';
|
||||||
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
||||||
import {
|
import {
|
||||||
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
Menu,
|
Menu,
|
||||||
MenuHandler,
|
MenuHandler,
|
||||||
@@ -160,24 +161,21 @@ const SiteNav: React.FC<unknown> = () => {
|
|||||||
{!loading && isAuthenticated && (
|
{!loading && isAuthenticated && (
|
||||||
<Menu placement="bottom-end">
|
<Menu placement="bottom-end">
|
||||||
<MenuHandler>
|
<MenuHandler>
|
||||||
<>
|
<div>
|
||||||
{/* {userPhoto && */}
|
{userPhoto && (
|
||||||
{/* <img className='rounded-full' src={userPhoto} /> */}
|
<img
|
||||||
{/* } */}
|
className="rounded-full cursor-pointer"
|
||||||
{/* {!userPhoto && */}
|
height="40"
|
||||||
<div className="rounded-full text-white text-center pt-2 bg-black h-[40px] w-[40px]">
|
width="40"
|
||||||
NS
|
src={userPhoto}
|
||||||
</div>
|
/>
|
||||||
|
)}
|
||||||
{/* <div className="flex gap-2">
|
{!userPhoto && (
|
||||||
<div className='flex-none'>
|
<div className="rounded-full cursor-pointer text-white text-center pt-2 bg-black h-[40px] w-[40px]">
|
||||||
|
NS
|
||||||
</div>
|
</div>
|
||||||
<Button className='flex-1' variant="text" size="sm">
|
)}
|
||||||
{appContext.state.userProfile.displayName}
|
</div>
|
||||||
</Button>
|
|
||||||
</div> */}
|
|
||||||
</>
|
|
||||||
</MenuHandler>
|
</MenuHandler>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem onClick={handleSignOut}>
|
<MenuItem onClick={handleSignOut}>
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -76,7 +76,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.6.8",
|
"@noahspan/noahspan-components": "^0.7.0",
|
||||||
"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",
|
||||||
@@ -3397,9 +3397,9 @@
|
|||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@noahspan/noahspan-components": {
|
"node_modules/@noahspan/noahspan-components": {
|
||||||
"version": "0.6.8",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@noahspan/noahspan-components/-/noahspan-components-0.6.8.tgz",
|
"resolved": "https://registry.npmjs.org/@noahspan/noahspan-components/-/noahspan-components-0.7.0.tgz",
|
||||||
"integrity": "sha512-X4ftKtH9/ICyOCRKUnPMAh/u27Bi391edSKvIwWOxxawyIEZj58whjB0RfgcHV0O/Ap84lGt7YYctZTTzJHBEQ==",
|
"integrity": "sha512-UJ0UBEwjt3IJTUhdNh/sUMFuuruJl6zn8pzy2JOZoMBfRmrX/6Dzz+u+3F0Q7RAitgV/XhJskSYrJBripenGzw==",
|
||||||
"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",
|
||||||
|
|||||||
Reference in New Issue
Block a user