mehr übersetzungen, added pending purchase indicator. datenschutzfenster funktioniert.
This commit is contained in:
@@ -24,14 +24,16 @@ class MarketingRegisterController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Show the registration page.
|
* Show the registration page.
|
||||||
*/
|
*/
|
||||||
public function create(Request $request, $package_id = null): Response
|
public function create(Request $request): Response
|
||||||
{
|
{
|
||||||
|
$package_id = $request->query('package_id');
|
||||||
$package = $package_id ? Package::find($package_id) : null;
|
$package = $package_id ? Package::find($package_id) : null;
|
||||||
|
|
||||||
//App::setLocale('de');
|
$privacyHtml = view('legal.datenschutz-partial')->render();
|
||||||
|
|
||||||
return Inertia::render('auth/register', [
|
return Inertia::render('auth/register', [
|
||||||
'package' => $package,
|
'package' => $package,
|
||||||
|
'privacyHtml' => $privacyHtml,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +67,13 @@ class MarketingRegisterController extends Controller
|
|||||||
'phone' => $validated['phone'],
|
'phone' => $validated['phone'],
|
||||||
'password' => Hash::make($validated['password']),
|
'password' => Hash::make($validated['password']),
|
||||||
'role' => 'user',
|
'role' => 'user',
|
||||||
|
'pending_purchase' => !empty($validated['package_id']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($user->pending_purchase) {
|
||||||
|
session()->put('pending_user_id', $user->id);
|
||||||
|
}
|
||||||
|
|
||||||
if ($shouldAutoVerify) {
|
if ($shouldAutoVerify) {
|
||||||
$user->forceFill(['email_verified_at' => now()])->save();
|
$user->forceFill(['email_verified_at' => now()])->save();
|
||||||
}
|
}
|
||||||
@@ -113,6 +120,7 @@ class MarketingRegisterController extends Controller
|
|||||||
$package = Package::find($validated['package_id']);
|
$package = Package::find($validated['package_id']);
|
||||||
if (!$package) {
|
if (!$package) {
|
||||||
// No action if package not found
|
// No action if package not found
|
||||||
|
return redirect()->route('dashboard')->with('success', 'Registrierung erfolgreich! Bitte verifizieren Sie Ihre E-Mail.');
|
||||||
} else if ((float) $package->price <= 0.0) {
|
} else if ((float) $package->price <= 0.0) {
|
||||||
// Assign free package
|
// Assign free package
|
||||||
TenantPackage::create([
|
TenantPackage::create([
|
||||||
@@ -133,20 +141,28 @@ class MarketingRegisterController extends Controller
|
|||||||
|
|
||||||
$tenant->update(['subscription_status' => 'active']);
|
$tenant->update(['subscription_status' => 'active']);
|
||||||
|
|
||||||
$user->update(['role' => 'tenant_admin']);
|
$user->update(['role' => 'tenant_admin', 'pending_purchase' => false]);
|
||||||
Auth::login($user); // Re-login to refresh session
|
Auth::login($user); // Re-login to refresh session
|
||||||
|
|
||||||
|
if ($shouldAutoVerify) {
|
||||||
|
return redirect()->route('dashboard')->with('success', 'Registrierung und Package-Zuweisung erfolgreich!');
|
||||||
|
} else {
|
||||||
|
return redirect()->route('verification.notice')->with('status', 'registration-success');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('buy.packages', $package->id);
|
// For paid package, keep pending_purchase true, redirect to buy
|
||||||
|
return redirect()->route('buy.packages', $package->id)->with('success', 'Registrierung erfolgreich! Fahren Sie mit dem Kauf fort.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No package
|
||||||
if ($shouldAutoVerify) {
|
if ($shouldAutoVerify) {
|
||||||
return Inertia::location($dashboardUrl);
|
return redirect()->route('dashboard')->with('success', 'Registrierung erfolgreich!');
|
||||||
}
|
}
|
||||||
|
|
||||||
session()->flash('status', 'registration-success');
|
session()->flash('status', 'registration-success');
|
||||||
|
|
||||||
return Inertia::location(route('verification.notice'));
|
return redirect()->route('verification.notice');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,17 @@ class StripeWebhookController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Activate user if pending purchase
|
||||||
|
$user = \App\Models\User::where('id', $tenant->user_id ?? $userId)->first();
|
||||||
|
if ($user && $user->pending_purchase) {
|
||||||
|
$user->update([
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
'role' => 'tenant_admin',
|
||||||
|
'pending_purchase' => false,
|
||||||
|
]);
|
||||||
|
Log::info('User activated after purchase: ' . $user->id);
|
||||||
|
}
|
||||||
|
|
||||||
// Create PackagePurchase for one-off payment
|
// Create PackagePurchase for one-off payment
|
||||||
\App\Models\PackagePurchase::create([
|
\App\Models\PackagePurchase::create([
|
||||||
'tenant_id' => $tenantId,
|
'tenant_id' => $tenantId,
|
||||||
@@ -209,6 +220,18 @@ class StripeWebhookController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Activate user if pending purchase
|
||||||
|
$tenant = \App\Models\Tenant::find($tenantId);
|
||||||
|
$user = $tenant ? $tenant->user : null;
|
||||||
|
if ($user && $user->pending_purchase) {
|
||||||
|
$user->update([
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
'role' => 'tenant_admin',
|
||||||
|
'pending_purchase' => false,
|
||||||
|
]);
|
||||||
|
Log::info('User activated after subscription purchase: ' . $user->id);
|
||||||
|
}
|
||||||
|
|
||||||
// Activate TenantPackage for initial subscription
|
// Activate TenantPackage for initial subscription
|
||||||
\App\Models\TenantPackage::updateOrCreate(
|
\App\Models\TenantPackage::updateOrCreate(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
'address',
|
'address',
|
||||||
'phone',
|
'phone',
|
||||||
'role',
|
'role',
|
||||||
|
'pending_purchase',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,6 +54,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
return [
|
return [
|
||||||
'email_verified_at' => 'datetime',
|
'email_verified_at' => 'datetime',
|
||||||
'password' => 'hashed',
|
'password' => 'hashed',
|
||||||
|
'pending_purchase' => 'boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('pending_purchase')->default(false)->after('role');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('pending_purchase');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -44,7 +44,18 @@
|
|||||||
"has_account": "Bereits registriert?",
|
"has_account": "Bereits registriert?",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"errors_title": "Fehler bei der Registrierung:",
|
"errors_title": "Fehler bei der Registrierung:",
|
||||||
"privacy_policy": "Datenschutzerklärung"
|
"privacy_policy": "Datenschutzerklärung",
|
||||||
|
"errors": {
|
||||||
|
"first_name": "Vorname ist erforderlich.",
|
||||||
|
"last_name": "Nachname ist erforderlich.",
|
||||||
|
"email": "E-Mail ist erforderlich.",
|
||||||
|
"address": "Adresse ist erforderlich.",
|
||||||
|
"phone": "Telefonnummer ist erforderlich.",
|
||||||
|
"username": "Benutzername ist erforderlich.",
|
||||||
|
"password": "Passwort ist erforderlich.",
|
||||||
|
"confirm_password": "Passwortbestätigung stimmt nicht überein.",
|
||||||
|
"privacy_consent": "Sie müssen der Datenschutzerklärung zustimmen."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
|
|||||||
35
public/lang/de/common.json
Normal file
35
public/lang/de/common.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"required": "*",
|
||||||
|
"unlimited": "Unbegrenzt",
|
||||||
|
"loading": "Laden...",
|
||||||
|
"included": "Enthalten",
|
||||||
|
"star": "Stern",
|
||||||
|
"date": {
|
||||||
|
"format": "d.m.Y"
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"previous": "Zurück",
|
||||||
|
"next": "Weiter"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"errors": {
|
||||||
|
"name": "Name ist erforderlich.",
|
||||||
|
"email": "E-Mail ist erforderlich.",
|
||||||
|
"message": "Nachricht ist erforderlich."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"euro": "€"
|
||||||
|
},
|
||||||
|
"testimonials": {
|
||||||
|
"anna": {
|
||||||
|
"name": "Anna M."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"name": "Max S."
|
||||||
|
},
|
||||||
|
"lisa": {
|
||||||
|
"name": "Lisa K."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -120,7 +120,12 @@
|
|||||||
"euro": "€"
|
"euro": "€"
|
||||||
},
|
},
|
||||||
"view_details": "Details ansehen",
|
"view_details": "Details ansehen",
|
||||||
"feature": "Feature"
|
"feature": "Feature",
|
||||||
|
"details": "Details",
|
||||||
|
"customer_opinions": "Kundenmeinungen",
|
||||||
|
"errors": {
|
||||||
|
"select_package": "Bitte wählen Sie ein Paket aus."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"blog": {
|
"blog": {
|
||||||
"title": "Fotospiel - Blog",
|
"title": "Fotospiel - Blog",
|
||||||
@@ -137,7 +142,11 @@
|
|||||||
"our_blog": "Unser Blog",
|
"our_blog": "Unser Blog",
|
||||||
"latest_posts": "Neueste Beiträge",
|
"latest_posts": "Neueste Beiträge",
|
||||||
"no_posts": "Keine Beiträge verfügbar.",
|
"no_posts": "Keine Beiträge verfügbar.",
|
||||||
"read_more_link": "Mehr lesen"
|
"read_more_link": "Mehr lesen",
|
||||||
|
"date_format": "Veröffentlicht am :date",
|
||||||
|
"post": {
|
||||||
|
"alt": "Beitragsbild"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"kontakt": {
|
"kontakt": {
|
||||||
"title": "Kontakt - Fotospiel",
|
"title": "Kontakt - Fotospiel",
|
||||||
@@ -262,5 +271,37 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"title": "Fotospiel - Sammle Gastfotos für Events mit QR-Codes",
|
"title": "Fotospiel - Sammle Gastfotos für Events mit QR-Codes",
|
||||||
"description": "Sammle Gastfotos für Events mit QR-Codes. Unsere sichere PWA-Plattform für Gäste und Organisatoren – einfach, mobil und datenschutzkonform."
|
"description": "Sammle Gastfotos für Events mit QR-Codes. Unsere sichere PWA-Plattform für Gäste und Organisatoren – einfach, mobil und datenschutzkonform."
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"unlimited": "Unbegrenzt",
|
||||||
|
"required": "*",
|
||||||
|
"loading": "Laden...",
|
||||||
|
"included": "Enthalten",
|
||||||
|
"star": "Stern",
|
||||||
|
"date": {
|
||||||
|
"format": "d.m.Y"
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"previous": "Zurück",
|
||||||
|
"next": "Weiter"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"errors": {
|
||||||
|
"name": "Name ist erforderlich.",
|
||||||
|
"email": "E-Mail ist erforderlich.",
|
||||||
|
"message": "Nachricht ist erforderlich."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"testimonials": {
|
||||||
|
"anna": {
|
||||||
|
"name": "Anna M."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"name": "Max S."
|
||||||
|
},
|
||||||
|
"lisa": {
|
||||||
|
"name": "Lisa K."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,18 @@
|
|||||||
"has_account": "Already registered?",
|
"has_account": "Already registered?",
|
||||||
"login": "Log in",
|
"login": "Log in",
|
||||||
"errors_title": "Registration errors:",
|
"errors_title": "Registration errors:",
|
||||||
"privacy_policy": "Privacy policy"
|
"privacy_policy": "Privacy policy",
|
||||||
|
"errors": {
|
||||||
|
"first_name": "First name is required.",
|
||||||
|
"last_name": "Last name is required.",
|
||||||
|
"email": "Email is required.",
|
||||||
|
"address": "Address is required.",
|
||||||
|
"phone": "Phone number is required.",
|
||||||
|
"username": "Username is required.",
|
||||||
|
"password": "Password is required.",
|
||||||
|
"confirm_password": "Confirm password does not match.",
|
||||||
|
"privacy_consent": "You must agree to the privacy policy."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"login": "Log in",
|
"login": "Log in",
|
||||||
|
|||||||
35
public/lang/en/common.json
Normal file
35
public/lang/en/common.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"required": "*",
|
||||||
|
"unlimited": "Unlimited",
|
||||||
|
"loading": "Loading...",
|
||||||
|
"included": "Included",
|
||||||
|
"star": "Star",
|
||||||
|
"date": {
|
||||||
|
"format": "M d, Y"
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"previous": "Previous",
|
||||||
|
"next": "Next"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"errors": {
|
||||||
|
"name": "Name is required.",
|
||||||
|
"email": "Email is required.",
|
||||||
|
"message": "Message is required."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"euro": "€"
|
||||||
|
},
|
||||||
|
"testimonials": {
|
||||||
|
"anna": {
|
||||||
|
"name": "Anna M."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"name": "Max S."
|
||||||
|
},
|
||||||
|
"lisa": {
|
||||||
|
"name": "Lisa K."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,7 +111,12 @@
|
|||||||
},
|
},
|
||||||
"what_customers_say": "What our customers say",
|
"what_customers_say": "What our customers say",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"to_order": "Order Now"
|
"to_order": "Order Now",
|
||||||
|
"details": "Details",
|
||||||
|
"customer_opinions": "Customer Opinions",
|
||||||
|
"errors": {
|
||||||
|
"select_package": "Please select a package."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"blog": {
|
"blog": {
|
||||||
"title": "Fotospiel - Blog",
|
"title": "Fotospiel - Blog",
|
||||||
@@ -128,7 +133,11 @@
|
|||||||
"our_blog": "Our Blog",
|
"our_blog": "Our Blog",
|
||||||
"latest_posts": "Latest Posts",
|
"latest_posts": "Latest Posts",
|
||||||
"no_posts": "No posts available.",
|
"no_posts": "No posts available.",
|
||||||
"read_more_link": "Read More"
|
"read_more_link": "Read More",
|
||||||
|
"date_format": "Published on :date",
|
||||||
|
"post": {
|
||||||
|
"alt": "Post Image"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"kontakt": {
|
"kontakt": {
|
||||||
"title": "Contact - Fotospiel",
|
"title": "Contact - Fotospiel",
|
||||||
@@ -253,5 +262,37 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"title": "Fotospiel - Collect Guest Photos for Events with QR Codes",
|
"title": "Fotospiel - Collect Guest Photos for Events with QR Codes",
|
||||||
"description": "Collect guest photos for events with QR codes. Our secure PWA platform for guests and organizers – simple, mobile, and privacy-compliant."
|
"description": "Collect guest photos for events with QR codes. Our secure PWA platform for guests and organizers – simple, mobile, and privacy-compliant."
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"unlimited": "Unlimited",
|
||||||
|
"required": "*",
|
||||||
|
"loading": "Loading...",
|
||||||
|
"included": "Included",
|
||||||
|
"star": "Star",
|
||||||
|
"date": {
|
||||||
|
"format": "M d, Y"
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"previous": "Previous",
|
||||||
|
"next": "Next"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"errors": {
|
||||||
|
"name": "Name is required.",
|
||||||
|
"email": "Email is required.",
|
||||||
|
"message": "Message is required."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"testimonials": {
|
||||||
|
"anna": {
|
||||||
|
"name": "Anna M."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"name": "Max S."
|
||||||
|
},
|
||||||
|
"lisa": {
|
||||||
|
"name": "Lisa K."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
32
resources/js/i18n.ts
Normal file
32
resources/js/i18n.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import i18n from 'i18next';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
import Backend from 'i18next-http-backend';
|
||||||
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(Backend)
|
||||||
|
.use(LanguageDetector)
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
fallbackLng: 'de',
|
||||||
|
supportedLngs: ['de', 'en'],
|
||||||
|
ns: ['marketing', 'auth', 'common'],
|
||||||
|
defaultNS: 'marketing',
|
||||||
|
debug: import.meta.env.DEV,
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false,
|
||||||
|
},
|
||||||
|
backend: {
|
||||||
|
loadPath: '/lang/{{lng}}/{{ns}}.json',
|
||||||
|
},
|
||||||
|
detection: {
|
||||||
|
order: ['path', 'localStorage', 'htmlTag'],
|
||||||
|
lookupFromPathIndex: 0,
|
||||||
|
caches: ['localStorage'],
|
||||||
|
},
|
||||||
|
react: {
|
||||||
|
useSuspense: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Head, Link, usePage } from '@inertiajs/react';
|
import { Head, Link, usePage, router } from '@inertiajs/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
interface MarketingLayoutProps {
|
interface MarketingLayoutProps {
|
||||||
@@ -10,6 +10,14 @@ interface MarketingLayoutProps {
|
|||||||
const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) => {
|
const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) => {
|
||||||
const { t } = useTranslation('marketing');
|
const { t } = useTranslation('marketing');
|
||||||
const { url } = usePage();
|
const { url } = usePage();
|
||||||
|
const i18n = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const locale = url.startsWith('/en/') ? 'en' : 'de';
|
||||||
|
if (i18n.i18n.language !== locale) {
|
||||||
|
i18n.i18n.changeLanguage(locale);
|
||||||
|
}
|
||||||
|
}, [url, i18n]);
|
||||||
|
|
||||||
const { translations } = usePage().props as any;
|
const { translations } = usePage().props as any;
|
||||||
const marketing = translations?.marketing || {};
|
const marketing = translations?.marketing || {};
|
||||||
@@ -39,6 +47,43 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
|
|||||||
<link rel="alternate" hrefLang="x-default" href="https://fotospiel.app/de" />
|
<link rel="alternate" hrefLang="x-default" href="https://fotospiel.app/de" />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="min-h-screen bg-white">
|
<div className="min-h-screen bg-white">
|
||||||
|
<header className="bg-white shadow-sm">
|
||||||
|
<div className="container mx-auto px-4 py-4">
|
||||||
|
<nav className="flex justify-between items-center">
|
||||||
|
<Link href="/" className="text-xl font-bold text-gray-900">
|
||||||
|
Fotospiel
|
||||||
|
</Link>
|
||||||
|
<div className="hidden md:flex space-x-8">
|
||||||
|
<Link href="/" className="text-gray-700 hover:text-gray-900">
|
||||||
|
{t('nav.home')}
|
||||||
|
</Link>
|
||||||
|
<Link href="/packages" className="text-gray-700 hover:text-gray-900">
|
||||||
|
{t('nav.packages')}
|
||||||
|
</Link>
|
||||||
|
<Link href="/blog" className="text-gray-700 hover:text-gray-900">
|
||||||
|
{t('nav.blog')}
|
||||||
|
</Link>
|
||||||
|
<Link href="/kontakt" className="text-gray-700 hover:text-gray-900">
|
||||||
|
{t('nav.contact')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<select
|
||||||
|
value={currentLocale}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newLocale = e.target.value;
|
||||||
|
const newPath = url.replace(/^\/(de|en)?/, `/${newLocale}`);
|
||||||
|
router.visit(newPath);
|
||||||
|
}}
|
||||||
|
className="border border-gray-300 rounded-md px-2 py-1"
|
||||||
|
>
|
||||||
|
<option value="de">DE</option>
|
||||||
|
<option value="en">EN</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
@@ -47,13 +92,13 @@ const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) =>
|
|||||||
<p>© 2025 Fotospiel. Alle Rechte vorbehalten.</p>
|
<p>© 2025 Fotospiel. Alle Rechte vorbehalten.</p>
|
||||||
<div className="mt-4 space-x-4">
|
<div className="mt-4 space-x-4">
|
||||||
<Link href="/datenschutz" className="hover:underline">
|
<Link href="/datenschutz" className="hover:underline">
|
||||||
{t('nav.privacy', getString('nav.privacy', 'Datenschutz'))}
|
{t('nav.privacy')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/impressum" className="hover:underline">
|
<Link href="/impressum" className="hover:underline">
|
||||||
{t('nav.impressum', getString('nav.impressum', 'Impressum'))}
|
{t('nav.impressum')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/kontakt" className="hover:underline">
|
<Link href="/kontakt" className="hover:underline">
|
||||||
{t('nav.contact', getString('nav.contact', 'Kontakt'))}
|
{t('nav.contact')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import MarketingLayout from '@/layouts/marketing/MarketingLayout';
|
|||||||
export default function Register({ package: initialPackage, privacyHtml }: RegisterProps) {
|
export default function Register({ package: initialPackage, privacyHtml }: RegisterProps) {
|
||||||
const [privacyOpen, setPrivacyOpen] = useState(false);
|
const [privacyOpen, setPrivacyOpen] = useState(false);
|
||||||
const [hasTriedSubmit, setHasTriedSubmit] = useState(false);
|
const [hasTriedSubmit, setHasTriedSubmit] = useState(false);
|
||||||
const { t } = useTranslation('auth');
|
const { t } = useTranslation(['auth', 'common']);
|
||||||
|
|
||||||
const { data, setData, post, processing, errors, clearErrors } = useForm({
|
const { data, setData, post, processing, errors, clearErrors } = useForm({
|
||||||
username: '',
|
username: '',
|
||||||
@@ -87,7 +87,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div className="md:col-span-1">
|
<div className="md:col-span-1">
|
||||||
<label htmlFor="first_name" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="first_name" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.first_name')} *
|
{t('register.first_name')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<User className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -112,7 +112,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
|
|
||||||
<div className="md:col-span-1">
|
<div className="md:col-span-1">
|
||||||
<label htmlFor="last_name" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="last_name" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.last_name')} *
|
{t('register.last_name')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<User className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -137,7 +137,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
|
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.email')} *
|
{t('register.email')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -162,7 +162,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
|
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label htmlFor="address" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="address" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.address')} *
|
{t('register.address')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<MapPin className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<MapPin className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -187,7 +187,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
|
|
||||||
<div className="md:col-span-1">
|
<div className="md:col-span-1">
|
||||||
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.phone')} *
|
{t('register.phone')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -212,7 +212,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
|
|
||||||
<div className="md:col-span-1">
|
<div className="md:col-span-1">
|
||||||
<label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.username')} *
|
{t('register.username')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<User className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -237,7 +237,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
|
|
||||||
<div className="md:col-span-1">
|
<div className="md:col-span-1">
|
||||||
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.password')} *
|
{t('register.password')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -265,7 +265,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
|
|
||||||
<div className="md:col-span-1">
|
<div className="md:col-span-1">
|
||||||
<label htmlFor="password_confirmation" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="password_confirmation" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
{t('register.confirm_password')} *
|
{t('register.confirm_password')} {t('common:required')}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
@@ -326,7 +326,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
|
|||||||
<ul className="text-sm text-red-800 space-y-1">
|
<ul className="text-sm text-red-800 space-y-1">
|
||||||
{Object.entries(errors).map(([key, value]) => (
|
{Object.entries(errors).map(([key, value]) => (
|
||||||
<li key={key} className="flex items-start">
|
<li key={key} className="flex items-start">
|
||||||
<span className="font-medium">{key.replace('_', ' ')}:</span> {value}
|
<span className="font-medium">{t(`register.errors.${key}`)}:</span> {value}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ const Home: React.FC<Props> = ({ packages }) => {
|
|||||||
<div key={pkg.id} className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md text-center">
|
<div key={pkg.id} className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md text-center">
|
||||||
<h3 className="text-2xl font-bold mb-2">{pkg.name}</h3>
|
<h3 className="text-2xl font-bold mb-2">{pkg.name}</h3>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">{pkg.description}</p>
|
<p className="text-gray-600 dark:text-gray-300 mb-4">{pkg.description}</p>
|
||||||
<p className="text-3xl font-bold text-[#FFB6C1]">{pkg.price} {t('home.currency.euro')}</p>
|
<p className="text-3xl font-bold text-[#FFB6C1]">{pkg.price} {t('common.currency.euro')}</p>
|
||||||
<Link href={`/packages/${pkg.id}`} className="mt-4 inline-block bg-[#FFB6C1] text-white px-6 py-2 rounded-full hover:bg-pink-600">
|
<Link href={`/register?package_id=${pkg.id}`} className="mt-4 inline-block bg-[#FFB6C1] text-white px-6 py-2 rounded-full hover:bg-pink-600">
|
||||||
{t('home.view_details')}
|
{t('home.view_details')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,7 +144,7 @@ const Home: React.FC<Props> = ({ packages }) => {
|
|||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="name" className="block text-sm font-medium mb-2">
|
<label htmlFor="name" className="block text-sm font-medium mb-2">
|
||||||
{t('home.name_label')}
|
{t('home.name_label')} {t('common.required')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -157,7 +157,7 @@ const Home: React.FC<Props> = ({ packages }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="email" className="block text-sm font-medium mb-2">
|
<label htmlFor="email" className="block text-sm font-medium mb-2">
|
||||||
{t('home.email_label')}
|
{t('home.email_label')} {t('common.required')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
@@ -170,7 +170,7 @@ const Home: React.FC<Props> = ({ packages }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="message" className="block text-sm font-medium mb-2">
|
<label htmlFor="message" className="block text-sm font-medium mb-2">
|
||||||
{t('home.message_label')}
|
{t('home.message_label')} {t('common.required')}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="message"
|
id="message"
|
||||||
@@ -199,15 +199,15 @@ const Home: React.FC<Props> = ({ packages }) => {
|
|||||||
<div className="grid md:grid-cols-3 gap-8">
|
<div className="grid md:grid-cols-3 gap-8">
|
||||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
|
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
|
||||||
<p className="italic mb-4">"{t('home.testimonial1')}"</p>
|
<p className="italic mb-4">"{t('home.testimonial1')}"</p>
|
||||||
<p className="font-semibold">- Anna M.</p>
|
<p className="font-semibold">{t('common.testimonials.anna.name')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
|
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
|
||||||
<p className="italic mb-4">"{t('home.testimonial2')}"</p>
|
<p className="italic mb-4">"{t('home.testimonial2')}"</p>
|
||||||
<p className="font-semibold">- Max S.</p>
|
<p className="font-semibold">{t('common.testimonials.max.name')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
|
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
|
||||||
<p className="italic mb-4">"{t('home.testimonial3')}"</p>
|
<p className="italic mb-4">"{t('home.testimonial3')}"</p>
|
||||||
<p className="font-semibold">- Lisa K.</p>
|
<p className="font-semibold">{t('common.testimonials.lisa.name')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Head, Link, usePage } from '@inertiajs/react';
|
import { Head, Link, usePage } from '@inertiajs/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel"
|
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel"
|
||||||
@@ -43,10 +43,24 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
const { auth } = props as any;
|
const { auth } = props as any;
|
||||||
const { t } = useTranslation('marketing');
|
const { t } = useTranslation('marketing');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const packageId = urlParams.get('package_id');
|
||||||
|
if (packageId) {
|
||||||
|
const id = parseInt(packageId);
|
||||||
|
const pkg = [...endcustomerPackages, ...resellerPackages].find(p => p.id === id);
|
||||||
|
if (pkg) {
|
||||||
|
setSelectedPackage(pkg);
|
||||||
|
setOpen(true);
|
||||||
|
setCurrentStep('step1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [endcustomerPackages, resellerPackages]);
|
||||||
|
|
||||||
const testimonials = [
|
const testimonials = [
|
||||||
{ name: 'Anna M.', text: t('packages.testimonials.anna'), rating: 5 },
|
{ name: t('common.testimonials.anna.name'), text: t('packages.testimonials.anna'), rating: 5 },
|
||||||
{ name: 'Max B.', text: t('packages.testimonials.max'), rating: 5 },
|
{ name: t('common.testimonials.max.name'), text: t('packages.testimonials.max'), rating: 5 },
|
||||||
{ name: 'Lisa K.', text: t('packages.testimonials.lisa'), rating: 5 },
|
{ name: t('common.testimonials.lisa.name'), text: t('packages.testimonials.lisa'), rating: 5 },
|
||||||
];
|
];
|
||||||
|
|
||||||
const allPackages = [...endcustomerPackages, ...resellerPackages];
|
const allPackages = [...endcustomerPackages, ...resellerPackages];
|
||||||
@@ -190,7 +204,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<div key={pkg.id} className="text-center">
|
<div key={pkg.id} className="text-center">
|
||||||
<p className="font-bold">{pkg.name}</p>
|
<p className="font-bold">{pkg.name}</p>
|
||||||
<p>{pkg.price === 0 ? t('free') : `${pkg.price} ${t('currency.euro')}`}</p>
|
<p>{pkg.price === 0 ? t('packages.free') : `${pkg.price} ${t('common.currency.euro')}`}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -203,7 +217,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<div key={pkg.id} className="text-center">
|
<div key={pkg.id} className="text-center">
|
||||||
<p className="font-bold">{pkg.name}</p>
|
<p className="font-bold">{pkg.name}</p>
|
||||||
<p>{pkg.limits?.max_photos || t('unlimited')}</p>
|
<p>{pkg.limits?.max_photos || t('common.unlimited')}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -216,7 +230,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<div key={pkg.id} className="text-center">
|
<div key={pkg.id} className="text-center">
|
||||||
<p className="font-bold">{pkg.name}</p>
|
<p className="font-bold">{pkg.name}</p>
|
||||||
<p>{pkg.limits?.max_guests || t('unlimited')}</p>
|
<p>{pkg.limits?.max_guests || t('common.unlimited')}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -229,7 +243,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<div key={pkg.id} className="text-center">
|
<div key={pkg.id} className="text-center">
|
||||||
<p className="font-bold">{pkg.name}</p>
|
<p className="font-bold">{pkg.name}</p>
|
||||||
<p>{pkg.limits?.gallery_days || t('unlimited')}</p>
|
<p>{pkg.limits?.gallery_days || t('common.unlimited')}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -267,7 +281,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
<TableCell className="font-semibold">{t('packages.price')}</TableCell>
|
<TableCell className="font-semibold">{t('packages.price')}</TableCell>
|
||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<TableCell key={pkg.id} className="text-center">
|
<TableCell key={pkg.id} className="text-center">
|
||||||
{pkg.price === 0 ? t('free') : `${pkg.price} ${t('currency.euro')}`}
|
{pkg.price === 0 ? t('packages.free') : `${pkg.price} ${t('common.currency.euro')}`}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -275,7 +289,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
<TableCell className="font-semibold">{t('packages.max_photos_label')} {getFeatureIcon('max_photos')}</TableCell>
|
<TableCell className="font-semibold">{t('packages.max_photos_label')} {getFeatureIcon('max_photos')}</TableCell>
|
||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<TableCell key={pkg.id} className="text-center">
|
<TableCell key={pkg.id} className="text-center">
|
||||||
{pkg.limits?.max_photos || t('unlimited')}
|
{pkg.limits?.max_photos || t('common.unlimited')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -283,7 +297,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
<TableCell className="font-semibold">{t('packages.max_guests_label')} {getFeatureIcon('max_guests')}</TableCell>
|
<TableCell className="font-semibold">{t('packages.max_guests_label')} {getFeatureIcon('max_guests')}</TableCell>
|
||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<TableCell key={pkg.id} className="text-center">
|
<TableCell key={pkg.id} className="text-center">
|
||||||
{pkg.limits?.max_guests || t('unlimited')}
|
{pkg.limits?.max_guests || t('common.unlimited')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -291,7 +305,7 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
|||||||
<TableCell className="font-semibold">{t('packages.gallery_days_label')} {getFeatureIcon('gallery_days')}</TableCell>
|
<TableCell className="font-semibold">{t('packages.gallery_days_label')} {getFeatureIcon('gallery_days')}</TableCell>
|
||||||
{endcustomerPackages.map((pkg) => (
|
{endcustomerPackages.map((pkg) => (
|
||||||
<TableCell key={pkg.id} className="text-center">
|
<TableCell key={pkg.id} className="text-center">
|
||||||
{pkg.limits?.gallery_days || t('unlimited')}
|
{pkg.limits?.gallery_days || t('common.unlimited')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
20
resources/views/legal/datenschutz-partial.blade.php
Normal file
20
resources/views/legal/datenschutz-partial.blade.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<div class="prose prose-sm max-w-none">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">{{ __('legal.datenschutz') }}</h1>
|
||||||
|
<p class="mb-4">{{ __('legal.datenschutz_intro') }}</p>
|
||||||
|
<p class="mb-4">{{ __('legal.responsible') }}</p>
|
||||||
|
<p class="mb-4">{{ __('legal.data_collection') }}</p>
|
||||||
|
<h2 class="text-xl font-semibold mb-2">{{ __('legal.payments') }}</h2>
|
||||||
|
<p class="mb-4">{{ __('legal.payments_desc') }} <a href="https://stripe.com/de/privacy" target="_blank" rel="noopener noreferrer">{{ __('legal.stripe_privacy') }}</a> {{ __('legal.and') }} <a href="https://www.paypal.com/de/webapps/mpp/ua/privacy-full" target="_blank" rel="noopener noreferrer">{{ __('legal.paypal_privacy') }}</a>.</p>
|
||||||
|
<p class="mb-4">{{ __('legal.data_retention') }}</p>
|
||||||
|
<p class="mb-4">{{ __('legal.rights') }} <a href="{{ route('kontakt') }}">{{ __('legal.contact') }}</a>.</p>
|
||||||
|
<p class="mb-4">{{ __('legal.cookies') }}</p>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold mb-2">{{ __('legal.personal_data') }}</h2>
|
||||||
|
<p class="mb-4">{{ __('legal.personal_data_desc') }}</p>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold mb-2">{{ __('legal.account_deletion') }}</h2>
|
||||||
|
<p class="mb-4">{{ __('legal.account_deletion_desc') }}</p>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold mb-2">{{ __('legal.data_security') }}</h2>
|
||||||
|
<p class="mb-4">{{ __('legal.data_security_desc') }}</p>
|
||||||
|
</div>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@@ -14,15 +15,18 @@ Route::get('/lang/{locale}/{namespace}', function ($locale, $namespace) {
|
|||||||
$content = json_decode(file_get_contents($path), true);
|
$content = json_decode(file_get_contents($path), true);
|
||||||
Log::info('JSON loaded', ['keys' => array_keys($content ?? [])]);
|
Log::info('JSON loaded', ['keys' => array_keys($content ?? [])]);
|
||||||
return response()->json($content);
|
return response()->json($content);
|
||||||
})->where(['locale' => 'de|en', 'namespace' => 'marketing|auth']);
|
})->where(['locale' => 'de|en', 'namespace' => 'marketing|auth|common']);
|
||||||
|
|
||||||
Route::prefix('{locale?}')->where(['locale' => 'de|en'])->middleware('locale')->group(function () {
|
Route::prefix('{locale?}')->where(['locale' => 'de|en'])->middleware('locale')->group(function () {
|
||||||
Route::get('/', [\App\Http\Controllers\MarketingController::class, 'index'])->name('marketing');
|
Route::get('/', [\App\Http\Controllers\MarketingController::class, 'index'])->name('marketing');
|
||||||
Route::get('/packages', [\App\Http\Controllers\MarketingController::class, 'packagesIndex'])->name('packages');
|
Route::get('/packages', [\App\Http\Controllers\MarketingController::class, 'packagesIndex'])->name('packages');
|
||||||
|
Route::get('/packages/{id}', function ($id) {
|
||||||
|
return redirect("/packages?package_id={$id}");
|
||||||
|
})->where('id', '\d+')->name('packages.detail');
|
||||||
Route::get('/login', [\App\Http\Controllers\Auth\AuthenticatedSessionController::class, 'create'])->name('login');
|
Route::get('/login', [\App\Http\Controllers\Auth\AuthenticatedSessionController::class, 'create'])->name('login');
|
||||||
Route::post('/login', [\App\Http\Controllers\Auth\AuthenticatedSessionController::class, 'store'])->name('login.store');
|
Route::post('/login', [\App\Http\Controllers\Auth\AuthenticatedSessionController::class, 'store'])->name('login.store');
|
||||||
Route::get('/register/{package_id?}', [\App\Http\Controllers\Auth\MarketingRegisterController::class, 'create'])->name('register');
|
Route::get('/register', [\App\Http\Controllers\Auth\MarketingRegisterController::class, 'create'])->name('register');
|
||||||
Route::post('/register', [\App\Http\Controllers\Auth\MarketingRegisterController::class, 'store'])->name('register.store');
|
Route::post('/register', [\App\Http\Controllers\Auth\MarketingRegisterController::class, 'store'])->middleware('throttle:6,1')->name('register.store');
|
||||||
Route::post('/logout', [\App\Http\Controllers\Auth\AuthenticatedSessionController::class, 'destroy'])->name('logout');
|
Route::post('/logout', [\App\Http\Controllers\Auth\AuthenticatedSessionController::class, 'destroy'])->name('logout');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -32,8 +36,8 @@ Route::prefix('{locale?}')->where(['locale' => 'de|en'])->middleware('locale')->
|
|||||||
})->name('login.fallback');
|
})->name('login.fallback');
|
||||||
|
|
||||||
// Fallback for /register (redirect to default locale)
|
// Fallback for /register (redirect to default locale)
|
||||||
Route::get('/register', function () {
|
Route::get('/register', function (Request $request) {
|
||||||
return redirect('/de/register');
|
return redirect('/de/register' . $request->getQueryString());
|
||||||
})->name('register.fallback');
|
})->name('register.fallback');
|
||||||
|
|
||||||
// Fallback for /logout (redirect to default locale)
|
// Fallback for /logout (redirect to default locale)
|
||||||
|
|||||||
Reference in New Issue
Block a user