diff --git a/src/App.jsx b/src/App.jsx index 1874251..5a1ca09 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -22,6 +22,8 @@ import Achievement from './pages/Achievement'; import Gallery from './pages/Gallery'; import Contact from './pages/Contact'; import Login from './pages/Login'; +import MailConsole from './pages/MailConsole'; +import FirstYearRegistration from './pages/FirstYearRegistration'; function AnimatedRoutes() { const location = useLocation(); @@ -100,6 +102,9 @@ function AnimatedRoutes() { } /> } /> } /> + } /> + } /> + ); diff --git a/src/pages/FirstYearRegistration.jsx b/src/pages/FirstYearRegistration.jsx new file mode 100644 index 0000000..f24c0a0 --- /dev/null +++ b/src/pages/FirstYearRegistration.jsx @@ -0,0 +1,391 @@ +import { useState } from 'react'; + +const SHEET_URL = 'PASTE_BACKEND_URL_HERE'; + +const DOMAINS = ['Software', 'Electrical', 'Aeronautics', 'Mechanical']; +const BRANCHES = [ + 'Computer Engineering', + 'Information Technology', + 'Electronics & Telecommunication', + 'Mechanical Engineering', + 'Electrical Engineering', + 'Civil Engineering', +]; + +const generateTempRtfId = () => `RTF-${Math.floor(100000 + Math.random() * 900000)}`; + +const emptyForm = { + name: '', + email: '', + branch: '', + yop: '2030', + phone: '', + domain: '', + rtfId: generateTempRtfId(), + tenth: '', + twelfth: '', + cet: '', + jee: '', + diploma: '', +}; + + +const inputClass = + 'w-full bg-black/50 border border-zinc-700/80 rounded-lg px-4 py-2.5 text-white ' + + 'placeholder-gray-500 focus:outline-none focus:border-red-600 transition backdrop-blur-xs'; + +const labelClass = 'block text-sm font-medium text-gray-300 mb-1'; + +export default function FirstYearRegistration() { + const [type, setType] = useState('first'); // 'first' or 'dsy' + const [form, setForm] = useState(emptyForm); + const [step, setStep] = useState('form'); // 'form' -> 'review' -> 'done' + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + const handleType = (t) => { + setType(t); + setForm((prev) => ({ ...prev, yop: t === 'first' ? '2030' : '2029' })); + }; + + const handleChange = (e) => + setForm({ ...form, [e.target.name]: e.target.value }); + + // Step 1: Check details, then show the review screen + const handleReview = (e) => { + e.preventDefault(); + if (!/^[6-9]\d{9}$/.test(form.phone)) { + setError('Enter a valid 10-digit mobile number.'); + return; + } + setError(''); + setStep('review'); + window.scrollTo(0, 0); + }; + + + const handleSubmit = async () => { + setLoading(true); + setError(''); + const payload = { + type, + ...form, + createdAt: Date.now(), + }; + + try { + if (SHEET_URL.includes('PASTE_BACKEND_URL_HERE')) { + console.log('Form data payload:', payload); + } else { + await fetch(SHEET_URL, { + method: 'POST', + mode: 'no-cors', + headers: { 'Content-Type': 'text/plain;charset=utf-8' }, + body: JSON.stringify(payload), + }); + } + setStep('done'); + } catch (err) { + setError('Something went wrong. Please try again.'); + } + setLoading(false); + }; + + const tabClass = (active) => + 'flex-1 py-2.5 rounded-lg font-semibold text-sm transition ' + + (active + ? 'bg-red-600 text-white shadow-lg shadow-red-950/50' + : 'bg-black/40 border border-zinc-700/80 text-gray-300 hover:border-red-600 hover:text-white'); + + + if (step === 'done') { + return ( +
+
+

Registration Successful!

+

+ Thank you for registering with The Robo-Tech Forum. +

+
+
+ ); + } + + // ---------- Review screen ---------- + if (step === 'review') { + const rows = [ + ['Student Type', type === 'first' ? 'First Year ' : 'DSY '], + ['Name', form.name], + ['Personal Email', form.email], + ['Branch', form.branch], + ['Year of Passing', form.yop], + ['Phone', form.phone], + ['Domain', form.domain], + ['RTF ID (Temp)', form.rtfId], + ['10th Score (%)', `${form.tenth}%`], + ...(type === 'first' + ? [ + ['12th Score (%)', `${form.twelfth}%`], + ['CET Score (%)', `${form.cet}%`], + ['JEE Main Score (%)', form.jee ? `${form.jee}%` : 'N/A (Optional)'], + ] + : [['Diploma Percentage (%)', `${form.diploma}%`]]), + ]; + + return ( +
+
+

+ Verify Your Details +

+

+ Please check everything carefully before submitting. +

+ +
+ {rows.map(([label, value]) => ( +
+ {label} + {value} +
+ ))} +
+ + {error &&

{error}

} + +
+ + +
+
+
+ ); + } + + // ---------- Form screen ---------- + return ( +
+
+
+

+ RTF Registration +

+

Select your admission type and enter your details

+
+ + {/* Admission Mode Switcher */} +
+ + +
+ + {/* SECTION 1: COMMON DETAILS */} +
+

+ 1. Basic Information +

+ +
+ + +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+ +
+ + +
+
+ + {/* SECTION 2: CONDITIONAL ACADEMIC DETAILS */} +
+

+ 2. {type === 'first' ? 'First Year Academic Scores' : 'DSY Academic Scores'} +

+ + {type === 'first' ? ( +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ ) : ( +
+ + +
+ )} +
+ + {error &&

{error}

} + + +
+
+ ); +} \ No newline at end of file diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 732814e..461d036 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,3 +1,5 @@ +import { Link } from 'react-router-dom'; + import { motion } from 'framer-motion'; import { pageTransition } from '../lib/animations'; import HeroSection from '../components/sections/HeroSection'; @@ -17,6 +19,9 @@ const MotionMain = motion.main; export default function Home() { return ( + + +
+ + + πŸŽ“ First Year Registration is Open. Click Here! + +
+
{/* */} diff --git a/src/pages/MailConsole/MailConsole.css b/src/pages/MailConsole/MailConsole.css new file mode 100644 index 0000000..553a774 --- /dev/null +++ b/src/pages/MailConsole/MailConsole.css @@ -0,0 +1,657 @@ +/* ---------------- Page + canvas background ---------------- */ +.mail-console { + position: relative; + min-height: 100vh; + background-color: #000000; + color: #ffffff; + font-family: 'Segoe UI', sans-serif; + overflow: hidden; +} + +.dotgrid-canvas { + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; +} + +.mail-content { + position: relative; + z-index: 1; + padding: 140px 40px 60px; + max-width: 1400px; + margin: 0 auto; +} + +/* ---------------- Toast ---------------- */ +.mail-toast { + position: fixed; + top: 24px; + right: 24px; + z-index: 50; + background: #0a0a0a; + border: 1px solid #ff2e2e; + color: #ffffff; + padding: 14px 22px; + border-radius: 10px; + font-size: 0.85rem; + font-weight: 600; + box-shadow: 0 0 20px rgba(255, 46, 46, 0.4); + animation: toast-in 0.3s ease; +} + +@keyframes toast-in { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +/* ---------------- Page header ---------------- */ +.page-header { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 24px; +} + +.page-title { + font-size: 2.1rem; + font-weight: 800; + margin: 0; + letter-spacing: 0.5px; +} + +.page-title-accent { + color: #ff2e2e; +} + +.page-date { + color: #8a8a8a; + font-size: 0.85rem; +} + +/* ---------------- Tabs ---------------- */ +.mail-tabs { + display: flex; + gap: 28px; + border-bottom: 1px solid #232323; + margin-bottom: 24px; +} + +.mail-tab { + background: none; + border: none; + color: #8a8a8a; + font-size: 0.92rem; + font-weight: 600; + padding: 0 0 12px; + cursor: pointer; + border-bottom: 2px solid transparent; + display: flex; + align-items: center; + gap: 8px; +} + +.mail-tab.active { + color: #ffffff; + border-bottom-color: #ff2e2e; +} + +.tab-count { + background: #1a1a1a; + border: 1px solid #333; + border-radius: 999px; + font-size: 0.7rem; + padding: 1px 8px; + color: #cccccc; +} + +/* ---------------- Grid + cards ---------------- */ +.mail-grid { + display: grid; + grid-template-columns: 1fr 1.4fr; + gap: 24px; + align-items: stretch; +} + +.mail-card { + background: rgba(10, 10, 10, 0.92); + border: 1px solid #232323; + border-radius: 14px; + padding: 22px 24px; + backdrop-filter: blur(4px); +} + +.card-eyebrow { + color: #ff2e2e; + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 1.2px; + margin-bottom: 6px; +} + +.card-title-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 18px; +} + +.card-title { + font-size: 1.15rem; + font-weight: 700; + margin: 0; +} + +.muted-small { + color: #8a8a8a; + font-size: 0.78rem; +} + +.pill-badge { + background: #1a0a0a; + border: 1px solid #ff2e2e; + color: #ff2e2e; + font-size: 0.72rem; + font-weight: 700; + padding: 4px 12px; + border-radius: 999px; +} + +/* ---------------- Labels + inputs ---------------- */ +.field-label { + display: block; + color: #8a8a8a; + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.8px; + margin-bottom: 6px; +} + +.field-label-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 14px; +} + +.field-label-row .field-label { + margin-bottom: 6px; +} + +.char-counter { + font-size: 0.7rem; + color: #666666; +} + +.mail-select, +.mail-input, +.mail-textarea { + width: 100%; + background: #000000; + border: 1px solid #2d2d2d; + border-radius: 8px; + padding: 11px 14px; + color: #ffffff; + margin-bottom: 14px; + font-size: 0.9rem; + outline: none; + transition: border-color 0.2s; + box-sizing: border-box; + font-family: inherit; +} + +.mail-select:focus, +.mail-input:focus, +.mail-textarea:focus { + border-color: #ff2e2e; +} + +.mail-textarea { + min-height: 130px; + resize: vertical; + margin-bottom: 0; +} + +/* ---------------- Select all / clear ---------------- */ +.mail-select-row { + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} + +.mail-link-btn { + background: none; + border: none; + color: #ff2e2e; + cursor: pointer; + font-size: 0.82rem; + font-weight: 700; + padding: 0; +} + +.mail-link-muted { + color: #8a8a8a; +} + +.mail-link-btn:hover { + text-decoration: underline; +} + +/* ---------------- Recipient list ---------------- */ +.mail-recipient-list { + max-height: 380px; + overflow-y: auto; + border-top: 1px solid #232323; + padding-top: 6px; + margin-right: -8px; + padding-right: 8px; +} + +.mail-recipient-list::-webkit-scrollbar { + width: 6px; +} +.mail-recipient-list::-webkit-scrollbar-thumb { + background: rgba(255, 46, 46, 0.35); + border-radius: 10px; +} + +.recipient-row { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 6px; + cursor: pointer; + border-radius: 8px; + border-bottom: 1px solid #161616; +} + +.recipient-row:last-child { + border-bottom: none; +} + +.recipient-row:hover { + background: #131313; +} + +.recipient-row input { + accent-color: #ff2e2e; + width: 16px; + height: 16px; + flex-shrink: 0; +} + +.avatar { + width: 34px; + height: 34px; + border-radius: 50%; + background: #1a1a1a; + border: 1px solid #333333; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + font-weight: 700; + color: #ffffff; + flex-shrink: 0; +} + +.recipient-info { + display: flex; + flex-direction: column; + min-width: 0; +} + +.recipient-name { + font-size: 0.9rem; + color: #ffffff; + font-weight: 600; +} + +.recipient-email { + font-size: 0.75rem; + color: #7a8a94; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.recipient-group { + font-size: 0.7rem; + color: #ff6b6b; + font-weight: 600; + margin-top: 1px; +} + +.empty-state { + color: #7a7a7a; + font-size: 0.85rem; + text-align: center; + padding: 30px 0; +} + +/* ---------------- To chips ---------------- */ +.to-box { + min-height: 46px; + background: #000000; + border: 1px solid #2d2d2d; + border-radius: 8px; + padding: 10px 12px; + margin-bottom: 4px; + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.to-placeholder { + color: #666666; + font-size: 0.85rem; +} + +.to-chip { + background: #1a0a0a; + border: 1px solid #ff2e2e; + color: #ffffff; + font-size: 0.78rem; + font-weight: 600; + padding: 4px 8px 4px 12px; + border-radius: 999px; + display: flex; + align-items: center; + gap: 6px; +} + +.to-chip-remove { + background: none; + border: none; + color: #ff6b6b; + cursor: pointer; + font-size: 1rem; + line-height: 1; + padding: 0; +} + +/* ---------------- Compose footer (no dead space) ---------------- */ +.compose-footer { + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid #1e1e1e; + display: flex; + align-items: center; + gap: 14px; + flex-wrap: wrap; +} + +.mail-attach-btn { + background: #161616; + border: 1px solid #2d2d2d; + border-radius: 999px; + padding: 9px 16px; + cursor: pointer; + font-size: 0.82rem; + color: #ffffff; + font-weight: 600; +} + +.mail-attach-btn:hover { + border-color: #ff2e2e; +} + +.mail-file-name { + font-size: 0.78rem; + color: #a0a0a0; + display: flex; + align-items: center; + gap: 6px; +} + +.file-remove { + background: none; + border: none; + color: #ff6b6b; + cursor: pointer; + font-size: 0.9rem; +} + +.footer-actions { + margin-left: auto; + display: flex; + gap: 10px; +} + +.btn-ghost, +.btn-outline, +.btn-primary { + border-radius: 999px; + font-size: 0.82rem; + font-weight: 700; + padding: 10px 20px; + cursor: pointer; + font-family: inherit; +} + +.btn-ghost { + background: none; + border: 1px solid #2d2d2d; + color: #a0a0a0; +} + +.btn-ghost:hover { + border-color: #555; + color: #ffffff; +} + +.btn-outline { + background: #161616; + border: 1px solid #2d2d2d; + color: #ffffff; +} + +.btn-outline:hover { + border-color: #ffffff; +} + +.btn-primary { + background: #ff2e2e; + border: none; + color: #ffffff; + box-shadow: 0 0 15px rgba(255, 46, 46, 0.35); + transition: background 0.2s, transform 0.15s; +} + +.btn-primary:disabled { + opacity: 0.4; + cursor: not-allowed; + box-shadow: none; +} + +.btn-primary:hover:not(:disabled) { + background: #ff0000; + transform: translateY(-1px); +} + +/* ---------------- History tab ---------------- */ +.history-card { + min-height: 340px; +} + +.history-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 60px 20px; + color: #8a8a8a; +} + +.history-empty-icon { + width: 56px; + height: 56px; + border-radius: 14px; + background: #161616; + border: 1px solid #2d2d2d; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.4rem; + color: #ff2e2e; + margin-bottom: 18px; +} + +.history-empty h3 { + color: #ffffff; + font-size: 1rem; + margin: 0 0 8px; +} + +.history-empty p { + font-size: 0.82rem; + max-width: 340px; + line-height: 1.5; + margin: 0; +} + +.history-list { + display: flex; + flex-direction: column; +} + +.history-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 14px 4px; + border-bottom: 1px solid #1a1a1a; +} + +.history-row:hover { + background: #101010; +} + +.history-row-main { + display: flex; + flex-direction: column; +} + +.history-subject { + color: #ffffff; + font-weight: 600; + font-size: 0.9rem; +} + +.history-recipients { + color: #7a8a94; + font-size: 0.78rem; + margin-top: 2px; +} + +.history-row-side { + display: flex; + align-items: center; + gap: 14px; +} + +.history-time { + color: #666666; + font-size: 0.75rem; +} + +.status-pill { + background: #06210f; + border: 1px solid #22c55e; + color: #22c55e; + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + padding: 3px 12px; + border-radius: 999px; +} + +/* ---------------- Stats bar (bottom) ---------------- */ +.stats-bar { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 16px; + margin-top: 40px; +} + +.stat-card { + background: rgba(10, 10, 10, 0.92); + border: 1px solid #232323; + border-radius: 12px; + padding: 16px 18px; + display: flex; + align-items: center; + gap: 14px; +} + +.stat-card-red { + border-color: #ff2e2e; + box-shadow: 0 0 12px rgba(255, 46, 46, 0.25); +} + +.stat-icon { + width: 38px; + height: 38px; + border-radius: 10px; + background: #161616; + border: 1px solid #2d2d2d; + display: flex; + align-items: center; + justify-content: center; + color: #ff2e2e; + font-size: 1rem; + flex-shrink: 0; +} + +.stat-label { + font-size: 0.68rem; + color: #8a8a8a; + text-transform: uppercase; + letter-spacing: 0.6px; +} + +.stat-value { + font-size: 1.3rem; + font-weight: 800; + color: #ffffff; +} + +/* ---------------- Floating scroll-to-top ---------------- */ +.scroll-top-btn { + position: fixed; + bottom: 28px; + right: 28px; + width: 48px; + height: 48px; + border-radius: 50%; + background: #0a0a0a; + border: 1px solid #ff2e2e; + color: #ff2e2e; + font-size: 1rem; + cursor: pointer; + z-index: 40; + box-shadow: 0 0 18px rgba(255, 46, 46, 0.45); + transition: transform 0.2s, box-shadow 0.2s; +} + +.scroll-top-btn:hover { + transform: translateY(-3px); + box-shadow: 0 0 26px rgba(255, 46, 46, 0.65); +} + +/* ---------------- Responsive ---------------- */ +@media (max-width: 900px) { + .mail-grid { + grid-template-columns: 1fr; + } + .stats-bar { + grid-template-columns: repeat(2, 1fr); + } + .mail-content { + padding: 120px 20px 40px; + } +} \ No newline at end of file diff --git a/src/pages/MailConsole/index.jsx b/src/pages/MailConsole/index.jsx index e69de29..c6edbba 100644 --- a/src/pages/MailConsole/index.jsx +++ b/src/pages/MailConsole/index.jsx @@ -0,0 +1,467 @@ +import { useState, useEffect, useRef } from 'react'; +import './MailConsole.css'; + +const RECIPIENTS = [ + { id: 1, name: 'Anushka Garpawar', email: 'aditi.rao@gmail.com', group: 'AIML' }, + { id: 2, name: 'Radhika Roy', email: 'karan.mehta@gmail.com', group: 'IOT' }, + { id: 3, name: 'sanika Kulkarni', email: 'neha.kulkarni@gmail.com', group: 'Advanced Mechanism' }, + { id: 4, name: 'Rohan Deshmukh', email: 'rohan.iyer@gmail.com', group: 'Robotics' }, + { id: 5, name: 'divya Shah', email: 'priya.shah@gmail.com', group: 'IOT' }, + { id: 6, name: 'Arjun Patil', email: 'arjun.patil@gmail.com', group: 'Advanced Mechanism' }, + { id: 7, name: 'mukta Joshi', email: 'sneha.joshi@gmail.com', group: 'Robotics' }, + { id: 8, name: 'gaurav Desai', email: 'vikram.desai@gmail.com', group: 'IOT' }, +]; + +const SUBJECT_LIMIT = 120; +const MESSAGE_LIMIT = 2000; + +/* ---------------- Interactive canvas dot-grid, glows red near cursor ---------------- */ +function DotGridBackground() { + const canvasRef = useRef(null); + const mouseRef = useRef({ x: -9999, y: -9999 }); + + useEffect(() => { + const canvas = canvasRef.current; + const ctx = canvas.getContext('2d'); + let animationId; + let width, height; + const spacing = 26; + const maxDist = 170; + + function resize() { + width = canvas.width = window.innerWidth; + height = canvas.height = window.innerHeight; + } + resize(); + window.addEventListener('resize', resize); + + function handleMove(e) { + mouseRef.current = { x: e.clientX, y: e.clientY }; + } + window.addEventListener('mousemove', handleMove); + + function handleLeave() { + mouseRef.current = { x: -9999, y: -9999 }; + } + window.addEventListener('mouseout', handleLeave); + + function draw() { + ctx.clearRect(0, 0, width, height); + const { x: mx, y: my } = mouseRef.current; + + for (let y = 0; y <= height; y += spacing) { + for (let x = 0; x <= width; x += spacing) { + const dx = x - mx; + const dy = y - my; + const dist = Math.sqrt(dx * dx + dy * dy); + + let radius = 1.1; + let color = 'rgba(255,255,255,0.06)'; + let glow = 0; + + if (dist < maxDist) { + const t = 1 - dist / maxDist; + radius = 1.1 + t * 2.2; + const g = Math.round(255 - t * 215); + const b = Math.round(255 - t * 212); + color = `rgba(255,${g},${b},${0.12 + t * 0.85})`; + glow = t * 12; + } + + ctx.beginPath(); + ctx.fillStyle = color; + ctx.shadowColor = 'rgba(255,46,46,0.85)'; + ctx.shadowBlur = glow; + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.fill(); + } + } + animationId = requestAnimationFrame(draw); + } + draw(); + + return () => { + window.removeEventListener('resize', resize); + window.removeEventListener('mousemove', handleMove); + window.removeEventListener('mouseout', handleLeave); + cancelAnimationFrame(animationId); + }; + }, []); + + return ; +} + +function initials(name) { + return name + .split(' ') + .map((w) => w[0]) + .join('') + .slice(0, 2) + .toUpperCase(); +} + +function timeAgo(ts) { + const diff = Math.floor((Date.now() - ts) / 1000); + if (diff < 60) return 'just now'; + if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; + return `${Math.floor(diff / 86400)}d ago`; +} + +function MailConsole() { + const [activeTab, setActiveTab] = useState('compose'); // 'compose' | 'history' + const [search, setSearch] = useState(''); + const [domain, setDomain] = useState('all'); + const [selected, setSelected] = useState([]); + const [subject, setSubject] = useState(''); + const [message, setMessage] = useState(''); + const [attachment, setAttachment] = useState(null); + const [sending, setSending] = useState(false); + const [toast, setToast] = useState(null); + const [drafts, setDrafts] = useState([]); + const [history, setHistory] = useState([]); + + const domains = ['all', ...new Set(RECIPIENTS.map((r) => r.email.split('@')[1]))]; + + const filtered = RECIPIENTS.filter((r) => { + const matchesSearch = + r.name.toLowerCase().includes(search.toLowerCase()) || + r.email.toLowerCase().includes(search.toLowerCase()); + const matchesDomain = domain === 'all' || r.email.endsWith(`@${domain}`); + return matchesSearch && matchesDomain; + }); + + const selectedRecipients = RECIPIENTS.filter((r) => selected.includes(r.id)); + + const toggleOne = (id) => { + setSelected((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id])); + }; + const removeChip = (id) => setSelected((prev) => prev.filter((x) => x !== id)); + const selectAll = () => setSelected(filtered.map((r) => r.id)); + const clearAll = () => setSelected([]); + + const showToast = (text) => { + setToast(text); + setTimeout(() => setToast(null), 2800); + }; + + const resetComposeFields = () => { + setSubject(''); + setMessage(''); + setAttachment(null); + }; + + const handleClear = () => resetComposeFields(); + + const handleSaveDraft = () => { + if (!subject.trim() && !message.trim()) return; + setDrafts((prev) => [ + { id: Date.now(), subject: subject || '(no subject)', recipients: selected.length }, + ...prev, + ]); + showToast('Draft saved'); + }; + + const handleSend = (e) => { + e.preventDefault(); + if (selected.length === 0 || !subject.trim()) return; + setSending(true); + + setTimeout(() => { + const entry = { + id: Date.now(), + recipients: selectedRecipients.map((r) => r.name), + subject, + status: 'sent', + ts: Date.now(), + }; + setHistory((prev) => [entry, ...prev]); + setSending(false); + showToast(`Sent to ${selected.length} recipient${selected.length > 1 ? 's' : ''}`); + resetComposeFields(); + setSelected([]); + }, 700); + }; + + const scrollToTop = () => window.scrollTo({ top: 0, behavior: 'smooth' }); + + const totalMembers = RECIPIENTS.length; + const membersEmailed = new Set(history.flatMap((h) => h.recipients)).size; + const draftsCount = drafts.length; + + return ( +
+ + + {toast &&
{toast}
} + +
+ {/* Page header */} +
+

+ Email Console +

+ + {new Date().toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })} + +
+ + {/* Tabs */} +
+ + +
+ + {/* ---------------- COMPOSE TAB ---------------- */} + {activeTab === 'compose' && ( +
+ {/* Recipients panel */} +
+
Recipients
+
+

Choose recipients

+ {selected.length} selected +
+ + + + + setSearch(e.target.value)} + /> + +
+ + +
+ +
+ {filtered.map((r) => ( + + ))} + {filtered.length === 0 && ( +

No recipients found.

+ )} +
+
+ + {/* Compose panel */} +
+
Message
+
+

Compose email

+ {selected.length} recipients +
+ + +
+ {selectedRecipients.length === 0 ? ( + Select recipients from the left + ) : ( + selectedRecipients.map((r) => ( + + {r.name} + + + )) + )} +
+ +
+ + + {subject.length}/{SUBJECT_LIMIT} + +
+ setSubject(e.target.value)} + /> + +
+ + + {message.length}/{MESSAGE_LIMIT} + +
+