Author SHA1 Message Date
1elle1 8d1d9d6f33 feat: refactor configuration files to use JavaScript and update package details 2026-02-06 14:15:23 +01:00
1elle1 11bbd1cf3f feat: add new components for navigation, contact, customer segments, hero section, and service overview
- Implement Navigation component for main navigation links.
- Create ContactSection for displaying contact information and a contact form.
- Add CustomerSegments to showcase target customer groups.
- Develop HeroSection for the landing page with a background image and call-to-action buttons.
- Introduce ServiceOverview to present available services with icons and descriptions.
- Create PageHero for consistent page headers with optional background images.
- Implement TariffTable for displaying internet and phone tariff plans.
- Add TeamGrid to showcase team members with images and roles.
- Create TrustSection to highlight company statistics.
- Implement ValueProposition to present company values with icons.
- Add reusable UI components: Badge, Button, Card, Container, and SectionHeading.
- Define constants for company information, services, and tariffs.
- Create utility function for class name management.
- Define TypeScript types for navigation links, team members, tariff plans, and service cards.
- Configure TypeScript settings for the project.
2026-02-06 13:59:08 +01:00
91 changed files with 3669 additions and 3031 deletions
-3
View File
@@ -1,3 +0,0 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}
+41
View File
@@ -31,3 +31,44 @@ npm-debug.log*
# Generated files # Generated files
generated/ generated/
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
-50
View File
@@ -1,50 +0,0 @@
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Datenschutz",
description: "Datenschutzhinweise der TeleNetSystems GmbH."
};
export default function DatenschutzPage() {
return (
<section className="section-block">
<div className="container-shell max-w-3xl space-y-7">
<header>
<p className="eyebrow">Rechtliches</p>
<h1 className="mt-2 text-4xl font-semibold text-alpine-900">Datenschutzerklaerung</h1>
</header>
<article className="surface-card space-y-4">
<h2 className="text-xl font-semibold text-alpine-900">1. Verantwortliche Stelle</h2>
<p className="text-sm text-alpine-700">
Verantwortlich fuer die Datenverarbeitung ist die TeleNetSystems GmbH, Untermarkt 12, 6600 Reutte,
Oesterreich, E-Mail: kontakt@telenetsystems.at.
</p>
<h2 className="text-xl font-semibold text-alpine-900">2. Zweck der Verarbeitung</h2>
<p className="text-sm text-alpine-700">
Personenbezogene Daten werden ausschliesslich fuer Anfragebearbeitung, Vertragsanbahnung und technischen
Support verarbeitet. Es werden keine Tracking- oder Marketing-Cookies ohne Einwilligung gesetzt.
</p>
<h2 className="text-xl font-semibold text-alpine-900">3. Ihre Rechte</h2>
<p className="text-sm text-alpine-700">
Sie haben das Recht auf Auskunft, Berichtigung, Loeschung, Einschraenkung, Datenuebertragbarkeit und
Widerspruch gemaess DSGVO. Anfragen richten Sie bitte an datenschutz@telenetsystems.at.
</p>
<h2 className="text-xl font-semibold text-alpine-900">4. Speicherdauer</h2>
<p className="text-sm text-alpine-700">
Daten werden nur so lange gespeichert, wie es fuer die jeweiligen Zwecke und gesetzliche Aufbewahrungspflichten
erforderlich ist.
</p>
<p className="text-sm text-alpine-700">
Hinweis: Vor Livegang sollten diese Informationen durch juristisch gepruefte, unternehmensspezifische Inhalte
ersetzt werden.
</p>
</article>
</div>
</section>
);
}
-135
View File
@@ -1,135 +0,0 @@
import type { Metadata } from "next";
import Image from "next/image";
import { ContactStrip } from "@/components/contact-strip";
import { PageHero } from "@/components/page-hero";
import { tvTariffs } from "@/lib/site-content";
export const metadata: Metadata = {
title: "Fernsehen fuer Privat und Business",
description:
"TV Privat, Pay TV und TV Business in Tirol: klar verglichen, einfach erklaert und mit persoenlicher Beratung in Reutte."
};
export default function FernsehenPage() {
return (
<>
<PageHero
eyebrow="Fernsehen"
title="TV-Pakete, die zu Ihrem Alltag passen"
intro="Privatpakete und Pay-TV sind direkt vergleichbar. TV Business planen wir separat, weil Anforderungen in Hotels, Gastro oder Unternehmen deutlich anders sind."
imageSrc="/assets/hero/tv-header-44c1e07b.jpg"
imageAlt="Fernsehangebot von TeleNetSystems"
primaryCta={{ label: "TV-Beratung anfragen", href: "/#kontakt" }}
secondaryCta={{ label: "Internet-Tarife", href: "/internet" }}
/>
<section className="section-block pt-6 bg-white/70">
<div className="container-shell space-y-10">
<div>
<p className="eyebrow">Vergleich | TV Privat</p>
<div className="mt-4 table-shell">
<table className="data-table">
<thead>
<tr>
<th>Paket</th>
<th>Sender</th>
<th>Extras</th>
<th>Passend fuer</th>
<th>Preis</th>
</tr>
</thead>
<tbody>
{tvTariffs.private.map((tariff) => (
<tr key={tariff.name}>
<td>{tariff.name}</td>
<td>{tariff.channels}</td>
<td>{tariff.extras}</td>
<td>{tariff.target}</td>
<td>{tariff.price}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div>
<p className="eyebrow">Vergleich | Pay TV</p>
<div className="mt-4 table-shell">
<table className="data-table">
<thead>
<tr>
<th>Paket</th>
<th>Sender</th>
<th>Extras</th>
<th>Passend fuer</th>
<th>Preis</th>
</tr>
</thead>
<tbody>
{tvTariffs.payTv.map((tariff) => (
<tr key={tariff.name}>
<td>{tariff.name}</td>
<td>{tariff.channels}</td>
<td>{tariff.extras}</td>
<td>{tariff.target}</td>
<td>{tariff.price}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</section>
<section className="section-block">
<div className="container-shell grid gap-8 lg:grid-cols-[1fr_1fr] lg:items-center">
<div className="surface-card">
<p className="eyebrow">TV Business</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Individuell statt One-Size-Fits-All.</h2>
<p className="mt-4 text-alpine-700">
Unternehmen brauchen meist mehr als ein Standardpaket. Darum planen wir TV Business immer als eigene
Loesung mit technischer Abstimmung vor Ort.
</p>
<ul className="mt-5 space-y-2 text-sm text-alpine-700">
{tvTariffs.business.map((point) => (
<li key={point}>- {point}</li>
))}
</ul>
</div>
<figure className="overflow-hidden rounded-3xl border border-alpine-200 bg-white shadow-soft">
<Image
alt="TV fuer Unternehmen"
className="h-[340px] w-full object-cover"
src="/assets/gallery/tv-business-c3bdfb94-23310090.jpg"
width={900}
height={560}
/>
</figure>
</div>
</section>
<section className="section-block bg-white/65">
<div className="container-shell grid gap-6 md:grid-cols-3">
<article className="surface-card">
<h3 className="text-xl font-semibold text-alpine-900">1. Bedarf klaeren</h3>
<p className="mt-3 text-sm text-alpine-700">Wie viele Geraete, welche Inhalte, welche Standorte?</p>
</article>
<article className="surface-card">
<h3 className="text-xl font-semibold text-alpine-900">2. Technik abstimmen</h3>
<p className="mt-3 text-sm text-alpine-700">Installation, Receiver, Netzstruktur und Laufzeit transparent festlegen.</p>
</article>
<article className="surface-card">
<h3 className="text-xl font-semibold text-alpine-900">3. Betrieb sichern</h3>
<p className="mt-3 text-sm text-alpine-700">Support aus Tirol, damit Ausfaelle in Stoerungszeiten kurz bleiben.</p>
</article>
</div>
</section>
<ContactStrip />
</>
);
}
-96
View File
@@ -1,96 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--bg-main: #f5f9fc;
--bg-alt: #eef5fb;
--text-main: #143047;
--text-soft: #30526b;
--line: #d8e5f0;
--surface: #ffffff;
}
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
min-height: 100%;
}
body {
background:
radial-gradient(circle at 8% 0%, rgba(94, 156, 197, 0.2), transparent 34%),
radial-gradient(circle at 92% 12%, rgba(209, 126, 58, 0.14), transparent 30%),
linear-gradient(180deg, var(--bg-main), var(--bg-alt));
color: var(--text-main);
font-family: "Source Sans 3", "Avenir Next", "Segoe UI", sans-serif;
line-height: 1.45;
}
a {
color: inherit;
}
.font-heading,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Space Grotesk", "Trebuchet MS", "Avenir Next", sans-serif;
}
::selection {
background: rgba(205, 114, 45, 0.24);
}
@layer components {
.container-shell {
@apply mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8;
}
.section-block {
@apply py-14 md:py-20;
}
.eyebrow {
@apply font-heading text-xs font-semibold uppercase tracking-[0.24em] text-copper-600;
}
.surface-card {
@apply rounded-2xl border border-alpine-100 bg-white p-6 shadow-card;
}
.btn-primary {
@apply inline-flex items-center justify-center rounded-full border border-copper-600 bg-copper-600 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-copper-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-copper-500;
}
.btn-secondary {
@apply inline-flex items-center justify-center rounded-full border border-alpine-300 bg-white px-5 py-2.5 text-sm font-semibold text-alpine-700 transition hover:border-alpine-500 hover:text-alpine-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-alpine-500;
}
.nav-chip {
@apply inline-flex rounded-full border border-transparent px-3 py-1.5 transition hover:border-alpine-200 hover:bg-alpine-50 hover:text-alpine-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-alpine-500;
}
.table-shell {
@apply w-full overflow-hidden rounded-2xl border border-alpine-200 bg-white;
}
.data-table {
@apply w-full border-collapse text-left text-sm;
}
.data-table thead th {
@apply bg-alpine-50 px-4 py-3 font-semibold text-alpine-800;
}
.data-table tbody td {
@apply border-t border-alpine-100 px-4 py-3 text-alpine-700;
}
}
-42
View File
@@ -1,42 +0,0 @@
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Impressum",
description: "Impressum der TeleNetSystems GmbH gemaess oesterreichischer Informationspflichten."
};
export default function ImpressumPage() {
return (
<section className="section-block">
<div className="container-shell max-w-3xl space-y-7">
<header>
<p className="eyebrow">Rechtliches</p>
<h1 className="mt-2 text-4xl font-semibold text-alpine-900">Impressum</h1>
</header>
<article className="surface-card space-y-4">
<h2 className="text-xl font-semibold text-alpine-900">Angaben gemaess Paragraf 5 ECG</h2>
<p className="text-sm text-alpine-700">
TeleNetSystems GmbH<br />
Untermarkt 12<br />
6600 Reutte<br />
Oesterreich
</p>
<p className="text-sm text-alpine-700">
Telefon: +43 5672 219 40<br />
E-Mail: kontakt@telenetsystems.at
</p>
<p className="text-sm text-alpine-700">
Firmenbuchnummer: FN 000000a<br />
Firmenbuchgericht: Landesgericht Innsbruck<br />
UID-Nummer: ATU00000000
</p>
<p className="text-sm text-alpine-700">
Hinweis: Diese Angaben sind als Platzhalter zu verstehen und muessen vor Livegang mit den offiziellen
Unternehmensdaten abgeglichen werden.
</p>
</article>
</div>
</section>
);
}
-137
View File
@@ -1,137 +0,0 @@
import type { Metadata } from "next";
import Image from "next/image";
import { ContactStrip } from "@/components/contact-strip";
import { PageHero } from "@/components/page-hero";
import { internetTariffs } from "@/lib/site-content";
export const metadata: Metadata = {
title: "Internet in Reutte und Tirol",
description:
"Internet-Tarife fuer Reutte und Tirol: Kabel, Glasfaser und Business-Leitungen mit transparenten Preisen und regionalem Support."
};
export default function InternetPage() {
return (
<>
<PageHero
eyebrow="Internet"
title="Schnelles Internet fuer Alltag und Betrieb"
intro="Ob Familie, Homeoffice oder Unternehmensstandort: Sie sehen auf einen Blick, welche Geschwindigkeit, Vertragsdauer und Kosten zu Ihrem Bedarf passen."
imageSrc="/assets/gallery/internet-7b594a2d-793cefee.jpg"
imageAlt="Internetinfrastruktur von TeleNetSystems"
primaryCta={{ label: "Jetzt beraten lassen", href: "/#kontakt" }}
secondaryCta={{ label: "Fernsehen ansehen", href: "/fernsehen" }}
/>
<section className="section-block pt-6">
<div className="container-shell grid gap-5 md:grid-cols-3">
<article className="surface-card">
<h2 className="text-xl font-semibold text-alpine-900">Transparent kalkuliert</h2>
<p className="mt-3 text-sm text-alpine-700">Monatspreis, Aktivierung und Laufzeit sind offen dargestellt.</p>
</article>
<article className="surface-card">
<h2 className="text-xl font-semibold text-alpine-900">Stabil in Spitzenzeiten</h2>
<p className="mt-3 text-sm text-alpine-700">Tarife sind fuer Streaming, Homeoffice und Cloud-Nutzung ausgelegt.</p>
</article>
<article className="surface-card">
<h2 className="text-xl font-semibold text-alpine-900">Support vor Ort</h2>
<p className="mt-3 text-sm text-alpine-700">Keine Hotline-Odyssee. Unser Team sitzt in Tirol und kennt die Netze.</p>
</article>
</div>
</section>
<section className="section-block bg-white/70">
<div className="container-shell space-y-12">
<div>
<p className="eyebrow">Privatkunden | Kabel</p>
<div className="mt-4 table-shell">
<table className="data-table">
<thead>
<tr>
<th>Tarif</th>
<th>Geschwindigkeit</th>
<th>Laufzeit</th>
<th>Setup</th>
<th>Preis</th>
</tr>
</thead>
<tbody>
{internetTariffs.privateCable.map((tariff) => (
<tr key={tariff.name}>
<td>{tariff.name}</td>
<td>{tariff.speed}</td>
<td>{tariff.contract}</td>
<td>{tariff.setup}</td>
<td>{tariff.price}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div>
<p className="eyebrow">Privatkunden | Glasfaser</p>
<div className="mt-4 table-shell">
<table className="data-table">
<thead>
<tr>
<th>Tarif</th>
<th>Geschwindigkeit</th>
<th>Laufzeit</th>
<th>Setup</th>
<th>Preis</th>
</tr>
</thead>
<tbody>
{internetTariffs.privateFiber.map((tariff) => (
<tr key={tariff.name}>
<td>{tariff.name}</td>
<td>{tariff.speed}</td>
<td>{tariff.contract}</td>
<td>{tariff.setup}</td>
<td>{tariff.price}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</section>
<section className="section-block">
<div className="container-shell grid gap-8 lg:grid-cols-[1fr_1fr] lg:items-center">
<div>
<p className="eyebrow">Business-Internet</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Wenn Leitungen Teil Ihrer Betriebszeit sind.</h2>
<p className="mt-4 text-alpine-700">
Fuer Betriebe konfigurieren wir Internet nicht als Standardpaket, sondern passend zu Ausfallsicherheit,
Standortanzahl und Lastprofil.
</p>
<ul className="mt-4 space-y-2 text-sm text-alpine-700">
{internetTariffs.business.map((point) => (
<li className="surface-card py-3" key={point}>
{point}
</li>
))}
</ul>
</div>
<div className="overflow-hidden rounded-3xl border border-alpine-200 bg-white shadow-soft">
<Image
alt="Serverraum TeleNetSystems"
className="h-[340px] w-full object-cover"
src="/assets/gallery/serverraum-7225cb6a.jpg"
width={900}
height={560}
/>
</div>
</div>
</section>
<ContactStrip />
</>
);
}
-65
View File
@@ -1,65 +0,0 @@
import type { Metadata, Viewport } from "next";
import { SiteFooter } from "@/components/site-footer";
import { SiteHeader } from "@/components/site-header";
import "./globals.css";
export const metadata: Metadata = {
metadataBase: new URL("https://www.telenetsystems.at"),
title: {
default: "TeleNetSystems Reutte | Internet, TV und Telefonie in Tirol",
template: "%s | TeleNetSystems"
},
description:
"TeleNetSystems aus Reutte bietet Internet, Fernsehen, Telefonie und IT-Services fuer Privat- und Geschaeftskunden in Tirol. Transparent, regional und gut erreichbar.",
keywords: [
"Internet Reutte",
"TV Anbieter Tirol",
"Telefonie Tirol",
"IT Support Reutte",
"Telekommunikation Tirol",
"Business Internet Tirol"
],
openGraph: {
title: "TeleNetSystems Reutte",
description:
"Regionaler IT- und Telekompartner in Tirol fuer Internet, Fernsehen und Telefonie mit direktem Support.",
url: "https://www.telenetsystems.at",
siteName: "TeleNetSystems",
locale: "de_AT",
type: "website",
images: [
{
url: "/assets/hero/internet-header-431619ac.jpg",
width: 1200,
height: 630,
alt: "TeleNetSystems Infrastruktur in Tirol"
}
]
},
icons: {
icon: "/assets/logo/logo-systems-9fd0db14.png"
}
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
themeColor: "#0c1d2a"
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="de">
<body>
<a className="sr-only focus:not-sr-only focus:absolute focus:left-3 focus:top-3 focus:z-50 focus:rounded-md focus:bg-white focus:px-3 focus:py-2" href="#inhalt">
Zum Inhalt springen
</a>
<SiteHeader />
<main id="inhalt">{children}</main>
<SiteFooter />
</body>
</html>
);
}
-111
View File
@@ -1,111 +0,0 @@
import type { Metadata } from "next";
import Link from "next/link";
import { ContactStrip } from "@/components/contact-strip";
import { PageHero } from "@/components/page-hero";
export const metadata: Metadata = {
title: "Leistungen fuer Privat und Business",
description:
"Uebersicht aller Leistungen von TeleNetSystems: Internet, Fernsehen, Telefonie und IT-Services fuer Privatkunden und Unternehmen in Tirol."
};
const serviceBlocks = [
{
title: "Internet",
text: "Kabel- und Glasfaseranschluesse fuer Zuhause sowie SLA-basierte Loesungen fuer Unternehmen.",
href: "/internet"
},
{
title: "Fernsehen",
text: "TV Privat, Pay TV und individuelle TV Business Konzepte fuer Hotels, Gastronomie und Betriebe.",
href: "/fernsehen"
},
{
title: "Telefonie",
text: "Festnetzpakete, SIP-Trunks und Erweiterungen fuer Teams mit mehreren Nebenstellen.",
href: "/telefonie"
},
{
title: "IT-Service",
text: "Planung, Inbetriebnahme und Wartung von Netzwerken sowie laufender technischer Support.",
href: "/#kontakt"
}
];
export default function LeistungenPage() {
return (
<>
<PageHero
eyebrow="Leistungen"
title="Alles aus einer Hand, ohne unnoetige Komplexitaet"
intro="TeleNetSystems kombiniert Telekommunikation mit praktischem IT-Service. Das reduziert Abstimmungsaufwand und sorgt dafuer, dass Technik zusammen funktioniert."
imageSrc="/assets/gallery/firma1-8a863d8b.jpg"
imageAlt="Standort TeleNetSystems in Tirol"
primaryCta={{ label: "Jetzt Bedarf klaeren", href: "/#kontakt" }}
secondaryCta={{ label: "Ueber uns", href: "/ueber-uns" }}
/>
<section className="section-block pt-6">
<div className="container-shell grid gap-5 md:grid-cols-2">
{serviceBlocks.map((service) => (
<article className="surface-card" key={service.title}>
<h2 className="text-2xl font-semibold text-alpine-900">{service.title}</h2>
<p className="mt-3 text-alpine-700">{service.text}</p>
<Link className="mt-4 inline-flex text-sm font-semibold text-copper-700 hover:text-copper-800" href={service.href}>
Mehr dazu -&gt;
</Link>
</article>
))}
</div>
</section>
<section className="section-block bg-white/70">
<div className="container-shell grid gap-6 lg:grid-cols-2">
<article className="surface-card border-moss-500/20 bg-gradient-to-br from-white via-white to-moss-500/5">
<p className="eyebrow text-moss-600">Privatkunden</p>
<h2 className="mt-2 text-2xl font-semibold text-alpine-900">Uebersichtlich und alltagstauglich</h2>
<ul className="mt-4 space-y-2 text-sm text-alpine-700">
<li>- Klare Pakete mit offenen Kosten</li>
<li>- Schnelle Hilfe bei Stoerungen</li>
<li>- Beratung ohne Fachjargon</li>
</ul>
</article>
<article className="surface-card border-copper-300/40 bg-gradient-to-br from-white via-white to-copper-100/40">
<p className="eyebrow">Geschaeftskunden</p>
<h2 className="mt-2 text-2xl font-semibold text-alpine-900">Planbar und skalierbar</h2>
<ul className="mt-4 space-y-2 text-sm text-alpine-700">
<li>- Individuelle Netz- und Kommunikationskonzepte</li>
<li>- SLA-orientierter Support</li>
<li>- Direkte Ansprechpartner fuer Technik und Projekte</li>
</ul>
</article>
</div>
</section>
<section className="section-block">
<div className="container-shell">
<p className="eyebrow">Zusammenarbeit</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Wie wir Projekte in Tirol umsetzen</h2>
<div className="mt-7 grid gap-5 md:grid-cols-3">
<article className="surface-card">
<h3 className="text-xl font-semibold text-alpine-900">1. Analyse</h3>
<p className="mt-3 text-sm text-alpine-700">Wir nehmen Ist-Situation, Anforderungen und Budget sauber auf.</p>
</article>
<article className="surface-card">
<h3 className="text-xl font-semibold text-alpine-900">2. Umsetzung</h3>
<p className="mt-3 text-sm text-alpine-700">Installation und Inbetriebnahme erfolgen abgestimmt auf Ihren Betrieb.</p>
</article>
<article className="surface-card">
<h3 className="text-xl font-semibold text-alpine-900">3. Betrieb</h3>
<p className="mt-3 text-sm text-alpine-700">Wir bleiben als Servicepartner erreichbar, auch nach dem Go-live.</p>
</article>
</div>
</div>
</section>
<ContactStrip />
</>
);
}
-142
View File
@@ -1,142 +0,0 @@
import type { Metadata } from "next";
import Link from "next/link";
import { ContactStrip } from "@/components/contact-strip";
import { PageHero } from "@/components/page-hero";
import { company, faqItems, serviceHighlights, testimonials } from "@/lib/site-content";
export const metadata: Metadata = {
title: "Regionaler Telekom- und IT-Service in Reutte",
description:
"TeleNetSystems praesentiert Internet, Fernsehen und Telefonie fuer Privat- und Geschaeftskunden in Tirol. Mit klaren Tarifen und direktem Support aus Reutte."
};
const localBusinessSchema = {
"@context": "https://schema.org",
"@type": "LocalBusiness",
name: company.name,
areaServed: "Tirol",
address: {
"@type": "PostalAddress",
streetAddress: "Untermarkt 12",
postalCode: "6600",
addressLocality: "Reutte",
addressCountry: "AT"
},
telephone: company.phone,
email: company.email,
openingHours: ["Mo-Fr 08:00-17:30"],
sameAs: []
};
export default function HomePage() {
return (
<>
<PageHero
eyebrow="TeleNetSystems Reutte"
title="Technik, die laeuft. Support, der erreichbar bleibt."
intro="TeleNetSystems verbindet in Tirol private Haushalte und Betriebe mit stabilem Internet, Fernsehen und Telefonie. Sie bekommen transparente Angebote und ein Team aus der Region, das sich kuemmert."
imageSrc="/assets/hero/internet-header-431619ac.jpg"
imageAlt="Netzwerktechnik von TeleNetSystems"
primaryCta={{ label: "Beratung starten", href: "#kontakt" }}
secondaryCta={{ label: "Leistungen ansehen", href: "/leistungen" }}
/>
<section className="section-block pt-6">
<div className="container-shell">
<div className="mb-8 flex flex-wrap items-end justify-between gap-4">
<div>
<p className="eyebrow">Leistungsuebersicht</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Drei Bereiche, klar getrennt aufgebaut.</h2>
</div>
<p className="max-w-xl text-alpine-700">
Erst Internet, dann Fernsehen, danach Telefonie: Die Reihenfolge folgt den haeufigsten Fragen in der
Beratung und macht die Entscheidung schneller.
</p>
</div>
<div className="grid gap-5 md:grid-cols-3">
{serviceHighlights.map((item) => (
<article className="surface-card flex h-full flex-col" key={item.title}>
<h3 className="text-xl font-semibold text-alpine-900">{item.title}</h3>
<p className="mt-3 flex-1 text-alpine-700">{item.text}</p>
<Link className="mt-5 inline-flex items-center text-sm font-semibold text-copper-700 hover:text-copper-800" href={item.href}>
Details ansehen -&gt;
</Link>
</article>
))}
</div>
</div>
</section>
<section className="section-block">
<div className="container-shell grid gap-6 lg:grid-cols-2">
<article className="surface-card border-moss-500/20 bg-gradient-to-br from-white via-white to-moss-500/5">
<p className="eyebrow text-moss-600">Privatkunden</p>
<h2 className="mt-2 text-2xl font-semibold text-alpine-900">Wenn Technik einfach funktionieren soll.</h2>
<ul className="mt-4 space-y-2 text-alpine-700">
<li>Klare Monatspreise ohne Tarifdschungel</li>
<li>TV-, Internet- und Telefoniepakete fuer den Alltag</li>
<li>Schnelle Hilfe bei Stoerungen, ohne Umwege</li>
</ul>
</article>
<article className="surface-card border-copper-300/40 bg-gradient-to-br from-white via-white to-copper-100/30">
<p className="eyebrow">Geschaeftskunden</p>
<h2 className="mt-2 text-2xl font-semibold text-alpine-900">Wenn Ausfaelle sofort Geld kosten.</h2>
<ul className="mt-4 space-y-2 text-alpine-700">
<li>Skalierbare Business-Leitungen mit SLA</li>
<li>Standortvernetzung und SIP-Telefonie aus einer Hand</li>
<li>Direkter Kontakt zu Technik und Projektleitung</li>
</ul>
</article>
</div>
</section>
<section className="section-block bg-white/65">
<div className="container-shell">
<p className="eyebrow">Stimmen aus Tirol</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Vertrauen entsteht im Tagesgeschaeft.</h2>
<div className="mt-8 grid gap-5 md:grid-cols-3">
{testimonials.map((item) => (
<blockquote className="surface-card" key={item.author}>
<p className="text-alpine-700">{item.quote}</p>
<footer className="mt-4 text-sm text-alpine-600">
<span className="font-semibold text-alpine-800">{item.author}</span> | {item.context}
</footer>
</blockquote>
))}
</div>
</div>
</section>
<section className="section-block">
<div className="container-shell grid gap-8 lg:grid-cols-[0.9fr_1.1fr] lg:items-start">
<div>
<p className="eyebrow">Hauefige Fragen</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Was vor einem Wechsel oft unklar ist.</h2>
<p className="mt-4 text-alpine-700">
Diese Fragen kommen in der Erstberatung am haeufigsten. Wenn Ihr Punkt nicht dabei ist, klaeren wir ihn
telefonisch in wenigen Minuten.
</p>
</div>
<div className="space-y-3">
{faqItems.map((item) => (
<details className="surface-card p-0" key={item.question}>
<summary className="cursor-pointer list-none px-5 py-4 font-semibold text-alpine-900">{item.question}</summary>
<p className="border-t border-alpine-100 px-5 py-4 text-sm text-alpine-700">{item.answer}</p>
</details>
))}
</div>
</div>
</section>
<ContactStrip />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(localBusinessSchema) }}
/>
</>
);
}
-11
View File
@@ -1,11 +0,0 @@
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: "*",
allow: "/"
},
sitemap: "https://www.telenetsystems.at/sitemap.xml"
};
}
-21
View File
@@ -1,21 +0,0 @@
import type { MetadataRoute } from "next";
const baseUrl = "https://www.telenetsystems.at";
export default function sitemap(): MetadataRoute.Sitemap {
return [
"",
"/internet",
"/fernsehen",
"/telefonie",
"/leistungen",
"/ueber-uns",
"/impressum",
"/datenschutz"
].map((path) => ({
url: `${baseUrl}${path}`,
lastModified: new Date(),
changeFrequency: "monthly",
priority: path === "" ? 1 : 0.8
}));
}
-105
View File
@@ -1,105 +0,0 @@
import type { Metadata } from "next";
import Image from "next/image";
import { ContactStrip } from "@/components/contact-strip";
import { PageHero } from "@/components/page-hero";
import { phoneTariffs } from "@/lib/site-content";
export const metadata: Metadata = {
title: "Telefonie fuer Privat und Unternehmen",
description:
"Telefonie-Angebote von TeleNetSystems in Tirol: einfache Pakete fuer Privatkunden und skalierbare SIP-Loesungen fuer Unternehmen."
};
export default function TelefoniePage() {
return (
<>
<PageHero
eyebrow="Telefonie"
title="Von Festnetz bis SIP: klar kalkulierbar"
intro="Telefonie muss im Alltag verlässlich sein. Deshalb zeigen wir Pakete mit klaren Inklusivleistungen und bieten fuer Unternehmen planbare SIP-Loesungen mit direktem Support."
imageSrc="/assets/gallery/telefon-fa5e8c21-271d2135.jpg"
imageAlt="Telefonie fuer Privat- und Geschaeftskunden"
primaryCta={{ label: "Telefonie anfragen", href: "/#kontakt" }}
secondaryCta={{ label: "Alle Leistungen", href: "/leistungen" }}
/>
<section className="section-block pt-6 bg-white/70">
<div className="container-shell">
<p className="eyebrow">Tarifvergleich</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">So unterscheiden sich die Pakete.</h2>
<div className="mt-6 table-shell">
<table className="data-table">
<thead>
<tr>
<th>Tarif</th>
<th>Rufnummern</th>
<th>Inklusive</th>
<th>Setup</th>
<th>Preis</th>
</tr>
</thead>
<tbody>
{phoneTariffs.map((tariff) => (
<tr key={tariff.name}>
<td>{tariff.name}</td>
<td>{tariff.lines}</td>
<td>{tariff.included}</td>
<td>{tariff.setup}</td>
<td>{tariff.price}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</section>
<section className="section-block">
<div className="container-shell grid gap-6 lg:grid-cols-2">
<article className="surface-card">
<p className="eyebrow">Privat</p>
<h2 className="mt-2 text-2xl font-semibold text-alpine-900">Einfach erreichbar bleiben</h2>
<p className="mt-3 text-alpine-700">
Ideal fuer Haushalte, die Festnetz als verlaesslichen Kanal behalten wollen. Portierung bestehender
Nummern ist bei jedem Paket vorgesehen.
</p>
</article>
<article className="surface-card">
<p className="eyebrow">Business</p>
<h2 className="mt-2 text-2xl font-semibold text-alpine-900">Skalierbar fuers Unternehmen</h2>
<p className="mt-3 text-alpine-700">
SIP-Trunks, Nebenstellen, zentrale Steuerung und saubere Einbindung in Ihre bestehende IT-Landschaft.
Genau abgestimmt auf Teamsize und Anrufvolumen.
</p>
</article>
</div>
</section>
<section className="section-block bg-white/65">
<div className="container-shell grid gap-7 lg:grid-cols-[1fr_1fr] lg:items-center">
<div>
<p className="eyebrow">Technik, verstaendlich erklaert</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Keine Konfigurationsraetsel.</h2>
<p className="mt-4 text-alpine-700">
Einrichtung und Umstellung begleiten wir Schritt fuer Schritt. Das gilt fuer den privaten Router genauso
wie fuer Business-Telefonanlagen mit mehreren Standorten.
</p>
</div>
<div className="overflow-hidden rounded-3xl border border-alpine-200 bg-white shadow-soft">
<Image
alt="Telefonie-Setup"
className="h-[320px] w-full object-cover"
src="/assets/gallery/telefonie1-ad7595f9.jpg"
width={900}
height={560}
/>
</div>
</div>
</section>
<ContactStrip />
</>
);
}
-108
View File
@@ -1,108 +0,0 @@
import type { Metadata } from "next";
import Image from "next/image";
import { ContactStrip } from "@/components/contact-strip";
import { PageHero } from "@/components/page-hero";
import { teamMembers } from "@/lib/site-content";
export const metadata: Metadata = {
title: "Ueber TeleNetSystems",
description:
"TeleNetSystems aus Reutte: Team, Werte und Arbeitsweise fuer regionale Telekommunikations- und IT-Dienstleistungen in Tirol."
};
const values = [
{
title: "Verlaesslichkeit",
text: "Wir versprechen nur Leistungen, die wir technisch und organisatorisch halten koennen."
},
{
title: "Nahbarkeit",
text: "Sie sprechen mit Menschen aus der Region, nicht mit anonymen Weiterleitungen."
},
{
title: "Klarheit",
text: "Angebote und Entscheidungen bleiben nachvollziehbar, auch wenn die Technik komplex ist."
}
];
export default function UeberUnsPage() {
return (
<>
<PageHero
eyebrow="Ueber uns"
title="TeleNetSystems ist in Reutte zuhause"
intro="Wir arbeiten fuer Menschen und Betriebe in Tirol, die eine stabile technische Basis und eine erreichbare Betreuung brauchen. Genau darauf ist unser Team ausgerichtet."
imageSrc="/assets/gallery/firmenkunden-96736712-5fa99704.jpg"
imageAlt="Teamarbeit bei TeleNetSystems"
primaryCta={{ label: "Kontakt aufnehmen", href: "/#kontakt" }}
secondaryCta={{ label: "Leistungen", href: "/leistungen" }}
/>
<section className="section-block pt-6">
<div className="container-shell grid gap-8 lg:grid-cols-[1fr_1fr]">
<article className="surface-card">
<p className="eyebrow">Unsere Geschichte</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Vom regionalen Anbieter zum festen IT-Partner</h2>
<p className="mt-4 text-alpine-700">
TeleNetSystems ist als regionales Telekom-Team gestartet und hat das Angebot schrittweise um
IT-Dienstleistungen erweitert. Heute betreuen wir private Haushalte, Handwerksbetriebe, Hotels und
mittelstaendische Unternehmen in ganz Tirol.
</p>
</article>
<article className="surface-card bg-alpine-900 text-alpine-50">
<p className="eyebrow text-copper-300">Regional verankert</p>
<h2 className="mt-2 text-3xl font-semibold text-white">Reutte als Basis, Tirol als Einsatzgebiet</h2>
<ul className="mt-4 space-y-2 text-sm text-alpine-100">
<li>- Kurze Wege zu Kundenterminen</li>
<li>- Schnelle Abstimmung zwischen Beratung und Technik</li>
<li>- Langfristige Kundenbeziehungen statt kurzfristiger Abschluesse</li>
</ul>
</article>
</div>
</section>
<section className="section-block bg-white/70">
<div className="container-shell">
<p className="eyebrow">Werte</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">So arbeiten wir im Alltag</h2>
<div className="mt-7 grid gap-5 md:grid-cols-3">
{values.map((value) => (
<article className="surface-card" key={value.title}>
<h3 className="text-xl font-semibold text-alpine-900">{value.title}</h3>
<p className="mt-3 text-sm text-alpine-700">{value.text}</p>
</article>
))}
</div>
</div>
</section>
<section className="section-block">
<div className="container-shell">
<p className="eyebrow">Team</p>
<h2 className="mt-2 text-3xl font-semibold text-alpine-900">Ihre Ansprechpartner bei TeleNetSystems</h2>
<div className="mt-7 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{teamMembers.map((member) => (
<article className="overflow-hidden rounded-2xl border border-alpine-200 bg-white shadow-card" key={member.name}>
<Image
alt={`${member.name} von TeleNetSystems`}
className="h-56 w-full object-cover"
src={member.image}
width={420}
height={420}
/>
<div className="p-4">
<h3 className="font-semibold text-alpine-900">{member.name}</h3>
<p className="mt-1 text-sm text-alpine-700">{member.role}</p>
</div>
</article>
))}
</div>
</div>
</section>
<ContactStrip />
</>
);
}
-48
View File
@@ -1,48 +0,0 @@
import { company } from "@/lib/site-content";
export function ContactStrip() {
return (
<section id="kontakt" className="section-block pb-10">
<div className="container-shell">
<div className="rounded-3xl border border-copper-200 bg-copper-50 p-7 shadow-card sm:p-10">
<div className="grid gap-6 md:grid-cols-[1.2fr_1fr] md:items-center">
<div>
<p className="eyebrow text-copper-700">Direkter Kontakt</p>
<h2 className="mt-3 text-3xl font-semibold text-alpine-900">Ein Anruf klaert oft mehr als zehn Mails.</h2>
<p className="mt-4 max-w-2xl text-alpine-700">
Wenn Sie einen Tarif wechseln, einen Standort anbinden oder eine Stoerung melden wollen: Sie landen
nicht in einem anonymen Ticketsystem, sondern bei einem Team aus der Region.
</p>
</div>
<div className="rounded-2xl border border-copper-200 bg-white p-6">
<ul className="space-y-3 text-sm text-alpine-800">
<li>
<span className="font-semibold">Telefon:</span>{" "}
<a className="text-alpine-700 underline decoration-copper-400 underline-offset-4" href={`tel:${company.phone.replace(/\s+/g, "")}`}>
{company.phone}
</a>
</li>
<li>
<span className="font-semibold">Beratung:</span>{" "}
<a className="text-alpine-700 underline decoration-copper-400 underline-offset-4" href={`mailto:${company.email}`}>
{company.email}
</a>
</li>
<li>
<span className="font-semibold">Stoerung:</span>{" "}
<a className="text-alpine-700 underline decoration-copper-400 underline-offset-4" href={`mailto:${company.supportEmail}`}>
{company.supportEmail}
</a>
</li>
<li>
<span className="font-semibold">Erreichbarkeit:</span> {company.openingHours}
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
);
}
-64
View File
@@ -1,64 +0,0 @@
import Link from "next/link";
import Image from "next/image";
type PageHeroProps = {
eyebrow: string;
title: string;
intro: string;
imageSrc: string;
imageAlt: string;
primaryCta?: { label: string; href: string };
secondaryCta?: { label: string; href: string };
};
export function PageHero({
eyebrow,
title,
intro,
imageSrc,
imageAlt,
primaryCta,
secondaryCta
}: PageHeroProps) {
return (
<section className="section-block pt-12 md:pt-16">
<div className="container-shell grid items-center gap-8 lg:grid-cols-[1.05fr_0.95fr]">
<div className="space-y-6">
<p className="eyebrow">{eyebrow}</p>
<h1 className="text-balance text-4xl font-semibold leading-tight text-alpine-900 sm:text-5xl">
{title}
</h1>
<p className="max-w-2xl text-lg leading-relaxed text-alpine-700">{intro}</p>
{(primaryCta || secondaryCta) && (
<div className="flex flex-wrap gap-3">
{primaryCta ? (
<Link className="btn-primary" href={primaryCta.href}>
{primaryCta.label}
</Link>
) : null}
{secondaryCta ? (
<Link className="btn-secondary" href={secondaryCta.href}>
{secondaryCta.label}
</Link>
) : null}
</div>
)}
</div>
<div className="relative overflow-hidden rounded-3xl border border-alpine-200 bg-white shadow-soft">
<div className="relative h-[300px] w-full sm:h-[360px]">
<Image
alt={imageAlt}
className="object-cover"
fill
priority
sizes="(min-width: 1024px) 42vw, 100vw"
src={imageSrc}
/>
</div>
<div className="absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-alpine-900/55 to-transparent" />
</div>
</div>
</section>
);
}
-72
View File
@@ -1,72 +0,0 @@
import Link from "next/link";
import Image from "next/image";
import { company, mainNavigation } from "@/lib/site-content";
export function SiteFooter() {
return (
<footer className="mt-20 border-t border-alpine-200 bg-alpine-900 text-alpine-50">
<div className="container-shell grid gap-10 py-14 md:grid-cols-[1.2fr_1fr_1fr]">
<div className="space-y-4">
<Image
alt="TeleNetSystems Logo hell"
className="h-10 w-auto"
src="/assets/logo/logo-weiss-ffa4cbf6.png"
width={190}
height={50}
/>
<p className="max-w-sm text-sm leading-relaxed text-alpine-100">
TeleNetSystems verbindet technisches Know-how mit regionaler Naehe. Sie bekommen klare Tarife,
erreichbare Ansprechpartner und Support aus Tirol.
</p>
</div>
<div>
<h2 className="font-heading text-lg text-white">Seiten</h2>
<ul className="mt-4 space-y-2 text-sm text-alpine-100">
{mainNavigation.map((item) => (
<li key={item.href}>
<Link className="hover:text-copper-200" href={item.href}>
{item.label}
</Link>
</li>
))}
</ul>
</div>
<div>
<h2 className="font-heading text-lg text-white">Kontakt</h2>
<ul className="mt-4 space-y-2 text-sm text-alpine-100">
<li>{company.name}</li>
<li>{company.address}</li>
<li>
<a className="hover:text-copper-200" href={`tel:${company.phone.replace(/\s+/g, "")}`}>
{company.phone}
</a>
</li>
<li>
<a className="hover:text-copper-200" href={`mailto:${company.email}`}>
{company.email}
</a>
</li>
<li>{company.openingHours}</li>
</ul>
</div>
</div>
<div className="border-t border-alpine-700/80">
<div className="container-shell flex flex-wrap items-center justify-between gap-3 py-4 text-xs text-alpine-200">
<p>© {new Date().getFullYear()} TeleNetSystems GmbH</p>
<div className="flex gap-4">
<Link className="hover:text-copper-200" href="/impressum">
Impressum
</Link>
<Link className="hover:text-copper-200" href="/datenschutz">
Datenschutz
</Link>
</div>
</div>
</div>
</footer>
);
}
-57
View File
@@ -1,57 +0,0 @@
import Link from "next/link";
import Image from "next/image";
import { company, mainNavigation } from "@/lib/site-content";
export function SiteHeader() {
return (
<header className="sticky top-0 z-40 border-b border-alpine-100/70 bg-white/90 backdrop-blur">
<div className="bg-alpine-900 text-alpine-50">
<div className="container-shell flex flex-wrap items-center justify-between gap-2 py-2 text-xs sm:text-sm">
<p>{company.region} | Persoenlicher IT- und Telekom-Service</p>
<div className="flex flex-wrap gap-4">
<a className="hover:text-copper-200" href={`tel:${company.phone.replace(/\s+/g, "")}`}>
{company.phone}
</a>
<a className="hover:text-copper-200" href={`mailto:${company.email}`}>
{company.email}
</a>
</div>
</div>
</div>
<div className="container-shell flex flex-wrap items-center justify-between gap-6 py-4">
<Link className="group inline-flex items-center gap-3" href="/">
<Image
alt="TeleNetSystems Logo"
className="h-10 w-auto transition group-hover:scale-[1.02]"
src="/assets/logo/logo-systems-9fd0db14.png"
width={190}
height={50}
priority
/>
<span className="font-heading text-sm uppercase tracking-[0.22em] text-alpine-700">
Reutte | Tirol
</span>
</Link>
<nav aria-label="Hauptnavigation" className="w-full overflow-x-auto pb-1 lg:w-auto lg:pb-0">
<ul className="flex min-w-max items-center gap-2 text-sm font-semibold text-alpine-700">
{mainNavigation.map((item) => (
<li key={item.href}>
<Link className="nav-chip" href={item.href}>
{item.label}
</Link>
</li>
))}
<li>
<Link className="btn-primary ml-2" href="/#kontakt">
Beratung anfragen
</Link>
</li>
</ul>
</nav>
</div>
</header>
);
}
+18
View File
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);
export default eslintConfig;
-218
View File
@@ -1,218 +0,0 @@
export const company = {
name: "TeleNetSystems GmbH",
shortName: "TeleNetSystems",
region: "Reutte, Tirol",
address: "Untermarkt 12, 6600 Reutte",
phone: "+43 5672 219 40",
email: "kontakt@telenetsystems.at",
supportEmail: "stoerung@telenetsystems.at",
openingHours: "Mo-Fr 08:00-17:30",
emergencyHint: "Stoerungsmeldungen werden auch ausserhalb der Buerozeiten angenommen."
};
export const mainNavigation = [
{ href: "/", label: "Start" },
{ href: "/internet", label: "Internet" },
{ href: "/fernsehen", label: "Fernsehen" },
{ href: "/telefonie", label: "Telefonie" },
{ href: "/leistungen", label: "Leistungen" },
{ href: "/ueber-uns", label: "Ueber uns" }
];
export const serviceHighlights = [
{
title: "Internet in Tirol",
text: "Kabel- und Glasfaser-Tarife mit klaren Geschwindigkeiten, fixen Monatskosten und regionalem Support.",
href: "/internet"
},
{
title: "Fernsehen fuer Zuhause und Betrieb",
text: "TV Privat, Pay TV und TV Business sauber getrennt, damit Sie schnell das passende Paket finden.",
href: "/fernsehen"
},
{
title: "Telefonie ohne Tarifdschungel",
text: "Festnetz und SIP-Loesungen mit nachvollziehbaren Paketen statt Kleingedrucktem.",
href: "/telefonie"
}
];
export const internetTariffs = {
privateCable: [
{
name: "Kabel 150",
speed: "150/30 Mbit/s",
contract: "12 Monate",
setup: "Aktivierung 49 EUR",
price: "34,90 EUR/Monat"
},
{
name: "Kabel 300",
speed: "300/50 Mbit/s",
contract: "12 Monate",
setup: "Aktivierung 49 EUR",
price: "42,90 EUR/Monat"
},
{
name: "Kabel 600",
speed: "600/80 Mbit/s",
contract: "12 Monate",
setup: "Aktivierung 29 EUR",
price: "54,90 EUR/Monat"
}
],
privateFiber: [
{
name: "Glasfaser 300",
speed: "300/150 Mbit/s",
contract: "12 Monate",
setup: "Hausanschluss auf Anfrage",
price: "46,90 EUR/Monat"
},
{
name: "Glasfaser 500",
speed: "500/250 Mbit/s",
contract: "12 Monate",
setup: "Hausanschluss auf Anfrage",
price: "58,90 EUR/Monat"
},
{
name: "Glasfaser 1000",
speed: "1000/500 Mbit/s",
contract: "12 Monate",
setup: "Hausanschluss auf Anfrage",
price: "74,90 EUR/Monat"
}
],
business: [
"Symmetrische Leitungen mit garantierter Bandbreite",
"SLA mit Reaktionszeit ab 4 Stunden",
"Standortvernetzung, Backup-Leitung und fixe Ansprechpartner"
]
};
export const tvTariffs = {
private: [
{
name: "TV Basis",
channels: "85 Sender",
extras: "Replay 24h",
target: "Wohnung, Haus",
price: "18,90 EUR/Monat"
},
{
name: "TV Komfort",
channels: "130 Sender",
extras: "Replay 7 Tage + App",
target: "Familienhaushalte",
price: "24,90 EUR/Monat"
}
],
payTv: [
{
name: "Sport",
channels: "12 Senderpakete",
extras: "monatlich kuendbar",
target: "Live-Sport Fans",
price: "ab 9,90 EUR/Monat"
},
{
name: "Film und Serie",
channels: "8 Senderpakete",
extras: "On-Demand Mediathek",
target: "Streaming-Ergaenzung",
price: "ab 11,90 EUR/Monat"
}
],
business: [
"TV-Loesungen fuer Hotels, Gastronomie und Wartezonen",
"Mehrgeraete-Lizenzierung inklusive zentraler Verwaltung",
"Persoenliche Planung vor Ort in Reutte und Umgebung"
]
};
export const phoneTariffs = [
{
name: "Phone S",
lines: "1 Rufnummer",
included: "500 Minuten AT",
setup: "Portierung inklusive",
price: "8,90 EUR/Monat"
},
{
name: "Phone M",
lines: "2 Rufnummern",
included: "Flat AT + 150 Min EU",
setup: "Portierung inklusive",
price: "14,90 EUR/Monat"
},
{
name: "Phone Business",
lines: "ab 5 Nebenstellen",
included: "SIP-Trunk + Zentrale",
setup: "Einrichtung nach Bedarf",
price: "ab 39,00 EUR/Monat"
}
];
export const testimonials = [
{
quote:
"Bei einer Stoerung war nach 40 Minuten jemand erreichbar, der unser Problem wirklich geloest hat. Genau so sollte regionaler Support sein.",
author: "Markus P.",
context: "Hotelbetrieb, Ausserfern"
},
{
quote:
"Wir wollten kein Callcenter, sondern eine klare Ansprechperson. Seit dem Wechsel laeuft Internet und TV stabil.",
author: "Sabine R.",
context: "Privatkundin, Reutte"
},
{
quote:
"Das Angebot war verstaendlich auf einer Seite erklaert. Keine versteckten Kosten, keine Ueberraschungen.",
author: "Florian K.",
context: "Tischlerei, Lechtal"
}
];
export const faqItems = [
{
question: "Wie schnell bekommen wir einen Termin?",
answer:
"In der Regel innerhalb von zwei Werktagen. Bei Ausfaellen priorisieren wir bestehende Kundinnen und Kunden in Tirol."
},
{
question: "Gibt es versteckte Nebenkosten?",
answer:
"Nein. Aktivierungs- oder Anschlusskosten sind bei jedem Tarif offen ausgewiesen, bevor Sie unterschreiben."
},
{
question: "Bietet TeleNetSystems auch Business-Beratung vor Ort?",
answer:
"Ja. Fuer Unternehmen analysieren wir Infrastruktur und Anforderungen direkt am Standort."
}
];
export const teamMembers = [
{
name: "Julia Besler",
role: "Kundenberatung Privat",
image: "/assets/team/julia-20besler-f4fe1bff-fb0f3af2.jpg"
},
{
name: "Furkan Demirel",
role: "Netzwerk und Service",
image: "/assets/team/furkan-demirel-4112a488-f6dfc223.jpg"
},
{
name: "Timo",
role: "Projektleitung Business",
image: "/assets/team/timo-224648e7-f0782cdb.jpg"
},
{
name: "Lorena",
role: "Support und Koordination",
image: "/assets/team/lorena-c31636eb-2f817bb4.jpg"
}
];
-5
View File
@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
+4 -4
View File
@@ -1,10 +1,10 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
output: "export", output: "export",
trailingSlash: true,
images: { images: {
unoptimized: true unoptimized: true,
} },
trailingSlash: true,
}; };
export default nextConfig; export default nextConfig;
+1353 -1429
View File
File diff suppressed because it is too large Load Diff
+18 -16
View File
@@ -1,28 +1,30 @@
{ {
"name": "telenetsystems-website", "name": "telenetsystems",
"version": "1.0.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "eslint"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/postcss": "^4.1.18", "clsx": "^2.1.1",
"next": "^14.2.0", "framer-motion": "^12.33.0",
"react": "^18.2.0", "lucide-react": "^0.563.0",
"react-dom": "^18.2.0" "next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.0", "@tailwindcss/postcss": "^4",
"@types/react": "^18.2.0", "@types/node": "^20",
"@types/react-dom": "^18.2.0", "@types/react": "^19",
"autoprefixer": "^10.4.17", "@types/react-dom": "^19",
"eslint": "^8.57.0", "eslint": "^9",
"eslint-config-next": "^14.2.0", "eslint-config-next": "16.1.6",
"postcss": "^8.4.35", "tailwindcss": "^4",
"tailwindcss": "^3.4.1", "typescript": "^5"
"typescript": "^5.3.3"
} }
} }
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
+7
View File
@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
-25
View File
@@ -1,25 +0,0 @@
# Asset-Struktur für Website
Diese Ordner enthalten die Bilder und Medien für die Website.
## Ordnerstruktur
| Ordner | Verwendung |
|--------|------------|
| `logo/` | Hauptlogo, Logo für dunkle Hintergründe, Favicon |
| `hero/` | Große Bilder für Startseite und Unterseiten |
| `team/` | Fotos von Teammitgliedern |
| `gallery/` | Projektbilder, Referenzen, Portfolio |
| `icons/` | Service-Icons, Feature-Icons (SVG bevorzugt) |
| `backgrounds/` | Muster, Texturen, Hintergrundbilder |
## Empfehlungen
- **Logos**: PNG mit transparentem Hintergrund, mindestens 500px breit
- **Hero-Bilder**: JPG, mindestens 1920x1080px
- **Team-Fotos**: Quadratisch, mindestens 400x400px
- **Icons**: SVG bevorzugt, alternativ PNG mit transparentem Hintergrund
## Verwendung
Die KI verwendet diese Bilder automatisch bei der Website-Generierung.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before

Width:  |  Height:  |  Size: 253 KiB

After

Width:  |  Height:  |  Size: 253 KiB

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 227 KiB

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 274 KiB

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 127 KiB

+3
View File
@@ -0,0 +1,3 @@
User-agent: *
Allow: /
Sitemap: https://www.telenetsystems.at/sitemap.xml
+43
View File
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.telenetsystems.at/</loc>
<lastmod>2026-02-06</lastmod>
<priority>1.0</priority>
</url>
<url>
<loc>https://www.telenetsystems.at/internet/</loc>
<lastmod>2026-02-06</lastmod>
<priority>0.9</priority>
</url>
<url>
<loc>https://www.telenetsystems.at/fernsehen/</loc>
<lastmod>2026-02-06</lastmod>
<priority>0.9</priority>
</url>
<url>
<loc>https://www.telenetsystems.at/telefonie/</loc>
<lastmod>2026-02-06</lastmod>
<priority>0.9</priority>
</url>
<url>
<loc>https://www.telenetsystems.at/leistungen/</loc>
<lastmod>2026-02-06</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://www.telenetsystems.at/ueber-uns/</loc>
<lastmod>2026-02-06</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://www.telenetsystems.at/impressum/</loc>
<lastmod>2026-02-06</lastmod>
<priority>0.3</priority>
</url>
<url>
<loc>https://www.telenetsystems.at/datenschutz/</loc>
<lastmod>2026-02-06</lastmod>
<priority>0.3</priority>
</url>
</urlset>
+99
View File
@@ -0,0 +1,99 @@
import type { Metadata } from "next";
import Container from "@/components/ui/Container";
export const metadata: Metadata = {
title: "Datenschutz",
description: "Datenschutzerklärung der TeleNetSystems gemäß DSGVO. Informationen zur Verarbeitung Ihrer personenbezogenen Daten.",
};
export default function DatenschutzPage() {
return (
<section className="py-20 lg:py-28">
<Container>
<article className="prose prose-neutral mx-auto max-w-3xl">
<h1 className="text-display font-bold tracking-tight text-neutral-900">Datenschutzerklärung</h1>
<h2 className="text-xl font-semibold text-neutral-900 mt-10">1. Verantwortlicher</h2>
<p className="text-neutral-600 leading-relaxed">
TeleNetSystems<br />
Untermarkt 16<br />
6600 Reutte, Tirol, Österreich<br />
E-Mail: info@telenetsystems.at<br />
Telefon: +43 5672 21400
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">2. Erhebung und Verarbeitung personenbezogener Daten</h2>
<p className="text-neutral-600 leading-relaxed">
Wenn Sie unsere Website besuchen, werden automatisch Informationen allgemeiner Natur
erfasst. Diese Informationen (Server-Logfiles) beinhalten etwa die Art des Webbrowsers,
das verwendete Betriebssystem, den Domainnamen Ihres Internet-Service-Providers und
ähnliches. Hierbei handelt es sich ausschließlich um Informationen, welche keine
Rückschlüsse auf Ihre Person zulassen.
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">3. Kontaktformular</h2>
<p className="text-neutral-600 leading-relaxed">
Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben
aus dem Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten
zwecks Bearbeitung der Anfrage und für den Fall von Anschlussfragen bei uns
gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
</p>
<p className="text-neutral-600 leading-relaxed mt-2">
Die Verarbeitung der in das Kontaktformular eingegebenen Daten erfolgt somit
ausschließlich auf Grundlage Ihrer Einwilligung (Art. 6 Abs. 1 lit. a DSGVO).
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">4. Cookies</h2>
<p className="text-neutral-600 leading-relaxed">
Diese Website verwendet keine Tracking-Cookies. Es werden ausschließlich technisch
notwendige Cookies eingesetzt, die für den Betrieb der Website erforderlich sind.
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">5. Ihre Rechte</h2>
<p className="text-neutral-600 leading-relaxed">
Sie haben jederzeit das Recht auf unentgeltliche Auskunft über Ihre gespeicherten
personenbezogenen Daten, deren Herkunft und Empfänger und den Zweck der
Datenverarbeitung sowie ein Recht auf Berichtigung, Sperrung oder Löschung
dieser Daten.
</p>
<p className="text-neutral-600 leading-relaxed mt-2">
Ihnen stehen im Wesentlichen folgende Rechte zu:
</p>
<ul className="text-neutral-600 list-disc pl-6 space-y-1 mt-2">
<li>Recht auf Auskunft (Art. 15 DSGVO)</li>
<li>Recht auf Berichtigung (Art. 16 DSGVO)</li>
<li>Recht auf Löschung (Art. 17 DSGVO)</li>
<li>Recht auf Einschränkung der Verarbeitung (Art. 18 DSGVO)</li>
<li>Recht auf Datenübertragbarkeit (Art. 20 DSGVO)</li>
<li>Recht auf Widerspruch (Art. 21 DSGVO)</li>
</ul>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">6. Beschwerderecht bei der Aufsichtsbehörde</h2>
<p className="text-neutral-600 leading-relaxed">
Wenn Sie der Ansicht sind, dass die Verarbeitung Ihrer personenbezogenen Daten
gegen die DSGVO verstößt, haben Sie das Recht, sich bei der Österreichischen
Datenschutzbehörde zu beschweren.
</p>
<p className="text-neutral-600 leading-relaxed mt-2">
Österreichische Datenschutzbehörde<br />
Barichgasse 40-42<br />
1030 Wien<br />
Telefon: +43 1 52 152-0<br />
E-Mail: dsb@dsb.gv.at
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">7. Änderung dieser Datenschutzerklärung</h2>
<p className="text-neutral-600 leading-relaxed">
Wir behalten uns vor, diese Datenschutzerklärung gelegentlich anzupassen, damit
sie stets den aktuellen rechtlichen Anforderungen entspricht oder um Änderungen
unserer Leistungen in der Datenschutzerklärung umzusetzen.
</p>
<p className="text-neutral-500 text-sm mt-10">
Stand: Februar 2026
</p>
</article>
</Container>
</section>
);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+114
View File
@@ -0,0 +1,114 @@
import type { Metadata } from "next";
import { Building2 } from "lucide-react";
import PageHero from "@/components/sections/PageHero";
import TariffTable from "@/components/sections/TariffTable";
import ContactSection from "@/components/sections/ContactSection";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import Button from "@/components/ui/Button";
import { TV_PRIVAT_PAKETE } from "@/lib/constants";
export const metadata: Metadata = {
title: "Fernsehen",
description:
"TV-Pakete für Privat- und Geschäftskunden in Tirol. Von der Grundversorgung bis zum individuellen Business-TV. Empfang über Kabel, kein Schüssel nötig.",
};
export default function FernsehenPage() {
return (
<>
<PageHero
title="Fernsehen mit TeleNetSystems"
description="Kein Herumfummeln an der Satellitenschüssel. Unser TV kommt über Kabel — zuverlässig, in HD und mit einer Senderauswahl, die passt."
backgroundImage="/images/hero/tv-header.jpg"
/>
{/* TV Privat */}
<section className="py-20 lg:py-24" aria-labelledby="tv-privat-heading">
<Container>
<SectionHeading
title="TV für Privatkunden"
subtitle="Drei Pakete, klare Inhalte. Wählen Sie, was zu Ihrem Fernsehabend passt."
/>
<TariffTable plans={TV_PRIVAT_PAKETE} />
</Container>
</section>
{/* Pay TV */}
<section className="bg-neutral-50 py-20 lg:py-24" aria-labelledby="paytv-heading">
<Container>
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-2">
<div className="overflow-hidden rounded-card">
<img
src="/images/gallery/tv-paytv.jpg"
alt="Pay-TV Zusatzpakete"
className="w-full object-cover"
loading="lazy"
width={600}
height={400}
/>
</div>
<div>
<h2 id="paytv-heading" className="text-title font-bold text-neutral-900">
Pay TV mehr Auswahl, wenn Sie möchten
</h2>
<p className="mt-4 text-neutral-600 leading-relaxed">
Sport, Filme, Dokumentationen oder internationale Sender: Mit unseren
Pay-TV-Zusatzpaketen erweitern Sie Ihr Programm gezielt. Kein Abo-Dschungel,
sondern klare Zusatzoptionen zu Ihrem bestehenden TV-Paket.
</p>
<p className="mt-4 text-neutral-600 leading-relaxed">
Welche Zusatzpakete für Sie sinnvoll sind, hängt von Ihren Interessen ab.
Wir beraten Sie gerne unverbindlich und ehrlich.
</p>
<Button href="/#kontakt" variant="outline" size="md" className="mt-8">
Zu Pay TV beraten lassen
</Button>
</div>
</div>
</Container>
</section>
{/* TV Business */}
<section className="py-20 lg:py-24" aria-labelledby="tv-business-heading">
<Container>
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-2">
<div>
<div className="flex items-center gap-3 mb-4">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-amber-100">
<Building2 className="h-5 w-5 text-amber-700" aria-hidden="true" />
</div>
<h2 id="tv-business-heading" className="text-title font-bold text-neutral-900">
TV für Unternehmen
</h2>
</div>
<p className="text-neutral-600 leading-relaxed">
Hotels, Pensionen, Wartezimmer, Gastronomiebetriebe überall dort, wo Fernsehen
zum Angebot gehört, brauchen Sie eine Lösung, die einfach funktioniert.
</p>
<p className="mt-4 text-neutral-600 leading-relaxed">
Wir richten Ihr Business-TV so ein, dass es zu Ihrem Betrieb passt. Senderauswahl,
Anzahl der Anschlüsse, zentrale Steuerung alles wird individuell abgestimmt.
</p>
<Button href="/#kontakt" variant="primary" size="md" className="mt-8">
Business-TV anfragen
</Button>
</div>
<div className="overflow-hidden rounded-card">
<img
src="/images/gallery/tv-business.jpg"
alt="TV-Lösungen für Hotels und Geschäftskunden"
className="w-full object-cover"
loading="lazy"
width={600}
height={400}
/>
</div>
</div>
</Container>
</section>
<ContactSection />
</>
);
}
+1 -3
View File
@@ -1,6 +1,4 @@
@tailwind base; @import "tailwindcss";
@tailwind components;
@tailwind utilities;
@theme inline { @theme inline {
--color-primary-50: #fef7ed; --color-primary-50: #fef7ed;
+75
View File
@@ -0,0 +1,75 @@
import type { Metadata } from "next";
import Container from "@/components/ui/Container";
export const metadata: Metadata = {
title: "Impressum",
description: "Impressum und rechtliche Informationen der TeleNetSystems in Reutte, Tirol.",
};
export default function ImpressumPage() {
return (
<section className="py-20 lg:py-28">
<Container>
<article className="prose prose-neutral mx-auto max-w-3xl">
<h1 className="text-display font-bold tracking-tight text-neutral-900">Impressum</h1>
<h2 className="text-xl font-semibold text-neutral-900 mt-10">Angaben gemäß § 5 ECG</h2>
<p>
TeleNetSystems<br />
Untermarkt 16<br />
6600 Reutte<br />
Tirol, Österreich
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">Kontakt</h2>
<p>
Telefon: +43 5672 21400<br />
E-Mail: info@telenetsystems.at
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">Unternehmensgegenstand</h2>
<p>
IT-Dienstleistungen und Telekommunikation
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">Aufsichtsbehörde</h2>
<p>
Bezirkshauptmannschaft Reutte
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">Berufsbezeichnung und berufsrechtliche Regelungen</h2>
<p>
Berufsbezeichnung: IT-Dienstleister<br />
Verleihungsstaat: Österreich
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">Haftungsausschluss</h2>
<h3 className="text-lg font-semibold text-neutral-900 mt-6">Haftung für Inhalte</h3>
<p className="text-neutral-600 leading-relaxed">
Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit,
Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen.
Als Diensteanbieter sind wir gemäß § 7 Abs. 1 ECG für eigene Inhalte auf diesen
Seiten nach den allgemeinen Gesetzen verantwortlich.
</p>
<h3 className="text-lg font-semibold text-neutral-900 mt-6">Haftung für Links</h3>
<p className="text-neutral-600 leading-relaxed">
Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir
keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine
Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige
Anbieter oder Betreiber der Seiten verantwortlich.
</p>
<h2 className="text-xl font-semibold text-neutral-900 mt-8">Urheberrecht</h2>
<p className="text-neutral-600 leading-relaxed">
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten
unterliegen dem österreichischen Urheberrecht. Die Vervielfältigung, Bearbeitung,
Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes
bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.
</p>
</article>
</Container>
</section>
);
}
+93
View File
@@ -0,0 +1,93 @@
import type { Metadata } from "next";
import { Building2 } from "lucide-react";
import PageHero from "@/components/sections/PageHero";
import TariffTable from "@/components/sections/TariffTable";
import ContactSection from "@/components/sections/ContactSection";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import Button from "@/components/ui/Button";
import { INTERNET_TARIFE } from "@/lib/constants";
export const metadata: Metadata = {
title: "Internet",
description:
"Schnelles Internet in Reutte und Tirol. Kabel- und Glasfaser-Tarife für Privat- und Geschäftskunden. Faire Preise, stabile Verbindung.",
};
export default function InternetPage() {
const kabelTarife = INTERNET_TARIFE.filter((t) => t.category === "kabel");
const glasfaserTarife = INTERNET_TARIFE.filter((t) => t.category === "glasfaser");
return (
<>
<PageHero
title="Internet für Reutte und Umgebung"
description="Ob Streaming, Homeoffice oder einfach sorgenfreies Surfen — wir bringen Sie ins Netz. Stabil, schnell und ohne Überraschungen auf der Rechnung."
backgroundImage="/images/hero/internet-header.jpg"
/>
{/* Kabel-Internet */}
<section className="py-20 lg:py-24" aria-labelledby="kabel-heading">
<Container>
<SectionHeading
title="Kabel-Internet"
subtitle="Unsere bewährten Tarife über das Kabelnetz. Verfügbar im Großteil des Außerferns."
/>
<TariffTable plans={kabelTarife} />
</Container>
</section>
{/* Glasfaser */}
<section className="bg-neutral-50 py-20 lg:py-24" aria-labelledby="glasfaser-heading">
<Container>
<SectionHeading
title="Glasfaser-Internet"
subtitle="Wo Glasfaser verfügbar ist, geht mehr. Deutlich mehr. Ideal für anspruchsvolle Nutzer und Mehrpersonenhaushalte."
/>
<TariffTable plans={glasfaserTarife} />
</Container>
</section>
{/* Business Internet */}
<section className="py-20 lg:py-24" aria-labelledby="business-internet-heading">
<Container>
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-2">
<div>
<div className="flex items-center gap-3 mb-4">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-amber-100">
<Building2 className="h-5 w-5 text-amber-700" aria-hidden="true" />
</div>
<h2 id="business-internet-heading" className="text-title font-bold text-neutral-900">
Internet für Ihr Unternehmen
</h2>
</div>
<p className="text-neutral-600 leading-relaxed">
Standardtarife passen nicht immer. Wenn Ihr Betrieb symmetrische Bandbreiten braucht,
feste IP-Adressen benötigt oder mehrere Standorte vernetzen will sprechen Sie mit uns.
</p>
<p className="mt-4 text-neutral-600 leading-relaxed">
Wir schauen uns Ihre Situation an und machen Ihnen ein Angebot, das zu Ihrem
Unternehmen passt. Keine Pakete von der Stange, sondern genau das, was Sie brauchen.
</p>
<Button href="/#kontakt" variant="primary" size="md" className="mt-8">
Individuelles Angebot anfragen
</Button>
</div>
<div className="overflow-hidden rounded-card">
<img
src="/images/gallery/serverraum.jpg"
alt="Serverraum für Business-Internet-Lösungen"
className="w-full object-cover"
loading="lazy"
width={600}
height={400}
/>
</div>
</div>
</Container>
</section>
<ContactSection />
</>
);
}
+108
View File
@@ -0,0 +1,108 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import "./globals.css";
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
display: "swap",
});
export const metadata: Metadata = {
metadataBase: new URL("https://www.telenetsystems.at"),
title: {
default: "TeleNetSystems | Internet, TV & Telefonie in Reutte, Tirol",
template: "%s | TeleNetSystems",
},
description:
"Ihr regionaler Partner für Internet, Fernsehen und Telefonie in Reutte und Tirol. Persönliche Beratung, stabile Technik, faire Preise.",
keywords: [
"Internet Reutte",
"TV Anbieter Tirol",
"Telefonie Reutte",
"TeleNetSystems",
"Internet Tirol",
"Glasfaser Reutte",
],
authors: [{ name: "TeleNetSystems" }],
openGraph: {
type: "website",
locale: "de_AT",
siteName: "TeleNetSystems",
title: "TeleNetSystems | Internet, TV & Telefonie in Reutte, Tirol",
description:
"Ihr regionaler Partner für Internet, Fernsehen und Telefonie in Reutte und Tirol.",
images: [
{
url: "/images/logo/logo-systems.png",
width: 500,
height: 200,
alt: "TeleNetSystems Logo",
},
],
},
icons: {
icon: "/favicon.ico",
},
};
const jsonLd = {
"@context": "https://schema.org",
"@type": "LocalBusiness",
name: "TeleNetSystems",
description:
"Regionaler IT- und Telekommunikationsanbieter in Reutte, Tirol. Internet, Fernsehen und Telefonie für Privat- und Geschäftskunden.",
address: {
"@type": "PostalAddress",
streetAddress: "Untermarkt 16",
addressLocality: "Reutte",
addressRegion: "Tirol",
postalCode: "6600",
addressCountry: "AT",
},
telephone: "+43 5672 21400",
email: "info@telenetsystems.at",
url: "https://www.telenetsystems.at",
image: "/images/logo/logo-systems.png",
areaServed: {
"@type": "GeoCircle",
geoMidpoint: {
"@type": "GeoCoordinates",
latitude: 47.4833,
longitude: 10.7167,
},
geoRadius: "50000",
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="de" className={inter.variable}>
<head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
</head>
<body className="antialiased">
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50 focus:rounded-lg focus:bg-primary-600 focus:px-4 focus:py-2 focus:text-white"
>
Zum Inhalt springen
</a>
<Header />
<main id="main-content" className="pt-16 lg:pt-20">
{children}
</main>
<Footer />
</body>
</html>
);
}
+147
View File
@@ -0,0 +1,147 @@
import type { Metadata } from "next";
import { Wifi, Tv, Phone, Server, Shield, Wrench } from "lucide-react";
import PageHero from "@/components/sections/PageHero";
import ContactSection from "@/components/sections/ContactSection";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import Button from "@/components/ui/Button";
export const metadata: Metadata = {
title: "Leistungen",
description:
"Alle Dienstleistungen von TeleNetSystems im Überblick. Internet, TV, Telefonie und IT-Lösungen für Privat- und Geschäftskunden in Reutte und Tirol.",
};
const services = [
{
icon: Wifi,
title: "Internet",
description:
"Kabel und Glasfaser für Privathaushalte und Unternehmen. Von 30 Mbit/s bis zu 500 Mbit/s — je nach Verfügbarkeit und Bedarf.",
href: "/internet",
image: "/images/gallery/internet.jpg",
imageAlt: "Internet-Dienste von TeleNetSystems",
},
{
icon: Tv,
title: "Fernsehen",
description:
"TV-Pakete über Kabel, von der Grundversorgung bis zum umfangreichen Premium-Paket. Für Privatkunden und Unternehmen.",
href: "/fernsehen",
image: "/images/gallery/tv-privat.jpg",
imageAlt: "TV-Dienste von TeleNetSystems",
},
{
icon: Phone,
title: "Telefonie",
description:
"Festnetz-Tarife, die zum Telefonierverhalten passen. Minutengenau oder als Flatrate, auch in Kombination mit Internet.",
href: "/telefonie",
image: "/images/gallery/telefonie1.jpg",
imageAlt: "Telefonie-Dienste von TeleNetSystems",
},
];
const additionalServices = [
{
icon: Server,
title: "IT-Infrastruktur",
description: "Netzwerke, Server und die Technik dahinter. Wir planen, installieren und betreuen Ihre IT-Umgebung.",
},
{
icon: Shield,
title: "Netzwerksicherheit",
description: "Firewalls, sichere Zugänge und Datenschutz. Damit Ihre Daten dort bleiben, wo sie hingehören.",
},
{
icon: Wrench,
title: "Technischer Support",
description: "Wenn etwas nicht funktioniert, sind wir da. Vor Ort in Reutte, nicht am anderen Ende einer Hotline.",
},
];
export default function LeistungenPage() {
return (
<>
<PageHero
title="Unsere Leistungen im Überblick"
description="Von Internet über TV bis zur kompletten IT-Betreuung. Alles aus einer Hand, alles aus Reutte."
backgroundImage="/images/gallery/firma1.jpg"
/>
{/* Main Services */}
<section className="py-20 lg:py-24" aria-labelledby="main-services-heading">
<Container>
<SectionHeading
title="Unsere Kernbereiche"
subtitle="Drei Dienste, auf die Sie sich verlassen können."
/>
<div className="space-y-16" id="main-services-heading">
{services.map((service, index) => {
const Icon = service.icon;
const isReversed = index % 2 !== 0;
return (
<div
key={service.title}
className={`grid grid-cols-1 items-center gap-12 lg:grid-cols-2 ${isReversed ? "lg:direction-rtl" : ""}`}
>
<div className={isReversed ? "lg:order-2" : ""}>
<div className="flex items-center gap-3 mb-4">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-100">
<Icon className="h-5 w-5 text-primary-700" aria-hidden="true" />
</div>
<h3 className="text-title font-bold text-neutral-900">{service.title}</h3>
</div>
<p className="text-neutral-600 leading-relaxed">{service.description}</p>
<Button href={service.href} variant="outline" size="sm" className="mt-6">
Zu den {service.title}-Tarifen
</Button>
</div>
<div className={`overflow-hidden rounded-card ${isReversed ? "lg:order-1" : ""}`}>
<img
src={service.image}
alt={service.imageAlt}
className="w-full object-cover"
loading="lazy"
width={600}
height={400}
/>
</div>
</div>
);
})}
</div>
</Container>
</section>
{/* Additional Services */}
<section className="bg-neutral-50 py-20 lg:py-24" aria-labelledby="additional-services-heading">
<Container>
<SectionHeading
title="Darüber hinaus"
subtitle="Neben Internet, TV und Telefonie unterstützen wir Sie auch bei der IT-Infrastruktur."
/>
<div className="grid grid-cols-1 gap-8 md:grid-cols-3" id="additional-services-heading">
{additionalServices.map((service) => {
const Icon = service.icon;
return (
<div
key={service.title}
className="rounded-card bg-white p-8 shadow-sm border border-neutral-200"
>
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary-100">
<Icon className="h-6 w-6 text-primary-700" aria-hidden="true" />
</div>
<h3 className="mt-4 text-lg font-semibold text-neutral-900">{service.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-neutral-600">{service.description}</p>
</div>
);
})}
</div>
</Container>
</section>
<ContactSection />
</>
);
}
+21
View File
@@ -0,0 +1,21 @@
import Container from "@/components/ui/Container";
import Button from "@/components/ui/Button";
export default function NotFound() {
return (
<section className="flex min-h-[60vh] items-center py-20">
<Container className="text-center">
<p className="text-6xl font-bold text-primary-500">404</p>
<h1 className="mt-4 text-2xl font-bold text-neutral-900">
Diese Seite gibt es leider nicht
</h1>
<p className="mt-4 text-neutral-600">
Die angeforderte Seite konnte nicht gefunden werden. Vielleicht hilft Ihnen die Startseite weiter.
</p>
<Button href="/" variant="primary" size="md" className="mt-8">
Zur Startseite
</Button>
</Container>
</section>
);
}
+19
View File
@@ -0,0 +1,19 @@
import HeroSection from "@/components/sections/HeroSection";
import ServiceOverview from "@/components/sections/ServiceOverview";
import CustomerSegments from "@/components/sections/CustomerSegments";
import ValueProposition from "@/components/sections/ValueProposition";
import TrustSection from "@/components/sections/TrustSection";
import ContactSection from "@/components/sections/ContactSection";
export default function Home() {
return (
<>
<HeroSection />
<ServiceOverview />
<CustomerSegments />
<ValueProposition />
<TrustSection />
<ContactSection />
</>
);
}
+63
View File
@@ -0,0 +1,63 @@
import type { Metadata } from "next";
import PageHero from "@/components/sections/PageHero";
import TariffTable from "@/components/sections/TariffTable";
import ContactSection from "@/components/sections/ContactSection";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import { TELEFONIE_TARIFE } from "@/lib/constants";
export const metadata: Metadata = {
title: "Telefonie",
description:
"Günstige Telefonie-Tarife in Reutte und Tirol. Festnetz-Flatrates und minutengenaue Abrechnung. Klar, einfach, regional.",
};
export default function TelefoniePage() {
return (
<>
<PageHero
title="Telefonie — klar und günstig"
description="Festnetz, das funktioniert. Ohne Vertragsfallen, ohne versteckte Kosten. Genau das, was Sie zum Telefonieren brauchen."
/>
{/* Telefonie Tarife */}
<section className="py-20 lg:py-24" aria-labelledby="telefonie-heading">
<Container>
<SectionHeading
title="Unsere Telefonie-Tarife"
subtitle="Von der minutengenauen Abrechnung bis zur Flatrate. Wählen Sie den Tarif, der zu Ihrem Telefonierverhalten passt."
/>
<TariffTable plans={TELEFONIE_TARIFE} />
</Container>
</section>
{/* Additional Info */}
<section className="bg-neutral-50 py-20 lg:py-24" aria-labelledby="telefonie-info-heading">
<Container>
<div className="mx-auto max-w-3xl">
<h2 id="telefonie-info-heading" className="text-title font-bold text-neutral-900 text-center">
Gut zu wissen
</h2>
<div className="mt-8 space-y-6 text-neutral-600 leading-relaxed">
<p>
Unsere Telefonie-Tarife lassen sich mit jedem Internet-Paket kombinieren. So nutzen
Sie beides über einen Anschluss und haben nur einen Ansprechpartner.
</p>
<p>
Rufnummernmitnahme? Kein Problem. Wir kümmern uns darum, dass Ihre bestehende
Nummer erhalten bleibt. Der Wechsel läuft im Hintergrund Sie müssen sich um
nichts kümmern.
</p>
<p>
Für Geschäftskunden bieten wir auch Telefonanlagen und SIP-Trunking an.
Sprechen Sie uns einfach an.
</p>
</div>
</div>
</Container>
</section>
<ContactSection />
</>
);
}
+117
View File
@@ -0,0 +1,117 @@
import type { Metadata } from "next";
import { Heart, Target, Users, Zap } from "lucide-react";
import PageHero from "@/components/sections/PageHero";
import TeamGrid from "@/components/sections/TeamGrid";
import ContactSection from "@/components/sections/ContactSection";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
export const metadata: Metadata = {
title: "Über uns",
description:
"Lernen Sie das Team hinter TeleNetSystems kennen. Regional verwurzelt in Reutte, Tirol. Persönlicher Service seit über 20 Jahren.",
};
const companyValues = [
{
icon: Heart,
title: "Ehrliche Beratung",
description: "Wir empfehlen, was sinnvoll ist — nicht, was die höchste Marge bringt.",
},
{
icon: Target,
title: "Klare Kommunikation",
description: "Was wir versprechen, halten wir. Ohne Sternchen im Kleingedruckten.",
},
{
icon: Users,
title: "Regionale Verantwortung",
description: "Wir leben und arbeiten hier. Was wir tun, hat direkten Einfluss auf unsere Nachbarn.",
},
{
icon: Zap,
title: "Schnelles Handeln",
description: "Wenn es brennt, sind wir da. Nicht morgen, sondern heute.",
},
];
export default function UeberUnsPage() {
return (
<>
<PageHero
title="Wir sind TeleNetSystems"
description="Ein Team aus Reutte, das Technik versteht und Menschen ernst nimmt."
backgroundImage="/images/team/team-telenet.jpg"
/>
{/* Company Story */}
<section className="py-20 lg:py-24" aria-labelledby="story-heading">
<Container>
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-2">
<div>
<h2 id="story-heading" className="text-title font-bold text-neutral-900">
Aus der Region, für die Region
</h2>
<div className="mt-6 space-y-4 text-neutral-600 leading-relaxed">
<p>
TeleNetSystems gibt es nicht erst seit gestern. Seit über zwei Jahrzehnten
versorgen wir Reutte und das Außerfern mit Internet, Fernsehen und Telefonie.
Angefangen hat alles mit ein paar Kabelanschlüssen. Heute betreuen wir
über tausend Kunden privat und gewerblich.
</p>
<p>
Was sich nicht geändert hat: Wir sitzen hier. In Reutte, nicht in Wien oder
München. Wenn Sie anrufen, heben wir ab. Wenn etwas nicht funktioniert,
kommen wir vorbei. Das klingt selbstverständlich, ist es aber bei den
Großen der Branche längst nicht mehr.
</p>
<p>
Uns geht es nicht darum, die meisten Kunden zu haben. Es geht darum,
die Kunden, die wir haben, gut zu betreuen. Daran messen wir uns selbst.
</p>
</div>
</div>
<div className="overflow-hidden rounded-card">
<img
src="/images/gallery/firma1.jpg"
alt="TeleNetSystems Firmengebäude in Reutte"
className="w-full object-cover"
loading="lazy"
width={600}
height={400}
/>
</div>
</div>
</Container>
</section>
{/* Values */}
<section className="bg-neutral-50 py-20 lg:py-24" aria-labelledby="values-heading">
<Container>
<SectionHeading
title="Wofür wir stehen"
subtitle="Vier Grundsätze, an denen wir uns im Alltag messen."
/>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4" id="values-heading">
{companyValues.map((value) => {
const Icon = value.icon;
return (
<div key={value.title} className="rounded-card bg-white p-6 shadow-sm border border-neutral-200">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary-100">
<Icon className="h-6 w-6 text-primary-700" aria-hidden="true" />
</div>
<h3 className="mt-4 text-base font-semibold text-neutral-900">{value.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-neutral-600">{value.description}</p>
</div>
);
})}
</div>
</Container>
</section>
<TeamGrid />
<ContactSection />
</>
);
}
+118
View File
@@ -0,0 +1,118 @@
import Link from "next/link";
import { Phone, Mail, MapPin } from "lucide-react";
import Container from "@/components/ui/Container";
import { COMPANY_NAME, CONTACT, FOOTER_LINKS } from "@/lib/constants";
export default function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="bg-neutral-900 text-neutral-300" role="contentinfo">
<Container className="py-16">
<div className="grid grid-cols-1 gap-12 sm:grid-cols-2 lg:grid-cols-4">
{/* Company Info */}
<div className="sm:col-span-2 lg:col-span-1">
<Link href="/" aria-label="TeleNetSystems Zur Startseite">
<img
src="/images/logo/logo-weiss.png"
alt="TeleNetSystems Logo"
className="h-10 w-auto"
width={160}
height={40}
/>
</Link>
<p className="mt-4 text-sm leading-relaxed text-neutral-400">
Ihr regionaler Partner für Internet, Fernsehen und Telefonie in Reutte und ganz Tirol.
</p>
<div className="mt-6 space-y-3">
<a
href={`tel:${CONTACT.phone.replace(/\s/g, "")}`}
className="flex items-center gap-2 text-sm text-neutral-400 transition-colors hover:text-white"
>
<Phone className="h-4 w-4 flex-shrink-0" aria-hidden="true" />
{CONTACT.phone}
</a>
<a
href={`mailto:${CONTACT.email}`}
className="flex items-center gap-2 text-sm text-neutral-400 transition-colors hover:text-white"
>
<Mail className="h-4 w-4 flex-shrink-0" aria-hidden="true" />
{CONTACT.email}
</a>
<div className="flex items-start gap-2 text-sm text-neutral-400">
<MapPin className="h-4 w-4 flex-shrink-0 mt-0.5" aria-hidden="true" />
<address className="not-italic">
{CONTACT.address.slice(1).join(", ")}
</address>
</div>
</div>
</div>
{/* Services */}
<div>
<h3 className="text-sm font-semibold uppercase tracking-wider text-white">
Dienste
</h3>
<ul className="mt-4 space-y-3">
{FOOTER_LINKS.services.map((link) => (
<li key={link.href}>
<Link
href={link.href}
className="text-sm text-neutral-400 transition-colors hover:text-white"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
{/* Company */}
<div>
<h3 className="text-sm font-semibold uppercase tracking-wider text-white">
Unternehmen
</h3>
<ul className="mt-4 space-y-3">
{FOOTER_LINKS.company.map((link) => (
<li key={link.href}>
<Link
href={link.href}
className="text-sm text-neutral-400 transition-colors hover:text-white"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
{/* Legal */}
<div>
<h3 className="text-sm font-semibold uppercase tracking-wider text-white">
Rechtliches
</h3>
<ul className="mt-4 space-y-3">
{FOOTER_LINKS.legal.map((link) => (
<li key={link.href}>
<Link
href={link.href}
className="text-sm text-neutral-400 transition-colors hover:text-white"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
</div>
{/* Bottom Bar */}
<div className="mt-12 border-t border-neutral-800 pt-8 text-center">
<p className="text-sm text-neutral-500">
&copy; {currentYear} {COMPANY_NAME}. Alle Rechte vorbehalten.
</p>
</div>
</Container>
</footer>
);
}
+79
View File
@@ -0,0 +1,79 @@
"use client";
import { useState, useEffect, useCallback } from "react";
import Link from "next/link";
import { Menu } from "lucide-react";
import { cn } from "@/lib/utils";
import Navigation from "./Navigation";
import MobileMenu from "./MobileMenu";
import Button from "@/components/ui/Button";
export default function Header() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
const closeMobileMenu = useCallback(() => {
setIsMobileMenuOpen(false);
}, []);
return (
<header
className={cn(
"fixed top-0 left-0 right-0 z-30 transition-all duration-300",
isScrolled
? "bg-white/95 shadow-sm backdrop-blur-sm"
: "bg-white"
)}
role="banner"
>
<div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-4 sm:px-6 lg:h-20 lg:px-8">
<Link
href="/"
className="flex-shrink-0"
aria-label="TeleNetSystems Zur Startseite"
>
<img
src="/images/logo/logo-systems.png"
alt="TeleNetSystems Logo"
className="h-9 w-auto lg:h-11"
width={180}
height={44}
/>
</Link>
<Navigation />
<div className="flex items-center gap-3">
<Button
href="/#kontakt"
variant="primary"
size="sm"
className="hidden lg:inline-flex"
>
Kontakt
</Button>
<button
onClick={() => setIsMobileMenuOpen(true)}
className="rounded-lg p-2 text-neutral-700 hover:bg-neutral-100 lg:hidden"
aria-label="Menü öffnen"
aria-expanded={isMobileMenuOpen}
aria-controls="mobile-menu"
>
<Menu className="h-6 w-6" />
</button>
</div>
</div>
<MobileMenu isOpen={isMobileMenuOpen} onClose={closeMobileMenu} />
</header>
);
}
+121
View File
@@ -0,0 +1,121 @@
"use client";
import { useEffect, useRef } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { motion, AnimatePresence } from "framer-motion";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";
import { NAV_LINKS, CONTACT } from "@/lib/constants";
import Button from "@/components/ui/Button";
interface MobileMenuProps {
isOpen: boolean;
onClose: () => void;
}
export default function MobileMenu({ isOpen, onClose }: MobileMenuProps) {
const pathname = usePathname();
const closeButtonRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
closeButtonRef.current?.focus();
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [isOpen]);
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
if (isOpen) {
window.addEventListener("keydown", handleEscape);
}
return () => window.removeEventListener("keydown", handleEscape);
}, [isOpen, onClose]);
useEffect(() => {
onClose();
}, [pathname, onClose]);
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-40 bg-black/50"
onClick={onClose}
aria-hidden="true"
/>
<motion.div
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{ type: "tween", duration: 0.3 }}
className="fixed inset-y-0 right-0 z-50 w-full max-w-sm bg-white shadow-xl"
role="dialog"
aria-modal="true"
aria-label="Menü"
>
<div className="flex h-full flex-col">
<div className="flex items-center justify-between border-b border-neutral-200 p-4">
<span className="text-lg font-semibold text-neutral-900">Menü</span>
<button
ref={closeButtonRef}
onClick={onClose}
className="rounded-lg p-2 text-neutral-500 hover:bg-neutral-100 hover:text-neutral-700"
aria-label="Menü schließen"
>
<X className="h-6 w-6" />
</button>
</div>
<nav aria-label="Mobile Navigation" className="flex-1 overflow-y-auto p-4">
<ul className="space-y-1">
{NAV_LINKS.map((link) => {
const isActive = pathname.startsWith(link.href);
return (
<li key={link.href}>
<Link
href={link.href}
className={cn(
"block rounded-lg px-4 py-3 text-base font-medium transition-colors",
isActive
? "bg-primary-50 text-primary-700"
: "text-neutral-700 hover:bg-neutral-100"
)}
aria-current={isActive ? "page" : undefined}
>
{link.label}
</Link>
</li>
);
})}
</ul>
</nav>
<div className="border-t border-neutral-200 p-4 space-y-3">
<Button href="/#kontakt" variant="primary" size="md" className="w-full">
Kontakt aufnehmen
</Button>
<a
href={`tel:${CONTACT.phone.replace(/\s/g, "")}`}
className="block text-center text-sm text-neutral-600"
>
{CONTACT.phone}
</a>
</div>
</div>
</motion.div>
</>
)}
</AnimatePresence>
);
}
+36
View File
@@ -0,0 +1,36 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { cn } from "@/lib/utils";
import { NAV_LINKS } from "@/lib/constants";
export default function Navigation() {
const pathname = usePathname();
return (
<nav aria-label="Hauptnavigation" className="hidden lg:block">
<ul className="flex items-center gap-1">
{NAV_LINKS.map((link) => {
const isActive = pathname.startsWith(link.href);
return (
<li key={link.href}>
<Link
href={link.href}
className={cn(
"rounded-lg px-4 py-2 text-sm font-medium transition-colors duration-200",
isActive
? "bg-primary-50 text-primary-700"
: "text-neutral-700 hover:bg-neutral-100 hover:text-neutral-900"
)}
aria-current={isActive ? "page" : undefined}
>
{link.label}
</Link>
</li>
);
})}
</ul>
</nav>
);
}
+137
View File
@@ -0,0 +1,137 @@
"use client";
import { Phone, Mail, MapPin, Clock } from "lucide-react";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import Button from "@/components/ui/Button";
import { CONTACT } from "@/lib/constants";
export default function ContactSection() {
return (
<section id="kontakt" className="bg-neutral-50 py-20 lg:py-24" aria-labelledby="contact-heading">
<Container>
<SectionHeading
title="Sprechen Sie mit uns"
subtitle="Rufen Sie an, schreiben Sie uns — oder kommen Sie einfach in Reutte vorbei."
/>
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2" id="contact-heading">
{/* Contact Info */}
<div>
<h3 className="text-lg font-semibold text-neutral-900">Direkt erreichen</h3>
<p className="mt-2 text-neutral-600 leading-relaxed">
Kein Callcenter, keine automatische Weiterleitung. Bei uns landen Sie direkt bei den richtigen Leuten.
</p>
<div className="mt-8 space-y-6">
<a
href={`tel:${CONTACT.phone.replace(/\s/g, "")}`}
className="flex items-center gap-4 group"
>
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary-100 transition-colors group-hover:bg-primary-200">
<Phone className="h-5 w-5 text-primary-700" aria-hidden="true" />
</div>
<div>
<p className="text-sm text-neutral-500">Telefon</p>
<p className="font-semibold text-neutral-900">{CONTACT.phone}</p>
</div>
</a>
<a
href={`mailto:${CONTACT.email}`}
className="flex items-center gap-4 group"
>
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary-100 transition-colors group-hover:bg-primary-200">
<Mail className="h-5 w-5 text-primary-700" aria-hidden="true" />
</div>
<div>
<p className="text-sm text-neutral-500">E-Mail</p>
<p className="font-semibold text-neutral-900">{CONTACT.email}</p>
</div>
</a>
<div className="flex items-center gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary-100">
<MapPin className="h-5 w-5 text-primary-700" aria-hidden="true" />
</div>
<div>
<p className="text-sm text-neutral-500">Adresse</p>
<address className="not-italic font-semibold text-neutral-900">
{CONTACT.address[1]}, {CONTACT.address[2]}
</address>
</div>
</div>
<div className="flex items-center gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary-100">
<Clock className="h-5 w-5 text-primary-700" aria-hidden="true" />
</div>
<div>
<p className="text-sm text-neutral-500">Öffnungszeiten</p>
<p className="font-semibold text-neutral-900">MoFr: 8:0017:00 Uhr</p>
</div>
</div>
</div>
</div>
{/* Contact Form */}
<div className="rounded-card bg-white p-8 shadow-sm border border-neutral-200">
<h3 className="text-lg font-semibold text-neutral-900">Nachricht senden</h3>
<p className="mt-1 text-sm text-neutral-500">Wir melden uns zeitnah bei Ihnen.</p>
<form className="mt-6 space-y-4" action={`mailto:${CONTACT.email}`} method="POST" encType="text/plain">
<div>
<label htmlFor="name" className="block text-sm font-medium text-neutral-700">
Name
</label>
<input
type="text"
id="name"
name="name"
required
className="mt-1 block w-full rounded-lg border border-neutral-300 px-4 py-3 text-neutral-900 placeholder:text-neutral-400 focus:border-primary-500 focus:ring-1 focus:ring-primary-500"
placeholder="Ihr Name"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-neutral-700">
E-Mail
</label>
<input
type="email"
id="email"
name="email"
required
className="mt-1 block w-full rounded-lg border border-neutral-300 px-4 py-3 text-neutral-900 placeholder:text-neutral-400 focus:border-primary-500 focus:ring-1 focus:ring-primary-500"
placeholder="ihre@email.at"
/>
</div>
<div>
<label htmlFor="phone-field" className="block text-sm font-medium text-neutral-700">
Telefon <span className="text-neutral-400">(optional)</span>
</label>
<input
type="tel"
id="phone-field"
name="phone"
className="mt-1 block w-full rounded-lg border border-neutral-300 px-4 py-3 text-neutral-900 placeholder:text-neutral-400 focus:border-primary-500 focus:ring-1 focus:ring-primary-500"
placeholder="+43 ..."
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-neutral-700">
Nachricht
</label>
<textarea
id="message"
name="message"
rows={4}
required
className="mt-1 block w-full rounded-lg border border-neutral-300 px-4 py-3 text-neutral-900 placeholder:text-neutral-400 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 resize-none"
placeholder="Wie können wir Ihnen helfen?"
/>
</div>
<Button type="submit" variant="primary" size="md" className="w-full">
Nachricht absenden
</Button>
</form>
</div>
</div>
</Container>
</section>
);
}
@@ -0,0 +1,104 @@
import { User, Building2 } from "lucide-react";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import Button from "@/components/ui/Button";
export default function CustomerSegments() {
return (
<section className="bg-neutral-50 py-20 lg:py-24" aria-labelledby="segments-heading">
<Container>
<SectionHeading
title="Für wen wir da sind"
subtitle="Ob Privathaushalt oder Unternehmen — wir kennen die Anforderungen und finden die passende Lösung."
/>
<div className="grid grid-cols-1 gap-8 lg:grid-cols-2" id="segments-heading">
{/* Privatkunden */}
<div className="overflow-hidden rounded-card bg-white shadow-sm border border-neutral-200">
<div className="aspect-video overflow-hidden">
<img
src="/images/gallery/privatkunden.jpg"
alt="Privatkunden von TeleNetSystems"
className="h-full w-full object-cover"
loading="lazy"
width={600}
height={338}
/>
</div>
<div className="p-8">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100">
<User className="h-5 w-5 text-blue-700" aria-hidden="true" />
</div>
<h3 className="text-xl font-semibold text-neutral-900">Privatkunden</h3>
</div>
<ul className="mt-5 space-y-2 text-neutral-600">
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
Stabile Internet-Verbindung für den Alltag
</li>
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
TV-Pakete, die zu Ihrem Sehverhalten passen
</li>
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
Telefonie ohne Kleingedrucktes
</li>
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
Persönlicher Ansprechpartner in Reutte
</li>
</ul>
<Button href="/internet" variant="outline" size="sm" className="mt-6">
Privatangebote ansehen
</Button>
</div>
</div>
{/* Geschäftskunden */}
<div className="overflow-hidden rounded-card bg-white shadow-sm border border-neutral-200">
<div className="aspect-video overflow-hidden">
<img
src="/images/gallery/firmenkunden.jpg"
alt="Geschäftskunden von TeleNetSystems"
className="h-full w-full object-cover"
loading="lazy"
width={600}
height={338}
/>
</div>
<div className="p-8">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-amber-100">
<Building2 className="h-5 w-5 text-amber-700" aria-hidden="true" />
</div>
<h3 className="text-xl font-semibold text-neutral-900">Geschäftskunden</h3>
</div>
<ul className="mt-5 space-y-2 text-neutral-600">
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
Individuelle Internet- und IT-Lösungen
</li>
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
TV für Hotels, Gastro und Büros
</li>
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
Schneller technischer Support vor Ort
</li>
<li className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary-500" aria-hidden="true" />
Beratung, die Ihre Abläufe versteht
</li>
</ul>
<Button href="/#kontakt" variant="outline" size="sm" className="mt-6">
Beratung anfragen
</Button>
</div>
</div>
</div>
</Container>
</section>
);
}
+68
View File
@@ -0,0 +1,68 @@
"use client";
import { motion } from "framer-motion";
import { ChevronDown } from "lucide-react";
import Button from "@/components/ui/Button";
export default function HeroSection() {
return (
<section
className="relative flex min-h-[90vh] items-center overflow-hidden bg-neutral-900"
aria-label="Willkommen bei TeleNetSystems"
>
{/* Background Image */}
<div className="absolute inset-0">
<img
src="/images/gallery/firma1.jpg"
alt=""
className="h-full w-full object-cover opacity-70"
width={1920}
height={1080}
aria-hidden="true"
/>
<div className="absolute inset-0 bg-gradient-to-r from-neutral-900/80 via-neutral-900/50 to-transparent" />
<div className="absolute inset-0 bg-gradient-to-t from-neutral-900/40 to-transparent" />
</div>
{/* Content */}
<div className="relative mx-auto max-w-6xl px-4 py-24 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="max-w-2xl"
>
<p className="mb-4 inline-block rounded-full bg-primary-500/20 px-4 py-1.5 text-sm font-semibold uppercase tracking-widest !text-white">
Aus Reutte. Für die Region.
</p>
<h1 className="text-4xl font-bold leading-tight tracking-tight !text-white sm:text-5xl lg:text-hero">
Technik, die verbindet.{" "}
<br className="hidden sm:block" />
<span className="text-primary-400">Service, der da ist.</span>
</h1>
<p className="mt-6 max-w-xl text-lg leading-relaxed !text-white/80 sm:text-xl">
Internet, Fernsehen und Telefonie direkt aus Reutte mit persönlicher Betreuung, die
Sie nicht bei einer Hotline suchen müssen.
</p>
<div className="mt-10 flex flex-col gap-4 sm:flex-row">
<Button href="/#kontakt" variant="primary" size="lg">
Jetzt beraten lassen
</Button>
<Button href="/leistungen" variant="outline" size="lg" className="border-white/30 text-white hover:bg-white/10 hover:text-white">
Unsere Leistungen
</Button>
</div>
</motion.div>
</div>
{/* Scroll Indicator */}
<motion.div
className="absolute bottom-8 left-1/2 -translate-x-1/2"
animate={{ y: [0, 8, 0] }}
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
>
<ChevronDown className="h-6 w-6 text-white/50" aria-hidden="true" />
</motion.div>
</section>
);
}
+45
View File
@@ -0,0 +1,45 @@
import Container from "@/components/ui/Container";
import { cn } from "@/lib/utils";
interface PageHeroProps {
title: string;
description?: string;
backgroundImage?: string;
}
export default function PageHero({ title, description, backgroundImage }: PageHeroProps) {
return (
<section
className={cn(
"relative flex items-end overflow-hidden py-20 sm:py-28 lg:py-32",
backgroundImage ? "bg-neutral-900" : "bg-gradient-to-br from-neutral-900 to-primary-950"
)}
>
{backgroundImage && (
<div className="absolute inset-0">
<img
src={backgroundImage}
alt=""
className="h-full w-full object-cover opacity-70"
width={1920}
height={600}
aria-hidden="true"
/>
<div className="absolute inset-0 bg-gradient-to-t from-neutral-900/70 via-neutral-900/30 to-transparent" />
<div className="absolute inset-0 bg-gradient-to-r from-neutral-900/60 to-transparent" />
</div>
)}
<Container className="relative">
<div className="mb-4 h-1 w-16 rounded bg-primary-500" />
<h1 className="text-3xl font-bold tracking-tight !text-white drop-shadow-lg sm:text-4xl lg:text-display">
{title}
</h1>
{description && (
<p className="mt-4 max-w-2xl text-lg !text-white/80 leading-relaxed drop-shadow-md">
{description}
</p>
)}
</Container>
</section>
);
}
@@ -0,0 +1,40 @@
import { Wifi, Tv, Phone } from "lucide-react";
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import Card from "@/components/ui/Card";
import { SERVICES } from "@/lib/constants";
const icons = [Wifi, Tv, Phone];
export default function ServiceOverview() {
return (
<section className="py-20 lg:py-24" aria-labelledby="services-heading">
<Container>
<SectionHeading
title="Was wir für Sie tun"
subtitle="Drei Dienste, ein Ansprechpartner. Alles aus einer Hand, direkt vor Ort in Reutte."
/>
<div className="grid grid-cols-1 gap-8 md:grid-cols-3" id="services-heading">
{SERVICES.map((service, index) => {
const Icon = icons[index];
return (
<Card
key={service.title}
title={service.title}
description={service.description}
imageSrc={service.image}
imageAlt={service.imageAlt}
href={service.href}
>
<div className="mt-4 flex items-center gap-2 text-primary-600 font-medium text-sm">
<Icon className="h-4 w-4" aria-hidden="true" />
<span>Mehr erfahren</span>
</div>
</Card>
);
})}
</div>
</Container>
</section>
);
}
+54
View File
@@ -0,0 +1,54 @@
import { Check } from "lucide-react";
import Badge from "@/components/ui/Badge";
import Button from "@/components/ui/Button";
import { cn } from "@/lib/utils";
import type { TariffPlan } from "@/types";
interface TariffTableProps {
plans: TariffPlan[];
contactHref?: string;
}
export default function TariffTable({ plans, contactHref = "/#kontakt" }: TariffTableProps) {
return (
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{plans.map((plan) => (
<div
key={plan.name}
className={cn(
"relative flex flex-col rounded-card border bg-white p-6 shadow-sm transition-shadow hover:shadow-md",
plan.recommended
? "border-primary-500 ring-1 ring-primary-500"
: "border-neutral-200"
)}
>
{plan.recommended && (
<div className="absolute -top-3 left-6">
<Badge variant="popular">Beliebt</Badge>
</div>
)}
<div className="mb-4">
<h3 className="text-lg font-semibold text-neutral-900">{plan.name}</h3>
<p className="mt-2 text-2xl font-bold text-neutral-900">{plan.price}</p>
</div>
<ul className="flex-1 space-y-3">
{plan.features.map((feature) => (
<li key={feature} className="flex items-start gap-2 text-sm text-neutral-600">
<Check className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary-600" aria-hidden="true" />
{feature}
</li>
))}
</ul>
<Button
href={contactHref}
variant={plan.recommended ? "primary" : "outline"}
size="sm"
className="mt-6 w-full"
>
Jetzt anfragen
</Button>
</div>
))}
</div>
);
}
+38
View File
@@ -0,0 +1,38 @@
import Container from "@/components/ui/Container";
import SectionHeading from "@/components/ui/SectionHeading";
import { TEAM_MEMBERS } from "@/lib/constants";
export default function TeamGrid() {
return (
<section className="py-20 lg:py-24" aria-labelledby="team-heading">
<Container>
<SectionHeading
title="Unser Team"
subtitle="Die Menschen hinter TeleNetSystems. Persönlich erreichbar, fachlich versiert."
/>
<div className="grid grid-cols-2 gap-6 sm:grid-cols-3 lg:grid-cols-4" id="team-heading">
{TEAM_MEMBERS.map((member) => (
<div key={member.name} className="text-center">
<div className="mx-auto aspect-square w-full max-w-[200px] overflow-hidden rounded-2xl bg-neutral-100">
<img
src={member.image}
alt={`${member.name}${member.role ? `, ${member.role}` : ""}`}
className="h-full w-full object-cover"
loading="lazy"
width={200}
height={200}
/>
</div>
<h3 className="mt-4 text-sm font-semibold text-neutral-900">
{member.name}
</h3>
{member.role && (
<p className="mt-1 text-xs text-neutral-500">{member.role}</p>
)}
</div>
))}
</div>
</Container>
</section>
);
}
+32
View File
@@ -0,0 +1,32 @@
import Container from "@/components/ui/Container";
const stats = [
{ value: "20+", label: "Jahre Erfahrung" },
{ value: "1.000+", label: "Kunden in der Region" },
{ value: "Reutte", label: "Unser Standort" },
{ value: "24/7", label: "Netzüberwachung" },
];
export default function TrustSection() {
return (
<section className="bg-primary-950 py-16 lg:py-20" aria-labelledby="trust-heading">
<Container>
<h2 id="trust-heading" className="sr-only">
TeleNetSystems in Zahlen
</h2>
<div className="grid grid-cols-2 gap-8 lg:grid-cols-4">
{stats.map((stat) => (
<div key={stat.label} className="text-center">
<p className="text-3xl font-bold text-primary-400 sm:text-4xl">
{stat.value}
</p>
<p className="mt-2 text-sm text-neutral-300">
{stat.label}
</p>
</div>
))}
</div>
</Container>
</section>
);
}
@@ -0,0 +1,55 @@
import { Shield, MapPin, Headphones, Award } from "lucide-react";
import Container from "@/components/ui/Container";
const values = [
{
icon: Shield,
title: "Zuverlässig",
description: "Stabile Netze, die halten, was sie versprechen. Wir setzen auf Technik, der Sie vertrauen können.",
},
{
icon: MapPin,
title: "Regional",
description: "Kein Callcenter, sondern echte Menschen in Reutte. Bei Fragen sind wir vor Ort.",
},
{
icon: Headphones,
title: "Persönlich",
description: "Sie erreichen uns direkt — ohne Warteschleifen und ohne Weiterleitung.",
},
{
icon: Award,
title: "Kompetent",
description: "Jahrelange Erfahrung im Außerfern. Wir kennen die Region und ihre Anforderungen.",
},
];
export default function ValueProposition() {
return (
<section className="py-20 lg:py-24" aria-labelledby="values-heading">
<Container>
<h2 id="values-heading" className="sr-only">
Unsere Werte
</h2>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
{values.map((value) => {
const Icon = value.icon;
return (
<div key={value.title} className="text-center">
<div className="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-primary-100">
<Icon className="h-7 w-7 text-primary-700" aria-hidden="true" />
</div>
<h3 className="mt-4 text-lg font-semibold text-neutral-900">
{value.title}
</h3>
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
{value.description}
</p>
</div>
);
})}
</div>
</Container>
</section>
);
}
+26
View File
@@ -0,0 +1,26 @@
import { cn } from "@/lib/utils";
interface BadgeProps {
children: React.ReactNode;
variant?: "default" | "popular" | "privat" | "business";
}
const variants = {
default: "bg-neutral-100 text-neutral-700",
popular: "bg-primary-100 text-primary-800",
privat: "bg-blue-100 text-blue-800",
business: "bg-amber-100 text-amber-800",
};
export default function Badge({ children, variant = "default" }: BadgeProps) {
return (
<span
className={cn(
"inline-block rounded-full px-3 py-1 text-xs font-semibold uppercase tracking-wide",
variants[variant]
)}
>
{children}
</span>
);
}
+56
View File
@@ -0,0 +1,56 @@
import Link from "next/link";
import { cn } from "@/lib/utils";
interface ButtonProps {
children: React.ReactNode;
variant?: "primary" | "secondary" | "outline" | "ghost";
size?: "sm" | "md" | "lg";
href?: string;
className?: string;
type?: "button" | "submit";
onClick?: () => void;
}
const variants = {
primary: "bg-primary-600 text-white hover:bg-primary-700 focus-visible:ring-primary-500",
secondary: "bg-neutral-800 text-white hover:bg-neutral-900",
outline: "border-2 border-primary-600 text-primary-700 hover:bg-primary-50",
ghost: "text-neutral-700 hover:bg-neutral-100",
};
const sizes = {
sm: "px-4 py-2 text-sm",
md: "px-6 py-3 text-base",
lg: "px-8 py-4 text-lg",
};
export default function Button({
children,
variant = "primary",
size = "md",
href,
className,
type = "button",
onClick,
}: ButtonProps) {
const classes = cn(
"inline-flex items-center justify-center gap-2 rounded-lg font-medium transition-colors duration-200",
variants[variant],
sizes[size],
className
);
if (href) {
return (
<Link href={href} className={classes}>
{children}
</Link>
);
}
return (
<button type={type} className={classes} onClick={onClick}>
{children}
</button>
);
}
+59
View File
@@ -0,0 +1,59 @@
import Link from "next/link";
import { cn } from "@/lib/utils";
interface CardProps {
title: string;
description: string;
imageSrc?: string;
imageAlt?: string;
href?: string;
children?: React.ReactNode;
className?: string;
}
export default function Card({
title,
description,
imageSrc,
imageAlt,
href,
children,
className,
}: CardProps) {
const content = (
<>
{imageSrc && (
<div className="aspect-video overflow-hidden rounded-t-card">
<img
src={imageSrc}
alt={imageAlt || title}
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
loading="lazy"
width={600}
height={338}
/>
</div>
)}
<div className="p-6">
<h3 className="text-xl font-semibold text-neutral-900">{title}</h3>
<p className="mt-2 text-neutral-600 leading-relaxed">{description}</p>
{children}
</div>
</>
);
const cardClasses = cn(
"group overflow-hidden rounded-card bg-white shadow-sm border border-neutral-200 transition-shadow duration-300 hover:shadow-md",
className
);
if (href) {
return (
<Link href={href} className={cn(cardClasses, "block")}>
{content}
</Link>
);
}
return <div className={cardClasses}>{content}</div>;
}
+15
View File
@@ -0,0 +1,15 @@
import { cn } from "@/lib/utils";
interface ContainerProps {
children: React.ReactNode;
className?: string;
as?: React.ElementType;
}
export default function Container({ children, className, as: Component = "div" }: ContainerProps) {
return (
<Component className={cn("mx-auto max-w-6xl px-4 sm:px-6 lg:px-8", className)}>
{children}
</Component>
);
}
+43
View File
@@ -0,0 +1,43 @@
import { cn } from "@/lib/utils";
interface SectionHeadingProps {
title: string;
subtitle?: string;
align?: "left" | "center";
as?: "h1" | "h2" | "h3";
className?: string;
}
export default function SectionHeading({
title,
subtitle,
align = "center",
as: Tag = "h2",
className,
}: SectionHeadingProps) {
return (
<div
className={cn(
"mb-12",
align === "center" && "text-center",
className
)}
>
<Tag className="text-display font-bold tracking-tight text-neutral-900">
{title}
</Tag>
{subtitle && (
<p className="mt-4 max-w-2xl text-body-lg text-neutral-600 leading-relaxed mx-auto">
{subtitle}
</p>
)}
<div
className={cn(
"mt-4 h-1 w-12 rounded-full bg-primary-500",
align === "center" && "mx-auto"
)}
aria-hidden="true"
/>
</div>
);
}
+150
View File
@@ -0,0 +1,150 @@
import type { NavLink, TeamMember, TariffPlan, ContactInfo, ServiceCard } from "@/types";
export const COMPANY_NAME = "TeleNetSystems";
export const COMPANY_TAGLINE = "Internet, TV & Telefonie in Reutte";
export const CONTACT: ContactInfo = {
phone: "+43 5672 21400",
email: "info@telenetsystems.at",
address: [
"TeleNetSystems",
"Untermarkt 16",
"6600 Reutte",
"Tirol, Österreich",
],
};
export const NAV_LINKS: NavLink[] = [
{ href: "/fernsehen", label: "Fernsehen" },
{ href: "/internet", label: "Internet" },
{ href: "/telefonie", label: "Telefonie" },
{ href: "/leistungen", label: "Leistungen" },
{ href: "/ueber-uns", label: "Über uns" },
];
export const FOOTER_LINKS = {
services: [
{ href: "/fernsehen", label: "Fernsehen" },
{ href: "/internet", label: "Internet" },
{ href: "/telefonie", label: "Telefonie" },
],
company: [
{ href: "/leistungen", label: "Leistungen" },
{ href: "/ueber-uns", label: "Über uns" },
],
legal: [
{ href: "/impressum", label: "Impressum" },
{ href: "/datenschutz", label: "Datenschutz" },
],
};
export const TEAM_MEMBERS: TeamMember[] = [
{ name: "Wolfgang Schwaiger", role: "Geschäftsführer", image: "/images/team/wolfgang-schwaiger.jpg" },
{ name: "Martin Müller", role: "Technik", image: "/images/team/martin-mueller.jpg" },
{ name: "David Müller", role: "Technik", image: "/images/team/david-mueller.jpg" },
{ name: "Jürgen Gräßle", role: "Kundenbetreuung", image: "/images/team/juergen-graessle.jpg" },
{ name: "Julia Besler", role: "Verwaltung", image: "/images/team/julia-besler.jpg" },
{ name: "Furkan Demirel", role: "Technik", image: "/images/team/furkan-demirel.jpg" },
{ name: "Lukas Schennach", role: "Technik", image: "/images/team/lukas-schennach.jpg" },
{ name: "Mario Kien", role: "Technik", image: "/images/team/mario-kien.jpg" },
{ name: "Franz", role: "Technik", image: "/images/team/franz.jpg" },
{ name: "Lorena", role: "Verwaltung", image: "/images/team/lorena.jpg" },
{ name: "Timo", role: "Technik", image: "/images/team/timo.jpg" },
];
export const SERVICES: ServiceCard[] = [
{
title: "Internet",
description: "Schnelles und stabiles Internet für Reutte und Umgebung. Kabel- und Glasfaser-Tarife, die zu Ihrem Bedarf passen.",
href: "/internet",
image: "/images/gallery/internet.jpg",
imageAlt: "Glasfaserkabel für schnelles Internet in Tirol",
},
{
title: "Fernsehen",
description: "TV-Pakete für jeden Geschmack. Von der Grundversorgung bis zum individuellen Business-TV.",
href: "/fernsehen",
image: "/images/gallery/tv-privat.jpg",
imageAlt: "Fernsehempfang über TeleNetSystems",
},
{
title: "Telefonie",
description: "Klar und günstig telefonieren. Einfache Tarife ohne versteckte Kosten.",
href: "/telefonie",
image: "/images/gallery/telefon.jpg",
imageAlt: "Telefonie-Dienste von TeleNetSystems",
},
];
export const INTERNET_TARIFE: TariffPlan[] = [
{
name: "Internet 30",
price: "ab € 29,90/Monat",
category: "kabel",
features: ["Download bis 30 Mbit/s", "Upload bis 5 Mbit/s", "Keine Drosselung", "Inkl. WLAN-Router"],
},
{
name: "Internet 60",
price: "ab € 39,90/Monat",
category: "kabel",
recommended: true,
features: ["Download bis 60 Mbit/s", "Upload bis 10 Mbit/s", "Keine Drosselung", "Inkl. WLAN-Router"],
},
{
name: "Internet 100",
price: "ab € 49,90/Monat",
category: "kabel",
features: ["Download bis 100 Mbit/s", "Upload bis 20 Mbit/s", "Keine Drosselung", "Inkl. WLAN-Router"],
},
{
name: "Glasfaser 200",
price: "ab € 59,90/Monat",
category: "glasfaser",
features: ["Download bis 200 Mbit/s", "Upload bis 50 Mbit/s", "Keine Drosselung", "Inkl. WLAN-Router"],
},
{
name: "Glasfaser 500",
price: "ab € 79,90/Monat",
category: "glasfaser",
recommended: true,
features: ["Download bis 500 Mbit/s", "Upload bis 100 Mbit/s", "Keine Drosselung", "Inkl. WLAN-Router"],
},
];
export const TV_PRIVAT_PAKETE: TariffPlan[] = [
{
name: "TV Basis",
price: "ab € 9,90/Monat",
features: ["Über 40 Sender", "ORF, ZDF, ARD und mehr", "HD-Qualität", "Digitaler Empfang"],
},
{
name: "TV Komfort",
price: "ab € 19,90/Monat",
recommended: true,
features: ["Über 80 Sender", "Alle Basis-Sender", "Zusätzliche Spartensender", "HD und Full-HD"],
},
{
name: "TV Premium",
price: "ab € 29,90/Monat",
features: ["Über 120 Sender", "Alle Komfort-Sender", "Internationale Sender", "HD, Full-HD und 4K"],
},
];
export const TELEFONIE_TARIFE: TariffPlan[] = [
{
name: "Telefon Basis",
price: "ab € 4,90/Monat",
features: ["Festnetzanschluss", "Minutengenaue Abrechnung", "Österreichweit"],
},
{
name: "Telefon Flat",
price: "ab € 9,90/Monat",
recommended: true,
features: ["Festnetzanschluss", "Flatrate Österreich Festnetz", "Günstige Mobilfunktarife"],
},
{
name: "Telefon Flat Plus",
price: "ab € 14,90/Monat",
features: ["Festnetzanschluss", "Flatrate Österreich & Deutschland", "Günstige Mobilfunktarife", "Inkl. Voicemail"],
},
];
+5
View File
@@ -0,0 +1,5 @@
import { clsx, type ClassValue } from "clsx";
export function cn(...inputs: ClassValue[]) {
return clsx(inputs);
}
+39
View File
@@ -0,0 +1,39 @@
export interface NavLink {
href: string;
label: string;
}
export interface TeamMember {
name: string;
role?: string;
image: string;
}
export interface TariffPlan {
name: string;
price: string;
features: string[];
recommended?: boolean;
category?: string;
description?: string;
}
export interface ServiceCard {
title: string;
description: string;
href: string;
image: string;
imageAlt: string;
}
export interface ValueItem {
title: string;
description: string;
icon: string;
}
export interface ContactInfo {
phone: string;
email: string;
address: string[];
}
-54
View File
@@ -1,54 +0,0 @@
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./lib/**/*.{js,ts,jsx,tsx,mdx}"
],
theme: {
extend: {
colors: {
alpine: {
50: "#f3f8fc",
100: "#dfeef8",
200: "#badbf0",
300: "#87bedd",
400: "#569fc7",
500: "#3a81ab",
600: "#2b6488",
700: "#204a66",
800: "#163246",
900: "#0c1d2a"
},
copper: {
50: "#fdf8f2",
100: "#f9ebdc",
200: "#f2d0ae",
300: "#e8b07a",
400: "#dd904f",
500: "#c9722d",
600: "#a55a23",
700: "#7f451b",
800: "#5a3113",
900: "#381e0c"
},
moss: {
500: "#1f6b56",
600: "#155141"
}
},
boxShadow: {
soft: "0 18px 36px rgba(8, 26, 41, 0.12)",
card: "0 10px 24px rgba(12, 32, 50, 0.10)"
},
backgroundImage: {
"alpine-grid":
"linear-gradient(rgba(16, 58, 90, 0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(16, 58, 90, 0.06) 1px, transparent 1px)"
}
}
},
plugins: []
};
export default config;
+5 -4
View File
@@ -6,7 +6,7 @@
"dom.iterable", "dom.iterable",
"esnext" "esnext"
], ],
"allowJs": false, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"noEmit": true, "noEmit": true,
@@ -15,7 +15,7 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "react-jsx",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
@@ -24,7 +24,7 @@
], ],
"paths": { "paths": {
"@/*": [ "@/*": [
"./*" "./src/*"
] ]
} }
}, },
@@ -33,7 +33,8 @@
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts", ".next/types/**/*.ts",
".next/dev/types/**/*.ts" ".next/dev/types/**/*.ts",
"**/*.mts"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules"