frontend api refactoring, req.user handling on backend

This commit is contained in:
Mikayla Dobson
2023-02-13 17:13:37 -06:00
parent 1b32ac38d1
commit bd282ce2bb
15 changed files with 241 additions and 78 deletions

View File

@@ -0,0 +1,27 @@
import { useEffect } from "react";
import { useAuthContext } from "../../context/AuthContext"
import API from "../../util/API";
import { Card } from "../ui"
function CollectionList() {
const { user, token } = useAuthContext();
useEffect(() => {
if (user && token) {
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored();
console.log(result);
})();
}
}, [user])
return (
<Card>
</Card>
)
}
export default CollectionList

View File

@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { v4 } from "uuid";
import { useAuthContext } from "../../context/AuthContext";
import { getAllUsers, getFriendships, getPendingFriendRequests, getUserByID } from "../../util/apiUtils";
import API from "../../util/API";
import UserCard from "../ui/UserCard";
import { IUser, IFriendship } from "../../schemas";
import { Card, Divider, Panel } from "../ui";
@@ -10,27 +11,32 @@ import FriendSearchWidget from "../ui/Widgets/FriendSearchWidget";
export default function Friends() {
const [friends, setFriends] = useState<IFriendship[]>();
const [userList, setUserList] = useState(new Array<IUser>());
const { user } = useAuthContext();
const { user, token } = useAuthContext();
useEffect(() => {
if (!user) return;
if (!user || !token) return;
(async function() {
try {
const rawResult = await getFriendships();
const Friends = new API.Friendship(token);
const result = await Friends.getAll();
if (rawResult.length) {
const result = rawResult.filter((item: IFriendship) => (item.senderid == user.id) && !(item.pending));
if (result.length) {
setFriends(result);
}
} catch(e) {
console.error(e);
console.log(result);
} catch (error) {
console.error(error);
}
})()
}, [user])
})();
}, [])
useEffect(() => {
friends && friends.map(async (friend: IFriendship) => {
const userData = await getUserByID(friend.targetid);
if (!token || !friends) return;
friends.map(async (friend: IFriendship) => {
const Friends = new API.Friendship(token);
const userData = await Friends.getByID(friend.targetid as string);
if (userData) setUserList((prev: IUser[]) => {
return [...prev, userData]
})
@@ -45,14 +51,14 @@ export default function Friends() {
<>
{ userList.length ?
(
<Panel extraStyles="flex-row">
<Card extraStyles="flex-row">
<h2>Your friendships:</h2>
{
userList.map((user: IUser) => {
return <UserCard key={v4()} user={user} />
})
}
</Panel>
</Card>
) :
(
<Card>

View File

@@ -1,22 +1,118 @@
import { useContext, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { IUser } from "../../schemas";
import { useNavigate } from "react-router-dom";
import { AuthContext, useAuthContext } from "../../context/AuthContext";
import { Button, Page } from "../ui";
import { useAuthContext } from "../../context/AuthContext";
import { Button, Divider, Page, Panel } from "../ui";
import Protect from "../../util/Protect";
import Friends from "../derived/Friends";
import API from "../../util/API";
import { AxiosError } from "axios";
import CollectionList from "../derived/CollectionList";
export default function Profile() {
const [message, setMessage] = useState<JSX.Element>();
const { user } = useAuthContext();
const [contents, setContents] = useState<JSX.Element>(<></>);
const [targetUser, setTargetUser] = useState<IUser>();
const { user, token } = useAuthContext();
const navigate = useNavigate();
return (
<Protect redirect="profile">
<div className="profile-authenticated">
<h1>{user && user.firstname}'s Profile</h1>
<Friends />
</div>
</Protect>
)
const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: "long" });
useEffect(() => {
if (!token) return;
const params = new URLSearchParams(window.location.search);
const targetID = params.get('id');
if (targetID) {
(async() => {
try {
const User = new API.User(token);
const result = await User.getByID(targetID);
if (result) setTargetUser(result);
} catch (error) {
if (error instanceof AxiosError) {
if (error?.response?.status == 404) {
// to do: replace with customizable 404 page
setContents(
<Page>
<h1>404: Not found</h1>
<Divider />
<Panel>
<p>No user found with ID {targetID}</p>
<Button onClick={() => navigate('/')}>Home</Button>
</Panel>
</Page>
)
}
} else {
console.error(error);
}
}
})();
} else {
const formattedDate = user!.datecreated ? dateFormatter.format(new Date(user!.datecreated)) : "(unknown)";
setContents(
<Protect redirect="profile">
<div className="profile-authenticated">
<h1>{user!.firstname}'s Profile</h1>
<div className="profile-grid">
<Panel>
<h2>About me:</h2>
<p>{user!.firstname} {user!.lastname}</p>
<p>Recipin Member since: {formattedDate}</p>
<Divider />
<p>30 recipes</p>
<p>2 collections</p>
</Panel>
<Panel>
{/* include number of collections */}
<h2>My collections:</h2>
<CollectionList />
</Panel>
<Panel>
<h2>My friends:</h2>
<Friends />
</Panel>
</div>
</div>
</Protect>
)
}
}, [token])
useEffect(() => {
if (targetUser) {
const formattedDate = targetUser.datecreated ? dateFormatter.format(new Date(targetUser.datecreated)) : "(unknown)";
setContents(
<Protect redirect="/">
<div className="profile-authenticated">
<h1>{targetUser.firstname}'s Profile</h1>
<div className="profile-grid">
<Panel>
<h2>About {targetUser.firstname} {targetUser.lastname}:</h2>
<p>Recipin Member since: {formattedDate}</p>
</Panel>
<Panel>
<h2>My collections:</h2>
</Panel>
<Panel>
<h2>My friends:</h2>
<Friends />
</Panel>
</div>
</div>
</Protect>
)
}
}, [targetUser]);
return contents
}

View File

@@ -1,10 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { v4 } from "uuid";
import { RegisterVariantType, VariantLabel } from ".";
import { useAuthContext } from "../../../context/AuthContext";
import { IUser, IUserAuth } from "../../../schemas";
import { attemptLogin, attemptRegister } from "../../../util/apiUtils";
import { IUser } from "../../../schemas";
import API from "../../../util/API";
import { Button, Page, Panel } from "../../ui";
import Divider from "../../ui/Divider";

View File

@@ -1,11 +1,9 @@
import { ChangeEvent, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { RegisterVariantType, VariantLabel } from ".";
import { useNow } from "../../../hooks/useNow";
import { ICollection, IUser, IUserAuth } from "../../../schemas";
import { attemptLogin, createNewCollection } from "../../../util/apiUtils";
import { ICollection } from "../../../schemas";
import API from "../../../util/API";
import { Button, Divider, Page, Panel } from "../../ui";
import TextField from "../../ui/TextField";
import { useAuthContext } from "../../../context/AuthContext";
const InitialCollection: RegisterVariantType = ({ transitionDisplay, input }) => {
@@ -28,10 +26,7 @@ const InitialCollection: RegisterVariantType = ({ transitionDisplay, input }) =>
datemodified: now
}
console.log(collection);
const result = await collectionAPI.post(collection);
console.log(result);
if (result) transitionDisplay(VariantLabel.AddFriends);
}
@@ -50,7 +45,7 @@ const InitialCollection: RegisterVariantType = ({ transitionDisplay, input }) =>
<h3>What would you like to call your main collection?</h3>
{/* <TextField onChange={(e: ChangeEvent<HTMLInputElement>) => setCollectionName(e.target.value)} placeholder={user.firstname + 's Collection'} /> */}
<input type="text" onChange={(e) => setCollectionName(e.target.value)} placeholder={user.firstname + 's Collection'}></input>
<input type="text" onChange={(e) => setCollectionName(e.target.value)} placeholder={user.firstname + '\'s Collection'}></input>
</Panel>
<Button onClick={handleClick}>Next</Button>

View File

@@ -1,4 +1,4 @@
import { FC, useEffect, useState } from "react";
import { FC, useState } from "react";
import { useAuthContext } from "../../../context/AuthContext";
import { IUser } from "../../../schemas";
import AboutYou from "./aboutyou";

View File

@@ -1,13 +1,11 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useAuthContext } from "../../context/AuthContext";
import { attemptLogout, checkCredientials } from "../../util/apiUtils";
import { Button, Page, Panel } from "../ui"
import Divider from "../ui/Divider";
const Welcome = () => {
const navigate = useNavigate();
const { user, setUser } = useAuthContext();
const { user } = useAuthContext();
const authUserActions = (
<Panel extraStyles="inherit-background c-papyrus uppercase flexrow">
@@ -21,7 +19,6 @@ const Welcome = () => {
<Panel extraStyles="inherit-background c-papyrus uppercase">
<h2>Ready to get started?</h2>
<Button onClick={() => navigate('/register')}>Register</Button>
<Button onClick={attemptLogout}>Log Out</Button>
</Panel>
)
@@ -41,7 +38,6 @@ const Welcome = () => {
<Panel extraStyles="inherit-background c-papyrus uppercase">
<h2>Build Shopping Lists Directly from Your Recipes</h2>
<button onClick={checkCredientials}></button>
</Panel>
<Divider />

View File

@@ -1,7 +1,6 @@
import API from "../../../util/API";
import { NavbarType } from "../../../util/types";
import { Button, Dropdown } from '..'
import { useEffect, useState } from "react";
import { useState } from "react";
import { useAuthContext } from "../../../context/AuthContext";
import { useNavigate } from "react-router-dom";
@@ -14,8 +13,7 @@ const LoggedIn = () => {
const [searchActive, setSearchActive] = useState(false);
const handleLogout = async () => {
const success = await auth.logout();
console.log(success);
await auth.logout();
// nullify cookie and unset user/token data
document.cookie = `token=;expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
@@ -40,10 +38,6 @@ const LoggedIn = () => {
navigate(payload);
}
useEffect(() => {
console.log(user);
}, [])
return (
<div>
<div id="navbar">

View File

@@ -3,6 +3,13 @@ import { IUser, IUserAuth, IFriendship, IRecipe, IIngredient, ICollection, IGroc
import { default as _instance } from "./axiosInstance";
module API {
export enum CRUDMETHOD {
GET,
PUT,
POST,
DELETE
}
export class Settings {
private static APISTRING = import.meta.env.APISTRING || "http://localhost:8080";
private static token?: string;
@@ -23,7 +30,7 @@ module API {
abstract class RestController<T> {
protected instance = _instance;
protected endpoint: string;
protected headers?: any
protected headers?: any;
constructor(endpoint: string, token: string) {
this.endpoint = endpoint;
@@ -35,6 +42,19 @@ module API {
};
}
async customRoute(method: CRUDMETHOD, path: string, data?: any, requireHeaders = true) {
switch (method) {
case CRUDMETHOD.GET:
return this.instance.get(this.endpoint + path, (requireHeaders && this.headers));
case CRUDMETHOD.PUT:
return this.instance.put(this.endpoint + path, data, (requireHeaders && this.headers));
case CRUDMETHOD.POST:
return this.instance.post(this.endpoint + path, data, (requireHeaders && this.headers));
case CRUDMETHOD.DELETE:
return this.instance.delete(this.endpoint + path, (requireHeaders && this.headers));
}
}
async getAll() {
const response = await this.instance.get(this.endpoint, this.headers);
return Promise.resolve(response.data);
@@ -144,6 +164,11 @@ module API {
constructor(token: string) {
super(Settings.getAPISTRING() + "/app/collection", token);
}
async getAllAuthored() {
const response = await this.customRoute(CRUDMETHOD.GET, "?authored=true");
return Promise.resolve(response.data);
}
}
export class GroceryList extends RestController<IGroceryList> {