Basic design created
This commit is contained in:
32
app/Domains/Dashboard/Controllers/DashboardController.php
Normal file
32
app/Domains/Dashboard/Controllers/DashboardController.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\AuthCheckProvider;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class DashboardController extends CommonController {
|
||||||
|
public function __invoke(Request $request) {
|
||||||
|
if ($this->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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Domains/Dashboard/Views/Dashboard.vue
Normal file
42
app/Domains/Dashboard/Views/Dashboard.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
navbar: Object,
|
||||||
|
tenant: String,
|
||||||
|
user: Object,
|
||||||
|
currentPath: String,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='Dashboard' :user="props.user" :navbar="props.navbar" :tenant="props.tenant" :currentPath="props.currentPath">
|
||||||
|
<diV class="dashboard-widget-container">
|
||||||
|
<shadowed-box class="dashboard-widget-box" style="width: 60%;">
|
||||||
|
Meine Anmeldungen
|
||||||
|
</shadowed-box>
|
||||||
|
|
||||||
|
<shadowed-box class="dashboard-widget-box">
|
||||||
|
Meine Abrechnungen
|
||||||
|
</shadowed-box>
|
||||||
|
</diV>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dashboard-widget-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-widget-box {
|
||||||
|
flex-grow: 1; display: inline-block;
|
||||||
|
height: 150px;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
21
app/Domains/UserManagement/Controllers/LogOutController.php
Normal file
21
app/Domains/UserManagement/Controllers/LogOutController.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class LogOutController {
|
||||||
|
public function __invoke(Request $request) {
|
||||||
|
Auth::logout();
|
||||||
|
|
||||||
|
// Session invalidieren
|
||||||
|
$request->session()->invalidate();
|
||||||
|
|
||||||
|
// CSRF-Token regenerieren (für Sicherheit)
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
|
// Redirect z.B. zur Login-Seite
|
||||||
|
return redirect()->intended('/')->with('status', 'Erfolgreich abgemeldet!');
|
||||||
|
}
|
||||||
|
}
|
||||||
51
app/Domains/UserManagement/Controllers/LoginController.php
Normal file
51
app/Domains/UserManagement/Controllers/LoginController.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class LoginController {
|
||||||
|
public function loginForm(Request $request) {
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
if ($request->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('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
64
app/Domains/UserManagement/Views/Login.vue
Normal file
64
app/Domains/UserManagement/Views/Login.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
|
import {onMounted, ref} from 'vue'
|
||||||
|
import { toast } from 'vue3-toastify'
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
navbar: Object,
|
||||||
|
tenant: String,
|
||||||
|
user: Object,
|
||||||
|
currentPath: String,
|
||||||
|
errors: Object,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (undefined !== props.errors && undefined !== props.errors.username) {
|
||||||
|
toast.error(props.errors.username[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//console.log(props.errors.password[0])
|
||||||
|
const username = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='Anmelden' :user="props.user" :navbar="props.navbar" :tenant="props.tenant" :currentPath="props.currentPath">
|
||||||
|
<form method="POST" action="/login">
|
||||||
|
<input type="hidden" name="_token" :value="csrfToken" />
|
||||||
|
<shadowed-box style="width: 50%; margin: 150px auto; padding: 20px;">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Anmeldename</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="username" id="username"></input>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>Passwort</th>
|
||||||
|
<td><input type="password" name="password" id="password"></input></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<input type="submit" value="Anmelden" style="margin-top: 20px;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</shadowed-box>
|
||||||
|
</form>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
th {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
13
app/Http/Controllers/TestRenderInertiaProvider.php
Normal file
13
app/Http/Controllers/TestRenderInertiaProvider.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
|
||||||
|
class TestRenderInertiaProvider
|
||||||
|
{
|
||||||
|
public function index() {
|
||||||
|
$inertiaProvider = new InertiaProvider('Invoice/CreateInvoice', ['appName' => app('tenant')->name]);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ class User extends Authenticatable
|
|||||||
'first_aid_permission',
|
'first_aid_permission',
|
||||||
'bank_account_iban',
|
'bank_account_iban',
|
||||||
'password',
|
'password',
|
||||||
|
'active',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
23
app/Providers/AuthCheckProvider.php
Normal file
23
app/Providers/AuthCheckProvider.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
class AuthCheckProvider {
|
||||||
|
public function checkLoggedIn() : bool {
|
||||||
|
if (!auth()->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use Inertia\Response;
|
use Inertia\Response;
|
||||||
|
|
||||||
@@ -10,17 +11,43 @@ final class InertiaProvider
|
|||||||
private string $vueFile;
|
private string $vueFile;
|
||||||
private array $props;
|
private array $props;
|
||||||
|
|
||||||
|
private ?User $user;
|
||||||
|
|
||||||
public function __construct(string $vueFile, array $props) {
|
public function __construct(string $vueFile, array $props) {
|
||||||
|
$this->user = auth()->user();
|
||||||
$this->vueFile = $vueFile;
|
$this->vueFile = $vueFile;
|
||||||
$this->props = $props;
|
$this->props = $props;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function render() : Response {
|
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(
|
return Inertia::render(
|
||||||
str_replace('/', '/Views/', $this->vueFile),
|
str_replace('/', '/Views/', $this->vueFile),
|
||||||
$this->props
|
$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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
return $query->first();
|
||||||
}
|
}
|
||||||
|
|||||||
12
app/Scopes/CommonController.php
Normal file
12
app/Scopes/CommonController.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Scopes;
|
||||||
|
|
||||||
|
use App\Providers\AuthCheckProvider;
|
||||||
|
|
||||||
|
abstract class CommonController {
|
||||||
|
protected function checkAuth() {
|
||||||
|
$authCheckProvider = new AuthCheckProvider;
|
||||||
|
return $authCheckProvider->checkLoggedIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Views/Components/Icon.vue
Normal file
17
app/Views/Components/Icon.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup>
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import * as SolidIcons from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
name: { type: String, required: true },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (SolidIcons[`fa${props.name.charAt(0).toUpperCase()}${props.name.slice(1)}`]) {
|
||||||
|
library.add(SolidIcons[`fa${props.name.charAt(0).toUpperCase()}${props.name.slice(1)}`])
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<font-awesome-icon :icon="name" />
|
||||||
|
</template>
|
||||||
23
app/Views/Components/ShadowedBox.vue
Normal file
23
app/Views/Components/ShadowedBox.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
style: { type: String},
|
||||||
|
class: { type: String},
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(props.style)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="shadowed-box" :style=props.style :class=props.class>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.shadowed-box {
|
||||||
|
box-shadow: 2px 2px 5px #c0c0c0;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
41
app/Views/Partials/GlobalWidgets/GlobalWidgets.vue
Normal file
41
app/Views/Partials/GlobalWidgets/GlobalWidgets.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
import ShadowedBox from "../../Components/ShadowedBox.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<diV class="widget-container">
|
||||||
|
<shadowed-box class="widget-box">
|
||||||
|
Widget 1
|
||||||
|
</shadowed-box>
|
||||||
|
|
||||||
|
<shadowed-box class="widget-box">
|
||||||
|
Widget 2
|
||||||
|
</shadowed-box>
|
||||||
|
<shadowed-box class="widget-box">
|
||||||
|
Widget 3
|
||||||
|
</shadowed-box>
|
||||||
|
<shadowed-box class="widget-box">
|
||||||
|
Widget 4
|
||||||
|
</shadowed-box>
|
||||||
|
</diV>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.widget-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-box {
|
||||||
|
flex-grow: 1; display: inline-block;
|
||||||
|
width: calc(25% - 40px);
|
||||||
|
height: 150px;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -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),
|
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ return new class extends Migration
|
|||||||
$table->string('first_aid_permission')->nullable();
|
$table->string('first_aid_permission')->nullable();
|
||||||
$table->string('bank_account_iban')->nullable();
|
$table->string('bank_account_iban')->nullable();
|
||||||
$table->string('activation_token')->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('tenant')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
$table->foreign('user_role')->references('slug')->on('user_roles')->cascadeOnDelete()->cascadeOnUpdate();
|
$table->foreign('user_role')->references('slug')->on('user_roles')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ services:
|
|||||||
sh -c "
|
sh -c "
|
||||||
npm install &&
|
npm install &&
|
||||||
npm install vue3-toastify && npm install @inertiajs/progress && npm install @inertiajs/progress &&
|
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
|
while true; do
|
||||||
npm run build
|
npm run build
|
||||||
echo 'Vite Dev-Server beendet. Neustart in 3 Sekunden...'
|
echo 'Vite Dev-Server beendet. Neustart in 3 Sekunden...'
|
||||||
|
|||||||
@@ -30,5 +30,16 @@ server {
|
|||||||
fastcgi_index index.php;
|
fastcgi_index index.php;
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
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:
|
||||||
|
#
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
218
package-lock.json
generated
218
package-lock.json
generated
@@ -5,6 +5,9 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"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/progress": "^0.2.7",
|
||||||
"@inertiajs/vue3": "^2.3.12",
|
"@inertiajs/vue3": "^2.3.12",
|
||||||
"vue": "^3.5.27",
|
"vue": "^3.5.27",
|
||||||
@@ -13,10 +16,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
|
"autoprefixer": "^10.4.24",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"laravel-vite-plugin": "^2.0.0",
|
"laravel-vite-plugin": "^2.0.0",
|
||||||
"tailwindcss": "^4.0.0",
|
"postcss": "^8.5.6",
|
||||||
|
"tailwindcss": "^4.1.18",
|
||||||
"vite": "^7.0.7"
|
"vite": "^7.0.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -508,6 +513,49 @@
|
|||||||
"node": ">=18"
|
"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": {
|
"node_modules/@inertiajs/core": {
|
||||||
"version": "2.3.12",
|
"version": "2.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.3.12.tgz",
|
"resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.3.12.tgz",
|
||||||
@@ -1421,6 +1469,43 @@
|
|||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/axios": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
|
||||||
@@ -1432,6 +1517,50 @@
|
|||||||
"proxy-from-env": "^1.1.0"
|
"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": {
|
"node_modules/call-bind-apply-helpers": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"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"
|
"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": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -1612,6 +1762,13 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
@@ -1802,6 +1959,20 @@
|
|||||||
"node": ">= 6"
|
"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": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"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": "^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": {
|
"node_modules/nprogress": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
||||||
@@ -2380,6 +2558,13 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"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": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
@@ -2659,6 +2844,37 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "0BSD"
|
"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": {
|
"node_modules/vite": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||||
|
|||||||
@@ -10,13 +10,18 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
|
"autoprefixer": "^10.4.24",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"laravel-vite-plugin": "^2.0.0",
|
"laravel-vite-plugin": "^2.0.0",
|
||||||
"tailwindcss": "^4.0.0",
|
"postcss": "^8.5.6",
|
||||||
|
"tailwindcss": "^4.1.18",
|
||||||
"vite": "^7.0.7"
|
"vite": "^7.0.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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/progress": "^0.2.7",
|
||||||
"@inertiajs/vue3": "^2.3.12",
|
"@inertiajs/vue3": "^2.3.12",
|
||||||
"vue": "^3.5.27",
|
"vue": "^3.5.27",
|
||||||
|
|||||||
125
public/css/app.css
Normal file
125
public/css/app.css
Normal file
@@ -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: ":";
|
||||||
|
}
|
||||||
47
public/css/elements.css
Normal file
47
public/css/elements.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
BIN
public/images/logo.png
Normal file
BIN
public/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 132 KiB |
@@ -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';
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,15 @@ import { InertiaProgress } from '@inertiajs/progress'
|
|||||||
import Vue3Toastify, { toast } from 'vue3-toastify'
|
import Vue3Toastify, { toast } from 'vue3-toastify'
|
||||||
import 'vue3-toastify/dist/index.css'
|
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()
|
InertiaProgress.init()
|
||||||
|
|
||||||
createInertiaApp({
|
createInertiaApp({
|
||||||
@@ -24,12 +33,28 @@ createInertiaApp({
|
|||||||
const vueApp = createApp({ render: () => h(App, props) })
|
const vueApp = createApp({ render: () => h(App, props) })
|
||||||
|
|
||||||
vueApp.use(plugin)
|
vueApp.use(plugin)
|
||||||
|
|
||||||
|
vueApp.component('font-awesome-icon', FontAwesomeIcon)
|
||||||
|
|
||||||
vueApp.use(Vue3Toastify, {
|
vueApp.use(Vue3Toastify, {
|
||||||
autoClose: 3000,
|
autoClose: 10000,
|
||||||
position: 'top-right',
|
position: 'bottom-right',
|
||||||
pauseOnHover: true,
|
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.config.globalProperties.$toast = toast
|
||||||
vueApp.mount(el)
|
vueApp.mount(el)
|
||||||
},
|
},
|
||||||
|
|||||||
195
resources/js/layouts/AppLayout.vue
Normal file
195
resources/js/layouts/AppLayout.vue
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<script setup>
|
||||||
|
import Icon from "../../../app/Views/Components/Icon.vue";
|
||||||
|
import GlobalWidgets from "../../../app/Views/Partials/GlobalWidgets/GlobalWidgets.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: { type: String, default: 'App' },
|
||||||
|
user: { type: Object, },
|
||||||
|
flash: { type: Object, default: () => ({}) },
|
||||||
|
navbar: { type: Object },
|
||||||
|
tenant: String,
|
||||||
|
currentPath: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(props.currentPath)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="app-layout">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<!-- Main content -->
|
||||||
|
<div class="main">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flexbox">
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="logo">
|
||||||
|
<img src="../../../public/images/logo.png" alt="Logo" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<nav class="nav">
|
||||||
|
<ul class="nav-links" v-if="props.navbar.personal.length > 0">
|
||||||
|
<li v-for="navlink in props.navbar.personal">
|
||||||
|
<a
|
||||||
|
:class="{ navlink_active: navlink.url.endsWith(props.currentPath) }"
|
||||||
|
:href="navlink.url"
|
||||||
|
>{{navlink.display}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="nav-links" v-if="props.navbar.common.length > 0">
|
||||||
|
<li v-for="navlink in props.navbar.common">
|
||||||
|
<a
|
||||||
|
:class="{ navlink_active: navlink.url.endsWith(props.currentPath) }"
|
||||||
|
:href="navlink.url"
|
||||||
|
>{{navlink.display}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="nav-links" v-if="props.navbar.costunits.length > 0">
|
||||||
|
<li v-for="navlink in props.navbar.costunits">
|
||||||
|
<a
|
||||||
|
:class="{ navlink_active: navlink.url.endsWith(props.currentPath) }"
|
||||||
|
:href="navlink.url"
|
||||||
|
>{{navlink.display}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="nav-links" v-if="props.navbar.events.length > 0">
|
||||||
|
<li v-for="navlink in props.navbar.events">
|
||||||
|
<a
|
||||||
|
:class="{ navlink_active: navlink.url.endsWith(props.currentPath) }"
|
||||||
|
:href="navlink.url"
|
||||||
|
>{{navlink.display}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main">
|
||||||
|
<div class="header">
|
||||||
|
<div class="left-side"><h1>{{ props.title }}</h1></div>
|
||||||
|
<div class="user-info" v-if="props.user !== null">
|
||||||
|
<a href="/messages" class="header-link-anonymous" title="Meine Nachrichten">
|
||||||
|
<Icon name="envelope" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="/profile" class="header-link-anonymous" title="Mein Profil">
|
||||||
|
<Icon name="user" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="/logout" class="header-link-anonymous-logout" title="Abmelden">
|
||||||
|
<Icon name="lock" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="anonymous-actions" v-else>
|
||||||
|
<a href="/register" class="header-link-anonymous">Registrieren</a>
|
||||||
|
<a href="/login" class="header-link-anonymous"> Anmelden </a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<global-widgets :user="props.user" :tenant="props.tenant" v-if="props.user !== null" />
|
||||||
|
<div class="content">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer">
|
||||||
|
© 2026 Your Company
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toaster -->
|
||||||
|
<transition name="fade">
|
||||||
|
<div v-if="flash.message" class="toaster">
|
||||||
|
{{ flash.message }}
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.header-link-anonymous,
|
||||||
|
.header-link-anonymous-logout {
|
||||||
|
color: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 10px 30px;
|
||||||
|
margin: 0 -20px 0 30px;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-link-anonymous-logout {
|
||||||
|
padding-right: 35px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-link-anonymous:hover {
|
||||||
|
background-color: #1d4899;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-link-anonymous-logout:hover {
|
||||||
|
background-color: #ff0000;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
width: 285px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links li a {
|
||||||
|
color: #b6b6b6;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px 25px;
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
width: calc(100% - 50px);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: -10px;
|
||||||
|
width: 275px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: block;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav a:hover {
|
||||||
|
background-color: #1d4899;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navlink_active {
|
||||||
|
background-color: #fae39c !important;
|
||||||
|
color: #1d4899 !important; /* Dunklere Schrift beim Hover */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,10 +1,19 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
|
<link rel="stylesheet" href="css/app.css" />
|
||||||
|
<link rel="stylesheet" href="css/elements.css" />
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Base href für relative Assets -->
|
||||||
|
<base href="/">
|
||||||
|
|
||||||
|
<!-- Vite Assets -->
|
||||||
@vite('resources/js/app.js')
|
@vite('resources/js/app.js')
|
||||||
|
|
||||||
@inertiaHead
|
@inertiaHead
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,16 +1,39 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Domains\Dashboard\Controllers\DashboardController;
|
||||||
|
use App\Domains\UserManagement\Controllers\LoginController;
|
||||||
|
use App\Domains\UserManagement\Controllers\LogOutController;
|
||||||
|
use App\Http\Controllers\TestRenderInertiaProvider;
|
||||||
|
use App\Middleware\IdentifyTenant;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
return view('welcome');
|
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'),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import laravel from 'laravel-vite-plugin'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: '',
|
||||||
plugins: [
|
plugins: [
|
||||||
laravel({
|
laravel({
|
||||||
input: 'resources/js/app.js',
|
input: 'resources/js/app.js',
|
||||||
@@ -18,4 +19,9 @@ export default defineConfig({
|
|||||||
'@domains': path.resolve(__dirname, 'app/Domains'),
|
'@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
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user