PHP Classes

File: src/pages/IndexPage.vue

Recommend this page to a friend!
  Packages of Robert Devenyi   Sibo Meal Planner   src/pages/IndexPage.vue   Download  
File: src/pages/IndexPage.vue
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Sibo Meal Planner
App to help treat the SIBO health condition
Author: By
Last change:
Date: 8 months ago
Size: 7,622 bytes
 

Contents

Class file image Download
<template> <q-page class="q-pa-md"> <!-- Fancy Title --> <div class="text-center q-mb-sm" style="margin-top: 40px"> <span style="font-family: 'Viga', sans-serif; font-size: 36px; font-weight: 400; color: #003ac3" > Sibo </span> <span style="font-family: 'Ubuntu', sans-serif; font-size: 36px; font-weight: 700; color: #111" > Mealplan </span> </div> <!-- Subtitle --> <div class="text-center q-mb-xl" style="font-size: 18px; font-weight: 500; color: #555"> Get a customized SIBO mealplan based on the type of SIBO you have. </div> <!-- SIBO Type Select (only when not selected) --> <div v-if="!selectedType" class="row justify-center q-mb-lg"> <div style="max-width: 400px; width: 100%"> <q-select v-model="selectedType" :options="siboTypes" label="Select your SIBO type" placeholder="Select your SIBO type" outlined dense emit-value map-options style="width: 100%" /> </div> </div> <div v-if="selectedType" class="row q-gutter-lg justify-center q-mt-lg"> <div v-for="meal in meals" :key="meal" class="col-12 col-md-4"> <q-card flat bordered class="q-pa-md" :style="{ minHeight: '200px', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', borderTop: `5px solid ${mealColors[meal]}`, }" > <!-- Icon & Title --> <div class="row items-center q-mb-md"> <q-icon :name="mealIcons[meal]" size="36px" class="q-mr-sm" :color="mealColors[meal]" /> <div class="text-h6">{{ meal.charAt(0).toUpperCase() + meal.slice(1) }}</div> </div> <!-- Recipe Info --> <div v-if="recipes[meal]"> <div class="text-subtitle1">{{ recipes[meal].name }}</div> <div class="text-body2 q-mt-sm">{{ recipes[meal].short_description }}</div> <!-- Toggle full recipe body --> <div v-if="showFull[meal]" class="q-mt-sm text-body1" style="white-space: pre-wrap" v-html="formatRecipe(recipes[meal].body)" ></div> <!-- Read More / Show Less Button --> <q-btn flat dense class="q-mt-sm self-start" :label="showFull[meal] ? 'Show Less' : 'Read More'" :icon="showFull[meal] ? 'mdi-chevron-up' : 'mdi-chevron-down'" @click="showFull[meal] = !showFull[meal]" /> </div> <div v-else class="text-grey q-mb-md">Loading recipe...</div> <!-- Refresh button --> <q-btn label="Refresh" color="primary" size="sm" unelevated @click="fetchRecipe(meal)" class="self-end q-mt-sm" /> </q-card> </div> </div> <div v-if="selectedType" class="row items-start justify-between q-mt-xl q-gutter-md"> <!-- Left: Signup --> <div style="max-width: 300px; width: 100%; padding-top: 40px"> <q-input v-model="email" label="Enter your email" outlined dense class="q-mb-sm" /> <q-btn label="Sign Up for a Daily Meal Plan" color="secondary" @click="signup" :disable="loadingSignup" dense /> <div v-if="message" class="text-positive q-mt-sm">{{ message }}</div> </div> <!-- Right: Social Share --> <div class="column items-end"> <div class="text-subtitle2 q-mb-sm">Share</div> <div class="row q-gutter-sm"> <q-btn v-for="platform in socialPlatforms" :key="platform.name" :label="platform.name" :icon="platform.icon" dense :style="{ backgroundColor: platform.color, color: 'white' }" @click="shareMealplan(platform.name)" /> </div> </div> </div> <br /><br /><br /> </q-page> </template> <script setup> import { ref, watch, inject, reactive } from 'vue' import axios from 'axios' // Injected from layout (already a ref) const selectedType = inject('selectedType') const siboTypes = [ 'Hydrogen-Dominant SIBO', 'Mixed-Dominant SIBO', 'Hydrogen sulfide SIBO', 'Methane-Dominant SIBO', ] const formatRecipe = (text) => { if (!text) return '' return text .replace(/(Ingredients:)/gi, '<strong>$1</strong>') .replace(/(Extended Notes:)/gi, '<strong>$1</strong>') .replace(/\n/g, '<br>') } const showFull = reactive({ breakfast: false, lunch: false, dinner: false, }) const meals = ['breakfast', 'lunch', 'dinner'] const recipes = ref({ breakfast: null, lunch: null, dinner: null, }) const mealIcons = { breakfast: 'mdi-coffee', lunch: 'mdi-food-apple', dinner: 'mdi-food-steak', } const socialPlatforms = [ { name: 'Twitter', icon: 'mdi-twitter', color: '#1DA1F2' }, { name: 'Facebook', icon: 'mdi-facebook', color: '#1877F2' }, { name: 'LinkedIn', icon: 'mdi-linkedin', color: '#0A66C2' }, { name: 'WhatsApp', icon: 'mdi-whatsapp', color: '#25D366' }, { name: 'Email', icon: 'mdi-email', color: '#D44638' }, ] const mealColors = { breakfast: '#FFA500', // orange lunch: '#4CAF50', // green dinner: '#9C27B0', // purple } const email = ref('') const message = ref('') // Watch for type selection and load recipes watch(selectedType, async (newType) => { if (newType) { for (const meal of meals) { await fetchRecipe(meal) } } }) const fetchRecipe = async (meal) => { try { const typeKey = selectedType.value.replace(/\s+/g, '-').toLowerCase() const res = await axios.get(`http://localhost:8080/api/recipe/${typeKey}/${meal}`) recipes.value[meal] = res.data } catch (err) { console.error(`Error fetching ${meal} recipe`, err) } } const shareMealplan = (platform) => { const planText = ` SIBO Mealplan (${selectedType.value}): Breakfast: ${recipes.value.breakfast?.name || 'Not loaded'} Lunch: ${recipes.value.lunch?.name || 'Not loaded'} Dinner: ${recipes.value.dinner?.name || 'Not loaded'} ` if (platform === 'Twitter') { window.open(`https://twitter.com/intent/tweet?text=${encodeURIComponent(planText)}`) } else if (platform === 'Facebook') { window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(planText)}`) } else if (platform === 'LinkedIn') { window.open( `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(planText)}`, ) } } const loadingSignup = ref(false) const signup = async () => { if (!email.value || !selectedType.value) { message.value = 'Please enter email to receive a daily mealplan.' return } loadingSignup.value = true try { const res = await axios.post('http://localhost:8080/api/signup', { email: email.value, type: selectedType.value, }) message.value = res.data.message email.value = '' } catch (err) { console.error(err) } finally { loadingSignup.value = false } } </script> <style> .social-twitter { border-color: #1da1f2; color: #1da1f2; } .social-facebook { border-color: #1877f2; color: #1877f2; } .social-linkedin { border-color: #0a66c2; color: #0a66c2; } .social-whatsapp { border-color: #25d366; color: #25d366; } .social-email { border-color: #d44638; color: #d44638; } </style>