in progress: table for units of measurements, etc
This commit is contained in:
@@ -16,6 +16,9 @@ function IngredientSelector({ position, ingredients, destroy }: IngredientSelect
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||||
|
<div className="ingredient-unit">
|
||||||
|
<label>Unit:</label>
|
||||||
|
</div>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
autoHighlight
|
autoHighlight
|
||||||
options={options}
|
options={options}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useAuthContext } from "../../context/AuthContext";
|
import { useAuthContext } from "../../context/AuthContext";
|
||||||
import { LegacyRef, MutableRefObject, useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { Button, Card, Divider, Form, Page, Panel } from "../ui"
|
import { Button, Card, Divider, Page, Panel } from "../ui"
|
||||||
import { IIngredient, IRecipe } from "../../schemas";
|
import { IIngredient, IRecipe } from "../../schemas";
|
||||||
import API from "../../util/API";
|
import API from "../../util/API";
|
||||||
import { createOptionFromText, useSelectorContext } from "../../context/SelectorContext";
|
import { useSelectorContext } from "../../context/SelectorContext";
|
||||||
import IngredientSelector from "../derived/IngredientSelector";
|
import IngredientSelector from "../derived/IngredientSelector";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
@@ -16,15 +16,6 @@ const AddRecipe = () => {
|
|||||||
const [toast, setToast] = useState(<></>)
|
const [toast, setToast] = useState(<></>)
|
||||||
const [input, setInput] = useState<IRecipe>({ name: '', preptime: '', description: '', authoruserid: '' })
|
const [input, setInput] = useState<IRecipe>({ name: '', preptime: '', description: '', authoruserid: '' })
|
||||||
|
|
||||||
const initialIngredient = useRef(null);
|
|
||||||
|
|
||||||
// clear out selector state on page load
|
|
||||||
/* useEffect(() => {
|
|
||||||
setData(new Array<IIngredient>());
|
|
||||||
setSelected(new Array<string>());
|
|
||||||
setOptions(new Array<OptionType>());
|
|
||||||
}, []) */
|
|
||||||
|
|
||||||
// store all ingredients on page mount
|
// store all ingredients on page mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
token && (async() => {
|
token && (async() => {
|
||||||
@@ -45,78 +36,9 @@ const AddRecipe = () => {
|
|||||||
}, [token])
|
}, [token])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data.length) {
|
if (data.length) setTriggerChange(true);
|
||||||
/* const autocompleteInstance = (
|
|
||||||
<Autocomplete
|
|
||||||
multiple
|
|
||||||
freeSolo
|
|
||||||
autoHighlight
|
|
||||||
fullWidth
|
|
||||||
filterSelectedOptions
|
|
||||||
className="ui-creatable-component"
|
|
||||||
id="ingredient-autocomplete"
|
|
||||||
options={options.map(each => each.label)}
|
|
||||||
onChange={(e) => updateSelection(e.target['innerText' as keyof EventTarget].toString())}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.code == 'Enter') {
|
|
||||||
const inputVal: string = e.target['value' as keyof EventTarget].toString();
|
|
||||||
console.log(inputVal)
|
|
||||||
if (inputVal.length) {
|
|
||||||
setSelected(prev => [...prev, inputVal])
|
|
||||||
const newOption = createOptionFromText(inputVal, optionCount + 1);
|
|
||||||
setOptions((prev) => [...prev, newOption]);
|
|
||||||
setOptionCount(prev => prev + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
renderTags={(value, getTagProps) => value.map((option, idx) => <Chip variant="outlined" label={option} { ...getTagProps({ index: idx }) } onDelete={() => handleDelete(option)} />)}
|
|
||||||
renderInput={(params) => (
|
|
||||||
<TextField
|
|
||||||
{...params}
|
|
||||||
variant="filled"
|
|
||||||
placeholder="Ingredient Name"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
) */
|
|
||||||
|
|
||||||
// create dropdown from new data
|
|
||||||
/*
|
|
||||||
const selectorInstance = <Creatable
|
|
||||||
className="ui-creatable-component"
|
|
||||||
id="ingredient-selector"
|
|
||||||
isMulti
|
|
||||||
value={selected}
|
|
||||||
options={options}
|
|
||||||
onChange={(selection: MultiValue<OptionType>) => onChange(selection)}
|
|
||||||
onCreateOption={(input: string) => onCreateOption(input, () => {})}
|
|
||||||
/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
// data.length && setSelector(autocompleteInstance);
|
|
||||||
setTriggerChange(true);
|
|
||||||
}
|
|
||||||
}, [data, options])
|
}, [data, options])
|
||||||
|
|
||||||
// once the dropdown data has populated, mount it within the full form
|
|
||||||
/* useEffect(() => {
|
|
||||||
triggerChange && setForm(
|
|
||||||
<Form<IRecipe> _config={{
|
|
||||||
parent: "AddRecipe",
|
|
||||||
keys: ["name", "preptime", "course", "cuisine", "ingredients", "description"],
|
|
||||||
labels: ["Recipe Name:", "Prep Time:", "Course:", "Cuisine:", "Ingredients:", "Description:"],
|
|
||||||
dataTypes: ['text', 'text', 'custom picker', 'custom picker', 'SELECTOR', 'TINYMCE'],
|
|
||||||
initialState: input,
|
|
||||||
getState: getFormState,
|
|
||||||
richTextInitialValue: "<p>Enter recipe details here!</p>",
|
|
||||||
}} />
|
|
||||||
)
|
|
||||||
}, [triggerChange]) */
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(options);
|
|
||||||
}, [options])
|
|
||||||
|
|
||||||
// once user information is available, store it in recipe data
|
// once user information is available, store it in recipe data
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
@@ -128,24 +50,6 @@ const AddRecipe = () => {
|
|||||||
})
|
})
|
||||||
}, [user])
|
}, [user])
|
||||||
|
|
||||||
// store input data from form
|
|
||||||
/*
|
|
||||||
const getFormState = useCallback((data: IRecipe) => {
|
|
||||||
setInput(data);
|
|
||||||
}, [input])
|
|
||||||
|
|
||||||
const updateSelection = (target: string) => {
|
|
||||||
setSelected((prev) => {
|
|
||||||
return [...prev, target];
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDelete = (target: string) => {
|
|
||||||
setSelected((prev) => {
|
|
||||||
return prev.filter(option => option !== target);
|
|
||||||
})
|
|
||||||
} */
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return;
|
return;
|
||||||
}, [ingredientFields])
|
}, [ingredientFields])
|
||||||
@@ -156,7 +60,6 @@ const AddRecipe = () => {
|
|||||||
|
|
||||||
for (let field of Object.keys(input)) {
|
for (let field of Object.keys(input)) {
|
||||||
if (!input[field as keyof IRecipe]) {
|
if (!input[field as keyof IRecipe]) {
|
||||||
console.log(field);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,10 +98,6 @@ const AddRecipe = () => {
|
|||||||
setIngredientFields((prev) => [...prev, <IngredientSelector position={optionCount + 1} key={v4()} ingredients={data} destroy={destroySelector} />])
|
setIngredientFields((prev) => [...prev, <IngredientSelector position={optionCount + 1} key={v4()} ingredients={data} destroy={destroySelector} />])
|
||||||
setOptionCount(prev => prev + 1);
|
setOptionCount(prev => prev + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(optionCount);
|
|
||||||
}, [optionCount])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
|||||||
36
server/controllers/DropdownCtl.ts
Normal file
36
server/controllers/DropdownCtl.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import Dropdown from "../models/dropdownValues";
|
||||||
|
import { DropdownDataType } from "../schemas";
|
||||||
|
import ControllerResponse from "../util/ControllerResponse";
|
||||||
|
import { StatusCode } from "../util/types";
|
||||||
|
const DDInstance = new Dropdown();
|
||||||
|
|
||||||
|
export default class DropdownCtl {
|
||||||
|
async getMeasurements() {
|
||||||
|
try {
|
||||||
|
const result = await DDInstance.getMeasurements();
|
||||||
|
return new ControllerResponse<any[] | string>(
|
||||||
|
((result !== null) ? StatusCode.OK : StatusCode.NotFound),
|
||||||
|
result || "Measurement unit data not found",
|
||||||
|
(result !== null)
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByType(type: DropdownDataType) {
|
||||||
|
switch (type) {
|
||||||
|
case "measurement":
|
||||||
|
const result = await DDInstance.getMeasurements();
|
||||||
|
return new ControllerResponse<any[] | string>(
|
||||||
|
((result !== null) ? StatusCode.OK : StatusCode.NotFound),
|
||||||
|
result || "Measurement unit data not found",
|
||||||
|
(result !== null)
|
||||||
|
);
|
||||||
|
case "course":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,10 +101,25 @@ export default async function populate() {
|
|||||||
;
|
;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const populateMeasurements = `
|
||||||
|
INSERT INTO recipin.dropdownVals
|
||||||
|
(name, datatype, datecreated)
|
||||||
|
VALUES
|
||||||
|
('cup', 'MEASUREMENTS', $1),
|
||||||
|
('tablespoon', 'MEASUREMENTS', $1),
|
||||||
|
('teaspoon', 'MEASUREMENTS', $1),
|
||||||
|
('gram', 'MEASUREMENTS', $1),
|
||||||
|
('ounce', 'MEASUREMENTS', $1),
|
||||||
|
('fluid ounce', 'MEASUREMENTS', $1),
|
||||||
|
('pound', 'MEASUREMENTS', $1)
|
||||||
|
;
|
||||||
|
`
|
||||||
|
|
||||||
const allStatements: Array<string> = [
|
const allStatements: Array<string> = [
|
||||||
populateUsers, populateCuisines, populateCourses,
|
populateUsers, populateCuisines, populateCourses,
|
||||||
populateCollection, populateIngredients, populateRecipes,
|
populateCollection, populateIngredients, populateRecipes,
|
||||||
populateGroceryList, populateFriendships, populateComments
|
populateGroceryList, populateFriendships, populateComments,
|
||||||
|
populateMeasurements
|
||||||
];
|
];
|
||||||
|
|
||||||
await pool.query(setup);
|
await pool.query(setup);
|
||||||
|
|||||||
@@ -28,11 +28,12 @@ dotenv.config();
|
|||||||
const recipecollection = fs.readFileSync(appRoot + '/db/sql/create/createcmp_recipecollection.sql').toString();
|
const recipecollection = fs.readFileSync(appRoot + '/db/sql/create/createcmp_recipecollection.sql').toString();
|
||||||
const usersubscriptions = fs.readFileSync(appRoot + '/db/sql/create/createcmp_usersubscriptions.sql').toString();
|
const usersubscriptions = fs.readFileSync(appRoot + '/db/sql/create/createcmp_usersubscriptions.sql').toString();
|
||||||
const userfriendships = fs.readFileSync(appRoot + '/db/sql/create/createcmp_userfriendships.sql').toString();
|
const userfriendships = fs.readFileSync(appRoot + '/db/sql/create/createcmp_userfriendships.sql').toString();
|
||||||
|
const dropdownValues = fs.readFileSync(appRoot + '/db/sql/create/createdropdown.sql').toString();
|
||||||
|
|
||||||
const allStatements = [
|
const allStatements = [
|
||||||
setRole, appusers, ingredient, collection, cuisine, course,
|
setRole, appusers, ingredient, collection, cuisine, course,
|
||||||
recipe, recipecomments, groceryList, recipeingredient,
|
recipe, recipecomments, groceryList, recipeingredient,
|
||||||
recipecollection, usersubscriptions, userfriendships
|
recipecollection, usersubscriptions, userfriendships, dropdownValues
|
||||||
]
|
]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
6
server/db/sql/create/createdropdown.sql
Normal file
6
server/db/sql/create/createdropdown.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS recipin.dropdownVals (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
name VARCHAR NOT NULL,
|
||||||
|
datatype VARCHAR CHECK(datatype in ('MEASUREMENTS', 'COURSE', 'INGREDIENT')),
|
||||||
|
datecreated VARCHAR NOT NULL
|
||||||
|
);
|
||||||
14
server/models/dropdownValues.ts
Normal file
14
server/models/dropdownValues.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import pool from "../db";
|
||||||
|
|
||||||
|
export default class Dropdown {
|
||||||
|
async getMeasurements() {
|
||||||
|
try {
|
||||||
|
const statement = `SELECT * FROM recipin.dropdownVals WHERE datatype = MEASUREMENTS`;
|
||||||
|
const result = await pool.query(statement);
|
||||||
|
if (result.rows.length) return result.rows;
|
||||||
|
return null;
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
server/routes/dropdownValues.ts
Normal file
27
server/routes/dropdownValues.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Express, Router } from 'express';
|
||||||
|
import DropdownCtl from '../controllers/DropdownCtl';
|
||||||
|
import { DropdownDataType } from '../schemas';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
const DDInstance = new DropdownCtl();
|
||||||
|
|
||||||
|
export const dropdownValue = (app: Express) => {
|
||||||
|
app.use('/app/dropdown', router);
|
||||||
|
|
||||||
|
router.get('/', async (req, res, next) => {
|
||||||
|
const { datatype } = req.query;
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (datatype) {
|
||||||
|
case "measurement":
|
||||||
|
const { code, data } = await DDInstance.getMeasurements();
|
||||||
|
res.status(code).send(data);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@@ -72,4 +72,11 @@ export interface ICourse extends HasHistory, CanDeactivate {
|
|||||||
export interface FlavorProfile extends HasHistory, CanDeactivate {
|
export interface FlavorProfile extends HasHistory, CanDeactivate {
|
||||||
name: string
|
name: string
|
||||||
description?: string
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DropdownData extends HasHistory {
|
||||||
|
name: string
|
||||||
|
datatype: DropdownDataType
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DropdownDataType = "measurement" | "course"
|
||||||
Reference in New Issue
Block a user