From 1d09afbeb3c8142855ee6eb1fd342895ca419c26 Mon Sep 17 00:00:00 2001 From: philipp lang Date: Sun, 22 Dec 2024 18:34:00 +0100 Subject: [PATCH] Add prevention settings --- app/Lib/Editor/EditorData.php | 6 +- app/Prevention/Actions/SettingApiAction.php | 19 -- app/Prevention/Actions/SettingStoreAction.php | 33 --- app/Setting/SettingServiceProvider.php | 5 - app/View/Form/Editor.php | 52 ++++ .../{SettingView.php => FormSettingView.php} | 2 +- ...ngViewTest.php => FormSettingViewTest.php} | 2 +- .../Form/Components/PreventionSettingView.php | 50 ++++ .../Components/PreventionSettingViewTest.php | 40 +++ modules/Form/FormServiceProvider.php | 8 +- resources/livewire-js/app.js | 5 + resources/livewire-js/editor.js | 227 ++++++++++++++++++ routes/api.php | 4 - tests/Feature/Prevention/SettingTest.php | 40 --- 14 files changed, 387 insertions(+), 106 deletions(-) delete mode 100644 app/Prevention/Actions/SettingApiAction.php delete mode 100644 app/Prevention/Actions/SettingStoreAction.php create mode 100644 app/View/Form/Editor.php rename modules/Form/Components/{SettingView.php => FormSettingView.php} (97%) rename modules/Form/Components/{SettingViewTest.php => FormSettingViewTest.php} (94%) create mode 100644 modules/Form/Components/PreventionSettingView.php create mode 100644 modules/Form/Components/PreventionSettingViewTest.php create mode 100644 resources/livewire-js/editor.js delete mode 100644 tests/Feature/Prevention/SettingTest.php diff --git a/app/Lib/Editor/EditorData.php b/app/Lib/Editor/EditorData.php index debffc23..cf250b1d 100644 --- a/app/Lib/Editor/EditorData.php +++ b/app/Lib/Editor/EditorData.php @@ -2,12 +2,16 @@ namespace App\Lib\Editor; +use Livewire\Wireable; +use Spatie\LaravelData\Concerns\WireableData; use Spatie\LaravelData\Data; /** @todo replace blocks with actual block data classes */ -class EditorData extends Data implements Editorable +class EditorData extends Data implements Editorable, Wireable { + use WireableData; + /** @param array $blocks */ public function __construct( public string $version, diff --git a/app/Prevention/Actions/SettingApiAction.php b/app/Prevention/Actions/SettingApiAction.php deleted file mode 100644 index edcdd707..00000000 --- a/app/Prevention/Actions/SettingApiAction.php +++ /dev/null @@ -1,19 +0,0 @@ -json([ - 'data' => app(PreventionSettings::class)->toArray(), - ]); - } -} diff --git a/app/Prevention/Actions/SettingStoreAction.php b/app/Prevention/Actions/SettingStoreAction.php deleted file mode 100644 index cf72952c..00000000 --- a/app/Prevention/Actions/SettingStoreAction.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ - public function rules(): array - { - return [ - 'formmail' => 'array', - ]; - } - - public function handle(ActionRequest $request): void - { - $settings = app(PreventionSettings::class); - $settings->formmail = EditorData::from($request->formmail); - $settings->save(); - - Succeeded::message('Einstellungen gespeichert.')->dispatch(); - } -} diff --git a/app/Setting/SettingServiceProvider.php b/app/Setting/SettingServiceProvider.php index cec22ee5..f851cd32 100644 --- a/app/Setting/SettingServiceProvider.php +++ b/app/Setting/SettingServiceProvider.php @@ -2,10 +2,7 @@ namespace App\Setting; -use App\Fileshare\FileshareSettings; -use App\Form\FormSettings; use Modules\Module\ModuleSettings; -use App\Prevention\PreventionSettings; use App\Setting\Data\SettingSynthesizer; use Illuminate\Routing\Router; use Illuminate\Support\ServiceProvider; @@ -35,8 +32,6 @@ class SettingServiceProvider extends ServiceProvider app(SettingFactory::class)->register(ModuleSettings::class); app(SettingFactory::class)->register(InvoiceSettings::class); app(SettingFactory::class)->register(NamiSettings::class); - app(SettingFactory::class)->register(FileshareSettings::class); - app(SettingFactory::class)->register(PreventionSettings::class); Livewire::propertySynthesizer(SettingSynthesizer::class); } diff --git a/app/View/Form/Editor.php b/app/View/Form/Editor.php new file mode 100644 index 00000000..4d246640 --- /dev/null +++ b/app/View/Form/Editor.php @@ -0,0 +1,52 @@ +id = str()->uuid()->toString(); + } + + public function render() + { + return <<<'HTML' +
+ @if ($label) + {{$label}} + @endif + +
+
+ + @if($hint) + {{$hint}} + @endif +
+
+ HTML; + } +} diff --git a/modules/Form/Components/SettingView.php b/modules/Form/Components/FormSettingView.php similarity index 97% rename from modules/Form/Components/SettingView.php rename to modules/Form/Components/FormSettingView.php index 9608cd41..74850fcf 100644 --- a/modules/Form/Components/SettingView.php +++ b/modules/Form/Components/FormSettingView.php @@ -5,7 +5,7 @@ namespace Modules\Form\Components; use App\Form\FormSettings; use Livewire\Component; -class SettingView extends Component +class FormSettingView extends Component { public string $clearCacheUrl; diff --git a/modules/Form/Components/SettingViewTest.php b/modules/Form/Components/FormSettingViewTest.php similarity index 94% rename from modules/Form/Components/SettingViewTest.php rename to modules/Form/Components/FormSettingViewTest.php index 8e5b1c47..41428d0e 100644 --- a/modules/Form/Components/SettingViewTest.php +++ b/modules/Form/Components/FormSettingViewTest.php @@ -13,7 +13,7 @@ uses(DatabaseTransactions::class); it('it renders page', function () { test()->withoutExceptionHandling()->login()->loginNami(); - test()->get('/setting/form')->assertSeeLivewire(SettingView::class); + test()->get('/setting/form')->assertSeeLivewire(FormSettingView::class); }); it('it displays active modules', function () { diff --git a/modules/Form/Components/PreventionSettingView.php b/modules/Form/Components/PreventionSettingView.php new file mode 100644 index 00000000..33e9fe39 --- /dev/null +++ b/modules/Form/Components/PreventionSettingView.php @@ -0,0 +1,50 @@ + 'array', + ]; + } + + public function mount(): void + { + $this->formmail = app(PreventionSettings::class)->formmail; + } + + public function save(): void + { + app(PreventionSettings::class)->fill(['formmail' => $this->formmail])->save(); + $this->dispatch('success', 'Einstellungen gespeichert.'); + } + + public function render() + { + return <<<'HTML' + + + + +
+ + Hier kannst du Einstellungen zu Prävention setzen. + + +
+
+ HTML; + } +} diff --git a/modules/Form/Components/PreventionSettingViewTest.php b/modules/Form/Components/PreventionSettingViewTest.php new file mode 100644 index 00000000..88559303 --- /dev/null +++ b/modules/Form/Components/PreventionSettingViewTest.php @@ -0,0 +1,40 @@ +withoutExceptionHandling()->login()->loginNami(); + + test()->get('/setting/prevention')->assertSeeLivewire(PreventionSettingView::class); +}); + +it('displays settings', function () { + test()->withoutExceptionHandling()->login()->loginNami(); + $text = EditorRequestFactory::new()->text(50, 'lorem ipsum')->toData(); + app(PreventionSettings::class)->fill(['formmail' => $text])->save(); + + Livewire::test(PreventionSettingView::class) + ->assertSet('formmail.blocks.0.data.text', 'lorem ipsum') + ->assertSeeHtml('data-active'); +}); + +it('saves settings', function () { + test()->withoutExceptionHandling()->login()->loginNami(); + + $text = EditorRequestFactory::new()->text(50, 'new lorem')->create(); + Livewire::test(PreventionSettingView::class) + ->set('formmail', $text) + ->call('save') + ->assertDispatched('success', 'Einstellungen gespeichert.'); + + $this->assertTrue(app(PreventionSettings::class)->formmail->hasAll(['new lorem'])); +}); diff --git a/modules/Form/FormServiceProvider.php b/modules/Form/FormServiceProvider.php index c97df9d0..c3e7761e 100644 --- a/modules/Form/FormServiceProvider.php +++ b/modules/Form/FormServiceProvider.php @@ -4,9 +4,11 @@ namespace Modules\Form; use Illuminate\Routing\Router; use Illuminate\Support\ServiceProvider; -use Modules\Form\Components\SettingView; +use Modules\Form\Components\FormSettingView; use App\Setting\SettingFactory; use App\Form\FormSettings; +use App\Prevention\PreventionSettings; +use Modules\Form\Components\PreventionSettingView; class FormServiceProvider extends ServiceProvider { @@ -27,9 +29,11 @@ class FormServiceProvider extends ServiceProvider public function boot() { app(SettingFactory::class)->register(FormSettings::class); + app(SettingFactory::class)->register(PreventionSettings::class); app(Router::class)->middleware(['web', 'auth:web'])->group(function ($router) { - $router->get('/setting/form', SettingView::class)->name('setting.form'); + $router->get('/setting/form', FormSettingView::class)->name('setting.form'); + $router->get('/setting/prevention', PreventionSettingView::class)->name('setting.prevention'); }); } } diff --git a/resources/livewire-js/app.js b/resources/livewire-js/app.js index b39ec7ec..0820e9da 100644 --- a/resources/livewire-js/app.js +++ b/resources/livewire-js/app.js @@ -6,6 +6,7 @@ import 'tippy.js/dist/tippy.css'; import 'tippy.js/animations/shift-toward.css'; import '../css/tooltip.css'; import {error, success} from './toastify.js'; +import editor from './editor.js'; Alpine.plugin( Tooltip.defaultProps({ @@ -16,4 +17,8 @@ Alpine.plugin( window.addEventListener('success', (event) => success(event.detail[0])); +document.addEventListener('alpine:init', () => { + Alpine.bind('editor', () => editor); +}); + Livewire.start(); diff --git a/resources/livewire-js/editor.js b/resources/livewire-js/editor.js new file mode 100644 index 00000000..dd1b4d2f --- /dev/null +++ b/resources/livewire-js/editor.js @@ -0,0 +1,227 @@ +import {debounce} from 'lodash'; +import EditorJS from '@editorjs/editorjs'; +import Header from '@editorjs/header'; +import Paragraph from '@editorjs/paragraph'; +import NestedList from '@editorjs/nested-list'; +import Alert from 'editorjs-alert'; + +class ConditionTune { + constructor({api, data, config, block}) { + this.api = api; + this.data = data || { + mode: 'all', + ifs: [], + }; + this.config = config; + this.block = block; + this.wrapper = null; + } + + static get isTune() { + return true; + } + + wrap(blockContent) { + this.wrapper = document.createElement('div'); + + var tooltip = document.createElement('div'); + tooltip.setAttribute('data-tooltip', ''); + + var content = document.createElement('div'); + content.setAttribute('data-content', ''); + + content.appendChild(blockContent); + + this.wrapper.appendChild(tooltip); + this.wrapper.appendChild(content); + + this.styleWrapper(); + + return this.wrapper; + } + + hasData() { + return this.data.ifs.length > 0; + } + + styleWrapper() { + if (this.hasData()) { + this.wrapper.querySelector('[data-content]').className = 'p-1 border border-blue-100 rounded'; + this.wrapper.querySelector('[data-tooltip]').className = + 'mt-1 inline-block tracking-wider font-semibold ml-2 mr-2 px-2 py-1 items-center text-xs leading-none bg-blue-100 text-blue-900 rounded-t-lg'; + this.wrapper.querySelector('[data-tooltip]').innerHTML = this.descriptionName(); + } else { + this.wrapper.querySelector('[data-content]').className = ''; + this.wrapper.querySelector('[data-tooltip]').className = ''; + this.wrapper.querySelector('[data-tooltip]').innerHTML = ''; + } + } + + descriptionName() { + return ( + 'Bedingung ' + + this.data.ifs + .map((i) => { + var parts = [i.field]; + + if (i.comparator === 'isEqual' || i.comparator === 'isIn') { + parts.push('='); + } + + if (i.comparator === 'isNotEqual' || i.comparator === 'isNotIn') { + parts.push('≠'); + } + + if (typeof i.value === 'string') { + parts.push(i.value); + } + + if (Array.isArray(i.value)) { + parts.push(i.value.join(', ')); + } + + if (typeof i.value === 'boolean') { + parts.push(i.value ? 'An' : 'Aus'); + } + + return parts.join(' '); + }) + .join(', ') + ); + } + + render() { + return { + label: 'Bedingungen', + closeOnActivate: true, + toggle: true, + onActivate: async () => { + this.data = await openPopup(this.data); + this.styleWrapper(); + this.block.dispatchChange(); + }, + }; + } + + save() { + return this.data; + } +} + +export default { + 'editor': null, + 'value': null, + + 'x-init': async function () { + var tools = { + paragraph: { + class: Paragraph, + shortcut: 'CTRL+P', + inlineToolbar: true, + config: { + preserveBlank: true, + placeholder: 'Absatz', + }, + }, + alert: { + class: Alert, + inlineToolbar: true, + config: { + defaultType: 'primary', + }, + }, + heading: { + class: Header, + shortcut: 'CTRL+H', + inlineToolbar: true, + config: { + placeholder: 'Überschrift', + levels: [2, 3, 4], + defaultLevel: 2, + }, + }, + list: { + class: NestedList, + shortcut: 'CTRL+L', + inlineToolbar: true, + }, + }; + + var tunes = []; + + // if (props.conditions) { + // tools.condition = { + // class: ConditionTune, + // }; + // tunes.push('condition'); + // } + + this.editor = new EditorJS({ + placeholder: '', + holder: this.$el.getAttribute('id'), + minHeight: 0, + defaultBlock: 'paragraph', + data: this.value, + tunes: tunes, + tools: tools, + onChange: debounce(async (api, event) => { + const data = await this.editor.save(); + this.$dispatch('updated', data); + }, 200), + onPopup: () => { + console.log('opened'); + }, + }); + await this.editor.isReady; + }, +}; + +// const props = defineProps({ +// required: { +// type: Boolean, +// default: false, +// }, +// size: { +// type: String, +// default: () => 'base', +// }, +// rows: { +// type: Number, +// default: () => 4, +// }, +// id: { +// type: String, +// required: true, +// }, +// conditions: { +// required: false, +// type: Boolean, +// default: () => false, +// }, +// hint: { +// type: String, +// default: () => '', +// }, +// modelValue: { +// default: undefined, +// }, +// label: { +// type: String, +// default: () => '', +// }, +// }); + +async function openPopup(data) { + return new Promise((resolve, reject) => { + new Promise((innerResolve, innerReject) => { + condition.value = { + resolve: innerResolve, + reject: innerReject, + data: data, + }; + }).then((data) => { + resolve(data); + condition.value = null; + }); + }); +} diff --git a/routes/api.php b/routes/api.php index 9e751f3a..70730f99 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,13 +3,9 @@ use App\Contribution\Actions\GenerateApiAction as ContributionGenerateApiAction; use App\Form\Actions\FormApiListAction; use App\Form\Actions\RegisterAction; -use App\Prevention\Actions\SettingStoreAction as PreventionStoreAction; use App\Group\Actions\GroupApiIndexAction; -use App\Prevention\Actions\SettingApiAction; Route::post('/contribution-generate', ContributionGenerateApiAction::class)->name('api.contribution.generate')->middleware('client:contribution-generate'); Route::post('/form/{form}/register', RegisterAction::class)->name('form.register'); Route::get('/group/{group?}', GroupApiIndexAction::class)->name('api.group'); Route::get('/form', FormApiListAction::class)->name('api.form.index'); -Route::get('/prevention', SettingApiAction::class)->name('api.prevention.index'); -Route::post('/prevention', PreventionStoreAction::class)->name('api.prevention.store'); diff --git a/tests/Feature/Prevention/SettingTest.php b/tests/Feature/Prevention/SettingTest.php deleted file mode 100644 index b67fd4b4..00000000 --- a/tests/Feature/Prevention/SettingTest.php +++ /dev/null @@ -1,40 +0,0 @@ -login()->loginNami(); - - $this->get('/setting/prevention')->assertComponent('setting/Prevention')->assertOk(); - } - - public function testItReceivesSettings(): void - { - $this->login()->loginNami(); - - $text = EditorRequestFactory::new()->text(50, 'lorem ipsum')->toData(); - app(PreventionSettings::class)->fill(['formmail' => $text])->save(); - - $this->get('/api/prevention') - ->assertJsonPath('data.formmail.blocks.0.data.text', 'lorem ipsum'); - } - - public function testItStoresSettings(): void - { - $this->login()->loginNami(); - - $this->post('/api/prevention', ['formmail' => EditorRequestFactory::new()->text(50, 'new lorem')->create()])->assertOk(); - $this->assertTrue(app(PreventionSettings::class)->formmail->hasAll(['new lorem'])); - } -}