AI and Food

This commit is contained in:
2024-09-15 01:38:51 -04:00
parent 39873ab767
commit f8ad3b2f72
14 changed files with 344 additions and 193 deletions

View File

@@ -2,28 +2,28 @@ import {Ollama} from 'ollama'
const ollama = new Ollama({ host: '172.29.186.121:11434' }) const ollama = new Ollama({ host: '172.29.186.121:11434' })
const response = await ollama.chat({ // const response = await ollama.chat({
model: 'llama3:latest', // model: 'llama3:latest',
messages: [{ role: 'user', content: // messages: [{ role: 'user', content:
`Generate a recipe for blueberry muffins: // `Generate a recipe for blueberry muffins:
Format it in a json object like this and don't send any other text: // Format it in a json object like this and don't send any other text:
{ // {
"name": "Blueberry Muffins", // "name": "Blueberry Muffins",
"description": "A delicious and healthy blueberry muffin recipe", // "description": "A delicious and healthy blueberry muffin recipe",
"nutritional_information": {}, // "nutritional_information": {},
ingredients: [ // ingredients: [
{ // {
"name": "flour", // "name": "flour",
"quantity": "2 cups" // "quantity": "2 cups"
} // }
], // ],
instructions: [ // instructions: [
"Preheat oven to 350 degrees" // "Preheat oven to 350 degrees"
] // ]
} // }
` // `
}], // }],
}); // });
const questions = [ const questions = [
"Is this for breakfast, lunch, dinner, snack or dessert?", "Is this for breakfast, lunch, dinner, snack or dessert?",
@@ -42,31 +42,27 @@ const questions = [
"Are you looking for budget-friendly options, or are you willing to splurge a bit for a special meal?", "Are you looking for budget-friendly options, or are you willing to splurge a bit for a special meal?",
"Whats the calorie range you want your meal to stay in? (You can specify yes or no)" "Whats the calorie range you want your meal to stay in? (You can specify yes or no)"
] ]
const testAnswers =[ // const testAnswers =[
"Lunch", // "Lunch",
"Italian", // "Italian",
"Healthy", // "Healthy",
"Cheese", // "Cheese",
"Beans", // "Beans",
"Moderate cook time", // "Moderate cook time",
"Chicken", // "Chicken",
"Medium", // "Medium",
"Wine", // "Wine",
"Regular Day", // "Regular Day",
"Crispy", // "Crispy",
"Oven,Microwave,Stove", // "Oven,Microwave,Stove",
"few leftovers", // "few leftovers",
"Budget-Friendly", // "Budget-Friendly",
"800-1000" // "800-1000"
] // ]
const restrictions=[ export default class FDAI {
]
class aiClass{
constructor(){ constructor(){
this.ai = new Ollama({ host: '172.29.186.121:11434' }); this.ai = new Ollama({ host: process.env.AI_URL });
} }
async suggestFood(questionAmount, answers, restrictions) { async suggestFood(questionAmount, answers, restrictions) {
@@ -77,19 +73,52 @@ class aiClass{
qaA += `Q${i}. ${answers[i]}\nA${i}. ${answers[i]}` qaA += `Q${i}. ${answers[i]}\nA${i}. ${answers[i]}`
} }
const response = await ollama.chat({ const response = await ollama.generate({
model: 'llama3:latest', model: 'llama3:latest',
messages: [{ role: 'user', content: format: "json",
`Give one food suggestion this question answers. prompt:
`Give one food suggestion for these question answers and then generate a recipe.
${qaA}
Consider the following restrictions: ${restrictions.join(", ")}
Format it in a json object like this example:
{
"name": "Blueberry Muffins",
"description": "A delicious and healthy blueberry muffin recipe",
"cuisineType": "American",
"estimatedExpense": 5,
"mealType": "Breakfast",
"nutritionFacts": {
"calories": "350-400",
"fat": "15-20g",
"carbs": "30-40g",
"protein": "35-40g"
},
ingredients: [
{
"name": "flour",
"quantity": "2 cups"
}
],
instructions: [
"Preheat oven to 350 degrees"
]
}
Do not send any other text other than the json object and or add extra text to the json object.
` `
}],
}); });
this.clearChatHistory();
return response;
} }
async clearChatHistory() {
await ollama.generate({
model: 'llama3:latest',
prompt: "clear all previous responses",
format: "json"
});
}
} }
console.log(response)
console.log(response.message.content)

View File

@@ -1,18 +1,24 @@
import APIRoute from "../APIRoute.js"; import APIRoute from "../APIRoute.js";
export default class UsersAPI extends APIRoute { import FDAI from "../ai.js";
const ai = new FDAI();
export default class RecipeAPI extends APIRoute {
constructor() { constructor() {
super('/recipes'); super('/recipes');
this.addSubRoute('/create', 'post', createRecipe); this.addSubRoute('/create', 'post', this.createRecipe);
this.addSubRoute('/:id', 'get', this.get);
this.addSubRoute('/:id/rate', 'post', this.rate);
} }
async get(req, res) { async get(req, res) {
res.send('GET request'); let db = req.app.get('mongo').recipes;
}
async post(req, res) { let result = await db.get(req.params.id);
res.send('POST request');
res.send(result);
} }
async createRecipe(req, res) { async createRecipe(req, res) {
@@ -20,16 +26,37 @@ export default class UsersAPI extends APIRoute {
let db = req.app.get('mongo').recipes; let db = req.app.get('mongo').recipes;
let result = await db.create({ let aiResult = await ai.suggestFood(recipe.currentQuestion, recipe.answers, recipe.restrictions);
userID: recipe.userID,
ingredients: recipe.ingredients,
productName: recipe.productName,
nutritionFacts: recipe.nutritionFacts,
rating: recipe.rating,
cuisine: recipe.cuisine,
expense: recipe.expense,
mealType: recipe.mealType
});
let suggestFood = JSON.parse(aiResult.response);
let result = await db.create(suggestFood);
res.json(result);
} }
async rate(req, res) {
let db = req.app.get('mongo').recipes;
let recipe = await db.get(req.params.id);
if (!recipe) {
res.status(404).send('Recipe not found');
return;
}
let rating = req.body.rating;
if (rating < 0 || rating > 5) {
res.status(400).send('Rating must be between 0 and 5');
return;
}
recipe.rating = rating;
let result = await db.update(req.params.id, { rating: rating });
res.send(result);
}
} }

View File

@@ -1,10 +1,12 @@
import mongoose from "mongoose"; import mongoose from "mongoose";
import Users from "./collections/users.js"; import Users from "./collections/users.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.users = new Users();
this.recipes = new Recipes();
} }
async connect(uri) { async connect(uri) {

View File

@@ -10,18 +10,16 @@ const reqString = {
const recipesSchema = new mongoose.Schema({ const recipesSchema = new mongoose.Schema({
id: reqString, id: reqString,
description: String,
userID: String, userID: String,
ingredients: Array, ingredients: Array,
productName: String, instructions: Array,
name: String,
nutritionFacts: Object, nutritionFacts: Object,
rating: Number, rating: Number,
cuisine: String, cuisine: String,
expense: Number, expense: Number,
mealType: Object mealType: Object
}, { timestamps: true }); }, { timestamps: true });
export default class Recipes { export default class Recipes {
@@ -39,18 +37,15 @@ export default class Recipes {
id: Id, id: Id,
userID: recipe.userID, userID: recipe.userID,
ingredients: recipe.ingredients, ingredients: recipe.ingredients,
productName: recipe.productName, description: recipe.description,
name: recipe.name,
nutritionFacts: recipe.nutritionFacts, nutritionFacts: recipe.nutritionFacts,
rating: recipe.rating,
cuisine: recipe.cuisine, cuisine: recipe.cuisine,
expense: recipe.expense, expense: recipe.expense,
mealType: recipe.mealType mealType: recipe.mealType,
instructions: recipe.instructions
}, this.upsert); }, this.upsert);
return await this.get(user.id); return await this.get(Id);
} }
async get(Id) { async get(Id) {

View File

@@ -1,3 +1,7 @@
<script>
import { page } from '$app/stores';
</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="/"><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 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>
@@ -8,7 +12,12 @@
<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>
<li class="nav-item"><a class="nav-link" href="/survey">Survey</a></li> <li class="nav-item"><a class="nav-link" href="/survey">Survey</a></li>
<li class="nav-item"><a class="nav-link" href="/contacts">Contact Us</a></li> <li class="nav-item"><a class="nav-link" href="/contacts">Contact Us</a></li>
</ul><a class="btn btn-primary shadow" role="button" href="signup.html" style="background: rgb(55, 99, 244);">Sign up</a> </ul>
{ #if $page.data.authInfo }
<a class="btn btn-primary shadow" role="button" href="https://auth.fooddecisive.co/account" style="background: rgb(55, 99, 244);">Dashboard</a>
{ :else }
<a class="btn btn-primary shadow" role="button" href="https://auth.fooddecisive.co/" style="background: rgb(55, 99, 244);">Sign up</a>
{ /if }
</div> </div>
</div> </div>
</nav> </nav>

View File

@@ -3,11 +3,7 @@
import Survey from "./Survey.svelte"; import Survey from "./Survey.svelte";
export let parentDiv; export let parentDiv;
console.log(parentDiv);
function open5Survey(questions) { function open5Survey(questions) {
console.log(questions);
parentDiv.innerHTML = ""; parentDiv.innerHTML = "";
new Survey({ new Survey({
target: parentDiv, target: parentDiv,
@@ -18,8 +14,6 @@
} }
function open10Survey(questions) { function open10Survey(questions) {
console.log(questions);
parentDiv.innerHTML = ""; parentDiv.innerHTML = "";
new Survey({ new Survey({
target: parentDiv, target: parentDiv,
@@ -30,8 +24,6 @@
} }
function open15Survey(questions) { function open15Survey(questions) {
console.log(questions);
parentDiv.innerHTML = ""; parentDiv.innerHTML = "";
new Survey({ new Survey({
target: parentDiv, target: parentDiv,

View File

@@ -1,11 +1,15 @@
<script> <script>
// @ts-nocheck // @ts-nocheck
import '../../survey.css';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
let formData = writable({ let formData = writable({
answers: [], answers: [],
currentQuestion: 1 currentQuestion: 1,
userID: $page.data.authInfo ? $page.data.authInfo.id : null,
}); });
export let numQuestion = 0; export let numQuestion = 0;
@@ -29,22 +33,54 @@
"Whats the calorie range you want your meal to stay in? (You can specify yes or no)" "Whats the calorie range you want your meal to stay in? (You can specify yes or no)"
]; ];
console.log(numQuestion); onMount(() => {
const buttons = document.querySelectorAll('.btn-info');
buttons.forEach(button => {
button.addEventListener('click', () => {
button.classList.toggle('active');
});
});
});
function nextQuestion() { function nextQuestion() {
if ($formData.currentQuestion === numQuestion) return; if ($formData.currentQuestion === numQuestion) return;
$formData.currentQuestion++; $formData.currentQuestion++;
$formData.answers.push(inputEle.value); $formData.answers.push(inputEle.value);
inputEle.value = '';
} }
function submitForm() { async function submitForm() {
let obj = $formData;
obj.answers.push(inputEle.value);
obj.restrictions = [];
const buttons = document.querySelectorAll('.btn-info');
buttons.forEach(button => {
if (button.classList.contains('active')) {
obj.restrictions.push(button.innerText);
}
});
let recipeData = await fetch('/api/recipes/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(obj)
});
recipeData = await recipeData.json();
console.log(recipeData);
goto(`/food/${recipeData.id}`, { replaceState: true });
} }
</script> </script>
<div class="container py-5"> <div class="container">
<div class="row mb-5"> <div class="row mb-5">
<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">Question {$formData.currentQuestion}&nbsp;</h2> <h2 class="fw-bold">Question {$formData.currentQuestion}&nbsp;</h2>
@@ -57,7 +93,7 @@
<div class="card-body mx-auto"> <div class="card-body mx-auto">
<p class="card-text" style="font-size: 40px;"> <p class="card-text" style="font-size: 40px;">
<strong> <strong>
<span style="background-color: transparent;">{questions[$formData.currentQuestion]}</span> <span style="background-color: transparent;">{questions[$formData.currentQuestion-1]}</span>
</strong> </strong>
</p> </p>
</div> </div>
@@ -70,6 +106,31 @@
</div> </div>
</div> </div>
<br> <br>
<h4 class="card-title" style="text-align: center;">Select Dietary Restriction</h4>
<div class="row">
<div class="col">
<div class="btn-group" role="group" style="width: 100%;">
<button class="btn btn-info" type="button">Vegetarian</button>
<button class="btn btn-info" type="button">Vegan</button>
<button class="btn btn-info" type="button">Gluten-free</button>
<button class="btn btn-info" type="button">Lactose-free</button>
<button class="btn btn-info" type="button">Nut-free</button>
</div>
</div>
</div>
<br>
<div class="row">
<div class="col">
<div class="btn-group" role="group" style="width: 100%;">
<button class="btn btn-info" type="button">Halal</button>
<button class="btn btn-info" type="button">Kosher</button>
<button class="btn btn-info" type="button">Keto</button>
<button class="btn btn-info" type="button">Diabetic</button>
<button class="btn btn-info" type="button">Pescatarian</button>
</div>
</div>
</div>
<br>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
{#if $formData.currentQuestion === numQuestion} {#if $formData.currentQuestion === numQuestion}
@@ -99,4 +160,9 @@
height: 50px; height: 50px;
} }
.container {
display: block;
min-width: 70% !important;
}
</style> </style>

7
src/lib/survey.css Normal file
View File

@@ -0,0 +1,7 @@
button.active {
background-color: #ff6a00 !important;
color: white;
border: none;
}

View File

@@ -1,24 +1,20 @@
import {createClient} from "@propelauth/javascript"; import {createClient} from "@propelauth/javascript";
const authClient = createClient({
authUrl: "https://auth.fooddecisive.co/",
enableBackgroundTokenRefresh: true,
});
export async function load() { export async function load() {
/* const authInfo = await authClient.getAuthenticationInfoOrNull();
const authClient = createClient({ console.log("authInfo", authInfo)
authUrl: "https://auth.fooddecisive.co/", if (authInfo) {
enableBackgroundTokenRefresh: true, console.log("User is logged in as", authInfo.user.email)
}); } else {
console.log("User is not logged in")
}
const authInfo = await authClient.getAuthenticationInfoOrNull(); return {
console.log("authInfo", authInfo) authInfo: authInfo ? authInfo : null
if (authInfo) { };
console.log("User is logged in as", authInfo.user.email)
} else {
console.log("User is not logged in")
}
return {
authInfo
};
*/
} }

View File

@@ -1,9 +1,3 @@
<script>
import { page } from '$app/stores';
console.log($page.data);
</script>
<header class="bg-dark"> <header class="bg-dark">
<div class="container pt-4 pt-xl-5"> <div class="container pt-4 pt-xl-5">

View File

@@ -1,71 +0,0 @@
<script>
</script>
<section class="py-5">
<div class="container py-5">
<div class="card">
<div class="card-body mx-auto">
<h4 class="card-title" style="font-size: 50px;">You Want</h4>
</div>
</div>
<div class="row">
<div class="col">
<div class="card" style="margin-bottom: 90px;margin-top: 20px;">
<div class="card-body mx-auto">
<p class="card-text mx-auto" style="font-size: 60px;font-weight: bold;text-align: center;">Butter Chicken</p>
<div class="card">
<div class="card-body">
<p class="card-text">Butter Chicken is a delicious Indian Meal. It was made by the British after they colonized the country.</p>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-xxl-6" style="margin-bottom: 20px;">
<div class="card" style="margin-bottom: 20px;">
<div class="card-body" style="margin-bottom: 20px;">
<h4 class="card-title mx-auto" style="text-align: center;margin-bottom: 20px;">Nutrition Facts</h4>
<p class="card-text" style="margin-bottom: 20px;">Nullam id dolor id nibh ultricies vehicula ut id elit. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus.</p>
</div>
</div>
</div>
<div class="col" style="margin-bottom: 20px;">
<div class="card">
<div class="card-body">
<h4 class="card-title" style="text-align: center;">Ingredients</h4>
<p class="card-text">Nullam id dolor id nibh ultricies vehicula ut id elit. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus.</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="card">
<div class="card-body">
<h4 class="card-title" style="text-align: center;">Title</h4>
<p class="card-text">Nullam id dolor id nibh ultricies vehicula ut id elit. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="card">
<div class="card-body mx-auto"><button class="btn btn-primary" type="button">Show Recipe</button></div>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<h4 class="card-title" style="text-align: center;">Rate Your Generated Recipe!</h4>
<p class="card-text">Nullam id dolor id nibh ultricies vehicula ut id elit. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus.</p>
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,9 @@
import { PUBLIC_URL } from '$env/static/public';
export async function load({ params }) {
const foodID = params.id;
let foodData = await fetch(`${PUBLIC_URL}/api/recipes/${foodID}`);
foodData = await foodData.json();
return foodData;
}

View File

@@ -0,0 +1,96 @@
<script>
// @ts-nocheck
import { page } from '$app/stores';
import { onMount } from "svelte";
let foodData = $page.data;
let nutritionFactsEle;
let IngredientsEle;
$: showRecipe = false;
function CLshowRecipe() {
showRecipe = true;
setTimeout(() => {
for (const [key, value] of Object.entries(foodData.nutritionFacts)) {
nutritionFactsEle.innerText += `${key}: ${value}\n`;
}
IngredientsEle.innerText = foodData.ingredients.map(ingredient => {
return `${ingredient.name}: ${ingredient.quantity}`;
}).join('\n');
}, 500);
}
</script>
<section class="py-5">
<div class="container py-5">
<div class="card">
<div class="card-body mx-auto">
<h4 class="card-title" style="text-align: center; font-size: 50px;">You Want</h4>
<p class="card-text mx-auto" style="font-size: 60px;font-weight: bold;text-align: center;">{foodData.name}</p>
<p class="card-text">{foodData.description}</p>
</div>
{#if !showRecipe}
<div class="row">
<div class="col">
<div class="card">
<div class="card-body mx-auto">
<button class="btn btn-primary" type="button" on:click={CLshowRecipe}>Show Recipe</button>
</div>
</div>
</div>
</div>
{:else }
<div class="row">
<div class="col">
<div class="card" style="margin-bottom: 90px;margin-top: 20px;">
<div class="card-body">
<div class="row">
<div class="col-xxl-6" style="margin-bottom: 20px;">
<div class="card" style="margin-bottom: 20px;">
<div class="card-body" style="margin-bottom: 20px;">
<h2 class="card-title mx-auto" style="text-align: center;margin-bottom: 20px;">Nutrition Facts</h2>
<p class="card-text" style="text-align:center; margin-bottom: 20px;" bind:this={nutritionFactsEle}></p>
</div>
</div>
</div>
<div class="col" style="margin-bottom: 20px;">
<div class="card">
<div class="card-body">
<h2 class="card-title" style="text-align: center;">Ingredients</h2>
<p class="card-text" style="text-align: center;" bind:this={IngredientsEle}></p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="card">
<div class="card-body">
<h2 class="card-title" style="text-align: center;">Instructions</h2>
<p class="card-text">
<ul>
{ #each foodData.instructions as instruction }
<li>{instruction}</li>
{ /each }
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/if}
<h4 class="card-title" style="text-align: center;">Rate Your Suggested Food!</h4>
<br>
<br>
</div>
</div>
</section>

View File

@@ -24,7 +24,7 @@
</div> </div>
</div> </div>
<div id="form" class="card" style="padding-top: 20px;padding-bottom: 100px;"> <div id="form" class="card" style="padding-top: 20px;padding-bottom: 100px;">
<div bind:this={parentDiv} class="card-body mx-auto"></div> <div bind:this={parentDiv} class="card-body"></div>
</div> </div>
</div> </div>
</section> </section>