invite team member
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Teams;
|
||||
|
||||
use App\Models\TeamInvite;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
readonly class AcceptTeamInvite
|
||||
{
|
||||
public function __invoke(TeamInvite $invite): void
|
||||
{
|
||||
abort_unless(auth()->user()->email === $invite->invited_email, 403);
|
||||
|
||||
$team = $invite->team;
|
||||
$newUser = User::whereEmail($invite->invited_email)->firstOrFail();
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($team, $newUser, $invite) {
|
||||
$team->members()->attach($newUser);
|
||||
$invite->delete();
|
||||
});
|
||||
} catch (\Exception $exception) {
|
||||
DB::rollBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
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\TeamResource;
|
||||
@@ -74,6 +75,18 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Teams\AcceptTeamInvite;
|
||||
use App\Http\Requests\AcceptTeamInviteRequest;
|
||||
use App\Models\TeamInvite;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class TeamInviteAcceptController extends Controller
|
||||
{
|
||||
public function create(Request $request, AcceptTeamInvite $accept)
|
||||
{
|
||||
$inviteToken = $request->input('inviteToken');
|
||||
if ($inviteToken) {
|
||||
$invite = TeamInvite::findByPlainToken($inviteToken)->first();
|
||||
|
||||
if ($invite) {
|
||||
$accept($invite);
|
||||
|
||||
return to_route('teams.index');
|
||||
}
|
||||
}
|
||||
|
||||
return Inertia::render('team/invite/Create', [
|
||||
'inviteToken' => $inviteToken,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(AcceptTeamInviteRequest $request, AcceptTeamInvite $accept)
|
||||
{
|
||||
$request->validated();
|
||||
|
||||
$accept($request->teamInvite());
|
||||
|
||||
return to_route('teams.index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\TeamInvite;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class AcceptTeamInviteRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'inviteToken' => 'required|string',
|
||||
];
|
||||
}
|
||||
|
||||
public function after(): array
|
||||
{
|
||||
return [
|
||||
function (Validator $validator) {
|
||||
if (($this->teamInvite() == null) || ($this->teamInvite()->invited_email !== auth()->user()->email)) {
|
||||
$validator->errors()->add('inviteToken', 'InviteToken is invalid or expired.');
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
public function teamInvite(): ?TeamInvite
|
||||
{
|
||||
return TeamInvite::findByPlainToken($this->inviteToken)->first();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AddTeamMemberRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|string|email|max:255',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Notifications\InviteToTeam;
|
||||
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\BelongsTo;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Str;
|
||||
|
||||
#[Fillable(['team_id', 'invited_by_user_id', 'invited_email'])]
|
||||
class TeamInvite extends Model
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
protected ?string $plainInviteToken = null;
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
self::creating(function (self $model) {
|
||||
$model->plainInviteToken = Str::random(128);
|
||||
$hashedToken = hash('sha256', $model->plainInviteToken);
|
||||
$model->invite_token = $hashedToken;
|
||||
});
|
||||
self::created(function (self $model) {
|
||||
$model->notify(new InviteToTeam);
|
||||
});
|
||||
}
|
||||
|
||||
public function team(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
}
|
||||
|
||||
public function invitedBy(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'invited_by_user_id');
|
||||
}
|
||||
|
||||
public function plainInviteToken(): ?string
|
||||
{
|
||||
return $this->plainInviteToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route notifications for the mail channel.
|
||||
*
|
||||
* @return array<string, string>|string
|
||||
*/
|
||||
public function routeNotificationForMail(Notification $notification): array|string
|
||||
{
|
||||
return $this->invited_email;
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
protected function findByPlainToken(Builder $query, string $token): Builder
|
||||
{
|
||||
return $query->where('invite_token', hash('sha256', $token));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\TeamInvite;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class InviteToTeam extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*/
|
||||
public function toMail(TeamInvite $invite): MailMessage
|
||||
{
|
||||
$user = User::whereEmail($invite->invited_email)->first();
|
||||
|
||||
return (new MailMessage)
|
||||
->subject('Team Invitation')
|
||||
->greeting(
|
||||
$user !== null
|
||||
? sprintf('Hello %s,', $user->name)
|
||||
: 'Hello',
|
||||
)
|
||||
->line(sprintf(
|
||||
'%s invited you to the %s team.',
|
||||
$invite->invitedBy->name,
|
||||
$invite->team->name,
|
||||
))
|
||||
->linesIf($user === null, [
|
||||
'It seems like you don\'t have an account yet.',
|
||||
'Create one now for free to join the team.',
|
||||
])
|
||||
->action($user === null
|
||||
? 'Register and join this team'
|
||||
: 'Join this team',
|
||||
route($user === null
|
||||
? 'register'
|
||||
: 'teams.invite.create',
|
||||
['inviteToken' => $invite->plainInviteToken()]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user