Feat: New project chat, uload refactor (postgress), hosting plans, uload landingpage

This commit is contained in:
Till-JS 2025-11-25 13:01:41 +01:00
parent 559eb08d8c
commit fcf3a344b1
123 changed files with 7106 additions and 3715 deletions

View file

@ -0,0 +1,76 @@
---
const features = [
{
icon: '🔗',
title: 'URL-Verkürzung',
description: 'Verwandeln Sie lange URLs in kurze, teilbare Links mit nur einem Klick. Perfekt für Social Media und Marketing.'
},
{
icon: '📊',
title: 'Detaillierte Analytics',
description: 'Verfolgen Sie Klicks, geografische Herkunft, Geräte und Engagement Ihrer Links in Echtzeit.'
},
{
icon: '🎨',
title: 'QR-Code Generator',
description: 'Erstellen Sie anpassbare QR-Codes in verschiedenen Farben und Formaten für jeden Link.'
},
{
icon: '💳',
title: 'Digitale Visitenkarten',
description: 'Erstellen Sie professionelle digitale Visitenkarten mit QR-Codes und Kontaktinformationen.'
},
{
icon: '🔒',
title: 'Passwortschutz',
description: 'Schützen Sie Ihre Links mit Passwörtern und setzen Sie Ablaufdaten für zeitlich begrenzte Aktionen.'
},
{
icon: '🏷️',
title: 'Tag-System',
description: 'Organisieren Sie Ihre Links mit Tags und Kategorien für eine bessere Übersicht und Filterung.'
},
{
icon: '👥',
title: 'Team Workspaces',
description: 'Arbeiten Sie im Team zusammen mit gemeinsamen Workspaces und granularen Berechtigungen.'
},
{
icon: '⚡',
title: 'Blitzschnell',
description: 'Unsere Links sind weltweit über ein CDN verteilt für minimale Ladezeiten und maximale Verfügbarkeit.'
},
{
icon: '🔌',
title: 'API Zugang',
description: 'Integrieren Sie uLoad in Ihre Anwendungen mit unserer RESTful API für automatisierte Workflows.'
}
];
---
<section id="features" class="px-4 py-16 sm:px-6 lg:px-8 lg:py-24">
<div class="mx-auto max-w-7xl">
<div class="text-center mb-16">
<h2 class="mb-4 text-3xl font-bold text-gray-900 sm:text-4xl">
Alles was du für professionelles Link-Management brauchst
</h2>
<p class="mx-auto max-w-2xl text-lg text-gray-600">
Von einfacher URL-Verkürzung bis hin zu Team-Kollaboration uLoad bietet alle Features die du brauchst.
</p>
</div>
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{features.map(feature => (
<div class="group relative rounded-xl border border-gray-200 bg-white p-6 transition hover:shadow-xl hover:border-primary-200">
<div class="mb-4 text-4xl">{feature.icon}</div>
<h3 class="mb-2 text-xl font-semibold text-gray-900 group-hover:text-primary-600 transition-colors">
{feature.title}
</h3>
<p class="text-gray-600">
{feature.description}
</p>
</div>
))}
</div>
</div>
</section>

View file

@ -0,0 +1,95 @@
---
const currentYear = new Date().getFullYear();
const footerLinks = {
produkt: [
{ href: '/features', label: 'Features' },
{ href: '/#pricing', label: 'Preise' },
{ href: '/blog', label: 'Blog' },
],
unternehmen: [
{ href: '/about', label: 'Über uns' },
],
rechtliches: [
{ href: '/datenschutz', label: 'Datenschutz' },
{ href: '/impressum', label: 'Impressum' },
{ href: '/agb', label: 'AGB' },
{ href: '/sicherheit', label: 'Sicherheit' },
],
};
const appUrl = 'https://app.ulo.ad';
---
<footer class="bg-gray-900 text-gray-300">
<div class="container-custom py-12 md:py-16">
<div class="grid grid-cols-2 md:grid-cols-4 gap-8">
<!-- Brand -->
<div class="col-span-2 md:col-span-1">
<a href="/" class="flex items-center gap-2 mb-4">
<div class="w-8 h-8 bg-primary-600 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-lg">u</span>
</div>
<span class="text-xl font-bold text-white">uLoad</span>
</a>
<p class="text-sm text-gray-400 mb-4">
Der intelligente URL-Shortener für Profis. Verkürzen Sie Links, erstellen Sie QR-Codes und analysieren Sie Klicks.
</p>
</div>
<!-- Produkt -->
<div>
<h3 class="text-white font-semibold mb-4">Produkt</h3>
<ul class="space-y-2">
{footerLinks.produkt.map(link => (
<li>
<a href={link.href} class="text-gray-400 hover:text-white transition-colors text-sm">
{link.label}
</a>
</li>
))}
</ul>
</div>
<!-- Unternehmen -->
<div>
<h3 class="text-white font-semibold mb-4">Unternehmen</h3>
<ul class="space-y-2">
{footerLinks.unternehmen.map(link => (
<li>
<a href={link.href} class="text-gray-400 hover:text-white transition-colors text-sm">
{link.label}
</a>
</li>
))}
</ul>
</div>
<!-- Rechtliches -->
<div>
<h3 class="text-white font-semibold mb-4">Rechtliches</h3>
<ul class="space-y-2">
{footerLinks.rechtliches.map(link => (
<li>
<a href={link.href} class="text-gray-400 hover:text-white transition-colors text-sm">
{link.label}
</a>
</li>
))}
</ul>
</div>
</div>
<!-- Bottom -->
<div class="border-t border-gray-800 mt-12 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
<p class="text-sm text-gray-400">
© {currentYear} uLoad. Alle Rechte vorbehalten.
</p>
<div class="flex items-center gap-4">
<a href={`${appUrl}/login`} class="text-sm text-gray-400 hover:text-white transition-colors">
App öffnen
</a>
</div>
</div>
</div>
</footer>

View file

@ -0,0 +1,138 @@
---
const appUrl = 'https://app.ulo.ad';
---
<section class="relative overflow-hidden bg-gradient-to-br from-primary-500/5 via-white to-purple-600/5 px-4 py-16 sm:px-6 lg:px-8 lg:py-24">
<!-- Background decoration -->
<div class="absolute inset-0 -z-10">
<div class="absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2 h-96 w-96 rounded-full bg-primary-500/10 blur-3xl"></div>
<div class="absolute bottom-0 right-0 translate-x-1/3 translate-y-1/3 h-96 w-96 rounded-full bg-purple-600/10 blur-3xl"></div>
</div>
<div class="mx-auto max-w-7xl">
<div class="text-center">
<!-- Trust badges -->
<div class="mb-6 flex flex-wrap justify-center gap-4 text-sm text-gray-500">
<span class="flex items-center gap-1">
<svg class="h-4 w-4 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
DSGVO-konform
</span>
<span class="flex items-center gap-1">
<svg class="h-4 w-4 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
Blitzschnell
</span>
<span class="flex items-center gap-1">
<svg class="h-4 w-4 text-purple-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
100% Sicher
</span>
</div>
<!-- Main headline -->
<h1 class="mb-4 text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl lg:text-6xl">
More than links.
<span class="bg-gradient-to-r from-primary-600 to-purple-600 bg-clip-text text-transparent">
Your digital identity.
</span>
</h1>
<p class="mx-auto mb-8 max-w-2xl text-lg text-gray-600 sm:text-xl">
Der einzige Link-Shortener mit integriertem Profile-Builder.
Erstelle kurze Links, beeindruckende Profilkarten und manage alles im Team.
</p>
<!-- CTA Buttons -->
<div class="mb-12 flex flex-col justify-center gap-4 sm:flex-row">
<a
href={`${appUrl}/register`}
class="rounded-lg bg-primary-600 px-8 py-3 font-semibold text-white shadow-lg transition hover:bg-primary-700 hover:shadow-xl"
>
Kostenlos starten →
</a>
<a
href="#features"
class="rounded-lg border-2 border-gray-200 bg-white px-8 py-3 font-semibold text-gray-900 transition hover:border-primary-500 hover:shadow-lg"
>
Features entdecken
</a>
</div>
<!-- Shortener teaser -->
<div class="mx-auto max-w-2xl">
<div class="flex flex-col gap-3 rounded-xl border border-gray-200 bg-white/80 p-4 backdrop-blur sm:flex-row sm:p-2">
<input
type="url"
placeholder="Deine lange URL hier einfügen..."
disabled
class="flex-1 rounded-lg border-0 bg-transparent px-4 py-3 text-gray-900 placeholder-gray-400 focus:outline-none sm:py-2"
/>
<a
href={`${appUrl}/register`}
class="rounded-lg bg-primary-600 px-6 py-3 font-medium text-white transition hover:bg-primary-700 sm:py-2 text-center"
>
Kürzen →
</a>
</div>
<p class="mt-2 text-sm text-gray-500">
Keine Anmeldung erforderlich • Kostenlos • QR-Code inklusive
</p>
</div>
</div>
<!-- Visual preview -->
<div class="mt-16 grid grid-cols-1 gap-8 lg:grid-cols-3">
<!-- Link shortening preview -->
<div class="group relative rounded-xl border border-gray-200 bg-white p-6 transition hover:shadow-xl">
<div class="mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-primary-100">
<svg class="h-6 w-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
</svg>
</div>
<h3 class="mb-2 font-semibold text-gray-900">Smart Links</h3>
<p class="text-sm text-gray-600">
Kurze URLs mit Tracking, Ablaufdatum und Passwortschutz
</p>
<a href="/features" class="mt-4 inline-block text-xs text-primary-600 group-hover:underline">
Mehr erfahren →
</a>
</div>
<!-- Profile cards preview -->
<div class="group relative rounded-xl border border-gray-200 bg-white p-6 transition hover:shadow-xl">
<div class="mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-purple-100">
<svg class="h-6 w-6 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
</svg>
</div>
<h3 class="mb-2 font-semibold text-gray-900">Profile Cards</h3>
<p class="text-sm text-gray-600">
Beeindruckende Profilseiten mit Drag & Drop Builder
</p>
<a href="/features" class="mt-4 inline-block text-xs text-purple-600 group-hover:underline">
Templates ansehen →
</a>
</div>
<!-- Team collaboration preview -->
<div class="group relative rounded-xl border border-gray-200 bg-white p-6 transition hover:shadow-xl">
<div class="mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-green-100">
<svg class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</div>
<h3 class="mb-2 font-semibold text-gray-900">Team Workspace</h3>
<p class="text-sm text-gray-600">
Gemeinsam Links verwalten mit granularen Berechtigungen
</p>
<a href="/features" class="mt-4 inline-block text-xs text-green-600 group-hover:underline">
Für Teams →
</a>
</div>
</div>
</div>
</section>

View file

@ -0,0 +1,87 @@
---
const navLinks = [
{ href: '/features', label: 'Features' },
{ href: '/blog', label: 'Blog' },
{ href: '/about', label: 'Über uns' },
];
const appUrl = 'https://app.ulo.ad';
---
<header class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-100">
<nav class="container-custom">
<div class="flex items-center justify-between h-16">
<!-- Logo -->
<a href="/" class="flex items-center gap-2">
<div class="w-8 h-8 bg-primary-600 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-lg">u</span>
</div>
<span class="text-xl font-bold text-gray-900">uLoad</span>
</a>
<!-- Desktop Navigation -->
<div class="hidden md:flex items-center gap-8">
{navLinks.map(link => (
<a
href={link.href}
class="text-gray-600 hover:text-gray-900 font-medium transition-colors"
>
{link.label}
</a>
))}
</div>
<!-- CTA Buttons -->
<div class="hidden md:flex items-center gap-4">
<a href={`${appUrl}/login`} class="text-gray-600 hover:text-gray-900 font-medium">
Anmelden
</a>
<a href={`${appUrl}/register`} class="btn-primary">
Kostenlos starten
</a>
</div>
<!-- Mobile Menu Button -->
<button
id="mobile-menu-btn"
class="md:hidden p-2 text-gray-600 hover:text-gray-900"
aria-label="Menü öffnen"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
<!-- Mobile Menu -->
<div id="mobile-menu" class="hidden md:hidden pb-4">
<div class="flex flex-col gap-4">
{navLinks.map(link => (
<a
href={link.href}
class="text-gray-600 hover:text-gray-900 font-medium py-2"
>
{link.label}
</a>
))}
<div class="flex flex-col gap-2 pt-4 border-t border-gray-100">
<a href={`${appUrl}/login`} class="btn-secondary text-center">
Anmelden
</a>
<a href={`${appUrl}/register`} class="btn-primary text-center">
Kostenlos starten
</a>
</div>
</div>
</div>
</nav>
</header>
<script>
const menuBtn = document.getElementById('mobile-menu-btn');
const mobileMenu = document.getElementById('mobile-menu');
menuBtn?.addEventListener('click', () => {
mobileMenu?.classList.toggle('hidden');
});
</script>

View file

@ -0,0 +1,185 @@
---
const appUrl = 'https://app.ulo.ad';
const plans = [
{
id: 'free',
name: 'Free',
price: 0,
period: '/Monat',
description: 'Perfekt zum Ausprobieren',
features: [
'10 Links pro Monat',
'Basis Analytics',
'QR-Code Generator',
'Link Anpassung',
'Standard Support'
],
cta: 'Kostenlos starten',
highlighted: false,
href: `${appUrl}/register`
},
{
id: 'pro-monthly',
name: 'Pro',
price: 4.99,
period: '/Monat',
description: 'Für Freelancer & Creators',
features: [
'Unbegrenzte Links',
'Erweiterte Analytics',
'Custom QR Codes',
'Link Anpassung',
'Priority Support',
'API Zugang'
],
cta: 'Pro wählen',
highlighted: false,
href: `${appUrl}/register?plan=pro`
},
{
id: 'pro-yearly',
name: 'Pro Jährlich',
price: 3.33,
period: '/Monat',
description: 'Beste Wahl für Power User',
features: [
'Unbegrenzte Links',
'Erweiterte Analytics',
'Custom QR Codes',
'Link Anpassung',
'Priority Support',
'API Zugang'
],
cta: 'Jährlich sparen',
highlighted: true,
badge: 'Spare 20€/Jahr',
href: `${appUrl}/register?plan=pro-yearly`
},
{
id: 'lifetime',
name: 'Pro Lifetime',
price: 129.99,
period: 'einmalig',
description: 'Einmalig zahlen, für immer nutzen',
features: [
'Alle Pro Features',
'Lebenslanger Zugang',
'Alle zukünftigen Features',
'Early Access',
'Priority Support'
],
cta: 'Lifetime sichern',
highlighted: false,
badge: 'Einmalig',
href: `${appUrl}/register?plan=lifetime`
}
];
function formatPrice(price: number): string {
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
minimumFractionDigits: price % 1 === 0 ? 0 : 2
}).format(price);
}
---
<section id="pricing" class="px-4 py-16 sm:px-6 lg:px-8 lg:py-24 bg-gray-50">
<div class="mx-auto max-w-7xl">
<div class="text-center">
<h2 class="mb-4 text-3xl font-bold text-gray-900 sm:text-4xl">
Transparente Preise, keine versteckten Kosten
</h2>
<p class="mx-auto mb-12 max-w-2xl text-lg text-gray-600">
Starte kostenlos und upgrade wenn du bereit bist. Jederzeit kündbar.
</p>
</div>
<!-- Pricing Cards -->
<div class="grid gap-8 lg:grid-cols-4">
{plans.map(plan => (
<div
class:list={[
"relative rounded-xl border-2 bg-white transition-all duration-300",
plan.highlighted
? "border-primary-500 shadow-2xl scale-105"
: "border-gray-200 hover:border-primary-300 hover:shadow-xl"
]}
>
{plan.badge && (
<div class="absolute -top-4 left-1/2 -translate-x-1/2">
<span class="rounded-full bg-primary-600 px-4 py-1 text-xs font-semibold text-white">
{plan.badge}
</span>
</div>
)}
<div class="p-6">
<h3 class="mb-2 text-xl font-bold text-gray-900">{plan.name}</h3>
<p class="mb-4 text-sm text-gray-500">{plan.description}</p>
<div class="mb-6">
<div class="flex items-baseline">
<span class="text-4xl font-bold text-gray-900">
{formatPrice(plan.price)}
</span>
<span class="ml-2 text-gray-500">{plan.period}</span>
</div>
</div>
<a
href={plan.href}
class:list={[
"mb-6 block w-full rounded-lg py-3 font-semibold text-center transition",
plan.highlighted
? "bg-primary-600 text-white hover:bg-primary-700"
: "border-2 border-gray-200 text-gray-900 hover:border-primary-500 hover:bg-primary-50"
]}
>
{plan.cta}
</a>
<div class="space-y-3">
<p class="text-xs font-semibold uppercase tracking-wide text-gray-500">
Inklusive:
</p>
{plan.features.map(feature => (
<div class="flex items-start gap-3">
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
<span class="text-sm text-gray-700">{feature}</span>
</div>
))}
</div>
</div>
</div>
))}
</div>
<!-- Benefits -->
<div class="mt-16 rounded-xl border border-gray-200 bg-white p-8">
<div class="grid gap-8 lg:grid-cols-3">
<div>
<h4 class="mb-2 font-semibold text-gray-900">💳 Keine Kreditkarte erforderlich</h4>
<p class="text-sm text-gray-600">
Starte komplett kostenlos. Upgrade nur wenn du mehr brauchst.
</p>
</div>
<div>
<h4 class="mb-2 font-semibold text-gray-900">🔄 Jederzeit kündbar</h4>
<p class="text-sm text-gray-600">
Keine Vertragsbindung. Kündige monatlich ohne Probleme.
</p>
</div>
<div>
<h4 class="mb-2 font-semibold text-gray-900">🚀 Sofort startklar</h4>
<p class="text-sm text-gray-600">
Nach der Anmeldung kannst du sofort alle Features nutzen.
</p>
</div>
</div>
</div>
</div>
</section>