further development of the predefinedmutations
This commit is contained in:
parent
ed23bb2a78
commit
11df4ef55c
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Dynamic;
|
use App\Models\Dynamic;
|
||||||
|
use App\Models\Ledger;
|
||||||
use App\Models\PredefinedMutation;
|
use App\Models\PredefinedMutation;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -15,20 +16,21 @@ class PredefinedMutationController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*/
|
*/
|
||||||
public function index(Dynamic $dynamic)
|
public function index(Dynamic $dynamic, Ledger $ledger)
|
||||||
{
|
{
|
||||||
$this->authorize('update', $dynamic);
|
$this->authorize('update', $dynamic);
|
||||||
|
|
||||||
return Inertia::render('Dynamics/PredefinedMutations/Index', [
|
return Inertia::render('Ledgers/PredefinedMutations/Index', [
|
||||||
'dynamic' => $dynamic,
|
'dynamic' => $dynamic,
|
||||||
'predefined_mutations' => $dynamic->predefinedMutations()->latest()->get(),
|
'ledger' => $ledger,
|
||||||
|
'predefined_mutations' => $ledger->predefinedMutations()->latest()->get(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
*/
|
*/
|
||||||
public function store(Request $request, Dynamic $dynamic)
|
public function store(Request $request, Dynamic $dynamic, Ledger $ledger)
|
||||||
{
|
{
|
||||||
$this->authorize('update', $dynamic);
|
$this->authorize('update', $dynamic);
|
||||||
|
|
||||||
@ -36,11 +38,10 @@ class PredefinedMutationController extends Controller
|
|||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'description' => ['nullable', 'string'],
|
'description' => ['nullable', 'string'],
|
||||||
'amount' => ['required', 'integer'],
|
'amount' => ['required', 'integer'],
|
||||||
'type' => ['required', 'string', 'in:reward,penalty'],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$dynamic->predefinedMutations()->create($request->all());
|
$ledger->predefinedMutations()->create($request->all());
|
||||||
|
|
||||||
return redirect()->route('dynamics.predefined-mutations.index', $dynamic);
|
return redirect()->route('dynamics.ledgers.predefined-mutations.index', [$dynamic, $ledger]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,11 @@ class Ledger extends Model
|
|||||||
return $this->hasMany(Mutation::class);
|
return $this->hasMany(Mutation::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function predefinedMutations(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(PredefinedMutation::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function media(): \Illuminate\Database\Eloquent\Relations\MorphMany
|
public function media(): \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||||
{
|
{
|
||||||
return $this->morphMany(Media::class, 'mediable');
|
return $this->morphMany(Media::class, 'mediable');
|
||||||
|
|||||||
@ -11,15 +11,14 @@ class PredefinedMutation extends Model
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'dynamic_id',
|
'ledger_id',
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'amount',
|
'amount',
|
||||||
'type',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public function dynamic(): BelongsTo
|
public function ledger(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Dynamic::class);
|
return $this->belongsTo(Ledger::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,11 +13,10 @@ return new class extends Migration
|
|||||||
{
|
{
|
||||||
Schema::create('predefined_mutations', function (Blueprint $table) {
|
Schema::create('predefined_mutations', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->foreignId('dynamic_id')->constrained()->cascadeOnDelete();
|
$table->foreignId('ledger_id')->constrained()->cascadeOnDelete();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->text('description')->nullable();
|
$table->text('description')->nullable();
|
||||||
$table->integer('amount');
|
$table->integer('amount');
|
||||||
$table->string('type')->default('reward');
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,12 +70,6 @@ function submit() {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8">
|
|
||||||
<InertiaLink :href="route('dynamics.predefined-mutations.index', dynamic.id)" class="c-dynamic-settings__submit-btn">
|
|
||||||
Manage Predefined Mutations
|
|
||||||
</InertiaLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -9,12 +9,15 @@ const props = defineProps<{
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
ledger: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
predefined_mutations: Array<{
|
predefined_mutations: Array<{
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
type: string;
|
|
||||||
}>;
|
}>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@ -22,7 +25,6 @@ const form = useForm({
|
|||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
amount: 0,
|
amount: 0,
|
||||||
type: 'reward',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const breadcrumbs = [
|
const breadcrumbs = [
|
||||||
@ -34,14 +36,18 @@ const breadcrumbs = [
|
|||||||
name: props.dynamic.name,
|
name: props.dynamic.name,
|
||||||
href: route('dynamics.show', props.dynamic.id),
|
href: route('dynamics.show', props.dynamic.id),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: props.ledger.name,
|
||||||
|
href: route('dynamics.ledgers.show', [props.dynamic.id, props.ledger.id]),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Predefined Mutations',
|
name: 'Predefined Mutations',
|
||||||
href: route('dynamics.predefined-mutations.index', props.dynamic.id),
|
href: route('dynamics.ledgers.predefined-mutations.index', [props.dynamic.id, props.ledger.id]),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function submit() {
|
function submit() {
|
||||||
form.post(route('dynamics.predefined-mutations.store', props.dynamic.id), {
|
form.post(route('dynamics.ledgers.predefined-mutations.store', [props.dynamic.id, props.ledger.id]), {
|
||||||
onSuccess: () => form.reset(),
|
onSuccess: () => form.reset(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -56,7 +62,7 @@ function submit() {
|
|||||||
<div class="c-predefined-mutations__card">
|
<div class="c-predefined-mutations__card">
|
||||||
<div class="c-predefined-mutations__body">
|
<div class="c-predefined-mutations__body">
|
||||||
<h3 class="c-predefined-mutations__title">
|
<h3 class="c-predefined-mutations__title">
|
||||||
Predefined Mutations for {{ dynamic.name }}
|
Predefined Mutations for {{ ledger.name }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="c-predefined-mutations__list">
|
<div class="c-predefined-mutations__list">
|
||||||
@ -133,22 +139,6 @@ function submit() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="c-predefined-mutations__field">
|
|
||||||
<label
|
|
||||||
for="type"
|
|
||||||
class="c-predefined-mutations__label"
|
|
||||||
>Type</label
|
|
||||||
>
|
|
||||||
<select
|
|
||||||
v-model="form.type"
|
|
||||||
id="type"
|
|
||||||
class="c-predefined-mutations__select"
|
|
||||||
>
|
|
||||||
<option value="reward">Reward</option>
|
|
||||||
<option value="penalty">Penalty</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="c-predefined-mutations__actions">
|
<div class="c-predefined-mutations__actions">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head } from '@inertiajs/vue3';
|
import { Head, Link as InertiaLink } from '@inertiajs/vue3';
|
||||||
import { useEcho } from '@laravel/echo-vue';
|
import { useEcho } from '@laravel/echo-vue';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { route } from 'ziggy-js';
|
import { route } from 'ziggy-js';
|
||||||
@ -168,6 +168,8 @@ function isOwnerUser(userId: number): boolean {
|
|||||||
<div class="c-ledger-show__container">
|
<div class="c-ledger-show__container">
|
||||||
<div class="c-ledger-show__card">
|
<div class="c-ledger-show__card">
|
||||||
<div class="c-ledger-show__body">
|
<div class="c-ledger-show__body">
|
||||||
|
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-4">
|
||||||
|
<div>
|
||||||
<h3 class="c-ledger-show__title">{{ ledger.name }}</h3>
|
<h3 class="c-ledger-show__title">{{ ledger.name }}</h3>
|
||||||
<p class="c-ledger-show__score">
|
<p class="c-ledger-show__score">
|
||||||
Score: {{ ledger.score }}
|
Score: {{ ledger.score }}
|
||||||
@ -175,6 +177,16 @@ function isOwnerUser(userId: number): boolean {
|
|||||||
<p class="c-ledger-show__rules">
|
<p class="c-ledger-show__rules">
|
||||||
{{ ledger.rules }}
|
{{ ledger.rules }}
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="isOwner" class="flex flex-col gap-2">
|
||||||
|
<InertiaLink
|
||||||
|
:href="route('dynamics.ledgers.predefined-mutations.index', [dynamic.id, ledger.id])"
|
||||||
|
class="c-ledger-show__manage-btn"
|
||||||
|
>
|
||||||
|
Predefined Mutations
|
||||||
|
</InertiaLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Ledger Alignment Badge / Subtitle -->
|
<!-- Ledger Alignment Badge / Subtitle -->
|
||||||
<div class="c-ledger-show__alignment-wrapper">
|
<div class="c-ledger-show__alignment-wrapper">
|
||||||
@ -288,6 +300,10 @@ function isOwnerUser(userId: number): boolean {
|
|||||||
@apply py-12;
|
@apply py-12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-ledger-show__manage-btn {
|
||||||
|
@apply inline-flex items-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-xs font-semibold tracking-widest text-white uppercase transition duration-150 ease-in-out hover:bg-gray-700 focus:bg-gray-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none active:bg-gray-900 dark:bg-gray-200 dark:text-gray-800 dark:hover:bg-white dark:focus:bg-white dark:focus:ring-offset-gray-800 dark:active:bg-gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
.c-ledger-show__container {
|
.c-ledger-show__container {
|
||||||
@apply mx-auto max-w-7xl sm:px-6 lg:px-8;
|
@apply mx-auto max-w-7xl sm:px-6 lg:px-8;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
Route::get('/dynamics/{dynamic}/ledgers/create', [LedgerController::class, 'create'])->name('dynamics.ledgers.create');
|
Route::get('/dynamics/{dynamic}/ledgers/create', [LedgerController::class, 'create'])->name('dynamics.ledgers.create');
|
||||||
Route::resource('dynamics.ledgers', LedgerController::class)->scoped()->except(['create']);
|
Route::resource('dynamics.ledgers', LedgerController::class)->scoped()->except(['create']);
|
||||||
|
|
||||||
Route::resource('dynamics.predefined-mutations', \App\Http\Controllers\PredefinedMutationController::class)->scoped();
|
Route::resource('dynamics.ledgers.predefined-mutations', \App\Http\Controllers\PredefinedMutationController::class)->scoped();
|
||||||
|
|
||||||
Route::resource('dynamics.ledgers.mutations', MutationController::class)->scoped();
|
Route::resource('dynamics.ledgers.mutations', MutationController::class)->scoped();
|
||||||
|
|
||||||
|
|||||||
92
tests/Feature/PredefinedMutationTest.php
Normal file
92
tests/Feature/PredefinedMutationTest.php
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Dynamic;
|
||||||
|
use App\Models\Ledger;
|
||||||
|
use App\Models\PredefinedMutation;
|
||||||
|
|
||||||
|
test('owner can view predefined mutations for ledger', function () {
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$dynamic = Dynamic::factory()->create();
|
||||||
|
$dynamic->participants()->attach($owner->id, ['role' => 'owner']);
|
||||||
|
$ledger = Ledger::factory()->create(['dynamic_id' => $dynamic->id]);
|
||||||
|
|
||||||
|
$predefined = PredefinedMutation::create([
|
||||||
|
'ledger_id' => $ledger->id,
|
||||||
|
'name' => 'Weekly Room Cleaning',
|
||||||
|
'description' => 'Cleaned up the master bedroom',
|
||||||
|
'amount' => 20,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->actingAs($owner);
|
||||||
|
|
||||||
|
$response = $this->get(route('dynamics.ledgers.predefined-mutations.index', [$dynamic->id, $ledger->id]));
|
||||||
|
|
||||||
|
$response->assertOk();
|
||||||
|
$response->assertInertia(fn ($page) => $page
|
||||||
|
->component('Ledgers/PredefinedMutations/Index')
|
||||||
|
->where('ledger.id', $ledger->id)
|
||||||
|
->has('predefined_mutations', 1)
|
||||||
|
->where('predefined_mutations.0.name', 'Weekly Room Cleaning')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non-owner cannot view predefined mutations for ledger', function () {
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$participant = User::factory()->create();
|
||||||
|
$dynamic = Dynamic::factory()->create();
|
||||||
|
$dynamic->participants()->attach($owner->id, ['role' => 'owner']);
|
||||||
|
$dynamic->participants()->attach($participant->id, ['role' => 'participant']);
|
||||||
|
$ledger = Ledger::factory()->create(['dynamic_id' => $dynamic->id]);
|
||||||
|
|
||||||
|
$this->actingAs($participant);
|
||||||
|
|
||||||
|
$response = $this->get(route('dynamics.ledgers.predefined-mutations.index', [$dynamic->id, $ledger->id]));
|
||||||
|
|
||||||
|
$response->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('owner can create predefined mutations for ledger', function () {
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$dynamic = Dynamic::factory()->create();
|
||||||
|
$dynamic->participants()->attach($owner->id, ['role' => 'owner']);
|
||||||
|
$ledger = Ledger::factory()->create(['dynamic_id' => $dynamic->id]);
|
||||||
|
|
||||||
|
$this->actingAs($owner);
|
||||||
|
|
||||||
|
$response = $this->post(route('dynamics.ledgers.predefined-mutations.store', [$dynamic->id, $ledger->id]), [
|
||||||
|
'name' => 'Polished mirrors',
|
||||||
|
'description' => 'Mirror polishing in dungeon',
|
||||||
|
'amount' => 15,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertRedirect();
|
||||||
|
$this->assertDatabaseHas('predefined_mutations', [
|
||||||
|
'ledger_id' => $ledger->id,
|
||||||
|
'name' => 'Polished mirrors',
|
||||||
|
'amount' => 15,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non-owner cannot create predefined mutations for ledger', function () {
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$participant = User::factory()->create();
|
||||||
|
$dynamic = Dynamic::factory()->create();
|
||||||
|
$dynamic->participants()->attach($owner->id, ['role' => 'owner']);
|
||||||
|
$dynamic->participants()->attach($participant->id, ['role' => 'participant']);
|
||||||
|
$ledger = Ledger::factory()->create(['dynamic_id' => $dynamic->id]);
|
||||||
|
|
||||||
|
$this->actingAs($participant);
|
||||||
|
|
||||||
|
$response = $this->post(route('dynamics.ledgers.predefined-mutations.store', [$dynamic->id, $ledger->id]), [
|
||||||
|
'name' => 'Polished mirrors',
|
||||||
|
'description' => 'Mirror polishing in dungeon',
|
||||||
|
'amount' => 15,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(403);
|
||||||
|
$this->assertDatabaseMissing('predefined_mutations', [
|
||||||
|
'ledger_id' => $ledger->id,
|
||||||
|
'name' => 'Polished mirrors',
|
||||||
|
]);
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user