Bläddra i källkod

feat: Implement email submission for Step 1 and Step 3

- Implemented email submission logic in Step 3 CTA section
- Implemented full contact form submission (name/email/industry) in Step 1
- Updated 'Get API Key' to 'Submit Application' in Step 3
- Added form validation and loading/success/error states
- Reused /api/send-welcome-email endpoint for both forms
bob 4 månader sedan
förälder
incheckning
32a7001a44
4 ändrade filer med 167 tillägg och 17 borttagningar
  1. 1 1
      src/locales/en.json
  2. 1 1
      src/locales/zh.json
  3. 105 12
      src/pages/Step1.tsx
  4. 60 3
      src/pages/Step3.tsx

+ 1 - 1
src/locales/en.json

@@ -322,7 +322,7 @@
         "cta_title": "Ready to Start?",
         "cta_desc": "Whether you are an indie dev, studio, or real estate developer, find your compute solution here.",
         "cta_input_placeholder": "Enter your email",
-        "cta_btn": "Get API Key",
+        "cta_btn": "Submit Application",
         "footer_rights": "© 2026 Pure Play Infrastructure. All Systems Operational."
     }
 }

+ 1 - 1
src/locales/zh.json

@@ -321,7 +321,7 @@
         "cta_title": "准备好开始了吗?",
         "cta_desc": "无论你是独立开发者、工作室还是地产开发商,都能在这里找到适合你的算力方案。",
         "cta_input_placeholder": "输入你的邮箱",
-        "cta_btn": "获取 API Key",
+        "cta_btn": "提交申请",
         "footer_rights": "© 2026 纯粹的玩基础设施. All Systems Operational."
     }
 }

+ 105 - 12
src/pages/Step1.tsx

@@ -38,6 +38,72 @@ const Step1 = () => {
 
     const currentLang = i18n.language === 'zh' ? 'zh' : 'en';
 
+    // Form State
+    const [formData, setFormData] = useState({
+        name: '',
+        contact: '',
+        industry: ''
+    });
+    const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
+    const [message, setMessage] = useState('');
+
+    const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
+        const { name, value } = e.target;
+        setFormData(prev => ({ ...prev, [name]: value }));
+    };
+
+    const handleSubmit = async (e: React.FormEvent) => {
+        e.preventDefault();
+
+        // Basic validation
+        if (!formData.contact) {
+            setStatus('error');
+            setMessage(currentLang === 'zh' ? '请填写联系方式' : 'Please enter your contact info');
+            return;
+        }
+
+        // Email validation (assuming contact is email for the API)
+        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+        if (!emailRegex.test(formData.contact)) {
+            setStatus('error');
+            setMessage(currentLang === 'zh' ? '请输入有效的邮箱地址' : 'Please enter a valid email');
+            return;
+        }
+
+        setStatus('loading');
+        setMessage('');
+
+        try {
+            // We verify email format, and pass other fields as extra data
+            const payload = {
+                email: formData.contact,
+                name: formData.name,
+                industry: formData.industry,
+                source: 'Step1_Consulting'
+            };
+
+            const response = await fetch('/api/send-welcome-email', {
+                method: 'POST',
+                headers: { 'Content-Type': 'application/json' },
+                body: JSON.stringify(payload),
+            });
+
+            const data = await response.json();
+
+            if (data.success) {
+                setStatus('success');
+                setMessage(data.message || (currentLang === 'zh' ? '提交成功!我们会尽快联系您。' : 'Submitted successfully! We will contact you soon.'));
+                setFormData({ name: '', contact: '', industry: '' });
+            } else {
+                setStatus('error');
+                setMessage(data.message || (currentLang === 'zh' ? '提交失败,请稍后重试。' : 'Submission failed. Please try again later.'));
+            }
+        } catch (error) {
+            setStatus('error');
+            setMessage(currentLang === 'zh' ? '网络错误,请稍后重试。' : 'Network error. Please try again later.');
+        }
+    };
+
     // Theme utility classes
     const themeClasses = {
         bg: isDark ? 'bg-stone-900' : 'bg-white',
@@ -319,36 +385,63 @@ const Step1 = () => {
                         </p>
                     </div>
 
-                    <div className={`border p-8 md:p-12 rounded-sm text-left ${themeClasses.borderStrong} ${themeClasses.cardBg} ${themeClasses.shadow}`}>
+                    <div
+                        className={`border p-8 md:p-12 rounded-sm text-left ${themeClasses.borderStrong} ${themeClasses.cardBg} ${themeClasses.shadow}`}>
                         <h3 className="font-bold text-lg mb-6 flex items-center gap-2">
                             <MessageSquare className="w-5 h-5" />
                             {t('step1.contact_form_title')}
                         </h3>
-                        <form className="space-y-4">
+                        <form className="space-y-4" onSubmit={handleSubmit}>
                             <div>
                                 <label className={`block text-xs font-bold uppercase mb-1 ${themeClasses.textLight}`}>{t('step1.form.name')}</label>
-                                <input type="text"
-                                    className={`w-full border-b py-2 focus:outline-none transition bg-transparent ${themeClasses.inputBorder}`}
-                                    placeholder={t('step1.form.placeholders.name')} />
+                                <input
+                                    type="text"
+                                    name="name"
+                                    value={formData.name}
+                                    onChange={handleInputChange}
+                                    disabled={status === 'loading' || status === 'success'}
+                                    className={`w-full border-b py-2 focus:outline-none transition bg-transparent ${themeClasses.inputBorder} disabled:opacity-50`}
+                                    placeholder={t('step1.form.placeholders.name')}
+                                />
                             </div>
                             <div>
                                 <label className={`block text-xs font-bold uppercase mb-1 ${themeClasses.textLight}`}>{t('step1.form.contact')}</label>
-                                <input type="text"
-                                    className={`w-full border-b py-2 focus:outline-none transition bg-transparent ${themeClasses.inputBorder}`}
-                                    placeholder={t('step1.form.placeholders.contact')} />
+                                <input
+                                    type="email" // Suggest email input
+                                    name="contact"
+                                    value={formData.contact}
+                                    onChange={handleInputChange}
+                                    disabled={status === 'loading' || status === 'success'}
+                                    className={`w-full border-b py-2 focus:outline-none transition bg-transparent ${themeClasses.inputBorder} disabled:opacity-50`}
+                                    placeholder={t('step1.form.placeholders.contact')}
+                                />
                             </div>
                             <div>
                                 <label className={`block text-xs font-bold uppercase mb-1 ${themeClasses.textLight}`}>{t('step1.form.industry')}</label>
                                 <select
-                                    className={`w-full border-b py-2 focus:outline-none transition bg-transparent ${themeClasses.inputBorder} ${isDark ? 'text-stone-300' : 'text-slate-700'}`}>
+                                    name="industry"
+                                    value={formData.industry}
+                                    onChange={handleInputChange}
+                                    disabled={status === 'loading' || status === 'success'}
+                                    className={`w-full border-b py-2 focus:outline-none transition bg-transparent ${themeClasses.inputBorder} ${isDark ? 'text-stone-300' : 'text-slate-700'} disabled:opacity-50`}>
+                                    <option value="" disabled>{currentLang === 'zh' ? '请选择' : 'Select...'}</option>
                                     {(t('step1.form.options', { returnObjects: true }) as string[]).map((opt, i) => (
-                                        <option key={i} className="text-black">{opt}</option>
+                                        <option key={i} value={opt} className="text-black">{opt}</option>
                                     ))}
                                 </select>
                             </div>
+
+                            {message && (
+                                <div className={`text-sm font-medium ${status === 'success' ? 'text-green-500' : 'text-red-500'}`}>
+                                    {message}
+                                </div>
+                            )}
+
                             <button
-                                className={`w-full py-4 mt-8 font-medium transition rounded-sm ${themeClasses.buttonPrimary}`}>
-                                {t('step1.form.btn_submit')}
+                                type="submit"
+                                disabled={status === 'loading' || status === 'success'}
+                                className={`w-full py-4 mt-8 font-medium transition rounded-sm ${themeClasses.buttonPrimary} disabled:opacity-50 disabled:cursor-not-allowed`}>
+                                {status === 'loading' ? (currentLang === 'zh' ? '提交中...' : 'Submitting...') : t('step1.form.btn_submit')}
                             </button>
                         </form>
                     </div>

+ 60 - 3
src/pages/Step3.tsx

@@ -77,6 +77,49 @@ const Step3 = () => {
         selection: isDark ? 'selection:bg-[#00f0ff] selection:text-black' : 'selection:bg-cyan-200 selection:text-cyan-900',
     };
 
+    // Email Submission Logic
+    const [email, setEmail] = useState('');
+    const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
+    const [message, setMessage] = useState('');
+
+    const handleEmailSubmit = async () => {
+        if (!email) return;
+
+        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+        if (!emailRegex.test(email)) {
+            setStatus('error');
+            setMessage(currentLang === 'zh' ? '请输入有效的邮箱地址' : 'Please enter a valid email address');
+            return;
+        }
+
+        setStatus('loading');
+        setMessage('');
+
+        try {
+            const response = await fetch('/api/send-welcome-email', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json',
+                },
+                body: JSON.stringify({ email }),
+            });
+
+            const data = await response.json();
+
+            if (data.success) {
+                setStatus('success');
+                setMessage(data.message || (currentLang === 'zh' ? '发送成功!我们将尽快与您联系。' : 'Success! We will contact you soon.'));
+                setEmail('');
+            } else {
+                setStatus('error');
+                setMessage(data.message || (currentLang === 'zh' ? '发送失败,请稍后重试。' : 'Failed to send. Please try again later.'));
+            }
+        } catch (error) {
+            setStatus('error');
+            setMessage(currentLang === 'zh' ? '网络错误,请稍后重试。' : 'Network error. Please try again later.');
+        }
+    };
+
     return (
         <div className={`step3-page min-h-screen ${theme.bg} ${theme.text} ${theme.selection} font-sans transition-colors duration-300`}>
             <SharedHeader
@@ -235,14 +278,28 @@ const Step3 = () => {
                     <div className="flex flex-col sm:flex-row max-w-md mx-auto gap-4">
                         <input
                             type="email"
+                            value={email}
+                            onChange={(e) => setEmail(e.target.value)}
+                            disabled={status === 'loading' || status === 'success'}
                             placeholder={t('step3.cta_input_placeholder')}
-                            className={`flex-1 ${theme.inputBg} border ${theme.borderLight} rounded px-4 py-3 ${theme.text} focus:outline-none focus:border-[#00f0ff] transition-colors`}
+                            className={`flex-1 ${theme.inputBg} border ${theme.borderLight} rounded px-4 py-3 ${theme.text} focus:outline-none focus:border-[#00f0ff] transition-colors disabled:opacity-50`}
                         />
-                        <button className="px-6 py-3 bg-[#00f0ff] text-black font-bold rounded hover:bg-[#00ccee] transition-colors whitespace-nowrap">
-                            {t('step3.cta_btn')}
+                        <button
+                            onClick={handleEmailSubmit}
+                            disabled={status === 'loading' || status === 'success'}
+                            className="px-6 py-3 bg-[#00f0ff] text-black font-bold rounded hover:bg-[#00ccee] transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed"
+                        >
+                            {status === 'loading' ? (currentLang === 'zh' ? '发送中...' : 'Sending...') : t('step3.cta_btn')}
                         </button>
                     </div>
 
+                    {/* Status Message */}
+                    {message && (
+                        <div className={`mt-4 text-sm font-medium ${status === 'success' ? 'text-green-500' : 'text-red-500'}`}>
+                            {message}
+                        </div>
+                    )}
+
                     <div className={`mt-24 pt-8 border-t ${theme.border} text-sm ${theme.textLight} font-mono`}>
                         {t('step3.footer_rights')}
                     </div>