How would you handle a Livewire popover? - laravel

I am looking for a way to make a popover/tooltip in Livewire, preferably without using Vue.
As of now I have created a UserPopover Livewire component with:
namespace App\Http\Livewire;
use App\User;
use Livewire\Component;
class UserPopover extends Component
{
public $isOpen = false;
public $user = null;
protected $listeners = [
'closeUserPopover',
'showUserPopover' => 'open',
];
public function open($username)
{
$this->user = User::whereName($username)->first();
$this->isOpen = true;
}
public function closeUserPopover()
{
$this->isOpen = false;
}
public function render()
{
return view('livewire.user-popover');
}
}
And rendered component:
<div>
#if($isOpen)
<div class="user-popover fixed top-0 shadow bg-white p-6 text-sm">
<h3>{{ $user->name }}</h3>
<div>{{ $user->profile->bio }}</div>
</div>
#endif
</div>
In my app.blade.php file I have included it with #livewire('user-popover') and I am calling the component with:
<a href="{{ route('users.show', $user) }}"
wire:mouseover="$emit('showUserPopover', {{ $user->name }})"
wire:mouseout="$emit('closeUserPopover')"
>
{{ $user->name }}
</a>
I don't know what to do next. Any ideas on positioning, re-usability and overall architecture?

For Example: you can use this tooltip :
Now, you can conditionally render class name (css class contains tooltip styles ) when isOpen is true.
<a class="{{ $isOpen ? 'tooltip' : '' }}" href="{{ route('users.show', $user) }}"
wire:mouseover="$emit('showUserPopover', {{ $user->name }})"
wire:mouseout="$emit('closeUserPopover')"
>
<span class="{{ $isOpen ? 'tooltiptext' : '' }}"> {{ $user->name }} </span>
</a>

Related

Livewire add comment modal component

I'm struggling to get a form parameter submitted to a livewire modal. I'm using laravel9 with php8.1 and have installed wire-elements/modal via composer (v1.0.6) I followed this guide.
So I have a modal.blade.php file in my view components folder with the following:
#props(['formAction' => false])
<div>
#if($formAction)
<form wire:submit.prevent="{{ $formAction }}">
#endif
<div class="bg-white p-4 sm:px-6 sm:py-4 border-b border-gray-150">
#if(isset($title))
<h3 class="text-lg leading-6 font-medium text-gray-900">
{{ $title }}
</h3>
#endif
</div>
<div class="bg-white px-4 sm:p-6">
<div class="space-y-6">
{{ $content }}
</div>
</div>
<div class="bg-white px-4 pb-5 sm:px-4 sm:flex">
{{ $buttons }}
</div>
#if($formAction)
</form>
#endif
</div>
On my test page I have a link to open the Modal which contains a text area for the comment:
<x-jet-button wire:click="$emit( 'openModal', 'add-comment',{{ json_encode(['job_number' => $job->number]) }} )">
{{ __('Add') }}
</x-jet-button>
The livewire component is as follows:
(add-comment.blade.php)
<x-modal formAction="create">
<x-slot name="title">
Add a Comment
</x-slot>
<x-slot name="content">
<label for="comment">Comment Narrative:</label>
<textarea id="comment" name="comment" rows="4" cols="36"></textarea>
</x-slot>
<x-slot name="buttons">
<x-jet-button class="mx-2" type="submit">
{{ __('Submit') }}
</x-jet-button>
<x-jet-button type="button" class="mx-2" wire:click="$emit('closeModal')">
{{ __('Close') }}
</x-jet-button>
</x-slot>
</x-modal>
(AddComment.php)
<?php
namespace App\Http\Livewire;
use App\Models\JobComment;
use Illuminate\Support\Facades\Auth;
use LivewireUI\Modal\ModalComponent;
class AddComment extends ModalComponent
{
public string $comment = '';
public int $job_number;
protected $listeners =['refresh' => '$refresh'];
public function render()
{
return view('livewire.add-comment');
}
public function create()
{
$comment = new JobComment();
$comment->comment = $this->comment;
$comment->job_number = $this->job_number;
$comment->user_id = Auth::id();
$comment->save();
$this->closeModal();
}
public static function closeModalOnEscape(): bool
{
return false;
}
public static function destroyOnClose(): bool
{
return true;
}
}
When i click the button on my test page the modal appears and has the desired content. If I enter some text in the textarea and click the submit a new comment is added to the database but the comment itself is an empty string.
In addition if I click the close button on this form I am unable to reopen the modal but if I click outside the modal it closes and can be opened again (but saving this comment is the issue here).
I have tried using true instead of false in the modal.blade.php file (#props(['formAction' => true])) but it made no difference and my comment is empty
Any ideas?
So with this the issue was i hadn't binded to the model so I amended the text area element to this
<textarea wire:model.defer="comment" id="comment" rows="4" cols="36">

LaravelShoppingcart package, rowId property not working

Hi I'm using the LaravelShoppingcart package(https://github.com/bumbummen99/LaravelShoppingcart) and When I output {{ $item->rowId }} on the blade template I get the rowId on the browser when the blade is rendered but when I pass it in the qtyIncreased({{ $item->rowId}}) method and try to dump and die it in the livewire component it doesn't work. Mark you when I pass the $item->id and dump and die it, it works. The only error I'm getting is on the console and is:
Error handling response: TypeError: Cannot destructure property 'yt' of 'undefined' as it is undefined.
at chrome-extension://cmedhionkhpnakcndndgjdbohmhepckk/scripts/contentscript.js:535:6
Any assistance is highly appreciated.
Live wire blade component
<div class="cart-overlay {{ $active ? 'transparent' : '' }}">
<div class="cart {{ $active ? 'showCart' : '' }}">
<span class="close-cart" wire:click="$emit('closeCart')">
<i class="fas fa-window-close"></i>
</span>
<h2>Cart</h2>
<div class="cart-content">
#foreach ($cartContent as $item)
<div class="cart-item">
<img src="{{ $item->options->image }}" alt="product" />
<div>
<h4>{{ $item->name }}</h4>
<h5>${{ $item->price }}.99</h5>
<span class="remove-item">remove</span>
</div>
<div>
<i class="fas fa-chevron-up" wire:click="qtyIncreased({{ $item-
>rowId }})"></i>
<p class="item-amount">{{ $item->qty }}</p>
<i class="fas fa-chevron-down"></i>
</div>
</div>
#endforeach
</div>
<div class="cart-footer">
<h3>your total : $ <span class="cart-total">{{ $cartTotal }}</span></h3>
<button class="clear-cart banner-btn">clear cart</button>
</div>
</div>
</div>
Livewire Class component
<?php
namespace App\Http\Livewire;
use Gloudemans\Shoppingcart\Facades\Cart;
use Livewire\Component;
class CartOverlay extends Component
{
public $active = false;
public function render()
{
$cartContent = Cart::content();
$cartTotal = Cart::total();
return view('livewire.cart-overlay', compact(['cartContent', 'cartTotal']));
}
public $listeners = [
'showCart',
'closeCart'
];
public function qtyIncreased($rowId)
{
dd($rowId);
}
public function showCart()
{
$this->active = true;
}
public function closeCart()
{
$this->active = false;
}
}
I managed to fixed it, the parameter in the function should be passed in as a string. So, I added quotes around the parameter. Changed it from <i class="fas fa-chevron-up" wire:click="qtyIncreased({{ $item->rowId }})"></i>
TO
<i class="fas fa-chevron-up" wire:click="qtyIncreased('{{ $item->rowId }}')"></i>

Laravel Notifications Mark One Notification as Read

I'm new to laravel notification
I want when to click on the notification the link takes me to the invoice and the notification should be marked as read
I don't know how to mark one notification as read.
I know that I should take the notification id to mark the specific notification as read but I don't know how to is it in a function.
blade :
<div id="unreadNotifications">
#foreach (auth()->user()->unreadNotifications as $notification)
<div class="main-notification-list Notification-scroll mark-as-read" >
<a class="d-flex p-3 border-bottom"
href="{{ url('InvoicesDetails') }}/{{ $notification->data['id'] }}" data-id="{{$notification->id}}" >
<div class="notifyimg ">
<i class="la la-file-alt text-pink text-center"></i>
</div>
<div class="ml-3">
<h5 class="notification-label mb-1">
{{ $notification->data['title'] }}
{{ $notification->data['user'] }}
</h5>
<div class="notification-subtext">{{ $notification->created_at }}
</div>
</div>
</a>
</div>
#endforeach
</div>
Controller:
public function MarkAsRead_all (Request $request)
{
$userUnreadNotification= auth()->user()->unreadNotifications;
if($userUnreadNotification) {
$userUnreadNotification->markAsRead();
return back();
}
}
public function unreadNotifications_count()
{
return auth()->user()->unreadNotifications->count();
}
public function unreadNotifications()
{
foreach (auth()->user()->unreadNotifications as $notification){
return $notification->data['title'];
}
Create a link in the blade
<a class="d-flex p-3 border-bottom" href="{{ url('ReadNotification') }}/{{ $notification->data['id'] }}" data-id="{{$notification->id}}" >
Define a route for it
Route::get('ReadNotification/{id}','BlahBlahController#ReadNotification')->name('ReadNotification');
In Controller
public function ReadNotification($id)
{
$userUnreadNotification = auth()->user()
->unreadNotifications
->where('id', $id)
->first();
if($userUnreadNotification) {
$userUnreadNotification->markAsRead();
}
return back();
}
You just need to send the id of the notification to your controller and make it as read it is the same as you did for all read

How to retrieve specific column data from model bind show method?

I'm setting up a new project and want to retrieve data from passed model binding controller show method but output in show view is null
model :
class Content extends Model
{
protected $fillable = ['title','body'];
}
this is my controller :
public function show(Content $content)
{
return view('content.show',compact('content'));
}
this is my index :
#foreach ($content as $item)
<td>
<a class="btn btn-primary" href="{{ route('Content.show',$item) }}">View</a>
</td>
#endforeach
and this is show method that result is null:
<div class="card-header">
{{ $content->title }}
</div>
<div class="card-body">
{{ $content->body }}
</div>
<div class="card-footer">
{{ $content->created_at }}
/div>
i expect have result in show method because everything is okay but there isn't where is my problem ?
Updated :
dd($content)
because you try to get $content while it is $item now :)
<div class="card-header">
{{ $item->title }}
</div>
<div class="card-body">
{{ $item->body }}
</div>
<div class="card-footer">
{{ $item->created_at }}
</div>
I found the answer
I changed the route resource from resource('Content','ContentController') to
resource('content','ContentController') and the problem has been fixed !
i dont know why !
//used to show a list, like what you are trying to do
public function index()
{
$contents = Content::get();
return view('content.index',compact('contents'));
}
//used to show one item
public function show(Content $content)
{
return view('content.show',compact('content'));
}
What you have for you view works if you are looping over multiple content, otherwise you want:
{{ $content->title }}
</div>
<div class="card-body">
{{ $content->body }}
</div>
<div class="card-footer">
{{ $content->created_at }}
</div>```

Laravel + Vue, How to do ->where() in vue-for?

I'm listing projects and those projects have statuses. First I'm listing the statuses and inside them projects, but this returns all the projects no matter what the status.
With Laravel I could just:
foreach ($projects->where($project->status_id == $status->id) as $project)
but how to do that with Vue?
<div v-for="status in statuses">
{{ status.title }}
<div v-for="project in projects">
{{ project.title }}
</div>
</div>
I also tried
<div v-for="status in statuses">
{{ status.title }}
<div v-for="project in filteredprojects">
{{ project.title }}
</div>
</div>
computed:{
filteredProjects() {
return this.projects.filter(function(project) {
return project.status_id == status.id;
})
}
},
But no luck
Try like this:
<div v-for="status in statuses">
{{ status.title }}
<div v-for="project in projects">
<div v-if="status.id === project.status_id">
{{ project.title }}
</div>
</div>
</div>
If you have set up a relationship between status and projects (as you should since you're using Laravel), you can load the relationship before it ever gets to the Vue component.
Controller
$statuses = Status::where(...your conditions...)->with('projects')->get();
// EDIT: or get all statuses without conditions
$statuses = Status::with('projects')->get();
// EDIT 2: get all statuses with conditional projects
$statuses = Status::with(['projects' => function($query) {
// add your conditions for projects
$query->where('completed', 0);
}])->get();
Vue
<div v-for="status in statuses">
{{ status.title }}
<div v-for="project in status.projects">
{{ project.title }}
</div>
</div>

Resources