prototyping the remainder of user reg workflow; some progress on login after register

This commit is contained in:
Mikayla Dobson
2022-12-01 20:59:01 -06:00
parent 671e250c60
commit 8a7eaa7db6
16 changed files with 283 additions and 84 deletions

View File

@@ -1,6 +1,7 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { AuthContext, IAuthContext } from './context/AuthContext';
import { useCallback, useEffect, useState } from 'react';
import { AuthContext, IAuthContext, useAuthContext } from './context/AuthContext';
import { IUser } from './schemas';
import { checkCredientials } from './util/apiUtils';
import Subscriptions from './components/pages/Subscriptions/Subscriptions';
import Browser from './components/pages/Browser';
@@ -11,9 +12,16 @@ import Recipe from './components/pages/Recipe';
import Register from './components/pages/Register';
import Welcome from './components/pages/Welcome';
import './sass/App.scss'
import { Navbar } from './components/ui';
function App() {
const [user, setUser] = useState<IAuthContext>({ user: undefined });
const authContext = useAuthContext();
const receiveChange = useCallback((change: IUser) => {
console.log(change);
authContext.user = change;
}, [])
useEffect(() => {
const wrapper = async () => {
@@ -29,13 +37,18 @@ function App() {
wrapper();
}, [])
useEffect(() => {
console.log(authContext);
}, [authContext]);
return (
<BrowserRouter>
<AuthContext.Provider value={ user }>
<div className="App">
<Navbar receiveChange={receiveChange} />
<Routes>
<Route path="/" element={<Welcome />} />
<Route path="/register" element={<Register />} />
<Route path="/register" element={<Register receiveChange={receiveChange} />} />
<Route path="/login" element={<Login />} />
<Route path="/profile" element={<Profile />} />
<Route path="/collection" element={<Collection />} />

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, 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";
@@ -18,7 +19,7 @@ const blankUser: IUser = {
isadmin: false
}
export default function AboutYou() {
const AboutYou: RegisterVariantType = ({ transitionDisplay }) => {
const navigate = useNavigate();
const authContext = useAuthContext();
const [form, setForm] = useState<JSX.Element[]>([<p key={v4()}>Loading content...</p>]);
@@ -40,16 +41,17 @@ export default function AboutYou() {
async function handleRegister() {
const res = await attemptRegister(input);
console.log(res);
setRegSuccess(res);
if (res.ok) {
transitionDisplay(VariantLabel.InitialCollection, input);
}
}
async function unwrapLogin() {
const data: IUserAuth = { email: input.email, password: input.password || "" }
console.log(data);
const login = await attemptLogin(data);
console.log(login);
authContext.user = login.user;
if (login) {
authContext.user = login.user;
}
navigate('/');
}
@@ -75,4 +77,6 @@ export default function AboutYou() {
</Panel>
</Page>
)
}
}
export default AboutYou;

View File

@@ -0,0 +1,49 @@
import { ChangeEvent, useEffect, useState } from "react";
import { RegisterVariantType, VariantLabel } from ".";
import { IUser } from "../../../schemas";
import { Button, Divider, Page, Panel, TextField, UserCard } from "../../ui";
const AddFriends: RegisterVariantType = ({ transitionDisplay }) => {
const [searchTerm, setSearchTerm] = useState<string>();
const [friendResults, setFriendResults] = useState<IUser[]>();
const handleClick = async () => {
transitionDisplay(VariantLabel.FinishUp);
}
useEffect(() => {
// run search when state changes and store it in friendresults
console.log(searchTerm);
}, [searchTerm])
return (
<Page>
<h1>Cool, we'll keep all the recipes you post in that collection.</h1>
<Divider />
<p>You can access that any time by clicking on "Collections" in your menu bar.</p>
<p>One last thing, and you'll be good to go!</p>
<Panel>
<h2>If any of your friends already use Recipin, you can use the widget below to find them and add them!</h2>
<p>This will allow you to share recipes and collections back and forth, and leave comments on each other's recipes.</p>
<h3>If you know their email or unique handle, type it in below!</h3>
<div id="friend-search-widget">
<TextField onChange={(e: ChangeEvent<HTMLInputElement>) => setSearchTerm(e.target.value)} placeholder={'Search'} />
{
friendResults && friendResults.map((friend: IUser) => {
return <UserCard user={friend} />
})
}
</div>
</Panel>
<Button onClick={handleClick}>Finish</Button>
</Page>
)
}
export default AddFriends;

View File

@@ -0,0 +1,72 @@
import { ChangeEvent, 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 { Button, Divider, Page, Panel } from "../../ui";
import TextField from "../../ui/TextField";
const InitialCollection: RegisterVariantType = ({ transitionDisplay, receiveChange, input }) => {
const [collectionName, setCollectionName] = useState<string>();
const [view, setView] = useState<JSX.Element>(<Page><h1>Loading...</h1></Page>);
const [user, setUser] = useState<IUser>();
const now = useNow();
async function unwrapLogin(data: IUser) {
const userInfo: IUserAuth = { email: data.email, password: data.password! }
const login = await attemptLogin(userInfo);
setUser(login.user);
}
const handleClick = async () => {
if (!user) return;
const collection: ICollection = {
name: collectionName || (user.firstname + "'s Collection"),
active: true,
ismaincollection: true,
ownerid: user.id!.toString(),
datecreated: now,
datemodified: now
}
const result = await createNewCollection(collection);
console.log(result);
if (result) transitionDisplay(VariantLabel.AddFriends);
}
useEffect(() => {
if (input) {
setTimeout(() => {
unwrapLogin(input);
}, 2000);
}
}, [])
useEffect(() => {
if (user && receiveChange) {
receiveChange(user);
setView(
<Page>
<h1>Hi, {user.firstname}! Great to meet you.</h1>
<Divider />
<h2>Before we finish up here, just a couple more things.</h2>
<Panel>
<h3>First, let's get your very first recipe collection set up!</h3>
<p>This is where you'll store your own recipes as well as information about all of them, such as their cuisine, prep time, and ingredients used.</p>
<Divider />
<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'} />
</Panel>
<Button onClick={handleClick}>Next</Button>
</Page>
)
}
}, [user]);
return view;
}
export default InitialCollection;

View File

@@ -0,0 +1,29 @@
import { useNavigate } from "react-router-dom";
import { RegisterVariantType } from ".";
import { Button, Divider, Page, Panel } from "../../ui";
const FinishUp: RegisterVariantType = () => {
const navigate = useNavigate();
return (
<Page>
<h1>Awesome, you're good to go!</h1>
<Divider />
<h2>If you aren't sure where to start, here are some great things to try out first:</h2>
<Panel>
<ul>
<li><a>Enter some recipes!</a></li>
<li>Set up additional collections for your most frequented recipes</li>
<li>Tell your friends so you can share your favorites back and forth!</li>
<li>Use your collections to automatically generate grocery lists</li>
<li>Or, if you're a developer, <a>contribute to our source code!</a></li>
</ul>
</Panel>
<Button onClick={() => navigate('/')}>Get Started!</Button>
</Page>
)
}
export default FinishUp

View File

@@ -1,11 +1,50 @@
import { useState } from "react";
import { FC, useCallback, useState } from "react";
import { useAuthContext } from "../../../context/AuthContext";
import { IUser } from "../../../schemas";
import { Page } from "../../ui";
import AboutYou from "./register.aboutyou";
import AboutYou from "./aboutyou";
import AddFriends from "./addfriends";
import InitialCollection from "./collection";
import FinishUp from "./finishup";
export default function Register() {
const [displayed, setDisplayed] = useState<JSX.Element>(<AboutYou />);
export type RegisterVariantType = FC<{
transitionDisplay: ((variant: number, input?: IUser) => void),
receiveChange?: (change: IUser) => void
input?: IUser
}>
export enum VariantLabel {
AboutYou,
InitialCollection,
AddFriends,
FinishUp
}
const Register: FC<{receiveChange: (change: IUser) => void}> = ({ receiveChange }) => {
const [displayed, setDisplayed] = useState<JSX.Element>();
const authContext = useAuthContext();
return displayed;
}
const transitionDisplay = (variant: number | VariantLabel, user?: IUser) => {
switch (variant) {
case 0:
setDisplayed(<AboutYou transitionDisplay={transitionDisplay} />);
break;
case 1:
setDisplayed(<InitialCollection transitionDisplay={transitionDisplay} input={user} receiveChange={receiveChange} />);
break;
case 2:
setDisplayed(<AddFriends transitionDisplay={transitionDisplay} input={user} />);
break;
case 3:
setDisplayed(<FinishUp transitionDisplay={transitionDisplay} />);
break;
default:
setDisplayed(<AboutYou transitionDisplay={transitionDisplay} input={authContext.user || user} />);
break;
}
}
return displayed || <AboutYou transitionDisplay={transitionDisplay} />;
}
export default Register;

View File

@@ -1,11 +0,0 @@
import { Page } from "../../ui";
export default function AddFriends() {
let user = null;
return (
<Page>
<h1>Hi, {user || "Mikayla"}! Great to meet you.</h1>
</Page>
)
}

View File

@@ -1,11 +0,0 @@
import { Page } from "../../ui";
export default function InitialCollection() {
let user = null;
return (
<Page>
<h1>Hi, {user || "Mikayla"}! Great to meet you.</h1>
</Page>
)
}

View File

@@ -34,6 +34,7 @@ const Welcome = () => {
<Panel extraStyles='inherit-background c-papyrus uppercase'>
<h1>Welcome to Recipin</h1>
<Button onClick={unwrap}>Check Credentials</Button>
<Button onClick={() => console.log(authContext)}>Auth Context</Button>
<Button onClick={attemptLogout}>Log Out</Button>
</Panel>

View File

@@ -1,11 +1,11 @@
import { useCallback, useEffect, useState } from "react";
import { FC, useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { LoggedIn, NotLoggedIn, Registering } from "./variants";
import { useAuthContext } from "../../../context/AuthContext";
import { IUser } from "../../../schemas";
import "/src/sass/components/Navbar.scss";
const Navbar = () => {
const Navbar: FC<{receiveChange: (change: IUser) => void}> = ({ receiveChange }) => {
// setup and local state
const navigate = useNavigate();
const authContext = useAuthContext();
@@ -30,6 +30,7 @@ const Navbar = () => {
}, [authContext])
useEffect(() => {
if (received) receiveChange(received);
setDisplayed(received ? variants.loggedin : variants.notloggedin);
}, [received, setReceived]);

View File

@@ -5,7 +5,6 @@ import "/src/sass/components/Page.scss";
const Page: PageComponent = ({ extraStyles, children }) => {
return (
<main id="view">
<Navbar />
<section className={`Page ${extraStyles || null}`}>
{ children || null }
</section>

View File

@@ -0,0 +1,20 @@
import { FC } from "react"
import { v4 } from 'uuid';
interface TextFieldParams {
onChange: (...params: any) => any
id?: string | number
label?: string
placeholder?: string
}
const TextField: FC<any> = ({ onChange, label, id, placeholder }) => {
return (
<>
{ label && <label htmlFor={''}>{label}</label>}
<input onChange={onChange} type="text" id={id || v4()} placeholder={placeholder || ''} />
</>
)
}
export default TextField;

View File

@@ -6,14 +6,8 @@ import Navbar from "./Navbar";
import Page from "./Page";
import Panel from "./Panel";
import UserCard from "./UserCard";
import TextField from "./TextField";
export {
Button,
Card,
Divider,
Form,
Navbar,
Page,
Panel,
UserCard
Button, Card, Divider, Form, Navbar, Page, Panel, UserCard, TextField
}

View File

@@ -1,4 +1,4 @@
import { IUser, IUserAuth } from "../schemas";
import { ICollection, IUser, IUserAuth } from "../schemas";
import { IAuthContext } from "../context/AuthContext";
import axios from "axios";
const API = import.meta.env.APISTRING || "http://localhost:8080";
@@ -10,24 +10,7 @@ export const getBaseAPI = async () => {
return fetch(API);
}
// auth handlers
export const attemptLogin = async (data: IUserAuth): Promise<IAuthContext> => {
try {
const response = await axios({
method: "POST",
url: API + '/auth/login',
data: data
});
console.log(response);
const result = Promise.resolve(response.data);
return result;
} catch (e: any) {
throw e;
}
}
// auth and general user handlers
export const checkCredientials = async () => {
try {
const response = await axios({
@@ -41,6 +24,21 @@ export const checkCredientials = async () => {
}
}
export const attemptLogin = async (data: IUserAuth) => {
try {
const response = await axios({
method: "POST",
url: API + '/auth/login',
data: data
});
const result = Promise.resolve(response.data);
return result;
} catch (e: any) {
throw e;
}
}
export const attemptLogout = async () => {
try {
await axios({
@@ -68,6 +66,21 @@ export const attemptRegister = async (body: IUser) => {
}
}
// methods for managing collections
export const createNewCollection = async (body: ICollection) => {
try {
const response = await axios({
method: "POST",
url: API + '/collection',
data: JSON.stringify(body)
});
return Promise.resolve(response.data);
} catch (e: any) {
throw e;
}
}
// for user friendships
export const getFriendships = async () => {
try {