Add Broadcast event when deleting member
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
3263e93da7
commit
aeb926e165
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Lib\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class JobEvent implements ShouldBroadcastNow
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public bool $reload = false;
|
||||||
|
public string $message = '';
|
||||||
|
|
||||||
|
final private function __construct(public string $channel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function on(string $channel): static
|
||||||
|
{
|
||||||
|
return new static($channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withMessage(string $message): static
|
||||||
|
{
|
||||||
|
$this->message = $message;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Broadcasting\Channel
|
||||||
|
*/
|
||||||
|
public function broadcastOn()
|
||||||
|
{
|
||||||
|
return new Channel($this->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldReload(): static
|
||||||
|
{
|
||||||
|
$this->reload = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Lib\Events;
|
||||||
|
|
||||||
|
class JobFinished extends JobEvent
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Lib\Events;
|
||||||
|
|
||||||
|
class JobStarted extends JobEvent
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Lib\JobMiddleware;
|
||||||
|
|
||||||
|
use App\Lib\Events\JobFinished;
|
||||||
|
use App\Lib\Events\JobStarted;
|
||||||
|
use Closure;
|
||||||
|
use Lorisleiva\Actions\Decorators\JobDecorator;
|
||||||
|
|
||||||
|
class WithJobState
|
||||||
|
{
|
||||||
|
|
||||||
|
public JobStarted $beforeMessage;
|
||||||
|
public JobFinished $afterMessage;
|
||||||
|
|
||||||
|
private function __construct(public string $channel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function make(string $channel): self
|
||||||
|
{
|
||||||
|
return new self($channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function before(string $message): self
|
||||||
|
{
|
||||||
|
$this->beforeMessage = JobStarted::on($this->channel)->withMessage($message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function after(string $message): self
|
||||||
|
{
|
||||||
|
$this->afterMessage = JobFinished::on($this->channel)->withMessage($message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldReload(): self
|
||||||
|
{
|
||||||
|
$this->afterMessage->shouldReload();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(JobDecorator $job, Closure $next): void
|
||||||
|
{
|
||||||
|
event($this->beforeMessage);
|
||||||
|
$next($job);
|
||||||
|
event($this->afterMessage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Member\Actions;
|
namespace App\Member\Actions;
|
||||||
|
|
||||||
use App\Lib\Events\ClientMessage;
|
use App\Lib\JobMiddleware\WithJobState;
|
||||||
use App\Maildispatcher\Actions\ResyncAction;
|
use App\Maildispatcher\Actions\ResyncAction;
|
||||||
use App\Member\Member;
|
use App\Member\Member;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
@ -13,16 +13,37 @@ class MemberDeleteAction
|
||||||
|
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Member $member): RedirectResponse
|
public function handle(int $memberId): void
|
||||||
{
|
{
|
||||||
|
$member = Member::findOrFail($memberId);
|
||||||
|
|
||||||
if ($member->nami_id) {
|
if ($member->nami_id) {
|
||||||
NamiDeleteMemberAction::dispatch($member->nami_id);
|
NamiDeleteMemberAction::run($member->nami_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$member->delete();
|
$member->delete();
|
||||||
ResyncAction::dispatch();
|
ResyncAction::run();
|
||||||
ClientMessage::make('Mitglied ' . $member->fullname . ' gelöscht.')->shouldReload()->dispatch();
|
}
|
||||||
|
|
||||||
|
public function asController(Member $member): RedirectResponse
|
||||||
|
{
|
||||||
|
static::dispatch($member->id);
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, object>
|
||||||
|
*/
|
||||||
|
public function getJobMiddleware(int $memberId): array
|
||||||
|
{
|
||||||
|
$member = Member::findOrFail($memberId);
|
||||||
|
|
||||||
|
return [
|
||||||
|
WithJobState::make('member')
|
||||||
|
->before('Lösche Mitglied ' . $member->fullname)
|
||||||
|
->after('Mitglied ' . $member->fullname . ' gelöscht')
|
||||||
|
->shouldReload(),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
"mockery/mockery": "^1.4.4",
|
"mockery/mockery": "^1.4.4",
|
||||||
"nunomaduro/larastan": "^2.0",
|
"nunomaduro/larastan": "^2.0",
|
||||||
"orchestra/testbench": "^7.0",
|
"orchestra/testbench": "^7.0",
|
||||||
|
"phpstan/phpstan-mockery": "^1.1",
|
||||||
"phpunit/phpunit": "^9.5.10"
|
"phpunit/phpunit": "^9.5.10"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "62586d4169459f71189b880d8a86e1ec",
|
"content-hash": "9b301aa44118f3ff66ba16b8688726a3",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "beyondcode/laravel-dump-server",
|
"name": "beyondcode/laravel-dump-server",
|
||||||
|
@ -10700,7 +10700,7 @@
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "path",
|
"type": "path",
|
||||||
"url": "./packages/tex",
|
"url": "./packages/tex",
|
||||||
"reference": "48251272de62e3fea044a7ad31e1a411c15eb4c6"
|
"reference": "6f162102ef7ceca41822d18c3e694abd926f550b"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
|
@ -11618,6 +11618,56 @@
|
||||||
],
|
],
|
||||||
"time": "2023-08-08T12:33:42+00:00"
|
"time": "2023-08-08T12:33:42+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpstan-mockery",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/phpstan/phpstan-mockery.git",
|
||||||
|
"reference": "6aa86bd8e9c9a1be97baf0558d4a2ed1374736a6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpstan-mockery/zipball/6aa86bd8e9c9a1be97baf0558d4a2ed1374736a6",
|
||||||
|
"reference": "6aa86bd8e9c9a1be97baf0558d4a2ed1374736a6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0",
|
||||||
|
"phpstan/phpstan": "^1.10"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.2.4",
|
||||||
|
"nikic/php-parser": "^4.13.0",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.0",
|
||||||
|
"phpstan/phpstan-strict-rules": "^1.0",
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
|
},
|
||||||
|
"type": "phpstan-extension",
|
||||||
|
"extra": {
|
||||||
|
"phpstan": {
|
||||||
|
"includes": [
|
||||||
|
"extension.neon"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPStan\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHPStan Mockery extension",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/phpstan/phpstan-mockery/issues",
|
||||||
|
"source": "https://github.com/phpstan/phpstan-mockery/tree/1.1.1"
|
||||||
|
},
|
||||||
|
"time": "2023-02-18T13:54:03+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.27",
|
"version": "9.2.27",
|
||||||
|
|
11
phpstan.neon
11
phpstan.neon
|
@ -1,5 +1,6 @@
|
||||||
includes:
|
includes:
|
||||||
- ./vendor/nunomaduro/larastan/extension.neon
|
- ./vendor/nunomaduro/larastan/extension.neon
|
||||||
|
- ./vendor/phpstan/phpstan-mockery/extension.neon
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
|
|
||||||
|
@ -558,16 +559,6 @@ parameters:
|
||||||
count: 1
|
count: 1
|
||||||
path: packages/laravel-nami/tests/TestCase.php
|
path: packages/laravel-nami/tests/TestCase.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:never\\(\\)\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: tests/Feature/Initialize/InitializeActionTest.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:once\\(\\)\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: tests/Feature/Initialize/InitializeMembersTest.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$mock of static method Phake\\:\\:verify\\(\\) expects Phake\\\\IMock, App\\\\Actions\\\\PullMemberAction given\\.$#"
|
message: "#^Parameter \\#1 \\$mock of static method Phake\\:\\:verify\\(\\) expects Phake\\\\IMock, App\\\\Actions\\\\PullMemberAction given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import {ref, computed} from 'vue';
|
import {ref, computed, onBeforeUnmount} from 'vue';
|
||||||
import {router} from '@inertiajs/vue3';
|
import {router} from '@inertiajs/vue3';
|
||||||
import Toast, {useToast} from 'vue-toastification';
|
import {useToast} from 'vue-toastification';
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
export function useIndex(props) {
|
export function useIndex(props, siteName) {
|
||||||
const rawProps = JSON.parse(JSON.stringify(props));
|
const rawProps = JSON.parse(JSON.stringify(props));
|
||||||
const inner = {
|
const inner = {
|
||||||
data: ref(rawProps.data),
|
data: ref(rawProps.data),
|
||||||
|
@ -61,14 +61,21 @@ export function useIndex(props) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
window.Echo.channel('jobs').listen('\\App\\Lib\\Events\\ClientMessage', (e) => {
|
function handleJobEvent(event) {
|
||||||
if (e.message) {
|
if (event.message) {
|
||||||
toast.success(e.message);
|
toast.success(event.message);
|
||||||
}
|
}
|
||||||
if (e.reload) {
|
if (event.reload) {
|
||||||
reload(false);
|
reload(false);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
window.Echo.channel('jobs').listen('\\App\\Lib\\Events\\ClientMessage', (e) => handleJobEvent(e));
|
||||||
|
window.Echo.channel(siteName)
|
||||||
|
.listen('\\App\\Lib\\Events\\JobStarted', (e) => handleJobEvent(e))
|
||||||
|
.listen('\\App\\Lib\\Events\\JobFinished', (e) => handleJobEvent(e));
|
||||||
|
onBeforeUnmount(() => window.Echo.leave(siteName));
|
||||||
|
onBeforeUnmount(() => window.Echo.leave('jobs'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: inner.data,
|
data: inner.data,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<page-layout page-class="pb-6">
|
<page-layout page-class="pb-6">
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Tätigkeit erstellen</page-toolbar-button>
|
<page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Tätigkeit
|
||||||
|
erstellen</page-toolbar-button>
|
||||||
</template>
|
</template>
|
||||||
<ui-popup v-if="deleting !== null" heading="Bitte bestätigen" @close="deleting = null">
|
<ui-popup v-if="deleting !== null" heading="Bitte bestätigen" @close="deleting = null">
|
||||||
<div>
|
<div>
|
||||||
|
@ -22,8 +23,10 @@
|
||||||
<td v-text="activity.name"></td>
|
<td v-text="activity.name"></td>
|
||||||
<td>
|
<td>
|
||||||
<div class="flex space-x-1">
|
<div class="flex space-x-1">
|
||||||
<i-link v-tooltip="`bearbeiten`" :href="activity.links.edit" class="inline-flex btn btn-warning btn-sm"><ui-sprite src="pencil"></ui-sprite></i-link>
|
<i-link v-tooltip="`bearbeiten`" :href="activity.links.edit"
|
||||||
<a v-tooltip="`Entfernen`" href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="deleting = activity"><ui-sprite src="trash"></ui-sprite></a>
|
class="inline-flex btn btn-warning btn-sm"><ui-sprite src="pencil"></ui-sprite></i-link>
|
||||||
|
<a v-tooltip="`Entfernen`" href="#" class="inline-flex btn btn-danger btn-sm"
|
||||||
|
@click.prevent="deleting = activity"><ui-sprite src="trash"></ui-sprite></a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -40,7 +43,7 @@ import {ref, defineProps} from 'vue';
|
||||||
import { indexProps, useIndex } from '../../composables/useIndex.js';
|
import { indexProps, useIndex } from '../../composables/useIndex.js';
|
||||||
|
|
||||||
const props = defineProps(indexProps);
|
const props = defineProps(indexProps);
|
||||||
const {router, data, meta} = useIndex(props.data);
|
const { router, data, meta } = useIndex(props.data, 'activity');
|
||||||
const deleting = ref(null);
|
const deleting = ref(null);
|
||||||
|
|
||||||
function remove() {
|
function remove() {
|
||||||
|
|
|
@ -10,56 +10,27 @@
|
||||||
<ui-box heading="Metadatem">
|
<ui-box heading="Metadatem">
|
||||||
<div class="grid gap-4 sm:grid-cols-2">
|
<div class="grid gap-4 sm:grid-cols-2">
|
||||||
<f-text id="name" v-model="model.name" name="name" label="Name" size="sm" required></f-text>
|
<f-text id="name" v-model="model.name" name="name" label="Name" size="sm" required></f-text>
|
||||||
<f-select id="gateway_id" v-model="model.gateway_id" name="gateway_id" :options="meta.gateways" label="Verbindung" size="sm" required></f-select>
|
<f-select id="gateway_id" v-model="model.gateway_id" name="gateway_id" :options="meta.gateways"
|
||||||
|
label="Verbindung" size="sm" required></f-select>
|
||||||
</div>
|
</div>
|
||||||
</ui-box>
|
</ui-box>
|
||||||
<ui-box v-if="members !== null" heading="Filterregeln">
|
<ui-box v-if="members !== null" heading="Filterregeln">
|
||||||
<div class="grid gap-4 sm:grid-cols-2">
|
<div class="grid gap-4 sm:grid-cols-2">
|
||||||
<f-multipleselect
|
<f-multipleselect id="activity_ids" v-model="model.filter.activity_ids" name="activity_ids"
|
||||||
id="activity_ids"
|
:options="members.meta.filterActivities" label="Tätigkeit" size="sm"
|
||||||
v-model="model.filter.activity_ids"
|
@update:model-value="reload(1)"></f-multipleselect>
|
||||||
name="activity_ids"
|
<f-multipleselect id="subactivity_ids" v-model="model.filter.subactivity_ids" name="subactivity_ids"
|
||||||
:options="members.meta.filterActivities"
|
:options="members.meta.filterSubactivities" label="Unterttätigkeit" size="sm"
|
||||||
label="Tätigkeit"
|
@update:model-value="reload(1)"></f-multipleselect>
|
||||||
size="sm"
|
<f-multipleselect id="include" v-model="model.filter.include" name="include"
|
||||||
@update:model-value="reload(1)"
|
:options="members.meta.members" label="Zusätzliche Mitglieder" size="sm"
|
||||||
></f-multipleselect>
|
@update:model-value="reload(1)"></f-multipleselect>
|
||||||
<f-multipleselect
|
<f-multipleselect id="exclude" v-model="model.filter.exclude" name="exclude"
|
||||||
id="subactivity_ids"
|
:options="members.meta.members" label="Mitglieder ausschließen" size="sm"
|
||||||
v-model="model.filter.subactivity_ids"
|
@update:model-value="reload(1)"></f-multipleselect>
|
||||||
name="subactivity_ids"
|
<f-multipleselect id="groupIds" v-model="model.filter.group_ids" name="groupIds"
|
||||||
:options="members.meta.filterSubactivities"
|
:options="members.meta.groups" label="Gruppierungen" size="sm"
|
||||||
label="Unterttätigkeit"
|
@update:model-value="reload(1)"></f-multipleselect>
|
||||||
size="sm"
|
|
||||||
@update:model-value="reload(1)"
|
|
||||||
></f-multipleselect>
|
|
||||||
<f-multipleselect
|
|
||||||
id="include"
|
|
||||||
v-model="model.filter.include"
|
|
||||||
name="include"
|
|
||||||
:options="members.meta.members"
|
|
||||||
label="Zusätzliche Mitglieder"
|
|
||||||
size="sm"
|
|
||||||
@update:model-value="reload(1)"
|
|
||||||
></f-multipleselect>
|
|
||||||
<f-multipleselect
|
|
||||||
id="exclude"
|
|
||||||
v-model="model.filter.exclude"
|
|
||||||
name="exclude"
|
|
||||||
:options="members.meta.members"
|
|
||||||
label="Mitglieder ausschließen"
|
|
||||||
size="sm"
|
|
||||||
@update:model-value="reload(1)"
|
|
||||||
></f-multipleselect>
|
|
||||||
<f-multipleselect
|
|
||||||
id="groupIds"
|
|
||||||
v-model="model.filter.group_ids"
|
|
||||||
name="groupIds"
|
|
||||||
:options="members.meta.groups"
|
|
||||||
label="Gruppierungen"
|
|
||||||
size="sm"
|
|
||||||
@update:model-value="reload(1)"
|
|
||||||
></f-multipleselect>
|
|
||||||
</div>
|
</div>
|
||||||
</ui-box>
|
</ui-box>
|
||||||
<ui-box v-if="members !== null" heading="Mitglieder">
|
<ui-box v-if="members !== null" heading="Mitglieder">
|
||||||
|
@ -101,7 +72,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const {toFilterString, router} = useIndex({data: [], meta: {}});
|
const { toFilterString, router } = useIndex({ data: [], meta: {} }, 'maildispatcher');
|
||||||
|
|
||||||
const model = ref(props.data === undefined ? { ...props.meta.default_model } : { ...props.data });
|
const model = ref(props.data === undefined ? { ...props.meta.default_model } : { ...props.data });
|
||||||
const members = ref(null);
|
const members = ref(null);
|
||||||
|
|
|
@ -1,39 +1,26 @@
|
||||||
<template>
|
<template>
|
||||||
<page-layout>
|
<page-layout>
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<page-toolbar-button color="primary" icon="plus" @click.prevent="model = {...meta.default}">Neue Verbindung</page-toolbar-button>
|
<page-toolbar-button color="primary" icon="plus" @click.prevent="model = { ...meta.default }">Neue
|
||||||
|
Verbindung</page-toolbar-button>
|
||||||
</template>
|
</template>
|
||||||
<ui-popup v-if="model !== null" :heading="model.id ? 'Verbindung bearbeiten' : 'Neue Verbindung'" @close="model = null">
|
<ui-popup v-if="model !== null" :heading="model.id ? 'Verbindung bearbeiten' : 'Neue Verbindung'"
|
||||||
|
@close="model = null">
|
||||||
<form @submit.prevent="submit">
|
<form @submit.prevent="submit">
|
||||||
<section class="grid grid-cols-2 gap-3 mt-6">
|
<section class="grid grid-cols-2 gap-3 mt-6">
|
||||||
<f-text id="name" v-model="model.name" name="name" label="Bezeichnung" required></f-text>
|
<f-text id="name" v-model="model.name" name="name" label="Bezeichnung" required></f-text>
|
||||||
<f-text id="domain" v-model="model.domain" name="domain" label="Domain" required></f-text>
|
<f-text id="domain" v-model="model.domain" name="domain" label="Domain" required></f-text>
|
||||||
<f-select
|
<f-select id="type" :model-value="model.type.cls" label="Typ" name="type" :options="meta.types"
|
||||||
id="type"
|
:placeholder="''" required @update:model-value="
|
||||||
:model-value="model.type.cls"
|
|
||||||
label="Typ"
|
|
||||||
name="type"
|
|
||||||
:options="meta.types"
|
|
||||||
:placeholder="''"
|
|
||||||
required
|
|
||||||
@update:model-value="
|
|
||||||
model.type = {
|
model.type = {
|
||||||
cls: $event,
|
cls: $event,
|
||||||
params: { ...getType($event).defaults },
|
params: { ...getType($event).defaults },
|
||||||
}
|
}
|
||||||
"
|
"></f-select>
|
||||||
></f-select>
|
|
||||||
<template v-for="(field, index) in getType(model.type.cls).fields">
|
<template v-for="(field, index) in getType(model.type.cls).fields">
|
||||||
<f-text
|
<f-text v-if="field.type === 'text' || field.type === 'password' || field.type === 'email'"
|
||||||
v-if="field.type === 'text' || field.type === 'password' || field.type === 'email'"
|
:id="field.name" :key="index" v-model="model.type.params[field.name]" :label="field.label"
|
||||||
:id="field.name"
|
:type="field.type" :name="field.name" :required="field.is_required"></f-text>
|
||||||
:key="index"
|
|
||||||
v-model="model.type.params[field.name]"
|
|
||||||
:label="field.label"
|
|
||||||
:type="field.type"
|
|
||||||
:name="field.name"
|
|
||||||
:required="field.is_required"
|
|
||||||
></f-text>
|
|
||||||
</template>
|
</template>
|
||||||
</section>
|
</section>
|
||||||
<section class="flex mt-4 space-x-2">
|
<section class="flex mt-4 space-x-2">
|
||||||
|
@ -58,14 +45,12 @@
|
||||||
<td v-text="gateway.domain"></td>
|
<td v-text="gateway.domain"></td>
|
||||||
<td v-text="gateway.type_human"></td>
|
<td v-text="gateway.type_human"></td>
|
||||||
<td>
|
<td>
|
||||||
<ui-boolean-display
|
<ui-boolean-display :value="gateway.works" long-label="Verbindungsstatus"
|
||||||
:value="gateway.works"
|
:label="gateway.works ? 'Verbindung erfolgreich' : 'Verbindung fehlgeschlagen'"></ui-boolean-display>
|
||||||
long-label="Verbindungsstatus"
|
|
||||||
:label="gateway.works ? 'Verbindung erfolgreich' : 'Verbindung fehlgeschlagen'"
|
|
||||||
></ui-boolean-display>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a v-tooltip="`Bearbeiten`" href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="model = {...gateway}"><ui-sprite src="pencil"></ui-sprite></a>
|
<a v-tooltip="`Bearbeiten`" href="#" class="inline-flex btn btn-warning btn-sm"
|
||||||
|
@click.prevent="model = { ...gateway }"><ui-sprite src="pencil"></ui-sprite></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -84,7 +69,7 @@ import {indexProps, useIndex} from '../../composables/useIndex.js';
|
||||||
import SettingLayout from '../setting/Layout.vue';
|
import SettingLayout from '../setting/Layout.vue';
|
||||||
|
|
||||||
const props = defineProps(indexProps);
|
const props = defineProps(indexProps);
|
||||||
const {meta, data, reload} = useIndex(props.data);
|
const { meta, data, reload } = useIndex(props.data, 'mailgateway');
|
||||||
const model = ref(null);
|
const model = ref(null);
|
||||||
const axios = inject('axios');
|
const axios = inject('axios');
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<page-layout page-class="pb-6">
|
<page-layout page-class="pb-6">
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Mitglied anlegen</page-toolbar-button>
|
<page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Mitglied
|
||||||
<page-toolbar-button v-if="hasModule('bill')" :href="meta.links.allpayment" color="primary" icon="invoice">Rechnungen erstellen</page-toolbar-button>
|
anlegen</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 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>
|
</template>
|
||||||
<ui-popup v-if="deleting !== null" heading="Mitglied löschen?" @close="deleting.reject()">
|
<ui-popup v-if="deleting !== null" heading="Mitglied löschen?" @close="deleting.reject()">
|
||||||
<div>
|
<div>
|
||||||
<p class="mt-4">Das Mitglied "{{ deleting.member.fullname }}" löschen?</p>
|
<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>
|
<p class="mt-2">Alle Zuordnungen (Ausbildungen, Rechnungen, Zahlungen, Tätigkeiten) werden ebenfalls
|
||||||
<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>
|
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">
|
<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>
|
</ui-note>
|
||||||
<div class="grid grid-cols-2 gap-3 mt-6">
|
<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>
|
<a href="#" class="text-center btn btn-danger" @click.prevent="deleting.resolve">Mitglied loschen</a>
|
||||||
|
@ -20,45 +26,22 @@
|
||||||
</div>
|
</div>
|
||||||
</ui-popup>
|
</ui-popup>
|
||||||
<div class="px-6 py-2 flex border-b border-gray-600 items-center space-x-3">
|
<div class="px-6 py-2 flex border-b border-gray-600 items-center space-x-3">
|
||||||
<f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text>
|
<f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm"
|
||||||
<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>
|
@update:model-value="setFilter('search', $event)"></f-text>
|
||||||
<f-multipleselect
|
<f-switch v-show="hasModule('bill')" id="ausstand" :model-value="getFilter('ausstand')" label="Nur Ausstände"
|
||||||
id="group_ids"
|
size="sm" @update:model-value="setFilter('ausstand', $event)"></f-switch>
|
||||||
:options="meta.groups"
|
<f-multipleselect id="group_ids" :options="meta.groups" :model-value="getFilter('group_ids')"
|
||||||
:model-value="getFilter('group_ids')"
|
label="Gruppierungen" size="sm" name="group_ids"
|
||||||
label="Gruppierungen"
|
@update:model-value="setFilter('group_ids', $event)"></f-multipleselect>
|
||||||
size="sm"
|
<f-select v-show="hasModule('bill')" id="billKinds" name="billKinds" :options="meta.billKinds"
|
||||||
name="group_ids"
|
:model-value="getFilter('bill_kind')" label="Rechnung" size="sm"
|
||||||
@update:model-value="setFilter('group_ids', $event)"
|
@update:model-value="setFilter('bill_kind', $event)"></f-select>
|
||||||
></f-multipleselect>
|
<f-multipleselect id="activity_ids" :options="meta.filterActivities" :model-value="getFilter('activity_ids')"
|
||||||
<f-select
|
label="Tätigkeiten" size="sm" name="activity_ids"
|
||||||
v-show="hasModule('bill')"
|
@update:model-value="setFilter('activity_ids', $event)"></f-multipleselect>
|
||||||
id="billKinds"
|
<f-multipleselect id="subactivity_ids" :options="meta.filterSubactivities"
|
||||||
name="billKinds"
|
:model-value="getFilter('subactivity_ids')" label="Untertätigkeiten" size="sm" name="subactivity_ids"
|
||||||
:options="meta.billKinds"
|
@update:model-value="setFilter('subactivity_ids', $event)"></f-multipleselect>
|
||||||
: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">
|
<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>
|
<ui-sprite class="w-3 h-3 xl:mr-2" src="save"></ui-sprite>
|
||||||
<span class="hidden xl:inline">Exportieren</span>
|
<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="text-xs text-gray-200" v-text="member.full_address"></div>
|
||||||
<div class="flex items-center mt-1 space-x-4">
|
<div class="flex items-center mt-1 space-x-4">
|
||||||
<tags :member="member"></tags>
|
<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>
|
</div>
|
||||||
<actions class="mt-2" :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)"> </actions>
|
<actions class="mt-2" :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)">
|
||||||
|
</actions>
|
||||||
<div class="absolute right-0 top-0 h-full flex items-center mr-2">
|
<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-down" 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-down"
|
||||||
|
class="w-6 h-6 text-teal-100 -rotate-90"></ui-sprite></i-link>
|
||||||
</div>
|
</div>
|
||||||
</ui-box>
|
</ui-box>
|
||||||
</div>
|
</div>
|
||||||
|
@ -120,22 +106,13 @@
|
||||||
<ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
|
<ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<member-payments
|
<member-payments v-if="single !== null && sidebar === 'payment.index'" :subscriptions="meta.subscriptions"
|
||||||
v-if="single !== null && sidebar === 'payment.index'"
|
:statuses="meta.statuses" :value="data[single]" @close="closeSidebar"></member-payments>
|
||||||
:subscriptions="meta.subscriptions"
|
<member-memberships v-if="single !== null && sidebar === 'membership.index'" :groups="meta.groups"
|
||||||
:statuses="meta.statuses"
|
:activities="meta.formActivities" :subactivities="meta.formSubactivities" :value="data[single]"
|
||||||
:value="data[single]"
|
@close="closeSidebar"></member-memberships>
|
||||||
@close="closeSidebar"
|
<member-courses v-if="single !== null && sidebar === 'courses.index'" :courses="meta.courses" :value="data[single]"
|
||||||
></member-payments>
|
@close="closeSidebar"></member-courses>
|
||||||
<member-memberships
|
|
||||||
v-if="single !== null && sidebar === 'membership.index'"
|
|
||||||
:groups="meta.groups"
|
|
||||||
:activities="meta.formActivities"
|
|
||||||
:subactivities="meta.formSubactivities"
|
|
||||||
:value="data[single]"
|
|
||||||
@close="closeSidebar"
|
|
||||||
></member-memberships>
|
|
||||||
<member-courses v-if="single !== null && sidebar === 'courses.index'" :courses="meta.courses" :value="data[single]" @close="closeSidebar"></member-courses>
|
|
||||||
</page-layout>
|
</page-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -153,7 +130,7 @@ const single = ref(null);
|
||||||
const deleting = ref(null);
|
const deleting = ref(null);
|
||||||
|
|
||||||
const props = defineProps(indexProps);
|
const props = defineProps(indexProps);
|
||||||
var {router, data, meta, getFilter, setFilter, filterString} = useIndex(props.data);
|
var { router, data, meta, getFilter, setFilter, filterString } = useIndex(props.data, 'member');
|
||||||
|
|
||||||
function exportMembers() {
|
function exportMembers() {
|
||||||
window.open(`/member-export?filter=${filterString.value}`);
|
window.open(`/member-export?filter=${filterString.value}`);
|
||||||
|
|
|
@ -22,7 +22,8 @@ class InitializeMembersTest extends TestCase
|
||||||
app(SearchFake::class)->fetches(1, 0, 100, [
|
app(SearchFake::class)->fetches(1, 0, 100, [
|
||||||
MemberEntry::factory()->toMember(['groupId' => 100, 'id' => 20]),
|
MemberEntry::factory()->toMember(['groupId' => 100, 'id' => 20]),
|
||||||
]);
|
]);
|
||||||
FullMemberAction::shouldRun()->once()->shouldReceive('configureJob');
|
FullMemberAction::partialMock()->shouldReceive('configureJob')->once();
|
||||||
|
FullMemberAction::partialMock()->shouldReceive('handle')->once();
|
||||||
|
|
||||||
app(InitializeMembers::class)->handle($api);
|
app(InitializeMembers::class)->handle($api);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +34,8 @@ class InitializeMembersTest extends TestCase
|
||||||
app(SearchFake::class)->fetches(1, 0, 100, [
|
app(SearchFake::class)->fetches(1, 0, 100, [
|
||||||
MemberEntry::factory()->toMember(['groupId' => 100, 'id' => 20]),
|
MemberEntry::factory()->toMember(['groupId' => 100, 'id' => 20]),
|
||||||
]);
|
]);
|
||||||
FullMemberAction::shouldRun()->once()->shouldReceive('configureJob');
|
FullMemberAction::partialMock()->shouldReceive('configureJob')->once();
|
||||||
|
FullMemberAction::partialMock()->shouldReceive('handle')->once();
|
||||||
|
|
||||||
Artisan::call('member:pull');
|
Artisan::call('member:pull');
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,22 @@ namespace Tests\Feature\Member;
|
||||||
|
|
||||||
use App\Course\Models\Course;
|
use App\Course\Models\Course;
|
||||||
use App\Course\Models\CourseMember;
|
use App\Course\Models\CourseMember;
|
||||||
|
use App\Lib\Events\ClientMessage;
|
||||||
|
use App\Lib\Events\JobFinished;
|
||||||
|
use App\Lib\Events\JobStarted;
|
||||||
|
use App\Member\Actions\MemberDeleteAction;
|
||||||
use App\Member\Actions\NamiDeleteMemberAction;
|
use App\Member\Actions\NamiDeleteMemberAction;
|
||||||
use App\Member\Member;
|
use App\Member\Member;
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Queue;
|
use Illuminate\Support\Facades\Queue;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
use Zoomyboy\LaravelNami\Fakes\MemberFake;
|
|
||||||
|
|
||||||
class DeleteTest extends TestCase
|
class DeleteTest extends TestCase
|
||||||
{
|
{
|
||||||
use DatabaseTransactions;
|
use DatabaseTransactions;
|
||||||
|
|
||||||
public function testItDeletesMemberFromNami(): void
|
public function testItFiresJob(): void
|
||||||
{
|
{
|
||||||
Queue::fake();
|
Queue::fake();
|
||||||
$this->login()->loginNami();
|
$this->login()->loginNami();
|
||||||
|
@ -26,7 +29,17 @@ class DeleteTest extends TestCase
|
||||||
|
|
||||||
$response->assertRedirect('/member');
|
$response->assertRedirect('/member');
|
||||||
|
|
||||||
NamiDeleteMemberAction::assertPushed();
|
MemberDeleteAction::assertPushed(fn ($action, $parameters) => $parameters[0] === $member->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItDeletesMemberFromNami(): void
|
||||||
|
{
|
||||||
|
$this->login()->loginNami();
|
||||||
|
NamiDeleteMemberAction::partialMock()->shouldReceive('handle')->with(123)->once();
|
||||||
|
$member = Member::factory()->defaults()->inNami(123)->create();
|
||||||
|
|
||||||
|
MemberDeleteAction::run($member->id);
|
||||||
|
|
||||||
$this->assertDatabaseMissing('members', [
|
$this->assertDatabaseMissing('members', [
|
||||||
'id' => $member->id,
|
'id' => $member->id,
|
||||||
]);
|
]);
|
||||||
|
@ -34,38 +47,33 @@ class DeleteTest extends TestCase
|
||||||
|
|
||||||
public function testItDoesntRunActionWhenMemberIsNotInNami(): void
|
public function testItDoesntRunActionWhenMemberIsNotInNami(): void
|
||||||
{
|
{
|
||||||
Queue::fake();
|
|
||||||
$this->login()->loginNami();
|
$this->login()->loginNami();
|
||||||
|
NamiDeleteMemberAction::partialMock()->shouldReceive('handle')->never();
|
||||||
$member = Member::factory()->defaults()->create();
|
$member = Member::factory()->defaults()->create();
|
||||||
|
|
||||||
$response = $this->from('/member')->delete("/member/{$member->id}");
|
MemberDeleteAction::run($member->id);
|
||||||
|
|
||||||
$response->assertRedirect('/member');
|
|
||||||
|
|
||||||
Queue::assertNotPushed(NamiDeleteMemberAction::class);
|
|
||||||
$this->assertDatabaseMissing('members', [
|
|
||||||
'id' => $member->id,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTheActionDeletesNamiMember(): void
|
|
||||||
{
|
|
||||||
app(MemberFake::class)->deletes(123, Carbon::parse('yesterday'));
|
|
||||||
$this->withoutExceptionHandling()->login()->loginNami();
|
|
||||||
$member = Member::factory()->defaults()->inNami(123)->create();
|
|
||||||
|
|
||||||
NamiDeleteMemberAction::dispatch(123);
|
|
||||||
|
|
||||||
app(MemberFake::class)->assertDeleted(123, Carbon::parse('yesterday'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testItDeletesMembersWithCourses(): void
|
public function testItDeletesMembersWithCourses(): void
|
||||||
{
|
{
|
||||||
$this->withoutExceptionHandling()->login()->loginNami();
|
$this->withoutExceptionHandling()->login()->loginNami();
|
||||||
$member = Member::factory()->defaults()->has(CourseMember::factory()->for(Course::factory()), 'courses')->create();
|
$member = Member::factory()->defaults()->has(CourseMember::factory()->for(Course::factory()), 'courses')->create();
|
||||||
|
|
||||||
$member->delete();
|
MemberDeleteAction::run($member->id);
|
||||||
|
|
||||||
$this->assertDatabaseCount('members', 0);
|
$this->assertDatabaseCount('members', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testItFiresEventWhenFinished(): void
|
||||||
|
{
|
||||||
|
Event::fake([JobStarted::class, JobFinished::class]);
|
||||||
|
$this->withoutExceptionHandling()->login()->loginNami();
|
||||||
|
$member = Member::factory()->defaults()->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||||
|
|
||||||
|
MemberDeleteAction::dispatch($member->id);
|
||||||
|
|
||||||
|
Event::assertDispatched(JobStarted::class, fn ($event) => $event->broadcastOn()->name === 'member' && $event->message === 'Lösche Mitglied Max Muster' && $event->reload === false);
|
||||||
|
Event::assertDispatched(JobFinished::class, fn ($event) => $event->message === 'Mitglied Max Muster gelöscht' && $event->reload === true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Member;
|
||||||
|
|
||||||
|
use App\Member\Actions\NamiDeleteMemberAction;
|
||||||
|
use App\Member\Member;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use Zoomyboy\LaravelNami\Fakes\MemberFake;
|
||||||
|
|
||||||
|
class NamiDeleteMemberActionTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
public function testTheActionDeletesNamiMember(): void
|
||||||
|
{
|
||||||
|
app(MemberFake::class)->deletes(123, Carbon::parse('yesterday'));
|
||||||
|
$this->withoutExceptionHandling()->login()->loginNami();
|
||||||
|
Member::factory()->defaults()->inNami(123)->create();
|
||||||
|
|
||||||
|
NamiDeleteMemberAction::dispatch(123);
|
||||||
|
|
||||||
|
app(MemberFake::class)->assertDeleted(123, Carbon::parse('yesterday'));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue