mirror of
https://github.com/Einundzwanzig-Podcast/einundzwanzig-portal.git
synced 2025-12-11 06:46:47 +00:00
proximity search for book cases
This commit is contained in:
@@ -35,8 +35,8 @@ class SyncOpenBooks extends Command
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'title' => $case['title'],
|
'title' => $case['title'],
|
||||||
'lat' => (float)$case['lat'],
|
'latitude' => (float)$case['lat'],
|
||||||
'lon' => (float)$case['lon'],
|
'longitude' => (float)$case['lon'],
|
||||||
'address' => $case['address'],
|
'address' => $case['address'],
|
||||||
'type' => $case['type'],
|
'type' => $case['type'],
|
||||||
'open' => $case['open'],
|
'open' => $case['open'],
|
||||||
|
|||||||
@@ -4,9 +4,14 @@ namespace App\Http\Livewire\Frontend;
|
|||||||
|
|
||||||
use App\Models\BookCase;
|
use App\Models\BookCase;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Livewire\WithFileUploads;
|
||||||
|
|
||||||
class CommentBookCase extends Component
|
class CommentBookCase extends Component
|
||||||
{
|
{
|
||||||
|
use WithFileUploads;
|
||||||
|
|
||||||
|
public $photo;
|
||||||
|
|
||||||
public string $c = 'de';
|
public string $c = 'de';
|
||||||
|
|
||||||
public BookCase $bookCase;
|
public BookCase $bookCase;
|
||||||
@@ -16,28 +21,26 @@ class CommentBookCase extends Component
|
|||||||
return view('livewire.frontend.comment-book-case');
|
return view('livewire.frontend.comment-book-case');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function save()
|
||||||
|
{
|
||||||
|
$this->validate([
|
||||||
|
'photo' => 'image|max:4096', // 4MB Max
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->bookCase
|
||||||
|
->addMedia($this->photo)
|
||||||
|
->toMediaCollection('images');
|
||||||
|
|
||||||
|
return to_route('comment.bookcase', ['bookCase' => $this->bookCase->id]);
|
||||||
|
}
|
||||||
|
|
||||||
protected function url_to_absolute($url)
|
protected function url_to_absolute($url)
|
||||||
{
|
{
|
||||||
// Determine request protocol
|
if (str($url)->contains('http')) {
|
||||||
$request_protocol = $request_protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
|
|
||||||
// If dealing with a Protocol Relative URL
|
|
||||||
if (stripos($url, '//') === 0) {
|
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
// If dealing with a Root-Relative URL
|
if (!str($url)->contains('http')) {
|
||||||
if (stripos($url, '/') === 0) {
|
return str($url)->prepend('https://');
|
||||||
return $request_protocol.'://'.$_SERVER['HTTP_HOST'].$url;
|
|
||||||
}
|
}
|
||||||
// If dealing with an Absolute URL, just return it as-is
|
|
||||||
if (stripos($url, 'http') === 0) {
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
// If dealing with a relative URL,
|
|
||||||
// and attempt to handle double dot notation ".."
|
|
||||||
do {
|
|
||||||
$url = preg_replace('/[^\/]+\/\.\.\//', '', $url, 1, $count);
|
|
||||||
} while ($count);
|
|
||||||
// Return the absolute version of a Relative URL
|
|
||||||
return $request_protocol.'://'.$_SERVER['HTTP_HOST'].'/'.$url;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,23 @@
|
|||||||
namespace App\Http\Livewire\Tables;
|
namespace App\Http\Livewire\Tables;
|
||||||
|
|
||||||
use App\Models\BookCase;
|
use App\Models\BookCase;
|
||||||
|
use App\Models\OrangePill;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
||||||
use Rappasoft\LaravelLivewireTables\Views\Column;
|
use Rappasoft\LaravelLivewireTables\Views\Column;
|
||||||
|
use Rappasoft\LaravelLivewireTables\Views\Filters\TextFilter;
|
||||||
|
use WireUi\Traits\Actions;
|
||||||
|
|
||||||
class BookCaseTable extends DataTableComponent
|
class BookCaseTable extends DataTableComponent
|
||||||
{
|
{
|
||||||
|
use Actions;
|
||||||
|
|
||||||
public bool $viewingModal = false;
|
public bool $viewingModal = false;
|
||||||
public $currentModal;
|
public $currentModal;
|
||||||
public array $orangepill = [
|
public array $orangepill = [
|
||||||
'amount' => 1,
|
'amount' => 1,
|
||||||
'date' => null,
|
'date' => null,
|
||||||
|
'comment' => '',
|
||||||
];
|
];
|
||||||
protected $model = BookCase::class;
|
protected $model = BookCase::class;
|
||||||
|
|
||||||
@@ -37,12 +44,27 @@ class BookCaseTable extends DataTableComponent
|
|||||||
->setPerPage(50);
|
->setPerPage(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function filters(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
TextFilter::make('By IDs', 'byids')
|
||||||
|
->filter(function (Builder $builder, string $value) {
|
||||||
|
$builder->whereIn('id', str($value)->explode(','));
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function columns(): array
|
public function columns(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
Column::make("Name", "title")
|
Column::make("Name", "title")
|
||||||
->sortable()
|
->sortable()
|
||||||
->searchable(),
|
->searchable(
|
||||||
|
function (Builder $query, $searchTerm) {
|
||||||
|
$query->where('title', 'ilike', '%'.$searchTerm.'%');
|
||||||
|
}
|
||||||
|
),
|
||||||
Column::make("Adresse", "address")
|
Column::make("Adresse", "address")
|
||||||
->sortable()
|
->sortable()
|
||||||
->searchable(),
|
->searchable(),
|
||||||
@@ -61,27 +83,20 @@ class BookCaseTable extends DataTableComponent
|
|||||||
|
|
||||||
private function url_to_absolute($url)
|
private function url_to_absolute($url)
|
||||||
{
|
{
|
||||||
// Determine request protocol
|
if (str($url)->contains('http')) {
|
||||||
$request_protocol = $request_protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
|
|
||||||
// If dealing with a Protocol Relative URL
|
|
||||||
if (stripos($url, '//') === 0) {
|
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
// If dealing with a Root-Relative URL
|
if (!str($url)->contains('http')) {
|
||||||
if (stripos($url, '/') === 0) {
|
return str($url)->prepend('https://');
|
||||||
return $request_protocol.'://'.$_SERVER['HTTP_HOST'].$url;
|
|
||||||
}
|
}
|
||||||
// If dealing with an Absolute URL, just return it as-is
|
}
|
||||||
if (stripos($url, 'http') === 0) {
|
|
||||||
return $url;
|
public function builder(): Builder
|
||||||
}
|
{
|
||||||
// If dealing with a relative URL,
|
return BookCase::query()
|
||||||
// and attempt to handle double dot notation ".."
|
->withCount([
|
||||||
do {
|
'orangePills',
|
||||||
$url = preg_replace('/[^\/]+\/\.\.\//', '', $url, 1, $count);
|
]);
|
||||||
} while ($count);
|
|
||||||
// Return the absolute version of a Relative URL
|
|
||||||
return $request_protocol.'://'.$_SERVER['HTTP_HOST'].'/'.$url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function viewHistoryModal($modelId): void
|
public function viewHistoryModal($modelId): void
|
||||||
@@ -90,6 +105,25 @@ class BookCaseTable extends DataTableComponent
|
|||||||
$this->currentModal = BookCase::findOrFail($modelId);
|
$this->currentModal = BookCase::findOrFail($modelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function submit(): void
|
||||||
|
{
|
||||||
|
$this->validate([
|
||||||
|
'orangepill.amount' => 'required|numeric',
|
||||||
|
'orangepill.date' => 'required|date',
|
||||||
|
]);
|
||||||
|
$orangePill = OrangePill::create([
|
||||||
|
'user_id' => auth()->id(),
|
||||||
|
'book_case_id' => $this->currentModal->id,
|
||||||
|
'amount' => $this->orangepill['amount'],
|
||||||
|
'date' => $this->orangepill['date'],
|
||||||
|
]);
|
||||||
|
if ($this->orangepill['comment']) {
|
||||||
|
$this->currentModal->comment($this->orangepill['comment']);
|
||||||
|
}
|
||||||
|
$this->resetModal();
|
||||||
|
$this->emit('refreshDatatable');
|
||||||
|
}
|
||||||
|
|
||||||
public function resetModal(): void
|
public function resetModal(): void
|
||||||
{
|
{
|
||||||
$this->reset('viewingModal', 'currentModal');
|
$this->reset('viewingModal', 'currentModal');
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Tables;
|
namespace App\Http\Livewire\Tables;
|
||||||
|
|
||||||
|
use App\Models\BookCase;
|
||||||
use App\Models\City;
|
use App\Models\City;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
||||||
@@ -87,4 +88,21 @@ class CityTable extends DataTableComponent
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function proximitySearchForBookCases($id)
|
||||||
|
{
|
||||||
|
$city = City::query()
|
||||||
|
->find($id);
|
||||||
|
$query = BookCase::radius($city->latitude, $city->longitude, 5);
|
||||||
|
return to_route('search.bookcases', [
|
||||||
|
'#table',
|
||||||
|
'country' => $this->country,
|
||||||
|
'table' => [
|
||||||
|
'filters' => [
|
||||||
|
'byids' => $query->pluck('id')
|
||||||
|
->implode(',')
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,35 +2,63 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Akuechler\Geoly;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Spatie\Comments\Models\Concerns\HasComments;
|
use Spatie\Comments\Models\Concerns\HasComments;
|
||||||
|
use Spatie\Image\Manipulations;
|
||||||
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
|
|
||||||
class BookCase extends Model
|
class BookCase extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use HasComments;
|
use HasComments;
|
||||||
|
use InteractsWithMedia;
|
||||||
|
use Geoly;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that aren't mass assignable.
|
* The attributes that aren't mass assignable.
|
||||||
*
|
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that should be cast to native types.
|
* The attributes that should be cast to native types.
|
||||||
*
|
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'id' => 'integer',
|
'id' => 'integer',
|
||||||
'lat' => 'double',
|
'lat' => 'double',
|
||||||
'lon' => 'array',
|
'lon' => 'array',
|
||||||
'digital' => 'boolean',
|
'digital' => 'boolean',
|
||||||
'deactivated' => 'boolean',
|
'deactivated' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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('images');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function orangePills(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(OrangePill::class);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This string will be used in notifications on what a new comment
|
* This string will be used in notifications on what a new comment
|
||||||
* was made.
|
* was made.
|
||||||
|
|||||||
@@ -14,13 +14,9 @@ class CreateBookCasesTable extends Migration
|
|||||||
{
|
{
|
||||||
Schema::create('book_cases', function (Blueprint $table) {
|
Schema::create('book_cases', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->boolean('orange_pilled')
|
|
||||||
->default(false);
|
|
||||||
$table->unsignedInteger('orange_pilled_amount')
|
|
||||||
->default(0);
|
|
||||||
$table->string('title');
|
$table->string('title');
|
||||||
$table->double('lat');
|
$table->double('latitude');
|
||||||
$table->double('lon');
|
$table->double('longitude');
|
||||||
$table->text('address')
|
$table->text('address')
|
||||||
->nullable();
|
->nullable();
|
||||||
$table->string('type');
|
$table->string('type');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Orange Pill Book Case": "Bücher-Schrank wurde orange pilled.",
|
"Orange Pill Book Case": "Wie viele Bitcoin-Bücher hast du hinzu gefügt?",
|
||||||
"Book": "Buch",
|
"Book": "Buch",
|
||||||
"Article": "Artikel",
|
"Article": "Artikel",
|
||||||
"Markdown Article": "Interner Artikel",
|
"Markdown Article": "Interner Artikel",
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<div class="flex flex-col space-y-1">
|
<div class="flex flex-col space-y-1">
|
||||||
@auth
|
@auth
|
||||||
@if($row->orange_pilled)
|
@if($row->orange_pills_count > 0)
|
||||||
<img class="aspect-auto max-h-12" src="{{ asset('img/social_credit_plus.webp') }}" alt="">
|
<img class="aspect-auto max-h-12" src="{{ asset('img/social_credit_plus.webp') }}" alt="">
|
||||||
@endif
|
@endif
|
||||||
@if(!$row->orange_pilled)
|
@if($row->orange_pills_count < 1)
|
||||||
<img class="aspect-auto max-h-12" src="{{ asset('img/social_credit_minus.webp') }}" alt="">
|
<img class="aspect-auto max-h-12" src="{{ asset('img/social_credit_minus.webp') }}" alt="">
|
||||||
@endif
|
@endif
|
||||||
<div class="flex items-center space-x-1">
|
<div class="flex items-center space-x-1">
|
||||||
<x-button wire:click="viewHistoryModal({{ $row->id }})">💊 Orange Pill Now</x-button>
|
<x-button primary class="text-21gray" wire:click="viewHistoryModal({{ $row->id }})">💊 Orange Pill Now</x-button>
|
||||||
<x-button :href="route('comment.bookcase', ['bookCase' => $row->id])">Kommentare</x-button>
|
<x-button :href="route('comment.bookcase', ['bookCase' => $row->id])">Details</x-button>
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1 +1,14 @@
|
|||||||
<x-button amber wire:click="proximitySearch({{ $row->id }})">Umkreis-Suche {{ $row->name }} (100km)</x-button>
|
<div class="flex flex-col space-y-1">
|
||||||
|
<div>
|
||||||
|
<x-button amber wire:click="proximitySearch({{ $row->id }})" class="text-21gray">
|
||||||
|
<i class="fa fa-thin fa-person-chalkboard mr-2"></i>
|
||||||
|
Umkreis-Suche Kurs-Termin {{ $row->name }}(100km)
|
||||||
|
</x-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<x-button amber wire:click="proximitySearchForBookCases({{ $row->id }})" class="text-21gray">
|
||||||
|
<i class="fa fa-thin fa-book mr-2"></i>
|
||||||
|
Umkreis-Suche Bücher-Schrank {{ $row->name }} (5km)
|
||||||
|
</x-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
@googlefonts
|
@googlefonts
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="https://kit.fontawesome.com/03bc14bd1e.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/03bc14bd1e.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://unpkg.com/smoothscroll-polyfill@0.4.4/dist/smoothscroll.js"></script>
|
||||||
@mapscripts
|
@mapscripts
|
||||||
<wireui:scripts/>
|
<wireui:scripts/>
|
||||||
<x-comments::scripts />
|
<x-comments::scripts />
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
@googlefonts
|
@googlefonts
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="https://kit.fontawesome.com/03bc14bd1e.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/03bc14bd1e.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://unpkg.com/smoothscroll-polyfill@0.4.4/dist/smoothscroll.js"></script>
|
||||||
<wireui:scripts/>
|
<wireui:scripts/>
|
||||||
<x-comments::scripts />
|
<x-comments::scripts />
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
|||||||
@@ -74,12 +74,12 @@
|
|||||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="relative flex items-center space-x-3 rounded-lg border border-gray-300 bg-white px-6 py-5 shadow-sm focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2 hover:border-gray-400">
|
class="relative flex items-center space-x-3 rounded-lg border border-gray-300 bg-white px-6 py-5 shadow-sm">
|
||||||
{{--<div class="flex-shrink-0">
|
{{--<div class="flex-shrink-0">
|
||||||
<img class="h-10 w-10 rounded-full" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
|
<img class="h-10 w-10 rounded-full" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
|
||||||
</div>--}}
|
</div>--}}
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
<div class="focus:outline-none">
|
<div class="focus:outline-none space-y-2">
|
||||||
<p class="text-sm font-medium text-gray-900">Name</p>
|
<p class="text-sm font-medium text-gray-900">Name</p>
|
||||||
<p class="truncate text-sm text-gray-500">{{ $bookCase->title }}</p>
|
<p class="truncate text-sm text-gray-500">{{ $bookCase->title }}</p>
|
||||||
<p class="text-sm font-medium text-gray-900">Link</p>
|
<p class="text-sm font-medium text-gray-900">Link</p>
|
||||||
@@ -89,20 +89,165 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="text-sm font-medium text-gray-900">Adresse</p>
|
<p class="text-sm font-medium text-gray-900">Adresse</p>
|
||||||
<p class="truncate text-sm text-gray-500">{{ $bookCase->address }}</p>
|
<p class="truncate text-sm text-gray-500">{{ $bookCase->address }}</p>
|
||||||
|
<p class="text-sm font-medium text-gray-900">Art</p>
|
||||||
|
<p class="truncate text-sm text-gray-500">{{ $bookCase->type }}</p>
|
||||||
|
<p class="text-sm font-medium text-gray-900">Geöffnet</p>
|
||||||
|
<p class="truncate text-sm text-gray-500">{{ $bookCase->open }}</p>
|
||||||
|
<p class="text-sm font-medium text-gray-900">Kontakt</p>
|
||||||
|
<p class="truncate text-sm text-gray-500">{{ $bookCase->contact }}</p>
|
||||||
|
<p class="text-sm font-medium text-gray-900">Information</p>
|
||||||
|
<p class="truncate text-sm text-gray-500">{{ $bookCase->comment }}</p>
|
||||||
|
|
||||||
|
<p class="text-sm font-medium text-gray-900">Neues Foto hochladen</p>
|
||||||
|
|
||||||
|
<form wire:submit.prevent="save">
|
||||||
|
<div class="text-sm text-gray-500">
|
||||||
|
<input type="file" wire:model="photo">
|
||||||
|
@error('photo') <span class="error">{{ $message }}</span> @enderror
|
||||||
|
<x-button xs secondary type="submit">Hochladen</x-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@if($bookCase->getMedia('images')->count() > 0)
|
||||||
|
<div
|
||||||
|
x-data="{
|
||||||
|
skip: 3,
|
||||||
|
atBeginning: false,
|
||||||
|
atEnd: false,
|
||||||
|
next() {
|
||||||
|
this.to((current, offset) => current + (offset * this.skip))
|
||||||
|
},
|
||||||
|
prev() {
|
||||||
|
this.to((current, offset) => current - (offset * this.skip))
|
||||||
|
},
|
||||||
|
to(strategy) {
|
||||||
|
let slider = this.$refs.slider
|
||||||
|
let current = slider.scrollLeft
|
||||||
|
let offset = slider.firstElementChild.getBoundingClientRect().width
|
||||||
|
slider.scrollTo({ left: strategy(current, offset), behavior: 'smooth' })
|
||||||
|
},
|
||||||
|
focusableWhenVisible: {
|
||||||
|
'x-intersect:enter'() {
|
||||||
|
this.$el.removeAttribute('tabindex')
|
||||||
|
},
|
||||||
|
'x-intersect:leave'() {
|
||||||
|
this.$el.setAttribute('tabindex', '-1')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disableNextAndPreviousButtons: {
|
||||||
|
'x-intersect:enter.threshold.05'() {
|
||||||
|
let slideEls = this.$el.parentElement.children
|
||||||
|
|
||||||
|
// If this is the first slide.
|
||||||
|
if (slideEls[0] === this.$el) {
|
||||||
|
this.atBeginning = true
|
||||||
|
// If this is the last slide.
|
||||||
|
} else if (slideEls[slideEls.length-1] === this.$el) {
|
||||||
|
this.atEnd = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'x-intersect:leave.threshold.05'() {
|
||||||
|
let slideEls = this.$el.parentElement.children
|
||||||
|
|
||||||
|
// If this is the first slide.
|
||||||
|
if (slideEls[0] === this.$el) {
|
||||||
|
this.atBeginning = false
|
||||||
|
// If this is the last slide.
|
||||||
|
} else if (slideEls[slideEls.length-1] === this.$el) {
|
||||||
|
this.atEnd = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
class="flex w-full flex-col"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
x-on:keydown.right="next"
|
||||||
|
x-on:keydown.left="prev"
|
||||||
|
tabindex="0"
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="carousel-label"
|
||||||
|
class="flex space-x-6"
|
||||||
|
>
|
||||||
|
<h2 id="carousel-label" class="sr-only" hidden>Carousel</h2>
|
||||||
|
|
||||||
|
<!-- Prev Button -->
|
||||||
|
<button
|
||||||
|
x-on:click="prev"
|
||||||
|
class="text-6xl"
|
||||||
|
:aria-disabled="atBeginning"
|
||||||
|
:tabindex="atEnd ? -1 : 0"
|
||||||
|
:class="{ 'opacity-50 cursor-not-allowed': atBeginning }"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-gray-600"
|
||||||
|
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"><path
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/></svg>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">Skip to previous slide page</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span id="carousel-content-label" class="sr-only" hidden>Carousel</span>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
x-ref="slider"
|
||||||
|
tabindex="0"
|
||||||
|
role="listbox"
|
||||||
|
aria-labelledby="carousel-content-label"
|
||||||
|
class="flex w-full snap-x snap-mandatory overflow-x-scroll"
|
||||||
|
>
|
||||||
|
|
||||||
|
@foreach($bookCase->getMedia('images') as $image)
|
||||||
|
<li x-bind="disableNextAndPreviousButtons"
|
||||||
|
class="flex w-1/3 shrink-0 snap-start flex-col items-center justify-center p-2"
|
||||||
|
role="option">
|
||||||
|
<a href="{{ $image->getUrl() }}" target="_blank">
|
||||||
|
<img class="mt-2 w-full" src="{{ $image->getUrl() }}"
|
||||||
|
alt="placeholder image">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button x-bind="focusableWhenVisible" class="px-4 py-2 text-sm">
|
||||||
|
#{{ $loop->iteration }} Bild
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Next Button -->
|
||||||
|
<button
|
||||||
|
x-on:click="next"
|
||||||
|
class="text-6xl"
|
||||||
|
:aria-disabled="atEnd"
|
||||||
|
:tabindex="atEnd ? -1 : 0"
|
||||||
|
:class="{ 'opacity-50 cursor-not-allowed': atEnd }"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-gray-600"
|
||||||
|
fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="3"><path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M9 5l7 7-7 7"/></svg>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">Skip to next slide page</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded">
|
<div class="rounded" wire:ignore>
|
||||||
@map([
|
@map([
|
||||||
'lat' => $bookCase->lat,
|
'lat' => $bookCase->latitude,
|
||||||
'lng' => $bookCase->lon,
|
'lng' => $bookCase->longitude,
|
||||||
'zoom' => 24,
|
'zoom' => 24,
|
||||||
'markers' => [
|
'markers' => [
|
||||||
[
|
[
|
||||||
'title' => $bookCase->title,
|
'title' => $bookCase->title,
|
||||||
'lat' => $bookCase->lat,
|
'lat' => $bookCase->latitude,
|
||||||
'lng' => $bookCase->lon,
|
'lng' => $bookCase->longitude,
|
||||||
'url' => 'https://gonoware.com',
|
'url' => 'https://gonoware.com',
|
||||||
'icon' => asset('img/btc-logo-6219386_1280.png'),
|
'icon' => asset('img/btc-logo-6219386_1280.png'),
|
||||||
'icon_size' => [42, 42],
|
'icon_size' => [42, 42],
|
||||||
|
|||||||
@@ -1,15 +1,4 @@
|
|||||||
<div class="bg-21gray flex flex-col h-screen justify-between">
|
<div class="bg-21gray flex flex-col h-screen justify-between">
|
||||||
<script src="{{ asset('earth/miniature.earth.js') }}"></script>
|
|
||||||
<style>
|
|
||||||
.earth-container::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
height: 22%;
|
|
||||||
bottom: 4%;
|
|
||||||
left: 13%;
|
|
||||||
right: 13%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{{-- HEADER --}}
|
{{-- HEADER --}}
|
||||||
<div>
|
<div>
|
||||||
<section class="w-full">
|
<section class="w-full">
|
||||||
@@ -58,7 +47,7 @@
|
|||||||
</div>
|
</div>
|
||||||
@endauth
|
@endauth
|
||||||
</div>
|
</div>
|
||||||
<div class="flex lg:flex-row flex-col pt-4 md:pt-4 lg:pt-4">
|
<div class="flex lg:flex-row flex-col pt-4 md:pt-4 lg:pt-4 mt-12">
|
||||||
<div
|
<div
|
||||||
class="w-full lg:w-1/2 flex lg:px-0 px-5 flex-col md:items-center lg:items-start justify-center -mt-12">
|
class="w-full lg:w-1/2 flex lg:px-0 px-5 flex-col md:items-center lg:items-start justify-center -mt-12">
|
||||||
|
|
||||||
@@ -75,41 +64,6 @@
|
|||||||
</a>
|
</a>
|
||||||
<p class="text-gray-400 font-normal mt-4">{{-- TEXT --}}</p>
|
<p class="text-gray-400 font-normal mt-4">{{-- TEXT --}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
x-data="{
|
|
||||||
earth: null,
|
|
||||||
init() {
|
|
||||||
this.earth = new Earth(this.$refs.myearth, {
|
|
||||||
location : {lat: {{ $bookCases->first()->lat }}, lng: {{ $bookCases->first()->lon }}},
|
|
||||||
zoom: 1,
|
|
||||||
light: 'sun',
|
|
||||||
polarLimit: 0.6,
|
|
||||||
|
|
||||||
transparent : true,
|
|
||||||
mapSeaColor : 'RGBA(34, 34, 34,0.76)',
|
|
||||||
mapLandColor : '#F7931A',
|
|
||||||
mapBorderColor : '#5D5D5D',
|
|
||||||
mapBorderWidth : 0.25,
|
|
||||||
mapHitTest : true,
|
|
||||||
|
|
||||||
autoRotate: true,
|
|
||||||
autoRotateSpeed: 0.7,
|
|
||||||
autoRotateDelay: 500,
|
|
||||||
});
|
|
||||||
this.earth.addEventListener('ready', function() {
|
|
||||||
@foreach($bookCases as $city)
|
|
||||||
this.addMarker( {
|
|
||||||
mesh : ['Needle'],
|
|
||||||
location : { lat: {{ $city->lat }}, lng: {{ $city->lon }} },
|
|
||||||
});
|
|
||||||
@endforeach
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}" class="hidden sm:inline-block w-1/2">
|
|
||||||
{{--<img src="https://cdn.devdojo.com/images/march2022/mesh-gradient1.png"
|
|
||||||
class="absolute lg:max-w-none max-w-3xl mx-auto mt-32 w-full h-full inset-0">--}}
|
|
||||||
<div x-ref="myearth" class="earth-container"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -6,7 +6,27 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
|
|
||||||
<x-slot name="content">
|
<x-slot name="content">
|
||||||
<div class="space-y-4 mt-16 flex flex-col justify-center">
|
<div class="space-y-4 mt-16 flex flex-col justify-center min-h-[600px]">
|
||||||
|
|
||||||
|
<div class="my-4">
|
||||||
|
<div class="border-b border-gray-200 pb-5">
|
||||||
|
<h3 class="text-lg font-medium leading-6 text-gray-200">Bisher waren hier</h3>
|
||||||
|
</div>
|
||||||
|
<ul role="list" class="divide-y divide-gray-200">
|
||||||
|
@foreach($currentModal?->orangePills ?? [] as $orangePill)
|
||||||
|
<li class="flex py-4">
|
||||||
|
<img class="h-10 w-10 rounded-full" src="{{ $orangePill->user->profile_photo_url }}" alt="">
|
||||||
|
<div class="ml-3">
|
||||||
|
<p class="text-sm text-gray-200">
|
||||||
|
{{ $orangePill->user->name }} hat am {{ $orangePill->date->asDateTime() }} {{ $orangePill->amount }} Bitcoin-Bücher hinzugefügt
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-span-6 sm:col-span-4">
|
<div class="col-span-6 sm:col-span-4">
|
||||||
<x-input
|
<x-input
|
||||||
min="1"
|
min="1"
|
||||||
@@ -36,6 +56,9 @@
|
|||||||
|
|
||||||
<x-slot name="footer">
|
<x-slot name="footer">
|
||||||
<x-jet-secondary-button wire:click="resetModal" wire:loading.attr="disabled">
|
<x-jet-secondary-button wire:click="resetModal" wire:loading.attr="disabled">
|
||||||
|
@lang('Close')
|
||||||
|
</x-jet-secondary-button>
|
||||||
|
<x-jet-secondary-button wire:click="submit" wire:loading.attr="disabled">
|
||||||
💊 @lang('Orange Pill Now')
|
💊 @lang('Orange Pill Now')
|
||||||
</x-jet-secondary-button>
|
</x-jet-secondary-button>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
|
|||||||
Reference in New Issue
Block a user