This commit is contained in:
2025-05-18 00:18:54 -05:00
parent 6fb15b6d5c
commit 68c069f02a
24 changed files with 836 additions and 10 deletions

View File

@ -0,0 +1,5 @@
{% extends 'base.html' %}
{% block content %}
<h2>Admin Dashboard</h2>
<p>You are logged in as an administrator.</p>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block content %}
<h2>Login</h2>
<form method="POST" action="/login">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block content %}
<h2>Register</h2>
<form method="POST" action="/register">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-success">Register</button>
</form>
{% endblock %}

98
app/templates/base.html Normal file
View File

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Nature In Pots Community</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
}
footer {
background-color: #f8f9fa;
padding: 1rem 0;
text-align: center;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4">
<div class="container">
<a class="navbar-brand" href="{{ url_for('core.index') }}">Nature In Pots</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="{{ url_for('core.index') }}">Home</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('core.user_dashboard') }}">Dashboard</a></li>
{% if current_user.is_authenticated and current_user.role == 'admin' %}
<li class="nav-item"><a class="nav-link text-danger" href="{{ url_for('core.admin_dashboard') }}">Admin Dashboard</a></li>
{% endif %}
{% block plugin_links %}{% endblock %}
</ul>
<ul class="navbar-nav align-items-center">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="filterDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Search
</a>
<div class="dropdown-menu dropdown-menu-end p-3 shadow" style="min-width: 300px;">
<form method="GET" action="{{ url_for('core.search') }}">
<div class="mb-2">
<input class="form-control" type="text" name="q" placeholder="Plant name">
</div>
<div class="mb-2">
<input class="form-control" type="text" name="source" placeholder="Source">
</div>
<div class="mb-2 d-flex gap-2">
<input class="form-control" type="number" name="min_price" placeholder="Min $" step="0.01">
<input class="form-control" type="number" name="max_price" placeholder="Max $" step="0.01">
</div>
<div class="mb-2 d-flex gap-2">
<input class="form-control" type="date" name="from_date">
<input class="form-control" type="date" name="to_date">
</div>
<button class="btn btn-sm btn-success w-100" type="submit">Apply Filters</button>
</form>
</div>
</li>
{% if current_user.is_authenticated %}
<li class="nav-item ms-3">
<a class="nav-link" href="{{ url_for('auth.logout') }}">Logout</a>
</li>
{% else %}
<li class="nav-item ms-3"><a class="nav-link" href="{{ url_for('auth.login') }}">Login</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('auth.register') }}">Register</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<main class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-warning">
{% for message in messages %}
<div>{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>
<footer>
&copy; {{ current_year }} Nature In Pots Community. All rights reserved.
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,5 @@
{% extends 'base.html' %}
{% block content %}
<h2>User Dashboard</h2>
<p>Welcome to your dashboard, {{ current_user.email }}.</p>
{% endblock %}

36
app/templates/index.html Normal file
View File

@ -0,0 +1,36 @@
{% extends 'base.html' %}
{% block content %}
<h1 class="mb-4 text-center">Welcome to the Plant Price Tracker!</h1>
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-4">
{% if use_placeholder %}
{% for i in range(6) %}
<div class="col">
<div class="card h-100 shadow-sm">
<img src="https://placehold.co/150x150" class="card-img-top" alt="Placeholder Plant {{ i+1 }}">
<div class="card-body">
<h5 class="card-title">Placeholder Plant {{ i+1 }}</h5>
<p class="card-text text-muted">No real submissions yet — submit a plant to see it here.</p>
</div>
</div>
</div>
{% endfor %}
{% else %}
{% for sub in submissions %}
<div class="col">
<div class="card h-100 shadow-sm">
<img src="{{ sub.image_url or 'https://placehold.co/150x150' }}" class="card-img-top" alt="{{ sub.common_name }}">
<div class="card-body">
<h5 class="card-title">{{ sub.common_name }}</h5>
<p class="card-text">
<strong>${{ '%.2f'|format(sub.price) }}</strong><br>
Source: {{ sub.source }}<br>
Date: {{ sub.timestamp.strftime('%Y-%m-%d') }}
</p>
</div>
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endblock %}

34
app/templates/search.html Normal file
View File

@ -0,0 +1,34 @@
{% extends 'base.html' %}
{% block content %}
<h2 class="mb-3">Search Results</h2>
<div id="active-filters">
{% include 'search_tags.html' %}
</div>
<div id="search-results">
{% include 'search_results.html' %}
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".remove-chip").forEach(btn => {
btn.addEventListener("click", function (e) {
e.preventDefault();
const tag = this.dataset.tag;
fetch("{{ url_for('core.search') }}", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ remove: tag })
})
.then(res => res.json())
.then(data => {
document.getElementById("active-filters").innerHTML = data.tags_html;
document.getElementById("search-results").innerHTML = data.results_html;
});
});
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% if results %}
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-4">
{% for sub in results %}
<div class="col">
<div class="card h-100 shadow-sm">
<img src="{{ sub.image_url or 'https://placehold.co/600x400' }}" class="card-img-top" alt="{{ sub.common_name }}">
<div class="card-body">
<h5 class="card-title">{{ sub.common_name }}</h5>
<p class="card-text">
<strong>${{ '%.2f'|format(sub.price) }}</strong><br>
Source: {{ sub.source }}<br>
Date: {{ sub.timestamp.strftime('%Y-%m-%d') }}
</p>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-muted">No results found.</p>
{% endif %}

View File

@ -0,0 +1,15 @@
{% if filter_tags %}
<div class="mb-3">
<strong>Filters:</strong>
{% for key, value in filter_tags.items() %}
<span class="badge bg-primary me-2">
{{ key }}: {{ value }}
<button class="btn-close btn-close-white btn-sm remove-chip ms-1" data-tag="{{ key }}" aria-label="Remove {{ key }}"></button>
</span>
{% endfor %}
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" onclick="document.getElementById('filterDropdown').click();">
Edit Filters
</button>
</div>
{% endif %}

65
app/templates/submit.html Normal file
View File

@ -0,0 +1,65 @@
{% extends 'base.html' %}
{% block content %}
<h2 class="mb-4">Submit a New Plant</h2>
<form method="POST" enctype="multipart/form-data">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Common Name *</label>
<input type="text" name="common_name" class="form-control" required>
</div>
<div class="col-md-6">
<label class="form-label">Scientific Name</label>
<input type="text" name="scientific_name" class="form-control">
</div>
<div class="col-md-3">
<label class="form-label">Price ($)*</label>
<input type="number" name="price" class="form-control" step="0.01" required>
</div>
<div class="col-md-3">
<label class="form-label">Source</label>
<input type="text" name="source" class="form-control">
</div>
<div class="col-md-3">
<label class="form-label">Height (in)</label>
<input type="number" name="height" class="form-control" step="0.1">
</div>
<div class="col-md-3">
<label class="form-label">Width (in)</label>
<input type="number" name="width" class="form-control" step="0.1">
</div>
<div class="col-md-3">
<label class="form-label">Leaf Count</label>
<input type="number" name="leaf_count" class="form-control">
</div>
<div class="col-md-3">
<label class="form-label">Potting Mix</label>
<input type="text" name="potting_mix" class="form-control">
</div>
<div class="col-md-3">
<label class="form-label">Container Size</label>
<input type="text" name="container_size" class="form-control">
</div>
<div class="col-md-3">
<label class="form-label">Health Status</label>
<select name="health_status" class="form-select">
<option value="">Select</option>
<option value="healthy">Healthy</option>
<option value="minor damage">Minor Damage</option>
<option value="pest observed">Pest Observed</option>
<option value="recovering">Recovering</option>
</select>
</div>
<div class="col-12">
<label class="form-label">Notes</label>
<textarea name="notes" class="form-control" rows="3"></textarea>
</div>
<div class="col-12">
<label class="form-label">Upload up to 5 images (jpg/png)</label>
<input type="file" name="images" accept=".jpg,.jpeg,.png" class="form-control" multiple>
</div>
</div>
<div class="mt-3">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends 'base.html' %}
{% block content %}
<h2>Unauthorized</h2>
<p>You do not have permission to access this page.</p>
{% endblock %}