mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2026-01-07 09:00:15 +00:00
This commit adds a new feature to the election view which allows users to vote for the presidency. It also modifies the confirmation message for board member candidates.
601 lines
34 KiB
PHP
601 lines
34 KiB
PHP
<?php
|
|
|
|
use Livewire\Volt\Component;
|
|
|
|
use swentel\nostr\Filter\Filter;
|
|
use swentel\nostr\Key\Key;
|
|
use swentel\nostr\Message\EventMessage;
|
|
use swentel\nostr\Message\RequestMessage;
|
|
use swentel\nostr\Relay\Relay;
|
|
use swentel\nostr\Relay\RelaySet;
|
|
use swentel\nostr\Request\Request;
|
|
use swentel\nostr\Subscription\Subscription;
|
|
use swentel\nostr\Event\Event as NostrEvent;
|
|
use swentel\nostr\Sign\Sign;
|
|
|
|
use function Livewire\Volt\computed;
|
|
use function Livewire\Volt\mount;
|
|
use function Livewire\Volt\state;
|
|
use function Livewire\Volt\with;
|
|
use function Livewire\Volt\updated;
|
|
use function Laravel\Folio\{middleware};
|
|
use function Laravel\Folio\name;
|
|
use function Livewire\Volt\{on};
|
|
|
|
name('association.election');
|
|
|
|
state(['isAllowed' => false]);
|
|
state(['currentPubkey' => null]);
|
|
state(['currentPleb' => null]);
|
|
state(['events' => []]);
|
|
state(['election' => fn() => $election]);
|
|
state(['plebs' => []]);
|
|
state(['search' => '']);
|
|
state(['signThisEvent' => '']);
|
|
state(['isNotClosed' => true]);
|
|
|
|
mount(function () {
|
|
$this->plebs = \App\Models\EinundzwanzigPleb::query()
|
|
->with([
|
|
'profile',
|
|
])
|
|
->whereIn('association_status', [3, 4])
|
|
->orderBy('association_status', 'desc')
|
|
->get()
|
|
->toArray();
|
|
$this->loadEvents();
|
|
if ($this->election->end_time->isPast() || !config('services.voting')) {
|
|
$this->isNotClosed = false;
|
|
}
|
|
});
|
|
|
|
on([
|
|
'nostrLoggedIn' => function ($pubkey) {
|
|
$this->currentPubkey = $pubkey;
|
|
$this->currentPleb = \App\Models\EinundzwanzigPleb::query()
|
|
->where('pubkey', $pubkey)->first();
|
|
if($this->currentPleb->association_status->value < 3) {
|
|
return redirect()->route('association.profile');
|
|
}
|
|
$this->isAllowed = true;
|
|
},
|
|
]);
|
|
|
|
on([
|
|
'echo:votes,.newVote' => function () {
|
|
$this->loadEvents();
|
|
}
|
|
]);
|
|
|
|
updated([
|
|
'search' => function ($value) {
|
|
$this->plebs = \App\Models\EinundzwanzigPleb::query()
|
|
->with([
|
|
'profile',
|
|
])
|
|
->whereIn('association_status', [3, 4])
|
|
->where(fn($query)
|
|
=> $query
|
|
->where('pubkey', 'like', "%$value%")
|
|
->orWhereHas('profile', function ($query) use ($value) {
|
|
$query->where('name', 'ilike', "%$value%");
|
|
}))
|
|
->orderBy('association_status', 'desc')
|
|
->get()
|
|
->toArray();
|
|
},
|
|
]);
|
|
|
|
$loadEvents = function () {
|
|
$subscription = new Subscription();
|
|
$subscriptionId = $subscription->setId();
|
|
|
|
$filter1 = new Filter();
|
|
$filter1->setKinds([2121]); // You can add multiple kind numbers
|
|
$filters = [$filter1]; // You can add multiple filters.
|
|
|
|
$requestMessage = new RequestMessage($subscriptionId, $filters);
|
|
|
|
$relays = [
|
|
new Relay(config('services.relay')),
|
|
];
|
|
$relaySet = new RelaySet();
|
|
$relaySet->setRelays($relays);
|
|
|
|
$request = new Request($relaySet, $requestMessage);
|
|
$response = $request->send();
|
|
|
|
$this->events = collect($response[config('services.relay')])
|
|
->map(fn($event)
|
|
=> [
|
|
'id' => $event->event->id,
|
|
'kind' => $event->event->kind,
|
|
'content' => $event->event->content,
|
|
'pubkey' => $event->event->pubkey,
|
|
'tags' => $event->event->tags,
|
|
'created_at' => $event->event->created_at,
|
|
])->toArray();
|
|
};
|
|
|
|
$vote = function ($pubkey, $type) {
|
|
if ($this->election->end_time->isPast()) {
|
|
$this->isNotClosed = false;
|
|
return;
|
|
}
|
|
$note = new NostrEvent();
|
|
$note->setContent($pubkey . ',' . $type);
|
|
$note->setKind(2121);
|
|
$this->signThisEvent = $note->toJson();
|
|
};
|
|
|
|
$checkElection = function () {
|
|
if ($this->election->end_time->isPast()) {
|
|
$this->isNotClosed = false;
|
|
}
|
|
};
|
|
|
|
$signEvent = function ($event) {
|
|
$note = new NostrEvent();
|
|
$note->setId($event['id']);
|
|
$note->setSignature($event['sig']);
|
|
$note->setKind($event['kind']);
|
|
$note->setContent($event['content']);
|
|
$note->setPublicKey($event['pubkey']);
|
|
$note->setTags($event['tags']);
|
|
$note->setCreatedAt($event['created_at']);
|
|
$eventMessage = new EventMessage($note);
|
|
$relayUrl = config('services.relay');
|
|
$relay = new Relay($relayUrl);
|
|
$relay->setMessage($eventMessage);
|
|
$result = $relay->send();
|
|
|
|
Broadcast::on('votes')
|
|
->as('newVote')
|
|
->sendNow();
|
|
};
|
|
|
|
?>
|
|
|
|
<x-layouts.app title="{{ __('Wahl') }}">
|
|
@volt
|
|
<div x-cloak x-show="isAllowed" class="relative flex h-full" x-data="nostrApp(@this)" wire:poll.600000ms="checkElection">
|
|
|
|
@php
|
|
$positions = [
|
|
'presidency' => ['icon' => 'fa-crown', 'title' => 'Präsidium'],
|
|
'vice_president' => ['icon' => 'fa-user-group-crown', 'title' => 'Vizepräsidium'],
|
|
'finances' => ['icon' => 'fa-bitcoin-sign', 'title' => 'Finanzen'],
|
|
'secretary' => ['icon' => 'fa-stapler', 'title' => 'Revisionsstelle'],
|
|
'press_officer' => ['icon' => 'fa-newspaper', 'title' => 'Pressewart'],
|
|
'it_manager' => ['icon' => 'fa-server', 'title' => 'Technikwart'],
|
|
];
|
|
$loadedEvents = collect($events)
|
|
->map(function($event) {
|
|
$profile = \App\Models\Profile::query()
|
|
->where('pubkey', $event['pubkey'])
|
|
->first()
|
|
?->toArray();
|
|
$votedFor = \App\Models\Profile::query()
|
|
->where('pubkey', str($event['content'])->before(',')->toString())
|
|
->first()
|
|
?->toArray();
|
|
|
|
return [
|
|
'id' => $event['id'],
|
|
'kind' => $event['kind'],
|
|
'content' => $event['content'],
|
|
'pubkey' => $event['pubkey'],
|
|
'tags' => $event['tags'],
|
|
'created_at' => $event['created_at'],
|
|
'profile' => $profile,
|
|
'votedFor' => $votedFor,
|
|
'type' => str($event['content'])->after(',')->toString(),
|
|
];
|
|
})
|
|
->sortByDesc('created_at')
|
|
->unique(fn ($event) => $event['pubkey'] . $event['type'])
|
|
->values();
|
|
@endphp
|
|
|
|
<!-- Inbox sidebar -->
|
|
<div id="inbox-sidebar"
|
|
class="absolute z-20 top-0 bottom-0 w-full md:w-auto md:static md:top-auto md:bottom-auto -mr-px md:translate-x-0 transition-transform duration-200 ease-in-out"
|
|
:class="inboxSidebarOpen ? 'translate-x-0' : '-translate-x-full'">
|
|
<div
|
|
class="sticky top-16 bg-white dark:bg-[#1B1B1B] overflow-x-hidden overflow-y-auto no-scrollbar shrink-0 border-r border-gray-200 dark:border-gray-700/60 md:w-[18rem] xl:w-[20rem] h-[calc(100dvh-64px)]">
|
|
|
|
<!-- #Marketing group -->
|
|
<div>
|
|
<!-- Group header -->
|
|
<div class="sticky top-0 z-10">
|
|
<div
|
|
class="flex items-center bg-white dark:bg-[#1B1B1B] border-b border-gray-200 dark:border-gray-700/60 px-5 h-16">
|
|
<div class="w-full flex items-center justify-between">
|
|
<!-- Channel menu -->
|
|
<div class="relative" x-data="{ open: false }">
|
|
<button class="grow flex items-center truncate" aria-haspopup="true"
|
|
@click.prevent="open = !open" :aria-expanded="open">
|
|
<div class="truncate">
|
|
<span
|
|
class="font-semibold text-gray-800 dark:text-gray-100">2024</span>
|
|
</div>
|
|
<svg class="w-3 h-3 shrink-0 ml-1 fill-current text-gray-400 dark:text-gray-500"
|
|
viewBox="0 0 12 12">
|
|
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z"/>
|
|
</svg>
|
|
</button>
|
|
<div
|
|
class="origin-top-right z-10 absolute top-full left-0 min-w-60 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700/60 py-1.5 rounded-lg shadow-lg overflow-hidden mt-1"
|
|
@click.outside="open = false" @keydown.escape.window="open = false"
|
|
x-show="open"
|
|
x-transition:enter="transition ease-out duration-200 transform"
|
|
x-transition:enter-start="opacity-0 -translate-y-2"
|
|
x-transition:enter-end="opacity-100 translate-y-0"
|
|
x-transition:leave="transition ease-out duration-200"
|
|
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
|
|
x-cloak>
|
|
<ul>
|
|
<li>
|
|
<a class="font-medium text-sm text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-200 block py-1.5 px-3"
|
|
href="#0" @click="open = false" @focus="open = true"
|
|
@focusout="open = false">
|
|
<div class="flex items-center justify-between">
|
|
<div class="grow flex items-center truncate">
|
|
<div class="truncate">2024</div>
|
|
</div>
|
|
<svg class="w-3 h-3 shrink-0 fill-current text-orange-500 ml-1"
|
|
viewBox="0 0 12 12">
|
|
<path
|
|
d="M10.28 1.28L3.989 7.575 1.695 5.28A1 1 0 00.28 6.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 1.28z"/>
|
|
</svg>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Group body -->
|
|
<div class="px-5 py-4">
|
|
<!-- Search form -->
|
|
<form class="relative">
|
|
<label for="inbox-search" class="sr-only">Search</label>
|
|
<input
|
|
wire:model.live.debounce="search"
|
|
id="inbox-search" class="form-input w-full pl-9 bg-white dark:bg-gray-800"
|
|
type="search" placeholder="Suche…"/>
|
|
<button class="absolute inset-0 right-auto group" type="submit" aria-label="Search">
|
|
<svg
|
|
class="shrink-0 fill-current text-gray-400 dark:text-gray-500 group-hover:text-gray-500 dark:group-hover:text-gray-400 ml-3 mr-2"
|
|
width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
<path
|
|
d="M7 14c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7zM7 2C4.243 2 2 4.243 2 7s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5z"/>
|
|
<path
|
|
d="M15.707 14.293L13.314 11.9a8.019 8.019 0 01-1.414 1.414l2.393 2.393a.997.997 0 001.414 0 .999.999 0 000-1.414z"/>
|
|
</svg>
|
|
</button>
|
|
</form>
|
|
<!-- Inbox -->
|
|
<div class="mt-4">
|
|
<div class="text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase mb-3">
|
|
Plebs
|
|
</div>
|
|
<ul class="mb-6">
|
|
@foreach($plebs as $pleb)
|
|
<li class="-mx-2">
|
|
<div class="flex w-full p-2 rounded text-left">
|
|
<img class="w-8 h-8 rounded-full mr-2 bg-black"
|
|
src="{{ $pleb['profile']['picture'] ?? 'https://robohash.org/test' }}"
|
|
onerror="this.onerror=null; this.src='https://robohash.org/test';"
|
|
width="32"
|
|
height="32"
|
|
alt="{{ $pleb['pubkey'] }}"/>
|
|
<div class="grow truncate">
|
|
<div class="flex items-center justify-between mb-1.5">
|
|
<div class="truncate">
|
|
<span
|
|
class="text-sm font-semibold text-gray-800 dark:text-gray-100 truncate">{{ $pleb['profile']['name'] ?? $pleb['pubkey'] }}</span>
|
|
</div>
|
|
<div class="text-xs text-gray-500 font-medium">
|
|
<x-badge
|
|
color="{{ \App\Enums\AssociationStatus::from($pleb['association_status'])->color() }}"
|
|
label="{{ \App\Enums\AssociationStatus::from($pleb['association_status'])->label() }}"/>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="text-xs font-medium text-gray-800 dark:text-gray-100 truncate mb-0.5">
|
|
<div class="flex items-center space-x-2 h-5">
|
|
@foreach($positions as $name => $p)
|
|
@php
|
|
$votedResult = $loadedEvents->filter(fn ($e) => $e['pubkey'] === $pleb['pubkey'])->firstWhere('type', $name);
|
|
@endphp
|
|
<div class="flex space-x-2" wire:key="p_{{ $name }}">
|
|
@if($votedResult)
|
|
<i class="fa-sharp-duotone fa-solid {{ $p['icon'] }} w-4 h-4 fill-current text-green-500"></i>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inbox body -->
|
|
@if($currentPubkey)
|
|
|
|
@php
|
|
$electionConfig = collect(json_decode($election->candidates, true, 512, JSON_THROW_ON_ERROR))
|
|
->map(function ($c) use ($loadedEvents, $currentPubkey) {
|
|
$candidates = \App\Models\Profile::query()
|
|
->whereIn('pubkey', $c['c'])
|
|
->get()
|
|
->map(function ($p) use ($loadedEvents, $c, $currentPubkey) {
|
|
$votedClass = ' bg-green-500/20 text-green-700';
|
|
$notVotedClass = ' bg-gray-500/20 text-gray-100';
|
|
$hasVoted = $loadedEvents
|
|
->filter(fn($e) => $e['type'] === $c['type'] && $e['pubkey'] === $currentPubkey)
|
|
->firstWhere('votedFor.pubkey', $p->pubkey);
|
|
|
|
return [
|
|
'pubkey' => $p->pubkey,
|
|
'name' => $p->name,
|
|
'picture' => $p->picture,
|
|
'votedClass' => $hasVoted ? $votedClass : $notVotedClass,
|
|
];
|
|
});
|
|
|
|
return [
|
|
'type' => $c['type'],
|
|
'c' => $c['c'],
|
|
'candidates' => $candidates,
|
|
];
|
|
});
|
|
@endphp
|
|
|
|
<div class="grow flex flex-col md:translate-x-0 transition-transform duration-300 ease-in-out"
|
|
:class="inboxSidebarOpen ? 'translate-x-1/3' : 'translate-x-0'">
|
|
|
|
<!-- Header -->
|
|
<div class="sticky top-16">
|
|
<div
|
|
class="flex items-center justify-between before:absolute before:inset-0 before:backdrop-blur-md before:bg-gray-50/90 dark:before:bg-[#1B1B1B]/90 before:-z-10 border-b border-gray-200 dark:border-gray-700/60 px-4 sm:px-6 md:px-5 h-16">
|
|
<div
|
|
class="flex flex-col space-y-2 sm:space-y-0 sm:flex-row justify-between items-center w-full">
|
|
<div>
|
|
@if($isNotClosed)
|
|
<x-badge success
|
|
label="Die Wahl ist geöffnet bis zum {{ $election->end_time->timezone('Europe/Berlin')->format('d.m.Y H:i') }}"/>
|
|
@else
|
|
<x-badge negative label="Die Wahl ist geschlossen"/>
|
|
@endif
|
|
</div>
|
|
<div>
|
|
<x-button secondary
|
|
:href="route('association.election.admin', ['election' => $election])"
|
|
label="Wahl-Admin"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Body -->
|
|
<div class="grow px-4 sm:px-6 md:px-5 py-4">
|
|
|
|
<!-- Mail subject -->
|
|
<header class="sm:flex sm:items-start sm:justify-between mb-4">
|
|
<h1 class="text-xl leading-snug text-gray-800 dark:text-gray-100 font-bold mb-1 sm:mb-0 ml-2">
|
|
Wahl des Vereinsvorstands
|
|
</h1>
|
|
<button
|
|
class="text-xs inline-flex font-medium bg-sky-500/20 text-sky-700 rounded-full text-center px-2.5 py-1 whitespace-nowrap">
|
|
2024
|
|
</button>
|
|
</header>
|
|
|
|
<!-- Messages box -->
|
|
<div
|
|
class="shadow-sm rounded-xl px-6 divide-y divide-gray-200 dark:divide-gray-700/60">
|
|
|
|
<!-- Mail -->
|
|
<div class="py-6">
|
|
<h1 class="text-xl leading-snug text-gray-800 dark:text-gray-100 font-bold mb-1 sm:mb-0 ml-2">
|
|
Wahl des Präsidiums
|
|
</h1>
|
|
@php
|
|
$president = $positions['presidency'];
|
|
@endphp
|
|
<div
|
|
class="col-span-full sm:col-span-6 xl:col-span-4 bg-white dark:bg-gray-800 shadow-sm rounded-xl">
|
|
<div class="flex flex-col h-full p-5">
|
|
<header>
|
|
<div class="flex items-center justify-between">
|
|
<i class="fa-sharp-duotone fa-solid {{ $president['icon'] }} w-9 h-9 fill-current text-white"></i>
|
|
</div>
|
|
</header>
|
|
<div class="grow mt-2">
|
|
<div
|
|
class="inline-flex text-gray-800 dark:text-gray-100 hover:text-gray-900 dark:hover:text-white mb-1">
|
|
<h2 class="text-xl leading-snug font-semibold">{{ $president['title'] }}</h2>
|
|
</div>
|
|
<div class="text-sm">
|
|
@php
|
|
$votedResult = $loadedEvents->filter(fn ($event) => $event['pubkey'] === $currentPubkey)->firstWhere('type', 'presidency');
|
|
@endphp
|
|
@if($votedResult)
|
|
<span>Du hast "{{ $votedResult['votedFor']['name'] ?? 'error' }}" gewählt</span>
|
|
@else
|
|
<span>Wähle deinen Kandidaten für das Präsidium.</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
<footer class="mt-5">
|
|
<div class="grid sm:grid-cols-2 gap-y-2">
|
|
@foreach($electionConfig->firstWhere('type', 'presidency')['candidates'] as $c)
|
|
<div
|
|
@if($isNotClosed)wire:click="vote('{{ $c['pubkey'] }}', 'presidency')"
|
|
@endif
|
|
class="{{ $c['votedClass'] }} cursor-pointer text-xs inline-flex font-medium rounded-full text-center px-2.5 py-1">
|
|
<div class="flex items-center">
|
|
<img class="w-6 h-6 rounded-full mr-2 bg-black"
|
|
src="{{ $c['picture'] ?? 'https://robohash.org/' . $c['pubkey'] }}"
|
|
onerror="this.onerror=null; this.src='https://robohash.org/{{ $c['pubkey'] }}';"
|
|
width="24" height="24"
|
|
alt="{{ $c['name'] }}"/>
|
|
{{ $c['name'] }}
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
|
|
<h1 class="mt-6 border-t text-xl leading-snug text-gray-800 dark:text-gray-100 font-bold mb-1 sm:mb-0 ml-2">
|
|
Bestätigung der Vorstandsmitglieder
|
|
</h1>
|
|
<div class="grid grid-cols-12 gap-6">
|
|
|
|
@foreach(collect($positions)->filter(fn($position, $type) => $type !== 'presidency') as $type => $position)
|
|
@if($electionConfig->firstWhere('type', $type))
|
|
<div
|
|
class="col-span-full sm:col-span-6 xl:col-span-4 bg-white dark:bg-gray-800 shadow-sm rounded-xl">
|
|
<div class="flex flex-col h-full p-5">
|
|
<header>
|
|
<div class="flex items-center justify-between">
|
|
<i class="fa-sharp-duotone fa-solid {{ $position['icon'] }} w-9 h-9 fill-current text-white"></i>
|
|
</div>
|
|
</header>
|
|
<div class="grow mt-2">
|
|
<div
|
|
class="inline-flex text-gray-800 dark:text-gray-100 hover:text-gray-900 dark:hover:text-white mb-1">
|
|
<h2 class="text-xl leading-snug font-semibold">{{ $position['title'] }}</h2>
|
|
</div>
|
|
<div class="text-sm">
|
|
@php
|
|
$votedResult = $loadedEvents->filter(fn ($event) => $event['pubkey'] === $currentPubkey)->firstWhere('type', $type);
|
|
@endphp
|
|
@if($votedResult)
|
|
<span>Du hast "{{ $votedResult['votedFor']['name'] ?? 'error' }}" gewählt</span>
|
|
@else
|
|
<span>Klicke auf den Kandidaten, um seine Position als Vorstandsmitglied zu bestätigen.</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
<footer class="mt-5">
|
|
<div class="grid sm:grid-cols-2 gap-y-2">
|
|
@foreach($electionConfig->firstWhere('type', $type)['candidates'] as $c)
|
|
<div
|
|
@if($isNotClosed)wire:click="vote('{{ $c['pubkey'] }}', '{{ $type }}')"
|
|
@endif
|
|
class="{{ $c['votedClass'] }} cursor-pointer text-xs inline-flex font-medium rounded-full text-center px-2.5 py-1">
|
|
<div class="flex items-center">
|
|
<img class="w-6 h-6 rounded-full mr-2 bg-black"
|
|
src="{{ $c['picture'] ?? 'https://robohash.org/' . $c['pubkey'] }}"
|
|
onerror="this.onerror=null; this.src='https://robohash.org/{{ $c['pubkey'] }}';"
|
|
width="24" height="24"
|
|
alt="{{ $c['name'] }}"/>
|
|
{{ $c['name'] }}
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@endforeach
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Log events -->
|
|
{{--<div class="mt-6 hidden sm:block">
|
|
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-xl mb-8">
|
|
<header class="px-5 py-4">
|
|
<h2 class="font-semibold text-gray-800 dark:text-gray-100">Logged Votes on Nostr <span
|
|
class="text-gray-400 dark:text-gray-500 font-medium">{{ $loadedEvents->count() }}</span>
|
|
</h2>
|
|
</header>
|
|
<div>
|
|
<!-- Table -->
|
|
<div class="overflow-x-auto">
|
|
<table
|
|
class="table-auto w-full dark:text-gray-300 divide-y divide-gray-100 dark:divide-gray-700/60">
|
|
<!-- Table header -->
|
|
<thead
|
|
class="text-xs uppercase text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-900/20 border-t border-gray-100 dark:border-gray-700/60">
|
|
<tr>
|
|
<th class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div class="font-semibold text-left">ID</div>
|
|
</th>
|
|
<th class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div class="font-semibold text-left">Kind</div>
|
|
</th>
|
|
<th class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div class="font-semibold text-left">Pubkey</div>
|
|
</th>
|
|
<th class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div class="font-semibold text-left">Created At</div>
|
|
</th>
|
|
<th class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div class="font-semibold text-left">Voted For</div>
|
|
</th>
|
|
<th class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div class="font-semibold text-left">Type</div>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<!-- Table body -->
|
|
<tbody class="text-sm">
|
|
@foreach($loadedEvents as $event)
|
|
<tr>
|
|
<td class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div
|
|
class="font-medium">{{ \Illuminate\Support\Str::limit($event['id'], 10) }}</div>
|
|
</td>
|
|
<td class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div>{{ $event['kind'] }}</div>
|
|
</td>
|
|
<td class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div>{{ $event['profile']['name'] ?? '' }}</div>
|
|
</td>
|
|
<td class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div>{{ $event['created_at'] }}</div>
|
|
</td>
|
|
<td class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div>{{ $event['votedFor']['name'] ?? '' }}</div>
|
|
</td>
|
|
<td class="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
<div>{{ $event['type'] }}</div>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>--}}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
@endif
|
|
|
|
</div>
|
|
@endvolt
|
|
</x-layouts.app>
|