fixed email messages

This commit is contained in:
2025-12-22 17:59:43 +01:00
parent c7f669fc11
commit 846379eccc
5 changed files with 228 additions and 58 deletions

View File

@@ -64,7 +64,8 @@
"lucide-react": "https://esm.sh/lucide-react@0.344.0?deps=react@18.2.0", "lucide-react": "https://esm.sh/lucide-react@0.344.0?deps=react@18.2.0",
"@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2.39.7", "@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2.39.7",
"react-dom/": "https://esm.sh/react-dom@^19.2.3/", "react-dom/": "https://esm.sh/react-dom@^19.2.3/",
"react/": "https://esm.sh/react@^19.2.3/" "react/": "https://esm.sh/react@^19.2.3/",
"@google/genai": "https://esm.sh/@google/genai@^1.34.0"
} }
} }
</script> </script>

View File

@@ -13,7 +13,8 @@
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-router-dom": "6.22.3", "react-router-dom": "6.22.3",
"lucide-react": "0.344.0", "lucide-react": "0.344.0",
"@supabase/supabase-js": "2.39.7" "@supabase/supabase-js": "2.39.7",
"@google/genai": "^1.34.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.14.0", "@types/node": "^22.14.0",

View File

@@ -32,6 +32,7 @@ interface EmailLogEntry {
id: string; id: string;
email_type: string; email_type: string;
sent_at: string; sent_at: string;
body?: string;
} }
interface AdminOrder { interface AdminOrder {
@@ -49,6 +50,122 @@ interface AdminOrder {
emailLogs?: EmailLogEntry[]; emailLogs?: EmailLogEntry[];
} }
const getEmailTemplate = (type: string, data: { customer: string, package: string, demoUrl?: string }) => {
const baseStyle = "font-family: 'Inter', Helvetica, Arial, sans-serif; line-height: 1.6; color: #1a1a1a; max-width: 600px; margin: 0 auto; padding: 40px 20px; border: 1px solid #f0f0f0; border-radius: 24px; background-color: #ffffff;";
const headerStyle = "color: #7c3aed; font-size: 28px; font-weight: 800; margin-bottom: 24px; letter-spacing: -0.02em; border-bottom: 1px solid #f0f0f0; padding-bottom: 20px;";
const footerStyle = "margin-top: 40px; padding-top: 24px; border-top: 1px solid #f0f0f0; font-size: 13px; color: #94a3b8;";
const buttonStyle = "display: inline-block; background-color: #7c3aed; color: #ffffff !important; padding: 14px 28px; text-decoration: none; border-radius: 12px; font-weight: 700; margin: 20px 0; font-size: 15px;";
const templates: Record<string, { subject: string, body: string }> = {
'Fejlesztés megkezdése': {
subject: `Projekt Indítása: Megkezdtük a fejlesztést - ${data.package}`,
body: `<div style="${baseStyle}">
<div style="${headerStyle}">MotionWeb</div>
<p>Kedves <strong>${data.customer}</strong>!</p>
<p>Örömmel értesítjük, hogy a <strong>${data.package}</strong> csomagjához tartozó fejlesztési folyamat a mai napon hivatalosan is elindult.</p>
<p>Szakembereink elkezdték a weboldal vázlatának és design elemeinek kidolgozását. Amint elkészülünk az első megtekinthető demó verzióval, azonnal jelentkezünk a hozzáférési linkkel.</p>
<p>Köszönjük a bizalmát!</p>
<div style="${footerStyle}">
Üdvözlettel,<br>
<strong>Balogh Bence</strong><br>
MotionWeb Stúdió | motionweb.hu
</div>
</div>`
},
'Demó oldal elkészült': {
subject: `Elkészült a weboldal demó verziója - ${data.package}`,
body: `<div style="${baseStyle}">
<div style="${headerStyle}">MotionWeb</div>
<p>Kedves <strong>${data.customer}</strong>!</p>
<p>Jó hírünk van: elkészültünk weboldala első, interaktív bemutató verziójával!</p>
<p>A demó oldalt az alábbi biztonságos linken tudja megtekinteni:</p>
<div style="text-align: center;">
<a href="${data.demoUrl || '#'}" style="${buttonStyle}">DEMÓ MEGTEKINTÉSE</a>
</div>
<p>Kérjük, nézze át az oldalt, és a MotionWeb ügyfélkapujában a <strong>Rendeléseim</strong> menüpont alatt küldje el visszajelzését, hogy rögzíthessük az esetleges módosítási kéréseit.</p>
<div style="${footerStyle}">
Üdvözlettel,<br>
<strong>Balogh Bence</strong><br>
MotionWeb Stúdió | motionweb.hu
</div>
</div>`
},
'Módosítások fejlesztése': {
subject: `Visszajelzés rögzítve: Módosítások folyamatban`,
body: `<div style="${baseStyle}">
<div style="${headerStyle}">MotionWeb</div>
<p>Kedves <strong>${data.customer}</strong>!</p>
<p>Köszönjük részletes visszajelzését! A kért módosításokat rögzítettük és szakembereink már dolgoznak azok átvezetésén.</p>
<p>Amint a frissített verzió megtekinthető lesz, e-mailben ismét értesíteni fogjuk.</p>
<div style="${footerStyle}">
Üdvözlettel,<br>
<strong>Balogh Bence</strong><br>
MotionWeb Stúdió | motionweb.hu
</div>
</div>`
},
'1 hete nincs visszajelzés': {
subject: `Emlékeztető: Visszajelzés várható a demó oldallal kapcsolatban`,
body: `<div style="${baseStyle}">
<div style="${headerStyle}">MotionWeb</div>
<p>Kedves <strong>${data.customer}</strong>!</p>
<p>Szeretnénk emlékeztetni, hogy egy héttel ezelőtt küldtük el Önnek a weboldal demó verzióját.</p>
<p>Annak érdekében, hogy tartsuk a fejlesztési ütemtervet, kérjük, amint ideje engedi, nézze át a tervet és küldje el véleményét az ügyfélkapun keresztül.</p>
<div style="${footerStyle}">
Üdvözlettel,<br>
<strong>Balogh Bence</strong><br>
MotionWeb Stúdió | motionweb.hu
</div>
</div>`
},
'2 hete nincs visszajelzés': {
subject: `Fontos: Továbblépéshez szükséges visszajelzés hiánya`,
body: `<div style="${baseStyle}">
<div style="${headerStyle}">MotionWeb</div>
<p>Kedves <strong>${data.customer}</strong>!</p>
<p>Szeretnénk felhívni figyelmét, hogy weboldala demó verziója már két hete várakozik az Ön jóváhagyására.</p>
<p>Amennyiben elégedett a látottakkal, kérjük, jelezze azt a felületen a véglegesítés megkezdéséhez. Bármilyen kérdés vagy észrevétel esetén állunk rendelkezésére.</p>
<div style="${footerStyle}">
Üdvözlettel,<br>
<strong>Balogh Bence</strong><br>
MotionWeb Stúdió | motionweb.hu
</div>
</div>`
},
'Projekt lezárva (Nincs válasz)': {
subject: `Értesítés: Projekt adminisztratív lezárása - ${data.package}`,
body: `<div style="${baseStyle}">
<div style="${headerStyle}">MotionWeb</div>
<p>Kedves <strong>${data.customer}</strong>!</p>
<p>Sajnálattal értesítjük, hogy mivel az elmúlt időszakban többszöri megkeresésünkre sem érkezett válasz, a <strong>${data.package}</strong> projektet a mai napon adminisztratív okokból lezártnak tekintjük.</p>
<p>Amennyiben a jövőben folytatni szeretné a fejlesztést, kérjük, vegye fel velünk a kapcsolatot egy új ütemterv egyeztetése érdekében.</p>
<div style="${footerStyle}">
Üdvözlettel,<br>
<strong>Balogh Bence</strong><br>
MotionWeb Stúdió | motionweb.hu
</div>
</div>`
},
'Végleges oldal elérhető': {
subject: `Gratulálunk! Weboldala elkészült és élesítésre került`,
body: `<div style="${baseStyle}">
<div style="${headerStyle}">MotionWeb</div>
<p>Kedves <strong>${data.customer}</strong>!</p>
<p>Örömmel jelentjük, hogy a <strong>${data.package}</strong> projektünk sikeresen befejeződött!</p>
<p>A weboldalt élesítettük, így az mostantól minden látogató számára elérhető a végleges domain címen.</p>
<p>Köszönjük, hogy minket választott digitális partnerének. Kérjük, amennyiben elégedett volt a munkánkkal, ajánljon minket ismerőseinek is!</p>
<div style="${footerStyle}">
Üdvözlettel,<br>
<strong>Balogh Bence</strong><br>
MotionWeb Stúdió | motionweb.hu
</div>
</div>`
}
};
return templates[type] || { subject: 'Értesítés a MotionWeb-től', body: 'Üzenete érkezett a MotionWeb Stúdiótól.' };
};
export const Admin: React.FC = () => { export const Admin: React.FC = () => {
const { user, isAdmin, loading } = useAuth(); const { user, isAdmin, loading } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -149,11 +266,17 @@ export const Admin: React.FC = () => {
const fetchEmailLogs = async (orderId: string) => { const fetchEmailLogs = async (orderId: string) => {
if (!isSupabaseConfigured) return []; if (!isSupabaseConfigured) return [];
try { try {
const { data } = await supabase // Megpróbáljuk lekérni, de ha hiányzik oszlop vagy tábla, hibatűrően kezeljük
const { data, error } = await supabase
.from('email_log') .from('email_log')
.select('*') .select('*')
.eq('order_id', orderId) .eq('order_id', orderId)
.order('sent_at', { ascending: false }); .order('sent_at', { ascending: false });
if (error) {
console.error("Email log fetch error:", error.message);
return [];
}
return data || []; return data || [];
} catch (e) { } catch (e) {
console.warn("Could not fetch email_log table."); console.warn("Could not fetch email_log table.");
@@ -213,6 +336,10 @@ export const Admin: React.FC = () => {
setOrders(prev => prev.map(o => o.id === orderId ? { ...o, status: targetStatus, details: updatedDetails } : o)); setOrders(prev => prev.map(o => o.id === orderId ? { ...o, status: targetStatus, details: updatedDetails } : o));
setViewOrder({ ...viewOrder, status: targetStatus, details: updatedDetails, history: newHist }); setViewOrder({ ...viewOrder, status: targetStatus, details: updatedDetails, history: newHist });
if (demoUrlInput.startsWith('http')) {
handleSendEmail('Demó oldal elkészült');
}
alert(`Sikeres mentés és visszajelzés kérése!`); alert(`Sikeres mentés és visszajelzés kérése!`);
} else { } else {
setViewOrder({ ...viewOrder, details: updatedDetails }); setViewOrder({ ...viewOrder, details: updatedDetails });
@@ -240,25 +367,62 @@ export const Admin: React.FC = () => {
setEmailSending(emailType); setEmailSending(emailType);
try { try {
console.log(`Email notification trigger: ${emailType}`); const template = getEmailTemplate(emailType, {
customer: viewOrder.customer,
package: viewOrder.package,
demoUrl: demoUrlInput || viewOrder.details?.demoUrl
});
if (isSupabaseConfigured) { if (isSupabaseConfigured) {
const { error } = await supabase.from('email_log').insert({ // MEGHÍVJUK A FÜGGVÉNYT
order_id: viewOrder.id, const response = await supabase.functions.invoke('resend', {
email_type: emailType body: {
to: viewOrder.email,
subject: template.subject,
html: template.body
}
}); });
if (error) throw error; // Supabase hiba kezelése (az invoke hiba objektumot ad vissza, ha a hívás sikertelen)
if (response.error) {
let errorDetail = response.error.message;
if (errorDetail.includes('API kulcs hiányzik')) {
throw new Error("HIÁNYZIK A BEÁLLÍTÁS: A Supabase Dashboardon az Edge Functions -> Secrets menüpontban fel kell venni a RESEND_API_KEY kulcsot!");
}
throw new Error(errorDetail);
}
// A függvény által visszaadott egyedi hiba (ha van)
if (response.data && response.data.error) {
throw new Error(response.data.error);
}
// SIKERES KÜLDÉS NAPLÓZÁSA (Hibatűrő módon)
try {
const { error: logError } = await supabase.from('email_log').insert({
order_id: viewOrder.id,
email_type: emailType,
body: template.body
});
if (logError) {
console.error("Adatbázis naplózási hiba (lehet, hogy hiányzik a 'body' oszlop):", logError.message);
// Itt nem dobunk hibát, mert az email már kiment! Csak logolunk a konzolra.
}
} catch (dbErr) {
console.error("Váratlan hiba az adatbázisba íráskor:", dbErr);
}
const newLogs = await fetchEmailLogs(viewOrder.id); const newLogs = await fetchEmailLogs(viewOrder.id);
setViewOrder({ ...viewOrder, emailLogs: newLogs }); setViewOrder({ ...viewOrder, emailLogs: newLogs });
alert(`E-mail ("${emailType}") naplózva és kiküldve (szimulált).`); alert(`Sikeres küldés! Az értesítőt kiküldtük ${viewOrder.email} címre.`);
} else { } else {
alert(`E-mail értesítő előkészítve: "${emailType}"\n\nJelenleg ez a funkció csak placeholder.`); alert(`DEMO MÓD: Tárgy: ${template.subject}`);
} }
} catch (e: any) { } catch (e: any) {
alert("Hiba az e-mail naplózásakor: " + e.message); console.error("Hiba az e-mail folyamatban:", e);
alert("Hiba az e-mail küldésekor:\n\n" + e.message);
} finally { } finally {
setEmailSending(null); setEmailSending(null);
} }
@@ -468,10 +632,9 @@ export const Admin: React.FC = () => {
</div> </div>
</section> </section>
{/* EMAIL NOTIFICATIONS & LOGS COMBINED */}
<section className="space-y-8"> <section className="space-y-8">
<div> <div>
<h3 className="text-[11px] font-black text-gray-400 uppercase tracking-[0.3em] mb-6 flex items-center gap-2"><Mail className="w-4 h-4" /> E-mail értesítők</h3> <h3 className="text-[11px] font-black text-gray-400 uppercase tracking-[0.3em] mb-6 flex items-center gap-2"><Mail className="w-4 h-4" /> E-mail értesítők (Közvetlen sablonok)</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{[ {[
{ label: 'Fejlesztés megkezdése', icon: Rocket }, { label: 'Fejlesztés megkezdése', icon: Rocket },
@@ -493,7 +656,9 @@ export const Admin: React.FC = () => {
</div> </div>
<div className="flex-grow"> <div className="flex-grow">
<p className="text-xs font-bold text-gray-900">{email.label}</p> <p className="text-xs font-bold text-gray-900">{email.label}</p>
<p className="text-[9px] text-gray-400 uppercase font-black tracking-widest mt-0.5">Értesítő küldése</p> <p className="text-[9px] text-gray-400 uppercase font-black tracking-widest mt-0.5">
{emailSending === email.label ? 'Küldés folyamatban...' : 'E-mail küldése'}
</p>
</div> </div>
<Send className="w-3 h-3 text-gray-300 group-hover:text-primary group-hover:translate-x-1 transition-all" /> <Send className="w-3 h-3 text-gray-300 group-hover:text-primary group-hover:translate-x-1 transition-all" />
</button> </button>
@@ -501,7 +666,6 @@ export const Admin: React.FC = () => {
</div> </div>
</div> </div>
{/* IN-PLACE EMAIL LOG */}
<div className="bg-purple-50/50 p-8 rounded-[32px] border border-purple-100 shadow-sm"> <div className="bg-purple-50/50 p-8 rounded-[32px] border border-purple-100 shadow-sm">
<h4 className="text-[11px] font-black text-purple-600 uppercase tracking-[0.3em] mb-6 flex items-center gap-2"><History className="w-4 h-4" /> Korábban kiküldött e-mailek</h4> <h4 className="text-[11px] font-black text-purple-600 uppercase tracking-[0.3em] mb-6 flex items-center gap-2"><History className="w-4 h-4" /> Korábban kiküldött e-mailek</h4>
<div className="space-y-6 relative before:absolute before:left-[11px] before:top-2 before:bottom-2 before:w-0.5 before:bg-purple-200"> <div className="space-y-6 relative before:absolute before:left-[11px] before:top-2 before:bottom-2 before:w-0.5 before:bg-purple-200">
@@ -510,8 +674,13 @@ export const Admin: React.FC = () => {
<div key={log.id} className="relative pl-8"> <div key={log.id} className="relative pl-8">
<div className={`absolute left-0 top-1.5 w-6 h-6 rounded-full border-4 border-white shadow-sm z-10 bg-purple-500`} /> <div className={`absolute left-0 top-1.5 w-6 h-6 rounded-full border-4 border-white shadow-sm z-10 bg-purple-500`} />
<div className="text-black"> <div className="text-black">
<p className="text-xs font-bold text-gray-900">{log.email_type}</p> <div className="flex justify-between items-start">
<p className="text-[10px] font-black text-purple-400 uppercase tracking-widest mt-0.5">{new Date(log.sent_at).toLocaleString('hu-HU')}</p> <p className="text-xs font-bold text-gray-900">{log.email_type}</p>
<p className="text-[10px] font-black text-purple-400 uppercase tracking-widest">{new Date(log.sent_at).toLocaleString('hu-HU')}</p>
</div>
{log.body && (
<div className="mt-2 p-3 bg-white border border-purple-100 rounded-xl text-[10px] text-gray-600 max-h-48 overflow-y-auto font-medium" dangerouslySetInnerHTML={{ __html: log.body }} />
)}
</div> </div>
</div> </div>
)) ))
@@ -557,7 +726,7 @@ export const Admin: React.FC = () => {
disabled={savingDemoUrl} disabled={savingDemoUrl}
className="bg-blue-600 hover:bg-blue-700 text-white font-black px-6 py-4 rounded-2xl text-xs uppercase tracking-widest transition-all disabled:opacity-50 shadow-lg shadow-blue-200" className="bg-blue-600 hover:bg-blue-700 text-white font-black px-6 py-4 rounded-2xl text-xs uppercase tracking-widest transition-all disabled:opacity-50 shadow-lg shadow-blue-200"
> >
{savingDemoUrl ? 'MENTÉS...' : 'PUBLIKÁLÁS ÉS VISSZAJELZÉS KÉRÉSE'} {savingDemoUrl ? 'MENTÉS...' : 'PUBLIKÁLÁS ÉS ÉRTESÍTÉS'}
</button> </button>
</div> </div>
</section> </section>

View File

@@ -3,7 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
import { supabase, isSupabaseConfigured } from '../../lib/supabaseClient'; import { supabase, isSupabaseConfigured } from '../../lib/supabaseClient';
import { Button } from '../../components/Button'; import { Button } from '../../components/Button';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { LogIn, AlertCircle, ArrowLeft, RefreshCw, Mail, ShieldCheck } from 'lucide-react'; import { LogIn, AlertCircle, ArrowLeft, RefreshCw, Mail } from 'lucide-react';
export const Login: React.FC = () => { export const Login: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -46,20 +46,22 @@ export const Login: React.FC = () => {
if (error) { if (error) {
console.error('Login error:', error); console.error('Login error:', error);
// Specifikus hibaüzenetek kezelése
if (error.message.includes('Invalid login credentials')) { if (error.message.includes('Invalid login credentials')) {
setError('Helytelen e-mail cím vagy jelszó. Kérjük, győződjön meg róla, hogy megerősítette az e-mail címét!'); setError('Helytelen e-mail cím vagy jelszó. Ha most regisztráltál, ellenőrizd a postafiókodat és kattints a megerősítő linkre!');
setNeedsConfirmation(true); setNeedsConfirmation(true);
} else if (error.message.includes('Email not confirmed')) { } else if (error.message.includes('Email not confirmed')) {
setError('Az e-mail cím még nincs megerősítve.'); setError('Az e-mail címed még nincs megerősítve. Kérjük, kattints a regisztrációkor kapott linkre!');
setNeedsConfirmation(true); setNeedsConfirmation(true);
} else { } else {
setError('Hiba történt a bejelentkezés során: ' + error.message); setError('Hiba történt: ' + error.message);
} }
} else { } else {
navigate('/dashboard'); navigate('/dashboard');
} }
} catch (err: any) { } catch (err: any) {
setError('Váratlan hiba történt.'); setError('Váratlan hiba történt a bejelentkezés során.');
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -86,9 +88,10 @@ export const Login: React.FC = () => {
setError('Hiba az újraküldéskor: ' + error.message); setError('Hiba az újraküldéskor: ' + error.message);
} else { } else {
setResendSuccess(true); setResendSuccess(true);
setNeedsConfirmation(false);
} }
} catch (err: any) { } catch (err: any) {
setError('Hiba történt.'); setError('Hiba történt az e-mail újraküldésekor.');
} finally { } finally {
setResendLoading(false); setResendLoading(false);
} }
@@ -99,22 +102,22 @@ export const Login: React.FC = () => {
<div className="w-full max-w-md"> <div className="w-full max-w-md">
<div className="text-center mb-8"> <div className="text-center mb-8">
<h2 className="text-3xl font-extrabold text-gray-900">Bejelentkezés</h2> <h2 className="text-3xl font-extrabold text-gray-900">Bejelentkezés</h2>
<p className="mt-2 text-sm text-gray-600 font-medium">Jelentkezzen be fiókjába a folytatáshoz.</p> <p className="mt-2 text-sm text-gray-600 font-medium">Lépjen be fiókjába a kezeléshez.</p>
</div> </div>
<div className="bg-white py-8 px-4 shadow-xl rounded-[24px] sm:px-10 border border-gray-100"> <div className="bg-white py-8 px-4 shadow-xl rounded-[32px] sm:px-10 border border-gray-100">
<form className="space-y-6" onSubmit={handleLogin}> <form className="space-y-6" onSubmit={handleLogin}>
{error && ( {error && (
<div className="rounded-lg bg-red-50 p-4 border border-red-100 flex flex-col items-start"> <div className="rounded-2xl bg-red-50 p-4 border border-red-100 flex flex-col animate-fade-in">
<div className="flex items-start"> <div className="flex items-start">
<div className="flex-shrink-0"><AlertCircle className="h-5 w-5 text-red-400" /></div> <div className="flex-shrink-0"><AlertCircle className="h-5 w-5 text-red-400" /></div>
<div className="ml-3"><h3 className="text-sm font-medium text-red-800">{error}</h3></div> <div className="ml-3"><p className="text-xs font-bold text-red-800 uppercase tracking-tight">{error}</p></div>
</div> </div>
{needsConfirmation && ( {needsConfirmation && (
<div className="mt-4 w-full p-4 bg-white/80 rounded-xl border border-red-200"> <div className="mt-3 pt-3 border-t border-red-100 w-full">
<button type="button" onClick={handleResendConfirmation} disabled={resendLoading} className="text-xs font-bold text-primary hover:text-primary-dark underline flex items-center"> <button type="button" onClick={handleResendConfirmation} disabled={resendLoading} className="text-xs font-black text-primary hover:underline flex items-center gap-2 uppercase tracking-widest">
{resendLoading ? <RefreshCw className="w-3 h-3 animate-spin mr-1" /> : <Mail className="w-3 h-3 mr-1" />} {resendLoading ? <RefreshCw className="w-3 h-3 animate-spin" /> : <Mail className="w-3 h-3" />}
Megerősítő e-mail újraküldése Megerősítő levél újraküldése
</button> </button>
</div> </div>
)} )}
@@ -122,47 +125,43 @@ export const Login: React.FC = () => {
)} )}
{resendSuccess && ( {resendSuccess && (
<div className="rounded-lg bg-green-50 p-4 border border-green-100 flex items-start animate-fade-in"> <div className="rounded-2xl bg-green-50 p-4 border border-green-100 flex items-start animate-fade-in shadow-sm">
<div className="flex-shrink-0"><Mail className="h-5 w-5 text-green-400" /></div> <div className="flex-shrink-0"><Mail className="h-5 w-5 text-green-400" /></div>
<div className="ml-3"><p className="text-sm font-medium text-green-800">A megerősítő e-mailt újraküldtük!</p></div> <div className="ml-3"><p className="text-xs font-bold text-green-800 uppercase tracking-tight">A megerősítő linket újra elküldtük!</p></div>
</div> </div>
)} )}
<div> <div className="space-y-4">
<label htmlFor="email" className="block text-sm font-medium text-gray-700">E-mail cím</label> <div>
<div className="mt-1"> <label htmlFor="email" className="block text-xs font-black text-gray-400 uppercase tracking-widest mb-1">E-mail cím</label>
<input id="email" type="email" required value={email} onChange={(e) => setEmail(e.target.value)} className="appearance-none block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-primary focus:border-primary sm:text-sm bg-white text-gray-900" /> <input id="email" type="email" required value={email} onChange={(e) => setEmail(e.target.value)} className="appearance-none block w-full px-4 py-3 border border-gray-200 rounded-xl shadow-sm focus:outline-none focus:ring-4 focus:ring-primary/10 focus:border-primary sm:text-sm bg-white text-gray-900 font-medium" placeholder="pelda@email.hu" />
</div>
<div>
<div className="flex justify-between items-center mb-1">
<label htmlFor="password" className="block text-xs font-black text-gray-400 uppercase tracking-widest">Jelszó</label>
<Link to="/auth/forgot-password" size="sm" className="text-[10px] font-black text-primary hover:underline uppercase tracking-widest">Elfelejtette?</Link>
</div>
<input id="password" type="password" required value={password} onChange={(e) => setPassword(e.target.value)} className="appearance-none block w-full px-4 py-3 border border-gray-200 rounded-xl shadow-sm focus:outline-none focus:ring-4 focus:ring-primary/10 focus:border-primary sm:text-sm bg-white text-gray-900 font-medium" placeholder="••••••••" />
</div> </div>
</div> </div>
<div> <div>
<div className="flex justify-between items-center mb-1"> <Button type="submit" fullWidth disabled={loading} size="lg" className="shadow-lg shadow-primary/20">
<label htmlFor="password" className="block text-sm font-medium text-gray-700">Jelszó</label>
<Link to="/auth/forgot-password" size="sm" className="text-xs font-bold text-primary hover:underline">Elfelejtette?</Link>
</div>
<div className="mt-1">
<input id="password" type="password" required value={password} onChange={(e) => setPassword(e.target.value)} className="appearance-none block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-primary focus:border-primary sm:text-sm bg-white text-gray-900" />
</div>
</div>
<div>
<Button type="submit" fullWidth disabled={loading} className="flex justify-center items-center">
{loading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>Bejelentkezés <LogIn className="ml-2 w-4 h-4" /></>} {loading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>Bejelentkezés <LogIn className="ml-2 w-4 h-4" /></>}
</Button> </Button>
</div> </div>
</form> </form>
<div className="mt-6"> <div className="mt-8 pt-6 border-t border-gray-100 text-center">
<div className="relative"><div className="absolute inset-0 flex items-center"><div className="w-full border-t border-gray-300" /></div><div className="relative flex justify-center text-sm"><span className="px-2 bg-white text-gray-500">Nincs még fiókod?</span></div></div> <p className="text-sm text-gray-500 font-medium mb-2">Nincs még fiókod?</p>
<div className="mt-6 text-center"> <Link to="/auth/register" className="text-sm font-bold text-primary hover:text-secondary transition-colors underline-offset-4 hover:underline">Regisztrálj ingyenesen</Link>
<Link to="/auth/register" className="font-medium text-primary hover:text-secondary transition-colors underline-offset-4 hover:underline">Regisztrálj ingyenesen</Link>
</div>
</div> </div>
</div> </div>
<div className="mt-8 text-center"> <div className="mt-8 text-center">
<Link to="/" className="text-sm text-gray-500 hover:text-gray-900 flex items-center justify-center group"> <Link to="/" className="text-sm font-bold text-gray-400 hover:text-gray-900 flex items-center justify-center group transition-colors">
<ArrowLeft className="w-4 h-4 mr-1 group-hover:-translate-x-1 transition-transform" /> Vissza a főoldalra <ArrowLeft className="w-4 h-4 mr-2 group-hover:-translate-x-1 transition-transform" /> Vissza a főoldalra
</Link> </Link>
</div> </div>
</div> </div>