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({
model: 'meta-llama-3-1-8b-1',
model: 'llama3:latest',
format: "json",
prompt:
`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);
console.log(aiResult);
let suggestFood = JSON.parse(aiResult.response);
suggestFood.userID = recipe.userID;
let result = await db.create(suggestFood);
@@ -38,6 +37,7 @@ export default class RecipeAPI extends APIRoute {
}
async rate(req, res) {
let db = req.app.get('mongo').recipes;
let recipe = await db.get(req.params.id);
@@ -54,9 +54,7 @@ export default class RecipeAPI extends APIRoute {
return;
}
recipe.rating = rating;
let result = await db.update(req.params.id, { rating: rating });
let result = await db.update(req.params.id, { rating: recipe.rating + parseInt(rating), ratingCount: recipe.ratingCount + 1 });
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 Users from "./collections/users.js";
import Recipes from "./collections/recipes.js";
export default class Mongo {
constructor(uri) {
this.connect(uri);
this.users = new Users();
this.recipes = new Recipes();
}

View File

@@ -19,7 +19,8 @@ const recipesSchema = new mongoose.Schema({
rating: Number,
cuisine: String,
expense: Number,
mealType: Object
mealType: Object,
ratingCount: Number
}, { timestamps: true });
export default class Recipes {
@@ -43,7 +44,9 @@ export default class Recipes {
cuisine: recipe.cuisine,
expense: recipe.expense,
mealType: recipe.mealType,
instructions: recipe.instructions
instructions: recipe.instructions,
ratingCount: 0,
rating: 0
}, this.upsert);
return await this.get(Id);
}
@@ -63,6 +66,13 @@ export default class Recipes {
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) {
let result = await this.get(Id);
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>
<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">
<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>
<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>
</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="container"><a class="navbar-brand d-flex align-items-center" href="/">
<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">
<ul class="navbar-nav mx-auto">
<li class="nav-item"><a class="nav-link active" href="/">Home</a></li>

View File

@@ -11,6 +11,7 @@
numQuestion: 5
}
});
location.href = "#form";
}
function open10Survey(questions) {
@@ -21,6 +22,7 @@
numQuestion: 10
}
});
location.href = "#form";
}
function open15Survey(questions) {
@@ -31,6 +33,7 @@
numQuestion: 15
}
});
location.href = "#form";
}
@@ -61,4 +64,8 @@
</div>
<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 { page } from '$app/stores';
import SignIn from './SignIn.svelte';
let formData = writable({
answers: [],
currentQuestion: 1,
@@ -14,6 +16,8 @@
export let numQuestion = 0;
let inputEle;
let submitEle;
let modelEle;
const questions = [
"Is this for breakfast, lunch, dinner, snack or dessert?",
@@ -40,6 +44,13 @@
button.classList.toggle('active');
});
});
setTimeout(() => {
if (!$page.data.authInfo) {
let modal = new bootstrap.Modal(modelEle);
modal.show();
}
}, 1000);
});
function nextQuestion() {
@@ -52,6 +63,9 @@
async function submitForm() {
submitEle.disabled = true;
submitEle.innerText = 'Finding your food...';
let obj = $formData;
obj.answers.push(inputEle.value);
obj.restrictions = [];
@@ -73,18 +87,35 @@
recipeData = await recipeData.json();
console.log(recipeData);
goto(`/food/${recipeData.id}`, { replaceState: true });
}
function signIn() {
goto("https://auth.fooddecisive.co/");
}
</script>
<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="col-md-8 col-xl-6 text-center mx-auto">
<h2 class="fw-bold">Question {$formData.currentQuestion}&nbsp;</h2>
<p class="text-muted">Question {$formData.currentQuestion} of {numQuestion}</p>
<div class="text-center">
<h2 class="fw-bold">Question {$formData.currentQuestion} of {numQuestion}</h2>
</div>
</div>
<div class="row">
@@ -134,7 +165,7 @@
<div class="row">
<div class="col">
{#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}
<button class="btn btn-primary" type="button" on:click={nextQuestion} >Next</button>
{/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,18 +4,17 @@
// enableBackgroundTokenRefresh: true,
// });
export async function load() {
// export async function load() {
// const authInfo = await authClient.getAuthenticationInfoOrNull();
// console.log("authInfo", authInfo)
// if (authInfo) {
// console.log("User is logged in as", authInfo.user.email)
// } else {
// console.log("User is not logged in")
// }
// const authInfo = await authClient.getAuthenticationInfoOrNull();
// console.log("authInfo", authInfo)
// if (authInfo) {
// console.log("User is logged in as", authInfo.user.email)
// } else {
// console.log("User is not logged in")
// }
// return {
// authInfo: authInfo ? authInfo : null
// };
}
// return {
// authInfo: authInfo ? authInfo : null
// };
// }

View File

@@ -1,5 +1,6 @@
<script>
// @ts-nocheck
import '$lib/food.css';
import { page } from '$app/stores';
import { onMount } from "svelte";
@@ -10,6 +11,8 @@
$: showRecipe = false;
let preRating = Math.round(foodData.rating / foodData.ratingCount);
function CLshowRecipe() {
showRecipe = true;
@@ -24,6 +27,35 @@
}, 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>
@@ -89,6 +121,16 @@
</div>
{/if}
<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>
</div>

View File

@@ -18,12 +18,14 @@
<section class="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">
<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 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>
</div>