switching to sqlite

This commit is contained in:
2025-08-20 21:04:32 -05:00
parent e1605992b0
commit ab304a469b
20 changed files with 231 additions and 232 deletions

View File

@@ -19,17 +19,17 @@ jobs:
version_number: ${{ github.run_id }} version_number: ${{ github.run_id }}
secrets: inherit secrets: inherit
deploy-api: # deploy-api:
name: deploy-api # name: deploy-api
needs: # needs:
- changes # - changes
- build-api # - build-api
uses: ./.github/workflows/deploy.yaml # uses: ./.github/workflows/deploy.yaml
with: # with:
app_name: flying-api # app_name: flying-api
environment_name: test # environment_name: test
version_number: ${{ github.run_id }} # version_number: ${{ github.run_id }}
secrets: inherit # secrets: inherit
build-app: build-app:
if: ${{ needs.changes.outputs.app == 'true' }} if: ${{ needs.changes.outputs.app == 'true' }}
@@ -42,14 +42,14 @@ jobs:
version_number: ${{ github.run_id }} version_number: ${{ github.run_id }}
secrets: inherit secrets: inherit
deploy-app: # deploy-app:
name: deploy-app # name: deploy-app
needs: # needs:
- changes # - changes
- build-app # - build-app
uses: ./.github/workflows/deploy.yaml # uses: ./.github/workflows/deploy.yaml
with: # with:
app_name: flying-app # app_name: flying-app
environment_name: test # environment_name: test
version_number: ${{ github.run_id }} # version_number: ${{ github.run_id }}
secrets: inherit # secrets: inherit

View File

@@ -1,6 +1,6 @@
{ {
"name": "api", "name": "api",
"version": "1.4.0", "version": "2.0.0-alpha",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,

View File

@@ -38,7 +38,6 @@ import { ConfigService } from '@nestjs/config';
} }
async uploadFile(file: Express.Multer.File, containerName: string, logId: string): Promise<string> { async uploadFile(file: Express.Multer.File, containerName: string, logId: string): Promise<string> {
console.log(file)
this.containerName = containerName; this.containerName = containerName;
const blockBlobClient = await this.getBlobClient(`${logId}/${file.originalname}`); const blockBlobClient = await this.getBlobClient(`${logId}/${file.originalname}`);

View File

@@ -1,23 +1,24 @@
import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common'; import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
import { Observable, map } from 'rxjs'; import { Observable, map } from 'rxjs';
import { LogEntity } from '../log.entity';
export class LogInterceptor implements NestInterceptor { export class LogInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, handler: CallHandler): Observable<any> { intercept(context: ExecutionContext, handler: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest(); const req = context.switchToHttp().getRequest();
const authHeader = req.headers.authorization; const authHeader = req.headers.authorization;
const token = authHeader && authHeader.split(' ')[1]; const token = authHeader && authHeader.split(' ')[1];
console.log(token)
if (!token) { if (!token) {
return handler.handle().pipe( return handler.handle().pipe(
map((data) => { map((data: LogEntity[]) => {
if (data.length) { if (data.length) {
const logs = data.map((log) => { const logs = data.map((log: LogEntity) => {
return { return {
partitionKey: log.partitionKey, id: log.id,
rowKey: log.rowKey, pilot: {
pilotId: log.pilotId, name: log.pilot.name
pilotName: log.pilotName, },
date: log.date, date: log.date,
aircraftMakeModel: log.aircraftMakeModel, aircraftMakeModel: log.aircraftMakeModel,
routeFrom: log.routeFrom, routeFrom: log.routeFrom,

View File

@@ -1,5 +1,4 @@
import { PilotEntity } from "src/pilot/pilot.entity"; import { PilotEntity } from "src/pilot/pilot.entity";
import { TrackEntity } from "src/track/track.entity";
export class LogDto { export class LogDto {
pilotId: string; pilotId: string;
@@ -26,5 +25,4 @@ export class LogDto {
pilotInCommand?: number; pilotInCommand?: number;
notes?: string; notes?: string;
pilot?: PilotEntity; pilot?: PilotEntity;
tracks?: TrackEntity[];
} }

View File

@@ -20,12 +20,14 @@ export class LogService {
where: { id: id }, where: { id: id },
relations: ['pilot', 'tracks'] relations: ['pilot', 'tracks']
}); });
console.log(logEntity);
return logEntity; return logEntity;
} }
async findAll(): Promise<LogEntity[]> { async findAll(): Promise<LogEntity[]> {
return await this.logRepository.find(); return await this.logRepository.find({
relations: ['pilot', 'tracks']
});
} }
async create(logDto: LogDto): Promise<InsertResult> { async create(logDto: LogDto): Promise<InsertResult> {

View File

@@ -29,7 +29,7 @@ export class TrackController {
) {} ) {}
// @Get('id') // @Get('id')
// async fin(@Param('id') id: string): Promise<TrackEntity> { // async find(@Param('id') id: string): Promise<TrackEntity> {
// try { // try {
// return await this.trackService.find(id); // return await this.trackService.find(id);
// } catch (error) { // } catch (error) {
@@ -39,21 +39,22 @@ export class TrackController {
// } // }
// } // }
// @Get() @Get(':logId')
// async findAdd(): Promise<TrackEntity[]> { async findAll(@Param('logId') logId: string): Promise<TrackEntity[]> {
// try { try {
// return await this.trackService.findAll(); return await this.trackService.findAll(logId);
// } catch (error) { } catch (error) {
// const customError = error as CustomError; const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode) throw new HttpException(customError.message, customError.statusCode);
// } }
// } }
@UseGuards(AuthGuard)
@Post(':logId/:order') @Post(':logId/:order')
@UseInterceptors(FileInterceptor('file'))
async create(@Param('logId') logId: string, @Param('order') order: number, @UploadedFile() file: Express.Multer.File): Promise<InsertResult> { async create(@Param('logId') logId: string, @Param('order') order: number, @UploadedFile() file: Express.Multer.File): Promise<InsertResult> {
try { try {
console.log(file)
return await this.trackService.create(logId, order, file); return await this.trackService.create(logId, order, file);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -62,21 +63,12 @@ export class TrackController {
} }
} }
// @Put(':id') @UseGuards(AuthGuard)
// async update(@Param('id') id: string, @Body() trackDto: TrackDto): Promise<UpdateResult> {
// try {
// return await this.trackService.update(id, trackDto);
// } catch (error) {
// const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode)
// }
// }
@Delete(':id') @Delete(':id')
async delete(@Param('id') id: string): Promise<DeleteResult> { async delete(@Param('id') id: string, @Query('fileName') fileName: string, @Query('logId') logId: string): Promise<DeleteResult> {
try { try {
return await this.trackService.delete(id);
return await this.trackService.delete(id, logId, fileName);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -84,37 +76,10 @@ export class TrackController {
} }
} }
@UseGuards(AuthGuard) @Get(':logId/:fileName')
@Post(':partitionKey/:rowKey/track') async downloadTrack(@Param('logId') logId: string, @Param('fileName') fileName: string): Promise<string> {
@UseInterceptors(FileInterceptor('file'))
async createTrack(@Param('rowKey') rowKey: string, @UploadedFile() file: Express.Multer.File) {
try { try {
const containerName = 'tracks'; return await this.trackService.downloadTrackFile(logId, fileName);
const url = await this.fileService.uploadFile(file, containerName, rowKey);
return { url }
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Get(':partitionKey/:rowKey/track')
async downloadTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<string> {
const containerName = 'tracks';
const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName)
return downloadedFile;
}
@UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey/track')
async deleteTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<void> {
try {
const containerName = 'tracks';
return await this.fileService.deleteFile(containerName, rowKey, fileName)
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -127,7 +92,3 @@ export class TrackController {

View File

@@ -10,32 +10,51 @@ import { FileService } from "src/file/file.service";
@Injectable() @Injectable()
export class TrackService { export class TrackService {
private readonly containerName: string = 'tracks'
constructor( constructor(
@InjectRepository(TrackEntity) private readonly trackRepository: Repository<TrackEntity>, @InjectRepository(TrackEntity) private readonly trackRepository: Repository<TrackEntity>,
private readonly fileService: FileService, private readonly fileService: FileService,
private readonly logService: LogService private readonly logService: LogService
) {} ) {}
// async find(id: string): Promise<TrackEntity> { async find(id: string): Promise<TrackEntity> {
// return await this.trackRepository.findOneBy({ id }); try {
// } return await this.trackRepository.findOneBy({ id });
} catch (error) {
throw new CustomError('Track not found', 'Not found', 404)
}
}
// async findAll(logId: string): Promise<TrackEntity[]> { async findAll(logId: string): Promise<TrackEntity[]> {
// try { try {
// await this.trackRepository.findOneBy({ }) const logEntity: LogEntity = await this.logService.find(logId);
// } catch (error) {
// } if (logEntity) {
console.log(logEntity)
// } const tracks = await this.trackRepository.find({
where: { log:
{
id: logEntity.id
}
},
})
console.log(tracks)
return tracks;
}
} catch (error) {
console.log(error)
throw new CustomError('Tracks not found', 'Not found', 404);
}
}
async create(logId: string, order: number, file: Express.Multer.File): Promise<InsertResult> { async create(logId: string, order: number, file: Express.Multer.File): Promise<InsertResult> {
try { try {
const logEntity: LogEntity = await this.logService.find(logId); const logEntity: LogEntity = await this.logService.find(logId);
if (logEntity) { if (logEntity) {
const containerName = 'tracks'; const url = await this.fileService.uploadFile(file, this.containerName, logId);
const url = await this.fileService.uploadFile(file, containerName, logId);
const track = this.trackRepository.create({ const track = this.trackRepository.create({
log: logEntity, log: logEntity,
order: order, order: order,
@@ -52,37 +71,31 @@ export class TrackService {
} }
} }
// async update(id: string, track: TrackDto): Promise<UpdateResult> { async update(id: string, track: TrackDto): Promise<UpdateResult> {
// return await this.trackRepository.update(id, track);
// }
async delete(id: string): Promise<DeleteResult> {
try { try {
return await this.trackRepository.update(id, track);
} catch (error) { } catch(error) {
throw error
} }
return await this.trackRepository.delete({ id });
} }
// async createTrack() async delete(id: string, logId: string, fileName: string): Promise<DeleteResult> {
try {
await this.fileService.deleteFile(this.containerName, logId, fileName);
// async downloadTrack(): Promise<string> { return await this.trackRepository.delete({ id });
// const containerName = 'tracks'; } catch (error) {
// const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName); throw error
}
}
// return downloadedFile; async downloadTrackFile(logId: string, fileName: string): Promise<string> {
// } try {
const downloadedFile: string = await this.fileService.downloadFile(this.containerName, logId, fileName);
// async deleteTrack(): Promsie<void> { return downloadedFile;
// try { } catch (error) {
// const containerName = 'tracks'; throw error
}
// return await this.fileService.deleteFile(containerName, rowKey, fileName) }
// } catch (error) {
// const customError = error as CustomError;
// throw customError;
// }
// }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "app", "name": "app",
"private": true, "private": true,
"version": "1.4.0", "version": "2.0.0-alpha",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -10,8 +10,9 @@ const Flights = () => {
const isMedium = useMediaQuery(theme.breakpoints.up('md')); const isMedium = useMediaQuery(theme.breakpoints.up('md'));
useEffect(() => { useEffect(() => {
console.log(logs)
const flights: ILogbookEntry[] | undefined = logs?.filter((log: ILogbookEntry) => { const flights: ILogbookEntry[] | undefined = logs?.filter((log: ILogbookEntry) => {
if (log.tracks && JSON.parse(log.tracks).length > 0) { if (log.tracks && log.tracks.length > 0) {
return log; return log;
} }
}) })

View File

@@ -12,24 +12,25 @@ import 'swiper/css';
import './LogTrackMaps.css'; import './LogTrackMaps.css';
import 'leaflet/dist/leaflet.css'; import 'leaflet/dist/leaflet.css';
const LogTrackMaps = ({ rowKey, trackUrls }: LogTrackMapsProps) => { const LogTrackMaps = ({ logId, tracks }: LogTrackMapsProps) => {
const [tracks, setTracks] = useState<any[]>([]) const [kmls, setKmls] = useState<any[]>([])
const httpClient: AxiosInstance = useHttpClient(); const httpClient: AxiosInstance = useHttpClient();
const { getAccessToken } = useAccessToken(); const { getAccessToken } = useAccessToken();
const isAuthenticated = useIsAuthenticated(); const isAuthenticated = useIsAuthenticated();
useEffect(() => { useEffect(() => {
console.log(tracks)
const getTracks = async () => { const getTracks = async () => {
const convertedTracks: any[] = [] const convertedTracks: any[] = []
for (const trackUrl of trackUrls) { for (const track of tracks) {
const trackUrlSplit = trackUrl.split('/') const trackUrlSplit = track.url.split('/')
const filename = trackUrlSplit[trackUrlSplit.length - 1]; const filename = trackUrlSplit[trackUrlSplit.length - 1];
const config = isAuthenticated const config = isAuthenticated
? { headers: { Authorization: await getAccessToken() } } ? { headers: { Authorization: await getAccessToken() } }
: {}; : {};
const response: AxiosResponse = await httpClient.get( const response: AxiosResponse = await httpClient.get(
`api/logs/log/${rowKey}/track?fileName=${filename}`, `api/tracks/${logId}/${filename}`,
config config
); );
const kml = new DOMParser().parseFromString(response.data, 'text/xml') const kml = new DOMParser().parseFromString(response.data, 'text/xml')
@@ -37,11 +38,11 @@ const LogTrackMaps = ({ rowKey, trackUrls }: LogTrackMapsProps) => {
convertedTracks.push(kml); convertedTracks.push(kml);
} }
setTracks(convertedTracks) setKmls(convertedTracks)
} }
getTracks(); getTracks();
}, [trackUrls]) }, [tracks])
return ( return (
<MapContainer <MapContainer
@@ -54,8 +55,8 @@ const LogTrackMaps = ({ rowKey, trackUrls }: LogTrackMapsProps) => {
attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/> />
{tracks.length > 0 && tracks.map((track) => ( {kmls.length > 0 && kmls.map((kml) => (
<ReactLeafletKml kml={track} /> <ReactLeafletKml kml={kml} />
))} ))}
</MapContainer> </MapContainer>
); );

View File

@@ -1,4 +1,4 @@
export interface LogTrackMapsProps { export interface LogTrackMapsProps {
rowKey: string; logId: string;
trackUrls: string[]; tracks: {id: string; order: number; url: string}[];
} }

View File

@@ -11,7 +11,7 @@ import ConfirmationDialog from "../confirmationDialog/ConfirmationDialog";
import { initialState, reducer } from "./reducer"; import { initialState, reducer } from "./reducer";
import LogTrackMaps from "../logTrackMaps/LogTrackMaps"; import LogTrackMaps from "../logTrackMaps/LogTrackMaps";
const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTracksProps) => { const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedLogId }: LogTracksProps) => {
const [state, dispatch] = useReducer(reducer, initialState) const [state, dispatch] = useReducer(reducer, initialState)
const httpClient: AxiosInstance = useHttpClient(); const httpClient: AxiosInstance = useHttpClient();
const { getAccessToken } = useAccessToken(); const { getAccessToken } = useAccessToken();
@@ -27,50 +27,49 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
return config return config
} }
const getLog = async (): Promise<ILogbookEntry> => { // const getLog = async (): Promise<ILogbookEntry> => {
const logResponse: AxiosResponse = await httpClient.get( // const logResponse: AxiosResponse = await httpClient.get(
`api/logs/${selectedRowKey}`, // `api/tracks/${selectedLogId}`,
await getConfig() // await getConfig()
); // );
const logData: ILogbookEntry = logResponse.data; // const logData: ILogbookEntry = logResponse.data;
return logData // return logData
} // }
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => { // const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>, order: number) => {
try { // try {
dispatch({ type: 'SET_IS_LOADING', payload: true}) // dispatch({ type: 'SET_IS_LOADING', payload: true})
const file = event.target.files![0] // const file = event.target.files![0]
const formData = new FormData(); // const formData = new FormData();
const config = await getConfig(); // const config = await getConfig();
const formDataConfig = { // const formDataConfig = {
headers: { // headers: {
...config.headers, // ...config.headers,
'Content-Type': 'multipart/form-data' // 'Content-Type': 'multipart/form-data'
} // }
} // }
formData.append('file', file); // formData.append('file', file);
const uploadResponse: AxiosResponse = await httpClient.post(`api/logs/log/${selectedRowKey}/track`, formData, formDataConfig); // const uploadResponse: AxiosResponse = await httpClient.post(`api/tracks/${selectedLogId}/${order}`, formData, formDataConfig);
const uploadUrl = uploadResponse.data.url; // const uploadUrl = uploadResponse.data.url;
const log = await getLog(); // const tracks: string[] = log.tracks ? JSON.parse(log.tracks!) : [];
const tracks: string[] = log.tracks ? JSON.parse(log.tracks!) : [];
tracks.push(uploadUrl) // tracks.push(uploadUrl)
log.tracks = JSON.stringify(tracks); // log.tracks = JSON.stringify(tracks);
await httpClient.put(`api/logs/log/${selectedRowKey}`, log, config); // await httpClient.put(`api/logs/log/${selectedRowKey}`, log, config);
const updatedLog = await getLog(); // const updatedLog = await getLog();
dispatch({ type: 'SET_TRACKS', payload: JSON.parse(updatedLog.tracks!) }) // dispatch({ type: 'SET_TRACKS', payload: JSON.parse(updatedLog.tracks!) })
} catch (error) { // } catch (error) {
console.log(error) // console.log(error)
} finally { // } finally {
dispatch({ type: 'SET_IS_LOADING', payload: false }); // dispatch({ type: 'SET_IS_LOADING', payload: false });
} // }
} // }
const onCancel = () => { const onCancel = () => {
onOpenClose(FormMode.CANCEL) onOpenClose(FormMode.CANCEL)
@@ -80,41 +79,54 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
dispatch({ type: 'SET_ON_DELETE_TRACK', payload: { isConfirmDialogOpen: true, selectedTrack: { fileName: fileName, index: index }}}) dispatch({ type: 'SET_ON_DELETE_TRACK', payload: { isConfirmDialogOpen: true, selectedTrack: { fileName: fileName, index: index }}})
} }
const onConfirmDialogConfirm = async () => { // const onConfirmDialogConfirm = async () => {
try { // try {
const config = await getConfig(); // const config = await getConfig();
await httpClient.delete(`api/logs/log/${selectedRowKey}/track?fileName=${state.selectedTrack!.fileName}`, config); // await httpClient.delete(`api/logs/log/${selectedRowKey}/track?fileName=${state.selectedTrack!.fileName}`, config);
const log = await getLog(); // const log = await getLog();
const tracks: string[] = JSON.parse(log.tracks!); // const tracks: string[] = JSON.parse(log.tracks!);
tracks.splice(state.selectedTrack!.index, 1); // tracks.splice(state.selectedTrack!.index, 1);
log.tracks = JSON.stringify(tracks); // log.tracks = JSON.stringify(tracks);
await httpClient.put(`api/logs/log/${selectedRowKey}`, log, config); // await httpClient.put(`api/logs/log/${selectedRowKey}`, log, config);
const updatedLog = await getLog(); // const updatedLog = await getLog();
dispatch({ type: 'SET_TRACKS', payload: JSON.parse(updatedLog.tracks!) }) // dispatch({ type: 'SET_TRACKS', payload: JSON.parse(updatedLog.tracks!) })
dispatch({ type: 'SET_IS_CONFIRM_DIALOG_OPEN', payload: false }) // dispatch({ type: 'SET_IS_CONFIRM_DIALOG_OPEN', payload: false })
} catch (error) { // } catch (error) {
console.log(error); // console.log(error);
} // }
} // }
const onConfirmDialogCancel = async () => { const onConfirmDialogCancel = async () => {
dispatch({ type: 'SET_IS_CONFIRM_DIALOG_OPEN', payload: false }) dispatch({ type: 'SET_IS_CONFIRM_DIALOG_OPEN', payload: false })
} }
useEffect(() => { // useEffect(() => {
const updateTracks = async () => { // const updateTracks = async () => {
const log = await getLog(); // const log = await getLog();
dispatch({ type: 'SET_TRACKS', payload: JSON.parse(log.tracks!) }); // dispatch({ type: 'SET_TRACKS', payload: log.tracks! });
} // }
updateTracks(); // updateTracks();
}, []) // }, [])
useEffect(() => {
if (isDrawerOpen) {
console.log(selectedLogId)
const getTracks = async () => {
const tracks = await httpClient.get(`api/tracks/${selectedLogId}`);
console.log(tracks);
}
getTracks();
}
}, [isDrawerOpen])
return ( return (
<Drawer <Drawer
@@ -149,7 +161,7 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
</> </>
} }
{!state.isLoading && state.tracks.length > 0 && state.tracks.map((track, index) => { {!state.isLoading && state.tracks.length > 0 && state.tracks.map((track, index) => {
const trackSplit = track.split('/') const trackSplit = track.url.split('/')
const filename = trackSplit[trackSplit.length - 1]; const filename = trackSplit[trackSplit.length - 1];
return ( return (
@@ -181,17 +193,17 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
variant='contained' variant='contained'
> >
Upload Track Upload Track
<input hidden onChange={handleFileUpload} type='file' /> {/* <input hidden onChange={handleFileUpload} type='file' /> */}
</Button> </Button>
)} )}
</Grid> </Grid>
</> </>
} }
{mode === FormMode.VIEW && {mode === FormMode.VIEW &&
<LogTrackMaps rowKey={selectedRowKey!} trackUrls={state.tracks} /> <LogTrackMaps logId={selectedLogId!} tracks={state.tracks} />
} }
</Grid> </Grid>
{state.isConfirmDialogOpen && ( {/* {state.isConfirmDialogOpen && (
<ConfirmationDialog <ConfirmationDialog
contentText="Are you sure you want to delete this track?" contentText="Are you sure you want to delete this track?"
isLoading={state.isConfirmDialogLoading} isLoading={state.isConfirmDialogLoading}
@@ -200,7 +212,7 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
onConfirm={onConfirmDialogConfirm} onConfirm={onConfirmDialogConfirm}
title="Confirm Delete" title="Confirm Delete"
/> />
)} )} */}
</Drawer> </Drawer>
) )

View File

@@ -4,5 +4,5 @@ export interface LogTracksProps {
isDrawerOpen: boolean; isDrawerOpen: boolean;
mode: FormMode; mode: FormMode;
onOpenClose: (mode: FormMode) => void; onOpenClose: (mode: FormMode) => void;
selectedRowKey: string | undefined; selectedLogId: string | undefined;
} }

View File

@@ -6,5 +6,5 @@ export interface LogTracksState {
fileName: string, fileName: string,
index: number index: number
} | undefined; } | undefined;
tracks: string[]; tracks: { id: string; order: number; url: string; }[];
} }

View File

@@ -5,7 +5,7 @@ type Action =
| { type: 'SET_IS_CONFORM_DIALOG_LOADING'; payload: boolean } | { type: 'SET_IS_CONFORM_DIALOG_LOADING'; payload: boolean }
| { type: 'SET_IS_LOADING'; payload: boolean } | { type: 'SET_IS_LOADING'; payload: boolean }
| { type: 'SET_ON_DELETE_TRACK'; payload: { isConfirmDialogOpen: boolean, selectedTrack: { fileName: string, index: number } }} | { type: 'SET_ON_DELETE_TRACK'; payload: { isConfirmDialogOpen: boolean, selectedTrack: { fileName: string, index: number } }}
| { type: 'SET_TRACKS'; payload: string[] }; | { type: 'SET_TRACKS'; payload: { id: string; order: number; url: string }[] };
export const initialState: LogTracksState = { export const initialState: LogTracksState = {
isConfirmDialogOpen: false, isConfirmDialogOpen: false,

View File

@@ -1,7 +1,6 @@
export interface ILogbookEntry { export interface ILogbookEntry {
id: string; id: string;
pilotId: string; pilot: string;
pilotName: string;
date: string; date: string;
aircraftMakeModel: string; aircraftMakeModel: string;
aircraftIdentity: string; aircraftIdentity: string;
@@ -23,6 +22,6 @@ export interface ILogbookEntry {
night: number | null; night: number | null;
solo: number | null; solo: number | null;
pilotInCommand: number | null; pilotInCommand: number | null;
tracks: string | undefined; tracks: {id: string; order: number; url: string}[];
notes: string; notes: string;
} }

View File

@@ -53,7 +53,7 @@ const Logbook: React.FC<unknown> = () => {
accessorKey: 'tracks', accessorKey: 'tracks',
header: 'Tracks', header: 'Tracks',
cell: (info: any) => { cell: (info: any) => {
if (info.row.original.tracks && JSON.parse(info.row.original.tracks).length > 0) { if (info.row.original.tracks.length > 0) {
return ( return (
<IconButton onClick={() => onOpenCloseTracks(FormMode.VIEW, info.row.original.rowKey)}><Icon iconName={IconName.MAP_LOCATION_DOT} /></IconButton> <IconButton onClick={() => onOpenCloseTracks(FormMode.VIEW, info.row.original.rowKey)}><Icon iconName={IconName.MAP_LOCATION_DOT} /></IconButton>
) )
@@ -70,7 +70,7 @@ const Logbook: React.FC<unknown> = () => {
: {}; : {};
const response: AxiosResponse = await httpClient.get(`api/logs`, config); const response: AxiosResponse = await httpClient.get(`api/logs`, config);
const entries: ILogbookEntry[] = response.data; const entries: ILogbookEntry[] = response.data;
console.log(entries)
entries.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) entries.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
if (response.data.length > 0) { if (response.data.length > 0) {
@@ -299,7 +299,7 @@ const Logbook: React.FC<unknown> = () => {
isDrawerOpen={state.isTracksOpen} isDrawerOpen={state.isTracksOpen}
mode={state.tracksMode} mode={state.tracksMode}
onOpenClose={(mode) => onOpenCloseTracks(mode)} onOpenClose={(mode) => onOpenCloseTracks(mode)}
selectedRowKey={state.selectedLogId} selectedLogId={state.selectedLogId}
/> />
} }
</Box> </Box>

View File

@@ -19,14 +19,25 @@ const columnTotal = (info: HeaderContext<ILogbookEntry, unknown>): number => {
const pilotName: ColumnDef<ILogbookEntry> = { const pilotName: ColumnDef<ILogbookEntry> = {
id: 'pilotName', id: 'pilotName',
accessorKey: 'pilotName', accessorKey: 'pilot',
header: 'Pilot', header: 'Pilot',
footer: 'PAGE TOTALS' footer: 'PAGE TOTALS',
cell: (info: CellContext<ILogbookEntry, unknown>) => {
const pilot: any = info.getValue();
return pilot.name
}
} }
const date: ColumnDef<ILogbookEntry> = { const date: ColumnDef<ILogbookEntry> = {
id: 'date', id: 'date',
accessorKey: 'date', accessorKey: 'date',
header: 'Date' header: 'Date',
cell: (info: CellContext<ILogbookEntry, unknown>) => {
const date = new Date(info.getValue() as string);
const formattedDate = `${date.getMonth()}/${date.getDate()}/${date.getFullYear()}`
return formattedDate;
}
} }
const aircraftMakeModel: ColumnDef<ILogbookEntry> = { const aircraftMakeModel: ColumnDef<ILogbookEntry> = {
id: 'aircraftMakeModel', id: 'aircraftMakeModel',

View File

@@ -8,14 +8,15 @@ const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps
return ( return (
<Grid container spacing={2}> <Grid container spacing={2}>
{logs.map((log) => { {logs.map((log) => {
console.log(log) const date = new Date(log.date);
const formattedDate: string = `${date.getMonth()}/${date.getDate()}/${date.getFullYear()}`;
return ( return (
<Grid size={12}> <Grid size={12}>
<Card key={log.rowKey}> <Card key={log.id}>
<CardHeader <CardHeader
action={mode === 'logbook' ? <ActionMenu id={log.rowKey} onDelete={onDelete!} onOpenCloseForm={onOpenCloseForm!} /> : null} action={mode === 'logbook' ? <ActionMenu id={log.id} onDelete={onDelete!} onOpenCloseForm={onOpenCloseForm!} /> : null}
subheader={log.pilotName} title={formattedDate}
title={log.date}
slotProps={{ slotProps={{
subheader: { subheader: {
fontSize: '16px' fontSize: '16px'
@@ -27,11 +28,11 @@ const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps
/> />
<CardContent> <CardContent>
<Grid container spacing={1}> <Grid container spacing={1}>
{mode === 'flights' && log.tracks && JSON.parse(log.tracks).length > 0 && {mode === 'flights' && log.tracks && log.tracks.length > 0 &&
<Grid size={12}> <Grid size={12}>
<LogTrackMaps <LogTrackMaps
rowKey={log.rowKey} logId={log.id}
trackUrls={JSON.parse(log.tracks)} tracks={log.tracks}
/> />
</Grid> </Grid>
} }
@@ -59,13 +60,13 @@ const LogbookCard = ({ logs, mode, onDelete, onOpenCloseForm }: LogbookCardProps
<Grid size={12}> <Grid size={12}>
<Typography variant="body1">{log.durationOfFlight}</Typography> <Typography variant="body1">{log.durationOfFlight}</Typography>
</Grid> </Grid>
{mode === 'logbook' && log.tracks && JSON.parse(log.tracks).length > 0 && {mode === 'logbook' && log.tracks && log.tracks.length > 0 &&
<> <>
<Grid size={12}> <Grid size={12}>
<Typography variant="subtitle2">Tracks</Typography> <Typography variant="subtitle2">Tracks</Typography>
</Grid> </Grid>
{JSON.parse(log.tracks).map((track: string) => { {log.tracks.map((track: { id: string; order: number; url: string}) => {
const trackSplit = track.split('/') const trackSplit = track.url.split('/')
const filename = trackSplit[trackSplit.length - 1]; const filename = trackSplit[trackSplit.length - 1];
return ( return (