Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99019ddeef | |||
| 80f0e5437c | |||
| 80cfa52413 | |||
| 07575302cc | |||
| c765b06404 | |||
| 280067648f | |||
| 0ff30e9761 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "api",
|
"name": "api",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { Public } from '@noahspan/noahspan-modules';
|
|||||||
export class LogController {
|
export class LogController {
|
||||||
constructor(private readonly logService: LogService) {}
|
constructor(private readonly logService: LogService) {}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Get(':partitionKey/:rowKey')
|
@Get(':partitionKey/:rowKey')
|
||||||
async find(
|
async find(
|
||||||
@Param('partitionKey') partitionKey: string,
|
@Param('partitionKey') partitionKey: string,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ async function bootstrap() {
|
|||||||
const httpService = new HttpService();
|
const httpService = new HttpService();
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
|
app.enableCors();
|
||||||
app.setGlobalPrefix('api');
|
app.setGlobalPrefix('api');
|
||||||
app.useGlobalFilters(new HttpExceptionFilter());
|
app.useGlobalFilters(new HttpExceptionFilter());
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,10 @@ export class PilotInterceptor implements NestInterceptor {
|
|||||||
name: pilot.name
|
name: pilot.name
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
console.log(pilots)
|
||||||
return pilots;
|
return pilots;
|
||||||
} else {
|
} else {
|
||||||
return {
|
return data;
|
||||||
partitionKey: data.partitionKey,
|
|
||||||
rowKey: data.rowKey,
|
|
||||||
id: data.id,
|
|
||||||
name: data.name
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,18 +7,21 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
UseGuards,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { PilotDto } from './pilot.dto';
|
import { PilotDto } from './pilot.dto';
|
||||||
import { Pilot } from './pilot.entity';
|
import { Pilot } from './pilot.entity';
|
||||||
import { PilotService } from './pilot.service';
|
import { PilotService } from './pilot.service';
|
||||||
import { CustomError } from '../error/customError';
|
import { CustomError } from '../error/customError';
|
||||||
import { AuthGuard } from '@nestjs/passport'
|
import { Public } from '@noahspan/noahspan-modules'
|
||||||
|
import { PilotInterceptor } from './interceptors/pilot.interceptor';
|
||||||
|
|
||||||
@Controller('pilots')
|
@Controller('pilots')
|
||||||
|
@UseInterceptors(new PilotInterceptor())
|
||||||
export class PilotController {
|
export class PilotController {
|
||||||
constructor(private readonly pilotService: PilotService) {}
|
constructor(private readonly pilotService: PilotService) {}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Get(':partitionKey/:rowKey')
|
@Get(':partitionKey/:rowKey')
|
||||||
async find(
|
async find(
|
||||||
@Param('partitionKey') partitionKey: string,
|
@Param('partitionKey') partitionKey: string,
|
||||||
@@ -33,6 +36,7 @@ export class PilotController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Get()
|
@Get()
|
||||||
async findAll() {
|
async findAll() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# Flying
|
|
||||||
|
|
||||||
A pilot's logbook for tracking flight hours.
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -12,11 +12,13 @@ import {
|
|||||||
TrashIcon
|
TrashIcon
|
||||||
} from '@noahspan/noahspan-components';
|
} from '@noahspan/noahspan-components';
|
||||||
import { FormMode } from '../../enums/formMode';
|
import { FormMode } from '../../enums/formMode';
|
||||||
|
import { useIsAuthenticated } from '@azure/msal-react';
|
||||||
|
|
||||||
const ActionMenu = ({ id, onDelete, onOpenCloseForm }: IActionMenuProps) => {
|
const ActionMenu = ({ id, onDelete, onOpenCloseForm }: IActionMenuProps) => {
|
||||||
const [anchorElAction, setAnchorElAction] = useState<null | HTMLElement>(
|
const [anchorElAction, setAnchorElAction] = useState<null | HTMLElement>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
|
||||||
const onOpenActionMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const onOpenActionMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorElAction(event.currentTarget);
|
setAnchorElAction(event.currentTarget);
|
||||||
@@ -37,25 +39,31 @@ const ActionMenu = ({ id, onDelete, onOpenCloseForm }: IActionMenuProps) => {
|
|||||||
open={Boolean(anchorElAction)}
|
open={Boolean(anchorElAction)}
|
||||||
onClose={onCloseActionMenu}
|
onClose={onCloseActionMenu}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={() => onOpenCloseForm(FormMode.EDIT, id)}>
|
{isAuthenticated &&
|
||||||
<ListItemIcon>
|
<MenuItem onClick={() => onOpenCloseForm(FormMode.EDIT, id)}>
|
||||||
<PenIcon size="lg" />
|
<ListItemIcon>
|
||||||
</ListItemIcon>
|
<PenIcon size="lg" />
|
||||||
<ListItemText>Edit</ListItemText>
|
</ListItemIcon>
|
||||||
</MenuItem>
|
<ListItemText>Edit</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
}
|
||||||
<MenuItem onClick={() => onOpenCloseForm(FormMode.VIEW, id)}>
|
<MenuItem onClick={() => onOpenCloseForm(FormMode.VIEW, id)}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<EyeIcon size="lg" />
|
<EyeIcon size="lg" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText>View</ListItemText>
|
<ListItemText>View</ListItemText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<hr className="my-3" />
|
{isAuthenticated &&
|
||||||
<MenuItem onClick={() => onDelete(id)}>
|
<>
|
||||||
<ListItemIcon>
|
<hr className="my-3" />
|
||||||
<TrashIcon size="lg" />
|
<MenuItem onClick={() => onDelete(id)}>
|
||||||
</ListItemIcon>
|
<ListItemIcon>
|
||||||
<ListItemText>Delete</ListItemText>
|
<TrashIcon size="lg" />
|
||||||
</MenuItem>
|
</ListItemIcon>
|
||||||
|
<ListItemText>Delete</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
</>
|
||||||
|
}
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -123,6 +123,15 @@ const LogForm: React.FC<ILogFormProps> = ({
|
|||||||
);
|
);
|
||||||
const entry = response.data;
|
const entry = response.data;
|
||||||
|
|
||||||
|
// if (mode !== FormMode.ADD) {
|
||||||
|
// const pilot = pilots?.find((pilot) => pilot.id === entry.pilotId);
|
||||||
|
// console.log(pilot.name)
|
||||||
|
// dispatch({
|
||||||
|
// type: 'SET_SELECTED_ENTRY_PILOT_NAME',
|
||||||
|
// payload: pilot.name
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
methods.reset(entry);
|
methods.reset(entry);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const axiosError = error as AxiosError;
|
const axiosError = error as AxiosError;
|
||||||
@@ -139,7 +148,7 @@ const LogForm: React.FC<ILogFormProps> = ({
|
|||||||
}, [entryId]);
|
}, [entryId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pilots) {
|
if (pilots && FormMode.ADD) {
|
||||||
const newPilotsOptions = pilots.map((pilot) => {
|
const newPilotsOptions = pilots.map((pilot) => {
|
||||||
return {
|
return {
|
||||||
label: pilot.name,
|
label: pilot.name,
|
||||||
@@ -166,7 +175,7 @@ const LogForm: React.FC<ILogFormProps> = ({
|
|||||||
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid size={11}>
|
<Grid size={11}>
|
||||||
<Typography variant="h4">{`${mode.toString().toLowerCase().charAt(0).toUpperCase() + mode.toString().slice(1).toLowerCase()} Pilot`}</Typography>
|
<Typography variant="h4">{`${mode.toString().toLowerCase().charAt(0).toUpperCase() + mode.toString().slice(1).toLowerCase()} Entry`}</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid display="flex" justifyContent="right" size={1}>
|
<Grid display="flex" justifyContent="right" size={1}>
|
||||||
<IconButton onClick={onCancel}>
|
<IconButton onClick={onCancel}>
|
||||||
@@ -194,50 +203,29 @@ const LogForm: React.FC<ILogFormProps> = ({
|
|||||||
name="pilotId"
|
name="pilotId"
|
||||||
control={methods.control}
|
control={methods.control}
|
||||||
render={({ field: { onChange, value } }) => {
|
render={({ field: { onChange, value } }) => {
|
||||||
useEffect(() => {
|
console.log(value)
|
||||||
if (value && mode !== FormMode.ADD) {
|
|
||||||
const pilot = pilots?.find((pilot) => pilot.id === value);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_SELECTED_ENTRY_PILOT_NAME',
|
|
||||||
payload: pilot.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Select
|
||||||
{mode === FormMode.ADD && (
|
disabled={state.isDisabled}
|
||||||
<Select
|
fullWidth
|
||||||
disabled={state.isDisabled}
|
onChange={(event) => {
|
||||||
fullWidth
|
const pilot = pilots?.find(
|
||||||
onChange={(event) => {
|
(pilot) => (pilot.id = event.target.value)
|
||||||
const pilot = pilots?.find(
|
);
|
||||||
(pilot) => (pilot.id = event.target.value)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (pilot) {
|
if (pilot) {
|
||||||
methods.setValue('pilotName', pilot.name);
|
methods.setValue('pilotName', pilot.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
methods.setValue('pilotId', event.target.value);
|
methods.setValue('pilotId', event.target.value);
|
||||||
}}
|
}}
|
||||||
options={
|
options={
|
||||||
state.pilotOptions && state.pilotOptions.length > 0
|
state.pilotOptions && state.pilotOptions.length > 0
|
||||||
? state.pilotOptions
|
? state.pilotOptions
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
value={value}
|
value={value ? value : ''}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{mode !== FormMode.ADD && (
|
|
||||||
<TextField
|
|
||||||
disabled={true}
|
|
||||||
fullWidth
|
|
||||||
value={state.selectedEntryPilotName}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
dispatch({ type: 'SET_IS_LOADING', payload: true });
|
dispatch({ type: 'SET_IS_LOADING', payload: true });
|
||||||
|
|
||||||
const response: AxiosResponse = await httpClient.get(`api/logs`);
|
const response: AxiosResponse = await httpClient.get(`api/logs`);
|
||||||
|
const entries: ILogbookEntry[] = response.data;
|
||||||
|
|
||||||
|
entries.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
|
||||||
|
|
||||||
if (response.data.length > 0) {
|
if (response.data.length > 0) {
|
||||||
dispatch({ type: 'SET_ENTRIES', payload: response.data });
|
dispatch({ type: 'SET_ENTRIES', payload: response.data });
|
||||||
@@ -167,9 +170,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info: any) =>
|
cell: (info: any) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'singleEngineLand',
|
accessorKey: 'singleEngineLand',
|
||||||
@@ -179,9 +180,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info: any) =>
|
cell: (info: any) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'landings',
|
id: 'landings',
|
||||||
@@ -223,9 +222,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'instrumentSimulated',
|
accessorKey: 'instrumentSimulated',
|
||||||
@@ -235,9 +232,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'instrumentApproaches',
|
accessorKey: 'instrumentApproaches',
|
||||||
@@ -280,9 +275,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'flightTrainingReceived',
|
accessorKey: 'flightTrainingReceived',
|
||||||
@@ -292,9 +285,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'crossCountry',
|
accessorKey: 'crossCountry',
|
||||||
@@ -304,9 +295,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'night',
|
accessorKey: 'night',
|
||||||
@@ -316,9 +305,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'solo',
|
accessorKey: 'solo',
|
||||||
@@ -328,9 +315,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'pilotInCommand',
|
accessorKey: 'pilotInCommand',
|
||||||
@@ -340,9 +325,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
headerAlign: 'right'
|
headerAlign: 'right'
|
||||||
},
|
},
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue() !== null
|
info.getValue() ? parseFloat(info.getValue()).toFixed(1) : ''
|
||||||
? parseFloat(info.getValue()).toFixed(1)
|
|
||||||
: ''
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- '3000:3000'
|
- '3000:3000'
|
||||||
env_file:
|
env_file:
|
||||||
- ./api/.env
|
- ./api/.env.compose
|
||||||
|
|
||||||
app:
|
app:
|
||||||
container_name: flying-app
|
container_name: flying-app
|
||||||
@@ -27,7 +27,5 @@ services:
|
|||||||
target: app
|
target: app
|
||||||
ports:
|
ports:
|
||||||
- '8080:8080'
|
- '8080:8080'
|
||||||
# env_file:
|
|
||||||
# - ./app/.env
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
module "storage" {
|
||||||
|
source = "github.com/noahspannbauer/noahspan-terraform/modules/storage"
|
||||||
|
resource_group_name = var.RESOURCE_GROUP_NAME
|
||||||
|
storage_account_name = module.environment.storage_account_name
|
||||||
|
storage_tables = module.environment.storage_tables
|
||||||
|
}
|
||||||
|
|
||||||
module "container_app" {
|
module "container_app" {
|
||||||
source = "github.com/noahspannbauer/noahspan-terraform/modules/container_app"
|
source = "github.com/noahspannbauer/noahspan-terraform/modules/container_app"
|
||||||
app_subdomain_name = var.APP_SUBDOMAIN_NAME
|
app_subdomain_name = var.APP_SUBDOMAIN_NAME
|
||||||
@@ -17,7 +24,6 @@ module "container_app" {
|
|||||||
domain_name = var.DOMAIN_NAME
|
domain_name = var.DOMAIN_NAME
|
||||||
log_analytics_workspace_name = module.environment.log_analytics_workspace_name
|
log_analytics_workspace_name = module.environment.log_analytics_workspace_name
|
||||||
resource_group_name = var.RESOURCE_GROUP_NAME
|
resource_group_name = var.RESOURCE_GROUP_NAME
|
||||||
storage_account_name = module.environment.storage_account_name
|
storage_account_primary_connection_string = module.storage.storage_account_primary_connection_string
|
||||||
storage_tables = module.environment.storage_tables
|
|
||||||
tenant_id = var.TENANT_ID
|
tenant_id = var.TENANT_ID
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@noahspan/flying",
|
"name": "@noahspan/flying",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "concurrently 'npm run start:azure -w api' 'wait-on tcp:0.0.0.0:7071 && npm run dev -w app'",
|
"start": "concurrently 'npm run start:azure -w api' 'wait-on tcp:0.0.0.0:7071 && npm run dev -w app'",
|
||||||
"format": "prettier --write \"**/src/**/*.ts\" \"**/test/**/*.ts\"",
|
"format": "prettier --write \"**/src/**/*.ts\" \"**/test/**/*.ts\"",
|
||||||
|
|||||||
Reference in New Issue
Block a user