mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2026-01-27 06:33:18 +00:00
✨ Refactor project-support forms: add admin-only fields, improve Flux form components, and enhance layout for consistency. 🛠️ Remove redundant NostrAuth methods and streamline authorization logic.
This commit is contained in:
@@ -35,6 +35,8 @@ class ProjectProposal extends Model implements HasMedia
|
|||||||
protected $casts = [
|
protected $casts = [
|
||||||
'id' => 'integer',
|
'id' => 'integer',
|
||||||
'einundzwanzig_pleb_id' => 'integer',
|
'einundzwanzig_pleb_id' => 'integer',
|
||||||
|
'accepted' => 'boolean',
|
||||||
|
'sats_paid' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected static function booted() {}
|
protected static function booted() {}
|
||||||
|
|||||||
@@ -113,11 +113,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="py-2">
|
<div class="py-2">
|
||||||
@if($project->sats_paid)
|
@if($project->sats_paid)
|
||||||
<div
|
<flux:badge color="green">Wurde mit {{ number_format($project->sats_paid, 0, ',', '.') }} Sats unterstützt</flux:badge>
|
||||||
class="text-sm inline-flex font-medium bg-green-500/20 text-green-700 rounded-full text-center px-2.5 py-1">
|
|
||||||
Wurde mit {{ number_format($project->sats_paid, 0, ',', '.') }} Sats
|
|
||||||
unterstützt
|
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -47,25 +47,6 @@ new class extends Component {
|
|||||||
$this->loadBoardVotes();
|
$this->loadBoardVotes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedOut(): void
|
|
||||||
{
|
|
||||||
$this->currentPubkey = null;
|
|
||||||
$this->currentPleb = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleNostrLoggedIn($pubkey): void
|
|
||||||
{
|
|
||||||
$this->currentPubkey = $pubkey;
|
|
||||||
$allowedPubkeys = [
|
|
||||||
'0adf67475ccc5ca456fd3022e46f5d526eb0af6284bf85494c0dd7847f3e5033',
|
|
||||||
'430169631f2f0682c60cebb4f902d68f0c71c498fd1711fd982f052cf1fd4279',
|
|
||||||
];
|
|
||||||
if (in_array($this->currentPubkey, $allowedPubkeys, true)) {
|
|
||||||
$this->isAllowed = true;
|
|
||||||
}
|
|
||||||
dd($this->isAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleNewVote(): void
|
public function handleNewVote(): void
|
||||||
{
|
{
|
||||||
$this->loadEvents();
|
$this->loadEvents();
|
||||||
|
|||||||
@@ -37,28 +37,6 @@ new class extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedOut(): void
|
|
||||||
{
|
|
||||||
$this->isAllowed = false;
|
|
||||||
$this->currentPubkey = null;
|
|
||||||
$this->currentPleb = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleNostrLoggedIn($pubkey): void
|
|
||||||
{
|
|
||||||
NostrAuth::login($pubkey);
|
|
||||||
$this->currentPubkey = $pubkey;
|
|
||||||
$this->currentPleb = \App\Models\EinundzwanzigPleb::query()
|
|
||||||
->where('pubkey', $pubkey)->first();
|
|
||||||
$logPubkeys = [
|
|
||||||
'0adf67475ccc5ca456fd3022e46f5d526eb0af6284bf85494c0dd7847f3e5033',
|
|
||||||
'430169631f2f0682c60cebb4f902d68f0c71c498fd1711fd982f052cf1fd4279',
|
|
||||||
];
|
|
||||||
if (in_array($this->currentPubkey, $logPubkeys, true)) {
|
|
||||||
$this->isAllowed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function saveElection($index): void
|
public function saveElection($index): void
|
||||||
{
|
{
|
||||||
$election = $this->elections[$index];
|
$election = $this->elections[$index];
|
||||||
|
|||||||
@@ -210,28 +210,6 @@ new class extends Component {
|
|||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedIn($pubkey): void
|
|
||||||
{
|
|
||||||
NostrAuth::login($pubkey);
|
|
||||||
$this->currentPubkey = $pubkey;
|
|
||||||
$this->currentPleb = EinundzwanzigPleb::query()->where('pubkey', $pubkey)->first();
|
|
||||||
$logPubkeys = [
|
|
||||||
'0adf67475ccc5ca456fd3022e46f5d526eb0af6284bf85494c0dd7847f3e5033',
|
|
||||||
'430169631f2f0682c60cebb4f902d68f0c71c498fd1711fd982f052cf1fd4279',
|
|
||||||
];
|
|
||||||
if (in_array($this->currentPubkey, $logPubkeys, true)) {
|
|
||||||
$this->showLog = true;
|
|
||||||
$this->isAllowed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleNostrLoggedOut(): void
|
|
||||||
{
|
|
||||||
$this->isAllowed = false;
|
|
||||||
$this->currentPubkey = null;
|
|
||||||
$this->currentPleb = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleNewVote(): void
|
public function handleNewVote(): void
|
||||||
{
|
{
|
||||||
$this->loadEvents();
|
$this->loadEvents();
|
||||||
|
|||||||
@@ -1,16 +1,41 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\AssociationStatus;
|
||||||
use App\Models\EinundzwanzigPleb;
|
use App\Models\EinundzwanzigPleb;
|
||||||
use App\Support\NostrAuth;
|
use App\Support\NostrAuth;
|
||||||
|
use Flux\Flux;
|
||||||
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
new class extends Component {
|
new class extends Component
|
||||||
|
{
|
||||||
public bool $isAllowed = false;
|
public bool $isAllowed = false;
|
||||||
|
|
||||||
public ?string $currentPubkey = null;
|
public ?string $currentPubkey = null;
|
||||||
|
|
||||||
public ?EinundzwanzigPleb $currentPleb = null;
|
public ?EinundzwanzigPleb $currentPleb = null;
|
||||||
|
|
||||||
|
public string $sortBy = 'association_status';
|
||||||
|
|
||||||
|
public string $sortDirection = 'desc';
|
||||||
|
|
||||||
|
public ?int $selectedPlebId = null;
|
||||||
|
|
||||||
|
public ?int $confirmAcceptId = null;
|
||||||
|
|
||||||
|
public ?int $confirmDeleteId = null;
|
||||||
|
|
||||||
|
public string $search = '';
|
||||||
|
|
||||||
|
public bool $showPaidOnly = false;
|
||||||
|
|
||||||
|
public $plebs = [];
|
||||||
|
|
||||||
|
public function updatedSearch(): void
|
||||||
|
{
|
||||||
|
$this->plebs = $this->loadPlebs();
|
||||||
|
}
|
||||||
|
|
||||||
protected $listeners = [
|
protected $listeners = [
|
||||||
'nostrLoggedOut' => 'handleNostrLoggedOut',
|
'nostrLoggedOut' => 'handleNostrLoggedOut',
|
||||||
'nostrLoggedIn' => 'handleNostrLoggedIn',
|
'nostrLoggedIn' => 'handleNostrLoggedIn',
|
||||||
@@ -34,43 +59,247 @@ new class extends Component {
|
|||||||
$this->isAllowed = true;
|
$this->isAllowed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->plebs = $this->loadPlebs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedOut(): void
|
private function loadPlebs()
|
||||||
{
|
{
|
||||||
$this->isAllowed = false;
|
$query = EinundzwanzigPleb::query()
|
||||||
$this->currentPubkey = null;
|
->with([
|
||||||
|
'profile',
|
||||||
|
'paymentEvents' => fn ($query) => $query
|
||||||
|
->where('year', date('Y'))
|
||||||
|
->where('paid', true),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($this->search) {
|
||||||
|
$query->where(function ($query) {
|
||||||
|
$query->whereHas('profile', function ($query) {
|
||||||
|
$query->where('name', 'ilike', '%'.$this->search.'%');
|
||||||
|
})->orWhere('npub', 'like', '%'.$this->search.'%');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedIn($pubkey): void
|
if ($this->showPaidOnly) {
|
||||||
|
$query->whereHas('paymentEvents', fn ($query) => $query
|
||||||
|
->where('year', date('Y'))
|
||||||
|
->where('paid', true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->tap(fn ($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query)
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sort(string $column): void
|
||||||
{
|
{
|
||||||
NostrAuth::login($pubkey);
|
if ($this->sortBy === $column) {
|
||||||
$this->currentPubkey = $pubkey;
|
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
||||||
$this->currentPleb = \App\Models\EinundzwanzigPleb::query()
|
} else {
|
||||||
->where('pubkey', $pubkey)->first();
|
$this->sortBy = $column;
|
||||||
$allowedPubkeys = [
|
$this->sortDirection = 'asc';
|
||||||
'0adf67475ccc5ca456fd3022e46f5d526eb0af6284bf85494c0dd7847f3e5033',
|
}
|
||||||
'430169631f2f0682c60cebb4f902d68f0c71c498fd1711fd982f052cf1fd4279',
|
|
||||||
'7acf30cf60b85c62b8f654556cc21e4016df8f5604b3b6892794f88bb80d7a1d',
|
$this->plebs = $this->loadPlebs();
|
||||||
'f240be2b684f85cc81566f2081386af81d7427ea86250c8bde6b7a8500c761ba',
|
}
|
||||||
'19e358b8011f5f4fc653c565c6d4c2f33f32661f4f90982c9eedc292a8774ec3',
|
|
||||||
'acbcec475a1a4f9481939ecfbd1c3d111f5b5a474a39ae039bbc720fdd305bec',
|
public function togglePaidFilter(): void
|
||||||
];
|
{
|
||||||
if (in_array($this->currentPubkey, $allowedPubkeys, true)) {
|
$this->showPaidOnly = !$this->showPaidOnly;
|
||||||
$this->isAllowed = true;
|
$this->plebs = $this->loadPlebs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function openPaymentModal(int $plebId): void
|
||||||
|
{
|
||||||
|
$this->selectedPlebId = $plebId;
|
||||||
|
Flux::modal('payment-details')->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function accept($rowId): void
|
||||||
|
{
|
||||||
|
$this->confirmAcceptId = $rowId;
|
||||||
|
Flux::modal('confirm-accept-pleb')->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($rowId): void
|
||||||
|
{
|
||||||
|
$this->confirmDeleteId = $rowId;
|
||||||
|
Flux::modal('confirm-delete-pleb')->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function acceptPleb(): void
|
||||||
|
{
|
||||||
|
if ($this->confirmAcceptId) {
|
||||||
|
$pleb = EinundzwanzigPleb::query()->findOrFail($this->confirmAcceptId);
|
||||||
|
$for = $pleb->application_for;
|
||||||
|
$text = $pleb->application_text;
|
||||||
|
$pleb->association_status = AssociationStatus::from($for);
|
||||||
|
$pleb->application_for = null;
|
||||||
|
$pleb->archived_application_text = $text;
|
||||||
|
$pleb->application_text = null;
|
||||||
|
$pleb->save();
|
||||||
|
|
||||||
|
$this->confirmAcceptId = null;
|
||||||
|
Flux::modal('confirm-accept-pleb')->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deletePleb(): void
|
||||||
|
{
|
||||||
|
if ($this->confirmDeleteId) {
|
||||||
|
$pleb = EinundzwanzigPleb::query()->findOrFail($this->confirmDeleteId);
|
||||||
|
$pleb->application_for = null;
|
||||||
|
$pleb->application_text = null;
|
||||||
|
$pleb->save();
|
||||||
|
|
||||||
|
$this->confirmDeleteId = null;
|
||||||
|
Flux::modal('confirm-delete-pleb')->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function closePaymentModal(): void
|
||||||
|
{
|
||||||
|
$this->selectedPlebId = null;
|
||||||
|
Flux::modal('payment-details')->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Computed]
|
||||||
|
public function selectedPleb(): ?EinundzwanzigPleb
|
||||||
|
{
|
||||||
|
return EinundzwanzigPleb::with(['paymentEvents'])->find($this->selectedPlebId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@if($isAllowed)
|
@if($isAllowed)
|
||||||
<div class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
|
<div class="mb-4 flex gap-2">
|
||||||
<livewire:einundzwanzig-pleb-table/>
|
<flux:input
|
||||||
|
wire:model.live.debounce.300ms="search"
|
||||||
|
placeholder="Suche nach Name oder Npub..."
|
||||||
|
icon="magnifying-glass"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
|
<flux:button
|
||||||
|
wire:click="togglePaidFilter"
|
||||||
|
:variant="$showPaidOnly ? 'primary' : 'ghost'"
|
||||||
|
icon="check"
|
||||||
|
>
|
||||||
|
{{ $showPaidOnly ? 'Alle anzeigen' : 'Nur Bezahlt' }}
|
||||||
|
</flux:button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<flux:table>
|
||||||
|
<flux:table.columns>
|
||||||
|
<flux:table.column>Avatar</flux:table.column>
|
||||||
|
<flux:table.column
|
||||||
|
sortable
|
||||||
|
:sorted="$sortBy === 'name'"
|
||||||
|
:direction="$sortDirection"
|
||||||
|
wire:click="sort('name')"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</flux:table.column>
|
||||||
|
<flux:table.column
|
||||||
|
sortable
|
||||||
|
:sorted="$sortBy === 'association_status'"
|
||||||
|
:direction="$sortDirection"
|
||||||
|
wire:click="sort('association_status')"
|
||||||
|
>
|
||||||
|
Aktueller Status
|
||||||
|
</flux:table.column>
|
||||||
|
<flux:table.column>Beitrag {{ date('Y') }}</flux:table.column>
|
||||||
|
<flux:table.column>Aktionen</flux:table.column>
|
||||||
|
</flux:table.columns>
|
||||||
|
|
||||||
|
<flux:table.rows>
|
||||||
|
@foreach ($this->plebs as $pleb)
|
||||||
|
<flux:table.row :key="$pleb->id">
|
||||||
|
<flux:table.cell>
|
||||||
|
<flux:avatar
|
||||||
|
size="xl"
|
||||||
|
:src="$pleb->profile?->picture ?? asset('einundzwanzig-alpha.jpg')"
|
||||||
|
:name="$pleb->profile?->name ?? ''"
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</flux:table.cell>
|
||||||
|
|
||||||
|
<flux:table.cell>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<flux:heading size="sm">
|
||||||
|
{{ $pleb->profile?->name ?: $pleb->profile?->display_name ?? '' }}
|
||||||
|
</flux:heading>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://nostrudel.ninja/u/{{ $pleb->npub }}"
|
||||||
|
class="text-xs text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200"
|
||||||
|
>
|
||||||
|
Nostr Profile
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</flux:table.cell>
|
||||||
|
|
||||||
|
<flux:table.cell>
|
||||||
|
<flux:badge
|
||||||
|
size="sm"
|
||||||
|
:color="match($pleb->association_status) {
|
||||||
|
AssociationStatus::DEFAULT => 'zinc',
|
||||||
|
AssociationStatus::PASSIVE => 'yellow',
|
||||||
|
AssociationStatus::ACTIVE => 'green',
|
||||||
|
AssociationStatus::HONORARY => 'blue',
|
||||||
|
default => 'red',
|
||||||
|
}"
|
||||||
|
inset="top bottom"
|
||||||
|
>
|
||||||
|
{{ $pleb->association_status->label() }}
|
||||||
|
</flux:badge>
|
||||||
|
</flux:table.cell>
|
||||||
|
|
||||||
|
<flux:table.cell>
|
||||||
|
@if($pleb->paymentEvents->count() > 0 && $pleb->paymentEvents->first()->paid)
|
||||||
|
<flux:button
|
||||||
|
wire:click="openPaymentModal({{ $pleb->id }})"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
icon="banknotes"
|
||||||
|
>
|
||||||
|
<span class="text-green-500">
|
||||||
|
{{ number_format($pleb->paymentEvents->first()->amount, 0, ',', '.') }}
|
||||||
|
</span>
|
||||||
|
</flux:button>
|
||||||
@else
|
@else
|
||||||
<div class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
|
<flux:text class="text-zinc-500">keine Zahlung vorhanden</flux:text>
|
||||||
|
@endif
|
||||||
|
</flux:table.cell>
|
||||||
|
|
||||||
|
<flux:table.cell align="end">
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<flux:button
|
||||||
|
wire:click="delete({{ $pleb->id }})"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
icon="trash"
|
||||||
|
inset="top bottom"
|
||||||
|
></flux:button>
|
||||||
|
|
||||||
|
@if($pleb->application_for)
|
||||||
|
<flux:button
|
||||||
|
wire:click="accept({{ $pleb->id }})"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
icon="check"
|
||||||
|
inset="top bottom"
|
||||||
|
></flux:button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</flux:table.cell>
|
||||||
|
</flux:table.row>
|
||||||
|
@endforeach
|
||||||
|
</flux:table.rows>
|
||||||
|
</flux:table>
|
||||||
|
@else
|
||||||
|
<div class="px-4 sm:px-6 lg:px-8 py-8 w-full mx-auto">
|
||||||
<flux:callout variant="warning" icon="exclamation-circle">
|
<flux:callout variant="warning" icon="exclamation-circle">
|
||||||
<flux:heading>Mitglieder können nicht bearbeitet werden</flux:heading>
|
<flux:heading>Mitglieder können nicht bearbeitet werden</flux:heading>
|
||||||
<p>
|
<p>
|
||||||
@@ -80,13 +309,68 @@ new class extends Component {
|
|||||||
@if(!NostrAuth::check())
|
@if(!NostrAuth::check())
|
||||||
Bitte melde dich zunächst mit Nostr an.
|
Bitte melde dich zunächst mit Nostr an.
|
||||||
@else
|
@else
|
||||||
Dein Benutzer-Account ist nicht für diese Funktion autorisiert. Bitte kontaktiere den Vorstand, wenn du Zugriff benötigst.
|
Dein Benutzer-Account ist nicht für diese Funktion autorisiert. Bitte kontaktiere den Vorstand,
|
||||||
|
wenn du Zugriff benötigst.
|
||||||
@endif
|
@endif
|
||||||
</p>
|
</p>
|
||||||
</flux:callout>
|
</flux:callout>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
<!-- Payment Details Modal -->
|
||||||
|
<flux:modal name="payment-details" class="max-w-full">
|
||||||
|
@if($this->selectedPleb)
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div>
|
||||||
|
<flux:heading size="lg">Zahlungsdetails</flux:heading>
|
||||||
|
<flux:subheading class="mt-2">
|
||||||
|
{{ $this->selectedPleb->profile?->name ?: $this->selectedPleb->profile?->display_name ?? 'Unbekannt' }}
|
||||||
|
</flux:subheading>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($this->selectedPleb->application_text)
|
||||||
|
<flux:callout icon="information-circle" variant="info">
|
||||||
|
{{ $this->selectedPleb->application_text }}
|
||||||
|
</flux:callout>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<flux:heading size="md" class="mb-4">Bisherige Zahlungen</flux:heading>
|
||||||
|
|
||||||
|
@if($this->selectedPleb->paymentEvents->count() > 0)
|
||||||
|
<flux:table>
|
||||||
|
<flux:table.columns>
|
||||||
|
<flux:table.column>Satoshis</flux:table.column>
|
||||||
|
<flux:table.column>Jahr</flux:table.column>
|
||||||
|
<flux:table.column>Event-ID</flux:table.column>
|
||||||
|
</flux:table.columns>
|
||||||
|
|
||||||
|
<flux:table.rows>
|
||||||
|
@foreach($this->selectedPleb->paymentEvents as $payment)
|
||||||
|
<flux:table.row :key="$payment->id">
|
||||||
|
<flux:table.cell variant="strong">{{ $payment->amount }}</flux:table.cell>
|
||||||
|
<flux:table.cell>{{ $payment->year }}</flux:table.cell>
|
||||||
|
<flux:table.cell class="text-xs">{{ $payment->event_id }}</flux:table.cell>
|
||||||
|
</flux:table.row>
|
||||||
|
@endforeach
|
||||||
|
</flux:table.rows>
|
||||||
|
</flux:table>
|
||||||
|
@else
|
||||||
|
<flux:callout variant="info" icon="information-circle">
|
||||||
|
Keine Zahlungen gefunden
|
||||||
|
</flux:callout>
|
||||||
|
@endif
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<flux:spacer/>
|
||||||
|
<flux:button wire:click="closePaymentModal" variant="primary">Schließen</flux:button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</flux:modal>
|
||||||
|
|
||||||
|
<!-- Accept Confirmation Modal -->
|
||||||
<flux:modal name="confirm-accept-pleb" class="min-w-88">
|
<flux:modal name="confirm-accept-pleb" class="min-w-88">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
@@ -105,6 +389,7 @@ new class extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</flux:modal>
|
</flux:modal>
|
||||||
|
|
||||||
|
<!-- Delete Confirmation Modal -->
|
||||||
<flux:modal name="confirm-delete-pleb" class="min-w-88">
|
<flux:modal name="confirm-delete-pleb" class="min-w-88">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -73,44 +73,6 @@ new class extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedIn($pubkey): void
|
|
||||||
{
|
|
||||||
NostrAuth::login($pubkey);
|
|
||||||
|
|
||||||
$this->currentPubkey = $pubkey;
|
|
||||||
$this->currentPleb = EinundzwanzigPleb::query()
|
|
||||||
->with([
|
|
||||||
'paymentEvents' => fn($query) => $query->where('year', date('Y')),
|
|
||||||
])
|
|
||||||
->where('pubkey', $pubkey)->first();
|
|
||||||
$this->email = $this->currentPleb->email;
|
|
||||||
$this->no = $this->currentPleb->no_email;
|
|
||||||
$this->showEmail = !$this->no;
|
|
||||||
if ($this->currentPleb->association_status === AssociationStatus::ACTIVE) {
|
|
||||||
$this->amountToPay = config('app.env') === 'production' ? 21000 : 1;
|
|
||||||
}
|
|
||||||
if ($this->currentPleb->paymentEvents->count() < 1) {
|
|
||||||
$this->createPaymentEvent();
|
|
||||||
$this->currentPleb->load('paymentEvents');
|
|
||||||
}
|
|
||||||
$this->loadEvents();
|
|
||||||
$this->listenForPayment();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleNostrLoggedOut(): void
|
|
||||||
{
|
|
||||||
NostrAuth::logout();
|
|
||||||
|
|
||||||
$this->currentPubkey = null;
|
|
||||||
$this->currentPleb = null;
|
|
||||||
$this->yearsPaid = [];
|
|
||||||
$this->events = [];
|
|
||||||
$this->payments = [];
|
|
||||||
$this->qrCode = null;
|
|
||||||
$this->amountToPay = config('app.env') === 'production' ? 21000 : 1;
|
|
||||||
$this->currentYearIsPaid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatedNo(): void
|
public function updatedNo(): void
|
||||||
{
|
{
|
||||||
$this->showEmail = !$this->no;
|
$this->showEmail = !$this->no;
|
||||||
@@ -562,7 +524,7 @@ new class extends Component {
|
|||||||
Pay {{ $amountToPay }} Sats
|
Pay {{ $amountToPay }} Sats
|
||||||
</flux:button>
|
</flux:button>
|
||||||
@else
|
@else
|
||||||
<flux:button disabled class="text-xl px-8 py-3">
|
<flux:button disabled variant="primary" color="green" class="text-xl px-8 py-3">
|
||||||
<i class="fa-sharp-duotone fa-solid fa-check-circle mr-2"></i>
|
<i class="fa-sharp-duotone fa-solid fa-check-circle mr-2"></i>
|
||||||
Aktuelles Jahr bezahlt
|
Aktuelles Jahr bezahlt
|
||||||
</flux:button>
|
</flux:button>
|
||||||
|
|||||||
@@ -2,21 +2,28 @@
|
|||||||
|
|
||||||
use App\Models\ProjectProposal;
|
use App\Models\ProjectProposal;
|
||||||
use App\Support\NostrAuth;
|
use App\Support\NostrAuth;
|
||||||
use Livewire\Component;
|
|
||||||
use Livewire\Attributes\Layout;
|
use Livewire\Attributes\Layout;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
new
|
new
|
||||||
#[Layout('layouts.app')]
|
#[Layout('layouts.app')]
|
||||||
#[Title('Projektförderung anlegen')]
|
#[Title('Projektförderung anlegen')]
|
||||||
class extends Component {
|
class extends Component
|
||||||
|
{
|
||||||
public array $form = [
|
public array $form = [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
|
'support_in_sats' => '',
|
||||||
|
'website' => '',
|
||||||
|
'accepted' => false,
|
||||||
|
'sats_paid' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
public bool $isAllowed = false;
|
public bool $isAllowed = false;
|
||||||
|
|
||||||
|
public bool $isAdmin = false;
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
if (NostrAuth::check()) {
|
if (NostrAuth::check()) {
|
||||||
@@ -26,6 +33,10 @@ class extends Component {
|
|||||||
if ($currentPleb && $currentPleb->association_status->value > 1 && $currentPleb->paymentEvents()->where('year', date('Y'))->where('paid', true)->exists()) {
|
if ($currentPleb && $currentPleb->association_status->value > 1 && $currentPleb->paymentEvents()->where('year', date('Y'))->where('paid', true)->exists()) {
|
||||||
$this->isAllowed = true;
|
$this->isAllowed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($currentPleb && in_array($currentPleb->npub, config('einundzwanzig.config.current_board'), true)) {
|
||||||
|
$this->isAdmin = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,12 +45,17 @@ class extends Component {
|
|||||||
$this->validate([
|
$this->validate([
|
||||||
'form.name' => 'required|string|max:255',
|
'form.name' => 'required|string|max:255',
|
||||||
'form.description' => 'required|string',
|
'form.description' => 'required|string',
|
||||||
|
'form.support_in_sats' => 'required|integer|min:0',
|
||||||
|
'form.website' => 'required|url|max:255',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
ProjectProposal::query()->create([
|
ProjectProposal::query()->create([
|
||||||
'name' => $this->form['name'],
|
'name' => $this->form['name'],
|
||||||
'description' => $this->form['description'],
|
'description' => $this->form['description'],
|
||||||
'support_in_sats' => 0,
|
'support_in_sats' => (int) $this->form['support_in_sats'],
|
||||||
|
'website' => $this->form['website'],
|
||||||
|
'accepted' => $this->form['accepted'],
|
||||||
|
'sats_paid' => $this->form['sats_paid'],
|
||||||
'einundzwanzig_pleb_id' => \App\Models\EinundzwanzigPleb::query()->where('pubkey', NostrAuth::pubkey())->first()->id,
|
'einundzwanzig_pleb_id' => \App\Models\EinundzwanzigPleb::query()->where('pubkey', NostrAuth::pubkey())->first()->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -59,47 +75,62 @@ class extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md:flex">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
<!-- Left column -->
|
<!-- Form card -->
|
||||||
<div class="w-full md:w-60 mb-4 md:mb-0">
|
<div class="lg:col-span-2">
|
||||||
<div
|
<flux:card>
|
||||||
class="bg-white dark:bg-gray-800 shadow-sm rounded-xl p-5">
|
<flux:fieldset>
|
||||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-4">
|
<flux:legend>Formular</flux:legend>
|
||||||
Formular
|
|
||||||
</h2>
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
|
||||||
<flux:field>
|
<flux:field>
|
||||||
<flux:label>Name</flux:label>
|
<flux:label>Name</flux:label>
|
||||||
<flux:input wire:model="form.name" placeholder="Projektname" />
|
<flux:input wire:model="form.name" placeholder="Projektname" />
|
||||||
<flux:error name="form.name" />
|
<flux:error name="form.name" />
|
||||||
</flux:field>
|
</flux:field>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<flux:field>
|
<flux:field>
|
||||||
<flux:label>Beschreibung</flux:label>
|
<flux:label>Website</flux:label>
|
||||||
<flux:textarea wire:model="form.description" rows="6" placeholder="Projektbeschreibung..." />
|
<flux:input wire:model="form.website" type="url" placeholder="https://example.com" />
|
||||||
|
<flux:error name="form.website" />
|
||||||
|
</flux:field>
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Unterstützung (Sats)</flux:label>
|
||||||
|
<flux:input wire:model="form.support_in_sats" type="number" placeholder="100000" />
|
||||||
|
<flux:error name="form.support_in_sats" />
|
||||||
|
</flux:field>
|
||||||
|
<flux:editor wire:model="form.description" label="Beschreibung" description="Projektbeschreibung..." />
|
||||||
<flux:error name="form.description" />
|
<flux:error name="form.description" />
|
||||||
|
|
||||||
|
@if($isAdmin)
|
||||||
|
<flux:separator />
|
||||||
|
<flux:heading level="3" class="text-sm font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Admin Felder</flux:heading>
|
||||||
|
<div class="space-y-3 mt-3">
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Akzeptiert</flux:label>
|
||||||
|
<flux:switch wire:model="form.accepted" />
|
||||||
|
</flux:field>
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Sats bezahlt</flux:label>
|
||||||
|
<flux:input type="number" wire:model="form.sats_paid" />
|
||||||
</flux:field>
|
</flux:field>
|
||||||
</div>
|
</div>
|
||||||
<flux:button wire:click="save" wire:loading.attr="disabled" variant="primary" class="w-full">
|
@endif
|
||||||
|
|
||||||
|
<flux:button wire:click="save" wire:loading.attr="disabled" variant="primary" class="w-full mt-4">
|
||||||
Speichern
|
Speichern
|
||||||
</flux:button>
|
</flux:button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</flux:fieldset>
|
||||||
|
</flux:card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right column -->
|
<!-- Information card -->
|
||||||
<div class="flex-1 md:ml-8">
|
<div>
|
||||||
<div
|
<flux:card>
|
||||||
class="bg-white dark:bg-gray-800 shadow-sm rounded-xl p-5">
|
<flux:heading level="2">Information</flux:heading>
|
||||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-4">
|
<p class="text-sm text-gray-800 dark:text-gray-100 mt-4">
|
||||||
Information
|
|
||||||
</h2>
|
|
||||||
<p class="text-sm text-gray-800 dark:text-gray-100">
|
|
||||||
Fülle das Formular aus, um eine neue Projektförderung anzulegen.
|
Fülle das Formular aus, um eine neue Projektförderung anzulegen.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</flux:card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,16 +9,23 @@ use Livewire\Component;
|
|||||||
new
|
new
|
||||||
#[Layout('layouts.app')]
|
#[Layout('layouts.app')]
|
||||||
#[Title('Projektförderung bearbeiten')]
|
#[Title('Projektförderung bearbeiten')]
|
||||||
class extends Component {
|
class extends Component
|
||||||
|
{
|
||||||
public ProjectProposal $project;
|
public ProjectProposal $project;
|
||||||
|
|
||||||
public array $form = [
|
public array $form = [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
|
'support_in_sats' => '',
|
||||||
|
'website' => '',
|
||||||
|
'accepted' => false,
|
||||||
|
'sats_paid' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
public bool $isAllowed = false;
|
public bool $isAllowed = false;
|
||||||
|
|
||||||
|
public bool $isAdmin = false;
|
||||||
|
|
||||||
public function mount($projectProposal): void
|
public function mount($projectProposal): void
|
||||||
{
|
{
|
||||||
$this->project = ProjectProposal::query()->where('slug', $projectProposal)->firstOrFail();
|
$this->project = ProjectProposal::query()->where('slug', $projectProposal)->firstOrFail();
|
||||||
@@ -38,8 +45,16 @@ class extends Component {
|
|||||||
$this->form = [
|
$this->form = [
|
||||||
'name' => $this->project->name,
|
'name' => $this->project->name,
|
||||||
'description' => $this->project->description,
|
'description' => $this->project->description,
|
||||||
|
'support_in_sats' => (string) $this->project->support_in_sats,
|
||||||
|
'website' => $this->project->website ?? '',
|
||||||
|
'accepted' => (bool) $this->project->accepted,
|
||||||
|
'sats_paid' => $this->project->sats_paid,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($currentPleb && in_array($currentPleb->npub, config('einundzwanzig.config.current_board'), true)) {
|
||||||
|
$this->isAdmin = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,11 +63,17 @@ class extends Component {
|
|||||||
$this->validate([
|
$this->validate([
|
||||||
'form.name' => 'required|string|max:255',
|
'form.name' => 'required|string|max:255',
|
||||||
'form.description' => 'required|string',
|
'form.description' => 'required|string',
|
||||||
|
'form.support_in_sats' => 'required|integer|min:0',
|
||||||
|
'form.website' => 'required|url|max:255',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->project->update([
|
$this->project->update([
|
||||||
'name' => $this->form['name'],
|
'name' => $this->form['name'],
|
||||||
'description' => $this->form['description'],
|
'description' => $this->form['description'],
|
||||||
|
'support_in_sats' => (int) $this->form['support_in_sats'],
|
||||||
|
'website' => $this->form['website'],
|
||||||
|
'accepted' => $this->isAdmin ? (bool) $this->form['accepted'] : $this->project->accepted,
|
||||||
|
'sats_paid' => $this->isAdmin ? $this->form['sats_paid'] : $this->project->sats_paid,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->redirectRoute('association.projectSupport.item', $this->project);
|
$this->redirectRoute('association.projectSupport.item', $this->project);
|
||||||
@@ -72,14 +93,12 @@ class extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md:flex">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
<!-- Left column -->
|
<!-- Left column - Form -->
|
||||||
<div class="w-full md:w-60 mb-4 md:mb-0">
|
<div class="lg:col-span-2">
|
||||||
<div
|
<flux:card>
|
||||||
class="bg-white dark:bg-gray-800 shadow-sm rounded-xl p-5">
|
<flux:fieldset>
|
||||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-4">
|
<flux:legend>Formular</flux:legend>
|
||||||
Formular
|
|
||||||
</h2>
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<flux:field>
|
<flux:field>
|
||||||
@@ -90,29 +109,54 @@ class extends Component {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<flux:field>
|
<flux:field>
|
||||||
<flux:label>Beschreibung</flux:label>
|
<flux:label>Website</flux:label>
|
||||||
<flux:textarea wire:model="form.description" rows="6" placeholder="Projektbeschreibung..." />
|
<flux:input wire:model="form.website" type="url" placeholder="https://example.com" />
|
||||||
<flux:error name="form.description" />
|
<flux:error name="form.website" />
|
||||||
</flux:field>
|
</flux:field>
|
||||||
</div>
|
</div>
|
||||||
<flux:button wire:click="update" wire:loading.attr="disabled" variant="primary" class="w-full">
|
<div>
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Unterstützung (Sats)</flux:label>
|
||||||
|
<flux:input wire:model="form.support_in_sats" type="number" placeholder="100000" />
|
||||||
|
<flux:error name="form.support_in_sats" />
|
||||||
|
</flux:field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<flux:editor wire:model="form.description" label="Beschreibung" description="Projektbeschreibung..." />
|
||||||
|
<flux:error name="form.description" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($isAdmin)
|
||||||
|
<flux:separator />
|
||||||
|
<flux:heading level="3" class="text-sm font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Admin Felder</flux:heading>
|
||||||
|
<div class="space-y-3 mt-3">
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Akzeptiert</flux:label>
|
||||||
|
<flux:switch wire:model="form.accepted" />
|
||||||
|
</flux:field>
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Sats bezahlt</flux:label>
|
||||||
|
<flux:input type="number" wire:model="form.sats_paid" />
|
||||||
|
</flux:field>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<flux:button wire:click="update" wire:loading.attr="disabled" variant="primary" class="w-full mt-4">
|
||||||
Speichern
|
Speichern
|
||||||
</flux:button>
|
</flux:button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</flux:fieldset>
|
||||||
|
</flux:card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right column -->
|
<!-- Right column - Information -->
|
||||||
<div class="flex-1 md:ml-8">
|
<div>
|
||||||
<div
|
<flux:card>
|
||||||
class="bg-white dark:bg-gray-800 shadow-sm rounded-xl p-5">
|
<flux:heading level="2">Information</flux:heading>
|
||||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-4">
|
<p class="text-sm text-gray-800 dark:text-gray-100 mt-4">
|
||||||
Information
|
|
||||||
</h2>
|
|
||||||
<p class="text-sm text-gray-800 dark:text-gray-100">
|
|
||||||
Bearbeite die Projektförderung und speichere deine Änderungen.
|
Bearbeite die Projektförderung und speichere deine Änderungen.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</flux:card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -64,21 +64,6 @@ new class extends Component {
|
|||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedIn($pubkey): void
|
|
||||||
{
|
|
||||||
NostrAuth::login($pubkey);
|
|
||||||
$this->currentPubkey = $pubkey;
|
|
||||||
$this->currentPleb = EinundzwanzigPleb::query()->where('pubkey', $pubkey)->first();
|
|
||||||
$this->isAllowed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleNostrLoggedOut(): void
|
|
||||||
{
|
|
||||||
$this->isAllowed = false;
|
|
||||||
$this->currentPubkey = null;
|
|
||||||
$this->currentPleb = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function confirmDeleteProject($id): void
|
public function confirmDeleteProject($id): void
|
||||||
{
|
{
|
||||||
$this->projectToDelete = ProjectProposal::query()->findOrFail($id);
|
$this->projectToDelete = ProjectProposal::query()->findOrFail($id);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ new class extends Component {
|
|||||||
$this->projectProposal = \App\Models\ProjectProposal::query()->where('slug', $projectProposal)->firstOrFail();
|
$this->projectProposal = \App\Models\ProjectProposal::query()->where('slug', $projectProposal)->firstOrFail();
|
||||||
if (NostrAuth::check()) {
|
if (NostrAuth::check()) {
|
||||||
$this->currentPubkey = NostrAuth::pubkey();
|
$this->currentPubkey = NostrAuth::pubkey();
|
||||||
$this->handleNostrLoggedIn($this->currentPubkey);
|
|
||||||
$this->isAllowed = true;
|
$this->isAllowed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,17 +44,6 @@ new class extends Component {
|
|||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleNostrLoggedIn($pubkey): void
|
|
||||||
{
|
|
||||||
$this->currentPubkey = $pubkey;
|
|
||||||
$this->currentPleb = \App\Models\EinundzwanzigPleb::query()->where('pubkey', $pubkey)->first();
|
|
||||||
$this->isAllowed = true;
|
|
||||||
$this->ownVoteExists = Vote::query()
|
|
||||||
->where('project_proposal_id', $this->projectProposal->id)
|
|
||||||
->where('einundzwanzig_pleb_id', $this->currentPleb->id)
|
|
||||||
->exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleApprove(): void
|
public function handleApprove(): void
|
||||||
{
|
{
|
||||||
Vote::query()->updateOrCreate([
|
Vote::query()->updateOrCreate([
|
||||||
@@ -95,7 +83,9 @@ new class extends Component {
|
|||||||
<h1 class="text-2xl md:text-3xl text-gray-800 dark:text-gray-100 font-bold mb-2">
|
<h1 class="text-2xl md:text-3xl text-gray-800 dark:text-gray-100 font-bold mb-2">
|
||||||
{{ $projectProposal->name }}
|
{{ $projectProposal->name }}
|
||||||
</h1>
|
</h1>
|
||||||
|
<x-markdown>
|
||||||
{!! $projectProposal->description !!}
|
{!! $projectProposal->description !!}
|
||||||
|
</x-markdown>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="space-y-3 sm:flex sm:items-center sm:justify-between sm:space-y-0 mb-6">
|
<div class="space-y-3 sm:flex sm:items-center sm:justify-between sm:space-y-0 mb-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user