All checks were successful
Build and Push Madplaner / build (push) Successful in 25s
204 lines
12 KiB
HTML
204 lines
12 KiB
HTML
{% 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">Let’s 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 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 %} |