add recipe workflow, viewing for collections

This commit is contained in:
Mikayla Dobson
2023-02-13 21:19:29 -06:00
parent fc1046bad5
commit 9945ebadb4
13 changed files with 224 additions and 56 deletions

View File

@@ -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>
)

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)

View File

View 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;
}
}
}

View File

@@ -24,4 +24,8 @@
flex-flow: row wrap;
justify-content: center;
}
&.width-80 {
width: 80vw;
}
}

View File

@@ -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;