diff --git a/client/src/pages/Campaigns.jsx b/client/src/pages/Campaigns.jsx index 330f086..fa9ea67 100644 --- a/client/src/pages/Campaigns.jsx +++ b/client/src/pages/Campaigns.jsx @@ -12,8 +12,14 @@ import BrandBadge from '../components/BrandBadge' import BudgetBar from '../components/BudgetBar' import InteractiveTimeline from '../components/InteractiveTimeline' import CampaignDetailPanel from '../components/CampaignDetailPanel' +import Modal from '../components/Modal' import { SkeletonStatCard, SkeletonTable } from '../components/SkeletonLoader' +const EMPTY_CAMPAIGN = { + name: '', description: '', brand_id: '', status: 'planning', + start_date: '', end_date: '', budget: '', team_id: '', +} + function ROIBadge({ revenue, spent }) { if (!spent || spent <= 0) return null const roi = ((revenue - spent) / spent * 100).toFixed(0) @@ -36,14 +42,17 @@ function MetricCard({ icon: Icon, label, value, color = 'text-text-primary' }) { } export default function Campaigns() { - const { brands, getBrandName } = useContext(AppContext) - const { lang, currencySymbol } = useLanguage() + const { brands, getBrandName, teams } = useContext(AppContext) + const { t, lang, currencySymbol } = useLanguage() const { permissions } = useAuth() const navigate = useNavigate() const [campaigns, setCampaigns] = useState([]) const [loading, setLoading] = useState(true) const [panelCampaign, setPanelCampaign] = useState(null) const [filters, setFilters] = useState({ brand: '', status: '' }) + const [showCreateModal, setShowCreateModal] = useState(false) + const [createForm, setCreateForm] = useState({ ...EMPTY_CAMPAIGN }) + const [createSaving, setCreateSaving] = useState(false) useEffect(() => { loadCampaigns() }, []) @@ -73,7 +82,34 @@ export default function Campaigns() { } const openNew = () => { - setPanelCampaign({ status: 'planning', platforms: [] }) + setCreateForm({ ...EMPTY_CAMPAIGN }) + setShowCreateModal(true) + } + + const handleCreate = async () => { + setCreateSaving(true) + try { + const data = { + name: createForm.name, + description: createForm.description, + brand_id: createForm.brand_id ? Number(createForm.brand_id) : null, + status: createForm.status, + start_date: createForm.start_date || null, + end_date: createForm.end_date || null, + budget: createForm.budget ? Number(createForm.budget) : null, + team_id: createForm.team_id ? Number(createForm.team_id) : null, + } + const created = await api.post('/campaigns', data) + setShowCreateModal(false) + loadCampaigns() + // Navigate to the new campaign detail page + const id = created?.Id || created?.id || created?._id + if (id) navigate(`/campaigns/${id}`) + } catch (err) { + console.error('Create campaign failed:', err) + } finally { + setCreateSaving(false) + } } const filtered = campaigns.filter(c => { @@ -295,7 +331,62 @@ export default function Campaigns() { - {/* Campaign Panel */} + {/* Create Campaign Modal */} + setShowCreateModal(false)} title={t('campaigns.newCampaign') || 'New Campaign'} size="md"> +
+
+ + setCreateForm(f => ({ ...f, name: 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 /> +
+
+ +