Skip to content

Commit

Permalink
Added admin settings (enable/disable requests) (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelraman authored Oct 25, 2019
1 parent 2292cd9 commit 8faaa22
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 33 deletions.
2 changes: 2 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import PrivateRoute from "./components/util/PrivateRoute";
import AdminOverviewContainer from "./components/admin/AdminOverviewContainer";
import {bugsnagClient, bugsnagEnabled} from "./index";
import AdminUsersListWrapper from "./components/admin/AdminUsersListWrapper";
import AdminRequestsWrapper from "./components/admin/AdminRequestsWrapper";
import UserProfileWrapper from "./components/users/UserProfileWrapper";
import DeskContainer from "./components/desk/DeskContainer";
import {User} from "./types/User";
Expand Down Expand Up @@ -91,6 +92,7 @@ class App extends Component<Props, {}> {
<PrivateRoute path="/admin/items" component={ItemWrapper}/>
<PrivateRoute exact path="/admin/csv" component={CSVWizard}/>
<PrivateRoute exact path="/admin/users" component={AdminUsersListWrapper}/>
<PrivateRoute exact path="/admin/requests" component={AdminRequestsWrapper}/>
<Route component={HomeContainer}/>
</Switch>
<Footer/>
Expand Down
2 changes: 2 additions & 0 deletions client/src/components/admin/AdminOverviewContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const hardwareDesk: AdminCardLink[] = [

const manage: AdminCardLink[] = [
adminCardLink("Users", "/admin/users"),
adminCardLink("Request settings", "/admin/requests"),
adminCardLink("Items")
];

const reports: AdminCardLink[] = [
Expand Down
79 changes: 79 additions & 0 deletions client/src/components/admin/AdminRequestsWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import {AppState} from "../../state/Store";
import {Button, Checkbox, Grid, Header, Loader, Message} from "semantic-ui-react";
import {Query, Mutation} from "react-apollo";
import {GET_SETTING} from "../util/graphql/Queries";
import {UPDATE_SETTING, CREATE_SETTING} from "../util/graphql/Mutations";

class AdminRequestsWrapper extends Component {
public render() {
let requests_allowed = false;
return <div>
<Header as="h1">Requests</Header>
<Query
query={GET_SETTING}
variables={{settingName: "requests_allowed"}}
>
{
({loading, error, data}: any) => {
if (loading) {
return <Loader active inline="centered" content="Just a sec!"/>;
}
if (error) {
return <div>
<Message error visible={true}
header="Can't load request settings"
content={`Hmm, an error is preventing us from displaying the request settings. The error was: ${error.message}`}
/>
<Mutation mutation={CREATE_SETTING}>
{(createSetting: any, {loading, data}: any) => (
<Button primary content={"Create setting: requests_allowed"}
onClick={((event, {checked}): any => {
createSetting({variables: {newSetting: {name: "requests_allowed", value: "true" }}});
requests_allowed = true;
})}
/>
)}
</Mutation>
</div>;
}
if(data.setting !== undefined) {
requests_allowed = (data.setting.value === "true");
}
return (
<Grid stackable>
<Grid.Row columns={1}>
<Grid.Column>
<Mutation mutation={UPDATE_SETTING}>
{(updateSetting: any, {loading, data}: any) => (
<Checkbox toggle
checked={requests_allowed}
label={"Requests allowed"}
onChange={((event, {checked}): any => {
updateSetting({variables: {settingName: "requests_allowed", updatedSetting: {name: "requests_allowed", value: checked ? "true" : "false" }}});
requests_allowed = !requests_allowed;
})}
/>
)}
</Mutation>
</Grid.Column>
</Grid.Row>
</Grid>
)
}
}
</Query>
</div>;
}
}

function mapStateToProps(state: AppState) {
return {
user: state.account
};
}

export default connect(
mapStateToProps
)(AdminRequestsWrapper);
13 changes: 10 additions & 3 deletions client/src/components/inventory/NewHardwareList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, {useState} from 'react';
import {useQuery} from "@apollo/react-hooks";
import {ALL_ITEMS} from "../util/graphql/Queries";
import {ALL_ITEMS, GET_SETTING} from "../util/graphql/Queries";
import {
Button,
Grid,
Expand All @@ -21,9 +21,11 @@ const NewHardwareList = ({user}: { user: User | null }) => {
const {data, loading, error} = useQuery(ALL_ITEMS);
const [searchQuery, setSearchQuery] = useState("");

const requestsEnabled = true;
const setting = useQuery(GET_SETTING, {
variables: {settingName: "requests_allowed"}
});

if (loading) {
if (loading || setting.loading) {
return (
<>
<Header size={"huge"}>Inventory</Header>
Expand All @@ -44,6 +46,11 @@ const NewHardwareList = ({user}: { user: User | null }) => {
</>;
}

let requestsEnabled = true;
if(!setting.error && setting.data.setting !== undefined) {
requestsEnabled = (setting.data.setting.value === "true");
}

let noRequestsMessageText = "";
if (!requestsEnabled) {
noRequestsMessageText = "Hardware checkout requests can't be made at this time.";
Expand Down
84 changes: 55 additions & 29 deletions client/src/components/inventory/RequestButton.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from "react";
import {Button, Icon} from "semantic-ui-react";
import {useMutation} from "@apollo/react-hooks";
import {Query} from "react-apollo";
import {CREATE_REQUEST} from "../util/graphql/Mutations";
import {RequestedItem} from "../../types/Hardware";
import {withToastManager} from "react-toast-notifications";
import {USER_REQUESTS} from "../util/graphql/Queries";
import {USER_REQUESTS, GET_SETTING} from "../util/graphql/Queries";
import {User} from "../../types/User";
import {Loader, Message} from "semantic-ui-react";

interface RequestButtonProps {
requestedItem: RequestedItem,
Expand All @@ -24,36 +26,60 @@ function RequestButton({requestedItem, user, toastManager}: RequestButtonProps)
},
],
});

if (loading) {
return <Loader active inline="centered" content="Just a sec!"/>;
}
if (error) {
return <Message error visible={true}
header="Can't load button"
content={`Hmm, an error is preventing us from displaying the button. The error was: ${error.message}`}
/>;
}
let requests_allowed = "true";
return (
<Button primary
icon
disabled={loading}
loading={loading}
onClick={event => createRequest({
variables: {
newRequest: {
user_id: requestedItem.user,
request_item_id: requestedItem.id,
quantity: requestedItem.qtyRequested
}
}
}).then(toastManager.add(`Successfully requested ${requestedItem.qtyRequested}x ${requestedItem.name}`, {
appearance: "success",
autoDismiss: true,
placement: "top-center"
})).catch((err: Error) => {
toastManager.add(`Successfully requested ${requestedItem.qtyRequested}x ${requestedItem.name}`, {
appearance: "error",
autoDismiss: true,
placement: "top-center"
})
})
<Query
query={GET_SETTING}
variables={{settingName: "requests_allowed"}}
>
{
({loading, error, data}: any) => {
if (loading) {
return <Loader active inline="centered" content="Just a sec!"/>;
}
labelPosition="right">
Request {requestedItem.qtyRequested}
<Icon name="arrow alternate circle right outline"/>
</Button>
if (!error && data.setting !== undefined) {
requests_allowed = data.setting.value;
}
return <Button primary
icon
disabled={loading || (requests_allowed === "false")}
loading={loading}
onClick={event => createRequest({
variables: {
newRequest: {
user_id: requestedItem.user,
request_item_id: requestedItem.id,
quantity: requestedItem.qtyRequested
}
}
}).then(toastManager.add(`Successfully requested ${requestedItem.qtyRequested}x ${requestedItem.name}`, {
appearance: "success",
autoDismiss: true,
placement: "top-center"
})).catch((err: Error) => {
toastManager.add(`Successfully requested ${requestedItem.qtyRequested}x ${requestedItem.name}`, {
appearance: "error",
autoDismiss: true,
placement: "top-center"
})
})
}
labelPosition="right">
Request {requestedItem.qtyRequested}
<Icon name="arrow alternate circle right outline"/>
</Button>
}
}
</Query>
);
}

Expand Down
18 changes: 18 additions & 0 deletions client/src/components/util/graphql/Mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,21 @@ export const UPDATE_REQUEST = gql`
}
}
`;

export const CREATE_SETTING = gql`
mutation createSetting ($newSetting: SettingInput!) {
createSetting(newSetting: $newSetting) {
name
value
}
}
`;

export const UPDATE_SETTING = gql`
mutation updateSetting ($settingName: String!, $updatedSetting: SettingInput!) {
updateSetting(name: $settingName, updatedSetting: $updatedSetting) {
name
value
}
}
`;
8 changes: 8 additions & 0 deletions client/src/components/util/graphql/Queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,11 @@ export const USER_PROFILE = gql`
}
}
`;

export const GET_SETTING = gql`
query getSetting($settingName: String!) {
setting(name: $settingName) {
name
value
}
}`;
2 changes: 2 additions & 0 deletions client/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export const client = new ApolloClient({
return "Request:" + object.request_id; // use `request_id` as the primary key
case 'Item':
return "Item:" + object.id;
case 'Setting':
return "Setting:" + object.name;
default:
return defaultDataIdFromObject(object); // fall back to default handling
}
Expand Down
4 changes: 4 additions & 0 deletions client/src/types/Setting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Setting {
name: string;
value: string;
}
16 changes: 16 additions & 0 deletions server/src/api/api.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ type Query {
items: [Item!]
# Return request(s) matching the search query provided and that the user has permission to see
requests(search: RequestSearch!): [Request!]
# Return setting(s) with the name provided
setting(name: String!): Setting
locations: [Location!]
}

type Mutation {
# Create a new item
createItem(newItem: ItemInput!): Item!
# Update an existing setting
updateSetting(name: String!, updatedSetting: SettingInput!): Setting!
# Create a new setting
createSetting(newSetting: SettingInput!): Setting!
# Update the item with the specified ID to have the properties in updatedItem. At this time
# all fields must be included
updateItem(id: Int!, updatedItem: ItemInput!): Item
Expand Down Expand Up @@ -84,6 +90,11 @@ type ItemsByCategory {
items: [Item!]
}

type Setting {
name: String!
value: String!
}

input ItemInput {
item_name: String!
description: String!
Expand All @@ -106,6 +117,11 @@ input ItemInput {
owner: String!
}

input SettingInput {
name: String!
value: String!
}

type Category {
# The unique ID for this category
category_id: Int!
Expand Down
Loading

0 comments on commit 8faaa22

Please sign in to comment.