Compare commits

...

7 Commits

13 changed files with 87 additions and 106 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "api", "name": "api",
"version": "1.0.0", "version": "1.0.1",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,

View File

@@ -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,

View File

@@ -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());

View File

@@ -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
};
} }
}) })
); );

View File

@@ -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 {

View File

@@ -1,3 +0,0 @@
# Flying
A pilot's logbook for tracking flight hours.

View File

@@ -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",

View File

@@ -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>
); );

View File

@@ -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}
/>
)}
</>
); );
}} }}
/> />

View File

@@ -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)
: ''
} }
] ]
}, },

View File

@@ -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

View File

@@ -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
} }

View File

@@ -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\"",