Skip to content

Commit

Permalink
feat(frontend): add sample dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
berdal84 committed Jan 19, 2024
1 parent 64adf92 commit 24e55f0
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 10 deletions.
90 changes: 90 additions & 0 deletions frontend/src/app/components/SampleDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import * as React from 'react';
import TextField from '@mui/material/TextField';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';

import Button from '@/app/components/Button';
import { Sample, SampleCreate } from '@/app/types';

type SampleDialogProps = {
title: string
open: boolean;
setOpen: (open: boolean) => void;
/** A promise returning a new Sample or null if create failed */
submit: (sample: SampleCreate) => Promise<Sample | null>;
}
export default function SampleDialog({ title, open, setOpen, submit }: SampleDialogProps) {

const handleClose = () => {
setOpen(false);
};

return (
<Dialog
open={open}
onClose={handleClose}
PaperProps={{
component: 'form',
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

// Convert formdata to json
const formData = new FormData(event.currentTarget);
const formJson = Object.fromEntries((formData as any).entries());

// TODO:
// We need a validation system with a visual feedback here

submit(formJson as SampleCreate)
.then(() => handleClose())
.catch(() => { /** TODO: show an error */ })

},
}}
>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>
<p>Please provide information about your sample and press create.</p>
<p className="text-xs italic">Note: you'll be able to attach a file to your sample once the sample is created.</p>
</DialogContentText>
<TextField
autoFocus
required
margin="dense"
id="name"
name="name"
label="Sample Name"
fullWidth
variant="standard"
/>
<TextField
required
margin="dense"
id="type"
name="type"
label="Experiment Type"
fullWidth
variant="standard"
/>
<TextField
required
margin="dense"
id="date"
name="date"
label="Date Collected"
fullWidth
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button type="submit">Create</Button>
</DialogActions>
</Dialog>

);
}
22 changes: 18 additions & 4 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import { Box } from "@mui/material"
import Button from "@/app/components/Button"
import Table from "@/app/components/Table"
import { useAppContext } from "@/app/contexts/AppContext";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useAPI } from "@/app/utilities/useApi";
import SampleEditor from "./components/SampleEditor";
import { Sample } from "@/app/types";
import { Sample, SampleCreate } from "@/app/types";
import { useQueryState } from "nuqs";
import SampleDialog from "./components/SampleDialog";

export default function Home() {

// Read url parameter "sample-id" to determine which sample to fetch
const [sampleId, setSampleId] = useQueryState('sample-id')

const { page, status, statusMessage, sample } = useAppContext()
const { fetchPage, fetchSample } = useAPI()
const { fetchPage, fetchSample, createSample } = useAPI()

const [dialogOpen, setDialogOpen] = useState(false);

/** Fetch sample when sampleId (url param) changes */
useEffect(() => {
Expand Down Expand Up @@ -44,7 +47,7 @@ export default function Home() {
}

const handleCreateSample = () => {
alert("Function not implemented.");
setDialogOpen(true)
}

function handleSampleChange(newValues: Sample): void {
Expand All @@ -63,6 +66,10 @@ export default function Home() {
setSampleId(null)
}

function handleCreate(sample: SampleCreate): Promise<Sample | null> {
return createSample(sample)
}

return (
<Box className="flex flex-col gap-5">

Expand Down Expand Up @@ -106,6 +113,13 @@ export default function Home() {
</Box>
<p hidden={status !== "loading"} className="text-grey-500">Loading...</p>
<p hidden={status !== "error"} className="text-red-500" title={statusMessage} >Error: see console</p>

<SampleDialog
open={dialogOpen}
setOpen={setDialogOpen}
title="Create a new Sample"
submit={handleCreate}
/>
</Box>
)
}
26 changes: 20 additions & 6 deletions frontend/src/app/utilities/useApi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios";
import { useCallback } from "react";
import { useAppContext, useAppDispatchContext } from "../contexts/AppContext";
import { Page, Sample } from "@/app/types";
import { Page, Sample, SampleCreate } from "@/app/types";

/**
* Create a new AXIOS instance to query the API
Expand All @@ -23,7 +23,7 @@ export function useAPI() {
/**
* Callback to trigger the fetch of a given page
*/
const fetchPage = useCallback(async (
const getPage = useCallback(async (
index: number = state.page.index,
limit: number = state.page.limit
) => {
Expand All @@ -48,6 +48,8 @@ export function useAPI() {

dispatch({ type: 'setPage', payload: { page: response.data } })

return response.data;

}).catch((reason: any) => {
dispatch({ type: 'setStatus', payload: { status: "error", message: JSON.stringify(reason) } })
});
Expand All @@ -56,7 +58,7 @@ export function useAPI() {
/**
* Callback to trigger the fetch of a given sample from a given id.
*/
const fetchSample = useCallback((id: number | null) => {
const getSample = useCallback((id: number | null) => {

if (id === null) {
return dispatch({ type: 'setSample', payload: { sample: null } })
Expand All @@ -67,15 +69,27 @@ export function useAPI() {
return api.get<Sample>(`/${id}`)
.then((response) => {
dispatch({ type: 'setSample', payload: { sample: response.data } })

return response.data;
}).catch((reason: any) => {
dispatch({ type: 'setStatus', payload: { status: "error", message: JSON.stringify(reason) } })
});
}, [])


const createSample = (sample: SampleCreate) => {
return api.post<Sample>("/", sample)
.then((response) => {
return response.data;
}).catch((reason: any) => {
dispatch({ type: 'setStatus', payload: { status: "error", message: JSON.stringify(reason) } })
return null;
});
}

return {
fetchPage,
fetchSample
fetchPage: getPage,
fetchSample: getSample,
createSample
}
}

0 comments on commit 24e55f0

Please sign in to comment.