From c6b359334188382821c1a6b841063614c1c7bbea Mon Sep 17 00:00:00 2001 From: fsociety Date: Wed, 23 Oct 2024 18:10:14 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20feat(project-support):=20add=20p?= =?UTF-8?q?roject=20proposal=20form=20and=20listing=20pages=20with=20image?= =?UTF-8?q?=20uploads=20and=20voting=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Livewire/Forms/ProjectProposalForm.php | 18 +++ app/Models/ProjectProposal.php | 79 ++++++++++ app/Models/Vote.php | 39 +++++ composer.lock | 16 +- ..._190300_create_project_proposals_table.php | 36 +++++ .../2023_03_10_190301_create_votes_table.php | 34 +++++ .../2024_10_23_143503_create_media_table.php | 32 ++++ ...epted_field_to_project_proposals_table.php | 28 ++++ public/site.webmanifest | 1 + resources/js/app.js | 2 + resources/js/nostrDefault.js | 5 + .../views/components/input/filepond.blade.php | 30 ++++ .../views/components/input/group.blade.php | 13 ++ .../components/input/simple-mde.blade.php | 65 ++++++++ .../views/components/layouts/app.blade.php | 2 + .../layouts/navigation/association.blade.php | 8 + .../election/[Election:year].blade.php | 15 +- .../form/[ProjectProposal:slug].blade.php | 29 ++++ .../project-support/form/create.blade.php | 116 ++++++++++++++ .../project-support/index.blade.php | 141 ++++++++++++++++++ resources/views/pages/index.blade.php | 5 +- 21 files changed, 693 insertions(+), 21 deletions(-) create mode 100644 app/Livewire/Forms/ProjectProposalForm.php create mode 100644 app/Models/ProjectProposal.php create mode 100644 app/Models/Vote.php create mode 100644 database/migrations/2023_03_10_190300_create_project_proposals_table.php create mode 100644 database/migrations/2023_03_10_190301_create_votes_table.php create mode 100644 database/migrations/2024_10_23_143503_create_media_table.php create mode 100644 database/migrations/2024_10_23_160756_add_accepted_field_to_project_proposals_table.php create mode 100644 public/site.webmanifest create mode 100644 resources/js/nostrDefault.js create mode 100644 resources/views/components/input/filepond.blade.php create mode 100644 resources/views/components/input/group.blade.php create mode 100644 resources/views/components/input/simple-mde.blade.php create mode 100644 resources/views/pages/association/project-support/form/[ProjectProposal:slug].blade.php create mode 100644 resources/views/pages/association/project-support/form/create.blade.php create mode 100644 resources/views/pages/association/project-support/index.blade.php diff --git a/app/Livewire/Forms/ProjectProposalForm.php b/app/Livewire/Forms/ProjectProposalForm.php new file mode 100644 index 0000000..c43341f --- /dev/null +++ b/app/Livewire/Forms/ProjectProposalForm.php @@ -0,0 +1,18 @@ + 'integer', + 'einundzwanzig_pleb_id' => 'integer', + ]; + + protected static function booted() + { + + } + + public function getSlugOptions(): SlugOptions + { + return SlugOptions::create() + ->generateSlugsFrom(['name']) + ->saveSlugsTo('slug') + ->usingLanguage(Cookie::get('lang', config('app.locale'))); + } + + public function registerMediaConversions(Media $media = null): void + { + $this + ->addMediaConversion('preview') + ->fit(Fit::Crop, 300, 300) + ->nonQueued(); + $this + ->addMediaConversion('thumb') + ->fit(Fit::Crop, 130, 130) + ->width(130) + ->height(130); + } + + public function registerMediaCollections(): void + { + $this + ->addMediaCollection('main') + ->singleFile() + ->useFallbackUrl(asset('img/einundzwanzig.png')); + } + + public function einundzwanzigPleb(): BelongsTo + { + return $this->belongsTo(EinundzwanzigPleb::class); + } + + public function votes(): HasMany + { + return $this->hasMany(Vote::class); + } +} diff --git a/app/Models/Vote.php b/app/Models/Vote.php new file mode 100644 index 0000000..6daa12f --- /dev/null +++ b/app/Models/Vote.php @@ -0,0 +1,39 @@ + 'integer', + 'einundzwanzig_pleb_id' => 'integer', + 'project_proposal_id' => 'integer', + 'value' => 'bool', + ]; + + public function einundzwanzigPleb(): BelongsTo + { + return $this->belongsTo(EinundzwanzigPleb::class); + } + + public function projectProposal(): BelongsTo + { + return $this->belongsTo(ProjectProposal::class); + } +} diff --git a/composer.lock b/composer.lock index 8d99986..a87d0be 100644 --- a/composer.lock +++ b/composer.lock @@ -6413,16 +6413,16 @@ }, { "name": "spatie/laravel-medialibrary", - "version": "11.9.1", + "version": "11.9.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-medialibrary.git", - "reference": "ff589ea5532a33d84faeb64bfdfd59057b4148b8" + "reference": "6a39eca52236bc1e1261f366d4022996521ae843" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/ff589ea5532a33d84faeb64bfdfd59057b4148b8", - "reference": "ff589ea5532a33d84faeb64bfdfd59057b4148b8", + "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/6a39eca52236bc1e1261f366d4022996521ae843", + "reference": "6a39eca52236bc1e1261f366d4022996521ae843", "shasum": "" }, "require": { @@ -6506,7 +6506,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-medialibrary/issues", - "source": "https://github.com/spatie/laravel-medialibrary/tree/11.9.1" + "source": "https://github.com/spatie/laravel-medialibrary/tree/11.9.2" }, "funding": [ { @@ -6518,7 +6518,7 @@ "type": "github" } ], - "time": "2024-09-02T06:32:15+00:00" + "time": "2024-10-18T14:24:58+00:00" }, { "name": "spatie/laravel-package-tools", @@ -12769,12 +12769,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/database/migrations/2023_03_10_190300_create_project_proposals_table.php b/database/migrations/2023_03_10_190300_create_project_proposals_table.php new file mode 100644 index 0000000..a84dff3 --- /dev/null +++ b/database/migrations/2023_03_10_190300_create_project_proposals_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('einundzwanzig_pleb_id')->constrained()->cascadeOnDelete()->cascadeOnUpdate(); + $table->string('slug')->unique(); + $table->string('name')->unique(); + $table->unsignedBigInteger('support_in_sats'); + $table->text('description'); + $table->timestamps(); + }); + + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('project_proposals'); + } +}; diff --git a/database/migrations/2023_03_10_190301_create_votes_table.php b/database/migrations/2023_03_10_190301_create_votes_table.php new file mode 100644 index 0000000..39fc172 --- /dev/null +++ b/database/migrations/2023_03_10_190301_create_votes_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('einundzwanzig_pleb_id')->constrained()->cascadeOnDelete()->cascadeOnUpdate(); + $table->foreignId('project_proposal_id')->constrained()->cascadeOnDelete()->cascadeOnUpdate(); + $table->unsignedInteger('value'); + $table->text('reason')->nullable(); + $table->timestamps(); + }); + + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('votes'); + } +}; diff --git a/database/migrations/2024_10_23_143503_create_media_table.php b/database/migrations/2024_10_23_143503_create_media_table.php new file mode 100644 index 0000000..47a4be9 --- /dev/null +++ b/database/migrations/2024_10_23_143503_create_media_table.php @@ -0,0 +1,32 @@ +id(); + + $table->morphs('model'); + $table->uuid()->nullable()->unique(); + $table->string('collection_name'); + $table->string('name'); + $table->string('file_name'); + $table->string('mime_type')->nullable(); + $table->string('disk'); + $table->string('conversions_disk')->nullable(); + $table->unsignedBigInteger('size'); + $table->json('manipulations'); + $table->json('custom_properties'); + $table->json('generated_conversions'); + $table->json('responsive_images'); + $table->unsignedInteger('order_column')->nullable()->index(); + + $table->nullableTimestamps(); + }); + } +}; diff --git a/database/migrations/2024_10_23_160756_add_accepted_field_to_project_proposals_table.php b/database/migrations/2024_10_23_160756_add_accepted_field_to_project_proposals_table.php new file mode 100644 index 0000000..9974037 --- /dev/null +++ b/database/migrations/2024_10_23_160756_add_accepted_field_to_project_proposals_table.php @@ -0,0 +1,28 @@ +boolean('accepted')->default(false); + $table->unsignedInteger('sats_paid')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('project_proposals', function (Blueprint $table) { + // + }); + } +}; diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/resources/js/app.js b/resources/js/app.js index 03e84c6..1676168 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,5 +1,6 @@ import {Alpine, Livewire} from '../../vendor/livewire/livewire/dist/livewire.esm'; +import nostrDefault from "./nostrDefault.js"; import nostrApp from "./nostrApp.js"; import nostrLogin from "./nostrLogin.js"; import nostrZap from "./nostrZap.js"; @@ -18,6 +19,7 @@ Alpine.store('nostr', { user: null, }); +Alpine.data('nostrDefault', nostrDefault); Alpine.data('nostrApp', nostrApp); Alpine.data('nostrLogin', nostrLogin); Alpine.data('nostrZap', nostrZap); diff --git a/resources/js/nostrDefault.js b/resources/js/nostrDefault.js new file mode 100644 index 0000000..ade8389 --- /dev/null +++ b/resources/js/nostrDefault.js @@ -0,0 +1,5 @@ +export default (livewireComponent) => ({ + + isAllowed: livewireComponent.entangle('isAllowed', true), + +}); diff --git a/resources/views/components/input/filepond.blade.php b/resources/views/components/input/filepond.blade.php new file mode 100644 index 0000000..39e3bbb --- /dev/null +++ b/resources/views/components/input/filepond.blade.php @@ -0,0 +1,30 @@ +
in Ordner suchen ' }}', + allowMultiple: {{ isset($attributes['multiple']) ? 'true' : 'false' }}, + server: { + process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => { + @this.upload('{{ $attributes['wire:model'] }}', file, load, error, progress) + }, + revert: (filename, load) => { + @this.removeUpload('{{ $attributes['wire:model'] }}', filename, load) + }, + load: (source, load, error, progress, abort, headers) => { + @this.load('{{ $attributes['wire:model'] }}', load, error, progress, abort, headers) + }, + }, + }); + FilePond.create($refs.input); + " +> + +
diff --git a/resources/views/components/input/group.blade.php b/resources/views/components/input/group.blade.php new file mode 100644 index 0000000..fd9d40e --- /dev/null +++ b/resources/views/components/input/group.blade.php @@ -0,0 +1,13 @@ +@props([ + 'for', + 'label', +]) +
+ +
+ {{ $slot }} +
+
diff --git a/resources/views/components/input/simple-mde.blade.php b/resources/views/components/input/simple-mde.blade.php new file mode 100644 index 0000000..7a26046 --- /dev/null +++ b/resources/views/components/input/simple-mde.blade.php @@ -0,0 +1,65 @@ +@props(['model']) + +
+
+ +
+ +
diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php index f44e0b1..05a8874 100644 --- a/resources/views/components/layouts/app.blade.php +++ b/resources/views/components/layouts/app.blade.php @@ -11,6 +11,8 @@ @vite(['resources/js/app.js','resources/css/app.css']) @googlefonts + + +
  • + +
    + + Projekt-Unterstützungen +
    +
    +
  • diff --git a/resources/views/pages/association/election/[Election:year].blade.php b/resources/views/pages/association/election/[Election:year].blade.php index c7eed17..b9b27f9 100644 --- a/resources/views/pages/association/election/[Election:year].blade.php +++ b/resources/views/pages/association/election/[Election:year].blade.php @@ -47,14 +47,6 @@ mount(function () { } }); -on([ - 'nostrLoggedOut' => function () { - $this->isAllowed = false; - $this->currentPubkey = null; - $this->currentPleb = null; - }, -]); - on([ 'nostrLoggedIn' => function ($pubkey) { $this->currentPubkey = $pubkey; @@ -74,7 +66,12 @@ on([ 'echo:votes,.newVote' => function () { $this->loadEvents(); $this->loadBoardEvents(); - } + }, + 'nostrLoggedOut' => function () { + $this->isAllowed = false; + $this->currentPubkey = null; + $this->currentPleb = null; +}, ]); updated([ diff --git a/resources/views/pages/association/project-support/form/[ProjectProposal:slug].blade.php b/resources/views/pages/association/project-support/form/[ProjectProposal:slug].blade.php new file mode 100644 index 0000000..32840a8 --- /dev/null +++ b/resources/views/pages/association/project-support/form/[ProjectProposal:slug].blade.php @@ -0,0 +1,29 @@ + fn() => $projectProposal, +]); + +?> + + + @volt +
    + @dd($projectProposal) +
    + @endvolt +
    diff --git a/resources/views/pages/association/project-support/form/create.blade.php b/resources/views/pages/association/project-support/form/create.blade.php new file mode 100644 index 0000000..4d1faef --- /dev/null +++ b/resources/views/pages/association/project-support/form/create.blade.php @@ -0,0 +1,116 @@ + false, + 'currentPubkey' => null, + 'currentPleb' => null, +]); + +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 berechtigt.")'); + } + $this->isAllowed = true; + }, + 'nostrLoggedOut' => function () { + $this->isAllowed = false; + $this->currentPubkey = null; + $this->currentPleb = null; + }, +]); + +$save = function () { + $this->form->validate(); + + \App\Models\ProjectProposal::query()->create([ + ...$this->form->all(), + 'einundzwanzig_pleb_id' => $this->currentPleb->id, + ]); + + return redirect()->route('association.projectSupport'); +}; + +?> + + + @volt +
    +
    +
    +
    + + +
    + @if ($image && str($image->getMimeType())->contains(['image/jpeg','image/jpg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp'])) +
    {{ __('Preview') }}:
    + + @endif + @if (isset($projectProposal) && $projectProposal->getFirstMediaUrl('main')) +
    {{ __('Current picture') }}:
    + + @endif +
    + + @error('image') {{ $message }} @enderror +
    + + + + + + + + + + + +
    + {{ __('Beschreibung') }} +
    +
    {{ __('Bitte verfasse einen ausführlichen und verständlichen Antragstext, damit die Abstimmung über eine mögliche Förderung erfolgen kann.') }}
    +
    +
    {{ __('Für Bilder in Markdown verwende bitte z.B. Imgur oder einen anderen Anbieter.') }}
    + + @error('form.description') {{ $message }} @enderror +
    + + + + + {{ __('Save') }} + + +
    +
    +
    +
    + @endvolt +
    diff --git a/resources/views/pages/association/project-support/index.blade.php b/resources/views/pages/association/project-support/index.blade.php new file mode 100644 index 0000000..97a894c --- /dev/null +++ b/resources/views/pages/association/project-support/index.blade.php @@ -0,0 +1,141 @@ + '', + 'projects' => fn() + => \App\Models\ProjectProposal::query() + ->with([ + 'einundzwanzigPleb.profile', + 'votes', + ]) + ->get(), +]); + +?> + + + @volt +
    + + +
    + + +
    +

    + Einundzwanzig Projektunterstützungen +

    +
    + + +
    + + +
    + + + + + +
    + +
    + + + {{--
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    --}} +
    {{ count($projects) }} Projekte
    + + +
    + + @foreach($projects as $project) +
    + + + Meetup 01 + + + +
    +
    +
    + Eingereicht von: {{ $project->einundzwanzigPleb->profile->name }} +
    + +

    + {{ $project->name }} +

    +
    +
    + {!! strip_tags($project->description) !!} +
    +
    + +
    + +
    + {{ number_format($project->support_in_sats, 0, ',', '.') }} Sats +
    + + @if($project->votes->count() > 0) +
    +
    + Anzahl der Unterstützer: +{{ $project->votes->count() }} +
    +
    + @endif +
    +
    +
    + @endforeach + +
    + +
    + @endvolt + diff --git a/resources/views/pages/index.blade.php b/resources/views/pages/index.blade.php index 0e8042d..e33eaae 100644 --- a/resources/views/pages/index.blade.php +++ b/resources/views/pages/index.blade.php @@ -8,12 +8,9 @@ use swentel\nostr\Relay\Relay; use swentel\nostr\Request\Request; use swentel\nostr\Subscription\Subscription; -use function Livewire\Volt\computed; -use function Livewire\Volt\mount; -use function Livewire\Volt\state; use function Laravel\Folio\{middleware}; use function Laravel\Folio\name; -use function Livewire\Volt\{on}; +use function Livewire\Volt\{state, mount, on, computed}; name('welcome');