Add comprehensive feature request admin functionality

- Update data model to include status, adminReply, and updatedAt fields
- Hide user emails from public API responses for privacy
- Add admin-only endpoints: list, reply, update status, delete
- Create admin-feature-requests.html with full management UI
- Add status badges and admin replies to public feature requests page
- Add Feature Requests link to all admin page sidebars

Admin capabilities:
- View all feature requests with author emails (admin only)
- Reply to feature requests with admin responses visible to public
- Update status: pending, planned, in-progress, completed, declined
- Delete feature requests
- Filter and sort by status, votes, date
This commit is contained in:
southseact-3d
2026-02-10 13:27:36 +00:00
parent 0205820589
commit 25d23d8dd1
14 changed files with 749 additions and 4 deletions

View File

@@ -271,6 +271,66 @@
gap: 16px;
font-size: 12px;
color: #adb5bd;
align-items: center;
}
.fr-status {
display: inline-block;
padding: 3px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
}
.fr-status.pending {
background: rgba(108, 117, 125, 0.1);
color: #6c757d;
}
.fr-status.planned {
background: rgba(0, 123, 255, 0.1);
color: #007bff;
}
.fr-status.in-progress {
background: rgba(255, 193, 7, 0.1);
color: #856404;
}
.fr-status.completed {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
}
.fr-status.declined {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
}
.fr-admin-reply {
background: linear-gradient(135deg, rgba(0, 128, 96, 0.05), rgba(0, 76, 63, 0.05));
border: 1px solid rgba(0, 128, 96, 0.2);
border-radius: 10px;
padding: 14px 16px;
margin-top: 14px;
}
.fr-admin-reply-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
color: var(--shopify-green);
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 6px;
}
.fr-admin-reply-text {
font-size: 14px;
line-height: 1.6;
color: #1a1a1a;
}
.fr-empty {
@@ -697,6 +757,17 @@
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}
function getStatusLabel(status) {
const labels = {
'pending': 'Pending',
'planned': 'Planned',
'in-progress': 'In Progress',
'completed': 'Completed',
'declined': 'Declined'
};
return labels[status] || status;
}
function renderFeatureRequests() {
if (state.featureRequests.length === 0) {
frList.innerHTML = `
@@ -725,10 +796,21 @@
<h3 class="fr-title">${escapeHtml(fr.title)}</h3>
<p class="fr-description">${escapeHtml(fr.description)}</p>
<div class="fr-meta">
<span>Submitted by ${escapeHtml(fr.authorEmail || 'Anonymous')}</span>
<span class="fr-status ${fr.status || 'pending'}">${getStatusLabel(fr.status || 'pending')}</span>
<span>•</span>
<span>${formatDate(fr.createdAt)}</span>
</div>
${fr.adminReply ? `
<div class="fr-admin-reply">
<div class="fr-admin-reply-label">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
Admin Response
</div>
<div class="fr-admin-reply-text">${escapeHtml(fr.adminReply)}</div>
</div>
` : ''}
</div>
</div>
</div>