diff --git a/api/package.json b/api/package.json
index 2608c99..41a601c 100644
--- a/api/package.json
+++ b/api/package.json
@@ -1,6 +1,6 @@
{
"name": "api",
- "version": "1.2.0",
+ "version": "1.3.0",
"description": "",
"author": "",
"private": true,
diff --git a/app/.gitignore b/app/.gitignore
index a547bf3..019e1dc 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1,5 +1,4 @@
# Logs
-logs
*.log
npm-debug.log*
yarn-debug.log*
diff --git a/app/package.json b/app/package.json
index fabb051..1b54d75 100644
--- a/app/package.json
+++ b/app/package.json
@@ -1,7 +1,7 @@
{
"name": "app",
"private": true,
- "version": "1.2.0",
+ "version": "1.3.0",
"type": "module",
"scripts": {
"dev": "vite",
@@ -16,9 +16,7 @@
"@noahspan/noahspan-components": "1.6.0",
"axios": "^1.7.2",
"dotenv": "^16.4.7",
- "install": "^0.13.0",
"leaflet": "^1.9.4",
- "pure-react-carousel": "^1.32.0",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"react-hook-form": "^7.51.4",
diff --git a/app/public/layers-2x.png b/app/public/layers-2x.png
new file mode 100644
index 0000000..200c333
Binary files /dev/null and b/app/public/layers-2x.png differ
diff --git a/app/public/layers.png b/app/public/layers.png
new file mode 100644
index 0000000..1a72e57
Binary files /dev/null and b/app/public/layers.png differ
diff --git a/app/public/marker-icon-2x.png b/app/public/marker-icon-2x.png
new file mode 100644
index 0000000..88f9e50
Binary files /dev/null and b/app/public/marker-icon-2x.png differ
diff --git a/app/public/marker-icon.png b/app/public/marker-icon.png
new file mode 100644
index 0000000..950edf2
Binary files /dev/null and b/app/public/marker-icon.png differ
diff --git a/app/public/marker-shadow.png b/app/public/marker-shadow.png
new file mode 100644
index 0000000..9fd2979
Binary files /dev/null and b/app/public/marker-shadow.png differ
diff --git a/app/src/App.tsx b/app/src/App.tsx
index d9607ea..bb2fac4 100644
--- a/app/src/App.tsx
+++ b/app/src/App.tsx
@@ -1,6 +1,7 @@
import { Navigate, Route, Routes } from 'react-router-dom';
-import Pilots from './components/pilots/Pilots';
+import Flights from './components/flights/Flights';
import Logbook from './components/logbook/Logbook';
+import Pilots from './components/pilots/Pilots';
import SiteNav from './components/siteNav/SiteNav';
import { useIsAuthenticated } from '@azure/msal-react';
@@ -19,8 +20,10 @@ const App = () => {
<>
+ } />
+ } />
} />
- } />
+
>
);
diff --git a/app/src/components/flights/Flights.tsx b/app/src/components/flights/Flights.tsx
new file mode 100644
index 0000000..2ceee39
--- /dev/null
+++ b/app/src/components/flights/Flights.tsx
@@ -0,0 +1,32 @@
+import { Box, Container } from "@noahspan/noahspan-components";
+import LogbookCard from "../logbookCard/LogbookCard";
+import { useEffect, useState } from "react";
+import { useLogs } from "../../hooks/logs/UseLogs";
+import { ILogbookEntry } from "../logbook/ILogbookEntry";
+
+const Flights = () => {
+ const [flights, setFlights] = useState([])
+ const { logs } = useLogs();
+
+ useEffect(() => {
+ const flights: ILogbookEntry[] | undefined = logs?.filter((log: ILogbookEntry) => {
+ if (log.tracks && JSON.parse(log.tracks).length > 0) {
+ return log;
+ }
+ })
+
+ if (flights && flights.length > 0) {
+ setFlights(flights)
+ }
+ }, [logs])
+
+ return (
+
+
+
+
+
+ )
+}
+
+export default Flights;
\ No newline at end of file
diff --git a/app/src/components/logTrackMaps/LogTrackMaps.tsx b/app/src/components/logTrackMaps/LogTrackMaps.tsx
index e10ea56..f50be45 100644
--- a/app/src/components/logTrackMaps/LogTrackMaps.tsx
+++ b/app/src/components/logTrackMaps/LogTrackMaps.tsx
@@ -35,10 +35,7 @@ const LogTrackMaps = ({ rowKey, trackUrls }: LogTrackMapsProps) => {
config
);
const kml = new DOMParser().parseFromString(response.data, 'text/xml')
- // const converted = toGeoJSON.kml(dom);
-
- // rewind(converted, false);
-
+
convertedTracks.push(kml);
}
diff --git a/app/src/components/logTracks/LogTracks.tsx b/app/src/components/logTracks/LogTracks.tsx
index 91a2b9b..c00b3c0 100644
--- a/app/src/components/logTracks/LogTracks.tsx
+++ b/app/src/components/logTracks/LogTracks.tsx
@@ -1,5 +1,5 @@
import { useEffect, useReducer } from "react";
-import { Button, Drawer, Grid, Icon, IconButton, IconName, TextField, theme, Typography, useMediaQuery } from "@noahspan/noahspan-components";
+import { Button, Drawer, Grid, Icon, IconButton, IconName, Spinner, TextField, theme, Typography, useMediaQuery } from "@noahspan/noahspan-components";
import { useHttpClient } from "../../hooks/httpClient/UseHttpClient";
import { AxiosInstance, AxiosResponse } from "axios";
import { useAccessToken } from "../../hooks/accessToken/UseAcessToken";
@@ -132,13 +132,23 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
{`${mode.toString().toLowerCase().charAt(0).toUpperCase() + mode.toString().slice(1).toLowerCase()} Tracks`}
-
+
{mode === FormMode.EDIT &&
<>
- {state.tracks.length > 0 && state.tracks.map((track, index) => {
+ {state.isLoading &&
+ <>
+
+
+
+
+ Loading...
+
+ >
+ }
+ {!state.isLoading && state.tracks.length > 0 && state.tracks.map((track, index) => {
const trackSplit = track.split('/')
const filename = trackSplit[trackSplit.length - 1];
@@ -155,6 +165,7 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
})}
}
variant="outlined"
onClick={onCancel}
@@ -165,7 +176,7 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
{mode.toString() !== FormMode.VIEW && (
}
variant='contained'
>
diff --git a/app/src/components/logbook/Logbook.tsx b/app/src/components/logbook/Logbook.tsx
index 03e7d03..9764945 100644
--- a/app/src/components/logbook/Logbook.tsx
+++ b/app/src/components/logbook/Logbook.tsx
@@ -53,7 +53,7 @@ const Logbook: React.FC = () => {
accessorKey: 'tracks',
header: 'Tracks',
cell: (info: any) => {
- if (info.row.original.tracks && info.row.original.tracks.length > 0) {
+ if (info.row.original.tracks && JSON.parse(info.row.original.tracks).length > 0) {
return (
onOpenCloseTracks(FormMode.VIEW, info.row.original.rowKey)}>
)
@@ -261,7 +261,7 @@ const Logbook: React.FC = () => {
)}
{!isMedium && state.entries.length > 0 &&
-
+
}
)}
diff --git a/app/src/components/logbookCard/LogbookCard.tsx b/app/src/components/logbookCard/LogbookCard.tsx
index a8b9c07..0895dd2 100644
--- a/app/src/components/logbookCard/LogbookCard.tsx
+++ b/app/src/components/logbookCard/LogbookCard.tsx
@@ -1,10 +1,10 @@
import { Card, CardContent, CardHeader, Grid, Typography } from "@noahspan/noahspan-components";
import { LogbookCardProps } from "./LogbookCardProps.interface";
-import { useEffect } from "react";
import ActionMenu from "../actionMenu/ActionMenu";
+import LogTrackMaps from "../logTrackMaps/LogTrackMaps";
-const LogbookCard = ({ logs, onDelete, onOpenCloseForm }: LogbookCardProps) => {
+const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps) => {
return (
{logs.map((log) => {
@@ -12,7 +12,7 @@ const LogbookCard = ({ logs, onDelete, onOpenCloseForm }: LogbookCardProps) => {
}
+ action={mode === 'logbook' ? : null}
subheader={log.pilotName}
title={log.date}
slotProps={{
@@ -26,6 +26,14 @@ const LogbookCard = ({ logs, onDelete, onOpenCloseForm }: LogbookCardProps) => {
/>
+ {mode === 'flights' && log.tracks && JSON.parse(log.tracks).length > 0 &&
+
+
+
+ }
Aircraft Make and Model
@@ -50,6 +58,24 @@ const LogbookCard = ({ logs, onDelete, onOpenCloseForm }: LogbookCardProps) => {
{log.durationOfFlight}
+ {mode === 'logbook' && log.tracks && JSON.parse(log.tracks).length > 0 &&
+ <>
+
+ Tracks
+
+ {JSON.parse(log.tracks).map((track: string) => {
+ const trackSplit = track.split('/')
+ const filename = trackSplit[trackSplit.length - 1];
+
+ return (
+
+ {filename}
+
+ )
+
+ })}
+ >
+ }
{log.notes &&
<>
diff --git a/app/src/components/logbookCard/LogbookCardProps.interface.ts b/app/src/components/logbookCard/LogbookCardProps.interface.ts
index 5f57c21..1797638 100644
--- a/app/src/components/logbookCard/LogbookCardProps.interface.ts
+++ b/app/src/components/logbookCard/LogbookCardProps.interface.ts
@@ -3,6 +3,7 @@ import { ILogbookEntry } from "../logbook/ILogbookEntry";
export interface LogbookCardProps {
logs: ILogbookEntry[];
- onDelete: (entryId: string) => void;
- onOpenCloseForm: (formMode: FormMode, id: string) => void;
+ mode: 'flights' | 'logbook';
+ onDelete?: (entryId: string) => void;
+ onOpenCloseForm?: (formMode: FormMode, id: string) => void;
}
\ No newline at end of file
diff --git a/app/src/components/siteNav/SiteNav.tsx b/app/src/components/siteNav/SiteNav.tsx
index 629953c..a447015 100644
--- a/app/src/components/siteNav/SiteNav.tsx
+++ b/app/src/components/siteNav/SiteNav.tsx
@@ -32,9 +32,13 @@ const SiteNav = () => {
const getPages = () => {
const pages = [
{
- name: 'Logbook',
+ name: 'Flights',
url: '/'
},
+ {
+ name: 'Logbook',
+ url: '/logbook'
+ },
{
name: 'Pilots',
url: '/pilots'
@@ -79,7 +83,6 @@ const SiteNav = () => {
return imageUrl;
} catch (error) {
- console.log(error);
throw new Error();
}
};
diff --git a/app/src/hooks/logs/UseLogs.tsx b/app/src/hooks/logs/UseLogs.tsx
new file mode 100644
index 0000000..65ebc84
--- /dev/null
+++ b/app/src/hooks/logs/UseLogs.tsx
@@ -0,0 +1,40 @@
+import { useEffect, useState } from "react";
+import { useHttpClient } from "../httpClient/UseHttpClient";
+import { useAccessToken } from "../accessToken/UseAcessToken";
+import { useIsAuthenticated } from "@azure/msal-react";
+import { AxiosInstance, AxiosResponse } from "axios";
+import { ILogbookEntry } from "../../components/logbook/ILogbookEntry";
+
+export const useLogs = () => {
+ const [logs, setLogs] = useState();
+ const httpClient: AxiosInstance = useHttpClient();
+ const { getAccessToken } = useAccessToken();
+ const isAuthenticated = useIsAuthenticated();
+
+ useEffect(() => {
+ const getLogs = async () => {
+ try {
+ const config = isAuthenticated
+ ? { headers: { Authorization: await getAccessToken() } }
+ : {};
+ const response: AxiosResponse = await httpClient.get(
+ `api/logs`,
+ config
+ );
+ const logs: ILogbookEntry[] = response.data;
+
+ logs.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
+
+ setLogs(logs)
+ } catch (error) {
+ return error;
+ }
+ }
+
+ getLogs();
+ }, [])
+
+ return {
+ logs
+ }
+}
\ No newline at end of file
diff --git a/app/src/index.css b/app/src/index.css
index 690f168..26e49ae 100644
--- a/app/src/index.css
+++ b/app/src/index.css
@@ -1,3 +1,4 @@
body {
background-color: #f2f2f2;
+ margin: 8px;
}
diff --git a/package.json b/package.json
index 0e3ad57..567b9ca 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@noahspan/flying",
- "version": "1.2.0",
+ "version": "1.3.0",
"scripts": {
"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\"",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 68f2c73..469f56a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -166,15 +166,9 @@ importers:
dotenv:
specifier: ^16.4.7
version: 16.4.7
- install:
- specifier: ^0.13.0
- version: 0.13.0
leaflet:
specifier: ^1.9.4
version: 1.9.4
- pure-react-carousel:
- specifier: ^1.32.0
- version: 1.32.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
react:
specifier: 19.0.0-rc.1
version: 19.0.0-rc.1
@@ -3037,16 +3031,9 @@ packages:
babel-plugin-macros:
optional: true
- deep-freeze@0.0.1:
- resolution: {integrity: sha512-Z+z8HiAvsGwmjqlphnHW5oz6yWlOwu6EQfFTjmeTWlDeda3FS2yv3jhq35TX/ewmsnqB+RX2IdsIOyjJCQN5tg==}
-
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
- deepmerge@2.2.1:
- resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==}
- engines: {node: '>=0.10.0'}
-
deepmerge@4.2.2:
resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
engines: {node: '>=0.10.0'}
@@ -3185,9 +3172,6 @@ packages:
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
engines: {node: '>=18'}
- equals@1.0.5:
- resolution: {integrity: sha512-wI15a6ZoaaXPv+55+Vh2Kqn3+efKRv8QPtcGTjW5xmanMnQzESdAt566jevtMZyt3W/jwLDTzXpMph5ECDJ2zg==}
-
errno@0.1.8:
resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
hasBin: true
@@ -3741,10 +3725,6 @@ packages:
resolution: {integrity: sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==}
engines: {node: '>=18'}
- install@0.13.0:
- resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==}
- engines: {node: '>= 0.10'}
-
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@@ -4026,9 +4006,6 @@ packages:
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
hasBin: true
- jkroso-type@1.1.1:
- resolution: {integrity: sha512-zZgay+fPG6PgMUrpyFADmQmvLo39+AZa7Gc5pZhev2RhDxwANEq2etwD8d0e6rTg5NkwOIlQmaEmns3draC6Ng==}
-
joi@17.13.3:
resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
@@ -4791,12 +4768,6 @@ packages:
pure-rand@6.1.0:
resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
- pure-react-carousel@1.32.0:
- resolution: {integrity: sha512-RW0zKjjId1Xmspcqdu3v0pmlce3BVDACJ0fzt1+ZPJ4GxqJbIqt2kPAh1e5M19S5BDewihnq6kz4kIaGXleO3w==}
- peerDependencies:
- react: 15.x || 16.x || 17.x || 18.x
- react-dom: 15.x || 16.x || 17.x || 18.x
-
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
@@ -9067,12 +9038,8 @@ snapshots:
optionalDependencies:
babel-plugin-macros: 3.1.0
- deep-freeze@0.0.1: {}
-
deep-is@0.1.4: {}
- deepmerge@2.2.1: {}
-
deepmerge@4.2.2: {}
deepmerge@4.3.1: {}
@@ -9177,10 +9144,6 @@ snapshots:
environment@1.1.0: {}
- equals@1.0.5:
- dependencies:
- jkroso-type: 1.1.1
-
errno@0.1.8:
dependencies:
prr: 1.0.1
@@ -9957,8 +9920,6 @@ snapshots:
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
- install@0.13.0: {}
-
ipaddr.js@1.9.1: {}
is-arguments@1.2.0:
@@ -10415,8 +10376,6 @@ snapshots:
jiti@1.21.7: {}
- jkroso-type@1.1.1: {}
-
joi@17.13.3:
dependencies:
'@hapi/hoek': 9.3.0
@@ -11166,16 +11125,6 @@ snapshots:
pure-rand@6.1.0: {}
- pure-react-carousel@1.32.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1):
- dependencies:
- '@babel/runtime': 7.26.10
- deep-freeze: 0.0.1
- deepmerge: 2.2.1
- equals: 1.0.5
- prop-types: 15.8.1
- react: 19.0.0-rc.1
- react-dom: 19.0.0-rc.1(react@19.0.0-rc.1)
-
qs@6.13.0:
dependencies:
side-channel: 1.1.0