-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Kyle Zarazan
committed
Nov 20, 2024
1 parent
42727c1
commit f4991ac
Showing
16 changed files
with
284 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
class ApplicationController < ActionController::Base | ||
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. | ||
allow_browser versions: :modern | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import React, { useState } from 'react'; | ||
import { useAppDispatch, useAppSelector } from '../store/hooks'; | ||
import { createRecipe } from '../store/recipesSlice'; | ||
import { Ingredient } from '../types/types'; | ||
|
||
const getCsrfToken = (): string => { | ||
const element = document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement; | ||
return element ? element.content : ''; | ||
}; | ||
|
||
export interface RecipeFormData { | ||
name: string; | ||
description: string; | ||
ingredients_attributes: Ingredient[]; | ||
} | ||
|
||
const NewRecipeForm: React.FC = () => { | ||
const dispatch = useAppDispatch(); | ||
const { items: foods } = useAppSelector((state) => state.foods); | ||
const csrfToken = getCsrfToken(); | ||
|
||
const [formData, setFormData] = useState<RecipeFormData>({ | ||
name: '', | ||
description: '', | ||
ingredients_attributes: [] | ||
}); | ||
|
||
const addIngredient = () => { | ||
setFormData({ | ||
...formData, | ||
ingredients_attributes: [ | ||
...formData.ingredients_attributes, | ||
{ food_id: 0, measurement: '' } | ||
] | ||
}); | ||
}; | ||
|
||
const handleIngredientChange = (index: number, field: keyof Ingredient, value: string | number) => { | ||
const newIngredients = [...formData.ingredients_attributes]; | ||
newIngredients[index] = { | ||
food_id: newIngredients[index]?.food_id || 0, | ||
measurement: newIngredients[index]?.measurement || '', | ||
[field]: value | ||
}; | ||
setFormData({ | ||
...formData, | ||
ingredients_attributes: newIngredients | ||
}); | ||
}; | ||
|
||
const handleSubmit = (e: React.FormEvent) => { | ||
e.preventDefault(); | ||
dispatch(createRecipe({ | ||
data: formData, | ||
csrfToken | ||
})) | ||
.unwrap() | ||
.then(() => { | ||
setFormData({ | ||
name: '', | ||
description: '', | ||
ingredients_attributes: [] | ||
}); | ||
}) | ||
.catch((error: Error) => { | ||
alert('Failed to create recipe: ' + error.message); | ||
}); | ||
}; | ||
return ( | ||
<form onSubmit={handleSubmit} className="max-w-2xl mx-auto p-4"> | ||
<div className="mb-4"> | ||
<label className="block mb-2">Recipe Name:</label> | ||
<input | ||
type="text" | ||
value={formData.name} | ||
onChange={(e) => setFormData({...formData, name: e.target.value})} | ||
className="w-full p-2 border rounded" | ||
/> | ||
</div> | ||
|
||
<div className="mb-4"> | ||
<label className="block mb-2">Description:</label> | ||
<textarea | ||
value={formData.description} | ||
onChange={(e) => setFormData({...formData, description: e.target.value})} | ||
className="w-full p-2 border rounded" | ||
/> | ||
</div> | ||
|
||
<div className="mb-4"> | ||
<h3 className="mb-2">Ingredients</h3> | ||
{formData.ingredients_attributes.map((ingredient, index) => ( | ||
<div key={index} className="flex gap-4 mb-2"> | ||
<select | ||
value={ingredient.food_id} | ||
onChange={(e) => handleIngredientChange(index, 'food_id', parseInt(e.target.value))} | ||
className="p-2 border rounded" | ||
> | ||
<option value="">Select Food</option> | ||
{foods.map((food) => ( | ||
<option key={food.id} value={food.id}>{food.name}</option> | ||
))} | ||
</select> | ||
|
||
<input | ||
type="text" | ||
value={ingredient.measurement} | ||
onChange={(e) => handleIngredientChange(index, 'measurement', e.target.value)} | ||
placeholder="Measurement" | ||
className="p-2 border rounded w-24" | ||
/> | ||
</div> | ||
))} | ||
<button | ||
type="button" | ||
onClick={addIngredient} | ||
className="bg-blue-500 text-white px-4 py-2 rounded mt-2" | ||
> | ||
Add Ingredient | ||
</button> | ||
</div> | ||
|
||
<button | ||
type="submit" | ||
className="bg-green-500 text-white px-6 py-2 rounded" | ||
> | ||
Create Recipe | ||
</button> | ||
</form> | ||
); | ||
}; | ||
|
||
export default NewRecipeForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' | ||
|
||
export const fetchFoods = createAsyncThunk( | ||
'foods/fetchFoods', | ||
async () => { | ||
const response = await fetch('/api/foods') | ||
return response.json() | ||
} | ||
) | ||
|
||
interface Food { | ||
id: number; | ||
name: string; | ||
} | ||
|
||
interface FoodsState { | ||
items: Food[] | ||
status: 'idle' | 'loading' | 'succeeded' | 'failed' | ||
error: string | null | ||
} | ||
|
||
const initialState: FoodsState = { | ||
items: [], | ||
status: 'idle', | ||
error: null | ||
} | ||
|
||
const foodsSlice = createSlice({ | ||
name: 'foods', | ||
initialState, | ||
reducers: {}, | ||
extraReducers: (builder) => { | ||
builder | ||
.addCase(fetchFoods.pending, (state) => { | ||
state.status = 'loading' | ||
}) | ||
.addCase(fetchFoods.fulfilled, (state, action) => { | ||
state.status = 'succeeded' | ||
state.items = action.payload | ||
}) | ||
.addCase(fetchFoods.rejected, (state, action) => { | ||
state.status = 'failed' | ||
state.error = action.error.message || null | ||
}) | ||
} | ||
}) | ||
|
||
export default foodsSlice.reducer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.