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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user