diff --git a/app/Http/Controllers/TeamController.php b/app/Http/Controllers/TeamController.php
index 17c2e6c..3a88786 100644
--- a/app/Http/Controllers/TeamController.php
+++ b/app/Http/Controllers/TeamController.php
@@ -3,9 +3,9 @@
namespace App\Http\Controllers;
use App\Enums\TeamMemberRole;
-use App\Http\Requests\AddTeamMemberRequest;
use App\Http\Requests\StoreTeamRequest;
use App\Http\Requests\UpdateTeamRequest;
+use App\Http\Resources\TeamInviteResource;
use App\Http\Resources\TeamResource;
use App\Models\Team;
use Illuminate\Http\Request;
@@ -28,10 +28,14 @@ class TeamController extends Controller
public function show(Request $request, Team $team)
{
+ $teamManager = auth()->user()->can('manage', $team);
+
return Inertia::render('team/Show', [
'team' => TeamResource::make($team)->resolve($request),
- 'canManageTeam' => auth()->user()->can('manage', $team),
- ]);
+ 'canManageTeam' => $teamManager,
+ ] + ($teamManager ? [
+ 'teamInvites' => TeamInviteResource::collection($team->invites)->resolve($request),
+ ] : []));
}
/**
@@ -75,18 +79,6 @@ class TeamController extends Controller
]);
}
- /**
- * Show the form for editing the specified resource.
- */
- public function addMember(AddTeamMemberRequest $request, Team $team)
- {
- $validated = $request->validated();
-
- $email = Arr::get($validated, 'email');
-
- return response()->json(['message' => __('')]);
- }
-
/**
* Update the specified resource in storage.
*/
@@ -104,15 +96,8 @@ class TeamController extends Controller
Inertia::flash('error', 'Team could not be updated.');
DB::rollBack();
}
- return to_route('teams.show', $team);
- }
- /**
- * Remove the specified resource from storage.
- */
- public function destroy(Team $team)
- {
- //
+ return to_route('teams.show', $team);
}
public function nameToSlug(Request $request)
@@ -132,4 +117,42 @@ class TeamController extends Controller
'taken' => $slug !== $ignoredSlug && Team::whereSlug($slug)->exists(),
]);
}
+
+ public function invite(Request $request, Team $team)
+ {
+ $validated = $request->validate([
+ 'email' => 'required|email|max:255',
+ ]);
+
+ abort_unless(auth()->user()->can('manage', $team), 403);
+
+ $team->invites()->create([
+ 'invited_email' => Arr::get($validated, 'email'),
+ 'invited_by_user_id' => auth()->user()->id,
+ ]);
+
+ Inertia::flash('success', 'User invited to your team.');
+ }
+
+ public function revokeInvite(Request $request, Team $team)
+ {
+ $validated = $request->validate([
+ 'email' => 'required|email|max:255|exists:team_invites,invited_email',
+ ]);
+
+ abort_unless(auth()->user()->can('manage', $team), 403);
+
+ $teamInvite = $team->invites()->whereInvitedEmail(Arr::get($validated, 'email'))->firstOrFail();
+
+ try {
+ DB::transaction(function () use ($teamInvite) {
+ $teamInvite->delete();
+ });
+ Inertia::flash('success', 'successfully deleted user invite.');
+ } catch (\Throwable $e) {
+ Inertia::flash('error', 'could not delete user invite.');
+ DB::rollBack();
+ }
+
+ }
}
diff --git a/app/Http/Requests/AddTeamMemberRequest.php b/app/Http/Requests/AddTeamMemberRequest.php
deleted file mode 100644
index a57f977..0000000
--- a/app/Http/Requests/AddTeamMemberRequest.php
+++ /dev/null
@@ -1,21 +0,0 @@
-|string>
- */
- public function rules(): array
- {
- return [
- 'email' => 'required|string|email|max:255',
- ];
- }
-}
diff --git a/app/Http/Resources/TeamInviteResource.php b/app/Http/Resources/TeamInviteResource.php
new file mode 100644
index 0000000..e0b18ff
--- /dev/null
+++ b/app/Http/Resources/TeamInviteResource.php
@@ -0,0 +1,28 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ 'invitedEmail' => $this->invited_email,
+ 'team' => TeamResource::make($this->team)->resolve($request),
+ 'invitedBy' => UserResource::make($this->invitedBy)->resolve($request),
+ 'invitedAt' => $this->created_at->toDateTimeString(),
+ ];
+ }
+}
diff --git a/app/Http/Resources/TeamMemberResource.php b/app/Http/Resources/TeamMemberResource.php
index c960e9e..596bc87 100644
--- a/app/Http/Resources/TeamMemberResource.php
+++ b/app/Http/Resources/TeamMemberResource.php
@@ -4,19 +4,15 @@ namespace App\Http\Resources;
use App\Models\User;
use Illuminate\Http\Request;
-use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin User
*/
-class TeamMemberResource extends JsonResource
+class TeamMemberResource extends UserResource
{
public function toArray(Request $request): array
{
- return [
- 'name' => $this->name,
- 'email' => $this->email,
- 'has_multi_factor' => $this->hasVerifiedEmail() || $this->hasEnabledTwoFactorAuthentication(),
+ return parent::toArray($request) + [
'role' => $this->pivot->role,
'joined_at' => $this->pivot->created_at->toDateTimeString(),
];
diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php
new file mode 100644
index 0000000..2f40320
--- /dev/null
+++ b/app/Http/Resources/UserResource.php
@@ -0,0 +1,27 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ 'name' => $this->name,
+ 'email' => $this->email,
+ 'has_multi_factor' => $this->hasVerifiedEmail() || $this->hasEnabledTwoFactorAuthentication(),
+ ];
+ }
+}
diff --git a/app/Models/Team.php b/app/Models/Team.php
index ce44553..621530a 100644
--- a/app/Models/Team.php
+++ b/app/Models/Team.php
@@ -3,9 +3,11 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
+use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
@@ -34,13 +36,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
#[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')
@@ -48,11 +43,23 @@ class Team extends Model
->withPivot('role')
->withTimestamps();
}
+ public function invites(): HasMany
+ {
+ return $this->hasMany(TeamInvite::class);
+ }
+ #[Scope]
public function scopeVisibleTo(Builder $query, User $user): Builder
{
return $query->whereHas('members', function (Builder $query) use ($user): void {
$query->whereKey($user->getKey());
});
}
+
+ protected function casts()
+ {
+ return [
+ 'require_2fa' => 'boolean',
+ ];
+ }
}
diff --git a/resources/js/pages/team/Show.vue b/resources/js/pages/team/Show.vue
index 9818c3a..0e27f59 100644
--- a/resources/js/pages/team/Show.vue
+++ b/resources/js/pages/team/Show.vue
@@ -1,15 +1,16 @@
@@ -61,7 +68,7 @@ setLayoutProps({
-
+
Mitglieder
@@ -86,5 +93,35 @@ setLayoutProps({
+
+
+ Ausstehende Invites
+
+
+
+ Invited
+ Invited von
+ Invited at
+
+
+
+
+ {{ invite.invitedEmail }}
+ {{ invite.invitedBy.name }}
+ {{ invite.invitedAt }}
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/types/models.ts b/resources/js/types/models.ts
index 454fb70..1d68a08 100644
--- a/resources/js/types/models.ts
+++ b/resources/js/types/models.ts
@@ -8,10 +8,20 @@ export interface Team {
export type TeamMemberRole = 'admin' | 'member';
-export interface TeamMember {
+export interface User {
name: string;
email: string;
has_multi_factor: boolean;
+}
+
+export interface TeamMember extends User {
role: TeamMemberRole;
- joined_at: string
+ joined_at: string;
+}
+
+export interface TeamInvite {
+ invitedEmail: string;
+ team: Team;
+ invitedBy: User;
+ invitedAt: string;
}
diff --git a/routes/web.php b/routes/web.php
index cd9685b..adb9362 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -10,9 +10,9 @@ Route::middleware(['auth'])->group(function () {
Route::inertia('dashboard', 'Dashboard')->name('dashboard');
Route::controller(TeamController::class)->prefix('teams')->name('teams.')->group(function () {
- Route::controller(TeamInviteAcceptController::class)->prefix('invite')->name('invite.')->group(function () {
- Route::get('/accept', 'create')->name('create');
- Route::post('/accept', 'store')->name('store');
+ Route::prefix('invite')->name('invite.')->group(function () {
+ Route::get('/accept', [TeamInviteAcceptController::class, 'create'])->name('create');
+ Route::post('/accept', [TeamInviteAcceptController::class, 'store'])->name('store');
});
Route::get('/', 'index')->name('index');
@@ -22,6 +22,8 @@ Route::middleware(['auth'])->group(function () {
Route::patch('/{team:slug}', 'update')->name('update');
Route::post('/name-to-slug', 'nameToSlug')->name('nameToSlug');
Route::get('/{team:slug}', 'show')->name('show');
+ Route::post('{team:slug}/invite', [TeamController::class, 'invite'])->name('invite');
+ Route::post('{team:slug}/invite/revoke', [TeamController::class, 'revokeInvite'])->name('invite.revoke');
});