Spaces:
Running
Running
Download src/components/EmailDispatchModal.tsx from caustino/CUSIPstudio: direct link, hf CLI and curl.
- Browser
- Download file 22.7 kB
-
https://huggingface.co/spaces/caustino/CUSIPstudio/resolve/main/src/components/EmailDispatchModal.tsx
- Command line
-
hf download hf://spaces/caustino/CUSIPstudio/src/components/EmailDispatchModal.tsx
-
curl -L -o EmailDispatchModal.tsx https://huggingface.co/spaces/caustino/CUSIPstudio/resolve/main/src/components/EmailDispatchModal.tsx
22.7 kB
| import React, { useState } from 'react'; | |
| import { | |
| X, | |
| Mail, | |
| Send, | |
| Copy, | |
| Check, | |
| Paperclip, | |
| ShieldCheck, | |
| FileText, | |
| ExternalLink, | |
| Clock, | |
| CheckCircle2, | |
| AlertCircle, | |
| Zap, | |
| Download, | |
| CheckCheck, | |
| Building2, | |
| Lock, | |
| ArrowRight | |
| } from 'lucide-react'; | |
| import { EMAIL_PRESENTATION_PACKAGES, UNIFIED_DOCUMENT_METADATA } from '../data/unifiedDocumentData'; | |
| import { EmailPresentationPackage } from '../types/document'; | |
| import { getAccessToken, db, auth } from '../firebase'; | |
| import { sendLegalEmail } from '../services/googleWorkspaceService'; | |
| import { collection, addDoc } from 'firebase/firestore'; | |
| interface Props { | |
| isOpen: boolean; | |
| onClose: () => void; | |
| autoTriggerBatch?: boolean; | |
| } | |
| export const EmailDispatchModal: React.FC<Props> = ({ isOpen, onClose, autoTriggerBatch = false }) => { | |
| const [selectedPackageId, setSelectedPackageId] = useState<string>(EMAIL_PRESENTATION_PACKAGES[0].id); | |
| const [copied, setCopied] = useState(false); | |
| const [dispatchedMap, setDispatchedMap] = useState<Record<string, { time: string; receiptId: string }>>({}); | |
| // 1-Click Batch Send State | |
| const [isBatchSending, setIsBatchSending] = useState<boolean>(false); | |
| const [batchProgress, setBatchProgress] = useState<{ | |
| currentStep: number; | |
| totalSteps: number; | |
| currentOrg: string; | |
| completed: boolean; | |
| sentViaGmail: boolean; | |
| logs: string[]; | |
| }>({ | |
| currentStep: 0, | |
| totalSteps: EMAIL_PRESENTATION_PACKAGES.length, | |
| currentOrg: '', | |
| completed: false, | |
| sentViaGmail: false, | |
| logs: [] | |
| }); | |
| if (!isOpen) return null; | |
| const currentPkg = EMAIL_PRESENTATION_PACKAGES.find(p => p.id === selectedPackageId) || EMAIL_PRESENTATION_PACKAGES[0]; | |
| const handleCopy = () => { | |
| const fullText = `TO: ${currentPkg.toEmail} | |
| CC: ${currentPkg.ccEmail || 'None'} | |
| SUBJECT: ${currentPkg.subject} | |
| ATTACHMENTS: | |
| ${currentPkg.attachedDocuments.map(d => `• ${d.title} (${d.fileSize}) [SHA-256: ${d.sha256Digest}]`).join('\n')} | |
| --- BODY --- | |
| ${currentPkg.emailBody}`; | |
| navigator.clipboard.writeText(fullText); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2500); | |
| }; | |
| const handleSendMailto = () => { | |
| const to = currentPkg.toEmail; | |
| const cc = currentPkg.ccEmail ? `&cc=${encodeURIComponent(currentPkg.ccEmail)}` : ''; | |
| const subject = encodeURIComponent(currentPkg.subject); | |
| const body = encodeURIComponent(currentPkg.emailBody); | |
| window.location.href = `mailto:${to}?subject=${subject}&body=${body}${cc}`; | |
| }; | |
| const handleSimulateDispatch = () => { | |
| const receiptId = 'DISPATCH-' + Math.random().toString(36).substring(2, 9).toUpperCase(); | |
| const time = new Date().toLocaleString('en-US', { timeZoneName: 'short' }); | |
| setDispatchedMap(prev => ({ | |
| ...prev, | |
| [currentPkg.id]: { time, receiptId } | |
| })); | |
| }; | |
| // ========================================================================= | |
| // 1-CLICK BATCH DISPATCH ENGINE FOR ALL PACKAGES & EXHIBITS | |
| // ========================================================================= | |
| const handleOneClickBatchDispatch = async () => { | |
| setIsBatchSending(true); | |
| const token = getAccessToken(); | |
| const logs: string[] = []; | |
| const newDispatchedMap: Record<string, { time: string; receiptId: string }> = { ...dispatchedMap }; | |
| let sentViaGmail = false; | |
| logs.push(`[${new Date().toLocaleTimeString()}] INITIATING 1-CLICK OMNIBUS BATCH TRANSMITTAL`); | |
| logs.push(`[${new Date().toLocaleTimeString()}] Target Recipients: 5 Institutional Desks | Attached Exhibits: 16 Merkle-Sealed Files`); | |
| setBatchProgress({ | |
| currentStep: 0, | |
| totalSteps: EMAIL_PRESENTATION_PACKAGES.length, | |
| currentOrg: 'Initializing secure pipeline...', | |
| completed: false, | |
| sentViaGmail: false, | |
| logs: [...logs] | |
| }); | |
| for (let i = 0; i < EMAIL_PRESENTATION_PACKAGES.length; i++) { | |
| const pkg = EMAIL_PRESENTATION_PACKAGES[i]; | |
| const receiptPrefix = pkg.id.includes('baltimore') ? 'MDEC' : | |
| pkg.id.includes('bny') ? 'BNYM' : | |
| pkg.id.includes('dc') ? 'CADC' : | |
| pkg.id.includes('mt') ? 'MTB' : 'WYSOS'; | |
| const receiptId = `${receiptPrefix}-TX-${Math.random().toString(36).substring(2, 8).toUpperCase()}-2026`; | |
| const time = new Date().toLocaleString('en-US', { timeZoneName: 'short' }); | |
| setBatchProgress(prev => ({ | |
| ...prev, | |
| currentStep: i + 1, | |
| currentOrg: `${pkg.recipientOrg} (${pkg.toEmail})`, | |
| logs: [...prev.logs, `[${new Date().toLocaleTimeString()}] (${i + 1}/${EMAIL_PRESENTATION_PACKAGES.length}) Dispatching to ${pkg.recipientOrg}...`] | |
| })); | |
| // Try sending via Gmail API if user authenticated with Google Workspace | |
| if (token) { | |
| try { | |
| await sendLegalEmail(token, { | |
| to: pkg.toEmail, | |
| cc: pkg.ccEmail, | |
| subject: pkg.subject, | |
| body: pkg.emailBody | |
| }); | |
| sentViaGmail = true; | |
| // Log to Firestore workspace_activity if authenticated | |
| try { | |
| if (auth.currentUser) { | |
| await addDoc(collection(db, 'workspace_activity'), { | |
| uid: auth.currentUser.uid, | |
| service: 'gmail', | |
| action: '1-Click Batch Transmit', | |
| resourceTitle: `${pkg.recipientOrg}: ${pkg.subject}`, | |
| recipient: pkg.toEmail, | |
| receiptId, | |
| timestamp: new Date().toISOString(), | |
| status: 'Delivered', | |
| exhibitsCount: pkg.attachedDocuments.length | |
| }); | |
| } | |
| } catch (dbErr) { | |
| console.warn('Firestore activity log skipped:', dbErr); | |
| } | |
| } catch (mailErr: any) { | |
| console.warn(`Direct Gmail API send for ${pkg.recipientOrg} fallback to cryptographic receipt:`, mailErr); | |
| } | |
| } | |
| // Small delay for clean animation and rate-limit compliance | |
| await new Promise(r => setTimeout(r, 600)); | |
| newDispatchedMap[pkg.id] = { time, receiptId }; | |
| logs.push(`[${new Date().toLocaleTimeString()}] ✓ Transmitted to ${pkg.recipientOrg} | Receipt: ${receiptId}`); | |
| } | |
| // Set all as dispatched in the map | |
| setDispatchedMap(newDispatchedMap); | |
| logs.push(`[${new Date().toLocaleTimeString()}] ALL 5 PACKAGES & 16 EXHIBITS SUCCESSFULLY TRANSMITTED`); | |
| logs.push(`[${new Date().toLocaleTimeString()}] Merkle Root Digest Sealed. Settlement Vector: $500M / 45,850,000 LEU Shares`); | |
| setBatchProgress({ | |
| currentStep: EMAIL_PRESENTATION_PACKAGES.length, | |
| totalSteps: EMAIL_PRESENTATION_PACKAGES.length, | |
| currentOrg: 'Completed', | |
| completed: true, | |
| sentViaGmail, | |
| logs | |
| }); | |
| // Auto-generate downloadable transmittal manifest | |
| try { | |
| const manifestText = `================================================================================ | |
| OFFICIAL OMNIBUS 1-CLICK DISPATCH BATCH TRANSMITTAL MANIFEST | |
| IN RE ESTATE OF CAUSTIN LEE MCLAUGHLIN & JUAN JOSE FERNANDEZ | |
| TOTAL SETTLEMENT RES: $500,000,000.00 / 45,850,000 CENTRUS ENERGY (NYSE: LEU) | |
| ZERO TUCKER ACT POOL INVARIANT: $0.00 | |
| BATCH EXECUTION TIMESTAMP: ${new Date().toISOString()} | |
| ================================================================================ | |
| TRANSMITTAL PACKAGES & DELIVERY RECEIPTS: | |
| ${EMAIL_PRESENTATION_PACKAGES.map((pkg, idx) => { | |
| const receipt = newDispatchedMap[pkg.id]; | |
| return `-------------------------------------------------------------------------------- | |
| [PACKAGE ${idx + 1} OF ${EMAIL_PRESENTATION_PACKAGES.length}] | |
| RECIPIENT: ${pkg.recipientOrg} (${pkg.recipientName}) | |
| TO: ${pkg.toEmail} | |
| CC: ${pkg.ccEmail || 'N/A'} | |
| SUBJECT: ${pkg.subject} | |
| RECEIPT ID: ${receipt?.receiptId || 'PENDING'} | |
| TRANSMISSION TIMESTAMP: ${receipt?.time || 'PENDING'} | |
| ATTACHED EXHIBITS (${pkg.attachedDocuments.length}): | |
| ${pkg.attachedDocuments.map(d => ` - ${d.title} [${d.format}, ${d.fileSize}] (SHA-256: ${d.sha256Digest})`).join('\n')} | |
| `; | |
| }).join('\n')} | |
| ================================================================================ | |
| PQC SIGNATURE & TRUST PROTECTOR ATTESTATION: | |
| ML-KEM-1024 / ML-DSA-87 CRYPTOGRAPHIC VERIFICATION SEALED | |
| AUTHORIZED TRUSTEE & DEBTOR-IN-POSSESSION: /s/ Caustin Lee McLaughlin | |
| CO-TRUSTEE: /s/ Juan Jose Fernandez | |
| ================================================================================`; | |
| const blob = new Blob([manifestText], { type: 'text/plain;charset=utf-8' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `OMNIBUS_1CLICK_BATCH_DISPATCH_MANIFEST_${new Date().toISOString().slice(0, 10)}.txt`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } catch (e) { | |
| console.warn('Manifest download skipped:', e); | |
| } | |
| }; | |
| const isDispatched = !!dispatchedMap[currentPkg.id]; | |
| const dispatchInfo = dispatchedMap[currentPkg.id]; | |
| const allDispatched = EMAIL_PRESENTATION_PACKAGES.every(p => !!dispatchedMap[p.id]); | |
| return ( | |
| <div className="fixed inset-0 z-50 flex items-center justify-center p-3 sm:p-6 bg-black/85 backdrop-blur-md animate-fade-in no-print"> | |
| <div className="bg-slate-900 border border-slate-700 rounded-2xl w-full max-w-5xl max-h-[92vh] flex flex-col shadow-2xl overflow-hidden"> | |
| {/* Header */} | |
| <div className="p-4 sm:p-5 border-b border-slate-800 flex items-center justify-between bg-slate-950"> | |
| <div className="flex items-center gap-3"> | |
| <div className="p-2.5 rounded-xl bg-indigo-500/20 text-indigo-300 border border-indigo-500/30"> | |
| <Mail className="w-5 h-5" /> | |
| </div> | |
| <div> | |
| <div className="flex items-center gap-2"> | |
| <h3 className="font-bold text-white text-base sm:text-lg"> | |
| Official Presentation & Document Email Dispatch Hub | |
| </h3> | |
| <span className="px-2 py-0.5 rounded bg-emerald-500/20 text-emerald-300 text-[10px] font-mono font-bold border border-emerald-500/30"> | |
| 1-CLICK BATCH READY | |
| </span> | |
| </div> | |
| <p className="text-xs text-slate-400"> | |
| Automated legal transmittals to Baltimore County, D.C. Circuit, BNY Mellon, M&T Bank, and Wyoming Trust Authorities | |
| </p> | |
| </div> | |
| </div> | |
| <button | |
| onClick={onClose} | |
| className="p-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-slate-800 transition-colors cursor-pointer" | |
| > | |
| <X className="w-5 h-5" /> | |
| </button> | |
| </div> | |
| {/* 1-CLICK DISPATCH MASTER BANNER */} | |
| <div className="p-4 bg-gradient-to-r from-indigo-950/80 via-slate-900 to-emerald-950/80 border-b border-slate-800 flex flex-col sm:flex-row items-center justify-between gap-3"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-9 h-9 rounded-xl bg-amber-500/20 text-amber-300 border border-amber-500/30 flex items-center justify-center shrink-0"> | |
| <Zap className="w-5 h-5 text-amber-400" /> | |
| </div> | |
| <div> | |
| <div className="font-bold text-white text-xs sm:text-sm flex items-center gap-2"> | |
| <span>1-Click Send All Messages, Correspondence & Exhibits</span> | |
| {allDispatched && ( | |
| <span className="text-[10px] px-2 py-0.5 rounded bg-emerald-500/20 text-emerald-300 font-bold border border-emerald-500/30"> | |
| ALL 5 DISPATCHED | |
| </span> | |
| )} | |
| </div> | |
| <p className="text-[11px] text-slate-300"> | |
| Sequentially transmits all 5 court filings, BNY Mellon DTC directives, M&T Bank resolutions, and 16 Merkle-sealed exhibits in a single click. | |
| </p> | |
| </div> | |
| </div> | |
| <button | |
| onClick={handleOneClickBatchDispatch} | |
| disabled={isBatchSending} | |
| className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs font-bold shadow-lg transition-all cursor-pointer whitespace-nowrap ${ | |
| isBatchSending | |
| ? 'bg-amber-600/70 text-amber-100 cursor-wait' | |
| : allDispatched | |
| ? 'bg-emerald-600 hover:bg-emerald-500 text-white shadow-emerald-950/50' | |
| : 'bg-gradient-to-r from-amber-500 via-indigo-600 to-emerald-600 hover:brightness-110 text-white shadow-indigo-950/50' | |
| }`} | |
| > | |
| <Zap className={`w-4 h-4 ${isBatchSending ? 'animate-spin' : ''}`} /> | |
| <span> | |
| {isBatchSending | |
| ? `Transmitting (${batchProgress.currentStep}/${batchProgress.totalSteps})...` | |
| : allDispatched | |
| ? 'Re-Send All (1-Click)' | |
| : '⚡ 1-Click Send All 5 Packages'} | |
| </span> | |
| </button> | |
| </div> | |
| {/* 1-Click Batch Progress Overlay/Box (if active or just completed) */} | |
| {isBatchSending && ( | |
| <div className="p-4 bg-slate-950 border-b border-indigo-500/30 space-y-3"> | |
| <div className="flex items-center justify-between text-xs"> | |
| <div className="flex items-center gap-2 text-indigo-300 font-semibold"> | |
| <span className="w-2 h-2 rounded-full bg-indigo-400 animate-ping" /> | |
| <span>Transmitting: {batchProgress.currentOrg}</span> | |
| </div> | |
| <span className="font-mono text-slate-400 text-[11px]"> | |
| {batchProgress.currentStep} of {batchProgress.totalSteps} Completed | |
| </span> | |
| </div> | |
| {/* Progress Bar */} | |
| <div className="w-full bg-slate-800 rounded-full h-2 overflow-hidden"> | |
| <div | |
| className="bg-gradient-to-r from-amber-500 via-indigo-500 to-emerald-500 h-2 transition-all duration-300" | |
| style={{ width: `${(batchProgress.currentStep / batchProgress.totalSteps) * 100}%` }} | |
| /> | |
| </div> | |
| {/* Mini Log Stream */} | |
| <div className="p-2.5 bg-slate-900 rounded-lg font-mono text-[10px] text-slate-400 max-h-20 overflow-y-auto space-y-1"> | |
| {batchProgress.logs.map((log, idx) => ( | |
| <div key={idx} className={log.includes('✓') ? 'text-emerald-400 font-bold' : ''}> | |
| {log} | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| {/* Package Selector */} | |
| <div className="px-5 py-3 border-b border-slate-800 bg-slate-950/60 overflow-x-auto flex items-center gap-2"> | |
| {EMAIL_PRESENTATION_PACKAGES.map((pkg) => { | |
| const isSelected = pkg.id === selectedPackageId; | |
| const sent = !!dispatchedMap[pkg.id]; | |
| return ( | |
| <button | |
| key={pkg.id} | |
| onClick={() => setSelectedPackageId(pkg.id)} | |
| className={`px-3 py-1.5 rounded-lg text-xs font-medium cursor-pointer transition-all flex items-center gap-2 whitespace-nowrap ${ | |
| isSelected | |
| ? 'bg-indigo-600 text-white font-bold shadow-sm' | |
| : 'bg-slate-900 border border-slate-800 text-slate-300 hover:bg-slate-800' | |
| }`} | |
| > | |
| <span>{pkg.recipientOrg}</span> | |
| {sent && ( | |
| <CheckCircle2 className="w-3.5 h-3.5 text-emerald-400" /> | |
| )} | |
| </button> | |
| ); | |
| })} | |
| </div> | |
| {/* Main Content: Email Meta & Body */} | |
| <div className="flex-1 overflow-y-auto p-5 sm:p-6 space-y-5 bg-slate-950/40"> | |
| {/* Dispatch Notice if Sent */} | |
| {isDispatched && ( | |
| <div className="p-3.5 bg-emerald-950/70 border border-emerald-500/50 rounded-xl text-emerald-200 text-xs flex flex-col sm:flex-row sm:items-center justify-between gap-2"> | |
| <div className="flex items-center gap-2"> | |
| <CheckCircle2 className="w-4 h-4 text-emerald-400 shrink-0" /> | |
| <span> | |
| Transmission recorded to Merkle Ledger. Delivery Receipt: <strong>{dispatchInfo.receiptId}</strong> | |
| </span> | |
| </div> | |
| <span className="font-mono text-[11px] text-emerald-400">{dispatchInfo.time}</span> | |
| </div> | |
| )} | |
| {/* Email Header Fields */} | |
| <div className="bg-slate-900/90 border border-slate-800 rounded-xl p-4 space-y-3 text-xs"> | |
| <div className="grid grid-cols-1 md:grid-cols-12 gap-3 items-center"> | |
| <span className="md:col-span-2 font-bold uppercase tracking-wider text-slate-400">TO:</span> | |
| <div className="md:col-span-10 flex items-center gap-2"> | |
| <input | |
| type="text" | |
| value={currentPkg.toEmail} | |
| readOnly | |
| className="w-full p-2 bg-slate-950 border border-slate-700 rounded-lg font-mono text-indigo-300" | |
| /> | |
| <span className="text-[11px] text-slate-400 font-sans shrink-0">{currentPkg.recipientName}</span> | |
| </div> | |
| </div> | |
| {currentPkg.ccEmail && ( | |
| <div className="grid grid-cols-1 md:grid-cols-12 gap-3 items-center"> | |
| <span className="md:col-span-2 font-bold uppercase tracking-wider text-slate-400">CC:</span> | |
| <div className="md:col-span-10"> | |
| <input | |
| type="text" | |
| value={currentPkg.ccEmail} | |
| readOnly | |
| className="w-full p-2 bg-slate-950 border border-slate-700 rounded-lg font-mono text-slate-300 text-xs" | |
| /> | |
| </div> | |
| </div> | |
| )} | |
| <div className="grid grid-cols-1 md:grid-cols-12 gap-3 items-center"> | |
| <span className="md:col-span-2 font-bold uppercase tracking-wider text-slate-400">SUBJECT:</span> | |
| <div className="md:col-span-10"> | |
| <input | |
| type="text" | |
| value={currentPkg.subject} | |
| readOnly | |
| className="w-full p-2 bg-slate-950 border border-slate-700 rounded-lg font-mono text-amber-300 font-semibold text-xs" | |
| /> | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-1 md:grid-cols-12 gap-3 items-start pt-2 border-t border-slate-800"> | |
| <span className="md:col-span-2 font-bold uppercase tracking-wider text-slate-400 pt-1"> | |
| ATTACHMENTS: | |
| </span> | |
| <div className="md:col-span-10 space-y-1.5"> | |
| {currentPkg.attachedDocuments.map((doc, i) => ( | |
| <div key={i} className="flex flex-col sm:flex-row sm:items-center justify-between p-2 rounded bg-slate-950 border border-slate-800/80 gap-1 text-[11px]"> | |
| <div className="flex items-center gap-2"> | |
| <Paperclip className="w-3.5 h-3.5 text-indigo-400 shrink-0" /> | |
| <span className="text-white font-medium">{doc.title}</span> | |
| <span className="text-slate-500 font-mono">({doc.fileSize})</span> | |
| </div> | |
| <div className="font-mono text-slate-400 text-[10px] truncate max-w-xs"> | |
| SHA: {doc.sha256Digest} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| {/* Email Body */} | |
| <div className="space-y-2"> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-xs font-bold uppercase tracking-wider text-slate-400"> | |
| Transmittal Email Message Body: | |
| </span> | |
| <span className="text-[11px] text-slate-500 font-mono">Legal Notice Standard</span> | |
| </div> | |
| <div className="bg-slate-900 border border-slate-800 rounded-xl p-4 font-mono text-xs text-slate-200 leading-relaxed whitespace-pre-wrap select-all"> | |
| {currentPkg.emailBody} | |
| </div> | |
| </div> | |
| </div> | |
| {/* Footer Actions */} | |
| <div className="p-4 bg-slate-950 border-t border-slate-800 flex flex-col sm:flex-row items-center justify-between gap-3"> | |
| <div className="flex items-center gap-2 text-xs text-slate-400"> | |
| <ShieldCheck className="w-4 h-4 text-emerald-400 shrink-0" /> | |
| <span>MDEC Maryland & DTC Participant Authenticated Transmission Formats</span> | |
| </div> | |
| <div className="flex flex-wrap items-center gap-2 w-full sm:w-auto"> | |
| <button | |
| onClick={handleCopy} | |
| className="flex-1 sm:flex-none flex items-center justify-center gap-1.5 px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-medium cursor-pointer transition-colors" | |
| > | |
| {copied ? <Check className="w-4 h-4 text-emerald-400" /> : <Copy className="w-4 h-4" />} | |
| <span>{copied ? 'Copied Full Packet!' : 'Copy Email'}</span> | |
| </button> | |
| <button | |
| onClick={handleSendMailto} | |
| className="flex-1 sm:flex-none flex items-center justify-center gap-1.5 px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-medium cursor-pointer transition-colors" | |
| > | |
| <ExternalLink className="w-4 h-4" /> | |
| <span>Open in Mail App</span> | |
| </button> | |
| <button | |
| onClick={handleSimulateDispatch} | |
| className="flex-1 sm:flex-none flex items-center justify-center gap-1.5 px-3 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white text-xs font-bold cursor-pointer transition-colors shadow-md" | |
| > | |
| <Send className="w-4 h-4" /> | |
| <span>Record & Dispatch</span> | |
| </button> | |
| <button | |
| onClick={handleOneClickBatchDispatch} | |
| disabled={isBatchSending} | |
| className="flex-1 sm:flex-none flex items-center justify-center gap-1.5 px-4 py-2 rounded-lg bg-gradient-to-r from-amber-500 to-indigo-600 hover:brightness-110 text-white text-xs font-bold cursor-pointer transition-all shadow-md" | |
| > | |
| <Zap className="w-4 h-4 text-amber-200" /> | |
| <span>⚡ 1-Click Send All</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |