Add new Kontakt page with motion effects, integrated Google Maps embed, and a contact form using shadcn UI components. Update dependencies to include @radix-ui/react-select and related packages. Refactor KontaktForm to redirect to the new Kontakt page.

This commit is contained in:
2025-06-26 10:55:05 +09:00
parent 0db35f0138
commit 11281ba316
4 changed files with 309 additions and 38 deletions

View File

@@ -0,0 +1,101 @@
"use client";
import { motion } from "framer-motion";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
export default function KontaktPage() {
return (
<section className="bg-white dark:bg-gray-950">
{/* Kontaktbereich */}
<div className="py-20 px-4 bg-white dark:bg-gray-950">
<div className="max-w-6xl mx-auto grid md:grid-cols-2 gap-12 items-start">
{/* Textbereich */}
<motion.div
initial={{ opacity: 0, x: -30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
>
<h2 className="text-2xl md:text-3xl font-bold text-left text-gray-900 dark:text-white mb-4">
Kontaktinformationen
</h2>
<p className="text-lg text-gray-700 dark:text-gray-300 mb-6 leading-relaxed">
Sie möchten einen Termin vereinbaren oder haben Fragen zu meinen Leistungen?
Senden Sie mir gern eine Nachricht über das Kontaktformular oder melden Sie sich direkt per E-Mail.
</p>
<div className="text-gray-800 dark:text-gray-300 space-y-2 text-sm">
<p><strong>Telefon:</strong> 030 / 123 456 78</p>
<p><strong>E-Mail:</strong> kontakt@kanzlei-mustermann.de</p>
<p><strong>Adresse:</strong> Musterstraße 1, 10115 Berlin</p>
</div>
</motion.div>
{/* Google Maps */}
<motion.div
className="w-full h-80 rounded-lg overflow-hidden"
initial={{ opacity: 0, x: 30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
>
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2429.994634045578!2d13.388859315790138!3d52.51703617981242!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47a851e3be111111%3A0x35e53e39c16c110b!2sReichstagsgeb%C3%A4ude!5e0!3m2!1sde!2sde!4v1619442878930!5m2!1sde!2sde"
width="100%"
height="100%"
style={{ border: 0 }}
allowFullScreen={false}
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
></iframe>
</motion.div>
</div>
{/* Formular unterhalb */}
<motion.div
className="max-w-4xl mx-auto mt-16"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<form onSubmit={(e) => e.preventDefault()} className="space-y-6 pt-6">
<div className="grid md:grid-cols-2 gap-4">
<div className="md:col-span-1">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Anrede</label>
<Select required>
<SelectTrigger className="w-full">
<SelectValue placeholder="Bitte auswählen..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="herr">Herr</SelectItem>
<SelectItem value="frau">Frau</SelectItem>
<SelectItem value="divers">Divers</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Titel</label>
<Input className="h-12" type="text" placeholder="z.B. Dr." />
</div>
<Input className="md:col-span-1 h-12" type="text" placeholder="Vorname*" required />
<Input className="md:col-span-1 h-12" type="text" placeholder="Nachname*" required />
<Input className="md:col-span-2 h-12" type="text" placeholder="Straße und Hausnummer*" required />
<Input className="md:col-span-1 h-12" type="text" placeholder="PLZ*" required />
<Input className="md:col-span-1 h-12" type="text" placeholder="Ort*" required />
<Input className="md:col-span-1 h-12" type="text" placeholder="Telefon" />
<Input className="md:col-span-1 h-12" type="email" placeholder="E-Mail*" required />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Ihre Nachricht</label>
<Textarea required rows={8} placeholder="Ihre Nachricht" className="text-base h-48" />
</div>
<Button type="submit" className="w-full h-12 text-base">
Senden
</Button>
</form>
</motion.div>
</div>
</section>
);
}

View File

@@ -1,14 +1,15 @@
"use client"; "use client";
import {Input} from "@/components/ui/input";
import {Textarea} from "@/components/ui/textarea";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useRouter } from "next/navigation";
export default function KontaktForm() { export default function KontaktForm() {
const router = useRouter();
return ( return (
<section className="py-20 px-4 bg-[#f9f9f6] dark:bg-[#1a1a1a]"> <section className="py-20 px-4 bg-[#f9f9f6] dark:bg-[#1a1a1a]">
<div className="max-w-6xl mx-auto grid md:grid-cols-2 gap-12 items-start"> <div className="max-w-6xl mx-auto grid md:grid-cols-2 gap-12 items-center">
{/* Text links */} {/* Text links */}
<motion.div <motion.div
initial={{ opacity: 0, x: -30 }} initial={{ opacity: 0, x: -30 }}
@@ -21,9 +22,7 @@ export default function KontaktForm() {
</h2> </h2>
<div className="w-20 h-1 bg-yellow-500 rounded mb-6" /> <div className="w-20 h-1 bg-yellow-500 rounded mb-6" />
<p className="text-lg text-gray-700 dark:text-gray-300 mb-6 leading-relaxed"> <p className="text-lg text-gray-700 dark:text-gray-300 mb-6 leading-relaxed">
Sie haben Fragen oder möchten einen Beratungstermin vereinbaren? Nutzen Sie gern das Sie haben Fragen oder möchten einen Beratungstermin vereinbaren? Nutzen Sie gern das Kontaktformular auf der nächsten Seite oder schreiben Sie mir direkt per E-Mail.
Kontaktformular oder schreiben Sie mir direkt per E-Mail. Ich melde mich schnellstmöglich bei
Ihnen zurück.
</p> </p>
<div className="text-gray-800 dark:text-gray-300 space-y-2 text-sm"> <div className="text-gray-800 dark:text-gray-300 space-y-2 text-sm">
<p><strong>Telefon:</strong> 030 / 123 456 78</p> <p><strong>Telefon:</strong> 030 / 123 456 78</p>
@@ -32,22 +31,18 @@ export default function KontaktForm() {
</div> </div>
</motion.div> </motion.div>
{/* Formular rechts */} {/* CTA rechts */}
<motion.form <motion.div
className="space-y-6"
initial={{ opacity: 0, x: 30 }} initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.2 }} transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }} viewport={{ once: true }}
onSubmit={(e) => e.preventDefault()} // Dummy kann ersetzt werden className="w-full flex justify-center"
> >
<Input type="text" placeholder="Ihr Name" required/> <Button size="lg" className="px-10 py-6 text-base" onClick={() => router.push("/kontakt")}>
<Input type="email" placeholder="Ihre E-Mail" required/> Zum Kontaktformular
<Textarea placeholder="Ihre Nachricht" rows={5} required/>
<Button type="submit" className="w-full">
Nachricht senden
</Button> </Button>
</motion.form> </motion.div>
</div> </div>
</section> </section>
); );

View File

@@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-navigation-menu": "^1.2.13", "@radix-ui/react-navigation-menu": "^1.2.13",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@@ -27,7 +28,6 @@
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@types/react-vertical-timeline-component": "^3.3.6",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.3.4", "eslint-config-next": "15.3.4",
"tailwindcss": "^4", "tailwindcss": "^4",
@@ -1872,6 +1872,44 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@floating-ui/core": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz",
"integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz",
"integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.1",
"@floating-ui/utils": "^0.2.9"
}
},
"node_modules/@floating-ui/react-dom": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz",
"integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==",
"license": "MIT",
"dependencies": {
"@floating-ui/dom": "^1.0.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
"license": "MIT"
},
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.1", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -2605,12 +2643,41 @@
"node": ">=12.4.0" "node": ">=12.4.0"
} }
}, },
"node_modules/@radix-ui/number": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
"integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
"license": "MIT"
},
"node_modules/@radix-ui/primitive": { "node_modules/@radix-ui/primitive": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": { "node_modules/@radix-ui/react-collection": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
@@ -2839,6 +2906,38 @@
} }
} }
}, },
"node_modules/@radix-ui/react-popper": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
"integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-rect": "1.1.1",
"@radix-ui/react-use-size": "1.1.1",
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-portal": { "node_modules/@radix-ui/react-portal": {
"version": "1.1.9", "version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
@@ -2910,6 +3009,49 @@
} }
} }
}, },
"node_modules/@radix-ui/react-select": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz",
"integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.10",
"@radix-ui/react-focus-guards": "1.1.2",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.7",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-visually-hidden": "1.2.3",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": { "node_modules/@radix-ui/react-slot": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
@@ -3028,6 +3170,42 @@
} }
} }
}, },
"node_modules/@radix-ui/react-use-rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
"license": "MIT",
"dependencies": {
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-size": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-visually-hidden": { "node_modules/@radix-ui/react-visually-hidden": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
@@ -3051,6 +3229,12 @@
} }
} }
}, },
"node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
"node_modules/@rtsao/scc": { "node_modules/@rtsao/scc": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -3418,16 +3602,6 @@
"@types/react": "^19.0.0" "@types/react": "^19.0.0"
} }
}, },
"node_modules/@types/react-vertical-timeline-component": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/@types/react-vertical-timeline-component/-/react-vertical-timeline-component-3.3.6.tgz",
"integrity": "sha512-OUvyPXRjXvUD/SNLO0CW0GbIxVF32Ios5qHecMSfw6kxnK1cPULD9NV80EuqZ3WmS/s6BgbcwmN8k4ISb3akhQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.34.1", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz",

View File

@@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-navigation-menu": "^1.2.13", "@radix-ui/react-navigation-menu": "^1.2.13",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",