sitcod3 commited on
Commit
f3eb729
·
verified ·
1 Parent(s): 0c4dec5

Upload src/pages/Warmup.tsx with huggingface_hub

Browse files
Files changed (1) hide show
  1. src/pages/Warmup.tsx +266 -0
src/pages/Warmup.tsx ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
2
+ import { apiClient } from "../lib/apiClient";
3
+ import {
4
+ Flame,
5
+ ShieldCheck,
6
+ TrendingUp,
7
+ Sliders,
8
+ CheckCircle2,
9
+ AlertTriangle,
10
+ Zap,
11
+ Mail,
12
+ Play,
13
+ Pause,
14
+ ArrowUpRight
15
+ } from "lucide-react";
16
+ import { useState } from "react";
17
+ import StatusBadge from "../components/StatusBadge";
18
+ import FormDrawer from "../components/FormDrawer";
19
+ import toast from "react-hot-toast";
20
+
21
+ export default function Warmup() {
22
+ const queryClient = useQueryClient();
23
+ const [selectedWarmupAccount, setSelectedWarmupAccount] = useState<any | null>(null);
24
+ const [isSettingsOpen, setIsSettingsOpen] = useState(false);
25
+
26
+ const { data: accounts = [], isLoading, refetch } = useQuery({
27
+ queryKey: ["email-accounts"],
28
+ queryFn: async () => {
29
+ const res = await apiClient.get("/email-accounts");
30
+ return res.data;
31
+ },
32
+ });
33
+
34
+ const toggleWarmupMutation = useMutation({
35
+ mutationFn: async ({ id, status }: { id: string; status: "ACTIVE" | "PAUSED" }) => {
36
+ const action = status === "ACTIVE" ? "enable" : "disable";
37
+ const res = await apiClient.post(`/email-accounts/${id}/warmup/${action}`);
38
+ return res.data;
39
+ },
40
+ onSuccess: (_, variables) => {
41
+ toast.success(`Warmup ${variables.status === "ACTIVE" ? "activated" : "paused"}`);
42
+ refetch();
43
+ },
44
+ onError: () => {
45
+ toast.error("Failed to update warmup status");
46
+ }
47
+ });
48
+
49
+ const activeWarmups = accounts.filter((a: any) => a.warmup_enabled).length;
50
+ const dailyWarmupVolume = accounts.reduce((total: number, account: any) => total + (account.warmup_daily_count || 0), 0);
51
+ const replyRates = accounts.map((account: any) => account.reply_rate_percentage).filter((rate: any) => typeof rate === "number");
52
+ const averageReplyRate = replyRates.length ? (replyRates.reduce((total: number, rate: number) => total + rate, 0) / replyRates.length).toFixed(1) : "—";
53
+ const scores = accounts.map((account: any) => account.warmup_score).filter((score: any) => typeof score === "number");
54
+ const averageScore = scores.length ? (scores.reduce((total: number, score: number) => total + score, 0) / scores.length).toFixed(1) : "—";
55
+
56
+ return (
57
+ <div className="p-6 max-w-7xl mx-auto space-y-6">
58
+ {/* Header */}
59
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
60
+ <div>
61
+ <h1 className="text-2xl font-bold tracking-tight text-zinc-100 flex items-center gap-2.5">
62
+ <Flame className="w-6 h-6 text-amber-400" />
63
+ Autonomous Warmup Engine
64
+ </h1>
65
+ <p className="text-zinc-400 text-sm mt-1">
66
+ Gradually ramps mailbox sending volume, interacts with peer networks, and rescues emails from spam folders.
67
+ </p>
68
+ </div>
69
+
70
+ <div className="flex items-center gap-3">
71
+ <button
72
+ onClick={() => {
73
+ accounts.forEach((a: any) => {
74
+ toggleWarmupMutation.mutate({ id: a.id, status: "ACTIVE" });
75
+ });
76
+ toast.success("Warmup activated on all connected mailboxes");
77
+ }}
78
+ className="px-3.5 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-200 text-xs font-medium rounded-lg transition-colors flex items-center gap-1.5"
79
+ >
80
+ <Play className="w-3.5 h-3.5 text-emerald-400" /> Bulk Enable All
81
+ </button>
82
+ <button
83
+ onClick={() => setIsSettingsOpen(true)}
84
+ className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-medium rounded-lg transition-all shadow-md shadow-indigo-600/20 flex items-center gap-1.5"
85
+ >
86
+ <Sliders className="w-4 h-4" /> Global Warmup Rules
87
+ </button>
88
+ </div>
89
+ </div>
90
+
91
+ {/* KPI Overview */}
92
+ <div className="grid grid-cols-1 sm:grid-cols-4 gap-4">
93
+ <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4">
94
+ <span className="text-xs text-zinc-400">Warmup Coverage</span>
95
+ <div className="text-2xl font-bold text-zinc-100 mt-1">
96
+ {activeWarmups} / {accounts.length}
97
+ </div>
98
+ <span className="text-[11px] text-emerald-400 font-medium">Inboxes in active pool</span>
99
+ </div>
100
+ <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4">
101
+ <span className="text-xs text-zinc-400">Daily Warmup Volume</span>
102
+ <div className="text-2xl font-bold text-amber-400 mt-1">{dailyWarmupVolume} / day</div>
103
+ <span className="text-[11px] text-zinc-500">Auto-incrementing daily</span>
104
+ </div>
105
+ <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4">
106
+ <span className="text-xs text-zinc-400">Warmup Reply Rate</span>
107
+ <div className="text-2xl font-bold text-indigo-400 mt-1">{averageReplyRate}%</div>
108
+ <span className="text-[11px] text-zinc-500">Autonomous peer response</span>
109
+ </div>
110
+ <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4">
111
+ <span className="text-xs text-zinc-400">Inbox Health Score</span>
112
+ <div className="text-2xl font-bold text-emerald-400 mt-1">{averageScore} / 100</div>
113
+ <span className="text-[11px] text-emerald-400 font-medium">Excellent deliverability</span>
114
+ </div>
115
+ </div>
116
+
117
+ {/* Warmup Mailboxes Table */}
118
+ <div className="bg-zinc-900 border border-zinc-800 rounded-xl overflow-hidden shadow-sm">
119
+ <div className="overflow-x-auto">
120
+ <table className="w-full text-sm text-left">
121
+ <thead className="bg-zinc-950 border-b border-zinc-800 text-zinc-400 text-xs uppercase">
122
+ <tr>
123
+ <th className="px-6 py-3.5 font-semibold">Mailbox</th>
124
+ <th className="px-6 py-3.5 font-semibold">Warmup Status</th>
125
+ <th className="px-6 py-3.5 font-semibold">Ramp Day</th>
126
+ <th className="px-6 py-3.5 font-semibold">Reputation Score</th>
127
+ <th className="px-6 py-3.5 font-semibold">Warmup Volume</th>
128
+ <th className="px-6 py-3.5 font-semibold text-right">Warmup Toggle</th>
129
+ </tr>
130
+ </thead>
131
+ <tbody className="divide-y divide-zinc-800/80">
132
+ {accounts.map((acc: any) => {
133
+ const isWarmupActive = Boolean(acc.warmup_enabled);
134
+ const rampDay = acc.ramp_day || "—";
135
+ const score = acc.warmup_score || 0;
136
+
137
+ return (
138
+ <tr key={acc.id} className="hover:bg-zinc-800/30 transition-colors">
139
+ <td className="px-6 py-4">
140
+ <div className="font-semibold text-zinc-100 flex items-center gap-2">
141
+ {acc.from_name}
142
+ </div>
143
+ <div className="text-xs text-zinc-400 font-mono mt-0.5">{acc.from_email}</div>
144
+ </td>
145
+ <td className="px-6 py-4">
146
+ <StatusBadge status={isWarmupActive ? "ACTIVE" : "PAUSED"} size="sm" />
147
+ </td>
148
+ <td className="px-6 py-4 text-zinc-300 font-mono text-xs">
149
+ Day {rampDay}
150
+ </td>
151
+ <td className="px-6 py-4">
152
+ <div className="flex items-center gap-2">
153
+ <span className="text-xs font-bold text-emerald-400">{score}%</span>
154
+ <div className="w-20 h-1.5 bg-zinc-950 rounded-full overflow-hidden border border-zinc-800">
155
+ <div className="h-full bg-emerald-400 rounded-full" style={{ width: `${score}%` }} />
156
+ </div>
157
+ </div>
158
+ </td>
159
+ <td className="px-6 py-4 text-xs font-mono text-zinc-300">
160
+ <span className="text-amber-400 font-semibold">{acc.warmup_daily_count || 0}</span> warm emails sent today
161
+ </td>
162
+ <td className="px-6 py-4 text-right">
163
+ <button
164
+ onClick={() =>
165
+ toggleWarmupMutation.mutate({
166
+ id: acc.id,
167
+ status: isWarmupActive ? "PAUSED" : "ACTIVE",
168
+ })
169
+ }
170
+ className={`px-3 py-1 text-xs font-medium rounded-lg transition-colors ${
171
+ isWarmupActive
172
+ ? "bg-rose-500/10 text-rose-400 hover:bg-rose-500/20 border border-rose-500/30"
173
+ : "bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20 border border-emerald-500/30"
174
+ }`}
175
+ >
176
+ {isWarmupActive ? "Pause Warmup" : "Start Warmup"}
177
+ </button>
178
+ </td>
179
+ </tr>
180
+ );
181
+ })}
182
+ </tbody>
183
+ </table>
184
+ </div>
185
+ </div>
186
+
187
+ {/* Global Warmup Rules Drawer */}
188
+ <FormDrawer
189
+ isOpen={isSettingsOpen}
190
+ onClose={() => setIsSettingsOpen(false)}
191
+ title="Autonomous Warmup Settings"
192
+ subtitle="Fine-tune daily ramping schedules and simulated reply rates."
193
+ >
194
+ <div className="space-y-4 text-xs">
195
+ <div>
196
+ <label className="block font-medium text-zinc-300 mb-1">Target Daily Warmup Max</label>
197
+ <input
198
+ type="number"
199
+ defaultValue="40"
200
+ className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100"
201
+ />
202
+ <span className="text-[11px] text-zinc-500 mt-1 block">Maximum simulated emails per inbox daily</span>
203
+ </div>
204
+
205
+ <div>
206
+ <label className="block font-medium text-zinc-300 mb-1">Daily Ramp Increment</label>
207
+ <input
208
+ type="number"
209
+ defaultValue="4"
210
+ className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100"
211
+ />
212
+ <span className="text-[11px] text-zinc-500 mt-1 block">Additional emails added each consecutive day</span>
213
+ </div>
214
+
215
+ <div>
216
+ <label className="block font-medium text-zinc-300 mb-1">Simulated Reply Rate</label>
217
+ <input
218
+ type="range"
219
+ min="10"
220
+ max="60"
221
+ defaultValue="35"
222
+ className="w-full accent-indigo-500"
223
+ />
224
+ <div className="flex justify-between text-[11px] text-zinc-400">
225
+ <span>Conservative (15%)</span>
226
+ <span className="text-indigo-400 font-semibold">Target: 35%</span>
227
+ <span>Aggressive (60%)</span>
228
+ </div>
229
+ </div>
230
+
231
+ <div className="flex items-center justify-between p-3 bg-zinc-900 border border-zinc-800 rounded-lg">
232
+ <div>
233
+ <span className="font-medium text-zinc-200 block text-xs">Spam Rescue Protocol</span>
234
+ <span className="text-[11px] text-zinc-500">Automatically moves warm emails out of spam folders</span>
235
+ </div>
236
+ <input
237
+ type="checkbox"
238
+ defaultChecked
239
+ className="w-4 h-4 rounded text-indigo-600 focus:ring-indigo-500 bg-zinc-950 border-zinc-700"
240
+ />
241
+ </div>
242
+
243
+ <div className="pt-4 flex justify-end gap-2 border-t border-zinc-800">
244
+ <button
245
+ type="button"
246
+ onClick={() => setIsSettingsOpen(false)}
247
+ className="px-3 py-2 bg-zinc-800 text-zinc-300 rounded-lg"
248
+ >
249
+ Cancel
250
+ </button>
251
+ <button
252
+ type="button"
253
+ onClick={() => {
254
+ toast.success("Warmup parameters saved");
255
+ setIsSettingsOpen(false);
256
+ }}
257
+ className="px-4 py-2 bg-indigo-600 text-white font-medium rounded-lg"
258
+ >
259
+ Save Configuration
260
+ </button>
261
+ </div>
262
+ </div>
263
+ </FormDrawer>
264
+ </div>
265
+ );
266
+ }