Add module management

This commit is contained in:
Philipp Lang 2023-11-16 10:52:57 +01:00
parent fc368bcd1a
commit 613096220f
14 changed files with 292 additions and 64 deletions

View File

@ -3,7 +3,6 @@ APP_ENV=production
APP_KEY=YOUR_APP_KEY APP_KEY=YOUR_APP_KEY
APP_DEBUG=false APP_DEBUG=false
APP_URL=http://localhost:8000 APP_URL=http://localhost:8000
APP_MODE=stamm
MAIL_MAILER=smtp MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io MAIL_HOST=smtp.mailtrap.io

View File

@ -41,7 +41,6 @@ steps:
APP_ENV: local APP_ENV: local
APP_DEBUG: true APP_DEBUG: true
APP_URL: http://scoutrobot.test APP_URL: http://scoutrobot.test
APP_MODE: stamm
LOG_CHANNEL: stack LOG_CHANNEL: stack
DB_CONNECTION: mysql DB_CONNECTION: mysql
DB_HOST: db DB_HOST: db

View File

@ -3,7 +3,7 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use App\Http\Resources\UserResource; use App\Http\Resources\UserResource;
use App\Setting\GeneralSettings; use App\Module\ModuleSettings;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Inertia\Middleware; use Inertia\Middleware;
@ -53,7 +53,7 @@ class HandleInertiaRequests extends Middleware
return session()->get('title', ''); return session()->get('title', '');
}, },
'settings' => [ 'settings' => [
'modules' => app(GeneralSettings::class)->modules, 'modules' => app(ModuleSettings::class)->modules,
], ],
]; ];
} }

35
app/Module/Module.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace App\Module;
enum Module: string
{
case BILL = 'bill';
case COURSE = 'course';
public function title(): string
{
return match ($this) {
static::BILL => 'Zahlungs-Management',
static::COURSE => 'Ausbildung',
};
}
/**
* @return array<int, array{id: int, name: string}>
*/
public static function forSelect(): array
{
return array_map(fn ($module) => ['id' => $module->value, 'name' => $module->title()], static::cases());
}
/**
* @return array<int, string>
*/
public static function values(): array
{
return array_map(fn ($module) => $module->value, static::cases());
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Module;
use Inertia\Inertia;
use Inertia\Response;
use Lorisleiva\Actions\Concerns\AsAction;
class ModuleIndexAction
{
use AsAction;
/**
* @return array<string, mixed>
*/
public function handle(ModuleSettings $settings): array
{
return [
'data' => [
'modules' => $settings->modules,
],
'meta' => ['modules' => Module::forSelect()],
];
}
public function asController(ModuleSettings $settings): Response
{
session()->put('menu', 'setting');
session()->put('title', 'Module');
return Inertia::render('setting/Module', [
'data' => $this->handle($settings),
]);
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Module;
use App\Setting\Contracts\Indexable;
use App\Setting\Contracts\Storeable;
use App\Setting\LocalSettings;
class ModuleSettings extends LocalSettings implements Indexable, Storeable
{
/** @var array<int, string> */
public array $modules;
public static function group(): string
{
return 'module';
}
public static function slug(): string
{
return 'module';
}
public static function title(): string
{
return 'Module';
}
public static function indexAction(): string
{
return ModuleIndexAction::class;
}
public static function storeAction(): string
{
return ModuleStoreAction::class;
}
public function hasModule(string $module): bool
{
return in_array($module, $this->modules);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Module;
use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\Rule;
use Lorisleiva\Actions\ActionRequest;
use Lorisleiva\Actions\Concerns\AsAction;
class ModuleStoreAction
{
use AsAction;
/**
* @param array<string, mixed> $input
*/
public function handle(array $input): void
{
$settings = app(ModuleSettings::class);
$settings->fill([
'modules' => $input['modules'],
]);
$settings->save();
}
/**
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'modules' => 'present|array',
'modules.*' => ['string', Rule::in(Module::values())],
];
}
public function asController(ActionRequest $request): RedirectResponse
{
$this->handle($request->validated());
return redirect()->back();
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace App\Setting;
use Spatie\LaravelSettings\Settings;
class GeneralSettings extends Settings
{
/** @var array<int, string> */
public array $modules;
public bool $single_view;
/** @var array<int, int> */
public array $allowed_nami_accounts;
public static function group(): string
{
return 'general';
}
public function hasModule(string $module): bool
{
return in_array($module, $this->modules);
}
}

View File

@ -4,6 +4,7 @@ namespace App\Setting;
use App\Invoice\InvoiceSettings; use App\Invoice\InvoiceSettings;
use App\Mailgateway\MailgatewaySettings; use App\Mailgateway\MailgatewaySettings;
use App\Module\ModuleSettings;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
class SettingServiceProvider extends ServiceProvider class SettingServiceProvider extends ServiceProvider
@ -25,6 +26,7 @@ class SettingServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
app(SettingFactory::class)->register(ModuleSettings::class);
app(SettingFactory::class)->register(InvoiceSettings::class); app(SettingFactory::class)->register(InvoiceSettings::class);
app(SettingFactory::class)->register(MailgatewaySettings::class); app(SettingFactory::class)->register(MailgatewaySettings::class);
app(SettingFactory::class)->register(NamiSettings::class); app(SettingFactory::class)->register(NamiSettings::class);

View File

@ -94,18 +94,6 @@ return [
'fallback_locale' => 'en', 'fallback_locale' => 'en',
/*
|--------------------------------------------------------------------------
| App Mode
|--------------------------------------------------------------------------
|
| The mode of the app will set some default settings for you on initial
| database setup. You can change these Settings anytime later, but it
| usually defines a good starting point for you to set up the world.
|
*/
'mode' => env('APP_MODE', 'stamm'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Faker Locale | Faker Locale

View File

@ -4,29 +4,9 @@ use Spatie\LaravelSettings\Migrations\SettingsMigration;
class CreateGeneralSettings extends SettingsMigration class CreateGeneralSettings extends SettingsMigration
{ {
/**
* @return array<string, array<int,string>|bool>
*/
public function defaults(string $mode): array
{
$defaults = [
'diözese' => [
'modules' => ['courses'],
'single_view' => false,
],
'stamm' => [
'modules' => ['bill', 'courses'],
'single_view' => true,
],
];
return $defaults[$mode];
}
public function up(): void public function up(): void
{ {
$defaults = $this->defaults(config('app.mode')); $this->migrator->add('general.modules', []);
$this->migrator->add('general.modules', $defaults['modules']); $this->migrator->add('general.single_view', false);
$this->migrator->add('general.single_view', $defaults['single_view']);
} }
} }

View File

@ -0,0 +1,14 @@
<?php
use App\Module\Module;
use Spatie\LaravelSettings\Migrations\SettingsMigration;
return new class extends SettingsMigration
{
public function up(): void
{
$this->migrator->delete('general.modules');
$this->migrator->delete('general.single_view');
$this->migrator->add('module.modules', collect(Module::cases())->map(fn ($module) => $module->value));
}
};

View File

@ -0,0 +1,51 @@
<template>
<page-layout>
<template #right>
<f-save-button form="modulesettingform"></f-save-button>
</template>
<setting-layout>
<form id="modulesettingform" class="grow p-6 grid grid-cols-2 gap-3 items-start content-start"
@submit.prevent="submit">
<div class="col-span-full text-gray-100 mb-3">
<p class="text-sm">Hier kannst du Funktionen innerhalb von Adrema (Module) aktivieren oder deaktivieren
und so den Funktionsumfang auf deine Bedürfnisse anpassen.</p>
</div>
<div class="grid grid-cols-2 gap-4">
<f-switch v-for="module in meta.modules" :id="module.id" v-model="inner.modules" :value="module.id"
size="sm" name="modules" :label="module.name"></f-switch>
</div>
</form>
</setting-layout>
</page-layout>
</template>
<script>
import SettingLayout from './Layout.vue';
export default {
components: {
SettingLayout,
},
props: {
data: {
type: Object,
default: () => {
return {};
},
},
},
data: function () {
return {
inner: { ...this.data.data },
meta: { ...this.data.meta },
};
},
methods: {
submit() {
this.$inertia.post('/setting/module', this.inner, {
onSuccess: () => this.$success('Einstellungen gespeichert.'),
});
},
},
};
</script>

View File

@ -0,0 +1,63 @@
<?php
namespace Tests\Feature;
use App\Module\Module;
use App\Module\ModuleSettings;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
class ModuleTest extends TestCase
{
use DatabaseTransactions;
public function testItGetsModuleSettings(): void
{
$this->login()->loginNami();
ModuleSettings::fake(['modules' => ['bill']]);
$response = $this->get('/setting/module');
$response->assertOk();
$this->assertCount(count(Module::cases()), $this->inertia($response, 'data.meta.modules'));
$this->assertInertiaHas([
'name' => 'Zahlungs-Management',
'id' => 'bill',
], $response, 'data.meta.modules.0');
$this->assertEquals(['bill'], $this->inertia($response, 'data.data.modules'));
}
public function testItSavesSettings(): void
{
$this->login()->loginNami();
$response = $this->from('/setting/module')->post('/setting/module', [
'modules' => ['bill'],
]);
$response->assertRedirect('/setting/module');
$this->assertEquals(['bill'], app(ModuleSettings::class)->modules);
}
public function testModuleMustExists(): void
{
$this->login()->loginNami();
$response = $this->from('/setting/module')->post('/setting/module', [
'modules' => ['lalala'],
]);
$response->assertSessionHasErrors('modules.0');
}
public function testItReturnsModulesOnEveryPage(): void
{
$this->login()->loginNami();
ModuleSettings::fake(['modules' => ['bill']]);
$response = $this->get('/');
$this->assertEquals(['bill'], $this->inertia($response, 'settings.modules'));
}
}