backup before mayor changes

This commit is contained in:
2026-08-05 16:35:09 -05:00
parent f5954247fc
commit 203ff88af1
8 changed files with 145 additions and 15 deletions
@@ -0,0 +1,24 @@
# Generated by Django 6.0.3 on 2026-08-05 20:54
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('announcements', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='announcementcomment',
name='created_at',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='announcementcomment',
name='updated_at',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
+12 -2
View File
@@ -1,5 +1,6 @@
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.utils import timezone
from library.models import Project from library.models import Project
@@ -63,12 +64,21 @@ class AnnouncementComment(models.Model):
related_name='announcement_comments', related_name='announcement_comments',
) )
body = models.TextField() body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(default=timezone.now)
class Meta: class Meta:
ordering = ['created_at', 'pk'] ordering = ['created_at', 'pk']
def save(self, *args, **kwargs):
if self._state.adding:
now = timezone.now()
self.created_at = now
self.updated_at = now
else:
self.updated_at = timezone.now()
super().save(*args, **kwargs)
def __str__(self): def __str__(self):
return f'{self.user.username}: {self.body[:40]}' return f'{self.user.username}: {self.body[:40]}'
@@ -0,0 +1,24 @@
# Generated by Django 6.0.3 on 2026-08-05 20:54
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0013_project_rating_count_project_rating_score_comment_and_more'),
]
operations = [
migrations.AlterField(
model_name='comment',
name='created_at',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='comment',
name='updated_at',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
+12 -2
View File
@@ -4,6 +4,7 @@ import uuid
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
def slugify_tag(value): def slugify_tag(value):
@@ -374,8 +375,17 @@ class Comment(models.Model):
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comments', settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comments',
) )
body = models.TextField() body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(default=timezone.now)
def save(self, *args, **kwargs):
if self._state.adding:
now = timezone.now()
self.created_at = now
self.updated_at = now
else:
self.updated_at = timezone.now()
super().save(*args, **kwargs)
def __str__(self): def __str__(self):
return f'{self.user.username} on {self.project.slug}: {self.body[:40]}' return f'{self.user.username} on {self.project.slug}: {self.body[:40]}'
+3 -1
View File
@@ -297,8 +297,10 @@ def browse(request):
if not tags: if not tags:
continue continue
items = [] items = []
has_active = False
for tag in tags: for tag in tags:
active = tag.name in tag_names active = tag.name in tag_names
has_active = has_active or active
if active: if active:
remaining = [t for t in tag_names if t != tag.name] remaining = [t for t in tag_names if t != tag.name]
url = f'?{_qs(tag=remaining)}' url = f'?{_qs(tag=remaining)}'
@@ -311,7 +313,7 @@ def browse(request):
'active': active, 'active': active,
'url': url, 'url': url,
}) })
tag_groups.append({'category': cat, 'items': items}) tag_groups.append({'category': cat, 'items': items, 'has_active': has_active})
paginator = Paginator(projects, 12) paginator = Paginator(projects, 12)
page_obj = paginator.get_page(request.GET.get('page')) page_obj = paginator.get_page(request.GET.get('page'))
+31 -6
View File
@@ -2054,6 +2054,31 @@ a.deletelink {
margin-top: 0; margin-top: 0;
font-size: 0.95rem; font-size: 0.95rem;
} }
.filter-group-toggle {
position: relative;
width: 100%; display: flex; align-items: center; gap: 6px;
background: none; border: none; color: inherit; cursor: pointer;
font-size: 0.95rem; font-weight: 600; padding: 4px 0; text-align: left;
}
.filter-group-toggle::before {
content: '';
position: absolute;
inset: 2px -16px;
border-radius: 8px;
background: transparent;
pointer-events: none;
}
.filter-group-toggle:hover,
.filter-group-toggle:focus-visible {
background: transparent;
}
.filter-group-toggle:hover::before,
.filter-group-toggle:focus-visible::before {
background: rgba(255, 255, 255, 0.07);
}
.filter-chevron { margin-left: auto; transition: transform 0.2s ease; }
.filter-group-toggle[aria-expanded="true"] .filter-chevron { transform: rotate(180deg); }
.filter-tags.collapsed { display: none; }
.filter-category { .filter-category {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -3797,7 +3822,7 @@ a.deletelink {
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
color: var(--md-sys-color-on-surface, #cdd6f4); color: var(--md-sys-color-on-surface, #cdd6f4);
} }
.announce-item.announce-unread { outline: 1px solid var(--md-sys-color-primary, #89b4fa); } .announce-item.announce-unread a { color: var(--ctp-mocha-blue, #1e66f5); font-weight: 600; }
.announce-meta { .announce-meta {
color: var(--md-sys-color-on-surface-variant, #a6adc8); color: var(--md-sys-color-on-surface-variant, #a6adc8);
font-size: 0.68rem; white-space: nowrap; flex-shrink: 0; font-size: 0.68rem; white-space: nowrap; flex-shrink: 0;
@@ -3837,11 +3862,11 @@ a.deletelink {
background: var(--md-sys-color-surface, #1e1e2e); background: var(--md-sys-color-surface, #1e1e2e);
color: var(--md-sys-color-on-surface, #cdd6f4); font-family: inherit; color: var(--md-sys-color-on-surface, #cdd6f4); font-family: inherit;
} }
.notify-list { list-style: none; margin: 0; padding: 0; } .notify-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.notify-item { .notify-item {
padding: 0.6rem 0.5rem; border-bottom: 1px solid var(--md-sys-color-outline-variant, #45475a); padding: 0.55rem 0.6rem; border-radius: 8px; font-size: 0.85rem;
font-size: 0.85rem; background: var(--md-sys-color-surface-variant, #45475a);
} }
.notify-item:last-child { border-bottom: none; } .notify-item a { color: var(--md-sys-color-on-surface, #cdd6f4); }
.notify-item.notify-unread { background: var(--md-sys-color-surface-variant, #45475a); border-radius: 8px; } .notify-item.notify-unread a { color: var(--ctp-mocha-blue, #1e66f5); font-weight: 600; }
.notify-new { color: var(--ctp-mocha-red); font-weight: 600; } .notify-new { color: var(--ctp-mocha-red); font-weight: 600; }
+20
View File
@@ -48,6 +48,11 @@
<a href="{% url 'announcements:list' %}"> <a href="{% url 'announcements:list' %}">
<i class="fas fa-bullhorn"></i><span class="nav-text"> Announcements</span> <i class="fas fa-bullhorn"></i><span class="nav-text"> Announcements</span>
</a> </a>
{% if user.is_authenticated %}
<a href="{% url 'library:project_create' %}">
<i class="fas fa-upload"></i><span class="nav-text"> Upload</span>
</a>
{% endif %}
{% endif %} {% endif %}
</nav> </nav>
</div> </div>
@@ -146,6 +151,21 @@
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
<script>
(function () {
const banner = document.querySelector('.messages-banner');
if (!banner) return;
const dismiss = () => {
if (banner.dataset.dismissing) return;
banner.dataset.dismissing = '1';
banner.style.transition = 'opacity 0.4s ease';
banner.style.opacity = '0';
setTimeout(() => banner.remove(), 450);
};
banner.addEventListener('click', dismiss);
setTimeout(dismiss, 6000);
})();
</script>
{% block extra_js %}{% endblock %} {% block extra_js %}{% endblock %}
</body> </body>
</html> </html>
+19 -4
View File
@@ -26,12 +26,13 @@
</div> </div>
{% for group in tag_groups %} {% for group in tag_groups %}
<div class="card filter-card"> <div class="card filter-card filter-group">
<h3> <button type="button" class="filter-group-toggle" aria-expanded="{% if group.has_active %}true{% else %}false{% endif %}">
<span class="tag-dot" style="background: {{ group.category.color }};"></span> <span class="tag-dot" style="background: {{ group.category.color }};"></span>
{{ group.category.name }} {{ group.category.name }}
</h3> <i class="fas fa-chevron-down filter-chevron"></i>
<div class="filter-tags"> </button>
<div class="filter-tags{% if not group.has_active %} collapsed{% endif %}">
{% for item in group.items %} {% for item in group.items %}
<a href="{{ item.url }}" class="tag-chip filter-tag {% if item.active %}active{% endif %}" <a href="{{ item.url }}" class="tag-chip filter-tag {% if item.active %}active{% endif %}"
style="border-color: {{ item.color }}; color: {{ item.color }};"> style="border-color: {{ item.color }}; color: {{ item.color }};">
@@ -89,3 +90,17 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block extra_js %}
<script>
document.querySelectorAll('.filter-group-toggle').forEach(btn => {
btn.addEventListener('click', () => {
const group = btn.closest('.filter-group');
const tags = group.querySelector('.filter-tags');
const expanded = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
if (tags) tags.classList.toggle('collapsed', expanded);
});
});
</script>
{% endblock %}