Incentive (E1): "Nur fuer VIP sichtbar" und Ranglisten-Seite mit Teilnahme-Block oben
Haken vip_only am Incentive: aktives Incentive erscheint dann nur fuer VIP-Konten in Sidenav, Dashboard, Teaser, Rangliste und Details, andere Berater bekommen 404; das Tracking laeuft fuer alle weiter. Seeder setzt den Haken fuer die Abstimmung der Recruiting Challenge. Ranglisten-Seite: solange die Teilnahme nicht bestaetigt ist, steht der Teilnahme-Block mit Praemien-Chips, Checkbox, Button und aufklappbaren Bedingungen ganz oben, darunter Trennlinie, Titel, Untertitel, Bild, Beschreibung und die Info-Kacheln. Ueberschriften vergroessert, Abo-Bloecke nur wenn Abos zaehlen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
4e4b58e90e
commit
3341c929eb
18 changed files with 717 additions and 185 deletions
187
tests/Feature/Incentive/IncentiveVipOnlyTest.php
Normal file
187
tests/Feature/Incentive/IncentiveVipOnlyTest.php
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Incentive;
|
||||
use App\Models\IncentiveParticipant;
|
||||
use App\Models\UserAccount;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
/**
|
||||
* E1: "Nur fuer VIP sichtbar" blendet ein aktives Incentive fuer normale Berater aus
|
||||
* (Navigation, Dashboard, Teaser, Rangliste, Details), VIP-Konten sehen es zur Abstimmung.
|
||||
* Dazu der Umbau der Ranglisten-Seite: Teilnahme-Block oben, solange nicht bestaetigt.
|
||||
* Berater-Routen sind host-abhaengig (CRM-Host).
|
||||
*/
|
||||
beforeEach(function () {
|
||||
$this->previousHttpHost = $_SERVER['HTTP_HOST'] ?? null;
|
||||
$_SERVER['HTTP_HOST'] = config('domains.domains.crm.host');
|
||||
$this->refreshApplication();
|
||||
$this->artisan('migrate');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if ($this->previousHttpHost === null) {
|
||||
unset($_SERVER['HTTP_HOST']);
|
||||
} else {
|
||||
$_SERVER['HTTP_HOST'] = $this->previousHttpHost;
|
||||
}
|
||||
});
|
||||
|
||||
function vipTestUser(int $admin = 0): User
|
||||
{
|
||||
$account = UserAccount::create(['first_name' => 'Vera', 'last_name' => 'Berater']);
|
||||
|
||||
return User::forceCreate([
|
||||
'email' => 'vip-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
'admin' => $admin,
|
||||
'account_id' => $account->id,
|
||||
'payment_account' => now()->addYear(),
|
||||
'wizard' => 100,
|
||||
]);
|
||||
}
|
||||
|
||||
function vipOnlyChallenge(array $overrides = []): Incentive
|
||||
{
|
||||
return Incentive::factory()->create(array_merge([
|
||||
'name' => 'Recruiting Challenge',
|
||||
'slug' => 'recruiting-challenge-vip-'.uniqid(),
|
||||
'qualification_start' => '2026-10-01',
|
||||
'qualification_end' => '2027-01-31',
|
||||
'calculation_end' => '2027-01-31',
|
||||
'points_partner_onetime' => 1,
|
||||
'points_abo_onetime' => 0,
|
||||
'min_direct_partners' => 0,
|
||||
'min_customer_abos' => 0,
|
||||
'max_winners' => 3,
|
||||
'amount_per_partner' => 50,
|
||||
'bonus_rank_1' => 500,
|
||||
'include_sales_points' => false,
|
||||
'vip_only' => true,
|
||||
], $overrides));
|
||||
}
|
||||
|
||||
it('zeigt ein VIP-Incentive in der Navigation nur VIP-Konten', function () {
|
||||
$public = Incentive::factory()->create(['qualification_start' => '2026-04-01']);
|
||||
$vipOnly = vipOnlyChallenge();
|
||||
|
||||
expect(Incentive::activeForNavigation(vipTestUser())->pluck('id')->all())->toBe([$public->id])
|
||||
->and(Incentive::activeForNavigation(vipTestUser(admin: 1))->pluck('id')->all())->toBe([$public->id, $vipOnly->id])
|
||||
->and(Incentive::activeForNavigation(null)->pluck('id')->all())->toBe([$public->id])
|
||||
->and($vipOnly->isVisibleFor(vipTestUser()))->toBeFalse()
|
||||
->and($vipOnly->isVisibleFor(vipTestUser(admin: 1)))->toBeTrue()
|
||||
->and($public->isVisibleFor(vipTestUser()))->toBeTrue();
|
||||
});
|
||||
|
||||
it('liefert normalen Beratern 404 fuer Teaser, Rangliste, Details und Teilnahme', function () {
|
||||
$incentive = vipOnlyChallenge();
|
||||
|
||||
$this->actingAs(vipTestUser());
|
||||
$this->get(route('user_incentive_teaser', [$incentive->slug]))->assertNotFound();
|
||||
$this->get(route('user_incentive_show', [$incentive->slug]))->assertNotFound();
|
||||
$this->get(route('user_incentive_details', [$incentive->slug]))->assertNotFound();
|
||||
$this->post(route('user_incentive_participate', [$incentive->slug]), ['accept_terms' => 1])->assertNotFound();
|
||||
});
|
||||
|
||||
it('zeigt VIP-Konten Teaser und Rangliste eines VIP-Incentives', function () {
|
||||
$incentive = vipOnlyChallenge();
|
||||
|
||||
$this->actingAs(vipTestUser(admin: 1));
|
||||
$teaser = $this->get(route('user_incentive_teaser', [$incentive->slug]));
|
||||
$teaser->assertOk();
|
||||
$this->get(route('user_incentive_show', [$incentive->slug]))->assertOk();
|
||||
});
|
||||
|
||||
it('zeigt allen Beratern ein Incentive ohne VIP-Sperre', function () {
|
||||
$incentive = vipOnlyChallenge(['vip_only' => false]);
|
||||
|
||||
$this->actingAs(vipTestUser());
|
||||
$this->get(route('user_incentive_show', [$incentive->slug]))->assertOk();
|
||||
});
|
||||
|
||||
it('speichert den VIP-Haken im Admin und zeigt ihn auf der Detailseite', function () {
|
||||
$this->actingAs(vipTestUser(admin: 2));
|
||||
|
||||
$this->post(route('admin_incentive_store'), [
|
||||
'name' => 'VIP Test Challenge',
|
||||
'qualification_start' => '01.10.2026',
|
||||
'qualification_end' => '31.01.2027',
|
||||
'calculation_end' => '31.01.2027',
|
||||
'points_partner_onetime' => 1,
|
||||
'points_abo_onetime' => 0,
|
||||
'min_direct_partners' => 0,
|
||||
'min_customer_abos' => 0,
|
||||
'max_winners' => 3,
|
||||
'include_sales_points' => '0',
|
||||
'vip_only' => '1',
|
||||
'status' => 1,
|
||||
])->assertRedirect(route('admin_incentives'));
|
||||
|
||||
$incentive = Incentive::where('name', 'VIP Test Challenge')->firstOrFail();
|
||||
expect($incentive->vip_only)->toBeTrue();
|
||||
|
||||
$this->get(route('admin_incentive_show', [$incentive->id]))
|
||||
->assertOk()
|
||||
->assertSee('id="incentive-vip-only-badge"', false);
|
||||
});
|
||||
|
||||
describe('Ranglisten-Seite: Teilnahme-Block oben', function () {
|
||||
it('zeigt ohne Bestaetigung den Teilnahme-Block mit Praemie zuerst, dann Trennlinie und Inhalt', function () {
|
||||
$incentive = vipOnlyChallenge(['vip_only' => false, 'terms' => '<p>Bedingungen Text</p>']);
|
||||
$this->actingAs(vipTestUser());
|
||||
|
||||
$response = $this->get(route('user_incentive_show', [$incentive->slug]))->assertOk();
|
||||
$html = $response->getContent();
|
||||
|
||||
$join = strpos($html, 'id="incentive-join"');
|
||||
$divider = strpos($html, 'class="inc-divider"');
|
||||
$title = strpos($html, 'class="inc-page-title"');
|
||||
$ranking = strpos($html, 'class="inc-ranking-card mt-2');
|
||||
|
||||
expect($join)->not->toBeFalse()
|
||||
->and($divider)->toBeGreaterThan($join)
|
||||
->and($title)->toBeGreaterThan($divider)
|
||||
->and($ranking)->toBeGreaterThan($title);
|
||||
|
||||
$response->assertSee(__('incentive.join_title'))
|
||||
->assertSee('50,00 €')
|
||||
->assertSee(__('incentive.join_highlight_rank', ['amount' => '500,00 €', 'n' => 1]))
|
||||
->assertSee(__('incentive.join_terms_toggle'))
|
||||
->assertSee('Bedingungen Text', false)
|
||||
->assertSee('id="incentive-payout-tile"', false);
|
||||
});
|
||||
|
||||
it('nennt den Block "Teilnahme bestaetigen", wenn Punkte schon laufen', function () {
|
||||
$incentive = vipOnlyChallenge(['vip_only' => false]);
|
||||
$user = vipTestUser();
|
||||
IncentiveParticipant::factory()->unconfirmed()->create([
|
||||
'incentive_id' => $incentive->id, 'user_id' => $user->id,
|
||||
'qualified_partners' => 2, 'total_points' => 2, 'is_qualified' => true, 'rank' => 1,
|
||||
]);
|
||||
$this->actingAs($user);
|
||||
|
||||
$this->get(route('user_incentive_show', [$incentive->slug]))
|
||||
->assertOk()
|
||||
->assertSee(__('incentive.join_confirm_title'))
|
||||
->assertSee(__('incentive.payout_so_far'))
|
||||
->assertSee('600,00 €');
|
||||
});
|
||||
|
||||
it('zeigt bestaetigten Teilnehmern keinen Teilnahme-Block', function () {
|
||||
$incentive = vipOnlyChallenge(['vip_only' => false]);
|
||||
$user = vipTestUser();
|
||||
IncentiveParticipant::factory()->create([
|
||||
'incentive_id' => $incentive->id, 'user_id' => $user->id,
|
||||
'qualified_partners' => 1, 'total_points' => 1, 'is_qualified' => true, 'rank' => 1,
|
||||
]);
|
||||
$this->actingAs($user);
|
||||
|
||||
$this->get(route('user_incentive_show', [$incentive->slug]))
|
||||
->assertOk()
|
||||
->assertDontSee('id="incentive-join"', false)
|
||||
->assertSee(__('incentive.my_details'));
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue