Sign In Prompt
This commit is contained in:
BIN
server.zip
Normal file
BIN
server.zip
Normal file
Binary file not shown.
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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: ""
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
6
src/lib/components/form/SignIn.svelte
Normal file
6
src/lib/components/form/SignIn.svelte
Normal 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>
|
||||
@@ -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} </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
18
src/lib/food.css
Normal 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);
|
||||
}
|
||||
@@ -4,8 +4,7 @@
|
||||
// enableBackgroundTokenRefresh: true,
|
||||
// });
|
||||
|
||||
export async function load() {
|
||||
|
||||
// export async function load() {
|
||||
|
||||
// const authInfo = await authClient.getAuthenticationInfoOrNull();
|
||||
// console.log("authInfo", authInfo)
|
||||
@@ -18,4 +17,4 @@ export async function load() {
|
||||
// return {
|
||||
// authInfo: authInfo ? authInfo : null
|
||||
// };
|
||||
}
|
||||
// }
|
||||
@@ -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;">★</span>
|
||||
{:else}
|
||||
<span class="star" data-rating={rating} style="font-size: 50px;">★</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user