fix: updated contact information in imprint & /contact page

This commit is contained in:
Melvin Ragusa
2026-01-24 10:47:40 +01:00
parent 516f29f312
commit 77fd62447c
3 changed files with 77 additions and 60 deletions

View File

@@ -1,6 +1,6 @@
import { Modal } from '../ui/Modal'; import { Modal } from "../ui/Modal";
import { useTranslation } from '../../i18n'; import { useTranslation } from "../../i18n";
import styles from './ImpressumModal.module.css'; import styles from "./ImpressumModal.module.css";
interface ImpressumModalProps { interface ImpressumModalProps {
isOpen: boolean; isOpen: boolean;
@@ -16,8 +16,10 @@ export function ImpressumModal({ isOpen, onClose }: ImpressumModalProps) {
<section> <section>
<h3>Angaben gemäß § 5 TMG</h3> <h3>Angaben gemäß § 5 TMG</h3>
<p> <p>
Melvin Ragusa<br /> Melvin Ragusa
Provinzialstraße 177<br /> <br />
Provinzialstraße 177
<br />
44388 Dortmund 44388 Dortmund
</p> </p>
</section> </section>
@@ -25,24 +27,31 @@ export function ImpressumModal({ isOpen, onClose }: ImpressumModalProps) {
<section> <section>
<h3>Kontakt</h3> <h3>Kontakt</h3>
<p> <p>
Telefon: 0160 95098973<br /> Telefon: 0160 95098973
E-Mail: info@ragusa-it.dev <br />
E-Mail: kontakt@ragusa-it.dev
</p> </p>
</section> </section>
<section> <section>
<h3>Umsatzsteuer-ID</h3> <h3>Umsatzsteuer-ID</h3>
<p> <p>
Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:<br /> Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:
<em>Als Kleinunternehmer im Sinne von § 19 Abs. 1 UStG wird keine Umsatzsteuer berechnet.</em> <br />
<em>
Als Kleinunternehmer im Sinne von § 19 Abs. 1 UStG wird keine
Umsatzsteuer berechnet.
</em>
</p> </p>
</section> </section>
<section> <section>
<h3>Redaktionell verantwortlich</h3> <h3>Redaktionell verantwortlich</h3>
<p> <p>
Melvin Ragusa<br /> Melvin Ragusa
Provinzialstraße 177<br /> <br />
Provinzialstraße 177
<br />
44388 Dortmund 44388 Dortmund
</p> </p>
</section> </section>

View File

@@ -1,10 +1,7 @@
import { type ReactNode, useEffect } from 'react'; import { type ReactNode, useEffect } from "react";
import { createPortal } from 'react-dom'; import { createPortal } from "react-dom";
import { motion, AnimatePresence } from 'motion/react'; import { motion, AnimatePresence } from "motion/react";
import styles from './Modal.module.css'; import styles from "./Modal.module.css";
// I need to check if lucide-react is installed.
// Based on package list, react-icons is installed.
interface ModalProps { interface ModalProps {
isOpen: boolean; isOpen: boolean;
@@ -17,22 +14,22 @@ export function Modal({ isOpen, onClose, title, children }: ModalProps) {
// Prevent body scroll when modal is open // Prevent body scroll when modal is open
useEffect(() => { useEffect(() => {
if (isOpen) { if (isOpen) {
document.body.style.overflow = 'hidden'; document.body.style.overflow = "hidden";
} else { } else {
document.body.style.overflow = 'unset'; document.body.style.overflow = "unset";
} }
return () => { return () => {
document.body.style.overflow = 'unset'; document.body.style.overflow = "unset";
}; };
}, [isOpen]); }, [isOpen]);
// Close on escape key // Close on escape key
useEffect(() => { useEffect(() => {
const handleEscape = (e: KeyboardEvent) => { const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose(); if (e.key === "Escape") onClose();
}; };
window.addEventListener('keydown', handleEscape); window.addEventListener("keydown", handleEscape);
return () => window.removeEventListener('keydown', handleEscape); return () => window.removeEventListener("keydown", handleEscape);
}, [onClose]); }, [onClose]);
return createPortal( return createPortal(
@@ -88,6 +85,6 @@ export function Modal({ isOpen, onClose, title, children }: ModalProps) {
</motion.div> </motion.div>
)} )}
</AnimatePresence>, </AnimatePresence>,
document.body document.body,
); );
} }

View File

@@ -1,10 +1,10 @@
import { useState, type FormEvent } from 'react'; import { useState, type FormEvent } from "react";
import { motion } from 'motion/react'; import { motion } from "motion/react";
import emailjs from '@emailjs/browser'; import emailjs from "@emailjs/browser";
import { useTranslation } from '../i18n'; import { useTranslation } from "../i18n";
import { config } from '../config'; import { config } from "../config";
import { Button, Input, Textarea } from '../components/ui'; import { Button, Input, Textarea } from "../components/ui";
import styles from './Contact.module.css'; import styles from "./Contact.module.css";
interface FormData { interface FormData {
name: string; name: string;
@@ -23,34 +23,36 @@ interface FormErrors {
export function Contact() { export function Contact() {
const { t } = useTranslation(); const { t } = useTranslation();
const [formData, setFormData] = useState<FormData>({ const [formData, setFormData] = useState<FormData>({
name: '', name: "",
email: '', email: "",
subject: '', subject: "",
message: '', message: "",
}); });
const [errors, setErrors] = useState<FormErrors>({}); const [errors, setErrors] = useState<FormErrors>({});
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [submitStatus, setSubmitStatus] = useState<'idle' | 'success' | 'error'>('idle'); const [submitStatus, setSubmitStatus] = useState<
"idle" | "success" | "error"
>("idle");
const validateForm = (): boolean => { const validateForm = (): boolean => {
const newErrors: FormErrors = {}; const newErrors: FormErrors = {};
if (!formData.name.trim()) { if (!formData.name.trim()) {
newErrors.name = 'Required'; newErrors.name = "Required";
} }
if (!formData.email.trim()) { if (!formData.email.trim()) {
newErrors.email = 'Required'; newErrors.email = "Required";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = 'Invalid email'; newErrors.email = "Invalid email";
} }
if (!formData.subject.trim()) { if (!formData.subject.trim()) {
newErrors.subject = 'Required'; newErrors.subject = "Required";
} }
if (!formData.message.trim()) { if (!formData.message.trim()) {
newErrors.message = 'Required'; newErrors.message = "Required";
} }
setErrors(newErrors); setErrors(newErrors);
@@ -63,7 +65,7 @@ export function Contact() {
if (!validateForm()) return; if (!validateForm()) return;
setIsSubmitting(true); setIsSubmitting(true);
setSubmitStatus('idle'); setSubmitStatus("idle");
try { try {
const templateParams = { const templateParams = {
@@ -79,7 +81,7 @@ export function Contact() {
config.emailJs.serviceId, config.emailJs.serviceId,
config.emailJs.templateIdAdmin, config.emailJs.templateIdAdmin,
templateParams, templateParams,
{ publicKey: config.emailJs.publicKey } { publicKey: config.emailJs.publicKey },
); );
// Send Auto-reply to User // Send Auto-reply to User
@@ -87,14 +89,14 @@ export function Contact() {
config.emailJs.serviceId, config.emailJs.serviceId,
config.emailJs.templateIdUser, config.emailJs.templateIdUser,
templateParams, templateParams,
{ publicKey: config.emailJs.publicKey } { publicKey: config.emailJs.publicKey },
); );
setSubmitStatus('success'); setSubmitStatus("success");
setFormData({ name: '', email: '', subject: '', message: '' }); setFormData({ name: "", email: "", subject: "", message: "" });
} catch (error) { } catch (error) {
console.error('EmailJS Error:', error); console.error("EmailJS Error:", error);
setSubmitStatus('error'); setSubmitStatus("error");
} finally { } finally {
setIsSubmitting(false); setIsSubmitting(false);
} }
@@ -141,7 +143,7 @@ export function Contact() {
label={t.contact.form.name} label={t.contact.form.name}
placeholder={t.contact.form.namePlaceholder} placeholder={t.contact.form.namePlaceholder}
value={formData.name} value={formData.name}
onChange={(e) => handleChange('name', e.target.value)} onChange={(e) => handleChange("name", e.target.value)}
error={errors.name} error={errors.name}
/> />
@@ -150,7 +152,7 @@ export function Contact() {
type="email" type="email"
placeholder={t.contact.form.emailPlaceholder} placeholder={t.contact.form.emailPlaceholder}
value={formData.email} value={formData.email}
onChange={(e) => handleChange('email', e.target.value)} onChange={(e) => handleChange("email", e.target.value)}
error={errors.email} error={errors.email}
/> />
@@ -158,7 +160,7 @@ export function Contact() {
label={t.contact.form.subject} label={t.contact.form.subject}
placeholder={t.contact.form.subjectPlaceholder} placeholder={t.contact.form.subjectPlaceholder}
value={formData.subject} value={formData.subject}
onChange={(e) => handleChange('subject', e.target.value)} onChange={(e) => handleChange("subject", e.target.value)}
error={errors.subject} error={errors.subject}
/> />
@@ -166,7 +168,7 @@ export function Contact() {
label={t.contact.form.message} label={t.contact.form.message}
placeholder={t.contact.form.messagePlaceholder} placeholder={t.contact.form.messagePlaceholder}
value={formData.message} value={formData.message}
onChange={(e) => handleChange('message', e.target.value)} onChange={(e) => handleChange("message", e.target.value)}
error={errors.message} error={errors.message}
rows={6} rows={6}
/> />
@@ -178,10 +180,12 @@ export function Contact() {
isLoading={isSubmitting} isLoading={isSubmitting}
disabled={isSubmitting} disabled={isSubmitting}
> >
{isSubmitting ? t.contact.form.sending : t.contact.form.submit} {isSubmitting
? t.contact.form.sending
: t.contact.form.submit}
</Button> </Button>
{submitStatus === 'success' && ( {submitStatus === "success" && (
<motion.p <motion.p
className={styles.success} className={styles.success}
initial={{ opacity: 0, y: 10 }} initial={{ opacity: 0, y: 10 }}
@@ -191,7 +195,7 @@ export function Contact() {
</motion.p> </motion.p>
)} )}
{submitStatus === 'error' && ( {submitStatus === "error" && (
<motion.p <motion.p
className={styles.error} className={styles.error}
initial={{ opacity: 0, y: 10 }} initial={{ opacity: 0, y: 10 }}
@@ -215,14 +219,21 @@ export function Contact() {
<div className={styles.infoList}> <div className={styles.infoList}>
<div className={styles.infoItem}> <div className={styles.infoItem}>
<div className={styles.infoIcon}> <div className={styles.infoIcon}>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> <svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
>
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" /> <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
<polyline points="22,6 12,13 2,6" /> <polyline points="22,6 12,13 2,6" />
</svg> </svg>
</div> </div>
<div> <div>
<h3>{t.contact.info.email}</h3> <h3>{t.contact.info.email}</h3>
<a href="mailto:info@ragusa-it.dev">info@ragusa-it.dev</a> <a href="mailto:kontakt@ragusa-it.dev">
kontakt@ragusa-it.dev
</a>
</div> </div>
</div> </div>