Skip to content

Commit

Permalink
feature: add backend error message in the UI (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
MDeLuise authored Sep 5, 2023
1 parent 9225d17 commit e7cd0fc
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@ public ResponseEntity<PlantDTO> save(@RequestBody PlantDTO plantDTO) {
public ResponseEntity<Long> countDistinctBotanicalInfo() {
return ResponseEntity.ok(plantService.getNumberOfDistinctBotanicalInfo());
}


@GetMapping("/{plantName}/_name-exists")
public ResponseEntity<Boolean> isNameAlreadyExisting(@PathVariable String plantName) {
return ResponseEntity.ok(plantService.isNameAlreadyExisting(plantName));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface PlantRepository extends JpaRepository<Plant, Long> {
Page<Plant> findAllByOwnerAndState(User user, PlantState plantState, Pageable pageable);

Long countByOwner(User user);

boolean existsByOwnerAndPersonalName(User user, String personalName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,9 @@ public long getNumberOfDistinctBotanicalInfo() {
.map(entity -> entity.getBotanicalInfo().getId())
.collect(Collectors.toSet()).size();
}


public boolean isNameAlreadyExisting(String plantName) {
return plantRepository.existsByOwnerAndPersonalName(authenticatedUserService.getAuthenticatedUser(), plantName);
}
}
105 changes: 93 additions & 12 deletions frontend/src/components/AddLogEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ import {
InputLabel,
TextField,
Typography,
useTheme,
} from "@mui/material";
import { AxiosInstance } from "axios";
import { AxiosError, AxiosInstance } from "axios";
import React, { useState } from "react";
import { diaryEntry, plant } from "../interfaces";
import { GrClose } from "react-icons/gr";
import { titleCase } from "../common";
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import dayjs, { Dayjs } from 'dayjs';
import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
import { alpha } from "@mui/material";
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CheckBoxIcon from '@mui/icons-material/CheckBox';
import ErrorDialog from "./ErrorDialog";

export default function AddLogEntry(props: {
requestor: AxiosInstance,
Expand All @@ -32,10 +33,35 @@ export default function AddLogEntry(props: {
const [date, setDate] = useState<Dayjs | null>(dayjs(new Date()));
const [selectedPlantName, setSelectedPlantName] = useState<string[]>([]);
const [selectedEventType, setSelectedEventType] = useState<string[]>([]);
const [plantNameError, setPlantNameError] = useState<string>();
const [eventTypeError, setEventTypeError] = useState<string>();
const [note, setNote] = useState<string>();
const [loading, setLoading] = useState<boolean>(false);
const theme = useTheme();
const [errorDialogShown, setErrorDialogShown] = useState<boolean>(false);
const [errorDialogText, setErrorDialogText] = useState<string>();


const showErrorDialog = (res: AxiosError) => {
setErrorDialogText((res.response?.data as any).message);
setErrorDialogShown(true);
};

const addEvent = (): void => {
if (plantNameError !== undefined) {
return;
}
if (eventTypeError !== undefined) {
return;
}
if (selectedPlantName.length == 0) {
setPlantNameError("At least 1 plant must be selected");
return;
}
if (selectedEventType.length == 0) {
setEventTypeError("At least 1 event must be selected");
return;
}
setLoading(true);
selectedPlantName.forEach((plantId) => {
selectedEventType.forEach((eventType) => {
Expand All @@ -48,25 +74,64 @@ export default function AddLogEntry(props: {
.then((res) => {
res.data.diaryTargetPersonalName = plantId; // TODO should not be necessary but backend problem
props.updateLog(res.data);
props.setOpen(false);
closeDrawer();
})
.catch((err) => {
showErrorDialog(err);
})
.finally(() => setLoading(false));
});
});
};


const changePlantName = (value: string[]): void => {
if (value.length == 0) {
setPlantNameError("At least 1 plant must be selected");
} else {
setPlantNameError(undefined);
}
setSelectedPlantName(value);
};


const changeEventType = (value: string[]): void => {
if (value.length == 0) {
setEventTypeError("At least 1 event must be selected");
} else {
setEventTypeError(undefined);
}
setSelectedEventType(value);
};


const closeDrawer = (): void => {
setSelectedPlantName([]);
setSelectedEventType([]);
setNote(undefined);
setEventTypeError(undefined);
setPlantNameError(undefined);
props.setOpen(false);
};

return (
<Drawer
anchor={"bottom"}
open={props.open}
onClose={() => props.setOpen(false)}
onClose={closeDrawer}
PaperProps={{
style: { borderRadius: "15px 15px 0 0" }
}}
>

<ErrorDialog
text={errorDialogText}
open={errorDialogShown}
close={() => setErrorDialogShown(false)}
/>

<GrClose
onClick={() => props.setOpen(false)}
onClick={closeDrawer}
style={{
position: "absolute",
top: "20px",
Expand All @@ -90,7 +155,7 @@ export default function AddLogEntry(props: {
multiple
options={props.plants.map(pl => pl.personalName)}
value={selectedPlantName}
onChange={(_event: any, newValue: string[]) => setSelectedPlantName(newValue)}
onChange={(_event: any, newValue: string[]) => changePlantName(newValue)}
fullWidth
renderTags={(selected) => {
let renderedValues = selected.join(", ");
Expand Down Expand Up @@ -119,7 +184,14 @@ export default function AddLogEntry(props: {
{option}
</li>
)}
renderInput={(params) => <TextField {...params} fullWidth />}
renderInput={(params) =>
<TextField
{...params}
fullWidth
error={plantNameError !== undefined}
helperText={plantNameError}
/>
}
/>
</Box>

Expand All @@ -131,7 +203,7 @@ export default function AddLogEntry(props: {
multiple
options={props.eventTypes}
value={selectedEventType}
onChange={(_event: any, newValue: string[]) => setSelectedEventType(newValue)}
onChange={(_event: any, newValue: string[]) => changeEventType(newValue)}
renderTags={(selected) => {
let renderedValues = selected.map(ev => titleCase(ev)).join(", ");
return (
Expand Down Expand Up @@ -159,7 +231,14 @@ export default function AddLogEntry(props: {
{titleCase(option)}
</li>
)}
renderInput={(params) => <TextField {...params} fullWidth />}
renderInput={(params) =>
<TextField
{...params}
fullWidth
error={eventTypeError !== undefined}
helperText={eventTypeError}
/>
}
/>
</Box>

Expand Down Expand Up @@ -187,17 +266,19 @@ export default function AddLogEntry(props: {
</Box>

<Button sx={{
backgroundColor: loading ? alpha("#3a5e49", .7) : "primary.main",
backgroundColor: theme.palette.primary.main + " !important",
color: "white",
width: "90%",
margin: "0 auto",
marginBottom: "20px",
padding: "15px",
}}
disabled={loading}
onClick={addEvent}
startIcon={<SaveOutlinedIcon />}
>Save event</Button>
onClick={addEvent}
>
Save event
</Button>
</Drawer>
);
}
Loading

0 comments on commit e7cd0fc

Please sign in to comment.