working on profile

This commit is contained in:
Mikayla Dobson
2023-02-13 18:13:01 -06:00
parent bd282ce2bb
commit 7bd29e4dba
5 changed files with 206 additions and 93 deletions

View File

@@ -1,27 +1,47 @@
import { useEffect } from "react";
import { FC, useEffect, useState } from "react";
import { v4 } from "uuid";
import { useAuthContext } from "../../context/AuthContext"
import useDateFormat from "../../hooks/useDateFormat";
import { ICollection } from "../../schemas";
import API from "../../util/API";
import { Card } from "../ui"
function CollectionList() {
type CollectionListType = FC<{ targetID?: number | string }>
const CollectionList: CollectionListType = ({ targetID = null }) => {
const [collections, setCollections] = useState<ICollection[]>();
const [author, setAuthor] = useState<string>();
const { user, token } = useAuthContext();
useEffect(() => {
if (user && token) {
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored();
console.log(result);
})();
if (targetID) {
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored(targetID);
setCollections(result);
})();
} else {
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored();
setCollections(result);
})();
}
}
}, [user])
return (
<Card>
{ collections && collections.map(each =>
<div className="collection-item" key={v4()}>
<h2>{each.name}</h2>
{ targetID && <p>Created by {author}</p>}
<p>Created {useDateFormat(each.datecreated)}</p>
</div>
)}
</Card>
)
}
export default CollectionList

View File

@@ -1,34 +1,71 @@
import { useEffect, useState } from "react";
import { IUser } from "../../schemas";
import { useNavigate } from "react-router-dom";
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 { useEffect, useState } from "react";
import { AxiosError } from "axios";
import { useAuthContext } from "../../context/AuthContext";
import API from "../../util/API";
import Protect from "../../util/Protect";
import { ICollection, IUser } from "../../schemas";
import Friends from "../derived/Friends";
import CollectionList from "../derived/CollectionList";
import { Button, Divider, Page, Panel } from "../ui";
import useDateFormat from "../../hooks/useDateFormat";
interface ProfileMetadata {
targetID?: string | number
targetUser?: IUser | undefined
formattedDate: string
collections: ICollection[]
friends: IUser[]
isSet: boolean
}
export default function Profile() {
const [contents, setContents] = useState<JSX.Element>(<></>);
const [targetUser, setTargetUser] = useState<IUser>();
// globals and router utils
const { user, token } = useAuthContext();
const navigate = useNavigate();
const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: "long" });
// UI state info
const [contents, setContents] = useState<JSX.Element>(<></>);
// master state for this page
const [metadata, setMetadata] = useState<ProfileMetadata>({
targetID: undefined,
targetUser: undefined,
formattedDate: "",
collections: [],
friends: [],
isSet: false
});
const dateFormatter = new Intl.DateTimeFormat('en-US', { timeStyle: undefined, dateStyle: "long" });
// STEP 1: FETCH METADATA (requires token)
useEffect(() => {
if (!token) return;
if (!token || !user) return;
const params = new URLSearchParams(window.location.search);
const targetID = params.get('id');
// if a target is specified in the url
if (targetID) {
setMetadata((prev: ProfileMetadata) => {
return { ...prev, targetID: targetID }
});
// fetch and store user data with associated user id
(async() => {
try {
const User = new API.User(token);
const result = await User.getByID(targetID);
if (result) setTargetUser(result);
if (result) {
setMetadata((prev) => {
return {
...prev,
targetUser: result,
formattedDate: useDateFormat(result.datecreated)
}
})
}
} catch (error) {
if (error instanceof AxiosError) {
if (error?.response?.status == 404) {
@@ -43,76 +80,120 @@ export default function Profile() {
</Panel>
</Page>
)
return;
}
} else {
console.error(error);
}
}
})();
// do the same for this user's collections and friends
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored(metadata.targetID);
if (result) {
setMetadata((prev: ProfileMetadata) => {
return {
...prev, collections: result
}
})
}
})();
} else {
const formattedDate = user!.datecreated ? dateFormatter.format(new Date(user!.datecreated)) : "(unknown)";
// otherwise, this is the current user's profile and should load some slightly different info
(async() => {
const Collections = new API.Collection(token);
const result = await Collections.getAllAuthored();
if (result) {
setMetadata((prev: ProfileMetadata) => {
return {
...prev, collections: result
}
})
}
})();
setContents(
<Protect redirect="profile">
<div className="profile-authenticated">
<h1>{user!.firstname}'s Profile</h1>
(async() => {
const Friends = new API.Friendship(token);
})
<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>
)
setMetadata((prev) => {
return {
...prev,
formattedDate: useDateFormat(user.datecreated)
}
})
}
}, [token])
setMetadata((prev) => {
return { ...prev, isSet: true }
})
}, [token]);
// STEP 2: set up page UI based on profile config above
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>
if (metadata.isSet) {
if (metadata.targetUser) {
setContents(
<Protect redirect="/">
<div className="profile-authenticated">
<h1>{metadata.targetUser.firstname}'s Profile</h1>
<div className="profile-grid">
<Panel>
<h2>About {metadata.targetUser.firstname} {metadata.targetUser.lastname}:</h2>
<p>Recipin Member since: {metadata.formattedDate}</p>
</Panel>
<Panel>
<h2>My collections:</h2>
<CollectionList targetID={metadata.targetUser.id} />
</Panel>
<Panel>
<h2>My friends:</h2>
<Friends />
</Panel>
</div>
</div>
</div>
</Protect>
)
</Protect>
)
} else {
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: {metadata.formattedDate}</p>
<Divider />
<p>30 recipes</p>
<p>2 collections</p>
</Panel>
<Panel>
{/* include number of collections */}
<h2>My collections ({ metadata.collections.length || 0 }):</h2>
<CollectionList />
</Panel>
<Panel>
<h2>My friends:</h2>
<Friends />
</Panel>
</div>
</div>
</Protect>
)
}
}
}, [targetUser]);
}, [metadata])
// STEP 3: mount the UI
return contents
}

View File

@@ -0,0 +1,10 @@
function useDateFormat(input: string | undefined) {
if (typeof input == 'undefined') return "unknown";
const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: "long" });
const output = dateFormatter.format(new Date(input));
return output;
}
export default useDateFormat

View File

@@ -1,4 +1,4 @@
import { AxiosHeaders, AxiosRequestHeaders } from "axios";
import { AxiosHeaders, AxiosRequestHeaders, AxiosResponse } from "axios";
import { IUser, IUserAuth, IFriendship, IRecipe, IIngredient, ICollection, IGroceryList } from "../schemas";
import { default as _instance } from "./axiosInstance";
@@ -165,8 +165,15 @@ module API {
super(Settings.getAPISTRING() + "/app/collection", token);
}
async getAllAuthored() {
const response = await this.customRoute(CRUDMETHOD.GET, "?authored=true");
async getAllAuthored(id?: number | string) {
let response: AxiosResponse;
if (id) {
response = await this.customRoute(CRUDMETHOD.GET, `?authored=true&authorID=${id}`);
} else {
response = await this.customRoute(CRUDMETHOD.GET, "?authored=true");
}
return Promise.resolve(response.data);
}
}

View File

@@ -2,6 +2,7 @@ import { Express, Router } from "express";
import { checkIsAdmin, restrictAccess } from "../auth/middlewares";
import CollectionCtl from "../controllers/CollectionCtl";
import { IUser } from "../schemas";
import { StatusCode } from "../util/types";
const CollectionInstance = new CollectionCtl();
const router = Router();
@@ -19,30 +20,24 @@ export const collectionRoute = (app: Express) => {
}
})
router.get('&authored=true', restrictAccess, async (req, res, next) => {
const user = req.user as IUser;
console.log(user.id);
try {
const { code, data } = await CollectionInstance.getAllAuthored(user.id as number);
res.status(code).send(data);
} catch (e) {
next(e);
}
})
// implement is admin on this route
router.get('/', restrictAccess, async (req, res, next) => {
const user = req.user as IUser;
const { authored } = req.query;
const { authored, authorID } = req.query;
try {
if (authored || authored == "true") {
if (authorID) {
const { code, data } = await CollectionInstance.getAllAuthored(parseInt(authorID as string));
res.status(code).send(data);
} else if (authored || authored == "true") {
const { code, data } = await CollectionInstance.getAllAuthored(user.id as number);
res.status(code).send(data);
} else {
if (user.isadmin) {
const { code, data } = await CollectionInstance.getAll();
res.status(code).send(data);
} else {
res.status(403).send("Unauthorized");
}
}
} catch(e) {