feat: implement landing page structure with legal pages, footer, CTA, and domain redirection proxy
This commit is contained in:
71
greenlns-landing/app/imprint/ImprintContent.tsx
Normal file
71
greenlns-landing/app/imprint/ImprintContent.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
const CONTENT = {
|
||||
de: {
|
||||
title: 'Impressum',
|
||||
companyLabel: 'Unternehmen',
|
||||
addressLabel: 'Adresse',
|
||||
representativeLabel: 'Vertretungsberechtigt',
|
||||
contactLabel: 'Kontakt',
|
||||
registryLabel: 'Register',
|
||||
vatLabel: 'USt-ID',
|
||||
},
|
||||
en: {
|
||||
title: 'Imprint',
|
||||
companyLabel: 'Company',
|
||||
addressLabel: 'Address',
|
||||
representativeLabel: 'Represented by',
|
||||
contactLabel: 'Contact',
|
||||
registryLabel: 'Registry',
|
||||
vatLabel: 'VAT ID',
|
||||
},
|
||||
es: {
|
||||
title: 'Aviso Legal',
|
||||
companyLabel: 'Empresa',
|
||||
addressLabel: 'Direccion',
|
||||
representativeLabel: 'Representante',
|
||||
contactLabel: 'Contacto',
|
||||
registryLabel: 'Registro',
|
||||
vatLabel: 'IVA',
|
||||
},
|
||||
}
|
||||
|
||||
export default function ImprintContent() {
|
||||
const { lang } = useLang()
|
||||
const c = CONTENT[lang]
|
||||
|
||||
return (
|
||||
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
|
||||
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
|
||||
<Link href="/">Home</Link> / <span>Legal</span> / <span>{c.title}</span>
|
||||
</p>
|
||||
<h1>{c.title}</h1>
|
||||
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
|
||||
<p>
|
||||
<strong>{c.companyLabel}:</strong> {siteConfig.company.legalName}
|
||||
</p>
|
||||
{siteConfig.company.addressLine1 ? (
|
||||
<p><strong>{c.addressLabel}:</strong> {siteConfig.company.addressLine1}</p>
|
||||
) : null}
|
||||
{siteConfig.company.addressLine2 ? <p>{siteConfig.company.addressLine2}</p> : null}
|
||||
<p>{siteConfig.company.country}</p>
|
||||
<p>
|
||||
<strong>{c.representativeLabel}:</strong> {siteConfig.company.representative}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
|
||||
</p>
|
||||
{siteConfig.company.registry ? (
|
||||
<p><strong>{c.registryLabel}:</strong> {siteConfig.company.registry}</p>
|
||||
) : null}
|
||||
{siteConfig.company.vatId ? (
|
||||
<p><strong>{c.vatLabel}:</strong> {siteConfig.company.vatId}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,67 +1,37 @@
|
||||
'use client'
|
||||
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import type { Metadata } from 'next'
|
||||
import Navbar from '@/components/Navbar'
|
||||
import Footer from '@/components/Footer'
|
||||
import ImprintContent from './ImprintContent'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
const CONTENT = {
|
||||
de: {
|
||||
title: 'Impressum',
|
||||
companyLabel: 'Unternehmen',
|
||||
addressLabel: 'Adresse',
|
||||
representativeLabel: 'Vertretungsberechtigt',
|
||||
contactLabel: 'Kontakt',
|
||||
registryLabel: 'Register',
|
||||
vatLabel: 'USt-ID',
|
||||
const title = 'Imprint'
|
||||
const description = 'Legal imprint and company contact information for GreenLens.'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: '/imprint',
|
||||
},
|
||||
en: {
|
||||
title: 'Imprint',
|
||||
companyLabel: 'Company',
|
||||
addressLabel: 'Address',
|
||||
representativeLabel: 'Represented by',
|
||||
contactLabel: 'Contact',
|
||||
registryLabel: 'Registry',
|
||||
vatLabel: 'VAT ID',
|
||||
openGraph: {
|
||||
title: `${title} | GreenLens`,
|
||||
description,
|
||||
url: `${siteConfig.domain}/imprint`,
|
||||
type: 'website',
|
||||
},
|
||||
es: {
|
||||
title: 'Aviso Legal',
|
||||
companyLabel: 'Empresa',
|
||||
addressLabel: 'Direccion',
|
||||
representativeLabel: 'Representante',
|
||||
contactLabel: 'Contacto',
|
||||
registryLabel: 'Registro',
|
||||
vatLabel: 'IVA',
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${title} | GreenLens`,
|
||||
description,
|
||||
},
|
||||
}
|
||||
|
||||
export default function ImprintPage() {
|
||||
const { lang } = useLang()
|
||||
const c = CONTENT[lang]
|
||||
|
||||
return (
|
||||
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
|
||||
<h1>{c.title}</h1>
|
||||
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
|
||||
<p>
|
||||
<strong>{c.companyLabel}:</strong> {siteConfig.company.legalName}
|
||||
</p>
|
||||
{siteConfig.company.addressLine1 ? (
|
||||
<p><strong>{c.addressLabel}:</strong> {siteConfig.company.addressLine1}</p>
|
||||
) : null}
|
||||
{siteConfig.company.addressLine2 ? <p>{siteConfig.company.addressLine2}</p> : null}
|
||||
<p>{siteConfig.company.country}</p>
|
||||
<p>
|
||||
<strong>{c.representativeLabel}:</strong> {siteConfig.company.representative}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
|
||||
</p>
|
||||
{siteConfig.company.registry ? (
|
||||
<p><strong>{c.registryLabel}:</strong> {siteConfig.company.registry}</p>
|
||||
) : null}
|
||||
{siteConfig.company.vatId ? (
|
||||
<p><strong>{c.vatLabel}:</strong> {siteConfig.company.vatId}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</main>
|
||||
<>
|
||||
<Navbar />
|
||||
<ImprintContent />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ export const metadata: Metadata = {
|
||||
type: 'website',
|
||||
url: siteConfig.domain,
|
||||
},
|
||||
alternates: {
|
||||
// Do not emit hreflang until each language has its own URL.
|
||||
languages: {},
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'GreenLens - Plant Identifier and Care Planner',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Metadata } from 'next'
|
||||
import Navbar from '@/components/Navbar'
|
||||
import Hero from '@/components/Hero'
|
||||
import Ticker from '@/components/Ticker'
|
||||
@@ -9,6 +10,12 @@ import FAQ from '@/components/FAQ'
|
||||
import CTA from '@/components/CTA'
|
||||
import Footer from '@/components/Footer'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
alternates: {
|
||||
canonical: '/',
|
||||
},
|
||||
}
|
||||
|
||||
const howToSchema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'HowTo',
|
||||
|
||||
84
greenlns-landing/app/privacy/PrivacyContent.tsx
Normal file
84
greenlns-landing/app/privacy/PrivacyContent.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
const CONTENT = {
|
||||
de: {
|
||||
title: 'Datenschutzerklaerung',
|
||||
intro:
|
||||
'Der Schutz personenbezogener Daten ist uns wichtig. Diese Seite beschreibt in knapper Form, welche Daten GreenLens verarbeiten kann und warum.',
|
||||
section1: '1. Verarbeitete Daten',
|
||||
text1:
|
||||
'Je nach Nutzung der App koennen Pflanzenfotos, Kontodaten, technische Geraeteinformationen, In-App-Kaufdaten sowie freiwillig uebermittelte Support-Anfragen verarbeitet werden.',
|
||||
section2: '2. Zweck der Verarbeitung',
|
||||
text2:
|
||||
'Diese Daten werden verwendet, um Pflanzenscans auszufuehren, deine Sammlung zu speichern, Erinnerungen bereitzustellen, Abos und Credits zu verwalten sowie Support-Anfragen zu beantworten.',
|
||||
section3: '3. Drittanbieter',
|
||||
text3:
|
||||
'GreenLens nutzt technische Dienstleister fuer App-Betrieb, Analyse, Authentifizierung und In-App-Kaeufe. Dazu koennen je nach Plattform Apple, RevenueCat, PostHog oder Hosting-Anbieter gehoeren.',
|
||||
section4: '4. Kontakt',
|
||||
text4: 'Bei Fragen zum Datenschutz oder zu deinen Datenrechten kannst du uns per E-Mail kontaktieren.',
|
||||
},
|
||||
en: {
|
||||
title: 'Privacy Policy',
|
||||
intro:
|
||||
'Protecting personal data matters to us. This page summarizes what GreenLens may process and why.',
|
||||
section1: '1. Data we may process',
|
||||
text1:
|
||||
'Depending on how you use the app, GreenLens may process plant photos, account details, technical device information, in-app purchase data, and support messages you send to us.',
|
||||
section2: '2. Why we process it',
|
||||
text2:
|
||||
'We use this information to run plant scans, store your collection, provide reminders, manage subscriptions and credits, and respond to support requests.',
|
||||
section3: '3. Third-party services',
|
||||
text3:
|
||||
'GreenLens uses service providers for app delivery, analytics, authentication, and in-app purchases. Depending on platform and setup, this can include Apple, RevenueCat, PostHog, or hosting providers.',
|
||||
section4: '4. Contact',
|
||||
text4: 'If you have privacy questions or want to exercise your data rights, contact us by email.',
|
||||
},
|
||||
es: {
|
||||
title: 'Politica de Privacidad',
|
||||
intro:
|
||||
'La proteccion de los datos personales es importante para nosotros. Esta pagina resume que datos puede procesar GreenLens y por que.',
|
||||
section1: '1. Datos que podemos procesar',
|
||||
text1:
|
||||
'Segun el uso de la app, GreenLens puede procesar fotos de plantas, datos de cuenta, informacion tecnica del dispositivo, datos de compras dentro de la app y mensajes de soporte.',
|
||||
section2: '2. Para que los usamos',
|
||||
text2:
|
||||
'Usamos estos datos para ejecutar escaneos, guardar tu coleccion, ofrecer recordatorios, gestionar suscripciones y creditos, y responder solicitudes de soporte.',
|
||||
section3: '3. Servicios de terceros',
|
||||
text3:
|
||||
'GreenLens utiliza proveedores para la operacion de la app, analitica, autenticacion y compras integradas. Segun la plataforma, esto puede incluir Apple, RevenueCat, PostHog o proveedores de hosting.',
|
||||
section4: '4. Contacto',
|
||||
text4: 'Si tienes preguntas sobre privacidad o tus derechos de datos, contactanos por correo electronico.',
|
||||
},
|
||||
}
|
||||
|
||||
export default function PrivacyContent() {
|
||||
const { lang } = useLang()
|
||||
const c = CONTENT[lang]
|
||||
|
||||
return (
|
||||
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
|
||||
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
|
||||
<Link href="/">Home</Link> / <span>Legal</span> / <span>{c.title}</span>
|
||||
</p>
|
||||
<h1>{c.title}</h1>
|
||||
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
|
||||
<p>{c.intro}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section1}</h2>
|
||||
<p>{c.text1}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section2}</h2>
|
||||
<p>{c.text2}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section3}</h2>
|
||||
<p>{c.text3}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section4}</h2>
|
||||
<p>{c.text4}</p>
|
||||
<p style={{ marginTop: '0.75rem' }}>
|
||||
<a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,80 +1,37 @@
|
||||
'use client'
|
||||
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import type { Metadata } from 'next'
|
||||
import Navbar from '@/components/Navbar'
|
||||
import Footer from '@/components/Footer'
|
||||
import PrivacyContent from './PrivacyContent'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
const CONTENT = {
|
||||
de: {
|
||||
title: 'Datenschutzerklaerung',
|
||||
intro:
|
||||
'Der Schutz personenbezogener Daten ist uns wichtig. Diese Seite beschreibt in knapper Form, welche Daten GreenLens verarbeiten kann und warum.',
|
||||
section1: '1. Verarbeitete Daten',
|
||||
text1:
|
||||
'Je nach Nutzung der App koennen Pflanzenfotos, Kontodaten, technische Geraeteinformationen, In-App-Kaufdaten sowie freiwillig uebermittelte Support-Anfragen verarbeitet werden.',
|
||||
section2: '2. Zweck der Verarbeitung',
|
||||
text2:
|
||||
'Diese Daten werden verwendet, um Pflanzenscans auszufuehren, deine Sammlung zu speichern, Erinnerungen bereitzustellen, Abos und Credits zu verwalten sowie Support-Anfragen zu beantworten.',
|
||||
section3: '3. Drittanbieter',
|
||||
text3:
|
||||
'GreenLens nutzt technische Dienstleister fuer App-Betrieb, Analyse, Authentifizierung und In-App-Kaeufe. Dazu koennen je nach Plattform Apple, RevenueCat, PostHog oder Hosting-Anbieter gehoeren.',
|
||||
section4: '4. Kontakt',
|
||||
text4: 'Bei Fragen zum Datenschutz oder zu deinen Datenrechten kannst du uns per E-Mail kontaktieren.',
|
||||
const title = 'Privacy Policy'
|
||||
const description = 'Learn what personal data GreenLens processes, why it is used, and how to contact us about privacy.'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: '/privacy',
|
||||
},
|
||||
en: {
|
||||
title: 'Privacy Policy',
|
||||
intro:
|
||||
'Protecting personal data matters to us. This page summarizes what GreenLens may process and why.',
|
||||
section1: '1. Data we may process',
|
||||
text1:
|
||||
'Depending on how you use the app, GreenLens may process plant photos, account details, technical device information, in-app purchase data, and support messages you send to us.',
|
||||
section2: '2. Why we process it',
|
||||
text2:
|
||||
'We use this information to run plant scans, store your collection, provide reminders, manage subscriptions and credits, and respond to support requests.',
|
||||
section3: '3. Third-party services',
|
||||
text3:
|
||||
'GreenLens uses service providers for app delivery, analytics, authentication, and in-app purchases. Depending on platform and setup, this can include Apple, RevenueCat, PostHog, or hosting providers.',
|
||||
section4: '4. Contact',
|
||||
text4: 'If you have privacy questions or want to exercise your data rights, contact us by email.',
|
||||
openGraph: {
|
||||
title: `${title} | GreenLens`,
|
||||
description,
|
||||
url: `${siteConfig.domain}/privacy`,
|
||||
type: 'website',
|
||||
},
|
||||
es: {
|
||||
title: 'Politica de Privacidad',
|
||||
intro:
|
||||
'La proteccion de los datos personales es importante para nosotros. Esta pagina resume que datos puede procesar GreenLens y por que.',
|
||||
section1: '1. Datos que podemos procesar',
|
||||
text1:
|
||||
'Segun el uso de la app, GreenLens puede procesar fotos de plantas, datos de cuenta, informacion tecnica del dispositivo, datos de compras dentro de la app y mensajes de soporte.',
|
||||
section2: '2. Para que los usamos',
|
||||
text2:
|
||||
'Usamos estos datos para ejecutar escaneos, guardar tu coleccion, ofrecer recordatorios, gestionar suscripciones y creditos, y responder solicitudes de soporte.',
|
||||
section3: '3. Servicios de terceros',
|
||||
text3:
|
||||
'GreenLens utiliza proveedores para la operacion de la app, analitica, autenticacion y compras integradas. Segun la plataforma, esto puede incluir Apple, RevenueCat, PostHog o proveedores de hosting.',
|
||||
section4: '4. Contacto',
|
||||
text4: 'Si tienes preguntas sobre privacidad o tus derechos de datos, contactanos por correo electronico.',
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${title} | GreenLens`,
|
||||
description,
|
||||
},
|
||||
}
|
||||
|
||||
export default function PrivacyPage() {
|
||||
const { lang } = useLang()
|
||||
const c = CONTENT[lang]
|
||||
|
||||
return (
|
||||
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
|
||||
<h1>{c.title}</h1>
|
||||
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
|
||||
<p>{c.intro}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section1}</h2>
|
||||
<p>{c.text1}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section2}</h2>
|
||||
<p>{c.text2}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section3}</h2>
|
||||
<p>{c.text3}</p>
|
||||
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section4}</h2>
|
||||
<p>{c.text4}</p>
|
||||
<p style={{ marginTop: '0.75rem' }}>
|
||||
<a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<>
|
||||
<Navbar />
|
||||
<PrivacyContent />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
import type { Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
import Navbar from '@/components/Navbar'
|
||||
import Footer from '@/components/Footer'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Support',
|
||||
description: 'Get support for GreenLens, including contact details, onboarding help, billing guidance, and privacy links.',
|
||||
alternates: {
|
||||
canonical: '/support',
|
||||
},
|
||||
openGraph: {
|
||||
title: 'Support | GreenLens',
|
||||
description: 'Get support for GreenLens, including contact details, onboarding help, billing guidance, and privacy links.',
|
||||
url: `${siteConfig.domain}/support`,
|
||||
type: 'website',
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'Support | GreenLens',
|
||||
description: 'Get support for GreenLens, including contact details, onboarding help, billing guidance, and privacy links.',
|
||||
},
|
||||
}
|
||||
|
||||
const faqs = [
|
||||
@@ -32,9 +48,14 @@ const faqs = [
|
||||
|
||||
export default function SupportPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="support-page">
|
||||
<section className="support-hero">
|
||||
<div className="container support-hero-inner">
|
||||
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
|
||||
<Link href="/">Home</Link> / <span>Support</span>
|
||||
</p>
|
||||
<p className="tag">Support</p>
|
||||
<h1>Help for scans, care plans, billing, and account questions.</h1>
|
||||
<p className="support-lead">
|
||||
@@ -75,7 +96,8 @@ export default function SupportPage() {
|
||||
<div className="support-card">
|
||||
<h2>Legal</h2>
|
||||
<p>
|
||||
Review our <Link href="/privacy">Privacy Policy</Link> and <Link href="/imprint">Imprint</Link>.
|
||||
Review our <Link href="/privacy">Privacy Policy</Link>, <Link href="/terms">Terms of Service</Link>,
|
||||
and <Link href="/imprint">Imprint</Link>.
|
||||
</p>
|
||||
<p>These links should be used in App Store Connect before submission.</p>
|
||||
</div>
|
||||
@@ -100,5 +122,7 @@ export default function SupportPage() {
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
51
greenlns-landing/app/terms/TermsContent.tsx
Normal file
51
greenlns-landing/app/terms/TermsContent.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
const CONTENT = {
|
||||
de: {
|
||||
title: 'Nutzungsbedingungen',
|
||||
intro: 'Diese Bedingungen regeln die Nutzung von GreenLens und der dazugehoerigen Services.',
|
||||
section1: 'GreenLens wird als digitale App und Web-Service fuer Pflanzenscans, Informationen und accountbezogene Funktionen bereitgestellt.',
|
||||
section2: 'Vor dem Livegang muessen diese Bedingungen durch rechtlich gepruefte und vollstaendige Vertragstexte ersetzt werden.',
|
||||
contactLabel: 'Kontakt',
|
||||
},
|
||||
en: {
|
||||
title: 'Terms of Service',
|
||||
intro: 'These terms govern the use of GreenLens and its related services.',
|
||||
section1: 'GreenLens is provided as a digital app and web service for plant scans, information, and account-related functionality.',
|
||||
section2: 'Before launch, replace this placeholder with legally reviewed and complete terms for your business.',
|
||||
contactLabel: 'Contact',
|
||||
},
|
||||
es: {
|
||||
title: 'Terminos del Servicio',
|
||||
intro: 'Estos terminos regulan el uso de GreenLens y sus servicios relacionados.',
|
||||
section1: 'GreenLens se ofrece como app y servicio web para escaneo de plantas, informacion y funciones de cuenta.',
|
||||
section2: 'Antes del lanzamiento, sustituye este texto por terminos completos revisados legalmente.',
|
||||
contactLabel: 'Contacto',
|
||||
},
|
||||
}
|
||||
|
||||
export default function TermsContent() {
|
||||
const { lang } = useLang()
|
||||
const c = CONTENT[lang]
|
||||
|
||||
return (
|
||||
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
|
||||
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
|
||||
<Link href="/">Home</Link> / <span>Legal</span> / <span>{c.title}</span>
|
||||
</p>
|
||||
<h1>{c.title}</h1>
|
||||
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
|
||||
<p>{c.intro}</p>
|
||||
<p>{c.section1}</p>
|
||||
<p>{c.section2}</p>
|
||||
<p>
|
||||
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,47 +1,37 @@
|
||||
'use client'
|
||||
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import type { Metadata } from 'next'
|
||||
import Navbar from '@/components/Navbar'
|
||||
import Footer from '@/components/Footer'
|
||||
import TermsContent from './TermsContent'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
const CONTENT = {
|
||||
de: {
|
||||
title: 'Nutzungsbedingungen',
|
||||
intro: 'Diese Bedingungen regeln die Nutzung von GreenLens und der dazugehoerigen Services.',
|
||||
section1: 'GreenLens wird als digitale App und Web-Service fuer Pflanzenscans, Informationen und accountbezogene Funktionen bereitgestellt.',
|
||||
section2: 'Vor dem Livegang muessen diese Bedingungen durch rechtlich gepruefte und vollstaendige Vertragstexte ersetzt werden.',
|
||||
contactLabel: 'Kontakt',
|
||||
const title = 'Terms of Service'
|
||||
const description = 'Review the current GreenLens terms governing use of the app and related services.'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title,
|
||||
description,
|
||||
alternates: {
|
||||
canonical: '/terms',
|
||||
},
|
||||
en: {
|
||||
title: 'Terms of Service',
|
||||
intro: 'These terms govern the use of GreenLens and its related services.',
|
||||
section1: 'GreenLens is provided as a digital app and web service for plant scans, information, and account-related functionality.',
|
||||
section2: 'Before launch, replace this placeholder with legally reviewed and complete terms for your business.',
|
||||
contactLabel: 'Contact',
|
||||
openGraph: {
|
||||
title: `${title} | GreenLens`,
|
||||
description,
|
||||
url: `${siteConfig.domain}/terms`,
|
||||
type: 'website',
|
||||
},
|
||||
es: {
|
||||
title: 'Terminos del Servicio',
|
||||
intro: 'Estos terminos regulan el uso de GreenLens y sus servicios relacionados.',
|
||||
section1: 'GreenLens se ofrece como app y servicio web para escaneo de plantas, informacion y funciones de cuenta.',
|
||||
section2: 'Antes del lanzamiento, sustituye este texto por terminos completos revisados legalmente.',
|
||||
contactLabel: 'Contacto',
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${title} | GreenLens`,
|
||||
description,
|
||||
},
|
||||
}
|
||||
|
||||
export default function TermsPage() {
|
||||
const { lang } = useLang()
|
||||
const c = CONTENT[lang]
|
||||
|
||||
return (
|
||||
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
|
||||
<h1>{c.title}</h1>
|
||||
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
|
||||
<p>{c.intro}</p>
|
||||
<p>{c.section1}</p>
|
||||
<p>{c.section2}</p>
|
||||
<p>
|
||||
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<>
|
||||
<Navbar />
|
||||
<TermsContent />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function CTA() {
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href={hasAndroidStoreUrl ? siteConfig.androidPlayStoreUrl : `mailto:${siteConfig.supportEmail}`}
|
||||
href={hasAndroidStoreUrl ? siteConfig.androidPlayStoreUrl : '/support'}
|
||||
className="store-btn"
|
||||
id="cta-googleplay"
|
||||
aria-label="Google Play or contact"
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import { siteConfig } from '@/lib/site'
|
||||
|
||||
const LINK_HREFS = [
|
||||
['#features', '#intelligence', '#cta', '/support'],
|
||||
['/#how', '/#faq', '/support'],
|
||||
['/imprint', '/privacy'],
|
||||
]
|
||||
|
||||
export default function Footer() {
|
||||
const pathname = usePathname()
|
||||
const { t } = useLang()
|
||||
const homeHref = (hash: string) => (pathname === '/' ? hash : `/${hash}`)
|
||||
const linkHrefs = [
|
||||
[homeHref('#features'), homeHref('#intelligence'), homeHref('#cta'), '/support'],
|
||||
[homeHref('#how'), homeHref('#faq'), '/support'],
|
||||
['/imprint', '/privacy', '/terms'],
|
||||
]
|
||||
|
||||
return (
|
||||
<footer className="footer" id="footer">
|
||||
@@ -28,7 +30,7 @@ export default function Footer() {
|
||||
<div className="footer-col" key={col.title}>
|
||||
<div className="footer-col-title">{col.title}</div>
|
||||
{col.links.map((label, li) => (
|
||||
<Link key={label} href={LINK_HREFS[ci]?.[li] ?? '/support'}>
|
||||
<Link key={label} href={linkHrefs[ci]?.[li] ?? '/support'}>
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
@@ -51,9 +53,9 @@ export default function Footer() {
|
||||
|
||||
<div className="footer-bottom">
|
||||
<p>{t.footer.copy}</p>
|
||||
<a href={`mailto:${siteConfig.supportEmail}`} className="footer-contact">
|
||||
{siteConfig.supportEmail}
|
||||
</a>
|
||||
<Link href="/support" className="footer-contact">
|
||||
Support
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useLang } from '@/context/LangContext'
|
||||
import type { Lang } from '@/lib/i18n'
|
||||
@@ -14,7 +15,9 @@ const LANGS: { code: Lang; label: string; flag: string }[] = [
|
||||
export default function Navbar() {
|
||||
const [scrolled, setScrolled] = useState(false)
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const pathname = usePathname()
|
||||
const { lang, setLang, t } = useLang()
|
||||
const homeHref = (hash: string) => (pathname === '/' ? hash : `/${hash}`)
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 40)
|
||||
@@ -30,12 +33,12 @@ export default function Navbar() {
|
||||
</Link>
|
||||
|
||||
<div className={`nav-links${menuOpen ? ' nav-links--open' : ''}`}>
|
||||
<a href="#features" onClick={() => setMenuOpen(false)}>{t.nav.features}</a>
|
||||
<a href="#intelligence" onClick={() => setMenuOpen(false)}>{t.nav.tech}</a>
|
||||
<a href="#faq" onClick={() => setMenuOpen(false)}>FAQ</a>
|
||||
<a href="#how" onClick={() => setMenuOpen(false)}>{t.nav.how}</a>
|
||||
<a href={homeHref('#features')} onClick={() => setMenuOpen(false)}>{t.nav.features}</a>
|
||||
<a href={homeHref('#intelligence')} onClick={() => setMenuOpen(false)}>{t.nav.tech}</a>
|
||||
<a href={homeHref('#faq')} onClick={() => setMenuOpen(false)}>FAQ</a>
|
||||
<a href={homeHref('#how')} onClick={() => setMenuOpen(false)}>{t.nav.how}</a>
|
||||
<Link href="/support" onClick={() => setMenuOpen(false)}>Support</Link>
|
||||
<a href="#cta" onClick={() => setMenuOpen(false)}>{t.nav.download}</a>
|
||||
<a href={homeHref('#cta')} onClick={() => setMenuOpen(false)}>{t.nav.download}</a>
|
||||
|
||||
<div className="lang-switcher" role="group" aria-label="Language selector">
|
||||
{LANGS.map((l) => (
|
||||
@@ -55,7 +58,7 @@ export default function Navbar() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a href="#cta" className="nav-cta" onClick={() => setMenuOpen(false)}>{t.nav.cta}</a>
|
||||
<a href={homeHref('#cta')} className="nav-cta" onClick={() => setMenuOpen(false)}>{t.nav.cta}</a>
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
@@ -63,7 +63,7 @@ export const translations = {
|
||||
cols: [
|
||||
{ title: 'Produkt', links: ['Features', 'Technologie', 'App laden', 'Support'] },
|
||||
{ title: 'Ressourcen', links: ['So funktioniert es', 'FAQ', 'Support'] },
|
||||
{ title: 'Rechtliches', links: ['Impressum', 'Datenschutz'] },
|
||||
{ title: 'Rechtliches', links: ['Impressum', 'Datenschutz', 'Nutzungsbedingungen'] },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -129,7 +129,7 @@ export const translations = {
|
||||
cols: [
|
||||
{ title: 'Product', links: ['Features', 'Technology', 'Get the App', 'Support'] },
|
||||
{ title: 'Resources', links: ['How it works', 'FAQ', 'Support'] },
|
||||
{ title: 'Legal', links: ['Imprint', 'Privacy Policy'] },
|
||||
{ title: 'Legal', links: ['Imprint', 'Privacy Policy', 'Terms of Service'] },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -195,7 +195,7 @@ export const translations = {
|
||||
cols: [
|
||||
{ title: 'Producto', links: ['Funciones', 'Tecnologia', 'Descargar', 'Support'] },
|
||||
{ title: 'Recursos', links: ['Como funciona', 'FAQ', 'Support'] },
|
||||
{ title: 'Legal', links: ['Aviso legal', 'Privacidad'] },
|
||||
{ title: 'Legal', links: ['Aviso legal', 'Privacidad', 'Terminos del servicio'] },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
33
greenlns-landing/proxy.ts
Normal file
33
greenlns-landing/proxy.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
const APEX_HOST = 'greenlenspro.com'
|
||||
const WWW_HOST = `www.${APEX_HOST}`
|
||||
|
||||
export function proxy(request: NextRequest) {
|
||||
const host = request.headers.get('host')
|
||||
const forwardedProto = request.headers.get('x-forwarded-proto')
|
||||
|
||||
if (!host || (host !== APEX_HOST && host !== WWW_HOST)) {
|
||||
return NextResponse.next()
|
||||
}
|
||||
|
||||
const url = request.nextUrl.clone()
|
||||
let shouldRedirect = false
|
||||
|
||||
if (host === WWW_HOST) {
|
||||
url.host = APEX_HOST
|
||||
shouldRedirect = true
|
||||
}
|
||||
|
||||
if (forwardedProto === 'http' || url.protocol === 'http:') {
|
||||
url.protocol = 'https:'
|
||||
shouldRedirect = true
|
||||
}
|
||||
|
||||
return shouldRedirect ? NextResponse.redirect(url, 308) : NextResponse.next()
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: '/:path*',
|
||||
}
|
||||
Reference in New Issue
Block a user