From 0b3765baa357fdc3952c3c041dfec7225d051357 Mon Sep 17 00:00:00 2001
From: philipp lang <philipp@aweos.de>
Date: Thu, 17 Nov 2022 20:43:51 +0100
Subject: [PATCH] Add Ps Block

---
 app/Home/DashboardFactory.php               |  2 +
 app/Member/PsPendingBlock.php               | 49 +++++++++++++++++
 resources/js/views/home/PsPending.vue       | 31 +++++++++++
 resources/js/views/home/VIndex.vue          |  1 +
 tests/Feature/Member/PsPendingBlockTest.php | 61 +++++++++++++++++++++
 5 files changed, 144 insertions(+)
 create mode 100644 app/Member/PsPendingBlock.php
 create mode 100644 resources/js/views/home/PsPending.vue
 create mode 100644 tests/Feature/Member/PsPendingBlockTest.php

diff --git a/app/Home/DashboardFactory.php b/app/Home/DashboardFactory.php
index fb344630..87302320 100644
--- a/app/Home/DashboardFactory.php
+++ b/app/Home/DashboardFactory.php
@@ -4,6 +4,7 @@ namespace App\Home;
 
 use App\Efz\EfzPendingBlock;
 use App\Home\Blocks\Block;
+use App\Member\PsPendingBlock;
 use App\Membership\AgeGroupCountBlock;
 use App\Membership\TestersBlock;
 use App\Payment\MemberPaymentBlock;
@@ -18,6 +19,7 @@ class DashboardFactory
         MemberPaymentBlock::class,
         TestersBlock::class,
         EfzPendingBlock::class,
+        PsPendingBlock::class,
     ];
 
     /**
diff --git a/app/Member/PsPendingBlock.php b/app/Member/PsPendingBlock.php
new file mode 100644
index 00000000..b73ca9b8
--- /dev/null
+++ b/app/Member/PsPendingBlock.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace App\Member;
+
+use App\Home\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));
+        })
+            ->orderByRaw('lastname, firstname')
+            ->whereHas('memberships', fn ($builder) => $builder->isLeader());
+    }
+
+    /**
+     * @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';
+    }
+}
diff --git a/resources/js/views/home/PsPending.vue b/resources/js/views/home/PsPending.vue
new file mode 100644
index 00000000..c2832b48
--- /dev/null
+++ b/resources/js/views/home/PsPending.vue
@@ -0,0 +1,31 @@
+<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>
diff --git a/resources/js/views/home/VIndex.vue b/resources/js/views/home/VIndex.vue
index 1682659e..1f10b20c 100644
--- a/resources/js/views/home/VIndex.vue
+++ b/resources/js/views/home/VIndex.vue
@@ -25,6 +25,7 @@ export default {
         'VBlock': () => import('./VBlock'),
         'age-group-count': () => import('./AgeGroupCount.vue'),
         'efz-pending': () => import('./EfzPending.vue'),
+        'ps-pending': () => import('./PsPending.vue'),
         'testers': () => import('./Testers.vue'),
         'member-payment': () => import('./MemberPayment.vue'),
     },
diff --git a/tests/Feature/Member/PsPendingBlockTest.php b/tests/Feature/Member/PsPendingBlockTest.php
new file mode 100644
index 00000000..df9c126c
--- /dev/null
+++ b/tests/Feature/Member/PsPendingBlockTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Tests\Feature\Member;
+
+use App\Member\Member;
+use App\Member\Membership;
+use App\Member\PsPendingBlock;
+use Illuminate\Foundation\Testing\DatabaseTransactions;
+use Tests\TestCase;
+
+class PsPendingBlockTest extends TestCase
+{
+    use DatabaseTransactions;
+
+    public function testItRendersContent(): void
+    {
+        $noPsAtAll = Member::factory()
+            ->defaults()
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->create(['firstname' => 'Jane', 'lastname' => 'Doe']);
+        $validPs = Member::factory()
+            ->defaults()
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->create(['firstname' => 'Max', 'lastname' => 'Doe', 'ps_at' => now()->subYears(4)]);
+        $validMorePs = Member::factory()
+            ->defaults()
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->create(['firstname' => 'Joe', 'lastname' => 'Doe', 'more_ps_at' => now()->subYears(4)]);
+        $invalidPs = Member::factory()
+            ->defaults()
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->create(['firstname' => 'Mike', 'lastname' => 'Doe', 'ps_at' => now()->subYears(5)]);
+        $invalidMorePs = Member::factory()
+            ->defaults()
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->create(['firstname' => 'Nora', 'lastname' => 'Doe', 'more_ps_at' => now()->subYears(5)]);
+        $invalidPsButValidMorePs = Member::factory()
+            ->defaults()
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->has(Membership::factory()->in('€ LeiterIn', 5, 'Wölfling', 8))
+            ->create(['firstname' => 'Hey', 'lastname' => 'Doe', 'ps_at' => now()->subYears(10), 'more_ps_at' => now()->subYears(3)]);
+        $notALeader = Member::factory()
+            ->defaults()
+            ->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);
+    }
+}