Skip to content

Commit

Permalink
adding redux thunk for incident form, modifying incident form to matc…
Browse files Browse the repository at this point in the history
…h post request for incident, modifying query parameters for incident data
  • Loading branch information
natapokie committed Feb 14, 2024
1 parent 4585976 commit 2fed5ee
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ export const CheckedOutTables = () =>
<TableCell align="right">
<Link
to={`/incident-form?data=${JSON.stringify(
row
{
...row,
checkedOutOrderId:
checkedOutOrder.id,
}
)}`}
>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import Header from "components/general/Header/Header";
import ArrowLeft from "../../assets/images/icons/arrow-left-solid.svg";
import { displaySnackbar } from "slices/ui/uiSlice";
import { useDispatch } from "react-redux";
import { OrderItemTableRow } from "api/types";
import { IncidentRequestBody, createIncident } from "slices/incident/incidentSlice";

export const INCIDENT_ERROR_MSG = {
state: "Please select an option",
Expand Down Expand Up @@ -95,6 +97,11 @@ const IncidentForm = () => {
// get info from url
const searchParams = new URLSearchParams(location.search);

let checkedOutOrder:
| (OrderItemTableRow & { checkedOutOrderId: number })
| undefined = undefined;
let hardwareQuantity: number; // quantity used for dropdown

useFirstRenderEffect(() => {
if (searchParams.toString() === "") {
// check if there are empty query params
Expand All @@ -110,47 +117,52 @@ const IncidentForm = () => {
}
});

let hardwareQuantity: number; // quantity used for dropdown
try {
const data = searchParams.get("data") ?? "";
const checkedOutOrder = JSON.parse(data); // parse string from url into an object
checkedOutOrder = JSON.parse(data) as OrderItemTableRow & {
checkedOutOrderId: number;
}; // parse string from url into an object
hardwareQuantity = checkedOutOrder?.quantityGranted; // set the hardware qty for dropdown
console.log(checkedOutOrder, hardwareQuantity);
} catch {
hardwareQuantity = 0; // set the qty to 0 if there is an error parsing
}

return (
<>
{searchParams.toString() === "" ? (
<></>
) : (
<IncidentFormRender hardwareQuantity={hardwareQuantity} />
)}
<IncidentFormRender
checkedOutOrder={checkedOutOrder}
hardwareQuantity={hardwareQuantity}
/>
</>
);
};

const IncidentFormRender = ({ hardwareQuantity }: { hardwareQuantity: number }) => {
const IncidentFormRender = ({
hardwareQuantity,
checkedOutOrder,
}: {
hardwareQuantity: number;
checkedOutOrder: (OrderItemTableRow & { checkedOutOrderId: number }) | undefined;
}) => {
const muiClasses = useStyles();

const dispatch = useDispatch();
const history = useHistory();

const handleSubmit = async (values: FormikValues, { resetForm }: any) => {
// TODO: submit the form
const incident: IncidentRequestBody = {
id: checkedOutOrder?.checkedOutOrderId, // to check... id is order #
state: values.state,
time_occurred: new Date(values.when).toISOString(),
description: `${values.what}\n${values.where}`,
order_item: checkedOutOrder?.id ?? 0, // order item is id?
team_id: 0,
created_at: new Date().toISOString(),
updated_at: "",
};

resetForm();
// display success snackbar
dispatch(
displaySnackbar({
message: `Successfully submitted incident form!`,
options: {
variant: "success",
},
})
);
// navigate back to previous page
history.goBack();
dispatch(createIncident(incident));
};

return (
Expand Down Expand Up @@ -183,7 +195,14 @@ const IncidentFormRender = ({ hardwareQuantity }: { hardwareQuantity: number })
type: "radio",
id: "state",
name: "state",
options: ["broken", "lost", "other"],
options: [
"Heavily Used",
"Broken",
"Missing",
"Minor Repair Required",
"Major Repair Required",
"Not Sure If Works",
],
helperText: errors?.state,
testId: "radio-state",
},
Expand Down Expand Up @@ -212,11 +231,11 @@ const IncidentFormRender = ({ hardwareQuantity }: { hardwareQuantity: number })
helperText: errors?.what,
},
{
type: "text",
type: "time",
id: "when",
name: "when",
label: "",
description: "",
description: "When did this occur?",
placeholder: "When did this occur?",
value: values.when,
error: !!errors?.when,
Expand Down Expand Up @@ -421,11 +440,41 @@ const IncidentFormRender = ({ hardwareQuantity }: { hardwareQuantity: number })
handleChange
}
variant="outlined"
type="text"
type={
item.name ===
"when"
? "time"
: "text"
}
multiline
fullWidth
/>
</div>
) : item.type ===
"time" ? (
<>
<TextField
id={item.id}
name={
item.name
}
type="datetime-local"
value={
item.value
}
onChange={
handleChange
}
error={
item.error
}
helperText={
item.helperText
}
variant="outlined"
fullWidth
/>
</>
) : (
<></>
)}
Expand Down
136 changes: 136 additions & 0 deletions hackathon_site/dashboard/frontend/src/slices/incident/incidentSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import {
createAsyncThunk,
createEntityAdapter,
createSelector,
createSlice,
} from "@reduxjs/toolkit";
import { post } from "api/api";
import { Incident } from "api/types";
import { push } from "connected-react-router";
import { AppDispatch, RootState } from "slices/store";
import { displaySnackbar } from "slices/ui/uiSlice";
import { IncidentState } from "api/types";

export interface IncidentRequestBody {
id?: number;
state: IncidentState;
time_occurred: string; //($date-time)
description: string;
order_item: number;
team_id?: number;
created_at?: string;
updated_at?: string;
}

export interface IncidentResponse {
id: number;
state: string;
time_occurred: string;
description: string;
order_item: number;
team_id: number;
created_at: string;
updated_at: string;
}

export interface IncidentInitialState {
isLoading: boolean;
error: null | string;
incident: null | IncidentResponse;
}

const extraState: IncidentInitialState = {
isLoading: false,
error: null,
incident: null,
};

const incidentAdapter = createEntityAdapter<Incident>();

export const incidentReducerName = "incident";
export const initialState: IncidentInitialState =
incidentAdapter.getInitialState(extraState);

interface RejectValue {
status: number;
message: any;
}

export const createIncident = createAsyncThunk<
IncidentResponse,
IncidentRequestBody,
{ state: RootState; rejectValue: RejectValue; dispatch: AppDispatch }
>(
`${incidentReducerName}/createIncident`,
async (incident, { dispatch, rejectWithValue }) => {
try {
const response = await post<IncidentResponse>(
"/api/hardware/incidents/",
incident
);

dispatch(push("/"));
dispatch(
displaySnackbar({
message: `Successfully submitted incident form!`,
options: { variant: "success" },
})
);
return response.data;
} catch (e: any) {
dispatch(
displaySnackbar({
message: `An error has occurred! We couldn't submit your incident!`,
options: { variant: "error" },
})
);
return rejectWithValue({
status: e.response.status,
message: e.response.data,
});
}
}
);

const incidentSlice = createSlice({
name: incidentReducerName,
initialState,
reducers: {}, // used when we want to change state (w/ network req.)
extraReducers: (builder) => {
// used specifically for thunks
// pending, fulfilled, rejects are built in states from redux
builder.addCase(createIncident.pending, (state) => {
state.isLoading = true;
state.error = null;
});
builder.addCase(createIncident.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.error = null;
state.incident = payload;
// need to use the payload and put it into incident
});
builder.addCase(createIncident.rejected, (state, { payload }) => {
state.isLoading = false;
state.error =
payload === undefined
? "An internal server error has occurred, please inform hackathon organizers if this continues to happen."
: payload.message;
});
},
});

export const { reducer, actions } = incidentSlice;
export default reducer;

// Selectors
export const incidentSliceSelector = (state: RootState) => state[incidentReducerName];

export const isLoadingSelector = createSelector(
[incidentSliceSelector],
(incidentSlice) => incidentSlice.isLoading
);

export const errorSelector = createSelector(
[incidentSliceSelector],
(incidentSlice) => incidentSlice.error
);
2 changes: 2 additions & 0 deletions hackathon_site/dashboard/frontend/src/slices/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import teamAdminReducer, { teamAdminReducerName } from "slices/event/teamAdminSl
import teamDetailReducer, { teamDetailReducerName } from "slices/event/teamDetailSlice";
import teamOrderReducer, { teamOrderReducerName } from "slices/order/teamOrderSlice";
import adminOrderReducer, { adminOrderReducerName } from "slices/order/adminOrderSlice";
import incidentReducer, { incidentReducerName } from "slices/incident/incidentSlice";

export const history = createBrowserHistory();

Expand All @@ -40,6 +41,7 @@ const reducers = {
[uiReducerName]: uiReducer,
[teamAdminReducerName]: teamAdminReducer,
[adminOrderReducerName]: adminOrderReducer,
[incidentReducerName]: incidentReducer,
router: connectRouter(history),
};

Expand Down

0 comments on commit 2fed5ee

Please sign in to comment.