Add: Delete participant
continuous-integration/drone/push Build is passing Details

This commit is contained in:
philipp lang 2024-04-25 21:49:31 +02:00
parent f05a919b9c
commit 8e3054da49
6 changed files with 90 additions and 5 deletions

View File

@ -0,0 +1,36 @@
<?php
namespace App\Form\Actions;
use App\Form\Models\Participant;
use App\Lib\JobMiddleware\JobChannels;
use App\Lib\JobMiddleware\WithJobState;
use App\Lib\Queue\TracksJob;
use Lorisleiva\Actions\Concerns\AsAction;
class ParticipantDestroyAction
{
use AsAction;
use TracksJob;
public function handle(int $participantId): void
{
Participant::findOrFail($participantId)->delete();
}
public function asController(Participant $participant): void
{
$this->startJob($participant->id);
}
/**
* @param mixed $parameters
*/
public function jobState(WithJobState $jobState, ...$parameters): WithJobState
{
return $jobState
->after('Teilnehmer gelöscht.')
->failed('Löschen von Teilnehmer fehlgeschlagen.')
->shouldReload(JobChannels::make()->add('participant'));
}
}

View File

@ -24,6 +24,9 @@ class ParticipantResource extends JsonResource
...$this->getModel()->getFields()->present(), ...$this->getModel()->getFields()->present(),
'created_at' => $this->created_at->format('Y-m-d H:i:s'), 'created_at' => $this->created_at->format('Y-m-d H:i:s'),
'created_at_display' => $this->created_at->format('d.m.Y'), 'created_at_display' => $this->created_at->format('d.m.Y'),
'links' => [
'destroy' => route('participant.destroy', ['participant' => $this->getModel()]),
]
]; ];
} }

View File

@ -1,5 +1,14 @@
<template> <template>
<div class="mt-5"> <div class="mt-5">
<ui-popup v-if="deleting !== null" heading="Teilnehmer*in löschen?" @close="deleting = null">
<div>
<p class="mt-4">Den*Die Teilnehmer*in löschen?</p>
<div class="grid grid-cols-2 gap-3 mt-6">
<a href="#" class="text-center btn btn-danger" @click.prevent="handleDelete">Mitglied loschen</a>
<a href="#" class="text-center btn btn-primary" @click.prevent="deleting = null">Abbrechen</a>
</div>
</div>
</ui-popup>
<page-filter breakpoint="lg"> <page-filter breakpoint="lg">
<f-multipleselect id="active_columns" v-model="activeColumnsConfig" :options="meta.columns" label="Aktive Spalten" size="sm" name="active_columns"></f-multipleselect> <f-multipleselect id="active_columns" v-model="activeColumnsConfig" :options="meta.columns" label="Aktive Spalten" size="sm" name="active_columns"></f-multipleselect>
</page-filter> </page-filter>
@ -9,13 +18,13 @@
<th></th> <th></th>
</thead> </thead>
<tr v-for="(form, index) in data" :key="index"> <tr v-for="(participant, index) in data" :key="index">
<td v-for="column in activeColumns" :key="column.id"> <td v-for="column in activeColumns" :key="column.id">
<div v-text="form[column.display_attribute]"></div> <div v-text="participant[column.display_attribute]"></div>
</td> </td>
<td> <td>
<a v-tooltip="`Bearbeiten`" href="#" class="ml-2 inline-flex btn btn-warning btn-sm" @click.prevent="edit(form)"><ui-sprite src="pencil"></ui-sprite></a> <a v-tooltip="`Bearbeiten`" href="#" class="ml-2 inline-flex btn btn-warning btn-sm" @click.prevent="edit(participant)"><ui-sprite src="pencil"></ui-sprite></a>
<a v-tooltip="`Löschen`" href="#" class="ml-2 inline-flex btn btn-danger btn-sm" @click.prevent="deleting = form"><ui-sprite src="trash"></ui-sprite></a> <a v-tooltip="`Löschen`" href="#" class="ml-2 inline-flex btn btn-danger btn-sm" @click.prevent="deleting = participant"><ui-sprite src="trash"></ui-sprite></a>
</td> </td>
</tr> </tr>
</table> </table>
@ -29,6 +38,8 @@
import {ref, computed} from 'vue'; import {ref, computed} from 'vue';
import {useApiIndex} from '../../composables/useApiIndex.js'; import {useApiIndex} from '../../composables/useApiIndex.js';
const deleting = ref(null);
const props = defineProps({ const props = defineProps({
url: { url: {
type: String, type: String,
@ -37,7 +48,7 @@ const props = defineProps({
}, },
}); });
var {meta, data, reload, reloadPage, axios} = useApiIndex(props.url, 'participant'); var {meta, data, reload, reloadPage, axios, remove} = useApiIndex(props.url, 'participant');
const activeColumns = computed(() => meta.value.columns.filter((c) => meta.value.form_meta.active_columns.includes(c.id))); const activeColumns = computed(() => meta.value.columns.filter((c) => meta.value.form_meta.active_columns.includes(c.id)));
@ -53,5 +64,10 @@ const activeColumnsConfig = computed({
}, },
}); });
async function handleDelete() {
await remove(deleting.value);
deleting.value = null;
}
await reload(); await reload();
</script> </script>

View File

@ -31,6 +31,7 @@ use App\Form\Actions\FormtemplateUpdateAction;
use App\Form\Actions\FormUpdateAction; use App\Form\Actions\FormUpdateAction;
use App\Form\Actions\FormUpdateMetaAction; use App\Form\Actions\FormUpdateMetaAction;
use App\Form\Actions\IsDirtyAction; use App\Form\Actions\IsDirtyAction;
use App\Form\Actions\ParticipantDestroyAction;
use App\Form\Actions\ParticipantIndexAction; use App\Form\Actions\ParticipantIndexAction;
use App\Initialize\Actions\InitializeAction; use App\Initialize\Actions\InitializeAction;
use App\Initialize\Actions\InitializeFormAction; use App\Initialize\Actions\InitializeFormAction;
@ -162,4 +163,5 @@ Route::group(['middleware' => 'auth:web'], function (): void {
Route::patch('/form/{form}/meta', FormUpdateMetaAction::class)->name('form.update-meta'); Route::patch('/form/{form}/meta', FormUpdateMetaAction::class)->name('form.update-meta');
Route::get('/form/{form}/participants', ParticipantIndexAction::class)->name('form.participant.index'); Route::get('/form/{form}/participants', ParticipantIndexAction::class)->name('form.participant.index');
Route::post('/form/{form}/is-dirty', IsDirtyAction::class)->name('form.is-dirty'); Route::post('/form/{form}/is-dirty', IsDirtyAction::class)->name('form.is-dirty');
Route::delete('/participant/{participant}', ParticipantDestroyAction::class)->name('participant.destroy');
}); });

View File

@ -0,0 +1,27 @@
<?php
namespace Tests\Feature\Form;
use App\Form\Models\Form;
use App\Form\Models\Participant;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ParticipantDestroyActionTest extends FormTestCase
{
use DatabaseTransactions;
public function testItCanDestroyAParticipant(): void
{
$this->login()->loginNami()->withoutExceptionHandling();
$form = Form::factory()
->has(Participant::factory())
->sections([])
->create();
$this->deleteJson(route('participant.destroy', ['participant' => $form->participants->first()]))
->assertOk();
$this->assertDatabaseCount('participants', 0);
}
}

View File

@ -45,6 +45,7 @@ class ParticipantIndexActionTest extends FormTestCase
->assertJsonPath('data.0.birthday', '1991-04-20') ->assertJsonPath('data.0.birthday', '1991-04-20')
->assertJsonPath('data.0.select', ['A', 'B']) ->assertJsonPath('data.0.select', ['A', 'B'])
->assertJsonPath('data.0.select_display', 'A, B') ->assertJsonPath('data.0.select_display', 'A, B')
->assertJsonPath('data.0.links.destroy', route('participant.destroy', ['participant' => $form->participants->first()]))
->assertJsonPath('meta.columns.0.name', 'Vorname') ->assertJsonPath('meta.columns.0.name', 'Vorname')
->assertJsonPath('meta.columns.0.base_type', class_basename(TextField::class)) ->assertJsonPath('meta.columns.0.base_type', class_basename(TextField::class))
->assertJsonPath('meta.columns.0.id', 'vorname') ->assertJsonPath('meta.columns.0.id', 'vorname')