Merge pull request #12 from Denshooter/d-branch-2

fix: improve project data handling and sitemap generation
This commit is contained in:
Denshooter
2025-02-12 17:19:16 +01:00
committed by GitHub
10 changed files with 146 additions and 317 deletions

View File

@@ -43,11 +43,16 @@ const ProjectDetails = () => {
useEffect(() => { useEffect(() => {
const projectData = searchParams.get("project"); const projectData = searchParams.get("project");
if (projectData) { if (projectData) {
setProject(JSON.parse(projectData)); setProject(JSON.parse(projectData as string));
// Remove the project data from the URL without reloading the page // Remove the project data from the URL without reloading the page
// @ts-expect-error window is defined
if (typeof window !== "undefined") {
// @ts-expect-error window is defined
const url = new URL(window.location.href); const url = new URL(window.location.href);
url.searchParams.delete("project"); url.searchParams.delete("project");
// @ts-expect-error window is defined
window.history.replaceState({}, "", url.toString()); window.history.replaceState({}, "", url.toString());
}
} else { } else {
// Fetch project data based on slug from URL // Fetch project data based on slug from URL
const slug = params.slug as string; const slug = params.slug as string;
@@ -61,8 +66,8 @@ const ProjectDetails = () => {
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to fetch project data"); throw new Error("Failed to fetch project data");
} }
const projectData = await response.json(); const projectData = (await response.json()) as { posts: Project[] };
setProject(projectData.posts[0]); // Assuming the API returns an array of posts setProject(projectData.posts[0]);
} catch (error) { } catch (error) {
console.error("Failed to fetch project data:", error); console.error("Failed to fetch project data:", error);
} }
@@ -80,7 +85,9 @@ const ProjectDetails = () => {
); );
} }
const featureImageUrl = `/api/fetchImage?url=${encodeURIComponent(project.feature_image)}`; const featureImageUrl = project.feature_image
? `/api/fetchImage?url=${encodeURIComponent(project.feature_image)}`
: "";
return ( return (
<div <div
@@ -88,11 +95,9 @@ const ProjectDetails = () => {
> >
<Header /> <Header />
<div className="flex-grow"> <div className="flex-grow">
{/* Hero Section */} <div className="flex justify-center mt-14 md:mt-28 px-4 md:px-0">
<div className="flex justify-center md:mt-28 px-4 md:px-0"> {featureImageUrl && (
<div className="relative w-full max-w-4xl h-0 pb-[56.25%] rounded-2xl overflow-hidden"> <div className="relative w-full max-w-4xl h-0 pb-[56.25%] rounded-2xl overflow-hidden">
{" "}
{/* 16:9 Aspect Ratio */}
<Image <Image
src={featureImageUrl} src={featureImageUrl}
alt={project.title} alt={project.title}
@@ -102,9 +107,10 @@ const ProjectDetails = () => {
priority={true} priority={true}
/> />
</div> </div>
)}
</div> </div>
<div className="flex items-center justify-center mt-4"> <div className="flex items-center justify-center mt-4">
<h1 className="text-4xl md:text-6xl font-bold text-white"> <h1 className="text-4xl md:text-6xl font-bold text-gray-600">
{project.title} {project.title}
</h1> </h1>
</div> </div>

68
app/api/sitemap/route.tsx Normal file
View File

@@ -0,0 +1,68 @@
import { NextResponse } from "next/server";
interface Project {
slug: string;
}
interface ProjectsData {
posts: Project[];
}
interface SitemapRoute {
url: string;
lastModified: string;
}
const baseUrl = "http://localhost:3000";
const generateSitemap = async (): Promise<SitemapRoute[]> => {
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(),
},
];
// Fetch project data from your API
console.log("Fetching project data from API...");
const response = await fetch(`${baseUrl}/api/fetchAllProjects`);
if (!response.ok) {
throw new Error(
`Failed to fetch projects from Ghost: ${response.statusText}`,
);
}
const projectsData = (await response.json()) as ProjectsData;
console.log("Fetched project data:", projectsData);
// Generate dynamic routes for projects
const projectRoutes: SitemapRoute[] = projectsData.posts.map((project) => ({
url: `${baseUrl}/projects/${project.slug}`,
lastModified: new Date().toISOString(),
}));
return [...staticRoutes, ...projectRoutes];
} catch (error) {
console.error("Error generating sitemap:", error);
throw error;
}
};
export async function GET() {
try {
const sitemap = await generateSitemap();
return NextResponse.json(sitemap);
} catch (error) {
console.error("Failed to generate sitemap:", error);
return NextResponse.json(
{ error: "Failed to generate sitemap" },
{ status: 500 },
);
}
}

View File

@@ -1,29 +0,0 @@
// app/api/stats/route.tsx
import {NextResponse} from "next/server";
const stats = {
views: 0,
projectsViewed: {} as { [key: string]: number },
};
export async function GET() {
return NextResponse.json(stats);
}
export async function POST(request: Request) {
const {type, projectId} = await request.json();
if (type === "page_view") {
stats.views += 1;
}
if (type === "project_view" && projectId) {
if (stats.projectsViewed[projectId]) {
stats.projectsViewed[projectId] += 1;
} else {
stats.projectsViewed[projectId] = 1;
}
}
return NextResponse.json({message: "Stats updated", stats});
}

View File

@@ -14,6 +14,10 @@ interface Project {
meta_description: string; meta_description: string;
} }
interface ProjectsData {
posts: Project[];
}
export default function Projects() { export default function Projects() {
const [projects, setProjects] = useState<Project[]>([]); const [projects, setProjects] = useState<Project[]>([]);
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
@@ -25,7 +29,7 @@ export default function Projects() {
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to fetch projects from Ghost"); throw new Error("Failed to fetch projects from Ghost");
} }
const projectsData = await response.json(); const projectsData = (await response.json()) as ProjectsData;
setProjects(projectsData.posts); setProjects(projectsData.posts);
setTimeout(() => { setTimeout(() => {
@@ -52,7 +56,7 @@ export default function Projects() {
<Link <Link
key={project.id} key={project.id}
href={{ href={{
pathname: `/Projects/${project.slug}`, pathname: `/projects/${project.slug}`,
query: { project: JSON.stringify(project) }, query: { project: JSON.stringify(project) },
}} }}
className="cursor-pointer" className="cursor-pointer"

View File

@@ -1,31 +1,52 @@
import {MetadataRoute} from "next"; import type { MetadataRoute } from "next";
import fs from "fs";
import path from "path";
export default function sitemap(): MetadataRoute.Sitemap { interface SitemapRoute {
const baseUrl = "https://dki.one"; url: string;
lastModified: string;
changeFrequency?:
| "always"
| "hourly"
| "daily"
| "weekly"
| "monthly"
| "yearly"
| "never";
priority?: number;
}
// Static pages export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const staticRoutes = [ const baseUrl = "http://localhost:3000";
{url: `${baseUrl}/`, lastModified: new Date().toISOString()},
{url: `${baseUrl}/privacy-policy`, lastModified: new Date().toISOString()},
];
// Read project markdown files from the public folder // Fetch the sitemap data from the dynamic API route
const projectsDirectory = path.join(process.cwd(), "public/projects"); const response = await fetch(`${baseUrl}/api/sitemap`);
let projectRoutes: { url: string; lastModified: string; }[] = []; if (!response.ok) {
throw new Error(`Failed to fetch sitemap: ${response.statusText}`);
}
const sitemapData = (await response.json()) as SitemapRoute[];
if (fs.existsSync(projectsDirectory)) { return sitemapData.map((route) => {
const projectFiles = fs.readdirSync(projectsDirectory).filter(file => file.endsWith(".md")); let changeFrequency: SitemapRoute["changeFrequency"];
let priority: number;
projectRoutes = projectFiles.map((file) => { if (route.url === `${baseUrl}/`) {
const slug = file.replace(".md", ""); changeFrequency = "weekly";
return { priority = 1.0;
url: `${baseUrl}/projects/${slug}`, } else if (route.url.startsWith(`${baseUrl}/projects`)) {
lastModified: new Date().toISOString(), changeFrequency = "monthly";
}; priority = 0.8;
}); } else if (route.url.startsWith(`${baseUrl}/Blog`)) {
changeFrequency = "weekly";
priority = 0.6;
} else {
changeFrequency = "monthly";
priority = 0.5;
} }
return [...staticRoutes, ...projectRoutes]; return {
url: route.url,
lastModified: route.lastModified,
changeFrequency,
priority,
};
});
} }

View File

@@ -1,89 +0,0 @@
"use client";
import { useState } from "react";
export default function StatsDashboard() {
const [authenticated, setAuthenticated] = useState(false);
const [password, setPassword] = useState("");
const [stats, setStats] = useState<{
views: number;
projectsViewed: Record<string, number>;
} | null>(null);
const [error, setError] = useState("");
const handleLogin = () => {
// Simple password check. Replace with secure authentication.
if (password === "admin123") {
setAuthenticated(true);
fetchStats();
} else {
setError("Incorrect password.");
}
};
const fetchStats = async () => {
try {
const res = await fetch("/api/stats");
const data = await res.json();
setStats(data);
} catch (err) {
console.error(err);
setError("Failed to fetch stats.");
}
};
if (!authenticated) {
return (
<div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-800">
<div className="p-10 bg-white dark:bg-gray-700 rounded shadow-md">
<h2 className="text-2xl font-bold mb-4 text-gray-800 dark:text-white">
Admin Login
</h2>
{error && <p className="text-red-500">{error}</p>}
<input
type="password"
placeholder="Enter Password"
className="w-full p-2 border rounded mb-4 dark:bg-gray-600 dark:border-gray-500 dark:text-white"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button
onClick={handleLogin}
className="w-full p-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Login
</button>
</div>
</div>
);
}
return (
<div className="p-10 bg-gray-100 dark:bg-gray-800 min-h-screen">
<h1 className="text-3xl font-bold text-gray-800 dark:text-white">
Statistics Dashboard
</h1>
{stats ? (
<div className="mt-6">
<p className="text-lg text-gray-700 dark:text-gray-300">
Total Views: {stats.views}
</p>
<h2 className="text-2xl font-semibold mt-4 text-gray-800 dark:text-white">
Project Views:
</h2>
<ul className="list-disc pl-5 mt-2 text-gray-700 dark:text-gray-300">
{Object.entries(stats.projectsViewed).map(([projectId, count]) => (
<li key={projectId}>
Project ID {projectId}: {count} view{count !== 1 ? "s" : ""}
</li>
))}
</ul>
</div>
) : (
<p className="mt-4 text-gray-700 dark:text-gray-300">
Loading statistics...
</p>
)}
</div>
);
}

View File

@@ -1,29 +0,0 @@
---
id: "4"
title: "Project Marie/Dennis"
description: "Ein liebevolles Projekt über die gemeinsame Reise von Marie und Dennis."
---
# Project Marie/Dennis
**Project Marie/Dennis** ist ein Herzensprojekt, das die tiefe Liebe zwischen Marie und Dennis feiert. Es dokumentiert
ihre schönsten gemeinsamen Erinnerungen, besondere Momente und die vielen kleinen Dinge, die ihre Beziehung
so einzigartig machen.
## Unsere Liebesgeschichte
Von den ersten Blicken bis zu den vielen gemeinsamen Abenteuern Marie und Dennis verbindet eine besondere
Geschichte voller Liebe, Vertrauen und Zusammenhalt. In jeder Herausforderung haben sie sich gegenseitig unterstützt,
in jedem glücklichen Moment haben sie gemeinsam gelacht.
## Besondere Erinnerungen
- **Erstes Treffen** Der magische Anfang einer wunderbaren Reise.
- **Gemeinsame Rituale** Vom Adventskalender voller liebevoller Überraschungen bis zu alltäglichen kleinen Gesten.
- **Erfolgreiche Meilensteine** Maries Abschlussarbeit, Dennis persönliche Entwicklungen und gemeinsame
Zukunftspläne.
## Warum dieses Projekt?
Es ist ein digitales Denkmal für eine wunderschöne Liebe ein Ort voller Erinnerungen, voller Emotionen und voller
Dankbarkeit. Dieses Projekt wird wachsen, so wie die Liebe zwischen Marie und Dennis mit jedem Tag stärker wird.

View File

@@ -1,41 +0,0 @@
---
id: "1"
title: "AI-Powered Chatbot"
description: "An intelligent chatbot designed to enhance customer service through natural language processing."
---
# AI-Powered Chatbot
The AI-Powered Chatbot is a sophisticated conversational agent designed to assist businesses in automating customer
interactions. Utilizing **Natural Language Processing (NLP)** and **Machine Learning (ML)**, it understands user queries
and responds appropriately.
## Overview
With the increasing demand for instant customer support, businesses are shifting towards AI-driven solutions to handle
customer interactions more efficiently. The AI-powered chatbot leverages state-of-the-art NLP techniques to process and
interpret customer messages, ensuring accurate and helpful responses. This chatbot is built to learn and adapt, refining
its accuracy with each conversation.
Customer engagement is critical in today's digital landscape. A well-implemented chatbot improves user experience by
reducing response times, ensuring accurate assistance, and enabling businesses to provide 24/7 support. Whether used
in e-commerce, banking, healthcare, or IT services, the AI chatbot enhances operational efficiency while reducing costs.
## Features
- **Multi-language support**: Communicate with customers in various languages to break language barriers and
reach a global audience.
- **24/7 availability**: Always online to handle customer inquiries, ensuring businesses never miss a potential lead or
support request.
- **Integration with CRM**: Seamlessly connect with customer relationship management systems to provide personalized
and context-aware responses.
- **Sentiment analysis**: Detect customer emotions to tailor responses accordingly, improving user satisfaction.
- **Self-learning capabilities**: Uses machine learning to improve responses over time based on user interactions.
- **Secure authentication**: Ensures secure user verification, protecting sensitive customer data.
## Use Cases
- **E-commerce support**: Automate order tracking, refunds, and general customer inquiries.
- **Healthcare industry**: Assist patients with appointment scheduling and medical inquiries.
- **Financial services**: Provide instant banking support and transaction details.
- **IT helpdesk**: Troubleshoot user issues with automated responses and guided solutions.

View File

@@ -1,45 +0,0 @@
---
id: "3"
title: "Smart IoT Home Automation System"
description: "An IoT-powered system to enhance home automation and energy efficiency."
---
# Smart IoT Home Automation System
The Smart IoT Home Automation System is designed to provide convenience, security, and energy efficiency by
interconnecting household devices via the Internet of Things (IoT). Users can remotely monitor and control their homes
through a mobile app or voice commands.
## Overview
Smart home technology is rapidly transforming the way people interact with their living spaces. By integrating
IoT-enabled
devices, homeowners can automate routine tasks, enhance security, and optimize energy usage. This system connects
various smart devices, allowing seamless communication between them.
With the rise of artificial intelligence and machine learning, smart home automation can now learn user preferences
and adjust device settings accordingly. This creates a highly personalized and efficient home environment that not only
offers convenience but also contributes to significant energy savings.
## Features
- **Voice and app control**: Operate devices via smartphone applications or virtual assistants like Alexa and Google
Assistant.
- **Energy management**: Optimize power consumption by monitoring and adjusting electricity usage, leading to reduced
energy bills.
- **Security integration**: Connect smart locks, cameras, and alarm systems to enhance home security.
- **Customizable automation**: Set schedules and rules for devices to function automatically based on user preferences.
- **Remote monitoring**: Check and control home appliances from anywhere using a mobile application.
- **Emergency alerts**: Receive real-time alerts in case of fire, gas leaks, or unauthorized access.
- **AI-driven automation**: Learn user habits and optimize home settings accordingly.
## Use Cases
- **Smart lighting**: Automatically adjust brightness based on time of day and room occupancy.
- **Home security**: Monitor security cameras, lock doors remotely, and receive alerts for unusual activity.
- **Climate control**: Adjust thermostats based on weather conditions and occupancy to maintain comfort and efficiency.
- **Elderly assistance**: Improve quality of life for seniors by automating tasks and ensuring their safety.
- **Energy efficiency**: Monitor and reduce power consumption dynamically based on usage patterns.
This IoT-based system not only makes daily life easier but also contributes to a more sustainable future by ensuring
intelligent energy usage and minimizing waste.

View File

@@ -1,37 +0,0 @@
---
id: "2"
title: "Blockchain-Based Voting System"
description: "A secure and transparent voting system leveraging blockchain technology."
---
# Blockchain-Based Voting System
This project aims to revolutionize voting systems by leveraging **blockchain** to ensure security, transparency, and
immutability. The system enables safe and verifiable elections while preventing voter fraud and manipulation.
## Overview
Traditional voting systems are prone to various security risks such as hacking, ballot tampering, and voter fraud.
Blockchain technology offers a decentralized approach where every vote is securely recorded, making the process
transparent and tamper-proof. By eliminating the need for intermediaries, blockchain voting provides a direct,
cost-effective,
and highly secure method of conducting elections.
The system ensures that each vote is unique, immutable, and instantly verifiable, thereby eliminating common voting
challenges such as multiple voting, unauthorized access, and discrepancies in counting. Voter anonymity is also
maintained through advanced cryptographic techniques, ensuring that privacy is never compromised.
## Features
- **Decentralized ledger**: Ensures tamper-proof voting records by storing votes on a distributed network.
- **Anonymous yet verifiable**: Protects voter privacy while ensuring authenticity through cryptographic verification.
- **Smart contracts**: Automates vote counting and validation, reducing the risk of human error.
- **Public auditing**: Allows independent parties to audit election results without compromising voter anonymity.
- **Remote voting**: Enables secure voting from anywhere, increasing accessibility and voter participation.
- **Immutable transactions**: Once recorded, votes cannot be altered, ensuring trust and fairness.
## Use Cases
- **National elections**: Ensure transparency and security in presidential and parliamentary elections.
- **Corporate voting**: Facilitate decision-making in organizations and shareholder meetings.
- **University elections**: Implement secure and transparent voting systems for student bodies.