mirror of
https://github.com/Einundzwanzig-Podcast/einundzwanzig-portal.git
synced 2025-12-11 06:46:47 +00:00
voting added
This commit is contained in:
30
.blueprint
30
.blueprint
@@ -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
.gitignore
vendored
2
.gitignore
vendored
@@ -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',
|
||||
|
||||
@@ -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', [
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
16
app/Http/Livewire/ProjectProposal/ProjectProposalTable.php
Normal file
16
app/Http/Livewire/ProjectProposal/ProjectProposalTable.php
Normal file
@@ -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');
|
||||
}
|
||||
}
|
||||
107
app/Http/Livewire/ProjectProposal/ProjectProposalVoting.php
Normal file
107
app/Http/Livewire/ProjectProposal/ProjectProposalVoting.php
Normal file
@@ -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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
66
app/Http/Livewire/Tables/ProjectProposalTable.php
Normal file
66
app/Http/Livewire/Tables/ProjectProposalTable.php
Normal file
@@ -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
app/Models/ProjectProposal.php
Normal file
67
app/Models/ProjectProposal.php
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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
app/Models/Vote.php
Normal file
41
app/Models/Vote.php
Normal 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
app/Policies/ProjectProposalPolicy.php
Normal file
66
app/Policies/ProjectProposalPolicy.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
862
composer.lock
generated
862
composer.lock
generated
File diff suppressed because it is too large
Load Diff
31
database/factories/ProjectProposalFactory.php
Normal file
31
database/factories/ProjectProposalFactory.php
Normal file
@@ -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
database/factories/VoteFactory.php
Normal file
32
database/factories/VoteFactory.php
Normal 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');
|
||||
}
|
||||
};
|
||||
35
database/migrations/2023_03_10_190301_create_votes_table.php
Normal file
35
database/migrations/2023_03_10_190301_create_votes_table.php
Normal file
@@ -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) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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
draft.yaml
11
draft.yaml
@@ -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
|
||||
|
||||
2
public/vendor/horizon/app.js
vendored
2
public/vendor/horizon/app.js
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/horizon/mix-manifest.json
vendored
2
public/vendor/horizon/mix-manifest.json
vendored
@@ -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",
|
||||
|
||||
2
public/vendor/nova/app.css
vendored
2
public/vendor/nova/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/nova/app.css.map
vendored
2
public/vendor/nova/app.css.map
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/nova/app.js
vendored
2
public/vendor/nova/app.js
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/nova/app.js.map
vendored
2
public/vendor/nova/app.js.map
vendored
File diff suppressed because one or more lines are too long
4
public/vendor/nova/mix-manifest.json
vendored
4
public/vendor/nova/mix-manifest.json
vendored
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
16
resources/views/columns/project_proposals/action.blade.php
Normal file
16
resources/views/columns/project_proposals/action.blade.php
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
* */
|
||||
|
||||
Reference in New Issue
Block a user