Sign In Prompt

This commit is contained in:
2024-09-15 04:43:05 -04:00
parent 2164f330ac
commit 062a9adf04
16 changed files with 149 additions and 145 deletions

BIN
server.zip Normal file

Binary file not shown.

View File

@@ -72,7 +72,7 @@ export default class FDAI {
} }
const response = await ollama.generate({ const response = await ollama.generate({
model: 'meta-llama-3-1-8b-1', model: 'llama3:latest',
format: "json", format: "json",
prompt: prompt:
`Give one food suggestion for these question answers and then generate a recipe. `Give one food suggestion for these question answers and then generate a recipe.

View File

@@ -28,9 +28,8 @@ export default class RecipeAPI extends APIRoute {
let aiResult = await ai.suggestFood(recipe.currentQuestion, recipe.answers, recipe.restrictions); let aiResult = await ai.suggestFood(recipe.currentQuestion, recipe.answers, recipe.restrictions);
console.log(aiResult);
let suggestFood = JSON.parse(aiResult.response); let suggestFood = JSON.parse(aiResult.response);
suggestFood.userID = recipe.userID;
let result = await db.create(suggestFood); let result = await db.create(suggestFood);
@@ -38,6 +37,7 @@ export default class RecipeAPI extends APIRoute {
} }
async rate(req, res) { async rate(req, res) {
let db = req.app.get('mongo').recipes; let db = req.app.get('mongo').recipes;
let recipe = await db.get(req.params.id); let recipe = await db.get(req.params.id);
@@ -54,9 +54,7 @@ export default class RecipeAPI extends APIRoute {
return; return;
} }
recipe.rating = rating; let result = await db.update(req.params.id, { rating: recipe.rating + parseInt(rating), ratingCount: recipe.ratingCount + 1 });
let result = await db.update(req.params.id, { rating: rating });
res.send(result); res.send(result);
} }

View File

@@ -1,32 +0,0 @@
import APIRoute from "../APIRoute.js";
export default class UsersAPI extends APIRoute {
constructor() {
super('/users');
this.addSubRoute('/create', 'post', this.createUser);
}
async get(req, res) {
res.send('GET request');
}
async post(req, res) {
res.send('POST request');
}
async createUser(req, res) {
let user = req.body;
let db = req.app.get('mongo').users;
let result = await db.create({
recipes: [],
dietaryRestrictions: [],
firstName: "String",
lastName: "OtherString",
email: ""
});
}
}

View File

@@ -1,11 +1,9 @@
import mongoose from "mongoose"; import mongoose from "mongoose";
import Users from "./collections/users.js";
import Recipes from "./collections/recipes.js"; import Recipes from "./collections/recipes.js";
export default class Mongo { export default class Mongo {
constructor(uri) { constructor(uri) {
this.connect(uri); this.connect(uri);
this.users = new Users();
this.recipes = new Recipes(); this.recipes = new Recipes();
} }

View File

@@ -19,7 +19,8 @@ const recipesSchema = new mongoose.Schema({
rating: Number, rating: Number,
cuisine: String, cuisine: String,
expense: Number, expense: Number,
mealType: Object mealType: Object,
ratingCount: Number
}, { timestamps: true }); }, { timestamps: true });
export default class Recipes { export default class Recipes {
@@ -43,7 +44,9 @@ export default class Recipes {
cuisine: recipe.cuisine, cuisine: recipe.cuisine,
expense: recipe.expense, expense: recipe.expense,
mealType: recipe.mealType, mealType: recipe.mealType,
instructions: recipe.instructions instructions: recipe.instructions,
ratingCount: 0,
rating: 0
}, this.upsert); }, this.upsert);
return await this.get(Id); return await this.get(Id);
} }
@@ -63,6 +66,13 @@ export default class Recipes {
return await this.get(Id); return await this.get(Id);
} }
async increment(Id, field, amount) {
let result = await this.get(Id);
if(!result) return null;
return await this.get(Id);
}
async delete(Id) { async delete(Id) {
let result = await this.get(Id); let result = await this.get(Id);
if(!result) return false; if(!result) return false;

View File

@@ -1,74 +0,0 @@
import mongoose from "mongoose";
import { v4 as uuidv4 } from 'uuid';
const reqString = {
type: String,
required: true
}
const UserSchema = new mongoose.Schema({
id: reqString,
recipes: Array,
dietaryRestrictions: Array,
firstName: String,
lastName: String,
email: String
}, { timestamps: true });
export default class Users {
constructor() {
this.model = mongoose.model('users', UserSchema);
this.upsert = { upsert: true };
}
async create(user) {
if(await this.model.findOne({ username: user.username }) || await this.model.findOne({ email: user.email })) return null;
let Id = uuidv4();
await this.model.findOneAndUpdate({ id: Id }, {
id: Id,
recipes: user.recipes,
dietaryRestrictions: user.dietaryRestrictions,
firstName: user.firstName,
lastName: user.lastName,
email: user.Email
}, this.upsert);
return await this.get(user.id);
}
async get(Id) {
let data = await this.model.findOne({ id: Id });
if(data) data.password = undefined;
return data;
}
async getByUsername(username) {
let data = await this.model.findOne({ username: username }) || await this.model.findOne({ email: username });
if(data) data.password = undefined;
return data;
}
async getAll(query) {
let data = await this.model.find(query);
data.forEach(user => {
user.password = undefined;
});
return data
}
async update(Id, data) {
if(!(await this.get(Id))) return null;
await this.model.findOneAndUpdate({ id: Id }, data, this.upsert);
return await this.get(Id);
}
async delete(Id) {
let result = await this.get(Id);
if(!result) return false;
await this.model.deleteOne({ id: Id })
return true;
}
}

View File

@@ -3,10 +3,9 @@
</script> </script>
<nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav"> <nav class="navbar navbar-expand-md sticky-top py-3 navbar-dark" id="mainNav">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span class="bs-icon-sm bs-icon-circle bs-icon-primary shadow d-flex justify-content-center align-items-center me-2 bs-icon"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-bezier"> <div class="container"><a class="navbar-brand d-flex align-items-center" href="/">
<path fill-rule="evenodd" d="M0 10.5A1.5 1.5 0 0 1 1.5 9h1A1.5 1.5 0 0 1 4 10.5v1A1.5 1.5 0 0 1 2.5 13h-1A1.5 1.5 0 0 1 0 11.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm10.5.5A1.5 1.5 0 0 1 13.5 9h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zM6 4.5A1.5 1.5 0 0 1 7.5 3h1A1.5 1.5 0 0 1 10 4.5v1A1.5 1.5 0 0 1 8.5 7h-1A1.5 1.5 0 0 1 6 5.5zM7.5 4a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5z"></path> <span>Food Decisive</span></a>
<path d="M6 4.5H1.866a1 1 0 1 0 0 1h2.668A6.517 6.517 0 0 0 1.814 9H2.5c.123 0 .244.015.358.043a5.517 5.517 0 0 1 3.185-3.185A1.503 1.503 0 0 1 6 5.5zm3.957 1.358A1.5 1.5 0 0 0 10 5.5v-1h4.134a1 1 0 1 1 0 1h-2.668a6.517 6.517 0 0 1 2.72 3.5H13.5c-.123 0-.243.015-.358.043a5.517 5.517 0 0 0-3.185-3.185z"></path> <button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
</svg></span><span>Food Decisive</span></a><button data-bs-toggle="collapse" class="navbar-toggler" data-bs-target="#navcol-1"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navcol-1"> <div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav mx-auto"> <ul class="navbar-nav mx-auto">
<li class="nav-item"><a class="nav-link active" href="/">Home</a></li> <li class="nav-item"><a class="nav-link active" href="/">Home</a></li>

View File

@@ -11,6 +11,7 @@
numQuestion: 5 numQuestion: 5
} }
}); });
location.href = "#form";
} }
function open10Survey(questions) { function open10Survey(questions) {
@@ -21,6 +22,7 @@
numQuestion: 10 numQuestion: 10
} }
}); });
location.href = "#form";
} }
function open15Survey(questions) { function open15Survey(questions) {
@@ -31,6 +33,7 @@
numQuestion: 15 numQuestion: 15
} }
}); });
location.href = "#form";
} }
@@ -61,4 +64,8 @@
</div> </div>
<br> <br>
<br> <br>
<p class="card-text" style="--bs-body-font-size: 1rem;font-size: 24px;"><strong><span style="background-color: transparent;">The more questions you answer, the more personalized your recommendation will be!</span></strong></p> <p style="text-align:center !important; --bs-body-font-size: 1rem;font-size: 24px;">
<strong>
<span style="background-color: transparent;">The more questions you answer, the more personalized your recommendation will be!</span>
</strong>
</p>

View File

@@ -0,0 +1,6 @@
<div style="background-color: --bs-body-bg; margin: 20px; display:block;">
<h2 class="card-title" style="text-align: center;">Do you want to sign in?</h2>
<p style="text-align: center;">By Signing in you can save your suggested foods!!</p>
</div>

View File

@@ -6,6 +6,8 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import SignIn from './SignIn.svelte';
let formData = writable({ let formData = writable({
answers: [], answers: [],
currentQuestion: 1, currentQuestion: 1,
@@ -14,6 +16,8 @@
export let numQuestion = 0; export let numQuestion = 0;
let inputEle; let inputEle;
let submitEle;
let modelEle;
const questions = [ const questions = [
"Is this for breakfast, lunch, dinner, snack or dessert?", "Is this for breakfast, lunch, dinner, snack or dessert?",
@@ -40,6 +44,13 @@
button.classList.toggle('active'); button.classList.toggle('active');
}); });
}); });
setTimeout(() => {
if (!$page.data.authInfo) {
let modal = new bootstrap.Modal(modelEle);
modal.show();
}
}, 1000);
}); });
function nextQuestion() { function nextQuestion() {
@@ -52,6 +63,9 @@
async function submitForm() { async function submitForm() {
submitEle.disabled = true;
submitEle.innerText = 'Finding your food...';
let obj = $formData; let obj = $formData;
obj.answers.push(inputEle.value); obj.answers.push(inputEle.value);
obj.restrictions = []; obj.restrictions = [];
@@ -73,18 +87,35 @@
recipeData = await recipeData.json(); recipeData = await recipeData.json();
console.log(recipeData);
goto(`/food/${recipeData.id}`, { replaceState: true }); goto(`/food/${recipeData.id}`, { replaceState: true });
} }
function signIn() {
goto("https://auth.fooddecisive.co/");
}
</script> </script>
<div class="container"> <div class="container">
{ #if !$page.data.authInfo }
<div bind:this={modelEle} class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<SignIn />
<button type="button" class="btn btn-primary" on:click={signIn}>Sign In</button>
<br>
<br>
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{ /if }
<div class="row mb-5"> <div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto"> <div class="text-center">
<h2 class="fw-bold">Question {$formData.currentQuestion}&nbsp;</h2> <h2 class="fw-bold">Question {$formData.currentQuestion} of {numQuestion}</h2>
<p class="text-muted">Question {$formData.currentQuestion} of {numQuestion}</p>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -134,7 +165,7 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
{#if $formData.currentQuestion === numQuestion} {#if $formData.currentQuestion === numQuestion}
<button class="btn btn-primary" type="button" on:click={submitForm} >Submit</button> <button bind:this={submitEle} class="btn btn-primary" type="button" on:click={submitForm} >Submit</button>
{:else} {:else}
<button class="btn btn-primary" type="button" on:click={nextQuestion} >Next</button> <button class="btn btn-primary" type="button" on:click={nextQuestion} >Next</button>
{/if} {/if}

18
src/lib/food.css Normal file
View File

@@ -0,0 +1,18 @@
.star-rating {
font-size: 0;
}
.star-rating .star {
display: inline-block;
font-size: 2rem;
color: #ffffff;
transition: transform 200ms;
}
.star-rating .star.active {
color: #ff6a00;
}
.star-rating .star:hover {
transform: scale(1.5);
}

View File

@@ -4,8 +4,7 @@
// enableBackgroundTokenRefresh: true, // enableBackgroundTokenRefresh: true,
// }); // });
export async function load() { // export async function load() {
// const authInfo = await authClient.getAuthenticationInfoOrNull(); // const authInfo = await authClient.getAuthenticationInfoOrNull();
// console.log("authInfo", authInfo) // console.log("authInfo", authInfo)
@@ -18,4 +17,4 @@ export async function load() {
// return { // return {
// authInfo: authInfo ? authInfo : null // authInfo: authInfo ? authInfo : null
// }; // };
} // }

View File

@@ -1,5 +1,6 @@
<script> <script>
// @ts-nocheck // @ts-nocheck
import '$lib/food.css';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { onMount } from "svelte"; import { onMount } from "svelte";
@@ -10,6 +11,8 @@
$: showRecipe = false; $: showRecipe = false;
let preRating = Math.round(foodData.rating / foodData.ratingCount);
function CLshowRecipe() { function CLshowRecipe() {
showRecipe = true; showRecipe = true;
@@ -24,6 +27,35 @@
}, 500); }, 500);
} }
async function rateFoodSuggestion(rating) {
const response = await fetch(`/api/recipes/${foodData.id}/rate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ rating })
});
}
onMount(() => {
const stars = document.querySelectorAll('.star');
stars.forEach((star, index) => {
star.addEventListener('click', () => {
const rating = star.dataset.rating;
stars.forEach((s, i) => {
if (i <= index) {
s.classList.add('active');
} else {
s.classList.remove('active');
}
});
rateFoodSuggestion(rating);
}, { once: true });
});
});
</script> </script>
@@ -89,6 +121,16 @@
</div> </div>
{/if} {/if}
<h4 class="card-title" style="text-align: center;">Rate Your Suggested Food!</h4> <h4 class="card-title" style="text-align: center;">Rate Your Suggested Food!</h4>
<div style="margin: auto;" class="star-rating">
{#each [1, 2, 3, 4, 5] as rating}
{#if preRating >= rating}
<span class="star active" data-rating={rating} style="font-size: 50px;">&#9733;</span>
{:else}
<span class="star" data-rating={rating} style="font-size: 50px;">&#9733;</span>
{/if}
{/each}
</div>
<br> <br>
<br> <br>
</div> </div>

View File

@@ -18,12 +18,14 @@
<section class="py-5"> <section class="py-5">
<div class="container py-5"> <div class="container py-5">
<div class="row mb-5" style="padding-bottom: 20px;"> <div class="row mb-5" style="padding-bottom: 5px;">
<div class="col-md-8 col-xl-6 text-center mx-auto"> <div class="col-md-8 col-xl-6 text-center mx-auto">
<h2 class="fw-bold"><strong><span style="background-color: transparent;">How many questions do you want to answer?</span></strong></h2> <h2 id="form" class="fw-bold"><strong>
<span style="background-color: transparent;">How many questions do you want to answer?</span></strong>
</h2>
</div> </div>
</div> </div>
<div id="form" class="card" style="padding-top: 20px;padding-bottom: 100px;"> <div class="card" style="padding-top: 20px;padding-bottom: 100px;">
<div bind:this={parentDiv} class="card-body"></div> <div bind:this={parentDiv} class="card-body"></div>
</div> </div>
</div> </div>