Skip to content

Commit

Permalink
Fix order button to use state
Browse files Browse the repository at this point in the history
  • Loading branch information
xendk committed Jun 7, 2021
1 parent d3d1971 commit 6ef7fe1
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 77 deletions.
116 changes: 66 additions & 50 deletions src/apps/order-material/order-material.entry.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import React, { useState, useEffect } from "react";
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import PropTypes from "prop-types";
import urlPropType from "url-prop-type";

import OrderMaterial from "./order-material";
import OpenPlatform from "../../core/OpenPlatform";
import DingIll from "../../core/DingIll";
import User from "../../core/user";

import {
checkAvailibility,
orderMaterial,
orderMaterialPending,
orderMaterialAborted,
resetStatusDelayed
} from "./order-material.slice";

/**
* Transform a set of ids to an array of ids.
*
Expand All @@ -20,6 +27,18 @@ function idsArray(ids) {
return typeof ids === "string" ? ids.split(",") : ids;
}

/**
* Do the reverse of idsArray
*
* This returns a string, separating ids with commas.
*
* @param {string|string[]} One or more ids.
* @returns {string} Comma separated list of ids.
*/
function idsString(ids) {
return typeof ids !== "string" ? ids.join() : ids;
}

function OrderMaterialEntry({
text,
helpText,
Expand All @@ -36,56 +55,53 @@ function OrderMaterialEntry({
pickupBranch,
expires
}) {
const [status, setStatus] = useState("initial");
// const [status, setStatus] = useState("initial");
const stateId = idsString(ids);
const status =
useSelector(state => state.orderMaterial.status[stateId]) || "initial";
const dispatch = useDispatch();
const loggedIn = User.isAuthenticated();

function orderMaterial() {
setStatus("processing");
const client = new OpenPlatform();
client
.orderMaterial({ pids: idsArray(ids), pickupBranch, expires })
.then(() => {
setStatus("finished");
})
.catch(() => {
setStatus("failed");
});
function onClick() {
dispatch(orderMaterialPending({ id: stateId }));
if (!loggedIn) {
User.authenticate(loginUrl);
}
}

useEffect(() => {
const client = new OpenPlatform();
const dingIll = new DingIll(illCheckUrl);
// Check that the material is available for ILL.
setStatus("checking");
dingIll
.isAvailableForIll(idsArray(ids))
.then(available => {
if (!available) {
setStatus("unavailable");
} else if (User.isAuthenticated()) {
// Check that the pickup branch accepts inter-library loans.
client
.getBranch(pickupBranch)
.then(branch => {
if (branch.willReceiveIll !== "1") {
setStatus("invalid branch");
} else {
setStatus("ready");
}
})
.catch(() => {
setStatus("failed");
});
} else {
// It's available and we're not logged in, show as available
// for order. Ready is the state we use for buttons which
// require login when accessed by anonymous users.
setStatus("ready");
}
})
.catch(() => {
setStatus("failed");
});
}, [ids, pickupBranch, illCheckUrl]);
if (status === "initial") {
// Check that the material is available for ILL.
dispatch(
checkAvailibility({ id: stateId, pids: idsArray(ids), illCheckUrl })
);
}
if (status === "pending") {
if (loggedIn) {
dispatch(
orderMaterial({
id: stateId,
pids: idsArray(ids),
pickupBranch,
expires
})
);
} else if (User.authenticationFailed()) {
// If authentication failed, abort.
dispatch(orderMaterialAborted({ id: stateId }));
dispatch(resetStatusDelayed({ id: stateId }));
}
}
}, [
ids,
pickupBranch,
illCheckUrl,
expires,
loggedIn,
stateId,
status,
dispatch
]);

return (
<OrderMaterial
Expand All @@ -99,7 +115,7 @@ function OrderMaterialEntry({
unavailableText={unavailableText}
invalidPickupBranchText={invalidPickupBranchText}
status={status}
onClick={orderMaterial}
onClick={onClick}
loginUrl={loginUrl}
materialIds={idsArray(ids)}
/>
Expand Down
31 changes: 4 additions & 27 deletions src/apps/order-material/order-material.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import urlPropType from "url-prop-type";

import Button from "../../components/atoms/button/button";
import Dialog from "../../components/atoms/dialog/dialog";
import Alert from "../../components/alert/alert";
import User from "../../core/user";
import replacePlaceholders from "../../core/replacePlaceholders";

function OrderMaterial({
status,
Expand All @@ -19,9 +16,7 @@ function OrderMaterial({
helpText,
errorText,
successText,
successMessage,
loginUrl,
materialIds
successMessage
}) {
const [open, setOpen] = useState(true);
const closeDialog = () => setOpen(false);
Expand Down Expand Up @@ -71,7 +66,7 @@ function OrderMaterial({
<Alert message={successText} type="polite" variant="success" />
</div>
<Dialog
label="Tilføj søgning til liste"
label="Materialet bestilt"
showCloseButton
dropDown
isOpen={open}
Expand All @@ -92,23 +87,7 @@ function OrderMaterial({
default:
return (
<div className="ddb-order-material__container">
<Button
href={
!User.isAuthenticated()
? replacePlaceholders({
text: loginUrl,
tags: {
// Urls only support a single material id so assume we
// want to use the first.
id: encodeURIComponent(materialIds.first)
}
})
: undefined
}
variant="black"
align="left"
onClick={onClick}
>
<Button variant="black" align="left" onClick={onClick}>
{text}
</Button>
{helpText ? (
Expand All @@ -129,7 +108,6 @@ OrderMaterial.propTypes = {
progressText: PropTypes.string.isRequired,
unavailableText: PropTypes.string.isRequired,
invalidPickupBranchText: PropTypes.string.isRequired,
loginUrl: urlPropType.isRequired,
onClick: PropTypes.func.isRequired,
status: PropTypes.oneOf([
"initial",
Expand All @@ -140,8 +118,7 @@ OrderMaterial.propTypes = {
"processing",
"failed",
"finished"
]),
materialIds: PropTypes.arrayOf(PropTypes.string).isRequired
])
};

OrderMaterial.defaultProps = {
Expand Down
86 changes: 86 additions & 0 deletions src/apps/order-material/order-material.slice.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

import OpenPlatform from "../../core/OpenPlatform";
import DingIll from "../../core/DingIll";

export const resetStatusDelayed = createAsyncThunk(
"orderMaterial/resetStatusDelayed",
() => {
return new Promise(resolve => {
setTimeout(() => resolve(), 4000);
});
}
);

export const checkAvailibility = createAsyncThunk(
"orderMaterial/checkAvailibility",
async ({ pids, illCheckUrl }, { rejectWithValue }) => {
const dingIll = new DingIll(illCheckUrl);
try {
return await dingIll.isAvailableForIll(pids);
} catch (err) {
return rejectWithValue(err);
}
}
);

export const orderMaterial = createAsyncThunk(
"orderMaterial/orderMaterial",
async (
{ id, pids, pickupBranch, expires },
{ dispatch, rejectWithValue }
) => {
const client = new OpenPlatform();
try {
return await client.orderMaterial({ pids, pickupBranch, expires });
} catch (err) {
dispatch(resetStatusDelayed({ id }));
return rejectWithValue(err);
}
}
);

export const orderMaterialSlice = createSlice({
name: "orderMaterial",
initialState: { status: {} },
reducers: {
orderMaterialPending(state, action) {
state.status[action.payload.id] = "pending";
},
orderMaterialAborted(state, action) {
state.status[action.meta.arg.id] = "failed";
}
},
extraReducers: {
[checkAvailibility.pending]: (state, action) => {
state.status[action.meta.arg.id] = "checking";
},
[checkAvailibility.fulfilled]: (state, action) => {
state.status[action.meta.arg.id] = action.payload
? "ready"
: "unavailable";
},
[checkAvailibility.pending]: (state, action) => {
state.status[action.meta.arg.id] = "failed";
},
[orderMaterial.pending]: (state, action) => {
state.status[action.meta.arg.id] = "processing";
},
[orderMaterial.fulfilled]: (state, action) => {
state.status[action.meta.arg.id] = "finished";
},
[orderMaterial.rejected]: (state, action) => {
state.status[action.meta.arg.id] = "failed";
},
[resetStatusDelayed.fulfilled]: (state, action) => {
state.status[action.meta.arg.id] = "ready";
}
}
});

export const {
orderMaterialPending,
orderMaterialAborted
} = orderMaterialSlice.actions;

export default orderMaterialSlice.reducer;
2 changes: 2 additions & 0 deletions src/core/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { persistReducer, persistStore } from "redux-persist";
import storage from "redux-persist/lib/storage/session";
import userReducer from "./user.slice";
import checklistMaterialButtonReducer from "../apps/checklist-button/checklist-material-button.slice";
import orderMaterialReducer from "../apps/order-material/order-material.slice";

const persistConfig = {
key: "ddb-react",
Expand All @@ -14,6 +15,7 @@ export const store = configureStore({
persistConfig,
combineReducers({
checklistMaterialButton: checklistMaterialButtonReducer,
orderMaterial: orderMaterialReducer,
user: userReducer
})
),
Expand Down

0 comments on commit 6ef7fe1

Please sign in to comment.