source: Klonkt/src/views/pages/admin-comments.ejs@ d2c43f9

main
Last change on this file since d2c43f9 was 8cb1dc7, checked in by roboburr <roboburr@…>, 3 months ago

hub: artist self-management — owner manages their own subsite (posts/appearance/comments)

A site owner (member) now gets a "My PrutFolio" dashboard and can manage their
OWN site; cross-site access is restricted.

  • auth.js: requireSiteManager (owner of res.locals.site or god) + requireSiteManagerBySlug (owner of :slug or god).
  • admin.js: /admin role-aware -> god=hub management, owner=my-site dashboard, member without site=403.
  • admin-sites.js: /:slug/edit + /save owner-or-god (was god-only); upload-photo requireAuth. list/new/create/delete remain god-only.
  • admin-comments.js: requireSiteManager + redirects/form actions via siteUrlBase so the artist context (/user/<slug>/admin/comments) is correct.
  • render.js: userOwnsSite flag; topnav + overview show "Admin" for owners; topnav "New post" uses siteUrlBase.
  • pages/my-site.ejs: artist dashboard.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 4.4 KB
RevLine 
[7bc636b]1<div class="container admin-comments-page">
[8b014ef]2 <p><a href="/admin" class="btn">&larr; Beheer</a></p>
[7bc636b]3 <h1>Comment moderation</h1>
4
5 <p class="muted">
6 Mode for this site: <strong><%= moderationMode %></strong>
7 <% if (moderationMode === 'trust') { %>
8 &mdash; comments are auto-approved. Switch to <em>moderate</em> in
9 <a href="/admin/sites/<%= site.slug %>/edit">site settings</a> to queue them.
10 <% } else { %>
11 &mdash; new comments require approval before showing up on posts.
12 <% } %>
13 </p>
14
15 <% if (success) { %><div class="audio-flash audio-flash--ok"><%= success %></div><% } %>
16 <% if (error) { %><div class="audio-flash audio-flash--err"><%= error %></div><% } %>
17
18 <h2>Pending (<%= pending.length %>)</h2>
19 <% if (!pending.length) { %>
20 <p class="muted">Nothing waiting.</p>
21 <% } else { %>
22 <ol class="moderation-list">
23 <% pending.forEach(function(c) { %>
24 <li class="moderation-item">
25 <div class="moderation-meta">
26 <strong><%= c.author_username %></strong>
27 <% if (c.parent_comment_id) { %><span class="muted">(reply)</span><% } %>
[8cb1dc7]28 on <a href="<%= siteUrlBase %>/<%= c.post_slug %>"><%= c.post_title || c.post_slug %></a>
[7bc636b]29 <span class="muted">&middot; <%= formatDateTime(c.created_at) %></span>
30 </div>
31 <blockquote class="moderation-content"><%= c.content %></blockquote>
32 <div class="moderation-actions">
[8cb1dc7]33 <form method="post" action="<%= siteUrlBase %>/admin/comments/<%= c.id %>/approve" style="display:inline">
[7bc636b]34 <button type="submit" class="btn btn-primary">Approve</button>
35 </form>
[8cb1dc7]36 <form method="post" action="<%= siteUrlBase %>/admin/comments/<%= c.id %>/reject" style="display:inline">
[7bc636b]37 <button type="submit" class="btn btn-danger">Reject</button>
38 </form>
39 </div>
40 </li>
41 <% }); %>
42 </ol>
43 <% } %>
44
45 <h2>Recent decisions</h2>
46 <% if (!recent.length) { %>
47 <p class="muted">Nothing yet.</p>
48 <% } else { %>
49 <table class="admin-table">
50 <thead>
51 <tr><th>Author</th><th>Post</th><th>Status</th><th>Date</th></tr>
52 </thead>
53 <tbody>
54 <% recent.forEach(function(c) { %>
55 <tr class="status-<%= c.status %>">
56 <td><%= c.author_username %></td>
[8cb1dc7]57 <td><a href="<%= siteUrlBase %>/<%= c.post_slug %>"><%= c.post_title || c.post_slug %></a></td>
[7bc636b]58 <td>
59 <span class="status-pill status-pill--<%= c.status %>"><%= c.status %></span>
60 </td>
61 <td><%= formatDateTime(c.created_at) %></td>
62 </tr>
63 <% }); %>
64 </tbody>
65 </table>
66 <% } %>
67</div>
68
69<style>
70.admin-comments-page { max-width: 900px; margin: 3rem auto; padding: 0 1rem; }
71.admin-comments-page h1 { font-family: var(--font-display, serif); font-size: 2rem; margin: 0 0 1rem; }
72.admin-comments-page h2 { font-family: var(--font-display, serif); font-size: 1.25rem; margin: 1.5rem 0 0.75rem; }
73.muted { color: var(--ink-muted, var(--ink-soft)); }
74.muted a { color: var(--accent); }
75
76.moderation-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 1rem; }
77.moderation-item {
78 background: var(--paper-2);
79 border: 1px solid var(--rule);
80 border-radius: 8px;
81 padding: 1rem;
82}
83.moderation-meta { font-size: 0.85rem; margin-bottom: 0.5rem; color: var(--ink-soft); }
84.moderation-meta a { color: var(--accent); }
85.moderation-content {
86 margin: 0 0 0.75rem;
87 padding: 0.5rem 0.75rem;
88 border-left: 3px solid var(--accent);
89 background: var(--paper);
90 white-space: pre-wrap;
91 word-wrap: break-word;
92 color: var(--ink);
93}
94.moderation-actions { display: flex; gap: 0.5rem; }
95
96.status-pill {
97 display: inline-block; padding: 0.1rem 0.5rem; border-radius: 10px;
98 font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;
99}
100.status-pill--approved { background: rgba(40, 160, 90, 0.15); color: #2a9d5e; }
101.status-pill--rejected { background: rgba(200, 60, 60, 0.15); color: #c33; }
102.status-pill--pending { background: var(--paper); color: var(--ink-muted, var(--ink-soft)); }
103
104.audio-flash { padding: 0.75rem 1rem; border-radius: 6px; margin: 1rem 0; font-size: 0.9rem; }
105.audio-flash--ok { background: rgba(40, 160, 90, 0.15); color: #2a9d5e; border: 1px solid rgba(40, 160, 90, 0.3); }
106.audio-flash--err { background: rgba(200, 60, 60, 0.15); color: #c33; border: 1px solid rgba(200, 60, 60, 0.3); }
107</style>
Note: See TracBrowser for help on using the repository browser.