an.leclerc wrote a reply+100 XP
2w ago
an.leclerc wrote a reply+100 XP
2w ago
an.leclerc started a new conversation+100 XP
2w ago
Hi,
I've a component (lead-not-assigned.blade.php)
<?php
use App\Enums\DossierCaseEnum;
use App\Models\DossierModel;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\On;
use Livewire\Attributes\Title;
use Livewire\Component;
new
#[Layout('layouts::admin.app'), Title('Lead not assigned')]
class extends Component {
use \Livewire\WithPagination;
#[Computed]
#[On('lead-assigned')]
public function dossiers()
{
return DossierModel::query()
->where('case', DossierCaseEnum::NOT_ASSIGNED_LEAD)
->orderBy('created_at', 'ASC')
->paginate(15);
}
};
?>
<div class="max-w-7xl h-full">
<div class="flex items-center justify-between">
<div>
<flux:heading size="xl">Leads not assigned</flux:heading>
<flux:text class="mt-2">Manage your not assigned leads</flux:text>
</div>
</div>
<div class="grid grid-cols-2 gap-6 mt-8">
@foreach($this->dossiers as $dossier)
<livewire:pages::admin.leads.lead-not-assigned-card :$dossier
:wire:key="$dossier->id"
:lazy.bundle="$dossier->iteration > 9"
/>
@endforeach
</div>
</div>
And his child component (lead-not-assigned-card.blade.php) :
<?php
use App\Actions\Dossier\AttachInitialStatusAction;
use App\Enums\DossierCaseEnum;
use App\Models\AgencyModel;
use App\Models\DossierModel;
use App\Models\User;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Lazy;
use Livewire\Component;
new
#[Lazy]
class extends Component {
public DossierModel $dossier;
public ?string $analyst = null;
#[Computed]
public function financingAmount()
{
return $this->dossier->getTotalLoansAttribute()
+ $this->dossier->getTotalOverdraftsAttribute()
+ $this->dossier->getTotalTreasuriesAttribute()
+ $this->dossier->getTotalDebtsAttribute();
}
#[Computed]
public function analysts()
{
$agency_id = AgencyModel::where('is_main', true)->first()?->id;
return User::whereHas('agencies', function ($query) use ($agency_id) {
$query
->whereIn('agency.id', [$agency_id]);
})->orderBy('name', 'ASC')
->get();
}
public function transfer()
{
$this->validate([
'analyst' => ['required', 'exists:users,id']
], [
'analyst.required' => 'Analyst is required.',
'analyst.exists' => 'Analyst is not valid.'
]);
try {
$user = User::find($this->analyst);
$this->dossier->sale_agent = $user->id;
$this->dossier->case = DossierCaseEnum::DISCOVER_DOSSIER;
$this->dossier->save();
$this->dossier->refresh();
$agencies = $user->agencies();
if (0 === $agencies->count()) {
throw new Exception('User has no agency.', 500);
}
AttachInitialStatusAction::execute($this->dossier, $this->dossier->case);
$this->dossier->agency()->associate($agencies->first());
$this->dossier->analysts()->attach($user);
$this->reset(['analyst']);
$this->dispatch('lead-assigned')->to('lead-not-assigned');
} catch (\Exception $e) {
}
}
};
?>
@placeholder
<flux:skeleton class="min-h-56 rounded-lg" animate="shimmer" />
@endplaceholder
<div class="border-2 rounded-md p-4 space-y-4">
<div class="flex items-center justify-between">
<flux:badge size="sm">{{ $dossier->updated_at->translatedFormat('d F Y H:i') }}</flux:badge>
<div>
<flux:badge size="sm">{{ $dossier->dossierBorrowers->first()->typeHousingStatus->name }}</flux:badge>
<flux:badge
size="sm">{{ $dossier->dossierJobs->first()->typeJobContract?->short_name ?? $dossier->dossierJobs->first()->typeJobContract?->name ?? 'N.C' }}</flux:badge>
</div>
</div>
<flux:separator />
<div class="space-y-2">
<flux:heading>Financing amount
: {{ Number::currency($this->financingAmount(), in: 'EUR', locale: 'fr') }}</flux:heading>
<flux:text>Total loans : {{ Number::currency($dossier->getTotalLoansAttribute(), in: 'EUR', locale: 'fr') }}
-- {{ Number::currency($dossier->getTotalLoansMonthlyPaymentAttribute(), in: 'EUR', locale: 'fr') }} / mois
</flux:text>
<flux:text>Total Treasuries
: {{ Number::currency($dossier->getTotalTreasuriesAttribute(), in: 'EUR', locale: 'fr') }}</flux:text>
<flux:text>Total Resources
: {{ Number::currency($dossier->getTotalResourcesAttribute(), in: 'EUR', locale: 'fr') }}</flux:text>
</div>
<div>
<div class="flex items-center gap-2">
<flux:text variant="strong">Quick transfer</flux:text>
<flux:field>
<flux:select variant="listbox" placeholder="Choose analyst..." wire:model="analyst">
@foreach($this->analysts as $analyst)
<flux:select.option value="{{ $analyst->id }}">{{ $analyst->full_name }}</flux:select.option>
@endforeach
</flux:select>
</flux:field>
<flux:button variant="primary" icon="arrow-path-rounded-square" wire:click.prevent.stop="transfer">
Transfer
</flux:button>
</div>
<flux:error name="analyst" />
</div>
</div>
But dispatch does not work ! dossiers method in parent component always display lead whereas it's not an not assigned lead anymore. Anyone has an idea ?
an.leclerc wrote a reply+100 XP
2mos ago
an.leclerc wrote a reply+100 XP
2mos ago
an.leclerc wrote a reply+100 XP
2mos ago
Bonjour Vincent :)
Du coup ça fonctionne mais quand je delete un par un les items, la liste ne se met pas à jour, comme si le computed ne se mettait pas à jour :
<?php
use App\Models\Referential\TreasuryTypeModel;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
new
#[Layout('layouts.admin.app')]
#[Title('Types de trésoreries')]
class extends Component {
use \Livewire\WithPagination;
public $sortBy = 'name';
public $sortDirection = 'asc';
public $selected = [];
#[Computed]
public function treasuries()
{
return TreasuryTypeModel::withTrashed()->select('id', 'name', 'updated_at', 'deleted_at')
->tap(fn($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query)
->paginate(15);
}
public function deletedSelected()
{
TreasuryTypeModel::whereIn('id', $this->selected)->delete();
$this->selected = [];
}
public function delete(string $id)
{
TreasuryTypeModel::where('id', $id)->delete();
}
public function restore(string $id)
{
TreasuryTypeModel::withTrashed()->find($id)->restore();
}
};
?>
<div class="max-w-5xl h-full">
<div class="flex items-center justify-between">
<div>
<flux:heading size="xl">Treasuries</flux:heading>
<flux:text class="mt-2">Manage your treasuries</flux:text>
</div>
<div class="flex gap-2">
<div wire:show="selected.length > 0" wire:cloak>
<div class="max-lg:hidden flex justify-start items-center gap-2">
<flux:subheading class="whitespace-nowrap">
<span wire:text="selected.length"></span> selected:
</flux:subheading>
<flux:button size="sm" variant="danger" icon="trash" wire:click="deletedSelected">Delete</flux:button>
</div>
<flux:separator vertical class="max-lg:hidden mx-2 my-2" />
</div>
<flux:button variant="primary" icon="plus" size="sm">Add treasury type</flux:button>
</div>
</div>
<div class="mt-8 grid grid-cols-3 gap-6">
@foreach($this->treasuries as $treasury)
<livewire:pages::admin.referentials.card
:$treasury
:wire:key="$treasury->id"
:class="$treasury->trashed() ? 'border-dashed bg-zinc-50' : '' "
>
<livewire:slot name="checkbox">
<flux:checkbox wire:model="selected" :value="$treasury->id" wire:show="{{ !$treasury->trashed() }}" wire:cloak />
</livewire:slot>
<livewire:slot name="options">
<div class="flex gap-x-2">
<flux:button size="sm" wire:show="{{ !$treasury->trashed() }}" wire:cloak>Edit</flux:button>
<flux:button size="sm" variant="danger" wire:show="{{ !$treasury->trashed() }}" wire:cloak wire:click="delete('{{ $treasury->id }}')">Delete</flux:button>
<flux:button size="sm" wire:show="{{ $treasury->trashed() }}" wire:cloak wire:click="restore('{{ $treasury->id }}')">Restore</flux:button>
</div>
</livewire:slot>
</livewire:pages::admin.referentials.card>
@endforeach
</div>
</div>
Si je delete le background de la card et la border devrait changer, ainsi que les buttons, mais il faut que je rafraichisse la page
an.leclerc wrote a reply+100 XP
2mos ago
an.leclerc started a new conversation+100 XP
2mos ago
Hi,
I've some problems with livewire slots. It doesn't works. I follow Caleb podcast and I don't understand what's wrong with my code .
Here is my list component :
<?php
use App\Models\Referential\TreasuryTypeModel;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
new
#[Layout('layouts.admin.app')]
#[Title('Types de trésoreries')]
class extends Component {
use \Livewire\WithPagination;
public $sortBy = 'name';
public $sortDirection = 'asc';
public $selected = [];
#[Computed]
public function treasuries()
{
return TreasuryTypeModel::select('id', 'name', 'updated_at', 'deleted_at')
->tap(fn($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query)
->paginate(15);
}
public function deletedSelected()
{
TreasuryTypeModel::whereIn('id', $this->selected)->delete();
$this->selected = [];
}
public function delete(string $id)
{
TreasuryTypeModel::where('id', $id)->delete();
}
};
?>
<div class="max-w-5xl h-full">
<div class="flex items-center justify-between">
<div>
<flux:heading size="xl">Treasuries</flux:heading>
<flux:text class="mt-2">Manage your treasuries</flux:text>
</div>
<div class="flex gap-2">
@if (count($this->selected) > 0)
<div class="max-lg:hidden flex justify-start items-center gap-2">
<flux:subheading class="whitespace-nowrap">
<span>{{ count($this->selected) }}</span> selected:
</flux:subheading>
<flux:button size="sm" variant="danger" icon="trash" wire:click="deletedSelected">Delete</flux:button>
</div>
<flux:separator vertical class="max-lg:hidden mx-2 my-2" />
@endif
<flux:button variant="primary" icon="plus" size="sm">Add treasury type</flux:button>
</div>
</div>
<div class="mt-8 grid grid-cols-3 gap-6">
@foreach($this->treasuries as $treasury)
<livewire:pages::admin.referentials.treasury-types.card
:$treasury
:wire:key="$treasury->id"
:lazy.bundle="$loop->iteration > 9"
>
<livewire:slot name="checkbox">
{{ $treasury->id }}
<flux:checkbox wire:model.live="selected" :value="$treasury->id" />
</livewire:slot>
<livewire:slot name="options">
<flux:dropdown position="bottom" align="end" offset="-15">
<flux:button variant="ghost" size="sm" icon="ellipsis-vertical" inset="top bottom"></flux:button>
<flux:menu>
<flux:menu.item>Edit</flux:menu.item>
<flux:menu.item variant="danger" wire:click="delete('{{ $treasury->id }}')">delete</flux:menu.item>
</flux:menu>
</flux:dropdown>
</livewire:slot>
</livewire:pages::admin.referentials.treasury-types.card>
@endforeach
</div>
</div>
Here is my card component :
<?php
use App\Models\Referential\TreasuryTypeModel;
use Livewire\Component;
new class extends Component {
public TreasuryTypeModel $treasury;
};
?>
@placeholder
<flux:skeleton class="min-h-56 rounded-lg" animate="shimmer" />
@endplaceholder
<flux:card {{ $attributes->class('flex flex-col justify-between p-4 rounded-lg') }} variant="filled">
<div>
<div class="flex items-start justify-between">
<flux:heading size="lg" class="{{ $treasury->trashed() ? 'text-zinc-500' : 'text-zinc-600' }}">{{ $treasury->name }}</flux:heading>
@if ($slots->has('checkbox'))
{{ $slots['checkbox'] }}
@endif
</div>
<flux:text class="mt-1 text-xs {{ $treasury->trashed() ? 'text-zinc-500' : 'text-zinc-600' }}">{{ $treasury->updated_at->format('Y-m-d H:i') }}</flux:text>
</div>
<div class="mt-6 flex justify-between">
<div class="flex item-center">
@if ($treasury->trashed())
<flux:badge rounded size="sm">Inactive</flux:badge>
@else
<flux:badge rounded size="sm" variant="solid">Active</flux:badge>
@endif
</div>
@if ($slots->has('options'))
{{ $slots['options'] }}
@endif
</div>
</flux:card>
The problem is, when I check checkboxes, nothing happen. It works if I put checkbox outside of the slot but inside no. I don't understand why. Anyone can help ?
an.leclerc wrote a reply+100 XP
2mos ago
an.leclerc started a new conversation+100 XP
2mos ago
Hi,
I've set up a dispatch on my code,but it's not working. What I'm doing wrong ?
In my livewire component I have :
$this->dispatch("document-uploaded.{$this->dossier->id}");
and in my volt component :
new class extends Component {
public DossierModel $dossier;
public int $total = 0;
public int $uploaded = 0;
public bool $all_uploaded = false;
public function mount()
{
$this->total = $this->documents()->count();
$this->uploaded = $this->documents()->filter(fn($doc) => $doc->is_present)->count();
$this->all_uploaded = $this->total == $this->uploaded;
}
public function documents(): Collection
{
return (new \App\Services\DossierDocumentService())->getRequiredDocuments($this->dossier);
}
#[On('document-uploaded.{dossier.id}')]
public function refreshCount(): void
{
$this->total = $this->documents()->count();
$this->uploaded = $this->documents()->filter(fn($doc) => $doc->is_present)->count();
$this->all_uploaded = $this->total == $this->uploaded;
}
}; ?>
...
<flux:navlist.item
icon="document-text"
href="{{ route('hub.dossier.documents', ['dossier' => $this->dossier]) }}"
badge="{{ sprintf('%s/%s', $this->uploaded, $this->total) }}" badge:color="{{ $this->all_uploaded ? 'green' : 'red' }}"
wire:navigate
>
But parameters "uploaded" and "total" are not updated .
Anybody have an idea ?
an.leclerc wrote a reply+100 XP
2mos ago
an.leclerc started a new conversation+100 XP
2mos ago
Hi,
I've a problem, in my component I've 2 methods :
#[Computed]
public function resourceTypes()
{
return ResourceTypeModel::orderBy('name', 'ASC')->get();
}
#[Computed]
public function periodicityTypes()
{
return PeriodicityTypeModel::orderByRaw("FIELD(reference, 'mensuelle', 'trimestrielle', 'semestrielle', 'annuelle') DESC")->get();
}
In my blade template, I try to use foreach, it doesn't work
<flux:select variant="listbox" placeholder="Choose industry...">
@foreach($this->resourceTypes as $item)
<flux:select.option>{{ $item->name }}</flux:select.option>
@endforeach
</flux:select>
I've this error
Trying to access array offset on null (View: /var/www/vendor/laravel/framework/src/Illuminate/Foundation/resources/exceptions/renderer/markdown.blade.php)
If I change resourceTypes by resources in my blade template IT WORKS, I don't what's wrong with resourceTypes !!! If anybody can help ?
an.leclerc wrote a reply+100 XP
3mos ago
it works if I specify props in email.blade.php
<x-mail::message :logo="$logo">
...
</x-mail:message>
in message.blade.php
<x-mail::layout>
{{-- Header --}}
<x-slot:header>
<x-mail::header :url="config('app.url')" :logo="$logo">
...
</x-mail:header>
</x-slot:header>
...
</x-mail:layout>
in notification
$message->viewData['logo'] = 'https://dummyimage.com/200x60/ccc/000.png&text=DEMO+LOGO';
But it requires a lot of manipulation for very little.
an.leclerc started a new conversation+100 XP
3mos ago
Hi,
I'd like to add custom properties in laravel notifications (laravel 12). I tried with :
...
$message = (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!')
;
$message->viewData['logo'] = 'https://dummyimage.com/200x60/ccc/000.png&text=DEMO+LOGO';
...
and
<x-mail::header :url="config('app.url')" :logo="$logo" :alt="$alt">
{{ config('app.name') }}
</x-mail::header>
But it doesn't works. I tried with @props and it doesn't works to.
Anyone can help me ?