fix: correct typo in progress tracking variable name from Progss to Progress

This commit is contained in:
2026-05-11 05:02:06 +00:00
parent 66b6448850
commit 0ab8d63cc4
40 changed files with 1554 additions and 49 deletions
+3
View File
@@ -10,6 +10,9 @@
<a href="/admin/users" class="admin-nav-link {% if admin_section == 'users' %}active{% endif %}">
<span class="admin-nav-icon">&#x1F465;</span> Users
</a>
<a href="/admin/services" class="admin-nav-link {% if admin_section == 'services' %}active{% endif %}">
<span class="admin-nav-icon">&#x2699;&#xFE0F;</span> Services
</a>
<a href="/admin/settings" class="admin-nav-link {% if admin_section == 'settings' %}active{% endif %}">
<span class="admin-nav-icon">&#x2699;&#xFE0F;</span> Settings
</a>
+9
View File
@@ -91,6 +91,15 @@
{% block content %}{% endblock %}
</main>
<footer class="site-footer">
<div class="footer-inner">
<span class="footer-copy">&copy; DevPlace</span>
<nav class="footer-links">
<a href="/bugs">Bug Report</a>
</nav>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
+115
View File
@@ -0,0 +1,115 @@
{% extends "base.html" %}
{% block extra_head %}
<link rel="stylesheet" href="/static/css/feed.css">
<style>
.bugs-layout {
max-width: 720px;
margin: 0 auto;
}
.bugs-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
}
.bugs-header h1 {
font-size: 1.5rem;
font-weight: 700;
}
.bug-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.25rem;
margin-bottom: 1rem;
}
.bug-card-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.5rem;
}
.bug-title {
font-size: 1rem;
font-weight: 600;
color: var(--text-primary);
}
.bug-status {
font-size: 0.6875rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 0.125rem 0.5rem;
border-radius: 999px;
}
.bug-status.open {
background: var(--accent-light);
color: var(--accent);
}
.bug-status.closed {
background: rgba(76, 175, 80, 0.1);
color: var(--success);
}
.bug-desc {
font-size: 0.875rem;
color: var(--text-secondary);
line-height: 1.5;
margin-bottom: 0.5rem;
}
.bug-meta {
font-size: 0.75rem;
color: var(--text-muted);
}
</style>
{% endblock %}
{% block content %}
<div class="bugs-layout">
<div class="bugs-header">
<h1>Bug Reports</h1>
{% if user %}
<button class="btn btn-primary btn-sm" data-modal="create-bug-modal">Report Bug</button>
{% endif %}
</div>
<div class="bugs-list">
{% for item in bugs %}
<div class="bug-card fade-in">
<div class="bug-card-header">
{% set _user = item.author %}{% set _size = 24 %}{% set _size_class = "sm" %}{% include "_avatar_link.html" %}
<span class="bug-title">{{ item.bug['title'] }}</span>
<span class="bug-status {{ item.bug['status'] }}">{{ item.bug['status'] }}</span>
</div>
<div class="bug-desc">{{ item.bug['description'][:300] }}</div>
<div class="bug-meta">{{ item.author['username'] if item.author else 'Unknown' }} &middot; {{ item.time_ago }}</div>
</div>
{% else %}
<div class="empty-state">No bug reports yet.</div>
{% endfor %}
</div>
</div>
{% if user %}
<div class="modal-overlay" id="create-bug-modal">
<div class="card" style="width: 100%; max-width: 560px; margin: 1rem;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<h3 style="font-size: 1.125rem; font-weight: 700;">Report a Bug</h3>
<button type="button" class="modal-close btn-ghost btn-icon" style="font-size: 1.25rem;">&times;</button>
</div>
<form method="POST" action="/bugs/create">
<div class="auth-field" style="margin-bottom: 0.75rem;">
<label for="bug-title">Title</label>
<input type="text" id="bug-title" name="title" required maxlength="200" placeholder="Brief description of the issue">
</div>
<div class="auth-field" style="margin-bottom: 1rem;">
<label for="bug-description">Description</label>
<textarea id="bug-description" name="description" required maxlength="5000" placeholder="Detailed steps to reproduce..." style="min-height: 120px;"></textarea>
</div>
<div style="display: flex; gap: 0.75rem; justify-content: flex-end;">
<button type="button" class="modal-close btn btn-secondary">Cancel</button>
<button type="submit" class="btn btn-primary">Submit Report</button>
</div>
</form>
</div>
</div>
{% endif %}
{% endblock %}
+16 -3
View File
@@ -32,6 +32,10 @@
<span class="dot"></span>
Fun
</a>
<a href="/feed?topic=signals" class="feed-category {% if current_topic == 'signals' %}active{% endif %}" data-topic="signals">
<span class="dot"></span>
Signals
</a>
</div>
</aside>
@@ -86,6 +90,15 @@
</a>
<button class="post-action-btn">&#x1F517; Share</button>
</div>
{% if user %}
<form class="feed-comment-form" method="POST" action="/comments/create">
<input type="hidden" name="post_uid" value="{{ item.post['uid'] }}">
<img src="{{ avatar_url('multiavatar', user['username'], 24) }}" class="avatar-img" style="width: 24px; height: 24px; border-radius: 50%;" alt="" loading="lazy">
<input type="text" name="content" placeholder="Your opinion goes here..." maxlength="1000" autocomplete="off">
<button type="submit" class="feed-comment-submit">Post</button>
</form>
{% endif %}
</article>
{% else %}
<div class="empty-state">No posts yet. Be the first!</div>
@@ -105,8 +118,8 @@
<h4>{{ daily_topic.get('title', '') }}</h4>
<p>{{ daily_topic.get('summary', '') }}</p>
<div class="topic-links">
<a href="#">Read More</a>
<a href="#">Source</a>
<a href="/feed">Read More</a>
<a href="/feed">Source</a>
</div>
</div>
@@ -154,7 +167,7 @@
<form id="create-post-form" method="POST" action="/posts/create" enctype="multipart/form-data">
<div style="display: flex; gap: 0.375rem; flex-wrap: wrap; margin-bottom: 1rem;">
{% for t in ['devlog', 'showcase', 'question', 'rant', 'fun', 'random'] %}
{% for t in ['devlog', 'showcase', 'question', 'rant', 'fun', 'random', 'signals'] %}
<label style="display: flex; align-items: center; gap: 0.25rem; padding: 0.375rem 0.75rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; cursor: pointer; background: var(--bg-card-hover); transition: all 0.2s;">
<input type="radio" name="topic" value="{{ t }}" {% if t == 'random' %}checked{% endif %} style="width: auto; accent-color: var(--accent);">
{{ t|capitalize }}
+2 -2
View File
@@ -43,8 +43,8 @@
<h3>{{ daily_topic.get('title', '') }}</h3>
<p>{{ daily_topic.get('summary', '') }}</p>
<div class="landing-daily-topic-links">
<a href="#">Read More</a>
<a href="#">Source</a>
<a href="/feed">Read More</a>
<a href="/feed">Source</a>
</div>
</div>
+1 -1
View File
@@ -37,7 +37,7 @@
<label>
<input type="checkbox" name="remember_me"> Remember me
</label>
<a href="#">Forgot your password?</a>
<a href="/auth/forgot-password">Forgot your password?</a>
</div>
<button type="submit" class="auth-submit">Sign in</button>
+36 -1
View File
@@ -1,5 +1,6 @@
{% extends "base.html" %}
{% block extra_head %}
<link rel="stylesheet" href="/static/css/feed.css">
<link rel="stylesheet" href="/static/css/post.css">
{% endblock %}
{% block content %}
@@ -37,6 +38,7 @@
</form>
<button class="post-action-btn">&#x1F517; Share</button>
{% if user and post['user_uid'] == user['uid'] %}
<button class="post-action-btn" data-modal="edit-post-modal">Edit</button>
<form method="POST" action="/posts/delete/{{ post['uid'] }}" style="display:inline;">
<button type="submit" class="post-action-btn" onclick="return confirm('Delete this post?')">Delete</button>
</form>
@@ -44,6 +46,39 @@
</div>
</article>
{% if user and post['user_uid'] == user['uid'] %}
<div class="modal-overlay" id="edit-post-modal">
<div class="card" style="width: 100%; max-width: 560px; margin: 1rem; max-height: 90vh; overflow-y: auto;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<h3 style="font-size: 1.125rem; font-weight: 700;">Edit Post</h3>
<button type="button" class="modal-close btn-ghost btn-icon" style="font-size: 1.25rem;">&times;</button>
</div>
<form method="POST" action="/posts/edit/{{ post['uid'] }}">
<div style="display: flex; gap: 0.375rem; flex-wrap: wrap; margin-bottom: 1rem;">
{% for t in topics %}
<label style="display: flex; align-items: center; gap: 0.25rem; padding: 0.375rem 0.75rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; cursor: pointer; background: var(--bg-card-hover); transition: all 0.2s;">
<input type="radio" name="topic" value="{{ t }}" {% if t == post['topic'] %}checked{% endif %} style="width: auto; accent-color: var(--accent);">
{{ t|capitalize }}
</label>
{% endfor %}
</div>
<div class="auth-field" style="margin-bottom: 0.75rem;">
<label for="edit-title">Title</label>
<input type="text" id="edit-title" name="title" maxlength="500" value="{{ post.get('title', '') }}">
</div>
<div class="auth-field" style="margin-bottom: 0.75rem;">
<label for="edit-content">Content</label>
<textarea id="edit-content" name="content" required maxlength="2000" style="min-height: 200px;">{{ post['content'] }}</textarea>
</div>
<div style="display: flex; gap: 0.75rem; justify-content: flex-end;">
<button type="button" class="modal-close btn btn-secondary">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
{% endif %}
<section class="comments-section">
<h3>Comments</h3>
@@ -61,7 +96,7 @@
</form>
</div>
<div class="comment-body">
<div class="comment-body" data-comment-uid="{{ item.comment['uid'] }}">
<div class="comment-header">
<a href="/profile/{{ item.author['username'] if item.author else '#' }}">
<img src="{{ avatar_url('multiavatar', item.author['username'] if item.author else '?', 24) }}" class="avatar-img" style="width: 24px; height: 24px; border-radius: 50%;" alt="{{ item.author['username'] if item.author else '?' }}" loading="lazy">
+5 -4
View File
@@ -54,7 +54,8 @@
</div>
{% if user and user['uid'] != profile_user['uid'] %}
<div style="margin-top: 1rem;">
<div style="margin-top: 1rem; display: flex; flex-direction: column; gap: 0.5rem;">
<a href="/messages?with_uid={{ profile_user['uid'] }}" class="btn btn-secondary btn-sm" style="width: 100%; justify-content: center;">Send Message</a>
{% if is_following %}
<form method="POST" action="/follow/unfollow/{{ profile_user['username'] }}">
<button type="submit" class="btn btn-secondary btn-sm" style="width: 100%;">Unfollow</button>
@@ -171,7 +172,7 @@
{% if item.post.get('title') %}
<h3 class="post-title">{{ item.post['title'] }}</h3>
{% endif %}
<div class="post-content rendered-content" data-render>{{ item.post['content'][:200] }}{% if item.post['content']|length > 200 %}...{% endif %}</div>
<div class="post-content rendered-content" data-render>{{ item.post['content'][:300] }}{% if item.post['content']|length > 300 %}...{% endif %}</div>
<div class="post-actions">
<span class="post-action-btn" style="cursor: default;">+{{ item.post.get('stars', 0) }}</span>
<span class="post-action-btn" style="cursor: default;">&#x1F4AC; {{ item.comment_count }}</span>
@@ -183,7 +184,7 @@
{% endfor %}
{% elif current_tab == 'projects' %}
{% for p in projects %}
<div class="project-card fade-in">
<a href="/projects/{{ p['uid'] }}" class="project-card fade-in" style="display: block; text-decoration: none; color: inherit;">
<div class="project-card-header">
<h3 class="project-card-title">{{ p['title'] }}</h3>
</div>
@@ -201,7 +202,7 @@
{% endfor %}
</div>
{% endif %}
</div>
</a>
{% else %}
<div class="empty-state">No projects yet.</div>
{% endfor %}
+144
View File
@@ -0,0 +1,144 @@
{% extends "base.html" %}
{% block extra_head %}
<link rel="stylesheet" href="/static/css/projects.css">
<style>
.project-detail-page {
max-width: 720px;
margin: 0 auto;
}
.project-detail {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.5rem;
}
.project-detail-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 1rem;
}
.project-detail-title {
font-size: 1.5rem;
font-weight: 700;
}
.project-detail-meta {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-bottom: 1rem;
font-size: 0.8125rem;
color: var(--text-muted);
}
.project-detail-meta span {
display: flex;
align-items: center;
gap: 0.25rem;
}
.project-detail-author {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border);
}
.project-detail-author a {
font-weight: 600;
font-size: 0.875rem;
color: var(--text-primary);
}
.project-detail-desc {
font-size: 0.9375rem;
color: var(--text-secondary);
line-height: 1.7;
margin-bottom: 1.5rem;
}
.project-detail-actions {
display: flex;
align-items: center;
gap: 0.5rem;
padding-top: 1rem;
border-top: 1px solid var(--border);
}
.project-star-btn {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.375rem 0.75rem;
border-radius: var(--radius);
font-size: 0.8125rem;
font-weight: 500;
color: var(--text-muted);
background: none;
border: none;
cursor: pointer;
transition: all 0.2s;
}
.project-star-btn:hover {
background: var(--bg-card-hover);
color: var(--warning);
}
</style>
{% endblock %}
{% block content %}
<div class="project-detail-page">
<a href="/projects" class="back-link">&larr; Back to Projects</a>
<article class="project-detail">
<div class="project-detail-header">
<h1 class="project-detail-title">{{ project['title'] }}</h1>
<div class="project-status {% if project.get('status') == 'Released' %}released{% else %}dev{% endif %}">
&#x25CF; {{ project.get('status', 'In Development') }}
</div>
</div>
<div class="project-detail-meta">
<span class="badge" style="background: var(--border); color: var(--text-muted); text-transform: capitalize;">{{ project.get('project_type', 'software').replace('_', ' ') }}</span>
{% if project.get('release_date') %}
<span>&#x1F4C5; Released: {{ project['release_date'] }}</span>
{% endif %}
{% if project.get('demo_date') %}
<span>&#x1F3AD; Demo: {{ project['demo_date'] }}</span>
{% endif %}
</div>
<div class="project-detail-author">
{% set _size = 32 %}{% set _size_class = "sm" %}{% set _user = author %}{% include "_avatar_link.html" %}
<div>
<a href="/profile/{{ author['username'] if author else '#' }}">{{ author['username'] if author else 'Unknown' }}</a>
{% if author and author.get('role') %}
<span style="font-size: 0.75rem; color: var(--text-muted);">&middot; {{ author['role'] }}</span>
{% endif %}
</div>
</div>
<div class="project-detail-desc rendered-content" data-render>{{ project.get('description', '') }}</div>
{% if platforms %}
<div style="margin-bottom: 1.5rem;">
<h4 style="font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin-bottom: 0.5rem;">Platforms</h4>
<div class="project-card-platforms">
{% for plat in platforms %}
<span class="platform-tag">{{ plat.strip() }}</span>
{% endfor %}
</div>
</div>
{% endif %}
<div class="project-detail-actions">
{% if user %}
<form method="POST" action="/votes/project/{{ project['uid'] }}" style="display:inline;">
<input type="hidden" name="value" value="1">
<button type="submit" class="project-star-btn">&#x2606; {{ star_count }}</button>
</form>
{% endif %}
{% if is_owner %}
<form method="POST" action="/projects/delete/{{ project['uid'] }}" style="display:inline;">
<button type="submit" class="project-star-btn" onclick="return confirm('Delete this project?')">Delete</button>
</form>
{% endif %}
</div>
</article>
</div>
{% endblock %}
+2 -2
View File
@@ -52,7 +52,7 @@
<div class="projects-grid">
{% for project in projects %}
<div class="project-card fade-in">
<a href="/projects/{{ project['uid'] }}" class="project-card fade-in" style="display: block; text-decoration: none; color: inherit;">
<div class="project-card-header">
<h3 class="project-card-title">{{ project['title'] }}</h3>
<form method="POST" action="/votes/project/{{ project['uid'] }}" style="display:inline;">
@@ -83,7 +83,7 @@
</div>
{% endif %}
</div>
</div>
</a>
{% else %}
<div class="empty-state" style="grid-column: 1 / -1;">No projects found. Create one!</div>
{% endfor %}
+74
View File
@@ -0,0 +1,74 @@
{% extends "admin_base.html" %}
{% block extra_head %}
{{ super() }}
<link rel="stylesheet" href="/static/css/services.css">
{% endblock %}
{% block admin_content %}
<div class="services-page">
<h1>Services</h1>
<div class="services-list" id="services-list">
{% for svc in services %}
<div class="service-card" data-service="{{ svc.name }}">
<div class="service-header">
<span class="service-name">{{ svc.name }}</span>
<span class="service-status {{ svc.status }}">{{ svc.status }}</span>
</div>
<div class="service-meta">
{% if svc.uptime %}
<span class="meta-item">Uptime: {{ svc.uptime }}</span>
{% endif %}
{% if svc.last_run %}
<span class="meta-item">Last run: {{ svc.last_run[:19] }}</span>
{% endif %}
{% if svc.next_run %}
<span class="meta-item">Next run: {{ svc.next_run[:19] }}</span>
{% endif %}
<span class="meta-item">Interval: every {{ svc.interval_seconds }}s</span>
</div>
<div class="service-log">
<div class="log-header">Log (last 20 lines)</div>
<pre class="log-output">{% for line in svc.log_buffer %}{{ line }}
{% else %}No log entries yet.
{% endfor %}</pre>
</div>
</div>
{% else %}
<p class="no-services">No services registered.</p>
{% endfor %}
</div>
</div>
{% endblock %}
{% block extra_js %}
<script type="module">
const POLL_INTERVAL = 5000;
async function pollServices() {
try {
const resp = await fetch('/admin/services/data');
const data = await resp.json();
const container = document.getElementById('services-list');
if (!container) return;
for (const svc of data.services) {
const card = container.querySelector(`.service-card[data-service="${svc.name}"]`);
if (!card) continue;
card.querySelector('.service-status').textContent = svc.status;
card.querySelector('.service-status').className = 'service-status ' + svc.status;
const metaItems = card.querySelectorAll('.meta-item');
if (metaItems.length >= 3) {
metaItems[0].textContent = 'Uptime: ' + (svc.uptime || '-');
metaItems[1].textContent = 'Last run: ' + (svc.last_run ? svc.last_run.slice(0, 19) : '-');
metaItems[2].textContent = 'Next run: ' + (svc.next_run ? svc.next_run.slice(0, 19) : '-');
}
const logPre = card.querySelector('.log-output');
if (logPre) {
logPre.textContent = svc.log_buffer.map(l => l + '\n').join('') || 'No log entries yet.\n';
}
}
} catch {
// silently ignore poll errors
}
}
setInterval(pollServices, POLL_INTERVAL);
</script>
{% endblock %}