rating and comment system complete
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user