Skip to content

Commit e9e35e8

Browse files
committed
Configure for GitHub Pages deployment
1 parent 0f2f891 commit e9e35e8

File tree

15 files changed

+268
-143
lines changed

15 files changed

+268
-143
lines changed

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<link rel="icon" type="image/png" href="/src/assets/logo.png" sizes="any" />
5+
<link rel="icon" type="image/png" href="/logo.png" sizes="any" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>LCM - Loop Charging Module | Charging While Driving</title>
88
<meta name="description" content="LCM is a breakthrough in-vehicle charging technology that generates power while you drive. Experience the future of EV charging with our innovative solution." />

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "lcm-website",
33
"private": true,
44
"version": "0.0.0",
5+
"homepage": "https://nanidurga.github.io/Loopchargingmodule/",
56
"type": "module",
67
"scripts": {
78
"predeploy": "npm run build",

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, Suspense, lazy } from 'react';
2-
import { BrowserRouter as Router, Routes, Route, useLocation, useNavigate } from 'react-router-dom';
2+
import { HashRouter as Router, Routes, Route, useLocation, useNavigate } from 'react-router-dom';
33
import { AnimatePresence } from 'framer-motion';
44
import Layout from './components/layout/Layout';
55
import HeroSection from './components/home/HeroSection';

src/components/home/ContactSection.tsx

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React, { useState, useRef, useCallback } from 'react';
1+
import React, { useState, useRef, useCallback, useEffect } from 'react';
22
import { Send, Rocket, Linkedin } from 'lucide-react';
33
import emailjs from '@emailjs/browser';
4-
import { emailConfig } from '../../config';
4+
import { emailConfig, RATE_LIMIT_MS } from '../../config';
55

66
interface FormState {
77
name: string;
@@ -10,6 +10,35 @@ interface FormState {
1010
message: string;
1111
}
1212

13+
interface FormErrors {
14+
name?: string;
15+
email?: string;
16+
message?: string;
17+
}
18+
19+
const validateEmail = (email: string): boolean => {
20+
const re = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
21+
return re.test(email);
22+
};
23+
24+
const validateForm = (formState: FormState): FormErrors => {
25+
const errors: FormErrors = {};
26+
27+
if (formState.name.length < 2) {
28+
errors.name = 'Name must be at least 2 characters long';
29+
}
30+
31+
if (!validateEmail(formState.email)) {
32+
errors.email = 'Please enter a valid email address';
33+
}
34+
35+
if (formState.message.length < 10) {
36+
errors.message = 'Message must be at least 10 characters long';
37+
}
38+
39+
return errors;
40+
};
41+
1342
const ContactSection: React.FC = () => {
1443
const form = useRef<HTMLFormElement>(null);
1544
const [formState, setFormState] = useState<FormState>({
@@ -21,46 +50,62 @@ const ContactSection: React.FC = () => {
2150

2251
const [isSubmitting, setIsSubmitting] = useState(false);
2352
const [isSubmitted, setIsSubmitted] = useState(false);
53+
const [error, setError] = useState<string | null>(null);
54+
const [formErrors, setFormErrors] = useState<FormErrors>({});
55+
const [lastSubmissionTime, setLastSubmissionTime] = useState<number>(0);
2456

2557
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
2658
const { name, value } = e.target;
2759
setFormState(prev => ({ ...prev, [name]: value }));
60+
// Clear errors when user starts typing
61+
setFormErrors(prev => ({ ...prev, [name]: undefined }));
2862
}, []);
2963

30-
const handleSubmit = useCallback((e: React.FormEvent) => {
64+
const handleSubmit = useCallback(async (e: React.FormEvent) => {
3165
e.preventDefault();
66+
setError(null);
67+
68+
// Validate form
69+
const errors = validateForm(formState);
70+
if (Object.keys(errors).length > 0) {
71+
setFormErrors(errors);
72+
return;
73+
}
74+
75+
// Check rate limiting
76+
const now = Date.now();
77+
if (now - lastSubmissionTime < RATE_LIMIT_MS) {
78+
setError(`Please wait ${Math.ceil((RATE_LIMIT_MS - (now - lastSubmissionTime)) / 1000)} seconds before submitting again`);
79+
return;
80+
}
81+
3282
setIsSubmitting(true);
3383

34-
if (form.current) {
35-
emailjs.sendForm(
84+
try {
85+
if (!form.current) throw new Error('Form not found');
86+
87+
await emailjs.sendForm(
3688
emailConfig.serviceId,
3789
emailConfig.templateId,
3890
form.current,
39-
{
40-
publicKey: emailConfig.publicKey,
41-
}
42-
)
43-
.then(
44-
() => {
45-
console.log('SUCCESS!');
46-
setIsSubmitted(true);
47-
setFormState({
48-
name: '',
49-
email: '',
50-
inquiryType: 'demo',
51-
message: ''
52-
});
53-
},
54-
(error) => {
55-
console.log('FAILED...', error.text);
56-
alert('Failed to send message. Please try again or contact us directly.');
57-
},
58-
)
59-
.finally(() => {
60-
setIsSubmitting(false);
91+
emailConfig.publicKey
92+
);
93+
94+
setIsSubmitted(true);
95+
setLastSubmissionTime(Date.now());
96+
setFormState({
97+
name: '',
98+
email: '',
99+
inquiryType: 'demo',
100+
message: ''
61101
});
102+
} catch (error) {
103+
console.error('Form submission failed:', error);
104+
setError('Failed to send message. Please try again or contact us directly.');
105+
} finally {
106+
setIsSubmitting(false);
62107
}
63-
}, [emailConfig]);
108+
}, [formState, lastSubmissionTime]);
64109

65110
const handleRequestFeasibilityStudy = useCallback(() => {
66111
setFormState(prev => ({...prev, inquiryType: 'integration'}));
@@ -117,11 +162,17 @@ const ContactSection: React.FC = () => {
117162
</button>
118163
</div>
119164
) : (
120-
<form ref={form} onSubmit={handleSubmit}>
165+
<form ref={form} onSubmit={handleSubmit} noValidate>
121166
<h3 className="font-display font-semibold text-xl text-white mb-6">
122167
Contact Us
123168
</h3>
124169

170+
{error && (
171+
<div className="bg-red-500/10 border border-red-500/20 rounded-lg p-4 mb-6 text-red-400">
172+
{error}
173+
</div>
174+
)}
175+
125176
<div className="space-y-4">
126177
<div>
127178
<label htmlFor="name" className="block text-white/80 mb-2 text-sm">
@@ -133,10 +184,14 @@ const ContactSection: React.FC = () => {
133184
name="name"
134185
value={formState.name}
135186
onChange={handleChange}
136-
required
137-
className="w-full bg-dark-700 border border-white/10 rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-500/50"
187+
className={`w-full bg-dark-700 border ${
188+
formErrors.name ? 'border-red-500' : 'border-white/10'
189+
} rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-500/50`}
138190
placeholder="Your name"
139191
/>
192+
{formErrors.name && (
193+
<p className="mt-1 text-sm text-red-400">{formErrors.name}</p>
194+
)}
140195
</div>
141196

142197
<div>
@@ -149,10 +204,14 @@ const ContactSection: React.FC = () => {
149204
name="email"
150205
value={formState.email}
151206
onChange={handleChange}
152-
required
153-
className="w-full bg-dark-700 border border-white/10 rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-500/50"
207+
className={`w-full bg-dark-700 border ${
208+
formErrors.email ? 'border-red-500' : 'border-white/10'
209+
} rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-500/50`}
154210
placeholder="[email protected]"
155211
/>
212+
{formErrors.email && (
213+
<p className="mt-1 text-sm text-red-400">{formErrors.email}</p>
214+
)}
156215
</div>
157216

158217
<div>
@@ -164,7 +223,6 @@ const ContactSection: React.FC = () => {
164223
name="inquiryType"
165224
value={formState.inquiryType}
166225
onChange={handleChange}
167-
required
168226
className="w-full bg-dark-700 border border-white/10 rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-500/50"
169227
>
170228
<option value="demo">Book a Demo</option>
@@ -184,11 +242,15 @@ const ContactSection: React.FC = () => {
184242
name="message"
185243
value={formState.message}
186244
onChange={handleChange}
187-
required
188245
rows={4}
189-
className="w-full bg-dark-700 border border-white/10 rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-500/50"
246+
className={`w-full bg-dark-700 border ${
247+
formErrors.message ? 'border-red-500' : 'border-white/10'
248+
} rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-500/50`}
190249
placeholder="Tell us how we can help you..."
191-
></textarea>
250+
/>
251+
{formErrors.message && (
252+
<p className="mt-1 text-sm text-red-400">{formErrors.message}</p>
253+
)}
192254
</div>
193255

194256
<div className="pt-2">

src/components/home/FAQSection.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState } from 'react';
22
import { ChevronDown, ChevronUp } from 'lucide-react';
3+
import { scrollToContact } from '../../utils/navigation';
34

45
interface FAQItemProps {
56
question: string;
@@ -36,6 +37,11 @@ const FAQItem: React.FC<FAQItemProps> = ({ question, answer, isOpen, toggleOpen
3637
const FAQSection: React.FC = () => {
3738
const [openIndex, setOpenIndex] = useState<number | null>(0);
3839

40+
const handleContactClick = (e: React.MouseEvent) => {
41+
e.preventDefault();
42+
scrollToContact('other');
43+
};
44+
3945
const faqs = [
4046
{
4147
question: "How much power does LCM generate?",
@@ -111,6 +117,7 @@ const FAQSection: React.FC = () => {
111117
</p>
112118
<a
113119
href="#contact"
120+
onClick={handleContactClick}
114121
className="inline-block bg-primary-500 hover:bg-primary-600 text-white px-6 py-3 rounded-full font-medium transition-colors"
115122
>
116123
Ask Us Anything

src/components/home/HeroSection.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
import React from 'react';
22
import { ArrowRight, PlugZap, UserPlus } from 'lucide-react';
3+
import L1Image from '../../assets/L1.webp';
4+
import { scrollToContact } from '../../utils/navigation';
35

46
const HeroSection: React.FC = () => {
7+
const handleDemoClick = (e: React.MouseEvent) => {
8+
e.preventDefault();
9+
scrollToContact('demo');
10+
};
11+
12+
const handlePartnershipClick = (e: React.MouseEvent) => {
13+
e.preventDefault();
14+
scrollToContact('partnership');
15+
};
16+
517
return (
618
<section className="relative pt-24 pb-20 md:pt-32 md:pb-24 bg-gradient-to-b from-dark-900 via-dark-800 to-dark-900 overflow-hidden">
719
{/* Background elements */}
@@ -22,12 +34,14 @@ const HeroSection: React.FC = () => {
2234
<div className="flex flex-col sm:flex-row gap-4">
2335
<a
2436
href="#contact"
37+
onClick={handleDemoClick}
2538
className="bg-primary-500 hover:bg-primary-600 text-white px-6 py-3 rounded-full font-medium transition-colors inline-flex items-center justify-center"
2639
>
2740
<PlugZap className="mr-2 h-5 w-5" /> Book a Demo
2841
</a>
2942
<a
3043
href="#contact"
44+
onClick={handlePartnershipClick}
3145
className="bg-dark-700/50 hover:bg-dark-700 backdrop-blur-sm text-white border border-white/20 px-6 py-3 rounded-full font-medium transition-colors inline-flex items-center justify-center"
3246
>
3347
<UserPlus className="mr-2 h-5 w-5" /> Contact for Partnership
@@ -39,9 +53,11 @@ const HeroSection: React.FC = () => {
3953
<div className="absolute inset-0 bg-gradient-to-r from-primary-500/20 to-secondary-500/20 rounded-2xl blur-xl"></div>
4054
<div className="relative bg-dark-800/80 backdrop-blur-sm border border-white/10 rounded-2xl p-6 shadow-xl animate-float">
4155
<img
42-
src="src/assets/L1.webp"
56+
src={L1Image}
4357
alt="LCM Technology Render"
4458
className="w-full h-auto rounded-lg mb-4"
59+
loading="eager"
60+
draggable="false"
4561
/>
4662
<div className="bg-dark-900/50 backdrop-blur-sm rounded-lg p-4 border border-white/10">
4763
<h3 className="font-display font-semibold text-lg text-white mb-2">Loop Charging Module</h3>

src/components/home/IntegrationSection.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { ShieldCheck, Cpu, Cable, Car } from 'lucide-react';
3+
import { scrollToContact } from '../../utils/navigation';
34

45
interface IntegrationFeatureProps {
56
icon: React.ReactNode;
@@ -22,6 +23,11 @@ const IntegrationFeature: React.FC<IntegrationFeatureProps> = ({ icon, title, de
2223
};
2324

2425
const IntegrationSection: React.FC = () => {
26+
const handlePartnershipClick = (e: React.MouseEvent) => {
27+
e.preventDefault();
28+
scrollToContact('partnership');
29+
};
30+
2531
const features = [
2632
{
2733
icon: <Car className="h-5 w-5" />,
@@ -64,6 +70,7 @@ const IntegrationSection: React.FC = () => {
6470
src="https://images.pexels.com/photos/3822843/pexels-photo-3822843.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
6571
alt="EV CAD Diagram with LCM"
6672
className="w-full h-auto"
73+
loading="lazy"
6774
/>
6875
</div>
6976
<div className="bg-dark-800 border border-white/10 rounded-xl p-4">
@@ -114,6 +121,7 @@ const IntegrationSection: React.FC = () => {
114121
</p>
115122
<a
116123
href="#contact"
124+
onClick={handlePartnershipClick}
117125
className="inline-block bg-primary-500 hover:bg-primary-600 text-white px-6 py-3 rounded-full font-medium transition-colors"
118126
>
119127
Explore Partnership Opportunities

0 commit comments

Comments
 (0)