übergang auf pakete, integration von stripe und paypal, blog hinzugefügt.

This commit is contained in:
Codex Agent
2025-09-29 07:59:39 +02:00
parent 0a643c3e4d
commit e52a4005aa
83 changed files with 4284 additions and 629 deletions

View File

@@ -10,8 +10,8 @@ return new class extends Migration {
Schema::create('events', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->onDelete('cascade');
$table->string('name');
$table->text('description')->nullable();
$table->json('name');
$table->json('description')->nullable();
$table->dateTime('date');
$table->string('slug')->unique();
$table->string('location')->nullable();

View File

@@ -12,9 +12,9 @@ return new class extends Migration {
$table->foreignId('tenant_id')->constrained()->onDelete('cascade');
$table->unsignedBigInteger('emotion_id')->nullable();
$table->unsignedBigInteger('event_type_id')->nullable();
$table->string('title');
$table->text('description')->nullable();
$table->text('example_text')->nullable();
$table->json('title');
$table->json('description')->nullable();
$table->json('example_text')->nullable();
$table->dateTime('due_date')->nullable();
$table->boolean('is_completed')->default(false);
$table->enum('priority', ['low', 'medium', 'high', 'urgent'])->default('medium');

View File

@@ -72,7 +72,7 @@ return new class extends Migration
}
// Migrate tenant credits to tenant_packages (Free package)
DB::table('tenants')->where('event_credits_balance', '>', 0)->chunk(100, function ($tenants) {
DB::table('tenants')->where('event_credits_balance', '>', 0)->orderBy('id')->chunk(100, function ($tenants) {
foreach ($tenants as $tenant) {
$freePackageId = DB::table('packages')->where('name', 'Free/Test')->first()->id;
DB::table('tenant_packages')->insert([
@@ -106,7 +106,7 @@ return new class extends Migration
});
// Migrate event purchases to event_packages (if any existing events)
DB::table('events')->chunk(100, function ($events) {
DB::table('events')->orderBy('id')->chunk(100, function ($events) {
foreach ($events as $event) {
if ($event->tenant->event_credits_balance > 0) { // or check if event was created with credits
$freePackageId = DB::table('packages')->where('name', 'Free/Test')->first()->id;

View File

@@ -15,15 +15,37 @@ return new class extends Migration
Schema::dropIfExists('purchase_history');
Schema::dropIfExists('event_purchases');
Schema::table('tenants', function (Blueprint $table) {
$table->dropColumn([
'event_credits_balance',
'subscription_tier',
'subscription_expires_at',
'free_event_granted_at',
'total_revenue'
]);
});
if (Schema::hasTable('package_purchases')) {
Schema::table('package_purchases', function (Blueprint $table) {
$table->dropIndex(['tenant_id', 'purchased_at']);
});
}
if (Schema::hasColumn('tenants', 'event_credits_balance')) {
Schema::table('tenants', function (Blueprint $table) {
$table->dropColumn('event_credits_balance');
});
}
if (Schema::hasColumn('tenants', 'subscription_tier')) {
Schema::table('tenants', function (Blueprint $table) {
$table->dropColumn('subscription_tier');
});
}
if (Schema::hasColumn('tenants', 'subscription_expires_at')) {
Schema::table('tenants', function (Blueprint $table) {
$table->dropColumn('subscription_expires_at');
});
}
if (Schema::hasColumn('tenants', 'free_event_granted_at')) {
Schema::table('tenants', function (Blueprint $table) {
$table->dropColumn('free_event_granted_at');
});
}
if (Schema::hasColumn('tenants', 'total_revenue')) {
Schema::table('tenants', function (Blueprint $table) {
$table->dropColumn('total_revenue');
});
}
}
/**

View File

@@ -0,0 +1,31 @@
<?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->string('first_name')->nullable()->after('name');
$table->string('last_name')->nullable()->after('first_name');
$table->text('address')->nullable()->after('last_name');
$table->string('phone')->nullable()->after('address');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['first_name', 'last_name', 'address', 'phone']);
});
}
};

View File

@@ -0,0 +1,29 @@
<?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('tenants', function (Blueprint $table) {
$table->foreignId('user_id')->nullable()->constrained('users')->onDelete('cascade')->after('id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('tenants', function (Blueprint $table) {
$table->dropForeign(['user_id']);
$table->dropColumn('user_id');
});
}
};

View File

@@ -0,0 +1,30 @@
<?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('package_purchases', function (Blueprint $table) {
$table->dropForeign(['tenant_id']);
$table->foreignId('tenant_id')->constrained()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('package_purchases', function (Blueprint $table) {
$table->dropForeign(['tenant_id']);
$table->foreignId('tenant_id')->nullable()->constrained()->change();
});
}
};

View File

@@ -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->unique('username');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropUnique(['username']);
});
}
};

View File

@@ -1,4 +1,4 @@
<?php
<?php
namespace Database\Seeders;
@@ -14,6 +14,7 @@ class DatabaseSeeder extends Seeder
// Seed basic system data
$this->call([
LegalPagesSeeder::class,
PackageSeeder::class,
]);
// Seed core demo data for frontend previews

View File

@@ -1,4 +1,4 @@
<?php
<?php
namespace Database\Seeders;

View File

@@ -11,7 +11,10 @@ class DemoEventSeeder extends Seeder
{
$type = EventType::where('slug','wedding')->first();
if(!$type){ return; }
$demoTenant = \App\Models\Tenant::where('slug', 'demo')->first();
if (!$demoTenant) { return; }
Event::updateOrCreate(['slug'=>'demo-wedding-2025'], [
'tenant_id' => $demoTenant->id,
'name' => ['de'=>'Demo Hochzeit 2025','en'=>'Demo Wedding 2025'],
'description' => ['de'=>'Demo-Event','en'=>'Demo event'],
'date' => now()->addMonths(3)->toDateString(),

View File

@@ -4,6 +4,7 @@ namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Package;
use App\Enums\PackageType;
class PackageSeeder extends Seeder
{
@@ -15,124 +16,98 @@ class PackageSeeder extends Seeder
// Endcustomer Packages
Package::create([
'name' => 'Free / Test',
'type' => 'endcustomer',
'type' => PackageType::ENDCUSTOMER,
'price' => 0.00,
'max_photos' => 30,
'max_guests' => 10,
'gallery_days' => 3,
'max_guests' => 50,
'gallery_days' => 7,
'max_tasks' => 5,
'watermark_allowed' => false,
'watermark_allowed' => true,
'branding_allowed' => false,
'features' => json_encode([
'basic_uploads' => true,
'limited_sharing' => true,
'no_branding' => true,
]),
'description' => 'Ideal für kleine Test-Events oder erste Erfahrungen.',
]);
Package::create([
'name' => 'Starter',
'type' => 'endcustomer',
'price' => 19.00,
'max_photos' => 300,
'max_guests' => 50,
'gallery_days' => 14,
'max_tasks' => 20,
'type' => PackageType::ENDCUSTOMER,
'price' => 29.00,
'max_photos' => 200,
'max_guests' => 100,
'gallery_days' => 30,
'max_tasks' => 10,
'watermark_allowed' => true,
'branding_allowed' => false,
'features' => json_encode([
'extended_gallery' => true,
'guest_sharing' => true,
'basic_analytics' => true,
'basic_uploads' => true,
'unlimited_sharing' => true,
'no_watermark' => true,
'custom_tasks' => true,
]),
'description' => 'Perfekt für kleine Events wie Geburtstage oder Firmenfeiern.',
]);
Package::create([
'name' => 'Pro',
'type' => 'endcustomer',
'price' => 49.00,
'type' => PackageType::ENDCUSTOMER,
'price' => 79.00,
'max_photos' => 1000,
'max_guests' => 200,
'gallery_days' => 30,
'max_tasks' => 50,
'max_guests' => 500,
'gallery_days' => 90,
'max_tasks' => 20,
'watermark_allowed' => false,
'branding_allowed' => false,
'features' => json_encode([
'basic_uploads' => true,
'unlimited_sharing' => true,
'no_watermark' => true,
'custom_tasks' => true,
'advanced_analytics' => true,
'priority_support' => true,
]),
]);
// Reseller Packages
Package::create([
'name' => 'S (Small Reseller)',
'type' => PackageType::RESELLER,
'price' => 199.00,
'max_photos' => 500, // per event limit
'max_guests' => null, // unlimited
'gallery_days' => null,
'max_tasks' => null, // unlimited
'watermark_allowed' => true,
'branding_allowed' => true,
'max_events_per_year' => 5,
'expires_after' => now()->addYear(),
'features' => json_encode([
'unlimited_sharing' => true,
'advanced_analytics' => true,
'reseller_dashboard' => true,
'custom_branding' => true,
'priority_support' => true,
]),
'description' => 'Für große Events wie Hochzeiten oder Konferenzen.',
]);
// Reseller Packages (jährliche Subscriptions)
Package::create([
'name' => 'Reseller S',
'type' => 'reseller',
'price' => 149.00,
'max_events_per_year' => 5,
'max_photos' => null, // Kein globales Limit, pro Event
'max_guests' => null,
'name' => 'M (Medium Reseller)',
'type' => PackageType::RESELLER,
'price' => 399.00,
'max_photos' => 1000, // per event limit
'max_guests' => null, // unlimited
'gallery_days' => null,
'max_tasks' => null,
'max_tasks' => null, // unlimited
'watermark_allowed' => true,
'branding_allowed' => true,
'expires_after' => now()->addYear(), // Jährlich
'features' => json_encode([
'event_management' => true,
'reseller_dashboard' => true,
'bulk_event_creation' => true,
'5_events_included' => true,
]),
'description' => 'Einstieg für kleine Agenturen: 5 Events pro Jahr.',
]);
Package::create([
'name' => 'Reseller M',
'type' => 'reseller',
'price' => 299.00,
'max_events_per_year' => 15,
'max_photos' => null,
'max_guests' => null,
'gallery_days' => null,
'max_tasks' => null,
'watermark_allowed' => true,
'branding_allowed' => true,
'expires_after' => now()->addYear(),
'features' => json_encode([
'event_management' => true,
'reseller_dashboard' => true,
'bulk_event_creation' => true,
'custom_branding' => true,
'priority_support' => true,
'advanced_reporting' => true,
'15_events_included' => true,
]),
'description' => 'Für wachsende Agenturen: 15 Events pro Jahr.',
]);
Package::create([
'name' => 'Reseller L',
'type' => 'reseller',
'price' => 499.00,
'max_events_per_year' => 30,
'max_photos' => null,
'max_guests' => null,
'gallery_days' => null,
'max_tasks' => null,
'watermark_allowed' => true,
'branding_allowed' => true,
'expires_after' => now()->addYear(),
'features' => json_encode([
'event_management' => true,
'reseller_dashboard' => true,
'bulk_event_creation' => true,
'priority_support' => true,
'custom_integration' => true,
'30_events_included' => true,
]),
'description' => 'Für große Agenturen: 30 Events pro Jahr mit Premium-Features.',
]);
}
}
}