Update course actions
This commit is contained in:
parent
6d4dda869a
commit
36c0ebced0
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Actions;
|
||||
|
||||
use App\Course\Models\CourseMember;
|
||||
use App\Lib\JobMiddleware\JobChannels;
|
||||
use App\Lib\JobMiddleware\WithJobState;
|
||||
use App\Lib\Queue\TracksJob;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class CourseDestroyAction
|
||||
{
|
||||
use AsAction;
|
||||
use TracksJob;
|
||||
|
||||
public function handle(CourseMember $course): void
|
||||
{
|
||||
app(NamiSettings::class)->login()->deleteCourse($course->member->nami_id, $course->nami_id);
|
||||
|
||||
$course->delete();
|
||||
}
|
||||
|
||||
public function asController(CourseMember $course): JsonResponse
|
||||
{
|
||||
$this->startJob($course);
|
||||
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $parameters
|
||||
*/
|
||||
public function jobState(WithJobState $jobState, ...$parameters): WithJobState
|
||||
{
|
||||
$member = $parameters[0]->member;
|
||||
|
||||
return $jobState
|
||||
->before('Ausbildung für ' . $member->fullname . ' wird gelöscht')
|
||||
->after('Ausbildung für ' . $member->fullname . ' gelöscht')
|
||||
->failed('Fehler beim Löschen der Ausbildung für ' . $member->fullname)
|
||||
->shouldReload(JobChannels::make()->add('member')->add('course'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Actions;
|
||||
|
||||
use App\Course\Models\Course;
|
||||
use App\Course\Resources\CourseMemberResource;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class CourseIndexAction
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
/**
|
||||
* @return Collection<int, Course>
|
||||
*/
|
||||
public function handle(Member $member): Collection
|
||||
{
|
||||
return $member->courses()->with('course')->get();
|
||||
}
|
||||
|
||||
public function asController(Member $member): AnonymousResourceCollection
|
||||
{
|
||||
return CourseMemberResource::collection($this->handle($member))
|
||||
->additional([
|
||||
'meta' => CourseMemberResource::memberMeta($member),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Actions;
|
||||
|
||||
use App\Course\Models\Course;
|
||||
use App\Lib\JobMiddleware\JobChannels;
|
||||
use App\Lib\JobMiddleware\WithJobState;
|
||||
use App\Lib\Queue\TracksJob;
|
||||
use App\Member\Member;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class CourseStoreAction
|
||||
{
|
||||
use AsAction;
|
||||
use TracksJob;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $attributes
|
||||
*/
|
||||
public function handle(Member $member, array $attributes): void
|
||||
{
|
||||
$course = Course::where('id', $attributes['course_id'])->firstOrFail();
|
||||
|
||||
$payload = collect($attributes)->only(['event_name', 'completed_at', 'organizer'])->merge([
|
||||
'course_id' => $course->nami_id,
|
||||
])->toArray();
|
||||
|
||||
$namiId = app(NamiSettings::class)->login()->createCourse($member->nami_id, $payload);
|
||||
|
||||
$member->courses()->create([
|
||||
...$attributes,
|
||||
'nami_id' => $namiId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'organizer' => 'required|max:255',
|
||||
'event_name' => 'required|max:255',
|
||||
'completed_at' => 'required|date',
|
||||
'course_id' => 'required|exists:courses,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function asController(Member $member, ActionRequest $request): JsonResponse
|
||||
{
|
||||
$this->startJob($member, $request->validated());
|
||||
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $parameters
|
||||
*/
|
||||
public function jobState(WithJobState $jobState, ...$parameters): WithJobState
|
||||
{
|
||||
$member = $parameters[0];
|
||||
|
||||
return $jobState
|
||||
->before('Ausbildung für ' . $member->fullname . ' wird gespeichert')
|
||||
->after('Ausbildung für ' . $member->fullname . ' gespeichert')
|
||||
->failed('Fehler beim Erstellen der Ausbildung für ' . $member->fullname)
|
||||
->shouldReload(JobChannels::make()->add('member')->add('course'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Actions;
|
||||
|
||||
use App\Course\Models\Course;
|
||||
use App\Course\Models\CourseMember;
|
||||
use App\Course\Resources\CourseMemberResource;
|
||||
use App\Lib\JobMiddleware\JobChannels;
|
||||
use App\Lib\JobMiddleware\WithJobState;
|
||||
use App\Lib\Queue\TracksJob;
|
||||
use App\Member\Member;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class CourseUpdateAction
|
||||
{
|
||||
use AsAction;
|
||||
use TracksJob;
|
||||
|
||||
/**
|
||||
* @return Collection<int, Course>
|
||||
*/
|
||||
public function handle(CourseMember $course, array $attributes): void
|
||||
{
|
||||
app(NamiSettings::class)->login()->updateCourse(
|
||||
$course->member->nami_id,
|
||||
$course->nami_id,
|
||||
[
|
||||
...$attributes,
|
||||
'course_id' => Course::find($attributes['course_id'])->nami_id,
|
||||
]
|
||||
);
|
||||
|
||||
$course->update($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'organizer' => 'required|max:255',
|
||||
'event_name' => 'required|max:255',
|
||||
'completed_at' => 'required|date',
|
||||
'course_id' => 'required|exists:courses,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function asController(CourseMember $course, ActionRequest $request): JsonResponse
|
||||
{
|
||||
$this->startJob($course, $request->validated());
|
||||
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $parameters
|
||||
*/
|
||||
public function jobState(WithJobState $jobState, ...$parameters): WithJobState
|
||||
{
|
||||
$member = $parameters[0]->member;
|
||||
|
||||
return $jobState
|
||||
->before('Ausbildung für ' . $member->fullname . ' wird gespeichert')
|
||||
->after('Ausbildung für ' . $member->fullname . ' gespeichert')
|
||||
->failed('Fehler beim Erstellen der Ausbildung für ' . $member->fullname)
|
||||
->shouldReload(JobChannels::make()->add('member')->add('course'));
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Controllers;
|
||||
|
||||
use App\Course\Models\CourseMember;
|
||||
use App\Course\Requests\DestroyRequest;
|
||||
use App\Course\Requests\StoreRequest;
|
||||
use App\Course\Requests\UpdateRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Member\Member;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class CourseController extends Controller
|
||||
{
|
||||
public function store(Member $member, StoreRequest $request, NamiSettings $settings): RedirectResponse
|
||||
{
|
||||
$request->persist($member, $settings);
|
||||
|
||||
return redirect()->back()->success('Ausbildung erstellt');
|
||||
}
|
||||
|
||||
public function update(Member $member, CourseMember $course, UpdateRequest $request, NamiSettings $settings): RedirectResponse
|
||||
{
|
||||
$request->persist($member, $course, $settings);
|
||||
|
||||
return redirect()->back()->success('Ausbildung aktualisiert');
|
||||
}
|
||||
|
||||
public function destroy(Member $member, CourseMember $course, DestroyRequest $request, NamiSettings $settings): RedirectResponse
|
||||
{
|
||||
$request->persist($member, $course, $settings);
|
||||
|
||||
return redirect()->back()->success('Ausbildung gelöscht');
|
||||
}
|
||||
}
|
|
@ -22,4 +22,12 @@ class Course extends Model
|
|||
->trim()
|
||||
->replaceMatches('/ - .*/', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{id: int, name: string}>
|
||||
*/
|
||||
public static function forSelect(): array
|
||||
{
|
||||
return static::select('name', 'id')->get()->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Course\Models;
|
||||
|
||||
use App\Member\Member;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
@ -20,4 +21,12 @@ class CourseMember extends Model
|
|||
{
|
||||
return $this->belongsTo(Course::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Member, self>
|
||||
*/
|
||||
public function member(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Member::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Requests;
|
||||
|
||||
use App\Course\Models\CourseMember;
|
||||
use App\Member\Member;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DestroyRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function persist(Member $member, CourseMember $course, NamiSettings $settings): void
|
||||
{
|
||||
$settings->login()->deleteCourse($member->nami_id, $course->nami_id);
|
||||
|
||||
$course->delete();
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Requests;
|
||||
|
||||
use App\Course\Models\Course;
|
||||
use App\Member\Member;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'organizer' => 'required|max:255',
|
||||
'event_name' => 'required|max:255',
|
||||
'completed_at' => 'required|date',
|
||||
'course_id' => 'required|exists:courses,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function persist(Member $member, NamiSettings $settings): void
|
||||
{
|
||||
$course = Course::where('id', $this->input('course_id'))->firstOrFail();
|
||||
|
||||
$payload = collect($this->input())->only(['event_name', 'completed_at', 'organizer'])->merge([
|
||||
'course_id' => $course->nami_id,
|
||||
])->toArray();
|
||||
|
||||
$namiId = $settings->login()->createCourse($member->nami_id, $payload);
|
||||
|
||||
$member->courses()->create($this->safe()->collect()->put('nami_id', $namiId)->toArray());
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Course\Requests;
|
||||
|
||||
use App\Course\Models\Course;
|
||||
use App\Course\Models\CourseMember;
|
||||
use App\Member\Member;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'organizer' => 'required|max:255',
|
||||
'event_name' => 'required|max:255',
|
||||
'completed_at' => 'required|date',
|
||||
'course_id' => 'required|exists:courses,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function persist(Member $member, CourseMember $course, NamiSettings $settings): void
|
||||
{
|
||||
$settings->login()->updateCourse(
|
||||
$member->nami_id,
|
||||
$course->nami_id,
|
||||
$this->safe()->merge(['course_id' => Course::find($this->input('course_id'))->nami_id])->toArray()
|
||||
);
|
||||
|
||||
$course->update($this->safe()->toArray());
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace App\Course\Resources;
|
||||
|
||||
use App\Course\Models\Course;
|
||||
use App\Member\Member;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
|
@ -28,6 +30,29 @@ class CourseMemberResource extends JsonResource
|
|||
'course_name' => $this->course->name,
|
||||
'course_id' => $this->course->id,
|
||||
'course' => new CourseResource($this->whenLoaded('course')),
|
||||
'links' => [
|
||||
'update' => route('course.update', ['course' => $this->getModel()]),
|
||||
'destroy' => route('course.destroy', ['course' => $this->getModel()]),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function memberMeta(Member $member): array
|
||||
{
|
||||
return [
|
||||
'default' => [
|
||||
'event_name' => '',
|
||||
'completed_at' => null,
|
||||
'course_id' => null,
|
||||
'organizer' => ''
|
||||
],
|
||||
'courses' => Course::forSelect(),
|
||||
'links' => [
|
||||
'store' => route('member.course.store', ['member' => $member]),
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class MemberController extends Controller
|
|||
'data' => MemberResource::collection(Member::search($filter->search)->query(
|
||||
fn ($q) => $q->select('*')
|
||||
->withFilter($filter)
|
||||
->with(['courses', 'subscription', 'leaderMemberships', 'ageGroupMemberships'])
|
||||
->with(['subscription', 'leaderMemberships', 'ageGroupMemberships'])
|
||||
->withPendingPayment()
|
||||
->ordered()
|
||||
)->paginate(15)),
|
||||
|
|
|
@ -107,6 +107,7 @@ class MemberResource extends JsonResource
|
|||
'links' => [
|
||||
'membership_index' => route('member.membership.index', ['member' => $this->getModel()]),
|
||||
'payment_index' => route('member.payment.index', ['member' => $this->getModel()]),
|
||||
'course_index' => route('member.course.index', ['member' => $this->getModel()]),
|
||||
'show' => route('member.show', ['member' => $this->getModel()]),
|
||||
'edit' => route('member.edit', ['member' => $this->getModel()]),
|
||||
],
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="fixed w-full w-[80vw] max-w-[30rem] shadow-2xl bg-gray-600 right-0 top-0 h-full flex flex-col group is-bright">
|
||||
<div
|
||||
class="fixed w-full w-[80vw] max-w-[40rem] shadow-2xl bg-gray-600 right-0 top-0 h-full flex flex-col group is-bright">
|
||||
<suspense>
|
||||
<slot></slot>
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
<form v-if="single" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
||||
<f-text id="completed_at" v-model="single.completed_at" type="date" label="Datum" required></f-text>
|
||||
<f-select id="course_id" v-model="single.course_id" name="course_id" :options="courses" label="Baustein"
|
||||
<f-select id="course_id" v-model="single.course_id" name="course_id" :options="meta.courses" label="Baustein"
|
||||
required></f-select>
|
||||
<f-text id="event_name" v-model="single.event_name" label="Veranstaltung" required></f-text>
|
||||
<f-text id="organizer" v-model="single.organizer" label="Veranstalter" required></f-text>
|
||||
|
@ -28,18 +28,16 @@
|
|||
<th></th>
|
||||
</thead>
|
||||
|
||||
<tr v-for="(course, index) in value.courses" :key="index">
|
||||
<tr v-for="(course, index) in data" :key="index">
|
||||
<td v-text="course.course_name"></td>
|
||||
<td v-text="course.event_name"></td>
|
||||
<td v-text="course.organizer"></td>
|
||||
<td v-text="course.completed_at_human"></td>
|
||||
<td class="flex">
|
||||
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="
|
||||
single = course;
|
||||
mode = 'edit';
|
||||
"><ui-sprite src="pencil"></ui-sprite></a>
|
||||
<i-link href="#" class="inline-flex btn btn-danger btn-sm"
|
||||
@click.prevent="remove(course)"><ui-sprite src="trash"></ui-sprite></i-link>
|
||||
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="edit(course)"><ui-sprite
|
||||
src="pencil"></ui-sprite></a>
|
||||
<a href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(course)"><ui-sprite
|
||||
src="trash"></ui-sprite></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -47,55 +45,18 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script setup>
|
||||
defineEmits(['close']);
|
||||
import { useApiIndex } from '../../composables/useApiIndex.js';
|
||||
|
||||
props: {
|
||||
courses: {},
|
||||
value: {},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
mode: null,
|
||||
single: null,
|
||||
};
|
||||
const props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
methods: {
|
||||
create() {
|
||||
this.mode = 'create';
|
||||
this.single = {};
|
||||
},
|
||||
cancel() {
|
||||
this.mode = this.single = null;
|
||||
},
|
||||
remove(payment) {
|
||||
this.$inertia.delete(`/member/${this.value.id}/course/${payment.id}`);
|
||||
},
|
||||
const { data, meta, reload, cancel, single, create, edit, submit, remove } = useApiIndex(props.url, 'course');
|
||||
|
||||
openLink(link) {
|
||||
if (link.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.open(link.href);
|
||||
},
|
||||
|
||||
submit() {
|
||||
var _self = this;
|
||||
|
||||
this.mode === 'create'
|
||||
? this.$inertia.post(`/member/${this.value.id}/course`, this.single, {
|
||||
onFinish() {
|
||||
_self.single = null;
|
||||
},
|
||||
})
|
||||
: this.$inertia.patch(`/member/${this.value.id}/course/${this.single.id}`, this.single, {
|
||||
onFinish() {
|
||||
_self.single = null;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
await reload();
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
<template>
|
||||
<page-layout page-class="pb-6">
|
||||
<template #toolbar>
|
||||
<page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Mitglied anlegen</page-toolbar-button>
|
||||
<page-toolbar-button v-if="hasModule('bill')" :href="meta.links.allpayment" color="primary" icon="invoice">Rechnungen erstellen</page-toolbar-button>
|
||||
<page-toolbar-button v-if="hasModule('bill')" :href="meta.links.sendpayment" color="info" icon="envelope">Rechnungen versenden</page-toolbar-button>
|
||||
<page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Mitglied
|
||||
anlegen</page-toolbar-button>
|
||||
<page-toolbar-button v-if="hasModule('bill')" :href="meta.links.allpayment" color="primary"
|
||||
icon="invoice">Rechnungen erstellen</page-toolbar-button>
|
||||
<page-toolbar-button v-if="hasModule('bill')" :href="meta.links.sendpayment" color="info"
|
||||
icon="envelope">Rechnungen versenden</page-toolbar-button>
|
||||
</template>
|
||||
<ui-popup v-if="deleting !== null" heading="Mitglied löschen?" @close="deleting.reject()">
|
||||
<div>
|
||||
<p class="mt-4">Das Mitglied "{{ deleting.member.fullname }}" löschen?</p>
|
||||
<p class="mt-2">Alle Zuordnungen (Ausbildungen, Rechnungen, Zahlungen, Tätigkeiten) werden ebenfalls entfernt.</p>
|
||||
<ui-note v-if="!deleting.member.has_nami" class="mt-5" type="warning"> Dieses Mitglied ist nicht in NaMi vorhanden und wird daher nur in der AdReMa gelöscht werden. </ui-note>
|
||||
<p class="mt-2">Alle Zuordnungen (Ausbildungen, Rechnungen, Zahlungen, Tätigkeiten) werden ebenfalls
|
||||
entfernt.</p>
|
||||
<ui-note v-if="!deleting.member.has_nami" class="mt-5" type="warning"> Dieses Mitglied ist nicht in NaMi
|
||||
vorhanden und wird daher nur in der AdReMa gelöscht werden. </ui-note>
|
||||
<ui-note v-if="deleting.member.has_nami" class="mt-5" type="danger">
|
||||
Dieses Mitglied ist in NaMi vorhanden und wird daher in NaMi abgemeldet werden. Sofern "Datenweiterverwendung" eingeschaltet ist, wird das Mitglied auf inaktiv gesetzt.
|
||||
Dieses Mitglied ist in NaMi vorhanden und wird daher in NaMi abgemeldet werden. Sofern
|
||||
"Datenweiterverwendung" eingeschaltet ist, wird das Mitglied auf inaktiv gesetzt.
|
||||
</ui-note>
|
||||
<div class="grid grid-cols-2 gap-3 mt-6">
|
||||
<a href="#" class="text-center btn btn-danger" @click.prevent="deleting.resolve">Mitglied loschen</a>
|
||||
|
@ -20,45 +26,22 @@
|
|||
</div>
|
||||
</ui-popup>
|
||||
<page-filter breakpoint="xl">
|
||||
<f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text>
|
||||
<f-switch v-show="hasModule('bill')" id="ausstand" :model-value="getFilter('ausstand')" label="Nur Ausstände" size="sm" @update:model-value="setFilter('ausstand', $event)"></f-switch>
|
||||
<f-multipleselect
|
||||
id="group_ids"
|
||||
:options="meta.groups"
|
||||
:model-value="getFilter('group_ids')"
|
||||
label="Gruppierungen"
|
||||
size="sm"
|
||||
name="group_ids"
|
||||
@update:model-value="setFilter('group_ids', $event)"
|
||||
></f-multipleselect>
|
||||
<f-select
|
||||
v-show="hasModule('bill')"
|
||||
id="billKinds"
|
||||
name="billKinds"
|
||||
:options="meta.billKinds"
|
||||
:model-value="getFilter('bill_kind')"
|
||||
label="Rechnung"
|
||||
size="sm"
|
||||
@update:model-value="setFilter('bill_kind', $event)"
|
||||
></f-select>
|
||||
<f-multipleselect
|
||||
id="activity_ids"
|
||||
:options="meta.filterActivities"
|
||||
:model-value="getFilter('activity_ids')"
|
||||
label="Tätigkeiten"
|
||||
size="sm"
|
||||
name="activity_ids"
|
||||
@update:model-value="setFilter('activity_ids', $event)"
|
||||
></f-multipleselect>
|
||||
<f-multipleselect
|
||||
id="subactivity_ids"
|
||||
:options="meta.filterSubactivities"
|
||||
:model-value="getFilter('subactivity_ids')"
|
||||
label="Untertätigkeiten"
|
||||
size="sm"
|
||||
name="subactivity_ids"
|
||||
@update:model-value="setFilter('subactivity_ids', $event)"
|
||||
></f-multipleselect>
|
||||
<f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm"
|
||||
@update:model-value="setFilter('search', $event)"></f-text>
|
||||
<f-switch v-show="hasModule('bill')" id="ausstand" :model-value="getFilter('ausstand')" label="Nur Ausstände"
|
||||
size="sm" @update:model-value="setFilter('ausstand', $event)"></f-switch>
|
||||
<f-multipleselect id="group_ids" :options="meta.groups" :model-value="getFilter('group_ids')"
|
||||
label="Gruppierungen" size="sm" name="group_ids"
|
||||
@update:model-value="setFilter('group_ids', $event)"></f-multipleselect>
|
||||
<f-select v-show="hasModule('bill')" id="billKinds" name="billKinds" :options="meta.billKinds"
|
||||
:model-value="getFilter('bill_kind')" label="Rechnung" size="sm"
|
||||
@update:model-value="setFilter('bill_kind', $event)"></f-select>
|
||||
<f-multipleselect id="activity_ids" :options="meta.filterActivities" :model-value="getFilter('activity_ids')"
|
||||
label="Tätigkeiten" size="sm" name="activity_ids"
|
||||
@update:model-value="setFilter('activity_ids', $event)"></f-multipleselect>
|
||||
<f-multipleselect id="subactivity_ids" :options="meta.filterSubactivities"
|
||||
:model-value="getFilter('subactivity_ids')" label="Untertätigkeiten" size="sm" name="subactivity_ids"
|
||||
@update:model-value="setFilter('subactivity_ids', $event)"></f-multipleselect>
|
||||
<button class="btn btn-primary label mr-2" @click.prevent="exportMembers">
|
||||
<ui-sprite class="w-3 h-3 xl:mr-2" src="save"></ui-sprite>
|
||||
<span class="hidden xl:inline">Exportieren</span>
|
||||
|
@ -107,11 +90,14 @@
|
|||
<div class="text-xs text-gray-200" v-text="member.full_address"></div>
|
||||
<div class="flex items-center mt-1 space-x-4">
|
||||
<tags :member="member"></tags>
|
||||
<ui-label v-show="hasModule('bill')" class="text-gray-100 block" :value="member.pending_payment" fallback=""></ui-label>
|
||||
<ui-label v-show="hasModule('bill')" class="text-gray-100 block" :value="member.pending_payment"
|
||||
fallback=""></ui-label>
|
||||
</div>
|
||||
<actions class="mt-2" :member="member" @sidebar="openSidebar($event, member)" @remove="remove(member)"> </actions>
|
||||
<actions class="mt-2" :member="member" @sidebar="openSidebar($event, member)" @remove="remove(member)">
|
||||
</actions>
|
||||
<div class="absolute right-0 top-0 h-full flex items-center mr-2">
|
||||
<i-link v-tooltip="`Details`" :href="member.links.show"><ui-sprite src="chevron" class="w-6 h-6 text-teal-100 -rotate-90"></ui-sprite></i-link>
|
||||
<i-link v-tooltip="`Details`" :href="member.links.show"><ui-sprite src="chevron"
|
||||
class="w-6 h-6 text-teal-100 -rotate-90"></ui-sprite></i-link>
|
||||
</div>
|
||||
</ui-box>
|
||||
</div>
|
||||
|
@ -121,9 +107,12 @@
|
|||
</div>
|
||||
|
||||
<ui-sidebar v-if="single !== null" @close="closeSidebar">
|
||||
<member-payments v-if="single.type === 'payment'" :url="single.model.links.payment_index" @close="closeSidebar"></member-payments>
|
||||
<member-memberships v-if="single.type === 'membership'" :url="single.model.links.membership_index" @close="closeSidebar"></member-memberships>
|
||||
<member-courses v-if="single.type === 'courses'" :courses="meta.courses" :value="single.model" @close="closeSidebar"></member-courses>
|
||||
<member-payments v-if="single.type === 'payment'" :url="single.model.links.payment_index"
|
||||
@close="closeSidebar"></member-payments>
|
||||
<member-memberships v-if="single.type === 'membership'" :url="single.model.links.membership_index"
|
||||
@close="closeSidebar"></member-memberships>
|
||||
<member-courses v-if="single.type === 'courses'" :url="single.model.links.course_index"
|
||||
@close="closeSidebar"></member-courses>
|
||||
</ui-sidebar>
|
||||
</page-layout>
|
||||
</template>
|
||||
|
@ -134,14 +123,14 @@ import MemberMemberships from './MemberMemberships.vue';
|
|||
import MemberCourses from './MemberCourses.vue';
|
||||
import Tags from './Tags.vue';
|
||||
import Actions from './index/Actions.vue';
|
||||
import {indexProps, useIndex} from '../../composables/useIndex.js';
|
||||
import {ref, defineProps} from 'vue';
|
||||
import { indexProps, useIndex } from '../../composables/useIndex.js';
|
||||
import { ref, defineProps } from 'vue';
|
||||
|
||||
const single = ref(null);
|
||||
const deleting = ref(null);
|
||||
|
||||
const props = defineProps(indexProps);
|
||||
var {router, data, meta, getFilter, setFilter, filterString} = useIndex(props.data, 'member');
|
||||
var { router, data, meta, getFilter, setFilter, filterString } = useIndex(props.data, 'member');
|
||||
|
||||
function exportMembers() {
|
||||
window.open(`/member-export?filter=${filterString.value}`);
|
||||
|
@ -149,7 +138,7 @@ function exportMembers() {
|
|||
|
||||
async function remove(member) {
|
||||
new Promise((resolve, reject) => {
|
||||
deleting.value = {resolve, reject, member};
|
||||
deleting.value = { resolve, reject, member };
|
||||
})
|
||||
.then(() => {
|
||||
router.delete(`/member/${member.id}`);
|
||||
|
|
|
@ -12,7 +12,10 @@ use App\Activity\Api\SubactivityUpdateAction;
|
|||
use App\Contribution\Actions\FormAction as ContributionFormAction;
|
||||
use App\Contribution\Actions\GenerateAction as ContributionGenerateAction;
|
||||
use App\Contribution\Actions\ValidateAction as ContributionValidateAction;
|
||||
use App\Course\Controllers\CourseController;
|
||||
use App\Course\Actions\CourseDestroyAction;
|
||||
use App\Course\Actions\CourseIndexAction;
|
||||
use App\Course\Actions\CourseStoreAction;
|
||||
use App\Course\Actions\CourseUpdateAction;
|
||||
use App\Dashboard\Actions\IndexAction as DashboardIndexAction;
|
||||
use App\Efz\ShowEfzDocumentAction;
|
||||
use App\Group\Actions\ListAction;
|
||||
|
@ -72,7 +75,6 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
|||
->name('member.singlepdf');
|
||||
Route::get('/sendpayment', [SendpaymentController::class, 'create'])->name('sendpayment.create');
|
||||
Route::get('/sendpayment/pdf', [SendpaymentController::class, 'send'])->name('sendpayment.pdf');
|
||||
Route::resource('member.course', CourseController::class);
|
||||
Route::get('/member/{member}/efz', ShowEfzDocumentAction::class)->name('efz');
|
||||
Route::get('/member/{member}/resync', MemberResyncAction::class)->name('member.resync');
|
||||
Route::get('member-export', ExportAction::class)->name('member-export');
|
||||
|
@ -118,4 +120,10 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
|||
Route::delete('/membership/{membership}', MembershipDestroyAction::class)->name('membership.destroy');
|
||||
Route::post('/api/membership/member-list', ListForGroupAction::class)->name('membership.member-list');
|
||||
Route::post('/api/membership/sync', StoreForGroupAction::class)->name('membership.sync');
|
||||
|
||||
// ----------------------------------- course ----------------------------------
|
||||
Route::get('/member/{member}/course', CourseIndexAction::class)->name('member.course.index');
|
||||
Route::post('/member/{member}/course', CourseStoreAction::class)->name('member.course.store');
|
||||
Route::patch('/course/{course}', CourseUpdateAction::class)->name('course.update');
|
||||
Route::delete('/course/{course}', CourseDestroyAction::class)->name('course.destroy');
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ class DeleteTest extends TestCase
|
|||
app(CourseFake::class)->deletesSuccessfully(123, 999);
|
||||
$member = Member::factory()->defaults()->inNami(123)->has(CourseMember::factory()->inNami(999)->for(Course::factory()), 'courses')->createOne();
|
||||
|
||||
$this->delete("/member/{$member->id}/course/{$member->courses->first()->id}");
|
||||
$this->delete("//course/{$member->courses->first()->id}");
|
||||
|
||||
$this->assertDatabaseCount('course_members', 0);
|
||||
app(CourseFake::class)->assertDeleted(123, 999);
|
||||
|
@ -31,7 +31,7 @@ class DeleteTest extends TestCase
|
|||
app(CourseFake::class)->failsDeleting(123, 999);
|
||||
$member = Member::factory()->defaults()->inNami(123)->has(CourseMember::factory()->inNami(999)->for(Course::factory()), 'courses')->createOne();
|
||||
|
||||
$response = $this->delete("/member/{$member->id}/course/{$member->courses->first()->id}");
|
||||
$response = $this->delete("/course/{$member->courses->first()->id}");
|
||||
|
||||
$this->assertDatabaseCount('course_members', 1);
|
||||
$response->assertSessionHasErrors(['id' => 'Unbekannter Fehler']);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Course;
|
||||
|
||||
use App\Course\Models\Course;
|
||||
use App\Course\Models\CourseMember;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\TestCase;
|
||||
|
||||
class IndexTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function testItShowsCourses(): void
|
||||
{
|
||||
$this->login()->withNamiSettings();
|
||||
$member = Member::factory()->defaults()->has(CourseMember::factory()->for(Course::factory()->state(['name' => '2a']))->state(['event_name' => 'WE', 'organizer' => 'DPSG', 'completed_at' => now()->subDays(2)]), 'courses')->create();
|
||||
|
||||
$this->get("/member/{$member->id}/course")
|
||||
->assertJsonPath('data.0.course_name', '2a')
|
||||
->assertJsonPath('data.0.event_name', 'WE')
|
||||
->assertJsonPath('data.0.organizer', 'DPSG')
|
||||
->assertJsonPath('data.0.links.update', route('course.update', ['course' => $member->courses->first()->id]))
|
||||
->assertJsonPath('data.0.links.destroy', route('course.destroy', ['course' => $member->courses->first()->id]))
|
||||
->assertJsonPath('data.0.completed_at_human', now()->subDays(2)->format('d.m.Y'))
|
||||
->assertJsonPath('meta.links.store', route('member.course.store', ['member' => $member]))
|
||||
->assertJsonPath('meta.default.completed_at', null)
|
||||
->assertJsonPath('meta.default.course_id', null)
|
||||
->assertJsonPath('meta.default.event_name', '')
|
||||
->assertJsonPath('meta.default.organizer', '')
|
||||
->assertJsonPath('meta.courses.0.name', '2a')
|
||||
->assertJsonPath('meta.courses.0.id', Course::first()->id);
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ class UpdateTest extends TestCase
|
|||
$member = Member::factory()->defaults()->inNami(123)->has(CourseMember::factory()->for(Course::factory()), 'courses')->createOne();
|
||||
$newCourse = Course::factory()->inNami(789)->create();
|
||||
|
||||
$response = $this->patch("/member/{$member->id}/course/{$member->courses->first()->id}", array_merge([
|
||||
$response = $this->patch("/course/{$member->courses->first()->id}", array_merge([
|
||||
'course_id' => $newCourse->id,
|
||||
'completed_at' => '1999-02-03',
|
||||
'event_name' => '::newevent::',
|
||||
|
@ -75,7 +75,7 @@ class UpdateTest extends TestCase
|
|||
$member = Member::factory()->defaults()->inNami(123)->has(CourseMember::factory()->inNami(999)->for(Course::factory()), 'courses')->createOne();
|
||||
$newCourse = Course::factory()->inNami(789)->create();
|
||||
|
||||
$this->patch("/member/{$member->id}/course/{$member->courses->first()->id}", array_merge([
|
||||
$this->patch("/course/{$member->courses->first()->id}", array_merge([
|
||||
'course_id' => $newCourse->id,
|
||||
'completed_at' => '1999-02-03',
|
||||
'event_name' => '::newevent::',
|
||||
|
@ -104,7 +104,7 @@ class UpdateTest extends TestCase
|
|||
$member = Member::factory()->defaults()->inNami(123)->has(CourseMember::factory()->inNami(999)->for(Course::factory()), 'courses')->createOne();
|
||||
$newCourse = Course::factory()->inNami(789)->create();
|
||||
|
||||
$response = $this->patch("/member/{$member->id}/course/{$member->courses->first()->id}", array_merge([
|
||||
$response = $this->patch("/course/{$member->courses->first()->id}", array_merge([
|
||||
'course_id' => $newCourse->id,
|
||||
'completed_at' => '1999-02-03',
|
||||
'event_name' => '::newevent::',
|
||||
|
@ -121,7 +121,7 @@ class UpdateTest extends TestCase
|
|||
$member = Member::factory()->defaults()->inNami(123)->has(CourseMember::factory()->inNami(999)->for(Course::factory()), 'courses')->createOne();
|
||||
$newCourse = Course::factory()->inNami(789)->create();
|
||||
|
||||
$response = $this->patch("/member/{$member->id}/course/{$member->courses->first()->id}", [
|
||||
$response = $this->patch("/course/{$member->courses->first()->id}", [
|
||||
'course_id' => $newCourse->id,
|
||||
'completed_at' => '2021-01-02',
|
||||
'event_name' => '::event::',
|
||||
|
|
|
@ -38,6 +38,7 @@ class IndexTest extends TestCase
|
|||
$this->assertInertiaHas(null, $response, 'data.data.0.memberships');
|
||||
$this->assertInertiaHas(url("/member/{$member->id}/membership"), $response, 'data.data.0.links.membership_index');
|
||||
$this->assertInertiaHas(url("/member/{$member->id}/payment"), $response, 'data.data.0.links.payment_index');
|
||||
$this->assertInertiaHas(url("/member/{$member->id}/course"), $response, 'data.data.0.links.course_index');
|
||||
$this->assertInertiaHas([
|
||||
'id' => $member->subscription->id,
|
||||
'name' => $member->subscription->name,
|
||||
|
|
Loading…
Reference in New Issue