Renamed jetstream team to organization

This commit is contained in:
Constantin Graf
2024-01-21 19:52:19 +01:00
parent 6377df1f44
commit 0a8d958ccc
63 changed files with 461 additions and 329 deletions

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Actions\Fortify;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
@@ -46,9 +46,9 @@ class CreateNewUser implements CreatesNewUsers
*/
protected function createTeam(User $user): void
{
$user->ownedTeams()->save(Team::forceCreate([
$user->ownedTeams()->save(Organization::forceCreate([
'user_id' => $user->id,
'name' => explode(' ', $user->name, 2)[0]."'s Team",
'name' => explode(' ', $user->name, 2)[0]."'s Organization",
'personal_team' => true,
]));
}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Closure;
use Illuminate\Support\Facades\Gate;
@@ -15,32 +15,32 @@ use Laravel\Jetstream\Events\TeamMemberAdded;
use Laravel\Jetstream\Jetstream;
use Laravel\Jetstream\Rules\Role;
class AddTeamMember implements AddsTeamMembers
class AddOrganizationMember implements AddsTeamMembers
{
/**
* Add a new team member to the given team.
*/
public function add(User $user, Team $team, string $email, ?string $role = null): void
public function add(User $user, Organization $organization, string $email, ?string $role = null): void
{
Gate::forUser($user)->authorize('addTeamMember', $team);
Gate::forUser($user)->authorize('addTeamMember', $organization);
$this->validate($team, $email, $role);
$this->validate($organization, $email, $role);
$newTeamMember = Jetstream::findUserByEmailOrFail($email);
AddingTeamMember::dispatch($team, $newTeamMember);
AddingTeamMember::dispatch($organization, $newTeamMember);
$team->users()->attach(
$organization->users()->attach(
$newTeamMember, ['role' => $role]
);
TeamMemberAdded::dispatch($team, $newTeamMember);
TeamMemberAdded::dispatch($organization, $newTeamMember);
}
/**
* Validate the add member operation.
*/
protected function validate(Team $team, string $email, ?string $role): void
protected function validate(Organization $organization, string $email, ?string $role): void
{
Validator::make([
'email' => $email,
@@ -48,7 +48,7 @@ class AddTeamMember implements AddsTeamMembers
], $this->rules(), [
'email.exists' => __('We were unable to find a registered user with this email address.'),
])->after(
$this->ensureUserIsNotAlreadyOnTeam($team, $email)
$this->ensureUserIsNotAlreadyOnTeam($organization, $email)
)->validateWithBag('addTeamMember');
}
@@ -70,7 +70,7 @@ class AddTeamMember implements AddsTeamMembers
/**
* Ensure that the user is not already on the team.
*/
protected function ensureUserIsNotAlreadyOnTeam(Team $team, string $email): Closure
protected function ensureUserIsNotAlreadyOnTeam(Organization $team, string $email): Closure
{
return function ($validator) use ($team, $email) {
$validator->errors()->addIf(

View File

@@ -4,22 +4,27 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Laravel\Jetstream\Contracts\CreatesTeams;
use Laravel\Jetstream\Events\AddingTeam;
use Laravel\Jetstream\Jetstream;
class CreateTeam implements CreatesTeams
class CreateOrganization implements CreatesTeams
{
/**
* Validate and create a new team for the given user.
*
* @param array<string, string> $input
*
* @throws AuthorizationException
* @throws ValidationException
*/
public function create(User $user, array $input): Team
public function create(User $user, array $input): Organization
{
Gate::forUser($user)->authorize('create', Jetstream::newTeamModel());
@@ -29,11 +34,14 @@ class CreateTeam implements CreatesTeams
AddingTeam::dispatch($user);
$user->switchTeam($team = $user->ownedTeams()->create([
/** @var Organization $organization */
$organization = $user->ownedTeams()->create([
'name' => $input['name'],
'personal_team' => false,
]));
]);
return $team;
$user->switchTeam($organization);
return $organization;
}
}

View File

@@ -4,15 +4,15 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Models\Team;
use App\Models\Organization;
use Laravel\Jetstream\Contracts\DeletesTeams;
class DeleteTeam implements DeletesTeams
class DeleteOrganization implements DeletesTeams
{
/**
* Delete the given team.
*/
public function delete(Team $team): void
public function delete(Organization $team): void
{
$team->purge();
}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Laravel\Jetstream\Contracts\DeletesTeams;
@@ -47,7 +47,7 @@ class DeleteUser implements DeletesUsers
{
$user->teams()->detach();
$user->ownedTeams->each(function (Team $team) {
$user->ownedTeams->each(function (Organization $team) {
$this->deletesTeams->delete($team);
});
}

View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Models\Organization;
use App\Models\OrganizationInvitation;
use App\Models\User;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator;
use Korridor\LaravelModelValidationRules\Rules\UniqueEloquent;
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 InviteOrganizationMember implements InvitesTeamMembers
{
/**
* Invite a new team member to the given team.
*/
public function invite(User $user, Organization $organization, string $email, ?string $role = null): void
{
Gate::forUser($user)->authorize('addTeamMember', $organization);
$this->validate($organization, $email, $role);
InvitingTeamMember::dispatch($organization, $email, $role);
$invitation = $organization->teamInvitations()->create([
'email' => $email,
'role' => $role,
]);
Mail::to($email)->send(new TeamInvitation($invitation));
}
/**
* Validate the invite member operation.
*/
protected function validate(Organization $organization, string $email, ?string $role): void
{
Validator::make([
'email' => $email,
'role' => $role,
], $this->rules($organization), [
'email.unique' => __('This user has already been invited to the team.'),
])->after(
$this->ensureUserIsNotAlreadyOnTeam($organization, $email)
)->validateWithBag('addTeamMember');
}
/**
* Get the validation rules for inviting a team member.
*
* @return array<string, ValidationRule|array|string>
*/
protected function rules(Organization $organization): array
{
return array_filter([
'email' => [
'required',
'email',
new UniqueEloquent(OrganizationInvitation::class, 'email', function (Builder $builder) use ($organization) {
/** @var Builder<OrganizationInvitation> $builder */
return $builder->whereBelongsTo($organization, 'organization');
}),
],
'role' => Jetstream::hasRoles()
? ['required', 'string', new Role]
: null,
]);
}
/**
* Ensure that the user is not already on the team.
*/
protected function ensureUserIsNotAlreadyOnTeam(Organization $organization, string $email): Closure
{
return function ($validator) use ($organization, $email) {
$validator->errors()->addIf(
$organization->hasUserWithEmail($email),
'email',
__('This user already belongs to the team.')
);
};
}
}

View File

@@ -1,90 +0,0 @@
<?php
declare(strict_types=1);
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('team_invitations')->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.')
);
};
}
}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Gate;
@@ -12,28 +12,28 @@ use Illuminate\Validation\ValidationException;
use Laravel\Jetstream\Contracts\RemovesTeamMembers;
use Laravel\Jetstream\Events\TeamMemberRemoved;
class RemoveTeamMember implements RemovesTeamMembers
class RemoveOrganizationMember implements RemovesTeamMembers
{
/**
* Remove the team member from the given team.
*/
public function remove(User $user, Team $team, User $teamMember): void
public function remove(User $user, Organization $organization, User $teamMember): void
{
$this->authorize($user, $team, $teamMember);
$this->authorize($user, $organization, $teamMember);
$this->ensureUserDoesNotOwnTeam($teamMember, $team);
$this->ensureUserDoesNotOwnTeam($teamMember, $organization);
$team->removeUser($teamMember);
$organization->removeUser($teamMember);
TeamMemberRemoved::dispatch($team, $teamMember);
TeamMemberRemoved::dispatch($organization, $teamMember);
}
/**
* Authorize that the user can remove the team member.
*/
protected function authorize(User $user, Team $team, User $teamMember): void
protected function authorize(User $user, Organization $organization, User $teamMember): void
{
if (! Gate::forUser($user)->check('removeTeamMember', $team) &&
if (! Gate::forUser($user)->check('removeTeamMember', $organization) &&
$user->id !== $teamMember->id) {
throw new AuthorizationException;
}
@@ -42,9 +42,9 @@ class RemoveTeamMember implements RemovesTeamMembers
/**
* Ensure that the currently authenticated user does not own the team.
*/
protected function ensureUserDoesNotOwnTeam(User $teamMember, Team $team): void
protected function ensureUserDoesNotOwnTeam(User $teamMember, Organization $organization): void
{
if ($teamMember->id === $team->owner->id) {
if ($teamMember->id === $organization->owner->id) {
throw ValidationException::withMessages([
'team' => [__('You may not leave a team that you created.')],
])->errorBag('removeTeamMember');

View File

@@ -4,28 +4,33 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Laravel\Jetstream\Contracts\UpdatesTeamNames;
class UpdateTeamName implements UpdatesTeamNames
class UpdateOrganization implements UpdatesTeamNames
{
/**
* Validate and update the given team's name.
*
* @param array<string, string> $input
*
* @throws AuthorizationException
* @throws ValidationException
*/
public function update(User $user, Team $team, array $input): void
public function update(User $user, Organization $organization, array $input): void
{
Gate::forUser($user)->authorize('update', $team);
Gate::forUser($user)->authorize('update', $organization);
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
])->validateWithBag('updateTeamName');
$team->forceFill([
$organization->forceFill([
'name' => $input['name'],
])->save();
}

View File

@@ -14,7 +14,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property string $id
* @property string $name
* @property string $organization_id
* @property-read Team $organization
* @property string $created_at
* @property string $updated_at
* @property-read Organization $organization
*
* @method static ClientFactory factory()
*/
@@ -33,10 +35,10 @@ class Client extends Model
];
/**
* @return BelongsTo<Team, Client>
* @return BelongsTo<Organization, Client>
*/
public function organization(): BelongsTo
{
return $this->belongsTo(Team::class, 'organization_id');
return $this->belongsTo(Organization::class, 'organization_id');
}
}

View File

@@ -7,14 +7,24 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Laravel\Jetstream\Membership as JetstreamMembership;
/**
* @property string $id
* @property string $role
* @property string $organization_id
* @property string $user_id
* @property string $created_at
* @property string $updated_at
* @property-read Organization $organization
* @property-read User $user
*/
class Membership extends JetstreamMembership
{
use HasUuids;
/**
* Indicates if the IDs are auto-incrementing.
* The table associated with the pivot model.
*
* @var bool
* @var string
*/
public $incrementing = true;
protected $table = 'organization_user';
}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Models;
use Database\Factories\TeamFactory;
use Database\Factories\OrganizationFactory;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -17,10 +17,10 @@ use Laravel\Jetstream\Team as JetstreamTeam;
* @property string $id
* @property User $owner
*
* @method HasMany<TeamInvitation> teamInvitations()
* @method static TeamFactory factory()
* @method HasMany<OrganizationInvitation> teamInvitations()
* @method static OrganizationFactory factory()
*/
class Team extends JetstreamTeam
class Organization extends JetstreamTeam
{
use HasFactory;
use HasUuids;

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Laravel\Jetstream\Jetstream;
use Laravel\Jetstream\TeamInvitation as JetstreamTeamInvitation;
/**
* @property string $id
* @property string $email
*/
class OrganizationInvitation extends JetstreamTeamInvitation
{
use HasUuids;
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'organization_invitations';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'email',
'role',
];
/**
* Get the organization that the invitation belongs to.
*/
public function organization(): BelongsTo
{
return $this->belongsTo(Jetstream::teamModel(), 'organization_id');
}
/**
* Get the organization that the invitation belongs to.
*/
public function team(): BelongsTo
{
return $this->belongsTo(Jetstream::teamModel(), 'organization_id');
}
}

View File

@@ -17,7 +17,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property string $name
* @property string $organization_id
* @property string $client_id
* @property-read Team $organization
* @property-read Organization $organization
* @property-read Client|null $client
* @property-read Collection<Task> $tasks
*
@@ -38,11 +38,11 @@ class Project extends Model
];
/**
* @return BelongsTo<Team, Project>
* @return BelongsTo<Organization, Project>
*/
public function organization(): BelongsTo
{
return $this->belongsTo(Team::class, 'organization_id');
return $this->belongsTo(Organization::class, 'organization_id');
}
/**

View File

@@ -14,7 +14,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property string $id
* @property string $name
* @property string $organization_id
* @property-read Team $organization
* @property-read Organization $organization
*
* @method static TagFactory factory()
*/
@@ -33,10 +33,10 @@ class Tag extends Model
];
/**
* @return BelongsTo<Team, Tag>
* @return BelongsTo<Organization, Tag>
*/
public function organization(): BelongsTo
{
return $this->belongsTo(Team::class, 'organization_id');
return $this->belongsTo(Organization::class, 'organization_id');
}
}

View File

@@ -16,7 +16,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property string $project_id
* @property string $organization_id
* @property-read Project $project
* @property-read Team $organization
* @property-read Organization $organization
*
* @method static TaskFactory factory()
*/
@@ -43,10 +43,10 @@ class Task extends Model
}
/**
* @return BelongsTo<Team, Task>
* @return BelongsTo<Organization, Task>
*/
public function organization(): BelongsTo
{
return $this->belongsTo(Team::class, 'organization_id');
return $this->belongsTo(Organization::class, 'organization_id');
}
}

View File

@@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Laravel\Jetstream\Jetstream;
use Laravel\Jetstream\TeamInvitation as JetstreamTeamInvitation;
class TeamInvitation extends JetstreamTeamInvitation
{
use HasUuids;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'email',
'role',
];
/**
* Get the team that the invitation belongs to.
*/
public function team(): BelongsTo
{
return $this->belongsTo(Jetstream::teamModel());
}
}

View File

@@ -19,7 +19,7 @@ use Illuminate\Support\Carbon;
* @property bool $billable
* @property array $tags
* @property-read User $user
* @property-read Team $organization
* @property-read Organization $organization
* @property-read Project|null $project
* @property-read Task|null $task
*
@@ -52,11 +52,11 @@ class TimeEntry extends Model
}
/**
* @return BelongsTo<Team, TimeEntry>
* @return BelongsTo<Organization, TimeEntry>
*/
public function organization(): BelongsTo
{
return $this->belongsTo(Team::class, 'organization_id');
return $this->belongsTo(Organization::class, 'organization_id');
}
/**

View File

@@ -8,6 +8,7 @@ use Database\Factories\UserFactory;
use Filament\Panel;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@@ -20,7 +21,7 @@ use Laravel\Passport\HasApiTokens;
* @property string $id
* @property string $name
*
* @method HasMany<Team> ownedTeams()
* @method HasMany<Organization> ownedTeams()
* @method static UserFactory factory()
*/
class User extends Authenticatable
@@ -79,4 +80,17 @@ class User extends Authenticatable
// TODO: Implement canAccessPanel() method.
return false;
}
/**
* @return BelongsToMany<Organization>
*/
public function organizations(): BelongsToMany
{
return $this->belongsToMany(Organization::class, Membership::class)
->withPivot([
'role',
])
->withTimestamps()
->as('membership');
}
}

View File

@@ -4,11 +4,11 @@ declare(strict_types=1);
namespace App\Policies;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class TeamPolicy
class OrganizationPolicy
{
use HandlesAuthorization;
@@ -23,9 +23,9 @@ class TeamPolicy
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Team $team): bool
public function view(User $user, Organization $organization): bool
{
return $user->belongsToTeam($team);
return $user->belongsToTeam($organization);
}
/**
@@ -39,40 +39,40 @@ class TeamPolicy
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Team $team): bool
public function update(User $user, Organization $organization): bool
{
return $user->ownsTeam($team);
return $user->ownsTeam($organization);
}
/**
* Determine whether the user can add team members.
*/
public function addTeamMember(User $user, Team $team): bool
public function addTeamMember(User $user, Organization $organization): bool
{
return $user->ownsTeam($team);
return $user->ownsTeam($organization);
}
/**
* Determine whether the user can update team member permissions.
*/
public function updateTeamMember(User $user, Team $team): bool
public function updateTeamMember(User $user, Organization $organization): bool
{
return $user->ownsTeam($team);
return $user->ownsTeam($organization);
}
/**
* Determine whether the user can remove team members.
*/
public function removeTeamMember(User $user, Team $team): bool
public function removeTeamMember(User $user, Organization $organization): bool
{
return $user->ownsTeam($team);
return $user->ownsTeam($organization);
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Team $team): bool
public function delete(User $user, Organization $organization): bool
{
return $user->ownsTeam($team);
return $user->ownsTeam($organization);
}
}

View File

@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace App\Providers;
use App\Models\Membership;
use App\Models\Team;
use App\Models\TeamInvitation;
use App\Models\Organization;
use App\Models\OrganizationInvitation;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
@@ -31,8 +31,8 @@ class AppServiceProvider extends ServiceProvider
Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction());
Relation::enforceMorphMap([
'membership' => Membership::class,
'team' => Team::class,
'team_invitation' => TeamInvitation::class,
'team' => Organization::class,
'team_invitation' => OrganizationInvitation::class,
'user' => User::class,
]);
}

View File

@@ -4,7 +4,8 @@ declare(strict_types=1);
namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use App\Models\Organization;
use App\Policies\OrganizationPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Jetstream\Jetstream;
use Laravel\Passport\Passport;
@@ -17,7 +18,7 @@ class AuthServiceProvider extends ServiceProvider
* @var array<class-string, class-string>
*/
protected $policies = [
//
Organization::class => OrganizationPolicy::class,
];
/**

View File

@@ -4,13 +4,15 @@ declare(strict_types=1);
namespace App\Providers;
use App\Actions\Jetstream\AddTeamMember;
use App\Actions\Jetstream\CreateTeam;
use App\Actions\Jetstream\DeleteTeam;
use App\Actions\Jetstream\AddOrganizationMember;
use App\Actions\Jetstream\CreateOrganization;
use App\Actions\Jetstream\DeleteOrganization;
use App\Actions\Jetstream\DeleteUser;
use App\Actions\Jetstream\InviteTeamMember;
use App\Actions\Jetstream\RemoveTeamMember;
use App\Actions\Jetstream\UpdateTeamName;
use App\Actions\Jetstream\InviteOrganizationMember;
use App\Actions\Jetstream\RemoveOrganizationMember;
use App\Actions\Jetstream\UpdateOrganization;
use App\Models\Organization;
use App\Models\OrganizationInvitation;
use Illuminate\Support\ServiceProvider;
use Laravel\Jetstream\Jetstream;
@@ -31,13 +33,15 @@ class JetstreamServiceProvider extends ServiceProvider
{
$this->configurePermissions();
Jetstream::createTeamsUsing(CreateTeam::class);
Jetstream::updateTeamNamesUsing(UpdateTeamName::class);
Jetstream::addTeamMembersUsing(AddTeamMember::class);
Jetstream::inviteTeamMembersUsing(InviteTeamMember::class);
Jetstream::removeTeamMembersUsing(RemoveTeamMember::class);
Jetstream::deleteTeamsUsing(DeleteTeam::class);
Jetstream::createTeamsUsing(CreateOrganization::class);
Jetstream::updateTeamNamesUsing(UpdateOrganization::class);
Jetstream::addTeamMembersUsing(AddOrganizationMember::class);
Jetstream::inviteTeamMembersUsing(InviteOrganizationMember::class);
Jetstream::removeTeamMembersUsing(RemoveOrganizationMember::class);
Jetstream::deleteTeamsUsing(DeleteOrganization::class);
Jetstream::deleteUsersUsing(DeleteUser::class);
Jetstream::useTeamModel(Organization::class);
Jetstream::useTeamInvitationModel(OrganizationInvitation::class);
}
/**

View File

@@ -9,6 +9,7 @@
"filament/filament": "^3.2",
"guzzlehttp/guzzle": "^7.2",
"inertiajs/inertia-laravel": "^0.6.8",
"korridor/laravel-model-validation-rules": "^3.0",
"laravel/framework": "^10.10",
"laravel/jetstream": "^4.2",
"laravel/passport": "*",

66
composer.lock generated
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": "34dbd2208afb93ce559614a5c014397e",
"content-hash": "68ea9179372fd4db583f50784a1ca6f7",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -2509,6 +2509,70 @@
},
"time": "2023-12-07T10:44:41+00:00"
},
{
"name": "korridor/laravel-model-validation-rules",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/korridor/laravel-model-validation-rules.git",
"reference": "23537e5bd296a042bbe0c3a1c6d556c89dfbad42"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/korridor/laravel-model-validation-rules/zipball/23537e5bd296a042bbe0c3a1c6d556c89dfbad42",
"reference": "23537e5bd296a042bbe0c3a1c6d556c89dfbad42",
"shasum": ""
},
"require": {
"illuminate/database": "^10",
"illuminate/support": "^10",
"php": ">=8.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.6",
"orchestra/testbench": "^8.0",
"phpunit/phpunit": "^10",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Korridor\\LaravelModelValidationRules\\ModelValidationServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Korridor\\LaravelModelValidationRules\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "korridor",
"email": "26689068+korridor@users.noreply.github.com"
}
],
"description": "A laravel validation rule that uses eloquent to validate if a model exists",
"homepage": "https://github.com/korridor/laravel-model-validation-rules",
"keywords": [
"eloquent",
"exist",
"laravel",
"model",
"rule",
"validation"
],
"support": {
"issues": "https://github.com/korridor/laravel-model-validation-rules/issues",
"source": "https://github.com/korridor/laravel-model-validation-rules/tree/3.0.0"
},
"time": "2023-02-16T11:15:08+00:00"
},
{
"name": "laravel/fortify",
"version": "v1.20.0",

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Client;
use App\Models\Team;
use App\Models\Organization;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
@@ -22,11 +22,11 @@ class ClientFactory extends Factory
{
return [
'name' => $this->faker->company(),
'organization_id' => Team::factory(),
'organization_id' => Organization::factory(),
];
}
public function forOrganization(Team $organization): self
public function forOrganization(Organization $organization): self
{
return $this->state(function (array $attributes) use ($organization) {
return [

View File

@@ -4,13 +4,14 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Team>
* @extends Factory<Organization>
*/
class TeamFactory extends Factory
class OrganizationFactory extends Factory
{
/**
* Define the model's default state.

View File

@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Client;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Team;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
@@ -24,12 +24,12 @@ class ProjectFactory extends Factory
return [
'name' => $this->faker->company(),
'color' => $this->faker->hexColor(),
'organization_id' => Team::factory(),
'organization_id' => Organization::factory(),
'client_id' => null,
];
}
public function forOrganization(Team $organization): self
public function forOrganization(Organization $organization): self
{
return $this->state(function (array $attributes) use ($organization) {
return [

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Organization;
use App\Models\Tag;
use App\Models\Team;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
@@ -22,11 +22,11 @@ class TagFactory extends Factory
{
return [
'name' => $this->faker->name,
'organization_id' => Team::factory(),
'organization_id' => Organization::factory(),
];
}
public function forOrganization(Team $organization): self
public function forOrganization(Organization $organization): self
{
return $this->state(function (array $attributes) use ($organization) {
return [

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Task;
use App\Models\Team;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
@@ -24,7 +24,7 @@ class TaskFactory extends Factory
return [
'name' => $this->faker->word(),
'project_id' => Project::factory(),
'organization_id' => Team::factory(),
'organization_id' => Organization::factory(),
];
}
@@ -37,7 +37,7 @@ class TaskFactory extends Factory
});
}
public function forOrganization(Team $organization): self
public function forOrganization(Organization $organization): self
{
return $this->state(function (array $attributes) use ($organization) {
return [

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Task;
use App\Models\Team;
use App\Models\TimeEntry;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
@@ -32,7 +32,7 @@ class TimeEntryFactory extends Factory
'billable' => $this->faker->boolean(),
'tags' => [],
'user_id' => User::factory(),
'organization_id' => Team::factory(),
'organization_id' => Organization::factory(),
];
}
@@ -45,7 +45,7 @@ class TimeEntryFactory extends Factory
});
}
public function forOrganization(Team $organization): self
public function forOrganization(Organization $organization): self
{
return $this->state(function (array $attributes) use ($organization) {
return [

View File

@@ -4,14 +4,13 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
use Laravel\Jetstream\Features;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
* @extends Factory<User>
*/
class UserFactory extends Factory
{
@@ -50,16 +49,12 @@ class UserFactory extends Factory
/**
* Indicate that the user should have a personal team.
*/
public function withPersonalTeam(?callable $callback = null): static
public function withPersonalOrganization(?callable $callback = null): static
{
if (! Features::hasTeamFeatures()) {
return $this->state([]);
}
return $this->has(
Team::factory()
Organization::factory()
->state(fn (array $attributes, User $user) => [
'name' => $user->name.'\'s Team',
'name' => $user->name.'\'s Organization',
'user_id' => $user->id,
'personal_team' => true,
])

View File

@@ -13,7 +13,7 @@ return new class extends Migration
*/
public function up(): void
{
Schema::create('teams', function (Blueprint $table) {
Schema::create('organizations', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->foreignUuid('user_id')->index();
$table->string('name');

View File

@@ -13,14 +13,14 @@ return new class extends Migration
*/
public function up(): void
{
Schema::create('team_user', function (Blueprint $table) {
Schema::create('organization_user', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->foreignUuid('team_id');
$table->foreignUuid('organization_id');
$table->foreignUuid('user_id');
$table->string('role')->nullable();
$table->timestamps();
$table->unique(['team_id', 'user_id']);
$table->unique(['organization_id', 'user_id']);
});
}

View File

@@ -13,16 +13,16 @@ return new class extends Migration
*/
public function up(): void
{
Schema::create('team_invitations', function (Blueprint $table) {
Schema::create('organization_invitations', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->foreignUuid('team_id')
$table->foreignUuid('organization_id')
->constrained()
->cascadeOnDelete();
$table->string('email');
$table->string('role')->nullable();
$table->timestamps();
$table->unique(['team_id', 'email']);
$table->unique(['organization_id', 'email']);
});
}

View File

@@ -19,7 +19,7 @@ return new class extends Migration
$table->uuid('organization_id');
$table->foreign('organization_id')
->references('id')
->on('teams')
->on('organizations')
->cascadeOnUpdate()
->restrictOnDelete();
$table->timestamps();

View File

@@ -26,7 +26,7 @@ return new class extends Migration
$table->uuid('organization_id');
$table->foreign('organization_id')
->references('id')
->on('teams')
->on('organizations')
->cascadeOnUpdate()
->restrictOnDelete();
$table->timestamps();

View File

@@ -25,7 +25,7 @@ return new class extends Migration
$table->uuid('organization_id');
$table->foreign('organization_id')
->references('id')
->on('teams')
->on('organizations')
->cascadeOnUpdate()
->restrictOnDelete();
$table->timestamps();

View File

@@ -19,7 +19,7 @@ return new class extends Migration
$table->uuid('organization_id');
$table->foreign('organization_id')
->references('id')
->on('teams')
->on('organizations')
->cascadeOnUpdate()
->restrictOnDelete();
$table->timestamps();

View File

@@ -28,7 +28,7 @@ return new class extends Migration
$table->uuid('organization_id');
$table->foreign('organization_id')
->references('id')
->on('teams')
->on('organizations')
->cascadeOnUpdate()
->restrictOnDelete();
$table->uuid('project_id')->nullable();

View File

@@ -6,9 +6,9 @@ namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use App\Models\Client;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Task;
use App\Models\Team;
use App\Models\TimeEntry;
use App\Models\User;
use Illuminate\Database\Seeder;
@@ -22,14 +22,23 @@ class DatabaseSeeder extends Seeder
public function run(): void
{
$this->deleteAll();
$organization = Team::factory()->create([
$organization = Organization::factory()->create([
'name' => 'ACME Corp',
]);
$user1 = User::factory()->withPersonalTeam()->create([
$user1 = User::factory()->withPersonalOrganization()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
$user1->teams()->attach($organization);
$userAcmeAdmin = User::factory()->create([
'name' => 'ACME Admin',
'email' => 'admin@acme.test',
]);
$user1->organizations()->attach($organization, [
'role' => 'editor',
]);
$userAcmeAdmin->organizations()->attach($organization, [
'role' => 'admin',
]);
$client = Client::factory()->create([
'name' => 'Big Company',
]);
@@ -50,6 +59,6 @@ class DatabaseSeeder extends Seeder
DB::table((new Project())->getTable())->delete();
DB::table((new Client())->getTable())->delete();
DB::table((new User())->getTable())->delete();
DB::table((new Team())->getTable())->delete();
DB::table((new Organization())->getTable())->delete();
}
}

View File

@@ -72,12 +72,12 @@ const logout = () => {
<template #content>
<div class="w-60">
<!-- Team Management -->
<!-- Organization Management -->
<div class="block px-4 py-2 text-xs text-gray-400">
Manage Team
</div>
<!-- Team Settings -->
<!-- Organization Settings -->
<DropdownLink :href="route('teams.show', $page.props.auth.user.current_team)">
Team Settings
</DropdownLink>
@@ -86,7 +86,7 @@ const logout = () => {
Create New Team
</DropdownLink>
<!-- Team Switcher -->
<!-- Organization Switcher -->
<template v-if="$page.props.auth.user.all_teams.length > 1">
<div class="border-t border-gray-200 dark:border-gray-600" />
@@ -229,7 +229,7 @@ const logout = () => {
</ResponsiveNavLink>
</form>
<!-- Team Management -->
<!-- Organization Management -->
<template v-if="$page.props.jetstream.hasTeamFeatures">
<div class="border-t border-gray-200 dark:border-gray-600" />
@@ -237,7 +237,7 @@ const logout = () => {
Manage Team
</div>
<!-- Team Settings -->
<!-- Organization Settings -->
<ResponsiveNavLink :href="route('teams.show', $page.props.auth.user.current_team)" :active="route().current('teams.show')">
Team Settings
</ResponsiveNavLink>
@@ -246,7 +246,7 @@ const logout = () => {
Create New Team
</ResponsiveNavLink>
<!-- Team Switcher -->
<!-- Organization Switcher -->
<template v-if="$page.props.auth.user.all_teams.length > 1">
<div class="border-t border-gray-200 dark:border-gray-600" />

View File

@@ -45,7 +45,7 @@ const deleteTeam = () => {
</DangerButton>
</div>
<!-- Delete Team Confirmation Modal -->
<!-- Delete Organization Confirmation Modal -->
<ConfirmationModal :show="confirmingTeamDeletion" @close="confirmingTeamDeletion = false">
<template #title>
Delete Team

View File

@@ -97,7 +97,7 @@ const displayableRole = (role) => {
<div v-if="userPermissions.canAddTeamMembers">
<SectionBorder />
<!-- Add Team Member -->
<!-- Add Organization Member -->
<FormSection @submitted="addTeamMember">
<template #title>
Add Team Member
@@ -177,7 +177,7 @@ const displayableRole = (role) => {
<div v-if="team.team_invitations.length > 0 && userPermissions.canAddTeamMembers">
<SectionBorder />
<!-- Team Member Invitations -->
<!-- Organization Member Invitations -->
<ActionSection class="mt-10 sm:mt-0">
<template #title>
Pending Team Invitations
@@ -187,7 +187,7 @@ const displayableRole = (role) => {
These people have been invited to your team and have been sent an invitation email. They may join the team by accepting the email invitation.
</template>
<!-- Pending Team Member Invitation List -->
<!-- Pending Organization Member Invitation List -->
<template #content>
<div class="space-y-6">
<div v-for="invitation in team.team_invitations" :key="invitation.id" class="flex items-center justify-between">
@@ -196,7 +196,7 @@ const displayableRole = (role) => {
</div>
<div class="flex items-center">
<!-- Cancel Team Invitation -->
<!-- Cancel Organization Invitation -->
<button
v-if="userPermissions.canRemoveTeamMembers"
class="cursor-pointer ms-6 text-sm text-red-500 focus:outline-none"
@@ -214,7 +214,7 @@ const displayableRole = (role) => {
<div v-if="team.users.length > 0">
<SectionBorder />
<!-- Manage Team Members -->
<!-- Manage Organization Members -->
<ActionSection class="mt-10 sm:mt-0">
<template #title>
Team Members
@@ -224,7 +224,7 @@ const displayableRole = (role) => {
All of the people that are part of this team.
</template>
<!-- Team Member List -->
<!-- Organization Member List -->
<template #content>
<div class="space-y-6">
<div v-for="user in team.users" :key="user.id" class="flex items-center justify-between">
@@ -236,7 +236,7 @@ const displayableRole = (role) => {
</div>
<div class="flex items-center">
<!-- Manage Team Member Role -->
<!-- Manage Organization Member Role -->
<button
v-if="userPermissions.canUpdateTeamMembers && availableRoles.length"
class="ms-2 text-sm text-gray-400 underline"
@@ -249,7 +249,7 @@ const displayableRole = (role) => {
{{ displayableRole(user.membership.role) }}
</div>
<!-- Leave Team -->
<!-- Leave Organization -->
<button
v-if="$page.props.auth.user.id === user.id"
class="cursor-pointer ms-6 text-sm text-red-500"
@@ -258,7 +258,7 @@ const displayableRole = (role) => {
Leave
</button>
<!-- Remove Team Member -->
<!-- Remove Organization Member -->
<button
v-else-if="userPermissions.canRemoveTeamMembers"
class="cursor-pointer ms-6 text-sm text-red-500"
@@ -328,7 +328,7 @@ const displayableRole = (role) => {
</template>
</DialogModal>
<!-- Leave Team Confirmation Modal -->
<!-- Leave Organization Confirmation Modal -->
<ConfirmationModal :show="confirmingLeavingTeam" @close="confirmingLeavingTeam = false">
<template #title>
Leave Team
@@ -354,7 +354,7 @@ const displayableRole = (role) => {
</template>
</ConfirmationModal>
<!-- Remove Team Member Confirmation Modal -->
<!-- Remove Organization Member Confirmation Modal -->
<ConfirmationModal :show="teamMemberBeingRemoved" @close="teamMemberBeingRemoved = null">
<template #title>
Remove Team Member

View File

@@ -35,7 +35,7 @@ const updateTeamName = () => {
</template>
<template #form>
<!-- Team Owner Information -->
<!-- Organization Owner Information -->
<div class="col-span-6">
<InputLabel value="Team Owner" />
@@ -51,7 +51,7 @@ const updateTeamName = () => {
</div>
</div>
<!-- Team Name -->
<!-- Organization Name -->
<div class="col-span-6 sm:col-span-4">
<InputLabel for="name" value="Team Name" />

View File

@@ -1,5 +1,5 @@
@component('mail::message')
{{ __('You have been invited to join the :team team!', ['team' => $invitation->team->name]) }}
{{ __('You have been invited to join the :team team!', ['team' => $invitation->organization->name]) }}
@if (Laravel\Fortify\Features::enabled(Laravel\Fortify\Features::registration()))
{{ __('If you do not have an account, you may create one by clicking the button below. After creating an account, you may click the invitation acceptance button in this email to accept the team invitation:') }}

View File

@@ -20,7 +20,7 @@ class ApiTokenPermissionsTest extends TestCase
$this->markTestSkipped('API support is not enabled.');
}
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$token = $user->tokens()->create([
'name' => 'Test Token',

View File

@@ -19,7 +19,7 @@ class CreateApiTokenTest extends TestCase
$this->markTestSkipped('API support is not enabled.');
}
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$response = $this->post('/user/api-tokens', [
'name' => 'Test Token',

View File

@@ -14,13 +14,13 @@ class CreateTeamTest extends TestCase
public function test_teams_can_be_created(): void
{
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$response = $this->post('/teams', [
'name' => 'Test Team',
'name' => 'Test Organization',
]);
$this->assertCount(2, $user->fresh()->ownedTeams);
$this->assertEquals('Test Team', $user->fresh()->ownedTeams()->latest('id')->first()->name);
$this->assertEquals('Test Organization', $user->fresh()->ownedTeams()->latest('id')->first()->name);
}
}

View File

@@ -20,7 +20,7 @@ class DeleteApiTokenTest extends TestCase
$this->markTestSkipped('API support is not enabled.');
}
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$token = $user->tokens()->create([
'name' => 'Test Token',

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Tests\Feature;
use App\Models\Team;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
@@ -15,9 +15,9 @@ class DeleteTeamTest extends TestCase
public function test_teams_can_be_deleted(): void
{
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$user->ownedTeams()->save($team = Team::factory()->make([
$user->ownedTeams()->save($team = Organization::factory()->make([
'personal_team' => false,
]));
@@ -33,7 +33,7 @@ class DeleteTeamTest extends TestCase
public function test_personal_teams_cant_be_deleted(): void
{
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$response = $this->delete('/teams/'.$user->currentTeam->id);

View File

@@ -23,7 +23,7 @@ class EmailVerificationTest extends TestCase
$this->markTestSkipped('Email verification not enabled.');
}
$user = User::factory()->withPersonalTeam()->unverified()->create();
$user = User::factory()->withPersonalOrganization()->unverified()->create();
$response = $this->actingAs($user)->get('/email/verify');

View File

@@ -7,7 +7,6 @@ namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Laravel\Jetstream\Features;
use Laravel\Jetstream\Mail\TeamInvitation;
use Tests\TestCase;
@@ -17,41 +16,37 @@ class InviteTeamMemberTest extends TestCase
public function test_team_members_can_be_invited_to_team(): void
{
if (! Features::sendsTeamInvitations()) {
$this->markTestSkipped('Team invitations not enabled.');
}
// Arrange
Mail::fake();
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
// Act
$response = $this->post('/teams/'.$user->currentTeam->id.'/members', [
'email' => 'test@example.com',
'role' => 'admin',
]);
// Assert
Mail::assertSent(TeamInvitation::class);
$this->assertCount(1, $user->currentTeam->fresh()->teamInvitations);
}
public function test_team_member_invitations_can_be_cancelled(): void
{
if (! Features::sendsTeamInvitations()) {
$this->markTestSkipped('Team invitations not enabled.');
}
// Arrange
Mail::fake();
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$invitation = $user->currentTeam->teamInvitations()->create([
'email' => 'test@example.com',
'role' => 'admin',
]);
// Act
$response = $this->delete('/team-invitations/'.$invitation->id);
// Assert
$this->assertCount(0, $user->currentTeam->fresh()->teamInvitations);
}
}

View File

@@ -14,7 +14,7 @@ class LeaveTeamTest extends TestCase
public function test_users_can_leave_teams(): void
{
$user = User::factory()->withPersonalTeam()->create();
$user = User::factory()->withPersonalOrganization()->create();
$user->currentTeam->users()->attach(
$otherUser = User::factory()->create(), ['role' => 'admin']
@@ -29,7 +29,7 @@ class LeaveTeamTest extends TestCase
public function test_team_owners_cant_leave_their_own_team(): void
{
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$response = $this->delete('/teams/'.$user->currentTeam->id.'/members/'.$user->id);

View File

@@ -14,7 +14,7 @@ class PasswordConfirmationTest extends TestCase
public function test_confirm_password_screen_can_be_rendered(): void
{
$user = User::factory()->withPersonalTeam()->create();
$user = User::factory()->withPersonalOrganization()->create();
$response = $this->actingAs($user)->get('/user/confirm-password');

View File

@@ -14,7 +14,7 @@ class RemoveTeamMemberTest extends TestCase
public function test_team_members_can_be_removed_from_teams(): void
{
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$user->currentTeam->users()->attach(
$otherUser = User::factory()->create(), ['role' => 'admin']
@@ -27,7 +27,7 @@ class RemoveTeamMemberTest extends TestCase
public function test_only_team_owner_can_remove_team_members(): void
{
$user = User::factory()->withPersonalTeam()->create();
$user = User::factory()->withPersonalOrganization()->create();
$user->currentTeam->users()->attach(
$otherUser = User::factory()->create(), ['role' => 'admin']

View File

@@ -14,7 +14,7 @@ class UpdateTeamMemberRoleTest extends TestCase
public function test_team_member_roles_can_be_updated(): void
{
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$user->currentTeam->users()->attach(
$otherUser = User::factory()->create(), ['role' => 'admin']
@@ -31,7 +31,7 @@ class UpdateTeamMemberRoleTest extends TestCase
public function test_only_team_owner_can_update_team_member_roles(): void
{
$user = User::factory()->withPersonalTeam()->create();
$user = User::factory()->withPersonalOrganization()->create();
$user->currentTeam->users()->attach(
$otherUser = User::factory()->create(), ['role' => 'admin']

View File

@@ -14,13 +14,13 @@ class UpdateTeamNameTest extends TestCase
public function test_team_names_can_be_updated(): void
{
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
$this->actingAs($user = User::factory()->withPersonalOrganization()->create());
$response = $this->put('/teams/'.$user->currentTeam->id, [
'name' => 'Test Team',
'name' => 'Test Organization',
]);
$this->assertCount(1, $user->fresh()->ownedTeams);
$this->assertEquals('Test Team', $user->currentTeam->fresh()->name);
$this->assertEquals('Test Organization', $user->currentTeam->fresh()->name);
}
}

View File

@@ -5,14 +5,14 @@ declare(strict_types=1);
namespace Tests\Unit\Model;
use App\Models\Client;
use App\Models\Team;
use App\Models\Organization;
class ClientModelTest extends ModelTestAbstract
{
public function test_it_belongs_to_a_organization(): void
{
// Arrange
$organization = Team::factory()->create();
$organization = Organization::factory()->create();
$client = Client::factory()->forOrganization($organization)->create();
// Act

View File

@@ -5,16 +5,16 @@ declare(strict_types=1);
namespace Tests\Unit\Model;
use App\Models\Client;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Task;
use App\Models\Team;
class ProjectModelTest extends ModelTestAbstract
{
public function test_it_belongs_to_a_organization(): void
{
// Arrange
$organization = Team::factory()->create();
$organization = Organization::factory()->create();
$project = Project::factory()->forOrganization($organization)->create();
// Act

View File

@@ -4,15 +4,15 @@ declare(strict_types=1);
namespace Tests\Unit\Model;
use App\Models\Organization;
use App\Models\Tag;
use App\Models\Team;
class TagModelTest extends ModelTestAbstract
{
public function test_it_belongs_to_a_organization(): void
{
// Arrange
$organization = Team::factory()->create();
$organization = Organization::factory()->create();
$task = Tag::factory()->forOrganization($organization)->create();
// Act

View File

@@ -4,16 +4,16 @@ declare(strict_types=1);
namespace Tests\Unit\Model;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Task;
use App\Models\Team;
class TaskModelTest extends ModelTestAbstract
{
public function test_it_belongs_to_a_organization(): void
{
// Arrange
$organization = Team::factory()->create();
$organization = Organization::factory()->create();
$task = Task::factory()->forOrganization($organization)->create();
// Act

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Tests\Unit\Model;
use App\Models\Organization;
use App\Models\Project;
use App\Models\Task;
use App\Models\Team;
use App\Models\TimeEntry;
use App\Models\User;
@@ -30,7 +30,7 @@ class TimeEntryModelTest extends ModelTestAbstract
public function test_it_belongs_to_a_organization(): void
{
// Arrange
$organization = Team::factory()->create();
$organization = Organization::factory()->create();
$timeEntry = TimeEntry::factory()->forOrganization($organization)->create();
// Act