Logo update
This commit is contained in:
268
app/api/contact/route.js
Normal file
268
app/api/contact/route.js
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
// app/api/contact/route.js
|
||||||
|
// ❌ REMOVE "use client" from here - API routes must NOT have this directive
|
||||||
|
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import nodemailer from 'nodemailer';
|
||||||
|
|
||||||
|
// Create transporter with Zoho configuration
|
||||||
|
const createTransport = () => {
|
||||||
|
return nodemailer.createTransport({
|
||||||
|
host: 'smtp.zoho.com',
|
||||||
|
port: 587,
|
||||||
|
secure: false,
|
||||||
|
auth: {
|
||||||
|
user: process.env.ZOHO_EMAIL,
|
||||||
|
pass: process.env.ZOHO_PASSWORD,
|
||||||
|
},
|
||||||
|
debug: true,
|
||||||
|
logger: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle Newsletter Subscription
|
||||||
|
async function handleNewsletter(data) {
|
||||||
|
console.log('Processing newsletter subscription for:', data.email);
|
||||||
|
|
||||||
|
const transporter = createTransport();
|
||||||
|
|
||||||
|
const mailOptions = {
|
||||||
|
from: process.env.ZOHO_EMAIL,
|
||||||
|
to: process.env.RECIPIENT_EMAIL,
|
||||||
|
subject: 'New Newsletter Subscription - Keystone Solutions',
|
||||||
|
html: `
|
||||||
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||||
|
<h2 style="color: #ff3a2d;">New Newsletter Subscription</h2>
|
||||||
|
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
|
||||||
|
<p><strong>Email:</strong> ${data.email}</p>
|
||||||
|
<p><strong>Subscribed at:</strong> ${new Date().toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
<hr style="margin: 20px 0;">
|
||||||
|
<p style="color: #666; font-size: 14px;">
|
||||||
|
This email was sent from your website's newsletter subscription form.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await transporter.sendMail(mailOptions);
|
||||||
|
console.log('Newsletter email sent successfully:', result.messageId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Contact Form
|
||||||
|
async function handleContact(data) {
|
||||||
|
console.log('Processing contact form for:', data.fullName, data.email);
|
||||||
|
|
||||||
|
const transporter = createTransport();
|
||||||
|
|
||||||
|
const mailOptions = {
|
||||||
|
from: process.env.ZOHO_EMAIL,
|
||||||
|
to: process.env.RECIPIENT_EMAIL,
|
||||||
|
subject: 'New Contact Form Submission - Keystone Solutions',
|
||||||
|
html: `
|
||||||
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||||
|
<h2 style="color: #ff3a2d;">New Contact Form Submission</h2>
|
||||||
|
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
|
||||||
|
<p><strong>Full Name:</strong> ${data.fullName}</p>
|
||||||
|
<p><strong>Email:</strong> ${data.email}</p>
|
||||||
|
<p><strong>Phone:</strong> ${data.phone || 'Not provided'}</p>
|
||||||
|
<p><strong>Message:</strong></p>
|
||||||
|
<div style="background: white; padding: 15px; border-left: 4px solid #ff3a2d; margin: 10px 0;">
|
||||||
|
${data.message || 'No message provided'}
|
||||||
|
</div>
|
||||||
|
<p><strong>Submitted at:</strong> ${new Date().toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
<hr style="margin: 20px 0;">
|
||||||
|
<p style="color: #666; font-size: 14px;">
|
||||||
|
This email was sent from your website's contact form.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await transporter.sendMail(mailOptions);
|
||||||
|
console.log('Contact email sent successfully:', result.messageId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Career Form
|
||||||
|
async function handleCareer(data) {
|
||||||
|
console.log('Processing career form for:', data.fullName, data.email);
|
||||||
|
|
||||||
|
const transporter = createTransport();
|
||||||
|
|
||||||
|
const attachments = [];
|
||||||
|
|
||||||
|
// Handle file attachment if present
|
||||||
|
if (data.resume) {
|
||||||
|
console.log('Resume file attached:', data.resume.name);
|
||||||
|
attachments.push({
|
||||||
|
filename: data.resume.name,
|
||||||
|
content: Buffer.from(data.resume.data, 'base64'),
|
||||||
|
contentType: data.resume.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const mailOptions = {
|
||||||
|
from: process.env.ZOHO_EMAIL,
|
||||||
|
to: process.env.RECIPIENT_EMAIL,
|
||||||
|
subject: 'New Career Application - Keystone Solutions',
|
||||||
|
html: `
|
||||||
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||||
|
<h2 style="color: #ff3a2d;">New Career Application</h2>
|
||||||
|
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
|
||||||
|
<p><strong>Full Name:</strong> ${data.fullName}</p>
|
||||||
|
<p><strong>Email:</strong> ${data.email}</p>
|
||||||
|
<p><strong>Phone:</strong> ${data.phone}</p>
|
||||||
|
<p><strong>Roles of Interest:</strong> ${data.roles}</p>
|
||||||
|
<p><strong>Resume:</strong> ${data.resume ? 'Attached' : 'Not provided'}</p>
|
||||||
|
<p><strong>Applied at:</strong> ${new Date().toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
<hr style="margin: 20px 0;">
|
||||||
|
<p style="color: #666; font-size: 14px;">
|
||||||
|
This email was sent from your website's career application form.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
attachments: attachments,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await transporter.sendMail(mailOptions);
|
||||||
|
console.log('Career email sent successfully:', result.messageId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST handler for all form submissions
|
||||||
|
export async function POST(request) {
|
||||||
|
console.log('\n🚀 === EMAIL API ROUTE STARTED ===');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check environment variables first
|
||||||
|
const requiredEnvVars = {
|
||||||
|
ZOHO_EMAIL: process.env.ZOHO_EMAIL,
|
||||||
|
ZOHO_PASSWORD: process.env.ZOHO_PASSWORD,
|
||||||
|
RECIPIENT_EMAIL: process.env.RECIPIENT_EMAIL,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Environment variables check:');
|
||||||
|
Object.entries(requiredEnvVars).forEach(([key, value]) => {
|
||||||
|
console.log(`${key}: ${value ? '✅ Set' : '❌ Missing'}`);
|
||||||
|
});
|
||||||
|
console.log(
|
||||||
|
'Loaded password (first 3 chars):',
|
||||||
|
process.env.ZOHO_PASSWORD?.slice(0, 3)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!requiredEnvVars.ZOHO_EMAIL || !requiredEnvVars.ZOHO_PASSWORD || !requiredEnvVars.RECIPIENT_EMAIL) {
|
||||||
|
console.error('❌ Missing required environment variables');
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Email configuration missing' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse request body
|
||||||
|
let body;
|
||||||
|
try {
|
||||||
|
body = await request.json();
|
||||||
|
console.log('✅ Request body parsed successfully');
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error('❌ Failed to parse request body:', parseError);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Invalid JSON in request body' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type, ...data } = body;
|
||||||
|
console.log(`📝 Form type: ${type}`);
|
||||||
|
console.log(`📋 Form data keys: ${Object.keys(data).join(', ')}`);
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
console.error('❌ No form type specified');
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Form type is required' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route to appropriate handler based on form type
|
||||||
|
let result;
|
||||||
|
switch (type) {
|
||||||
|
case 'newsletter':
|
||||||
|
if (!data.email) {
|
||||||
|
console.error('❌ Newsletter: Email is required');
|
||||||
|
return NextResponse.json({ error: 'Email is required' }, { status: 400 });
|
||||||
|
}
|
||||||
|
result = await handleNewsletter(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'contact':
|
||||||
|
if (!data.fullName || !data.email) {
|
||||||
|
console.error('❌ Contact: Full name and email are required');
|
||||||
|
return NextResponse.json({ error: 'Full name and email are required' }, { status: 400 });
|
||||||
|
}
|
||||||
|
result = await handleContact(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'career':
|
||||||
|
if (!data.fullName || !data.email || !data.phone) {
|
||||||
|
console.error('❌ Career: Full name, email, and phone are required');
|
||||||
|
return NextResponse.json({ error: 'Full name, email, and phone are required' }, { status: 400 });
|
||||||
|
}
|
||||||
|
result = await handleCareer(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.error('❌ Invalid form type:', type);
|
||||||
|
return NextResponse.json({ error: 'Invalid form type' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Email sent successfully!');
|
||||||
|
console.log('📧 Message ID:', result.messageId);
|
||||||
|
console.log('🎉 === EMAIL API ROUTE SUCCESS ===\n');
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
message: 'Email sent successfully',
|
||||||
|
messageId: result.messageId,
|
||||||
|
type: type
|
||||||
|
},
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('\n💥 === EMAIL API ROUTE ERROR ===');
|
||||||
|
console.error('Error name:', error.name);
|
||||||
|
console.error('Error message:', error.message);
|
||||||
|
console.error('Error code:', error.code);
|
||||||
|
console.error('Error stack:', error.stack);
|
||||||
|
|
||||||
|
// Provide more specific error messages
|
||||||
|
let errorMessage = 'Failed to send email';
|
||||||
|
let statusCode = 500;
|
||||||
|
|
||||||
|
if (error.code === 'EAUTH') {
|
||||||
|
errorMessage = 'Authentication failed. Please check your Zoho email and app password.';
|
||||||
|
console.error('🔐 Authentication issue - check ZOHO_EMAIL and ZOHO_PASSWORD');
|
||||||
|
} else if (error.code === 'ECONNECTION' || error.code === 'ETIMEDOUT') {
|
||||||
|
errorMessage = 'Connection to email server failed. Please try again.';
|
||||||
|
console.error('🌐 Network connection issue');
|
||||||
|
} else if (error.code === 'EMESSAGE') {
|
||||||
|
errorMessage = 'Invalid email message format.';
|
||||||
|
statusCode = 400;
|
||||||
|
console.error('📧 Email format issue');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('💥 === END ERROR DETAILS ===\n');
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: errorMessage,
|
||||||
|
details: error.message,
|
||||||
|
code: error.code
|
||||||
|
},
|
||||||
|
{ status: statusCode }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,6 +12,8 @@ export default function Careerform() {
|
|||||||
resume: null
|
resume: null
|
||||||
});
|
});
|
||||||
const [uploadStatus, setUploadStatus] = useState('');
|
const [uploadStatus, setUploadStatus] = useState('');
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [submitStatus, setSubmitStatus] = useState(null);
|
||||||
|
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = (e) => {
|
||||||
const { id, value } = e.target;
|
const { id, value } = e.target;
|
||||||
@ -39,15 +41,25 @@ export default function Careerform() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFormData(prev => ({
|
// Convert file to base64
|
||||||
...prev,
|
const reader = new FileReader();
|
||||||
resume: file
|
reader.onload = () => {
|
||||||
}));
|
const base64 = reader.result.split(',')[1]; // Remove data:mime;base64, prefix
|
||||||
setUploadStatus(`${file.name} uploaded successfully`);
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
resume: {
|
||||||
|
name: file.name,
|
||||||
|
data: base64,
|
||||||
|
type: file.type
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
setUploadStatus(`${file.name} uploaded successfully`);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// Basic form validation
|
// Basic form validation
|
||||||
@ -61,40 +73,47 @@ export default function Careerform() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here you would typically send the form data to your backend
|
setIsSubmitting(true);
|
||||||
console.log('Form submitted:', formData);
|
setSubmitStatus(null);
|
||||||
|
|
||||||
// Example of how you might handle the form submission
|
|
||||||
const submitData = new FormData();
|
|
||||||
submitData.append('fullName', formData.fullName);
|
|
||||||
submitData.append('email', formData.email);
|
|
||||||
submitData.append('phone', formData.phone);
|
|
||||||
submitData.append('roles', formData.roles);
|
|
||||||
submitData.append('resume', formData.resume);
|
|
||||||
|
|
||||||
// Replace with your actual API endpoint
|
try {
|
||||||
// fetch('/api/submit-application', {
|
const response = await fetch('/api/contact', {
|
||||||
// method: 'POST',
|
method: 'POST',
|
||||||
// body: submitData
|
headers: {
|
||||||
// }).then(response => {
|
'Content-Type': 'application/json',
|
||||||
// // Handle response
|
},
|
||||||
// });
|
body: JSON.stringify({
|
||||||
|
type: 'career',
|
||||||
|
...formData,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
alert('Application submitted successfully!');
|
if (response.ok) {
|
||||||
|
setSubmitStatus('success');
|
||||||
// Reset form
|
|
||||||
setFormData({
|
// Reset form
|
||||||
fullName: '',
|
setFormData({
|
||||||
email: '',
|
fullName: '',
|
||||||
phone: '',
|
email: '',
|
||||||
roles: '',
|
phone: '',
|
||||||
resume: null
|
roles: '',
|
||||||
});
|
resume: null
|
||||||
setUploadStatus('');
|
});
|
||||||
|
setUploadStatus('');
|
||||||
// Reset file input
|
|
||||||
const fileInput = document.getElementById('resume-upload');
|
// Reset file input
|
||||||
if (fileInput) fileInput.value = '';
|
const fileInput = document.getElementById('resume-upload');
|
||||||
|
if (fileInput) fileInput.value = '';
|
||||||
|
} else {
|
||||||
|
setSubmitStatus('error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
setSubmitStatus('error');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setTimeout(() => setSubmitStatus(null), 5000);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -120,6 +139,20 @@ export default function Careerform() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
|
{submitStatus && (
|
||||||
|
<div className={`alert mb-3 ${submitStatus === 'success' ? 'alert-success' : 'alert-danger'}`} style={{
|
||||||
|
padding: '10px 15px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
marginBottom: '20px',
|
||||||
|
backgroundColor: submitStatus === 'success' ? '#d4edda' : '#f8d7da',
|
||||||
|
color: submitStatus === 'success' ? '#155724' : '#721c24',
|
||||||
|
border: `1px solid ${submitStatus === 'success' ? '#c3e6cb' : '#f5c6cb'}`
|
||||||
|
}}>
|
||||||
|
{submitStatus === 'success'
|
||||||
|
? 'Application submitted successfully! We will review your application and get back to you soon.'
|
||||||
|
: 'Failed to submit application. Please try again.'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<form className="form-contact" onSubmit={handleSubmit}>
|
<form className="form-contact" onSubmit={handleSubmit}>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label className="mb_15" htmlFor="fullName">
|
<label className="mb_15" htmlFor="fullName">
|
||||||
@ -200,22 +233,22 @@ export default function Careerform() {
|
|||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" className="tf-btn btn-primary2 mt_22">
|
<button
|
||||||
<span>Submit Application</span>
|
type="submit"
|
||||||
|
className="tf-btn btn-primary2 mt_22"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
style={{
|
||||||
|
opacity: isSubmitting ? 0.7 : 1,
|
||||||
|
cursor: isSubmitting ? 'not-allowed' : 'pointer'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{isSubmitting ? 'Submitting...' : 'Submit Application'}</span>
|
||||||
<span className="bg-effect" />
|
<span className="bg-effect" />
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="shape position-absolute">
|
|
||||||
<Image
|
|
||||||
alt="item"
|
|
||||||
src="/images/item/shape-5.png"
|
|
||||||
width={1105}
|
|
||||||
height={720}
|
|
||||||
/>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,63 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
export default function Contact() {
|
export default function Contact() {
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
fullName: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
|
message: ''
|
||||||
|
});
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [submitStatus, setSubmitStatus] = useState(null);
|
||||||
|
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
const { id, value } = e.target;
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
[id]: value
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsSubmitting(true);
|
||||||
|
setSubmitStatus(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/contact', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: 'contact',
|
||||||
|
...formData,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
setSubmitStatus('success');
|
||||||
|
setFormData({
|
||||||
|
fullName: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
|
message: ''
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setSubmitStatus('error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
setSubmitStatus('error');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setTimeout(() => setSubmitStatus(null), 5000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="section-contact style-default position-relative" style={{ paddingLeft: "30px", paddingRight: "30px" }}>
|
<div className="section-contact style-default position-relative" style={{ paddingLeft: "30px", paddingRight: "30px" }}>
|
||||||
<div className="tf-container-2" style={{ padding:"30px", background: "linear-gradient(89.8deg, #ff3a2d -0.43%, #ffa13f 100.84%)", borderRadius: "26px" }}>
|
<div className="tf-container-2" style={{ padding:"30px", background: "linear-gradient(89.8deg, #ff3a2d -0.43%, #ffa13f 100.84%)", borderRadius: "26px" }}>
|
||||||
@ -22,30 +76,37 @@ export default function Contact() {
|
|||||||
Call us for urgent Inquiry
|
Call us for urgent Inquiry
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/* <Link
|
|
||||||
href="https://maps.app.goo.gl/pznAE2GE6p3YUhrE7?g_st=com.google.maps.preview.copy"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="tf-btn btn-primary2 h36"
|
|
||||||
style={{
|
|
||||||
backgroundColor: 'white',
|
|
||||||
color: 'black',
|
|
||||||
border: '1px solid #ddd'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="text-caption" style={{ color: 'black' }}>Get Direction</span>
|
|
||||||
<span className="bg-effect" />
|
|
||||||
</Link> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<form onSubmit={(e) => e.preventDefault()} className="form-contact" style={{ gap: "10px !important" }}>
|
{submitStatus && (
|
||||||
|
<div className={`alert mb-3 ${submitStatus === 'success' ? 'alert-success' : 'alert-danger'}`} style={{
|
||||||
|
padding: '10px 15px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
marginBottom: '20px',
|
||||||
|
backgroundColor: submitStatus === 'success' ? '#d4edda' : '#f8d7da',
|
||||||
|
color: submitStatus === 'success' ? '#155724' : '#721c24',
|
||||||
|
border: `1px solid ${submitStatus === 'success' ? '#c3e6cb' : '#f5c6cb'}`
|
||||||
|
}}>
|
||||||
|
{submitStatus === 'success'
|
||||||
|
? 'Message sent successfully! We will get back to you soon.'
|
||||||
|
: 'Failed to send message. Please try again.'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<form onSubmit={handleSubmit} className="form-contact" style={{ gap: "10px !important" }}>
|
||||||
<fieldset style={{ marginBottom: "5px !important" }}>
|
<fieldset style={{ marginBottom: "5px !important" }}>
|
||||||
<label htmlFor="name" style={{ display: "block", marginBottom: "5px !important", color: "black", fontWeight: "500" }}>
|
<label htmlFor="fullName" style={{ display: "block", marginBottom: "5px !important", color: "black", fontWeight: "500" }}>
|
||||||
Full Name*
|
Full Name*
|
||||||
</label>
|
</label>
|
||||||
<input type="text" placeholder="Full Name" id="name" required />
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Full Name"
|
||||||
|
id="fullName"
|
||||||
|
value={formData.fullName}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div className="grid-2 gap_24" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "5px !important", marginBottom: "5px !important" }}>
|
<div className="grid-2 gap_24" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "5px !important", marginBottom: "5px !important" }}>
|
||||||
<fieldset style={{ marginBottom: "0px !important" }}>
|
<fieldset style={{ marginBottom: "0px !important" }}>
|
||||||
@ -56,6 +117,8 @@ export default function Contact() {
|
|||||||
type="email"
|
type="email"
|
||||||
placeholder="Your email address*"
|
placeholder="Your email address*"
|
||||||
id="email"
|
id="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleInputChange}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@ -68,6 +131,8 @@ export default function Contact() {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Your phone number"
|
placeholder="Your phone number"
|
||||||
id="phone"
|
id="phone"
|
||||||
|
value={formData.phone}
|
||||||
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@ -79,29 +144,26 @@ export default function Contact() {
|
|||||||
className="message"
|
className="message"
|
||||||
placeholder="Write your message here..."
|
placeholder="Write your message here..."
|
||||||
id="message"
|
id="message"
|
||||||
defaultValue={""}
|
value={formData.message}
|
||||||
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<a href="#" className="link attachment text-dark" style={{ marginBottom: "5px !important", display: "inline-block" }}>
|
<button
|
||||||
<i className="icon-paperclip-solid" />
|
type="submit"
|
||||||
Add an attachment
|
className="tf-btn btn-primary2"
|
||||||
</a>
|
disabled={isSubmitting}
|
||||||
<button type="submit" className="tf-btn btn-primary2">
|
style={{
|
||||||
<span>Send Message</span>
|
opacity: isSubmitting ? 0.7 : 1,
|
||||||
|
cursor: isSubmitting ? 'not-allowed' : 'pointer'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{isSubmitting ? 'Sending...' : 'Send Message'}</span>
|
||||||
<span className="bg-effect" />
|
<span className="bg-effect" />
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="shape position-absolute">
|
|
||||||
<Image
|
|
||||||
alt="item"
|
|
||||||
src="/images/item/shape-5.png"
|
|
||||||
width={1105}
|
|
||||||
height={720}
|
|
||||||
/>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import axios from "axios";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
@ -16,30 +15,34 @@ export default function Footer3() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const sendEmail = async (e) => {
|
const sendEmail = async (e) => {
|
||||||
e.preventDefault(); // Prevent default form submission behavior
|
e.preventDefault();
|
||||||
const email = e.target.email.value;
|
const email = e.target.email.value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await fetch('/api/contact', {
|
||||||
"https://express-brevomail.vercel.app/api/contacts",
|
method: 'POST',
|
||||||
{
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: 'newsletter',
|
||||||
email,
|
email,
|
||||||
}
|
}),
|
||||||
);
|
});
|
||||||
|
|
||||||
if ([200, 201].includes(response.status)) {
|
if (response.ok) {
|
||||||
e.target.reset(); // Reset the form
|
e.target.reset();
|
||||||
setSuccess(true); // Set success state
|
setSuccess(true);
|
||||||
handleShowMessage();
|
handleShowMessage();
|
||||||
} else {
|
} else {
|
||||||
setSuccess(false); // Handle unexpected responses
|
setSuccess(false);
|
||||||
handleShowMessage();
|
handleShowMessage();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error.response?.data || "An error occurred");
|
console.error('Error:', error);
|
||||||
setSuccess(false); // Set error state
|
setSuccess(false);
|
||||||
handleShowMessage();
|
handleShowMessage();
|
||||||
e.target.reset(); // Reset the form
|
e.target.reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,13 +66,12 @@ export default function Footer3() {
|
|||||||
heading.addEventListener("click", toggleOpen);
|
heading.addEventListener("click", toggleOpen);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clean up event listeners when the component unmounts
|
|
||||||
return () => {
|
return () => {
|
||||||
headings.forEach((heading) => {
|
headings.forEach((heading) => {
|
||||||
heading.removeEventListener("click", toggleOpen);
|
heading.removeEventListener("click", toggleOpen);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, []); // Empty dependency array means this will run only once on mount
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer id="footer" className="footer style-2" style={{ backgroundColor: '#272727' }}>
|
<footer id="footer" className="footer style-2" style={{ backgroundColor: '#272727' }}>
|
||||||
@ -80,30 +82,19 @@ export default function Footer3() {
|
|||||||
<div className="col-lg-4">
|
<div className="col-lg-4">
|
||||||
<div className="footer-about">
|
<div className="footer-about">
|
||||||
<Link href={`/`} className="footer-logo">
|
<Link href={`/`} className="footer-logo">
|
||||||
<svg
|
<Image
|
||||||
width="139.5"
|
src="/images/logo/keystonesolution_white.png"
|
||||||
|
alt="Keystone Logo"
|
||||||
|
width={100}
|
||||||
height={40}
|
height={40}
|
||||||
viewBox="0 0 140 40"
|
style={{
|
||||||
fill="none"
|
maxWidth: '100%',
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
height: 'auto'
|
||||||
>
|
}}
|
||||||
<path
|
/>
|
||||||
d="M18.2183 15.7033L29.395 34.7289C29.4936 34.8967 29.6741 34.9994 29.8687 34.9985L41.1397 34.9463C41.3308 34.9455 41.5075 34.8448 41.6058 34.6809L47.5 24.8527L35.944 24.8365C35.7518 24.8362 35.5738 24.7349 35.4754 24.5698L23.9713 5.2667C23.8727 5.10131 23.6944 5 23.5019 5L12.3005 5C12.1083 5 11.9302 5.10099 11.8315 5.26594L5.91087 15.1639L0.167756 24.7208C0.0644005 24.8928 0.0636223 25.1076 0.165723 25.2804L5.75222 34.7316C5.85055 34.8979 6.02941 35 6.22264 35L16.7708 35C17.1948 35 17.4573 34.538 17.2402 34.1738L5.91087 15.1639L17.276 15.1639C17.6635 15.1639 18.022 15.3691 18.2183 15.7033Z"
|
|
||||||
fill="white"
|
|
||||||
/>
|
|
||||||
<text
|
|
||||||
x="55"
|
|
||||||
y="30"
|
|
||||||
fontFamily="Arial, sans-serif"
|
|
||||||
fontSize="20"
|
|
||||||
fill="white"
|
|
||||||
>
|
|
||||||
Keystone
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
</Link>
|
</Link>
|
||||||
<div className="footer-info mb_51">
|
<div className="footer-info mb_51">
|
||||||
<a href="#" className="link text-body-2" style={{ color: 'white' }}>
|
<a href="mailto:koushik@keystonesolution.in" className="link text-body-2" style={{ color: 'white' }}>
|
||||||
koushik@keystonesolution.in
|
koushik@keystonesolution.in
|
||||||
</a>
|
</a>
|
||||||
<div className="text-body-2" style={{ color: 'white' }}>
|
<div className="text-body-2" style={{ color: 'white' }}>
|
||||||
@ -143,22 +134,11 @@ export default function Footer3() {
|
|||||||
Contact us
|
Contact us
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li className="text-body-2">
|
<li className="text-body-2">
|
||||||
<Link href={`/career`} className="link footer-menu_item" style={{ color: 'white' }}>
|
<Link href={`/career`} className="link footer-menu_item" style={{ color: 'white' }}>
|
||||||
Careers
|
Careers
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
{/* <li className="text-body-2">
|
|
||||||
<Link
|
|
||||||
href={`/#`}
|
|
||||||
className="link footer-menu_item"
|
|
||||||
style={{ color: 'white' }}
|
|
||||||
>
|
|
||||||
Career
|
|
||||||
</Link>
|
|
||||||
</li> */}
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -191,10 +171,7 @@ export default function Footer3() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<form
|
<form
|
||||||
onSubmit={(e) => {
|
onSubmit={sendEmail}
|
||||||
e.preventDefault();
|
|
||||||
sendEmail(e);
|
|
||||||
}}
|
|
||||||
id="subscribe-form"
|
id="subscribe-form"
|
||||||
className="form-newsletter style-1 subscribe-form mb_18"
|
className="form-newsletter style-1 subscribe-form mb_18"
|
||||||
>
|
>
|
||||||
@ -225,7 +202,7 @@ export default function Footer3() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<i className="icon-envelope-solid" style={{ color: 'white' }} />
|
<i className="icon-envelope-solid" style={{ color: 'black' }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="subscribe-msg" className="subscribe-msg" />
|
<div id="subscribe-msg" className="subscribe-msg" />
|
||||||
@ -248,7 +225,7 @@ export default function Footer3() {
|
|||||||
<div className="tf-container">
|
<div className="tf-container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="wrapper d-flex align-items-center flex-wrap gap_12">
|
<div className="wrapper d-flex align-items-center justify-content-between flex-wrap gap_12">
|
||||||
<p className="text-body-2 m-0" style={{ color: 'white' }}>
|
<p className="text-body-2 m-0" style={{ color: 'white' }}>
|
||||||
© {new Date().getFullYear()} Copyright by{" "}
|
© {new Date().getFullYear()} Copyright by{" "}
|
||||||
<a href="#" className="link-black text-body-3" style={{ color: 'white', fontWeight: '600' }}>
|
<a href="#" className="link-black text-body-3" style={{ color: 'white', fontWeight: '600' }}>
|
||||||
@ -256,6 +233,18 @@ export default function Footer3() {
|
|||||||
</a>{" "}
|
</a>{" "}
|
||||||
. All Rights Reserved.
|
. All Rights Reserved.
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-body-2 m-0" style={{ color: 'rgba(255,255,255,0.7)' }}>
|
||||||
|
Designed by{" "}
|
||||||
|
<a
|
||||||
|
href="https://rootxwire.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="link text-body-3"
|
||||||
|
style={{ color: 'white', fontWeight: '600', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
|
Rootxwire
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -71,30 +71,17 @@ export default function Header4() {
|
|||||||
<div className="header-left d-flex align-items-center">
|
<div className="header-left d-flex align-items-center">
|
||||||
<div className="header-logo" style={{ display: 'flex', alignItems: 'center' }}>
|
<div className="header-logo" style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<Link href={`/`} className="site-logo" style={{ display: 'flex', alignItems: 'center' }}>
|
<Link href={`/`} className="site-logo" style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<svg
|
<Image
|
||||||
width="160"
|
src="/images/logo/keystonesolution.png" // Update this path to match your PNG file location
|
||||||
|
alt="Keystone Logo"
|
||||||
|
width={90}
|
||||||
height={40}
|
height={40}
|
||||||
viewBox="0 0 150 40"
|
priority
|
||||||
fill="none"
|
style={{
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
maxWidth: '100%',
|
||||||
style={{ display: 'block' }}
|
height: 'auto'
|
||||||
>
|
}}
|
||||||
<path
|
/>
|
||||||
d="M18.2183 15.7033L29.395 34.7289C29.4936 34.8967 29.6741 34.9994 29.8687 34.9985L41.1397 34.9463C41.3308 34.9455 41.5075 34.8448 41.6058 34.6809L47.5 24.8527L35.944 24.8365C35.7518 24.8362 35.5738 24.7349 35.4754 24.5698L23.9713 5.2667C23.8727 5.10131 23.6944 5 23.5019 5L12.3005 5C12.1083 5 11.9302 5.10099 11.8315 5.26594L5.91087 15.1639L0.167756 24.7208C0.0644005 24.8928 0.0636223 25.1076 0.165723 25.2804L5.75222 34.7316C5.85055 34.8979 6.02941 35 6.22264 35L16.7708 35C17.1948 35 17.4573 34.538 17.2402 34.1738L5.91087 15.1639L17.276 15.1639C17.6635 15.1639 18.022 15.3691 18.2183 15.7033Z"
|
|
||||||
fill="#28285B"
|
|
||||||
/>
|
|
||||||
<text
|
|
||||||
x="55"
|
|
||||||
y="20"
|
|
||||||
fontFamily="Arial, sans-serif"
|
|
||||||
fontSize="22"
|
|
||||||
fontWeight="600"
|
|
||||||
fill="#121416"
|
|
||||||
dominantBaseline="central"
|
|
||||||
>
|
|
||||||
Keystone
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<nav className="main-menu style-3">
|
<nav className="main-menu style-3">
|
||||||
@ -110,7 +97,7 @@ export default function Header4() {
|
|||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '4px',
|
gap: '6px',
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
color: '#333333',
|
color: '#333333',
|
||||||
transition: 'color 0.3s ease'
|
transition: 'color 0.3s ease'
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import Image from "next/image";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
@ -25,31 +26,17 @@ export default function Mobilemenu() {
|
|||||||
<div className="offcanvas offcanvas-start canvas-mb" id="menu-mobile">
|
<div className="offcanvas offcanvas-start canvas-mb" id="menu-mobile">
|
||||||
<div className="offcanvas-header top-nav-mobile justify-content-between">
|
<div className="offcanvas-header top-nav-mobile justify-content-between">
|
||||||
<Link href={`/`} className="logo">
|
<Link href={`/`} className="logo">
|
||||||
{/* Updated logo to match Header4 SVG logo */}
|
<Image
|
||||||
<svg
|
src="/images/logo/keystonesolution.png" // Update this path to match your PNG file location
|
||||||
width="140"
|
alt="Keystone Logo"
|
||||||
|
width={100}
|
||||||
height={35}
|
height={35}
|
||||||
viewBox="0 0 150 40"
|
priority
|
||||||
fill="none"
|
style={{
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
maxWidth: '100%',
|
||||||
style={{ display: 'block' }}
|
height: 'auto'
|
||||||
>
|
}}
|
||||||
<path
|
/>
|
||||||
d="M18.2183 15.7033L29.395 34.7289C29.4936 34.8967 29.6741 34.9994 29.8687 34.9985L41.1397 34.9463C41.3308 34.9455 41.5075 34.8448 41.6058 34.6809L47.5 24.8527L35.944 24.8365C35.7518 24.8362 35.5738 24.7349 35.4754 24.5698L23.9713 5.2667C23.8727 5.10131 23.6944 5 23.5019 5L12.3005 5C12.1083 5 11.9302 5.10099 11.8315 5.26594L5.91087 15.1639L0.167756 24.7208C0.0644005 24.8928 0.0636223 25.1076 0.165723 25.2804L5.75222 34.7316C5.85055 34.8979 6.02941 35 6.22264 35L16.7708 35C17.1948 35 17.4573 34.538 17.2402 34.1738L5.91087 15.1639L17.276 15.1639C17.6635 15.1639 18.022 15.3691 18.2183 15.7033Z"
|
|
||||||
fill="#28285B"
|
|
||||||
/>
|
|
||||||
<text
|
|
||||||
x="55"
|
|
||||||
y="20"
|
|
||||||
fontFamily="Arial, sans-serif"
|
|
||||||
fontSize="20"
|
|
||||||
fontWeight="600"
|
|
||||||
fill="#121416"
|
|
||||||
dominantBaseline="central"
|
|
||||||
>
|
|
||||||
Keystone
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
</Link>
|
</Link>
|
||||||
<div className="close-menu" data-bs-dismiss="offcanvas">
|
<div className="close-menu" data-bs-dismiss="offcanvas">
|
||||||
<i className="icon-times-solid" />
|
<i className="icon-times-solid" />
|
||||||
|
|||||||
@ -1,9 +1,70 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
export default function Contact() {
|
export default function Contactother() {
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
fullName: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
|
message: ''
|
||||||
|
});
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [submitStatus, setSubmitStatus] = useState(null);
|
||||||
|
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
const { id, value } = e.target;
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
[id]: value
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (!formData.fullName || !formData.email) {
|
||||||
|
alert('Please fill in all required fields');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
|
setSubmitStatus(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/contact', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: 'contact',
|
||||||
|
...formData,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
setSubmitStatus('success');
|
||||||
|
setFormData({
|
||||||
|
fullName: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
|
message: ''
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setSubmitStatus('error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
setSubmitStatus('error');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setTimeout(() => setSubmitStatus(null), 5000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-title style-default -mb_11">
|
<div className="page-title style-default -mb_11">
|
||||||
<div className="section-contact style-default position-relative py-0">
|
<div className="section-contact style-default position-relative py-0">
|
||||||
@ -28,25 +89,41 @@ export default function Contact() {
|
|||||||
<div className="content mb-0 ">
|
<div className="content mb-0 ">
|
||||||
<h6 className="text_primary">+91 90954 50005</h6>
|
<h6 className="text_primary">+91 90954 50005</h6>
|
||||||
<p className="text-body-2 text_primary">
|
<p className="text-body-2 text_primary">
|
||||||
Call us for urgent
|
Call us for urgent inquiry
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
|
{submitStatus && (
|
||||||
|
<div className={`alert mb-3 ${submitStatus === 'success' ? 'alert-success' : 'alert-danger'}`} style={{
|
||||||
|
padding: '10px 15px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
marginBottom: '20px',
|
||||||
|
backgroundColor: submitStatus === 'success' ? '#d4edda' : '#f8d7da',
|
||||||
|
color: submitStatus === 'success' ? '#155724' : '#721c24',
|
||||||
|
border: `1px solid ${submitStatus === 'success' ? '#c3e6cb' : '#f5c6cb'}`
|
||||||
|
}}>
|
||||||
|
{submitStatus === 'success'
|
||||||
|
? 'Message sent successfully! We will get back to you soon.'
|
||||||
|
: 'Failed to send message. Please try again.'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<form
|
<form
|
||||||
className="form-contact"
|
className="form-contact"
|
||||||
onSubmit={(e) => e.preventDefault()}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label className="mb_15" htmlFor="name">
|
<label className="mb_15" htmlFor="fullName">
|
||||||
Full Name*
|
Full Name*
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Full Name"
|
placeholder="Full Name"
|
||||||
id="name"
|
id="fullName"
|
||||||
|
value={formData.fullName}
|
||||||
|
onChange={handleInputChange}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@ -59,19 +136,22 @@ export default function Contact() {
|
|||||||
type="email"
|
type="email"
|
||||||
placeholder="Your email address*"
|
placeholder="Your email address*"
|
||||||
id="email"
|
id="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleInputChange}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label className="mb_15" htmlFor="phone">
|
<label className="mb_15" htmlFor="phone">
|
||||||
Phone Number
|
Phone Number
|
||||||
<span className="text_mono-gray-5">(Optional)</span>
|
<span className="text_mono-gray-5"> (Optional)</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Your phone number"
|
placeholder="Your phone number"
|
||||||
id="phone"
|
id="phone"
|
||||||
required
|
value={formData.phone}
|
||||||
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@ -83,15 +163,20 @@ export default function Contact() {
|
|||||||
className="message"
|
className="message"
|
||||||
placeholder="Write your message here..."
|
placeholder="Write your message here..."
|
||||||
id="message"
|
id="message"
|
||||||
defaultValue={""}
|
value={formData.message}
|
||||||
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<a href="#" className="link attachment">
|
<button
|
||||||
<i className="icon-paperclip-solid" />
|
type="submit"
|
||||||
Add an attachment
|
className="tf-btn btn-primary2 mt_22"
|
||||||
</a>
|
disabled={isSubmitting}
|
||||||
<button type="submit" className="tf-btn btn-primary2 mt_22">
|
style={{
|
||||||
<span>Send Message</span>
|
opacity: isSubmitting ? 0.7 : 1,
|
||||||
|
cursor: isSubmitting ? 'not-allowed' : 'pointer'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{isSubmitting ? 'Sending...' : 'Send Message'}</span>
|
||||||
<span className="bg-effect" />
|
<span className="bg-effect" />
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@ -109,4 +194,4 @@ export default function Contact() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
10
package-lock.json
generated
10
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"drift-zoom": "^1.5.1",
|
"drift-zoom": "^1.5.1",
|
||||||
"gsap": "^3.12.7",
|
"gsap": "^3.12.7",
|
||||||
"next": "15.3.0",
|
"next": "15.3.0",
|
||||||
|
"nodemailer": "^7.0.5",
|
||||||
"odometer": "^0.4.8",
|
"odometer": "^0.4.8",
|
||||||
"photoswipe": "^5.4.4",
|
"photoswipe": "^5.4.4",
|
||||||
"rc-slider": "^11.1.8",
|
"rc-slider": "^11.1.8",
|
||||||
@ -1472,6 +1473,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/nodemailer": {
|
||||||
|
"version": "7.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz",
|
||||||
|
"integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==",
|
||||||
|
"license": "MIT-0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/odometer": {
|
"node_modules/odometer": {
|
||||||
"version": "0.4.8",
|
"version": "0.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/odometer/-/odometer-0.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/odometer/-/odometer-0.4.8.tgz",
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
"drift-zoom": "^1.5.1",
|
"drift-zoom": "^1.5.1",
|
||||||
"gsap": "^3.12.7",
|
"gsap": "^3.12.7",
|
||||||
"next": "15.3.0",
|
"next": "15.3.0",
|
||||||
|
"nodemailer": "^7.0.5",
|
||||||
"odometer": "^0.4.8",
|
"odometer": "^0.4.8",
|
||||||
"photoswipe": "^5.4.4",
|
"photoswipe": "^5.4.4",
|
||||||
"rc-slider": "^11.1.8",
|
"rc-slider": "^11.1.8",
|
||||||
|
|||||||
BIN
public/images/logo/keystonesolution.png
Normal file
BIN
public/images/logo/keystonesolution.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
public/images/logo/keystonesolution_white.png
Normal file
BIN
public/images/logo/keystonesolution_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
Reference in New Issue
Block a user