prototyping the remainder of user reg workflow; some progress on login after register
This commit is contained in:
@@ -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 />} />
|
||||
|
||||
@@ -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;
|
||||
49
client/src/components/pages/Register/addfriends.tsx
Normal file
49
client/src/components/pages/Register/addfriends.tsx
Normal 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;
|
||||
72
client/src/components/pages/Register/collection.tsx
Normal file
72
client/src/components/pages/Register/collection.tsx
Normal 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;
|
||||
29
client/src/components/pages/Register/finishup.tsx
Normal file
29
client/src/components/pages/Register/finishup.tsx
Normal 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
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user