adding brand management
This commit is contained in:
@@ -4,6 +4,7 @@ import { ArrowLeft, Plus, Edit2, Trash2, DollarSign, Eye, MousePointer, Target,
|
||||
import { format } from 'date-fns'
|
||||
import { AppContext } from '../App'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { useLanguage } from '../i18n/LanguageContext'
|
||||
import { api, PLATFORMS, getInitials } from '../utils/api'
|
||||
import PlatformIcon, { PlatformIcons } from '../components/PlatformIcon'
|
||||
import StatusBadge from '../components/StatusBadge'
|
||||
@@ -43,7 +44,8 @@ function MetricBox({ label, value, icon: Icon, color = 'text-text-primary' }) {
|
||||
export default function CampaignDetail() {
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const { brands } = useContext(AppContext)
|
||||
const { brands, getBrandName } = useContext(AppContext)
|
||||
const { lang } = useLanguage()
|
||||
const { permissions, user } = useAuth()
|
||||
const isSuperadmin = user?.role === 'superadmin'
|
||||
const [campaign, setCampaign] = useState(null)
|
||||
@@ -69,6 +71,7 @@ export default function CampaignDetail() {
|
||||
const [showDiscussion, setShowDiscussion] = useState(false)
|
||||
const [showEditModal, setShowEditModal] = useState(false)
|
||||
const [editForm, setEditForm] = useState({})
|
||||
const [showDeleteCampaignConfirm, setShowDeleteCampaignConfirm] = useState(false)
|
||||
|
||||
const isCreator = campaign?.createdByUserId === user?.id || campaign?.created_by_user_id === user?.id
|
||||
const canManage = isSuperadmin || (permissions?.canEditCampaigns && isCreator)
|
||||
@@ -215,6 +218,8 @@ export default function CampaignDetail() {
|
||||
goals: campaign.goals || '',
|
||||
platforms: campaign.platforms || [],
|
||||
notes: campaign.notes || '',
|
||||
brand_id: campaign.brand_id || '',
|
||||
budget: campaign.budget || '',
|
||||
})
|
||||
setShowEditModal(true)
|
||||
}
|
||||
@@ -230,6 +235,8 @@ export default function CampaignDetail() {
|
||||
goals: editForm.goals,
|
||||
platforms: editForm.platforms,
|
||||
notes: editForm.notes,
|
||||
brand_id: editForm.brand_id || null,
|
||||
budget: editForm.budget ? Number(editForm.budget) : null,
|
||||
})
|
||||
setShowEditModal(false)
|
||||
loadAll()
|
||||
@@ -284,7 +291,7 @@ export default function CampaignDetail() {
|
||||
<div className="flex items-center gap-3 mb-1">
|
||||
<h1 className="text-xl font-bold text-text-primary">{campaign.name}</h1>
|
||||
<StatusBadge status={campaign.status} />
|
||||
{campaign.brand_name && <BrandBadge brand={campaign.brand_name} />}
|
||||
{(campaign.brand_id || campaign.brand_name) && <BrandBadge brand={getBrandName(campaign.brand_id) || campaign.brand_name} />}
|
||||
</div>
|
||||
{campaign.description && <p className="text-sm text-text-secondary">{campaign.description}</p>}
|
||||
<div className="flex items-center gap-3 mt-2 text-xs text-text-tertiary">
|
||||
@@ -852,6 +859,19 @@ export default function CampaignDetail() {
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">Brand</label>
|
||||
<select
|
||||
value={editForm.brand_id || ''}
|
||||
onChange={e => setEditForm(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="">No brand</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-sm font-medium text-text-primary mb-1">Description</label>
|
||||
<textarea
|
||||
@@ -861,7 +881,7 @@ export default function CampaignDetail() {
|
||||
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 resize-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">Status</label>
|
||||
<select
|
||||
@@ -885,6 +905,17 @@ export default function CampaignDetail() {
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">Budget (SAR)</label>
|
||||
<input
|
||||
type="number"
|
||||
value={editForm.budget || ''}
|
||||
onChange={e => setEditForm(f => ({ ...f, budget: e.target.value }))}
|
||||
min="0"
|
||||
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"
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
@@ -949,6 +980,14 @@ export default function CampaignDetail() {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-3 pt-4 border-t border-border">
|
||||
{permissions?.canDeleteCampaigns && (
|
||||
<button
|
||||
onClick={() => setShowDeleteCampaignConfirm(true)}
|
||||
className="px-4 py-2 text-sm font-medium text-red-600 hover:bg-red-50 rounded-lg mr-auto"
|
||||
>
|
||||
Delete Campaign
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setShowEditModal(false)}
|
||||
className="px-4 py-2 text-sm font-medium text-text-secondary hover:bg-surface-tertiary rounded-lg"
|
||||
@@ -966,6 +1005,28 @@ export default function CampaignDetail() {
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* Delete Campaign Confirmation */}
|
||||
<Modal
|
||||
isOpen={showDeleteCampaignConfirm}
|
||||
onClose={() => setShowDeleteCampaignConfirm(false)}
|
||||
title="Delete Campaign?"
|
||||
isConfirm
|
||||
danger
|
||||
confirmText="Delete Campaign"
|
||||
onConfirm={async () => {
|
||||
try {
|
||||
await api.delete(`/campaigns/${id}`)
|
||||
setShowDeleteCampaignConfirm(false)
|
||||
setShowEditModal(false)
|
||||
navigate('/campaigns')
|
||||
} catch (err) {
|
||||
console.error('Failed to delete campaign:', err)
|
||||
}
|
||||
}}
|
||||
>
|
||||
Are you sure you want to delete this campaign? All tracks and linked data will be permanently removed. This action cannot be undone.
|
||||
</Modal>
|
||||
|
||||
{/* Post Detail Modal */}
|
||||
<Modal
|
||||
isOpen={!!selectedPost}
|
||||
|
||||
Reference in New Issue
Block a user