Implement multi-tenancy support with OAuth2 authentication for tenant admins, Stripe integration for event purchases and credits ledger, new Filament resources for event purchases, updated API routes and middleware for tenant isolation and token guarding, added factories/seeders/migrations for new models (Tenant, EventPurchase, OAuth entities, etc.), enhanced tests, and documentation updates. Removed outdated DemoAchievementsSeeder.
This commit is contained in:
63
database/factories/EventFactory.php
Normal file
63
database/factories/EventFactory.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Event;
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class EventFactory extends Factory
|
||||
{
|
||||
protected $model = Event::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
$name = $this->faker->words(3, true);
|
||||
$slug = Str::slug($name);
|
||||
|
||||
return [
|
||||
'tenant_id' => Tenant::factory(),
|
||||
'name' => $name,
|
||||
'slug' => $slug,
|
||||
'description' => $this->faker->paragraph(),
|
||||
'date' => $this->faker->dateTimeBetween('now', '+6 months'),
|
||||
'location' => $this->faker->address(),
|
||||
'max_participants' => $this->faker->numberBetween(50, 500),
|
||||
'is_active' => true,
|
||||
'join_link_enabled' => true,
|
||||
'photo_upload_enabled' => true,
|
||||
'task_checklist_enabled' => true,
|
||||
];
|
||||
}
|
||||
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function withTasks(int $count = 3): static
|
||||
{
|
||||
return $this->afterCreating(function (Event $event) use ($count) {
|
||||
$event->tasks()->attach(
|
||||
\App\Models\Task::factory($count)->create(['tenant_id' => $event->tenant_id])
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public function past(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'date' => $this->faker->dateTimeBetween('-1 month', 'now'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function upcoming(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'date' => $this->faker->dateTimeBetween('now', '+1 month'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
46
database/factories/TaskCollectionFactory.php
Normal file
46
database/factories/TaskCollectionFactory.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\TaskCollection;
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class TaskCollectionFactory extends Factory
|
||||
{
|
||||
protected $model = TaskCollection::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
$categories = ['Allgemein', 'Vorbereitung', 'Event-Tag', 'Aufräumen', 'Follow-up'];
|
||||
$colors = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6'];
|
||||
|
||||
return [
|
||||
'tenant_id' => Tenant::factory(),
|
||||
'name' => $this->faker->randomElement($categories),
|
||||
'description' => $this->faker->sentence(),
|
||||
'is_default' => $this->faker->boolean(20),
|
||||
'position' => $this->faker->numberBetween(1, 10),
|
||||
];
|
||||
}
|
||||
|
||||
public function withTasks(int $count = 3): static
|
||||
{
|
||||
return $this->afterCreating(function (TaskCollection $collection) use ($count) {
|
||||
\App\Models\Task::factory($count)
|
||||
->create(['tenant_id' => $collection->tenant_id])
|
||||
->each(function ($task) use ($collection) {
|
||||
$task->taskCollection()->associate($collection);
|
||||
$task->save();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public function default(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_default' => true,
|
||||
'position' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
56
database/factories/TaskFactory.php
Normal file
56
database/factories/TaskFactory.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskCollection;
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class TaskFactory extends Factory
|
||||
{
|
||||
protected $model = Task::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'tenant_id' => Tenant::factory(),
|
||||
'title' => $this->faker->sentence(4),
|
||||
'description' => $this->faker->paragraph(),
|
||||
'due_date' => $this->faker->dateTimeBetween('now', '+1 month'),
|
||||
'is_completed' => $this->faker->boolean(20), // 20% chance completed
|
||||
'collection_id' => null,
|
||||
];
|
||||
}
|
||||
|
||||
public function completed(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_completed' => true,
|
||||
'completed_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function urgent(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'priority' => 'urgent',
|
||||
'due_date' => $this->faker->dateTimeBetween('now', '+3 days'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function withCollection(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'collection_id' => TaskCollection::factory(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function assignedToEvent(): static
|
||||
{
|
||||
return $this->afterCreating(function (Task $task) {
|
||||
$event = \App\Models\Event::factory()->create(['tenant_id' => $task->tenant_id]);
|
||||
$task->assignedEvents()->attach($event);
|
||||
});
|
||||
}
|
||||
}
|
||||
66
database/factories/TenantFactory.php
Normal file
66
database/factories/TenantFactory.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TenantFactory extends Factory
|
||||
{
|
||||
protected $model = Tenant::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
$name = $this->faker->company();
|
||||
$slug = Str::slug($name);
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'slug' => $slug,
|
||||
'contact_email' => $this->faker->companyEmail(),
|
||||
'event_credits_balance' => $this->faker->numberBetween(1, 20),
|
||||
'subscription_tier' => $this->faker->randomElement(['free', 'starter', 'pro']),
|
||||
'subscription_expires_at' => $this->faker->dateTimeBetween('now', '+1 year'),
|
||||
'is_active' => true,
|
||||
'is_suspended' => false,
|
||||
'settings' => json_encode([
|
||||
'branding' => [
|
||||
'logo_url' => null,
|
||||
'primary_color' => '#3B82F6',
|
||||
'secondary_color' => '#1F2937',
|
||||
'font_family' => 'Inter, sans-serif',
|
||||
],
|
||||
'features' => [
|
||||
'photo_likes_enabled' => true,
|
||||
'event_checklist' => true,
|
||||
'custom_domain' => false,
|
||||
'advanced_analytics' => false,
|
||||
],
|
||||
'custom_domain' => null,
|
||||
]),
|
||||
'settings_updated_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
public function suspended(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_suspended' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function withLowCredits(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'event_credits_balance' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user