sitcod3 commited on
Commit
d715383
·
verified ·
1 Parent(s): da191dd

Upload src/pages/Dashboard.tsx with huggingface_hub

Browse files
Files changed (1) hide show
  1. src/pages/Dashboard.tsx +543 -0
src/pages/Dashboard.tsx ADDED
@@ -0,0 +1,543 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { apiClient } from "../lib/apiClient";
3
+ import {
4
+ Megaphone,
5
+ Users,
6
+ Mail,
7
+ Inbox,
8
+ Flame,
9
+ TrendingUp,
10
+ Sparkles,
11
+ Activity,
12
+ Plus,
13
+ ArrowUpRight,
14
+ CheckCircle2,
15
+ ShieldCheck,
16
+ Radio,
17
+ Zap
18
+ } from "lucide-react";
19
+ import {
20
+ LineChart,
21
+ Line,
22
+ XAxis,
23
+ YAxis,
24
+ CartesianGrid,
25
+ Tooltip,
26
+ ResponsiveContainer,
27
+ PieChart,
28
+ Pie,
29
+ Cell
30
+ } from "recharts";
31
+ import { useState } from "react";
32
+ import { Link } from "react-router-dom";
33
+ import StatCard from "../components/StatCard";
34
+ import StatusBadge from "../components/StatusBadge";
35
+ import FormDrawer from "../components/FormDrawer";
36
+ import toast from "react-hot-toast";
37
+
38
+ const lineData = [
39
+ { date: "Day 1", sent: 480, open: 310, reply: 28, bounce: 6 },
40
+ { date: "Day 4", sent: 820, open: 530, reply: 46, bounce: 10 },
41
+ { date: "Day 7", sent: 1250, open: 820, reply: 74, bounce: 14 },
42
+ { date: "Day 10", sent: 1780, open: 1190, reply: 105, bounce: 19 },
43
+ { date: "Day 14", sent: 2420, open: 1650, reply: 148, bounce: 23 },
44
+ { date: "Day 18", sent: 3150, open: 2190, reply: 196, bounce: 31 },
45
+ { date: "Day 22", sent: 4020, open: 2840, reply: 254, bounce: 38 },
46
+ { date: "Day 26", sent: 4980, open: 3510, reply: 312, bounce: 46 },
47
+ { date: "Day 30", sent: 5890, open: 4120, reply: 378, bounce: 52 },
48
+ ];
49
+
50
+ const pieData = [
51
+ { name: "Interested", value: 340, color: "#10b981" },
52
+ { name: "Meeting Booked", value: 160, color: "#6366f1" },
53
+ { name: "Replied", value: 290, color: "#f59e0b" },
54
+ { name: "Not Interested", value: 180, color: "#f43f5e" },
55
+ { name: "Bounced", value: 45, color: "#71717a" },
56
+ ];
57
+
58
+ export default function Dashboard() {
59
+ const [isCreateCampaignOpen, setIsCreateCampaignOpen] = useState(false);
60
+ const [isAddLeadOpen, setIsAddLeadOpen] = useState(false);
61
+ const [activeMetric, setActiveMetric] = useState<"all" | "open" | "reply">("all");
62
+
63
+ const { data: analytics } = useQuery({
64
+ queryKey: ["analytics-summary"],
65
+ queryFn: async () => {
66
+ const res = await apiClient.get("/analytics/campaigns");
67
+ return res.data;
68
+ },
69
+ });
70
+
71
+ const { data: campaigns } = useQuery({
72
+ queryKey: ["campaigns"],
73
+ queryFn: async () => {
74
+ const res = await apiClient.get("/campaigns");
75
+ return res.data;
76
+ },
77
+ });
78
+
79
+ const { data: emailAccounts } = useQuery({
80
+ queryKey: ["email-accounts"],
81
+ queryFn: async () => {
82
+ const res = await apiClient.get("/email-accounts");
83
+ return res.data;
84
+ },
85
+ });
86
+
87
+ return (
88
+ <div className="p-6 max-w-7xl mx-auto space-y-7">
89
+ {/* Header with Live Status & Quick Action Buttons */}
90
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
91
+ <div>
92
+ <div className="flex items-center gap-2">
93
+ <h1 className="text-2xl font-bold tracking-tight text-zinc-100 flex items-center gap-2.5">
94
+ Command Center
95
+ </h1>
96
+ <span className="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-emerald-400 bg-emerald-500/10 border border-emerald-500/20 px-2.5 py-0.5 rounded-full">
97
+ <span className="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-ping" />
98
+ Live Engine
99
+ </span>
100
+ </div>
101
+ <p className="text-zinc-400 text-sm mt-1">
102
+ Real-time deliverability radar, campaign sequencing, and cold outreach throughput.
103
+ </p>
104
+ </div>
105
+
106
+ <div className="flex items-center gap-3">
107
+ <button
108
+ onClick={() => setIsCreateCampaignOpen(true)}
109
+ className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-sm font-medium rounded-lg transition-all shadow-md shadow-indigo-600/20 flex items-center gap-2"
110
+ >
111
+ <Plus className="w-4 h-4" />
112
+ Create Campaign
113
+ </button>
114
+ </div>
115
+ </div>
116
+
117
+ {/* High-Impact Stat Cards */}
118
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
119
+ <StatCard
120
+ title="Total Campaigns"
121
+ value={campaigns?.length || 3}
122
+ subtitle="Outbound Sequences"
123
+ icon={Megaphone}
124
+ iconColor="indigo"
125
+ change={{ value: "+2 new", isPositive: true, period: "this week" }}
126
+ />
127
+ <StatCard
128
+ title="Total Leads"
129
+ value={analytics?.total_leads ? analytics.total_leads.toLocaleString() : "4,670"}
130
+ subtitle="Prospects Targeted"
131
+ icon={Users}
132
+ iconColor="teal"
133
+ change={{ value: "+380", isPositive: true, period: "last 7 days" }}
134
+ />
135
+ <StatCard
136
+ title="Emails Sent"
137
+ value={analytics?.total_sent ? analytics.total_sent.toLocaleString() : "11,140"}
138
+ subtitle="Across All Mailboxes"
139
+ icon={Mail}
140
+ iconColor="blue"
141
+ change={{ value: "+14.8%", isPositive: true, period: "month-over-month" }}
142
+ />
143
+ <StatCard
144
+ title="Avg Reply Rate"
145
+ value={analytics?.reply_rate ? `${analytics.reply_rate}%` : "5.8%"}
146
+ subtitle="Deliverability High"
147
+ icon={Inbox}
148
+ iconColor="emerald"
149
+ change={{ value: "+1.2%", isPositive: true, period: "vs benchmark" }}
150
+ />
151
+ </div>
152
+
153
+ {/* Deliverability & Mailbox Health Ribbon */}
154
+ <div className="bg-gradient-to-r from-white/5 via-indigo-900/20 to-white/5 border border-white/10 backdrop-blur-md rounded-xl p-4 flex flex-col md:flex-row items-center justify-between gap-4 shadow-[0_8px_32px_-8px_rgba(0,0,0,0.5)]">
155
+ <div className="flex items-center gap-3">
156
+ <div className="p-2.5 bg-emerald-500/20 border border-emerald-500/30 rounded-xl text-emerald-300 shadow-[inset_0_1px_1px_rgba(255,255,255,0.1)]">
157
+ <ShieldCheck className="w-5 h-5 drop-shadow-[0_0_8px_rgba(52,211,153,0.5)]" />
158
+ </div>
159
+ <div>
160
+ <h4 className="text-sm font-semibold text-zinc-100 flex items-center gap-2">
161
+ Deliverability & Reputation Guard
162
+ <span className="text-[10px] font-bold uppercase tracking-wider bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 px-2 py-0.5 rounded-full drop-shadow-[0_0_8px_rgba(52,211,153,0.3)]">
163
+ 98.4% Score
164
+ </span>
165
+ </h4>
166
+ <p className="text-xs text-zinc-400 mt-0.5">
167
+ 4 active mailboxes in automatic ramp-up. Spam rescue active across Google & Outlook nodes.
168
+ </p>
169
+ </div>
170
+ </div>
171
+
172
+ <div className="flex items-center gap-4 text-xs">
173
+ <div className="flex items-center gap-2 bg-black/40 border border-white/5 px-3 py-1.5 rounded-lg shadow-[inset_0_1px_1px_rgba(255,255,255,0.02)]">
174
+ <Flame className="w-4 h-4 text-amber-400 drop-shadow-[0_0_8px_rgba(251,191,36,0.5)]" />
175
+ <span className="text-zinc-300 font-medium font-sans">Warmup:</span>
176
+ <span className="text-amber-400 font-bold font-mono drop-shadow-[0_0_8px_rgba(251,191,36,0.3)]">120 daily / 4 inboxes</span>
177
+ </div>
178
+ <Link
179
+ to="/warmup"
180
+ className="text-indigo-400 hover:text-indigo-300 font-medium flex items-center gap-1 transition-colors"
181
+ >
182
+ Warmup Center <ArrowUpRight className="w-3.5 h-3.5" />
183
+ </Link>
184
+ </div>
185
+ </div>
186
+
187
+ {/* Main Visualizations Grid */}
188
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
189
+ {/* 30-Day Performance Multi-Line Chart */}
190
+ <div className="lg:col-span-2 bg-white/5 border border-white/10 backdrop-blur-md rounded-xl p-5 shadow-[0_8px_32px_-8px_rgba(0,0,0,0.5)] flex flex-col">
191
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-6">
192
+ <div>
193
+ <h3 className="text-base font-semibold text-zinc-100 flex items-center gap-2">
194
+ <Activity className="w-4 h-4 text-indigo-400 drop-shadow-[0_0_8px_rgba(99,102,241,0.5)]" />
195
+ Performance Trajectory (30 Days)
196
+ </h3>
197
+ <p className="text-xs text-zinc-400 mt-0.5">
198
+ Sent volume, open rate, and reply convergence.
199
+ </p>
200
+ </div>
201
+
202
+ {/* Metric toggles */}
203
+ <div className="flex items-center bg-black/40 p-1 rounded-lg border border-white/5 text-xs shadow-[inset_0_1px_1px_rgba(255,255,255,0.02)]">
204
+ <button
205
+ onClick={() => setActiveMetric("all")}
206
+ className={`px-3 py-1 rounded-md font-medium transition-all duration-300 ${
207
+ activeMetric === "all" ? "bg-indigo-600 text-white shadow-[0_0_12px_rgba(79,70,229,0.5)]" : "text-zinc-400 hover:text-zinc-200"
208
+ }`}
209
+ >
210
+ All Metrics
211
+ </button>
212
+ <button
213
+ onClick={() => setActiveMetric("open")}
214
+ className={`px-3 py-1 rounded-md font-medium transition-all duration-300 ${
215
+ activeMetric === "open" ? "bg-emerald-600 text-white shadow-[0_0_12px_rgba(5,150,105,0.5)]" : "text-zinc-400 hover:text-zinc-200"
216
+ }`}
217
+ >
218
+ Opens
219
+ </button>
220
+ <button
221
+ onClick={() => setActiveMetric("reply")}
222
+ className={`px-3 py-1 rounded-md font-medium transition-all duration-300 ${
223
+ activeMetric === "reply" ? "bg-purple-600 text-white shadow-[0_0_12px_rgba(147,51,234,0.5)]" : "text-zinc-400 hover:text-zinc-200"
224
+ }`}
225
+ >
226
+ Replies
227
+ </button>
228
+ </div>
229
+ </div>
230
+
231
+ <div className="h-[300px] w-full">
232
+ <ResponsiveContainer width="100%" height="100%">
233
+ <LineChart data={lineData} margin={{ top: 5, right: 10, bottom: 5, left: -10 }}>
234
+ <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" vertical={false} />
235
+ <XAxis dataKey="date" stroke="#a1a1aa" fontSize={12} tickLine={false} axisLine={false} />
236
+ <YAxis stroke="#a1a1aa" fontSize={12} tickLine={false} axisLine={false} className="font-mono" />
237
+ <Tooltip
238
+ contentStyle={{
239
+ backgroundColor: "rgba(9, 9, 11, 0.8)",
240
+ backdropFilter: "blur(12px)",
241
+ borderColor: "rgba(255,255,255,0.1)",
242
+ borderRadius: "12px",
243
+ color: "#f4f4f5",
244
+ fontSize: "12px",
245
+ }}
246
+ />
247
+ {(activeMetric === "all" || activeMetric === "open") && (
248
+ <Line
249
+ type="monotone"
250
+ dataKey="sent"
251
+ name="Sent"
252
+ stroke="#3b82f6"
253
+ strokeWidth={3}
254
+ dot={{ r: 4, fill: "#3b82f6", strokeWidth: 2, stroke: "#000" }}
255
+ activeDot={{ r: 6, fill: "#60a5fa" }}
256
+ animationDuration={1500}
257
+ />
258
+ )}
259
+ {(activeMetric === "all" || activeMetric === "open") && (
260
+ <Line
261
+ type="monotone"
262
+ dataKey="open"
263
+ name="Opens"
264
+ stroke="#10b981"
265
+ strokeWidth={3}
266
+ dot={{ r: 4, fill: "#10b981", strokeWidth: 2, stroke: "#000" }}
267
+ activeDot={{ r: 6, fill: "#34d399" }}
268
+ animationDuration={1500}
269
+ />
270
+ )}
271
+ {(activeMetric === "all" || activeMetric === "reply") && (
272
+ <Line
273
+ type="monotone"
274
+ dataKey="reply"
275
+ name="Replies"
276
+ stroke="#a855f7"
277
+ strokeWidth={3}
278
+ dot={{ r: 4, fill: "#a855f7", strokeWidth: 2, stroke: "#000" }}
279
+ activeDot={{ r: 6, fill: "#c084fc" }}
280
+ animationDuration={1500}
281
+ />
282
+ )}
283
+ {activeMetric === "all" && (
284
+ <Line
285
+ type="monotone"
286
+ dataKey="bounce"
287
+ name="Bounces"
288
+ stroke="#f43f5e"
289
+ strokeWidth={2}
290
+ strokeDasharray="4 4"
291
+ dot={false}
292
+ animationDuration={1500}
293
+ />
294
+ )}
295
+ </LineChart>
296
+ </ResponsiveContainer>
297
+ </div>
298
+
299
+ <div className="mt-4 pt-3 border-t border-white/5 flex items-center justify-between text-xs text-zinc-400">
300
+ <div className="flex items-center gap-4 font-sans">
301
+ <span className="flex items-center gap-1.5">
302
+ <span className="w-2.5 h-2.5 rounded-full bg-blue-500 shadow-[0_0_8px_rgba(59,130,246,0.8)]" /> Sent
303
+ </span>
304
+ <span className="flex items-center gap-1.5">
305
+ <span className="w-2.5 h-2.5 rounded-full bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.8)]" /> Opens
306
+ </span>
307
+ <span className="flex items-center gap-1.5">
308
+ <span className="w-2.5 h-2.5 rounded-full bg-purple-500 shadow-[0_0_8px_rgba(168,85,247,0.8)]" /> Replies
309
+ </span>
310
+ <span className="flex items-center gap-1.5">
311
+ <span className="w-2.5 h-2.5 rounded-full bg-rose-500 shadow-[0_0_8px_rgba(244,63,94,0.8)]" /> Bounces
312
+ </span>
313
+ </div>
314
+ <Link to="/analytics" className="text-indigo-400 hover:text-indigo-300 font-medium">
315
+ View Detailed Analytics →
316
+ </Link>
317
+ </div>
318
+ </div>
319
+
320
+ {/* Lead Status Distribution Donut */}
321
+ <div className="bg-white/5 border border-white/10 backdrop-blur-md rounded-xl p-5 shadow-[0_8px_32px_-8px_rgba(0,0,0,0.5)] flex flex-col justify-between">
322
+ <div>
323
+ <h3 className="text-base font-semibold text-zinc-100 flex items-center gap-2">
324
+ <Zap className="w-4 h-4 text-amber-400 drop-shadow-[0_0_8px_rgba(251,191,36,0.5)]" />
325
+ Lead Intent Distribution
326
+ </h3>
327
+ <p className="text-xs text-zinc-400 mt-0.5">Classification of prospect responses.</p>
328
+ </div>
329
+
330
+ <div className="relative h-[220px] w-full flex items-center justify-center my-2">
331
+ <ResponsiveContainer width="100%" height="100%">
332
+ <PieChart>
333
+ <Pie
334
+ data={pieData}
335
+ innerRadius={65}
336
+ outerRadius={95}
337
+ paddingAngle={4}
338
+ dataKey="value"
339
+ stroke="none"
340
+ animationDuration={1500}
341
+ >
342
+ {pieData.map((entry, index) => (
343
+ <Cell key={`cell-${index}`} fill={entry.color} />
344
+ ))}
345
+ </Pie>
346
+ <Tooltip
347
+ contentStyle={{
348
+ backgroundColor: "rgba(9, 9, 11, 0.8)",
349
+ backdropFilter: "blur(12px)",
350
+ borderColor: "rgba(255,255,255,0.1)",
351
+ borderRadius: "12px",
352
+ fontSize: "12px",
353
+ }}
354
+ />
355
+ </PieChart>
356
+ </ResponsiveContainer>
357
+ <div className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none drop-shadow-[0_0_12px_rgba(255,255,255,0.3)]">
358
+ <span className="text-2xl font-bold text-white font-mono">1,015</span>
359
+ <span className="text-[10px] uppercase tracking-wider text-zinc-400 font-medium font-sans">
360
+ Classified
361
+ </span>
362
+ </div>
363
+ </div>
364
+
365
+ <div className="space-y-1.5 pt-3 border-t border-white/5 text-xs font-sans">
366
+ {pieData.map((item) => (
367
+ <div key={item.name} className="flex items-center justify-between text-zinc-300">
368
+ <span className="flex items-center gap-2">
369
+ <span className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: item.color, boxShadow: `0 0 8px ${item.color}` }} />
370
+ {item.name}
371
+ </span>
372
+ <span className="font-semibold text-zinc-200 font-mono">{item.value}</span>
373
+ </div>
374
+ ))}
375
+ </div>
376
+ </div>
377
+ </div>
378
+
379
+ {/* Active Campaigns Overview Table */}
380
+ <div className="bg-white/5 border border-white/10 backdrop-blur-md rounded-xl overflow-hidden shadow-[0_8px_32px_-8px_rgba(0,0,0,0.5)]">
381
+ <div className="px-6 py-4 border-b border-white/5 flex items-center justify-between bg-white/[0.02]">
382
+ <div>
383
+ <h3 className="text-base font-semibold text-zinc-100 flex items-center gap-2">
384
+ <Radio className="w-4 h-4 text-indigo-400 drop-shadow-[0_0_8px_rgba(99,102,241,0.5)]" />
385
+ Active Outreach Sequences
386
+ </h3>
387
+ <p className="text-xs text-zinc-400 mt-0.5">
388
+ Live status, recipient quotas, and response metrics.
389
+ </p>
390
+ </div>
391
+ <Link
392
+ to="/campaigns"
393
+ className="text-xs font-semibold text-indigo-400 hover:text-indigo-300 transition-colors"
394
+ >
395
+ Manage Campaigns →
396
+ </Link>
397
+ </div>
398
+
399
+ <div className="overflow-x-auto">
400
+ <table className="w-full text-sm text-left">
401
+ <thead className="bg-black/20 border-b border-white/5 text-zinc-400 text-xs uppercase">
402
+ <tr>
403
+ <th className="px-6 py-3 font-medium">Campaign</th>
404
+ <th className="px-6 py-3 font-medium">Status</th>
405
+ <th className="px-6 py-3 font-medium">Leads</th>
406
+ <th className="px-6 py-3 font-medium">Sent</th>
407
+ <th className="px-6 py-3 font-medium">Opens</th>
408
+ <th className="px-6 py-3 font-medium">Replies</th>
409
+ <th className="px-6 py-3 font-medium text-right">Action</th>
410
+ </tr>
411
+ </thead>
412
+ <tbody className="divide-y divide-white/5">
413
+ {(Array.isArray(campaigns) ? campaigns : []).map((camp: any) => (
414
+ <tr key={camp.id} className="hover:bg-white/5 transition-colors">
415
+ <td className="px-6 py-4 font-semibold text-zinc-100">
416
+ <Link to={`/campaigns/${camp.id}`} className="hover:text-indigo-400 transition-colors">
417
+ {camp.name}
418
+ </Link>
419
+ <div className="text-[11px] text-zinc-400 font-normal mt-0.5 font-sans">
420
+ {camp.timezone} • {camp.sequences?.length || 1} step sequence
421
+ </div>
422
+ </td>
423
+ <td className="px-6 py-4">
424
+ <StatusBadge status={camp.status} />
425
+ </td>
426
+ <td className="px-6 py-4 text-zinc-300 font-mono">{camp.lead_count || 0}</td>
427
+ <td className="px-6 py-4 text-zinc-300 font-mono">{camp.sent_count || 0}</td>
428
+ <td className="px-6 py-4 text-emerald-400 font-mono drop-shadow-[0_0_8px_rgba(52,211,153,0.3)]">{camp.open_count || 0}</td>
429
+ <td className="px-6 py-4 text-purple-400 font-mono font-semibold drop-shadow-[0_0_8px_rgba(168,85,247,0.3)]">{camp.reply_count || 0}</td>
430
+ <td className="px-6 py-4 text-right">
431
+ <Link
432
+ to={`/campaigns/${camp.id}`}
433
+ className="px-3 py-1.5 bg-white/5 hover:bg-white/10 text-zinc-200 text-xs font-medium rounded-lg border border-white/5 transition-all inline-flex items-center gap-1 shadow-[inset_0_1px_1px_rgba(255,255,255,0.05)]"
434
+ >
435
+ Inspect <ArrowUpRight className="w-3 h-3" />
436
+ </Link>
437
+ </td>
438
+ </tr>
439
+ ))}
440
+ </tbody>
441
+ </table>
442
+ </div>
443
+ </div>
444
+
445
+ {/* Quick Campaign Creator Slide-Over Drawer */}
446
+ <FormDrawer
447
+ isOpen={isCreateCampaignOpen}
448
+ onClose={() => setIsCreateCampaignOpen(false)}
449
+ title="Create New Campaign"
450
+ subtitle="Configure schedule, tracking domains, and email sequence steps."
451
+ >
452
+ <form
453
+ onSubmit={async (e) => {
454
+ e.preventDefault();
455
+ const form = e.target as HTMLFormElement;
456
+ const name = (form.elements.namedItem("name") as HTMLInputElement).value;
457
+ const timezone = (form.elements.namedItem("timezone") as HTMLSelectElement).value;
458
+ const initial_subject = (form.elements.namedItem("subject") as HTMLInputElement).value;
459
+ const initial_body = (form.elements.namedItem("body") as HTMLTextAreaElement).value;
460
+
461
+ try {
462
+ await apiClient.post("/campaigns", {
463
+ name,
464
+ timezone,
465
+ initial_subject,
466
+ initial_body,
467
+ track_settings: "TRACK_EMAIL_OPEN_AND_CLICK",
468
+ });
469
+ toast.success("Campaign created successfully!");
470
+ setIsCreateCampaignOpen(false);
471
+ } catch (err: any) {
472
+ toast.error(err.message || "Failed to create campaign");
473
+ }
474
+ }}
475
+ className="space-y-4 text-sm"
476
+ >
477
+ <div>
478
+ <label className="block font-medium text-zinc-300 mb-1">Campaign Name</label>
479
+ <input
480
+ name="name"
481
+ required
482
+ placeholder="e.g. Q4 Tech VP Outreach"
483
+ className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100 focus:outline-none focus:border-indigo-500"
484
+ />
485
+ </div>
486
+
487
+ <div>
488
+ <label className="block font-medium text-zinc-300 mb-1">Timezone</label>
489
+ <select
490
+ name="timezone"
491
+ defaultValue="America/New_York"
492
+ className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100 focus:outline-none focus:border-indigo-500"
493
+ >
494
+ <option value="America/New_York">Eastern Time (US & Canada)</option>
495
+ <option value="America/Chicago">Central Time (US & Canada)</option>
496
+ <option value="America/Los_Angeles">Pacific Time (US & Canada)</option>
497
+ <option value="Europe/London">London (GMT)</option>
498
+ <option value="Europe/Paris">Central European Time (CET)</option>
499
+ <option value="Asia/Singapore">Singapore (SGT)</option>
500
+ </select>
501
+ </div>
502
+
503
+ <div>
504
+ <label className="block font-medium text-zinc-300 mb-1">Step 1: Subject Line</label>
505
+ <input
506
+ name="subject"
507
+ required
508
+ defaultValue="Question about {{company_name}}'s pipeline"
509
+ className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100 focus:outline-none focus:border-indigo-500"
510
+ />
511
+ </div>
512
+
513
+ <div>
514
+ <label className="block font-medium text-zinc-300 mb-1">Step 1: Email Body</label>
515
+ <textarea
516
+ name="body"
517
+ rows={4}
518
+ required
519
+ defaultValue="Hi {{first_name}},\n\nWanted to reach out and see if you are exploring new outbound channels this quarter?\n\nBest,\nAlex"
520
+ className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100 focus:outline-none focus:border-indigo-500 font-mono text-xs"
521
+ />
522
+ </div>
523
+
524
+ <div className="pt-4 flex justify-end gap-3">
525
+ <button
526
+ type="button"
527
+ onClick={() => setIsCreateCampaignOpen(false)}
528
+ className="px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg"
529
+ >
530
+ Cancel
531
+ </button>
532
+ <button
533
+ type="submit"
534
+ className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white font-medium rounded-lg shadow-md"
535
+ >
536
+ Create Campaign
537
+ </button>
538
+ </div>
539
+ </form>
540
+ </FormDrawer>
541
+ </div>
542
+ );
543
+ }