Add Dashboard
continuous-integration/drone/push Build is failing Details

This commit is contained in:
philipp lang 2024-10-13 21:00:47 +02:00
parent 6d85a7c37c
commit 580238d665
32 changed files with 237 additions and 536 deletions

View File

@ -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';
}
}

View File

@ -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';
}
}

View File

@ -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';
}
}

View File

@ -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';
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Modules\Base; namespace App\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Blade;
@ -21,9 +21,8 @@ class BaseServiceProvider extends ServiceProvider
*/ */
public function register(): void public function register(): void
{ {
Blade::componentNamespace('Modules\\Base\\Components\\Ui', 'ui'); Blade::componentNamespace('App\\View\\Ui', 'ui');
Blade::componentNamespace('Modules\\Base\\Components\\Page', 'page'); Blade::componentNamespace('App\\View\\Page', 'page');
app(DashboardFactory::class)->register(AgeGroupCountBlock::class); app(DashboardFactory::class)->register(AgeGroupCountBlock::class);
app(DashboardFactory::class)->register(MemberPaymentBlock::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(EfzPendingBlock::class);
app(DashboardFactory::class)->register(PsPendingBlock::class); app(DashboardFactory::class)->register(PsPendingBlock::class);
Livewire::component('page.sidebar', Sidebar::class);
ComponentAttributeBag::macro('mergeWhen', function ($condition, $key, $attributes) { ComponentAttributeBag::macro('mergeWhen', function ($condition, $key, $attributes) {
/** @var ComponentAttributeBag */ /** @var ComponentAttributeBag */
$self = $this; $self = $this;
@ -45,7 +42,5 @@ class BaseServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
Livewire::component('pagesidebar', Sidebar::class);
//
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Modules\Base\Components\Page; namespace App\View\Page;
use Illuminate\View\Component; use Illuminate\View\Component;

57
app/View/Page/Layout.php Normal file
View File

@ -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;
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Modules\Base\Components\Page; namespace App\View\Page;
use Illuminate\View\Component; use Illuminate\View\Component;

View File

@ -1,19 +1,25 @@
<?php <?php
namespace Modules\Base\Components\Page; namespace App\View\Page;
use Livewire\Component; use Livewire\Component;
class Sidebar extends Component class Sidebar extends Component
{ {
public $isShifted = false; public $mobile = false;
public function render() public function render()
{ {
return <<<'HTML' return <<<'HTML'
<div <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"> <div class="grid gap-2">
<x-page::menu-entry href="/" menu="dashboard" icon="loss">Dashboard</x-page::menu-entry> <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> <x-page::menu-entry href="/maildispatcher" menu="maildispatcher" icon="at">Mail-Verteiler</x-page::menu-entry>
</div> </div>
<div class="grid gap-2"> <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> <x-ui::sprite class="text-white w-6 h-6 mr-4" src="search"></x-ui::sprite>
<div class="">Suchen</div> <div class="">Suchen</div>
</a> </a>
<x-page::menu-entry href="/setting" menu="setting" icon="setting">Einstellungen</x-page::menu-entry> <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> <x-page::menu-entry href="/logout" menu="" icon="logout">Abmelden</x-page::menu-entry>
</div> </div>
<a v-if="menuStore.hideable" href="#" class="absolute right-0 top-0 mr-2 mt-2" @click.prevent="menuStore.hide()"> @if($mobile)
<ui-sprite src="close" class="w-5 h-5 text-gray-300"></ui-sprite> <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> </a>
@endif
</div> </div>
HTML; HTML;
} }

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Modules\Base\Components\Ui; namespace App\View\Ui;
use Illuminate\View\Component; use Illuminate\View\Component;

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Modules\Base\Components\Ui; namespace App\View\Ui;
use Illuminate\View\Component; use Illuminate\View\Component;

View File

@ -178,7 +178,7 @@ return [
// App\Dashboard\DashboardServiceProvider::class, // App\Dashboard\DashboardServiceProvider::class,
App\Providers\PluginServiceProvider::class, App\Providers\PluginServiceProvider::class,
Modules\Dashboard\DashboardServiceProvider::class, Modules\Dashboard\DashboardServiceProvider::class,
Modules\Base\BaseServiceProvider::class, App\Providers\BaseServiceProvider::class,
], ],
/* /*

View File

@ -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;
}
}

View File

@ -8,21 +8,20 @@ use App\Member\Member;
class MemberPaymentBlock extends Block class MemberPaymentBlock extends Block
{ {
/**
* @return array<string, string|int> public string $amount = '';
*/ public int $members = 0;
public function data(): array public int $totalMembers = 0;
public function mount(): void
{ {
$amount = InvoicePosition::whereHas('invoice', fn ($query) => $query->whereNeedsPayment()) $amount = InvoicePosition::whereHas('invoice', fn ($query) => $query->whereNeedsPayment())
->selectRaw('sum(price) AS price') ->selectRaw('sum(price) AS price')
->first(); ->first();
$members = Member::whereHasPendingPayment()->count();
return [ $this->amount = number_format((int) $amount->price / 100, 2, ',', '.') . ' €';
'members' => $members, $this->members = Member::whereHasPendingPayment()->count();
'total_members' => Member::count(), $this->totalMembers = Member::count();
'amount' => number_format((int) $amount->price / 100, 2, ',', '.') . ' €',
];
} }
public function title(): string public function title(): string
@ -32,6 +31,13 @@ class MemberPaymentBlock extends Block
public function render(): string 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;
} }
} }

View File

@ -8,10 +8,13 @@ use Illuminate\Database\Eloquent\Builder;
class AgeGroupCountBlock extends Block class AgeGroupCountBlock extends Block
{ {
public $groups;
/** /**
* @return Builder<Membership> * @return Builder<Membership>
*/ */
public function memberQuery(): Builder protected function memberQuery(): Builder
{ {
return Membership::select('subactivities.slug', 'subactivities.name') return Membership::select('subactivities.slug', 'subactivities.name')
->selectRaw('COUNT(member_id) AS count') ->selectRaw('COUNT(member_id) AS count')
@ -27,7 +30,7 @@ class AgeGroupCountBlock extends Block
/** /**
* @return Builder<Membership> * @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') return Membership::selectRaw('"leiter" AS slug, "Leiter" AS name, COUNT(member_id) AS count')
->join('activities', 'memberships.activity_id', 'activities.id') ->join('activities', 'memberships.activity_id', 'activities.id')
@ -36,13 +39,11 @@ class AgeGroupCountBlock extends Block
->isLeader(); ->isLeader();
} }
protected function data(): array public function mount(): void
{ {
return [ $this->groups = [
'groups' => [ ...$this->memberQuery()->get(),
...$this->memberQuery()->get()->toArray(), ...$this->leaderQuery()->get(),
...$this->leaderQuery()->get()->toArray(),
],
]; ];
} }
@ -51,11 +52,30 @@ class AgeGroupCountBlock extends Block
return 'Gruppierungs-Verteilung'; 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 public function render(): string
{ {
return '<div> return <<<'HTML'
<div>
lalala @foreach($groups as $group)
</div>'; <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;
} }
} }

View File

@ -5,30 +5,23 @@ namespace Modules\Member;
use Modules\Dashboard\Block; use Modules\Dashboard\Block;
use App\Member\Member; use App\Member\Member;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
class TestersBlock extends Block class TestersBlock extends Block
{ {
/**
* @return Builder<Member> private $months = 8;
*/
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}>} * @var Collection<Member>
*/ */
public function data(): array public Collection $members;
public function mount(): void
{ {
return [ $this->members = Member::whereHas('memberships', fn ($q) => $q->isTrying())
'members' => $this->query()->get()->map(fn ($member) => [ ->with('memberships', fn ($q) => $q->isTrying())
'name' => $member->fullname, ->get();
'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 public function title(): string
@ -38,6 +31,16 @@ class TestersBlock extends Block
public function render(): string 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;
} }
} }

View File

@ -5,31 +5,26 @@ namespace Modules\Prevention;
use Modules\Dashboard\Block; use Modules\Dashboard\Block;
use App\Member\Member; use App\Member\Member;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
class EfzPendingBlock extends Block 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()) return $query->where('efz', '<=', now()->subYears(5)->endOfYear())
->orWhereNull('efz'); ->orWhereNull('efz');
}) })
->whereCurrentGroup() ->whereCurrentGroup()
->orderByRaw('lastname, firstname') ->orderByRaw('lastname, firstname')
->whereHas('memberships', fn ($builder) => $builder->isLeader()->active()); ->whereHas('memberships', fn ($builder) => $builder->isLeader()->active())
} ->get();
/**
* @return array{members: array<int, string>}
*/
public function data(): array
{
return [
'members' => $this->query()->get()->map(fn ($member) => $member->fullname)->toArray(),
];
} }
public function title(): string public function title(): string
@ -39,6 +34,14 @@ class EfzPendingBlock extends Block
public function render(): string 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;
} }
} }

View File

@ -2,17 +2,22 @@
namespace Modules\Prevention; namespace Modules\Prevention;
use App\Member\Member;
use Modules\Dashboard\Block; use Modules\Dashboard\Block;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
class PsPendingBlock extends Block 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(); $time = now()->subYears(5)->endOfYear();
return $query return $query
@ -23,19 +28,8 @@ class PsPendingBlock extends Block
}) })
->whereCurrentGroup() ->whereCurrentGroup()
->orderByRaw('lastname, firstname') ->orderByRaw('lastname, firstname')
->whereHas('memberships', fn ($builder) => $builder->isLeader()->active()); ->whereHas('memberships', fn ($builder) => $builder->isLeader()->active())
} ->get();
/**
* @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 public function title(): string
@ -45,6 +39,14 @@ class PsPendingBlock extends Block
public function render(): string 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;
} }
} }

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

1
resources/livewire-js/app.js vendored Normal file
View File

@ -0,0 +1 @@
import '../css/app.css';

12
tailwind.config.js vendored
View File

@ -1,14 +1,7 @@
const {colors} = require('tailwindcss/defaultTheme'); const {colors} = require('tailwindcss/defaultTheme');
module.exports = { module.exports = {
content: [ content: ['app/View/**/*.php', 'resources/views/**/*.blade.php', 'modules/**/*.php'],
'resources/js/views/**/*.vue',
'resources/js/components/**/*.vue',
'resources/js/layouts/**/*.vue',
'resources/views/**/*.blade.php',
'resources/js/composables/**/*.js',
'packages/medialibrary-helper/**/*.vue',
],
theme: { theme: {
extend: { extend: {
colors: { colors: {
@ -30,6 +23,9 @@ module.exports = {
900: 'hsl(181, 94%, 10%)', 900: 'hsl(181, 94%, 10%)',
}, },
}, },
screens: {
navbar: '1024px',
},
}, },
}, },

View File

@ -1,14 +1,12 @@
<?php <?php
namespace Modules\Dashboard\Tests; namespace Tests\Feature\Base;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Component; use Livewire\Component;
use Livewire\Livewire; use Livewire\Livewire;
use Tests\TestCase;
uses(DatabaseTransactions::class); uses(DatabaseTransactions::class);
uses(TestCase::class);
it('renders successfully', function () { it('renders successfully', function () {
$this->login()->loginNami(); $this->login()->loginNami();

View File

@ -8,6 +8,8 @@ use App\Invoice\Models\Invoice;
use App\Invoice\Models\InvoicePosition; use App\Invoice\Models\InvoicePosition;
use App\Member\Member; use App\Member\Member;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Invoice\MemberPaymentBlock as InvoiceMemberPaymentBlock;
use Tests\TestCase; use Tests\TestCase;
class MemberPaymentBlockTest extends 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(600)->for($member), 'positions')->status(InvoiceStatus::NEW)->create();
Invoice::factory()->has(InvoicePosition::factory()->price(1000)->for($member), 'positions')->status(InvoiceStatus::PAID)->create(); Invoice::factory()->has(InvoicePosition::factory()->price(1000)->for($member), 'positions')->status(InvoiceStatus::PAID)->create();
$data = app(MemberPaymentBlock::class)->render()['data']; Livewire::test(InvoiceMemberPaymentBlock::class)
->assertSee('1 / 2')
$this->assertEquals([ ->assertSee('51,00 €');
'amount' => '51,00 €',
'members' => 1,
'total_members' => 2,
], $data);
} }
} }

View File

@ -5,8 +5,9 @@ namespace Tests\Feature\Member;
use App\Group; use App\Group;
use App\Member\Member; use App\Member\Member;
use App\Member\Membership; use App\Member\Membership;
use App\Member\PsPendingBlock;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Prevention\PsPendingBlock;
use Tests\TestCase; use Tests\TestCase;
class PsPendingBlockTest extends TestCase class PsPendingBlockTest extends TestCase
@ -47,7 +48,7 @@ class PsPendingBlockTest extends TestCase
->defaults() ->defaults()
->for($group) ->for($group)
->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8)->ended()) ->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() $invalidPsButValidMorePs = Member::factory()
->defaults() ->defaults()
->for($group) ->for($group)
@ -59,15 +60,15 @@ class PsPendingBlockTest extends TestCase
->has(Membership::factory()->in('€ Mitglied', 5, 'Wölfling', 8)) ->has(Membership::factory()->in('€ Mitglied', 5, 'Wölfling', 8))
->create(['firstname' => 'Mae', 'lastname' => 'Doe']); ->create(['firstname' => 'Mae', 'lastname' => 'Doe']);
$data = app(PsPendingBlock::class)->render()['data']; Livewire::test(PsPendingBlock::class)
->assertSee('Jane Doe')
$this->assertEquals([ ->assertDontSee('Max Doe')
'members' => [ ->assertDontSee('Joe Doe')
['fullname' => 'Jane Doe'], ->assertSee('Mike Doe')
['fullname' => 'Mike Doe'], ->assertSee('Nora Doe')
['fullname' => 'Nora Doe'], ->assertDontSee('Lisa Doe')
], ->assertDontSee('Hey Doe')
], $data); ->assertDontSee('Mae Doe');
} }
public function testItExcludesForeignGroups(): void public function testItExcludesForeignGroups(): void
@ -80,10 +81,9 @@ class PsPendingBlockTest extends TestCase
->defaults() ->defaults()
->for($otherGroup) ->for($otherGroup)
->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8)) ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
->create(); ->create(['lastname' => 'Doe']);
$data = app(PsPendingBlock::class)->render()['data']; Livewire::test(PsPendingBlock::class)
->assertDontSee('Doe');
$this->assertCount(0, $data['members']);
} }
} }

View File

@ -4,8 +4,9 @@ namespace Tests\Feature\Membership;
use App\Member\Member; use App\Member\Member;
use App\Member\Membership; use App\Member\Membership;
use App\Membership\AgeGroupCountBlock;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Member\AgeGroupCountBlock;
use Tests\TestCase; use Tests\TestCase;
class AgeGroupCountBlockTest extends TestCase class AgeGroupCountBlockTest extends TestCase
@ -33,14 +34,10 @@ class AgeGroupCountBlockTest extends TestCase
->defaults() ->defaults()
->create(); ->create();
$data = app(AgeGroupCountBlock::class)->render()['data']; Livewire::test(AgeGroupCountBlock::class)
->assertSee('Biber')
$this->assertEquals([ ->assertSee('Wölfling')
'groups' => [ ->assertSee('Leiter')
['slug' => 'biber', 'name' => 'Biber', 'count' => 3], ->assertSeeInOrder([3, 4, 4]);
['slug' => 'woelfling', 'name' => 'Wölfling', 'count' => 4],
['slug' => 'leiter', 'name' => 'Leiter', 'count' => 4],
],
], $data);
} }
} }

View File

@ -7,6 +7,8 @@ use App\Group;
use App\Member\Member; use App\Member\Member;
use App\Member\Membership; use App\Member\Membership;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Prevention\EfzPendingBlock as PreventionEfzPendingBlock;
use Tests\TestCase; use Tests\TestCase;
class EfzPendingBlockTest extends TestCase class EfzPendingBlockTest extends TestCase
@ -54,11 +56,10 @@ class EfzPendingBlockTest extends TestCase
->for($group) ->for($group)
->create(['firstname' => 'Doe', 'lastname' => 'Muster', 'efz' => now()->subYears(5)]); ->create(['firstname' => 'Doe', 'lastname' => 'Muster', 'efz' => now()->subYears(5)]);
$data = app(EfzPendingBlock::class)->render()['data']; Livewire::test(PreventionEfzPendingBlock::class)
->assertSee('Joe Muster')
$this->assertEquals([ ->assertSee('Mae Muster')
'members' => ['Joe Muster', 'Mae Muster', 'Moa Muster'], ->assertSee('Moa Muster');
], $data);
} }
public function testItExcludesForeignGroups(): void public function testItExcludesForeignGroups(): void
@ -73,8 +74,7 @@ class EfzPendingBlockTest extends TestCase
->for($otherGroup) ->for($otherGroup)
->create(['firstname' => 'Joe', 'lastname' => 'Muster', 'efz' => now()->subYears(5)->endOfYear()]); ->create(['firstname' => 'Joe', 'lastname' => 'Muster', 'efz' => now()->subYears(5)->endOfYear()]);
$data = app(EfzPendingBlock::class)->render()['data']; Livewire::test(PreventionEfzPendingBlock::class)
->assertDontSee('Joe');
$this->assertCount(0, $data['members']);
} }
} }

View File

@ -4,8 +4,9 @@ namespace Tests\Feature\Membership;
use App\Member\Member; use App\Member\Member;
use App\Member\Membership; use App\Member\Membership;
use App\Membership\TestersBlock;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
use Modules\Member\TestersBlock;
use Tests\TestCase; use Tests\TestCase;
class TestersBlockTest extends TestCase class TestersBlockTest extends TestCase
@ -20,21 +21,15 @@ class TestersBlockTest extends TestCase
->defaults() ->defaults()
->has(Membership::factory()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['from' => now()->subMonths(10)])) ->has(Membership::factory()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['from' => now()->subMonths(10)]))
->create(['firstname' => 'Max', 'lastname' => 'Muster']); ->create(['firstname' => 'Max', 'lastname' => 'Muster']);
$inactiveMember = Member::factory() Member::factory()
->defaults() ->defaults()
->has(Membership::factory()->ended()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['from' => now()->subMonths(10)])) ->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']; Livewire::test(TestersBlock::class)
->assertSee('Max Muster')
$this->assertEquals([ ->assertSee(now()->subMonths(10)->addWeeks(8)->format('d.m.Y'))
'members' => [ ->assertSee(now()->subMonths(10)->addWeeks(8)->diffForHumans())
[ ->assertDontSee('Jane');
'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);
} }
} }

2
vite.config.js vendored
View File

@ -5,7 +5,7 @@ import path from 'path';
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
laravel(['resources/js/app.js', 'resources/livewire-js/app.js']), laravel(['resources/livewire-js/app.js']),
vue({ vue({
template: { template: {
transformAssetUrls: { transformAssetUrls: {