checkpoint
This commit is contained in:
parent
925135d08c
commit
22e2dab830
7 changed files with 155 additions and 2 deletions
|
@ -7,7 +7,9 @@ const app = express();
|
||||||
app.use(cors()); // to remove cors origin error in dev TODO: remove when dockerized
|
app.use(cors()); // to remove cors origin error in dev TODO: remove when dockerized
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
// routes
|
// ####### ROUTES #######
|
||||||
|
|
||||||
|
// ### GET ALL RECIPES ###
|
||||||
app.get("/recipes", async (req, res) => {
|
app.get("/recipes", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const recipes = await db('recipes').select('id', 'name');
|
const recipes = await db('recipes').select('id', 'name');
|
||||||
|
@ -18,6 +20,7 @@ app.get("/recipes", async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ### GET RECIPE ###
|
||||||
app.get("/recipe/:id", async (req, res) => {
|
app.get("/recipe/:id", async (req, res) => {
|
||||||
const id = req.params.id
|
const id = req.params.id
|
||||||
try {
|
try {
|
||||||
|
@ -52,6 +55,7 @@ app.get("/recipe/:id", async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ### ADD RECIPE ###
|
||||||
app.post("/add-recipe", async (req, res) => {
|
app.post("/add-recipe", async (req, res) => {
|
||||||
const { name, cuisine, ingredients, steps } = req.body;
|
const { name, cuisine, ingredients, steps } = req.body;
|
||||||
try {
|
try {
|
||||||
|
@ -99,6 +103,7 @@ app.post("/add-recipe", async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ### DELETE RECIPE ###
|
||||||
app.delete("/delete-recipe", async (req, res) => {
|
app.delete("/delete-recipe", async (req, res) => {
|
||||||
const { id } = req.body;
|
const { id } = req.body;
|
||||||
try {
|
try {
|
||||||
|
|
55
frontend/src/components/AddBulkIngredients.tsx
Normal file
55
frontend/src/components/AddBulkIngredients.tsx
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { type Ingredient } from "../types/Recipe"
|
||||||
|
|
||||||
|
|
||||||
|
const BulkIngredientsForm: React.FC = () => {
|
||||||
|
const [ingredients, setIngredients] = useState<Ingredient[]>([]);
|
||||||
|
|
||||||
|
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
// Split the input by newline to get individual ingredients
|
||||||
|
const lines = e.target.value.split('\n').filter(line => line.trim() !== '');
|
||||||
|
const pattern = /^([0-9/.]+)?\s*(\S+)\s*((\w+\s*)*)$/;
|
||||||
|
const parsedIngredients: Ingredient[] = lines.map(line => {
|
||||||
|
const parts = line.match(pattern); // Updated regex pattern for fractions
|
||||||
|
let quantity;
|
||||||
|
if (parts?.[1]) {
|
||||||
|
// Try to parse the quantity as a fraction first
|
||||||
|
const [num, denom] = parts[1].split('/');
|
||||||
|
if (denom) {
|
||||||
|
quantity = parseFloat(num) / parseFloat(denom);
|
||||||
|
} else {
|
||||||
|
quantity = parseFloat(parts[1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quantity = 0; // Default to zero if no quantity is found
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
quantity: +quantity.toFixed(2),
|
||||||
|
name: parts?.[3]?.trim() || '',
|
||||||
|
unit: parts?.[2]?.trim() || ''
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setIngredients(parsedIngredients);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Please enter ingredients in the following order: Quantity, Unit, Name</p> {/* Prompt for correct input format */}
|
||||||
|
<textarea
|
||||||
|
rows={4} // Adjust the number of rows based on your input size expectations
|
||||||
|
cols={50} // Adjust the number of columns based on your input width expectations
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter ingredients separated by newline..."
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
{ingredients.map((ing, index) => (
|
||||||
|
<li key={index}>{`${ing.quantity} ${ing.unit} ${ing.name}`}</li> // Changed order to quantity, unit, name
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BulkIngredientsForm;
|
0
frontend/src/components/AddBulkSteps.tsx
Normal file
0
frontend/src/components/AddBulkSteps.tsx
Normal file
87
frontend/src/components/AddIngredientsForm.tsx
Normal file
87
frontend/src/components/AddIngredientsForm.tsx
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { type Ingredient } from "../types/Recipe"
|
||||||
|
|
||||||
|
const AddIngredientsForm: React.FC = () => {
|
||||||
|
const [ingredients, setIngredients] = useState<Ingredient[]>([]);
|
||||||
|
const [newIngredient, setNewIngredient] = useState<Partial<Ingredient>>({});
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setNewIngredient(prev => ({ ...prev, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddIngredient = () => {
|
||||||
|
if (newIngredient.name && newIngredient.quantity !== undefined && newIngredient.unit) {
|
||||||
|
setIngredients(prev => [...prev, newIngredient as Ingredient]);
|
||||||
|
setNewIngredient({});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleAddIngredient();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleIngredientsSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// Here you can parse the input and convert it into ingredients
|
||||||
|
const lines = newIngredient.name?.split('\n').filter(line => line.trim() !== '');
|
||||||
|
if (lines) {
|
||||||
|
const parsedIngredients: Ingredient[] = lines.map(line => {
|
||||||
|
const parts = line.match(/^([^0-9]*)([0-9.]+)?([a-zA-Z]*)$/);
|
||||||
|
return {
|
||||||
|
name: parts?.[1]?.trim() || '',
|
||||||
|
quantity: parseFloat(parts?.[2]?.trim() || '0'),
|
||||||
|
unit: parts?.[3]?.trim() || ''
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setIngredients(parsedIngredients);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<form onSubmit={handleIngredientsSubmit}>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="quantity"
|
||||||
|
placeholder="Quantity"
|
||||||
|
value={(newIngredient.quantity !== undefined ? newIngredient.quantity : '')}
|
||||||
|
onChange={handleChange}
|
||||||
|
onKeyDown={handleInputKeyDown}
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
name="unit"
|
||||||
|
value={newIngredient.unit || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<option value="">Select Unit</option>
|
||||||
|
<option value="grams">Grams</option>
|
||||||
|
<option value="kilograms">Kilograms</option>
|
||||||
|
<option value="cups">Cups</option>
|
||||||
|
{/* Add more units as needed */}
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
placeholder="Ingredient Name"
|
||||||
|
value={newIngredient.name || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
onKeyDown={handleInputKeyDown}
|
||||||
|
/>
|
||||||
|
<button type="button" onClick={handleAddIngredient}>Add Ingredient</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
{ingredients.map((ing, index) => (
|
||||||
|
<li key={index}>{`${ing.quantity} ${ing.unit} ${ing.name} `}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddIngredientsForm;
|
0
frontend/src/components/AddRecipeForm.tsx
Normal file
0
frontend/src/components/AddRecipeForm.tsx
Normal file
|
@ -1,6 +1,8 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { addRecipe } from "../services/frontendApi.js";
|
import { addRecipe } from "../services/frontendApi.js";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import AddIngredientsForm from "../components/AddIngredientsForm.tsx"
|
||||||
|
import AddBulkIngredients from "../components/AddBulkIngredients.tsx"
|
||||||
|
|
||||||
function AddRecipe() {
|
function AddRecipe() {
|
||||||
const [newRecipeId, setNewRecipeId] = useState<number | null>(null);
|
const [newRecipeId, setNewRecipeId] = useState<number | null>(null);
|
||||||
|
@ -47,6 +49,10 @@ function AddRecipe() {
|
||||||
submit
|
submit
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
<div>
|
||||||
|
<AddIngredientsForm />
|
||||||
|
<AddBulkIngredients />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ function Cookbook() {
|
||||||
) : (
|
) : (
|
||||||
<div className="recipes-grid">
|
<div className="recipes-grid">
|
||||||
{recipes.map((recipe: Recipe) => (
|
{recipes.map((recipe: Recipe) => (
|
||||||
<CookbookRecipeTile recipe={recipe} key={recipe.id} handleDelete={handleDelete} />
|
<CookbookRecipeTile recipe={recipe} key={recipe.details.id} handleDelete={handleDelete} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue