Add mailman settings
continuous-integration/drone/push Build is passing Details

This commit is contained in:
philipp lang 2022-10-20 02:15:28 +02:00
parent e59f9fa27d
commit 4076564c5e
20 changed files with 364 additions and 143 deletions

49
app/Bill/BillSettings.php Normal file
View File

@ -0,0 +1,49 @@
<?php
namespace App\Bill;
use App\Setting\LocalSettings;
class BillSettings extends LocalSettings
{
public string $from_long;
public string $from;
public string $mobile;
public string $email;
public string $website;
public string $address;
public string $place;
public string $zip;
public static function group(): string
{
return 'bill';
}
public static function slug(): string
{
return 'bill';
}
public static function indexAction(): string
{
return SettingIndexAction::class;
}
public static function saveAction(): string
{
return SettingSaveAction::class;
}
public static function title(): string
{
return 'Rechnung';
}
}

View File

@ -2,7 +2,6 @@
namespace App\Bill;
use App\Setting\BillSettings;
use Inertia\Inertia;
use Inertia\Response;
use Lorisleiva\Actions\Concerns\AsAction;
@ -31,9 +30,9 @@ class SettingIndexAction
public function asController(BillSettings $settings): Response
{
session()->put('menu', 'setting');
session()->put('title', 'Einstellungen');
session()->put('title', 'Rechnungs-Einstellungen');
return Inertia::render('setting/Index', [
return Inertia::render('setting/Bill', [
'data' => $this->handle($settings),
]);
}

View File

@ -2,7 +2,6 @@
namespace App\Bill;
use App\Setting\BillSettings;
use Illuminate\Http\RedirectResponse;
use Lorisleiva\Actions\ActionRequest;
use Lorisleiva\Actions\Concerns\AsAction;

View File

@ -28,7 +28,7 @@ class SettingIndexAction
session()->put('menu', 'setting');
session()->put('title', 'Mailman-Einstellungen');
return Inertia::render('setting/Index', [
return Inertia::render('setting/Mailman', [
'data' => $this->handle($settings),
]);
}

View File

@ -2,9 +2,11 @@
namespace App\Mailman;
use Spatie\LaravelSettings\Settings;
use App\Mailman\Actions\SettingIndexAction;
use App\Mailman\Actions\SettingSaveAction;
use App\Setting\LocalSettings;
class MailmanSettings extends Settings
class MailmanSettings extends LocalSettings
{
public ?string $base_url;
@ -16,4 +18,24 @@ class MailmanSettings extends Settings
{
return 'mailman';
}
public static function slug(): string
{
return 'mailman';
}
public static function indexAction(): string
{
return SettingIndexAction::class;
}
public static function saveAction(): string
{
return SettingSaveAction::class;
}
public static function title(): string
{
return 'Mailman';
}
}

View File

@ -3,6 +3,7 @@
namespace App\Mailman\Support;
use App\Mailman\Exceptions\MailmanServiceException;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\LazyCollection;
@ -25,9 +26,13 @@ class MailmanService
public function check(): bool
{
$response = $this->http()->get('/system/versions');
try {
$response = $this->http()->get('/system/versions');
return 200 === $response->status();
return 200 === $response->status();
} catch (ConnectionException $e) {
return false;
}
}
/**

View File

@ -1,29 +0,0 @@
<?php
namespace App\Setting;
use Spatie\LaravelSettings\Settings;
class BillSettings extends Settings
{
public string $from_long;
public string $from;
public string $mobile;
public string $email;
public string $website;
public string $address;
public string $place;
public string $zip;
public static function group(): string
{
return 'bill';
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace App\Setting\Controllers;
use App\Http\Controllers\Controller;
use App\Setting\GeneralSettings;
use Inertia\Inertia;
use Inertia\Response;
class SettingController extends Controller
{
/**
* @wip
*/
public function index(GeneralSettings $generalSettings): Response
{
return Inertia::render('setting/Index', [
'options' => [
'modules' => $generalSettings->moduleOptions(),
],
'general' => [
'modules' => $generalSettings->modules,
],
]);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Setting;
use Spatie\LaravelSettings\Settings;
abstract class LocalSettings extends Settings
{
abstract public static function slug(): string;
/**
* @return class-string
*/
abstract public static function indexAction(): string;
/**
* @return class-string
*/
abstract public static function saveAction(): string;
abstract public static function title(): string;
public static function url(): string
{
return '/setting/'.static::slug();
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Setting;
use Illuminate\Routing\Router;
class SettingFactory
{
/**
* @var array<int, class-string<LocalSettings>>
*/
private array $settings = [];
/**
* @param class-string<LocalSettings> $setting
*/
public function register(string $setting): void
{
$this->settings[] = $setting;
app(Router::class)->middleware(['web', 'auth:web', SettingMiddleware::class])->get($setting::url(), $setting::indexAction());
app(Router::class)->middleware(['web', 'auth:web', SettingMiddleware::class])->post($setting::url(), $setting::saveAction());
if (1 === count($this->settings)) {
app(Router::class)->redirect('/setting', '/setting/'.$setting::slug());
}
}
/**
* @return array<int, array{url: string, is_active: bool}>
*/
public function getShare(): array
{
return collect($this->settings)->map(fn ($setting) => [
'url' => $setting::url(),
'is_active' => request()->fullUrlIs(url($setting::url())),
'title' => $setting::title(),
])
->toArray();
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Setting;
use Closure;
use Illuminate\Http\Request;
use Inertia;
class SettingMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
*
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
Inertia::share([
'setting_menu' => app(SettingFactory::class)->getShare(),
]);
return $next($request);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Setting;
use App\Bill\BillSettings;
use App\Mailman\MailmanSettings;
use Illuminate\Support\ServiceProvider;
class SettingServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
app()->singleton(SettingFactory::class, fn () => new SettingFactory());
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
app(SettingFactory::class)->register(BillSettings::class);
app(SettingFactory::class)->register(MailmanSettings::class);
}
}

View File

@ -187,6 +187,7 @@ return [
App\Providers\TelescopeServiceProvider::class,
App\Tex\TexServiceProvider::class,
App\Dav\ServiceProvider::class,
App\Setting\SettingServiceProvider::class,
],
/*

View File

@ -5,10 +5,9 @@
v-for="(item, index) in entries"
:key="index"
href="#"
:data-cy="`menu-${item.id}`"
@click.prevent="openMenu(index)"
class="rounded py-1 px-3 text-gray-400"
:class="index == value ? `bg-gray-600` : ''"
:class="index === value ? `bg-gray-600` : ''"
v-text="item.title"
></a>
</div>

View File

@ -0,0 +1,58 @@
<template>
<form class="grow p-6 grid grid-cols-2 gap-3 items-start content-start" @submit.prevent="submit">
<f-text
label="Absender"
hint="Absender-Name, i.d.R. der kurze Stammesname"
name="from"
id="from"
v-model="inner.from"
></f-text>
<f-text
label="Absender (lang)"
v-model="inner.from_long"
name="from_long"
id="from_long"
hint="Absender-Name in Kurzform, i.d.R. der Stammesname"
></f-text>
<h2 class="text-lg font-semibold text-gray-300 col-span-2 mt-5">Kontaktdaten</h2>
<div class="col-span-2 text-gray-300 text-sm">
Diese Kontaktdaten stehen im Absender-Bereich auf der Rechnung.
</div>
<f-text label="Straße" v-model="inner.address" name="address" id="address"></f-text>
<f-text label="PLZ" v-model="inner.zip" name="zip" id="zip"></f-text>
<f-text label="Ort" v-model="inner.place" name="place" id="place"></f-text>
<f-text label="E-Mail-Adresse" v-model="inner.email" name="email" id="email"></f-text>
<f-text label="Telefonnummer" v-model="inner.mobile" name="mobile" id="mobile"></f-text>
<f-text label="Webseite" v-model="inner.website" name="website" id="website"></f-text>
<div>
<button type="submit" class="mt-3 btn block btn-primary">Speichern</button>
</div>
</form>
</template>
<script>
import AppLayout from '../../layouts/AppLayout.vue';
import SettingLayout from './Layout.vue';
export default {
layout: [AppLayout, SettingLayout],
data: function () {
return {
inner: {},
};
},
props: {
data: {},
},
methods: {
submit() {
this.$inertia.post('/setting/bill', this.inner);
},
},
created() {
this.inner = this.data;
},
};
</script>

View File

@ -1,67 +0,0 @@
<template>
<form class="flex grow relative" @submit.prevent="submit">
<!-- ********************************* Tabs ********************************** -->
<v-tabs v-model="active" :entries="menu">
<div slot="bottom">
<button type="submit" class="mt-3 btn block w-full btn-primary">Speichern</button>
</div>
</v-tabs>
<div class="grow p-6 grid grid-cols-2 gap-3 items-start content-start" v-show="menuTitle === 'Rechnung'">
<f-text
label="Absender"
hint="Absender-Name, i.d.R. der kurze Stammesname"
name="bill_from"
id="bill_from"
v-model="inner.bill_from"
></f-text>
<f-text
label="Absender (lang)"
v-model="inner.bill_from_long"
name="bill_from_long"
id="bill_from_long"
hint="Absender-Name in Kurzform, i.d.R. der Stammesname"
></f-text>
<h2 class="text-lg font-semibold text-gray-300 col-span-2 mt-5">Kontaktdaten</h2>
<div class="col-span-2 text-gray-300 text-sm">
Diese Kontaktdaten stehen im Absender-Bereich auf der Rechnung.
</div>
<f-text label="Straße" v-model="inner.bill_address" name="bill_address" id="bill_address"></f-text>
<f-text label="PLZ" v-model="inner.bill_zip" name="bill_zip" id="bill_zip"></f-text>
<f-text label="Ort" v-model="inner.bill_place" name="bill_place" id="bill_place"></f-text>
<f-text label="E-Mail-Adresse" v-model="inner.bill_email" name="bill_email" id="bill_email"></f-text>
<f-text label="Telefonnummer" v-model="inner.bill_mobile" name="bill_mobile" id="bill_mobile"></f-text>
<f-text label="Webseite" v-model="inner.bill_website" name="bill_website" id="bill_website"></f-text>
</div>
</form>
</template>
<script>
export default {
data: function () {
return {
inner: {},
active: 0,
menu: [{id: 'bill', title: 'Rechnung'}],
};
},
props: {
data: {},
},
methods: {
submit() {
this.$inertia.post('/setting', this.inner);
},
},
components: {
'v-tabs': () => import('../../components/VTabs.vue'),
},
computed: {
menuTitle() {
return this.menu[this.active].title;
},
},
created() {
this.inner = this.data;
},
};
</script>

View File

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

View File

@ -0,0 +1,37 @@
<template>
<form class="grow p-6 grid grid-cols-2 gap-3 items-start content-start" @submit.prevent="submit">
<f-text label="URL" hint="URL der Mailman Api" name="base_url" id="base_url" v-model="inner.base_url"></f-text>
<f-text label="Benutzername" name="username" id="username" v-model="inner.username"></f-text>
<f-text label="Passwort" name="password" id="password" v-model="inner.password"></f-text>
<div></div>
<div>
<button type="submit" class="mt-3 btn block btn-primary">Speichern</button>
</div>
</form>
</template>
<script>
import AppLayout from '../../layouts/AppLayout.vue';
import SettingLayout from './Layout.vue';
export default {
layout: [AppLayout, SettingLayout],
data: function () {
return {
inner: {},
};
},
props: {
data: {},
},
methods: {
submit() {
this.$inertia.post('/setting/mailman', this.inner);
},
},
created() {
this.inner = this.data;
},
};
</script>

View File

@ -1,14 +1,10 @@
<?php
use App\Bill\SettingIndexAction as BillSettingIndexAction;
use App\Bill\SettingSaveAction as BillSettingSaveAction;
use App\Contribution\ContributionController;
use App\Course\Controllers\CourseController;
use App\Http\Controllers\HomeController;
use App\Initialize\Actions\InitializeAction;
use App\Initialize\Actions\InitializeFormAction;
use App\Mailman\Actions\SettingIndexAction as MailmanSettingIndexAction;
use App\Mailman\Actions\SettingSaveAction as MailmanSettingSaveAction;
use App\Member\Controllers\MemberResyncController;
use App\Member\MemberConfirmController;
use App\Member\MemberController;
@ -38,10 +34,6 @@ Route::group(['middleware' => 'auth:web'], function (): void {
Route::get('/sendpayment', [SendpaymentController::class, 'create'])->name('sendpayment.create');
Route::get('/sendpayment/pdf', [SendpaymentController::class, 'send'])->name('sendpayment.pdf');
Route::apiResource('member.membership', MembershipController::class);
Route::get('setting/bill', BillSettingIndexAction::class);
Route::post('setting/bill', BillSettingSaveAction::class);
Route::get('setting/mailman', MailmanSettingIndexAction::class);
Route::post('setting/mailman', MailmanSettingSaveAction::class);
Route::resource('member.course', CourseController::class);
Route::get('/member/{member}/efz', MemberEfzController::class)->name('efz');
Route::get('/member/{member}/resync', MemberResyncController::class)->name('member.resync');

View File

@ -1,8 +1,8 @@
<?php
namespace Tests\Feature\Setting;
namespace Tests\Feature\Bill;
use App\Setting\BillSettings;
use App\Bill\BillSettings;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
@ -39,6 +39,25 @@ class SettingTest extends TestCase
], $response, 'data');
}
public function testItReturnsTabs(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$response = $this->get('/setting/bill');
/** @var array<int, array{url: string, is_active: bool}> */
$menus = $this->inertia($response, 'setting_menu');
$this->assertTrue(
collect($menus)
->pluck('url')
->contains('/setting/bill')
);
$settingMenu = collect($menus)->first(fn ($menu) => '/setting/bill' === $menu['url']);
$this->assertTrue($settingMenu['is_active']);
$this->assertEquals('Rechnung', $settingMenu['title']);
}
public function testItCanChangeSettings(): void
{
$this->withoutExceptionHandling()->login()->loginNami();