d-branch-2 (#18)

*  refactor: streamline sitemap generation and contact form logic

*  refactor: update sendEmail function to handle JSON data
This commit is contained in:
Denshooter
2025-02-13 12:42:06 +01:00
committed by GitHub
parent 996b1b74f9
commit 2c4842cf1f
4 changed files with 140 additions and 133 deletions

View File

@@ -7,7 +7,12 @@ import dotenv from "dotenv";
dotenv.config(); dotenv.config();
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
const { email, name, message } = await request.json(); const body = (await request.json()) as {
email: string;
name: string;
message: string;
};
const { email, name, message } = body;
const user = process.env.MY_EMAIL ?? ""; const user = process.env.MY_EMAIL ?? "";
const pass = process.env.MY_PASSWORD ?? ""; const pass = process.env.MY_PASSWORD ?? "";
@@ -38,7 +43,7 @@ export async function POST(request: NextRequest) {
from: user, from: user,
to: user, // Ensure this is the correct email address to: user, // Ensure this is the correct email address
subject: `Message from ${name} (${email})`, subject: `Message from ${name} (${email})`,
text: message + `\n\nSent from ${email}`, text: message + "\n\n" + email,
}; };
const sendMailPromise = () => const sendMailPromise = () =>

View File

@@ -17,40 +17,20 @@ interface SitemapRoute {
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "https://dki.one"; const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "https://dki.one";
const generateSitemap = async (): Promise<SitemapRoute[]> => { const generateSitemap = async (): Promise<SitemapRoute[]> => {
// Static pages
const staticRoutes: SitemapRoute[] = [
{ url: `${baseUrl}/`, lastModified: new Date().toISOString() },
{
url: `${baseUrl}/privacy-policy`,
lastModified: new Date().toISOString(),
},
{
url: `${baseUrl}/legal-notice`,
lastModified: new Date().toISOString(),
},
];
try { try {
// Static pages
const staticRoutes: SitemapRoute[] = [
{ url: `${baseUrl}/`, lastModified: new Date().toISOString() },
{
url: `${baseUrl}/privacy-policy`,
lastModified: new Date().toISOString(),
},
{
url: `${baseUrl}/legal-notice`,
lastModified: new Date().toISOString(),
},
];
// Check if running in build environment
if (process.env.IS_BUILD) {
console.log("Running in build mode - using mock data");
const mockProjectsData: ProjectsData = {
posts: [
{ slug: "project-1" },
{ slug: "project-2" },
{ slug: "project-3" },
],
};
const projectRoutes: SitemapRoute[] = mockProjectsData.posts.map(
(project) => ({
url: `${baseUrl}/projects/${project.slug}`,
lastModified: new Date().toISOString(),
}),
);
return [...staticRoutes, ...projectRoutes];
}
// Fetch project data from API
console.log("Fetching project data from API..."); console.log("Fetching project data from API...");
const response = await fetch(`${baseUrl}/api/fetchAllProjects`, { const response = await fetch(`${baseUrl}/api/fetchAllProjects`, {
headers: { "Cache-Control": "no-cache" }, headers: { "Cache-Control": "no-cache" },
@@ -58,13 +38,13 @@ const generateSitemap = async (): Promise<SitemapRoute[]> => {
if (!response.ok) { if (!response.ok) {
console.error(`Failed to fetch projects: ${response.statusText}`); console.error(`Failed to fetch projects: ${response.statusText}`);
return staticRoutes; // Return static pages instead of throwing an error return staticRoutes; // Fallback auf statische Seiten
} }
const projectsData = (await response.json()) as ProjectsData; const projectsData = (await response.json()) as ProjectsData;
console.log("Fetched project data:", projectsData); console.log("Fetched project data:", projectsData);
// Generate dynamic routes for projects // Dynamische Projekt-Routen generieren
const projectRoutes: SitemapRoute[] = projectsData.posts.map((project) => ({ const projectRoutes: SitemapRoute[] = projectsData.posts.map((project) => ({
url: `${baseUrl}/projects/${project.slug}`, url: `${baseUrl}/projects/${project.slug}`,
lastModified: project.updated_at lastModified: project.updated_at
@@ -75,7 +55,7 @@ const generateSitemap = async (): Promise<SitemapRoute[]> => {
return [...staticRoutes, ...projectRoutes]; return [...staticRoutes, ...projectRoutes];
} catch (error) { } catch (error) {
console.error("Failed to generate sitemap:", error); console.error("Failed to generate sitemap:", error);
return staticRoutes; // Return static pages in case of failure return staticRoutes; // Fallback nur auf statische Seiten
} }
}; };

View File

@@ -1,87 +1,106 @@
import React, {useEffect, useState} from "react"; import React, { useEffect, useState } from "react";
import {sendEmail} from "@/app/utils/send-email"; import { sendEmail } from "@/app/utils/send-email";
export type FormData = { export type ContactFormData = {
name: string; name: string;
email: string; email: string;
message: string; message: string;
} };
export default function Contact() { export default function Contact() {
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
const [banner, setBanner] = useState<{ show: boolean, message: string, type: 'success' | 'error' }>({ const [banner, setBanner] = useState<{
show: false, show: boolean;
message: '', message: string;
type: 'success' type: "success" | "error";
}); }>({
show: false,
message: "",
type: "success",
});
useEffect(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {
setIsVisible(true); setIsVisible(true);
}, 350); // Delay to start the animation after Projects }, 350); // Delay to start the animation after Projects
}, []); }, []);
async function onSubmit(e: React.FormEvent<HTMLFormElement>) { async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault(); e.preventDefault();
const form = e.currentTarget;
const data: FormData = { const form = e.currentTarget as HTMLFormElement;
name: (form.elements.namedItem('name') as HTMLInputElement).value, const formData = new FormData(form);
email: (form.elements.namedItem('email') as HTMLInputElement).value,
message: (form.elements.namedItem('message') as HTMLTextAreaElement).value, const data: ContactFormData = {
}; name: formData.get("name") as string,
const response = await sendEmail(data); email: formData.get("email") as string,
if (response.success) { message: formData.get("message") as string,
form.reset(); };
}
setBanner({show: true, message: response.message, type: response.success ? 'success' : 'error'}); // Convert FormData to a plain object
setTimeout(() => { const jsonData = JSON.stringify(data);
setBanner({...banner, show: false});
}, 3000); // Hide banner after 3 seconds const response = await sendEmail(jsonData);
if (response.success) {
form.reset();
} }
return ( setBanner({
<section id="contact" className={`p-10 ${isVisible ? 'animate-fly-in' : 'opacity-0'}`}> show: true,
<h2 className="text-3xl font-bold text-center text-gray-800 dark:text-white"> message: response.message,
Contact Me type: response.success ? "success" : "error",
</h2> });
<div setTimeout(() => {
className="flex flex-col items-center p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl max-w-lg mx-auto mt-6 relative"> setBanner((prev) => ({ ...prev, show: false }));
{banner.show && ( }, 3000);
<div }
className={`absolute top-0 left-0 right-0 text-white text-center py-2 rounded-2xl animate-fade-out ${banner.type === 'success' ? 'bg-green-500' : 'bg-red-500'}`}>
{banner.message} return (
</div> <section
)} id="contact"
<form className="w-full space-y-4" onSubmit={onSubmit}> className={`p-10 ${isVisible ? "animate-fly-in" : "opacity-0"}`}
<input >
type="text" <h2 className="text-3xl font-bold text-center text-gray-800 dark:text-white">
name="name" Contact Me
placeholder="Name" </h2>
className="w-full p-2 border rounded dark:text-white" <div className="flex flex-col items-center p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl max-w-lg mx-auto mt-6 relative">
required {banner.show && (
/> <div
<input className={`absolute top-0 left-0 right-0 text-white text-center py-2 rounded-2xl animate-fade-out ${banner.type === "success" ? "bg-green-500" : "bg-red-500"}`}
type="email" >
name="email" {banner.message}
placeholder="Email" </div>
className="w-full p-2 border rounded dark:text-white" )}
required <form className="w-full space-y-4" onSubmit={onSubmit}>
/> <input
<textarea type="text"
name="message" name="name"
placeholder="Message" placeholder="Name"
className="w-full p-2 border rounded dark:text-white" className="w-full p-2 border rounded dark:text-white"
rows={5} required
required />
></textarea> <input
<button type="email"
type="submit" name="email"
className="w-full p-2 text-white bg-gradient-to-r from-blue-500 to-purple-500 rounded hover:from-blue-600 hover:to-purple-600 transition" placeholder="Email"
> className="w-full p-2 border rounded dark:text-white"
Send required
</button> />
</form> <textarea
</div> name="message"
</section> placeholder="Message"
); className="w-full p-2 border rounded dark:text-white"
rows={5}
required
></textarea>
<button
type="submit"
className="w-full p-2 text-white bg-gradient-to-r from-blue-500 to-purple-500 rounded hover:from-blue-600 hover:to-purple-600 transition"
>
Send
</button>
</form>
</div>
</section>
);
} }

View File

@@ -1,17 +1,20 @@
import {FormData} from "@/app/components/Contact"; export function sendEmail(
data: string,
): Promise<{ success: boolean; message: string }> {
const apiEndpoint = "/api/email";
export function sendEmail(data: FormData): Promise<{ success: boolean, message: string }> { return fetch(apiEndpoint, {
const apiEndpoint = '/api/email'; method: "POST",
headers: {
return fetch(apiEndpoint, { "Content-Type": "application/json",
method: 'POST', },
body: JSON.stringify(data), body: data,
})
.then((res) => res.json())
.then((response) => {
return { success: true, message: response.message };
}) })
.then((res) => res.json()) .catch((err) => {
.then((response) => { return { success: false, message: err.message };
return {success: true, message: response.message}; });
})
.catch((err) => {
return {success: false, message: err.message};
});
} }