fix: require feedback on post rejection, post-specific review text, show superadmins in team list
All checks were successful
Deploy / deploy (push) Successful in 11s
All checks were successful
Deploy / deploy (push) Successful in 11s
- Reject requires feedback on both client and server (400 if empty) - PublicPostReview uses post-specific i18n keys instead of artefact ones - Team list always includes superadmins/managers for non-superadmin users Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -902,5 +902,11 @@
|
|||||||
"posts.submitForReview": "إرسال للمراجعة",
|
"posts.submitForReview": "إرسال للمراجعة",
|
||||||
"posts.schedulePost": "جدولة المنشور",
|
"posts.schedulePost": "جدولة المنشور",
|
||||||
"review.postReview": "مراجعة المنشور",
|
"review.postReview": "مراجعة المنشور",
|
||||||
"review.createdBy": "أنشئ بواسطة"
|
"review.createdBy": "أنشئ بواسطة",
|
||||||
|
"review.confirmApprovePost": "الموافقة على هذا المنشور؟",
|
||||||
|
"review.confirmRejectPost": "رفض هذا المنشور؟",
|
||||||
|
"review.confirmApprovePostDesc": "هل أنت متأكد من الموافقة على هذا المنشور؟",
|
||||||
|
"review.confirmRejectPostDesc": "هل أنت متأكد من رفض هذا المنشور؟ يرجى تقديم ملاحظات توضح السبب.",
|
||||||
|
"review.feedbackRequired": "الملاحظات (مطلوبة)",
|
||||||
|
"review.feedbackRequiredError": "يرجى تقديم ملاحظات عند الرفض"
|
||||||
}
|
}
|
||||||
@@ -902,5 +902,11 @@
|
|||||||
"posts.submitForReview": "Submit for Review",
|
"posts.submitForReview": "Submit for Review",
|
||||||
"posts.schedulePost": "Schedule Post",
|
"posts.schedulePost": "Schedule Post",
|
||||||
"review.postReview": "Post Review",
|
"review.postReview": "Post Review",
|
||||||
"review.createdBy": "Created by"
|
"review.createdBy": "Created by",
|
||||||
|
"review.confirmApprovePost": "Approve this post?",
|
||||||
|
"review.confirmRejectPost": "Reject this post?",
|
||||||
|
"review.confirmApprovePostDesc": "Are you sure you want to approve this post?",
|
||||||
|
"review.confirmRejectPostDesc": "Are you sure you want to reject this post? Please provide feedback explaining why.",
|
||||||
|
"review.feedbackRequired": "Feedback (required)",
|
||||||
|
"review.feedbackRequiredError": "Please provide feedback when rejecting"
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,10 @@ export default function PublicPostReview() {
|
|||||||
toast.error(t('review.enterName'))
|
toast.error(t('review.enterName'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (action === 'reject' && !feedback.trim()) {
|
||||||
|
toast.error(t('review.feedbackRequiredError'))
|
||||||
|
return
|
||||||
|
}
|
||||||
setPendingAction(action)
|
setPendingAction(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +280,7 @@ export default function PublicPostReview() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('review.feedbackOptional')}</label>
|
<label className="block text-sm font-medium text-text-primary mb-1">{t('review.feedbackRequired')}</label>
|
||||||
<textarea value={feedback} onChange={e => setFeedback(e.target.value)} rows={4}
|
<textarea value={feedback} onChange={e => setFeedback(e.target.value)} rows={4}
|
||||||
placeholder={t('review.feedbackPlaceholder')}
|
placeholder={t('review.feedbackPlaceholder')}
|
||||||
className="w-full px-4 py-2 text-sm border border-border rounded-lg bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary" />
|
className="w-full px-4 py-2 text-sm border border-border rounded-lg bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary" />
|
||||||
@@ -328,13 +332,13 @@ export default function PublicPostReview() {
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={!!pendingAction}
|
isOpen={!!pendingAction}
|
||||||
onClose={() => setPendingAction(null)}
|
onClose={() => setPendingAction(null)}
|
||||||
title={pendingAction === 'approve' ? t('review.confirmApprove') : t('review.confirmReject')}
|
title={pendingAction === 'approve' ? t('review.confirmApprovePost') : t('review.confirmRejectPost')}
|
||||||
isConfirm
|
isConfirm
|
||||||
danger={pendingAction === 'reject'}
|
danger={pendingAction === 'reject'}
|
||||||
onConfirm={() => { const a = pendingAction; setPendingAction(null); executeAction(a) }}
|
onConfirm={() => { const a = pendingAction; setPendingAction(null); executeAction(a) }}
|
||||||
confirmText={pendingAction === 'approve' ? t('review.approve') : t('review.reject')}
|
confirmText={pendingAction === 'approve' ? t('review.approve') : t('review.reject')}
|
||||||
>
|
>
|
||||||
{pendingAction === 'approve' ? t('review.confirmApproveDesc') : t('review.confirmRejectDesc')}
|
{pendingAction === 'approve' ? t('review.confirmApprovePostDesc') : t('review.confirmRejectPostDesc')}
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -898,9 +898,11 @@ app.get('/api/users/team', requireAuth, async (req, res) => {
|
|||||||
try { myBrands = JSON.parse(currentUser?.brands || '[]'); } catch (err) { console.error('Parse user brands:', err.message); }
|
try { myBrands = JSON.parse(currentUser?.brands || '[]'); } catch (err) { console.error('Parse user brands:', err.message); }
|
||||||
|
|
||||||
filtered = users.filter(u => {
|
filtered = users.filter(u => {
|
||||||
|
// Always include self, superadmins, and managers
|
||||||
|
if (u.Id === req.session.userId || u.role === 'superadmin' || u.role === 'manager') return true;
|
||||||
let theirBrands = [];
|
let theirBrands = [];
|
||||||
try { theirBrands = JSON.parse(u.brands || '[]'); } catch (err) { console.error('Parse team brands:', err.message); }
|
try { theirBrands = JSON.parse(u.brands || '[]'); } catch (err) { console.error('Parse team brands:', err.message); }
|
||||||
return u.Id === req.session.userId || theirBrands.some(b => myBrands.includes(b));
|
return theirBrands.some(b => myBrands.includes(b));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1586,6 +1588,9 @@ app.post('/api/public/review-post/:token/approve', async (req, res) => {
|
|||||||
// Public: Reject post
|
// Public: Reject post
|
||||||
app.post('/api/public/review-post/:token/reject', async (req, res) => {
|
app.post('/api/public/review-post/:token/reject', async (req, res) => {
|
||||||
const { approved_by_name, feedback } = req.body;
|
const { approved_by_name, feedback } = req.body;
|
||||||
|
if (!feedback || !feedback.trim()) {
|
||||||
|
return res.status(400).json({ error: 'Feedback is required when rejecting' });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const posts = await nocodb.list('Posts', {
|
const posts = await nocodb.list('Posts', {
|
||||||
where: `(approval_token,eq,${sanitizeWhereValue(req.params.token)})`,
|
where: `(approval_token,eq,${sanitizeWhereValue(req.params.token)})`,
|
||||||
|
|||||||
Reference in New Issue
Block a user