mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2025-12-13 05:26:47 +00:00
🎨 feat(project): enhance project support form with image upload and validation features.
🗃️ refactor(project): rename project support route for clarity and consistency. 🗑️ chore(project): implement delete confirmation for project proposals in the index view. 🔧 fix(editor): adjust initialization delay for SimpleMDE editor to improve responsiveness. 📸 fix(media): update fallback image URL in ProjectProposal model for better asset management.
This commit is contained in:
@@ -64,7 +64,7 @@ class ProjectProposal extends Model implements HasMedia
|
|||||||
$this
|
$this
|
||||||
->addMediaCollection('main')
|
->addMediaCollection('main')
|
||||||
->singleFile()
|
->singleFile()
|
||||||
->useFallbackUrl(asset('img/einundzwanzig.png'));
|
->useFallbackUrl(asset('einundzwanzig-alpha.jpg'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function einundzwanzigPleb(): BelongsTo
|
public function einundzwanzigPleb(): BelongsTo
|
||||||
|
|||||||
@@ -1,55 +1,43 @@
|
|||||||
@props(['model'])
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
wire:ignore
|
wire:ignore
|
||||||
x-data="{
|
x-data="{
|
||||||
value: $wire.entangle('{{ $model }}'),
|
value: $wire.entangle('{{ $model }}'),
|
||||||
init() {
|
init() {
|
||||||
let editor = new EasyMDE({
|
this.$nextTick(() => {
|
||||||
element: this.$refs.editor,
|
setTimeout(() => {
|
||||||
lineNumbers: true,
|
let editor = new EasyMDE({
|
||||||
uploadImage: false,
|
element: this.$refs.editor,
|
||||||
spellChecker: false,
|
lineNumbers: true,
|
||||||
{{-- imageMaxSize: 1024 * 1024 * 10,--}}
|
uploadImage: false,
|
||||||
{{-- imageUploadFunction: (file, onSuccess, onError) => {--}}
|
spellChecker: false,
|
||||||
{{-- @this.upload('images', file, (uploadedFilename) => {--}}
|
showIcons: [
|
||||||
{{-- const currentImage = @this.get('currentImage');--}}
|
'heading',
|
||||||
{{-- const temporaryUrls = @this.get('temporaryUrls');--}}
|
'heading-smaller',
|
||||||
{{-- onSuccess(temporaryUrls[currentImage]);--}}
|
'heading-bigger',
|
||||||
{{-- @this.set('currentImage', currentImage + 1)--}}
|
'heading-1',
|
||||||
{{-- }, () => {--}}
|
'heading-2',
|
||||||
{{-- // Error callback.--}}
|
'heading-3',
|
||||||
{{-- }, (event) => {--}}
|
'code',
|
||||||
{{-- // Progress callback.--}}
|
'table',
|
||||||
{{-- // event.detail.progress contains a number between 1 and 100 as the upload progresses.--}}
|
'quote',
|
||||||
{{-- })--}}
|
'strikethrough',
|
||||||
{{-- },--}}
|
'unordered-list',
|
||||||
showIcons: [
|
'ordered-list',
|
||||||
'heading',
|
'clean-block',
|
||||||
'heading-smaller',
|
'horizontal-rule',
|
||||||
'heading-bigger',
|
'undo',
|
||||||
'heading-1',
|
'redo',
|
||||||
'heading-2',
|
//'upload-image',
|
||||||
'heading-3',
|
],
|
||||||
'code',
|
});
|
||||||
'table',
|
|
||||||
'quote',
|
|
||||||
'strikethrough',
|
|
||||||
'unordered-list',
|
|
||||||
'ordered-list',
|
|
||||||
'clean-block',
|
|
||||||
'horizontal-rule',
|
|
||||||
'undo',
|
|
||||||
'redo',
|
|
||||||
//'upload-image',
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.value(this.value)
|
editor.value(this.value);
|
||||||
|
|
||||||
editor.codemirror.on('change', () => {
|
editor.codemirror.on('change', () => {
|
||||||
this.value = editor.value()
|
this.value = editor.value();
|
||||||
})
|
});
|
||||||
|
}, 100); // Adjust the delay as needed
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Livewire\Forms\ProjectProposalForm;
|
||||||
use Livewire\Volt\Component;
|
use Livewire\Volt\Component;
|
||||||
use swentel\nostr\Filter\Filter;
|
use swentel\nostr\Filter\Filter;
|
||||||
use swentel\nostr\Key\Key;
|
use swentel\nostr\Key\Key;
|
||||||
@@ -10,20 +11,122 @@ use swentel\nostr\Subscription\Subscription;
|
|||||||
|
|
||||||
use function Laravel\Folio\{middleware};
|
use function Laravel\Folio\{middleware};
|
||||||
use function Laravel\Folio\name;
|
use function Laravel\Folio\name;
|
||||||
use function Livewire\Volt\{state, mount, on, computed};
|
use function Livewire\Volt\{state, mount, on, computed, form, usesFileUploads};
|
||||||
|
|
||||||
name('association.projectSupport.form');
|
name('association.projectSupport.edit');
|
||||||
|
|
||||||
|
form(ProjectProposalForm::class);
|
||||||
|
|
||||||
state([
|
state([
|
||||||
'projectProposal' => fn() => $projectProposal,
|
'projectProposal' => fn() => $projectProposal,
|
||||||
|
'image',
|
||||||
|
'isAllowed' => false,
|
||||||
|
'currentPubkey' => null,
|
||||||
|
'currentPleb' => null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
mount(function ($projectProposal) {
|
||||||
|
$this->form->fill($projectProposal->toArray());
|
||||||
|
$this->image = $projectProposal->getFirstMedia('main');
|
||||||
|
});
|
||||||
|
|
||||||
|
usesFileUploads();
|
||||||
|
|
||||||
|
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 $this->js('alert("Du bist hierzu nicht berechtigt.")');
|
||||||
|
}
|
||||||
|
$this->isAllowed = true;
|
||||||
|
},
|
||||||
|
'nostrLoggedOut' => function () {
|
||||||
|
$this->isAllowed = false;
|
||||||
|
$this->currentPubkey = null;
|
||||||
|
$this->currentPleb = null;
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$save = function () {
|
||||||
|
$this->form->validate();
|
||||||
|
|
||||||
|
$this->projectProposal->update([
|
||||||
|
...$this->form->except('id', 'slug'),
|
||||||
|
'einundzwanzig_pleb_id' => $this->currentPleb->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->route('association.projectSupport');
|
||||||
|
};
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<x-layouts.app title="Welcome">
|
<x-layouts.app title="{{ $projectProposal->name }}">
|
||||||
@volt
|
@volt
|
||||||
<div>
|
<div x-cloak x-show="isAllowed" class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto"
|
||||||
@dd($projectProposal)
|
x-data="nostrDefault(@this)">
|
||||||
|
<form class="space-y-8 divide-y divide-gray-700 pb-24">
|
||||||
|
<div class="space-y-8 divide-y divide-gray-700 sm:space-y-5">
|
||||||
|
<div class="mt-6 sm:mt-5 space-y-6 sm:space-y-5">
|
||||||
|
|
||||||
|
<x-input.group :for=" md5('image')" :label="__('Bild')">
|
||||||
|
<div class="py-4">
|
||||||
|
@if ($image && str($image->getMimeType())->contains(['image/jpeg','image/jpg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp']))
|
||||||
|
<div class="text-gray-200">{{ __('Preview') }}:</div>
|
||||||
|
<img class="h-48 object-contain" src="{{ $image->temporaryUrl() }}">
|
||||||
|
@endif
|
||||||
|
@if (isset($projectProposal) && $projectProposal->getFirstMediaUrl('main'))
|
||||||
|
<div class="text-gray-200">{{ __('Current picture') }}:</div>
|
||||||
|
<img class="h-48 object-contain" src="{{ $projectProposal->getFirstMediaUrl('main') }}">
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<input class="text-gray-200" type="file" wire:model="image">
|
||||||
|
@error('image') <span class="text-red-500">{{ $message }}</span> @enderror
|
||||||
|
</x-input.group>
|
||||||
|
|
||||||
|
<x-input.group :for="md5('form.name')" :label="__('Name')">
|
||||||
|
<x-input autocomplete="off" wire:model.debounce="form.name"
|
||||||
|
:placeholder="__('Name')"/>
|
||||||
|
</x-input.group>
|
||||||
|
|
||||||
|
<x-input.group :for="md5('form.website')" :label="__('Webseite des Projekts')">
|
||||||
|
<x-input autocomplete="off" wire:model.debounce="form.website"
|
||||||
|
:placeholder="__('Website')"/>
|
||||||
|
</x-input.group>
|
||||||
|
|
||||||
|
<x-input.group :for="md5('form.name')" :label="__('Beabsichtigte Unterstützung in Sats')">
|
||||||
|
<x-input type="number" autocomplete="off" wire:model.debounce="form.support_in_sats"
|
||||||
|
:placeholder="__('Beabsichtigte Unterstützung in Sats')"/>
|
||||||
|
</x-input.group>
|
||||||
|
|
||||||
|
<x-input.group :for="md5('form.description')">
|
||||||
|
<x-slot name="label">
|
||||||
|
<div>
|
||||||
|
{{ __('Beschreibung') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-amber-500 text-xs py-2">{{ __('Bitte verfasse einen ausführlichen und verständlichen Antragstext, damit die Abstimmung über eine mögliche Förderung erfolgen kann.') }}</div>
|
||||||
|
</x-slot>
|
||||||
|
<div
|
||||||
|
class="text-amber-500 text-xs py-2">{{ __('Für Bilder in Markdown verwende bitte z.B. Imgur oder einen anderen Anbieter.') }}</div>
|
||||||
|
<x-input.simple-mde model="form.description"/>
|
||||||
|
@error('form.description') <span
|
||||||
|
class="text-red-500 py-2">{{ $message }}</span> @enderror
|
||||||
|
</x-input.group>
|
||||||
|
|
||||||
|
<x-input.group :for="md5('save')" label="">
|
||||||
|
<x-button secondary :href="route('association.projectSupport')">
|
||||||
|
<i class="fa fa-thin fa-arrow-left"></i>
|
||||||
|
{{ __('Zurück') }}
|
||||||
|
</x-button>
|
||||||
|
<x-button primary wire:click="save">
|
||||||
|
<i class="fa fa-thin fa-save"></i>
|
||||||
|
{{ __('Speichern') }}
|
||||||
|
</x-button>
|
||||||
|
</x-input.group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@endvolt
|
@endvolt
|
||||||
</x-layouts.app>
|
</x-layouts.app>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ $save = function () {
|
|||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<x-layouts.app title="Welcome">
|
<x-layouts.app title="Neuer Vorschlag für eine Unterstützung">
|
||||||
@volt
|
@volt
|
||||||
<div x-cloak x-show="isAllowed" class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto"
|
<div x-cloak x-show="isAllowed" class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto"
|
||||||
x-data="nostrDefault(@this)">
|
x-data="nostrDefault(@this)">
|
||||||
@@ -109,6 +109,10 @@ $save = function () {
|
|||||||
</x-input.group>
|
</x-input.group>
|
||||||
|
|
||||||
<x-input.group :for="md5('save')" label="">
|
<x-input.group :for="md5('save')" label="">
|
||||||
|
<x-button secondary :href="route('association.projectSupport')">
|
||||||
|
<i class="fa fa-thin fa-arrow-left"></i>
|
||||||
|
{{ __('Zurück') }}
|
||||||
|
</x-button>
|
||||||
<x-button primary wire:click="save">
|
<x-button primary wire:click="save">
|
||||||
<i class="fa fa-thin fa-save"></i>
|
<i class="fa fa-thin fa-save"></i>
|
||||||
{{ __('Save') }}
|
{{ __('Save') }}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use swentel\nostr\Message\RequestMessage;
|
|||||||
use swentel\nostr\Relay\Relay;
|
use swentel\nostr\Relay\Relay;
|
||||||
use swentel\nostr\Request\Request;
|
use swentel\nostr\Request\Request;
|
||||||
use swentel\nostr\Subscription\Subscription;
|
use swentel\nostr\Subscription\Subscription;
|
||||||
|
use WireUi\Actions\Notification;
|
||||||
|
|
||||||
use function Laravel\Folio\{middleware};
|
use function Laravel\Folio\{middleware};
|
||||||
use function Laravel\Folio\name;
|
use function Laravel\Folio\name;
|
||||||
@@ -44,11 +45,35 @@ on([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$confirmDelete = function ($id) {
|
||||||
|
$notification = new Notification($this);
|
||||||
|
$notification->confirm([
|
||||||
|
'title' => 'Projektunterstützung löschen',
|
||||||
|
'message' => 'Bist du sicher, dass du diese Projektunterstützung löschen möchtest?',
|
||||||
|
'accept' => [
|
||||||
|
'label' => 'Ja, löschen',
|
||||||
|
'method' => 'delete',
|
||||||
|
'params' => $id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
$delete = function ($id) {
|
||||||
|
\App\Models\ProjectProposal::query()->findOrFail($id)->delete();
|
||||||
|
$this->projects = \App\Models\ProjectProposal::query()
|
||||||
|
->with([
|
||||||
|
'einundzwanzigPleb.profile',
|
||||||
|
'votes',
|
||||||
|
])
|
||||||
|
->get();
|
||||||
|
};
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<x-layouts.app title="Projekt Unterstützungen">
|
<x-layouts.app title="Projekt Unterstützungen">
|
||||||
@volt
|
@volt
|
||||||
<div class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto" x-data="nostrDefault(@this)" x-cloak x-show="isAllowed">
|
<div class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto" x-data="nostrDefault(@this)" x-cloak
|
||||||
|
x-show="isAllowed">
|
||||||
|
|
||||||
<!-- Page header -->
|
<!-- Page header -->
|
||||||
<div class="sm:flex sm:justify-between sm:items-center mb-5">
|
<div class="sm:flex sm:justify-between sm:items-center mb-5">
|
||||||
@@ -149,6 +174,17 @@ on([
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex justify-between items-center mt-3">
|
||||||
|
@if($currentPleb && $currentPleb->id === $project->einundzwanzig_pleb_id)
|
||||||
|
<x-button
|
||||||
|
negative
|
||||||
|
wire:click="confirmDelete({{ $project->id }})"
|
||||||
|
label="Löschen"/>
|
||||||
|
<x-button
|
||||||
|
:href="route('association.projectSupport.edit', ['projectProposal' => $project])"
|
||||||
|
label="Editieren"/>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|||||||
Reference in New Issue
Block a user