3import React, { useState, useEffect } from 'react';
4import Link from 'next/link';
6interface MarketingProgram {
12 status: 'planning' | 'active' | 'completed' | 'paused';
17export default function MarketingCampaignsPage() {
18 const [programs, setPrograms] = useState<MarketingProgram[]>([]);
19 const [loading, setLoading] = useState(true);
20 const [createModal, setCreateModal] = useState(false);
21 const [reviewModal, setReviewModal] = useState(false);
22 const [selectedProgram, setSelectedProgram] = useState<MarketingProgram | null>(null);
25 const [name, setName] = useState('');
26 const [startDate, setStartDate] = useState('');
27 const [endDate, setEndDate] = useState('');
28 const [programType, setProgramType] = useState('loyalty');
29 const [description, setDescription] = useState('');
35 const loadPrograms = async () => {
37 const response = await fetch('/api/v1/marketing/programs');
39 if (!response.ok || !response.headers.get('content-type')?.includes('application/json')) {
41 setPrograms(getDemoPrograms());
46 const data = await response.json();
47 setPrograms(data.programs || []);
50 console.error('Error loading marketing programs:', error);
51 setPrograms(getDemoPrograms());
56 const getDemoPrograms = (): MarketingProgram[] => {
60 name: 'Summer Sale 2025',
61 createDt: '2025-11-15T10:00:00Z',
62 startDate: '2025-12-01',
63 endDate: '2026-02-28',
66 description: 'Seasonal summer promotional campaign targeting new customers',
70 name: 'VIP Loyalty Club',
71 createDt: '2025-09-01T08:30:00Z',
72 startDate: '2025-10-01',
73 endDate: '2026-09-30',
76 description: 'Year-long loyalty program with tiered benefits',
80 name: 'Back to School Email Campaign',
81 createDt: '2025-12-10T14:20:00Z',
82 startDate: '2026-01-15',
83 endDate: '2026-02-15',
86 description: 'Targeted email campaign for school supplies',
91 const getStatusBadgeColor = (status: string) => {
93 case 'active': return 'bg-green-100 text-green-800';
94 case 'planning': return 'bg-blue-100 text-blue-800';
95 case 'completed': return 'bg-gray-100 text-gray-800';
96 case 'paused': return 'bg-yellow-100 text-yellow-800';
97 default: return 'bg-gray-100 text-gray-800';
101 const getStatusLabel = (status: string) => {
103 case 'active': return 'Active';
104 case 'planning': return 'Planning';
105 case 'completed': return 'Completed';
106 case 'paused': return 'Paused';
107 default: return status;
111 const handleCreateProgram = async () => {
112 if (!name || !startDate || !endDate) {
113 alert('Please fill in all required fields');
126 const response = await fetch('/api/v1/marketing/programs', {
128 headers: { 'Content-Type': 'application/json' },
129 body: JSON.stringify(newProgram),
133 alert('Program created (demo mode)');
134 const demo: MarketingProgram = {
135 id: programs.length + 1,
137 createDt: new Date().toISOString(),
144 setPrograms([demo, ...programs]);
145 setCreateModal(false);
150 alert('Marketing program created successfully');
152 setCreateModal(false);
155 console.error('Error creating program:', error);
156 alert('Program created (demo mode)');
157 setCreateModal(false);
162 const resetForm = () => {
166 setProgramType('loyalty');
170 const handleReviewProgram = (program: MarketingProgram) => {
171 setSelectedProgram(program);
172 setReviewModal(true);
175 const formatDate = (dateString: string): string => {
176 const date = new Date(dateString);
177 return date.toLocaleDateString('en-US', {
184 const formatDateTime = (dateString: string): string => {
185 const date = new Date(dateString);
186 return date.toLocaleDateString('en-US', {
196 <div className="container mx-auto px-4 py-8">
197 <div className="mb-6">
198 <div className="flex items-center gap-4 mb-4">
199 <Link href="/marketing" className="text-blue-600 hover:text-blue-800">
202 <h1 className="text-3xl font-bold text-gray-900">Marketing Programs</h1>
205 <p className="text-gray-600 mb-4">
206 Marketing programs allows you to manage and report on customer and sales promotion efforts.
207 A marketing program is used to track things like loyalty clubs and also the responses to
208 promotional offers sent to a subset of customers.
212 {/* Active Programs Summary */}
213 <div className="bg-white rounded-lg shadow-md p-6 mb-6">
214 <div className="flex items-center justify-between">
216 <h2 className="text-xl font-semibold text-gray-900 mb-2">Active Programs</h2>
217 <p className="text-3xl font-bold text-[#00946b]">
218 {programs.filter(p => p.status === 'active').length}
220 <p className="text-sm text-gray-500">Currently running</p>
222 <div className="text-right">
223 <p className="text-sm text-gray-500">Planning</p>
224 <p className="text-2xl font-semibold text-blue-600">
225 {programs.filter(p => p.status === 'planning').length}
228 <div className="text-right">
229 <p className="text-sm text-gray-500">Completed</p>
230 <p className="text-2xl font-semibold text-gray-600">
231 {programs.filter(p => p.status === 'completed').length}
237 {/* Action Buttons */}
238 <div className="flex flex-wrap gap-4 mb-6">
240 onClick={() => setCreateModal(true)}
241 className="flex items-center gap-3 bg-white border-2 border-[#00946b] rounded-lg shadow-md p-6 hover:shadow-lg transition"
243 <div className="text-5xl">📋</div>
244 <div className="text-left">
245 <div className="font-semibold text-gray-900">Create New Program</div>
246 <div className="text-sm text-gray-600">Start a new campaign</div>
251 href="/marketing/campaigns/analytics"
252 className="flex items-center gap-3 bg-white border-2 border-gray-300 rounded-lg shadow-md p-6 hover:shadow-lg transition opacity-50 cursor-not-allowed"
253 onClick={(e) => { e.preventDefault(); alert('Analytics coming soon'); }}
255 <div className="text-5xl">📊</div>
256 <div className="text-left">
257 <div className="font-semibold text-gray-900">Campaign Analytics</div>
258 <div className="text-sm text-gray-600">View performance reports</div>
263 {/* Programs Table */}
264 <div className="bg-white rounded-lg shadow-md overflow-hidden">
265 <div className="px-6 py-4 border-b border-gray-200">
266 <h3 className="text-lg font-semibold text-gray-900">
267 All Programs ({programs.length})
271 <div className="overflow-x-auto">
272 <table className="min-w-full divide-y divide-gray-200">
273 <thead className="bg-gray-50">
275 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
278 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
281 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
284 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
287 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
290 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
293 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
298 <tbody className="bg-white divide-y divide-gray-200">
301 <td colSpan={7} className="px-6 py-8 text-center text-gray-500">
305 ) : programs.length === 0 ? (
307 <td colSpan={7} className="px-6 py-8 text-center text-gray-500">
308 No marketing programs found. Create your first program to get started.
312 programs.map((program) => (
313 <tr key={program.id} className="hover:bg-gray-50">
314 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
315 {formatDateTime(program.createDt)}
317 <td className="px-6 py-4">
318 <div className="text-sm font-medium text-gray-900">{program.name}</div>
319 {program.description && (
320 <div className="text-sm text-gray-500 truncate max-w-xs">
321 {program.description}
325 <td className="px-6 py-4 whitespace-nowrap">
326 <span className="text-sm text-gray-900 capitalize">{program.type}</span>
328 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
329 {formatDate(program.startDate)}
331 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
332 {formatDate(program.endDate)}
334 <td className="px-6 py-4 whitespace-nowrap">
335 <span className={`px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${getStatusBadgeColor(program.status)}`}>
336 {getStatusLabel(program.status)}
339 <td className="px-6 py-4 whitespace-nowrap text-sm">
341 onClick={() => handleReviewProgram(program)}
342 className="text-blue-600 hover:text-blue-900 font-medium"
355 {/* Create Program Modal */}
358 className="modal-overlay"
359 onClick={() => setCreateModal(false)}
362 className="bg-white rounded-lg shadow-xl max-w-2xl w-full"
363 onClick={(e) => e.stopPropagation()}
365 <div className="border-b border-gray-200 px-6 py-4 flex items-center justify-between">
366 <h2 className="text-xl font-semibold text-gray-900">Create New Marketing Program</h2>
368 onClick={() => setCreateModal(false)}
369 className="text-gray-400 hover:text-gray-600 text-2xl font-bold"
375 <div className="p-6">
376 <div className="space-y-4">
378 <label className="block text-sm font-medium text-gray-700 mb-1">
379 Program Name <span className="text-red-500">*</span>
384 onChange={(e) => setName(e.target.value)}
385 placeholder="e.g., Summer Sale 2025"
386 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
388 <p className="text-xs text-gray-500 mt-1">
389 What short nickname will you internally refer to this program in reports etc
394 <label className="block text-sm font-medium text-gray-700 mb-1">
399 onChange={(e) => setProgramType(e.target.value)}
400 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
402 <option value="loyalty">Loyalty Program</option>
403 <option value="promotion">Promotional Campaign</option>
404 <option value="email">Email Campaign</option>
405 <option value="sms">SMS Campaign</option>
406 <option value="event">Event Marketing</option>
407 <option value="other">Other</option>
411 <div className="grid grid-cols-2 gap-4">
413 <label className="block text-sm font-medium text-gray-700 mb-1">
414 Start Date <span className="text-red-500">*</span>
419 onChange={(e) => setStartDate(e.target.value)}
420 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
422 <p className="text-xs text-gray-500 mt-1">
423 When are you planning on starting promotional efforts? This is used in reports to determine any change in sales.
428 <label className="block text-sm font-medium text-gray-700 mb-1">
429 End Date <span className="text-red-500">*</span>
434 onChange={(e) => setEndDate(e.target.value)}
435 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
437 <p className="text-xs text-gray-500 mt-1">
438 When are you planning on stopping promotional efforts?
444 <label className="block text-sm font-medium text-gray-700 mb-1">
449 onChange={(e) => setDescription(e.target.value)}
450 placeholder="Brief description of the program goals and target audience..."
452 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
458 <div className="border-t border-gray-200 px-6 py-4 flex justify-end gap-3">
460 onClick={() => setCreateModal(false)}
461 className="px-4 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300"
466 onClick={handleCreateProgram}
467 className="px-4 py-2 bg-[#00946b] text-white rounded hover:bg-[#007a59]"
476 {/* Review Program Modal */}
477 {reviewModal && selectedProgram && (
479 className="modal-overlay"
480 onClick={() => setReviewModal(false)}
483 className="bg-white rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden"
484 onClick={(e) => e.stopPropagation()}
486 <div className="border-b border-gray-200 px-6 py-4 flex items-center justify-between">
487 <h2 className="text-xl font-semibold text-gray-900">{selectedProgram.name}</h2>
489 onClick={() => setReviewModal(false)}
490 className="text-gray-400 hover:text-gray-600 text-2xl font-bold"
496 <div className="p-6 overflow-y-auto max-h-[calc(90vh-120px)]">
497 <div className="space-y-6">
498 {/* Status & Type */}
499 <div className="grid grid-cols-2 gap-4">
501 <label className="block text-sm font-medium text-gray-500 mb-1">Status</label>
502 <span className={`px-3 py-1 inline-flex text-sm font-semibold rounded-full ${getStatusBadgeColor(selectedProgram.status)}`}>
503 {getStatusLabel(selectedProgram.status)}
507 <label className="block text-sm font-medium text-gray-500 mb-1">Type</label>
508 <p className="text-gray-900 capitalize">{selectedProgram.type}</p>
513 <div className="grid grid-cols-3 gap-4">
515 <label className="block text-sm font-medium text-gray-500 mb-1">Created</label>
516 <p className="text-gray-900">{formatDateTime(selectedProgram.createDt)}</p>
519 <label className="block text-sm font-medium text-gray-500 mb-1">Start Date</label>
520 <p className="text-gray-900">{formatDate(selectedProgram.startDate)}</p>
523 <label className="block text-sm font-medium text-gray-500 mb-1">End Date</label>
524 <p className="text-gray-900">{formatDate(selectedProgram.endDate)}</p>
529 {selectedProgram.description && (
531 <label className="block text-sm font-medium text-gray-500 mb-1">Description</label>
532 <p className="text-gray-900">{selectedProgram.description}</p>
536 {/* Performance Metrics */}
537 <div className="border-t pt-4">
538 <h3 className="text-lg font-semibold text-gray-900 mb-4">Performance Metrics</h3>
539 <div className="grid grid-cols-3 gap-4">
540 <div className="bg-gray-50 rounded-lg p-4">
541 <p className="text-sm text-gray-500">Total Reach</p>
542 <p className="text-2xl font-bold text-gray-900">-</p>
543 <p className="text-xs text-gray-400">Demo mode</p>
545 <div className="bg-gray-50 rounded-lg p-4">
546 <p className="text-sm text-gray-500">Conversion Rate</p>
547 <p className="text-2xl font-bold text-gray-900">-</p>
548 <p className="text-xs text-gray-400">Demo mode</p>
550 <div className="bg-gray-50 rounded-lg p-4">
551 <p className="text-sm text-gray-500">Revenue Impact</p>
552 <p className="text-2xl font-bold text-gray-900">-</p>
553 <p className="text-xs text-gray-400">Demo mode</p>
559 <div className="border-t pt-4">
560 <h3 className="text-lg font-semibold text-gray-900 mb-3">Program Actions</h3>
561 <div className="flex flex-wrap gap-3">
562 <button className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
565 <button className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">
568 <button className="px-4 py-2 bg-yellow-600 text-white rounded hover:bg-yellow-700">
571 <button className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700">
575 <p className="text-sm text-gray-500 mt-3">
576 Note: Program actions are in demo mode and will not affect actual data.
582 <div className="border-t border-gray-200 px-6 py-4 flex justify-end">
584 onClick={() => setReviewModal(false)}
585 className="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700"