diff --git a/applications/webapp/src/Components/Button.test.tsx b/applications/webapp/src/Components/Button.test.tsx
deleted file mode 100644
index 79c526a2..00000000
--- a/applications/webapp/src/Components/Button.test.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * Humanitech Supply Trail
- *
- * Copyright (c) Humanitech, Peter Rogov and Contributors
- *
- * Website: https://humanitech.net
- * Repository: https://github.com/humanitech-net/supply-trail
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import React from "react";
-import {
- cleanup,
- render,
- fireEvent,
- waitFor,
- screen,
-} from "@testing-library/react";
-import GraphQlButton, { Connection } from "./Button";
-import { MockedProvider } from "@apollo/client/testing";
-import { GraphQLError } from "graphql";
-
-const mockData = {
- findAll: {
- id: 1,
- firstName: "Test",
- },
-};
-
-const successMock = {
- request: {
- query: Connection,
- },
- result: {
- data: mockData,
- },
-};
-
-const errorMock = {
- request: {
- query: Connection,
- },
- error: new GraphQLError("the fetch was unsuccessful"),
-};
-
-describe("GraphQlButton", () => {
- afterEach(() => {
- cleanup();
- });
-
- it("should render data when fetched successfully", async () => {
- render(
-
-
- ,
- );
-
- fireEvent.click(screen.getByText("Click Me"));
-
- await waitFor(() => {
- expect(screen.getByText("ID: 1")).toBeInTheDocument;
- expect(screen.getByText("First Name: Test")).toBeInTheDocument;
- });
- });
-
- it("should render error message when fetch fails", async () => {
- render(
-
-
- ,
- );
-
- fireEvent.click(screen.getByText("Click Me"));
-
- await waitFor(() => {
- expect(screen.getByText("Error: the fetch was unsuccessful"))
- .toBeInTheDocument;
- });
- });
-});
diff --git a/applications/webapp/src/Components/Button.tsx b/applications/webapp/src/Components/Button.tsx
deleted file mode 100644
index 35b09412..00000000
--- a/applications/webapp/src/Components/Button.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Humanitech Supply Trail
- *
- * Copyright (c) Humanitech, Peter Rogov and Contributors
- *
- * Website: https://humanitech.net
- * Repository: https://github.com/humanitech-net/supply-trail
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import React, { useState } from "react";
-import { useQuery, gql } from "@apollo/client";
-import { Button } from "@mui/material";
-
-export const Connection = gql`
- query {
- findAll {
- id
- firstName
- }
- }
-`;
-
-const GraphQlButton: React.FC = () => {
- const [isDataFetched, setIsDataFetched] = useState(false);
-
- const { error, data, refetch } = useQuery(Connection, {
- skip: !isDataFetched,
- });
-
- const handleButtonClick = () => {
- setIsDataFetched(true);
- refetch();
- };
-
- if (error) {
- return (
-
-
-
Error: {error.message}
-
- );
- }
-
- return (
-
-
- {isDataFetched && (
-
-
-
First Name: {data?.findAll?.firstName}
-
- )}
-
- );
-};
-
-export default GraphQlButton;
diff --git a/applications/webapp/src/Components/leftDrawer.tsx b/applications/webapp/src/Components/leftDrawer.tsx
deleted file mode 100644
index f5890d1d..00000000
--- a/applications/webapp/src/Components/leftDrawer.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * Humanitech Supply Trail
- *
- * Copyright (c) Humanitech, Peter Rogov and Contributors
- *
- * Website: https://humanitech.net
- * Repository: https://github.com/humanitech-net/supply-trail
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import React from "react";
-import { styled, useTheme } from "@mui/material/styles";
-import Drawer from "@mui/material/Drawer";
-import List from "@mui/material/List";
-import IconButton from "@mui/material/IconButton";
-import ListItem from "@mui/material/ListItem";
-import ListItemButton from "@mui/material/ListItemButton";
-import ListItemText from "@mui/material/ListItemText";
-import MenuIcon from "@mui/icons-material/Menu";
-import useMediaQuery from "@mui/material/useMediaQuery";
-
-interface DrawerProps {
- open: boolean;
- closeDrawer: () => void;
-}
-
-export default function LeftDrawer({
- open,
- closeDrawer,
-}: Readonly) {
- const drawerWidth = 240;
- const theme = useTheme();
- const isDesktop = useMediaQuery(theme.breakpoints.up("md"));
- const menu = [
- "Supply",
- "Tracking",
- "Inventory",
- "Logistics",
- "Schools",
- "Analytics",
- "Contact List",
- ];
-
- const DrawerHeader = styled("div")(({ theme }) => ({
- display: "flex",
- alignItems: "center",
- padding: theme.spacing(0, 1),
- ...theme.mixins.toolbar,
- justifyContent: "flex-start",
- paddingLeft: 22,
- backgroundColor: "#011C27",
- }));
-
- return (
-
-
-
-
-
-
-
- {menu.map((text) => (
-
-
-
-
-
- ))}
-
-
- );
-}
diff --git a/applications/webapp/src/Pages/Home/ContextProvider/drawerProvider.tsx b/applications/webapp/src/Pages/Home/ContextProvider/drawerProvider.tsx
new file mode 100644
index 00000000..8cb43825
--- /dev/null
+++ b/applications/webapp/src/Pages/Home/ContextProvider/drawerProvider.tsx
@@ -0,0 +1,37 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React, { ReactNode, createContext, useState, useMemo } from "react";
+
+interface DrawerContextProps {
+ open: boolean;
+ setOpen: (open: boolean) => void;
+}
+
+interface DrawerProviderProps {
+ children: ReactNode;
+}
+
+export const DrawerContext = createContext({
+ open: false,
+ setOpen: () => {},
+});
+
+export const DrawerProvider: React.FC = ({ children }) => {
+ const [open, setOpen] = useState(false);
+
+ const props = useMemo(() => ({ open, setOpen }), [open, setOpen]);
+
+ return (
+ {children}
+ );
+};
diff --git a/applications/webapp/src/Components/footer.tsx b/applications/webapp/src/Pages/Home/Footer/footer.tsx
similarity index 100%
rename from applications/webapp/src/Components/footer.tsx
rename to applications/webapp/src/Pages/Home/Footer/footer.tsx
diff --git a/applications/webapp/src/Components/test/footer.test.tsx b/applications/webapp/src/Pages/Home/Footer/tests/footer.test.tsx
similarity index 100%
rename from applications/webapp/src/Components/test/footer.test.tsx
rename to applications/webapp/src/Pages/Home/Footer/tests/footer.test.tsx
diff --git a/applications/webapp/src/Pages/Home/LeftDrawer/Components/drawerHeader.tsx b/applications/webapp/src/Pages/Home/LeftDrawer/Components/drawerHeader.tsx
new file mode 100644
index 00000000..3bee4700
--- /dev/null
+++ b/applications/webapp/src/Pages/Home/LeftDrawer/Components/drawerHeader.tsx
@@ -0,0 +1,49 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React, { useContext } from "react";
+import { IconButton, styled } from "@mui/material";
+
+import MenuIcon from "@mui/icons-material/Menu";
+import { DrawerContext } from "../../ContextProvider/drawerProvider";
+
+export default function DrawerHeader() {
+ const { open, setOpen } = useContext(DrawerContext);
+
+ const closeDrawer = React.useCallback(() => {
+ setOpen(false);
+ }, [open]);
+
+ const Header = styled("div")(({ theme }) => ({
+ display: "flex",
+ alignItems: "center",
+ padding: theme.spacing(0, 1),
+ ...theme.mixins.toolbar,
+ justifyContent: "flex-start",
+ paddingLeft: 22,
+ backgroundColor: "#011C27",
+ }));
+
+ return (
+
+ );
+}
diff --git a/applications/webapp/src/Pages/Home/LeftDrawer/Components/drawerMenu.tsx b/applications/webapp/src/Pages/Home/LeftDrawer/Components/drawerMenu.tsx
new file mode 100644
index 00000000..27e71521
--- /dev/null
+++ b/applications/webapp/src/Pages/Home/LeftDrawer/Components/drawerMenu.tsx
@@ -0,0 +1,41 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from "react";
+import { List, ListItem, ListItemButton, ListItemText } from "@mui/material";
+import { drawerMenu } from "../util/constants";
+
+export default function DrawerMenu() {
+ return (
+
+ {drawerMenu.map((text) => (
+
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/applications/webapp/src/Pages/Home/LeftDrawer/leftDrawer.tsx b/applications/webapp/src/Pages/Home/LeftDrawer/leftDrawer.tsx
new file mode 100644
index 00000000..2986751b
--- /dev/null
+++ b/applications/webapp/src/Pages/Home/LeftDrawer/leftDrawer.tsx
@@ -0,0 +1,41 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React, { useContext } from "react";
+import { useTheme } from "@mui/material/styles";
+import Drawer from "@mui/material/Drawer";
+import useMediaQuery from "@mui/material/useMediaQuery";
+import DrawerHeader from "./Components/drawerHeader";
+import DrawerMenu from "./Components/drawerMenu";
+import { styles } from "./util/style";
+import { DrawerContext } from "../ContextProvider/drawerProvider";
+
+export default function LeftDrawer() {
+ const theme = useTheme();
+ const style = styles();
+
+ const isDesktop = useMediaQuery(theme.breakpoints.up("md"));
+
+ const { open } = useContext(DrawerContext);
+
+ return (
+
+
+
+
+ );
+}
diff --git a/applications/webapp/src/Components/test/drawer.test.tsx b/applications/webapp/src/Pages/Home/LeftDrawer/tests/drawer.test.tsx
similarity index 82%
rename from applications/webapp/src/Components/test/drawer.test.tsx
rename to applications/webapp/src/Pages/Home/LeftDrawer/tests/drawer.test.tsx
index b57d1cef..baee6e6c 100644
--- a/applications/webapp/src/Components/test/drawer.test.tsx
+++ b/applications/webapp/src/Pages/Home/LeftDrawer/tests/drawer.test.tsx
@@ -16,7 +16,6 @@ import LeftDrawer from "../leftDrawer";
describe("LeftDrawer", () => {
test("renders LeftDrawer component", () => {
- const closeDrawerMock = jest.fn();
- render();
+ render();
});
});
diff --git a/applications/webapp/src/Pages/Home/LeftDrawer/util/constants.ts b/applications/webapp/src/Pages/Home/LeftDrawer/util/constants.ts
new file mode 100644
index 00000000..382f0b5b
--- /dev/null
+++ b/applications/webapp/src/Pages/Home/LeftDrawer/util/constants.ts
@@ -0,0 +1,21 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export const drawerMenu = [
+ "Supply",
+ "Tracking",
+ "Inventory",
+ "Logistics",
+ "Schools",
+ "Analytics",
+ "Contact List",
+];
diff --git a/applications/webapp/src/Pages/Home/LeftDrawer/util/style.ts b/applications/webapp/src/Pages/Home/LeftDrawer/util/style.ts
new file mode 100644
index 00000000..6d7e10a7
--- /dev/null
+++ b/applications/webapp/src/Pages/Home/LeftDrawer/util/style.ts
@@ -0,0 +1,23 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export const styles = () => ({
+ drawer: {
+ width: 240,
+ flexShrink: 0,
+ "& .MuiDrawer-paper": {
+ width: 240,
+ boxSizing: "border-box",
+ backgroundColor: "#011C27",
+ },
+ },
+});
diff --git a/applications/webapp/src/Components/mainContent.tsx b/applications/webapp/src/Pages/Home/MainContent/mainContent.tsx
similarity index 51%
rename from applications/webapp/src/Components/mainContent.tsx
rename to applications/webapp/src/Pages/Home/MainContent/mainContent.tsx
index 1dd052ee..a73ba3a8 100644
--- a/applications/webapp/src/Components/mainContent.tsx
+++ b/applications/webapp/src/Pages/Home/MainContent/mainContent.tsx
@@ -10,46 +10,33 @@
* LICENSE file in the root directory of this source tree.
*/
-import React from "react";
-import { styled, useTheme } from "@mui/material/styles";
-import useMediaQuery from "@mui/material/useMediaQuery";
+import React, { useContext } from "react";
+import { styled } from "@mui/material/styles";
import { Routes, Route } from "react-router-dom";
-import UserPage from "../Pages/User/userPage";
+import UserPage from "../../User/userPage";
+import { DrawerContext } from "src/Pages/Home/ContextProvider/drawerProvider";
+import { useMediaQuery, useTheme } from "@mui/material";
-interface HandleDrawer {
- open: boolean;
-}
-
-export default function MainContent({ open }: Readonly) {
- const drawerWidth = 240;
+export default function MainContent() {
+ const { open } = useContext(DrawerContext);
const theme = useTheme();
const isDesktop = useMediaQuery(theme.breakpoints.up("md"));
- const Main = styled("main", {
- shouldForwardProp: (prop) => prop !== "open",
- })<{
- open?: boolean;
- }>(({ theme }) => ({
+ const leftMargin = 240;
+
+ const Main = styled("main")(({ theme }) => ({
+ marginLeft: open && isDesktop ? leftMargin : 0,
marginTop: 80,
flexGrow: 1,
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
-
- ...(isDesktop && {
- ...(open && {
- marginLeft: drawerWidth,
- transition: theme.transitions.create("margin", {
- easing: theme.transitions.easing.sharp,
- duration: theme.transitions.duration.enteringScreen,
- }),
- }),
- }),
}));
+
return (
-
+
} />
diff --git a/applications/webapp/src/Components/test/mainContent.test.tsx b/applications/webapp/src/Pages/Home/MainContent/tests/mainContent.test.tsx
similarity index 94%
rename from applications/webapp/src/Components/test/mainContent.test.tsx
rename to applications/webapp/src/Pages/Home/MainContent/tests/mainContent.test.tsx
index 74d0d0e8..10b81a9d 100644
--- a/applications/webapp/src/Components/test/mainContent.test.tsx
+++ b/applications/webapp/src/Pages/Home/MainContent/tests/mainContent.test.tsx
@@ -19,7 +19,7 @@ describe("MainContent", () => {
test("renders MainContent component", () => {
render(
-
+
,
);
});
diff --git a/applications/webapp/src/Components/navbar.tsx b/applications/webapp/src/Pages/Home/NavBar/navbar.tsx
similarity index 91%
rename from applications/webapp/src/Components/navbar.tsx
rename to applications/webapp/src/Pages/Home/NavBar/navbar.tsx
index 14f60f97..6f31d2d9 100644
--- a/applications/webapp/src/Components/navbar.tsx
+++ b/applications/webapp/src/Pages/Home/NavBar/navbar.tsx
@@ -10,7 +10,7 @@
* LICENSE file in the root directory of this source tree.
*/
-import React from "react";
+import React, { useContext } from "react";
import { Link } from "react-router-dom";
import Box from "@mui/material/Box";
import { styled } from "@mui/material/styles";
@@ -22,14 +22,10 @@ import MenuIcon from "@mui/icons-material/Menu";
import AccountCircle from "@mui/icons-material/AccountCircle";
import Brightness4Icon from "@mui/icons-material/Brightness4";
import NotificationsIcon from "@mui/icons-material/Notifications";
+import { DrawerContext } from "src/Pages/Home/ContextProvider/drawerProvider";
const drawerWidth = 240;
-interface HandleDrawer {
- open: boolean;
- openDrawer: () => void;
-}
-
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
@@ -51,7 +47,11 @@ const AppBar = styled(MuiAppBar, {
}),
}));
-export default function NavBar({ open, openDrawer }: Readonly) {
+export default function NavBar() {
+ const { open, setOpen } = useContext(DrawerContext);
+ const openDrawer = React.useCallback(() => {
+ setOpen(true);
+ }, [open]);
return (
{
test("renders NavBar component", () => {
- const openDrawerMock = jest.fn();
-
render(
-
+
,
);
});
diff --git a/applications/webapp/src/Pages/Home/test/home.test.tsx b/applications/webapp/src/Pages/Home/home.test.tsx
similarity index 87%
rename from applications/webapp/src/Pages/Home/test/home.test.tsx
rename to applications/webapp/src/Pages/Home/home.test.tsx
index 3d2fa8c7..6e7b38d0 100644
--- a/applications/webapp/src/Pages/Home/test/home.test.tsx
+++ b/applications/webapp/src/Pages/Home/home.test.tsx
@@ -13,9 +13,10 @@
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
-import Home from "../home";
+import Home from "./home";
describe("Home", () => {
+ const openDrawerLabel = "open drawer";
test("renders navbar", () => {
const { getByLabelText } = render(
@@ -32,7 +33,7 @@ describe("Home", () => {
,
);
- const OpenDrawerButton = getByLabelText("open drawer");
+ const OpenDrawerButton = getByLabelText(openDrawerLabel);
fireEvent.click(OpenDrawerButton);
const Drawer = getByLabelText("drawer");
@@ -46,7 +47,7 @@ describe("Home", () => {
,
);
- const OpenDrawerButton = getByLabelText("open drawer");
+ const OpenDrawerButton = getByLabelText(openDrawerLabel);
fireEvent.click(OpenDrawerButton);
const CloseDrawerButton = getByLabelText("close drawer");
diff --git a/applications/webapp/src/Pages/Home/home.tsx b/applications/webapp/src/Pages/Home/home.tsx
index a248ba50..66226b2e 100644
--- a/applications/webapp/src/Pages/Home/home.tsx
+++ b/applications/webapp/src/Pages/Home/home.tsx
@@ -13,27 +13,20 @@
import React from "react";
import Box from "@mui/material/Box";
import CssBaseline from "@mui/material/CssBaseline";
-import NavBar from "../../Components/navbar";
-import LeftDrawer from "../../Components/leftDrawer";
-import MainContent from "../../Components/mainContent";
+import NavBar from "./NavBar/navbar";
+import LeftDrawer from "./LeftDrawer/leftDrawer";
+import MainContent from "./MainContent/mainContent";
+import { DrawerProvider } from "./ContextProvider/drawerProvider";
export default function Home() {
- const [open, setOpen] = React.useState(false);
-
- function openDrawer() {
- setOpen(true);
- }
-
- function closeDrawer() {
- setOpen(false);
- }
-
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
}
diff --git a/applications/webapp/src/Pages/User/components/ContextProvider/UserPageContextProvider.tsx b/applications/webapp/src/Pages/User/components/ContextProvider/UserPageContextProvider.tsx
index 004e4de0..7535511b 100644
--- a/applications/webapp/src/Pages/User/components/ContextProvider/UserPageContextProvider.tsx
+++ b/applications/webapp/src/Pages/User/components/ContextProvider/UserPageContextProvider.tsx
@@ -10,22 +10,42 @@
* LICENSE file in the root directory of this source tree.
*/
-import React, { useMemo } from "react";
+import React, { useMemo, useState } from "react";
import { useCurrentUserData } from "../../../../hooks/useCurrentUserData";
import { UserContext } from "../../context";
+import useUserEntry from "../../../../hooks/useUserEntry";
export function UserPageContextProvider({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
- const { user, loading, error } = useCurrentUserData();
+ const {
+ user: initialUser,
+ loading: queryLoading,
+ error: queryError,
+ } = useCurrentUserData();
+
+ const {
+ update,
+ user: updatedUser,
+ loading: mutationLoading,
+ error: mutationError,
+ } = useUserEntry();
+
+ const [userUpdated, setUserUpdated] = useState(false);
+
+ const user = userUpdated ? updatedUser : initialUser;
+ const loading = userUpdated ? mutationLoading : queryLoading;
+ const error = userUpdated ? mutationError : queryError;
const userpagecontext = useMemo(
() => ({
user,
loading,
error,
+ setUserUpdated,
+ update,
}),
[user, loading, error],
);
diff --git a/applications/webapp/src/Pages/User/components/detailHolder.tsx b/applications/webapp/src/Pages/User/components/detailHolder.tsx
index 662ef89c..f1b773e7 100644
--- a/applications/webapp/src/Pages/User/components/detailHolder.tsx
+++ b/applications/webapp/src/Pages/User/components/detailHolder.tsx
@@ -11,144 +11,80 @@
*/
import React, { useState } from "react";
-import {
- Card,
- CardContent,
- FormControl,
- Grid,
- useTheme,
- TextField,
- Button,
- Box,
-} from "@mui/material";
+import { Card, CardContent, Grid, useTheme, Button, Box } from "@mui/material";
import { styles } from "../util/style";
-import { useMutation } from "@apollo/client";
-import { EditUserMutation } from "../../../hooks/mutation";
import { useCardContext, useUserContext } from "../context";
import { Link } from "react-router-dom";
-import { CHANGE_PASSWORD_URL } from "../util/constants";
+import { CHANGE_PASSWORD_URL, fields } from "../util/constants";
+import { UserDetailGridItem } from "./userDetailHolderGridItem";
+import { User } from "../../interface";
export default function DetailHolder() {
const theme = useTheme();
const style = styles(theme).detailHolder;
+ const boxStyle = styles(theme).userPage;
+
+ const { user, update, setUserUpdated } = useUserContext();
+ const { elevation } = useCardContext();
+ const [updatedUser, setUpdatedUser] = useState(user);
+ const { editable, setEditable, setElevation } = useCardContext();
+
+ const newUserData = {
+ userInput: {
+ username: updatedUser.username,
+ firstName: updatedUser.firstName,
+ lastName: updatedUser.lastName,
+ },
+ };
+
+ const handleFieldChange = React.useCallback(
+ (field: keyof User, value: string) => {
+ setUpdatedUser((prevUserData) => ({
+ ...prevUserData,
+ [field]: value,
+ }));
+ },
+ [setUpdatedUser],
+ );
- const { user } = useUserContext();
- const { firstName, lastName, email, address, birthdate, phoneNumber } = user;
-
- const card = useCardContext();
- const { editable, setEditable, setElevation } = card;
-
- const [firstname, setFirstname] = useState(firstName);
-
- const [lastname, setLastname] = useState(lastName);
-
- const [editUser] = useMutation(EditUserMutation);
-
- async function updateUser() {
- try {
- const { data } = await editUser({
- variables: {
- firstname,
- lastname,
- },
- });
- console.log("User:", data.editUser);
- } catch (error) {
- console.error("Error:", error);
- }
- setEditable(!editable);
- setElevation(0);
- }
-
- function firstNameChanged(
- event: React.ChangeEvent,
- ) {
- return setFirstname(event.target.value);
- }
-
- function lastNameChanged(
- event: React.ChangeEvent,
- ) {
- return setLastname(event.target.value);
- }
+ const onClickUpdate = React.useCallback(async () => {
+ await update(newUserData).then(() => {
+ setEditable(!editable);
+ setElevation(0);
+ setUserUpdated(true);
+ });
+ }, [newUserData]);
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {fields.map((field, index) => (
+
-
+ ))}
-
- {!card.editable && (
-
-
-
-
-
- )}
-
-
+ )}
+
+
+
);
}
diff --git a/applications/webapp/src/Pages/User/components/profileHolder.tsx b/applications/webapp/src/Pages/User/components/profileHolder.tsx
index 44d393a9..87b2e7f2 100644
--- a/applications/webapp/src/Pages/User/components/profileHolder.tsx
+++ b/applications/webapp/src/Pages/User/components/profileHolder.tsx
@@ -28,6 +28,7 @@ import { useCardContext, useUserContext } from "../context";
export default function ProfileHolder() {
const theme = useTheme();
const style = styles(theme).profileholder;
+ const boxStyle = styles(theme).userPage;
const { user } = useUserContext();
const { username, description } = user;
@@ -36,32 +37,34 @@ export default function ProfileHolder() {
const { editUser } = card;
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {username}
+
+
+ {description}
+
+
+
+ Edit Profile
+
+
-
- {username}
-
-
- {description}
-
-
-
- Edit Profile
-
-
-
-
-
-
+
+
+
+
);
}
diff --git a/applications/webapp/src/Pages/User/components/userDetailHolderGridItem.tsx b/applications/webapp/src/Pages/User/components/userDetailHolderGridItem.tsx
new file mode 100644
index 00000000..8e127d88
--- /dev/null
+++ b/applications/webapp/src/Pages/User/components/userDetailHolderGridItem.tsx
@@ -0,0 +1,47 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from "react";
+import { Grid, TextField } from "@mui/material";
+import { User } from "../../../Pages/interface";
+import { useCardContext } from "../context";
+import { labels } from "../util/constants";
+
+export function UserDetailGridItem(
+ props: Readonly<{
+ user: User;
+ field: keyof User;
+ onChange: (field: keyof User, val: string) => void;
+ }>,
+) {
+ const { user, field, onChange } = props;
+ const card = useCardContext();
+
+ const updateValue = React.useCallback(
+ (event: React.ChangeEvent) => {
+ onChange(field, event.target.value);
+ },
+ [field],
+ );
+
+ return (
+
+
+
+ );
+}
diff --git a/applications/webapp/src/Pages/User/context.tsx b/applications/webapp/src/Pages/User/context.tsx
index a69d736c..8baa7788 100644
--- a/applications/webapp/src/Pages/User/context.tsx
+++ b/applications/webapp/src/Pages/User/context.tsx
@@ -12,15 +12,8 @@
import { createContext, useContext } from "react";
-import { Card, User } from "../interface";
-import { ApolloError } from "@apollo/client";
-
-type UserContextType = {
- user: User;
- loading: boolean;
- error: ApolloError | undefined;
-};
-
+import { Card } from "../interface";
+import { UserContextType } from "../../hooks/util/types";
export const UserContext = createContext(
undefined,
);
diff --git a/applications/webapp/src/Pages/User/test/profileHolder.test.tsx b/applications/webapp/src/Pages/User/test/profileHolder.test.tsx
index dbdc9eee..f47b5c02 100644
--- a/applications/webapp/src/Pages/User/test/profileHolder.test.tsx
+++ b/applications/webapp/src/Pages/User/test/profileHolder.test.tsx
@@ -17,7 +17,7 @@ import ProfileHolder from "../components/profileHolder";
import { UserPageContextProvider } from "../components/ContextProvider/UserPageContextProvider";
import { CardContextProvider } from "../components/ContextProvider/CardContextProvider";
import { MockedProvider } from "@apollo/client/testing";
-import { GET_USER_QUERY } from "../../../hooks/query";
+import { GET_USER_QUERY } from "../../../hooks/util/query";
describe("ProfileHolder", () => {
const mockData = {
diff --git a/applications/webapp/src/Pages/User/test/userPage.test.tsx b/applications/webapp/src/Pages/User/test/userPage.test.tsx
index f3ff9e8c..550559b2 100644
--- a/applications/webapp/src/Pages/User/test/userPage.test.tsx
+++ b/applications/webapp/src/Pages/User/test/userPage.test.tsx
@@ -14,7 +14,7 @@ import React from "react";
import { render, fireEvent, screen, waitFor } from "@testing-library/react";
import UserPage from "../userPage";
import { MockedProvider } from "@apollo/client/testing";
-import { GET_USER_QUERY } from "../../../hooks/query";
+import { GET_USER_QUERY } from "../../../hooks/util/query";
import { BrowserRouter } from "react-router-dom";
describe("UserPage", () => {
diff --git a/applications/webapp/src/Pages/User/userPage.tsx b/applications/webapp/src/Pages/User/userPage.tsx
index 49f8b56b..a12c40b2 100644
--- a/applications/webapp/src/Pages/User/userPage.tsx
+++ b/applications/webapp/src/Pages/User/userPage.tsx
@@ -15,32 +15,20 @@ import { Box, useTheme } from "@mui/material";
import ProfileHolder from "./components/profileHolder";
import DetailHolder from "./components/detailHolder";
import Overlay from "./components/overlay";
-import { styles } from "./util/style";
import { UserPageContextProvider } from "./components/ContextProvider/UserPageContextProvider";
import { CardContextProvider } from "./components/ContextProvider/CardContextProvider";
+import { styles } from "./util/style";
export default function UserPage() {
const theme = useTheme();
const style = styles(theme).userPage;
-
return (
-
-
-
-
-
-
-
-
+
+
+
diff --git a/applications/webapp/src/Pages/User/util/constants.ts b/applications/webapp/src/Pages/User/util/constants.ts
index 0db1d5cc..0d567574 100644
--- a/applications/webapp/src/Pages/User/util/constants.ts
+++ b/applications/webapp/src/Pages/User/util/constants.ts
@@ -10,5 +10,25 @@
* LICENSE file in the root directory of this source tree.
*/
+import { User } from "../../../Pages/interface";
+
export const CHANGE_PASSWORD_URL =
"https://dev.supply-trail.humanitech.net/auth/realms/humanitech/protocol/openid-connect/auth?response_type=code&client_id=supply-trail-app&kc_action=UPDATE_PASSWORD";
+
+export const labels: Partial> = {
+ firstName: "First Name",
+ lastName: "Last Name",
+ email: "Email",
+ phoneNumber: "Phone Number",
+ address: "Address",
+ birthdate: "Birth Date",
+};
+
+export const fields: Array = [
+ "firstName",
+ "lastName",
+ "email",
+ "phoneNumber",
+ "address",
+ "birthdate",
+];
diff --git a/applications/webapp/src/Pages/User/util/style.ts b/applications/webapp/src/Pages/User/util/style.ts
index 30e146cf..b4f8e995 100644
--- a/applications/webapp/src/Pages/User/util/style.ts
+++ b/applications/webapp/src/Pages/User/util/style.ts
@@ -69,12 +69,11 @@ export const styles = (theme: Theme) => ({
userPage: {
userPageContainer: {
display: "flex",
- flexDirection: ["column", "column", "row"],
+ flexDirection: { xs: "column", md: "row" },
margin: "0 20px 0 20px",
},
profilePageHolder: {
display: "grid",
- gridTemplateRows: ["auto", "auto", "repeat(2, 0.2fr)"],
gap: [minGap, minGap, maxGap],
width: ["100%", "100%", "30%"],
margin: ["0 0 20px", "0 0 20px", "0 20px 0 0"],
diff --git a/applications/webapp/src/hooks/types.ts b/applications/webapp/src/hooks/types.ts
deleted file mode 100644
index ed4f7754..00000000
--- a/applications/webapp/src/hooks/types.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Humanitech Supply Trail
- *
- * Copyright (c) Humanitech, Peter Rogov and Contributors
- *
- * Website: https://humanitech.net
- * Repository: https://github.com/humanitech-net/supply-trail
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import Joi from "joi";
-import { DocumentNode } from "@apollo/client";
-
-export type UserData = {
- getUser: {
- id: string;
- username: string;
- firstName: string;
- lastName: string;
- email: string;
- };
-};
-export type UserQueryVariables = Record;
-
-export type QueryConfigs = {
- GET_USER: {
- query: DocumentNode;
- data: UserData;
- variables?: UserQueryVariables;
- schema: Joi.ObjectSchema;
- };
-};
diff --git a/applications/webapp/src/hooks/useGenericMutation.ts b/applications/webapp/src/hooks/useGenericMutation.ts
new file mode 100644
index 00000000..ac8028a7
--- /dev/null
+++ b/applications/webapp/src/hooks/useGenericMutation.ts
@@ -0,0 +1,38 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { ApolloError, useMutation } from "@apollo/client";
+import { MutationConfigs } from "./util/types";
+import { mutationConfigs } from "./util/configs";
+
+export function useGenericMutation(
+ mutationType: T,
+): {
+ callMutation: (
+ mutationVariables: MutationConfigs[T]["variables"],
+ ) => Promise;
+ data: MutationConfigs[T]["data"];
+ loading: boolean;
+ error: ApolloError | undefined;
+} {
+ const config = mutationConfigs[mutationType];
+ const [mutate, { data, loading, error }] = useMutation(config.mutation);
+
+ const callMutation = async (
+ mutationVariables: MutationConfigs[T]["variables"],
+ ) => {
+ mutate({
+ variables: mutationVariables,
+ });
+ };
+ return { callMutation, data, loading, error };
+}
diff --git a/applications/webapp/src/hooks/useGenericQuery.ts b/applications/webapp/src/hooks/useGenericQuery.ts
index 8420b5af..d33015ae 100644
--- a/applications/webapp/src/hooks/useGenericQuery.ts
+++ b/applications/webapp/src/hooks/useGenericQuery.ts
@@ -10,8 +10,8 @@
* LICENSE file in the root directory of this source tree.
*/
-import { queryConfigs } from "./configs";
-import { QueryConfigs } from "./types";
+import { queryConfigs } from "./util/configs";
+import { QueryConfigs } from "./util/types";
import { ApolloError, useQuery } from "@apollo/client";
export default function useGenericQuery(
diff --git a/applications/webapp/src/hooks/useUserEntry.ts b/applications/webapp/src/hooks/useUserEntry.ts
new file mode 100644
index 00000000..1eb045b5
--- /dev/null
+++ b/applications/webapp/src/hooks/useUserEntry.ts
@@ -0,0 +1,42 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { useMemo } from "react";
+import { useGenericMutation } from "./useGenericMutation";
+import { EditUserVariable } from "./util/types";
+
+export default function useUserEntry() {
+ const { callMutation, data, loading, error } =
+ useGenericMutation("EDIT_USER");
+
+ const update = async (newUserData: EditUserVariable) => {
+ callMutation(newUserData);
+ };
+
+ const { username, firstName, lastName } = data?.editUser ?? {};
+
+ const user = useMemo(
+ () => ({
+ username: username ?? "",
+ firstName: firstName ?? "",
+ lastName: lastName ?? "",
+ email: "email",
+ birthdate: "birthdate",
+ address: "address",
+ phoneNumber: "123456789",
+ description: `I am ${firstName}`,
+ }),
+ [username, firstName, lastName],
+ );
+
+ return { user, loading, error, update };
+}
diff --git a/applications/webapp/src/hooks/configs.ts b/applications/webapp/src/hooks/util/configs.ts
similarity index 62%
rename from applications/webapp/src/hooks/configs.ts
rename to applications/webapp/src/hooks/util/configs.ts
index acf5ca0f..697c4588 100644
--- a/applications/webapp/src/hooks/configs.ts
+++ b/applications/webapp/src/hooks/util/configs.ts
@@ -10,9 +10,16 @@
* LICENSE file in the root directory of this source tree.
*/
+import { EditUserMutation } from "./mutation";
import { GET_USER_QUERY } from "./query";
import { userSchema } from "./schema";
-import { QueryConfigs, UserData } from "./types";
+import {
+ MutationConfigs,
+ QueryConfigs,
+ UserData,
+ EditUserVariable,
+ EditData,
+} from "./types";
export const queryConfigs: QueryConfigs = {
GET_USER: {
@@ -21,3 +28,11 @@ export const queryConfigs: QueryConfigs = {
data: {} as UserData,
},
};
+
+export const mutationConfigs: MutationConfigs = {
+ EDIT_USER: {
+ mutation: EditUserMutation,
+ data: {} as EditData,
+ variables: {} as EditUserVariable,
+ },
+};
diff --git a/applications/webapp/src/hooks/mutation.ts b/applications/webapp/src/hooks/util/mutation.ts
similarity index 83%
rename from applications/webapp/src/hooks/mutation.ts
rename to applications/webapp/src/hooks/util/mutation.ts
index 62a1b412..7e544143 100644
--- a/applications/webapp/src/hooks/mutation.ts
+++ b/applications/webapp/src/hooks/util/mutation.ts
@@ -14,6 +14,10 @@ import { gql } from "@apollo/client";
export const EditUserMutation = gql`
mutation EditUser($userInput: UpdateUser!) {
- editUser(userInput: $userInput)
+ editUser(userInput: $userInput) {
+ username
+ firstName
+ lastName
+ }
}
`;
diff --git a/applications/webapp/src/hooks/query.ts b/applications/webapp/src/hooks/util/query.ts
similarity index 100%
rename from applications/webapp/src/hooks/query.ts
rename to applications/webapp/src/hooks/util/query.ts
diff --git a/applications/webapp/src/hooks/schema.ts b/applications/webapp/src/hooks/util/schema.ts
similarity index 100%
rename from applications/webapp/src/hooks/schema.ts
rename to applications/webapp/src/hooks/util/schema.ts
diff --git a/applications/webapp/src/hooks/util/types.ts b/applications/webapp/src/hooks/util/types.ts
new file mode 100644
index 00000000..c0d17d6b
--- /dev/null
+++ b/applications/webapp/src/hooks/util/types.ts
@@ -0,0 +1,73 @@
+/**
+ * Humanitech Supply Trail
+ *
+ * Copyright (c) Humanitech, Peter Rogov and Contributors
+ *
+ * Website: https://humanitech.net
+ * Repository: https://github.com/humanitech-net/supply-trail
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import Joi from "joi";
+import { DocumentNode, ApolloError } from "@apollo/client";
+import { User } from "../../Pages/interface";
+
+export type UserData = {
+ getUser: {
+ id: string;
+ username: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ };
+};
+export type UserQueryVariables = Record;
+
+export type QueryConfigs = {
+ GET_USER: {
+ query: DocumentNode;
+ data: UserData;
+ variables?: UserQueryVariables;
+ schema: Joi.ObjectSchema;
+ };
+};
+
+export type EditUserVariable = {
+ userInput: {
+ username: string;
+ firstName: string;
+ lastName: string;
+ };
+};
+
+export type MutationConfigs = {
+ EDIT_USER: {
+ mutation: DocumentNode;
+ data: EditData;
+ variables: EditUserVariable;
+ };
+};
+
+export type UserContextType = {
+ user: User;
+ loading: boolean;
+ error: ApolloError | undefined;
+ update: (newUserData: EditUserVariable) => Promise;
+ setUserUpdated: React.Dispatch>;
+};
+
+export type EditData = {
+ editUser: {
+ id?: string;
+ username: string;
+ firstName: string;
+ lastName: string;
+ email?: string;
+ };
+};
+
+export type UserInput = {
+ userInput: { username: string; firstName: string; lastName: string };
+};