add recipe workflow, viewing for collections
This commit is contained in:
@@ -1,33 +1,53 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useRef, useEffect, useState, createRef } from "react";
|
||||
import { useAuthContext } from "../../context/AuthContext";
|
||||
import { Button, Card, Divider, Form, Page, Panel } from "../ui"
|
||||
import { IRecipe } from "../../schemas";
|
||||
import { Button, Divider, Form, Page, Panel } from "../ui"
|
||||
import API from "../../util/API";
|
||||
|
||||
const AddRecipe = () => {
|
||||
const authContext = useAuthContext();
|
||||
const { user, token } = useAuthContext();
|
||||
const [input, setInput] = useState<IRecipe>({ name: '', preptime: '', description: '', authoruserid: '', ingredients: [] })
|
||||
const [form, setForm] = useState<JSX.Element>();
|
||||
const [toast, setToast] = useState(<></>)
|
||||
|
||||
const getFormState = useCallback((data: IRecipe) => {
|
||||
setInput(data);
|
||||
}, [input])
|
||||
|
||||
const handleCreate = () => {
|
||||
const handleCreate = async () => {
|
||||
if (!token) return;
|
||||
|
||||
for (let field of Object.keys(input)) {
|
||||
if (!input[field as keyof IRecipe]) return;
|
||||
if (!input[field as keyof IRecipe]) {
|
||||
console.log(field);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('good to go!')
|
||||
|
||||
const recipe = new API.Recipe(token);
|
||||
const result = await recipe.post(input);
|
||||
|
||||
const recipeID = result.recipe.id;
|
||||
const recipeName = result.recipe.name;
|
||||
|
||||
setToast(
|
||||
<Card>
|
||||
<p>Created recipe {recipeName} successfully!</p>
|
||||
<p>View your new recipe <a href={`/recipe/${recipeID}`}>here!</a></p>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
authContext.user && setInput((prev: IRecipe) => {
|
||||
if (!user) return;
|
||||
user && setInput((prev: IRecipe) => {
|
||||
return {
|
||||
...prev,
|
||||
authoruserid: authContext.user!.id!
|
||||
authoruserid: user.id!
|
||||
}
|
||||
})
|
||||
}, [authContext])
|
||||
}, [user])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(input);
|
||||
@@ -38,7 +58,7 @@ const AddRecipe = () => {
|
||||
<h1>Add a New Recipe</h1>
|
||||
<Divider />
|
||||
|
||||
<Panel>
|
||||
<Panel extraStyles="width-80">
|
||||
<Form parent={input} _config={{
|
||||
parent: "AddRecipe",
|
||||
keys: ["name", "preptime", "course", "cuisine", "ingredients", "description"],
|
||||
@@ -49,9 +69,9 @@ const AddRecipe = () => {
|
||||
richTextInitialValue: "<p>Enter recipe details here!</p>"
|
||||
}} />
|
||||
|
||||
{ form || <h2>Loading...</h2> }
|
||||
|
||||
<Button onClick={handleCreate}>Create Recipe!</Button>
|
||||
|
||||
<div id="toast">{ toast }</div>
|
||||
</Panel>
|
||||
</Page>
|
||||
)
|
||||
|
||||
@@ -1,38 +1,69 @@
|
||||
import { useAuthContext } from "../../context/AuthContext";
|
||||
import Protect from "../../util/Protect";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import API from "../../util/API";
|
||||
import { ICollection, IRecipe, IUser } from "../../schemas";
|
||||
import { v4 } from "uuid";
|
||||
import { Panel } from "../ui";
|
||||
|
||||
const Collection = () => {
|
||||
const [isDefault, setIsDefault] = useState(true);
|
||||
const { user } = useAuthContext();
|
||||
const [data, setData] = useState<ICollection>();
|
||||
const [owner, setOwner] = useState<IUser>();
|
||||
const [recipes, setRecipes] = useState<IRecipe[]>();
|
||||
const [content, setContent] = useState(<></>);
|
||||
const { user, token } = useAuthContext();
|
||||
const { id } = useParams();
|
||||
|
||||
if (id) {
|
||||
setIsDefault(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
token && (async() => {
|
||||
const collections = new API.Collection(token);
|
||||
const users = new API.User(token);
|
||||
|
||||
const result: ICollection = await collections.getByID(id);
|
||||
setData(result);
|
||||
|
||||
const allRecipes = await collections.getRecipesFromOne(id);
|
||||
setRecipes(allRecipes);
|
||||
|
||||
const actualUser = await users.getByID(result.ownerid as string);
|
||||
setOwner(actualUser);
|
||||
})();
|
||||
}, [token])
|
||||
|
||||
useEffect(() => {
|
||||
if (user && data && recipes) {
|
||||
setContent(
|
||||
<>
|
||||
<div className="section-header">
|
||||
<h1>COLLECTION: {data.name}</h1>
|
||||
{ <p>Collection by: {owner ? `${owner.firstname} ${owner.lastname}` : `${user.firstname} ${user.lastname}`}</p> }
|
||||
<p>{recipes.length || 0} recipe{recipes.length != 1 && "s" }</p>
|
||||
{ data.ismaincollection && (
|
||||
owner ? <p>(This is {owner.firstname}'s main collection)</p> : <p>(This is your main collection)</p>
|
||||
)}
|
||||
|
||||
<Panel>
|
||||
{
|
||||
recipes && recipes.map((each: IRecipe) =>
|
||||
<div className="recipe-card" key={v4()}>
|
||||
<h2>{each.name}</h2>
|
||||
{ each.description && <div dangerouslySetInnerHTML={{ __html: each.description }}></div> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Panel>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}, [data, recipes])
|
||||
|
||||
return (
|
||||
<Protect>
|
||||
{ isDefault ?
|
||||
|
||||
<>
|
||||
<h1>Mikayla's collection</h1>
|
||||
<p>37 recipes</p>
|
||||
<p>71 ingredients</p>
|
||||
<p>11 types of cuisine</p>
|
||||
</>
|
||||
|
||||
:
|
||||
|
||||
<>
|
||||
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
|
||||
{/* recipes */}
|
||||
{ content }
|
||||
</Protect>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,53 @@
|
||||
import { Page } from "../ui";
|
||||
import { useEffect, useState } from "react";
|
||||
import { v4 } from "uuid";
|
||||
import { useAuthContext } from "../../context/AuthContext";
|
||||
import { ICollection, IRecipe } from "../../schemas";
|
||||
import API from "../../util/API";
|
||||
import { Card, Page } from "../ui";
|
||||
|
||||
interface CollectionDetails {
|
||||
idx: number
|
||||
collection: ICollection
|
||||
recipes: IRecipe[]
|
||||
}
|
||||
|
||||
const CollectionBrowser = () => {
|
||||
const [list, setList] = useState<ICollection[]>();
|
||||
const { token } = useAuthContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) return;
|
||||
(async() => {
|
||||
const collections = new API.Collection(token);
|
||||
const recipes = new API.Recipe(token);
|
||||
|
||||
const allRecipes = await collections.getAllAuthored();
|
||||
if (allRecipes) {
|
||||
const result = new Array<CollectionDetails[]>();
|
||||
|
||||
let i = 0;
|
||||
for (let each of allRecipes) {
|
||||
}
|
||||
|
||||
setList(allRecipes);
|
||||
}
|
||||
})();
|
||||
}, [token])
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<h1>Browsing your {2} collections:</h1>
|
||||
|
||||
{
|
||||
list && list.map(each => {
|
||||
return (
|
||||
<Card key={v4()}>
|
||||
<h2>{each.name}</h2>
|
||||
<a href={`/collections/${each.id}`}>Link to details</a>
|
||||
</Card>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ChangeEvent, FC, useEffect, useState } from "react"
|
||||
import { v4 } from "uuid"
|
||||
import RichText from "./RichText"
|
||||
import "/src/sass/components/Form.scss";
|
||||
|
||||
export interface FormConfig<T> {
|
||||
parent: string
|
||||
@@ -72,14 +73,14 @@ const Form: FC<FormProps> = ({ parent, _config }) => {
|
||||
|
||||
if (config.dataTypes![i] == 'TINYMCE') {
|
||||
return (
|
||||
<div id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<div className="form-row-editor" id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<label htmlFor={`${config.parent}-${each}`}>{config.labels![i]}</label>
|
||||
<RichText id={`${config.parent}-${each}`} initialValue={config.richTextInitialValue} getState={(txt) => updateRichText(txt, i)} />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<div className="form-row" id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<label htmlFor={`${config.parent}-${each}`}>{config.labels![i]}</label>
|
||||
<input
|
||||
type={config.dataTypes![i]}
|
||||
@@ -98,7 +99,7 @@ const Form: FC<FormProps> = ({ parent, _config }) => {
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<div className={`ui-form-component ${_config.extraStyles}`}>
|
||||
<div className={`ui-form-component ${_config.extraStyles ?? ""}`}>
|
||||
{ contents }
|
||||
</div>
|
||||
)
|
||||
|
||||
0
client/src/components/ui/Grid.tsx
Normal file
0
client/src/components/ui/Grid.tsx
Normal file
32
client/src/sass/components/Form.scss
Normal file
32
client/src/sass/components/Form.scss
Normal file
@@ -0,0 +1,32 @@
|
||||
.ui-form-component {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: center;
|
||||
width: 75%;
|
||||
|
||||
.form-row {
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 6px;
|
||||
|
||||
label {
|
||||
text-align: left;
|
||||
width: 25%;
|
||||
}
|
||||
input {
|
||||
border-radius: 4px;
|
||||
width: 65%;
|
||||
}
|
||||
}
|
||||
|
||||
.form-row-editor {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
justify-content: flex-start;
|
||||
|
||||
label {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,4 +24,8 @@
|
||||
flex-flow: row wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&.width-80 {
|
||||
width: 80vw;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ module API {
|
||||
|
||||
export class Recipe extends RestController<IRecipe> {
|
||||
constructor(token: string) {
|
||||
super(Settings.getAPISTRING() + "/app/recipes", token);
|
||||
super(Settings.getAPISTRING() + "/app/recipe", token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +183,11 @@ module API {
|
||||
super(Settings.getAPISTRING() + "/app/collection", token);
|
||||
}
|
||||
|
||||
async getRecipesFromOne(id?: number | string) {
|
||||
const response = await this.instance.get(this.endpoint + `/${id}?getRecipes=true`, this.headers);
|
||||
return Promise.resolve(response.data);
|
||||
}
|
||||
|
||||
async getAllAuthored(id?: number | string) {
|
||||
let response: AxiosResponse;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user