Refactor website to use shadcn components

This commit is contained in:
2025-06-28 12:01:43 +00:00
parent 1648e376bf
commit 8c05ad29cb
78 changed files with 3858 additions and 2722 deletions

View File

@@ -0,0 +1,45 @@
'use client';
import React, {useEffect} from "react";
import HomeServices from "@/app/(root)/sections/HomeServices";
import {motion} from "framer-motion";
import Hero from "@/app/(root)/sections/Hero";
import About from "@/app/(root)/sections/About";
import ProcessSection from "@/app/(root)/sections/ProcessSection";
import WhyUs from "@/app/(root)/sections/WhyUs";
import Faq from "@/app/(root)/sections/Faq";
import ReferralSection from "@/app/(root)/sections/ReferralSection";
const Home = () => {
useEffect(() => {
const scrollToId = localStorage.getItem('scrollToId')
if (scrollToId) {
localStorage.removeItem('scrollToId')
const el = document.getElementById(scrollToId)
if (el) {
setTimeout(() => {
el.scrollIntoView({behavior: 'smooth', block: 'start'})
}, 200)
}
}
}, [])
return (
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.7, ease: "easeOut"}}
className="overflow-hidden"
>
<Hero/>
<HomeServices/>
<About/>
<ProcessSection/>
<WhyUs/>
<ReferralSection/>
<Faq/>
</motion.div>
);
};
export default Home;

View File

@@ -1,37 +0,0 @@
import type {Metadata} from "next";
import "../globals.css";
import Nav from "@/components/Navbar/Nav";
import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react";
import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = {
title: "Rhein Software",
description: "Rhein Software Development",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return (
<html lang="de" data-theme={theme}>
<head/>
<body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider>
<Nav/>
{children}
<Footer/>
</ThemeProvider>
</body>
</html>
);
}

View File

@@ -1,12 +1,10 @@
import React from 'react';
import Home from "@/components/Home/Home";
import Home from "@/app/(root)/Home";
const HomePage = () => {
return (
<div>
<Home />
</div>
);
return (
<Home/>
);
};
export default HomePage;

View File

@@ -0,0 +1,86 @@
'use client';
import {motion} from 'framer-motion';
const About = () => {
return (
<section
id="about"
className="relative w-full py-24 bg-background text-foreground transition-colors duration-700 ease-in-out">
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<div className="flex flex-col">
{/* Title */}
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Über uns
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
{/* Text */}
<div className="p-0 max-w-4xl">
<motion.p
className="text-base md:text-lg leading-relaxed text-muted-foreground"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir sind Rhein-Software ein Team, das sich auf individuelle Softwarelösungen spezialisiert
hat. Unsere Anwendungen sind technisch solide, skalierbar und durchdacht gebaut für
langfristigen Erfolg.
</motion.p>
<motion.p
className="mt-6 text-base md:text-lg leading-relaxed text-muted-foreground"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.3}}
>
Von der ersten Idee bis zum Go-Live begleiten wir Unternehmen und Startups mit einem
flexiblen Netzwerk, klarer Kommunikation und einem hohen Anspruch an Qualität. Gemeinsam
realisieren wir digitale Produkte, die wirklich passen.
</motion.p>
<motion.p
className="mt-6 text-base md:text-lg leading-relaxed text-muted-foreground"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.4}}
>
Egal ob App-Entwicklung, interne Tools, Web-Plattformen oder komplexe Schnittstellen wir
entwickeln Softwarelösungen, die intuitiv, effizient und exakt auf deine Anforderungen
zugeschnitten sind.
</motion.p>
</div>
{/* CTA Placeholder */}
<motion.div
className="mt-10 flex justify-end"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.5}}
>
{/* CTA button can go here */}
</motion.div>
</div>
</div>
</section>
);
};
export default About;

View File

@@ -0,0 +1,109 @@
'use client'
import {Accordion, AccordionContent, AccordionItem, AccordionTrigger,} from "@/components/ui/accordion"
import {motion} from "framer-motion"
const faqItems = [
{
id: "dauer",
question: "Wie lange dauert es, bis meine Website oder App online ist?",
answers: [
"Das hängt vom Umfang des Projekts ab einfache Websites oder MVPs sind meist innerhalb von 46 Wochen realisierbar.",
"Komplexere Anwendungen oder individuelle Features benötigen entsprechend mehr Zeit. Wir geben dir zu Beginn eine realistische Einschätzung.",
],
},
{
id: "inhalte",
question: "Muss ich Texte und Bilder selbst liefern?",
answers: [
"Wenn du Inhalte hast, bauen wir diese gerne ein. Falls nicht, unterstützen wir dich mit Textvorschlägen, Icons oder lizenzfreien Bildern.",
"Bei Apps helfen wir dir auch bei der Strukturierung und Formulierung von App-Inhalten, z.B. Onboarding-Texte oder UI-Texte.",
],
},
{
id: "technik",
question: "Ich habe keine Ahnung von Technik funktioniert das trotzdem?",
answers: [
"Auf jeden Fall. Wir begleiten dich Schritt für Schritt und erklären alles verständlich ganz ohne Fachkenntnisse..",
"Du bekommst eine Lösung, die für dich funktioniert egal ob Website, App oder Backend.",
],
},
{
id: "änderungen",
question: "Was ist, wenn ich im Nachhinein etwas ändern möchte?",
answers: [
"Kein Problem. Du kannst jederzeit neue Inhalte, Features oder Anpassungen beauftragen.",
"Auf Wunsch übernehmen wir auch die laufende Wartung oder stellen dir ein CMS bzw. Admin-Interface bereit.",
],
},
{
id: "seo",
question: "Wird meine Website oder App auch für Suchmaschinen optimiert?",
answers: [
"Ja. Jede Website wird suchmaschinenfreundlich aufgebaut inkl. technischer SEO-Basics wie saubere Struktur, schnelle Ladezeit und mobile Optimierung.",
"Bei Apps unterstützen wir dich z.B. auch mit App Store Optimierung (ASO), damit du besser gefunden wirst.",
],
},
]
export default function Faq() {
return (
<section id="faq" className="py-24 px-4 bg-background text-foreground">
<div className="max-w-3xl mx-auto">
<motion.h2
className="text-3xl md:text-4xl font-bold mb-2 text-center"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Fragen? Antworten.
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500 mx-auto"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<motion.p
className="text-sm md:text-base mb-10 text-muted-foreground text-center"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.2}}
>
Hier beantworten wir häufige Fragen rund um Web- und App-Projekte klar gegliedert nach Themen.
Wenn du darüber hinaus etwas wissen möchtest, melde dich gerne persönlich bei uns.
</motion.p>
<motion.div
className="text-sm md:text-base mb-10 text-muted-foreground text-center"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.4}}
>
<Accordion
type="single"
collapsible
className="w-full"
>
{faqItems.map((item, index) => (
<AccordionItem key={index} value={`faq-${index}`}>
<AccordionTrigger>{item.question}</AccordionTrigger>
<AccordionContent className="flex flex-col gap-4 text-left">
{item.answers.map((text, idx) => (
<p key={idx}>{text}</p>
))}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</motion.div>
</div>
</section>
)
}

View File

@@ -0,0 +1,76 @@
'use client';
import {motion} from 'framer-motion';
import Image from 'next/image';
import {Typewriter} from 'react-simple-typewriter';
import PulsatingButton from "@/components/PulsatingButton";
const Hero = () => {
return (
<section id="start" className="relative w-full h-screen overflow-hidden">
{/* Background */}
<div className="absolute inset-0 z-0">
<Image
src="/images/home_hero.jpg"
alt="Rhein river aerial view"
fill
className="object-cover scale-105 blur-sm"
priority
/>
<div className="absolute inset-0 bg-black/60"/>
</div>
{/* Content */}
<div
className="relative z-10 flex flex-col justify-center items-start h-full w-[90%] sm:w-[80%] max-w-6xl mx-auto text-white">
<motion.h1
className="text-3xl sm:text-5xl font-bold mb-6 leading-tight"
initial={{opacity: 0, y: 40}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6}}
>
Digitale Lösungen, <br/> die wirklich passen.
</motion.h1>
<motion.p
className="text-lg sm:text-xl text-gray-300 mb-6 max-w-2xl"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6, delay: 0.2}}
>
Wir entwickeln individuelle Softwarelösungen für Unternehmen und Startups.
</motion.p>
<motion.div
className="text-xl font-semibold text-white"
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{delay: 0.6}}
>
<Typewriter
words={['Webdesign', 'App-Entwicklung', 'Interne Tools']}
loop={true}
cursor
cursorStyle="_"
typeSpeed={60}
deleteSpeed={40}
delaySpeed={2000}
/>
</motion.div>
<div className="mt-10 relative flex items-center justify-center">
<PulsatingButton
label="Jetzt Kontakt aufnehmen"
href="/contact"
color="#2563eb" // Tailwind blue-600
width={256}
pulse
/>
</div>
</div>
</section>
);
};
export default Hero;

View File

@@ -0,0 +1,115 @@
'use client';
import {motion} from 'framer-motion';
import {ChevronRight} from 'lucide-react';
import Link from 'next/link';
const services = [
{
title: 'Webdesign',
description: 'Moderne Websites, die Vertrauen schaffen und verkaufen.',
bullets: [
'Maßgeschneidertes Design',
'Klare Struktur & überzeugende Inhalte',
'Nutzerführung mit System & Strategie',
'Für alle Geräte optimiert',
],
},
{
title: 'App-Entwicklung',
description: 'Skalierbare Apps für Web und Mobile von der Idee bis zum Launch.',
bullets: [
'Plattformübergreifend mit modernen Technologien',
'Backend & API-Entwicklung inklusive',
'Individuelle Funktionen & Logik',
'Stabil, performant & wartbar',
],
},
{
title: 'Interne Tools',
description: 'Digitale Werkzeuge, die Prozesse vereinfachen und Zeit sparen.',
bullets: [
'Prozessdigitalisierung & Automatisierung',
'Zugeschnitten auf eure Workflows',
'Skalierbar & zukunftssicher',
'Intuitiv & effizient bedienbar',
],
},
];
const HomeServices = () => {
return (
<section id="services"
className="w-full py-24 bg-background text-foreground">
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Leistungen
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-10 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{services.map((service, index) => (
<motion.div
key={service.title}
className="flex flex-col justify-between h-full p-6 rounded-3xl border bg-muted text-foreground"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: index * 0.1}}
whileHover={{
scale: 1.03,
boxShadow: '0px 12px 30px rgba(0, 0, 0, 0.08)',
}}
>
<div>
<h3 className="text-xl font-semibold mb-2">{service.title}</h3>
<p className="text-muted-foreground mb-4">{service.description}</p>
<ul className="space-y-3">
{service.bullets.map((point, i) => (
<li key={i} className="flex items-start gap-2">
<ChevronRight className="w-4 h-4 text-primary mt-1"/>
<span className="text-sm text-foreground">{point}</span>
</li>
))}
</ul>
</div>
</motion.div>
))}
</div>
<motion.div
className="mt-12 text-center"
initial={{opacity: 0}}
whileInView={{opacity: 1}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.3}}
>
<p className="text-muted-foreground mb-4 text-base md:text-lg">
Du möchtest mehr über unsere Leistungen erfahren oder hast ein konkretes Projekt im Kopf?
</p>
<Link
href="/contact"
className="text-sm font-semibold text-primary hover:underline"
>
Jetzt Kontakt aufnehmen
</Link>
</motion.div>
</div>
</section>
);
};
export default HomeServices;

View File

@@ -0,0 +1,88 @@
'use client';
import React from 'react';
import {VerticalTimeline, VerticalTimelineElement} from 'react-vertical-timeline-component';
import {FaRocket, FaLightbulb, FaCode, FaPaperPlane} from 'react-icons/fa';
import 'react-vertical-timeline-component/style.min.css';
import {motion} from 'framer-motion';
const steps = [
{
title: 'Kick-Off & Strategie',
description:
'In einem gemeinsamen Auftakt klären wir deine Ziele, Zielgruppen und Herausforderungen. Daraus entsteht ein strukturierter Plan als Basis für alles Weitere.',
icon: <FaRocket/>,
},
{
title: 'Konzept & Inhalte',
description:
'Wir erarbeiten eine klare Struktur und passende Inhalte abgestimmt auf deine Botschaft und deine Nutzer. So entsteht ein roter Faden für Design und Umsetzung.',
icon: <FaLightbulb/>,
},
{
title: 'Design & Entwicklung',
description:
'Wir gestalten ein modernes Design und setzen es technisch um. Durch regelmäßige Feedback-Schleifen bist du jederzeit im Prozess eingebunden.',
icon: <FaCode/>,
},
{
title: 'Go-Live',
description:
'Nach erfolgreichen Tests geht dein Projekt live. Auch danach begleiten wir dich weiter für einen reibungslosen Betrieb und mögliche Weiterentwicklungen.',
icon: <FaPaperPlane/>,
},
];
const ProcessSection = () => {
return (
<section id="process" className="w-full py-24 bg-background text-foreground">
<div className="max-w-6xl px-6 md:px-10 mx-auto">
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Unser Prozess
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-12 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<VerticalTimeline
lineColor="#eab308"
animate={true}
>
{steps.map((step, idx) => (
<VerticalTimelineElement
key={idx}
contentStyle={{
background: 'hsl(var(--muted))',
color: 'hsl(var(--foreground))',
border: '1px solid hsl(var(--border))',
}}
contentArrowStyle={{borderRight: '7px solid hsl(var(--muted))'}}
iconStyle={{
background: '#eab308',
color: '#fff',
}}
icon={step.icon}
>
<h3 className="text-xl font-semibold">{step.title}</h3>
<p className="text-muted-foreground mt-2">{step.description}</p>
</VerticalTimelineElement>
))}
</VerticalTimeline>
</div>
</section>
);
};
export default ProcessSection;

View File

@@ -0,0 +1,60 @@
'use client'
import {motion} from 'framer-motion'
import Link from 'next/link'
export default function ReferralSection() {
return (
<section id="referral" className="py-24 px-4 bg-background/80 text-foreground">
<div className="max-w-3xl mx-auto text-center">
<motion.h2
className="text-3xl md:text-4xl font-bold mb-2"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Weiterempfehlen lohnt sich
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500 mx-auto"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<motion.div
className="text-sm md:text-base mb-8 text-muted-foreground space-y-4"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.2}}
>
<p>
Du empfiehlst uns weiter und dein Kontakt wird Kunde?
Als Dank erhältst du <strong>10 % Rabatt</strong> auf dein nächstes Projekt bei uns.
</p>
<p>
Einfach, fair und lohnend ideal für alle, die mit unserer Arbeit zufrieden sind.
</p>
</motion.div>
<motion.div
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.3}}
>
<Link
href="/contact"
className="inline-block bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-full font-semibold transition"
>
Jetzt empfehlen
</Link>
</motion.div>
</div>
</section>
)
}

View File

@@ -0,0 +1,87 @@
'use client';
import Image from 'next/image';
import {motion} from 'framer-motion';
import {techStack} from "@/constant/TechStack";
const TechStack = () => {
return (
<section className="w-full py-20 bg-background text-foreground transition-colors duration-700 ease-in-out">
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Technologien
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<motion.p
className="text-sm md:text-base mb-10 text-muted-foreground"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.3, delay: 0.2}}
>
Mit diesen Technologien realisieren wir moderne, leistungsstarke Softwarelösungen.
</motion.p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{techStack.row1.map((group, index) => (
<TechCard key={group.category} group={group} delay={index * 0.2}/>
))}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{techStack.row2.map((group, index) => (
<TechCard key={group.category} group={group} delay={index * 0.2 + 0.4}/>
))}
</div>
</div>
</section>
);
};
const TechCard = ({
group,
delay,
}: {
group: { category: string; items: { id: string; label: string }[] };
delay: number;
}) => (
<motion.div
className="p-4 rounded-lg border bg-muted shadow-md text-foreground"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
whileHover={{scale: 1.03, boxShadow: '0 10px 20px rgba(0,0,0,0.1)'}}
viewport={{once: true}}
transition={{duration: 0.4, delay}}
>
<h3 className="text-base font-semibold mb-4">{group.category}</h3>
<div className="grid grid-cols-3 gap-3">
{group.items.map(({id, label}) => (
<div key={id} className="flex flex-col items-center text-center">
<Image
src={`/images/svg/${id}.svg`}
alt={label}
width={32}
height={32}
className="object-contain"
/>
<span className="text-[10px] mt-1 text-muted-foreground">{label}</span>
</div>
))}
</div>
</motion.div>
);
export default TechStack;

View File

@@ -0,0 +1,69 @@
'use client';
import {CheckCircle} from 'lucide-react';
import {motion} from 'framer-motion';
import Link from 'next/link';
import Image from 'next/image';
const points = [
'Fertigstellung in 48 Wochen',
'Fester Ansprechpartner von Anfang bis Ende',
'Struktur & Klarheit',
'Starkes Alleinstellungsmerkmal',
'Zuverlässiges Team mit Weitblick',
];
export default function WhyUs() {
return (
<section id="whyus" className="py-24 px-4 bg-background text-foreground">
<div className="max-w-xl mx-auto">
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-center"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Warum wir?
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-10 bg-amber-500 mx-auto"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<motion.div
className="rounded-2xl p-8 md:p-10 bg-gradient-to-br from-[#1e3a8a] to-[#2563eb] text-white shadow-xl relative overflow-hidden"
initial={{opacity: 0, y: 40}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5}}
>
{/* Logo */}
<div className="mb-6">
<Image src="/logo.svg" alt="Rhein Software Logo" width={120} height={32}/>
</div>
<ul className="space-y-4 mb-8">
{points.map((point, index) => (
<li key={index} className="flex items-start gap-2">
<CheckCircle className="text-white w-5 h-5 flex-shrink-0 mt-0.5"/>
<span>{point}</span>
</li>
))}
</ul>
<Link
href="/contact"
className="inline-block bg-white text-blue-700 font-semibold px-6 py-3 rounded-full text-center shadow-md hover:bg-slate-100 transition"
>
Kostenlose Beratung anfragen
</Link>
</motion.div>
</div>
</section>
);
}

View File

@@ -0,0 +1,89 @@
'use client';
import React from 'react';
import Image from 'next/image';
import {motion} from 'framer-motion';
const team = [
{
name: 'Thatsaphorn',
role: 'Gründer & Entwickler',
picture: '',
},
{
name: 'Anonym',
role: 'Vertrieb',
picture: '',
},
];
const fallbackImage = '/images/team/default-avatar.jpg';
const TeamSection = () => {
return (
<section
className="w-full px-6 sm:px-12 py-24 bg-background text-foreground transition-colors duration-700 ease-in-out">
<motion.h2
className="text-2xl sm:text-3xl font-bold text-left"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5}}
>
Das Team
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-12 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<div className="flex justify-center">
<div
className={`grid gap-8 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-${Math.min(
team.length,
4
)}`}
>
{team.map((member, idx) => (
<motion.div
key={member.name}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: idx * 0.1}}
whileHover={{scale: 1.015}}
className="flex flex-col items-center text-center rounded-xl border border-border shadow-md hover:shadow-lg transition-all p-6 bg-muted"
>
<motion.div
whileHover={{scale: 1.05}}
transition={{type: 'spring', stiffness: 300, damping: 20}}
className="w-28 h-28 relative mb-4"
>
<Image
src={member.picture || fallbackImage}
alt={member.name}
fill
sizes="112px"
className="rounded-full object-cover shadow"
/>
</motion.div>
<div className="h-px w-8 bg-border my-4"/>
<div className="text-lg font-semibold">{member.name}</div>
<div className="text-sm mt-1 text-muted-foreground">
{member.role}
</div>
</motion.div>
))}
</div>
</div>
</section>
);
};
export default TeamSection;

View File

@@ -1,37 +0,0 @@
import type {Metadata} from "next";
import "../globals.css";
import Nav from "@/components/Navbar/Nav";
import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react";
import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = {
title: "Über Uns | Rhein Software",
description: "Rhein Software Development",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return (
<html lang="de" data-theme={theme}>
<head/>
<body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider>
<Nav/>
{children}
<Footer/>
</ThemeProvider>
</body>
</html>
);
}

View File

@@ -1,12 +0,0 @@
import React from 'react';
import AboutContent from "@/components/About/AboutContent";
const AboutPage = () => {
return (
<div>
<AboutContent/>
</div>
);
};
export default AboutPage;

View File

@@ -1,37 +0,0 @@
import type {Metadata} from "next";
import "../globals.css";
import Nav from "@/components/Navbar/Nav";
import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react";
import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = {
title: "Kontakt | Rhein Software",
description: "Rhein Software Development",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return (
<html lang="de" data-theme={theme}>
<head/>
<body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider>
<Nav/>
{children}
<Footer/>
</ThemeProvider>
</body>
</html>
);
}

View File

@@ -1,5 +1,12 @@
import React from 'react';
import Contact from "@/components/Contact/Contact";
import type {Metadata} from "next";
export async function generateMetadata(): Promise<Metadata> {
return {
title: "Kontakt | Rhein Software",
};
}
const ContactPage = () => {
return (

View File

@@ -25,3 +25,71 @@
.animate-float {
animation: float 3.5s ease-in-out infinite;
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

58
frontend/app/layout.tsx Normal file
View File

@@ -0,0 +1,58 @@
import type {Metadata} from "next";
import "./globals.css";
import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/theme-provider";
import React from "react";
import CookieConsentBanner from "@/components/Cookie/CookieConsentBanner";
import Navbar from "@/components/Navbar/Navbar";
export const metadata: Metadata = {
title: "Rhein Software Maßgeschneiderte Softwarelösung",
description: "Rhein Software bietet individuelle Softwarelösungen für moderne Unternehmen.",
keywords: ["Webentwicklung", "Software", "Next.js", "Full Stack", "Rhein Software"],
authors: [{name: "Rhein Software"}],
creator: "Rhein Software",
robots: "index, follow",
openGraph: {
title: "Rhein Software Maßgeschneiderte Softwarelösung",
description: "Individuelle Softwarelösungen für Unternehmen mit Fokus auf Qualität und Performance.",
url: "https://www.rhein-software.dev",
siteName: "Rhein Software",
locale: "de_DE",
type: "website",
images: [
{
url: "https://www.rhein-software.dev/og-image.jpg",
width: 1200,
height: 630,
alt: "Rhein Software Individuelle Softwarelösung",
},
],
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="de" suppressHydrationWarning>
<body className="antialiased">
<ThemeProvider
attribute="class"
defaultTheme="system"
// disableTransitionOnChange
>
<Navbar/>
<main>
{children}
</main>
<Footer/>
</ThemeProvider>
<CookieConsentBanner/>
</body>
</html>
);
}

View File

@@ -0,0 +1,117 @@
"use client";
import {motion} from "framer-motion";
const fadeInUp = {
hidden: {opacity: 0, y: 30},
visible: (i: number) => ({
opacity: 1,
y: 0,
transition: {
duration: 0.6,
delay: i * 0.2,
ease: "easeOut",
},
}),
};
const ImprintComp = () => {
const sections = [
{
title: "Impressum",
content: (
<>
Thatsaphorn Atchariyaphap<br/>
Rhein-Software (Einzelunternehmer)<br/>
Mühlenstrasse 13<br/>
79664 Wehr
</>
),
},
{
title: "Kontakt",
content: (
<>
Telefon: +49 (0) 151 24003632<br/>
E-Mail:{" "}
<a
href="mailto:contact@rhein-software.dev"
className="underline text-primary"
>
contact@rhein-software.dev
</a>
</>
),
},
{
title: "EU-Streitschlichtung",
content: (
<>
Die Europäische Kommission stellt eine Plattform zur
Online-Streitbeilegung (OS) bereit:{" "}
<a
href="https://ec.europa.eu/consumers/odr/"
target="_blank"
rel="noopener noreferrer"
className="underline text-primary"
>
https://ec.europa.eu/consumers/odr/
</a>
.<br/>
Unsere E-Mail-Adresse finden Sie oben im Impressum.
</>
),
},
{
title: "Verbraucherstreitbeilegung / Universalschlichtungsstelle",
content: (
<>
Wir sind nicht bereit oder verpflichtet, an
Streitbeilegungsverfahren vor einer
Verbraucherschlichtungsstelle teilzunehmen.
</>
),
},
];
return (
<div className="overflow-hidden bg-background text-foreground">
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12 space-y-10 text-base leading-relaxed">
{sections.map((section, i) => (
<motion.div
key={section.title}
custom={i}
initial="hidden"
whileInView="visible"
viewport={{once: true, amount: 0.2}}
variants={fadeInUp}
>
<h2 className="text-2xl font-bold mb-4">{section.title}</h2>
<div className="space-y-4">{section.content}</div>
</motion.div>
))}
<motion.p
className="text-sm text-muted-foreground"
custom={4}
initial="hidden"
whileInView="visible"
viewport={{once: true, amount: 0.2}}
variants={fadeInUp}
>
Quelle:{" "}
<a
href="https://www.e-recht24.de"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
www.e-recht24.de
</a>
</motion.p>
</div>
</div>
);
};
export default ImprintComp;

View File

@@ -1,10 +1,17 @@
import React from 'react';
import ImprintComp from "@/components/Legal/Imprint/ImprintComp";
import ImprintComp from "@/app/legal/imprint/ImprintComp";
import type {Metadata} from "next";
export async function generateMetadata(): Promise<Metadata> {
return {
title: "Impressum | Rhein Software",
};
}
const ImprintPage = () => {
return (
<div>
<ImprintComp />
<ImprintComp/>
</div>
);
};

View File

@@ -1,38 +0,0 @@
import type {Metadata} from "next";
import "../globals.css";
import Nav from "@/components/Navbar/Nav";
import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react";
import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = {
title: "Rechtliches | Rhein Software",
description: "Rhein Software Development",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return (
<html lang="de" data-theme={theme}>
<head/>
<body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider>
<Nav/>
{children}
<Footer/>
</ThemeProvider>
</body>
</html>
);
}

View File

@@ -1,10 +1,60 @@
import React from 'react';
'use client'
const LegalPage = () => {
import Link from 'next/link'
import {motion} from 'framer-motion'
import SmallHero from '@/components/Helper/SmallHero'
import {Card, CardContent} from '@/components/ui/card'
const legalLinks = [
{
label: 'Impressum',
href: '/legal/imprint',
},
{
label: 'Datenschutz',
href: '/legal/privacy',
},
{
label: 'Widerrufsrecht',
href: '/legal/revocation',
},
{
label: 'Nutzungsbedingungen',
href: '/legal/terms-of-use',
},
]
export default function LegalOverviewPage() {
return (
<div>
</div>
);
};
<>
<SmallHero
title="Rechtliches"
subtitle="Alle rechtlich relevanten Informationen auf einen Blick."
backgroundImage="/images/contact.png"
blurBackground
/>
export default LegalPage;
<section className="px-6 sm:px-12 py-16 max-w-6xl mx-auto">
<motion.div
className="grid grid-cols-1 sm:grid-cols-2 gap-6"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.3}}
>
{legalLinks.map(({label, href}) => (
<Card key={href} className="hover:shadow-md transition-shadow">
<CardContent className="p-6">
<Link
href={href}
className="text-primary font-medium text-lg hover:underline"
>
{label}
</Link>
</CardContent>
</Card>
))}
</motion.div>
</section>
</>
)
}

View File

@@ -0,0 +1,124 @@
"use client";
import {motion} from "framer-motion";
const fadeInUp = {
hidden: {opacity: 0, y: 30},
visible: (i: number) => ({
opacity: 1,
y: 0,
transition: {
duration: 0.6,
delay: i * 0.2,
ease: "easeOut",
},
}),
};
const PrivacyComp = () => {
const sections = [
{
title: "1. Datenschutz auf einen Blick",
content: (
<>
<p>
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen
Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit
denen Sie persönlich identifiziert werden können.
</p>
<p>
Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten
finden Sie im Abschnitt Hinweis zur Verantwortlichen Stelle.
</p>
</>
),
},
{
title: "2. Allgemeine Hinweise und Pflichtinformationen",
content: (
<>
<p>
Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen
Datenschutzvorschriften sowie dieser Datenschutzerklärung. Wir weisen darauf hin, dass die
Datenübertragung im Internet Sicherheitslücken aufweisen kann.
</p>
<p>
Verantwortlich für die Datenverarbeitung:
<br/>
Rhein-Software Development
<br/>
Mühlenstrasse 13
<br/>
79664 Wehr
<br/>
<br/>
Telefon: +49 (0) 151 24003632
<br/>
E-Mail: <a href="mailto:contact@rhein-software.dev"
className="underline text-primary">contact@rhein-software.dev</a>
</p>
</>
),
},
{
title: "3. Ihre Rechte",
content: (
<p>
Sie haben das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung,
Datenübertragbarkeit sowie auf Widerspruch gegen die Verarbeitung Ihrer Daten. Zudem haben Sie
ein Beschwerderecht bei der zuständigen Aufsichtsbehörde.
</p>
),
},
{
title: "4. Analyse-Tools und Cookies",
content: (
<p>
Beim Besuch dieser Website kann Ihr Surfverhalten statistisch ausgewertet werden. Das geschieht
vor allem mit sogenannten Analyseprogrammen und Cookies. Detaillierte Informationen hierzu
entnehmen Sie bitte unserer vollständigen Datenschutzerklärung.
</p>
),
},
{
title: "5. Sicherheit",
content: (
<p>
Diese Seite nutzt eine SSL- bzw. TLS-Verschlüsselung. Eine verschlüsselte Verbindung erkennen
Sie an der Adresszeile des Browsers (https://“) und dem Schloss-Symbol.
</p>
),
},
{
title: "Quelle",
content: (
<p className="text-sm text-muted-foreground">
Quelle: <a href="https://www.e-recht24.de" target="_blank" rel="noopener noreferrer"
className="underline">www.e-recht24.de</a>
</p>
),
},
];
return (
<div className="overflow-hidden bg-background text-foreground">
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12 space-y-10 text-base leading-relaxed">
{sections.map((section, i) => (
<motion.div
key={section.title}
custom={i}
initial="hidden"
whileInView="visible"
viewport={{once: true, amount: 0.2}}
variants={fadeInUp}
>
<h2 className="text-2xl font-bold mb-4">{section.title}</h2>
<div className="space-y-4">{section.content}</div>
</motion.div>
))}
</div>
</div>
);
};
export default PrivacyComp;

View File

@@ -1,5 +1,12 @@
import React from 'react';
import PrivacyComp from "@/components/Legal/Privacy/PrivacyComp";
import PrivacyComp from "@/app/legal/privacy/PrivacyComp";
import type {Metadata} from "next";
export async function generateMetadata(): Promise<Metadata> {
return {
title: "Datenschutz | Rhein Software",
};
}
const PrivacyPage = () => {
return (

View File

@@ -1,12 +0,0 @@
import React from 'react';
import RevocationComp from "@/components/Legal/RevocationComp";
const RevocationPage = () => {
return (
<div>
<RevocationComp />
</div>
);
};
export default RevocationPage;

View File

@@ -0,0 +1,98 @@
'use client';
import SmallHero from '@/components/Helper/SmallHero';
import React from 'react';
import {motion} from 'framer-motion';
const TermsOfUseComp = () => {
return (
<div className="overflow-hidden bg-background text-foreground">
{/* Hero Section */}
<div className="mt-[10vh]">
<SmallHero
title="AGB"
subtitle=""
backgroundImage="/images/contact.png"
/>
</div>
{/* Contact Form */}
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12">
<motion.h2
className="text-2xl md:text-3xl font-bold text-center"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.6}}
>
Schreib uns eine Nachricht
</motion.h2>
<motion.p
className="text-center mt-3 text-muted-foreground"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir melden uns schnellstmöglich bei dir!
</motion.p>
<form className="mt-8 max-w-2xl mx-auto space-y-6">
{/* Name & Email */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{["Dein Name", "Deine E-Mail"].map((label, index) => (
<motion.div
key={index}
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: index * 0.2}}
>
<label className="block font-semibold">{label}</label>
<input
type={index === 0 ? "text" : "email"}
placeholder={index === 0 ? "Max Mustermann" : "max@example.com"}
className="w-full p-3 rounded-lg border border-border bg-card text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
/>
</motion.div>
))}
</div>
{/* Message */}
<motion.div
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.5}}
>
<label className="block font-semibold">Deine Nachricht</label>
<textarea
rows={4}
placeholder="Schreibe deine Nachricht..."
className="w-full p-3 rounded-lg border border-border bg-card text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
/>
</motion.div>
{/* Submit Button */}
<motion.div
className="text-center"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.7}}
>
<button
type="submit"
className="px-6 py-3 bg-blue-600 text-white text-lg font-semibold rounded-lg shadow-md hover:bg-blue-700 transition-all"
>
📩 Nachricht senden
</button>
</motion.div>
</form>
</div>
</div>
);
};
export default TermsOfUseComp;

View File

@@ -1,5 +1,12 @@
import React from 'react';
import TermsOfUseComp from "@/components/Legal/TermsOfUseComp";
import TermsOfUseComp from "@/app/legal/terms-of-use/TermsOfUseComp";
import type {Metadata} from "next";
export async function generateMetadata(): Promise<Metadata> {
return {
title: "Nutzungsbedingungen | Rhein Software",
};
}
const TermsOfUsePage = () => {
return (

View File

@@ -1,37 +0,0 @@
import type {Metadata} from "next";
import "../globals.css";
import Nav from "@/components/Navbar/Nav";
import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react";
import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = {
title: "Leistungen | Rhein Software",
description: "Rhein Software Development",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return (
<html lang="de" data-theme={theme}>
<head/>
<body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider>
<Nav/>
{children}
<Footer/>
</ThemeProvider>
</body>
</html>
);
}

View File

@@ -1,12 +0,0 @@
import React from 'react';
import Services from "@/components/Services/Services";
const ContactPage = () => {
return (
<div>
<Services/>
</div>
);
};
export default ContactPage;