Private
Public Access
1
0

moved to server

Update readme.md

Signed-off-by: rasmus <rsbendtsen@gmail.com>

Update readme.md

Update Dockerfile

Update Dockerfile
This commit is contained in:
2025-12-29 06:19:31 +01:00
commit dd2c15ba67
15 changed files with 1022 additions and 0 deletions

204
templates/dashboard.html Normal file
View File

@@ -0,0 +1,204 @@
{% extends "base.html" %}
{% block content %}
<div class="max-w-6xl mx-auto px-6 py-12">
<div class="mb-12">
<h1 class="text-4xl font-light text-stone-900 tracking-tight">
Hej, <span class="font-serif italic text-emerald-800">{{ current_user.username }}</span>
</h1>
<p class="text-stone-500 font-light mt-2">Lets design a menu for the days ahead.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12">
<div class="lg:col-span-2">
<div class="mb-6 flex items-center gap-3 px-1">
<div class="w-8 h-8 bg-emerald-50 rounded-lg flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-emerald-700" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
</svg>
</div>
<h2 class="text-xl font-medium text-stone-800 tracking-tight">Personal preferences</h2>
</div>
<div class="bg-white p-8 md:p-10 rounded-[2rem] shadow-sm border border-stone-100">
<form action="{{ url_for('generate') }}" method="POST" class="space-y-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6">
{% set fields = [
('people_count', 'Number of People', [('1 adult', '1 adult'), ('2 adults', '2 adults'), ('Family
of 4', 'Family of 4')]),
('budget_level', 'Budget Level', [('Low', 'Low'), ('Medium', 'Medium'), ('High', 'High')]),
('max_cooking_time', 'Max Cooking Time', [('20', '20 min'), ('30', '30 min'), ('45', '45 min'),
('60', '60 min')]),
('skill_level', 'Cooking Skill', [('Beginner (simple steps)', 'Beginner'), ('Intermediate
(comfortable with techniques)', 'Intermediate'), ('Expert (advanced methods)', 'Advanced')]),
('dietary_preference', 'Dietary Preference', [('Standard (Omnivore)', 'Standard'),
('Vegetarian', 'Vegetarian'), ('Vegan', 'Vegan'), ('Pescatarian', 'Pescatarian')]),
('dietary_focus', 'Dietary Focus', [('Balanced', 'Balanced'), ('High Protein', 'High Protein'),
('Low Carb', 'Low Carb'), ('Kid-Friendly', 'Kid-Friendly')])
] %}
{% for name, label, options in fields %}
<div>
<label class="block text-xs font-semibold text-stone-400 uppercase tracking-widest mb-3">{{
label }}</label>
<select name="{{ name }}"
class="w-full px-4 py-3 bg-stone-50 border border-stone-200 rounded-xl text-stone-700 focus:ring-2 focus:ring-emerald-800/20 focus:border-emerald-800 outline-none transition-all appearance-none cursor-pointer">
{% for val, display in options %}
<option value="{{ val }}" {% if val=='2 adults' or val=='Medium' or val=='30' or
val=='Intermediate (comfortable with techniques)' or val=='Standard (Omnivore)' or
val=='Balanced' %}selected{% endif %}>{{ display }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
<div class="pt-8 border-t border-stone-50 space-y-6">
<div>
<label
class="block text-xs font-semibold text-stone-400 uppercase tracking-widest mb-3">Meal
Strategy</label>
<select name="meal_strategy"
class="w-full px-4 py-3 bg-stone-50 border border-stone-200 rounded-xl text-stone-700 focus:ring-2 focus:ring-emerald-800/20 focus:border-emerald-800 outline-none transition-all appearance-none cursor-pointer">
<option value="Maximize ingredient reuse" selected>Maximize Reuse (Zero Waste)</option>
<option value="Variety-focused">Maximum Variety</option>
<option value="Quick & Easy">Quick & Easy</option>
</select>
</div>
<div>
<label
class="block text-xs font-semibold text-stone-400 uppercase tracking-widest mb-3">Fridge
Clear-out</label>
<input type="text" name="fridge_items" placeholder="e.g. 2 Carrots, half a cabbage..."
class="w-full px-4 py-4 border-2 border-emerald-50/50 rounded-xl bg-emerald-50/30 text-stone-700 focus:border-emerald-800/30 focus:bg-white outline-none transition-all placeholder:text-stone-300">
</div>
<div>
<label
class="block text-xs font-semibold text-stone-400 uppercase tracking-widest mb-3">Output
Language</label>
<div class="flex gap-4">
<label class="relative flex-1 cursor-pointer group">
<input type="radio" name="output_language" value="English" checked
class="peer sr-only">
<div
class="flex items-center justify-center gap-3 p-3 bg-stone-50 border border-stone-200 rounded-xl peer-checked:border-emerald-800 peer-checked:bg-emerald-50 transition-all">
<span class="text-2xl">🇬🇧</span>
<span
class="text-sm font-medium text-stone-600 group-hover:text-stone-900">English</span>
</div>
</label>
<label class="relative flex-1 cursor-pointer group">
<input type="radio" name="output_language" value="Danish" class="peer sr-only">
<div
class="flex items-center justify-center gap-3 p-3 bg-stone-50 border border-stone-200 rounded-xl peer-checked:border-emerald-800 peer-checked:bg-emerald-50 transition-all">
<span class="text-2xl">🇩🇰</span>
<span
class="text-sm font-medium text-stone-600 group-hover:text-stone-900">Dansk</span>
</div>
</label>
</div>
</div>
</div>
<button type="submit" id="submitBtn"
class="w-full bg-emerald-800 text-white font-medium py-5 rounded-2xl shadow-lg hover:bg-emerald-700 hover:-translate-y-0.5 transition-all duration-300 flex items-center justify-center gap-3">
<span id="btnText">Generate Personalized Plan</span>
<div id="btnLoader" class="hidden">
<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
</div>
</button>
</form>
</div>
</div>
<div class="lg:col-span-1">
<div class="mb-6 flex items-center gap-3 px-1">
<div class="w-8 h-8 bg-stone-100 rounded-lg flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-stone-600" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h2 class="text-xl font-medium text-stone-800 tracking-tight">Recent Activity</h2>
</div>
<div class="space-y-4 max-h-[800px] overflow-y-auto pr-2 custom-scrollbar">
{% for plan in plans %}
<div
class="bg-white p-6 rounded-2xl border border-stone-100 shadow-sm hover:shadow-md hover:border-emerald-100 transition-all group">
<h3 class="text-stone-800 font-medium leading-snug group-hover:text-emerald-800 transition-colors">
{{ plan.description }}</h3>
<div class="flex justify-between items-center mt-6">
<span class="text-[10px] font-bold text-stone-300 uppercase tracking-widest">Plan #{{ plan.id
}}</span>
<a href="{{ url_for('view_plan', plan_id=plan.id) }}"
class="text-xs font-semibold text-emerald-800 flex items-center gap-1 hover:gap-2 transition-all">
View Plan <span></span>
</a>
</div>
</div>
{% else %}
<div class="text-center py-16 bg-stone-50/50 rounded-3xl border-2 border-dashed border-stone-100">
<p class="text-stone-400 font-light italic text-sm px-4">Your culinary history will appear here.</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<style>
/* Subtle scrollbar for history sidebar */
.custom-scrollbar::-webkit-scrollbar {
width: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #e7e5e4;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #d6d3d1;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 1. Identify the form and button elements
const planForm = document.querySelector('form[action="{{ url_for("generate") }}"]');
const submitBtn = document.getElementById('submitBtn');
const btnText = document.getElementById('btnText');
const btnLoader = document.getElementById('btnLoader');
// 2. Add submission logic
if (planForm && submitBtn) {
planForm.addEventListener('submit', function () {
// Disable button to prevent double-clicks
submitBtn.disabled = true;
// Trigger Nordic-style loading animations
submitBtn.classList.add('opacity-80', 'cursor-not-allowed', 'animate-pulse');
// Update text and show the SVG spinner
if (btnText) btnText.innerText = 'Generating your plan...';
if (btnLoader) btnLoader.classList.remove('hidden');
});
}
});
</script>
{% endblock %}