inital checkin

This commit is contained in:
2026-03-14 06:12:56 -05:00
commit 5f2a61ea75
35 changed files with 1401 additions and 0 deletions

106
templates/base.html Normal file
View File

@@ -0,0 +1,106 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RigbyRaffle</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap" rel="stylesheet">
{% load static %}
<link rel="stylesheet" href="{% static 'style.css' %}">
</head>
<body data-theme="light">
<script>
// Check for saved theme preference or system preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
document.body.setAttribute('data-theme', savedTheme);
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.setAttribute('data-theme', 'dark');
}
</script>
<div class="blob-bg"></div>
<nav class="glass-nav">
<a href="{% url 'item_list' %}" class="logo">Rigby<span>Raffle</span></a>
<div class="nav-links">
{% if user.is_authenticated %}
<span class="welcome-text">Hi, {{ user.username }}</span>
{% if user.is_auctioneer %}
<a href="{% url 'item_create' %}" class="btn-primary-small">Add Item</a>
{% endif %}
<form method="post" action="{% url 'logout' %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn-secondary-small">Logout</button>
</form>
{% else %}
<a href="{% url 'login' %}" class="btn-secondary-small">Login</a>
<a href="{% url 'register' %}" class="btn-primary-small">Register</a>
{% endif %}
<button id="theme-toggle" class="btn-secondary-small"
style="padding: 0.5rem; display: flex; align-items: center; justify-content: center; border-radius: 50%; width: 40px; height: 40px;"
aria-label="Toggle Theme">
<!-- Sun Icon -->
<svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
style="display: none;">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
<!-- Moon Icon -->
<svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
style="display: none;">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
</button>
</div>
</nav>
<main class="container">
{% block content %}
{% endblock %}
</main>
<script>
const themeToggleBtn = document.getElementById('theme-toggle');
const sunIcon = document.getElementById('sun-icon');
const moonIcon = document.getElementById('moon-icon');
function updateIcon(theme) {
if (theme === 'dark') {
sunIcon.style.display = 'block';
moonIcon.style.display = 'none';
} else {
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
}
}
// Initialize icon based on current theme
const currentTheme = document.body.getAttribute('data-theme') || 'light';
updateIcon(currentTheme);
themeToggleBtn.addEventListener('click', () => {
let theme = document.body.getAttribute('data-theme');
let newTheme = theme === 'dark' ? 'light' : 'dark';
document.body.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
updateIcon(newTheme);
// Add a subtle click animation
themeToggleBtn.style.transform = 'scale(0.9)';
setTimeout(() => themeToggleBtn.style.transform = 'scale(1)', 150);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% block content %}
<div class="auth-card glassmorphism">
<h2 style="color: #ef4444;">Delete Item</h2>
<p class="subtitle">Are you sure you want to delete "{{ object.title }}"?</p>
<form method="post" style="margin-top: 2rem;">
{% csrf_token %}
<div style="display: flex; gap: 1rem;">
<button type="submit" class="btn-danger" style="width: 100%; padding: 0.875rem;">Yes, delete it</button>
<a href="{% url 'item_detail' object.pk %}" class="btn-secondary"
style="margin-top: 0; text-align: center;">Cancel</a>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,54 @@
{% extends 'base.html' %}
{% block content %}
<div class="glassmorphism" style="max-width: 800px; margin: 0 auto; padding: 2.5rem;">
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 2rem;">
<div>
<span style="color:var(--primary); font-weight: 800;">#{{ item.item_number }}</span>
<h2>{{ item.title }}</h2>
</div>
<div style="display: flex; gap: 0.5rem;">
<a href="{% url 'item_update' item.pk %}" class="btn-secondary-small">Edit</a>
<a href="{% url 'item_delete' item.pk %}" class="btn-danger" style="padding: 0.5rem 1rem;">Delete</a>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
<div>
{% if item.picture %}
<img src="{{ item.picture.url }}" style="width: 100%; border-radius: 8px;" alt="{{ item.title }}">
{% else %}
<div
style="height: 300px; background: var(--secondary); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: var(--text-muted);">
No Image Available</div>
{% endif %}
<p style="margin-top: 1.5rem; color: var(--text-muted); line-height: 1.6;">{{ item.description|linebreaksbr
}}</p>
</div>
<div>
<div class="glassmorphism" style="padding: 1.5rem; box-shadow: none; background: var(--secondary);">
<h3 style="margin-bottom: 1rem; font-size: 1.1rem; color: var(--primary);">Favorite Activity</h3>
<p style="margin-bottom: 1rem; font-size: 0.9rem;">Total Favorites: <strong
style="font-size: 1.25rem;">{{ item.favorited_by.count }}</strong></p>
{% if item.favorited_by.count > 0 %}
<ul style="list-style: none; display: flex; flex-direction: column; gap: 0.5rem;">
{% for fav in item.favorited_by.all %}
<li
style="padding: 0.75rem; background: var(--secondary); border-radius: 6px; display: flex; align-items: center; gap: 0.5rem;">
<div style="width:8px; height:8px; border-radius:50%; background:var(--primary);"></div>
{{ fav.user.username }}
<span style="color:var(--text-muted); font-size:0.8rem; margin-left:auto;">{{
fav.created_at|date:"M d, Y" }}</span>
</li>
{% endfor %}
</ul>
{% else %}
<p style="color: var(--text-muted); font-size: 0.9rem;">No one has favorited this item yet.</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block content %}
<div class="auth-card glassmorphism" style="max-width: 600px;">
<h2>{% if object %}Edit Item{% else %}Add New Item{% endif %}</h2>
<form method="post" enctype="multipart/form-data" class="auth-form" style="margin-top: 1.5rem;">
{% csrf_token %}
{{ form.as_p }}
<div style="display: flex; gap: 1rem; margin-top: 2rem;">
<button type="submit" class="btn-primary" style="margin-top: 0;">Save Item</button>
<a href="{% url 'item_list' %}" class="btn-secondary" style="margin-top: 0;">Cancel</a>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,102 @@
{% extends 'base.html' %}
{% block content %}
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem;">
<h2>Raffle Items</h2>
<div style="display: flex; gap: 1rem;">
<a href="?" class="btn-secondary-small"
style="{% if not request.GET.favorites %}background: var(--primary); color: white; border-color: var(--primary);{% endif %}">All
Items</a>
<a href="?favorites=1" class="btn-secondary-small"
style="{% if request.GET.favorites == '1' %}background: var(--primary); color: white; border-color: var(--primary);{% endif %}">My
Favorites</a>
</div>
</div>
<div class="item-grid">
{% for item in items %}
<div class="item-card glassmorphism">
{% if item.picture %}
<img src="{{ item.picture.url }}" alt="{{ item.title }}" class="item-img">
{% else %}
<div class="item-img"
style="display:flex;align-items:center;justify-content:center;color:var(--text-muted);background:var(--secondary);">
No Image</div>
{% endif %}
<div style="flex-grow: 1;">
<span style="color:var(--primary); font-weight: 800; font-size: 0.85rem; letter-spacing: 1px;">#{{
item.item_number }}</span>
<h3 style="margin-top: 0.25rem;">{{ item.title }}</h3>
<p style="color:var(--text-muted); font-size: 0.95rem; margin-top: 0.5rem;">{{ item.description|linebreaksbr
}}</p>
</div>
<div
style="display: flex; justify-content: space-between; align-items: center; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--glass-border);">
<div style="display: flex; gap: 0.5rem;">
{% if user.is_auctioneer %}
<a href="{% url 'item_detail' item.pk %}" class="btn-secondary-small"
style="padding: 0.35rem 0.75rem;">View</a>
<a href="{% url 'item_update' item.pk %}" class="btn-secondary-small"
style="padding: 0.35rem 0.75rem;">Edit</a>
{% endif %}
</div>
<button class="favorite-btn" data-id="{{ item.id }}"
style="background:none;border:none;color:white;cursor:pointer;display:flex;align-items:center;gap:0.5rem;font-size:1rem;transition:transform 0.2s;">
<svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2"
fill="{% if item.id in user_favorites %}#ef4444{% else %}none{% endif %}" stroke-linecap="round"
stroke-linejoin="round" class="heart-icon"
style="color: {% if item.id in user_favorites %}#ef4444{% else %}var(--text-muted){% endif %}; transition: all 0.2s;">
<path
d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z">
</path>
</svg>
<span class="fav-count">{{ item.favorited_by.count }}</span>
</button>
</div>
</div>
{% empty %}
<div class="glassmorphism"
style="grid-column: 1 / -1; padding: 3rem; text-align: center; color: var(--text-muted);">
<p>No items found.</p>
</div>
{% endfor %}
</div>
<script>
const csrfToken = '{{ csrf_token }}';
document.querySelectorAll('.favorite-btn').forEach(btn => {
btn.addEventListener('click', async () => {
// Animate button push
btn.style.transform = 'scale(0.9)';
setTimeout(() => btn.style.transform = 'scale(1)', 150);
const id = btn.dataset.id;
try {
const res = await fetch(`/item/${id}/favorite/`, {
method: 'POST',
headers: {
'X-CSRFToken': csrfToken,
'Content-Type': 'application/json'
}
});
const data = await res.json();
if (data.status === 'success') {
const icon = btn.querySelector('.heart-icon');
if (data.is_favorite) {
icon.style.fill = '#ef4444';
icon.style.color = '#ef4444';
} else {
icon.style.fill = 'none';
icon.style.color = 'var(--text-muted)';
}
btn.querySelector('.fav-count').textContent = data.count;
}
} catch (err) {
console.error('Error toggling favorite', err);
}
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block content %}
<div class="auth-card glassmorphism">
<h2>Welcome Back</h2>
<p class="subtitle">Enter your details to access RigbyRaffle</p>
<form method="post" class="auth-form">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn-primary">Sign In</button>
</form>
<p class="auth-footer">Don't have an account? <a href="{% url 'register' %}">Register here</a></p>
</div>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block content %}
<div class="auth-card glassmorphism">
<h2>Join the Raffle</h2>
<p class="subtitle">Create an account to browse and favorite items</p>
<form method="post" class="auth-form">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn-primary">Register</button>
</form>
<p class="auth-footer">Already have an account? <a href="{% url 'login' %}">Log in</a></p>
</div>
{% endblock %}