rating and comment system complete

This commit is contained in:
2026-08-05 12:25:59 -05:00
parent 26e9179be4
commit aed6a7c24d
9 changed files with 407 additions and 1 deletions
@@ -21,6 +21,7 @@
<span class="pack-card-version"><i class="fas fa-tag"></i> {{ latest.version_name|default:"no versions" }}</span>
{% endwith %}
<span class="pack-card-downloads"><i class="fas fa-download"></i> {{ project.downloads }}</span>
<span class="pack-card-rating"><i class="fas fa-thumbs-up"></i> {{ project.rating_score }}</span>
</div>
</div>
</a>
+2
View File
@@ -57,6 +57,8 @@
<select name="sort" onchange="this.form.submit()">
<option value="recent" {% if sort == 'recent' %}selected{% endif %}>Newest</option>
<option value="downloads" {% if sort == 'downloads' %}selected{% endif %}>Most downloaded</option>
<option value="most_liked" {% if sort == 'most_liked' %}selected{% endif %}>Most liked</option>
<option value="most_voted" {% if sort == 'most_voted' %}selected{% endif %}>Most voted</option>
<option value="name" {% if sort == 'name' %}selected{% endif %}>Name A-Z</option>
</select>
</form>
@@ -81,6 +81,8 @@
{% if skins_manifest %}<button class="tab-btn" data-tab="skins" role="tab">Skins</button>{% endif %}
{% if mod_manifest %}<button class="tab-btn" data-tab="modinfo" role="tab">Mod Info</button>{% endif %}
<button class="tab-btn" data-tab="gallery" role="tab">Gallery</button>
<button class="tab-btn" data-tab="rating" role="tab">Rating</button>
<button class="tab-btn" data-tab="forum" role="tab">Forum</button>
</div>
<section class="tab-panel active" id="tab-{% if project.category == 'guide' %}guide{% else %}description{% endif %}">
@@ -517,6 +519,105 @@
</div>
</section>
<section class="tab-panel" id="tab-rating">
<div class="card">
<h2><i class="fas fa-thumbs-up"></i> Rating</h2>
{% if rating_stats.total %}
<div class="rating-summary">
<span class="rating-percent">{{ rating_stats.percent }}% positive</span>
<span class="rating-count">{{ rating_stats.total }} vote{{ rating_stats.total|pluralize }} · {{ rating_stats.positive }} up · {{ rating_stats.negative }} down</span>
<div class="rating-bar">
<div class="rating-bar-up" style="width: {{ rating_stats.percent }}%"></div>
<div class="rating-bar-down" style="width: {{ rating_stats.negative }}%"></div>
</div>
</div>
{% else %}
<p class="empty-hint">No ratings yet — be the first to vote.</p>
{% endif %}
{% if can_comment %}
<div class="rating-vote">
<form method="post" action="{% url 'library:rate_project' project.slug %}">
{% csrf_token %}
<input type="hidden" name="value" value="1">
<button type="submit" class="rating-btn {% if user_rating %}active{% endif %}" title="Positive"><i class="fas fa-thumbs-up"></i> Good</button>
</form>
<form method="post" action="{% url 'library:rate_project' project.slug %}">
{% csrf_token %}
<input type="hidden" name="value" value="0">
<button type="submit" class="rating-btn {% if user_rating is False %}active{% endif %}" title="Negative"><i class="fas fa-thumbs-down"></i> Bad</button>
</form>
{% if user_rating is not None %}
<form method="post" action="{% url 'library:rate_project' project.slug %}">
{% csrf_token %}
<input type="hidden" name="value" value="">
<button type="submit" class="rating-btn" title="Clear your vote"><i class="fas fa-undo"></i></button>
</form>
{% endif %}
</div>
{% else %}
<p class="empty-hint"><a href="{% url 'profiles:login' %}?next={{ request.path }}" class="btn btn-secondary btn-sm">Log in</a> to rate this pack.</p>
{% endif %}
</div>
</section>
<section class="tab-panel" id="tab-forum">
<div class="card">
<h2><i class="fas fa-comments"></i> Forum</h2>
{% if can_comment %}
<form method="post" action="{% url 'library:add_comment' project.slug %}" class="comment-form">
{% csrf_token %}
<textarea name="body" rows="4" placeholder="Write a comment… Markdown supported." required></textarea>
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> Post</button>
</form>
{% else %}
<p class="empty-hint"><a href="{% url 'profiles:login' %}?next={{ request.path }}" class="btn btn-secondary btn-sm">Log in</a> to join the discussion.</p>
{% endif %}
<div class="comment-list">
{% for comment in comments %}
<div class="comment" id="comment-{{ comment.pk }}">
<div class="comment-head">
<strong>{{ comment.user.username }}</strong>
<span class="comment-time">{{ comment.created_at|timesince }} ago</span>
{% if comment.updated_at != comment.created_at %}<span class="comment-edited">· edited</span>{% endif %}
{% if can_comment and comment.user == request.user %}
<span class="comment-actions">
<a href="#" class="comment-edit btn btn-secondary btn-sm" data-comment="{{ comment.pk }}"><i class="fas fa-edit"></i> Edit</a>
<form method="post" action="{% url 'library:delete_comment' project.slug comment.pk %}" class="inline">
{% csrf_token %}
<button type="submit" class="link-btn" title="Delete"><i class="fas fa-trash"></i></button>
</form>
</span>
{% elif can_comment and request.user.is_staff %}
<span class="comment-actions">
<form method="post" action="{% url 'library:delete_comment' project.slug comment.pk %}" class="inline" title="Delete this comment">
{% csrf_token %}
<button type="submit" class="btn btn-secondary btn-sm"><i class="fas fa-trash"></i> Delete</button>
</form>
</span>
{% endif %}
</div>
<div class="comment-body markdown-body">{{ comment.body|markdown }}</div>
{% if can_comment and comment.user == request.user %}
<form method="post" action="{% url 'library:edit_comment' project.slug comment.pk %}" class="comment-edit-form" data-comment="{{ comment.pk }}" hidden>
{% csrf_token %}
<textarea name="body" rows="3" required>{{ comment.body }}</textarea>
<div class="comment-edit-actions">
<button type="submit" class="btn btn-primary btn-sm">Save</button>
<button type="button" class="btn btn-secondary btn-sm comment-edit-cancel">Cancel</button>
</div>
</form>
{% endif %}
</div>
{% empty %}
<p class="empty-hint">No comments yet.</p>
{% endfor %}
</div>
</div>
</section>
<div class="modal-overlay" id="gallery-modal" hidden>
<div class="modal-content media-modal-content">
<button type="button" class="modal-close" data-close-modal aria-label="Close"><i class="fas fa-times"></i></button>
@@ -542,6 +643,34 @@
});
});
// Land on the #rating / #forum tab when the URL points there.
const hashTab = location.hash.replace('#', '');
if (hashTab === 'rating' || hashTab === 'forum') {
const btn = document.querySelector(`.tab-btn[data-tab="${hashTab}"]`);
if (btn) btn.click();
}
// YouTube-style inline comment editing.
document.querySelectorAll('.comment-edit').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const id = link.dataset.comment;
const body = document.querySelector(`#comment-${id} .comment-body`);
const form = document.querySelector(`#comment-${id} .comment-edit-form`);
if (body) body.hidden = true;
if (form) form.hidden = false;
});
});
document.querySelectorAll('.comment-edit-cancel').forEach(btn => {
btn.addEventListener('click', () => {
const form = btn.closest('.comment-edit-form');
const id = form.dataset.comment;
const body = document.querySelector(`#comment-${id} .comment-body`);
if (body) body.hidden = false;
form.hidden = true;
});
});
// Gallery deletion without a page reload, behind a confirmation dialog.
document.querySelectorAll('.gallery-delete').forEach(link => {
link.addEventListener('click', (e) => {