added teams
CI/CD / test_and_check_app (push) Failing after 2m15s
CI/CD / Publish immutable image (push) Skipped

This commit is contained in:
2026-09-11 02:28:44 +02:00
parent 76bd1536bf
commit 6220a26852
22 changed files with 818 additions and 28 deletions
+4 -1
View File
@@ -3,10 +3,13 @@ root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.php]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Enums;
enum TeamMemberRole: string
{
case Admin = 'admin';
case Member = 'member';
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreTeamRequest;
use App\Http\Requests\UpdateTeamRequest;
use App\Models\Team;
use Illuminate\Http\Request;
use Inertia\Inertia;
class TeamController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
return Inertia::render('team/Index', [
'teams' => Team::visibleTo($request->user()),
]);
}
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreTeamRequest $request)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Team $team)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateTeamRequest $request, Team $team)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Team $team)
{
//
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class StoreTeamRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'slug' => ['required', 'string', 'max:255', 'unique:teams,slug'],
'require_2fa' => ['required', 'boolean'],
];
}
protected function prepareForValidation(): void
{
$this->merge([
'slug' => Str::slug($this->name),
]);
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class UpdateTeamRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['nullable', 'string', 'max:255'],
'slug' => ['nullable', 'required_if:name', 'string', 'max:255', 'unique:teams,slug,'.$this->route('team')->id],
'require_2fa' => ['required', 'boolean'],
];
}
protected function prepareForValidation(): void
{
$this->merge([
'slug' => Str::slug($this->name),
]);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin User
*/
class TeamMemberResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'name' => $this->name,
'email' => $this->email,
'has_multi_factor' => $this->hasVerifiedEmail() || $this->hasEnabledTwoFactorAuthentication(),
'role' => $this->pivot->role,
];
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources;
use App\Models\Team;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin Team
*/
class TeamResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'name' => $this->name,
'slug' => $this->slug,
'members' => TeamMemberResource::collection($this->members),
'multi_factor_required' => $this->require_2fa,
];
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property int $created_by
* @property bool $require_2fa
* @property \Carbon\CarbonImmutable|null $created_at
* @property \Carbon\CarbonImmutable|null $updated_at
* @property-read \App\Models\TeamMember|null $pivot
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $members
* @property-read int|null $members_count
* @method static Builder<static>|Team newModelQuery()
* @method static Builder<static>|Team newQuery()
* @method static Builder<static>|Team query()
* @method static Builder<static>|Team visibleTo(\App\Models\User $user)
* @method static Builder<static>|Team whereCreatedAt($value)
* @method static Builder<static>|Team whereCreatedBy($value)
* @method static Builder<static>|Team whereId($value)
* @method static Builder<static>|Team whereName($value)
* @method static Builder<static>|Team whereRequire2fa($value)
* @method static Builder<static>|Team whereSlug($value)
* @method static Builder<static>|Team whereUpdatedAt($value)
* @mixin \Eloquent
*/
#[Fillable(['name', 'slug', 'require_2fa'])]
class Team extends Model
{
protected function casts()
{
return [
'require_2fa' => 'boolean',
];
}
public function members(): BelongsToMany
{
return $this->belongsToMany(User::class, 'team_members')
->using(TeamMember::class)
->withPivot('role')
->withTimestamps();
}
public function scopeVisibleTo(Builder $query, User $user): Builder
{
return $query->whereHas('members', function (Builder $query) use ($user): void {
$query->whereKey($user->getKey());
});
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace App\Models;
use App\Enums\TeamMemberRole;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
/**
* @property int $team_id
* @property int $user_id
* @property TeamMemberRole $role
* @property \Carbon\CarbonImmutable|null $created_at
* @property \Carbon\CarbonImmutable|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember whereRole($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember whereTeamId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TeamMember whereUserId($value)
* @mixin \Eloquent
*/
class TeamMember extends Pivot
{
protected $table = 'team_members';
protected function casts(): array
{
return [
'role' => TeamMemberRole::class,
];
}
}
+39 -8
View File
@@ -2,14 +2,14 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Carbon;
use Laravel\Fortify\Contracts\PasskeyUser;
use Laravel\Fortify\PasskeyAuthenticatable;
use Laravel\Fortify\TwoFactorAuthenticatable;
@@ -18,18 +18,41 @@ use Laravel\Fortify\TwoFactorAuthenticatable;
* @property int $id
* @property string $name
* @property string $email
* @property Carbon|null $email_verified_at
* @property \Carbon\CarbonImmutable|null $email_verified_at
* @property string $password
* @property string|null $remember_token
* @property \Carbon\CarbonImmutable|null $created_at
* @property \Carbon\CarbonImmutable|null $updated_at
* @property string|null $two_factor_secret
* @property string|null $two_factor_recovery_codes
* @property Carbon|null $two_factor_confirmed_at
* @property string|null $remember_token
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property \Carbon\CarbonImmutable|null $two_factor_confirmed_at
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
* @property-read int|null $notifications_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Laravel\Passkeys\Passkey> $passkeys
* @property-read int|null $passkeys_count
* @property-read \App\Models\TeamMember|null $pivot
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Team> $teams
* @property-read int|null $teams_count
* @method static \Database\Factories\UserFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmailVerifiedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTwoFactorConfirmedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTwoFactorRecoveryCodes($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTwoFactorSecret($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
* @mixin \Eloquent
*/
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'two_factor_secret', 'two_factor_recovery_codes', 'remember_token'])]
class User extends Authenticatable implements PasskeyUser
class User extends Authenticatable implements PasskeyUser, MustVerifyEmail
{
/** @use HasFactory<UserFactory> */
use HasFactory, Notifiable, PasskeyAuthenticatable, TwoFactorAuthenticatable;
@@ -47,4 +70,12 @@ class User extends Authenticatable implements PasskeyUser
'two_factor_confirmed_at' => 'datetime',
];
}
public function teams(): BelongsToMany
{
return $this->belongsToMany(Team::class, 'team_members')
->using(TeamMember::class)
->withPivot(['role'])
->withTimestamps();
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Policies;
use App\Enums\TeamMemberRole;
use App\Models\Team;
use App\Models\User;
class TeamPolicy
{
public function view(User $user, Team $team): bool
{
return Team::query()
->visibleTo($user)
->whereKey($team->getKey())
->exists();
}
public function create(User $user): bool
{
return $user->hasVerifiedEmail();
}
public function manage(User $user, Team $team): bool
{
$membership = $team->members()->whereKey($user->id)->first();
if (! $membership) {
return false;
}
return $membership->pivot->role === TeamMemberRole::Admin;
}
}
+1
View File
@@ -18,6 +18,7 @@
"laravel/wayfinder": "^0.1.14"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^3.7",
"fakerphp/faker": "^1.24",
"larastan/larastan": "^3.9",
"laravel/boost": "^2.2",
Generated
+294 -2
View File
@@ -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": "859863f5ca81e4a2e3cf62f862513073",
"content-hash": "5dc37eb696378f9735e0d1d9e44899f6",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -7629,6 +7629,153 @@
}
],
"packages-dev": [
{
"name": "barryvdh/laravel-ide-helper",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
"reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a",
"reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a",
"shasum": ""
},
"require": {
"barryvdh/reflection-docblock": "^2.4",
"composer/class-map-generator": "^1.0",
"ext-json": "*",
"illuminate/console": "^11.15 || ^12 || ^13.0",
"illuminate/database": "^11.15 || ^12 || ^13.0",
"illuminate/filesystem": "^11.15 || ^12 || ^13.0",
"illuminate/support": "^11.15 || ^12 || ^13.0",
"php": "^8.2"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^3",
"illuminate/config": "^11.15 || ^12 || ^13.0",
"illuminate/view": "^11.15 || ^12 || ^13.0",
"larastan/larastan": "^3.1",
"mockery/mockery": "^1.4",
"orchestra/testbench": "^9.2 || ^10 || ^11.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.12",
"spatie/phpunit-snapshot-assertions": "^4 || ^5",
"vlucas/phpdotenv": "^5"
},
"suggest": {
"illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)."
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.6-dev"
}
},
"autoload": {
"psr-4": {
"Barryvdh\\LaravelIdeHelper\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.",
"keywords": [
"autocomplete",
"codeintel",
"dev",
"helper",
"ide",
"laravel",
"netbeans",
"phpdoc",
"phpstorm",
"sublime"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.7.0"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2026-03-17T14:12:51+00:00"
},
{
"name": "barryvdh/reflection-docblock",
"version": "v2.4.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/ReflectionDocBlock.git",
"reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b",
"reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^8.5.14|^9"
},
"suggest": {
"dflydev/markdown": "~1.0",
"erusev/parsedown": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3.x-dev"
}
},
"autoload": {
"psr-0": {
"Barryvdh": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "mike.vanriel@naenius.com"
}
],
"support": {
"source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.1"
},
"time": "2026-03-05T20:09:01+00:00"
},
{
"name": "brianium/paratest",
"version": "v7.24.1",
@@ -7722,6 +7869,151 @@
],
"time": "2026-08-17T06:31:26+00:00"
},
{
"name": "composer/class-map-generator",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/composer/class-map-generator.git",
"reference": "86d8208fc3c649a3a999daf1a63c25201be2990f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/86d8208fc3c649a3a999daf1a63c25201be2990f",
"reference": "86d8208fc3c649a3a999daf1a63c25201be2990f",
"shasum": ""
},
"require": {
"composer/pcre": "^2.1 || ^3.1",
"php": "^7.2 || ^8.0",
"symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8"
},
"require-dev": {
"phpstan/phpstan": "^1.12 || ^2",
"phpstan/phpstan-deprecation-rules": "^1 || ^2",
"phpstan/phpstan-phpunit": "^1 || ^2",
"phpstan/phpstan-strict-rules": "^1.1 || ^2",
"phpunit/phpunit": "^8",
"symfony/filesystem": "^5.4 || ^6 || ^7 || ^8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\ClassMapGenerator\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "https://seld.be"
}
],
"description": "Utilities to scan PHP code and generate class maps.",
"keywords": [
"classmap"
],
"support": {
"issues": "https://github.com/composer/class-map-generator/issues",
"source": "https://github.com/composer/class-map-generator/tree/1.7.3"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
}
],
"time": "2026-05-05T09:17:07+00:00"
},
{
"name": "composer/pcre",
"version": "3.4.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "d5a341b3fb61f3001970940afb1d332968a183ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed",
"reference": "d5a341b3fb61f3001970940afb1d332968a183ed",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<2.2.2"
},
"require-dev": {
"phpstan/phpstan": "^2",
"phpstan/phpstan-deprecation-rules": "^2",
"phpstan/phpstan-strict-rules": "^2",
"phpunit/phpunit": "^9"
},
"type": "library",
"extra": {
"phpstan": {
"includes": [
"extension.neon"
]
},
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\Pcre\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
"keywords": [
"PCRE",
"preg",
"regex",
"regular expression"
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.4.0"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
}
],
"time": "2026-06-07T11:47:49+00:00"
},
{
"name": "composer/semver",
"version": "3.4.4",
@@ -11528,5 +11820,5 @@
"php": "^8.3"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.9.0"
}
@@ -0,0 +1,31 @@
<?php
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('teams', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->boolean('require_2fa')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('teams');
}
};
@@ -0,0 +1,34 @@
<?php
use App\Enums\TeamMemberRole;
use App\Models\Team;
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('team_members', function (Blueprint $table) {
$table->foreignIdFor(Team::class)->constrained()->cascadeOnDelete();
$table->foreignIdFor(User::class)->constrained()->cascadeOnDelete();
$table->enum('role', TeamMemberRole::cases())->default(TeamMemberRole::Member);
$table->timestamps();
$table->unique(['team_id', 'user_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('team_members');
}
};
+5 -14
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { Link } from '@inertiajs/vue3';
import { BookOpen, FolderGit2, LayoutGrid } from '@lucide/vue';
import { UserGroup } from '@lucide/vue';
import AppLogo from '@/components/AppLogo.vue';
import NavFooter from '@/components/NavFooter.vue';
import NavMain from '@/components/NavMain.vue';
@@ -16,26 +16,17 @@ import {
} from '@/components/ui/sidebar';
import { dashboard } from '@/routes';
import type { NavItem } from '@/types';
import teams from "@/routes/teams";
const mainNavItems: NavItem[] = [
{
title: 'Dashboard',
href: dashboard(),
icon: LayoutGrid,
title: 'Meine Teams',
href: teams.index(),
icon: UserGroup,
},
];
const footerNavItems: NavItem[] = [
{
title: 'Repository',
href: 'https://github.com/laravel/vue-starter-kit',
icon: FolderGit2,
},
{
title: 'Documentation',
href: 'https://laravel.com/docs/starter-kits#vue',
icon: BookOpen,
},
];
</script>
+11
View File
@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped>
</style>
+11
View File
@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped>
</style>
+55
View File
@@ -0,0 +1,55 @@
<script setup lang="ts">
import teamRoutes from "@/routes/teams";
import {Head} from "@inertiajs/vue3";
import type {Team} from "@/types/models";
import TextLink from "@/components/TextLink.vue";
defineOptions({
layout: {
breadcrumbs: [
{
title: 'Meine Teams',
href: teamRoutes.index(),
},
],
},
});
defineProps<{
teams: Team[]
}>()
</script>
<template>
<Head title="Teams"/>
<div>
</div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Slug</th>
<th>Mitglieder</th>
<th>2FA</th>
</tr>
</thead>
<tbody>
<tr
v-if="teams.length > 0"
v-for="team in teams"
>
<th>{{ team.name }}</th>
<th>{{ team.slug }}</th>
<th>{{ team.members.length }}</th>
<th>{{ team.multi_factor_required }}</th>
</tr>
<tr v-else class="text-center font-bold">
Du bist noch von keinem Team mitglied.
Tritt einem Team bei oder <TextLink href="{{ route('teams.create') }}">erstelle eins.</TextLink>
</tr>
</tbody>
</table>
</template>
+16
View File
@@ -0,0 +1,16 @@
export interface Team {
name: string;
slug: string;
members: TeamMember[];
multi_factor_required: boolean;
}
export type TeamMemberRole = 'admin' | 'member'
export interface TeamMember {
name: string;
email: string;
has_multi_factor: boolean;
role: TeamMemberRole;
}
-2
View File
@@ -10,9 +10,7 @@ Route::middleware(['auth'])->group(function () {
Route::get('settings/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('settings/profile', [ProfileController::class, 'update'])->name('profile.update');
});
Route::middleware(['auth', 'verified'])->group(function () {
Route::delete('settings/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
Route::get('settings/security', [SecurityController::class, 'edit'])
+8 -1
View File
@@ -1,11 +1,18 @@
<?php
use App\Http\Controllers\TeamController;
use Illuminate\Support\Facades\Route;
Route::inertia('/', 'Welcome')->name('home');
Route::middleware(['auth', 'verified'])->group(function () {
Route::middleware(['auth'])->group(function () {
Route::inertia('dashboard', 'Dashboard')->name('dashboard');
Route::controller(TeamController::class)->prefix('teams')->name('teams.')->group(function () {
Route::get('/', 'index')->name('index');
Route::get('/create', 'create')->name('create');
});
});
require __DIR__.'/settings.php';