adding logbook entry edit #29

Merged
noahspannbauer merged 1 commits from feature/25-logbook-edit-entry into main 2024-11-18 21:30:46 -05:00
6 changed files with 186 additions and 85 deletions

View File

@@ -4,7 +4,8 @@ import {
Get,
HttpException,
Param,
Post
Post,
Put
} from '@nestjs/common';
import { LogbookService } from './logbook.service';
import { LogbookDto } from './logbook.dto';
@@ -18,7 +19,6 @@ export class LogbookController {
@Get(':entryId')
async find(@Param() params: any): Promise<LogbookEntity> {
console.log(params);
try {
const entry: LogbookEntity = await this.logbookService.find(
params.entryId
@@ -43,7 +43,7 @@ export class LogbookController {
return logbookEntries;
} catch (error) {
const customError = error as CustomError;
console.log(error);
throw new HttpException(customError.message, customError.statusCode, {
cause: customError.name
});
@@ -63,4 +63,20 @@ export class LogbookController {
});
}
}
@Put(':entryId')
async update(
@Param() params: any,
@Body() logbookData: LogbookDto
): Promise<void> {
try {
return await this.logbookService.update(logbookData);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode, {
cause: customError.name
});
}
}
}

View File

@@ -8,16 +8,18 @@ import { v4 as uuidv4 } from 'uuid';
@Injectable()
export class LogbookService {
private readonly tableName = 'Logbook';
constructor(private readonly tableService: TableService) {}
async getLogbookEntries(filter: string): Promise<LogbookEntity[]> {
try {
const client: TableClient =
await this.tableService.getTableClient('Logbook');
const client: TableClient = await this.tableService.getTableClient(
this.tableName
);
const entities = await client.listEntities({
queryOptions: { filter: filter }
});
console.log(entities);
const logbookEntries: LogbookEntity[] = [];
for await (const entity of entities) {
@@ -73,7 +75,7 @@ export class LogbookService {
: null,
notes: entity.notes.toString()
};
console.log(logbookEntry);
logbookEntries.push(logbookEntry);
}
@@ -118,8 +120,9 @@ export class LogbookService {
}
async create(logbookData: LogbookDto): Promise<TableInsertEntityHeaders> {
const client: TableClient =
await this.tableService.getTableClient('Logbook');
const client: TableClient = await this.tableService.getTableClient(
this.tableName
);
const logbook: LogbookEntity = new LogbookEntity();
Object.assign(logbook, logbookData);
@@ -138,4 +141,25 @@ export class LogbookService {
);
}
}
async update(logbookData: LogbookDto): Promise<void> {
const client: TableClient = await this.tableService.getTableClient(
this.tableName
);
const logbook: LogbookEntity = new LogbookEntity();
Object.assign(logbook, logbookData);
try {
await client.upsertEntity(logbook, 'Replace');
} catch (error) {
const restError: RestError = error as RestError;
throw new CustomError(
restError.details['odataError']['message']['value'],
restError.details['odataError']['code'],
restError.statusCode
);
}
}
}

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { IActionMenuProps } from './IActionMenuProps';
import {
EllipsisVerticalIcon,
@@ -26,10 +26,6 @@ const ActionMenu = ({ id, onOpenCloseForm }: IActionMenuProps) => {
setAnchorElAction(null);
};
useEffect(() => {
console.log(id);
}, [id]);
return (
<div>
<IconButton onClick={onOpenActionMenu}>

View File

@@ -0,0 +1,6 @@
export interface ILogbookEntryFormState {
isDisabled: boolean;
isLoading: boolean;
pilotOptions: { label: string; value: string }[];
selectedEntryPilotName: string;
}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useReducer, useState } from 'react';
import {
Accordion,
AccordionDetails,
@@ -15,13 +15,9 @@ import {
Typography,
XmarkIcon
} from '@noahspan/noahspan-components';
import {
useForm,
Controller,
SubmitHandler,
FormProvider
} from 'react-hook-form';
import { useForm, Controller, FormProvider } from 'react-hook-form';
import { ILogbookEntryFormProps } from './ILogbookEntryFormProps';
import { initialState, reducer } from './reducer';
import axios, { AxiosInstance, AxiosResponse } from 'axios';
import { useHttpClient } from '../../hooks/httpClient/UseHttpClient';
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
@@ -35,13 +31,8 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
mode,
onOpenClose
}) => {
const [state, dispatch] = useReducer(reducer, initialState);
const httpClient: AxiosInstance = useHttpClient();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [selectedEntry, setSelectedEntry] = useState();
const [isDisabled, setIsDisabled] = useState<boolean>(false);
const [pilotOptions, setPilotOptions] = useState<
{ label: string; value: string }[]
>([]);
const { getAccessToken } = useAccessToken();
const isAuthenticated = useIsAuthenticated();
const defaultValues = {
@@ -77,45 +68,55 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
const onCancel = () => {
methods.reset(defaultValues);
dispatch({ type: 'SET_IS_DISABLED', payload: false });
onOpenClose(FormMode.CANCEL);
};
const onSubmit = async (data: unknown) => {
try {
setIsLoading(true);
dispatch({ type: 'SET_IS_LOADING', payload: true });
const accessToken: string = await getAccessToken();
await httpClient.post(`api/logbook`, data, {
headers: {
Authorization: accessToken
}
});
if (!entryId) {
await httpClient.post(`api/logbook`, data, {
headers: {
Authorization: accessToken
}
});
} else {
await httpClient.put(`api/logbook/${entryId}`, data, {
headers: {
Authorization: accessToken
}
});
}
methods.reset(defaultValues);
dispatch({ type: 'SET_IS_DISABLED', payload: false });
onOpenClose(FormMode.CANCEL);
} catch (error) {
if (axios.isAxiosError(error)) {
const errResp = error.response;
console.log(errResp?.data.message);
console.log(errResp);
} else {
}
} finally {
setIsLoading(false);
dispatch({ type: 'SET_IS_LOADING', payload: false });
}
};
useEffect(() => {
if (mode === FormMode.VIEW) {
setIsDisabled(true);
dispatch({ type: 'SET_IS_DISABLED', payload: true });
}
}, [mode]);
useEffect(() => {
const getEntry = async () => {
try {
setIsLoading(true);
dispatch({ type: 'SET_IS_LOADING', payload: true });
const config = isAuthenticated
? { headers: { Authorization: await getAccessToken() } }
@@ -130,7 +131,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
dispatch({ type: 'SET_IS_LOADING', payload: false });
}
};
@@ -148,7 +149,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
};
});
setPilotOptions(newPilotsOptions);
dispatch({ type: 'SET_PILOT_OPTIONS', payload: newPilotsOptions });
}
}, [pilots]);
@@ -182,28 +183,40 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
name="pilotId"
control={methods.control}
render={({ field: { onChange, value } }) => {
useEffect(() => {
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 (
<Select
disabled={isDisabled}
fullWidth
onChange={(event) => {
const pilot = pilots?.find(
(pilot) => pilot.id === event.target.value
);
if (pilot) {
methods.setValue('pilotName', pilot.name);
}
methods.setValue('pilotId', event.target.value);
}}
options={
pilotOptions && pilotOptions.length > 0
? pilotOptions
: []
}
value={value}
/>
<>
{mode === FormMode.ADD && (
<Select
disabled={state.isDisabled}
fullWidth
onChange={onChange}
options={
state.pilotOptions && state.pilotOptions.length > 0
? state.pilotOptions
: []
}
value={value}
/>
)}
{mode !== FormMode.ADD && (
<TextField
disabled={true}
fullWidth
value={state.selectedEntryPilotName}
/>
)}
</>
);
}}
/>
@@ -217,7 +230,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<DatePicker
disabled={isDisabled}
disabled={state.isDisabled}
onChange={onChange}
value={value}
/>
@@ -233,7 +246,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -256,7 +269,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -279,7 +292,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -302,7 +315,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -325,7 +338,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -354,7 +367,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -383,7 +396,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -419,7 +432,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -450,7 +463,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -492,7 +505,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -523,7 +536,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -556,7 +569,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -587,7 +600,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -618,7 +631,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -662,7 +675,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -695,7 +708,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -726,7 +739,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -757,7 +770,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -788,7 +801,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -819,7 +832,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={
methods.formState.errors.address ? true : false
}
@@ -854,7 +867,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
control={methods.control}
render={({ field: { onChange, value } }) => (
<TextField
disabled={isDisabled}
disabled={state.isDisabled}
error={methods.formState.errors.address ? true : false}
fullWidth
helperText={
@@ -873,8 +886,8 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
<Grid display="flex" gap={2} justifyContent="right" size={12}>
<Button
disabled={
isDisabled && mode.toString() !== FormMode.VIEW
? isDisabled
state.isDisabled && mode.toString() !== FormMode.VIEW
? state.isDisabled
: false
}
startIcon={<XmarkIcon />}
@@ -886,7 +899,7 @@ const LogbookEntryForm: React.FC<ILogbookEntryFormProps> = ({
</Button>
{mode.toString() !== FormMode.VIEW && (
<Button
disabled={isDisabled}
disabled={state.isDisabled}
startIcon={<SaveIcon />}
size="small"
type="submit"

View File

@@ -0,0 +1,46 @@
import { ILogbookEntryFormState } from './ILogbookEntryFormState';
type Action =
| { type: 'SET_IS_DISABLED'; payload: boolean }
| { type: 'SET_IS_LOADING'; payload: boolean }
| { type: 'SET_PILOT_OPTIONS'; payload: { label: string; value: string }[] }
| { type: 'SET_SELECTED_ENTRY_PILOT_NAME'; payload: string };
export const initialState: ILogbookEntryFormState = {
isDisabled: false,
isLoading: true,
pilotOptions: [],
selectedEntryPilotName: ''
};
export const reducer = (
state: ILogbookEntryFormState,
action: Action
): ILogbookEntryFormState => {
switch (action.type) {
case 'SET_IS_DISABLED': {
return {
...state,
isDisabled: action.payload
};
}
case 'SET_IS_LOADING': {
return {
...state,
isLoading: action.payload
};
}
case 'SET_PILOT_OPTIONS': {
return {
...state,
pilotOptions: action.payload
};
}
case 'SET_SELECTED_ENTRY_PILOT_NAME': {
return {
...state,
selectedEntryPilotName: action.payload
};
}
}
};