diff --git a/app/Domains/Dashboard/Controllers/DashboardController.php b/app/Domains/Dashboard/Controllers/DashboardController.php
new file mode 100644
index 0000000..c37456b
--- /dev/null
+++ b/app/Domains/Dashboard/Controllers/DashboardController.php
@@ -0,0 +1,32 @@
+checkAuth()) {
+ return $this->renderForLoggedInUser($request);
+ }
+
+ return redirect()->intended('/login');
+
+ dd('U');
+ return $this->renderForGuest($request);
+
+ }
+
+ private function renderForLoggedInUser(Request $request) {
+ $authCheckProvider = new AuthCheckProvider;
+ $inertiaProvider = new InertiaProvider('Dashboard/Dashboard', ['appName' => app('tenant')->name]);
+ return $inertiaProvider->render();
+ }
+
+ private function renderForGuest(Request $request) {
+ }
+}
diff --git a/app/Domains/Dashboard/Views/Dashboard.vue b/app/Domains/Dashboard/Views/Dashboard.vue
new file mode 100644
index 0000000..5d0b9d7
--- /dev/null
+++ b/app/Domains/Dashboard/Views/Dashboard.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ 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 0000000..38a9503
Binary files /dev/null and b/public/images/logo.png differ
diff --git a/resources/css/app.css b/resources/css/app.css
deleted file mode 100644
index 3e6abea..0000000
--- a/resources/css/app.css
+++ /dev/null
@@ -1,11 +0,0 @@
-@import 'tailwindcss';
-
-@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
-@source '../../storage/framework/views/*.php';
-@source '../**/*.blade.php';
-@source '../**/*.js';
-
-@theme {
- --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
- 'Segoe UI Symbol', 'Noto Color Emoji';
-}
diff --git a/resources/js/app.js b/resources/js/app.js
index 05e5ddc..4ac41e8 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -5,6 +5,15 @@ import { InertiaProgress } from '@inertiajs/progress'
import Vue3Toastify, { toast } from 'vue3-toastify'
import 'vue3-toastify/dist/index.css'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
+
+// Icons importieren
+import { faUser, faTrash, faCheck } from '@fortawesome/free-solid-svg-icons'
+
+library.add(faUser, faTrash, faCheck)
+
+
InertiaProgress.init()
createInertiaApp({
@@ -24,12 +33,28 @@ createInertiaApp({
const vueApp = createApp({ render: () => h(App, props) })
vueApp.use(plugin)
+
+ vueApp.component('font-awesome-icon', FontAwesomeIcon)
+
vueApp.use(Vue3Toastify, {
- autoClose: 3000,
- position: 'top-right',
+ autoClose: 10000,
+ position: 'bottom-right',
pauseOnHover: true,
+ hideProgressBar: false, // Progressbar anzeigen
+ toastDefaults: {
+ success: {
+ style: {background: '#4caf50', color: '#fff'}, // grün
+ progressStyle: {background: '#2e7d32', height: '4px'},
+ },
+ error: {
+ style: {background: '#f44336', color: '#fff'}, // rot
+ progressStyle: {background: '#c62828', height: '4px'},
+ },
+ },
})
+
+
vueApp.config.globalProperties.$toast = toast
vueApp.mount(el)
},
diff --git a/resources/js/layouts/AppLayout.vue b/resources/js/layouts/AppLayout.vue
new file mode 100644
index 0000000..e2c85af
--- /dev/null
+++ b/resources/js/layouts/AppLayout.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ flash.message }}
+
+
+
+
+
+
diff --git a/resources/views/app.blade.php b/resources/views/app.blade.php
index ec72952..d63bb32 100644
--- a/resources/views/app.blade.php
+++ b/resources/views/app.blade.php
@@ -1,10 +1,19 @@
+
+
+
+
+
+
+
+
@vite('resources/js/app.js')
+
@inertiaHead
diff --git a/routes/web.php b/routes/web.php
index 5b73edb..c85a4b6 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,16 +1,39 @@
group(function () {
+ Route::get('/', DashboardController::class);
+
+
+
+ Route::middleware(['auth'])->group(function () {
+
+ Route::get('/messages', fn () => inertia('Messages'));
+ Route::post('/logout', [LogoutController::class, 'logout']);
+
+ });
+
+
+
+
+
+
+
+ route::get('/logout', LogOutController::class);
+ route::post('/login', [LoginController::class, 'doLogin']);
+ route::get('/login', [LoginController::class, 'loginForm']);
+
+
+ Route::get('/messages', [TestRenderInertiaProvider::class, 'index']);
});
-Route::get('/inertia', function () {
- return Inertia::render('Pages/Home', [
- 'appName' => config('app.name'),
- ]);
-});
+
diff --git a/vite.config.js b/vite.config.js
index 7ae0d0b..08e87b7 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,6 +4,7 @@ import laravel from 'laravel-vite-plugin'
import path from 'path'
export default defineConfig({
+ base: '',
plugins: [
laravel({
input: 'resources/js/app.js',
@@ -18,4 +19,9 @@ export default defineConfig({
'@domains': path.resolve(__dirname, 'app/Domains'),
},
},
+ server: {
+ host: true, // Dev-Server auch über Subdomains erreichbar
+ strictPort: true, // verhindert zufällige Portwechsel
+ cors: true, // erlaubt Dev-HMR über Subdomains
+ },
})