Add service disclaimer component and Nostr validation links

-  Introduced reusable `<x-service-disclaimer>` component for better clarity and consistency on service pages.
- 🔗 Added `njump` links for Nostr profile validation in service listings and landing pages.
- 🧪 Included feature tests to verify disclaimer visibility and correct `njump` link rendering or omission for anonymous services.
This commit is contained in:
HolgerHatGarKeineNode
2026-06-25 14:12:21 +02:00
parent dc1d679e4b
commit 4c9dec42f2
4 changed files with 81 additions and 0 deletions
@@ -0,0 +1,23 @@
{{-- ponytail: ein wiederverwendbarer Disclaimer für Index- und Landingpage --}}
<div role="alert"
class="mb-8 rounded-2xl border-4 border-amber-500 bg-amber-50 text-amber-950 dark:border-amber-400 dark:bg-amber-950 dark:text-amber-50 shadow-lg">
<div class="flex flex-col gap-4 p-5 sm:flex-row sm:items-start sm:gap-5 sm:p-7">
<flux:icon.exclamation-triangle
class="size-12 shrink-0 text-amber-600 dark:text-amber-300 motion-safe:animate-pulse"/>
<div class="space-y-3">
<p class="text-2xl font-black uppercase tracking-tight sm:text-3xl">
{{ __('Keine Empfehlung von EINUNDZWANZIG') }}
</p>
<p class="text-base font-semibold leading-relaxed sm:text-lg">
{{ __('Diese Einträge stammen von Plebs selbst nicht von EINUNDZWANZIG geprüft, kuratiert oder empfohlen.') }}
</p>
<p class="text-base font-bold leading-relaxed sm:text-lg">
{{ __("Don't trust, verify: Prüfe jeden Service eigenständig auf Qualität und Sicherheit.") }}
</p>
<ul class="list-disc space-y-1 pl-5 text-sm font-medium sm:text-base">
<li>{{ __('Achte darauf, dass der Service stets die neueste Version der Software betreibt.') }}</li>
<li>{{ __('Validiere den Einsteller über sein npub.') }}</li>
</ul>
</div>
</div>
</div>
@@ -42,6 +42,8 @@ class extends Component {
}; ?> }; ?>
<div> <div>
<x-service-disclaimer/>
<div class="flex items-center justify-between flex-col md:flex-row mb-6"> <div class="flex items-center justify-between flex-col md:flex-row mb-6">
<flux:heading size="xl">{{ __('Self Hosted Services') }}</flux:heading> <flux:heading size="xl">{{ __('Self Hosted Services') }}</flux:heading>
<div class="flex flex-col md:flex-row items-center gap-4"> <div class="flex flex-col md:flex-row items-center gap-4">
@@ -193,6 +195,14 @@ class extends Component {
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<flux:avatar size="xs" src="{{ $service->createdBy->profile_photo_url }}"/> <flux:avatar size="xs" src="{{ $service->createdBy->profile_photo_url }}"/>
<span>{{ Str::length($service->createdBy->name) > 20 ? Str::substr($service->createdBy->name, 0, 4) . '...' . Str::substr($service->createdBy->name, -3) : $service->createdBy->name }}</span> <span>{{ Str::length($service->createdBy->name) > 20 ? Str::substr($service->createdBy->name, 0, 4) . '...' . Str::substr($service->createdBy->name, -3) : $service->createdBy->name }}</span>
@if($service->createdBy->nostr)
<flux:tooltip content="{{ __('Ersteller auf Nostr validieren (njump)') }}">
<flux:link :href="'https://njump.me/'.$service->createdBy->nostr" external
variant="subtle" class="inline-flex items-center">
<flux:icon.magnifying-glass variant="mini" class="text-purple-600 dark:text-purple-400"/>
</flux:link>
</flux:tooltip>
@endif
</div> </div>
@endif @endif
</flux:table.cell> </flux:table.cell>
@@ -52,6 +52,8 @@ class extends Component {
@endsection @endsection
<div class="container mx-auto px-4 py-8 max-w-5xl"> <div class="container mx-auto px-4 py-8 max-w-5xl">
<x-service-disclaimer/>
<!-- Header --> <!-- Header -->
<div class="mb-8"> <div class="mb-8">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
@@ -196,6 +198,14 @@ class extends Component {
<flux:avatar size="xs" src="{{ $service->createdBy->profile_photo_url }}"/> <flux:avatar size="xs" src="{{ $service->createdBy->profile_photo_url }}"/>
<span class="font-medium">{{ $service->createdBy->name }}</span> <span class="font-medium">{{ $service->createdBy->name }}</span>
</div> </div>
@if($service->createdBy->nostr)
<flux:link :href="'https://njump.me/'.$service->createdBy->nostr" external
variant="subtle"
class="mt-2 inline-flex items-center gap-1 text-purple-600 dark:text-purple-400">
<flux:icon.magnifying-glass variant="mini"/>
<span>{{ __('Ersteller auf Nostr validieren (njump)') }}</span>
</flux:link>
@endif
@endif @endif
</div> </div>
+38
View File
@@ -0,0 +1,38 @@
<?php
use App\Models\SelfHostedService;
use App\Models\User;
it('shows the disclaimer and njump link on the services index', function () {
SelfHostedService::factory()->for(
User::factory()->create(['nostr' => 'npub1example']),
'createdBy'
)->create();
$this->get(route('services.index', ['country' => 'de']))
->assertOk()
->assertSee('Keine Empfehlung von EINUNDZWANZIG')
->assertSee('npub')
->assertSee('https://njump.me/npub1example');
});
it('shows the disclaimer and njump link on the service landingpage', function () {
$service = SelfHostedService::factory()->for(
User::factory()->create(['nostr' => 'npub1example']),
'createdBy'
)->create();
$this->get(route('services.landingpage', ['country' => 'de', 'service' => $service->slug]))
->assertOk()
->assertSee('Keine Empfehlung von EINUNDZWANZIG')
->assertSee('https://njump.me/npub1example');
});
it('omits the njump link for anonymous services', function () {
$service = SelfHostedService::factory()->anonymous()->create();
$this->get(route('services.landingpage', ['country' => 'de', 'service' => $service->slug]))
->assertOk()
->assertSee('Keine Empfehlung von EINUNDZWANZIG')
->assertDontSee('njump.me');
});