Add membership status to member view
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
59c2c527fb
commit
0de90be8c3
|
@ -4,26 +4,26 @@ namespace App\Member;
|
|||
|
||||
use App\Country;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Setting\GeneralSettings;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Response;
|
||||
use Zoomyboy\LaravelNami\Exceptions\ConflictException;
|
||||
use Inertia;
|
||||
|
||||
class MemberController extends Controller
|
||||
{
|
||||
public function index(Request $request, GeneralSettings $settings): Response
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
session()->put('menu', 'member');
|
||||
session()->put('title', 'Mitglieder');
|
||||
$filter = FilterScope::fromRequest($request->input('filter', ''));
|
||||
|
||||
return \Inertia::render('member/VIndex', [
|
||||
return Inertia::render('member/VIndex', [
|
||||
'data' => MemberResource::collection(Member::search($filter->search)->query(
|
||||
fn ($q) => $q->select('*')
|
||||
->withFilter($filter)
|
||||
->with('payments.subscription')->with(['memberships' => fn ($query) => $query->active()])->with('courses')->with('subscription')->with('leaderMemberships')->with('ageGroupMemberships')
|
||||
->with(['payments.subscription', 'memberships', 'courses', 'subscription', 'leaderMemberships', 'ageGroupMemberships'])
|
||||
->withPendingPayment()
|
||||
->ordered()
|
||||
)->paginate(15)),
|
||||
|
|
|
@ -61,6 +61,11 @@ class Membership extends Model
|
|||
return $this->belongsTo(Member::class);
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->from->isBefore(now()) && (null === $this->to || $this->to->isAfter(now()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<Membership> $query
|
||||
*
|
||||
|
@ -68,7 +73,8 @@ class Membership extends Model
|
|||
*/
|
||||
public function scopeActive(Builder $query): Builder
|
||||
{
|
||||
return $query->whereNull('to');
|
||||
return $query->where('from', '<=', now())
|
||||
->where(fn ($query) => $query->whereNull('to')->orWhere('to', '>=', now()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,7 +112,7 @@ class Membership extends Model
|
|||
*
|
||||
* @return Builder<Membership>
|
||||
*/
|
||||
public function scopeTrying(Builder $query): Builder
|
||||
public function scopeIsTrying(Builder $query): Builder
|
||||
{
|
||||
return $query->active()->whereHas('activity', fn ($builder) => $builder->where('is_try', true));
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ class MembershipResource extends JsonResource
|
|||
'subactivity_name' => $this->subactivity?->name,
|
||||
'human_date' => $this->from->format('d.m.Y'),
|
||||
'promised_at' => $this->promised_at?->format('Y-m-d'),
|
||||
'is_active' => $this->isActive(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,8 @@ class TestersBlock extends Block
|
|||
*/
|
||||
public function query(): Builder
|
||||
{
|
||||
return Member::whereHas('memberships', fn ($q) => $q
|
||||
->where('created_at', '<=', now()->subWeeks(7))
|
||||
->trying()
|
||||
)
|
||||
->with(['memberships' => fn ($query) => $query->trying()]);
|
||||
return Member::whereHas('memberships', fn ($q) => $q->isTrying())
|
||||
->with('memberships', fn ($q) => $q->isTrying());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,8 +25,8 @@ class TestersBlock extends Block
|
|||
return [
|
||||
'members' => $this->query()->get()->map(fn ($member) => [
|
||||
'name' => $member->fullname,
|
||||
'try_ends_at' => $member->memberships->first()->created_at->addWeeks(8)->format('d.m.Y'),
|
||||
'try_ends_at_human' => $member->memberships->first()->created_at->addWeeks(8)->diffForHumans(),
|
||||
'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(),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div v-tooltip="longLabel" class="flex space-x-2 items-center">
|
||||
<div class="border-2 rounded-full w-5 h-5 flex items-center justify-center" :class="value ? 'border-green-700' : 'border-red-700'">
|
||||
<ui-sprite :src="value ? 'check' : 'close'" :class="value ? 'text-green-800' : 'text-red-800'" class="w-3 h-3 flex-none"></ui-sprite>
|
||||
<div class="border-2 rounded-full w-5 h-5 flex items-center justify-center"
|
||||
:class="value ? (dark ? 'border-green-500' : 'border-green-700') : dark ? 'border-red-500' : 'border-red-700'">
|
||||
<ui-sprite :src="value ? 'check' : 'close'"
|
||||
:class="value ? (dark ? 'text-green-600' : 'text-green-800') : dark ? 'text-red-600' : 'text-red-800'"
|
||||
class="w-3 h-3 flex-none"></ui-sprite>
|
||||
</div>
|
||||
<div class="text-gray-400 text-xs" v-text="label"></div>
|
||||
</div>
|
||||
|
@ -23,6 +26,10 @@ export default {
|
|||
return null;
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
<template>
|
||||
<div class="sidebar flex flex-col group is-bright">
|
||||
<page-header @close="$emit('close')" title="Mitgliedschaften">
|
||||
<page-header title="Mitgliedschaften" @close="$emit('close')">
|
||||
<template #toolbar>
|
||||
<page-toolbar-button @click.prevent="create" color="primary" icon="plus" v-if="single === null">Neue Mitgliedschaft</page-toolbar-button>
|
||||
<page-toolbar-button @click.prevent="cancel" color="primary" icon="undo" v-if="single !== null">Zurück</page-toolbar-button>
|
||||
<page-toolbar-button v-if="single === null" color="primary" icon="plus" @click.prevent="create">Neue
|
||||
Mitgliedschaft</page-toolbar-button>
|
||||
<page-toolbar-button v-if="single !== null" color="primary" icon="undo"
|
||||
@click.prevent="cancel">Zurück</page-toolbar-button>
|
||||
</template>
|
||||
</page-header>
|
||||
|
||||
<form v-if="single" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
||||
<f-select id="group_id" name="group_id" :options="groups" v-model="single.group_id" label="Gruppierung" required></f-select>
|
||||
<f-select id="activity_id" name="activity_id" :options="activities" v-model="single.activity_id" label="Tätigkeit" required></f-select>
|
||||
<f-select
|
||||
v-if="single.activity_id"
|
||||
name="subactivity_id"
|
||||
:options="subactivities[single.activity_id]"
|
||||
id="subactivity_id"
|
||||
v-model="single.subactivity_id"
|
||||
label="Untertätigkeit"
|
||||
size="sm"
|
||||
></f-select>
|
||||
<f-switch id="has_promise" :modelValue="single.promised_at !== null" @update:modelValue="single.promised_at = $event ? '2000-02-02' : null" label="Hat Versprechen"></f-switch>
|
||||
<f-text v-show="single.promised_at !== null" type="date" id="promised_at" v-model="single.promised_at" label="Versprechensdatum" size="sm"></f-text>
|
||||
<f-select id="group_id" v-model="single.group_id" name="group_id" :options="groups" label="Gruppierung"
|
||||
required></f-select>
|
||||
<f-select id="activity_id" v-model="single.activity_id" name="activity_id" :options="activities"
|
||||
label="Tätigkeit" required></f-select>
|
||||
<f-select v-if="single.activity_id" id="subactivity_id" v-model="single.subactivity_id" name="subactivity_id"
|
||||
:options="subactivities[single.activity_id]" label="Untertätigkeit" size="sm"></f-select>
|
||||
<f-switch id="has_promise" :model-value="single.promised_at !== null" label="Hat Versprechen"
|
||||
@update:modelValue="single.promised_at = $event ? '2000-02-02' : null"></f-switch>
|
||||
<f-text v-show="single.promised_at !== null" id="promised_at" v-model="single.promised_at" type="date"
|
||||
label="Versprechensdatum" size="sm"></f-text>
|
||||
<button type="submit" class="btn btn-primary">Absenden</button>
|
||||
</form>
|
||||
|
||||
<div class="grow" v-else>
|
||||
<div v-else class="grow">
|
||||
<table class="custom-table custom-table-light custom-table-sm text-sm">
|
||||
<thead>
|
||||
<th>Tätigkeit</th>
|
||||
<th>Untertätigkeit</th>
|
||||
<th>Datum</th>
|
||||
<th>Aktiv</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
|
||||
|
@ -37,17 +37,14 @@
|
|||
<td v-text="membership.activity_name"></td>
|
||||
<td v-text="membership.subactivity_name"></td>
|
||||
<td v-text="membership.human_date"></td>
|
||||
<td><ui-boolean-display :value="membership.is_active" dark></ui-boolean-display></td>
|
||||
<td class="flex">
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="
|
||||
single = membership;
|
||||
mode = 'edit';
|
||||
"
|
||||
class="inline-flex btn btn-warning btn-sm"
|
||||
><ui-sprite src="pencil"></ui-sprite
|
||||
></a>
|
||||
<i-link href="#" @click.prevent="remove(membership)" class="inline-flex btn btn-danger btn-sm"><ui-sprite src="trash"></ui-sprite></i-link>
|
||||
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="
|
||||
single = membership;
|
||||
mode = 'edit';
|
||||
"><ui-sprite src="pencil"></ui-sprite></a>
|
||||
<i-link href="#" class="inline-flex btn btn-danger btn-sm"
|
||||
@click.prevent="remove(membership)"><ui-sprite src="trash"></ui-sprite></i-link>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -57,6 +54,12 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {},
|
||||
activities: {},
|
||||
subactivities: {},
|
||||
groups: {},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
mode: null,
|
||||
|
@ -78,7 +81,7 @@ export default {
|
|||
methods: {
|
||||
create() {
|
||||
this.mode = 'create';
|
||||
this.single = {...this.def};
|
||||
this.single = { ...this.def };
|
||||
},
|
||||
cancel() {
|
||||
this.mode = this.single = null;
|
||||
|
@ -88,7 +91,7 @@ export default {
|
|||
},
|
||||
|
||||
accept(payment) {
|
||||
this.$inertia.patch(`/member/${this.value.id}/payment/${payment.id}`, {...payment, status_id: 3});
|
||||
this.$inertia.patch(`/member/${this.value.id}/payment/${payment.id}`, { ...payment, status_id: 3 });
|
||||
},
|
||||
|
||||
openLink(link) {
|
||||
|
@ -114,12 +117,5 @@ export default {
|
|||
: this.$inertia.patch(`/member/${this.value.id}/membership/${this.single.id}`, this.single, options);
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {},
|
||||
activities: {},
|
||||
subactivities: {},
|
||||
groups: {},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
<th>Tätigkeit</th>
|
||||
<th>Untertätigkeit</th>
|
||||
<th>Datum</th>
|
||||
<th>Aktiv</th>
|
||||
</thead>
|
||||
<tr v-for="(membership, index) in inner" :key="index">
|
||||
<td v-text="membership.activity_name"></td>
|
||||
<td v-text="membership.subactivity_name"></td>
|
||||
<td v-text="membership.human_date"></td>
|
||||
<td><ui-boolean-display :value="membership.is_active"></ui-boolean-display></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
@ -17,6 +19,7 @@
|
|||
<ui-box class="relative" :heading="membership.activity_name" v-for="(membership, index) in inner" :key="index" second>
|
||||
<div class="text-xs text-gray-200" v-text="membership.subactivity_name"></div>
|
||||
<div class="text-xs text-gray-200" v-text="membership.human_date"></div>
|
||||
<div class="text-xs text-gray-200"><ui-boolean-display :value="membership.is_active"></ui-boolean-display></div>
|
||||
</ui-box>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,8 @@ use App\Member\Member;
|
|||
use App\Member\Membership;
|
||||
use App\Payment\Payment;
|
||||
use App\Subactivity;
|
||||
use Carbon\Carbon;
|
||||
use Generator;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\RequestFactories\Child;
|
||||
use Tests\TestCase;
|
||||
|
@ -80,6 +82,29 @@ class IndexTest extends TestCase
|
|||
$this->assertInertiaHas(false, $response, 'data.data.2.is_leader');
|
||||
}
|
||||
|
||||
public function membershipDataProvider(): Generator
|
||||
{
|
||||
yield [now()->subMonth(), null, true];
|
||||
yield [now()->subMonth(), now()->subDay(), false];
|
||||
yield [now()->addDay(), null, false];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider membershipDataProvider
|
||||
*/
|
||||
public function testItShowsIfMembershipIsActive(Carbon $from, ?Carbon $to, bool $isActive): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$member = Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->in('€ LeiterIn', 455, 'Pfadfinder', 15)->state(['from' => $from, 'to' => $to]))
|
||||
->create();
|
||||
|
||||
$response = $this->get('/member');
|
||||
|
||||
$this->assertInertiaHas($isActive, $response, 'data.data.0.memberships.0.is_active');
|
||||
}
|
||||
|
||||
public function testItHasNoEfzLinkWhenAddressIsMissing(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
|
@ -187,11 +212,11 @@ class IndexTest extends TestCase
|
|||
'subscription_id' => $member->payments->first()->subscription->id,
|
||||
'status_name' => 'Nicht bezahlt',
|
||||
'nr' => '2019',
|
||||
], $response, 'data.data.0.payments.0');
|
||||
], $response, 'data.data.0.payments.0');
|
||||
$this->assertInertiaHas([
|
||||
'id' => $member->subscription->id,
|
||||
'name' => $member->subscription->name,
|
||||
], $response, 'data.data.0.subscription');
|
||||
], $response, 'data.data.0.subscription');
|
||||
}
|
||||
|
||||
public function testItCanFilterForBillKinds(): void
|
||||
|
|
|
@ -14,6 +14,7 @@ use App\Payment\Payment;
|
|||
use App\Payment\Subscription;
|
||||
use App\Region;
|
||||
use Carbon\Carbon;
|
||||
use Generator;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\RequestFactories\Child;
|
||||
use Tests\TestCase;
|
||||
|
@ -119,7 +120,7 @@ class ShowTest extends TestCase
|
|||
'id' => $member->memberships->first()->id,
|
||||
'human_date' => '19.11.2022',
|
||||
'promised_at' => now()->format('Y-m-d'),
|
||||
], $response, 'data.memberships.0');
|
||||
], $response, 'data.memberships.0');
|
||||
$this->assertInertiaHas([
|
||||
'organizer' => 'DPSG',
|
||||
'event_name' => 'Wochenende',
|
||||
|
@ -128,7 +129,7 @@ class ShowTest extends TestCase
|
|||
'name' => ' Baustein 2e - Gewalt gegen Kinder und Jugendliche: Vertiefung, Prävention ',
|
||||
'short_name' => '2e',
|
||||
],
|
||||
], $response, 'data.courses.0');
|
||||
], $response, 'data.courses.0');
|
||||
$this->assertInertiaHas([
|
||||
'subscription' => [
|
||||
'name' => 'Free',
|
||||
|
@ -138,7 +139,7 @@ class ShowTest extends TestCase
|
|||
],
|
||||
'status_name' => 'Nicht bezahlt',
|
||||
'nr' => '2019',
|
||||
], $response, 'data.payments.0');
|
||||
], $response, 'data.payments.0');
|
||||
}
|
||||
|
||||
public function testItShowsMinimalSingleMember(): void
|
||||
|
@ -169,4 +170,27 @@ class ShowTest extends TestCase
|
|||
'multiply_more_pv' => false,
|
||||
], $response, 'data');
|
||||
}
|
||||
|
||||
public function membershipDataProvider(): Generator
|
||||
{
|
||||
yield [now()->subMonth(), null, true];
|
||||
yield [now()->subMonth(), now()->subDay(), false];
|
||||
yield [now()->addDay(), null, false];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider membershipDataProvider
|
||||
*/
|
||||
public function testItShowsIfMembershipIsActive(Carbon $from, ?Carbon $to, bool $isActive): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$member = Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->in('€ LeiterIn', 455, 'Pfadfinder', 15)->state(['from' => $from, 'to' => $to]))
|
||||
->create();
|
||||
|
||||
$response = $this->get("/member/{$member->id}");
|
||||
|
||||
$this->assertInertiaHas($isActive, $response, 'data.memberships.0.is_active');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@ class TestersBlockTest extends TestCase
|
|||
|
||||
Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['created_at' => now()->subMonths(10)]))
|
||||
->has(Membership::factory()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['from' => now()->subMonths(10)]))
|
||||
->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
$inactiveMember = Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->ended()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['created_at' => now()->subMonths(10)]))
|
||||
->has(Membership::factory()->ended()->in('Schnuppermitgliedschaft', 7, 'Wölfling', 8)->state(['from' => now()->subMonths(10)]))
|
||||
->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
|
||||
$data = app(TestersBlock::class)->render()['data'];
|
||||
|
|
Loading…
Reference in New Issue