feat: use modals for creation across all pages + fix profile prompt
All checks were successful
Deploy / deploy (push) Successful in 11s
All checks were successful
Deploy / deploy (push) Successful in 11s
- Campaigns: add create modal (name, brand, team, dates, budget) - PostProduction: add create modal (title, brand, campaign, assignee), auto-opens detail panel after creation - Tasks: add create modal (title, project, priority, assignee), auto-opens detail panel after creation - Fix profileComplete check: use !!user.name instead of !!user.team_role in /api/auth/me (was always showing profile prompt since team_role is now deprecated in favor of role_id) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,9 @@ export default function PostProduction() {
|
||||
const [selectedIds, setSelectedIds] = useState(new Set())
|
||||
const [showBulkDeleteConfirm, setShowBulkDeleteConfirm] = useState(false)
|
||||
const [showFilters, setShowFilters] = useState(false)
|
||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
||||
const [createForm, setCreateForm] = useState({ ...EMPTY_POST })
|
||||
const [createSaving, setCreateSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
loadPosts()
|
||||
@@ -133,7 +136,32 @@ export default function PostProduction() {
|
||||
}
|
||||
|
||||
const openNew = () => {
|
||||
setPanelPost(EMPTY_POST)
|
||||
setCreateForm({ ...EMPTY_POST })
|
||||
setShowCreateModal(true)
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
setCreateSaving(true)
|
||||
try {
|
||||
const data = {
|
||||
title: createForm.title,
|
||||
brand_id: createForm.brand_id ? Number(createForm.brand_id) : null,
|
||||
campaign_id: createForm.campaign_id ? Number(createForm.campaign_id) : null,
|
||||
assigned_to: createForm.assigned_to ? Number(createForm.assigned_to) : null,
|
||||
status: 'draft',
|
||||
}
|
||||
const created = await api.post('/posts', data)
|
||||
setShowCreateModal(false)
|
||||
toast.success(t('posts.created'))
|
||||
loadPosts()
|
||||
// Open the detail panel for further editing
|
||||
if (created) setPanelPost(created)
|
||||
} catch (err) {
|
||||
console.error('Create post failed:', err)
|
||||
toast.error(t('common.saveFailed'))
|
||||
} finally {
|
||||
setCreateSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const filteredPosts = posts.filter(p => {
|
||||
@@ -369,7 +397,48 @@ export default function PostProduction() {
|
||||
{t('common.bulkDeleteDesc')}
|
||||
</Modal>
|
||||
|
||||
{/* Post Detail Panel */}
|
||||
{/* Create Post Modal */}
|
||||
<Modal isOpen={showCreateModal} onClose={() => setShowCreateModal(false)} title={t('posts.newPost')} size="md">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('posts.postTitle')} *</label>
|
||||
<input type="text" value={createForm.title} onChange={e => setCreateForm(f => ({ ...f, title: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary" autoFocus />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('posts.brand')}</label>
|
||||
<select value={createForm.brand_id} onChange={e => setCreateForm(f => ({ ...f, brand_id: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary">
|
||||
<option value="">{t('posts.allBrands')}</option>
|
||||
{brands.map(b => <option key={b._id} value={b._id}>{lang === 'ar' && b.name_ar ? b.name_ar : b.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('posts.campaign')}</label>
|
||||
<select value={createForm.campaign_id} onChange={e => setCreateForm(f => ({ ...f, campaign_id: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary">
|
||||
<option value="">—</option>
|
||||
{campaigns.map(c => <option key={c._id || c.id} value={c._id || c.id}>{c.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('posts.assignedTo')}</label>
|
||||
<select value={createForm.assigned_to} onChange={e => setCreateForm(f => ({ ...f, assigned_to: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary">
|
||||
<option value="">{t('common.unassigned')}</option>
|
||||
{teamMembers.map(m => <option key={m._id} value={String(m._id)}>{m.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<button onClick={handleCreate} disabled={!createForm.title || createSaving}
|
||||
className={`w-full px-4 py-2.5 bg-brand-primary text-white rounded-lg text-sm font-medium hover:bg-brand-primary-light disabled:opacity-50 disabled:cursor-not-allowed shadow-sm ${createSaving ? 'btn-loading' : ''}`}>
|
||||
{t('posts.newPost')}
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* Post Detail Panel (edit only) */}
|
||||
{panelPost && (
|
||||
<PostDetailPanel
|
||||
post={panelPost}
|
||||
|
||||
Reference in New Issue
Block a user