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;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Enums\TeamMemberRole;
|
use App\Enums\TeamMemberRole;
|
||||||
|
use App\Http\Requests\AddTeamMemberRequest;
|
||||||
use App\Http\Requests\StoreTeamRequest;
|
use App\Http\Requests\StoreTeamRequest;
|
||||||
use App\Http\Requests\UpdateTeamRequest;
|
use App\Http\Requests\UpdateTeamRequest;
|
||||||
use App\Http\Resources\TeamResource;
|
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.
|
* 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 [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('team_invites', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignIdFor(Team::class)->constrained();
|
||||||
|
$table->foreignIdFor(User::class, 'invited_by_user_id')->constrained();
|
||||||
|
$table->string('invited_email');
|
||||||
|
$table->string('invite_token');
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['team_id', 'invited_email']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('team_invites');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ import InputError from '@/components/InputError.vue';
|
|||||||
import {Checkbox} from '@/components/ui/checkbox';
|
import {Checkbox} from '@/components/ui/checkbox';
|
||||||
import {ref, watch} from "vue";
|
import {ref, watch} from "vue";
|
||||||
import TeamSlug from "@/components/TeamSlug.vue";
|
import TeamSlug from "@/components/TeamSlug.vue";
|
||||||
|
import {Card, CardContent} from "@/components/ui/card";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
layout: {
|
layout: {
|
||||||
@@ -42,33 +43,37 @@ watch(() => (form.errors as any).slug, (newError) => slugError.value = newError)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Head title="Teams" />
|
<Head title="Teams"/>
|
||||||
|
|
||||||
<div class="flex flex-col justify-center p-16">
|
<div class="flex flex-col justify-center p-16">
|
||||||
<form class="grid w-full gap-5 py-4" @submit.prevent="submit">
|
<Card class="w-full">
|
||||||
<div class="grid grid-cols-2 gap-5 items-start">
|
<CardContent>
|
||||||
<div class="grid items-start gap-3">
|
<form class="grid w-full gap-5 py-4" @submit.prevent="submit">
|
||||||
<Label for="name">Name</Label>
|
<div class="grid grid-cols-2 gap-5 items-start">
|
||||||
<Input id="name" v-model="form.name" name="name" />
|
<div class="grid items-start gap-3">
|
||||||
<InputError :message="form.errors.name" />
|
<Label for="name">Name</Label>
|
||||||
</div>
|
<Input id="name" v-model="form.name" name="name"/>
|
||||||
<TeamSlug :name="form.name" :slug="slug" :slug-error="slugError" :slug-taken="slugTaken" />
|
<InputError :message="form.errors.name"/>
|
||||||
<div class="grid items-start gap-3">
|
</div>
|
||||||
<Label for="require_2fa">Require 2FA</Label>
|
<TeamSlug :name="form.name" :slug="slug" :slug-error="slugError" :slug-taken="slugTaken"/>
|
||||||
<Checkbox
|
<div class="grid items-start gap-3">
|
||||||
id="require_2fa"
|
<Label for="require_2fa">Require 2FA</Label>
|
||||||
v-model="form.require_2fa"
|
<Checkbox
|
||||||
name="require_2fa"
|
id="require_2fa"
|
||||||
/>
|
v-model="form.require_2fa"
|
||||||
<InputError :message="form.errors.require_2fa" />
|
name="require_2fa"
|
||||||
</div>
|
/>
|
||||||
</div>
|
<InputError :message="form.errors.require_2fa"/>
|
||||||
<div class="flex w-full justify-end">
|
</div>
|
||||||
<Button :disabled="form.processing || slugTaken" class="w-fit" type="submit">
|
</div>
|
||||||
<component :is="Save" />
|
<div class="flex w-full justify-end">
|
||||||
Erstellen
|
<Button :disabled="form.processing || slugTaken" class="w-fit" type="submit">
|
||||||
</Button>
|
<component :is="Save"/>
|
||||||
</div>
|
Erstellen
|
||||||
</form>
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {Checkbox} from '@/components/ui/checkbox';
|
|||||||
import {Team} from '@/types/models';
|
import {Team} from '@/types/models';
|
||||||
import {ref, watch} from "vue";
|
import {ref, watch} from "vue";
|
||||||
import TeamSlug from "@/components/TeamSlug.vue";
|
import TeamSlug from "@/components/TeamSlug.vue";
|
||||||
|
import {Card, CardContent} from "@/components/ui/card";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
team: Team;
|
team: Team;
|
||||||
@@ -48,30 +49,35 @@ watch(() => (form.errors as any).slug, (newError) => slugError.value = newError)
|
|||||||
<Head title="Teams"/>
|
<Head title="Teams"/>
|
||||||
|
|
||||||
<div class="flex flex-col justify-center p-16">
|
<div class="flex flex-col justify-center p-16">
|
||||||
<form class="grid w-full gap-5 py-4" @submit.prevent="submit">
|
<Card class="w-full">
|
||||||
<div class="grid grid-cols-2 gap-5 items-start">
|
<CardContent>
|
||||||
<div class="grid items-start gap-3">
|
<form class="grid w-full gap-5 py-4" @submit.prevent="submit">
|
||||||
<Label for="name">Name</Label>
|
<div class="grid grid-cols-2 gap-5 items-start">
|
||||||
<Input id="name" v-model="form.name" name="name"/>
|
<div class="grid items-start gap-3">
|
||||||
<InputError :message="form.errors.name"/>
|
<Label for="name">Name</Label>
|
||||||
</div>
|
<Input id="name" v-model="form.name" name="name"/>
|
||||||
<TeamSlug :ignored-slug="team.slug" :name="form.name" :slug="slug" :slug-error="slugError" :slug-taken="slugTaken" />
|
<InputError :message="form.errors.name"/>
|
||||||
<div class="grid items-start gap-3">
|
</div>
|
||||||
<Label for="require_2fa">Require 2FA</Label>
|
<TeamSlug :ignored-slug="team.slug" :name="form.name" :slug="slug" :slug-error="slugError"
|
||||||
<Checkbox
|
:slug-taken="slugTaken"/>
|
||||||
id="require_2fa"
|
<div class="grid items-start gap-3">
|
||||||
v-model="form.require_2fa"
|
<Label for="require_2fa">Require 2FA</Label>
|
||||||
name="require_2fa"
|
<Checkbox
|
||||||
/>
|
id="require_2fa"
|
||||||
<InputError :message="form.errors.require_2fa"/>
|
v-model="form.require_2fa"
|
||||||
</div>
|
name="require_2fa"
|
||||||
</div>
|
/>
|
||||||
<div class="flex w-full justify-end">
|
<InputError :message="form.errors.require_2fa"/>
|
||||||
<Button :disabled="form.processing || slugTaken" class="w-fit" type="submit">
|
</div>
|
||||||
<component :is="Save"/>
|
</div>
|
||||||
Speichern
|
<div class="flex w-full justify-end">
|
||||||
</Button>
|
<Button :disabled="form.processing || slugTaken" class="w-fit" type="submit">
|
||||||
</div>
|
<component :is="Save"/>
|
||||||
</form>
|
Speichern
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import teamRoutes from '@/routes/teams';
|
||||||
|
import {Form, Head, useForm} from '@inertiajs/vue3';
|
||||||
|
import {Card, CardContent} from "@/components/ui/card";
|
||||||
|
import {Input} from "@/components/ui/input";
|
||||||
|
import InputError from "@/components/InputError.vue";
|
||||||
|
import {Button} from "@/components/ui/button";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
layout: {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
title: 'Teams',
|
||||||
|
href: teamRoutes.index(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Invite',
|
||||||
|
href: teamRoutes.invite.create(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
inviteToken?: string,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
inviteToken: props.inviteToken
|
||||||
|
})
|
||||||
|
|
||||||
|
const submit = () => form.post(teamRoutes.invite.store().url)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Team Invite"/>
|
||||||
|
|
||||||
|
<div class="flex flex-col justify-center p-16">
|
||||||
|
<Card class="w-full">
|
||||||
|
<CardContent>
|
||||||
|
<h1 class="font-bold text-2xl">Team Einladung</h1>
|
||||||
|
<form class="grid gap-y-3" @submit.prevent="submit">
|
||||||
|
<div class="grid items-start gap-3">
|
||||||
|
<Input id="inviteToken" v-model="form.inviteToken" name="inviteToken" placeholder="Token"/>
|
||||||
|
<InputError :message="form.errors.inviteToken"/>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex justify-end">
|
||||||
|
|
||||||
|
<Button :disabled="form.processing" type="submit">Team Beitreten</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
+9
-1
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\TeamController;
|
use App\Http\Controllers\TeamController;
|
||||||
|
use App\Http\Controllers\TeamInviteAcceptController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::inertia('/', 'Welcome')->name('home');
|
Route::inertia('/', 'Welcome')->name('home');
|
||||||
@@ -9,15 +10,22 @@ Route::middleware(['auth'])->group(function () {
|
|||||||
Route::inertia('dashboard', 'Dashboard')->name('dashboard');
|
Route::inertia('dashboard', 'Dashboard')->name('dashboard');
|
||||||
|
|
||||||
Route::controller(TeamController::class)->prefix('teams')->name('teams.')->group(function () {
|
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::get('/', 'index')->name('index');
|
Route::get('/', 'index')->name('index');
|
||||||
Route::get('/create', 'create')->name('create');
|
Route::get('/create', 'create')->name('create');
|
||||||
Route::post('/', 'store')->name('store');
|
Route::post('/', 'store')->name('store');
|
||||||
Route::get('/{team:slug}', 'show')->name('show');
|
|
||||||
Route::get('/edit/{team:slug}', 'edit')->name('edit');
|
Route::get('/edit/{team:slug}', 'edit')->name('edit');
|
||||||
Route::patch('/{team:slug}', 'update')->name('update');
|
Route::patch('/{team:slug}', 'update')->name('update');
|
||||||
Route::post('/name-to-slug', 'nameToSlug')->name('nameToSlug');
|
Route::post('/name-to-slug', 'nameToSlug')->name('nameToSlug');
|
||||||
|
Route::get('/{team:slug}', 'show')->name('show');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
require __DIR__.'/settings.php';
|
require __DIR__.'/settings.php';
|
||||||
|
|||||||
Reference in New Issue
Block a user