From 3570f442f5611a6fbbd5c792669f4aeda4bca9dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20G=C3=BCnther?=
Date: Sat, 31 Jan 2026 20:07:41 +0100
Subject: [PATCH 01/47] Basic tenant structure
---
.gitignore | 1 +
Makefile | 5 +
app/Enumerations/CostUnitType.php | 21 ++++
app/Enumerations/EatingHabit.php | 22 ++++
app/Enumerations/FirstAidPermission.php | 22 ++++
app/Enumerations/SwimmingPermission.php | 26 +++++
app/Enumerations/UserRole.php | 17 +++
app/Http/Controllers/Controller.php | 8 --
app/Installer/DevelopmentDataSeeder.php | 43 +++++++
app/Installer/ProductionDataSeeder.php | 71 ++++++++++++
.../Middleware/HandleInertiaRequests.php | 2 +-
app/Middleware/IdentifyTenant.php | 24 ++++
app/Models/Tenant.php | 39 +++++++
app/Models/User.php | 28 ++++-
app/Providers/AppServiceProvider.php | 8 +-
app/Providers/InertiaProvider.php | 26 +++++
app/Providers/TenantUserProvider.php | 23 ++++
app/Scopes/CommonModel.php | 10 ++
app/Scopes/InstancedModel.php | 13 +++
app/Scopes/SiteScope.php | 14 +++
bootstrap/app.php | 4 +-
bootstrap/providers.php | 1 +
composer.json | 2 +-
composer.lock | 20 ++--
config/auth.php | 4 +-
database/factories/UserFactory.php | 44 --------
.../0001_01_01_000000_create_users_table.php | 49 --------
.../2026_01_30_140001_create_tenants.php | 32 ++++++
.../2026_01_30_140002_create_users_table.php | 106 ++++++++++++++++++
.../2026_01_30_140010_create_cost_units.php | 43 +++++++
database/seeders/DatabaseSeeder.php | 15 ++-
docker-compose.yaml => docker-compose.dev | 4 +-
resources/js/app.js | 15 +--
routes/api.php | 15 +++
vite.config.js | 1 +
35 files changed, 634 insertions(+), 144 deletions(-)
create mode 100644 app/Enumerations/CostUnitType.php
create mode 100644 app/Enumerations/EatingHabit.php
create mode 100644 app/Enumerations/FirstAidPermission.php
create mode 100644 app/Enumerations/SwimmingPermission.php
create mode 100644 app/Enumerations/UserRole.php
delete mode 100644 app/Http/Controllers/Controller.php
create mode 100644 app/Installer/DevelopmentDataSeeder.php
create mode 100644 app/Installer/ProductionDataSeeder.php
rename app/{Http => }/Middleware/HandleInertiaRequests.php (96%)
create mode 100644 app/Middleware/IdentifyTenant.php
create mode 100644 app/Models/Tenant.php
create mode 100644 app/Providers/InertiaProvider.php
create mode 100644 app/Providers/TenantUserProvider.php
create mode 100644 app/Scopes/CommonModel.php
create mode 100644 app/Scopes/InstancedModel.php
create mode 100644 app/Scopes/SiteScope.php
delete mode 100644 database/factories/UserFactory.php
delete mode 100644 database/migrations/0001_01_01_000000_create_users_table.php
create mode 100644 database/migrations/2026_01_30_140001_create_tenants.php
create mode 100644 database/migrations/2026_01_30_140002_create_users_table.php
create mode 100644 database/migrations/2026_01_30_140010_create_cost_units.php
rename docker-compose.yaml => docker-compose.dev (92%)
create mode 100644 routes/api.php
diff --git a/.gitignore b/.gitignore
index b71b1ea..656cf1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,4 @@
Homestead.json
Homestead.yaml
Thumbs.db
+/docker-compose.yaml
diff --git a/Makefile b/Makefile
index fbaa382..8baf640 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,11 @@
FRONTEND_DIR ?= .
+ setup:
+ rm -f docker-compose.yaml
+ cp docker-compose.dev docker-compose.yaml
+ docker-compose up -d
+
frontend:
@cd $(FRONTEND_DIR) && \
export QT_QPA_PLATFORM=offscreen && \
diff --git a/app/Enumerations/CostUnitType.php b/app/Enumerations/CostUnitType.php
new file mode 100644
index 0000000..855409b
--- /dev/null
+++ b/app/Enumerations/CostUnitType.php
@@ -0,0 +1,21 @@
+installTenants();
+ $this->installUsers();
+ }
+
+
+
+ private function installTenants() {
+ Tenant::create([
+ 'slug' => 'wilde-moehre',
+ 'local_group_name' => 'Stamm Wilde Möhre',
+ 'url' => 'wilde-moehre.mareike.local',
+ 'account_iban' => 'DE12345678901234567890',
+ 'email' => 'test@example1.com',
+ 'city' => 'Halle (Saale)',
+ 'postcode' => '06120',
+ 'is_active_local_group' => true,
+ 'has_active_instance' => true,
+ ]);
+ }
+
+ private function installUsers() {
+ User::create([
+ 'firstname' => 'Development',
+ 'lastname' => 'User',
+ 'user_role' => UserRole::USER_ROLE_ADMIN,
+ 'tenant' => 'lv',
+ 'email' => 'th.guenther@saale-mail.de',
+ 'password' => bcrypt('development'),
+ 'local_group_id' => 1,
+ 'username' => 'development',
+ ]);
+ }
+}
diff --git a/app/Installer/ProductionDataSeeder.php b/app/Installer/ProductionDataSeeder.php
new file mode 100644
index 0000000..dcebdd5
--- /dev/null
+++ b/app/Installer/ProductionDataSeeder.php
@@ -0,0 +1,71 @@
+installUserRoles();
+ $this->installCostUnitTypes();
+ $this->installSwimmingPermissions();
+ $this->installEatingHabits();
+ $this->installFirstAidPermissions();
+ $this->installTenants();
+ }
+
+ private function installUserRoles() {
+ UserRole::create(['name' => 'Administrator*in', 'slug' => UserRole::USER_ROLE_ADMIN]);
+ UserRole::create(['name' => 'Vorstandsmitglied', 'slug' => UserRole::USER_ROLE_GROUP_LEADER]);
+ UserRole::create(['name' => 'Benutzer*in', 'slug' => UserRole::USER_ROLE_USER]);
+ }
+
+ private function installSwimmingPermissions() {
+ SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED]);
+ SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann NICHT schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_LIMITED]);
+ SwimmingPermission::create(['name' => 'Mein Kind darf nicht baden', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_DENIED]);
+ }
+
+ private function installEatingHabits() {
+ EatingHabit::create(['name' => 'Vegan', 'slug' => EatingHabit::EATING_HABIT_VEGAN]);
+ EatingHabit::create(['name' => 'Vegetarisch', 'slug' => EatingHabit::EATING_HABIT_VEGETARIAN]);
+ EatingHabit::create(['name' => 'Omnivor', 'slug' => EatingHabit::EATING_HABIT_OMNIVOR]);
+
+ }
+ private function installFirstAidPermissions() {
+ FirstAidPermission::create([
+ 'name' => 'Zugestimmt',
+ 'description' => 'Ich STIMME der Anwendung von erweiteren Erste-Hilfe-Maßnahmen an meinem Kind explizit ZU.',
+ 'slug' => FirstAidPermission::FIRST_AID_PERMISSION_ALLOWED]);
+
+ FirstAidPermission::create([
+ 'name' => 'Verweigert',
+ 'description' => 'Ich LEHNE die Anwendung von erweiteren Erste-Hilfe-Maßnahmen an meinem Kind explizit AB.',
+ 'slug' => FirstAidPermission::FIRST_AID_PERMISSION_DENIED]);
+ }
+
+ private function installCostUnitTypes() {
+ CostUnitType::create(['slug' => CostUnitType::COST_UNIT_TYPE_EVENT, 'name' => 'Veranstaltung']);
+ CostUnitType::create(['slug' => CostUnitType::COST_UNIT_TYPE_RUNNING_JOB, 'name' => 'Laufende Tätigkeit']);
+
+ }
+
+ private function installTenants() {
+ Tenant::create([
+ 'slug' => 'lv',
+ 'local_group_name' => 'Landesunmittelbare Mitglieder',
+ 'url' => 'mareike.local',
+ 'account_iban' => 'DE12345678901234567890',
+ 'email' => 'test@example.com',
+ 'city' => 'Lommatzsch',
+ 'postcode' => '01623',
+ 'is_active_local_group' => true,
+ 'has_active_instance' => true,
+ ]);
+ }
+}
diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Middleware/HandleInertiaRequests.php
similarity index 96%
rename from app/Http/Middleware/HandleInertiaRequests.php
rename to app/Middleware/HandleInertiaRequests.php
index c19ce18..e20fd21 100644
--- a/app/Http/Middleware/HandleInertiaRequests.php
+++ b/app/Middleware/HandleInertiaRequests.php
@@ -1,6 +1,6 @@
getHost();
+ $tenant = Tenant::where(['url' => $host, 'has_active_instance' => true])->first();
+
+ if (! $tenant) {
+ throw new NotFoundHttpException('Tenant not found');
+ }
+
+ app()->instance('tenant', $tenant);
+ return $next($request);
+ }
+}
diff --git a/app/Models/Tenant.php b/app/Models/Tenant.php
new file mode 100644
index 0000000..35bed28
--- /dev/null
+++ b/app/Models/Tenant.php
@@ -0,0 +1,39 @@
+ */
- use HasFactory, Notifiable;
+ use Notifiable;
/**
* The attributes that are mass assignable.
@@ -18,8 +15,28 @@ class User extends Authenticatable
* @var list
*/
protected $fillable = [
- 'name',
+ 'tenant_id',
+ 'user_role',
+ 'username',
+ 'firstname',
+ 'nickname',
+ 'lastname',
+ 'local_group_id',
+ 'membership_id',
+ 'address_1',
+ 'address_2',
+ 'postcode',
+ 'city',
'email',
+ 'phone',
+ 'birthday',
+ 'medications',
+ 'allergies',
+ 'intolerances',
+ 'eating_habits',
+ 'swimming_permission',
+ 'first_aid_permission',
+ 'bank_account_iban',
'password',
];
@@ -29,7 +46,6 @@ class User extends Authenticatable
* @var list
*/
protected $hidden = [
- 'password',
'remember_token',
];
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 452e6b6..7a8c57f 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,6 +2,7 @@
namespace App\Providers;
+use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@@ -19,6 +20,11 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(): void
{
- //
+ Auth::provider('tenant-users', function ($app, array $config) {
+ return new TenantUserProvider(
+ $app['hash'],
+ $config['model']
+ );
+ });
}
}
diff --git a/app/Providers/InertiaProvider.php b/app/Providers/InertiaProvider.php
new file mode 100644
index 0000000..e4cf233
--- /dev/null
+++ b/app/Providers/InertiaProvider.php
@@ -0,0 +1,26 @@
+vueFile = $vueFile;
+ $this->props = $props;
+
+ }
+
+
+ public function render() : Response {
+ return Inertia::render(
+ str_replace('/', '/Views/', $this->vueFile),
+ $this->props
+ );
+ }
+}
diff --git a/app/Providers/TenantUserProvider.php b/app/Providers/TenantUserProvider.php
new file mode 100644
index 0000000..51702b9
--- /dev/null
+++ b/app/Providers/TenantUserProvider.php
@@ -0,0 +1,23 @@
+createModel()->newQuery();
+
+ foreach ($credentials as $key => $value) {
+ if (! str_contains($key, 'password')) {
+ $query->where($key, $value);
+ }
+ }
+
+ $query->where('tenant', app('tenant')->slug);
+
+ return $query->first();
+ }
+}
diff --git a/app/Scopes/CommonModel.php b/app/Scopes/CommonModel.php
new file mode 100644
index 0000000..b937fc2
--- /dev/null
+++ b/app/Scopes/CommonModel.php
@@ -0,0 +1,10 @@
+where($model->getTable() . '.tenant', app('tenant')->slug);
+ }
+}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index c183276..2b5ff81 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -1,5 +1,6 @@
withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
+ api: __DIR__.'/../routes/api.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
- //
+ $middleware->append(IdentifyTenant::class);
})
->withExceptions(function (Exceptions $exceptions): void {
//
diff --git a/bootstrap/providers.php b/bootstrap/providers.php
index 38b258d..c93fc13 100644
--- a/bootstrap/providers.php
+++ b/bootstrap/providers.php
@@ -2,4 +2,5 @@
return [
App\Providers\AppServiceProvider::class,
+
];
diff --git a/composer.json b/composer.json
index 22311da..e7da8ea 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
- "php": "^8.2",
+ "php": "^8.5",
"inertiajs/inertia-laravel": "^2.0",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1"
diff --git a/composer.lock b/composer.lock
index 452325b..13f363a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "0e42fe1a7066e7a110e956ae26703d94",
+ "content-hash": "587caff9de06de75c1e22cceac366334",
"packages": [
{
"name": "brick/math",
@@ -2194,16 +2194,16 @@
},
{
"name": "nesbot/carbon",
- "version": "3.11.0",
+ "version": "3.11.1",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
- "reference": "bdb375400dcd162624531666db4799b36b64e4a1"
+ "reference": "f438fcc98f92babee98381d399c65336f3a3827f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1",
- "reference": "bdb375400dcd162624531666db4799b36b64e4a1",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/f438fcc98f92babee98381d399c65336f3a3827f",
+ "reference": "f438fcc98f92babee98381d399c65336f3a3827f",
"shasum": ""
},
"require": {
@@ -2227,7 +2227,7 @@
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.22",
"phpunit/phpunit": "^10.5.53",
- "squizlabs/php_codesniffer": "^3.13.4"
+ "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0"
},
"bin": [
"bin/carbon"
@@ -2270,14 +2270,14 @@
}
],
"description": "An API extension for DateTime that supports 281 different languages.",
- "homepage": "https://carbon.nesbot.com",
+ "homepage": "https://carbonphp.github.io/carbon/",
"keywords": [
"date",
"datetime",
"time"
],
"support": {
- "docs": "https://carbon.nesbot.com/docs",
+ "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html",
"issues": "https://github.com/CarbonPHP/carbon/issues",
"source": "https://github.com/CarbonPHP/carbon"
},
@@ -2295,7 +2295,7 @@
"type": "tidelift"
}
],
- "time": "2025-12-02T21:04:28+00:00"
+ "time": "2026-01-29T09:26:29+00:00"
},
{
"name": "nette/schema",
@@ -8436,7 +8436,7 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
- "php": "^8.2"
+ "php": "^8.5"
},
"platform-dev": {},
"plugin-api-version": "2.9.0"
diff --git a/config/auth.php b/config/auth.php
index 7d1eb0d..e6e4ace 100644
--- a/config/auth.php
+++ b/config/auth.php
@@ -61,8 +61,8 @@ return [
'providers' => [
'users' => [
- 'driver' => 'eloquent',
- 'model' => env('AUTH_MODEL', App\Models\User::class),
+ 'driver' => 'tenant-users',
+ 'model' => App\Models\User::class,
],
// 'users' => [
diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php
deleted file mode 100644
index 584104c..0000000
--- a/database/factories/UserFactory.php
+++ /dev/null
@@ -1,44 +0,0 @@
-
- */
-class UserFactory extends Factory
-{
- /**
- * The current password being used by the factory.
- */
- protected static ?string $password;
-
- /**
- * Define the model's default state.
- *
- * @return array
- */
- public function definition(): array
- {
- return [
- 'name' => fake()->name(),
- 'email' => fake()->unique()->safeEmail(),
- 'email_verified_at' => now(),
- 'password' => static::$password ??= Hash::make('password'),
- 'remember_token' => Str::random(10),
- ];
- }
-
- /**
- * Indicate that the model's email address should be unverified.
- */
- public function unverified(): static
- {
- return $this->state(fn (array $attributes) => [
- 'email_verified_at' => null,
- ]);
- }
-}
diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php
deleted file mode 100644
index 05fb5d9..0000000
--- a/database/migrations/0001_01_01_000000_create_users_table.php
+++ /dev/null
@@ -1,49 +0,0 @@
-id();
- $table->string('name');
- $table->string('email')->unique();
- $table->timestamp('email_verified_at')->nullable();
- $table->string('password');
- $table->rememberToken();
- $table->timestamps();
- });
-
- Schema::create('password_reset_tokens', function (Blueprint $table) {
- $table->string('email')->primary();
- $table->string('token');
- $table->timestamp('created_at')->nullable();
- });
-
- Schema::create('sessions', function (Blueprint $table) {
- $table->string('id')->primary();
- $table->foreignId('user_id')->nullable()->index();
- $table->string('ip_address', 45)->nullable();
- $table->text('user_agent')->nullable();
- $table->longText('payload');
- $table->integer('last_activity')->index();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('users');
- Schema::dropIfExists('password_reset_tokens');
- Schema::dropIfExists('sessions');
- }
-};
diff --git a/database/migrations/2026_01_30_140001_create_tenants.php b/database/migrations/2026_01_30_140001_create_tenants.php
new file mode 100644
index 0000000..ddd4f7e
--- /dev/null
+++ b/database/migrations/2026_01_30_140001_create_tenants.php
@@ -0,0 +1,32 @@
+id();
+ $table->string('slug')->unique();
+ $table->string('local_group_name');
+ $table->string('email');
+ $table->string('url');
+ $table->string('account_iban');
+ $table->string('city');
+ $table->string('postcode');
+ $table->string('gdpr_text')-> nullable();
+ $table->string('impress_text')->nullable();
+ $table->string('url_participation_rules')->nullable();
+ $table->boolean('has_active_instance')->default(true);
+ $table->boolean('is_active_local_group')->default(true);
+
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('tenants');
+ }
+};
diff --git a/database/migrations/2026_01_30_140002_create_users_table.php b/database/migrations/2026_01_30_140002_create_users_table.php
new file mode 100644
index 0000000..5833b76
--- /dev/null
+++ b/database/migrations/2026_01_30_140002_create_users_table.php
@@ -0,0 +1,106 @@
+string('slug')->unique()->primary();
+ $table->string('name');
+ $table->timestamps();
+ });
+
+ Schema::create('eating_habits', function (Blueprint $table) {
+ $table->string('slug')->unique()->primary();
+ $table->string('name');
+ $table->timestamps();
+ });
+
+ Schema::create('swimming_permissions', function (Blueprint $table) {
+ $table->string('slug')->unique()->primary();
+ $table->string('name');
+ $table->timestamps();
+ });
+
+ Schema::create('first_aid_permissions', function (Blueprint $table) {
+ $table->string('slug')->unique()->primary();
+ $table->string('name');
+ $table->string('description');
+ $table->timestamps();
+ });
+
+ Schema::create('users', function (Blueprint $table) {
+ $table->id();
+ $table->string('tenant');
+ $table->string('user_role');
+ $table->string('username')->unique();
+ $table->string('password')->nullable();
+ $table->string('firstname');
+ $table->string('nickname')->nullable();
+ $table->string('lastname');
+ $table->foreignId('local_group_id')->references('id')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
+ $table->string('membership_id')->nullable();
+ $table->string('address_1')->nullable();
+ $table->string('address_2')->nullable();
+ $table->string('postcode')->nullable();
+ $table->string('city')->nullable();
+ $table->string('email')->nullable();
+ $table->string('phone')->nullable();
+ $table->date('birthday')->nullable();
+ $table->string('medications')->nullable();
+ $table->string('allergies')->nullable();
+ $table->string('intolerances')->nullable();
+ $table->string('eating_habits')->nullable();
+ $table->string('swimming_permission')->nullable();
+ $table->string('first_aid_permission')->nullable();
+ $table->string('bank_account_iban')->nullable();
+ $table->string('activation_token')->nullable();
+ $table->boolean('activated')->default(false);
+
+ $table->foreign('tenant')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
+ $table->foreign('user_role')->references('slug')->on('user_roles')->cascadeOnDelete()->cascadeOnUpdate();
+ $table->foreign('swimming_permission')->references('slug')->on('swimming_permissions')->cascadeOnDelete()->cascadeOnUpdate();
+ $table->foreign('eating_habits')->references('slug')->on('eating_habits')->cascadeOnDelete()->cascadeOnUpdate();
+ $table->foreign('first_aid_permission')->references('slug')->on('first_aid_permissions')->cascadeOnDelete()->cascadeOnUpdate();
+
+ $table->rememberToken();
+ $table->timestamps();
+ });
+
+ Schema::create('password_reset_tokens', function (Blueprint $table) {
+ $table->string('email')->primary();
+ $table->string('token');
+ $table->timestamp('created_at')->nullable();
+ });
+
+ Schema::create('sessions', function (Blueprint $table) {
+ $table->string('id')->primary();
+ $table->foreignId('user_id')->nullable()->index();
+ $table->string('ip_address', 45)->nullable();
+ $table->text('user_agent')->nullable();
+ $table->longText('payload');
+ $table->integer('last_activity')->index();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('users');
+ Schema::dropIfExists('password_reset_tokens');
+ Schema::dropIfExists('sessions');
+ Schema::dropIfExists('user_roles');
+ Schema::dropIfExists('eating_habits');
+ Schema::dropIfExists('swimming_permissions');
+ Schema::dropIfExists('first_aid_permissions');
+ }
+};
diff --git a/database/migrations/2026_01_30_140010_create_cost_units.php b/database/migrations/2026_01_30_140010_create_cost_units.php
new file mode 100644
index 0000000..8856e89
--- /dev/null
+++ b/database/migrations/2026_01_30_140010_create_cost_units.php
@@ -0,0 +1,43 @@
+string('slug')->primary();
+ $table->string('name');
+ $table->timestamps();
+ });
+
+
+
+ Schema::create('cost_units', function (Blueprint $table) {
+ $table->id();
+ $table->string('tenant');
+ $table->string('name');
+ $table->string('type');
+ $table->dateTime('billing_deadline');
+ $table->float('distance_allowance');
+ $table->boolean('mail_on_new')->default(true);
+ $table->boolean('allow_new')->default(true);
+ $table->boolean('archived')->default(false);
+ $table->string('treasurers');
+
+ $table->foreign('tenant')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
+ $table->foreign('type')->references('slug')->on('cost_unit_types')->cascadeOnDelete()->cascadeOnUpdate();
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('cost_units');
+ Schema::dropIfExists('cost_unit_types');
+ }
+};
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 6b901f8..a8a01af 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -2,7 +2,9 @@
namespace Database\Seeders;
-use App\Models\User;
+use App\Installer\DevelopmentDataSeeder;
+use App\Installer\ProductionData;
+use App\Installer\ProductionDataSeeder;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
@@ -15,11 +17,12 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
- // User::factory(10)->create();
+ $productionSeeeder = new ProductionDataSeeder();
+ $productionSeeeder->execute();
- User::factory()->create([
- 'name' => 'Test User',
- 'email' => 'test@example.com',
- ]);
+ if (str_ends_with(env('APP_URL'), 'mareike.local')) {
+ $deveopmentDataSeeder = new DevelopmentDataSeeder();
+ $deveopmentDataSeeder->execute();
+ }
}
}
diff --git a/docker-compose.yaml b/docker-compose.dev
similarity index 92%
rename from docker-compose.yaml
rename to docker-compose.dev
index 95a230a..6a7134d 100644
--- a/docker-compose.yaml
+++ b/docker-compose.dev
@@ -18,12 +18,12 @@ services:
labels:
- "traefik.enable=true"
- - "traefik.http.routers.mareike.rule=Host(`mareike.local`)"
+ - "traefik.http.routers.mareike.rule=Host(`mareike.local`) || Host(`admin.mareike.local`) || Host(`wilde-moehre.mareike.local`)"
- "traefik.http.routers.mareike.entrypoints=websecure"
- "traefik.http.routers.mareike.tls=true"
- "traefik.http.services.mareike.loadbalancer.server.port=80"
- - "traefik.http.routers.mareike-http.rule=Host(`mareike.local`)"
+ - "traefik.http.routers.mareike-http.rule=Host(`mareike.local`) || Host(`admin.mareike.local`) || Host(`wilde-moehre.mareike.local`)"
- "traefik.http.routers.mareike-http.entrypoints=web"
- "traefik.http.routers.mareike-http.middlewares=redirect-to-https"
diff --git a/resources/js/app.js b/resources/js/app.js
index 104602f..05e5ddc 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -5,43 +5,32 @@ import { InertiaProgress } from '@inertiajs/progress'
import Vue3Toastify, { toast } from 'vue3-toastify'
import 'vue3-toastify/dist/index.css'
-// Optional: Lade-Balken für Inertia
InertiaProgress.init()
-// Inertia App starten
createInertiaApp({
- // Alle Pages in app/Views/Pages/**/*.vue werden automatisch importiert
resolve: name => {
- // Vite scannt die Pages dynamisch
- const pages = import.meta.glob('@views/**/*.vue')
+ const pages = import.meta.glob('@domains/**/*.vue')
- // Suche nach der richtigen Page-Datei
const key = Object.keys(pages).find(k =>
k.endsWith(`/${name}.vue`) || k.endsWith(`/${name}/index.vue`)
)
if (!key) throw new Error(`Page not found: ${name}`)
- // Unterstützt sowohl
+
+
+
+
+
+ Meine Anmeldungen
+
+
+
+ Meine Abrechnungen
+
+
+
+
+
+
diff --git a/app/Domains/UserManagement/Controllers/LogOutController.php b/app/Domains/UserManagement/Controllers/LogOutController.php
new file mode 100644
index 0000000..3b4456b
--- /dev/null
+++ b/app/Domains/UserManagement/Controllers/LogOutController.php
@@ -0,0 +1,21 @@
+session()->invalidate();
+
+ // CSRF-Token regenerieren (für Sicherheit)
+ $request->session()->regenerateToken();
+
+ // Redirect z.B. zur Login-Seite
+ return redirect()->intended('/')->with('status', 'Erfolgreich abgemeldet!');
+ }
+}
diff --git a/app/Domains/UserManagement/Controllers/LoginController.php b/app/Domains/UserManagement/Controllers/LoginController.php
new file mode 100644
index 0000000..640cbde
--- /dev/null
+++ b/app/Domains/UserManagement/Controllers/LoginController.php
@@ -0,0 +1,51 @@
+session()->has('errors')) {
+ $errors = $request->session()->get('errors')->getBag('default')->getMessages();
+ }
+
+
+ $inertiaProvider = new InertiaProvider('UserManagement/Login', ['errors' => $errors, 'appName' => app('tenant')->name]);
+ return $inertiaProvider->render();
+ }
+
+ public function doLogin(Request $request)
+ {
+
+ $credentials = $request->validate([
+ 'username' => ['required', 'string'],
+ 'password' => ['required'],
+ ],
+ [
+ 'username.required' => 'Bitte gib deinen Anmeldenamen ein.',
+ 'username.string' => 'Der Anmeldename muss eine E-Mail-Adresse sein.',
+ 'password.required' => 'Bitte gib dein Passwort ein.',
+ ]);
+
+ #$credentials = ['username' => 'development', 'password' => 'development'];
+
+ if (!Auth::attempt($credentials)) {
+ return back()->withErrors([
+ 'username' => 'Diese Zugangsdaten sind ungültig.',
+ ]);
+ }
+
+ $request->session()->regenerate();
+ $user = Auth::user();
+
+
+# dd($user->firstname . ' ' . $user->lastname);
+
+ return redirect()->intended('/');
+ }
+}
diff --git a/app/Domains/UserManagement/Views/Login.vue b/app/Domains/UserManagement/Views/Login.vue
new file mode 100644
index 0000000..0616102
--- /dev/null
+++ b/app/Domains/UserManagement/Views/Login.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/Http/Controllers/TestRenderInertiaProvider.php b/app/Http/Controllers/TestRenderInertiaProvider.php
new file mode 100644
index 0000000..8e30566
--- /dev/null
+++ b/app/Http/Controllers/TestRenderInertiaProvider.php
@@ -0,0 +1,13 @@
+ app('tenant')->name]);
+ return $inertiaProvider->render();
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
index 04308d2..f93e9d6 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -38,6 +38,7 @@ class User extends Authenticatable
'first_aid_permission',
'bank_account_iban',
'password',
+ 'active',
];
/**
diff --git a/app/Providers/AuthCheckProvider.php b/app/Providers/AuthCheckProvider.php
new file mode 100644
index 0000000..c93b018
--- /dev/null
+++ b/app/Providers/AuthCheckProvider.php
@@ -0,0 +1,23 @@
+check()) {
+ return false;
+ }
+
+ $user = auth()->user();
+ $tenant = app('tenant');
+ return $user->active && $tenant->slug === $user->tenant;
+ }
+
+ public function getUserRole() : ?string {
+ if (!$this->checkLoggedIn()) {
+ return null;
+ }
+
+ return auth()->user()->user_role;
+ }
+}
diff --git a/app/Providers/InertiaProvider.php b/app/Providers/InertiaProvider.php
index e4cf233..49be165 100644
--- a/app/Providers/InertiaProvider.php
+++ b/app/Providers/InertiaProvider.php
@@ -2,6 +2,7 @@
namespace App\Providers;
+use App\Models\User;
use Inertia\Inertia;
use Inertia\Response;
@@ -10,17 +11,43 @@ final class InertiaProvider
private string $vueFile;
private array $props;
+ private ?User $user;
+
public function __construct(string $vueFile, array $props) {
+ $this->user = auth()->user();
$this->vueFile = $vueFile;
$this->props = $props;
-
}
-
public function render() : Response {
+ $this->props['navbar'] = $this->generateNavbar();
+ $this->props['tenant'] = app('tenant')->local_group_name;
+ $this->props['user'] = $this->user;
+ $this->props['currentPath'] = request()->path();
+
return Inertia::render(
str_replace('/', '/Views/', $this->vueFile),
$this->props
);
}
+
+ private function generateNavbar() : array {
+ $navigation = [
+ 'personal' => [],
+ 'common' => [],
+ 'costunits' => [],
+ 'events' => [],
+ ];
+
+ $navigation['personal'][] = ['url' => '/', 'display' => 'Home'];
+ if (null !== $this->user) {
+ $navigation['personal'][] = ['url' => '/personal-data', 'display' => 'Meine Daten'];
+ $navigation['personal'][] = ['url' => '/messages', 'display' => 'Meine Nachrichten'];
+ }
+
+ $navigation['common'][] = ['url' => '/capture-invoice', 'display' => 'Neue Abrechnung'];
+ $navigation['common'][] = ['url' => '/available-events', 'display' => 'Verfügbare Veranstaltungen'];
+
+ return $navigation;
+ }
}
diff --git a/app/Providers/TenantUserProvider.php b/app/Providers/TenantUserProvider.php
index 51702b9..c15b9cc 100644
--- a/app/Providers/TenantUserProvider.php
+++ b/app/Providers/TenantUserProvider.php
@@ -16,7 +16,11 @@ class TenantUserProvider extends EloquentUserProvider
}
}
- $query->where('tenant', app('tenant')->slug);
+ $query->where([
+ 'tenant' => app('tenant')->slug,
+ 'active' => true
+
+ ]);
return $query->first();
}
diff --git a/app/Scopes/CommonController.php b/app/Scopes/CommonController.php
new file mode 100644
index 0000000..18f4c94
--- /dev/null
+++ b/app/Scopes/CommonController.php
@@ -0,0 +1,12 @@
+checkLoggedIn();
+ }
+}
diff --git a/app/Views/Components/Icon.vue b/app/Views/Components/Icon.vue
new file mode 100644
index 0000000..24996a4
--- /dev/null
+++ b/app/Views/Components/Icon.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/app/Views/Components/ShadowedBox.vue b/app/Views/Components/ShadowedBox.vue
new file mode 100644
index 0000000..e81fa41
--- /dev/null
+++ b/app/Views/Components/ShadowedBox.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/Views/Partials/GlobalWidgets/GlobalWidgets.vue b/app/Views/Partials/GlobalWidgets/GlobalWidgets.vue
new file mode 100644
index 0000000..268200f
--- /dev/null
+++ b/app/Views/Partials/GlobalWidgets/GlobalWidgets.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+ Widget 1
+
+
+
+ Widget 2
+
+
+ Widget 3
+
+
+ Widget 4
+
+
+
+
+
diff --git a/config/session.php b/config/session.php
index 5b541b7..cac7a5b 100644
--- a/config/session.php
+++ b/config/session.php
@@ -32,7 +32,7 @@ return [
|
*/
- 'lifetime' => (int) env('SESSION_LIFETIME', 120),
+ 'lifetime' => (int) env('SESSION_LIFETIME', 43200), // 30 days
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
diff --git a/database/migrations/2026_01_30_140002_create_users_table.php b/database/migrations/2026_01_30_140002_create_users_table.php
index 5833b76..b745d24 100644
--- a/database/migrations/2026_01_30_140002_create_users_table.php
+++ b/database/migrations/2026_01_30_140002_create_users_table.php
@@ -62,7 +62,7 @@ return new class extends Migration
$table->string('first_aid_permission')->nullable();
$table->string('bank_account_iban')->nullable();
$table->string('activation_token')->nullable();
- $table->boolean('activated')->default(false);
+ $table->boolean('active')->default(false);
$table->foreign('tenant')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
$table->foreign('user_role')->references('slug')->on('user_roles')->cascadeOnDelete()->cascadeOnUpdate();
diff --git a/docker-compose.dev b/docker-compose.dev
index 6a7134d..ea842a7 100644
--- a/docker-compose.dev
+++ b/docker-compose.dev
@@ -48,6 +48,11 @@ services:
sh -c "
npm install &&
npm install vue3-toastify && npm install @inertiajs/progress && npm install @inertiajs/progress &&
+ npm install @fortawesome/fontawesome-svg-core &&
+ npm install @fortawesome/free-solid-svg-icons &&
+ npm install @fortawesome/vue-fontawesome@latest &&
+
+
while true; do
npm run build
echo 'Vite Dev-Server beendet. Neustart in 3 Sekunden...'
diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf
index c16a27b..52e8b16 100644
--- a/docker/nginx/default.conf
+++ b/docker/nginx/default.conf
@@ -29,6 +29,17 @@ server {
fastcgi_pass mareike-app:9000; # Containername vom PHP-FPM
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- }
+ }
+
+ # In der Nginx-Konfiguration von mareike.local
+ location /build/assets/ {
+ # Erlaubt explizit deine Subdomain
+ add_header 'Access-Control-Allow-Origin' "$http_origin" always;
+ add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
+ add_header 'Access-Control-Allow-Headers' 'Content-Type, X-Inertia' always;
+
+ # Falls du alle Subdomains von mareike.local erlauben willst:
+ #
+ }
}
diff --git a/package-lock.json b/package-lock.json
index bf4ce3c..7cc4a6e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,6 +5,9 @@
"packages": {
"": {
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^7.1.0",
+ "@fortawesome/free-solid-svg-icons": "^7.1.0",
+ "@fortawesome/vue-fontawesome": "^3.1.3",
"@inertiajs/progress": "^0.2.7",
"@inertiajs/vue3": "^2.3.12",
"vue": "^3.5.27",
@@ -13,10 +16,12 @@
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
"@vitejs/plugin-vue": "^6.0.3",
+ "autoprefixer": "^10.4.24",
"axios": "^1.11.0",
"concurrently": "^9.0.1",
"laravel-vite-plugin": "^2.0.0",
- "tailwindcss": "^4.0.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
"vite": "^7.0.7"
}
},
@@ -508,6 +513,49 @@
"node": ">=18"
}
},
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.1.0.tgz",
+ "integrity": "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz",
+ "integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==",
+ "license": "MIT",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "7.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.1.0.tgz",
+ "integrity": "sha512-Udu3K7SzAo9N013qt7qmm22/wo2hADdheXtBfxFTecp+ogsc0caQNRKEb7pkvvagUGOpG9wJC1ViH6WXs8oXIA==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "7.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/vue-fontawesome": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.1.3.tgz",
+ "integrity": "sha512-OHHUTLPEzdwP8kcYIzhioUdUOjZ4zzmi+midwa4bqscza4OJCOvTKJEHkXNz8PgZ23kWci1HkKVX0bm8f9t9gQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7",
+ "vue": ">= 3.0.0 < 4"
+ }
+ },
"node_modules/@inertiajs/core": {
"version": "2.3.12",
"resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.3.12.tgz",
@@ -1421,6 +1469,43 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
+ "node_modules/autoprefixer": {
+ "version": "10.4.24",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz",
+ "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001766",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
"node_modules/axios": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
@@ -1432,6 +1517,50 @@
"proxy-from-env": "^1.1.0"
}
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.19",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
+ "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -1461,6 +1590,27 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001766",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz",
+ "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -1612,6 +1762,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.283",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz",
+ "integrity": "sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -1802,6 +1959,20 @@
"node": ">= 6"
}
},
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2315,6 +2486,13 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/nprogress": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
@@ -2380,6 +2558,13 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -2659,6 +2844,37 @@
"dev": true,
"license": "0BSD"
},
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
"node_modules/vite": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
diff --git a/package.json b/package.json
index 457f442..b9b69b2 100644
--- a/package.json
+++ b/package.json
@@ -10,13 +10,18 @@
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
"@vitejs/plugin-vue": "^6.0.3",
+ "autoprefixer": "^10.4.24",
"axios": "^1.11.0",
"concurrently": "^9.0.1",
"laravel-vite-plugin": "^2.0.0",
- "tailwindcss": "^4.0.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
"vite": "^7.0.7"
},
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^7.1.0",
+ "@fortawesome/free-solid-svg-icons": "^7.1.0",
+ "@fortawesome/vue-fontawesome": "^3.1.3",
"@inertiajs/progress": "^0.2.7",
"@inertiajs/vue3": "^2.3.12",
"vue": "^3.5.27",
diff --git a/public/css/app.css b/public/css/app.css
new file mode 100644
index 0000000..fc27ddc
--- /dev/null
+++ b/public/css/app.css
@@ -0,0 +1,125 @@
+@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
+@source '../../storage/framework/views/*.php';
+@source '../**/*.blade.php';
+@source '../**/*.js';
+
+html {
+ background-color: #FAFAFB !important;
+}
+
+.content {
+ padding: 50px 0px;
+}
+
+.main {
+ margin: 0 auto;
+ box-shadow: 20px 54px 15px rgba(0, 0, 0, 0.1);
+ border-radius: 0 10px 0 0;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.header {
+ height: 80px; /* Höhe anpassen */
+ align-items: center;
+ justify-content: space-between;
+ width: calc(100% - 40px);
+
+}
+
+.header .anonymous-actions {
+ position: relative;
+ right: calc(-100% + 330px);
+ width: 350px;
+ overflow: hidden;
+ border-radius: 50px 0 0 50px;
+ background-color: #FAFAFB !important;
+ text-align: right;
+ padding: 10px;
+ top: -75px;
+}
+
+.header .user-info {
+ position: relative;
+ right: calc(-100% + 275px);
+ width: 295px;
+ overflow: hidden;
+ border-radius: 50px 0 0 50px;
+ background-color: #FAFAFB !important;
+ text-align: right;
+ padding: 10px;
+ top: -75px;
+}
+
+.header .left-side {
+ height: 75px;
+ background: #ffffff;
+ align-content: center;
+ padding: 0 50px;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+ width: 100%;
+ margin-top: -5px;
+
+}
+
+.flexbox {
+ display: flex;
+ background-color: #FAFAFB;
+ height: 100%;
+ padding: 0;
+ gap: 1px;
+ margin: 0;
+
+
+}
+
+/* Layout */
+.app-layout {
+ display: flex;
+ height: 95vh;
+ margin: 20px;
+ background: #f0f2f5;
+ font-family: sans-serif;
+}
+
+.sidebar {
+ flex-basis:275px;
+ box-shadow: 2px 0 5px rgba(0,0,0,0.1);
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background-color: #ffffff !important;
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.logo img {
+ width: 135px !important;
+ height: 70px !important;
+}
+
+.footer {
+ height: 40px;
+ background: #666666;
+ border-top: 1px solid #ddd;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ color: #ffffff;
+}
+
+th {
+ text-align: left;
+ padding-right: 1em;
+}
+
+th:after {
+ content: ":";
+}
diff --git a/public/css/elements.css b/public/css/elements.css
new file mode 100644
index 0000000..56e0e27
--- /dev/null
+++ b/public/css/elements.css
@@ -0,0 +1,47 @@
+/* Toaster */
+.toaster {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ background: #4caf50;
+ color: #fff;
+ padding: 12px 20px;
+ border-radius: 6px;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
+}
+
+input[type="text"],
+input[type="email"],
+input[type="password"] {
+ width: 100%;
+ font-size: 13pt;
+ padding: 5px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ color: #656363;
+}
+
+input[type="text"]:focus,
+input[type="email"]:focus,
+input[type="password"]:focus {
+ outline: none;
+ border-color: #1d4899;
+}
+
+
+table {
+ width: 100%;
+}
+
+button, input[type="submit"] {
+ cursor: pointer;
+ background-color: #ffffff;
+ border: 1px solid #809dd5 !important;
+ padding: 10px 20px;
+ font-weight: bold;
+}
+
+button:hover, input[type="submit"]:hover {
+ background-color: #1d4899;
+ color: #ffffff;
+}
diff --git a/public/images/logo.png b/public/images/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..38a9503806d78c7c6dd01e66728e7af3b7d155c3
GIT binary patch
literal 135440
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