continuing to build on ui, starting on add recipe
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
// framework tools and custom utils
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||||
import { AuthContext, IAuthContext, useAuthContext } from './context/AuthContext';
|
import { AuthContext, IAuthContext, useAuthContext } from './context/AuthContext';
|
||||||
|
import { attemptLogout, checkCredientials } from './util/apiUtils';
|
||||||
import { IUser } from './schemas';
|
import { IUser } from './schemas';
|
||||||
import { checkCredientials } from './util/apiUtils';
|
|
||||||
|
// pages, ui, styles
|
||||||
import Subscriptions from './components/pages/Subscriptions/Subscriptions';
|
import Subscriptions from './components/pages/Subscriptions/Subscriptions';
|
||||||
import Browser from './components/pages/Browser';
|
import Browser from './components/pages/Browser';
|
||||||
import Collection from './components/pages/Collection';
|
import Collection from './components/pages/Collection';
|
||||||
@@ -11,8 +14,10 @@ import Profile from './components/pages/Profile';
|
|||||||
import Recipe from './components/pages/Recipe';
|
import Recipe from './components/pages/Recipe';
|
||||||
import Register from './components/pages/Register';
|
import Register from './components/pages/Register';
|
||||||
import Welcome from './components/pages/Welcome';
|
import Welcome from './components/pages/Welcome';
|
||||||
import './sass/App.scss'
|
import AddRecipe from './components/pages/AddRecipe';
|
||||||
|
import CollectionBrowser from './components/pages/CollectionBrowser';
|
||||||
import { Navbar } from './components/ui';
|
import { Navbar } from './components/ui';
|
||||||
|
import './sass/App.scss'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [user, setUser] = useState<IAuthContext>({ user: undefined });
|
const [user, setUser] = useState<IAuthContext>({ user: undefined });
|
||||||
@@ -51,11 +56,14 @@ function App() {
|
|||||||
<Route path="/register" element={<Register receiveChange={receiveChange} />} />
|
<Route path="/register" element={<Register receiveChange={receiveChange} />} />
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/profile" element={<Profile />} />
|
<Route path="/profile" element={<Profile />} />
|
||||||
<Route path="/collection" element={<Collection />} />
|
<Route path="/collections" element={<CollectionBrowser />} />
|
||||||
|
<Route path="/collections/:id" element={<Collection />} />
|
||||||
<Route path="/explore" element={<Browser />} />
|
<Route path="/explore" element={<Browser />} />
|
||||||
<Route path="/recipe/:id" element={<Recipe />} />
|
<Route path="/recipe/:id" element={<Recipe />} />
|
||||||
<Route path="/subscriptions" element={<Subscriptions />} />
|
<Route path="/subscriptions" element={<Subscriptions />} />
|
||||||
<Route path="/subscriptions/:id" element={<Collection />} />
|
<Route path="/subscriptions/:id" element={<Collection />} />
|
||||||
|
|
||||||
|
<Route path="/add-recipe" element={<AddRecipe />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { useAuthContext } from "../../context/AuthContext";
|
||||||
|
import { IRecipe } from "../../schemas";
|
||||||
|
import { Button, Divider, Form, Page, Panel } from "../ui"
|
||||||
|
|
||||||
|
const AddRecipe = () => {
|
||||||
|
const { user } = useAuthContext();
|
||||||
|
const [input, setInput] = useState<IRecipe>({ name: '', preptime: '', description: '', authoruserid: user?.id || '', ingredients: [] })
|
||||||
|
const [form, setForm] = useState<JSX.Element[]>();
|
||||||
|
|
||||||
|
const getFormState = useCallback((data: IRecipe) => {
|
||||||
|
setInput(data);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleCreate = () => {
|
||||||
|
for (let field of Object.keys(input)) {
|
||||||
|
if (!input[field as keyof IRecipe]) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('good to go!')
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
setInput((prev: IRecipe) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
authoruserid: user.id!
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [user])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setForm(
|
||||||
|
new Form<IRecipe>({
|
||||||
|
parent: "AddRecipe",
|
||||||
|
keys: ["name", "preptime", "ingredients", "description"],
|
||||||
|
labels: ["Recipe Name:", "Prep Time:", "Ingredients:", "Description:"],
|
||||||
|
dataTypes: ['text', 'text', 'custom picker', 'text'],
|
||||||
|
initialState: input,
|
||||||
|
getState: getFormState
|
||||||
|
}).mount()
|
||||||
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<h1>Add a New Recipe</h1>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Panel>
|
||||||
|
{ form }
|
||||||
|
<Button onClick={handleCreate}>Create Recipe!</Button>
|
||||||
|
</Panel>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddRecipe;
|
||||||
11
client/src/components/pages/CollectionBrowser.tsx
Normal file
11
client/src/components/pages/CollectionBrowser.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Page } from "../ui";
|
||||||
|
|
||||||
|
const CollectionBrowser = () => {
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<h1>Browsing your {2} collections:</h1>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CollectionBrowser;
|
||||||
@@ -21,21 +21,14 @@ const Welcome = () => {
|
|||||||
<Panel extraStyles="inherit-background c-papyrus uppercase">
|
<Panel extraStyles="inherit-background c-papyrus uppercase">
|
||||||
<h2>Ready to get started?</h2>
|
<h2>Ready to get started?</h2>
|
||||||
<Button onClick={() => navigate('/register')}>Register</Button>
|
<Button onClick={() => navigate('/register')}>Register</Button>
|
||||||
|
<Button onClick={attemptLogout}>Log Out</Button>
|
||||||
</Panel>
|
</Panel>
|
||||||
)
|
)
|
||||||
|
|
||||||
const unwrap = async () => {
|
|
||||||
const result = await checkCredientials();
|
|
||||||
console.log(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page extraStyles="narrow-dividers">
|
<Page extraStyles="narrow-dividers">
|
||||||
<Panel extraStyles='inherit-background c-papyrus uppercase'>
|
<Panel extraStyles='inherit-background c-papyrus uppercase'>
|
||||||
<h1>Welcome to Recipin</h1>
|
<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>
|
</Panel>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { Panel } from ".";
|
||||||
|
import { PortalBase } from "../../util/types";
|
||||||
|
import "/src/sass/components/Dropdown.scss";
|
||||||
|
|
||||||
|
// expects to receive buttons as children
|
||||||
|
const Dropdown: FC<PortalBase> = ({ children, extraStyles = null }) => {
|
||||||
|
return (
|
||||||
|
<Panel extraStyles={`ui-dropdown ${extraStyles}`}>
|
||||||
|
{ children }
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Dropdown;
|
||||||
@@ -54,6 +54,11 @@ export default class Form<T>{
|
|||||||
let output = new Array<JSX.Element>();
|
let output = new Array<JSX.Element>();
|
||||||
|
|
||||||
for (let i = 0; i < this.keys.length; i++) {
|
for (let i = 0; i < this.keys.length; i++) {
|
||||||
|
if (this.dataTypes[i] == 'custom picker') {
|
||||||
|
console.log('noted!');
|
||||||
|
this.dataTypes[i] = 'text';
|
||||||
|
}
|
||||||
|
|
||||||
output.push(
|
output.push(
|
||||||
<div id={`${this.parent}-row-${i}`} key={v4()}>
|
<div id={`${this.parent}-row-${i}`} key={v4()}>
|
||||||
<label htmlFor={`${this.parent}-${this.keys[i]}`}>{this.labels[i]}</label>
|
<label htmlFor={`${this.parent}-${this.keys[i]}`}>{this.labels[i]}</label>
|
||||||
|
|||||||
5
client/src/components/ui/FormElements/CustomPicker.tsx
Normal file
5
client/src/components/ui/FormElements/CustomPicker.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const CustomPicker = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomPicker;
|
||||||
20
client/src/components/ui/FormElements/OptionFetcher.tsx
Normal file
20
client/src/components/ui/FormElements/OptionFetcher.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { PortalBase } from "../../../util/types";
|
||||||
|
|
||||||
|
interface FetcherParams extends PortalBase {
|
||||||
|
fetchTarget: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const OptionFetcher: FC<FetcherParams> = ({ children, fetchTarget }) => {
|
||||||
|
const fetchOptions = async () => {
|
||||||
|
console.log(fetchTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OptionFetcher;
|
||||||
@@ -1,25 +1,65 @@
|
|||||||
import { attemptLogout } from "../../../util/apiUtils";
|
import { attemptLogout } from "../../../util/apiUtils";
|
||||||
import { NavbarType } from "../../../util/types";
|
import { NavbarType } from "../../../util/types";
|
||||||
import Button from "../Button";
|
import { Button, Dropdown } from '../.'
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
const LoggedIn: NavbarType = ({ received, liftChange, navigate }) => {
|
const LoggedIn: NavbarType = ({ received, liftChange, navigate }) => {
|
||||||
|
const [dropdownActive, setDropdownActive] = useState(false);
|
||||||
|
const [searchActive, setSearchActive] = useState(false);
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
const success = await attemptLogout();
|
const success = await attemptLogout();
|
||||||
if (success) liftChange!(undefined);
|
if (success) liftChange!(undefined);
|
||||||
navigate('/');
|
navigate('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleUIChange = (target: string) => {
|
||||||
|
if (target == "SEARCH") {
|
||||||
|
setSearchActive(!searchActive);
|
||||||
|
setDropdownActive(false);
|
||||||
|
} else {
|
||||||
|
setSearchActive(false);
|
||||||
|
setDropdownActive(!dropdownActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionSelect = (payload: string) => {
|
||||||
|
setSearchActive(false);
|
||||||
|
setDropdownActive(false);
|
||||||
|
navigate(payload);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="navbar">
|
<div>
|
||||||
<div className="navbar-block">
|
<div id="navbar">
|
||||||
<a onClick={() => navigate('/')}>RECIPIN</a>
|
<div className="navbar-block">
|
||||||
</div>
|
<a onClick={() => navigate('/')}>RECIPIN</a>
|
||||||
<div className="navbar-block">
|
</div>
|
||||||
<p>Hi, {received?.firstname}.</p>
|
<div className="navbar-block">
|
||||||
<span id="search-icon"></span>
|
<p>Hi, {received?.firstname}.</p>
|
||||||
<Button onClick={() => navigate('/profile')}>Profile</Button>
|
<span id="search-icon"></span>
|
||||||
<Button onClick={handleLogout}>Log Out</Button>
|
<Button onClick={() => handleUIChange("SEARCH")}>Search</Button>
|
||||||
|
<Button onClick={() => handleUIChange("ACTIONS")}>Actions</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{
|
||||||
|
dropdownActive && (
|
||||||
|
<Dropdown extraStyles="top-menu-bar actions-bar">
|
||||||
|
<Button onClick={() => handleOptionSelect('/add-recipe')}>Add a Recipe</Button>
|
||||||
|
<Button onClick={() => handleOptionSelect('/collections')}>My Collections</Button>
|
||||||
|
<Button onClick={() => handleOptionSelect('/subscriptions')}>Subscriptions</Button>
|
||||||
|
<Button onClick={() => handleOptionSelect('/profile')}>Profile</Button>
|
||||||
|
<Button onClick={handleLogout}>Log Out</Button>
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
searchActive && (
|
||||||
|
<Dropdown extraStyles="top-menu-bar search-bar">
|
||||||
|
<Button>Run Search</Button>
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { PortalBase } from "../../util/types";
|
||||||
|
|
||||||
|
const Tooltip: FC<PortalBase> = ({ children, extraStyles = null }) => {
|
||||||
|
return (
|
||||||
|
<aside className={`ui-tooltip ${extraStyles}`}>
|
||||||
|
{ children }
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tooltip;
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import Card from "./Card";
|
import Card from "./Card";
|
||||||
import Divider from "./Divider";
|
import Divider from "./Divider";
|
||||||
|
import Dropdown from "./Dropdown";
|
||||||
import Form from "./Form";
|
import Form from "./Form";
|
||||||
import Navbar from "./Navbar";
|
import Navbar from "./Navbar";
|
||||||
import Page from "./Page";
|
import Page from "./Page";
|
||||||
import Panel from "./Panel";
|
import Panel from "./Panel";
|
||||||
import UserCard from "./UserCard";
|
|
||||||
import TextField from "./TextField";
|
import TextField from "./TextField";
|
||||||
|
import Tooltip from "./Tooltip";
|
||||||
|
import UserCard from "./UserCard";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Button, Card, Divider, Form, Navbar, Page, Panel, UserCard, TextField
|
Button, Card, Dropdown, Divider, Form, Navbar, Page, Panel, TextField, Tooltip, UserCard
|
||||||
}
|
}
|
||||||
29
client/src/sass/components/Dropdown.scss
Normal file
29
client/src/sass/components/Dropdown.scss
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
.ui-dropdown {
|
||||||
|
&.top-menu-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
top: 5rem;
|
||||||
|
background-color: rgb(52, 29, 29);
|
||||||
|
color: white;
|
||||||
|
border-radius: 0;
|
||||||
|
z-index: 3;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.ui-button {
|
||||||
|
background-color: red;
|
||||||
|
margin: 0 1rem;
|
||||||
|
z-index: 7;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { Form } from "../components/ui";
|
import { Form } from "../components/ui";
|
||||||
import { IUser } from "../schemas";
|
import { IUser } from "../schemas";
|
||||||
|
|
||||||
interface PortalBase {
|
export interface PortalBase {
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
extraStyles?: string
|
extraStyles?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ export interface IUserAuth {
|
|||||||
|
|
||||||
export interface IRecipe extends HasHistory, CanDeactivate {
|
export interface IRecipe extends HasHistory, CanDeactivate {
|
||||||
name: string
|
name: string
|
||||||
description?: string
|
|
||||||
preptime: string
|
preptime: string
|
||||||
authoruserid: string | number
|
authoruserid: string | number
|
||||||
|
description?: string
|
||||||
|
ingredients?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IIngredient extends HasHistory {
|
export interface IIngredient extends HasHistory {
|
||||||
|
|||||||
Reference in New Issue
Block a user