Add Dashboard
This commit is contained in:
parent
ee75bdae63
commit
1e5e13f85e
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Efz;
|
||||
|
||||
use App\Dashboard\Blocks\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';
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice;
|
||||
|
||||
use App\Dashboard\Blocks\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';
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Member;
|
||||
|
||||
use App\Dashboard\Blocks\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';
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Membership;
|
||||
|
||||
use App\Dashboard\Blocks\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';
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Base;
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
|
@ -21,9 +21,8 @@ class BaseServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function register(): void
|
||||
{
|
||||
Blade::componentNamespace('Modules\\Base\\Components\\Ui', 'ui');
|
||||
Blade::componentNamespace('Modules\\Base\\Components\\Page', 'page');
|
||||
|
||||
Blade::componentNamespace('App\\View\\Ui', 'ui');
|
||||
Blade::componentNamespace('App\\View\\Page', 'page');
|
||||
|
||||
app(DashboardFactory::class)->register(AgeGroupCountBlock::class);
|
||||
app(DashboardFactory::class)->register(MemberPaymentBlock::class);
|
||||
|
@ -31,8 +30,6 @@ class BaseServiceProvider 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;
|
||||
|
@ -45,7 +42,5 @@ class BaseServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Livewire::component('pagesidebar', Sidebar::class);
|
||||
//
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Base\Components\Page;
|
||||
namespace App\View\Page;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Page;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Layout extends Component
|
||||
{
|
||||
|
||||
public function __construct(public string $pageClass = '')
|
||||
{
|
||||
}
|
||||
|
||||
public function userName(): string
|
||||
{
|
||||
return auth()->user()->firstname . ' ' . auth()->user()->lastname;
|
||||
}
|
||||
|
||||
public function userAvatar(): string
|
||||
{
|
||||
return auth()->user()->getGravatarUrl();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return <<<'HTML'
|
||||
<div class="grow flex flex-col">
|
||||
<div class="grow bg-gray-900 flex flex-col duration-300 navbar:ml-60">
|
||||
<x-page::header title="{{ session()->get('title') }}">
|
||||
<x-slot:beforeTitle>
|
||||
<a href="#" class="mr-2 lg:hidden" wire:click.prevent="dispatch('toggle-sidebar')">
|
||||
<x-ui::sprite src="menu" class="text-gray-100 w-5 h-5"></x-ui::sprite>
|
||||
</a>
|
||||
</x-slot:beforeTitle>
|
||||
<x-slot:toolbar>
|
||||
{{ $toolbar ?? ''}}
|
||||
</x-slot:toolbar>
|
||||
<x-slot:right>
|
||||
{{ $right ?? '' }}
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="rounded-full overflow-hidden border-2 border-solid border-gray-300">
|
||||
<img src="{{ $userAvatar() }}" class="w-8 h-8 object-cover" />
|
||||
</div>
|
||||
<div class="text-gray-300"">{{ $userName() }}</div>
|
||||
</div>
|
||||
</x-slot:right>
|
||||
</x-page::header>
|
||||
|
||||
<div class="grow flex flex-col {{$pageClass}}">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
<livewire:page.sidebar :mobile="true" />
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Base\Components\Page;
|
||||
namespace App\View\Page;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
|
@ -1,19 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Base\Components\Page;
|
||||
namespace App\View\Page;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Sidebar extends Component
|
||||
{
|
||||
|
||||
public $isShifted = false;
|
||||
public $mobile = false;
|
||||
|
||||
public function render()
|
||||
{
|
||||
return <<<'HTML'
|
||||
<div
|
||||
class="fixed z-40 bg-gray-800 p-6 w-56 top-0 h-screen border-r border-gray-600 border-solid flex flex-col justify-between transition-all {{ $isShifted ? '-left-[14rem]' : 'left-0' }}"
|
||||
class="fixed z-40 bg-gray-800 p-6 w-60 top-0 h-screen border-r border-gray-600 border-solid flex flex-col justify-between duration-300
|
||||
@if (!$mobile) left-[-16rem] navbar:left-0 @endif"
|
||||
@if($mobile)
|
||||
x-data="{ visible: false }"
|
||||
x-on:toggle-sidebar.window="visible = true"
|
||||
:class="{'left-[-16rem]' : !visible, 'left-0': visible}"
|
||||
@endif
|
||||
>
|
||||
<div class="grid gap-2">
|
||||
<x-page::menu-entry href="/" menu="dashboard" icon="loss">Dashboard</x-page::menu-entry>
|
||||
|
@ -27,16 +33,18 @@ class Sidebar extends Component
|
|||
<x-page::menu-entry href="/maildispatcher" menu="maildispatcher" icon="at">Mail-Verteiler</x-page::menu-entry>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<a href="#" class="flex w-full px-3 py-2 rounded-xl text-gray-300 bg-gray-700" @click.prevent="searchVisible = true">
|
||||
<a href="#" class="flex w-full px-3 py-2 rounded-xl text-gray-300 bg-gray-700" @click.prevent="dispatch('show-search')">
|
||||
<x-ui::sprite class="text-white w-6 h-6 mr-4" src="search"></x-ui::sprite>
|
||||
<div class="">Suchen</div>
|
||||
</a>
|
||||
<x-page::menu-entry href="/setting" menu="setting" icon="setting">Einstellungen</x-page::menu-entry>
|
||||
<x-page::menu-entry href="/logout" menu="" icon="logout">Abmelden</x-page::menu-entry>
|
||||
</div>
|
||||
<a v-if="menuStore.hideable" href="#" class="absolute right-0 top-0 mr-2 mt-2" @click.prevent="menuStore.hide()">
|
||||
<ui-sprite src="close" class="w-5 h-5 text-gray-300"></ui-sprite>
|
||||
@if($mobile)
|
||||
<a href="#" class="absolute right-0 top-0 mr-2 mt-2" @click.prevent="visible = false">
|
||||
<x-ui::sprite src="close" class="w-5 h-5 text-gray-300"></x-ui::sprite>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
HTML;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Base\Components\Ui;
|
||||
namespace App\View\Ui;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Base\Components\Ui;
|
||||
namespace App\View\Ui;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
|
@ -178,7 +178,7 @@ return [
|
|||
// App\Dashboard\DashboardServiceProvider::class,
|
||||
App\Providers\PluginServiceProvider::class,
|
||||
Modules\Dashboard\DashboardServiceProvider::class,
|
||||
Modules\Base\BaseServiceProvider::class,
|
||||
App\Providers\BaseServiceProvider::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Base\Components\Page;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Layout extends Component
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function userName(): string
|
||||
{
|
||||
return auth()->user()->firstname . ' ' . auth()->user()->lastname;
|
||||
}
|
||||
|
||||
public function userAvatar(): string
|
||||
{
|
||||
return auth()->user()->getGravatarUrl();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return <<<'HTML'
|
||||
<div class="grow bg-gray-900 flex flex-col transition-all ml-56" :class="{'ml-56': menuStore.visible, 'ml-0': !menuStore.visible}">
|
||||
<x-page::header title="{{ session()->get('title') }}">
|
||||
<x-slot:beforeTitle>
|
||||
<a href="#" class="mr-2 lg:hidden" @click.prevent="menuStore.toggle()">
|
||||
<ui-sprite src="menu" class="text-gray-100 w-5 h-5"></ui-sprite>
|
||||
</a>
|
||||
</x-slot:beforeTitle>
|
||||
<x-slot:toolbar>
|
||||
{{ $toolbar ?? ''}}
|
||||
</x-slot:toolbar>
|
||||
<x-slot:right>
|
||||
{{ $right ?? '' }}
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="rounded-full overflow-hidden border-2 border-solid border-gray-300">
|
||||
<img src="{{ $userAvatar() }}" class="w-8 h-8 object-cover" />
|
||||
</div>
|
||||
<div class="text-gray-300"">{{ $userName() }}</div>
|
||||
</div>
|
||||
</x-slot:right>
|
||||
</x-page::header>
|
||||
|
||||
<div :class="pageClass" class="grow flex flex-col">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
|
@ -8,21 +8,20 @@ use App\Member\Member;
|
|||
|
||||
class MemberPaymentBlock extends Block
|
||||
{
|
||||
/**
|
||||
* @return array<string, string|int>
|
||||
*/
|
||||
public function data(): array
|
||||
|
||||
public string $amount = '';
|
||||
public int $members = 0;
|
||||
public int $totalMembers = 0;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$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, ',', '.') . ' €',
|
||||
];
|
||||
$this->amount = number_format((int) $amount->price / 100, 2, ',', '.') . ' €';
|
||||
$this->members = Member::whereHasPendingPayment()->count();
|
||||
$this->totalMembers = Member::count();
|
||||
}
|
||||
|
||||
public function title(): string
|
||||
|
@ -32,6 +31,13 @@ class MemberPaymentBlock extends Block
|
|||
|
||||
public function render(): string
|
||||
{
|
||||
return '<div></div>';
|
||||
return <<<'HTML'
|
||||
<div>
|
||||
<div class="text-gray-100">
|
||||
<span class="text-xl mr-1 font-semibold">{{$amount}}</span>
|
||||
<span class="text-sm">von {{$members}} / {{$totalMembers}} Mitgliedern</span>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
|
||||
class AgeGroupCountBlock extends Block
|
||||
{
|
||||
|
||||
public $groups;
|
||||
|
||||
/**
|
||||
* @return Builder<Membership>
|
||||
*/
|
||||
public function memberQuery(): Builder
|
||||
protected function memberQuery(): Builder
|
||||
{
|
||||
return Membership::select('subactivities.slug', 'subactivities.name')
|
||||
->selectRaw('COUNT(member_id) AS count')
|
||||
|
@ -27,7 +30,7 @@ class AgeGroupCountBlock extends Block
|
|||
/**
|
||||
* @return Builder<Membership>
|
||||
*/
|
||||
public function leaderQuery(): Builder
|
||||
protected function leaderQuery(): Builder
|
||||
{
|
||||
return Membership::selectRaw('"leiter" AS slug, "Leiter" AS name, COUNT(member_id) AS count')
|
||||
->join('activities', 'memberships.activity_id', 'activities.id')
|
||||
|
@ -36,13 +39,11 @@ class AgeGroupCountBlock extends Block
|
|||
->isLeader();
|
||||
}
|
||||
|
||||
protected function data(): array
|
||||
public function mount(): void
|
||||
{
|
||||
return [
|
||||
'groups' => [
|
||||
...$this->memberQuery()->get()->toArray(),
|
||||
...$this->leaderQuery()->get()->toArray(),
|
||||
],
|
||||
$this->groups = [
|
||||
...$this->memberQuery()->get(),
|
||||
...$this->leaderQuery()->get(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -51,11 +52,30 @@ class AgeGroupCountBlock extends Block
|
|||
return 'Gruppierungs-Verteilung';
|
||||
}
|
||||
|
||||
public function groupColor(string $slug): string
|
||||
{
|
||||
return data_get([
|
||||
'biber' => 'text-biber',
|
||||
'woelfling' => 'text-woelfling',
|
||||
'jungpfadfinder' => 'text-jungpfadfinder',
|
||||
'pfadfinder' => 'text-pfadfinder',
|
||||
'rover' => 'text-rover',
|
||||
'leiter' => 'text-leiter',
|
||||
], $slug);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
return '<div>
|
||||
|
||||
lalala
|
||||
</div>';
|
||||
return <<<'HTML'
|
||||
<div>
|
||||
@foreach($groups as $group)
|
||||
<div class="flex mt-2 items-center leading-none text-gray-100">
|
||||
<x-ui::sprite class="w-4 h-4 mr-2 {{ $this->groupColor($group->slug) }}" src="lilie"></x-ui::sprite>
|
||||
<span class="grow">{{$group->name}}</span>
|
||||
<span>{{$group->count}}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,30 +5,23 @@ namespace Modules\Member;
|
|||
use Modules\Dashboard\Block;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
private $months = 8;
|
||||
|
||||
/**
|
||||
* @return array{members: array<int, array{name: string, try_ends_at: string, try_ends_at_human: string}>}
|
||||
* @var Collection<Member>
|
||||
*/
|
||||
public function data(): array
|
||||
public Collection $members;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
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(),
|
||||
];
|
||||
$this->members = Member::whereHas('memberships', fn ($q) => $q->isTrying())
|
||||
->with('memberships', fn ($q) => $q->isTrying())
|
||||
->get();
|
||||
}
|
||||
|
||||
public function title(): string
|
||||
|
@ -38,6 +31,16 @@ class TestersBlock extends Block
|
|||
|
||||
public function render(): string
|
||||
{
|
||||
return '<div></div>';
|
||||
return <<<'HTML'
|
||||
<div>
|
||||
@foreach($members as $member)
|
||||
<div class="flex mt-2 items-center leading-none text-gray-100">
|
||||
<span class="grow">{{ $member->fullname }}</span>
|
||||
<span class="mr-2 text-sm tex-gray-600">{{ $member->memberships->first()->from->addWeeks($this->months)->format('d.m.Y') }}</span>
|
||||
<span class="text-xs tex-gray-600">{{ $member->memberships->first()->from->addWeeks($this->months)->diffForHumans() }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,31 +5,26 @@ namespace Modules\Prevention;
|
|||
use Modules\Dashboard\Block;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class EfzPendingBlock extends Block
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Builder<Member>
|
||||
* @var Collection<Member>
|
||||
*/
|
||||
public function query(): Builder
|
||||
public Collection $members;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
return Member::where(function ($query) {
|
||||
$this->members = 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(),
|
||||
];
|
||||
->whereHas('memberships', fn ($builder) => $builder->isLeader()->active())
|
||||
->get();
|
||||
}
|
||||
|
||||
public function title(): string
|
||||
|
@ -39,6 +34,14 @@ class EfzPendingBlock extends Block
|
|||
|
||||
public function render(): string
|
||||
{
|
||||
return '<div></div>';
|
||||
return <<<'HTML'
|
||||
<div>
|
||||
@foreach($members as $member)
|
||||
<div class="flex mt-2 items-center leading-none text-gray-100">
|
||||
<span class="grow">{{$member->fullname}}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,22 @@
|
|||
|
||||
namespace Modules\Prevention;
|
||||
|
||||
use App\Member\Member;
|
||||
use Modules\Dashboard\Block;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class PsPendingBlock extends Block
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Builder<Member>
|
||||
* @var Collection<Member>
|
||||
*/
|
||||
public function query(): Builder
|
||||
public Collection $members;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
return Member::where(function ($query) {
|
||||
$this->members = Member::where(function ($query) {
|
||||
$time = now()->subYears(5)->endOfYear();
|
||||
|
||||
return $query
|
||||
|
@ -23,19 +28,8 @@ class PsPendingBlock extends Block
|
|||
})
|
||||
->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(),
|
||||
];
|
||||
->whereHas('memberships', fn ($builder) => $builder->isLeader()->active())
|
||||
->get();
|
||||
}
|
||||
|
||||
public function title(): string
|
||||
|
@ -45,6 +39,14 @@ class PsPendingBlock extends Block
|
|||
|
||||
public function render(): string
|
||||
{
|
||||
return '<div></div>';
|
||||
return <<<'HTML'
|
||||
<div>
|
||||
@foreach ($members as $member)
|
||||
<div class="flex mt-2 items-center leading-none text-gray-100">
|
||||
<span class="grow">{{ $member->fullname }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-for="(group, index) in inner.groups" :key="index" class="flex mt-2 items-center leading-none text-gray-100">
|
||||
<ui-sprite class="w-4 h-4 mr-2" src="lilie" :class="`text-${group.slug}`"></ui-sprite>
|
||||
<span v-text="group.name" class="grow"></span>
|
||||
<span v-text="group.count"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
inner: {
|
||||
groups: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
data: {},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.inner = this.data;
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-for="(member, index) in inner.members"
|
||||
:key="index"
|
||||
class="flex mt-2 items-center leading-none text-gray-100"
|
||||
>
|
||||
<span class="grow" v-text="`${member}`"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
inner: {
|
||||
members: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
data: {},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.inner = this.data;
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,26 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="text-gray-100">
|
||||
<span class="text-xl mr-1 font-semibold" v-text="inner.amount"></span>
|
||||
<span class="text-sm" v-text="`von ${inner.members} / ${inner.total_members} Mitgliedern`"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
inner: {},
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
data: {},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.inner = this.data;
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-for="(member, index) in inner.members"
|
||||
:key="index"
|
||||
class="flex mt-2 items-center leading-none text-gray-100"
|
||||
>
|
||||
<span class="grow" v-text="`${member.fullname}`"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
inner: {
|
||||
members: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
data: {},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.inner = this.data;
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,33 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-for="(member, index) in inner.members"
|
||||
:key="index"
|
||||
class="flex mt-2 items-center leading-none text-gray-100"
|
||||
>
|
||||
<span class="grow" v-text="`${member.name}`"></span>
|
||||
<span class="mr-2 text-sm tex-gray-600" v-text="`${member.try_ends_at}`"></span>
|
||||
<span class="text-xs tex-gray-600" v-text="`${member.try_ends_at_human}`"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
inner: {
|
||||
members: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
data: {},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.inner = this.data;
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1 @@
|
|||
import '../css/app.css';
|
|
@ -1,14 +1,7 @@
|
|||
const {colors} = require('tailwindcss/defaultTheme');
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
'resources/js/views/**/*.vue',
|
||||
'resources/js/components/**/*.vue',
|
||||
'resources/js/layouts/**/*.vue',
|
||||
'resources/views/**/*.blade.php',
|
||||
'resources/js/composables/**/*.js',
|
||||
'packages/medialibrary-helper/**/*.vue',
|
||||
],
|
||||
content: ['app/View/**/*.php', 'resources/views/**/*.blade.php', 'modules/**/*.php'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
|
@ -30,6 +23,9 @@ module.exports = {
|
|||
900: 'hsl(181, 94%, 10%)',
|
||||
},
|
||||
},
|
||||
screens: {
|
||||
navbar: '1024px',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace Modules\Dashboard\Tests;
|
||||
namespace Tests\Feature\Base;
|
||||
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Livewire\Component;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(DatabaseTransactions::class);
|
||||
uses(TestCase::class);
|
||||
|
||||
it('renders successfully', function () {
|
||||
$this->login()->loginNami();
|
|
@ -8,6 +8,8 @@ use App\Invoice\Models\Invoice;
|
|||
use App\Invoice\Models\InvoicePosition;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Livewire\Livewire;
|
||||
use Modules\Invoice\MemberPaymentBlock as InvoiceMemberPaymentBlock;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MemberPaymentBlockTest extends TestCase
|
||||
|
@ -27,12 +29,8 @@ class MemberPaymentBlockTest extends TestCase
|
|||
Invoice::factory()->has(InvoicePosition::factory()->price(600)->for($member), 'positions')->status(InvoiceStatus::NEW)->create();
|
||||
Invoice::factory()->has(InvoicePosition::factory()->price(1000)->for($member), 'positions')->status(InvoiceStatus::PAID)->create();
|
||||
|
||||
$data = app(MemberPaymentBlock::class)->render()['data'];
|
||||
|
||||
$this->assertEquals([
|
||||
'amount' => '51,00 €',
|
||||
'members' => 1,
|
||||
'total_members' => 2,
|
||||
], $data);
|
||||
Livewire::test(InvoiceMemberPaymentBlock::class)
|
||||
->assertSee('1 / 2')
|
||||
->assertSee('51,00 €');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ namespace Tests\Feature\Member;
|
|||
use App\Group;
|
||||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use App\Member\PsPendingBlock;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Livewire\Livewire;
|
||||
use Modules\Prevention\PsPendingBlock;
|
||||
use Tests\TestCase;
|
||||
|
||||
class PsPendingBlockTest extends TestCase
|
||||
|
@ -47,7 +48,7 @@ class PsPendingBlockTest extends TestCase
|
|||
->defaults()
|
||||
->for($group)
|
||||
->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8)->ended())
|
||||
->create(['firstname' => 'Nora', 'lastname' => 'Doe', 'more_ps_at' => now()->subYears(5)]);
|
||||
->create(['firstname' => 'Lisa', 'lastname' => 'Doe', 'more_ps_at' => now()->subYears(5)]);
|
||||
$invalidPsButValidMorePs = Member::factory()
|
||||
->defaults()
|
||||
->for($group)
|
||||
|
@ -59,15 +60,15 @@ class PsPendingBlockTest extends TestCase
|
|||
->has(Membership::factory()->in('€ Mitglied', 5, 'Wölfling', 8))
|
||||
->create(['firstname' => 'Mae', 'lastname' => 'Doe']);
|
||||
|
||||
$data = app(PsPendingBlock::class)->render()['data'];
|
||||
|
||||
$this->assertEquals([
|
||||
'members' => [
|
||||
['fullname' => 'Jane Doe'],
|
||||
['fullname' => 'Mike Doe'],
|
||||
['fullname' => 'Nora Doe'],
|
||||
],
|
||||
], $data);
|
||||
Livewire::test(PsPendingBlock::class)
|
||||
->assertSee('Jane Doe')
|
||||
->assertDontSee('Max Doe')
|
||||
->assertDontSee('Joe Doe')
|
||||
->assertSee('Mike Doe')
|
||||
->assertSee('Nora Doe')
|
||||
->assertDontSee('Lisa Doe')
|
||||
->assertDontSee('Hey Doe')
|
||||
->assertDontSee('Mae Doe');
|
||||
}
|
||||
|
||||
public function testItExcludesForeignGroups(): void
|
||||
|
@ -80,10 +81,9 @@ class PsPendingBlockTest extends TestCase
|
|||
->defaults()
|
||||
->for($otherGroup)
|
||||
->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
|
||||
->create();
|
||||
->create(['lastname' => 'Doe']);
|
||||
|
||||
$data = app(PsPendingBlock::class)->render()['data'];
|
||||
|
||||
$this->assertCount(0, $data['members']);
|
||||
Livewire::test(PsPendingBlock::class)
|
||||
->assertDontSee('Doe');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ namespace Tests\Feature\Membership;
|
|||
|
||||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use App\Membership\AgeGroupCountBlock;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Livewire\Livewire;
|
||||
use Modules\Member\AgeGroupCountBlock;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AgeGroupCountBlockTest extends TestCase
|
||||
|
@ -33,14 +34,10 @@ class AgeGroupCountBlockTest extends TestCase
|
|||
->defaults()
|
||||
->create();
|
||||
|
||||
$data = app(AgeGroupCountBlock::class)->render()['data'];
|
||||
|
||||
$this->assertEquals([
|
||||
'groups' => [
|
||||
['slug' => 'biber', 'name' => 'Biber', 'count' => 3],
|
||||
['slug' => 'woelfling', 'name' => 'Wölfling', 'count' => 4],
|
||||
['slug' => 'leiter', 'name' => 'Leiter', 'count' => 4],
|
||||
],
|
||||
], $data);
|
||||
Livewire::test(AgeGroupCountBlock::class)
|
||||
->assertSee('Biber')
|
||||
->assertSee('Wölfling')
|
||||
->assertSee('Leiter')
|
||||
->assertSeeInOrder([3, 4, 4]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ use App\Group;
|
|||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Livewire\Livewire;
|
||||
use Modules\Prevention\EfzPendingBlock as PreventionEfzPendingBlock;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EfzPendingBlockTest extends TestCase
|
||||
|
@ -54,11 +56,10 @@ class EfzPendingBlockTest extends TestCase
|
|||
->for($group)
|
||||
->create(['firstname' => 'Doe', 'lastname' => 'Muster', 'efz' => now()->subYears(5)]);
|
||||
|
||||
$data = app(EfzPendingBlock::class)->render()['data'];
|
||||
|
||||
$this->assertEquals([
|
||||
'members' => ['Joe Muster', 'Mae Muster', 'Moa Muster'],
|
||||
], $data);
|
||||
Livewire::test(PreventionEfzPendingBlock::class)
|
||||
->assertSee('Joe Muster')
|
||||
->assertSee('Mae Muster')
|
||||
->assertSee('Moa Muster');
|
||||
}
|
||||
|
||||
public function testItExcludesForeignGroups(): void
|
||||
|
@ -73,8 +74,7 @@ class EfzPendingBlockTest extends TestCase
|
|||
->for($otherGroup)
|
||||
->create(['firstname' => 'Joe', 'lastname' => 'Muster', 'efz' => now()->subYears(5)->endOfYear()]);
|
||||
|
||||
$data = app(EfzPendingBlock::class)->render()['data'];
|
||||
|
||||
$this->assertCount(0, $data['members']);
|
||||
Livewire::test(PreventionEfzPendingBlock::class)
|
||||
->assertDontSee('Joe');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ namespace Tests\Feature\Membership;
|
|||
|
||||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use App\Membership\TestersBlock;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Livewire\Livewire;
|
||||
use Modules\Member\TestersBlock;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TestersBlockTest extends TestCase
|
||||
|
@ -20,21 +21,15 @@ class TestersBlockTest extends TestCase
|
|||
->defaults()
|
||||
->has(Membership::factory()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['from' => now()->subMonths(10)]))
|
||||
->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
$inactiveMember = Member::factory()
|
||||
Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->ended()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['from' => now()->subMonths(10)]))
|
||||
->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
->create(['firstname' => 'Jane', 'lastname' => 'Muster']);
|
||||
|
||||
$data = app(TestersBlock::class)->render()['data'];
|
||||
|
||||
$this->assertEquals([
|
||||
'members' => [
|
||||
[
|
||||
'name' => 'Max Muster',
|
||||
'try_ends_at' => now()->subMonths(10)->addWeeks(8)->format('d.m.Y'),
|
||||
'try_ends_at_human' => now()->subMonths(10)->addWeeks(8)->diffForHumans(),
|
||||
],
|
||||
],
|
||||
], $data);
|
||||
Livewire::test(TestersBlock::class)
|
||||
->assertSee('Max Muster')
|
||||
->assertSee(now()->subMonths(10)->addWeeks(8)->format('d.m.Y'))
|
||||
->assertSee(now()->subMonths(10)->addWeeks(8)->diffForHumans())
|
||||
->assertDontSee('Jane');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import path from 'path';
|
|||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
laravel(['resources/js/app.js', 'resources/livewire-js/app.js']),
|
||||
laravel(['resources/livewire-js/app.js']),
|
||||
vue({
|
||||
template: {
|
||||
transformAssetUrls: {
|
||||
|
|
Loading…
Reference in New Issue