- Add internal tickets flag

- Move view share with Internal status
 - Update .env.example
This commit is contained in:
Vincent BRIDIER 2022-02-23 15:15:47 +00:00
parent 53485ee69d
commit 7d409d35da
10 changed files with 261 additions and 142 deletions

View file

@ -29,3 +29,4 @@ MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=noreply-sw@ldc.fr
MAIL_FROM_NAME="${APP_NAME}"
MAIL_LIST_SERVICEWEB=equipe.web@ldc.fr
MAIL_ADMIN=equipe.web@ldc.fr

View file

@ -7,9 +7,51 @@ use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use App\Enums\TicketStatus;
use App\Models\Ticket;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
class Controller extends BaseController
{
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
public function __construct()
{
$ignore = [
9, // Support Web
15, // Gen-Game- Web
19, // Store LDC
27, // CRM
55, // LDC - WEB
];
$ticketsNew = Ticket::where('status', TicketStatus::new());
$ticketsWaiting = Ticket::where('status', TicketStatus::waiting());
$ticketsInProgress = Ticket::whereNotIn(
'status',
[
TicketStatus::new(),
TicketStatus::estimateTodo(),
TicketStatus::estimateValidated(),
TicketStatus::toPlan(),
TicketStatus::planned(),
TicketStatus::waiting(),
TicketStatus::production(),
TicketStatus::archived()
]
);
if (!Auth::user()->isAdmin()) {
$ticketsNew = $ticketsNew->where('is_internal', 0)->whereNotIn('site_id', $ignore);
$ticketsWaiting = $ticketsWaiting->where('is_internal', 0)->whereNotIn('site_id', $ignore);
$ticketsInProgress = $ticketsInProgress->where('is_internal', 0)->whereNotIn('site_id', $ignore);
}
View::share('ticketsNew', $ticketsNew->count());
View::share('ticketsWaiting', $ticketsWaiting->count());
View::share('ticketsInProgress', $ticketsInProgress->count());
}
}

View file

@ -29,6 +29,7 @@ class TicketController extends Controller
{
public function __construct()
{
parent::__construct();
$this->authorizeResource(Ticket::class, 'ticket');
}
@ -39,7 +40,7 @@ class TicketController extends Controller
$tickets = Ticket::with('user');
if ($request->s) {
$tickets = $tickets->where('text', 'LIKE', "%$request->s%")
->orWhere('id', $request->s)
->orWhere('id', $request->s)
->orWhereHas('user', function ($q) use ($request) {
$q->where('name', 'LIKE', "%$request->s%");
});
@ -69,9 +70,10 @@ class TicketController extends Controller
$q->whereId($user->id);
});
});
$tickets = $tickets->where('is_internal', 0);
}
$tickets = $tickets->where('status', ($request->status == TicketStatus::archived()) ?
'=' : '<>', TicketStatus::archived());
'=' : '<>', TicketStatus::archived());
if ($request->search) {
$tickets = $this->searchTicket($tickets, $request);
}
@ -184,8 +186,19 @@ class TicketController extends Controller
'value' => $ticket->id
]);
}
$user = Auth::user();
if (!$user->can('update', $ticket)) {
abort(403);
}
if ($ticket->is_internal === 1 && !$user->can('changeInternalStatus', $ticket)) {
abort(403);
}
$messages = $ticket->messages()->orderBy('created_at', 'DESC')->get();
$isSubscribed = (bool)$ticket->subscriptions()->where('user_id', Auth::user()->id)->count();
$isSubscribed = (bool)$ticket->subscriptions()->where('user_id', $user->id)->count();
return view('ticket.show', compact('ticket', 'messages', 'isSubscribed'));
}
@ -219,6 +232,12 @@ class TicketController extends Controller
$ticket->file2 = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket2);
}
if ($request->has('is_internal')) {
$ticket->is_internal = 1;
} else {
$ticket->is_internal = 0;
}
$ticket->user()->associate(auth()->user());
$ticket->saveOrFail();
/** @var \App\Models\User */
@ -266,6 +285,12 @@ class TicketController extends Controller
$ticket->file2 = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket2);
}
if ($request->has('is_internal')) {
$ticket->is_internal = 1;
} else {
$ticket->is_internal = 0;
}
$ticket->save();
return redirect()->route('ticket.edit', $ticket->id)
@ -309,7 +334,7 @@ class TicketController extends Controller
$siteSlug = Str::slug($ticket->site->name);
$fileMessage = $request->file_message;
$fileName = $siteSlug . '-ticket-' . $ticket->id .
'-message-' . $message->id . '.' . $fileMessage->extension();
'-message-' . $message->id . '.' . $fileMessage->extension();
$fileMessage->move(public_path('fichiers/' . $siteSlug . '/' . $ticket->id . '/'), $fileName);
$filePath = 'fichiers/' . $siteSlug . '/' . $ticket->id . '/' . $fileName;
$message->file = $filePath;
@ -326,7 +351,7 @@ class TicketController extends Controller
$ticket->status = TicketStatus::archived();
$ticket->saveOrFail();
return redirect()->route('ticket.index')
->with('warning', trans('Le ticket est archivé'));
->with('warning', trans('Le ticket est archivé'));
}
public function subscribe(Ticket $ticket)

View file

@ -31,7 +31,8 @@ class Ticket extends Model
'site_id',
'type',
'status',
'text'
'text',
'is_internal'
];
protected static function booted()
@ -125,11 +126,22 @@ class Ticket extends Model
})->orWhere('role', Role::admin()->value)
->orWhere('role', Role::developper()->value);
})->firstOrFail();
$usersNotified->add($user);
SendTicketMentionMail::dispatchAfterResponse(
$user,
new TicketMention($ticket, $message->message)
);
if ($ticket->is_internal === 1) {
if ($user->isAdmin()) {
$usersNotified->add($user);
SendTicketMentionMail::dispatchAfterResponse(
$user,
new TicketMention($ticket, $message->message)
);
}
} else {
$usersNotified->add($user);
SendTicketMentionMail::dispatchAfterResponse(
$user,
new TicketMention($ticket, $message->message)
);
}
} catch (ModelNotFoundException $e) {
continue;
}

View file

@ -38,6 +38,7 @@ class TicketPolicy extends RootPolicy
SSR::make('userSitesHasTicket', $user, $model)
]),
'changeStatus' => URR::make('userIsAdmin', $user),
'changeInternalStatus' => URR::make('userIsAdmin', $user),
'delete' => URR::make('denyEveryOne'),
'forceDelete' => URR::make('denyEveryOne'),
'restore' => StaticRules::or([

View file

@ -2,8 +2,7 @@
namespace App\Providers;
use App\Enums\TicketStatus;
use App\Models\Ticket;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\URL;
@ -43,21 +42,5 @@ class AppServiceProvider extends ServiceProvider
View::share('name', $appInfos->name);
View::share('version', $appInfos->version);
View::share('license', $appInfos->license);
View::share('ticketsNew', Ticket::where('status', TicketStatus::new())->count());
View::share('ticketsWaiting', Ticket::where('status', TicketStatus::waiting())->count());
View::share('ticketsInProgress', Ticket::whereNotIn(
'status',
[
TicketStatus::new(),
TicketStatus::estimateTodo(),
TicketStatus::estimateValidated(),
TicketStatus::toPlan(),
TicketStatus::planned(),
TicketStatus::waiting(),
TicketStatus::production(),
TicketStatus::archived()
]
)->count());
}
}

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsInternalOnTicketsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tickets', function (Blueprint $table) {
$table->tinyInteger('is_internal')->default(0)->after('status');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tickets', function (Blueprint $table) {
$table->dropColumn('is_internal')->default(0);
});
}
}

View file

@ -15,7 +15,7 @@ $userPr = Auth::user()->sites ?? collect([]);
</select>
</div>
<div class="form-group col">
<label for="type" class="col-form-label">{{ __('Type') }}</label>
<label for="type" class="col-form-label">{{ __('Type') }}</label>
<select class="form-select" name="type" id="type">
@foreach(TicketType::toArray() as $ticketType)
<option value="{{ $ticketType->value }}" @if($ticketType->value == old('type') or (isset($ticket) and $ticketType->equals($ticket->type)))selected @endif>{{ $ticketType->label }}</option>
@ -32,7 +32,7 @@ $userPr = Auth::user()->sites ?? collect([]);
</select>
</div>
@else
<input type="hidden" name="status" value="{{ \App\Enums\TicketStatus::new() }}" required>
<input type="hidden" name="status" value="{{ \App\Enums\TicketStatus::new() }}" required>
@endcan
</div>
@ -60,3 +60,9 @@ $userPr = Auth::user()->sites ?? collect([]);
</div>
@endif
</div>
@can('changeInternalStatus', App\Model\Ticket::class)
<div class="form-group text-center">
<input type="checkbox" name="is_internal" id="is_internal" @if ((old('is_internal') && old('is_internal')===1) || (isset($ticket) && $ticket->is_internal === 1)) checked @endif>
<label for="is_internal" class="col-form-label">Ticket interne</label>
</div>
@endcan

View file

@ -24,9 +24,9 @@ $userPr = Auth::user()->sites ?? collect([]);
<option value="-1">{{ __('Tous') }}</option>
@foreach($sites as $site)
@if (Auth::user()->isAdmin())
<option value="{{ $site->id }}" @if(intval(session('tickets.pr')) == $site->id)selected="selected" @endif>{{ $site->name }}</option>
<option value="{{ $site->id }}" @if(intval(session('tickets.pr'))==$site->id)selected="selected" @endif>{{ $site->name }}</option>
@elseif ($userPr->filter(function($pr) use($site) { return $pr->id === $site->id; })->count())
<option value="{{ $site->id }}" @if(intval(session('tickets.pr')) == $site->id)selected="selected" @endif>{{ $site->name }}</option>
<option value="{{ $site->id }}" @if(intval(session('tickets.pr'))==$site->id)selected="selected" @endif>{{ $site->name }}</option>
@endif
@endforeach
</select>
@ -35,7 +35,7 @@ $userPr = Auth::user()->sites ?? collect([]);
<select class="form-select" onchange="window.location='{{ route('ticket.index') }}?'+(this.value == -1 ? 'sts=-1' : 'st='+this.value);">
<option value="-1">{{ __('Tous') }}</option>
@foreach($status as $statut)
<option value="{{ $statut->value }}" @if(intval(session('tickets.st')) == $statut->value)selected="selected" @endif>{{ $statut->label }}</option>
<option value="{{ $statut->value }}" @if(intval(session('tickets.st'))==$statut->value)selected="selected" @endif>{{ $statut->label }}</option>
@endforeach
</select>
</div>
@ -67,15 +67,18 @@ $userPr = Auth::user()->sites ?? collect([]);
</th>
<th scope="col">
{{__('Type') }}&nbsp;
@include('modules.sorter', ['colname' => 'type'])</th>
@include('modules.sorter', ['colname' => 'type'])
</th>
<th scope="col">{{__('Fichier') }}</th>
<th scope="col">{{__('Fichier 2') }}</th>
<th scope="col">
{{__('Date de création') }}&nbsp;
@include('modules.sorter', ['colname' => 'created_at'])</th>
@include('modules.sorter', ['colname' => 'created_at'])
</th>
<th scope="col">
{{__('Créé par') }}&nbsp;
@include('modules.sorter', ['colname' => 'created_by'])</th>
@include('modules.sorter', ['colname' => 'created_by'])
</th>
<th scope="col">
{{__('Statut') }}&nbsp;
@include('modules.sorter', ['colname' => 'status'])
@ -87,34 +90,39 @@ $userPr = Auth::user()->sites ?? collect([]);
<tbody>
@if(!count($tickets))
<tr>
<td colspan="10" class="text-center"><h2>{{ __('Aucun ticket') }}</h2></td>
<td colspan="10" class="text-center">
<h2>{{ __('Aucun ticket') }}</h2>
</td>
</tr>
@endif
@foreach ($tickets as $ticket)
@php
$cls = $ticket->status == TicketStatus::archived() ? 'text-secondary' : null;
$cls = (!$cls && $ticket->status == TicketStatus::new()) ? 'text-danger' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::processed()) ? 'text-info' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::recette()) ? 'text-primary' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::production()) ? 'text-success' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::inprogress()) ? 'text-dark' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::waiting()) ? 'text-white bg-danger' : $cls;
$ticketClass = 'bg-primary';
switch($ticket->type) {
case TicketType::bug():
$ticketClass = 'bg-danger';
break;
case TicketType::evolution():
$ticketClass = 'bg-success';
break;
case TicketType::newsletter():
$ticketClass = 'bg-warning';
break;
}
$cls = $ticket->status == TicketStatus::archived() ? 'text-secondary' : null;
$cls = (!$cls && $ticket->status == TicketStatus::new()) ? 'text-danger' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::processed()) ? 'text-info' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::recette()) ? 'text-primary' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::production()) ? 'text-success' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::inprogress()) ? 'text-dark' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::waiting()) ? 'text-white bg-danger' : $cls;
$ticketClass = 'bg-primary';
switch($ticket->type) {
case TicketType::bug():
$ticketClass = 'bg-danger';
break;
case TicketType::evolution():
$ticketClass = 'bg-success';
break;
case TicketType::newsletter():
$ticketClass = 'bg-warning';
break;
}
@endphp
<tr>
<td scope="row">
{{ $ticket->id }}
{{ $ticket->id }}
@if ($ticket->is_internal === 1)
🔒
@endif
</td>
<td>{{ $ticket->site->name }}</td>
<td>
@ -122,12 +130,12 @@ $userPr = Auth::user()->sites ?? collect([]);
</td>
<td>
@if ($ticket->file !== null)
<a data-fslightbox="gallery" href="{{ asset($ticket->file) }}" class="btn btn-sm btn-outline-primary" target="_blank">Fichier</a>
<a data-fslightbox="gallery" href="{{ asset($ticket->file) }}" class="btn btn-sm btn-outline-primary" target="_blank">Fichier</a>
@endif
</td>
<td>
@if ($ticket->file2 !== null)
<a data-fslightbox="gallery" href="{{ asset($ticket->file2) }}" class="btn btn-sm btn-outline-primary" target="_blank">Fichier 2</a>
<a data-fslightbox="gallery" href="{{ asset($ticket->file2) }}" class="btn btn-sm btn-outline-primary" target="_blank">Fichier 2</a>
@endif
</td>
<td>{{ Carbon\Carbon::parse($ticket->created_at)->format('d/m/Y à H:i') }}</td>
@ -143,23 +151,29 @@ $userPr = Auth::user()->sites ?? collect([]);
<!-- Modal -->
<div class="modal fade" id="ticket{{ $ticket->id }}Modal" tabindex="-1" aria-labelledby="ticket{{ $ticket->id }}ModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ticket{{ $ticket->id }}ModalLabel">
<b>{{ $ticket->site->name }}</b> - <u>{{__('Ticket n°') }} {{ $ticket->id }}</u>&nbsp;-&nbsp;
<span class="badge {{ $ticketClass }}">{{
$ticket->type->label
}}</span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ __('Fermer') }}"></button>
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ticket{{ $ticket->id }}ModalLabel">
<b>{{ $ticket->site->name }}</b> -
<u>
{{__('Ticket n°') }} {{ $ticket->id }}
@if ($ticket->is_internal === 1)
🔒
@endif
</u>&nbsp;-&nbsp;
<span class="badge {{ $ticketClass }}">
{{$ticket->type->label}}
</span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ __('Fermer') }}"></button>
</div>
<div class="modal-body">
<p>{!! clean(nl2br($ticket->text)) !!}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-primary" data-bs-dismiss="modal">{{ __('Fermer') }}</button>
</div>
</div>
<div class="modal-body">
<p>{!! clean(nl2br($ticket->text)) !!}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-primary" data-bs-dismiss="modal">{{ __('Fermer') }}</button>
</div>
</div>
</div>
</div>
</td>
@ -184,4 +198,4 @@ $userPr = Auth::user()->sites ?? collect([]);
</table>
{{ $tickets->withQueryString()->links() }}
</div>
@endsection
@endsection

View file

@ -4,20 +4,20 @@
<div class="form-row row">
<h2 class="col">
@php
$back = route('ticket.index');
$url = parse_url(url()->previous());
if (
($url['host'] ?? '') === parse_url(config('app.url'), PHP_URL_HOST) and
($url['path'] ?? '') === '/ticket'
) {
$queries = explode('&', $url['query'] ?? '');
foreach($queries as $q) {
$q = explode('=', $q);
if(($q[0] ?? '') === 'pr' and ctype_digit(($q[1] ?? ''))) {
$back = route('ticket.index', ['pr' => $q[1]]);
}
}
}
$back = route('ticket.index');
$url = parse_url(url()->previous());
if (
($url['host'] ?? '') === parse_url(config('app.url'), PHP_URL_HOST) and
($url['path'] ?? '') === '/ticket'
) {
$queries = explode('&', $url['query'] ?? '');
foreach($queries as $q) {
$q = explode('=', $q);
if(($q[0] ?? '') === 'pr' and ctype_digit(($q[1] ?? ''))) {
$back = route('ticket.index', ['pr' => $q[1]]);
}
}
}
@endphp
<a href="{{ $back }}" class="text-decoration-none">←</a>
{{ __('Ticket n° :ticketId : :ticketSiteName - :ticketTypeLabel', [
@ -25,6 +25,9 @@
'ticketSiteName' => $ticket->site->name,
'ticketTypeLabel' => $ticket->type->label
]) }}
@if ($ticket->is_internal === 1)
🔒
@endif
<span class="badge bg-info">{{ $ticket->status->label }}</span>
</h2>
<div class="col d-flex flex-row text-end">
@ -67,66 +70,66 @@
</button>
<div class="modal fade" id="messageModal" tabindex="-1" aria-labelledby="messageModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form action="{{ route('ticket.message.create', $ticket) }}" method="POST" enctype="multipart/form-data">
@method('PUT')
@csrf
<input type="hidden" name="ticket_id" value="{{ $ticket->id }}">
<div class="modal-header">
<h5 class="modal-title" id="messageModalLabel">
{{ __('Répondre au ticket n°:ticketId', ['ticketId' => $ticket->id]) }}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{__('Fermer') }}"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col">
<textarea name="message" class="form-control" rows="10"></textarea>
</div>
<div class="modal-content">
<form action="{{ route('ticket.message.create', $ticket) }}" method="POST" enctype="multipart/form-data">
@method('PUT')
@csrf
<input type="hidden" name="ticket_id" value="{{ $ticket->id }}">
<div class="modal-header">
<h5 class="modal-title" id="messageModalLabel">
{{ __('Répondre au ticket n°:ticketId', ['ticketId' => $ticket->id]) }}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{__('Fermer') }}"></button>
</div>
<div class="row mt-4">
<div class="col">
<label for="formFile" class="form-label">{{ __('Joindre un fichier (jpg, png, pdf) 2Mo max') }}</label>
<input class="form-control" type="file" id="formFile" name="file_message" accept="image/jpeg, image/png, image/jpg, .pdf">
<div class="modal-body">
<div class="row">
<div class="col">
<textarea name="message" class="form-control" rows="10"></textarea>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col">
<div class="form-check">
<input name="subscribe" type="checkbox" class="form-check-input" id="subscribeResponse" autocomplete="off" @if ($isSubscribed)checked @endif value="1">
<label class="form-check-label" for="subscribeResponse">{{ __('Suivre le ticket') }}</label>
<div class="row mt-4">
<div class="col">
<label for="formFile" class="form-label">{{ __('Joindre un fichier (jpg, png, pdf) 2Mo max') }}</label>
<input class="form-control" type="file" id="formFile" name="file_message" accept="image/jpeg, image/png, image/jpg, .pdf">
</div>
</div>
<div class="row mt-4">
<div class="col">
<div class="form-check">
<input name="subscribe" type="checkbox" class="form-check-input" id="subscribeResponse" autocomplete="off" @if ($isSubscribed)checked @endif value="1">
<label class="form-check-label" for="subscribeResponse">{{ __('Suivre le ticket') }}</label>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-sm btn-danger" data-bs-dismiss="modal">{{ __('Fermer') }}</button>
<button type="submit" class="btn btn-sm btn-success">{{ __('Envoyer') }}</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-sm btn-danger" data-bs-dismiss="modal">{{ __('Fermer') }}</button>
<button type="submit" class="btn btn-sm btn-success">{{ __('Envoyer') }}</button>
</div>
</form>
</div>
</div>
</div>
@endcan
</h4>
@foreach ($messages as $message)
@if ($message->user_id === null)
<div class="offset-md-3 col-md-6 alert alert-info text-center text-dark">
{!! nl2br(clean($message->message)) !!} - <span class="fw-bold text-light">Le {{ \Carbon\Carbon::parse($message->created_at)->format('d/m/Y à H:i')}}</span>
<small class="fw-bold">({{ __('Message système') }})</small>
</div>
@else
<div class="@if($message->user_id === Auth::user()->id)offset-md-4 alert-dark @else alert-light @endif col-md-8 alert">
{!! Utils::plainToHtml($message->message) !!}
@if ($message->file !== null)
<br><a @if(Utils::isImageFile($message->file))data-fslightbox="gallery" @endif href="{{ asset($message->file) }}" target="_blank">Ouvrir la pièce jointe</a><br>
@endif<br><br>
<small class="text-muted fw-bold">{!! __('Créé le :date par <b>:userName</b>', [
'date' => \Carbon\Carbon::parse($message->created_at)->format('d/m/Y à H:i'),
'userName' => $message->user ? $message->user->name : __('Utilisateur supprimé')
@if ($message->user_id === null)
<div class="offset-md-3 col-md-6 alert alert-info text-center text-dark">
{!! nl2br(clean($message->message)) !!} - <span class="fw-bold text-light">Le {{ \Carbon\Carbon::parse($message->created_at)->format('d/m/Y à H:i')}}</span>
<small class="fw-bold">({{ __('Message système') }})</small>
</div>
@else
<div class="@if($message->user_id === Auth::user()->id)offset-md-4 alert-dark @else alert-light @endif col-md-8 alert">
{!! Utils::plainToHtml($message->message) !!}
@if ($message->file !== null)
<br><a @if(Utils::isImageFile($message->file))data-fslightbox="gallery" @endif href="{{ asset($message->file) }}" target="_blank">Ouvrir la pièce jointe</a><br>
@endif<br><br>
<small class="text-muted fw-bold">{!! __('Créé le :date par <b>:userName</b>', [
'date' => \Carbon\Carbon::parse($message->created_at)->format('d/m/Y à H:i'),
'userName' => $message->user ? $message->user->name : __('Utilisateur supprimé')
]) !!}</small>
</div>
@endif
</div>
@endif
@endforeach
</div>
<hr>
@ -136,7 +139,7 @@
<small class="text-muted">{!! __('Créé le :date par <b>:userName</b>', [
'date' => \Carbon\Carbon::parse($ticket->created_at)->format('d/m/Y à H:i'),
'userName' => $ticket->user ? $ticket->user->name : __('Utilisateur supprimé')
]) !!}</small>
]) !!}</small>
</div>
@if (!is_null($ticket->file) or !is_null($ticket->file2))
<hr>
@ -158,4 +161,4 @@
</ul>
</div>
@endif
@endsection
@endsection