96 switch from azure table storage to sqlite (#97)

* adding typeorm to api

* switching to sqlite

* switching to sqlite

* switching to sqlite

* migrating to sqlite

* updating terraform

* updating infrastructure
This commit was merged in pull request #97.
This commit is contained in:
2025-11-23 10:42:31 -06:00
committed by GitHub
parent f94d0f7ca9
commit f98a2ab127
208 changed files with 27135 additions and 16875 deletions

View File

@@ -0,0 +1,241 @@
import { useEffect, useReducer, useRef } from "react";
import { AxiosError, AxiosResponse } from "axios";
import { TracksFormProps } from "./TracksFormProps.interface";
import { FormMode } from "../../enums/formMode";
import { LogbookEntry } from "../logbook/LogbookEntry.interface";
import ConfirmationDialog from "../confirmationDialog/ConfirmationDialog";
import { initialState, reducer } from "./reducer";
import TrackMap from "../trackMap/TrackMap";
import httpClient from "../../httpClient/httpClient";
import { Button, Input, Spinner } from '@heroui/react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUpload, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
import { useLogbookContext } from "../../hooks/logbookContext/UseLogbookContext";
const TracksForm = () => {
const [state, dispatch] = useReducer(reducer, initialState)
const logbookContext = useLogbookContext();
const getTracks = async () => {
try {
const response: AxiosResponse = await httpClient.get(
`api/tracks/${logbookContext.state.selectedLogId}`
)
const tracks = response.data;
dispatch({ type: 'SET_TRACKS', payload: tracks })
} catch (error) {
const axiosError = error as AxiosError;
logbookContext.dispatch({ type: 'SET_FORM_ALERT', payload: { severity: 'danger', message: axiosError.message }})
}
}
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
try {
dispatch({ type: 'SET_IS_LOADING', payload: true})
const file = event.target.files![0]
const formData = new FormData();
const order = state.tracks.length + 1
formData.append('file', file);
httpClient.interceptors.request.use((config) => {
config.headers["Content-Type"] = 'multipart/form-data'
return config
});
await httpClient.post(`api/tracks/${logbookContext.state.selectedLogId}/${order}`, formData);
await getTracks();
// const uploadUrl = uploadResponse.data.url;
// const tracks: string[] = log.tracks ? JSON.parse(log.tracks!) : [];
// tracks.push(uploadUrl)
// log.tracks = JSON.stringify(tracks);
// await httpClient.put(`api/logs/log/${logbookContext.state.selectedLogId}`, log, config);
// const updatedLog = await getLog();
// dispatch({ type: 'SET_TRACKS', payload: JSON.parse(updatedLog.tracks!) })
} catch (error) {
console.log(error)
} finally {
dispatch({ type: 'SET_IS_LOADING', payload: false });
}
}
const onDeleteTrack = async (id: string, filename: string, index: number) => {
dispatch({ type: 'SET_ON_DELETE_TRACK', payload: { isConfirmDialogOpen: true, selectedTrack: { id: id, filename: filename, index: index }}})
}
const onConfirmDialogConfirm = async () => {
try {
await httpClient.delete(`api/tracks/${state.selectedTrack!.id}/${state.selectedTrack!.filename}/${logbookContext.state.selectedLogId}`);
await getTracks();
// tracks.splice(state.selectedTrack!.index, 1);
// log.tracks = JSON.stringify(tracks);
// await httpClient.put(`api/logs/log/${selectedRowKey}`, log, config);
// const updatedLog = await getLog();
// dispatch({ type: 'SET_TRACKS', payload: JSON.parse(updatedLog.tracks!) })
dispatch({ type: 'SET_IS_CONFIRM_DIALOG_OPEN', payload: false })
} catch (error) {
console.log(error);
}
}
const onConfirmDialogCancel = async () => {
dispatch({ type: 'SET_IS_CONFIRM_DIALOG_OPEN', payload: false })
}
useEffect(() => {
if (logbookContext.state.selectedLogId) {
getTracks();
}
}, [logbookContext.state.selectedLogId])
useEffect(() => {
console.log(logbookContext.state.formMode)
if (logbookContext.state.formMode === FormMode.VIEW) {
dispatch({ type: 'SET_IS_DISABLED', payload: true });
}
}, [logbookContext.state.formMode]);
useEffect(() => {
console.log(state.isDisabled)
}, [state.isDisabled])
return (
<div className='grid grid-cols-12 gap-3'>
{state.tracks.length > 0 &&
<div className="col-span-12">
<TrackMap height='400px' logId={logbookContext.state.selectedLogId!} tracks={state.tracks} />
</div>
}
<>
{state.tracks.length > 0 && state.tracks.map((track, index) => {
const filename: string = track.url.substring(track.url.lastIndexOf('/') + 1);
return (
<>
<div className='col-span-10'>
<Input isDisabled={state.isDisabled} key={index} type='text' value={filename}/>
</div>
<div className='col-span-2'>
<Button isDisabled={state.isDisabled} key={index} isIconOnly onPress={() => onDeleteTrack(track.id, filename, index)}><FontAwesomeIcon icon={faTrash} /></Button>
</div>
</>
)
})}
<div className='col-span-12'>
<Button
as='label'
color='primary'
isDisabled={state.isDisabled}
fullWidth={true}
startContent={<FontAwesomeIcon icon={faUpload} />}
>
Upload Track
<input hidden onChange={handleFileUpload} type='file' />
</Button>
</div>
{state.isConfirmDialogOpen && (
<ConfirmationDialog
contentText="Are you sure you want to delete this track?"
isLoading={state.isConfirmDialogLoading}
isOpen={state.isConfirmDialogOpen}
onCancel={onConfirmDialogCancel}
onConfirm={onConfirmDialogConfirm}
title="Confirm Delete"
/>
)}
</>
</div>
);
}
// const getConfig = async () => {
// const config = auth.isAuthenticated
// ? { headers: { Authorization: auth.user?.access_token } }
// : {};
// return config
// }
// const getLog = async (): Promise<ILogbookEntry> => {
// const logResponse: AxiosResponse = await httpClient.get(
// `api/tracks/${selectedLogId}`,
// await getConfig()
// );
// const logData: ILogbookEntry = logResponse.data;
// return logData
// }
// const onCancel = () => {
// onOpenClose(FormMode.CANCEL)
// }
// const onDeleteTrack = async (fileName: string, index: number) => {
// dispatch({ type: 'SET_ON_DELETE_TRACK', payload: { isConfirmDialogOpen: true, selectedTrack: { fileName: fileName, index: index }}})
// }
// useEffect(() => {
// const updateTracks = async () => {
// const log = await getLog();
// dispatch({ type: 'SET_TRACKS', payload: log.tracks! });
// }
// updateTracks();
// }, [])
// return (
// <div className='grid grid-cols-12 gap-3'>
// {}
// {logbookContext.state.formMode === FormMode.EDIT &&
// <>
// {state.isLoading &&
// <>
// <div>
// <Spinner size='lg' />
// </div>
// <div>
// Loading...
// </div>
// </>
// }
// {!state.isLoading && state.tracks.length > 0 && state.tracks.map((track, index) => {
// const trackSplit = track.url.split('/')
// const filename = trackSplit[trackSplit.length - 1];
// return (
// <>
// <div>
// <Input disabled={true} value={filename} />
// </div>
// <div>
// <Button isIconOnly onPress={() => onDeleteTrack(filename, index)}><FontAwesomeIcon icon={faTrash} /></Button>
// </div>
// </>
// )
// })}
// </>
// }
// </div>
// )
// }
export default TracksForm;

View File

@@ -0,0 +1,8 @@
import { FormMode } from "../../enums/formMode";
export interface TracksFormProps {
isDrawerOpen: boolean;
mode: FormMode;
onOpenClose: (mode: FormMode) => void;
selectedLogId: string | undefined;
}

View File

@@ -0,0 +1,12 @@
export interface TracksFormState {
isConfirmDialogOpen: boolean;
isConfirmDialogLoading: boolean;
isDisabled: boolean;
isLoading: boolean;
selectedTrack: {
id: string,
filename: string,
index: number
} | undefined;
tracks: { id: string; order: number; url: string; }[];
}

View File

@@ -0,0 +1,63 @@
import { TracksFormState } from "./TracksFormState.interface";
type Action =
| { type: 'SET_IS_CONFIRM_DIALOG_OPEN'; payload: boolean }
| { type: 'SET_IS_CONFORM_DIALOG_LOADING'; payload: boolean }
| { type: 'SET_IS_DISABLED'; payload: boolean }
| { type: 'SET_IS_LOADING'; payload: boolean }
| { type: 'SET_ON_DELETE_TRACK'; payload: { isConfirmDialogOpen: boolean, selectedTrack: { id: string, filename: string, index: number } }}
| { type: 'SET_TRACKS'; payload: { id: string; order: number; url: string }[] };
export const initialState: TracksFormState = {
isConfirmDialogOpen: false,
isConfirmDialogLoading: false,
isDisabled: false,
isLoading: false,
selectedTrack: undefined,
tracks: []
}
export const reducer = (state: TracksFormState, action: Action): TracksFormState => {
switch (action.type) {
case 'SET_IS_CONFIRM_DIALOG_OPEN': {
return {
...state,
isConfirmDialogOpen: action.payload
}
}
case 'SET_IS_CONFORM_DIALOG_LOADING': {
return {
...state,
isConfirmDialogLoading: action.payload
}
}
case 'SET_IS_DISABLED': {
return {
...state,
isDisabled: action.payload
}
}
case 'SET_IS_LOADING': {
return {
...state,
isLoading: action.payload
}
}
case 'SET_ON_DELETE_TRACK': {
return {
...state,
isConfirmDialogOpen: action.payload.isConfirmDialogOpen,
selectedTrack: action.payload.selectedTrack
}
}
case 'SET_TRACKS': {
return {
...state,
tracks: action.payload
}
}
default: {
return state
}
}
}