From 8feac575ad5fda6f3d8fd649cbec98fb7bc38b8b Mon Sep 17 00:00:00 2001 From: philipp lang Date: Tue, 24 Sep 2024 01:26:08 +0200 Subject: [PATCH] Add Service Provider --- composer.json | 1 + config/app.php | 2 +- .../Base/BaseServiceProvider.php | 13 ++-- modules/Base/Components/Page/Header.php | 34 +++++++++++ modules/Base/Components/Page/Layout.php | 54 ++++++++++++++++ .../Base/Components}/Page/MenuEntry.php | 2 +- .../Base/Components}/Page/Sidebar.php | 2 +- .../Base/Components}/Ui/Box.php | 2 +- .../Base/Components}/Ui/Sprite.php | 2 +- modules/Base/tests/PageLayoutTest.php | 34 +++++++++++ modules/Dashboard/Block.php | 12 ++++ .../Components/DashboardComponent.php | 35 +++++++++++ modules/Dashboard/DashboardFactory.php | 41 +++++++++++++ .../Dashboard/DashboardServiceProvider.php | 32 ++++++++++ .../tests/DashboardComponentTest.php | 47 ++++++++++++++ modules/Invoice/MemberPaymentBlock.php | 37 +++++++++++ modules/Member/AgeGroupCountBlock.php | 61 +++++++++++++++++++ modules/Member/TestersBlock.php | 43 +++++++++++++ modules/Prevention/EfzPendingBlock.php | 44 +++++++++++++ modules/Prevention/PsPendingBlock.php | 50 +++++++++++++++ .../views/components/layouts/app.blade.php | 20 ++++++ 21 files changed, 559 insertions(+), 9 deletions(-) rename app/Providers/LivewireServiceProvider.php => modules/Base/BaseServiceProvider.php (75%) create mode 100644 modules/Base/Components/Page/Header.php create mode 100644 modules/Base/Components/Page/Layout.php rename {app/View => modules/Base/Components}/Page/MenuEntry.php (94%) rename {app/View => modules/Base/Components}/Page/Sidebar.php (98%) rename {app/View => modules/Base/Components}/Ui/Box.php (96%) rename {app/View => modules/Base/Components}/Ui/Sprite.php (90%) create mode 100644 modules/Base/tests/PageLayoutTest.php create mode 100644 modules/Dashboard/Block.php create mode 100644 modules/Dashboard/Components/DashboardComponent.php create mode 100644 modules/Dashboard/DashboardFactory.php create mode 100644 modules/Dashboard/DashboardServiceProvider.php create mode 100644 modules/Dashboard/tests/DashboardComponentTest.php create mode 100644 modules/Invoice/MemberPaymentBlock.php create mode 100644 modules/Member/AgeGroupCountBlock.php create mode 100644 modules/Member/TestersBlock.php create mode 100644 modules/Prevention/EfzPendingBlock.php create mode 100644 modules/Prevention/PsPendingBlock.php create mode 100644 resources/views/components/layouts/app.blade.php diff --git a/composer.json b/composer.json index e9408df0..7380fee0 100644 --- a/composer.json +++ b/composer.json @@ -114,6 +114,7 @@ "autoload-dev": { "psr-4": { "Modules\\Dashboard\\Tests\\": "modules/dashboard/tests/", + "Modules\\Base\\Tests\\": "modules/base/tests/", "Tests\\": "tests/", "Zoomyboy\\LaravelNami\\Tests\\": "packages/laravel-nami/tests/" } diff --git a/config/app.php b/config/app.php index a763ad6e..f567ace3 100644 --- a/config/app.php +++ b/config/app.php @@ -178,7 +178,7 @@ return [ // App\Dashboard\DashboardServiceProvider::class, App\Providers\PluginServiceProvider::class, Modules\Dashboard\DashboardServiceProvider::class, - App\Providers\LivewireServiceProvider::class, + Modules\Base\BaseServiceProvider::class, ], /* diff --git a/app/Providers/LivewireServiceProvider.php b/modules/Base/BaseServiceProvider.php similarity index 75% rename from app/Providers/LivewireServiceProvider.php rename to modules/Base/BaseServiceProvider.php index 880ad376..0cb597eb 100644 --- a/app/Providers/LivewireServiceProvider.php +++ b/modules/Base/BaseServiceProvider.php @@ -1,11 +1,12 @@ register(AgeGroupCountBlock::class); app(DashboardFactory::class)->register(MemberPaymentBlock::class); @@ -29,6 +31,8 @@ class LivewireServiceProvider extends ServiceProvider app(DashboardFactory::class)->register(EfzPendingBlock::class); app(DashboardFactory::class)->register(PsPendingBlock::class); + Livewire::component('page.sidebar', Sidebar::class); + ComponentAttributeBag::macro('mergeWhen', function ($condition, $key, $attributes) { /** @var ComponentAttributeBag */ $self = $this; @@ -41,6 +45,7 @@ class LivewireServiceProvider extends ServiceProvider */ public function boot(): void { + Livewire::component('pagesidebar', Sidebar::class); // } } diff --git a/modules/Base/Components/Page/Header.php b/modules/Base/Components/Page/Header.php new file mode 100644 index 00000000..960223ad --- /dev/null +++ b/modules/Base/Components/Page/Header.php @@ -0,0 +1,34 @@ + +
+ {{ $beforeTitle ?? ''}} + {{ $title }} + {{ $toolbar ?? '' }} +
+
+ @if ($closeable) + + + + @endif + {{ $right ?? '' }} +
+ + HTML; + } +} diff --git a/modules/Base/Components/Page/Layout.php b/modules/Base/Components/Page/Layout.php new file mode 100644 index 00000000..d0cb95d8 --- /dev/null +++ b/modules/Base/Components/Page/Layout.php @@ -0,0 +1,54 @@ +user()->firstname . ' ' . auth()->user()->lastname; + } + + public function userAvatar(): string + { + return auth()->user()->getGravatarUrl(); + } + + public function render() + { + return <<<'HTML' +
+ + + + + + + + {{ $toolbar ?? ''}} + + + {{ $right ?? '' }} +
+
+ +
+
{{ $userName() }}
+
+
+
+ +
+ {{ $slot }} +
+
+ HTML; + } +} diff --git a/app/View/Page/MenuEntry.php b/modules/Base/Components/Page/MenuEntry.php similarity index 94% rename from app/View/Page/MenuEntry.php rename to modules/Base/Components/Page/MenuEntry.php index 260fc395..85ea81df 100644 --- a/app/View/Page/MenuEntry.php +++ b/modules/Base/Components/Page/MenuEntry.php @@ -1,6 +1,6 @@ login()->loginNami(); + + Livewire::test(DummyComponent::class) + ->assertSee('Testcontent') + ->assertSee(auth()->user()->lastname); +}); + +class DummyComponent extends Component +{ + + public function render(): string + { + return <<<'HTML' +
+ + Testcontent + +
+ HTML; + } +} diff --git a/modules/Dashboard/Block.php b/modules/Dashboard/Block.php new file mode 100644 index 00000000..3376e3f2 --- /dev/null +++ b/modules/Dashboard/Block.php @@ -0,0 +1,12 @@ +put('menu', 'dashboard'); + session()->put('title', 'Dashboard'); + + $this->blocks = $factory->load(); + } + + public function render(): string + { + return <<<'HTML' + +
+ @foreach($this->blocks as $block) + + + + @endforeach +
+
+ HTML; + } +} diff --git a/modules/Dashboard/DashboardFactory.php b/modules/Dashboard/DashboardFactory.php new file mode 100644 index 00000000..03fa0789 --- /dev/null +++ b/modules/Dashboard/DashboardFactory.php @@ -0,0 +1,41 @@ +> + */ + private array $blocks = []; + + /** + * @return array + */ + public function load(): array + { + return collect($this->blocks)->map(fn ($block) => app($block))->toArray(); + } + + /** + * @param class-string $block + */ + public function register(string $block): self + { + $this->blocks[] = $block; + + $componentName = str($block)->replace('\\', '.')->explode('.')->map(fn ($part) => str($part)->lcfirst()->kebab())->implode('.'); + Livewire::component($componentName, $block); + + return $this; + } + + public function purge(): self + { + $this->blocks = []; + + return $this; + } +} diff --git a/modules/Dashboard/DashboardServiceProvider.php b/modules/Dashboard/DashboardServiceProvider.php new file mode 100644 index 00000000..25613e67 --- /dev/null +++ b/modules/Dashboard/DashboardServiceProvider.php @@ -0,0 +1,32 @@ +singleton(DashboardFactory::class, fn () => new DashboardFactory()); + } + + /** + * Bootstrap services. + * + * @return void + */ + public function boot() + { + app(Router::class)->middleware(['web', 'auth:web'])->group(function ($router) { + $router->get('/', DashboardComponent::class)->name('home'); + }); + } +} diff --git a/modules/Dashboard/tests/DashboardComponentTest.php b/modules/Dashboard/tests/DashboardComponentTest.php new file mode 100644 index 00000000..d2e38264 --- /dev/null +++ b/modules/Dashboard/tests/DashboardComponentTest.php @@ -0,0 +1,47 @@ +login()->loginNami(); + + app(DashboardFactory::class)->purge(); + app(DashboardFactory::class)->register(ExampleBlock::class); + + Livewire::test(DashboardComponent::class) + ->assertSee('ExampleTitle'); +}); + +it('renders page successfully', function () { + $this->login()->loginNami(); + + $this->get('/')->assertOk()->assertSee('Dashboard'); +}); + +class ExampleBlock extends Block +{ + + public function title(): string + { + return 'ExampleTitle'; + } + + public function render(): string + { + return <<<'HTML' +
+ Example Content +
+ HTML; + } +} diff --git a/modules/Invoice/MemberPaymentBlock.php b/modules/Invoice/MemberPaymentBlock.php new file mode 100644 index 00000000..ecd0c7db --- /dev/null +++ b/modules/Invoice/MemberPaymentBlock.php @@ -0,0 +1,37 @@ + + */ + public function data(): array + { + $amount = InvoicePosition::whereHas('invoice', fn ($query) => $query->whereNeedsPayment()) + ->selectRaw('sum(price) AS price') + ->first(); + $members = Member::whereHasPendingPayment()->count(); + + return [ + 'members' => $members, + 'total_members' => Member::count(), + 'amount' => number_format((int) $amount->price / 100, 2, ',', '.') . ' €', + ]; + } + + public function title(): string + { + return 'Ausstehende Mitgliedsbeiträge'; + } + + public function render(): string + { + return '
'; + } +} diff --git a/modules/Member/AgeGroupCountBlock.php b/modules/Member/AgeGroupCountBlock.php new file mode 100644 index 00000000..48d88d79 --- /dev/null +++ b/modules/Member/AgeGroupCountBlock.php @@ -0,0 +1,61 @@ + + */ + public function memberQuery(): Builder + { + return Membership::select('subactivities.slug', 'subactivities.name') + ->selectRaw('COUNT(member_id) AS count') + ->join('activities', 'memberships.activity_id', 'activities.id') + ->join('subactivities', 'memberships.subactivity_id', 'subactivities.id') + ->isAgeGroup() + ->isMember() + ->active() + ->groupBy('subactivities.slug', 'subactivities.name') + ->orderBy('subactivity_id'); + } + + /** + * @return Builder + */ + public function leaderQuery(): Builder + { + return Membership::selectRaw('"leiter" AS slug, "Leiter" AS name, COUNT(member_id) AS count') + ->join('activities', 'memberships.activity_id', 'activities.id') + ->join('subactivities', 'memberships.subactivity_id', 'subactivities.id') + ->active() + ->isLeader(); + } + + protected function data(): array + { + return [ + 'groups' => [ + ...$this->memberQuery()->get()->toArray(), + ...$this->leaderQuery()->get()->toArray(), + ], + ]; + } + + public function title(): string + { + return 'Gruppierungs-Verteilung'; + } + + public function render(): string + { + return '
+ + lalala +
'; + } +} diff --git a/modules/Member/TestersBlock.php b/modules/Member/TestersBlock.php new file mode 100644 index 00000000..e8fbd71e --- /dev/null +++ b/modules/Member/TestersBlock.php @@ -0,0 +1,43 @@ + + */ + public function query(): Builder + { + return Member::whereHas('memberships', fn ($q) => $q->isTrying()) + ->with('memberships', fn ($q) => $q->isTrying()); + } + + /** + * @return array{members: array} + */ + public function data(): array + { + return [ + 'members' => $this->query()->get()->map(fn ($member) => [ + 'name' => $member->fullname, + 'try_ends_at' => $member->memberships->first()->from->addWeeks(8)->format('d.m.Y'), + 'try_ends_at_human' => $member->memberships->first()->from->addWeeks(8)->diffForHumans(), + ])->toArray(), + ]; + } + + public function title(): string + { + return 'Endende Schhnupperzeiten'; + } + + public function render(): string + { + return '
'; + } +} diff --git a/modules/Prevention/EfzPendingBlock.php b/modules/Prevention/EfzPendingBlock.php new file mode 100644 index 00000000..473ae64d --- /dev/null +++ b/modules/Prevention/EfzPendingBlock.php @@ -0,0 +1,44 @@ + + */ + public function query(): Builder + { + return Member::where(function ($query) { + return $query->where('efz', '<=', now()->subYears(5)->endOfYear()) + ->orWhereNull('efz'); + }) + ->whereCurrentGroup() + ->orderByRaw('lastname, firstname') + ->whereHas('memberships', fn ($builder) => $builder->isLeader()->active()); + } + + /** + * @return array{members: array} + */ + public function data(): array + { + return [ + 'members' => $this->query()->get()->map(fn ($member) => $member->fullname)->toArray(), + ]; + } + + public function title(): string + { + return 'Ausstehende Führungszeugnisse'; + } + + public function render(): string + { + return '
'; + } +} diff --git a/modules/Prevention/PsPendingBlock.php b/modules/Prevention/PsPendingBlock.php new file mode 100644 index 00000000..e6bef7e7 --- /dev/null +++ b/modules/Prevention/PsPendingBlock.php @@ -0,0 +1,50 @@ + + */ + public function query(): Builder + { + return Member::where(function ($query) { + $time = now()->subYears(5)->endOfYear(); + + return $query + ->orWhere(fn ($query) => $query->whereNull('ps_at')->whereNull('more_ps_at')) + ->orWhere(fn ($query) => $query->whereNull('ps_at')->where('more_ps_at', '<=', $time)) + ->orWhere(fn ($query) => $query->where('ps_at', '<=', $time)->whereNull('more_ps_at')) + ->orWhere(fn ($query) => $query->where('ps_at', '>=', $time)->where('more_ps_at', '<=', $time)); + }) + ->whereCurrentGroup() + ->orderByRaw('lastname, firstname') + ->whereHas('memberships', fn ($builder) => $builder->isLeader()->active()); + } + + /** + * @return array{members: array{fullname: string}} + */ + public function data(): array + { + return [ + 'members' => $this->query()->get()->map(fn ($member) => [ + 'fullname' => $member->fullname, + ])->toArray(), + ]; + } + + public function title(): string + { + return 'Ausstehende Präventionsschulungen'; + } + + public function render(): string + { + return '
'; + } +} diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php new file mode 100644 index 00000000..846ac352 --- /dev/null +++ b/resources/views/components/layouts/app.blade.php @@ -0,0 +1,20 @@ + + + + + + + {{ $title ?? 'Page Title' }} + + + @if(auth()->id()) + + @endif + @vite('resources/livewire-js/app.js') + + + + {{ $slot }} + + +