diff --git a/public/MakarSankranti.jpg b/public/MakarSankranti.jpg new file mode 100644 index 0000000..9903328 Binary files /dev/null and b/public/MakarSankranti.jpg differ diff --git a/public/makarsankranti.jpeg b/public/makarsankranti.jpeg new file mode 100644 index 0000000..afa75bf Binary files /dev/null and b/public/makarsankranti.jpeg differ diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index c68a75a..aea4a98 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -1,23 +1,27 @@ -import {db} from "@/lib/drizzle"; -import {otps, users} from "@/lib/schema"; -import {signupSchema} from "@/lib/zod"; -import {hash} from "bcryptjs"; -import {NextRequest, NextResponse} from "next/server"; -import {z} from "zod"; - -import otpEmailTemplate from "@/lib/templates/otp-template"; -import mailer from "@/lib/mailer"; - - -// `onboarding@uk-culture.org` email is for development only +import {z} from "zod"; // Zod is a validation library for handling structured data validation +import {hash} from "bcryptjs"; // bcryptjs is used for securely hashing passwords +import {db} from "@/lib/drizzle"; // This imports the database handler from Drizzle ORM +import mailer from "@/lib/mailer"; // This imports the mailer function for sending emails +import {signupSchema} from "@/lib/zod"; // A Zod schema for validating incoming signup data +import {otps, users} from "@/lib/schema"; // Imports the OTP and users tables from the database schema +import {NextRequest, NextResponse} from "next/server"; // These are Next.js server functions for handling HTTP requests and responses +import otpEmailTemplate from "@/lib/templates/otp-template"; // A template for the OTP email + +// Define the sender email address for OTP verification emails. +// This is used in the development environment as a fallback if the environment variable is not set. const senderEmail = process.env.SENDER_EMAIL || "onboarding@uk-culture.org"; - +// POST request handler for user registration export async function POST(req: NextRequest) { try { + // Parse the incoming request JSON to extract the user data const payload = await req.json(); + // Validate the request data using the predefined Zod schema + // This checks that the data conforms to the expected types and structure const {email, name, password, username, alerts} = signupSchema.parse(payload) + + // Basic validation to ensure required fields are provided if (!email || !name || !password || !username) { return NextResponse.json( {error: "name, username, email and password are required."}, @@ -25,60 +29,78 @@ export async function POST(req: NextRequest) { ); } + // Check if a user with the same username already exists in the database const userWithUsername = await db.query.users.findFirst({ where: (users, {eq}) => eq(users.username, username), }); + + // If the username already exists, return an error if (userWithUsername) throw new Error("User with username already exists."); + // Check if a user with the same email already exists const userWithEmail = await db.query.users.findFirst({ where: (users, {eq}) => eq(users.email, email), }); + // If the email already exists, return an error if (userWithEmail) throw new Error("User with email already exists."); + // If no duplicates, hash the password for secure storage const hashedPassword = await hash(password, 10); + // Insert the new user data into the 'users' table in the database await db.insert(users).values({ + username, email, name, password: hashedPassword, - alerts, - username + alerts // 'alerts' is a user setting, likely indicating whether the user wants notifications }); + // Generate a 6-digit OTP (One Time Password) for email verification + const otp = Math.floor(100000 + Math.random() * 900000).toString(); - const otp = Math.floor(100000 + Math.random() * 900000).toString(); // 6-digit OTP - // Set expiration time (e.g., 10 minutes from now) + // Set an expiration time for the OTP (10 minutes from now) const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes in the future + // Fetch the user from the database by email to associate the OTP with the correct user const user = await db.query.users.findFirst({ where: (users, {eq}) => eq(users.email, email), - }) + }); + // Insert the OTP and its expiration time into the 'otps' table await db.insert(otps).values({ expiresAt, otp, - userId: user!.id, - createdAt: new Date(), + userId: user!.id, // Use the user's ID from the fetched record + createdAt: new Date(), // Set the current time as the creation time of the OTP }); - + // Send an email to the user with the OTP for email verification await mailer.sendMail({ - from: `Uttarakhand Culture <${senderEmail}>`, - to: [email], - subject: 'Verify you email with OTP', - html: otpEmailTemplate(name, otp), - }) - + from: `Uttarakhand Culture <${senderEmail}>`, // From address (e.g., a sender email) + to: [email], // Recipient email + subject: 'Verify you email with OTP', // Email subject + html: otpEmailTemplate(name, otp), // The email body generated using the OTP template + }); + // Return a success response indicating the user has been registered return NextResponse.json({ message: `Registered. Now login!`, }); + } catch (error: any) { + // Handle errors that occur during the registration process + + // If the error is a Zod validation error, return the validation error details if (error instanceof z.ZodError) { return NextResponse.json({error: error.errors}, {status: 500}); } + + // Log unexpected errors to the console for debugging purposes console.log("[REGISTER_ERROR]: ", error); + + // Return a generic error message return NextResponse.json({error: error.message}, {status: 500}); } -} \ No newline at end of file +} diff --git a/src/app/api/contact/route.ts b/src/app/api/contact/route.ts new file mode 100644 index 0000000..cb03d3b --- /dev/null +++ b/src/app/api/contact/route.ts @@ -0,0 +1,43 @@ +import { NextResponse } from 'next/server'; +import mailer from '@/lib/mailer'; + +export async function POST(request: Request) { + try { + const { name, email, reason, message } = await request.json(); + + if (!name || !email || !reason || !message) { + return NextResponse.json({ error: 'All fields are required.' }, { status: 400 }); + } + + // Send the email using Nodemailer + await mailer.sendMail({ + from: process.env.SENDER_EMAIL, + to: process.env.CONTACT_FORM_RECEIVER_EMAIL, + subject: `Contact Form: ${reason}`, + html: ` +
+

You Have a New Contact Form Submission

+

Name: ${name}

+

Email: ${email}

+

Reason: ${reason}

+

Message:

+
+ ${message} +
+
+

This is an automated email. Please do not reply to this email.

+
+ `, + }); + + return NextResponse.json({ message: 'Email sent successfully.' }, { status: 200 }); + } catch (error) { + console.error('Error sending email:', error); + return NextResponse.json({ error: 'Failed to send email.' }, { status: 500 }); + } +} + +// Handle other HTTP methods +export async function GET() { + return NextResponse.json({ error: 'Method Not Allowed' }, { status: 405 }); +} diff --git a/src/app/auth/page.module.css b/src/app/auth/page.module.css index d055102..ef5efe2 100644 --- a/src/app/auth/page.module.css +++ b/src/app/auth/page.module.css @@ -8,9 +8,7 @@ } .container { - width: 100%; /* Make the container take full available width */ - max-width: 500px; /* Limit the container width to 500px */ - /*min-width: 300px; !* Ensure it doesn't go below 300px *!*/ + width: 500px; /* Limit the container width to 500px */ margin: 0 auto; /* Center it horizontally */ padding: 40px 30px; /* Add padding */ background-color: #fff; /* Soft white background */ @@ -150,7 +148,10 @@ /* Responsive design: Stack elements on small screens */ @media (max-width: 480px) { + .container { + max-width: 500px; + width: 100%; padding: 20px; } diff --git a/src/app/contactform/ContactForm.module.css b/src/app/contactform/ContactForm.module.css new file mode 100644 index 0000000..d787f83 --- /dev/null +++ b/src/app/contactform/ContactForm.module.css @@ -0,0 +1,132 @@ +/* Container styles */ +.container { + display: flex; + justify-content: center; + align-items: center; + height: 85vh; + box-sizing: border-box; + padding: 20px; +} + +/* Card styles */ +.card { + background: #ffffff; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 500px; + min-width: 300px; + padding: 20px; + text-align: center; + overflow: hidden; + transition: transform 0.3s ease-in-out; +} + +.card:hover { + transform: translateY(-10px); +} + +/* Title styles */ +.title { + font-size: 26px; + font-weight: 600; + color: #333333; + margin-bottom: 10px; +} + +/* Subtitle styles */ +.subtitle { + font-size: 16px; + color: #666666; + line-height: 1.6; +} + +.lastsubtitle{ + font-size: 16px; + color: #666666; + line-height: 1.6; + margin-bottom: 20px; +} + +/* Form styles */ +.form { + display: flex; + flex-direction: column; + gap: 20px; +} + +/* Input group styles */ +.inputGroup { + margin-bottom: 15px; +} + +/* Label styles */ +.label { + font-size: 15px; + font-weight: 500; + color: #333333; + text-align: left; + margin-bottom: 8px; + display: block; +} + +/* Input field styles */ +.input, +.textarea { + width: 100%; + padding: 12px; + font-size: 15px; + border: 1px solid #cccccc; + border-radius: 6px; + outline: none; + transition: border-color 0.3s ease; +} + +.input:focus, +.textarea:focus { + border-color: #0070f3; +} + +/* Textarea styles */ +.textarea { + height: auto; + min-height: 120px; + resize: vertical; + transition: height 0.2s ease; + +} + +/* Button styles */ +.button { + padding: 12px 24px; + background-color: #333333; + color: #ffffff; + border: none; + border-radius: 6px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.button:hover { + background-color: #444444; +} + +/* Status message styles */ +.status { + margin-top: 20px; + font-size: 15px; + color: #0070f3; + text-align: center; +} + +/* Responsive styles */ +@media (max-width: 768px) { + .container { + padding: 10px; + } + + .card { + padding: 15px; + } +} diff --git a/src/app/contactform/page.tsx b/src/app/contactform/page.tsx new file mode 100644 index 0000000..7c65b03 --- /dev/null +++ b/src/app/contactform/page.tsx @@ -0,0 +1,127 @@ +"use client"; +import { useState, ChangeEvent, FormEvent } from 'react'; +import styles from './ContactForm.module.css'; +import {toast} from "sonner"; + +interface FormData { + name: string; + email: string; + reason: string; + message: string; +} + +export default function ContactForm() { + const [formData, setFormData] = useState({ + name: '', + email: '', + reason: '', + message: '', + }); + + const [status, setStatus] = useState(''); + + const handleChange = (e: ChangeEvent) => { + const { name, value } = e.target; + setFormData({ ...formData, [name]: value }); + }; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + + try { + const res = await fetch('/api/contact', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(formData), + }); + + if (res.ok) { + toast.success("Thank you for contacting us! Your message has been sent."); + setFormData({ name: '', email: '', reason: '', message: '' }); + } else { + toast.error("There was a problem sending the message. Please try again"); + } + } catch (error) { + toast.error("An unexpected error occurred. Please try again."); + } + }; + + return ( +
+
+

Contact Form

+

Please complete the form below to get in touch with us.

+

We’re here to help! ❤️

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + {status &&

{status}

} +
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index a1001ab..00e702a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -13,6 +13,11 @@ import PahadiWomen from "/public/bhotiaWoman.webp" import Faq from "@/components/Faq"; import Proverbs from "@/components/ui/Proverbs"; import {getUpcomingFestival} from "@/utils/festivals"; +import MakarSankranti from "/public/MakarSankranti.jpg"; +import Map from "@/components/ui/map"; +import MapModal from "@/components/ui/MapModal"; +// import MakarSankranti from "/public/makarsankranti.jpeg" + export default function Home() { const festival = getUpcomingFestival(); @@ -116,10 +121,10 @@ export default function Home() { @@ -161,6 +166,9 @@ export default function Home() { {/* FAQ */} + + {/**/} + ); } \ No newline at end of file diff --git a/src/components/Faq.tsx b/src/components/Faq.tsx index 5e4de1c..ed3acaa 100644 --- a/src/components/Faq.tsx +++ b/src/components/Faq.tsx @@ -2,6 +2,8 @@ import React, {useState} from "react"; import Image from "next/image"; import styles from "../components/ui/Faq.module.css"; import Chat from "/public/chat.svg"; +import Link from "next/link"; +import Page from "@/app/contactform/page"; const Faq: React.FC = () => { const [faqs, setFaqs] = useState([ @@ -65,7 +67,9 @@ const Faq: React.FC = () => { {"FAQ

Still have questions?

- + + + ); }; diff --git a/src/components/ui/Faq.module.css b/src/components/ui/Faq.module.css index 7525b46..744966d 100644 --- a/src/components/ui/Faq.module.css +++ b/src/components/ui/Faq.module.css @@ -6,7 +6,6 @@ margin-top: 5rem; user-select: none; /* Prevent text selection for the entire container */ - } .faqContainer h1 { @@ -104,12 +103,15 @@ overflow: hidden; transition: all 0.2s ease-in-out; text-align: left; + line-height: 20px; + color: var(--slate-black); + font-size: 18px; } .faq.open .faqAnswer { opacity: 1; max-height: 500px; - margin-top: 10px; + margin-top: 1.5rem; } .contactButton { diff --git a/src/components/ui/file.txt b/src/components/ui/file.txt new file mode 100644 index 0000000..482fc67 --- /dev/null +++ b/src/components/ui/file.txt @@ -0,0 +1,242 @@ + + + + + +
+
+
+

Explore The Unexplored

+

A mystical land of mountains and mythologies, exquisite landscapes and exhilarating adventure, and wellness and yoga, Uttarakhand has something to offer every traveller. Popularly known as Devbhoomi, or the land of gods, the state is framed by the Himalayas and divided into two main regions, Garhwal and Kumaon. While Uttarakhand has several well-known destinations like Nainital, Mussoorie, Corbett National Park and Auli, and pilgrimage sites like Kedarnath, Badrinath, Rishikesh and Haridwar, explore the 13 lesser-known destinations from the state's 13 districts.

+
+
+ Garhwal Region + Kumaon Region +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 52 Shakti PeethTheme Park + + Tehri Lake + + +
+
\ No newline at end of file diff --git a/src/components/ui/map.jsx b/src/components/ui/map.jsx new file mode 100644 index 0000000..ddb9a4e --- /dev/null +++ b/src/components/ui/map.jsx @@ -0,0 +1,187 @@ +import React from 'react'; +import "./map.module.css"; + +const Map = () => { + return ( +
+ +
+
+

Explore The Unexplored

+

A mystical land of mountains and mythologies, exquisite landscapes and exhilarating adventure, + and wellness and yoga, Uttarakhand has something to offer every traveller. Popularly known as + Devbhoomi, or the land of gods, the state is framed by the Himalayas and divided into two main + regions, Garhwal and Kumaon. While Uttarakhand has several well-known destinations like + Nainital, Mussoorie, Corbett National Park and Auli, and pilgrimage sites like Kedarnath, + Badrinath, Rishikesh and Haridwar, explore the 13 lesser-known destinations from the states 13 + districts.

+
+
+ Garhwal Region + Kumaon Region +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 52 Shakti Peeth + + Theme Park + + + Tehri Lake + + + +
+
+ ); +}; + +export default Map; \ No newline at end of file diff --git a/src/components/ui/map.module.css b/src/components/ui/map.module.css new file mode 100644 index 0000000..62d567d --- /dev/null +++ b/src/components/ui/map.module.css @@ -0,0 +1,23 @@ +.about-region { + max-width: 500px; + text-align: center; + margin: 0 auto; + background: rgba(0, 0, 0, 0.5); + color: #fff; + border-radius: 5px; +} + +.region-seprator { + text-align: center; + padding-top: 30px; +} + +.gh, +.ku { + font-size: 18px; + font-weight: bold; +} + +.regions:hover { + fill: #FF9900; +} diff --git a/src/components/ui/sectionCard.module.css b/src/components/ui/sectionCard.module.css index 743f21b..28b502d 100644 --- a/src/components/ui/sectionCard.module.css +++ b/src/components/ui/sectionCard.module.css @@ -78,6 +78,7 @@ } .linkContainer1 { + } .linkContainer2 { diff --git a/src/routes.ts b/src/routes.ts index 670c899..3125af0 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -3,7 +3,7 @@ * These routes do not require authentication * @type {string[]} */ -export const publicRoutes: string[] = ["/", "/explore", "/language", "/trekking", "/map", "/about"]; +export const publicRoutes: string[] = ["/", "/explore", "/language", "/trekking", "/map", "/about", "/contactform"]; /** * An array of routes that are used for authentication diff --git a/src/utils/festivals.ts b/src/utils/festivals.ts index 5be84a4..7ef6d89 100644 --- a/src/utils/festivals.ts +++ b/src/utils/festivals.ts @@ -3,26 +3,49 @@ // 1. 'name' - a string that stores the name of the festival. // 2. 'date' - a string that represents the date of the festival (in DD-MM format). import GheeSankranti from "/public/Ghee_Sankranti.jpeg"; +import Ghughutiya from "/public/MakarSankranti.jpg"; +import {StaticImageData} from "next/image"; + + +const currentDate = new Date(); +currentDate.getFullYear(); export interface Festival { - name: string; // The name of the festival, e.g., "Harela". - date: string; // The date of the festival in DD-MM format, e.g., "16-07". + date: string; // The date of the festival in DD-MM format, e.g., "16-07". + title: string; // The name of the festival, e.g., "Harela". + subTitle: { + date: string; + location: string; + } description: string; + image?: StaticImageData; location?: string; - image?: string; } // Define a constant 'festivals', which is an array of objects, each object conforming // to the 'Festival' interface. This array contains details about several Uttarakhand festivals. export const festivals: Festival[] = [ - {name:"Makar Sankranti", date:"14-01",description:"It marks the transition of the sun into Capricorn (Makar Rashi) and the start of longer days. In Uttarakhand, it is celebrated with great joy and enthusiasm, especially in the hill areas. People gather in open fields to fly kites, symbolizing the victory of light over darkness. Bonfires are lit, and people prepare traditional sweets like tilgul (made of sesame and jaggery) to share with friends and family. The festival also has agricultural importance, marking the end of winter and the beginning of the harvest season. Devotees take a holy dip in the Ganges or other rivers, believing it cleanses their sins. It’s a time for prayers for a good harvest, prosperity, and happiness in the coming year. People also worship the Sun God for health and well-being."}, - {name: "Ghee Sankranti", date: "17-02", description:" "}, - {name: "Phool Dei", date: "14-03", description:" "}, - {name: "Bikhauti", date: "14-04", description:" "}, - {name: "Harela", date: "16-07", description:" "}, - {name: "Nanda Devi Raj Jat", date: "05-09", description:" "}, - {name: "Kauthig", date: "22-02", description:" "}, - {name: "Egaas Bhagwal", date: "12-11", description:"Egaas Bagwal is a unique festival celebrated 11 days after Diwali in the hilly regions of Uttarakhand. According to local belief, Lord Rama returned from exile late to these areas, which is why people here celebrate Egaas with great enthusiasm. The festival involves preparing traditional delicacies, performing joyful folk dances, and lighting up homes, much like Diwali. A standout tradition is spinning a flaming rope called \"Bhailo\", where villagers twirl a fire-lit rope, creating a mesmerizing display symbolizing light’s triumph over darkness. Egaas Bagwal reflects Uttarakhand’s rich cultural heritage, offering a glimpse into the community’s deep-rooted traditions and festive spirit. For the people of Uttarakhand, it’s not just a festival, but a meaningful tribute to their ancestors and the vibrant life of the hills."}, + // {name:"Ghughutiya", date:"14-01", displaydate:`14 JANUARY ${currentDate.getFullYear()}`,description:"It marks the transition of the sun into Capricorn (Makar Rashi) and the start of longer days. In Uttarakhand, it is celebrated with great joy and enthusiasm, especially in the hill areas. People gather in open fields to fly kites, symbolizing the victory of light over darkness. Bonfires are lit, and people prepare traditional sweets like tilgul (made of sesame and jaggery) to share with friends and family. The festival also has agricultural importance, marking the end of winter and the beginning of the harvest season. Devotees take a holy dip in the Ganges or other rivers, believing it cleanses their sins. It’s a time for prayers for a good harvest, prosperity, and happiness in the coming year. People also worship the Sun God for health and well-being."}, + // {name: "Ghee Sankranti", date: "17-02", description:" ", subTitle:{date:"", location:"" } }, + // {name: "Phool Dei", date: "14-03", description:" "}, + // {name: "Bikhauti", date: "14-04", description:" "}, + // {name: "Harela", date: "16-07", description:" "}, + // {name: "Nanda Devi Raj Jat", date: "05-09", description:" "}, + // {name: "Kauthig", date: "22-02", description:" "}, + // {name: "Egaas Bhagwal", date: "12-11", description:"Egaas Bagwal is a unique festival celebrated 11 days after Diwali in the hilly regions of Uttarakhand. According to local belief, Lord Rama returned from exile late to these areas, which is why people here celebrate Egaas with great enthusiasm. The festival involves preparing traditional delicacies, performing joyful folk dances, and lighting up homes, much like Diwali. A standout tradition is spinning a flaming rope called \"Bhailo\", where villagers twirl a fire-lit rope, creating a mesmerizing display symbolizing light’s triumph over darkness. Egaas Bagwal reflects Uttarakhand’s rich cultural heritage, offering a glimpse into the community’s deep-rooted traditions and festive spirit. For the people of Uttarakhand, it’s not just a festival, but a meaningful tribute to their ancestors and the vibrant life of the hills."}, + + { + date: "14-01", + title: "Ghughutiya", + subTitle: { + date:`14 JANUARY ${currentDate.getFullYear()}` , + location:"UTTARAKHAND" + }, + description: "It marks the transition of the sun into Capricorn (Makar Rashi) and the start of longer days. In Uttarakhand, it is celebrated with great joy and enthusiasm, especially in the hill areas. People gather in open fields to fly kites, symbolizing the victory of light over darkness. Bonfires are lit, and people prepare traditional sweets like tilgul (made of sesame and jaggery) to share with friends and family. The festival also has agricultural importance, marking the end of winter and the beginning of the harvest season. Devotees take a holy dip in the Ganges or other rivers, believing it cleanses their sins. It’s a time for prayers for a good harvest, prosperity, and happiness in the coming year. People also worship the Sun God for health and well-being." , + image: Ghughutiya + + } + ]; // Define a function 'getUpcomingFestival' which returns the festival happening today or the next upcoming festival,