Merge branch 'dev' into 'production'
Merge dev into production See merge request rheinsw/website!27
This commit is contained in:
100
components/Contact/Contact.tsx
Normal file
100
components/Contact/Contact.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import React, {useEffect, useContext} from "react";
|
||||
import AOS from "aos";
|
||||
import "aos/dist/aos.css";
|
||||
import SmallHero from "@/components/Helper/SmallHero";
|
||||
import {ThemeContext} from "@/components/provider/ThemeProvider";
|
||||
import {themeColors} from "@/components/Helper/ThemeColors";
|
||||
|
||||
const Contact = () => {
|
||||
const {theme} = useContext(ThemeContext);
|
||||
const colors = themeColors[theme];
|
||||
|
||||
useEffect(() => {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
easing: "ease",
|
||||
once: true,
|
||||
anchorPlacement: "top-bottom",
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden transition-colors duration-500"
|
||||
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}>
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="mt-[10vh]">
|
||||
<SmallHero
|
||||
title="Kontakt"
|
||||
subtitle="Hast du Fragen? Wir sind für dich da!"
|
||||
backgroundImage="/images/contact.png"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Contact Form */}
|
||||
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12">
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-center"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="400"
|
||||
>
|
||||
Schreib uns eine Nachricht
|
||||
</h2>
|
||||
<p data-aos="fade-up" data-aos-delay="600"
|
||||
className="text-center mt-3 text-[var(--secondary-text)]">
|
||||
Wir melden uns schnellstmöglich bei dir!
|
||||
</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) => (
|
||||
<div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
|
||||
<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 focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div data-aos="fade-up" data-aos-delay="300">
|
||||
<label className="block font-semibold">Deine Nachricht</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
placeholder="Schreibe deine Nachricht..."
|
||||
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="text-center" data-aos="fade-up" data-aos-delay="400">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
97
components/Footer/Footer.tsx
Normal file
97
components/Footer/Footer.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import {motion} from 'framer-motion';
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<motion.footer
|
||||
initial={{opacity: 0, y: 20}}
|
||||
whileInView={{opacity: 1, y: 0}}
|
||||
viewport={{once: true}}
|
||||
transition={{duration: 0.6, ease: 'easeOut'}}
|
||||
className="py-10 transition-theme text-white"
|
||||
style={{
|
||||
backgroundColor: '#16171f', // modern dark blue-purple tone
|
||||
}}
|
||||
>
|
||||
<div className="w-[90%] mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-8">
|
||||
{/* Logo and description */}
|
||||
<div>
|
||||
<h1 className="text-xl md:text-2xl font-bold text-white">
|
||||
<span className="text-3xl md:text-4xl text-pink-700">R</span>hein Software
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Informationen */}
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white">Informationen</h3>
|
||||
<ul className="mt-4 space-y-4 text-sm font-semibold text-gray-400">
|
||||
<li>
|
||||
<Link href="/contact">
|
||||
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
|
||||
Kontakt
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/contact">
|
||||
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
|
||||
Zahlung und Versand
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Rechtliches */}
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white">Rechtliches</h3>
|
||||
<ul className="mt-4 space-y-4 text-sm font-semibold text-gray-400">
|
||||
<li>
|
||||
<Link href="/legal/terms-of-use">
|
||||
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
|
||||
AGB
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/legal/revocation">
|
||||
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
|
||||
Widerruf
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/legal/privacy">
|
||||
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
|
||||
Datenschutz
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/legal/imprint">
|
||||
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
|
||||
Impressum
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Section */}
|
||||
<div
|
||||
className="mt-8 border-t border-gray-600 pt-8 flex flex-col md:flex-row justify-between items-center text-sm text-gray-400">
|
||||
<p className="text-center md:text-left">
|
||||
© 2025 Rhein Software Development. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
24
components/Helper/SectionDivider.tsx
Normal file
24
components/Helper/SectionDivider.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
export const SectionDivider1 = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-20 transition-all duration-500 ease-in-out"
|
||||
style={{
|
||||
background: `linear-gradient(to bottom, var(--primary-bg), var(--secondary-bg))`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const SectionDivider2 = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-20 transition-all duration-500 ease-in-out"
|
||||
style={{
|
||||
background: `linear-gradient(to bottom, var(--secondary-bg), var(--primary-bg))`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
40
components/Helper/SmallHero.tsx
Normal file
40
components/Helper/SmallHero.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from "react";
|
||||
|
||||
type SmallHeroProps = {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
backgroundImage?: string; // Optional background image
|
||||
};
|
||||
|
||||
const SmallHero = ({title, subtitle, backgroundImage}: SmallHeroProps) => {
|
||||
return (
|
||||
<div
|
||||
className="w-full py-20 text-center flex flex-col items-center justify-center bg-cover bg-center"
|
||||
style={{
|
||||
backgroundColor: backgroundImage ? "transparent" : "var(--primary-bg)", // Fallback if no image
|
||||
color: "var(--primary-text)",
|
||||
backgroundImage: backgroundImage ? `url(${backgroundImage})` : "none",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundBlendMode: "overlay",
|
||||
transition: "background-color 0.4s ease-in-out, color 0.4s ease-in-out",
|
||||
}}
|
||||
>
|
||||
<h1 className="text-3xl sm:text-4xl font-bold"
|
||||
data-aos="fade-up"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
{subtitle &&
|
||||
<p className="mt-2 text-lg text-[var(--secondary-text)]"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="200"
|
||||
>
|
||||
{subtitle}
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SmallHero;
|
||||
18
components/Helper/Theme.ts
Normal file
18
components/Helper/Theme.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
"use server";
|
||||
|
||||
import {cookies} from "next/headers";
|
||||
|
||||
// ✅ Get theme from cookies OR detect system preference
|
||||
export async function getInitialTheme(): Promise<"dark" | "light"> {
|
||||
const themeCookie = (await cookies()).get("theme")?.value;
|
||||
|
||||
if (themeCookie === "dark" || themeCookie === "light") {
|
||||
return themeCookie;
|
||||
}
|
||||
|
||||
// Detect system preference
|
||||
const prefersDarkMode =
|
||||
typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
|
||||
return prefersDarkMode ? "dark" : "light";
|
||||
}
|
||||
28
components/Helper/ThemeColors.ts
Normal file
28
components/Helper/ThemeColors.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export const themeColors: Record<
|
||||
"light" | "dark",
|
||||
{
|
||||
primaryBg: string;
|
||||
secondaryBg: string;
|
||||
primaryText: string;
|
||||
secondaryText: string;
|
||||
inputFieldBg: string;
|
||||
inputBorder: string;
|
||||
}
|
||||
> = {
|
||||
dark: {
|
||||
primaryBg: "#121212", // Dark gray/black background (closer to true dark mode)
|
||||
secondaryBg: "#1e1e1e", // Slightly lighter gray for contrast
|
||||
primaryText: "#e0e0e0", // Light gray for good readability
|
||||
secondaryText: "#b0b0b0", // Muted gray for subtle contrast
|
||||
inputFieldBg: "#252525", // Dark but slightly distinguishable from primaryBg
|
||||
inputBorder: "#3a3a3a", // Slightly lighter gray for a soft contrast
|
||||
},
|
||||
light: {
|
||||
primaryBg: "#f7f6fb",
|
||||
secondaryBg: "#ffffff",
|
||||
primaryText: "#1a1a2e",
|
||||
secondaryText: "#4a4a4a", // Muted text color
|
||||
inputFieldBg: "#ffffff", // White input field (same as secondaryBg)
|
||||
inputBorder: "#dcdcdc", // Light gray border for subtle visibility
|
||||
},
|
||||
};
|
||||
82
components/Home/About/About.tsx
Normal file
82
components/Home/About/About.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import {FiArrowRight} from 'react-icons/fi';
|
||||
import {motion} from 'framer-motion';
|
||||
|
||||
const About = () => {
|
||||
return (
|
||||
<section className="relative w-full py-24 bg-[var(--secondary-bg)] transition-theme">
|
||||
<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"
|
||||
style={{color: 'var(--primary-text)'}}
|
||||
initial={{opacity: 0, y: 20}}
|
||||
whileInView={{opacity: 1, y: 0}}
|
||||
viewport={{once: true}}
|
||||
transition={{duration: 0.5}}
|
||||
>
|
||||
Ü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">
|
||||
<motion.p
|
||||
className="text-base md:text-lg leading-relaxed text-[var(--secondary-text)] max-w-4xl"
|
||||
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 und digitale
|
||||
Services 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-[var(--secondary-text)] max-w-4xl"
|
||||
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. Unsere
|
||||
Lösungen sind
|
||||
intuitiv, effizient – und genau auf deine Anforderungen zugeschnitten.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
<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}}
|
||||
>
|
||||
<Link href="/about">
|
||||
<button
|
||||
className="flex items-center gap-2 bg-blue-700 hover:bg-blue-900 text-white font-semibold px-5 py-2 rounded-full shadow-lg transition-all">
|
||||
Mehr über uns <FiArrowRight size={18}/>
|
||||
</button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
57
components/Home/Contact/ContactCTA.tsx
Normal file
57
components/Home/Contact/ContactCTA.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { motion } from 'framer-motion';
|
||||
import { FiArrowRight } from 'react-icons/fi';
|
||||
|
||||
const ContactCTA = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative w-full py-24 transition-theme overflow-hidden"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, var(--secondary-bg), var(--primary-bg))',
|
||||
}}
|
||||
>
|
||||
<div className="w-full max-w-4xl px-6 md:px-10 mx-auto text-center">
|
||||
{/* Headline */}
|
||||
<motion.h2
|
||||
className="text-3xl md:text-4xl font-bold text-[var(--primary-text)]"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
Interesse geweckt?
|
||||
</motion.h2>
|
||||
|
||||
{/* Description */}
|
||||
<motion.p
|
||||
className="mt-4 text-sm md:text-base text-[var(--secondary-text)] max-w-xl mx-auto"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
Lass uns über dein Projekt sprechen. Wir freuen uns darauf, deine Ideen in die Realität umzusetzen.
|
||||
</motion.p>
|
||||
|
||||
{/* CTA Button */}
|
||||
<motion.div
|
||||
className="mt-8 flex justify-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
>
|
||||
<Link href="/contact">
|
||||
<button className="inline-flex items-center gap-2 px-6 py-3 text-sm md:text-base font-semibold rounded-full bg-blue-700 hover:bg-blue-900 text-white shadow-md transition-all duration-300">
|
||||
Jetzt Kontakt aufnehmen <FiArrowRight size={18} />
|
||||
</button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactCTA;
|
||||
100
components/Home/Hero/Hero.tsx
Normal file
100
components/Home/Hero/Hero.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import {Typewriter} from 'react-simple-typewriter';
|
||||
import {motion} from 'framer-motion';
|
||||
|
||||
const Hero = () => {
|
||||
return (
|
||||
<div
|
||||
className="relative w-full pt-[4vh] md:pt-[12vh] h-screen flex flex-col overflow-hidden"
|
||||
style={{
|
||||
backgroundColor: 'var(--primary-bg)',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
src="/images/home_hero.jpg"
|
||||
alt="Rhein river aerial view"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
className="blur-md scale-105"
|
||||
priority
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/40"/>
|
||||
</div>
|
||||
|
||||
{/* Main content */}
|
||||
<div className="relative z-10 flex justify-center flex-col w-[90%] sm:w-[80%] h-full mx-auto">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 items-center gap-12">
|
||||
{/* Text Content */}
|
||||
<div>
|
||||
<motion.h1
|
||||
className="text-3xl sm:text-4xl md:text-5xl mt-6 mb-6 font-bold text-white"
|
||||
initial={{opacity: 0, y: 30}}
|
||||
animate={{opacity: 1, y: 0}}
|
||||
transition={{duration: 0.6, ease: 'easeOut'}}
|
||||
>
|
||||
Rhein-Software Development
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
className="text-lg md:text-xl text-gray-400"
|
||||
initial={{opacity: 0, y: 30}}
|
||||
animate={{opacity: 1, y: 0}}
|
||||
transition={{duration: 0.6, delay: 0.2, ease: 'easeOut'}}
|
||||
>
|
||||
Digitale Lösungen für dein Unternehmen.
|
||||
</motion.p>
|
||||
|
||||
<motion.p
|
||||
className="mt-4 text-lg md:text-xl font-semibold text-white"
|
||||
initial={{opacity: 0}}
|
||||
animate={{opacity: 1}}
|
||||
transition={{delay: 0.9, duration: 0.6}}
|
||||
>
|
||||
<Typewriter
|
||||
words={['Beratung', 'Entwicklung', 'Wartung', 'Fehlerbehebung']}
|
||||
loop={true}
|
||||
cursor
|
||||
cursorStyle="_"
|
||||
typeSpeed={60}
|
||||
deleteSpeed={40}
|
||||
delaySpeed={1500}
|
||||
/>
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Floating Image */}
|
||||
<motion.div
|
||||
className="hidden lg:block"
|
||||
initial={{opacity: 0, y: 30}}
|
||||
animate={{opacity: 1, y: 0}}
|
||||
transition={{duration: 0.8, ease: 'easeOut', delay: 0.3}}
|
||||
>
|
||||
<motion.div
|
||||
animate={{y: [0, -10, 0]}}
|
||||
transition={{
|
||||
duration: 4,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
}}
|
||||
className="animate-float"
|
||||
>
|
||||
<Image
|
||||
src="/images/hero.png"
|
||||
alt="hero graphic"
|
||||
width={700}
|
||||
height={700}
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
27
components/Home/Home.tsx
Normal file
27
components/Home/Home.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
'use client';
|
||||
|
||||
import React from "react";
|
||||
import Hero from "./Hero/Hero";
|
||||
import About from "@/components/Home/About/About";
|
||||
import ContactCTA from "@/components/Home/Contact/ContactCTA";
|
||||
import {SectionDivider1, SectionDivider2} from "@/components/Helper/SectionDivider";
|
||||
import HomeServices from "@/components/Home/HomeServices";
|
||||
import TechStack from "@/components/Home/TechStack";
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<div className="overflow-hidden">
|
||||
<Hero/>
|
||||
<SectionDivider1/>
|
||||
<About/>
|
||||
<SectionDivider2/>
|
||||
<HomeServices/>
|
||||
<SectionDivider1/>
|
||||
<TechStack/>
|
||||
<SectionDivider2/>
|
||||
<ContactCTA/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
115
components/Home/HomeServices.tsx
Normal file
115
components/Home/HomeServices.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
FiServer,
|
||||
FiTool,
|
||||
FiMonitor,
|
||||
FiZap,
|
||||
FiArrowRight,
|
||||
} from 'react-icons/fi';
|
||||
import {motion} from 'framer-motion';
|
||||
|
||||
const services = [
|
||||
{
|
||||
title: 'Beratung',
|
||||
icon: <FiMonitor size={24}/>,
|
||||
description:
|
||||
'Strategische und technische Beratung rund um digitale Produkte und Prozesse. Wir analysieren bestehende Systeme, identifizieren Potenziale und helfen dir, die passende Architektur für dein Projekt zu finden.',
|
||||
},
|
||||
{
|
||||
title: 'Entwicklung',
|
||||
icon: <FiZap size={24}/>,
|
||||
description:
|
||||
'Individuelle Softwareentwicklung – skalierbar, wartbar, zukunftssicher. Ob Web-App, API oder internes Tool: Wir setzen moderne Technologien ein, um genau die Lösung zu bauen, die du brauchst.',
|
||||
},
|
||||
{
|
||||
title: 'Managed Services',
|
||||
icon: <FiServer size={24}/>,
|
||||
description:
|
||||
'Wir betreuen Infrastruktur, Server und Systeme – verlässlich und performant. Unser Team kümmert sich um Hosting, Monitoring, Backups und sorgt für einen reibungslosen Betrieb deiner Plattform.',
|
||||
},
|
||||
{
|
||||
title: 'Fehlerbehebung',
|
||||
icon: <FiTool size={24}/>,
|
||||
description:
|
||||
'Schnelle Hilfe bei Bugs, Performance-Problemen oder Sicherheitslücken. Wir analysieren Probleme, beheben sie gezielt und sorgen dafür, dass dein System wieder stabil läuft – langfristig und zuverlässig.',
|
||||
},
|
||||
];
|
||||
|
||||
const HomeServices = () => {
|
||||
return (
|
||||
<section
|
||||
className="w-full py-24 bg-[var(--primary-bg)] transition-theme"
|
||||
id="leistungen"
|
||||
>
|
||||
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
|
||||
{/* Heading */}
|
||||
<motion.h2
|
||||
className="text-3xl md:text-4xl font-bold mb-1 text-left text-[var(--primary-text)]"
|
||||
initial={{opacity: 0, y: 20}}
|
||||
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}}
|
||||
/>
|
||||
|
||||
{/* Service Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{services.map((service, index) => (
|
||||
<motion.div
|
||||
key={service.title}
|
||||
className="p-6 rounded-xl border border-[var(--secondary-bg)] bg-[var(--secondary-bg)] shadow-md"
|
||||
whileHover={{
|
||||
scale: 1.03,
|
||||
boxShadow: '0px 10px 20px rgba(0,0,0,0.1)',
|
||||
}}
|
||||
initial={{opacity: 0, y: 30}}
|
||||
whileInView={{opacity: 1, y: 0}}
|
||||
viewport={{once: true}}
|
||||
transition={{
|
||||
duration: 0.4,
|
||||
delay: index * 0.1,
|
||||
ease: 'easeOut',
|
||||
}}
|
||||
>
|
||||
<div className="mb-3 text-blue-600">{service.icon}</div>
|
||||
<h3 className="text-xl font-semibold mb-2 text-[var(--primary-text)]">
|
||||
{service.title}
|
||||
</h3>
|
||||
<p className="text-sm text-[var(--secondary-text)] leading-relaxed">
|
||||
{service.description}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Weitere Leistungen */}
|
||||
<motion.div
|
||||
className="mt-10 flex justify-end"
|
||||
initial={{opacity: 0}}
|
||||
whileInView={{opacity: 1}}
|
||||
viewport={{once: true}}
|
||||
transition={{duration: 0.4, delay: 0.3}}
|
||||
>
|
||||
<a
|
||||
href="/services"
|
||||
className="text-sm font-semibold text-blue-600 hover:underline flex items-center gap-1"
|
||||
>
|
||||
Weitere Leistungen <FiArrowRight size={16}/>
|
||||
</a>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeServices;
|
||||
75
components/Home/Offer/FullStack.tsx
Normal file
75
components/Home/Offer/FullStack.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
const FullStack = () => {
|
||||
return (
|
||||
<div
|
||||
className="w-[95%] sm:w-[80%] mx-auto items-center grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16 transition-theme">
|
||||
<div className="p-0"
|
||||
>
|
||||
<h1 data-aos="fade-up"
|
||||
className="mt-4 text-xl sm:text-2xl md:text-3xl font-bold"
|
||||
style={{color: "var(--primary-text)"}} // Dynamic text color
|
||||
>
|
||||
💻 Full-Stack Entwicklung
|
||||
</h1>
|
||||
<div data-aos="fade-up"
|
||||
data-aos-delay="200"
|
||||
>
|
||||
<p
|
||||
className="mt-4 text-sm font-medium leading-[2rem]"
|
||||
style={{color: "var(--secondary-text)"}} // Secondary text color
|
||||
>
|
||||
Wir entwickeln skalierbare Backends, performante Frontends und native Apps – genau auf deine
|
||||
Anforderungen zugeschnitten. Dabei setzen wir auf neueste und modernste Technologien, um
|
||||
flexible
|
||||
und zukunftssichere Lösungen zu schaffen.
|
||||
</p>
|
||||
{/* Technology Logos Section */}
|
||||
<h2
|
||||
className="mt-8 text-lg font-semibold"
|
||||
style={{color: "var(--primary-text)"}}
|
||||
>
|
||||
🔧 Tech Stack
|
||||
</h2>
|
||||
<span
|
||||
className="mt-4 text-sm font-medium leading-[2rem]"
|
||||
style={{color: "var(--secondary-text)"}}
|
||||
>
|
||||
Programmiersprachen und Frameworks, die wir für unsere Lösungen einsetzen.
|
||||
</span>
|
||||
<div className="mt-6 flex flex-wrap items-center gap-8">
|
||||
{/* Tech Stack Logos */}
|
||||
{[
|
||||
{src: "/images/flutter_logo.png", label: "Flutter", width: 30, height: 30},
|
||||
{src: "/images/dart_logo.png", label: "Dart", width: 40, height: 40},
|
||||
{src: "/images/java_logo.png", label: "Java", width: 40, height: 40},
|
||||
{src: "/images/nextjs_logo.png", label: "NextJS", width: 40, height: 40},
|
||||
].map(({src, label, width, height}) => (
|
||||
<div key={label} className="flex flex-col items-center">
|
||||
<Image src={src} alt={label} width={width} height={height} className="object-contain"/>
|
||||
<span
|
||||
className="text-xs mt-2"
|
||||
style={{color: "var(--secondary-text)"}} // Match theme
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-aos="fade-up" data-aos-anchor-placement="top-center" className="justify-self-center">
|
||||
<Image
|
||||
src="/images/software_dev.jpg"
|
||||
alt="image"
|
||||
width={200}
|
||||
height={200}
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FullStack;
|
||||
57
components/Home/Offer/ManagedServices.tsx
Normal file
57
components/Home/Offer/ManagedServices.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import {TiChevronRightOutline} from "react-icons/ti";
|
||||
|
||||
const ManagedServices = () => {
|
||||
return (
|
||||
<div
|
||||
className="w-[95%] sm:w-[80%] mx-auto items-center grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="200"
|
||||
>
|
||||
{/* Image Section */}
|
||||
<div
|
||||
data-aos="fade-up"
|
||||
data-aos-anchor-placement="top-center"
|
||||
className="justify-self-center"
|
||||
>
|
||||
<Image
|
||||
src="/images/software_dev.jpg"
|
||||
alt="image"
|
||||
width={200}
|
||||
height={200}
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
{/* Text Content */}
|
||||
<div className="p-0">
|
||||
<h1
|
||||
className="mt-4 text-xl sm:text-2xl md:text-3xl font-bold transition-theme"
|
||||
style={{color: "var(--primary-text)"}}>
|
||||
☁️ Managed Services
|
||||
</h1>
|
||||
<p
|
||||
className="mt-4 text-sm font-medium leading-[2rem] transition-theme"
|
||||
style={{color: "var(--secondary-text)"}}>
|
||||
Wir übernehmen das Hosting und Management deiner Server, damit du dich auf dein
|
||||
Business konzentrieren kannst. Egal ob Cloud-Hosting, dedizierte Server oder hybride Lösungen – wir
|
||||
sorgen für eine sichere, performante und skalierbare Infrastruktur.
|
||||
</p>
|
||||
<ul className="mt-7 space-y-2">
|
||||
{[
|
||||
"Web- & Cloud-Hosting",
|
||||
"Mailserver-Management",
|
||||
"Webserver-Administration",
|
||||
].map((item, index) => (
|
||||
<li key={index} className="flex items-center font-semibold transition-theme">
|
||||
<TiChevronRightOutline className="text-blue-500 mr-2"/>
|
||||
<span style={{color: "var(--primary-text)"}}>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManagedServices;
|
||||
28
components/Home/Offer/Offer.tsx
Normal file
28
components/Home/Offer/Offer.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
import FullStack from "@/components/Home/Offer/FullStack";
|
||||
import ManagedServices from "@/components/Home/Offer/ManagedServices";
|
||||
|
||||
const Offer = () => {
|
||||
return (
|
||||
<div
|
||||
className="transition-theme"
|
||||
style={{
|
||||
backgroundColor: "var(--primary-bg)", // Using CSS variable
|
||||
color: "var(--primary-text)", // Ensuring text color follows theme
|
||||
transition: "background-color 0.5s ease, color 0.5s ease", // Smooth transition
|
||||
}}>
|
||||
<div className="pt-24 pb-16">
|
||||
<h1 className="mt-6 text-2xl md:text-3xl capitalize font-bold text-center"
|
||||
data-aos="fade-up"
|
||||
style={{color: "var(--primary-text)"}}
|
||||
>
|
||||
Was bieten wir?
|
||||
</h1>
|
||||
<FullStack/>
|
||||
<ManagedServices/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Offer;
|
||||
150
components/Home/TechStack.tsx
Normal file
150
components/Home/TechStack.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import {motion} from 'framer-motion';
|
||||
|
||||
const techStack = {
|
||||
row1: [
|
||||
{
|
||||
category: 'Programmiersprachen & Frameworks – Backend',
|
||||
items: [
|
||||
{id: 'java', label: 'Java'},
|
||||
{id: 'dart', label: 'Dart'},
|
||||
{id: 'kotlin', label: 'Kotlin'},
|
||||
{id: 'spring', label: 'Spring'},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Programmiersprachen & Frameworks – Frontend',
|
||||
items: [
|
||||
{id: 'html', label: 'HTML'},
|
||||
{id: 'css', label: 'CSS'},
|
||||
{id: 'bootstrap', label: 'Bootstrap'},
|
||||
{id: 'nextjs', label: 'Next.js'},
|
||||
{id: 'typescript', label: 'TypeScript'},
|
||||
{id: 'flutter', label: 'Flutter'},
|
||||
],
|
||||
},
|
||||
],
|
||||
row2: [
|
||||
{
|
||||
category: 'Betriebssysteme',
|
||||
items: [
|
||||
{id: 'macos', label: 'macOS'},
|
||||
{id: 'debian', label: 'Debian'},
|
||||
{id: 'ubuntu', label: 'Ubuntu'},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Version Control & Collaboration',
|
||||
items: [
|
||||
{id: 'gitlab', label: 'GitLab'},
|
||||
{id: 'outline', label: 'Outline'},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'DevOps & Infrastruktur',
|
||||
items: [
|
||||
{id: 'gitlab-ci', label: 'GitLab CI'},
|
||||
{id: 'docker', label: 'Docker'},
|
||||
{id: 'proxmox', label: 'Proxmox'},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const TechStack = () => {
|
||||
return (
|
||||
<section className="w-full py-20 bg-[var(--secondary-bg)] transition-theme">
|
||||
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
|
||||
<motion.h2
|
||||
className="text-2xl md:text-3xl font-bold mb-1 text-left text-[var(--primary-text)]"
|
||||
initial={{opacity: 0, y: 20}}
|
||||
whileInView={{opacity: 1, y: 0}}
|
||||
viewport={{once: true}}
|
||||
transition={{duration: 0.5}}
|
||||
>
|
||||
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-[var(--secondary-text)]"
|
||||
initial={{opacity: 0, y: 10}}
|
||||
whileInView={{opacity: 1, y: 0}}
|
||||
viewport={{once: true}}
|
||||
transition={{duration: 0.4, delay: 0.2}}
|
||||
>
|
||||
Mit diesen Technologien realisieren wir moderne, leistungsstarke Softwarelösungen.
|
||||
</motion.p>
|
||||
|
||||
{/* Row 1 */}
|
||||
<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>
|
||||
|
||||
{/* Row 2 */}
|
||||
<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 border-[var(--primary-bg)] bg-[var(--primary-bg)] shadow-md transition-theme"
|
||||
initial={{opacity: 0, y: 20}}
|
||||
whileInView={{opacity: 1, y: 0}}
|
||||
whileHover={{
|
||||
scale: 1.03,
|
||||
boxShadow: '0px 10px 20px rgba(0,0,0,0.1)',
|
||||
}}
|
||||
viewport={{once: true}}
|
||||
transition={{duration: 0.4, delay, ease: 'easeOut'}}
|
||||
>
|
||||
<h3 className="text-base font-semibold mb-4 text-[var(--primary-text)]">
|
||||
{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-[var(--secondary-text)]">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default TechStack;
|
||||
99
components/Legal/ImprintComp.tsx
Normal file
99
components/Legal/ImprintComp.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
"use client";
|
||||
|
||||
import SmallHero from "@/components/Helper/SmallHero";
|
||||
import React, {useContext, useEffect} from "react";
|
||||
import {ThemeContext} from "@/components/provider/ThemeProvider";
|
||||
import {themeColors} from "@/components/Helper/ThemeColors";
|
||||
import AOS from "aos";
|
||||
|
||||
const ImprintComp = () => {
|
||||
const {theme} = useContext(ThemeContext);
|
||||
const colors = themeColors[theme];
|
||||
|
||||
useEffect(() => {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
easing: "ease",
|
||||
once: true,
|
||||
anchorPlacement: "top-bottom",
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden transition-colors duration-500"
|
||||
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}>
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="mt-[10vh]">
|
||||
<SmallHero
|
||||
title="Impressum"
|
||||
subtitle=""
|
||||
backgroundImage="/images/contact.png"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Contact Form */}
|
||||
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12">
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-center"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="400"
|
||||
>
|
||||
Schreib uns eine Nachricht
|
||||
</h2>
|
||||
<p data-aos="fade-up" data-aos-delay="600"
|
||||
className="text-center mt-3 text-[var(--secondary-text)]">
|
||||
Wir melden uns schnellstmöglich bei dir!
|
||||
</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) => (
|
||||
<div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
|
||||
<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 focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div data-aos="fade-up" data-aos-delay="300">
|
||||
<label className="block font-semibold">Deine Nachricht</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
placeholder="Schreibe deine Nachricht..."
|
||||
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="text-center" data-aos="fade-up" data-aos-delay="400">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImprintComp;
|
||||
99
components/Legal/PrivacyComp.tsx
Normal file
99
components/Legal/PrivacyComp.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
"use client";
|
||||
|
||||
import SmallHero from "@/components/Helper/SmallHero";
|
||||
import React, {useContext, useEffect} from "react";
|
||||
import {ThemeContext} from "@/components/provider/ThemeProvider";
|
||||
import {themeColors} from "@/components/Helper/ThemeColors";
|
||||
import AOS from "aos";
|
||||
|
||||
const PrivacyComp = () => {
|
||||
const {theme} = useContext(ThemeContext);
|
||||
const colors = themeColors[theme];
|
||||
|
||||
useEffect(() => {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
easing: "ease",
|
||||
once: true,
|
||||
anchorPlacement: "top-bottom",
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden transition-colors duration-500"
|
||||
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}>
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="mt-[10vh]">
|
||||
<SmallHero
|
||||
title="Datenschutz"
|
||||
subtitle=""
|
||||
backgroundImage="/images/contact.png"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Contact Form */}
|
||||
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12">
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-center"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="400"
|
||||
>
|
||||
Schreib uns eine Nachricht
|
||||
</h2>
|
||||
<p data-aos="fade-up" data-aos-delay="600"
|
||||
className="text-center mt-3 text-[var(--secondary-text)]">
|
||||
Wir melden uns schnellstmöglich bei dir!
|
||||
</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) => (
|
||||
<div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
|
||||
<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 focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div data-aos="fade-up" data-aos-delay="300">
|
||||
<label className="block font-semibold">Deine Nachricht</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
placeholder="Schreibe deine Nachricht..."
|
||||
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="text-center" data-aos="fade-up" data-aos-delay="400">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrivacyComp;
|
||||
98
components/Legal/RevocationComp.tsx
Normal file
98
components/Legal/RevocationComp.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import SmallHero from "@/components/Helper/SmallHero";
|
||||
import React, {useContext, useEffect} from "react";
|
||||
import {ThemeContext} from "@/components/provider/ThemeProvider";
|
||||
import {themeColors} from "@/components/Helper/ThemeColors";
|
||||
import AOS from "aos";
|
||||
|
||||
const RevocationComp = () => {
|
||||
const {theme} = useContext(ThemeContext);
|
||||
const colors = themeColors[theme];
|
||||
|
||||
useEffect(() => {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
easing: "ease",
|
||||
once: true,
|
||||
anchorPlacement: "top-bottom",
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden transition-colors duration-500"
|
||||
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}>
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="mt-[10vh]">
|
||||
<SmallHero
|
||||
title="Widerruf"
|
||||
subtitle=""
|
||||
backgroundImage="/images/contact.png"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Contact Form */}
|
||||
<div className="mt-16 w-[90%] sm:w-[80%] mx-auto py-12">
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-center"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="400"
|
||||
>
|
||||
Schreib uns eine Nachricht
|
||||
</h2>
|
||||
<p data-aos="fade-up" data-aos-delay="600"
|
||||
className="text-center mt-3 text-[var(--secondary-text)]">
|
||||
Wir melden uns schnellstmöglich bei dir!
|
||||
</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) => (
|
||||
<div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
|
||||
<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 focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div data-aos="fade-up" data-aos-delay="300">
|
||||
<label className="block font-semibold">Deine Nachricht</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
placeholder="Schreibe deine Nachricht..."
|
||||
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="text-center" data-aos="fade-up" data-aos-delay="400">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);};
|
||||
|
||||
export default RevocationComp;
|
||||
99
components/Legal/TermsOfUseComp.tsx
Normal file
99
components/Legal/TermsOfUseComp.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
"use client";
|
||||
|
||||
import SmallHero from "@/components/Helper/SmallHero";
|
||||
import React, {useContext, useEffect} from "react";
|
||||
import {ThemeContext} from "@/components/provider/ThemeProvider";
|
||||
import {themeColors} from "@/components/Helper/ThemeColors";
|
||||
import AOS from "aos";
|
||||
|
||||
const TermsOfUseComp = () => {
|
||||
const {theme} = useContext(ThemeContext);
|
||||
const colors = themeColors[theme];
|
||||
|
||||
useEffect(() => {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
easing: "ease",
|
||||
once: true,
|
||||
anchorPlacement: "top-bottom",
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden transition-colors duration-500"
|
||||
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}>
|
||||
|
||||
{/* 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">
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-center"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="400"
|
||||
>
|
||||
Schreib uns eine Nachricht
|
||||
</h2>
|
||||
<p data-aos="fade-up" data-aos-delay="600"
|
||||
className="text-center mt-3 text-[var(--secondary-text)]">
|
||||
Wir melden uns schnellstmöglich bei dir!
|
||||
</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) => (
|
||||
<div key={index} data-aos="fade-up" data-aos-delay={index * 100}>
|
||||
<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 focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div data-aos="fade-up" data-aos-delay="300">
|
||||
<label className="block font-semibold">Deine Nachricht</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
placeholder="Schreibe deine Nachricht..."
|
||||
className="w-full p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||
style={{
|
||||
backgroundColor: colors.inputFieldBg,
|
||||
border: `1px solid ${colors.inputBorder}`,
|
||||
color: colors.primaryText
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="text-center" data-aos="fade-up" data-aos-delay="400">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TermsOfUseComp;
|
||||
114
components/Navbar/DesktopNav.tsx
Normal file
114
components/Navbar/DesktopNav.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
"use client";
|
||||
|
||||
import {usePathname} from "next/navigation";
|
||||
import {navLinks} from "@/constant/Constant";
|
||||
import Link from "next/link";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import {HiBars3BottomRight} from "react-icons/hi2";
|
||||
import {ThemeContext} from "@/components/provider/ThemeProvider";
|
||||
import {themeColors} from "@/components/Helper/ThemeColors";
|
||||
|
||||
type Props = {
|
||||
openNav: () => void;
|
||||
};
|
||||
|
||||
const Nav = ({openNav}: Props) => {
|
||||
const [navBg, setNavBg] = useState(false);
|
||||
const [navHeight, setNavHeight] = useState("h-[10vh]");
|
||||
const [contentSize, setContentSize] = useState("text-base md:text-lg");
|
||||
const [buttonSize, setButtonSize] = useState("md:px-6 md:py-2 px-4 py-1 text-sm");
|
||||
const {theme, toggleTheme} = useContext(ThemeContext);
|
||||
const colors = themeColors[theme];
|
||||
const pathname = usePathname();
|
||||
|
||||
const navColorClass = theme === "dark" || !navBg ? "text-white" : "text-black";
|
||||
|
||||
useEffect(() => {
|
||||
const handler = () => {
|
||||
if (window.scrollY >= 90) {
|
||||
setNavBg(true);
|
||||
setNavHeight("h-[8vh]");
|
||||
setContentSize("text-sm md:text-base");
|
||||
setButtonSize("md:px-5 md:py-1.5 px-3 py-1 text-xs");
|
||||
} else {
|
||||
setNavBg(false);
|
||||
setNavHeight("h-[10vh]");
|
||||
setContentSize("text-base md:text-lg");
|
||||
setButtonSize("md:px-6 md:py-2 px-4 py-1 text-sm");
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handler);
|
||||
return () => window.removeEventListener("scroll", handler);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`fixed w-full transition-all duration-300 ease-in-out ${navHeight} z-[1000] ${
|
||||
navBg ? "shadow-md" : ""
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: navBg ? "var(--nav-bg)" : "transparent",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="flex items-center h-full justify-between w-[90%] xl:w-[80%] mx-auto transition-all duration-300 ease-in-out">
|
||||
{/* LOGO */}
|
||||
<h1 className={`${contentSize} font-bold transition-all duration-300 ease-in-out ${navColorClass}`}>
|
||||
<span className="text-lg md:text-xl text-pink-700">R</span>hein Software
|
||||
</h1>
|
||||
|
||||
{/* Desktop Nav Links */}
|
||||
<div className="hidden lg:flex items-center space-x-6 transition-all duration-300 ease-in-out">
|
||||
{navLinks.map((link) => (
|
||||
<Link href={link.url} key={link.id}>
|
||||
<p
|
||||
className={`relative group ${contentSize} uppercase transition-all duration-300 ease-in-out ${
|
||||
pathname === link.url
|
||||
? "text-white font-bold"
|
||||
: "text-gray-300 font-medium"
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
{pathname !== link.url && (
|
||||
<span
|
||||
className="absolute bottom-0 left-0 w-full h-[2px] bg-current transform transition-transform duration-300 origin-right scale-x-0 group-hover:scale-x-100"
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Side Buttons */}
|
||||
<div className="flex items-center space-x-3 transition-all duration-300 ease-in-out">
|
||||
{/* Contact Button */}
|
||||
<Link href="/contact">
|
||||
<button
|
||||
className={`${buttonSize} text-white font-semibold bg-blue-700 hover:bg-blue-900 transition-all duration-300 ease-in-out rounded-full`}
|
||||
>
|
||||
Kontakt
|
||||
</button>
|
||||
</Link>
|
||||
|
||||
{/* Theme Toggle Button */}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className={`w-7 h-7 flex items-center justify-center rounded-full transition-all duration-300 ease-in-out ${navColorClass}`}
|
||||
style={{backgroundColor: colors.secondaryBg}}
|
||||
>
|
||||
{theme === "dark" ? "🌙" : "☀️"}
|
||||
</button>
|
||||
|
||||
{/* Burger Menu (for mobile) */}
|
||||
<HiBars3BottomRight
|
||||
onClick={openNav}
|
||||
className={`w-6 h-6 cursor-pointer lg:hidden transition-all duration-300 ease-in-out ${navColorClass}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
62
components/Navbar/MobileNav.tsx
Normal file
62
components/Navbar/MobileNav.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { navLinks } from "@/constant/Constant";
|
||||
import Link from "next/link";
|
||||
import React, { useContext } from "react";
|
||||
import { CgClose } from "react-icons/cg";
|
||||
import { ThemeContext } from "@/components/provider/ThemeProvider";
|
||||
import { themeColors } from "@/components/Helper/ThemeColors";
|
||||
|
||||
type Props = {
|
||||
showNav: boolean;
|
||||
closeNav: () => void;
|
||||
};
|
||||
|
||||
const MobileNav = ({ closeNav, showNav }: Props) => {
|
||||
const navOpen = showNav ? "translate-y-0 opacity-100" : "-translate-y-20 opacity-0 pointer-events-none";
|
||||
const { theme, toggleTheme } = useContext(ThemeContext);
|
||||
const colors = themeColors[theme];
|
||||
return (
|
||||
<div className="lg:hidden">
|
||||
{/* overlay background */}
|
||||
<div
|
||||
className={`fixed inset-0 z-[10000] transition-opacity duration-500 ${
|
||||
showNav ? "opacity-60 bg-black" : "opacity-0 pointer-events-none"
|
||||
}`}
|
||||
onClick={closeNav}
|
||||
/>
|
||||
|
||||
{/* nav menu */}
|
||||
<div
|
||||
className={`fixed top-0 left-0 w-full z-[10006] transform ${navOpen} transition-all duration-500 ease-in-out text-[var(--primary-text)] shadow-md rounded-b-2xl`}
|
||||
style={{ backgroundColor: theme === "dark" ? "#2A2A2A" : "#ffffff", color: theme === "dark" ? "#f5f5f5" : "#1a1a1a" }}
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center py-8 space-y-4 px-4 relative">
|
||||
{/* Close icon */}
|
||||
<CgClose
|
||||
onClick={closeNav}
|
||||
className="absolute top-4 right-6 sm:right-8 sm:w-7 sm:h-7 w-6 h-6 cursor-pointer p-1"
|
||||
style={{ color: colors.primaryText }}
|
||||
/>
|
||||
|
||||
{navLinks.map((link) => (
|
||||
<Link href={link.url} key={link.id}>
|
||||
<p className="nav__link uppercase text-[14px] sm:text-[16px] border-b pb-1 border-gray-400 transition-all duration-300 ease-in-out hover:scale-105">
|
||||
{link.label}
|
||||
</p>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* Theme toggle button */}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="mt-4 w-8 h-8 flex items-center justify-center rounded-full border border-gray-400 transition-all duration-300"
|
||||
style={{ backgroundColor: colors.secondaryBg, color: colors.primaryText }}
|
||||
>
|
||||
{theme === "dark" ? "🌙" : "☀️"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileNav;
|
||||
24
components/Navbar/Nav.tsx
Normal file
24
components/Navbar/Nav.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import React, {useState} from "react";
|
||||
import DesktopNav from "./DesktopNav";
|
||||
import MobileNav from "./MobileNav";
|
||||
|
||||
const Nav = () => {
|
||||
const [showNav, setShowNav] = useState(false);
|
||||
const handleNavShow = () => {
|
||||
setShowNav(true);
|
||||
};
|
||||
const handleNavHide = () => {
|
||||
setShowNav(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DesktopNav openNav={handleNavShow}/>
|
||||
<MobileNav showNav={showNav} closeNav={handleNavHide}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
54
components/provider/ThemeProvider.tsx
Normal file
54
components/provider/ThemeProvider.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useEffect, useState } from "react";
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
// Define theme options
|
||||
type ThemeType = "light" | "dark";
|
||||
|
||||
export const ThemeContext = createContext<{
|
||||
theme: ThemeType;
|
||||
toggleTheme: () => void;
|
||||
}>({
|
||||
theme: "light",
|
||||
toggleTheme: () => {},
|
||||
});
|
||||
|
||||
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [theme, setTheme] = useState<ThemeType>("light");
|
||||
|
||||
useEffect(() => {
|
||||
// Get theme from cookies or system preference
|
||||
const savedTheme = Cookies.get("theme") as ThemeType | undefined;
|
||||
if (savedTheme === "dark" || savedTheme === "light") {
|
||||
setTheme(savedTheme);
|
||||
} else {
|
||||
// Detect system preference
|
||||
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
setTheme(prefersDark ? "dark" : "light");
|
||||
Cookies.set("theme", prefersDark ? "dark" : "light", { expires: 365 });
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Apply the transition effect when theme changes
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.add("transition-theme");
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
|
||||
return () => {
|
||||
document.documentElement.classList.remove("transition-theme");
|
||||
};
|
||||
}, [theme]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
const newTheme: ThemeType = theme === "dark" ? "light" : "dark";
|
||||
setTheme(newTheme);
|
||||
Cookies.set("theme", newTheme, { expires: 365 });
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ theme, toggleTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user