Skip to content

Commit

Permalink
Merge pull request #1 from alnutile/teams
Browse files Browse the repository at this point in the history
Teams and Auto Login
  • Loading branch information
alnutile authored Sep 1, 2024
2 parents 3086a37 + 89c2f8c commit 8bc7589
Show file tree
Hide file tree
Showing 50 changed files with 2,008 additions and 20 deletions.
81 changes: 81 additions & 0 deletions app/Actions/Jetstream/AddTeamMember.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace App\Actions\Jetstream;

use App\Models\Team;
use App\Models\User;
use Closure;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Laravel\Jetstream\Contracts\AddsTeamMembers;
use Laravel\Jetstream\Events\AddingTeamMember;
use Laravel\Jetstream\Events\TeamMemberAdded;
use Laravel\Jetstream\Jetstream;
use Laravel\Jetstream\Rules\Role;

class AddTeamMember implements AddsTeamMembers
{
/**
* Add a new team member to the given team.
*/
public function add(User $user, Team $team, string $email, ?string $role = null): void
{
Gate::forUser($user)->authorize('addTeamMember', $team);

$this->validate($team, $email, $role);

$newTeamMember = Jetstream::findUserByEmailOrFail($email);

AddingTeamMember::dispatch($team, $newTeamMember);

$team->users()->attach(
$newTeamMember, ['role' => $role]
);

TeamMemberAdded::dispatch($team, $newTeamMember);
}

/**
* Validate the add member operation.
*/
protected function validate(Team $team, string $email, ?string $role): void
{
Validator::make([
'email' => $email,
'role' => $role,
], $this->rules(), [
'email.exists' => __('We were unable to find a registered user with this email address.'),
])->after(
$this->ensureUserIsNotAlreadyOnTeam($team, $email)
)->validateWithBag('addTeamMember');
}

/**
* Get the validation rules for adding a team member.
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
protected function rules(): array
{
return array_filter([
'email' => ['required', 'email', 'exists:users'],
'role' => Jetstream::hasRoles()
? ['required', 'string', new Role]
: null,
]);
}

/**
* Ensure that the user is not already on the team.
*/
protected function ensureUserIsNotAlreadyOnTeam(Team $team, string $email): Closure
{
return function ($validator) use ($team, $email) {
$validator->errors()->addIf(
$team->hasUserWithEmail($email),
'email',
__('This user already belongs to the team.')
);
};
}
}
37 changes: 37 additions & 0 deletions app/Actions/Jetstream/CreateTeam.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace App\Actions\Jetstream;

use App\Models\Team;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Laravel\Jetstream\Contracts\CreatesTeams;
use Laravel\Jetstream\Events\AddingTeam;
use Laravel\Jetstream\Jetstream;

class CreateTeam implements CreatesTeams
{
/**
* Validate and create a new team for the given user.
*
* @param array<string, string> $input
*/
public function create(User $user, array $input): Team
{
Gate::forUser($user)->authorize('create', Jetstream::newTeamModel());

Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
])->validateWithBag('createTeam');

AddingTeam::dispatch($user);

$user->switchTeam($team = $user->ownedTeams()->create([
'name' => $input['name'],
'personal_team' => false,
]));

return $team;
}
}
17 changes: 17 additions & 0 deletions app/Actions/Jetstream/DeleteTeam.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App\Actions\Jetstream;

use App\Models\Team;
use Laravel\Jetstream\Contracts\DeletesTeams;

class DeleteTeam implements DeletesTeams
{
/**
* Delete the given team.
*/
public function delete(Team $team): void
{
$team->purge();
}
}
88 changes: 88 additions & 0 deletions app/Actions/Jetstream/InviteTeamMember.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace App\Actions\Jetstream;

use App\Models\Team;
use App\Models\User;
use Closure;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Jetstream\Contracts\InvitesTeamMembers;
use Laravel\Jetstream\Events\InvitingTeamMember;
use Laravel\Jetstream\Jetstream;
use Laravel\Jetstream\Mail\TeamInvitation;
use Laravel\Jetstream\Rules\Role;

class InviteTeamMember implements InvitesTeamMembers
{
/**
* Invite a new team member to the given team.
*/
public function invite(User $user, Team $team, string $email, ?string $role = null): void
{
Gate::forUser($user)->authorize('addTeamMember', $team);

$this->validate($team, $email, $role);

InvitingTeamMember::dispatch($team, $email, $role);

$invitation = $team->teamInvitations()->create([
'email' => $email,
'role' => $role,
]);

Mail::to($email)->send(new TeamInvitation($invitation));
}

/**
* Validate the invite member operation.
*/
protected function validate(Team $team, string $email, ?string $role): void
{
Validator::make([
'email' => $email,
'role' => $role,
], $this->rules($team), [
'email.unique' => __('This user has already been invited to the team.'),
])->after(
$this->ensureUserIsNotAlreadyOnTeam($team, $email)
)->validateWithBag('addTeamMember');
}

/**
* Get the validation rules for inviting a team member.
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
protected function rules(Team $team): array
{
return array_filter([
'email' => [
'required', 'email',
Rule::unique(Jetstream::teamInvitationModel())->where(function (Builder $query) use ($team) {
$query->where('team_id', $team->id);
}),
],
'role' => Jetstream::hasRoles()
? ['required', 'string', new Role]
: null,
]);
}

/**
* Ensure that the user is not already on the team.
*/
protected function ensureUserIsNotAlreadyOnTeam(Team $team, string $email): Closure
{
return function ($validator) use ($team, $email) {
$validator->errors()->addIf(
$team->hasUserWithEmail($email),
'email',
__('This user already belongs to the team.')
);
};
}
}
51 changes: 51 additions & 0 deletions app/Actions/Jetstream/RemoveTeamMember.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace App\Actions\Jetstream;

use App\Models\Team;
use App\Models\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\ValidationException;
use Laravel\Jetstream\Contracts\RemovesTeamMembers;
use Laravel\Jetstream\Events\TeamMemberRemoved;

class RemoveTeamMember implements RemovesTeamMembers
{
/**
* Remove the team member from the given team.
*/
public function remove(User $user, Team $team, User $teamMember): void
{
$this->authorize($user, $team, $teamMember);

$this->ensureUserDoesNotOwnTeam($teamMember, $team);

$team->removeUser($teamMember);

TeamMemberRemoved::dispatch($team, $teamMember);
}

/**
* Authorize that the user can remove the team member.
*/
protected function authorize(User $user, Team $team, User $teamMember): void
{
if (! Gate::forUser($user)->check('removeTeamMember', $team) &&
$user->id !== $teamMember->id) {
throw new AuthorizationException;
}
}

/**
* Ensure that the currently authenticated user does not own the team.
*/
protected function ensureUserDoesNotOwnTeam(User $teamMember, Team $team): void
{
if ($teamMember->id === $team->owner->id) {
throw ValidationException::withMessages([
'team' => [__('You may not leave a team that you created.')],
])->errorBag('removeTeamMember');
}
}
}
30 changes: 30 additions & 0 deletions app/Actions/Jetstream/UpdateTeamName.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Actions\Jetstream;

use App\Models\Team;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Laravel\Jetstream\Contracts\UpdatesTeamNames;

class UpdateTeamName implements UpdatesTeamNames
{
/**
* Validate and update the given team's name.
*
* @param array<string, string> $input
*/
public function update(User $user, Team $team, array $input): void
{
Gate::forUser($user)->authorize('update', $team);

Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
])->validateWithBag('updateTeamName');

$team->forceFill([
'name' => $input['name'],
])->save();
}
}
56 changes: 56 additions & 0 deletions app/Console/Commands/FixTeams.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace App\Console\Commands;

use App\Models\Team;
use App\Models\User;
use Illuminate\Console\Command;

class FixTeams extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:fix-teams';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Fix missing teams';

/**
* Execute the console command.
*/
public function handle()
{
foreach (User::all() as $user) {

$team = Team::updateOrCreate([
'name' => $user->name,
'user_id' => $user->id,
'personal_team' => true,
]);

$user->update([
'current_team_id' => $team->id,
]);

if (! $team->hasUser($user)) {
$team->users()->attach($user, ['role' => 'admin']);
} else {
// User is already in the team, you might want to update their role instead
$team->users()->updateExistingPivot($user->id, ['role' => 'admin']);
}

foreach ($user->campaigns as $campaign) {
$campaign->update([
'team_id' => $team->id,
]);
}
}
}
}
10 changes: 9 additions & 1 deletion app/Http/Controllers/CampaignController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ public function index()
{

$campaigns = CampaignResource::collection(
Campaign::whereUserId(auth()->user()->id)->paginate()
Campaign::whereIn(
'team_id',
auth()->user()
->teams
->pluck('id')
->values()
->toArray()
)->paginate()
);

return inertia('Campaigns/Index', [
Expand Down Expand Up @@ -78,6 +85,7 @@ public function store()

$validated['chat_status'] = ChatStatusEnum::Pending->value;
$validated['user_id'] = auth()->user()->id;
$validated['team_id'] = auth()->user()->current_team_id;
$campaign = Campaign::create($validated);

return redirect()->route('campaigns.show', $campaign);
Expand Down
Loading

0 comments on commit 8bc7589

Please sign in to comment.