Compare commits

..

10 Commits

Author SHA1 Message Date
Hayden Hargreaves
e1b9d9718e (DOCS): Began specifications for user authentication.
This includes API and UI specs. The next step is actually beginning to
write some API code.
2025-06-10 21:46:27 -07:00
Hayden Hargreaves
d1a1a2714e (DOCS): Updated API requirements for the create page.
These are very simple. Final step before API implementation can continue
is defining the user specs.
2025-06-10 21:34:25 -07:00
Hayden Hargreaves
0781a6c942 (UI/STYLE): Finished the UI designs for the creation wizard!
This means its almost time for the backend to begin!
2025-06-04 21:28:14 -07:00
Hayden Hargreaves
a0c67cedc5 (UI/STYLE): Began working on the spec and UI for the create page.
Nearly completed the UI spec, but did not start the API spec. Started on
the UI implementation using an example from my Potion Gem.
2025-06-03 21:36:17 -07:00
Hayden Hargreaves
6ec6f4d1c7 (UI/STYLE): Completed* the home page.
Of course, its not 100% done, it will always be a WIP. But for now,
moving on. Specs are up to date as well.
2025-06-03 20:37:20 -07:00
Hayden Hargreaves
a6bf728f06 (DOCS): Determined what the last list should be on the home page. 2025-06-02 20:46:43 -07:00
Hayden Hargreaves
2a8d8ab3be (UI/STYLE): Implemented the UI design for the recently viewed list. 2025-06-02 20:31:27 -07:00
Hayden Hargreaves
56fcebf5a8 (DOCS): Added the recently viewed list to specs 2025-06-02 20:25:10 -07:00
Hayden Hargreaves
456b104fb7 (DOCS): More work on the technical specifications.
Moved them to a dedicated directory, but I cannot use doc since ExDoc
generates code there which is ignored by the git ignore file.
2025-06-01 22:24:31 -07:00
Hayden Hargreaves
71d79cc27e (DOCS): Began working on the tech specs.
This includes some details for the home page as well as the DB specs. Or
at least the current DB specs.
2025-06-01 21:27:53 -07:00
5 changed files with 1397 additions and 32 deletions

View File

@ -28,6 +28,7 @@ module.exports = {
400: "#8a0bd9",
500: "#6707a3",
600: "#4a0575",
700: "#2a0442",
},
}
},

View File

@ -1,5 +1,738 @@
<div class="text-white my-5">
<h1 class="text-xl">
Page: <span class="font-bold">{@current_page}</span>
</h1>
<style>
.star-rating input[type="radio"]:checked ~ label {
color: #fbbf24;
}
</style>
<div class="w-full">
<!-- Container: Page -->
<div class="w-full flex flex-col">
<!-- Content: Recipe Creation Form -->
<main class="w-full min-h-screen p-4">
<!-- Section: Heading Banner -->
<section class="w-full bg-gradient-to-tr from-primary-600 to-primary-500
text-white text-center px-6 py-10 rounded-lg">
<h1 class="text-3xl font-extrabold mb-2">
Create Your Recipe
</h1>
<p class="text-lg mt-4">
Share your culinary masterpieces with the world! Fill out the details below
to get started.
</p>
</section>
<!-- Section: Recipe Details -->
<section class="w-full my-8">
<div class="flex flex-col mb-4">
<h2 class="text-2xl font-semibold text-white flex items-center">
Recipe Details
</h2>
<p class="text-red-500 text-sm my-1">*Required</p>
</div>
<div class="grid grid-cols-1 gap-6">
<!-- Input: Title -->
<div>
<label for="title" class="block font-sm font-medium text-background-100">
Recipe Title <span class="text-red-500 text-sm my-1">*</span>
</label>
<input
type="text"
id="title"
name="title"
placeholder="e.g., Avocado Toast"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
maxlength="128"
required
/>
<!-- Hidden Messages: Valid or Invalid -->
<!-- <div class="hidden items-center text-red-600 text-xs mt-1" id="title-error"> -->
<!-- <svg class="h-4 w-4 mr-1" fill="currentColor" viewBox="0 0 20 20"> -->
<!-- <path -->
<!-- fill-rule="evenodd" -->
<!-- d="M10 18a8 8 0 100-16 8 8 0 000 16zm-1-12a1 1 0 112 0v4a1 1 0 11-2 0V6zm0 8a1 1 0 102 0 1 1 0 00-2 0z" -->
<!-- clip-rule="evenodd" -->
<!-- > -->
<!-- </path> -->
<!-- </svg> -->
<!-- Title is required and max 128 characters. -->
<!-- </div> -->
<!-- <div class="hidden items-center text-green-600 text-xs mt-1" id="title-valid"> -->
<!-- <svg class="h-4 w-4 mr-1" fill="currentColor" viewBox="0 0 20 20"> -->
<!-- <path -->
<!-- fill-rule="evenodd" -->
<!-- d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" -->
<!-- clip-rule="evenodd" -->
<!-- > -->
<!-- </path> -->
<!-- </svg> -->
<!-- Looks good! -->
<!-- </div> -->
</div>
<!-- Input: Description -->
<div>
<label for="description" class="block font-sm font-medium text-background-100">
Recipe Description <span class="text-red-500 text-sm my-1">*</span>
</label>
<textarea
id="description"
name="description"
rows="6"
placeholder="A brief, enticing overview of your delicious recipe..."
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
required
/>
<!-- Hidden Messages: Valid or Invalid -->
<div class="hidden items-center text-red-600 text-xs" id="description-error">
<svg class="h-4 w-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm-1-12a1 1 0 112 0v4a1 1 0 11-2 0V6zm0 8a1 1 0 102 0 1 1 0 00-2 0z"
clip-rule="evenodd"
>
</path>
</svg>
Title is required and max 128 characters.
</div>
<div class="hidden items-center text-green-600 text-xs" id="description-valid">
<svg class="h-4 w-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
>
</path>
</svg>
Looks good!
</div>
</div>
<!-- Input: Meal Category -->
<div>
<label for="category" class="block font-sm font-medium text-background-100">
Recipe Category <span class="text-red-500 text-sm my-1">*</span>
</label>
<div class="flex flex-wrap gap-3 mt-2 w-4/5">
<div>
<input
class="hidden peer"
type="radio"
id="category_breakfast"
name="category"
value="breakfast"
/>
<label
for="category_breakfast"
class="bg-background-100 px-3 py-1.5 rounded-full text-sm font-medium
peer-checked:bg-primary-400 peer-checked:text-white transition-all duration-150"
>
Breakfast
</label>
</div>
<div>
<input
class="hidden peer"
type="radio"
id="category_lunch"
name="category"
value="lunch"
/>
<label
for="category_lunch"
class="bg-background-100 px-3 py-1.5 rounded-full text-sm font-medium
peer-checked:bg-primary-400 peer-checked:text-white transition-all duration-150"
>
Lunch
</label>
</div>
<div>
<input
class="hidden peer"
type="radio"
id="category_dinner"
name="category"
value="dinner"
/>
<label
for="category_dinner"
class="bg-background-100 px-3 py-1.5 rounded-full text-sm font-medium
peer-checked:bg-primary-400 peer-checked:text-white transition-all duration-150"
>
Dinner
</label>
</div>
<div>
<input
class="hidden peer"
type="radio"
id="category_desert"
name="category"
value="desert"
/>
<label
for="category_desert"
class="bg-background-100 px-3 py-1.5 rounded-full text-sm font-medium
peer-checked:bg-primary-400 peer-checked:text-white transition-all duration-150"
>
Desert
</label>
</div>
<div>
<input
class="hidden peer"
type="radio"
id="category_Snack"
name="category"
value="Snack"
/>
<label
for="category_Snack"
class="bg-background-100 px-3 py-1.5 rounded-full text-sm font-medium
peer-checked:bg-primary-400 peer-checked:text-white transition-all duration-150"
>
Snack
</label>
</div>
<div>
<input
class="hidden peer"
type="radio"
id="category_Side"
name="category"
value="Side"
/>
<label
for="category_Side"
class="bg-background-100 px-3 py-1.5 rounded-full text-sm font-medium
peer-checked:bg-primary-400 peer-checked:text-white transition-all duration-150"
>
Side
</label>
</div>
<div>
<input
class="hidden peer"
type="radio"
id="category_other"
name="category"
value="other"
/>
<label
for="category_other"
class="bg-background-100 px-3 py-1.5 rounded-full text-sm font-medium
peer-checked:bg-primary-400 peer-checked:text-white transition-all duration-150"
>
Other
</label>
</div>
</div>
</div>
<!-- Input: Serving Side -->
<div>
<label for="title" class="block font-sm font-medium text-background-100">
Serves <span class="text-red-500 text-sm my-1">*</span>
</label>
<input
type="number"
id="serving"
name="serving"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
value="4"
min="1"
max="16"
required
/>
</div>
<!-- Input: Difficult -->
<div>
<label for="title" class="block font-sm font-medium text-background-100">
Difficulty <span class="text-red-500 text-sm my-1">*</span>
</label>
<div class="star-rating flex flex-row-reverse justify-end unicode-bidi-override">
<input type="radio" id="star5" name="difficulty" value="5" class="hidden" />
<label
for="star5"
title="5 stars"
class="text-3xl cursor-pointer transition-colors duration-200 ease-in-out text-background-100"
>
</label>
<input type="radio" id="star4" name="difficulty" value="4" class="hidden" />
<label
for="star4"
title="4 stars"
class="text-3xl cursor-pointer transition-colors duration-200 ease-in-out text-background-100"
>
</label>
<input type="radio" id="star3" name="difficulty" value="3" class="hidden" />
<label
for="star3"
title="3 stars"
class="text-3xl cursor-pointer transition-colors duration-200 ease-in-out text-background-100"
>
</label>
<input type="radio" id="star2" name="difficulty" value="2" class="hidden" />
<label
for="star2"
title="2 stars"
class="text-3xl cursor-pointer transition-colors duration-200 ease-in-out text-background-100"
>
</label>
<input type="radio" id="star1" name="difficulty" value="1" required class="hidden" />
<label
for="star1"
title="1 star"
class="text-3xl cursor-pointer transition-colors duration-200 ease-in-out text-background-100"
>
</label>
</div>
</div>
<!-- Input: Serving Side -->
<div class="grid grid-cols-2 gap-4">
<div>
<label for="title" class="block font-sm font-medium text-background-100">
Prep Time (minutes) <span class="text-red-500 text-sm my-1">*</span>
</label>
<input
type="number"
id="prep-time"
name="prep-time"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
value="15"
min="0"
max="120"
required
/>
</div>
<div>
<label for="title" class="block font-sm font-medium text-background-100">
Cook Time (minutes) <span class="text-red-500 text-sm my-1">*</span>
</label>
<input
type="number"
id="cook-time"
name="cook-time"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
value="30"
min="0"
max="120"
required
/>
</div>
</div>
</div>
</section>
<hr class="text-background-200" />
<!-- Section: Ingredients -->
<section class="w-full my-8">
<div class="flex flex-col mb-4">
<h2 class="text-2xl font-semibold text-white flex items-center">
Ingredients <span class="text-red-500 text-sm m-1">*</span>
</h2>
</div>
<!-- List: Ingredients -->
<ul class="">
<li class="flex gap-2">
<input
type="text"
id="ingredient-1"
name="ingredient-1"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm flex-grow
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
placeholder="e.g., All-purpose flour"
required
/>
<input
type="text"
id="quantity-1"
name="quantity-1"
class="my-1 w-2/5 px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
placeholder="2 cups"
required
/>
<button class="my-1 p-2 border border-background-200 rounded-md shadow-sm">
<svg
class="h-6 text-red-500"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 7H20"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6 10L7.70141 19.3578C7.87432 20.3088 8.70258 21 9.66915 21H14.3308C15.2974 21 16.1257 20.3087 16.2986 19.3578L18 10"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</li>
<li class="flex gap-2">
<input
type="text"
id="ingredient-1"
name="ingredient-1"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm flex-grow
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
placeholder="e.g., All-purpose flour"
required
/>
<input
type="text"
id="quantity-1"
name="quantity-1"
class="my-1 w-2/5 px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
placeholder="2 cups"
required
/>
<button class="my-1 p-2 border border-background-200 rounded-md shadow-sm">
<svg
class="h-6 text-red-500"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 7H20"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6 10L7.70141 19.3578C7.87432 20.3088 8.70258 21 9.66915 21H14.3308C15.2974 21 16.1257 20.3087 16.2986 19.3578L18 10"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</li>
<li class="flex gap-2">
<input
type="text"
id="ingredient-1"
name="ingredient-1"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm flex-grow
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
placeholder="e.g., All-purpose flour"
required
/>
<input
type="text"
id="quantity-1"
name="quantity-1"
class="my-1 w-2/5 px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
placeholder="2 cups"
required
/>
<button class="my-1 p-2 border border-background-200 rounded-md shadow-sm">
<svg
class="h-6 text-red-500"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 7H20"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6 10L7.70141 19.3578C7.87432 20.3088 8.70258 21 9.66915 21H14.3308C15.2974 21 16.1257 20.3087 16.2986 19.3578L18 10"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</li>
</ul>
<div class="flex items-center justify-start my-4">
<button class="bg-primary-400 text-white px-6 py-2 rounded-md font-medium
focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
Add Ingredient
</button>
</div>
</section>
<hr class="text-background-200" />
<!-- Section: Instructions -->
<section class="w-full my-8">
<div class="flex flex-col mb-4">
<h2 class="text-2xl font-semibold text-white flex items-center">
Instructions <span class="text-red-500 text-sm m-1">*</span>
</h2>
</div>
<!-- List: Instructions -->
<ul class="flex flex-col gap-2">
<li class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<label class="text-white text-xl font-semibold">
Step 1
</label>
<button class="my-1 p-2 border border-background-200 rounded-md shadow-sm">
<svg
class="h-5 text-red-500"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 7H20"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6 10L7.70141 19.3578C7.87432 20.3088 8.70258 21 9.66915 21H14.3308C15.2974 21 16.1257 20.3087 16.2986 19.3578L18 10"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</div>
<textarea
id="step-1"
name="step-1"
rows="6"
placeholder="Describe this step in detail..."
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
required
/>
</li>
<li class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<label class="text-white text-xl font-semibold">
Step 2
</label>
<button class="my-1 p-2 border border-background-200 rounded-md shadow-sm">
<svg
class="h-5 text-red-500"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 7H20"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6 10L7.70141 19.3578C7.87432 20.3088 8.70258 21 9.66915 21H14.3308C15.2974 21 16.1257 20.3087 16.2986 19.3578L18 10"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</div>
<textarea
id="step-2"
name="step-2"
rows="6"
placeholder="Describe this step in detail..."
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
required
/>
</li>
</ul>
<div class="flex items-center justify-start my-4">
<button class="bg-primary-400 text-white px-6 py-2 rounded-md font-medium
focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
Add Instruction
</button>
</div>
</section>
<hr class="text-background-200" />
<!-- Section: Media & Tags -->
<section class="w-full my-8">
<div class="flex flex-col mb-4">
<h2 class="text-2xl font-semibold text-white flex items-center">
Media & Tags <span class="text-background-100 text-sm mx-2">(optional)</span>
</h2>
</div>
<!-- Element: Image Upload -->
<div>
<label for="image" class="block font-sm font-medium text-background-100">
Recipe Image
</label>
<div class="mt-1 flex justify-center p-6 border-2 border-background-200 border-dashed rounded-md">
<div class="space-y-1 text-center">
<svg
class="size-16 mx-auto text-background-200"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.2639 15.9375L12.5958 14.2834C11.7909 13.4851 11.3884 13.086 10.9266 12.9401C10.5204 12.8118 10.0838 12.8165 9.68048 12.9536C9.22188 13.1095 8.82814 13.5172 8.04068 14.3326L4.04409 18.2801M14.2639 15.9375L14.6053 15.599C15.4112 14.7998 15.8141 14.4002 16.2765 14.2543C16.6831 14.126 17.12 14.1311 17.5236 14.2687C17.9824 14.4251 18.3761 14.8339 19.1634 15.6514L20 16.4934M14.2639 15.9375L18.275 19.9565M18.275 19.9565C17.9176 20 17.4543 20 16.8 20H7.2C6.07989 20 5.51984 20 5.09202 19.782C4.71569 19.5903 4.40973 19.2843 4.21799 18.908C4.12796 18.7313 4.07512 18.5321 4.04409 18.2801M18.275 19.9565C18.5293 19.9256 18.7301 19.8727 18.908 19.782C19.2843 19.5903 19.5903 19.2843 19.782 18.908C20 18.4802 20 17.9201 20 16.8V16.4934M4.04409 18.2801C4 17.9221 4 17.4575 4 16.8V7.2C4 6.0799 4 5.51984 4.21799 5.09202C4.40973 4.71569 4.71569 4.40973 5.09202 4.21799C5.51984 4 6.07989 4 7.2 4H16.8C17.9201 4 18.4802 4 18.908 4.21799C19.2843 4.40973 19.5903 4.71569 19.782 5.09202C20 5.51984 20 6.0799 20 7.2V16.4934M17 8.99989C17 10.1045 16.1046 10.9999 15 10.9999C13.8954 10.9999 13 10.1045 13 8.99989C13 7.89532 13.8954 6.99989 15 6.99989C16.1046 6.99989 17 7.89532 17 8.99989Z"
stroke="currentColor"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<div class="flex text-sm text-background-100">
<label
for="file-upload"
class="relative cursor-pointer font-medium text-primary-200 underline"
>
<span>Upload a file</span>
<input id="file-upload" name="file-upload" type="file" class="sr-only" />
</label>
<p class="pl-1">or drag and drop</p>
</div>
<p class="text-xs text-background-200">PNG, JPG, GIF up to 10MB</p>
</div>
</div>
</div>
<!-- Element: Tag Selector & List -->
<div class="my-4">
<label for="tags" class="block font-sm font-medium text-background-100">
Tags
</label>
<input
type="text"
id="tags"
name="tags"
placeholder="Add tags (e.g., quick, pasta, high protien)"
class="my-1 w-full px-4 py-2 border border-background-200 rounded-md shadow-sm
focus:ring-primary-500 focus:border-primary-500 bg-transparent text-background-100"
maxlength="32"
/>
<ul class="flex flex-wrap gap-2 my-2">
<li class="">
<button class="px-3 py-0.5 bg-primary-400 text-white rounded-full text-sm font-medium">
Quick
</button>
</li>
<li class="">
<button class="px-3 py-0.5 bg-primary-400 text-white rounded-full text-sm font-medium">
High-Protein
</button>
</li>
<li class="">
<button class="px-3 py-0.5 bg-primary-400 text-white rounded-full text-sm font-medium">
Healthy
</button>
</li>
<li class="">
<button class="px-3 py-0.5 bg-primary-400 text-white rounded-full text-sm font-medium">
Nutrient-Rich
</button>
</li>
</ul>
</div>
</section>
<!-- Section: Save Recipe -->
<section class="w-full mt-16 mb-8 flex justify-end">
<button
type="submit"
class="px-6 py-3 bg-gradient-to-tr from-primary-500 to-primary-400 text-white
rounded-md font-semibold"
>
Save Recipe
</button>
</section>
</main>
</div>
</div>

View File

@ -1,18 +1,64 @@
<!--
<div class="text-white my-5">
<h1 class="text-xl">
Page: <span class="font-bold">{@current_page}</span>
</h1>
</div>
-->
<div class="w-full h-full">
<div class="w-full h-full mb-12">
<!-- Container: Page -->
<div class="w-full h-full flex flex-col p-4">
<div class="w-full h-full flex flex-col">
<!-- Section: Welcome Banner -->
<section class="w-full h-fit">
<!-- Element: Background Video -->
<div class="relative">
<video autoplay loop muted playsinline class="opacity-80">
<source src="/images/cooking_salt.mp4" type="video/mp4" />
</video>
<h1 class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-center
text-white text-3xl w-4/5 font-bold z-10">
Discover Your Next Favorite Meal!
</h1>
</div>
<p class="leading-relaxed text-white p-4 my-8">
Welcome to your ultimate recipe hub! Whether you're a seasoned chef or just starting your culinary adventure,
we're here to inspire. Explore thousands of delicious recipes, from quick weeknight dinners to gourmet delights,
all at your fingertips. Find exactly what you're craving with our powerful search and intuitive filters, or
browse our trending dishes for fresh ideas.
</p>
</section>
<!-- Section: Craving Something... Banner -->
<section class="w-full flex flex-col items-center justify-center my-8 py-4">
<h2 class="text-xl sm:text-2xl font-semibold text-white mb-6
bg-gradient-to-t from-background-400 to-background-300
py-4 w-full text-center border-b-background-500
">
Craving Something Specific?
</h2>
<!-- Element: Pill Button Group -->
<div class="px-4 grid grid-cols-3 gap-4">
<div class="text-md py-1.5 px-3 rounded-lg text-white bg-gradient-to-tr from-primary-600 to-primary-500 shadow-md shadow-primary-600 text-center">
<p>Breakfast</p>
</div>
<div class="text-md py-1.5 px-3 rounded-lg text-white bg-gradient-to-tr from-primary-600 to-primary-500 shadow-md shadow-primary-600 text-center">
<p>Lunch</p>
</div>
<div class="text-md py-1.5 px-3 rounded-lg text-white bg-gradient-to-tr from-primary-600 to-primary-500 shadow-md shadow-primary-600 text-center">
<p>Dinner</p>
</div>
<div class="text-md py-1.5 px-3 rounded-lg text-white bg-gradient-to-tr from-primary-600 to-primary-500 shadow-md shadow-primary-600 text-center">
<p>Dessert</p>
</div>
<div class="text-md py-1.5 px-3 rounded-lg text-white bg-gradient-to-tr from-primary-600 to-primary-500 shadow-md shadow-primary-600 text-center">
<p>Snack</p>
</div>
<div class="text-md py-1.5 px-3 rounded-lg text-white bg-gradient-to-tr from-primary-600 to-primary-500 shadow-md shadow-primary-600 text-center">
<p>Any</p>
</div>
</div>
</section>
<!-- Section: Search -->
<section class="w-full h-fit flex items-center justify-center gap-x-5 my-8">
<section class="w-full h-fit flex items-center justify-center gap-x-5 my-8 p-4">
<input
type="search"
name="search-bar"
@ -36,13 +82,52 @@
</svg>
</section>
<!-- Section: Make Again -->
<section class="w-full flex flex-col">
<!-- Section: Recipe of the Week -->
<section class="w-full flex flex-col my-4 p-4 relative">
<h2 class="text-md text-white my-2">
Make again
Recipe of the Week
</h2>
<!-- List: Recent Recipes -->
<!-- Element: Spotlight -->
<div class="absolute size-64 bg-white rounded-full blur-3xl -z-10 bottom-0 -translate-y-1/4 left-1/2 -translate-x-1/2" />
<!-- Element: Recipe OTW Display -->
<div class="w-full flex justify-center my-4">
<div class="w-3/4 h-[450px] bg-gradient-to-t from-background-400 to-background-300
rounded-lg shadow-black shadow-md flex flex-col items-start justify-end p-4 text-white">
<h3 class="text-lg overflow-hidden whitespace-nowrap text-ellipsis w-full font-semibold">
Avacado Toast
</h3>
<p class="text-sm overflow-hidden text-white [display:-webkit-box] [-webkit-box-orient:vertical] [-webkit-line-clamp:4]">
Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to
make the BEST avocado toast with this recipe, plus fun variations.
Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to
make the BEST avocado toast with this recipe, plus fun variations.
Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to
make the BEST avocado toast with this recipe, plus fun variations.
</p>
<p class="text-sm italic text-background-100 mt-2">
Breakfast - 10 min
</p>
<div class="w-full flex justify-center mt-4">
<button class="w-full py-3 bg-gradient-to-t from-background-300 to-background-200 rounded-lg shadow-sm shadow-black">
Make Now!
</button>
</div>
</div>
</div>
</section>
<!-- Section: Viewed Recently -->
<section class="w-full flex flex-col my-4 p-4">
<h2 class="text-md text-white my-2">
Recently viewed
</h2>
<!-- List: Recent Views-->
<div class="overflow-x-auto whitespace-nowrap">
<ul class="inline-flex space-x-4">
<li class="flex-shrink-0 w-40 h-40 bg-gradient-to-t from-background-400 to-background-300
@ -51,29 +136,33 @@
Avacado Toast
</p>
<p class="text-xs italic text-background-100">
Breakfast
</p>
<div class="flex items-center justify-between w-full text-xs">
<p>10 min</p>
<svg
class="h-6 text-red-500"
class="h-6 text-white"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<!-- Hollow -->
<!-- <path -->
<!-- fill-rule="evenodd" -->
<!-- clip-rule="evenodd" -->
<!-- d="M12 6.00019C10.2006 3.90317 7.19377 3.2551 4.93923 5.17534C2.68468 7.09558 2.36727 10.3061 4.13778 12.5772C5.60984 14.4654 10.0648 18.4479 11.5249 19.7369C11.6882 19.8811 11.7699 19.9532 11.8652 19.9815C11.9483 20.0062 12.0393 20.0062 12.1225 19.9815C12.2178 19.9532 12.2994 19.8811 12.4628 19.7369C13.9229 18.4479 18.3778 14.4654 19.8499 12.5772C21.6204 10.3061 21.3417 7.07538 19.0484 5.17534C16.7551 3.2753 13.7994 3.90317 12 6.00019Z" -->
<!-- stroke="currentColor" -->
<!-- stroke-width="2" -->
<!-- stroke-linecap="round" -->
<!-- stroke-linejoin="round" -->
<!-- /> -->
<!-- Filled -->
<path
d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 6.00019C10.2006 3.90317 7.19377 3.2551 4.93923 5.17534C2.68468 7.09558 2.36727 10.3061 4.13778 12.5772C5.60984 14.4654 10.0648 18.4479 11.5249 19.7369C11.6882 19.8811 11.7699 19.9532 11.8652 19.9815C11.9483 20.0062 12.0393 20.0062 12.1225 19.9815C12.2178 19.9532 12.2994 19.8811 12.4628 19.7369C13.9229 18.4479 18.3778 14.4654 19.8499 12.5772C21.6204 10.3061 21.3417 7.07538 19.0484 5.17534C16.7551 3.2753 13.7994 3.90317 12 6.00019Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<!-- Filled -->
<!-- <path -->
<!-- d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z" -->
<!-- fill="currentColor" -->
<!-- /> -->
</svg>
</div>
</li>
@ -84,6 +173,10 @@
Fried Chicken
</p>
<p class="text-xs italic text-background-100">
Dinner
</p>
<div class="flex items-center justify-between w-full text-xs">
<p>90 min</p>
<svg
@ -117,6 +210,133 @@
Steak & Eggs
</p>
<p class="text-xs italic text-background-100">
Breakfast
</p>
<div class="flex items-center justify-between w-full text-xs">
<p>15 min</p>
<svg
class="h-6 text-red-500"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<!-- Hollow -->
<!-- <path -->
<!-- fill-rule="evenodd" -->
<!-- clip-rule="evenodd" -->
<!-- d="M12 6.00019C10.2006 3.90317 7.19377 3.2551 4.93923 5.17534C2.68468 7.09558 2.36727 10.3061 4.13778 12.5772C5.60984 14.4654 10.0648 18.4479 11.5249 19.7369C11.6882 19.8811 11.7699 19.9532 11.8652 19.9815C11.9483 20.0062 12.0393 20.0062 12.1225 19.9815C12.2178 19.9532 12.2994 19.8811 12.4628 19.7369C13.9229 18.4479 18.3778 14.4654 19.8499 12.5772C21.6204 10.3061 21.3417 7.07538 19.0484 5.17534C16.7551 3.2753 13.7994 3.90317 12 6.00019Z" -->
<!-- stroke="currentColor" -->
<!-- stroke-width="2" -->
<!-- stroke-linecap="round" -->
<!-- stroke-linejoin="round" -->
<!-- /> -->
<!-- Filled -->
<path
d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z"
fill="currentColor"
/>
</svg>
</div>
</li>
</ul>
</div>
</section>
<!-- Section: Make Again -->
<section class="w-full flex flex-col my-4 p-4">
<h2 class="text-md text-white my-2">
Make again
</h2>
<!-- List: Recent Recipes -->
<div class="overflow-x-auto whitespace-nowrap">
<ul class="inline-flex space-x-4">
<li class="flex-shrink-0 w-40 h-40 bg-gradient-to-t from-background-400 to-background-300
rounded-lg text-white shadow-black shadow-md flex flex-col items-start justify-end p-2">
<p class="overflow-hidden whitespace-nowrap text-ellipsis w-full">
Avacado Toast
</p>
<p class="text-xs italic text-background-100">
Breakfast
</p>
<div class="flex items-center justify-between w-full text-xs">
<p>10 min</p>
<svg
class="h-6 text-red-500"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<!-- Hollow -->
<!-- <path -->
<!-- fill-rule="evenodd" -->
<!-- clip-rule="evenodd" -->
<!-- d="M12 6.00019C10.2006 3.90317 7.19377 3.2551 4.93923 5.17534C2.68468 7.09558 2.36727 10.3061 4.13778 12.5772C5.60984 14.4654 10.0648 18.4479 11.5249 19.7369C11.6882 19.8811 11.7699 19.9532 11.8652 19.9815C11.9483 20.0062 12.0393 20.0062 12.1225 19.9815C12.2178 19.9532 12.2994 19.8811 12.4628 19.7369C13.9229 18.4479 18.3778 14.4654 19.8499 12.5772C21.6204 10.3061 21.3417 7.07538 19.0484 5.17534C16.7551 3.2753 13.7994 3.90317 12 6.00019Z" -->
<!-- stroke="currentColor" -->
<!-- stroke-width="2" -->
<!-- stroke-linecap="round" -->
<!-- stroke-linejoin="round" -->
<!-- /> -->
<!-- Filled -->
<path
d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z"
fill="currentColor"
/>
</svg>
</div>
</li>
<li class="flex-shrink-0 w-40 h-40 bg-gradient-to-t from-background-400 to-background-300
rounded-lg text-white shadow-black shadow-md flex flex-col items-start justify-end p-2">
<p class="overflow-hidden whitespace-nowrap text-ellipsis w-full">
Fried Chicken
</p>
<p class="text-xs italic text-background-100">
Dinner
</p>
<div class="flex items-center justify-between w-full text-xs">
<p>90 min</p>
<svg
class="h-6 text-white"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<!-- Hollow -->
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 6.00019C10.2006 3.90317 7.19377 3.2551 4.93923 5.17534C2.68468 7.09558 2.36727 10.3061 4.13778 12.5772C5.60984 14.4654 10.0648 18.4479 11.5249 19.7369C11.6882 19.8811 11.7699 19.9532 11.8652 19.9815C11.9483 20.0062 12.0393 20.0062 12.1225 19.9815C12.2178 19.9532 12.2994 19.8811 12.4628 19.7369C13.9229 18.4479 18.3778 14.4654 19.8499 12.5772C21.6204 10.3061 21.3417 7.07538 19.0484 5.17534C16.7551 3.2753 13.7994 3.90317 12 6.00019Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<!-- Filled -->
<!-- <path -->
<!-- d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z" -->
<!-- fill="currentColor" -->
<!-- /> -->
</svg>
</div>
</li>
<li class="flex-shrink-0 w-40 h-40 bg-gradient-to-t from-background-400 to-background-300
rounded-lg text-white shadow-black shadow-md flex flex-col items-start justify-end p-2">
<p class="overflow-hidden whitespace-nowrap text-ellipsis w-full">
Steak & Eggs
</p>
<p class="text-xs italic text-background-100">
Breakfast
</p>
<div class="flex items-center justify-between w-full text-xs">
<p>15 min</p>
<svg
@ -146,5 +366,18 @@
</ul>
</div>
</section>
<!-- Section: Recipe to Share Section -->
<section class="w-full my-8 p-4">
<div class="bg-gradient-to-tr from-primary-700 to-primary-600 rounded-lg px-8 py-12 text-center shadow-xl">
<h2 class="text-2xl font-bold text-white mb-4">Got a Recipe to Share?</h2>
<p class="text-white text-lg leading-relaxed mb-6">
Unleash your inner chef! Share your unique recipes with our community and inspire others with your culinary genius.
</p>
<button class="inline-block px-10 py-4 bg-white text-primary-700 font-bold text-lg rounded-full shadow-lg">
Create Now!
</button>
</div>
</section>
</div>
</div>

Binary file not shown.

View File

@ -0,0 +1,398 @@
# Technical Specifications
<!--toc:start-->
- [Technical Specifications](#technical-specifications)
- [Devices & Responsiveness](#devices-responsiveness)
- [Page Requirements](#page-requirements)
- [Home](#home)
- [UI Requirements](#ui-requirements)
- [API Requirements](#api-requirements)
- [Favorites](#favorites)
- [Create](#create)
- [Shopping List](#shopping-list)
- [Profile](#profile)
- [Database Requirements](#database-requirements)
- [Table ID Choice](#table-id-choice)
- [Tables](#tables)
- [Enums and Types](#enums-and-types)
<!--toc:end-->
Application specifications.
## Devices & Responsiveness
This application is a web app, however, it is built following a "mobile-first" pattern. Which
means the desktop interface is lacking and will be designed after the completion of the mobile
web interface.
## Page Requirements
This section outlines the specific technical requirements for the application. It is broken down
by page.
#### Home
The home page will server as the landing page where users can search recipes, filter their search, as
well as view lists of recently made recipes, recently viewed and trending recipes lists.
##### UI Requirements
- [ ] Welcome Banner
- [ ] Video display with large text overhead
- [ ] Sub text below the video with a "Call to Action"
- [ ] Interactive banner that can be hidden with a "Got it!" or "X"
- [ ] Message & Pills Banner
- [ ] Message with text like "Craving Something Specific?"
- [ ] Pill buttons below which allow for a direct search for meal type
- [ ] Search bar
- [ ] Ability to search for recipes, meals, and tags
- [ ] Filter drop down
- [ ] **Filter by meal** - *E_Meal enum type*
- [ ] **Filter by time requirements** - *15min, 30min, 1hr, 1.5hr, +1.5hr*
- [ ] **Filter by difficulty** - *1 to 5 stars*
- [ ] **Filter by serving size** - *1 to 16*
- [ ] Recipe of The Week
- [ ] **Single recipe display** of the best performing recipe, with spotlight effect
- [ ] **Display recipe details in depth** - Title, duration, **image***, meal category, simplified description
- [ ] **Make now** button to allow easy access to meal directions
- [ ] Recently Viewed List
- [ ] Scrolling list of the most recent 5-10 recipes viewed
- [ ] **Display recipe details** - Title, duration, **image***, meal category, and a heart to like the recipe
- [ ] Make Again List
- [ ] Scrolling list of the most recent 5-10 recipes made
- [ ] **Display recipe details** - Title, duration, **image***, meal category, and a heart to like the recipe
- [ ] Create Recipe CTA (call-to-action)
- [ ] **Large CTA banner** with text prompting users to create a recipe
- [ ] **Button** to take the user to the create page
'*': Not sure yet, still under consideration
##### API Requirements
- [ ] Message & Pills Banner
- [ ] Search all recipes of a specific meal type
- [ ] Search bar
- [ ] Text search on titles based on search query
- [ ] Text search on tags based on search query
- [ ] Text search on **meal** based on search query
- [ ] Filter drop down
- [ ] Update search to only contain meals from selected filter
- [ ] Update search to only contain means that meet the time requirement of the selected filter
- [ ] Update search to only contain meals that meet the difficulty level of the selected filter
- [ ] Update search to only contain meals that meet the serving size of the selected filter
- [ ] Recipe of The Week
- [ ] Fetch the most performing recipe of the last 7 days and all of the required meta data
- [ ] Most views (W: 0.1)
- [ ] Most makes (W: 0.5)
- [ ] Most likes (W: 0.3)
- [ ] Most reviews (W: 0.4)
- [ ] Highest avg rating (W: 0.2/star, 0.0 - 1.0)
- [ ] Direct a user to the recipes display page
- [ ] Telemetry data to collect clicks for each recipe
- [ ] Recently Viewed List
- [ ] Fetch a list of recently viewed meals and all required meta data
- [ ] Like a post and store it in user's liked posts list
- [ ] Direct a user to the recipes display page
- [ ] Telemetry data to collect clicks for each recipe
- [ ] Make Again List
- [ ] Fetch a list of recently viewed meals and all required meta data
- [ ] Like a post and store it in user's liked posts list
- [ ] Direct a user to the recipes display page
- [ ] Telemetry data to collect clicks for each recipe
'**': Not sure implementation
#### Favorites
#### Create
The create page will contain a page to create a new recipe. It will include
all of the required details without any need to move to other pages. The entire
creation process will take place here
##### UI Requirements
- [ ] Create Recipe Heading Banner
- [ ] Large, simple text banner that displays a header
- [ ] Smaller text section that contains some useful information for using the wizard
- [ ] Recipe Creation Wizard
- [ ] **Recipe Details:** required
- [ ] **Recipe title:** small text input (1 to 128)
- [ ] **Recipe description:** large text input
- [ ] **Meal category:** pill radio buttons or drop down*
- [ ] **Service size:** numeric input (1 to 16)
- [ ] **Difficulty rating:** star buttons that can be selected (up to 5)
- [ ] **Duration:** prep and cook time, individual numeric inputs
- [ ] **Ingredients:** required
- [ ] **Dynamic ingredient list:** List of each ingredient
- [ ] **Content:** name and quantity (both text inputs)
- [ ] **Actions:** delete button and **reorder element***
- [ ] **Add ingredient button:** Simple button to add a blank ingredient row
- [ ] **Instructions:** required
- [ ] **Dynamic instructions list:** Numbered list of each instruction
- [ ] **Content:** number and large text instruction
- [ ] **Rich text editor?***
- [ ] **Actions:** delete button and **reorder element***
- [ ] **Add step button:** Simple button to add a blank instruction element
- [ ] **Media & Tags:** optional
- [ ] **Image Upload**
- [ ] Single image selector for the thumbnail image
- [ ] Small image display once one has been upload
- [ ] Remove button to remove the image
- [ ] Replace button to replace the image
- [ ] **Tags**
- [ ] Text input to add tags
- [ ] Using list of existing tags, use a prefill while typing
- [ ] Tags that don't exist will be added
- [ ] Display a small list of added tags
- [ ] Clicking a tag will remove it from the list
- [ ] **Footer & Submit**
- [ ] Save recipe button to complete the form
- [ ] Button is disabled until the minimum required fields are complete*
- [ ] Input Validation
- [ ] Required elements should have a **required indicator**
- [ ] Required elements will be validated input
- [ ] **Valid:** Green outline or indicator* maybe a small, green check mark
- [ ] **Invalid:** Red outline or indicator* maybe a red border with error text
'*': Not sure yet, still under consideration
##### API Requirements
- [ ] Middleware
- [ ] **Authentication:** User must be logged in to access this page
- [ ] **Authentication:** User must be logged in to submit the creation of a recipe (fallback)
- [ ] Recipe Creation Wizard
- [ ] Create a new recipe object in the database
- [ ] Recipe should be attached to a user (logged in)
- [ ] User should be directed to the view recipe page on a successful creation
#### Shopping List
#### Profile
## Authentication
This section outlines the authentication requirements for the application. This section
is **not** broken down by page, they are simple defined.
#### Pages
- [ ] **Register Page**
- [ ] Input form with required details: *name, email and password*
- [ ] *Password strength meter**
- [ ] User should be directed to the home page when account is created
- [ ] User will be logged in
- [ ] A notification will be provided to the user indicating success and redirection
- [ ] User should see error messages when validation issues occur
- [ ] Email already in use
- [ ] Passwords do not match
- [ ] Server failure (this should never happen)
- [ ] **Sign In Page**
- [ ] Input form with required details: *email, password and forgot password button*
- [ ] User should be directed to the home page after signing in
- [ ] A notification will be provided to the user indicating success and redirection
- [ ] User should see error messages when validation issues occur
- [ ] Invalid password
- [ ] Server failure (this should never happen)
'*': Future ideas
#### API Requirements
- [ ] **Register Page**
- [ ] Create a new user in the database
- [ ] Password should be stored in the database as a hash
- [ ] Ensure that emails are not duplicated
- [ ] "Log user in" when account is created
- [ ] User should be redirected to the home page on success
- [ ] **Sign In Page**
- [ ] Sign user in and return data to be stored in the session
- [ ] Validate password to the hash stored in the DB
- [ ] User should be redirected to the home page on success
- [ ] **Session Management**
- [ ] *Uses cookies to store required data**
'*': Unsure on technical implementation
## Database Requirements
This section outlines the specific technical requirements for the database store for
this application. It will describe the required tables, fields, and other expectations.
##### Table ID Choice
Typically, I like to use UUID's for ID's. However, after some research I have concluded that for this
application, the use of the `SERIAL` or `BIGSERIAL` type will work sufficiently. Security is not a huge
concern with this application since no major data will be stored, however, measures will still be in
place (of course).
One of the biggest reasons **I** choose UUID's is their obscurity which provides some basic security
against specific attack styles (enumeration attacks, etc.), however, I do not anticipate that being a
large concern, so the simplicity of using an integer will surpass the small, niche benefits of using
UUID's.
### Tables
Below is a breakdown of the required tables and their respective fields. The fields will
also have a list of attributes which are to be implemented at the database level. **JSONB**
data fields will also have a small example object. A more in-depth data structure can be
found in **OTHER** section.
- [ ] Recipe: Represents a single recipe.
- [ ] ID (PK) BigSerial
- [ ] Title (Unique, Required) string(128)
- [ ] Description (Required) text
- [ ] Instructions (Required) string(1024)[]
- [ ] Serves (Required) int(0..16)
- [ ] Difficulty (Required) int(1..5)
- [ ] Duration (Required) JSONB({ "total": int, "prep": int, "cook": int })
- [ ] Category (Required) E_Meal (defined in the [enums](#enums-and-types) section)
- [ ] Ingredients (Required) JSONB({ "item_a": [{ "name": string, "quantity": string }], "item_b": ... })
- [ ] UserId (FK: User.Id) BigSerial
- [ ] Modified () date/time stamp
- [ ] Created (Required) date/time stamp
- [ ] User: Represents a single user.
- [ ] ID (PK) BigSerial
- [ ] Name (Required) string(64)
- [ ] Email (Unique, Required) string(128)
- [ ] Password (Required) string(128) *stored as hash***
- [ ] Created (Required) date/time stamp
- [ ] Engagement: Represents a single engagement from a single user.
- [ ] ID (PK) BigSerial
- [ ] Message () text (Used to store any relevant notes, if needed)
- [ ] Entity (BigSerial) BigSerial (Used to relate an entity, if needed)
- [ ] UserId (FK: User.Id, Required) BigSerial
- [ ] Created (Required) date/time stamp
- [ ] Like: **Many-to-many** table to represent a list of recipes liked by a user.
- [ ] ID (PK) *Composite key***
- [ ] UserId (FK: User.Id, Required) BigSerial
- [ ] RecipeId (FK: Recipe.Id, Required) BigSerial
- [ ] Created (Required) date/time stamp
- [ ] Tag: Represents a single tag that can be had by many recipes.
- [ ] ID (PK) BigSerial
- [ ] Name (Unique, Required) string(32)
- [ ] Created (Required) date/time stamp
- [ ] RecipeTag: **Many-to-many** table to represent a list of tags on a recipe.
- [ ] ID (PK) BigSerial
- [ ] RecipeId (FK: Recipe.Id, Required) BigSerial
- [ ] TagId (FK: Tag.Id, Required) BigSerial
- [ ] Created (Required) date/time stamp
- [ ] List: Represents a single users shopping list.
- [ ] ID (PK) BigSerial
- [ ] UserId (FK: User.Id, Required) BigSerial
- [ ] Content (Required) JSONB([ { "name": string, "quantity": string }, ... ])
- [ ] Created (Required) date/time stamp
- [ ] Image: Represents a single image used by a single recipe.
- [ ] ID (PK) BigSerial
- [ ] RecipeId (FK: Recipe.Id, Required) BigSerial
- [ ] Alt (Required) string(128) (alt text for accessibility, same as recipe title)
- [ ] Url (Required) text
- [ ] Created (Required) date/time stamp
- [ ] Review: Represents a single review on a recipe from a single author.
- [ ] ID (PK) BigSerial
- [ ] Comment (Required) text
- [ ] Rating () int(0..5) (Optional b/c nested replies don't need a rating)
- [ ] ReviewId (FK: Review.Id) BigSerial (This is used for nested replies)
- [ ] RecipeId (FK: Recipe.Id, Required) BigSerial
- [ ] UserId (FK: User.Id, Required) BigSerial
- [ ] Created (Required) date/time stamp
- [ ] Notification: Represents a single user notification.
- [ ] ID (PK) BigSerial
- [ ] UserId (FK: User.Id, Required) BigSerial
- [ ] Type (Required) E_Notification
- [ ] Message (Required) text
- [ ] Entity (BigSerial) BigSerial (Used to relate an entity, if needed)
- [ ] Read (Required, Default: F) boolean
- [ ] Created (Required) date/time stamp
'**': Not sure implementation
### Enums and Types
Below is a breakdown of the required enumerated types that should be stored in the database.
Various tables will reference these types.
- [ ] E_Meal: Type to represent the type of meal of a recipe.
- [ ] breakfast: string
- [ ] lunch: string
- [ ] dinner: string
- [ ] desert: string
- [ ] snack: string
- [ ] side: string
- [ ] other: string
- [ ] E_Notification: Type to represent a type of user notification.
- [ ] comment: string
- [ ] like: string
- [ ] system: string
- [ ] E_Engagement: Type to represent a type of user engagement.
- [ ] made: string
- [ ] liked: string
- [ ] viewed: string
- [ ] shared: string
- [ ] reviewed: string
- [ ] rated: string