Compare commits

..

4 Commits

Author SHA1 Message Date
philipp lang 653d59fc18 --wip-- [skip ci] 2024-09-23 02:03:15 +02:00
philipp lang 528b716705 Install Livewire Package 2024-09-23 02:02:03 +02:00
philipp lang 096224fe98 Lint
continuous-integration/drone/push Build is passing Details
2024-09-23 02:01:35 +02:00
philipp lang 646ce647da Move Dashboard Route to DashboardServiceProvider 2024-09-23 01:53:46 +02:00
23 changed files with 439 additions and 47 deletions

View File

@ -1,3 +0,0 @@
#!/bin/bash
docker buildx build -f .docker/base.Dockerfile .

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,31 @@
<?php
namespace App\Dashboard\Actions;
use App\Dashboard\DashboardFactory;
use Inertia;
use Inertia\Response;
use Lorisleiva\Actions\Concerns\AsAction;
class IndexAction
{
use AsAction;
/**
* @return array<array-key, mixed>
*/
public function handle(): array
{
return [
'blocks' => app(DashboardFactory::class)->render(),
];
}
public function asController(): Response
{
session()->put('menu', 'dashboard');
session()->put('title', 'Dashboard');
return Inertia::render('dashboard/VIndex', $this->handle());
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Dashboard\Blocks;
abstract class Block
{
/**
* @return array<array-key, mixed>
*/
abstract protected function data(): array;
abstract protected function title(): string;
abstract protected function component(): string;
/**
* @return array{data: array<array-key, mixed>, title: string, component: string}
*/
public function render(): array
{
return [
'data' => $this->data(),
'title' => $this->title(),
'component' => $this->component(),
];
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Dashboard;
use App\Dashboard\Blocks\Block;
use App\Efz\EfzPendingBlock;
use App\Invoice\MemberPaymentBlock;
use App\Member\PsPendingBlock;
use App\Membership\AgeGroupCountBlock;
use App\Membership\TestersBlock;
class DashboardFactory
{
/**
* @var array<int, class-string<Block>>
*/
private array $blocks = [
AgeGroupCountBlock::class,
MemberPaymentBlock::class,
TestersBlock::class,
EfzPendingBlock::class,
PsPendingBlock::class,
];
/**
* @return array<array-key, mixed>
*/
public function render(): array
{
return collect($this->blocks)->map(fn ($block): array => app($block)->render())->toArray();
}
/**
* @param class-string<Block> $block
*/
public function register(string $block): self
{
$this->blocks[] = $block;
return $this;
}
public function purge(): self
{
$this->blocks = [];
return $this;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Dashboard;
use Illuminate\Routing\Router;
use Illuminate\Support\ServiceProvider;
use App\Dashboard\Actions\IndexAction as DashboardIndexAction;
class DashboardServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
app()->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('/', DashboardIndexAction::class)->name('home');
});
}
}

View File

@ -2,7 +2,7 @@
namespace App\Efz;
use Modules\Dashboard\Block;
use App\Dashboard\Blocks\Block;
use App\Member\Member;
use Illuminate\Database\Eloquent\Builder;
@ -41,9 +41,4 @@ class EfzPendingBlock extends Block
{
return 'Ausstehende Führungszeugnisse';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -2,7 +2,7 @@
namespace App\Invoice;
use Modules\Dashboard\Block;
use App\Dashboard\Blocks\Block;
use App\Invoice\Models\InvoicePosition;
use App\Member\Member;
@ -34,9 +34,4 @@ class MemberPaymentBlock extends Block
{
return 'Ausstehende Mitgliedsbeiträge';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -2,7 +2,7 @@
namespace App\Member;
use Modules\Dashboard\Block;
use App\Dashboard\Blocks\Block;
use Illuminate\Database\Eloquent\Builder;
class PsPendingBlock extends Block
@ -47,9 +47,4 @@ class PsPendingBlock extends Block
{
return 'Ausstehende Präventionsschulungen';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -2,7 +2,7 @@
namespace App\Membership;
use Modules\Dashboard\Block;
use App\Dashboard\Blocks\Block;
use App\Member\Membership;
use Illuminate\Database\Eloquent\Builder;
@ -55,9 +55,4 @@ class AgeGroupCountBlock extends Block
{
return 'Gruppierungs-Verteilung';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -2,7 +2,7 @@
namespace App\Membership;
use Modules\Dashboard\Block;
use App\Dashboard\Blocks\Block;
use App\Member\Member;
use Illuminate\Database\Eloquent\Builder;
@ -40,9 +40,4 @@ class TestersBlock extends Block
{
return 'Endende Schhnupperzeiten';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -37,7 +37,6 @@ class AppServiceProvider extends ServiceProvider
app()->extend('media-library-helpers', fn ($p) => $p->put('form', Form::class));
Blade::componentNamespace('App\\View\\Ui', 'ui');
Blade::componentNamespace('App\\View\\Mail', 'mail-view');
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
class LivewireServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
Blade::componentNamespace('App\\View\\Ui', 'ui');
}
/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}

View File

@ -168,7 +168,6 @@ return [
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\HorizonServiceProvider::class,
App\Providers\RouteServiceProvider::class,
@ -176,8 +175,10 @@ return [
App\Tex\TexServiceProvider::class,
App\Dav\ServiceProvider::class,
App\Setting\SettingServiceProvider::class,
Modules\Dashboard\DashboardServiceProvider::class,
App\Dashboard\DashboardServiceProvider::class,
App\Providers\PluginServiceProvider::class,
// Modules\Dashboard\DashboardServiceProvider::class,
// App\Providers\LivewireServiceProvider::class,
],
/*

View File

@ -2,11 +2,11 @@
namespace Modules\Dashboard;
use App\Efz\EfzPendingBlock;
use App\Invoice\MemberPaymentBlock;
use App\Member\PsPendingBlock;
use App\Membership\AgeGroupCountBlock;
use App\Membership\TestersBlock;
use Modules\Invoice\MemberPaymentBlock;
use Modules\Member\AgeGroupCountBlock;
use Modules\Member\TestersBlock;
use Modules\Prevention\EfzPendingBlock;
use Modules\Prevention\PsPendingBlock;
class DashboardFactory
{

View File

@ -2,7 +2,9 @@
namespace Modules\Dashboard;
use Illuminate\Routing\Router;
use Illuminate\Support\ServiceProvider;
use Modules\Dashboard\Components\DashboardComponent;
class DashboardServiceProvider extends ServiceProvider
{
@ -23,5 +25,8 @@ class DashboardServiceProvider extends ServiceProvider
*/
public function boot()
{
app(Router::class)->middleware(['web', 'auth:web'])->group(function ($router) {
$router->get('/', DashboardComponent::class)->name('home');
});
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Modules\Invoice;
use Modules\Dashboard\Block;
use App\Invoice\Models\InvoicePosition;
use App\Member\Member;
class MemberPaymentBlock extends Block
{
/**
* @return array<string, string|int>
*/
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 component(): string
{
return 'member-payment';
}
public function title(): string
{
return 'Ausstehende Mitgliedsbeiträge';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace Modules\Member;
use Modules\Dashboard\Block;
use App\Member\Membership;
use Illuminate\Database\Eloquent\Builder;
class AgeGroupCountBlock extends Block
{
/**
* @return Builder<Membership>
*/
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<Membership>
*/
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 component(): string
{
return 'age-group-count';
}
public function title(): string
{
return 'Gruppierungs-Verteilung';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace Modules\Member;
use Modules\Dashboard\Block;
use App\Member\Member;
use Illuminate\Database\Eloquent\Builder;
class TestersBlock extends Block
{
/**
* @return Builder<Member>
*/
public function query(): Builder
{
return Member::whereHas('memberships', fn ($q) => $q->isTrying())
->with('memberships', fn ($q) => $q->isTrying());
}
/**
* @return array{members: array<int, array{name: string, try_ends_at: string, try_ends_at_human: string}>}
*/
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 component(): string
{
return 'testers';
}
public function title(): string
{
return 'Endende Schhnupperzeiten';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Modules\Prevention;
use Modules\Dashboard\Block;
use App\Member\Member;
use Illuminate\Database\Eloquent\Builder;
class EfzPendingBlock extends Block
{
/**
* @return Builder<Member>
*/
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<int, string>}
*/
public function data(): array
{
return [
'members' => $this->query()->get()->map(fn ($member) => $member->fullname)->toArray(),
];
}
public function component(): string
{
return 'efz-pending';
}
public function title(): string
{
return 'Ausstehende Führungszeugnisse';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Modules\Prevention;
use Modules\Dashboard\Block;
use Illuminate\Database\Eloquent\Builder;
class PsPendingBlock extends Block
{
/**
* @return Builder<Member>
*/
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 component(): string
{
return 'ps-pending';
}
public function title(): string
{
return 'Ausstehende Präventionsschulungen';
}
public function render(): string
{
return '<div></div>';
}
}

View File

@ -17,7 +17,6 @@ use App\Course\Actions\CourseIndexAction;
use App\Course\Actions\CourseStoreAction;
use App\Invoice\Actions\InvoiceStoreAction;
use App\Course\Actions\CourseUpdateAction;
use Modules\Dashboard\Actions\IndexAction as DashboardIndexAction;
use App\Efz\ShowEfzDocumentAction;
use App\Fileshare\Actions\FileshareApiIndexAction;
use App\Fileshare\Actions\FileshareStoreAction;
@ -78,14 +77,12 @@ use App\Membership\Actions\MembershipDestroyAction;
use App\Membership\Actions\MembershipStoreAction;
use App\Membership\Actions\MembershipUpdateAction;
use App\Payment\SubscriptionController;
use Modules\Dashboard\Components\DashboardComponent;
Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void {
Auth::routes(['register' => false]);
});
Route::group(['middleware' => 'auth:web'], function (): void {
Route::get('/', DashboardComponent::class)->name('home');
Route::post('/nami/login-check', NamiLoginCheckAction::class)->name('nami.login-check');
Route::post('/nami/get-search-layer', NamiGetSearchLayerAction::class)->name('nami.get-search-layer');
Route::post('/nami/search', NamiSearchAction::class)->name('nami.search');

View File

@ -1,2 +0,0 @@
*
!.gitignore