fix: select dropdowns clipped by sections, add approval actions to posts
All checks were successful
Deploy / deploy (push) Successful in 12s
All checks were successful
Deploy / deploy (push) Successful in 12s
- Fix CollapsibleSection overflow clipping select dropdowns - Add SlidePanel footer prop for always-visible save/cancel bar - Add approval action buttons: Send to Review, Approve, Reject, Schedule - Add sync-brands.js script for local→remote NocoDB brand sync - Add posts.reject i18n key (en + ar) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { X, Trash2, Upload, FileText, Link2, ExternalLink, FolderOpen, Image as ImageIcon, Music, Film } from 'lucide-react'
|
||||
import { X, Trash2, Upload, FileText, Link2, ExternalLink, FolderOpen, Image as ImageIcon, Music, Film, Send, CheckCircle2, XCircle } from 'lucide-react'
|
||||
import { useLanguage } from '../i18n/LanguageContext'
|
||||
import { api, PLATFORMS, getBrandColor } from '../utils/api'
|
||||
import ApproverMultiSelect from './ApproverMultiSelect'
|
||||
@@ -124,6 +124,18 @@ export default function PostDetailPanel({ post, onClose, onSave, onDelete, brand
|
||||
}
|
||||
}
|
||||
|
||||
const handleStatusAction = async (newStatus) => {
|
||||
if (!postId || saving) return
|
||||
setSaving(true)
|
||||
try {
|
||||
await onSave(postId, { ...form, status: newStatus, approver_ids: (form.approver_ids || []).length > 0 ? form.approver_ids.join(',') : null })
|
||||
setForm(f => ({ ...f, status: newStatus }))
|
||||
setDirty(false)
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const confirmDelete = async () => {
|
||||
setShowDeleteConfirm(false)
|
||||
await onDelete(postId)
|
||||
@@ -380,13 +392,59 @@ export default function PostDetailPanel({ post, onClose, onSave, onDelete, brand
|
||||
|
||||
{/* Approval Section */}
|
||||
<CollapsibleSection title={t('posts.approval')}>
|
||||
<div className="px-5 pb-4">
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('posts.approvers')}</label>
|
||||
<ApproverMultiSelect
|
||||
users={teamMembers || []}
|
||||
selected={form.approver_ids || []}
|
||||
onChange={ids => update('approver_ids', ids)}
|
||||
/>
|
||||
<div className="px-5 pb-4 space-y-3">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('posts.approvers')}</label>
|
||||
<ApproverMultiSelect
|
||||
users={teamMembers || []}
|
||||
selected={form.approver_ids || []}
|
||||
onChange={ids => update('approver_ids', ids)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!isCreateMode && (
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{(form.status === 'draft' || form.status === 'rejected') && (
|
||||
<button
|
||||
onClick={() => handleStatusAction('in_review')}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 bg-amber-600 text-white rounded-lg text-xs font-medium hover:bg-amber-700 disabled:opacity-50"
|
||||
>
|
||||
<Send className="w-3.5 h-3.5" />
|
||||
{t('posts.sendToReview')}
|
||||
</button>
|
||||
)}
|
||||
{form.status === 'in_review' && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => handleStatusAction('approved')}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 bg-emerald-600 text-white rounded-lg text-xs font-medium hover:bg-emerald-700 disabled:opacity-50"
|
||||
>
|
||||
<CheckCircle2 className="w-3.5 h-3.5" />
|
||||
{t('posts.approve')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleStatusAction('rejected')}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 bg-red-600 text-white rounded-lg text-xs font-medium hover:bg-red-700 disabled:opacity-50"
|
||||
>
|
||||
<XCircle className="w-3.5 h-3.5" />
|
||||
{t('posts.reject')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{form.status === 'approved' && (
|
||||
<button
|
||||
onClick={() => handleStatusAction('scheduled')}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 bg-purple-600 text-white rounded-lg text-xs font-medium hover:bg-purple-700 disabled:opacity-50"
|
||||
>
|
||||
{t('posts.schedule')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
|
||||
@@ -888,5 +888,6 @@
|
||||
"posts.approvers": "المعتمدون",
|
||||
"posts.selectApprovers": "اختر المعتمدين...",
|
||||
"posts.scheduling": "الجدولة والتعيين",
|
||||
"posts.content": "المحتوى"
|
||||
"posts.content": "المحتوى",
|
||||
"posts.reject": "رفض"
|
||||
}
|
||||
@@ -888,5 +888,6 @@
|
||||
"posts.approvers": "Approvers",
|
||||
"posts.selectApprovers": "Select approvers...",
|
||||
"posts.scheduling": "Scheduling & Assignment",
|
||||
"posts.content": "Content"
|
||||
"posts.content": "Content",
|
||||
"posts.reject": "Reject"
|
||||
}
|
||||
@@ -421,6 +421,10 @@ textarea {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.collapsible-content.is-open > .collapsible-inner {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Stagger children */
|
||||
.stagger-children > * {
|
||||
opacity: 0;
|
||||
|
||||
Reference in New Issue
Block a user