voting added

This commit is contained in:
HolgerHatGarKeineNode
2023-03-10 23:03:19 +01:00
parent 4905134ea6
commit c2b7042eab
50 changed files with 1955 additions and 592 deletions
-30
View File
@@ -1,30 +0,0 @@
created:
- database/factories/BitcoinEventFactory.php
- database/migrations/2022_12_12_171115_create_bitcoin_events_table.php
- app/Models/BitcoinEvent.php
- app/Nova/BitcoinEvent.php
models:
BookCase: { title: string, latitude: 'float:10', longitude: 'float:10', address: 'text nullable', type: string, open: 'string nullable', comment: 'text nullable', contact: 'text nullable', bcz: 'text nullable', digital: boolean, icontype: string, deactivated: boolean, deactreason: string, entrytype: string, homepage: 'text nullable' }
Category: { name: string, slug: string }
City: { country_id: biginteger, name: string, slug: string, longitude: 'float:10', latitude: 'float:10' }
Country: { name: string, code: string, language_codes: 'json default:[]' }
Course: { lecturer_id: biginteger, name: string, description: 'text nullable' }
CourseEvent: { course_id: biginteger, venue_id: biginteger, '"from"': datetime, '"to"': datetime, link: string }
Episode: { guid: string, podcast_id: biginteger, data: json }
Lecturer: { team_id: biginteger, name: string, slug: string, active: 'boolean default:1', description: 'text nullable' }
Library: { name: string, is_public: 'boolean default:1', language_codes: 'json default:[]' }
LibraryItem: { lecturer_id: biginteger, episode_id: 'biginteger nullable', order_column: integer, name: string, type: string, language_code: string, value: 'text nullable' }
LoginKey: { k1: string, user_id: biginteger }
Meetup: { city_id: biginteger, name: string, link: string }
MeetupEvent: { meetup_id: biginteger, start: datetime, location: 'string nullable', description: 'text nullable', link: 'string nullable' }
Membership: { team_id: biginteger, user_id: biginteger, role: 'string nullable' }
OrangePill: { user_id: biginteger, book_case_id: biginteger, date: datetime, amount: integer }
Participant: { first_name: string, last_name: string }
Podcast: { guid: string, locked: 'boolean default:1', title: string, link: string, language_code: string, data: 'json nullable' }
Registration: { course_event_id: biginteger, participant_id: biginteger, active: 'boolean default:1' }
Tag: { name: json, slug: json, type: 'string nullable', order_column: 'integer nullable', icon: 'string default:tag' }
Team: { user_id: biginteger, name: string, personal_team: boolean }
TeamInvitation: { team_id: biginteger, email: string, role: 'string nullable' }
User: { name: string, public_key: 'string nullable', email: 'string nullable', email_verified_at: 'datetime nullable', password: 'string nullable', remember_token: 'string:100 nullable', current_team_id: 'biginteger nullable', profile_photo_path: 'string:2048 nullable', is_lecturer: 'boolean default:', two_factor_secret: 'text nullable', two_factor_recovery_codes: 'text nullable', two_factor_confirmed_at: 'datetime nullable', timezone: 'string default:Europe/Berlin' }
Venue: { city_id: biginteger, name: string, slug: string, street: string }
BitcoinEvent: { venue_id: foreign, from: datetime, to: datetime, title: string, description: text, link: string }
+2
View File
@@ -28,3 +28,5 @@ auth.json
yarn-error.log
.yarn/*
!.yarn/releases
.blueprint
@@ -54,6 +54,7 @@ class CreatePermissions extends Command
'OrangePillPolicy',
'ParticipantPolicy',
'PodcastPolicy',
'ProjectProposalPolicy',
'RegistrationPolicy',
'TeamPolicy',
'UserPolicy',
+68 -67
View File
@@ -82,74 +82,75 @@ class Header extends Component
Cookie::queue('lang', $this->l, 60 * 24 * 365);
return view('livewire.frontend.header', [
'news' => LibraryItem::query()
->with([
'createdBy.roles',
'lecturer',
'tags',
])
->where('type', 'markdown_article')
->where('approved', true)
->where('news', true)
->orderByDesc('created_at')
->take(2)
->get(),
'meetups' => MeetupEvent::query()
->with([
'meetup.users',
'meetup.city.country',
])
->where('start', '>', now())
->orderBy('start')
->take(2)
->get(),
'courseEvents' => CourseEvent::query()
->with([
'venue.city.country',
'course.lecturer',
])
->where('from', '>', now())
->orderBy('from')
->take(2)
->get(),
'libraryItems' => LibraryItem::query()
->with([
'lecturer',
])
->where('type', '<>', 'markdown_article')
->orderByDesc('created_at')
->take(2)
->get(),
'bitcoinEvents' => BitcoinEvent::query()
->with([
'venue',
])
->where('from', '>', now())
->orderBy('from')
->take(2)
->get(),
'orangePills' => OrangePill::query()
->with([
'user',
'bookCase',
])
->orderByDesc('date')
->take(2)
->get(),
'cities' => City::query()
->select(['latitude', 'longitude'])
->get(),
'countries' => Country::query()
->select('id', 'name', 'code')
->orderBy('name')
->get()
->map(function (Country $country) {
$country->name = config('countries.emoji_flags')[str($country->code)
->upper()
->toString()].' '.$country->name;
'news' => LibraryItem::query()
->with([
'createdBy.roles',
'lecturer',
'tags',
])
->where('type', 'markdown_article')
->where('approved', true)
->where('news', true)
->orderByDesc('created_at')
->take(2)
->get(),
'meetups' => MeetupEvent::query()
->with([
'meetup.users',
'meetup.city.country',
])
->where('start', '>', now())
->orderBy('start')
->take(2)
->get(),
'courseEvents' => CourseEvent::query()
->with([
'venue.city.country',
'course.lecturer',
])
->where('from', '>', now())
->orderBy('from')
->take(2)
->get(),
'libraryItems' => LibraryItem::query()
->with([
'lecturer',
])
->where('type', '<>', 'markdown_article')
->orderByDesc('created_at')
->take(2)
->get(),
'bitcoinEvents' => BitcoinEvent::query()
->with([
'venue',
])
->where('from', '>', now())
->orderBy('from')
->take(2)
->get(),
'orangePills' => OrangePill::query()
->with([
'user',
'bookCase',
])
->orderByDesc('date')
->take(2)
->get(),
'projectProposals' => [],
'cities' => City::query()
->select(['latitude', 'longitude'])
->get(),
'countries' => Country::query()
->select('id', 'name', 'code')
->orderBy('name')
->get()
->map(function (Country $country) {
$country->name = config('countries.emoji_flags')[str($country->code)
->upper()
->toString()].' '.$country->name;
return $country;
}),
return $country;
}),
]);
}
}
@@ -0,0 +1,67 @@
<?php
namespace App\Http\Livewire\ProjectProposal\Form;
use App\Models\Country;
use App\Models\ProjectProposal;
use Livewire\Component;
use Livewire\WithFileUploads;
class ProjectProposalForm extends Component
{
use WithFileUploads;
public Country $country;
public ?ProjectProposal $projectProposal = null;
public $image;
public ?string $fromUrl = '';
protected $queryString = [
'fromUrl' => ['except' => ''],
];
public function rules()
{
return [
'image' => [
'nullable', 'mimes:jpeg,png,jpg,gif', 'max:10240'
],
'projectProposal.user_id' => 'required',
'projectProposal.name' => 'required',
'projectProposal.support_in_sats' => 'required|numeric',
'projectProposal.description' => 'required',
];
}
public function mount()
{
if (!$this->projectProposal) {
$this->projectProposal = new ProjectProposal([
'user_id' => auth()->id(),
'description' => '',
]);
}
if (!$this->fromUrl) {
$this->fromUrl = url()->previous();
}
}
public function save()
{
$this->validate();
$this->projectProposal->save();
return redirect($this->fromUrl);
}
public function render()
{
return view('livewire.project-proposal.form.project-proposal-form', [
]);
}
}
@@ -0,0 +1,16 @@
<?php
namespace App\Http\Livewire\ProjectProposal;
use App\Models\Country;
use Livewire\Component;
class ProjectProposalTable extends Component
{
public Country $country;
public function render()
{
return view('livewire.project-proposal.project-proposal-table');
}
}
@@ -0,0 +1,107 @@
<?php
namespace App\Http\Livewire\ProjectProposal;
use App\Models\Country;
use App\Models\ProjectProposal;
use App\Models\User;
use App\Models\Vote;
use Illuminate\Validation\Rule;
use Livewire\Component;
class ProjectProposalVoting extends Component
{
public Country $country;
public ?ProjectProposal $projectProposal = null;
public ?Vote $vote = null;
public ?string $fromUrl = '';
protected $queryString = [
'fromUrl' => ['except' => ''],
];
public function rules()
{
return [
'vote.user_id' => 'required',
'vote.project_proposal_id' => 'required',
'vote.value' => 'required|boolean',
'vote.reason' => [
Rule::requiredIf(!$this->vote->value),
]
];
}
public function mount()
{
$vote = Vote::query()
->where('user_id', auth()->id())
->where('project_proposal_id', $this->projectProposal->id)
->first();
if ($vote) {
$this->vote = $vote;
} else {
$this->vote = new Vote();
$this->vote->user_id = auth()->id();
$this->vote->project_proposal_id = $this->projectProposal->id;
$this->vote->value = false;
}
}
public function yes()
{
$this->vote->value = true;
$this->vote->save();
return to_route('project.voting.projectFunding',
['country' => $this->country, 'projectProposal' => $this->projectProposal, 'fromUrl' => $this->fromUrl]);
}
public function no()
{
$this->validate();
$this->vote->value = false;
$this->vote->save();
return to_route('project.voting.projectFunding',
['country' => $this->country, 'projectProposal' => $this->projectProposal, 'fromUrl' => $this->fromUrl]);
}
public function render()
{
return view('livewire.project-proposal.project-proposal-voting', [
'entitledVoters' => User::query()
->with([
'votes' => fn($query) => $query->where('project_proposal_id',
$this->projectProposal->id)
])
->withCount([
'votes' => fn($query) => $query->where('project_proposal_id',
$this->projectProposal->id)
])
->whereHas('roles', function ($query) {
return $query->where('roles.name', 'entitled-voter');
})
->orderByDesc('votes_count')
->get(),
'otherVoters' => User::query()
->with([
'votes' => fn($query) => $query->where('project_proposal_id',
$this->projectProposal->id)
])
->withCount([
'votes' => fn($query) => $query->where('project_proposal_id',
$this->projectProposal->id)
])
->whereDoesntHave('roles', function ($query) {
return $query->where('roles.name', 'entitled-voter');
})
->orderByDesc('votes_count')
->get(),
]);
}
}
@@ -0,0 +1,66 @@
<?php
namespace App\Http\Livewire\Tables;
use Illuminate\Database\Eloquent\Builder;
use Rappasoft\LaravelLivewireTables\DataTableComponent;
use Rappasoft\LaravelLivewireTables\Views\Column;
use App\Models\ProjectProposal;
class ProjectProposalTable extends DataTableComponent
{
public ?string $country = null;
public string $tableName = 'project_proposals';
public function configure(): void
{
$this->setPrimaryKey('id')
->setAdditionalSelects(['id', 'created_by'])
->setThAttributes(function (Column $column) {
return [
'class' => 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider dark:bg-gray-800 dark:text-gray-400',
'default' => false,
];
})
->setTdAttributes(function (Column $column, $row, $columnIndex, $rowIndex) {
return [
'class' => 'px-6 py-4 text-sm font-medium dark:text-white',
'default' => false,
];
})
->setColumnSelectStatus(false)
->setPerPage(10)
->setConfigurableAreas([
'toolbar-left-end' => [
'columns.project_proposals.areas.toolbar-left-end', [
'country' => $this->country,
],
],
]);
}
public function columns(): array
{
return [
Column::make("Id", "id")
->sortable(),
Column::make("Name", "name")
->sortable(),
Column::make(__('Intended support in sats'), "support_in_sats")
->format(
fn ($value, $row, Column $column) => number_format($value, 0, ',', '.')
)
->sortable(),
Column::make('')
->label(
fn ($row, Column $column) => view('columns.project_proposals.action')->withRow($row)->withCountry($this->country)
),
];
}
public function builder(): Builder
{
return ProjectProposal::query();
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class ProjectProposal extends Model implements HasMedia
{
use InteractsWithMedia;
use HasFactory;
/**
* 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',
'user_id' => 'integer',
];
protected static function booted()
{
static::creating(function ($model) {
if (! $model->created_by) {
$model->created_by = auth()->id();
}
});
}
public function registerMediaConversions(Media $media = null): void
{
$this
->addMediaConversion('preview')
->fit(Manipulations::FIT_CROP, 300, 300)
->nonQueued();
$this->addMediaConversion('thumb')
->fit(Manipulations::FIT_CROP, 130, 130)
->width(130)
->height(130);
}
public function registerMediaCollections(): void
{
$this->addMediaCollection('main')
->singleFile()
->useFallbackUrl(asset('img/einundzwanzig.png'));
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
+5 -3
View File
@@ -36,7 +36,6 @@ class User extends Authenticatable implements MustVerifyEmail, CanComment, Ciphe
/**
* The attributes that should be hidden for serialization.
*
* @var array
*/
protected $hidden = [
@@ -48,7 +47,6 @@ class User extends Authenticatable implements MustVerifyEmail, CanComment, Ciphe
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
@@ -57,7 +55,6 @@ class User extends Authenticatable implements MustVerifyEmail, CanComment, Ciphe
/**
* The accessors to append to the model's array form.
*
* @var array
*/
protected $appends = [
@@ -93,4 +90,9 @@ class User extends Authenticatable implements MustVerifyEmail, CanComment, Ciphe
{
return $this->morphMany('QCod\Gamify\Reputation', 'subject');
}
public function votes()
{
return $this->hasMany(Vote::class);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Vote extends Model
{
use HasFactory;
/**
* 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',
'user_id' => 'integer',
'project_proposal_id' => 'integer',
'value' => 'bool',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function projectProposal(): BelongsTo
{
return $this->belongsTo(ProjectProposal::class);
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\ProjectProposal;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ProjectProposalPolicy extends BasePolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return true;
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, ProjectProposal $projectProposal): bool
{
return true;
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return true;
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, ProjectProposal $projectProposal): bool
{
return $projectProposal->created_by === $user->id || $user->can((new \ReflectionClass($this))->getShortName().'.'.__FUNCTION__);
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, ProjectProposal $projectProposal): bool
{
return $projectProposal->created_by === $user->id || $user->can((new \ReflectionClass($this))->getShortName().'.'.__FUNCTION__);
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, ProjectProposal $projectProposal): bool
{
return false;
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, ProjectProposal $projectProposal): bool
{
return false;
}
}
+7 -1
View File
@@ -61,7 +61,8 @@
"symfony/mailgun-mailer": "^6.2",
"wesselperik/nova-status-field": "^2.1",
"wireui/wireui": "dev-main",
"ziffmedia/nova-select-plus": "^2.0"
"ziffmedia/nova-select-plus": "^2.0",
"jantinnerezo/livewire-range-slider": "dev-main#3b6b28ca22be89bcb65b6b90e32ca7d04c267382"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
@@ -70,6 +71,7 @@
"laravel-lang/http-statuses": "^3.1",
"laravel-lang/lang": "^12.5",
"laravel-lang/publisher": "^14.6",
"laravel-shift/blueprint": "^2.7",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
@@ -138,6 +140,10 @@
"symlink": false
}
},
{
"type": "vcs",
"url": "https://github.com/HolgerHatGarKeineNode/livewire-range-slider"
},
{
"type": "vcs",
"url": "https://github.com/HolgerHatGarKeineNode/blade-country-flags"
Generated
+524 -338
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
use App\Models\ProjectProposal;
use App\Models\User;
class ProjectProposalFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = ProjectProposal::class;
/**
* Define the model's default state.
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'name' => $this->faker->name,
'support_in_sats' => $this->faker->randomNumber(),
'description' => $this->faker->text,
];
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
use App\Models\ProjectProposal;
use App\Models\User;
use App\Models\Vote;
class VoteFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Vote::class;
/**
* Define the model's default state.
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'project_proposal_id' => ProjectProposal::factory(),
'value' => $this->faker->randomNumber(),
'reason' => $this->faker->text,
];
}
}
@@ -0,0 +1,35 @@
<?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('user_id')->constrained()->cascadeOnDelete()->cascadeOnUpdate();
$table->string('name')->unique();
$table->unsignedInteger('support_in_sats');
$table->text('description');
$table->timestamps();
});
Schema::enableForeignKeyConstraints();
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('project_proposals');
}
};
@@ -0,0 +1,35 @@
<?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('user_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');
}
};
@@ -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::table('project_proposals', function (Blueprint $table) {
$table->foreignId('created_by')
->nullable()
->constrained('users')
->onDelete('set null');
});
Schema::table('votes', function (Blueprint $table) {
$table->foreignId('created_by')
->nullable()
->constrained('users')
->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('project_proposals', function (Blueprint $table) {
//
});
}
};
@@ -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->unsignedBigInteger('support_in_sats')
->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('project_proposals', function (Blueprint $table) {
//
});
}
};
+155 -134
View File
@@ -39,145 +39,166 @@ class DatabaseSeeder extends Seeder
{
Artisan::call(CreateTags::class);
Role::create([
'name' => 'super-admin',
'name' => 'super-admin',
'guard_name' => 'web',
]);
Role::create([
'name' => 'entitled-voter',
'guard_name' => 'web',
]);
Permission::create([
'name' => 'translate',
'name' => 'translate',
'guard_name' => 'web',
]);
$user = User::create([
'name' => 'Admin',
'email' => 'admin@einundzwanzig.space',
'name' => 'Admin',
'email' => 'admin@einundzwanzig.space',
'email_verified_at' => now(),
'password' => bcrypt('1234'),
'remember_token' => Str::random(10),
'is_lecturer' => true,
'password' => bcrypt('1234'),
'remember_token' => Str::random(10),
'is_lecturer' => true,
]);
$entitledUser = User::create([
'name' => 'Entitled Voter',
'email' => 'voter1@einundzwanzig.space',
'email_verified_at' => now(),
'password' => bcrypt('1234'),
'remember_token' => Str::random(10),
'is_lecturer' => true,
]);
$entitledUser->assignRole('entitled-voter');
User::create([
'name' => 'Not Entitled Voter',
'email' => 'voter1@einundzwanzig.space',
'email_verified_at' => now(),
'password' => bcrypt('1234'),
'remember_token' => Str::random(10),
'is_lecturer' => true,
]);
$team = Team::create([
'name' => 'Admin Team',
'user_id' => $user->id,
'name' => 'Admin Team',
'user_id' => $user->id,
'personal_team' => true,
]);
$user->givePermissionTo('translate');
$user->current_team_id = $team->id;
$user->save();
Country::create([
'name' => 'Deutschland',
'code' => 'de',
'name' => 'Deutschland',
'code' => 'de',
'language_codes' => ['de'],
]);
Country::create([
'name' => 'Österreich',
'code' => 'at',
'name' => 'Österreich',
'code' => 'at',
'language_codes' => ['de'],
]);
Country::create([
'name' => 'Schweiz',
'code' => 'ch',
'name' => 'Schweiz',
'code' => 'ch',
'language_codes' => ['de'],
]);
Country::create([
'name' => 'France',
'code' => 'fr',
'name' => 'France',
'code' => 'fr',
'language_codes' => ['fr'],
]);
Country::create([
'name' => 'Sweden',
'code' => 'se',
'name' => 'Sweden',
'code' => 'se',
'language_codes' => ['sv'],
]);
Country::create([
'name' => 'Serbia',
'code' => 'rs',
'name' => 'Serbia',
'code' => 'rs',
'language_codes' => ['rs'],
'longitude' => 21,
'latitude' => 44,
'longitude' => 21,
'latitude' => 44,
]);
City::create([
'country_id' => 1,
'name' => 'Füssen',
'latitude' => 47.57143,
'longitude' => 10.70171,
'name' => 'Füssen',
'latitude' => 47.57143,
'longitude' => 10.70171,
'created_by' => 1,
]);
City::create([
'country_id' => 1,
'name' => 'Kempten',
'latitude' => 47.728569,
'longitude' => 10.315784,
'name' => 'Kempten',
'latitude' => 47.728569,
'longitude' => 10.315784,
'created_by' => 1,
]);
City::create([
'country_id' => 1,
'name' => 'Pfronten',
'latitude' => 47.582359,
'longitude' => 10.5598,
'name' => 'Pfronten',
'latitude' => 47.582359,
'longitude' => 10.5598,
'created_by' => 1,
]);
City::create([
'country_id' => 2,
'name' => 'Wien',
'latitude' => 48.20835,
'longitude' => 16.37250,
'name' => 'Wien',
'latitude' => 48.20835,
'longitude' => 16.37250,
'created_by' => 1,
]);
City::create([
'country_id' => 3,
'name' => 'Zürich',
'latitude' => 47.41330,
'longitude' => 8.65639,
'name' => 'Zürich',
'latitude' => 47.41330,
'longitude' => 8.65639,
'created_by' => 1,
]);
City::create([
'country_id' => 1,
'name' => 'Hessen',
'latitude' => 50.526501,
'longitude' => 9.004440,
'name' => 'Hessen',
'latitude' => 50.526501,
'longitude' => 9.004440,
'created_by' => 1,
]);
Venue::create([
'city_id' => 1,
'name' => 'The Blue Studio Coworking (Füssen)',
'street' => 'Teststraße 1',
'city_id' => 1,
'name' => 'The Blue Studio Coworking (Füssen)',
'street' => 'Teststraße 1',
'created_by' => 1,
]);
Venue::create([
'city_id' => 2,
'name' => 'The Blue Studio Coworking (Kempten)',
'street' => 'Teststraße 2',
'city_id' => 2,
'name' => 'The Blue Studio Coworking (Kempten)',
'street' => 'Teststraße 2',
'created_by' => 1,
]);
Venue::create([
'city_id' => 3,
'name' => 'The Blue Studio Coworking (Pfronten)',
'street' => 'Teststraße 3',
'city_id' => 3,
'name' => 'The Blue Studio Coworking (Pfronten)',
'street' => 'Teststraße 3',
'created_by' => 1,
]);
Venue::create([
'city_id' => 4,
'name' => 'Innsbruck',
'street' => 'Innsbrucker Straße 1',
'city_id' => 4,
'name' => 'Innsbruck',
'street' => 'Innsbrucker Straße 1',
'created_by' => 1,
]);
Lecturer::create([
'name' => 'Markus Turm',
'active' => true,
'name' => 'Markus Turm',
'active' => true,
'created_by' => 1,
]);
Lecturer::create([
'name' => 'Beppo',
'active' => true,
'name' => 'Beppo',
'active' => true,
'created_by' => 1,
]);
Lecturer::create([
'name' => 'Helper',
'active' => true,
'name' => 'Helper',
'active' => true,
'created_by' => 1,
]);
Lecturer::create([
'name' => 'Gigi',
'active' => true,
'name' => 'Gigi',
'active' => true,
'created_by' => 1,
]);
$category = Category::create([
@@ -190,8 +211,8 @@ class DatabaseSeeder extends Seeder
]);
$course = Course::create([
'lecturer_id' => 1,
'name' => 'Hands on Bitcoin',
'created_by' => 1,
'name' => 'Hands on Bitcoin',
'created_by' => 1,
'description' => '
Klimakiller Bitcoin! Bitcoin hat keinen Nutzen! Bitcoin wird nur von Kriminellen genutzt!
@@ -211,8 +232,8 @@ Deshalb werden Sie von mir in diesem Kurs leicht verständlich an das Thema hera
->attach($category);
$course = Course::create([
'lecturer_id' => 1,
'name' => 'Bitcoin <> Crypto',
'created_by' => 1,
'name' => 'Bitcoin <> Crypto',
'created_by' => 1,
'description' => '
Klimakiller Bitcoin! Bitcoin hat keinen Nutzen! Bitcoin wird nur von Kriminellen genutzt!
@@ -232,8 +253,8 @@ Deshalb werden Sie von mir in diesem Kurs leicht verständlich an das Thema hera
->attach($categoryOnline);
$course = Course::create([
'lecturer_id' => 2,
'name' => 'Bitcoin Lightning Network',
'created_by' => 1,
'name' => 'Bitcoin Lightning Network',
'created_by' => 1,
'description' => '
Klimakiller Bitcoin! Bitcoin hat keinen Nutzen! Bitcoin wird nur von Kriminellen genutzt!
@@ -253,55 +274,55 @@ Deshalb werden Sie von mir in diesem Kurs leicht verständlich an das Thema hera
->attach($categoryOnline);
Participant::create([
'first_name' => 'Roman',
'last_name' => 'Reher',
'last_name' => 'Reher',
]);
CourseEvent::create([
'course_id' => 2,
'venue_id' => 1,
'link' => 'https://einundzwanzig.space',
'from' => now()
'course_id' => 2,
'venue_id' => 1,
'link' => 'https://einundzwanzig.space',
'from' => now()
->addDays(14)
->startOfDay(),
'to' => now()
'to' => now()
->addDays(14)
->startOfDay()
->addHour(),
'created_by' => 1,
]);
CourseEvent::create([
'course_id' => 1,
'venue_id' => 2,
'link' => 'https://einundzwanzig.space',
'from' => now()
'course_id' => 1,
'venue_id' => 2,
'link' => 'https://einundzwanzig.space',
'from' => now()
->addDays(31)
->startOfDay(),
'to' => now()
'to' => now()
->addDays(31)
->startOfDay()
->addHour(),
'created_by' => 1,
]);
CourseEvent::create([
'course_id' => 1,
'venue_id' => 3,
'link' => 'https://einundzwanzig.space',
'from' => now()
'course_id' => 1,
'venue_id' => 3,
'link' => 'https://einundzwanzig.space',
'from' => now()
->addDays(4)
->startOfDay(),
'to' => now()
'to' => now()
->addDays(4)
->startOfDay()
->addHour(),
'created_by' => 1,
]);
CourseEvent::create([
'course_id' => 3,
'venue_id' => 3,
'link' => 'https://einundzwanzig.space',
'from' => now()
'course_id' => 3,
'venue_id' => 3,
'link' => 'https://einundzwanzig.space',
'from' => now()
->addDays(4)
->startOfDay(),
'to' => now()
'to' => now()
->addDays(4)
->startOfDay()
->addHour(),
@@ -309,72 +330,72 @@ Deshalb werden Sie von mir in diesem Kurs leicht verständlich an das Thema hera
]);
Registration::create([
'course_event_id' => 1,
'participant_id' => 1,
'participant_id' => 1,
]);
$library = Library::create([
'name' => 'Einundzwanzig',
'name' => 'Einundzwanzig',
'language_codes' => ['de'],
'created_by' => 1,
'created_by' => 1,
]);
$libraryItem = LibraryItem::create([
'lecturer_id' => 3,
'name' => 'BITCOIN - Eine Reise in den Kaninchenbau🐇🕳️',
'type' => 'youtube_video',
'lecturer_id' => 3,
'name' => 'BITCOIN - Eine Reise in den Kaninchenbau🐇🕳️',
'type' => 'youtube_video',
'language_code' => 'de',
'value' => 'https://www.youtube.com/watch?v=Oztd2Sja4k0',
'created_by' => 1,
'value' => 'https://www.youtube.com/watch?v=Oztd2Sja4k0',
'created_by' => 1,
]);
$libraryItem->setStatus('published');
$libraryItem->syncTagsWithType(['Bitcoin'], 'library_item');
$library->libraryItems()
->attach($libraryItem);
$library = Library::create([
'name' => 'Apricot',
'name' => 'Apricot',
'language_codes' => ['de', 'en'],
'created_by' => 1,
'created_by' => 1,
]);
$libraryItem = LibraryItem::create([
'lecturer_id' => 4,
'name' => 'Liebe Krypto- und Fiat-Bros',
'type' => 'blog_article',
'lecturer_id' => 4,
'name' => 'Liebe Krypto- und Fiat-Bros',
'type' => 'blog_article',
'language_code' => 'de',
'value' => 'https://aprycot.media/blog/liebe-krypto-und-fiat-bros/',
'created_by' => 1,
'value' => 'https://aprycot.media/blog/liebe-krypto-und-fiat-bros/',
'created_by' => 1,
]);
$libraryItem->setStatus('published');
$libraryItem->syncTagsWithType(['Bitcoin'], 'library_item');
$library->libraryItems()
->attach($libraryItem);
$library = Library::create([
'name' => 'Gigi',
'name' => 'Gigi',
'language_codes' => ['de', 'en'],
'created_by' => 1,
'created_by' => 1,
]);
$libraryItem = LibraryItem::create([
'lecturer_id' => 4,
'name' => 'Cryptography is Not Enough - Gigi @ Baltic Honeybadger 2022 ',
'type' => 'youtube_video',
'lecturer_id' => 4,
'name' => 'Cryptography is Not Enough - Gigi @ Baltic Honeybadger 2022 ',
'type' => 'youtube_video',
'language_code' => 'de',
'value' => 'https://www.youtube.com/watch?v=C7ynm0Zkwfk',
'created_by' => 1,
'value' => 'https://www.youtube.com/watch?v=C7ynm0Zkwfk',
'created_by' => 1,
]);
$libraryItem->setStatus('published');
$libraryItem->syncTagsWithType(['Proof of Work'], 'library_item');
$library->libraryItems()
->attach($libraryItem);
$nonPublicLibrary = Library::create([
'name' => 'Einundzwanzig Dozenten',
'is_public' => false,
'name' => 'Einundzwanzig Dozenten',
'is_public' => false,
'language_codes' => ['de', 'en'],
'created_by' => 1,
'created_by' => 1,
]);
$libraryItem = LibraryItem::create([
'lecturer_id' => 4,
'name' => 'Präsentation: Bitcoin VHS Kurs 2022',
'type' => 'downloadable_file',
'lecturer_id' => 4,
'name' => 'Präsentation: Bitcoin VHS Kurs 2022',
'type' => 'downloadable_file',
'language_code' => 'de',
'value' => null,
'created_by' => 1,
'value' => null,
'created_by' => 1,
]);
$libraryItem->setStatus('published');
$libraryItem->syncTagsWithType(['Präsentationen'], 'library_item');
@@ -384,44 +405,44 @@ Deshalb werden Sie von mir in diesem Kurs leicht verständlich an das Thema hera
Artisan::call(SyncOpenBooks::class);
Artisan::call(ImportGithubMeetups::class);
MeetupEvent::create([
'meetup_id' => 1,
'start' => now()
'meetup_id' => 1,
'start' => now()
->addDays(31)
->startOfDay()
->addHours(20),
'location' => 'Einundzwanzig Kempten',
'location' => 'Einundzwanzig Kempten',
'description' => fake()->text(),
'link' => 'https://t.me/EinundzwanzigKempten',
'created_by' => 1,
'link' => 'https://t.me/EinundzwanzigKempten',
'created_by' => 1,
]);
MeetupEvent::create([
'meetup_id' => 1,
'start' => now()
'meetup_id' => 1,
'start' => now()
->subDays(2)
->startOfDay()
->addHours(20),
'location' => 'Einundzwanzig Kempten',
'location' => 'Einundzwanzig Kempten',
'description' => fake()->text(),
'link' => 'https://t.me/EinundzwanzigKempten',
'created_by' => 1,
'link' => 'https://t.me/EinundzwanzigKempten',
'created_by' => 1,
]);
BitcoinEvent::create([
'venue_id' => 4,
'from' => Carbon::parse('2023-09-12')
'venue_id' => 4,
'from' => Carbon::parse('2023-09-12')
->startOfDay()
->addHours(8),
'to' => Carbon::parse('2023-09-16')
'to' => Carbon::parse('2023-09-16')
->startOfDay()
->addHours(18),
'title' => 'BTC23',
'title' => 'BTC23',
'description' => 'The largest Bitcoin conference in German is entering the second round: The BTC23 will be even better, more diverse and quite controversial. We are open - for Bitcoiners, interested parties and skeptics from all directions. Three days with bitcoiners, thought leaders and entrepreneurs from space - full of relevant lectures, debates and conversations. And of course parties! The following applies: The BTC23 is there for everyone, no matter what level of knowledge you have on the subject. #bitcoinonly
Advance ticket sales begin on December 21, 2022 at 9:21 p.m. with time-limited early bird tickets.
Ticket presale begins on December 21, 2022 at 9:21 p.m. Be quick - there are a limited amount of early bird tickets again.
',
'link' => 'https://bconf.de/en/',
'created_by' => 1,
'link' => 'https://bconf.de/en/',
'created_by' => 1,
]);
Artisan::call(SynchroniseMissingTranslationKeys::class);
}
+11
View File
@@ -0,0 +1,11 @@
models:
ProjectProposal:
user_id: foreign
name: string unique
support_in_sats: integer unsigned
description: text
Vote:
user_id: foreign
project_proposal_id: foreign
value: integer unsigned
reason: text nullable
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,5 +1,5 @@
{
"/app.js": "/app.js?id=6148211f5802ae601d59d3dfa95d1d35",
"/app.js": "/app.js?id=45904d8bd75c65ee5c136a52a5e8ead6",
"/app-dark.css": "/app-dark.css?id=15c72df05e2b1147fa3e4b0670cfb435",
"/app.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
"/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,7 +1,7 @@
{
"/app.js": "/app.js?id=98c5c53ef4ec8700776f999086de129e",
"/app.js": "/app.js?id=04e348094a54ac4cf95cdaa45acb8f58",
"/manifest.js": "/manifest.js?id=d75058ce2144a4049857d3ff9e02de1e",
"/app.css": "/app.css?id=f33fcd06eab1fae1d91d0347f8a3f06c",
"/app.css": "/app.css?id=30653583355cfd55b0fd95f328ff4706",
"/vendor.js": "/vendor.js?id=de86bde8857e857b607852d11627d8e4",
"/fonts/snunitosansv11pe01mimslybiv1o4x1m8cce4g1pty10iurt9w6fk2a.woff2": "/fonts/snunitosansv11pe01mimslybiv1o4x1m8cce4g1pty10iurt9w6fk2a.woff2?id=c8390e146be0a3c8a5498355dec892ae",
"/fonts/snunitosansv11pe01mimslybiv1o4x1m8cce4g1pty14iurt9w6fk2a.woff2": "/fonts/snunitosansv11pe01mimslybiv1o4x1m8cce4g1pty14iurt9w6fk2a.woff2?id=b0735c7dd6126471acbaf9d6e9f5e41a",
+13 -1
View File
@@ -799,5 +799,17 @@
"Language": "Sprache",
"minutes": "Minuten",
"Recurring appointment \/ monthly": "Wiederkehrende Termine \/ monatlich",
"Search and find Bitcoin Podcast episodes.": "Suche und finde Bitcoin Podcast-Episoden."
"Search and find Bitcoin Podcast episodes.": "Suche und finde Bitcoin Podcast-Episoden.",
"Intended support in sats": "Beabsichtigte Unterstützung in sats",
"Vote": "Abstimmen",
"Submit project for funding": "Projekt zur Finanzierung einreichen",
"Project Funding": "Projekt-Unterstützung",
"Project Funding and Voting": "Projekt-Unterstützung und Abstimmung",
"Submitted projects": "Eingereichte Projekte",
"Project Proposal": "Projekt-Vorschlag",
"Project description": "Projekt-Beschreibung",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "Bitte schreibe einen detaillierten und verständlichen Bewerbungstext, damit die Abstimmung über eine mögliche Unterstützung stattfinden kann.",
"Voting": "Abstimmung",
"Reason": "Ablehnungsgrund",
"not voted yet": "bisher nicht abgestimmt"
}
+13 -1
View File
@@ -796,5 +796,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -796,5 +796,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -797,5 +797,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -797,5 +797,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -797,5 +797,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -797,5 +797,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -797,5 +797,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -797,5 +797,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -759,5 +759,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
+13 -1
View File
@@ -771,5 +771,17 @@
"Language": "",
"minutes": "",
"Recurring appointment \/ monthly": "",
"Search and find Bitcoin Podcast episodes.": ""
"Search and find Bitcoin Podcast episodes.": "",
"Intended support in sats": "",
"Vote": "",
"Submit project for funding": "",
"Project Funding": "",
"Project Funding and Voting": "",
"Submitted projects": "",
"Project Proposal": "",
"Project description": "",
"Please write a detailed and understandable application text, so that the vote on a possible support can take place.": "",
"Voting": "",
"Reason": "",
"not voted yet": ""
}
@@ -0,0 +1,16 @@
<div class="flex flex-col space-y-1">
@can('update', $row)
<div>
<x-button xs amber :href="route('project.projectProposal.form', ['country' => $country, 'projectProposal' => $row])">
<i class="fa fa-thin fa-edit mr-2"></i>
{{ __('Edit') }}
</x-button>
</div>
@endcan
<div>
<x-button xs black :href="route('project.voting.projectFunding', ['country' => $country, 'projectProposal' => $row])">
<i class="fa fa-thin fa-check-to-slot mr-2"></i>
{{ __('Vote') }} [0]
</x-button>
</div>
</div>
@@ -0,0 +1,6 @@
<div class="w-full mb-4 md:w-auto md:mb-0">
<x-button :href="route('project.projectProposal.form', ['country' => $country, 'projectProposal' => null])">
<i class="fa fa-thin fa-plus"></i>
{{ __('Submit project for funding') }}
</x-button>
</div>
+1
View File
@@ -65,5 +65,6 @@
<!-- ProductLift SDK - Include it only once -->
<script defer src="https://bitcoin.productlift.dev/widgets_sdk"></script>
<x-comments::scripts/>
<x-livewire-range-slider::scripts />
</body>
</html>
+1
View File
@@ -63,5 +63,6 @@
<!-- ProductLift SDK - Include it only once -->
<script defer src="https://bitcoin.productlift.dev/widgets_sdk"></script>
<x-comments::scripts/>
<x-livewire-range-slider::scripts />
</body>
</html>
@@ -31,6 +31,8 @@
@include('livewire.frontend.navigation.bookcases')
@include('livewire.frontend.navigation.association')
@auth
@include('livewire.frontend.navigation.profile')
@endauth
@@ -0,0 +1,91 @@
<div x-data="Components.popover({ open: false, focus: false })" x-init="init()" @keydown.escape="onEscape"
@close-popover-group.window="onClosePopoverGroup">
<button type="button" class="flex items-center gap-x-1 text-sm font-semibold leading-6 text-gray-900"
@click="toggle" @mousedown="if (open) $event.preventDefault()" aria-expanded="true"
:aria-expanded="open.toString()">
{{ __('Project Funding') }}
<svg class="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<path fill-rule="evenodd"
d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
clip-rule="evenodd"></path>
</svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 -translate-y-1"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 -translate-y-1"
x-description="'Product' flyout menu, show/hide based on flyout menu state."
class="absolute inset-x-0 top-0 -z-10 bg-white pt-16 shadow-lg ring-1 ring-gray-900/5"
x-ref="panel" @click.away="open = false" x-cloak>
<div class="mx-auto grid max-w-7xl grid-cols-1 gap-y-10 gap-x-8 py-10 px-6 lg:grid-cols-2 lg:px-8">
<div class="grid grid-cols-2 gap-x-6 sm:gap-x-8">
<div>
<h3 class="text-sm font-medium leading-6 text-gray-500">{{ __('Project Funding and Voting') }}</h3>
<div class="mt-6 flow-root">
<div class="-my-2">
<a href="{{ route('project.table.projectFunding', ['country' => $country]) }}"
class="flex gap-x-4 py-2 text-sm font-semibold leading-6 text-gray-900">
<i class="fa-thin fa-search flex-none text-gray-400"></i>
{{ __('Submitted projects') }}
</a>
</div>
</div>
</div>
<div>
<h3 class="text-sm font-medium leading-6 text-gray-500">{{ __('Manage') }}</h3>
<div class="mt-6 flow-root">
<div class="-my-2">
<a href="{{ route('project.projectProposal.form', ['country' => $country]) }}"
class="flex gap-x-4 py-2 text-sm font-semibold leading-6 text-gray-900">
<i class="fa-thin fa-plus flex-none text-gray-400"></i>
{{ __('Submit project for funding') }}
</a>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 gap-10 sm:gap-8 lg:grid-cols-2">
<h3 class="sr-only">Recent posts</h3>
@foreach($projectProposals as $item)
<article
class="relative isolate flex max-w-2xl flex-col gap-x-8 gap-y-6 sm:flex-row sm:items-start lg:flex-col lg:items-stretch">
<div class="relative flex-none">
<img
class="aspect-[2/1] w-full rounded-lg bg-gray-100 object-cover sm:aspect-[16/9] sm:h-32 lg:h-auto"
src="{{ $item->getFirstMediaUrl('main') }}"
alt="">
<div
class="absolute inset-0 rounded-lg ring-1 ring-inset ring-gray-900/10"></div>
</div>
<div>
<div class="flex items-center gap-x-4">
<div
class="relative z-10 rounded-full bg-gray-50 py-1.5 px-3 text-xs font-medium text-gray-600 hover:bg-gray-100">
{{ $item->name }}
</div>
</div>
<h4 class="mt-2 text-sm font-semibold leading-6 text-gray-900">
<a href="{{ route('libraryItem.view', ['libraryItem' => $item]) }}">
<span class="absolute inset-0"></span>
{{ $item->name }}
</a>
</h4>
<p class="mt-2 text-sm leading-6 text-gray-600 truncate">
{{ $item->description }}
</p>
</div>
</article>
@endforeach
</div>
</div>
</div>
</div>
@@ -0,0 +1,75 @@
<div>
{{-- HEADER --}}
<livewire:frontend.header :country="null"/>
<div class="container p-4 mx-auto bg-21gray my-2">
<div class="pb-5 flex flex-row justify-between">
<h3 class="text-lg font-medium leading-6 text-gray-200">{{ __('Project Proposal') }}</h3>
<div class="flex flex-row space-x-2 items-center">
<div>
<x-button :href="$fromUrl">
<i class="fa fa-thin fa-arrow-left"></i>
{{ __('Back') }}
</x-button>
</div>
</div>
</div>
<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="__('Main picture')">
<div class="py-4">
@if ($image)
<div class="text-gray-200">{{ __('Preview') }}:</div>
<img class="h-48 object-contain" src="{{ $image->temporaryUrl() }}">
@endif
@if ($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('projectProposal.name')" :label="__('Name')">
<x-input autocomplete="off" wire:model.debounce="projectProposal.name"
:placeholder="__('Name')"/>
</x-input.group>
<x-input.group :for="md5('projectProposal.name')" :label="__('Intended support in sats')">
<x-input type="number" autocomplete="off" wire:model.debounce="projectProposal.support_in_sats"
:placeholder="__('Intended support in sats')"/>
</x-input.group>
<x-input.group :for="md5('projectProposal.description')">
<x-slot name="label">
<div>
{{ __('Project description') }}
</div>
<div
class="text-amber-500 text-xs py-2">{{ __('Please write a detailed and understandable application text, so that the vote on a possible support can take place.') }}</div>
</x-slot>
<div
class="text-amber-500 text-xs py-2">{{ __('For images in Markdown, please use eg. Imgur or another provider.') }}</div>
<x-input.simple-mde wire:model.defer="projectProposal.description"/>
@error('projectProposal.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>
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
</div>
@@ -0,0 +1,22 @@
<div class="bg-21gray flex flex-col h-screen justify-between">
{{-- HEADER --}}
<livewire:frontend.header :country="$country"/>
{{-- MAIN --}}
<section class="w-full mb-12">
<div class="max-w-screen-2xl mx-auto px-2 sm:px-10 space-y-4" id="table">
<div class="md:flex md:items-center md:justify-between">
<div class="min-w-0 flex-1">
<h2 class="text-2xl font-bold leading-7 text-white sm:truncate sm:text-3xl sm:tracking-tight">
{{ __('Submitted projects') }}
</h2>
</div>
<div class="mt-4 flex md:mt-0 md:ml-4">
{{----}}
</div>
</div>
<livewire:tables.project-proposal-table :country="$country->code"/>
</div>
</section>
{{-- FOOTER --}}
<livewire:frontend.footer/>
</div>
@@ -0,0 +1,171 @@
<div>
{{-- HEADER --}}
<livewire:frontend.header :country="null"/>
<div class="container p-4 mx-auto bg-21gray my-2">
<div class="pb-5 flex flex-row justify-between">
<h3 class="text-lg font-medium leading-6 text-gray-200">{{ __('Voting') }}
: {{ $projectProposal->name }}</h3>
<div class="flex flex-row space-x-2 items-center">
<div>
<x-button :href="$fromUrl">
<i class="fa fa-thin fa-arrow-left"></i>
{{ __('Back') }}
</x-button>
</div>
</div>
</div>
<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">
<div class="w-full flex space-x-4">
<x-button lg primary wire:click="yes">
Yes, support it!
</x-button>
<x-button lg primary wire:click="no">
No, don't support it!
</x-button>
</div>
<div>
<x-input.group :for="md5('vote.reason')" :label="__('Reason')">
<x-textarea autocomplete="off" wire:model.debounce="vote.reason"
:placeholder="__('Reason')"/>
</x-input.group>
</div>
<div wire:ignore>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div
x-data="{
yes: [{{ $entitledVoters->pluck('votes')->collapse()->where('value', 1)->count() }},{{ $otherVoters->pluck('votes')->collapse()->where('value', 1)->count() }}],
no: [{{ $entitledVoters->pluck('votes')->collapse()->where('value', 0)->count() }},{{ $otherVoters->pluck('votes')->collapse()->where('value', 0)->count() }}],
labels: ['Entitled voters', 'Other voters',],
init() {
let chart = new ApexCharts(this.$refs.chart, this.options)
chart.render()
this.$watch('valuesEligible', () => {
chart.updateOptions(this.options)
})
this.$watch('valuesOther', () => {
chart.updateOptions(this.options)
})
},
get options() {
return {
theme: { palette: 'palette3' },
chart: { type: 'bar', toolbar: true, height: 350, stacked: true, stackType: '100%'},
xaxis: { categories: this.labels },
plotOptions: { bar: { horizontal: true } },
series: [
{
name: 'Yes',
data: this.yes,
},
{
name: 'No',
data: this.no,
},
],
}
}
}"
class="w-full"
>
<div x-ref="chart" class="rounded-lg bg-white p-8"></div>
</div>
</div>
<div class="w-full grid grid-cols-2">
<div>
<div class="border-b border-gray-200 bg-dark px-4 py-5 sm:px-6">
<h3 class="text-base font-semibold leading-6 text-gray-200">Entitled voters</h3>
</div>
<ul role="list" class="divide-y divide-gray-200">
@foreach($entitledVoters as $voter)
@php
$vote = $voter->votes->first();
if (!$voter->votes->first()) {
$text = __('not voted yet');
} elseif (!$vote->value) {
$text = __('Reason') . ': ' . $voter->votes->first()?->reason;
}
@endphp
<li class="flex py-4">
<img class="h-10 w-10 rounded-full" src="{{ $voter->profile_photo_url }}"
alt="">
<div class="ml-3">
<p class="text-sm font-medium text-gray-200">
{{ $voter->name }}
@if($voter->votes->first()?->value)
<x-badge green>{{ __('Yes') }}</x-badge>
@endif
@if($voter->votes->first() && !$voter->votes->first()?->value)
<x-badge red>{{ __('No') }}</x-badge>
@endif
</p>
<p class="text-sm text-gray-300">
{{ $text ?? '' }}
</p>
</div>
</li>
@endforeach
</ul>
</div>
<div>
<div class="border-b border-gray-200 bg-dark px-4 py-5 sm:px-6">
<h3 class="text-base font-semibold leading-6 text-gray-200">Other voters</h3>
</div>
<ul role="list" class="divide-y divide-gray-200">
@foreach($otherVoters as $voter)
@php
$vote = $voter->votes->first();
if (!$voter->votes->first()) {
$text = __('not voted yet');
} elseif (!$vote->value) {
$text = __('Reason') . ': ' . $voter->votes->first()?->reason;
}
@endphp
<li class="flex py-4">
<img class="h-10 w-10 rounded-full" src="{{ $voter->profile_photo_url }}"
alt="">
<div class="ml-3">
<p class="text-sm font-medium text-gray-200">
{{ $voter->name }}
@if($voter->votes->first()?->value)
<x-badge green>{{ __('Yes') }}</x-badge>
@endif
@if($voter->votes->first() && !$voter->votes->first()?->value)
<x-badge red>{{ __('No') }}</x-badge>
@endif
</p>
<p class="text-sm text-gray-300">
{{ $text ?? '' }}
</p>
</div>
</li>
@endforeach
</ul>
</div>
</div>
</div>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
</div>
+19
View File
@@ -209,6 +209,25 @@ Route::middleware([])
->name('table.lecturer');
});
/*
* Project Funding
* */
Route::middleware([])
->as('project.')
->prefix('/{country:code}/project-funding')
->group(function () {
Route::get('/project/form/{projectProposal?}', \App\Http\Livewire\ProjectProposal\Form\ProjectProposalForm::class)
->name('projectProposal.form')
->middleware(['auth']);
Route::get('/project/voting/{projectProposal?}', \App\Http\Livewire\ProjectProposal\ProjectProposalVoting::class)
->name('voting.projectFunding')
->middleware(['auth']);
Route::get('/projects', \App\Http\Livewire\ProjectProposal\ProjectProposalTable::class)
->name('table.projectFunding');
});
/*
* Books
* */