Download src/pages/Warmup.tsx from sitcod3/modul-e: direct link, hf CLI and curl.
- Browser
- Download file 12.1 kB
-
https://huggingface.co/spaces/sitcod3/modul-e/resolve/main/src/pages/Warmup.tsx
- Command line
-
hf download hf://spaces/sitcod3/modul-e/src/pages/Warmup.tsx
-
curl -L -o Warmup.tsx https://huggingface.co/spaces/sitcod3/modul-e/resolve/main/src/pages/Warmup.tsx
12.1 kB
| import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; | |
| import { apiClient } from "../lib/apiClient"; | |
| import { | |
| Flame, | |
| ShieldCheck, | |
| TrendingUp, | |
| Sliders, | |
| CheckCircle2, | |
| AlertTriangle, | |
| Zap, | |
| Mail, | |
| Play, | |
| Pause, | |
| ArrowUpRight | |
| } from "lucide-react"; | |
| import { useState } from "react"; | |
| import StatusBadge from "../components/StatusBadge"; | |
| import FormDrawer from "../components/FormDrawer"; | |
| import toast from "react-hot-toast"; | |
| export default function Warmup() { | |
| const queryClient = useQueryClient(); | |
| const [selectedWarmupAccount, setSelectedWarmupAccount] = useState<any | null>(null); | |
| const [isSettingsOpen, setIsSettingsOpen] = useState(false); | |
| const { data: accounts = [], isLoading, refetch } = useQuery({ | |
| queryKey: ["email-accounts"], | |
| queryFn: async () => { | |
| const res = await apiClient.get("/email-accounts"); | |
| return res.data; | |
| }, | |
| }); | |
| const toggleWarmupMutation = useMutation({ | |
| mutationFn: async ({ id, status }: { id: string; status: "ACTIVE" | "PAUSED" }) => { | |
| const action = status === "ACTIVE" ? "enable" : "disable"; | |
| const res = await apiClient.post(`/email-accounts/${id}/warmup/${action}`); | |
| return res.data; | |
| }, | |
| onSuccess: (_, variables) => { | |
| toast.success(`Warmup ${variables.status === "ACTIVE" ? "activated" : "paused"}`); | |
| refetch(); | |
| }, | |
| onError: () => { | |
| toast.error("Failed to update warmup status"); | |
| } | |
| }); | |
| const activeWarmups = accounts.filter((a: any) => a.warmup_enabled).length; | |
| const dailyWarmupVolume = accounts.reduce((total: number, account: any) => total + (account.warmup_daily_count || 0), 0); | |
| const replyRates = accounts.map((account: any) => account.reply_rate_percentage).filter((rate: any) => typeof rate === "number"); | |
| const averageReplyRate = replyRates.length ? (replyRates.reduce((total: number, rate: number) => total + rate, 0) / replyRates.length).toFixed(1) : "—"; | |
| const scores = accounts.map((account: any) => account.warmup_score).filter((score: any) => typeof score === "number"); | |
| const averageScore = scores.length ? (scores.reduce((total: number, score: number) => total + score, 0) / scores.length).toFixed(1) : "—"; | |
| return ( | |
| <div className="p-6 max-w-7xl mx-auto space-y-6"> | |
| {/* Header */} | |
| <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4"> | |
| <div> | |
| <h1 className="text-2xl font-bold tracking-tight text-zinc-100 flex items-center gap-2.5"> | |
| <Flame className="w-6 h-6 text-amber-400" /> | |
| Autonomous Warmup Engine | |
| </h1> | |
| <p className="text-zinc-400 text-sm mt-1"> | |
| Gradually ramps mailbox sending volume, interacts with peer networks, and rescues emails from spam folders. | |
| </p> | |
| </div> | |
| <div className="flex items-center gap-3"> | |
| <button | |
| onClick={() => { | |
| accounts.forEach((a: any) => { | |
| toggleWarmupMutation.mutate({ id: a.id, status: "ACTIVE" }); | |
| }); | |
| toast.success("Warmup activated on all connected mailboxes"); | |
| }} | |
| 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" | |
| > | |
| <Play className="w-3.5 h-3.5 text-emerald-400" /> Bulk Enable All | |
| </button> | |
| <button | |
| onClick={() => setIsSettingsOpen(true)} | |
| 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" | |
| > | |
| <Sliders className="w-4 h-4" /> Global Warmup Rules | |
| </button> | |
| </div> | |
| </div> | |
| {/* KPI Overview */} | |
| <div className="grid grid-cols-1 sm:grid-cols-4 gap-4"> | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4"> | |
| <span className="text-xs text-zinc-400">Warmup Coverage</span> | |
| <div className="text-2xl font-bold text-zinc-100 mt-1"> | |
| {activeWarmups} / {accounts.length} | |
| </div> | |
| <span className="text-[11px] text-emerald-400 font-medium">Inboxes in active pool</span> | |
| </div> | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4"> | |
| <span className="text-xs text-zinc-400">Daily Warmup Volume</span> | |
| <div className="text-2xl font-bold text-amber-400 mt-1">{dailyWarmupVolume} / day</div> | |
| <span className="text-[11px] text-zinc-500">Auto-incrementing daily</span> | |
| </div> | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4"> | |
| <span className="text-xs text-zinc-400">Warmup Reply Rate</span> | |
| <div className="text-2xl font-bold text-indigo-400 mt-1">{averageReplyRate}%</div> | |
| <span className="text-[11px] text-zinc-500">Autonomous peer response</span> | |
| </div> | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4"> | |
| <span className="text-xs text-zinc-400">Inbox Health Score</span> | |
| <div className="text-2xl font-bold text-emerald-400 mt-1">{averageScore} / 100</div> | |
| <span className="text-[11px] text-emerald-400 font-medium">Excellent deliverability</span> | |
| </div> | |
| </div> | |
| {/* Warmup Mailboxes Table */} | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-xl overflow-hidden shadow-sm"> | |
| <div className="overflow-x-auto"> | |
| <table className="w-full text-sm text-left"> | |
| <thead className="bg-zinc-950 border-b border-zinc-800 text-zinc-400 text-xs uppercase"> | |
| <tr> | |
| <th className="px-6 py-3.5 font-semibold">Mailbox</th> | |
| <th className="px-6 py-3.5 font-semibold">Warmup Status</th> | |
| <th className="px-6 py-3.5 font-semibold">Ramp Day</th> | |
| <th className="px-6 py-3.5 font-semibold">Reputation Score</th> | |
| <th className="px-6 py-3.5 font-semibold">Warmup Volume</th> | |
| <th className="px-6 py-3.5 font-semibold text-right">Warmup Toggle</th> | |
| </tr> | |
| </thead> | |
| <tbody className="divide-y divide-zinc-800/80"> | |
| {accounts.map((acc: any) => { | |
| const isWarmupActive = Boolean(acc.warmup_enabled); | |
| const rampDay = acc.ramp_day || "—"; | |
| const score = acc.warmup_score || 0; | |
| return ( | |
| <tr key={acc.id} className="hover:bg-zinc-800/30 transition-colors"> | |
| <td className="px-6 py-4"> | |
| <div className="font-semibold text-zinc-100 flex items-center gap-2"> | |
| {acc.from_name} | |
| </div> | |
| <div className="text-xs text-zinc-400 font-mono mt-0.5">{acc.from_email}</div> | |
| </td> | |
| <td className="px-6 py-4"> | |
| <StatusBadge status={isWarmupActive ? "ACTIVE" : "PAUSED"} size="sm" /> | |
| </td> | |
| <td className="px-6 py-4 text-zinc-300 font-mono text-xs"> | |
| Day {rampDay} | |
| </td> | |
| <td className="px-6 py-4"> | |
| <div className="flex items-center gap-2"> | |
| <span className="text-xs font-bold text-emerald-400">{score}%</span> | |
| <div className="w-20 h-1.5 bg-zinc-950 rounded-full overflow-hidden border border-zinc-800"> | |
| <div className="h-full bg-emerald-400 rounded-full" style={{ width: `${score}%` }} /> | |
| </div> | |
| </div> | |
| </td> | |
| <td className="px-6 py-4 text-xs font-mono text-zinc-300"> | |
| <span className="text-amber-400 font-semibold">{acc.warmup_daily_count || 0}</span> warm emails sent today | |
| </td> | |
| <td className="px-6 py-4 text-right"> | |
| <button | |
| onClick={() => | |
| toggleWarmupMutation.mutate({ | |
| id: acc.id, | |
| status: isWarmupActive ? "PAUSED" : "ACTIVE", | |
| }) | |
| } | |
| className={`px-3 py-1 text-xs font-medium rounded-lg transition-colors ${ | |
| isWarmupActive | |
| ? "bg-rose-500/10 text-rose-400 hover:bg-rose-500/20 border border-rose-500/30" | |
| : "bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20 border border-emerald-500/30" | |
| }`} | |
| > | |
| {isWarmupActive ? "Pause Warmup" : "Start Warmup"} | |
| </button> | |
| </td> | |
| </tr> | |
| ); | |
| })} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| {/* Global Warmup Rules Drawer */} | |
| <FormDrawer | |
| isOpen={isSettingsOpen} | |
| onClose={() => setIsSettingsOpen(false)} | |
| title="Autonomous Warmup Settings" | |
| subtitle="Fine-tune daily ramping schedules and simulated reply rates." | |
| > | |
| <div className="space-y-4 text-xs"> | |
| <div> | |
| <label className="block font-medium text-zinc-300 mb-1">Target Daily Warmup Max</label> | |
| <input | |
| type="number" | |
| defaultValue="40" | |
| className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100" | |
| /> | |
| <span className="text-[11px] text-zinc-500 mt-1 block">Maximum simulated emails per inbox daily</span> | |
| </div> | |
| <div> | |
| <label className="block font-medium text-zinc-300 mb-1">Daily Ramp Increment</label> | |
| <input | |
| type="number" | |
| defaultValue="4" | |
| className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-2.5 text-zinc-100" | |
| /> | |
| <span className="text-[11px] text-zinc-500 mt-1 block">Additional emails added each consecutive day</span> | |
| </div> | |
| <div> | |
| <label className="block font-medium text-zinc-300 mb-1">Simulated Reply Rate</label> | |
| <input | |
| type="range" | |
| min="10" | |
| max="60" | |
| defaultValue="35" | |
| className="w-full accent-indigo-500" | |
| /> | |
| <div className="flex justify-between text-[11px] text-zinc-400"> | |
| <span>Conservative (15%)</span> | |
| <span className="text-indigo-400 font-semibold">Target: 35%</span> | |
| <span>Aggressive (60%)</span> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between p-3 bg-zinc-900 border border-zinc-800 rounded-lg"> | |
| <div> | |
| <span className="font-medium text-zinc-200 block text-xs">Spam Rescue Protocol</span> | |
| <span className="text-[11px] text-zinc-500">Automatically moves warm emails out of spam folders</span> | |
| </div> | |
| <input | |
| type="checkbox" | |
| defaultChecked | |
| className="w-4 h-4 rounded text-indigo-600 focus:ring-indigo-500 bg-zinc-950 border-zinc-700" | |
| /> | |
| </div> | |
| <div className="pt-4 flex justify-end gap-2 border-t border-zinc-800"> | |
| <button | |
| type="button" | |
| onClick={() => setIsSettingsOpen(false)} | |
| className="px-3 py-2 bg-zinc-800 text-zinc-300 rounded-lg" | |
| > | |
| Cancel | |
| </button> | |
| <button | |
| type="button" | |
| onClick={() => { | |
| toast.success("Warmup parameters saved"); | |
| setIsSettingsOpen(false); | |
| }} | |
| className="px-4 py-2 bg-indigo-600 text-white font-medium rounded-lg" | |
| > | |
| Save Configuration | |
| </button> | |
| </div> | |
| </div> | |
| </FormDrawer> | |
| </div> | |
| ); | |
| } | |