Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@bugsnag/plugin-react": "^7.9.2",
"accounting": "^0.4.1",
"cleave.js": "^1.6.0",
"dateformat": "^4.5.1",
"graphql": "^15.5.0",
"graphql-tag": "^2.11.0",
"graphql-tools": "^7.0.4",
Expand Down Expand Up @@ -59,6 +60,7 @@
"@hex-labs/stylelint-config": "^1.1.5",
"@types/accounting": "^0.4.1",
"@types/cleave.js": "^1.4.4",
"@types/dateformat": "^3.0.1",
"@types/jest": "26.0.22",
"@types/lodash": "^4.14.168",
"@types/react": "^17.0.3",
Expand Down
6 changes: 6 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import { USER_INFO } from "./graphql/Queries";
import LoadingSpinner from "./components/util/LoadingSpinner";
import CreateItemWrapper from "./components/items/CreateItemWrapper";
import EditItemWrapper from "./components/items/EditItemWrapper";
import AdminRequestsWrapper from "./components/requests/AdminRequestsWrapper";
import EditRequestWrapper from "./components/requests/EditRequestWrapper";
import CreateRequestWrapper from "./components/requests/CreateRequestWrapper";

interface OwnProps {}

Expand Down Expand Up @@ -81,6 +84,9 @@ const App: React.FC<Props> = props => {
<PrivateRoute exact path="/admin/items/:itemId" component={EditItemWrapper} />
<PrivateRoute exact path="/admin/csv" component={CSVWizard} />
<PrivateRoute exact path="/admin/users" component={AdminUsersListWrapper} />
<PrivateRoute exact path="/admin/requests" component={AdminRequestsWrapper} />
<PrivateRoute exact path="/admin/requests/new" component={CreateRequestWrapper} />
<PrivateRoute exact path="/admin/requests/:requestId" component={EditRequestWrapper} />
<PrivateRoute exact path="/admin/settings" component={AdminRequestSettingsWrapper} />
<PrivateRoute
exact
Expand Down
5 changes: 4 additions & 1 deletion client/src/components/admin/AdminOverviewContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ export type AdminCardLink = {
function adminCardLink(name: string, to = "#", external = false): AdminCardLink {
return { name, to, external };
}
const hardwareDesk: AdminCardLink[] = [adminCardLink("Work hardware desk", "/admin/desk")];
const hardwareDesk: AdminCardLink[] = [
adminCardLink("Work hardware desk", "/admin/desk"),
adminCardLink("Manage requests", "/admin/requests"),
];

const manage: AdminCardLink[] = [
adminCardLink("Users", "/admin/users"),
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/admin/desk/DeskContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function getUpdateQuery() {
}

function DeskContainer() {
const { subscribeToMore, ...query } = useQuery(DESK_REQUESTS);
const { subscribeToMore, ...query } = useQuery(DESK_REQUESTS, { fetchPolicy: "network-only" });
const randomPhrase = useState(
`${pickRandomElement(starters)} ${Math.floor((Math.random() + 1) * 900)} ${pickRandomElement(
funPhrases
Expand Down
141 changes: 141 additions & 0 deletions client/src/components/requests/AdminRequestsWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { useQuery } from "@apollo/client";
import dateFormat from "dateformat";
import React, { useState } from "react";
import DataTable from "react-data-table-component";
import { Link } from "react-router-dom";
import { Button, Header, Icon, Input, Message } from "semantic-ui-react";

import { Request } from "../../types/Request";
import { ALL_REQUESTS } from "../../graphql/Queries";
import LoadingSpinner from "../util/LoadingSpinner";

const columns = [
{
name: "Id",
selector: "id",
sortable: true,
},
{
name: "Item Name",
selector: "item.name",
sortable: true,
grow: 2,
},
{
name: "Quantity",
selector: "quantity",
sortable: true,
center: true,
},
{
name: "User",
selector: "user.name",
sortable: true,
center: true,
grow: 2,
},
{
name: "Status",
selector: "status",
sortable: true,
center: true,
grow: 3,
},
{
name: "Created At",
selector: (row: any) => dateFormat(row.createdAt, "hh:MM:ss TT -- mm/dd/yy"),
sortable: true,
center: true,
grow: 3,
},
{
name: "Edit",
selector: (row: any) => (
<Button
as={Link}
primary
basic
compact
size="tiny"
to={`/admin/requests/${row.id}`}
style={{ margin: 0 }}
>
Edit
</Button>
),
center: true,
},
];

const AdminRequestsWrapper: React.FC = () => {
const { data, loading, error } = useQuery(ALL_REQUESTS);
const [searchQuery, setSearchQuery] = useState("");

if (error) {
return (
<Message
error
visible
header="Can't fetch requests"
content={`Hmm, an error is preventing us from displaying the list of requests. The error was: ${error.message}`}
/>
);
}

const filteredData =
data && data.requests
? data.requests.filter(
(request: Request) =>
request.id.toString().indexOf(searchQuery) !== -1 ||
request.item.name.toLowerCase().indexOf(searchQuery) !== -1 ||
request.user.name.toLowerCase().indexOf(searchQuery) !== -1 ||
request.status.toLowerCase().indexOf(searchQuery) !== -1
)
: [];

return (
<>
<Header content="Manage Requests" size="huge" />
<Input
type="text"
label="Search requests"
name="searchQuery"
onChange={(e, { value }) => {
setSearchQuery(value.trim().toLowerCase());
}}
style={{ marginBottom: "10px", marginRight: "30px" }}
/>
<Button
primary
icon
labelPosition="left"
as={Link}
to="/admin/requests/new"
style={{ marginBottom: "10px" }}
>
<Icon name="plus circle" />
Create request
</Button>
<DataTable
columns={columns}
data={filteredData}
dense
defaultSortField="id"
defaultSortAsc={false}
fixedHeader
pagination
paginationComponentOptions={{
selectAllRowsItem: true,
}}
paginationPerPage={25}
paginationRowsPerPageOptions={[25, 50, 100]}
progressPending={loading}
noHeader
striped
progressComponent={<LoadingSpinner active content="Crunching the numbers..." />}
/>
</>
);
};

export default AdminRequestsWrapper;
22 changes: 22 additions & 0 deletions client/src/components/requests/CreateRequestWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import { Header } from "semantic-ui-react";

import { READY_FOR_PICKUP } from "../../types/Hardware";
import RequestEditForm from "./RequestEditForm";

const CreateRequestWrapper: React.FC = () => (
<div>
<Header as="h1">Create Request</Header>
<RequestEditForm
preloadRequest={{
userId: undefined,
itemId: undefined,
quantity: 1,
status: READY_FOR_PICKUP,
}}
createRequest
/>
</div>
);

export default CreateRequestWrapper;
46 changes: 46 additions & 0 deletions client/src/components/requests/EditRequestWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useQuery } from "@apollo/client";
import React from "react";
import { useParams } from "react-router";
import { Header, Message } from "semantic-ui-react";

import { GET_REQUEST } from "../../graphql/Queries";
import RequestEditForm from "./RequestEditForm";

interface ParamTypes {
requestId: string;
}

const EditRequestWrapper: React.FC = () => {
const { requestId } = useParams<ParamTypes>();

const { data, loading, error } = useQuery(GET_REQUEST, {
variables: {
requestId: parseInt(requestId),
},
});

if (loading) {
return null;
}

if (error) {
return <Message error visible header="Can't fetch request" content={error.message} />;
}

return (
<div>
<Header as="h1">Edit Request</Header>
<RequestEditForm
preloadRequestId={data.request.id}
preloadRequest={{
userId: data.request.user.uuid,
itemId: data.request.item.id,
quantity: data.request.quantity,
status: data.request.status,
}}
/>
</div>
);
};

export default EditRequestWrapper;
Loading