Add index for mail gateways

This commit is contained in:
Philipp Lang 2023-06-01 15:02:35 +02:00
parent d2a000cb31
commit 27c61ff8af
17 changed files with 464 additions and 65 deletions

View File

@ -0,0 +1,33 @@
<?php
namespace App\Mailgateway\Actions;
use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Resources\MailgatewayResource;
use Illuminate\Database\Eloquent\Builder;
use Inertia\Inertia;
use Inertia\Response;
use Lorisleiva\Actions\Concerns\AsAction;
class IndexAction
{
use AsAction;
/**
* @return Builder<Mailgateway>
*/
public function handle(): Builder
{
return (new Mailgateway())->newQuery();
}
public function asController(): Response
{
session()->put('menu', 'setting');
session()->put('title', 'E-Mail-Verbindungen');
return Inertia::render('mailgateway/Index', [
'data' => MailgatewayResource::collection($this->handle()->paginate(10)),
]);
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Mailgateway\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class TypeCast implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param mixed $value
*
* @return mixed
*/
public function get($model, string $key, $value, array $attributes)
{
$value = json_decode($value);
return new $value->class($value->params);
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param mixed $value
*
* @return mixed
*/
public function set($model, string $key, $value, array $attributes)
{
return json_encode($value);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Mailgateway;
use App\Mailgateway\Actions\IndexAction;
use App\Setting\Contracts\Indexable;
use App\Setting\LocalSettings;
class MailgatewaySettings extends LocalSettings implements Indexable
{
public static function group(): string
{
return 'mailgateway';
}
public static function slug(): string
{
return 'mailgateway';
}
public static function indexAction(): string
{
return IndexAction::class;
}
public static function title(): string
{
return 'E-Mail-Verbindungen';
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Mailgateway\Models;
use App\Mailgateway\Casts\TypeCast;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Mailgateway extends Model
{
use HasFactory;
use HasUuids;
public $casts = ['type' => TypeCast::class];
public $guarded = [];
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Mailgateway\Resources;
use App\Lib\HasMeta;
use App\Mailgateway\Models\Mailgateway;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin Mailgateway
*/
class MailgatewayResource extends JsonResource
{
use HasMeta;
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
*
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'name' => $this->name,
'domain' => $this->domain,
'type_human' => $this->type::name(),
'works' => $this->type->works(),
];
}
public static function meta(): array
{
return [
'links' => [
'store' => route('api.mailgateway.store'),
],
];
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Mailgateway\Types;
class LocalType extends Type
{
public static function name(): string
{
return 'Lokal';
}
public function works(): bool
{
return true;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Mailgateway\Types;
abstract class Type
{
abstract public static function name(): string;
abstract public function works(): bool;
}

View File

@ -2,6 +2,7 @@
namespace App\Providers; namespace App\Providers;
use App\Mailgateway\Types\LocalType;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
@ -26,6 +27,10 @@ class AppServiceProvider extends ServiceProvider
return $this; return $this;
}); });
app()->bind('mail-gateways', fn () => collect([
LocalType::class,
]));
} }
/** /**

View File

@ -3,6 +3,7 @@
namespace App\Setting; namespace App\Setting;
use App\Invoice\InvoiceSettings; use App\Invoice\InvoiceSettings;
use App\Mailgateway\MailgatewaySettings;
use App\Mailman\MailmanSettings; use App\Mailman\MailmanSettings;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
@ -27,5 +28,6 @@ class SettingServiceProvider extends ServiceProvider
{ {
app(SettingFactory::class)->register(InvoiceSettings::class); app(SettingFactory::class)->register(InvoiceSettings::class);
app(SettingFactory::class)->register(MailmanSettings::class); app(SettingFactory::class)->register(MailmanSettings::class);
app(SettingFactory::class)->register(MailgatewaySettings::class);
} }
} }

View File

@ -0,0 +1,54 @@
<?php
namespace Database\Factories\Mailgateway\Models;
use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Types\Type;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Mailgateway\Models\Mailgateway>
*/
class MailgatewayFactory extends Factory
{
protected $model = Mailgateway::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'name' => $this->faker->words(5, true),
'type' => [
'class' => app('mail-gateways')->random(),
'params' => [],
],
'domain' => $this->faker->safeEmailDomain(),
];
}
/**
* @param class-string<Type> $type
* @param array<string, mixed> $params
*/
public function type(string $type, array $params): self
{
return $this->state(['type' => [
'class' => $type,
'params' => $params,
]]);
}
public function name(string $name): self
{
return $this->state(['name' => $name]);
}
public function domain(string $domain): self
{
return $this->state(['domain' => $domain]);
}
}

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class() extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('mailgateways', function (Blueprint $table) {
$table->uuid('id');
$table->string('name');
$table->string('domain');
$table->json('type');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('mailgateways');
}
};

View File

@ -0,0 +1,74 @@
<template>
<page-layout>
<template #toolbar>
<page-toolbar-button @click.prevent="popup = true" color="primary" icon="plus">Neue Verbindung</page-toolbar-button>
</template>
<ui-popup heading="Neue Verbindung" v-if="popup === true" @close="popup = false">
<div>
<div class="grid grid-cols-2 gap-3 mt-6">
<a href="#" @click.prevent="submit" class="text-center btn btn-danger">Speichern</a>
<a
href="#"
@click.prevent="
value = {};
popup = false;
"
class="text-center btn btn-primary"
>Abbrechen</a
>
</div>
</div>
</ui-popup>
<setting-layout>
<div class="w-full h-full pb-6">
<table cellspacing="0" cellpadding="0" border="0" class="custom-table custom-table-sm hidden md:table">
<thead>
<th>Name</th>
<th>Domain</th>
<th>Typ</th>
<th>Prüfung</th>
<th>Aktion</th>
</thead>
<tr v-for="(gateway, index) in inner" :key="index">
<td v-text="gateway.name"></td>
<td v-text="gateway.domain"></td>
<td v-text="gateway.type_human"></td>
<td>
<ui-boolean-display
:value="gateway.works"
long-label="Verbindungsstatus"
:label="gateway.works ? 'Verbindung erfolgreich' : 'Verbindung fehlgeschlagen'"
></ui-boolean-display>
</td>
<td></td>
</tr>
</table>
<div class="px-6">
<ui-pagination class="mt-4" :value="data.meta" :only="['data']"></ui-pagination>
</div>
</div>
</setting-layout>
</page-layout>
</template>
<script>
import SettingLayout from '../setting/Layout.vue';
export default {
data: function () {
return {
popup: false,
inner: [...this.data.data],
meta: {...this.data.meta},
};
},
props: {
data: {},
},
components: {
SettingLayout,
},
};
</script>

View File

@ -1,4 +1,6 @@
<template> <template>
<page-layout>
<setting-layout>
<form id="billsettingform" class="grow p-6 grid grid-cols-2 gap-3 items-start content-start" @submit.prevent="submit"> <form id="billsettingform" class="grow p-6 grid grid-cols-2 gap-3 items-start content-start" @submit.prevent="submit">
<f-save-button form="billsettingform"></f-save-button> <f-save-button form="billsettingform"></f-save-button>
<f-text label="Absender" hint="Absender-Name in Kurzform, i.d.R. der kurze Stammesname" name="from" id="from" v-model="inner.from"></f-text> <f-text label="Absender" hint="Absender-Name in Kurzform, i.d.R. der kurze Stammesname" name="from" id="from" v-model="inner.from"></f-text>
@ -14,18 +16,17 @@
<f-text label="IBAN" v-model="inner.iban" name="iban" id="iban"></f-text> <f-text label="IBAN" v-model="inner.iban" name="iban" id="iban"></f-text>
<f-text label="BIC" v-model="inner.bic" name="bic" id="bic"></f-text> <f-text label="BIC" v-model="inner.bic" name="bic" id="bic"></f-text>
</form> </form>
</setting-layout>
</page-layout>
</template> </template>
<script> <script>
import AppLayout from '../../layouts/AppLayout.vue';
import SettingLayout from './Layout.vue'; import SettingLayout from './Layout.vue';
export default { export default {
layout: [AppLayout, SettingLayout],
data: function () { data: function () {
return { return {
inner: {}, inner: {...this.data},
}; };
}, },
props: { props: {
@ -36,8 +37,8 @@ export default {
this.$inertia.post('/setting/bill', this.inner); this.$inertia.post('/setting/bill', this.inner);
}, },
}, },
created() { components: {
this.inner = this.data; SettingLayout,
}, },
}; };
</script> </script>

View File

@ -1,17 +1,15 @@
<template> <template>
<page-layout>
<div class="flex grow relative"> <div class="flex grow relative">
<ui-tabs v-model="active" :entries="$page.props.setting_menu"></ui-tabs> <ui-tabs v-model="active" :entries="$page.props.setting_menu"></ui-tabs>
<slot></slot> <slot></slot>
</div> </div>
</page-layout>
</template> </template>
<script> <script>
export default { export default {
data: function () { data: function () {
return { return {
innerActive: 0, innerActive: this.$page.props.setting_menu.findIndex((menu) => menu.is_active),
}; };
}, },
computed: { computed: {
@ -19,19 +17,15 @@ export default {
get() { get() {
return this.innerActive; return this.innerActive;
}, },
set(v, old) { set(v) {
var _self = this; var _self = this;
this.$inertia.visit(this.$page.props.setting_menu[v].url, { this.$inertia.visit(this.$page.props.setting_menu[v].url, {
onSuccess(page) { onSuccess() {
console.log('A');
_self.innerActive = v; _self.innerActive = v;
}, },
}); });
}, },
}, },
}, },
mounted() {
this.innerActive = this.$page.props.setting_menu.findIndex((menu) => menu.is_active);
},
}; };
</script> </script>

View File

@ -1,4 +1,6 @@
<template> <template>
<page-layout>
<setting-layout>
<form id="mailmansettingform" class="grow p-6 grid grid-cols-2 gap-3 items-start content-start" @submit.prevent="submit"> <form id="mailmansettingform" class="grow p-6 grid grid-cols-2 gap-3 items-start content-start" @submit.prevent="submit">
<f-save-button form="mailmansettingform"></f-save-button> <f-save-button form="mailmansettingform"></f-save-button>
<div class="col-span-full text-gray-100 mb-3"> <div class="col-span-full text-gray-100 mb-3">
@ -24,18 +26,17 @@
<f-select label="Liste für passive Leiter" name="passive_leaders_list" id="passive_leaders_list" v-model="inner.passive_leaders_list" :options="lists"></f-select> <f-select label="Liste für passive Leiter" name="passive_leaders_list" id="passive_leaders_list" v-model="inner.passive_leaders_list" :options="lists"></f-select>
<div></div> <div></div>
</form> </form>
</setting-layout>
</page-layout>
</template> </template>
<script> <script>
import AppLayout from '../../layouts/AppLayout.vue';
import SettingLayout from './Layout.vue'; import SettingLayout from './Layout.vue';
export default { export default {
layout: [AppLayout, SettingLayout],
data: function () { data: function () {
return { return {
inner: {}, inner: {...this.data},
}; };
}, },
props: { props: {
@ -78,8 +79,8 @@ export default {
}); });
}, },
}, },
created() { components: {
this.inner = this.data; SettingLayout,
}, },
}; };
</script> </script>

View File

@ -78,4 +78,7 @@ Route::group(['middleware' => 'auth:web'], function (): void {
Route::get('/contribution', ContributionFormAction::class)->name('contribution.form'); Route::get('/contribution', ContributionFormAction::class)->name('contribution.form');
Route::get('/contribution-generate', ContributionGenerateAction::class)->name('contribution.generate'); Route::get('/contribution-generate', ContributionGenerateAction::class)->name('contribution.generate');
Route::post('/contribution-validate', ContributionValidateAction::class)->name('contribution.validate'); Route::post('/contribution-validate', ContributionValidateAction::class)->name('contribution.validate');
// -------------------------------- Mailgateway --------------------------------
Route::post('/api/mailgateway', fn () => '')->name('api.mailgateway.store');
}); });

View File

@ -0,0 +1,49 @@
<?php
namespace Tests\Feature\Mailgateway;
use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Types\LocalType;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
class IndexTest extends TestCase
{
use DatabaseTransactions;
public function setUp(): void
{
parent::setUp();
$this->login()->loginNami();
}
public function testItCanViewIndexPage(): void
{
$response = $this->get('/setting/mailgateway');
$response->assertOk();
}
public function testItDisplaysGateways(): void
{
$this->withoutExceptionHandling();
Mailgateway::factory()->type(LocalType::class, [])->name('Lore')->domain('example.com')->create();
$response = $this->get('/setting/mailgateway');
$this->assertInertiaHas('example.com', $response, 'data.data.0.domain');
$this->assertInertiaHas('Lore', $response, 'data.data.0.name');
$this->assertInertiaHas('Lokal', $response, 'data.data.0.type_human');
$this->assertInertiaHas(true, $response, 'data.data.0.works');
}
public function testItHasMeta(): void
{
$this->withoutExceptionHandling();
$response = $this->get('/setting/mailgateway');
$this->assertInertiaHas(route('api.mailgateway.store'), $response, 'data.meta.links.store');
}
}