mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2025-12-13 05:26:47 +00:00
🚀 feat(project-support): add project proposal form and listing pages with image uploads and voting functionality
This commit is contained in:
18
app/Livewire/Forms/ProjectProposalForm.php
Normal file
18
app/Livewire/Forms/ProjectProposalForm.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Form;
|
||||
|
||||
class ProjectProposalForm extends Form
|
||||
{
|
||||
#[Validate('required|min:5')]
|
||||
public $name = '';
|
||||
|
||||
#[Validate('required|numeric|min:21')]
|
||||
public $support_in_sats = '';
|
||||
|
||||
#[Validate('required|string|min:5')]
|
||||
public $description = '';
|
||||
}
|
||||
79
app/Models/ProjectProposal.php
Normal file
79
app/Models/ProjectProposal.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Facades\Cookie;
|
||||
use Spatie\Image\Enums\Fit;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
use Spatie\Sluggable\HasSlug;
|
||||
use Spatie\Sluggable\SlugOptions;
|
||||
|
||||
class ProjectProposal extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia;
|
||||
use HasSlug;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'id' => '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);
|
||||
}
|
||||
}
|
||||
39
app/Models/Vote.php
Normal file
39
app/Models/Vote.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Vote extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'id' => '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);
|
||||
}
|
||||
}
|
||||
16
composer.lock
generated
16
composer.lock
generated
@@ -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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::disableForeignKeyConstraints();
|
||||
|
||||
Schema::create('project_proposals', function (Blueprint $table) {
|
||||
$table->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');
|
||||
}
|
||||
};
|
||||
34
database/migrations/2023_03_10_190301_create_votes_table.php
Normal file
34
database/migrations/2023_03_10_190301_create_votes_table.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::disableForeignKeyConstraints();
|
||||
|
||||
Schema::create('votes', function (Blueprint $table) {
|
||||
$table->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');
|
||||
}
|
||||
};
|
||||
32
database/migrations/2024_10_23_143503_create_media_table.php
Normal file
32
database/migrations/2024_10_23_143503_create_media_table.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('media', function (Blueprint $table) {
|
||||
$table->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();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('project_proposals', function (Blueprint $table) {
|
||||
$table->boolean('accepted')->default(false);
|
||||
$table->unsignedInteger('sats_paid')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('project_proposals', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
1
public/site.webmanifest
Normal file
1
public/site.webmanifest
Normal file
@@ -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"}
|
||||
@@ -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);
|
||||
|
||||
5
resources/js/nostrDefault.js
Normal file
5
resources/js/nostrDefault.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default (livewireComponent) => ({
|
||||
|
||||
isAllowed: livewireComponent.entangle('isAllowed', true),
|
||||
|
||||
});
|
||||
30
resources/views/components/input/filepond.blade.php
Normal file
30
resources/views/components/input/filepond.blade.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<div
|
||||
wire:ignore
|
||||
x-data
|
||||
x-init="
|
||||
FilePond.registerPlugin(
|
||||
FilePondPluginImagePreview,
|
||||
FilePondPluginImageExifOrientation,
|
||||
FilePondPluginFileValidateSize,
|
||||
FilePondPluginImageEdit
|
||||
);
|
||||
FilePond.setOptions({
|
||||
labelIdle: '{{ 'Drag & Drop Deiner Dateien oder <span class="filepond--label-action"> in Ordner suchen </span>' }}',
|
||||
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);
|
||||
"
|
||||
>
|
||||
<input type="file" x-ref="input" name="{{ $attributes['name'] }}">
|
||||
</div>
|
||||
13
resources/views/components/input/group.blade.php
Normal file
13
resources/views/components/input/group.blade.php
Normal file
@@ -0,0 +1,13 @@
|
||||
@props([
|
||||
'for',
|
||||
'label',
|
||||
])
|
||||
<div class="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-700 sm:pt-5">
|
||||
<label for="{{ $for }}"
|
||||
class="block text-sm font-medium text-gray-100 sm:mt-px sm:pt-2">
|
||||
{{ $label }}
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
65
resources/views/components/input/simple-mde.blade.php
Normal file
65
resources/views/components/input/simple-mde.blade.php
Normal file
@@ -0,0 +1,65 @@
|
||||
@props(['model'])
|
||||
|
||||
<div
|
||||
wire:ignore
|
||||
x-data="{
|
||||
value: $wire.entangle('{{ $model }}'),
|
||||
init() {
|
||||
let editor = new EasyMDE({
|
||||
element: this.$refs.editor,
|
||||
lineNumbers: true,
|
||||
uploadImage: false,
|
||||
spellChecker: false,
|
||||
{{-- imageMaxSize: 1024 * 1024 * 10,--}}
|
||||
{{-- imageUploadFunction: (file, onSuccess, onError) => {--}}
|
||||
{{-- @this.upload('images', file, (uploadedFilename) => {--}}
|
||||
{{-- const currentImage = @this.get('currentImage');--}}
|
||||
{{-- const temporaryUrls = @this.get('temporaryUrls');--}}
|
||||
{{-- onSuccess(temporaryUrls[currentImage]);--}}
|
||||
{{-- @this.set('currentImage', currentImage + 1)--}}
|
||||
{{-- }, () => {--}}
|
||||
{{-- // Error callback.--}}
|
||||
{{-- }, (event) => {--}}
|
||||
{{-- // Progress callback.--}}
|
||||
{{-- // event.detail.progress contains a number between 1 and 100 as the upload progresses.--}}
|
||||
{{-- })--}}
|
||||
{{-- },--}}
|
||||
showIcons: [
|
||||
'heading',
|
||||
'heading-smaller',
|
||||
'heading-bigger',
|
||||
'heading-1',
|
||||
'heading-2',
|
||||
'heading-3',
|
||||
'code',
|
||||
'table',
|
||||
'quote',
|
||||
'strikethrough',
|
||||
'unordered-list',
|
||||
'ordered-list',
|
||||
'clean-block',
|
||||
'horizontal-rule',
|
||||
'undo',
|
||||
'redo',
|
||||
//'upload-image',
|
||||
],
|
||||
})
|
||||
|
||||
editor.value(this.value)
|
||||
|
||||
editor.codemirror.on('change', () => {
|
||||
this.value = editor.value()
|
||||
})
|
||||
},
|
||||
}"
|
||||
class="w-full"
|
||||
>
|
||||
<div class="prose max-w-none">
|
||||
<textarea x-ref="editor"></textarea>
|
||||
</div>
|
||||
<style>
|
||||
.EasyMDEContainer {
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
@@ -11,6 +11,8 @@
|
||||
@vite(['resources/js/app.js','resources/css/app.css'])
|
||||
@googlefonts
|
||||
<script src="https://kit.fontawesome.com/866fd3d0ab.js" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
|
||||
</head>
|
||||
<body
|
||||
class="font-sans antialiased bg-gray-100 dark:bg-[#222222] text-gray-600 dark:text-gray-400"
|
||||
|
||||
@@ -22,6 +22,14 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ $currentRoute === 'association.projectSupport' ? $isCurrentRouteClass : $isNotCurrentRouteClass }}">
|
||||
<a class="block text-gray-800 dark:text-gray-100 hover:text-gray-900 dark:hover:text-white truncate transition" href="{{ route('association.projectSupport') }}">
|
||||
<div class="flex items-center">
|
||||
<i class="fa-sharp-duotone fa-solid fa-hand-heart h-4 w-4"></i>
|
||||
<span class="text-sm font-medium ml-4 lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">Projekt-Unterstützungen</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ $currentRoute === 'association.elections' ? $isCurrentRouteClass : $isNotCurrentRouteClass }}">
|
||||
<a class="block text-gray-800 dark:text-gray-100 hover:text-gray-900 dark:hover:text-white truncate transition" href="{{ route('association.elections') }}">
|
||||
<div class="flex items-center">
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Volt\Component;
|
||||
use swentel\nostr\Filter\Filter;
|
||||
use swentel\nostr\Key\Key;
|
||||
use swentel\nostr\Message\RequestMessage;
|
||||
use swentel\nostr\Relay\Relay;
|
||||
use swentel\nostr\Request\Request;
|
||||
use swentel\nostr\Subscription\Subscription;
|
||||
|
||||
use function Laravel\Folio\{middleware};
|
||||
use function Laravel\Folio\name;
|
||||
use function Livewire\Volt\{state, mount, on, computed};
|
||||
|
||||
name('association.projectSupport.form');
|
||||
|
||||
state([
|
||||
'projectProposal' => fn() => $projectProposal,
|
||||
]);
|
||||
|
||||
?>
|
||||
|
||||
<x-layouts.app title="Welcome">
|
||||
@volt
|
||||
<div>
|
||||
@dd($projectProposal)
|
||||
</div>
|
||||
@endvolt
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Forms\ProjectProposalForm;
|
||||
use Livewire\Volt\Component;
|
||||
use swentel\nostr\Filter\Filter;
|
||||
use swentel\nostr\Key\Key;
|
||||
use swentel\nostr\Message\RequestMessage;
|
||||
use swentel\nostr\Relay\Relay;
|
||||
use swentel\nostr\Request\Request;
|
||||
use swentel\nostr\Subscription\Subscription;
|
||||
|
||||
use function Laravel\Folio\{middleware};
|
||||
use function Laravel\Folio\name;
|
||||
use function Livewire\Volt\{state, mount, on, computed, form, usesFileUploads};
|
||||
|
||||
name('association.projectSupport.create');
|
||||
|
||||
form(ProjectProposalForm::class);
|
||||
|
||||
state([
|
||||
'image',
|
||||
'isAllowed' => 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');
|
||||
};
|
||||
|
||||
?>
|
||||
|
||||
<x-layouts.app title="Welcome">
|
||||
@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" 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.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 primary wire:click="save">
|
||||
<i class="fa fa-thin fa-save"></i>
|
||||
{{ __('Save') }}
|
||||
</x-button>
|
||||
</x-input.group>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endvolt
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Volt\Component;
|
||||
use swentel\nostr\Filter\Filter;
|
||||
use swentel\nostr\Key\Key;
|
||||
use swentel\nostr\Message\RequestMessage;
|
||||
use swentel\nostr\Relay\Relay;
|
||||
use swentel\nostr\Request\Request;
|
||||
use swentel\nostr\Subscription\Subscription;
|
||||
|
||||
use function Laravel\Folio\{middleware};
|
||||
use function Laravel\Folio\name;
|
||||
use function Livewire\Volt\{state, mount, on, computed};
|
||||
|
||||
name('association.projectSupport');
|
||||
|
||||
state([
|
||||
'search' => '',
|
||||
'projects' => fn()
|
||||
=> \App\Models\ProjectProposal::query()
|
||||
->with([
|
||||
'einundzwanzigPleb.profile',
|
||||
'votes',
|
||||
])
|
||||
->get(),
|
||||
]);
|
||||
|
||||
?>
|
||||
|
||||
<x-layouts.app title="Projekt Unterstützungen">
|
||||
@volt
|
||||
<div class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
|
||||
|
||||
<!-- Page header -->
|
||||
<div class="sm:flex sm:justify-between sm:items-center mb-5">
|
||||
|
||||
<!-- Left: Title -->
|
||||
<div class="mb-4 sm:mb-0">
|
||||
<h1 class="text-2xl md:text-3xl text-gray-800 dark:text-gray-100 font-bold">
|
||||
Einundzwanzig Projektunterstützungen
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- Right: Actions -->
|
||||
<div class="grid grid-flow-col sm:auto-cols-max justify-start sm:justify-end gap-2">
|
||||
|
||||
<!-- Search form -->
|
||||
<form class="relative">
|
||||
<x-input type="search" wire:model.live.debounce="search"
|
||||
placeholder="Suche…"/>
|
||||
</form>
|
||||
|
||||
<!-- Add meetup button -->
|
||||
<x-button :href="route('association.projectSupport.create')" icon="plus"
|
||||
label="Projekt für Unterstützung einreichen"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
{{--<div class="mb-5">
|
||||
<ul class="flex flex-wrap -m-1">
|
||||
<li class="m-1">
|
||||
<button class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-transparent shadow-sm bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-800 transition">View All</button>
|
||||
</li>
|
||||
<li class="m-1">
|
||||
<button class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 shadow-sm bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 transition">Online</button>
|
||||
</li>
|
||||
<li class="m-1">
|
||||
<button class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 shadow-sm bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 transition">Local</button>
|
||||
</li>
|
||||
<li class="m-1">
|
||||
<button class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 shadow-sm bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 transition">This Week</button>
|
||||
</li>
|
||||
<li class="m-1">
|
||||
<button class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 shadow-sm bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 transition">This Month</button>
|
||||
</li>
|
||||
<li class="m-1">
|
||||
<button class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 shadow-sm bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 transition">Following</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>--}}
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400 italic mb-4">{{ count($projects) }} Projekte</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="grid xl:grid-cols-2 gap-6 mb-8">
|
||||
|
||||
@foreach($projects as $project)
|
||||
<article
|
||||
wire:key="project_{{ $project->id }}"
|
||||
class="flex bg-white dark:bg-gray-800 shadow-sm rounded-xl overflow-hidden">
|
||||
<!-- Image -->
|
||||
<a class="relative block w-24 sm:w-56 xl:sidebar-expanded:w-40 2xl:sidebar-expanded:w-56 shrink-0"
|
||||
href="meetups-post.html">
|
||||
<img class="absolute object-cover object-center w-full h-full"
|
||||
src="{{ asset('einundzwanzig-alpha.jpg') }}" width="220" height="236" alt="Meetup 01">
|
||||
<button class="absolute top-0 right-0 mt-4 mr-4">
|
||||
<img class="rounded-full h-8 w-8" src="{{ $project->einundzwanzigPleb->profile->picture }}"
|
||||
alt="">
|
||||
</button>
|
||||
</a>
|
||||
<!-- Content -->
|
||||
<div class="grow p-5 flex flex-col">
|
||||
<div class="grow">
|
||||
<div class="text-sm font-semibold text-amber-500 uppercase mb-2">
|
||||
Eingereicht von: {{ $project->einundzwanzigPleb->profile->name }}
|
||||
</div>
|
||||
<a class="inline-flex mb-2">
|
||||
<h3 class="text-lg font-bold text-gray-800 dark:text-gray-100">
|
||||
{{ $project->name }}
|
||||
</h3>
|
||||
</a>
|
||||
<div class="text-sm">
|
||||
{!! strip_tags($project->description) !!}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<div class="flex justify-between items-center mt-3">
|
||||
<!-- Tag -->
|
||||
<div
|
||||
class="text-xs inline-flex items-center font-bold border border-gray-200 dark:border-gray-700/60 text-gray-600 dark:text-gray-200 rounded-full text-center px-2.5 py-1">
|
||||
<span>{{ number_format($project->support_in_sats, 0, ',', '.') }} Sats</span>
|
||||
</div>
|
||||
<!-- Avatars -->
|
||||
@if($project->votes->count() > 0)
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="text-xs font-medium text-gray-400 dark:text-gray-300 italic">
|
||||
Anzahl der Unterstützer: +{{ $project->votes->count() }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endvolt
|
||||
</x-layouts.app>
|
||||
@@ -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');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user