Compare commits
4
Commits
1637a4a45d
...
044f788835
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
044f788835 | ||
|
|
2bffca4b33 | ||
|
|
2c1a4c150c | ||
|
|
dc27e508dd |
No files matched your search
+14
-1
@@ -2,7 +2,8 @@ import { Toaster } from "@/components/ui/toaster";
|
||||
import { Toaster as Sonner } from "@/components/ui/sonner";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
|
||||
import { ThemeProvider } from "@/components/ThemeProvider";
|
||||
import { AnalyticsProvider } from "@/components/AnalyticsProvider";
|
||||
import Index from "./pages/Index";
|
||||
@@ -11,11 +12,21 @@ import AGBPage from "./pages/AGB";
|
||||
import ImpressumPage from "./pages/Impressum";
|
||||
import AboutPage from "./pages/About";
|
||||
import WartungsAboPage from "./pages/WartungsAbo";
|
||||
import HostingAboPage from "./pages/HostingAbo";
|
||||
import DatenschutzPage from "./pages/Datenschutz";
|
||||
import NotFound from "./pages/NotFound";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
/** Scrollt bei einem Seitenwechsel nach oben; Anker-Links (URL mit #) bleiben unberührt. */
|
||||
const ScrollToTop = () => {
|
||||
const { pathname, hash } = useLocation();
|
||||
useEffect(() => {
|
||||
if (!hash) window.scrollTo(0, 0);
|
||||
}, [pathname, hash]);
|
||||
return null;
|
||||
};
|
||||
|
||||
const App = () => (
|
||||
<ThemeProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
@@ -23,6 +34,7 @@ const App = () => (
|
||||
<Toaster />
|
||||
<Sonner />
|
||||
<BrowserRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
||||
<ScrollToTop />
|
||||
<AnalyticsProvider>
|
||||
<Routes>
|
||||
<Route path="/" element={<Index />} />
|
||||
@@ -31,6 +43,7 @@ const App = () => (
|
||||
<Route path="/impressum" element={<ImpressumPage />} />
|
||||
<Route path="/ueber-uns" element={<AboutPage />} />
|
||||
<Route path="/wartungs-abo" element={<WartungsAboPage />} />
|
||||
<Route path="/hosting-abo" element={<HostingAboPage />} />
|
||||
<Route path="/datenschutz" element={<DatenschutzPage />} />
|
||||
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
|
||||
@@ -25,6 +25,7 @@ const footerLinks: { title: string; links: FooterLink[] }[] = [
|
||||
{ id: 3, title: "Leistungen", href: "/#services" },
|
||||
{ id: 4, title: "Kontakt", to: "/kontakt" },
|
||||
{ id: 9, title: "Wartungs-Abo", to: "/wartungs-abo" },
|
||||
{ id: 13, title: "Hosting-Abo", to: "/hosting-abo" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
+10
-7
@@ -118,14 +118,17 @@ const AGB = () => {
|
||||
{/* 5. Leistungsmodul C – Hosting */}
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight text-foreground mb-2">
|
||||
5. Leistungsmodul C – Hosting (jährlich)
|
||||
5. Leistungsmodul C – Hosting
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground mb-4">(aktiv, wenn im Angebot enthalten)</p>
|
||||
<ol className="list-decimal list-inside space-y-2 text-muted-foreground leading-relaxed [&>li]:pl-2">
|
||||
<li>WEBklar stellt optional Hosting-Leistungen zur Verfügung.</li>
|
||||
<li>Hostingverträge haben eine jährliche Laufzeit und verlängern sich automatisch, sofern nicht fristgerecht gekündigt wird.</li>
|
||||
<li>WEBklar ist berechtigt, externe Anbieter (z. B. Rechenzentren) einzusetzen.</li>
|
||||
<li>WEBklar übernimmt keine Haftung für Ausfälle externer Anbieter.</li>
|
||||
<li>
|
||||
Hosting bietet WEBklar als Hosting-Abo an. Für das Hosting-Abo und das Wartungs-Abo gelten die{" "}
|
||||
<a href="https://project.webklar.com/abo-bedingungen.html" className="text-foreground underline hover:no-underline">Vertragsbedingungen für die WEBklar-Abos</a>
|
||||
{" "}mit Leistungsbeschreibungen; sie gehen diesen AGB vor.
|
||||
</li>
|
||||
<li>Bestehende Hosting-Verträge mit jährlicher Laufzeit behalten die bei ihrem Abschluss vereinbarten Bedingungen. Abweichend davon können Verbraucher solche Verträge nach einer automatischen Verlängerung jederzeit mit einer Frist von einem Monat kündigen.</li>
|
||||
<li>WEBklar ist berechtigt, externe Anbieter (z. B. Rechenzentren) einzusetzen.</li>
|
||||
</ol>
|
||||
</section>
|
||||
{contractDivider}
|
||||
@@ -190,7 +193,7 @@ const AGB = () => {
|
||||
<ol className="list-decimal list-inside space-y-2 text-muted-foreground leading-relaxed [&>li]:pl-2">
|
||||
<li>Die Vergütung ergibt sich aus dem Angebot.</li>
|
||||
<li>Projektleistungen sind nach Vereinbarung fällig.</li>
|
||||
<li>Hosting-Leistungen sind jährlich im Voraus zu zahlen.</li>
|
||||
<li>Für die Abos gelten die Vertragsbedingungen für die WEBklar-Abos.</li>
|
||||
</ol>
|
||||
</section>
|
||||
{contractDivider}
|
||||
@@ -239,7 +242,7 @@ const AGB = () => {
|
||||
12. Kündigung
|
||||
</h2>
|
||||
<ol className="list-decimal list-inside space-y-2 text-muted-foreground leading-relaxed [&>li]:pl-2">
|
||||
<li>Laufzeitverträge (z. B. Hosting, Wartung) können zum Ende der jeweiligen Laufzeit gekündigt werden.</li>
|
||||
<li>Laufzeitverträge können zum Ende der jeweiligen Laufzeit gekündigt werden. Laufzeit und Kündigung der Abos richten sich nach den Vertragsbedingungen für die WEBklar-Abos.</li>
|
||||
<li>Das Recht zur außerordentlichen Kündigung bleibt unberührt.</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft, Server } from "lucide-react";
|
||||
|
||||
const PORTAL = "https://project.webklar.com";
|
||||
const STEUER = "Endpreis, gemäß § 19 UStG ohne Umsatzsteuer";
|
||||
|
||||
const divider = <div className="my-8 border-t border-border" aria-hidden />;
|
||||
const linkClass = "text-foreground underline hover:no-underline";
|
||||
|
||||
const leistungen = [
|
||||
{
|
||||
titel: "Bereitstellung Ihrer Website",
|
||||
text: "Hosting auf unserem Server in Deutschland, SSL-Zertifikat, tägliche Datensicherung, Überwachung der Erreichbarkeit sowie Updates von Betriebssystem und Server-Software.",
|
||||
},
|
||||
{
|
||||
titel: "1 Domain inklusive",
|
||||
text: "Registrierung auf Ihren Namen, Verlängerung und DNS-Verwaltung (.de, .com, .eu, .net oder .org).",
|
||||
},
|
||||
{
|
||||
titel: "1 E-Mail-Postfach",
|
||||
text: `Unter Ihrer eigenen Domain, 5 GB Speicher, Webmail und Zugriff per Outlook oder Smartphone. Mehr Platz gibt es über den E-Mail-Zusatzspeicher: je 5 GB für 1,00 € pro Monat (${STEUER}).`,
|
||||
},
|
||||
{
|
||||
titel: "Störungen inklusive",
|
||||
text: "Störungen an Server, Domain und Postfach in unserem Verantwortungsbereich beheben wir ohne Berechnung. Servicezeiten Montag bis Freitag 9 bis 17 Uhr (außer an gesetzlichen Feiertagen in Rheinland-Pfalz), Antwort spätestens bis zum Ende des nächsten Werktags.",
|
||||
},
|
||||
];
|
||||
|
||||
const HostingAbo = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<header className="fixed top-0 left-0 right-0 z-50 glass-nav py-4">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link to="/" className="flex items-center gap-2 group">
|
||||
<span className="text-xl font-display font-medium text-foreground tracking-tight">Webklar</span>
|
||||
</Link>
|
||||
<Button asChild variant="ghost" className="text-muted-foreground hover:text-foreground">
|
||||
<Link to="/">
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Zurück
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="pt-32 pb-24">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="mb-12">
|
||||
<div className="label-tag mb-4 flex items-center gap-2">
|
||||
<Server className="w-4 h-4" />
|
||||
Leistungen
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-display font-medium text-foreground tracking-tight uppercase mb-2">
|
||||
Hosting-Abo
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
Ihre statische Website auf unserem Server in Deutschland: 15,00 € pro Monat, 12 Monate Mindestlaufzeit.
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm mt-1">Alle Preise sind Endpreise, gemäß § 19 UStG ohne Umsatzsteuer.</p>
|
||||
</div>
|
||||
|
||||
<article className="space-y-8 text-foreground">
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-4">Das ist enthalten</h2>
|
||||
<ul className="grid gap-5 sm:grid-cols-2">
|
||||
{leistungen.map((l) => (
|
||||
<li key={l.titel} className="rounded-2xl border border-border p-5">
|
||||
<p className="font-medium mb-1">{l.titel}</p>
|
||||
<p className="text-muted-foreground text-sm leading-relaxed">{l.text}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Nur für statische Websites</h2>
|
||||
<div className="text-muted-foreground leading-relaxed space-y-3">
|
||||
<p>
|
||||
Das Hosting-Abo ist für statische Websites bestimmt, die wir erstellt oder zur Betreuung übernommen haben.
|
||||
Statisch heißt: Der Webserver liefert die Dateien Ihrer Website (HTML, CSS, JavaScript, Bilder) unverändert
|
||||
aus. Dazu gehören auch die React- und Vite-Websites, die wir erstellen.
|
||||
</p>
|
||||
<p>
|
||||
Nicht möglich sind ein Content-Management-System, ein Login, eine Datenbank und eigener serverseitiger Code
|
||||
(etwa PHP). Kontakt über E-Mail-Links auf der Website ist möglich. Formulare, die Eingaben an einen Server
|
||||
übermitteln, sind nicht enthalten; eine Lösung dafür bieten wir auf Anfrage gesondert an.
|
||||
</p>
|
||||
<p>
|
||||
Updates und Pflege der Website selbst sind nicht enthalten; bei statischen Websites sind sie für den sicheren
|
||||
Betrieb in der Regel nicht nötig. Braucht Ihre Website dynamische Teile oder wünschen Sie laufende Pflege und
|
||||
eine Stunde Arbeitszeit pro Monat, ist unser{" "}
|
||||
<Link to="/wartungs-abo" className={linkClass}>Wartungs-Abo</Link> das Richtige.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Preis und Laufzeit</h2>
|
||||
<ul className="text-muted-foreground leading-relaxed space-y-1 list-disc pl-5">
|
||||
<li>15,00 € pro Monat, monatlich im Voraus. {STEUER}.</li>
|
||||
<li>Mindestlaufzeit 12 Monate, kündbar zum Ende der Mindestlaufzeit bis einen Monat vorher; danach jederzeit zum Ende des Abrechnungsmonats. Bei Geschäftsaufgabe Sonderkündigung mit einem Monat Frist.</li>
|
||||
<li>Zahlung per Kreditkarte oder SEPA-Lastschrift über unseren Zahlungsdienstleister Stripe.</li>
|
||||
<li>Ein Stundenkontingent ist nicht enthalten. Änderungen an Ihrer Website und andere Aufträge führen wir nur nach Ihrer Freigabe aus: 100 € pro Stunde ({STEUER}), abgerechnet in 15-Minuten-Schritten. Vorher nennen wir Ihnen den geschätzten Aufwand.</li>
|
||||
</ul>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">E-Mail-Zusatzspeicher</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Brauchen Sie mehr Platz für E-Mails, buchen Sie im Kundenportal unter „Verträge“ Zusatzspeicher: Pakete von je
|
||||
5 GB für 1,00 € pro Monat ({STEUER}), bis zu 10 Pakete. Bestellen und verringern können Sie die Pakete dort,
|
||||
sobald Ihr Abo begonnen hat, solange es nicht gekündigt ist und keine Zahlung offen ist. Eine Verringerung wirkt
|
||||
zum Ende des Abrechnungsmonats. Einzelheiten:{" "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#zusatzspeicher`} className={linkClass}>§ 10 der Vertragsbedingungen</a>.
|
||||
</p>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Wechsel ins Wartungs-Abo</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Braucht Ihre Website später dynamische Teile oder möchten Sie Pflege und Arbeitszeit dazubuchen, wechseln Sie
|
||||
im Kundenportal unter „Verträge“ ins{" "}
|
||||
<Link to="/wartungs-abo" className={linkClass}>Wartungs-Abo</Link>, auch während der Mindestlaufzeit: sobald
|
||||
Ihr Hosting-Abo begonnen hat, solange es nicht gekündigt ist und keine Zahlung offen ist. Mit dem Wechsel
|
||||
beginnt eine neue Mindestlaufzeit von 12 Monaten. Einzelheiten:{" "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#wechsel`} className={linkClass}>§ 9 der Vertragsbedingungen</a>.
|
||||
</p>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">So schließen Sie das Abo ab</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Das Hosting-Abo schließen Sie in unserem Kundenportal ab. Den Zugang richten wir Ihnen ein, sprechen Sie uns
|
||||
einfach an. Nach der Anmeldung finden Sie das Angebot im Bereich „Verträge“. Im Kundenportal sehen Sie danach
|
||||
jederzeit Ihre Verträge und Rechnungen, buchen Zusatzspeicher und schicken uns Aufträge über den Chat.
|
||||
</p>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Widerruf und Kündigung</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Verbraucher können jede Bestellung innerhalb von 14 Tagen ab Vertragsschluss widerrufen: den Abschluss des
|
||||
Abos, den Wechsel ins Wartungs-Abo und die Bestellung von Zusatzspeicher. Bereits gezahlte Beträge erstatten
|
||||
wir dann nach der{" "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#widerruf`} className={linkClass}>Widerrufsbelehrung</a>.
|
||||
Kündigen können Sie online ohne Anmeldung oder per E-Mail.
|
||||
</p>
|
||||
<div className="mt-4 flex flex-wrap gap-3">
|
||||
<Button asChild variant="outline" className="rounded-full">
|
||||
<a href={`${PORTAL}/kuendigen.html`}>Verträge hier kündigen</a>
|
||||
</Button>
|
||||
<Button asChild variant="outline" className="rounded-full">
|
||||
<a href={`${PORTAL}/widerrufen.html`}>Vertrag widerrufen</a>
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Unterlagen und Kontakt</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
<a href={`${PORTAL}/abo-bedingungen.html`} className={linkClass}>Vertragsbedingungen für die WEBklar-Abos</a>
|
||||
{" · "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#hosting`} className={linkClass}>Leistungsbeschreibung A (Hosting-Abo)</a>
|
||||
{" · "}
|
||||
<a href={`${PORTAL}/WEBklar-Abo-Vertragsunterlagen.pdf`} className={linkClass}>Vertragsunterlagen als PDF</a>
|
||||
{" · "}
|
||||
<a href={`${PORTAL}/datenschutz.html`} className={linkClass}>Datenschutz</a>
|
||||
<br />
|
||||
WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler · Telefon 0176 23726355 · E-Mail support@webklar.com
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<div className="mt-16 pt-12 border-t border-border flex flex-wrap gap-4">
|
||||
<Button asChild className="btn-minimal rounded-full">
|
||||
<a href={`${PORTAL}/login.html`}>Zum Kundenportal</a>
|
||||
</Button>
|
||||
<Button asChild variant="outline" className="rounded-full">
|
||||
<Link to="/kontakt">Kontakt aufnehmen</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HostingAbo;
|
||||
+64
-30
@@ -3,21 +3,23 @@ import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft, ShieldCheck } from "lucide-react";
|
||||
|
||||
const PORTAL = "https://project.webklar.com";
|
||||
const STEUER = "Endpreis, gemäß § 19 UStG ohne Umsatzsteuer";
|
||||
|
||||
const divider = <div className="my-8 border-t border-border" aria-hidden />;
|
||||
const linkClass = "text-foreground underline hover:no-underline";
|
||||
|
||||
const leistungen = [
|
||||
{
|
||||
titel: "Betrieb & Pflege Ihrer Website",
|
||||
text: "Hosting auf unserem Server in Deutschland, Sicherheitsupdates, SSL-Zertifikat, tägliche Datensicherung und Überwachung der Erreichbarkeit.",
|
||||
text: "Hosting auf unserem Server in Deutschland, Sicherheitsupdates für den Server und für die Software-Komponenten Ihrer Website, die wir einsetzen, SSL-Zertifikat, tägliche Datensicherung und Überwachung der Erreichbarkeit. Für alle Websites und Web-Anwendungen, die wir erstellt oder zur Betreuung übernommen haben, auch mit Content-Management-System, Login oder Datenbank.",
|
||||
},
|
||||
{
|
||||
titel: "1 Domain inklusive",
|
||||
text: "Registrierung auf Ihren Namen, Verlängerung und DNS-Verwaltung (.de, .com, .eu, .net oder .org).",
|
||||
},
|
||||
{
|
||||
titel: "1 E-Mail-Postfach",
|
||||
text: "Mit Ihrer eigenen Domain, 5 GB Speicher, Webmail und Zugriff per Outlook oder Smartphone.",
|
||||
titel: "Beliebig viele Postfächer",
|
||||
text: `Unter Ihrer eigenen Domain, zusammen 5 GB Speicher, Webmail und Zugriff per Outlook oder Smartphone. Mehr Platz gibt es über den E-Mail-Zusatzspeicher: je 5 GB für 1,00 € pro Monat (${STEUER}).`,
|
||||
},
|
||||
{
|
||||
titel: "1 Stunde Arbeitszeit pro Monat",
|
||||
@@ -34,12 +36,12 @@ const WartungsAbo = () => {
|
||||
<Link to="/" className="flex items-center gap-2 group">
|
||||
<span className="text-xl font-display font-medium text-foreground tracking-tight">Webklar</span>
|
||||
</Link>
|
||||
<Link to="/">
|
||||
<Button variant="ghost" className="text-muted-foreground hover:text-foreground">
|
||||
<Button asChild variant="ghost" className="text-muted-foreground hover:text-foreground">
|
||||
<Link to="/">
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Zurück
|
||||
</Button>
|
||||
</Link>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -56,8 +58,9 @@ const WartungsAbo = () => {
|
||||
Wartungs-Abo
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
Betrieb, Pflege und Betreuung Ihrer Website: 50 € pro Monat, 12 Monate Mindestlaufzeit.
|
||||
Betrieb, Pflege und Betreuung Ihrer Website: 50,00 € pro Monat, 12 Monate Mindestlaufzeit.
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm mt-1">Alle Preise sind Endpreise, gemäß § 19 UStG ohne Umsatzsteuer.</p>
|
||||
</div>
|
||||
|
||||
<article className="space-y-8 text-foreground">
|
||||
@@ -77,20 +80,48 @@ const WartungsAbo = () => {
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Preis und Laufzeit</h2>
|
||||
<ul className="text-muted-foreground leading-relaxed space-y-1 list-disc pl-5">
|
||||
<li>50,00 € pro Monat, monatlich im Voraus. Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.</li>
|
||||
<li>50,00 € pro Monat, monatlich im Voraus. {STEUER}.</li>
|
||||
<li>Mindestlaufzeit 12 Monate, kündbar zum Ende der Mindestlaufzeit bis einen Monat vorher; danach jederzeit zum Ende des Abrechnungsmonats. Bei Geschäftsaufgabe Sonderkündigung mit einem Monat Frist.</li>
|
||||
<li>Zahlung per Kreditkarte oder SEPA-Lastschrift über unseren Zahlungsdienstleister Stripe.</li>
|
||||
<li>Arbeit über die enthaltene Stunde hinaus nur nach Ihrer Freigabe, 100 € pro Stunde, abgerechnet in 15-Minuten-Schritten.</li>
|
||||
<li>Arbeit über die enthaltene Stunde hinaus nur nach Ihrer Freigabe, 100 € pro Stunde ({STEUER}), abgerechnet in 15-Minuten-Schritten.</li>
|
||||
</ul>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">E-Mail-Postfächer und Zusatzspeicher</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Im Wartungs-Abo richten wir Ihnen beliebig viele Postfächer unter Ihrer Domain ein, mit zusammen 5 GB
|
||||
Speicher. Wie der Speicher auf die Postfächer verteilt wird, bestimmen Sie. Brauchen Sie mehr Platz, buchen Sie
|
||||
im Kundenportal unter „Verträge“ Zusatzspeicher: Pakete von je 5 GB für 1,00 € pro Monat ({STEUER}), bis zu
|
||||
10 Pakete. Bestellen und verringern können Sie die Pakete dort, sobald Ihr Abo begonnen hat, solange es nicht
|
||||
gekündigt ist und keine Zahlung offen ist. Eine Verringerung wirkt zum Ende des Abrechnungsmonats.
|
||||
Einzelheiten:{" "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#zusatzspeicher`} className={linkClass}>§ 10 der Vertragsbedingungen</a>.
|
||||
</p>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Hosting-Abo für statische Websites</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Sie haben eine statische Website und brauchen weder Pflege noch Arbeitszeit? Dann genügt vielleicht unser{" "}
|
||||
<Link to="/hosting-abo" className={linkClass}>Hosting-Abo</Link> für 15,00 € pro Monat ({STEUER}). Vom
|
||||
Hosting- ins Wartungs-Abo wechseln Sie im Kundenportal, sobald das Hosting-Abo begonnen hat, solange es nicht
|
||||
gekündigt ist und keine Zahlung offen ist; mit dem Wechsel beginnt eine neue Mindestlaufzeit von 12 Monaten.
|
||||
Ein Wechsel vom Wartungs- ins Hosting-Abo ist auf Anfrage nach Ablauf der Mindestlaufzeit zum Ende eines
|
||||
Abrechnungsmonats möglich, wenn Ihre Website statisch ist. Einzelheiten in{" "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#wechsel`} className={linkClass}>§ 9 der Vertragsbedingungen</a>.
|
||||
</p>
|
||||
</section>
|
||||
{divider}
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">So schließen Sie das Abo ab</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Das Wartungs-Abo schließen Sie in unserem Kundenportal ab. Den Zugang richten wir Ihnen ein, sprechen Sie uns
|
||||
einfach an. Nach der Anmeldung finden Sie das Angebot auf der Startseite. Dort sehen Sie danach jederzeit Ihr
|
||||
Stundenkontingent, Ihre Rechnungen und können uns Aufträge schicken.
|
||||
einfach an. Nach der Anmeldung finden Sie das Angebot im Bereich „Verträge“. Im Kundenportal sehen Sie danach
|
||||
jederzeit Ihr Stundenkontingent und Ihre Rechnungen und schicken uns Aufträge über den Chat.
|
||||
</p>
|
||||
</section>
|
||||
{divider}
|
||||
@@ -98,18 +129,19 @@ const WartungsAbo = () => {
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Widerruf und Kündigung</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Verbraucher können den Vertrag innerhalb von 14 Tagen widerrufen. Bereits gezahlte Beträge erstatten wir dann
|
||||
nach der{" "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#widerruf`} className="text-foreground underline hover:no-underline">Widerrufsbelehrung</a>.
|
||||
Verbraucher können jede Bestellung innerhalb von 14 Tagen ab Vertragsschluss widerrufen: den Abschluss des
|
||||
Abos, den Wechsel ins Wartungs-Abo und die Bestellung von Zusatzspeicher. Bereits gezahlte Beträge erstatten
|
||||
wir dann nach der{" "}
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#widerruf`} className={linkClass}>Widerrufsbelehrung</a>.
|
||||
Kündigen können Sie online ohne Anmeldung oder per E-Mail.
|
||||
</p>
|
||||
<div className="mt-4 flex flex-wrap gap-3">
|
||||
<a href={`${PORTAL}/kuendigen.html`}>
|
||||
<Button variant="outline" className="rounded-full">Verträge hier kündigen</Button>
|
||||
</a>
|
||||
<a href={`${PORTAL}/widerrufen.html`}>
|
||||
<Button variant="outline" className="rounded-full">Vertrag widerrufen</Button>
|
||||
</a>
|
||||
<Button asChild variant="outline" className="rounded-full">
|
||||
<a href={`${PORTAL}/kuendigen.html`}>Verträge hier kündigen</a>
|
||||
</Button>
|
||||
<Button asChild variant="outline" className="rounded-full">
|
||||
<a href={`${PORTAL}/widerrufen.html`}>Vertrag widerrufen</a>
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
{divider}
|
||||
@@ -117,11 +149,13 @@ const WartungsAbo = () => {
|
||||
<section>
|
||||
<h2 className="text-lg font-display font-medium uppercase tracking-tight mb-3">Unterlagen und Kontakt</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
<a href={`${PORTAL}/abo-bedingungen.html`} className="text-foreground underline hover:no-underline">Vertragsbedingungen mit Leistungsbeschreibung</a>
|
||||
<a href={`${PORTAL}/abo-bedingungen.html`} className={linkClass}>Vertragsbedingungen für die WEBklar-Abos</a>
|
||||
{" · "}
|
||||
<a href={`${PORTAL}/WEBklar-Wartungs-Abo-Vertragsunterlagen.pdf`} className="text-foreground underline hover:no-underline">Vertragsunterlagen als PDF</a>
|
||||
<a href={`${PORTAL}/abo-bedingungen.html#wartung`} className={linkClass}>Leistungsbeschreibung B (Wartungs-Abo)</a>
|
||||
{" · "}
|
||||
<a href={`${PORTAL}/datenschutz.html`} className="text-foreground underline hover:no-underline">Datenschutz</a>
|
||||
<a href={`${PORTAL}/WEBklar-Abo-Vertragsunterlagen.pdf`} className={linkClass}>Vertragsunterlagen als PDF</a>
|
||||
{" · "}
|
||||
<a href={`${PORTAL}/datenschutz.html`} className={linkClass}>Datenschutz</a>
|
||||
<br />
|
||||
WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler · Telefon 0176 23726355 · E-Mail support@webklar.com
|
||||
</p>
|
||||
@@ -129,12 +163,12 @@ const WartungsAbo = () => {
|
||||
</article>
|
||||
|
||||
<div className="mt-16 pt-12 border-t border-border flex flex-wrap gap-4">
|
||||
<a href={`${PORTAL}/login.html`}>
|
||||
<Button className="btn-minimal rounded-full">Zum Kundenportal</Button>
|
||||
</a>
|
||||
<Link to="/kontakt">
|
||||
<Button variant="outline" className="rounded-full">Kontakt aufnehmen</Button>
|
||||
</Link>
|
||||
<Button asChild className="btn-minimal rounded-full">
|
||||
<a href={`${PORTAL}/login.html`}>Zum Kundenportal</a>
|
||||
</Button>
|
||||
<Button asChild variant="outline" className="rounded-full">
|
||||
<Link to="/kontakt">Kontakt aufnehmen</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen, within } from "@testing-library/react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import HostingAbo from "@/pages/HostingAbo";
|
||||
import WartungsAbo from "@/pages/WartungsAbo";
|
||||
import AGB from "@/pages/AGB";
|
||||
|
||||
// Dieselben Future-Flags wie der BrowserRouter in App.tsx, sonst warnt React Router in der Testausgabe.
|
||||
const routerFuture = { v7_startTransition: true, v7_relativeSplatPath: true };
|
||||
|
||||
// Texte, die auf einer Seite mehrfach vorkommen können, werden mit getAllByText
|
||||
// geprüft (getByText wirft bei mehreren Treffern); Abwesenheit mit queryAllByText.
|
||||
|
||||
/** Liefert den <section>-Abschnitt zu einer Überschrift. */
|
||||
const sectionOf = (name: string | RegExp): HTMLElement => {
|
||||
const section = screen.getByRole("heading", { name }).closest("section");
|
||||
if (!section) throw new Error(`Abschnitt zur Überschrift ${String(name)} fehlt`);
|
||||
return section;
|
||||
};
|
||||
|
||||
describe("Abo-Seiten", () => {
|
||||
it("Hosting-Abo-Seite: Preis, Postfach, Links", () => {
|
||||
render(
|
||||
<MemoryRouter future={routerFuture}>
|
||||
<HostingAbo />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
expect(screen.getAllByText(/15,00 € pro Monat/).length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText(/1 E-Mail-Postfach/).length).toBeGreaterThan(0);
|
||||
expect(screen.getByRole("link", { name: "Verträge hier kündigen" })).toHaveAttribute(
|
||||
"href",
|
||||
"https://project.webklar.com/kuendigen.html",
|
||||
);
|
||||
expect(screen.getByRole("link", { name: /Vertragsunterlagen als PDF/ })).toHaveAttribute(
|
||||
"href",
|
||||
"https://project.webklar.com/WEBklar-Abo-Vertragsunterlagen.pdf",
|
||||
);
|
||||
});
|
||||
|
||||
it("Hosting-Abo-Seite: Wechsel und Zusatzspeicher nur unter den Bedingungen des Portals", () => {
|
||||
render(
|
||||
<MemoryRouter future={routerFuture}>
|
||||
<HostingAbo />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
// § 9 Abs. 1: Wechsel nur, wenn das Hosting-Abo begonnen hat, nicht gekündigt ist und keine Zahlung offen ist;
|
||||
// mit dem Wechsel beginnt eine neue Mindestlaufzeit.
|
||||
const wechsel = within(sectionOf("Wechsel ins Wartungs-Abo"));
|
||||
expect(wechsel.getAllByText(/begonnen hat/).length).toBeGreaterThan(0);
|
||||
expect(wechsel.getAllByText(/nicht gekündigt/).length).toBeGreaterThan(0);
|
||||
expect(wechsel.getAllByText(/keine Zahlung offen/).length).toBeGreaterThan(0);
|
||||
expect(wechsel.getAllByText(/neue Mindestlaufzeit von 12 Monaten/).length).toBeGreaterThan(0);
|
||||
// § 10 Abs. 1: dieselben Bedingungen für Bestellung und Verringerung des Zusatzspeichers.
|
||||
const speicher = within(sectionOf("E-Mail-Zusatzspeicher"));
|
||||
expect(speicher.getAllByText(/begonnen hat/).length).toBeGreaterThan(0);
|
||||
expect(speicher.getAllByText(/nicht gekündigt/).length).toBeGreaterThan(0);
|
||||
expect(speicher.getAllByText(/keine Zahlung offen/).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("Wartungs-Abo-Seite: beliebig viele Postfächer, Zusatzspeicher", () => {
|
||||
render(
|
||||
<MemoryRouter future={routerFuture}>
|
||||
<WartungsAbo />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
expect(screen.getAllByText("Beliebig viele Postfächer").length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText(/beliebig viele Postfächer/).length).toBeGreaterThan(0);
|
||||
expect(screen.queryAllByText("1 E-Mail-Postfach").length).toBe(0);
|
||||
expect(screen.queryAllByText(/1 E-Mail-Postfach/).length).toBe(0);
|
||||
expect(screen.getAllByText(/1,00 €/).length).toBeGreaterThan(0);
|
||||
// Wechsel und Zusatzspeicher auch hier nur unter den Bedingungen des Portals.
|
||||
const speicher = within(sectionOf("E-Mail-Postfächer und Zusatzspeicher"));
|
||||
expect(speicher.getAllByText(/begonnen hat/).length).toBeGreaterThan(0);
|
||||
expect(speicher.getAllByText(/nicht gekündigt/).length).toBeGreaterThan(0);
|
||||
expect(speicher.getAllByText(/keine Zahlung offen/).length).toBeGreaterThan(0);
|
||||
const hosting = within(sectionOf("Hosting-Abo für statische Websites"));
|
||||
expect(hosting.getAllByText(/begonnen hat/).length).toBeGreaterThan(0);
|
||||
expect(hosting.getAllByText(/nicht gekündigt/).length).toBeGreaterThan(0);
|
||||
expect(hosting.getAllByText(/keine Zahlung offen/).length).toBeGreaterThan(0);
|
||||
expect(hosting.getAllByText(/neue Mindestlaufzeit von 12 Monaten/).length).toBeGreaterThan(0);
|
||||
expect(hosting.getByRole("link", { name: /§ 9 der Vertragsbedingungen/ })).toHaveAttribute(
|
||||
"href",
|
||||
"https://project.webklar.com/abo-bedingungen.html#wechsel",
|
||||
);
|
||||
});
|
||||
|
||||
it("AGB Modul C verweist auf das Hosting-Abo", () => {
|
||||
render(
|
||||
<MemoryRouter future={routerFuture}>
|
||||
<AGB />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
const modulC = within(sectionOf(/Leistungsmodul C/));
|
||||
expect(modulC.getAllByText(/Hosting-Abo/).length).toBeGreaterThan(0);
|
||||
expect(modulC.getByRole("link", { name: "Vertragsbedingungen für die WEBklar-Abos" })).toHaveAttribute(
|
||||
"href",
|
||||
"https://project.webklar.com/abo-bedingungen.html",
|
||||
);
|
||||
expect(screen.queryAllByText(/verlängern sich automatisch/).length).toBe(0);
|
||||
// Rechtsrunde: kein eigener Haftungssatz mehr in Modul C (für die Abos gilt § 13 der Abo-Bedingungen) …
|
||||
expect(modulC.queryAllByText(/keine Haftung für Ausfälle externer Anbieter/).length).toBe(0);
|
||||
// … und Verbraucher können – als Ausnahme von den vereinbarten Bedingungen – automatisch verlängerte
|
||||
// Jahresverträge mit einer Frist von einem Monat kündigen.
|
||||
expect(
|
||||
modulC.getAllByText(
|
||||
/Abweichend davon können Verbraucher solche Verträge nach einer automatischen Verlängerung jederzeit mit einer Frist von einem Monat kündigen\./,
|
||||
).length,
|
||||
).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
|
||||
Reference in new issue
Block a user