From 8c129c2a21a5b45b330575918c4436ed910ae338 Mon Sep 17 00:00:00 2001 From: Sawan Date: Fri, 8 Nov 2024 19:33:19 +0530 Subject: [PATCH 1/5] Added ai supported chatbot --- src/App.tsx | 2 ++ src/components/AIChatbot.tsx | 33 +++++++++++++++++++++++++++++++++ src/components/BackToTop.tsx | 2 +- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/components/AIChatbot.tsx diff --git a/src/App.tsx b/src/App.tsx index 6c63c65..7435de8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -43,6 +43,7 @@ import Statistic from './components/StatisticDetails'; import GalleryDetailsPage from './components/GalleryDetailsPage'; import SpinnerDetailsPage from './components/SpinnerDetailsPage'; import ProductCardDetailsPage from './components/ProductCardDetailsPage'; +import AiChatbot from './components/AIChatbot'; const ThemeToggle: React.FC = () => { const [theme, setTheme] = useState(localStorage.getItem('theme') || 'light'); @@ -74,6 +75,7 @@ const App: React.FC = () => { return (
+ {/* */} {/* Add the ScrollProgressBar component here */} diff --git a/src/components/AIChatbot.tsx b/src/components/AIChatbot.tsx new file mode 100644 index 0000000..3dcacbc --- /dev/null +++ b/src/components/AIChatbot.tsx @@ -0,0 +1,33 @@ +import React, { useEffect } from 'react'; + +const AiChatbot: React.FC = () => { + useEffect(() => { + const script = document.createElement('script'); + script.src = 'https://www.chatbase.co/embed.min.js'; + script.defer = true; + script.async = true; + script.setAttribute('chatbotId', 'NwunJFmeijfG8mzeXqdPw'); + script.setAttribute('domain', 'www.chatbase.co'); + + const chatbotConfig = document.createElement('script'); + chatbotConfig.innerHTML = ` + window.embeddedChatbotConfig = { + chatbotId: "NwunJFmeijfG8mzeXqdPw", + domain: "www.chatbase.co" + }; + `; + + document.body.appendChild(chatbotConfig); + document.body.appendChild(script); + + // Clean up the scripts on component unmount + return () => { + document.body.removeChild(chatbotConfig); + document.body.removeChild(script); + }; + }, []); + + return null; // No visual elements needed in this component +}; + +export default AiChatbot; diff --git a/src/components/BackToTop.tsx b/src/components/BackToTop.tsx index b8ad91a..6b4228d 100644 --- a/src/components/BackToTop.tsx +++ b/src/components/BackToTop.tsx @@ -34,7 +34,7 @@ const BackToTopButton: React.FC = () => { {isVisible && ( From 69f000489e39c6e88be0162f75a99b69102cf5cf Mon Sep 17 00:00:00 2001 From: Sawan Date: Fri, 8 Nov 2024 20:09:36 +0530 Subject: [PATCH 2/5] Added contact form with frontend and Backend API --- server/.env.example | 5 + server/.gitignore | 6 + server/app.js | 22 +++ server/controllers/contactController.js | 28 ++++ server/model/contact.js | 34 ++++ server/package.json | 24 +++ server/routes/contactRoutes.js | 8 + server/utils/db.js | 15 ++ server/utils/sendMail.js | 71 +++++++++ src/App.tsx | 2 + src/components/ContactUs.tsx | 203 ++++++++++++++++++++++++ src/components/Header.tsx | 10 ++ 12 files changed, 428 insertions(+) create mode 100644 server/.env.example create mode 100644 server/.gitignore create mode 100644 server/app.js create mode 100644 server/controllers/contactController.js create mode 100644 server/model/contact.js create mode 100644 server/package.json create mode 100644 server/routes/contactRoutes.js create mode 100644 server/utils/db.js create mode 100644 server/utils/sendMail.js create mode 100644 src/components/ContactUs.tsx diff --git a/server/.env.example b/server/.env.example new file mode 100644 index 0000000..ae680ee --- /dev/null +++ b/server/.env.example @@ -0,0 +1,5 @@ +MONGO_URI=mongodb://localhost:27017/finveda +PORT=5000 +EMAIL_ID= +PASS_KEY= +ADMIN_EMAIL_ID= \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..aff6b1d --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,6 @@ +node_modules +./node_modules +.env +package-lock.json +uploads +./uploads \ No newline at end of file diff --git a/server/app.js b/server/app.js new file mode 100644 index 0000000..9b2ee27 --- /dev/null +++ b/server/app.js @@ -0,0 +1,22 @@ +import express from 'express'; +import dotenv from 'dotenv'; +import connectDB from './utils/db.js'; +import cors from 'cors'; +import contactRoutes from './routes/contactRoutes.js'; + +dotenv.config(); +const app = express(); +connectDB(); + +app.use(express.json()); + +// to avoid cross-origin error +app.use(cors()); + +// Serve static files from the uploads directory +app.use('/api/contact', contactRoutes); + +const PORT = process.env.PORT || 5000; +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); +}); diff --git a/server/controllers/contactController.js b/server/controllers/contactController.js new file mode 100644 index 0000000..2076e67 --- /dev/null +++ b/server/controllers/contactController.js @@ -0,0 +1,28 @@ +import Contact from '../model/contact.js'; +import { sendMailToAdmin } from '../utils/sendMail.js'; + +export async function saveContact(req, res) { + try { + const { fullName, phoneNumber, email, message } = req.body; + + if (!fullName || !phoneNumber || !email || !message) { + return res.status(400).json({ message: 'All fields are required.' }); + } + + const newContact = new Contact({ fullName, phoneNumber, email, message }); + sendMailToAdmin(newContact); + + await newContact.save(); + + res + .status(201) + .json({ message: 'Contact form submitted successfully!', newContact }); + } catch (error) { + console.error('Error saving contact form:', error); + res.status(500).json({ message: 'Failed to submit contact form.', error }); + } +} + +export async function getContact(req, res) { + res.send('hello contact'); +} diff --git a/server/model/contact.js b/server/model/contact.js new file mode 100644 index 0000000..9cb2189 --- /dev/null +++ b/server/model/contact.js @@ -0,0 +1,34 @@ +import mongoose from 'mongoose'; + +const contactSchema = new mongoose.Schema({ + fullName: { + type: String, + required: true, + trim: true, + }, + phoneNumber: { + type: String, + required: true, + trim: true, + match: [/^\d{10}$/, 'Please enter a valid 10-digit phone number'], + }, + email: { + type: String, + required: true, + trim: true, + match: [/^\S+@\S+\.\S+$/, 'Please enter a valid email address'], + }, + message: { + type: String, + required: true, + trim: true, + }, + createdAt: { + type: Date, + default: Date.now, + }, +}); + +const Contact = mongoose.model('Contact', contactSchema); + +export default Contact; diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..813bb3b --- /dev/null +++ b/server/package.json @@ -0,0 +1,24 @@ +{ + "name": "server", + "version": "1.0.0", + "main": "app.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "finveda" + ], + "author": "", + "license": "ISC", + "dependencies": { + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.21.1", + "mongoose": "^8.8.0", + "multer": "^1.4.5-lts.1", + "nodemailer": "^6.9.16", + "nodemon": "^3.1.7" + }, + "description": "" +} diff --git a/server/routes/contactRoutes.js b/server/routes/contactRoutes.js new file mode 100644 index 0000000..3140aad --- /dev/null +++ b/server/routes/contactRoutes.js @@ -0,0 +1,8 @@ +import express from 'express'; +const router = express.Router(); +import { getContact, saveContact } from '../controllers/contactController.js'; + +router.post('/saveContact', saveContact); +router.get('/saveContact', getContact); + +export default router; diff --git a/server/utils/db.js b/server/utils/db.js new file mode 100644 index 0000000..c951c34 --- /dev/null +++ b/server/utils/db.js @@ -0,0 +1,15 @@ +import mongoose from 'mongoose'; +import dotenv from 'dotenv'; +dotenv.config(); + +const connectDB = async () => { + try { + await mongoose.connect(process.env.MONGO_URI); + console.log('MongoDB Connected'); + } catch (error) { + console.error('Database connection error:', error); + process.exit(1); + } +}; + +export default connectDB; diff --git a/server/utils/sendMail.js b/server/utils/sendMail.js new file mode 100644 index 0000000..3b5c100 --- /dev/null +++ b/server/utils/sendMail.js @@ -0,0 +1,71 @@ +import nodemailer from 'nodemailer'; +import 'dotenv/config'; + +const sendMailToAdmin = userdata => { + const transporter = nodemailer.createTransport({ + service: 'gmail', + host: 'smtp.gmail.com', + port: 587, + secure: false, // Use `true` for port 465, `false` for all other ports + auth: { + user: process.env.EMAIL_ID, // Email ID to send the mail + pass: process.env.PASS_KEY, // Passkey + }, + }); + + async function main() { + await transporter.sendMail({ + from: { + name: `GLASSYUI Contact Form - ${new Date().toLocaleString()}`, + address: process.env.EMAIL_ID, + }, // sender address + to: process.env.ADMIN_EMAIL_ID, // list of receivers + subject: 'New Contact Form Submission from GLASSYUI ✔', // Subject line + text: 'GLASSYUI Contact Form Submission', // plain text body + html: `
+
+ GLASSYUI Contact Form Submission +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Field + + Value +
Name${userdata.fullName}
Phone${userdata.phoneNumber}
Email${userdata.email}
Message${userdata.message}
Submitted At${new Date().toLocaleString()}
+
`, // html body + }); + } + + main().catch(console.error); +}; + +// Export as a named export +export { sendMailToAdmin }; diff --git a/src/App.tsx b/src/App.tsx index 6c63c65..490273d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -43,6 +43,7 @@ import Statistic from './components/StatisticDetails'; import GalleryDetailsPage from './components/GalleryDetailsPage'; import SpinnerDetailsPage from './components/SpinnerDetailsPage'; import ProductCardDetailsPage from './components/ProductCardDetailsPage'; +import ContactUs from './components/ContactUs'; const ThemeToggle: React.FC = () => { const [theme, setTheme] = useState(localStorage.getItem('theme') || 'light'); @@ -112,6 +113,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> } />
diff --git a/src/components/ContactUs.tsx b/src/components/ContactUs.tsx new file mode 100644 index 0000000..772344b --- /dev/null +++ b/src/components/ContactUs.tsx @@ -0,0 +1,203 @@ +import React, { ChangeEvent, FormEvent } from 'react'; +import { useState } from 'react'; + +const ContactUs = () => { + interface FormData { + fullName: string; + phoneNumber: string; + email: string; + message: string; + } + + const [formData, setFormData] = useState({ + fullName: '', + phoneNumber: '', + email: '', + message: '', + }); + + const handleChange = ( + e: ChangeEvent, + ) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + }; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + + try { + const response = await fetch( + 'http://localhost:5000/api/contact/saveContact', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData), + }, + ); + + if (!response.ok) { + throw new Error('Something went wrong!'); + } + + alert('Message Sent Successfully!'); + setFormData({ fullName: '', phoneNumber: '', email: '', message: '' }); + } catch (error) { + console.error(error); + alert('Failed to send message. Please try again later.'); + } + }; + + return ( +
+ {/* Left-side Information with glassmorphism effect */} +
+
+

+ Welcome to GlassyUI-Components! +

+

+ This open-source library features stunning React components designed + with a captivating glassmorphism effect, perfect for giving your web + applications a modern and sleek design. +

+
    +
  • + Email: contact@glassyui.com +
  • +
  • + Phone: +123-456-7890 +
  • +
  • + Website: www.glassyui.com +
  • +
+
+

✨ Features

+
    +
  • Glassmorphism-themed React components
  • +
  • Customizable styles with SCSS
  • +
  • Beginner-friendly and easy to contribute
  • +
  • Modular and reusable components
  • +
+
+
+
+ + {/* Form Section */} +
+

+ Contact Us +

+ +
+
+ {/* Full Name Input */} +
+ + +
+ {/* Phone Number Input */} +
+ + +
+
+ +
+ {/* Email Input */} + + +
+ + {/* Message Input */} +
+ + +
+ + {/* reCAPTCHA & Submit Button */} +
+
+
+ + +
+
+ + +
+
+
+
+ ); +}; + +export default ContactUs; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index c50ed69..6585ebe 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -63,6 +63,16 @@ const Header: React.FC = () => { About Us +
  • + (e.currentTarget.style.color = '#fde047')} + onMouseLeave={e => (e.currentTarget.style.color = 'white')} + > + Contact Us + +
    • Date: Sat, 9 Nov 2024 13:54:55 +0530 Subject: [PATCH 3/5] fixes the footer issues --- src/App.tsx | 2 +- src/components/Footer.tsx | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6c63c65..1d353fc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -120,6 +120,6 @@ const App: React.FC = () => { }; const ConditionalFooter: React.FC = () => { const location = useLocation(); - return location.pathname === '/' ?