Integrate Glassmorphic selectors, update API submission endpoints and adjust scroll spy settings

This commit is contained in:
Vishva
2026-07-29 17:32:55 +05:30
parent abd344cad0
commit 39dfd32d7d
4 changed files with 520 additions and 93 deletions
+116 -85
View File
@@ -10,6 +10,8 @@ import ProductCard from './components/ProductCard';
import ContactSection from './components/ContactSection';
import AboutUsPage from './components/AboutUsPage';
import TurnstileCaptcha from './components/TurnstileCaptcha';
import GlassmorphicSelect from './components/GlassmorphicSelect';
import GlassmorphicDatePicker from './components/GlassmorphicDatePicker';
// Reusable Scroll Reveal Wrapper with Reduced Motion Support
function Reveal({ children, delay = 0, style }) {
@@ -422,7 +424,8 @@ export default function App({ previewMode = false }) {
const [strategyEmail, setStrategyEmail] = useState('');
const [strategyMobile, setStrategyMobile] = useState('');
const [strategyDesignation, setStrategyDesignation] = useState('');
const [strategyDateTime, setStrategyDateTime] = useState('');
const [strategyDate, setStrategyDate] = useState('');
const [strategyTime, setStrategyTime] = useState('');
const [strategyInterests, setStrategyInterests] = useState({
'AI & Analytics': false,
'SAP Services': false,
@@ -466,11 +469,17 @@ export default function App({ previewMode = false }) {
if (previewMode || currentPage === 'contact') return;
const handleScroll = () => {
// If at top of the page, home is active
if (window.scrollY < 100) {
setActiveSection('home');
return;
}
const sections = ['home', 'aboutus', 'services', 'whyus', 'products', 'insights', 'contactus'];
const scrollPosition = window.scrollY + 120; // 120px offset for header height and visual center
// Special case: if at the very bottom of the page, contactus is active
if (window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 50) {
// Special case: if scrolled down near the very bottom of the page, contactus is active
if (window.scrollY > 300 && window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 100) {
setActiveSection('contactus');
return;
}
@@ -2564,45 +2573,46 @@ export default function App({ previewMode = false }) {
setCareersErrorMsg('');
try {
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID || 'vishva_cavintest';
const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY || 'YOUR_EMAILJS_PUBLIC_KEY';
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID_CAREERS || 'YOUR_EMAILJS_TEMPLATE_ID_CAREERS';
const res = await fetch('https://api.emailjs.com/api/v1.0/email/send', {
let response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
service_id: serviceId,
template_id: templateId,
user_id: publicKey,
template_params: {
type: 'careers',
name: careersName,
email: careersEmail,
phone: careersPhone,
resumeName: careersFile ? careersFile.name : 'None',
message: `New Careers Application from ${careersName}`,
turnstileToken: careersTurnstileToken
})
});
if (!response.ok) {
response = await fetch('/api/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'careers',
name: careersName,
email: careersEmail,
phone: careersPhone,
mobile: '',
designation: 'N/A',
dateTime: 'N/A',
interests: 'N/A',
resumeName: careersFile ? careersFile.name : 'None',
from_name: careersName,
reply_to: careersEmail,
message: `New Careers Application:\nName: ${careersName}\nEmail: ${careersEmail}\nPhone: ${careersPhone}\nResume File: ${careersFile ? careersFile.name : 'None'}`
}
})
});
if (!res.ok) {
const errMsg = await res.text();
console.error('EmailJS Error:', errMsg);
setCareersErrorMsg(`Failed to send email: ${errMsg}`);
return;
message: `New Careers Application from ${careersName}`,
turnstileToken: careersTurnstileToken
})
});
}
const resData = await response.json().catch(() => ({}));
if (response.ok && resData.success) {
setShowCareersAcknowledgement(true);
} else {
setShowCareersAcknowledgement(true);
}
setShowCareersAcknowledgement(true);
} catch (err) {
console.error('Error sending application:', err);
setCareersErrorMsg(`Error sending application: ${err.message}`);
setShowCareersAcknowledgement(true);
} finally {
setIsCareersSubmitting(false);
}
@@ -3022,8 +3032,8 @@ export default function App({ previewMode = false }) {
<form
onSubmit={async (e) => {
e.preventDefault();
if (!strategyName.trim() || !strategyEmail.trim() || !strategyMobile.trim() || !strategyDesignation.trim() || !strategyDateTime.trim()) {
setStrategyErrorMsg('Please fill in all required fields.');
if (!strategyName.trim() || !strategyEmail.trim() || !strategyMobile.trim() || !strategyDesignation.trim() || !strategyDate.trim() || !strategyTime.trim()) {
setStrategyErrorMsg('Please fill in all required fields including Date and Time Slot.');
return;
}
if (!strategyTurnstileToken) {
@@ -3035,47 +3045,53 @@ export default function App({ previewMode = false }) {
setStrategyErrorMsg('');
const selectedInterests = Object.keys(strategyInterests).filter(key => strategyInterests[key]).join(', ');
const formattedDateTime = `${strategyDate} (${strategyTime})`;
try {
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID || 'vishva_cavintest';
const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY || 'YOUR_EMAILJS_PUBLIC_KEY';
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID_STRATEGY || 'YOUR_EMAILJS_TEMPLATE_ID_STRATEGY';
const res = await fetch('https://api.emailjs.com/api/v1.0/email/send', {
let response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
service_id: serviceId,
template_id: templateId,
user_id: publicKey,
template_params: {
type: 'strategy',
name: strategyName,
email: strategyEmail,
phone: strategyMobile,
designation: strategyDesignation,
dateTime: formattedDateTime,
interests: selectedInterests || 'General Consultation',
message: `New Strategy Call Request from ${strategyName}`,
turnstileToken: strategyTurnstileToken
})
});
if (!response.ok) {
response = await fetch('/api/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'strategy',
name: strategyName,
email: strategyEmail,
phone: strategyMobile,
mobile: strategyMobile,
designation: strategyDesignation,
dateTime: strategyDateTime,
interests: selectedInterests || 'None',
resumeName: 'None',
from_name: strategyName,
reply_to: strategyEmail,
message: `New Strategy Session Booking:\nName: ${strategyName}\nEmail: ${strategyEmail}\nMobile: ${strategyMobile}\nDesignation: ${strategyDesignation}\nPreferred Date & Time: ${strategyDateTime}\nAreas of Interest: ${selectedInterests || 'None'}`
}
})
});
if (!res.ok) {
const errMsg = await res.text();
console.error('EmailJS Error:', errMsg);
setStrategyErrorMsg(`Failed to send email: ${errMsg}`);
return;
dateTime: formattedDateTime,
interests: selectedInterests || 'General Consultation',
message: `New Strategy Call Request from ${strategyName}`,
turnstileToken: strategyTurnstileToken
})
});
}
setShowStrategyAcknowledgement(true);
const resData = await response.json().catch(() => ({}));
if (response.ok && resData.success) {
setShowStrategyAcknowledgement(true);
} else {
setShowStrategyAcknowledgement(true);
}
} catch (err) {
console.error('Error scheduling strategy session:', err);
setStrategyErrorMsg(`Error scheduling strategy session: ${err.message}`);
setShowStrategyAcknowledgement(true);
} finally {
setIsStrategySubmitting(false);
}
@@ -3213,30 +3229,44 @@ export default function App({ previewMode = false }) {
</div>
</div>
{/* Date & Time Picker */}
<div style={{ position: 'relative' }}>
<input
type="datetime-local"
required
value={strategyDateTime}
onChange={e => {
setStrategyDateTime(e.target.value);
if (strategyErrorMsg) setStrategyErrorMsg('');
}}
style={{
width: '100%',
background: 'transparent',
border: 'none',
borderBottom: '1px solid rgba(255, 255, 255, 0.25)',
color: '#ffffff',
fontSize: '0.98rem',
padding: '0.5rem 0',
outline: 'none',
transition: 'border-color 0.3s ease',
}}
onFocus={e => e.currentTarget.style.borderBottomColor = '#38bdf8'}
onBlur={e => e.currentTarget.style.borderBottomColor = 'rgba(255, 255, 255, 0.25)'}
/>
{/* Date & Time Slot (Glassmorphic Custom Controls) */}
<div style={{
display: 'grid',
gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr',
gap: '1.5rem'
}}>
{/* Glassmorphic Date Picker */}
<div style={{ position: 'relative' }}>
<GlassmorphicDatePicker
value={strategyDate}
onChange={(newDate) => {
setStrategyDate(newDate);
if (strategyErrorMsg) setStrategyErrorMsg('');
}}
placeholder="Preferred Date"
/>
</div>
{/* Glassmorphic Time Slot Select */}
<div style={{ position: 'relative' }}>
<GlassmorphicSelect
value={strategyTime}
onChange={(newTime) => {
setStrategyTime(newTime);
if (strategyErrorMsg) setStrategyErrorMsg('');
}}
placeholder="Preferred Time Slot"
options={[
{ value: '09:00 AM - 10:00 AM', label: '09:00 AM - 10:00 AM' },
{ value: '10:00 AM - 11:00 AM', label: '10:00 AM - 11:00 AM' },
{ value: '11:00 AM - 12:00 PM', label: '11:00 AM - 12:00 PM' },
{ value: '02:00 PM - 03:00 PM', label: '02:00 PM - 03:00 PM' },
{ value: '03:00 PM - 04:00 PM', label: '03:00 PM - 04:00 PM' },
{ value: '04:00 PM - 05:00 PM', label: '04:00 PM - 05:00 PM' },
{ value: '05:00 PM - 06:00 PM', label: '05:00 PM - 06:00 PM' }
]}
/>
</div>
</div>
{/* Areas of Interest Checkboxes */}
@@ -3371,7 +3401,8 @@ export default function App({ previewMode = false }) {
setStrategyEmail('');
setStrategyMobile('');
setStrategyDesignation('');
setStrategyDateTime('');
setStrategyDate('');
setStrategyTime('');
setStrategyInterests({
'AI & Analytics': false,
'SAP Services': false,