Skip to content

Commit 1d6ba3e

Browse files
Configure Jetstream classes in providers and separate panels into Admin and App
1 parent 7e7ffd9 commit 1d6ba3e

File tree

61 files changed

+1861
-111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1861
-111
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace App\Actions\Fortify;
4+
5+
use App\Models\Team;
6+
use App\Models\User;
7+
use Illuminate\Support\Facades\DB;
8+
use Illuminate\Support\Facades\Hash;
9+
use Illuminate\Support\Facades\Validator;
10+
use Laravel\Fortify\Contracts\CreatesNewUsers;
11+
use Laravel\Jetstream\Jetstream;
12+
13+
class CreateNewUser implements CreatesNewUsers
14+
{
15+
use PasswordValidationRules;
16+
17+
/**
18+
* Create a newly registered user.
19+
*
20+
* @param array<string, string> $input
21+
*/
22+
public function create(array $input): User
23+
{
24+
Validator::make($input, [
25+
'name' => ['required', 'string', 'max:255'],
26+
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
27+
'password' => $this->passwordRules(),
28+
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '',
29+
])->validate();
30+
31+
return DB::transaction(function () use ($input) {
32+
return tap(User::create([
33+
'name' => $input['name'],
34+
'email' => $input['email'],
35+
'password' => Hash::make($input['password']),
36+
]), function (User $user) {
37+
$this->createTeam($user);
38+
});
39+
});
40+
}
41+
42+
/**
43+
* Create a personal team for the user.
44+
*/
45+
protected function createTeam(User $user): void
46+
{
47+
$user->ownedTeams()->save(Team::forceCreate([
48+
'user_id' => $user->id,
49+
'name' => explode(' ', $user->name, 2)[0]."'s Team",
50+
'personal_team' => true,
51+
]));
52+
}
53+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace App\Actions\Jetstream;
4+
5+
use App\Models\Team;
6+
use App\Models\User;
7+
use Closure;
8+
use Illuminate\Support\Facades\Gate;
9+
use Illuminate\Support\Facades\Validator;
10+
use Laravel\Jetstream\Contracts\AddsTeamMembers;
11+
use Laravel\Jetstream\Events\AddingTeamMember;
12+
use Laravel\Jetstream\Events\TeamMemberAdded;
13+
use Laravel\Jetstream\Jetstream;
14+
use Laravel\Jetstream\Rules\Role;
15+
16+
class AddTeamMember implements AddsTeamMembers
17+
{
18+
/**
19+
* Add a new team member to the given team.
20+
*/
21+
public function add(User $user, Team $team, string $email, string $role = null): void
22+
{
23+
Gate::forUser($user)->authorize('addTeamMember', $team);
24+
25+
$this->validate($team, $email, $role);
26+
27+
$newTeamMember = Jetstream::findUserByEmailOrFail($email);
28+
29+
AddingTeamMember::dispatch($team, $newTeamMember);
30+
31+
$team->users()->attach(
32+
$newTeamMember,
33+
['role' => $role]
34+
);
35+
36+
TeamMemberAdded::dispatch($team, $newTeamMember);
37+
}
38+
39+
/**
40+
* Validate the add member operation.
41+
*/
42+
protected function validate(Team $team, string $email, ?string $role): void
43+
{
44+
Validator::make([
45+
'email' => $email,
46+
'role' => $role,
47+
], $this->rules(), [
48+
'email.exists' => __('We were unable to find a registered user with this email address.'),
49+
])->after(
50+
$this->ensureUserIsNotAlreadyOnTeam($team, $email)
51+
)->validateWithBag('addTeamMember');
52+
}
53+
54+
/**
55+
* Get the validation rules for adding a team member.
56+
*
57+
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
58+
*/
59+
protected function rules(): array
60+
{
61+
return array_filter([
62+
'email' => ['required', 'email', 'exists:users'],
63+
'role' => Jetstream::hasRoles()
64+
? ['required', 'string', new Role()]
65+
: null,
66+
]);
67+
}
68+
69+
/**
70+
* Ensure that the user is not already on the team.
71+
*/
72+
protected function ensureUserIsNotAlreadyOnTeam(Team $team, string $email): Closure
73+
{
74+
return function ($validator) use ($team, $email) {
75+
$validator->errors()->addIf(
76+
$team->hasUserWithEmail($email),
77+
'email',
78+
__('This user already belongs to the team.')
79+
);
80+
};
81+
}
82+
}

app/Actions/Jetstream/CreateTeam.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace App\Actions\Jetstream;
4+
5+
use App\Models\Team;
6+
use App\Models\User;
7+
use Illuminate\Support\Facades\Gate;
8+
use Illuminate\Support\Facades\Validator;
9+
use Laravel\Jetstream\Contracts\CreatesTeams;
10+
use Laravel\Jetstream\Events\AddingTeam;
11+
use Laravel\Jetstream\Jetstream;
12+
13+
class CreateTeam implements CreatesTeams
14+
{
15+
/**
16+
* Validate and create a new team for the given user.
17+
*
18+
* @param array<string, string> $input
19+
*/
20+
public function create(User $user, array $input): Team
21+
{
22+
Gate::forUser($user)->authorize('create', Jetstream::newTeamModel());
23+
24+
Validator::make($input, [
25+
'name' => ['required', 'string', 'max:255'],
26+
])->validateWithBag('createTeam');
27+
28+
AddingTeam::dispatch($user);
29+
30+
$user->switchTeam($team = $user->ownedTeams()->create([
31+
'name' => $input['name'],
32+
'personal_team' => true,
33+
]));
34+
35+
return $team;
36+
}
37+
}

app/Actions/Jetstream/DeleteTeam.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace App\Actions\Jetstream;
4+
5+
use App\Models\Team;
6+
use Laravel\Jetstream\Contracts\DeletesTeams;
7+
8+
class DeleteTeam implements DeletesTeams
9+
{
10+
/**
11+
* Delete the given team.
12+
*/
13+
public function delete(Team $team): void
14+
{
15+
$team->purge();
16+
}
17+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace App\Actions\Jetstream;
4+
5+
use App\Models\Team;
6+
use App\Models\User;
7+
use Illuminate\Support\Facades\DB;
8+
use Laravel\Jetstream\Contracts\DeletesTeams;
9+
use Laravel\Jetstream\Contracts\DeletesUsers;
10+
11+
class DeleteUser implements DeletesUsers
12+
{
13+
/**
14+
* Create a new action instance.
15+
*/
16+
public function __construct(protected DeletesTeams $deletesTeams)
17+
{
18+
}
19+
20+
/**
21+
* Delete the given user.
22+
*/
23+
public function delete(User $user): void
24+
{
25+
DB::transaction(function () use ($user) {
26+
$this->deleteTeams($user);
27+
$user->deleteProfilePhoto();
28+
$user->tokens->each->delete();
29+
$user->delete();
30+
});
31+
}
32+
33+
/**
34+
* Delete the teams and team associations attached to the user.
35+
*/
36+
protected function deleteTeams(User $user): void
37+
{
38+
$user->teams()->detach();
39+
40+
$user->ownedTeams->each(function (Team $team) {
41+
$this->deletesTeams->delete($team);
42+
});
43+
}
44+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace App\Actions\Jetstream;
4+
5+
use App\Models\Team;
6+
use App\Models\User;
7+
use Closure;
8+
use Illuminate\Database\Query\Builder;
9+
use Illuminate\Support\Facades\Gate;
10+
use Illuminate\Support\Facades\Mail;
11+
use Illuminate\Support\Facades\Validator;
12+
use Illuminate\Validation\Rule;
13+
use Laravel\Jetstream\Contracts\InvitesTeamMembers;
14+
use Laravel\Jetstream\Events\InvitingTeamMember;
15+
use Laravel\Jetstream\Jetstream;
16+
use Laravel\Jetstream\Mail\TeamInvitation;
17+
use Laravel\Jetstream\Rules\Role;
18+
19+
class InviteTeamMember implements InvitesTeamMembers
20+
{
21+
/**
22+
* Invite a new team member to the given team.
23+
*/
24+
public function invite(User $user, Team $team, string $email, string $role = null): void
25+
{
26+
Gate::forUser($user)->authorize('addTeamMember', $team);
27+
28+
$this->validate($team, $email, $role);
29+
30+
InvitingTeamMember::dispatch($team, $email, $role);
31+
32+
$invitation = $team->teamInvitations()->create([
33+
'email' => $email,
34+
'role' => $role,
35+
]);
36+
37+
Mail::to($email)->send(new TeamInvitation($invitation));
38+
}
39+
40+
/**
41+
* Validate the invite member operation.
42+
*/
43+
protected function validate(Team $team, string $email, ?string $role): void
44+
{
45+
Validator::make([
46+
'email' => $email,
47+
'role' => $role,
48+
], $this->rules($team), [
49+
'email.unique' => __('This user has already been invited to the team.'),
50+
])->after(
51+
$this->ensureUserIsNotAlreadyOnTeam($team, $email)
52+
)->validateWithBag('addTeamMember');
53+
}
54+
55+
/**
56+
* Get the validation rules for inviting a team member.
57+
*
58+
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
59+
*/
60+
protected function rules(Team $team): array
61+
{
62+
return array_filter([
63+
'email' => [
64+
'required', 'email',
65+
Rule::unique(Jetstream::teamInvitationModel())->where(function (Builder $query) use ($team) {
66+
$query->where('team_id', $team->id);
67+
}),
68+
],
69+
'role' => Jetstream::hasRoles()
70+
? ['required', 'string', new Role()]
71+
: null,
72+
]);
73+
}
74+
75+
/**
76+
* Ensure that the user is not already on the team.
77+
*/
78+
protected function ensureUserIsNotAlreadyOnTeam(Team $team, string $email): Closure
79+
{
80+
return function ($validator) use ($team, $email) {
81+
$validator->errors()->addIf(
82+
$team->hasUserWithEmail($email),
83+
'email',
84+
__('This user already belongs to the team.')
85+
);
86+
};
87+
}
88+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace App\Actions\Jetstream;
4+
5+
use App\Models\Team;
6+
use App\Models\User;
7+
use Illuminate\Auth\Access\AuthorizationException;
8+
use Illuminate\Support\Facades\Gate;
9+
use Illuminate\Validation\ValidationException;
10+
use Laravel\Jetstream\Contracts\RemovesTeamMembers;
11+
use Laravel\Jetstream\Events\TeamMemberRemoved;
12+
13+
class RemoveTeamMember implements RemovesTeamMembers
14+
{
15+
/**
16+
* Remove the team member from the given team.
17+
*/
18+
public function remove(User $user, Team $team, User $teamMember): void
19+
{
20+
$this->authorize($user, $team, $teamMember);
21+
22+
$this->ensureUserDoesNotOwnTeam($teamMember, $team);
23+
24+
$team->removeUser($teamMember);
25+
26+
TeamMemberRemoved::dispatch($team, $teamMember);
27+
}
28+
29+
/**
30+
* Authorize that the user can remove the team member.
31+
*/
32+
protected function authorize(User $user, Team $team, User $teamMember): void
33+
{
34+
if (!Gate::forUser($user)->check('removeTeamMember', $team) &&
35+
$user->id !== $teamMember->id) {
36+
throw new AuthorizationException();
37+
}
38+
}
39+
40+
/**
41+
* Ensure that the currently authenticated user does not own the team.
42+
*/
43+
protected function ensureUserDoesNotOwnTeam(User $teamMember, Team $team): void
44+
{
45+
if ($teamMember->id === $team->owner->id) {
46+
throw ValidationException::withMessages([
47+
'team' => [__('You may not leave a team that you created.')],
48+
])->errorBag('removeTeamMember');
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)