diff --git a/app/Casts/HtmlColor.php b/app/Casts/HtmlColor.php new file mode 100644 index 0000000..de6e071 --- /dev/null +++ b/app/Casts/HtmlColor.php @@ -0,0 +1,56 @@ +zeropad(dechex($value), 6)}"; + } + + /** + * Prepare the given value for storage. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param mixed $value + * @param array $attributes + * @return integer + * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed + * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed + */ + public function set($model, $key, $value, $attributes) + { + return \intval(hexdec(ltrim($value, '#'))); + } + + /** + * Pad with zeros a numeric string + * + * @param string $num + * @param integer $lim + * @return string + */ + private function zeropad(string $num, int $lim): string + { + return (strlen($num) >= $lim) ? $num : $this->zeropad("0" . $num, $lim); + } +} diff --git a/app/Http/Controllers/LabelController.php b/app/Http/Controllers/LabelController.php new file mode 100644 index 0000000..490c3e7 --- /dev/null +++ b/app/Http/Controllers/LabelController.php @@ -0,0 +1,21 @@ +user(); - $tickets = Ticket::with('site')->has('site'); + $tickets = Ticket::with(['site', 'labels']); if (!$user->isAdmin()) { - $tickets = $tickets->whereHas('site', function ($q) use ($user) { + $tickets->whereHas('site', function ($q) use ($user) { $q->whereHas('users', function ($q) use ($user) { $q->whereId($user->id); }); }); - $tickets = $tickets->where('is_internal', false); + $tickets->where('is_internal', false); + } else { + $tickets->has('site'); } - $tickets = $tickets->where('status', ($request->st == TicketStatus::archived()) ? - '=' : '<>', TicketStatus::archived()); + + $tickets->where( + 'status', + ($request->st == TicketStatus::archived()) ? '=' : '<>', + TicketStatus::archived() + ); $tickets = $this->searchTicket($tickets, $request); $tickets = $this->filterTickets($tickets, $request); @@ -184,6 +190,9 @@ class TicketController extends Controller $ticket->file2 = ToolboxHelper::storeImage($ticket->getTable(), $fileTicket2); } + if ($request->labels) { + $ticket->labels()->sync($request->labels); + } $ticket->user()->associate(auth()->user()); $ticket->site()->associate($site); $ticket->saveOrFail(); @@ -254,6 +263,10 @@ class TicketController extends Controller $ticket->file2 = ToolboxHelper::storeImage($ticket->getTable(), $fileTicket2); } + if ($request->labels) { + $ticket->labels()->sync($request->labels); + } + $ticket->site()->associate($site); $ticket->saveOrFail(); diff --git a/app/Http/Middleware/SecureHeaders.php b/app/Http/Middleware/SecureHeaders.php index 1a04dc5..15047e0 100644 --- a/app/Http/Middleware/SecureHeaders.php +++ b/app/Http/Middleware/SecureHeaders.php @@ -46,7 +46,7 @@ class SecureHeaders // phpcs:ignore Generic.Files.LineLength.TooLong "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;", // phpcs:ignore Generic.Files.LineLength.TooLong - "script-src 'self' $jsDev;" + "script-src 'self' $jsDev https://cdnjs.cloudflare.com;" ]; $response->headers->set( 'Content-Security-Policy', diff --git a/app/Http/Requests/StoreTicket.php b/app/Http/Requests/StoreTicket.php index 01db4fd..96b2ead 100644 --- a/app/Http/Requests/StoreTicket.php +++ b/app/Http/Requests/StoreTicket.php @@ -4,6 +4,7 @@ namespace App\Http\Requests; use App\Enums\TicketStatus; use App\Enums\TicketType; +use App\Models\Ticket; use Illuminate\Foundation\Http\FormRequest; class StoreTicket extends FormRequest @@ -50,6 +51,8 @@ class StoreTicket extends FormRequest */ public function rules() { + /** @var \App\Models\User */ + $user = auth()->user(); return [ 'site_id' => [ 'required', @@ -93,7 +96,16 @@ class StoreTicket extends FormRequest 'text' => 'required|string|min:40', 'file_ticket' => 'mimes:jpeg,jpg,png,pdf|max:2048', 'file_ticket2' => 'mimes:jpeg,jpg,png,pdf|max:2048', - 'is_internal' => 'required|boolean' + 'is_internal' => 'required|boolean', + 'labels' => [ + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + 'sometimes', 'array', function ($attribute, $value, $fail) use ($user) { + if (!$user->can('assignLabels', Ticket::class)) { + $fail(trans('Vous n\'êtes pas autorisé à modifier cette valeur')); + } + } + ], + 'labels.*' => 'required|exists:labels,id' ]; } } diff --git a/app/Lib/ToolboxHelper.php b/app/Lib/ToolboxHelper.php index ab9ca09..32b48ca 100644 --- a/app/Lib/ToolboxHelper.php +++ b/app/Lib/ToolboxHelper.php @@ -3,12 +3,15 @@ namespace App\Lib; use App\Enums\LoadLevel; +use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\View; use Illuminate\Support\Str; +use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -252,4 +255,35 @@ class ToolboxHelper } return $fullPath; } + + /** + * Assert Is Unique + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param array $columns + * @return void + * @throws \Illuminate\Validation\ValidationException If a model like this already exists. + */ + public static function assertIsUnique(Model $model, array $columns): void + { + $columns = collect($columns); + /** @var string|null */ + $firstColumn = $columns->pull($columns->keys()->first()); + if (!$firstColumn) { + return; + } + $modelClass = \get_class($model); + $uniqueRule = Rule::unique((new $modelClass())->getTable()); + if ($model->id) { + $uniqueRule->ignore($model->id); + } + $uniqueRule->where(function ($q) use ($model, $columns) { + $columns->map(function (string $column) use ($model, $q) { + $q->where($column, $model->{$column}); + }); + }); + Validator::make([$firstColumn => $model->{$firstColumn}], [ + $firstColumn => ['required', $uniqueRule] + ])->validate(); + } } diff --git a/app/Models/Label.php b/app/Models/Label.php new file mode 100644 index 0000000..f38d4a4 --- /dev/null +++ b/app/Models/Label.php @@ -0,0 +1,136 @@ + HtmlColor::class, + ]; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', 'color' + ]; + + /** + * Site + * + * @param array $attributes + */ + public function __construct(array $attributes = []) + { + self::$editableProperties = [ + 'name' => [ + 'name' => trans('Libellé'), + 'type' => CrudType::string(), + 'validate' => [ + 'required', + 'min:2', + 'max:255', + (new Unique('labels', 'name'))->ignore(request()->label) + ], + 'required' => true, + 'nullable' => false, + 'actions' => [ + CrudAction::viewAny(), + CrudAction::create(), + CrudAction::update(), + ] + ], + 'color' => [ + 'label' => trans('Couleur'), + 'type' => CrudType::color(), + 'validate' => ['required', new Hex()], + 'required' => true, + 'nullable' => false, + 'actions' => [ + CrudAction::viewAny(), + CrudAction::create(), + CrudAction::update(), + ] + ] + ]; + parent::__construct($attributes); + } + + /** + * Perform any actions required after the model boots. + * + * @return void + */ + protected static function booted() + { + static::creating(function (self $model) { + ToolboxHelper::assertIsUnique($model, ['name']); + }); + static::updating(function (self $model) { + ToolboxHelper::assertIsUnique($model, ['name']); + }); + } + + // * RELATIONSHIPS + + /** + * BelongsToMany Ticket relation + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function tickets() + { + return $this->belongsToMany(Ticket::class); + } + + /** + * BelongsTo Site relation + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function site() + { + return $this->belongsTo(Site::class); + } +} diff --git a/app/Models/Site.php b/app/Models/Site.php index dba17f9..af32bdc 100644 --- a/app/Models/Site.php +++ b/app/Models/Site.php @@ -174,31 +174,12 @@ class Site extends Model */ protected static function booted() { - static::created(function (Site $site) { - $path = Site::ticketAssetsFullPath($site->slug_name); - if (!File::exists($path)) { - File::makeDirectory($path, 0755, true); - } + static::created(function (self $site) { + Label::createDefaultLabelsFor($site); }); - static::updating(function (Site $site) { - $oldName = Site::where('id', $site->id)->first()->name; - $oldPath = Site::ticketAssetsFullPath($oldName); - $newPath = Site::ticketAssetsFullPath($site->slug_name); - if (File::exists($oldPath)) { - File::move($oldPath, $newPath); - } elseif (!File::exists($newPath)) { - File::makeDirectory($newPath, 0755, true); - } - }); - static::updated(function (Site $site) { + static::updated(function (self $site) { event(new SiteUpdated($site, \auth()->user())); }); - static::deleted(function (Site $site) { - $path = Site::ticketAssetsFullPath($site->slug_name); - if (File::exists($path)) { - File::deleteDirectory($path); - } - }); } // * RELATIONSHIPS @@ -265,14 +246,4 @@ class Site extends Model { return Str::slug($this->name); } - - public static function ticketAssetsFullPath(string $str) - { - return public_path(self::ticketAssetsPath($str)); - } - - public static function ticketAssetsPath(string $str) - { - return sprintf('fichiers/%s', $str); - } } diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index 372b5a4..4cdc6f5 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -142,6 +142,16 @@ class Ticket extends Model return $this->belongsToMany(User::class, 'subscriptions'); } + /** + * BelongsToMany Label relation + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function labels() + { + return $this->belongsToMany(Label::class, 'label_ticket'); + } + // * METHODS /** diff --git a/app/Policies/LabelPolicy.php b/app/Policies/LabelPolicy.php new file mode 100644 index 0000000..7e57ccf --- /dev/null +++ b/app/Policies/LabelPolicy.php @@ -0,0 +1,37 @@ +policies = [ + 'label' => [ + 'viewAny' => URR::make('userIsAdmin', $user), + 'create' => URR::make('userIsAdmin', $user), + 'update' => URR::make('userIsAdmin', $user), + 'delete' => URR::make('userIsAdmin', $user) + ] + ]; + } +} diff --git a/app/Policies/TicketPolicy.php b/app/Policies/TicketPolicy.php index 5dbdb0c..fd2fca7 100644 --- a/app/Policies/TicketPolicy.php +++ b/app/Policies/TicketPolicy.php @@ -55,7 +55,9 @@ class TicketPolicy extends RootPolicy URR::make('userIsAdmin', $user), TicketStaticRules::make('ticketIsNotInProduction', $model), TicketStaticRules::make('ticketIsNotArchived', $model) - ]) + ]), + 'assignTickets' => URR::make('userIsAdmin', $user), + 'assignLabels' => URR::make('userIsAdmin', $user) ] ]; } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index be41a7d..f1f6c37 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use App\Models\File; +use App\Models\Label; use App\Models\Log; use App\Models\MacroSchedule\Marker; use App\Models\Project; @@ -47,6 +48,7 @@ class RouteServiceProvider extends ServiceProvider ->namespace($this->namespace) ->group(base_path('routes/web.php')); }); + Route::model('label', Label::class); Route::model('marker', Marker::class); Route::model('file', File::class); Route::model('log', Log::class); diff --git a/composer.json b/composer.json index 89ce10a..0ac633a 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "kwaadpepper/enum": "^2.1", "kwaadpepper/exception-handler": "^0.0.5", "laravel-lang/lang": "~8.0", + "laravel-validation-rules/colour": "^1.6", "laravel/framework": "^8.12", "laravel/tinker": "^2.5", "laravel/ui": "^3.3", diff --git a/composer.lock b/composer.lock index d3cab31..1c50d9c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2a618153177d1ef96c1f71551d669f8c", + "content-hash": "a9e301f0bab428769de214ee3b71cd46", "packages": [ { "name": "asm89/stack-cors", @@ -1743,6 +1743,59 @@ }, "time": "2021-04-14T17:41:45+00:00" }, + { + "name": "laravel-validation-rules/colour", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/laravel-validation-rules/colour.git", + "reference": "1e7ab2420879738f45adcf0320f8c571789e9fb4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel-validation-rules/colour/zipball/1e7ab2420879738f45adcf0320f8c571789e9fb4", + "reference": "1e7ab2420879738f45adcf0320f8c571789e9fb4", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^5.5|^6.0|^7.0|^8.0|^9.0" + }, + "require-dev": { + "orchestra/testbench": "^5.0|^7.0", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "LVR\\Colour\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ashley Clarke", + "email": "me@ashleyclarke.me" + }, + { + "name": "Scott Robinson", + "email": "scott@dor.ky" + } + ], + "description": "Validate colours", + "keywords": [ + "colour", + "laravel", + "validation" + ], + "support": { + "issues": "https://github.com/laravel-validation-rules/colour/issues", + "source": "https://github.com/laravel-validation-rules/colour/tree/1.6.0" + }, + "time": "2022-02-08T08:13:39+00:00" + }, { "name": "laravel/framework", "version": "v8.83.13", diff --git a/database/migrations/2022_05_25_120339_create_labels_table.php b/database/migrations/2022_05_25_120339_create_labels_table.php new file mode 100644 index 0000000..94e75bb --- /dev/null +++ b/database/migrations/2022_05_25_120339_create_labels_table.php @@ -0,0 +1,44 @@ +id(); + $table->string('name')->unique()->comment('Un ou deux mot désignant le label'); + $table->unsignedInteger('color')->comment('Cast HTML color'); + }); + + $defaultLabels = [ + ['name' => 'urgent', 'color' => '#ebb734'], + ['name' => 'bloquant', 'color' => '#851400'] + ]; + collect($defaultLabels)->map(function (array $defaultLabel) { + $label = new Label(); + $label->name = $defaultLabel['name']; + $label->color = $defaultLabel['color']; + $label->saveOrFail(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('labels'); + } +} diff --git a/database/migrations/2022_05_25_121407_create_label_ticket_table.php b/database/migrations/2022_05_25_121407_create_label_ticket_table.php new file mode 100644 index 0000000..29bd99d --- /dev/null +++ b/database/migrations/2022_05_25_121407_create_label_ticket_table.php @@ -0,0 +1,35 @@ +foreignIdFor(Ticket::class) + ->constrained()->cascadeOnDelete(); + $table->foreignIdFor(Label::class) + ->constrained()->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('label_ticket'); + } +} diff --git a/resources/js/app.js b/resources/js/app.js index b165fd3..b484615 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -2,3 +2,4 @@ require('./bootstrap') require('./session_watch') require('./vue') require('./files_add') +require('./label_colorize') diff --git a/resources/js/components/LabelSelectComponent.vue b/resources/js/components/LabelSelectComponent.vue new file mode 100644 index 0000000..9be286e --- /dev/null +++ b/resources/js/components/LabelSelectComponent.vue @@ -0,0 +1,33 @@ + + + + + {{ option.label }} + + + + + + + diff --git a/resources/js/label_colorize.js b/resources/js/label_colorize.js new file mode 100644 index 0000000..377a6cb --- /dev/null +++ b/resources/js/label_colorize.js @@ -0,0 +1,19 @@ +import colors from './modules/colors' + +document.addEventListener('DOMContentLoaded', () => { + + // Ticket index colorize labels + if (document.querySelector('.content.ticketsIndex') || document.querySelector('.content.ticketsShow')) { + let labels = document.querySelectorAll('.label.colorize') + + labels.forEach(label => { + if (label.dataset.color) { + if (colors.methods.isrgb(label.dataset.color)) { + label.dataset.color = colors.methods.rgb2hex(label.dataset.color) + } + label.style.color = colors.methods.invertColor(label.dataset.color, true) + label.style.backgroundColor = label.dataset.color + } + }) + } +}) diff --git a/resources/js/modules/colors.js b/resources/js/modules/colors.js new file mode 100644 index 0000000..4ab1bd7 --- /dev/null +++ b/resources/js/modules/colors.js @@ -0,0 +1,40 @@ +export default { + methods: { + isrgb(rgb) { + return (String(rgb).match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) ?? []).length !== 0 + }, + rgb2hex(rgb) { + return `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map( + n => parseInt(n, 10).toString(16).padStart(2, '0') + ).join('')}` + }, + invertColor(hex, bw) { + if (hex.indexOf('#') === 0) { + hex = hex.slice(1); + } + // convert 3-digit hex to 6-digits. + if (hex.length === 3) { + hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + } + if (hex.length !== 6) { + throw new Error('Invalid HEX color.'); + } + var r = parseInt(hex.slice(0, 2), 16), + g = parseInt(hex.slice(2, 4), 16), + b = parseInt(hex.slice(4, 6), 16); + if (bw) { + // https://stackoverflow.com/a/3943023/112731 + return (r * 0.299 + g * 0.587 + b * 0.114) > 186 + ? '#000000' + : '#FFFFFF'; + } + // invert color components + r = (255 - r).toString(16); + g = (255 - g).toString(16); + b = (255 - b).toString(16); + // pad each with zeros and return + return "#" + r.padStart(1, '0') + g.padStart(1, '0') + b.padStart(1, '0') + } + }, +} + diff --git a/resources/sass/bootstrap.scss b/resources/sass/bootstrap.scss index cde3ffe..881dfb3 100644 --- a/resources/sass/bootstrap.scss +++ b/resources/sass/bootstrap.scss @@ -8,3 +8,13 @@ font-size: 0.8rem; border-radius: 0.3rem; } + + +// Tickets Labels +.ticketIndexLabels { + display: -webkit-box; + max-width: 15rem; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} diff --git a/resources/views/ticket/create.blade.php b/resources/views/ticket/create.blade.php index e6456ec..b7c116b 100644 --- a/resources/views/ticket/create.blade.php +++ b/resources/views/ticket/create.blade.php @@ -4,7 +4,12 @@ @section('content') - {{ __('Créer un ticket') }}{{ __('Retour') }} + + + + {{ __('Créer un ticket') }} + + @csrf diff --git a/resources/views/ticket/edit.blade.php b/resources/views/ticket/edit.blade.php index 4bb5a41..189d501 100644 --- a/resources/views/ticket/edit.blade.php +++ b/resources/views/ticket/edit.blade.php @@ -4,7 +4,12 @@ @section('content') - {{ __('Modifier un ticket') }}{{ __('Retour') }} + + + + {{ __('Modifier un ticket') }} + + @csrf diff --git a/resources/views/ticket/form-inputs.blade.php b/resources/views/ticket/form-inputs.blade.php index 098f878..77e2872 100644 --- a/resources/views/ticket/form-inputs.blade.php +++ b/resources/views/ticket/form-inputs.blade.php @@ -24,7 +24,7 @@ $userPr = Auth::user()->sites ?? collect([]); @can('changeStatus', App\Model\Ticket::class) - Status + {{ __('Status') }} @foreach(TicketStatus::toArray() as $ticketStatus) value == old('status') or (isset($ticket) and $ticketStatus->equals($ticket->status)))selected @endif>{{ $ticketStatus->label }} @@ -34,6 +34,23 @@ $userPr = Auth::user()->sites ?? collect([]); @else @endcan + + @can('assignLabels', App\Model\Ticket::class) + + {{ __('Étiquettes') }} + @php + $dataVue = [ + 'name' => 'labels', + 'value' => old('labels', optional($ticket)->labels), + 'labels' => \App\Models\Label::all() + ]; + @endphp + + + @endcan diff --git a/resources/views/ticket/index.blade.php b/resources/views/ticket/index.blade.php index b83876f..586e89f 100644 --- a/resources/views/ticket/index.blade.php +++ b/resources/views/ticket/index.blade.php @@ -1,5 +1,7 @@ @extends('layout.app') +@section('bodyClass', 'content ticketsIndex') + @section('content') @php $userPr = Auth::user()->sites ?? collect([]); @@ -9,18 +11,21 @@ $userPr = Auth::user()->sites ?? collect([]); - Gestion des tickets @if(request()->status) (Archivés) @endif + {{ __('Gestion des tickets') }} @if(request()->st) (Archivés) @endif - {{ __('Créer un ticket') }} - @if(session('tickets.st') == TicketStatus::archived()) - {{ __('Tickets Actifs') }} - @else - {{ __('Tickets Archivés') }} + @can('viewAny', App\Models\Label::class) + @endif - {{ __('Réinitialiser le tri') }} + {{ __('ticket') }} + @if(session('tickets.st') == TicketStatus::archived()) + + @else + + @endif + {{ __(' le tri') }} @@ -51,6 +56,7 @@ $userPr = Auth::user()->sites ?? collect([]); + @can('assignTickets', App\Models\Ticket::class) @@ -64,6 +70,7 @@ $userPr = Auth::user()->sites ?? collect([]); + @endcan @@ -88,6 +95,9 @@ $userPr = Auth::user()->sites ?? collect([]); {{__('Type') }} @include('modules.sorter', ['colname' => 'type']) + @can('assignLabels', App\Models\Ticket::class) + {{ __('Étiquettes') }} + @endcan @if(Auth::user()->isAdmin()) {{__('Assigné à') }} @@ -114,12 +124,18 @@ $userPr = Auth::user()->sites ?? collect([]); @endif @foreach ($tickets as $ticket) - + - {{ $ticket->id }} @if($ticket->is_internal)🔒@endif + {{ $ticket->id }} @if($ticket->is_internal)@endif + @if (Auth::user()->isAdmin()) + {{ $ticket->site->name }} + @else {{ $ticket->site->name }} + @endif {{ $ticket->status->label }} @@ -127,6 +143,15 @@ $userPr = Auth::user()->sites ?? collect([]); {{ $ticket->type->label }} + @can('assignLabels', App\Models\Ticket::class) + + + @foreach ($ticket->labels as $label) + {{ $label->name }} + @endforeach + + + @endcan @if(Auth::user()->isAdmin()) {{ $ticket->assignedUser ? $ticket->assignedUser->name : __('Personne') }} @endif @@ -167,11 +192,17 @@ $userPr = Auth::user()->sites ?? collect([]); - {{ $ticket->site->name }} - + @can('view', App\Models\Site::class) + {{ $ticket->site->name }} + @else + {{ $ticket->site->name }} + @endcan - {{__('Ticket n°') }} {{ $ticket->id }} @if ($ticket->is_internal) - 🔒 + @endif - @@ -181,6 +212,13 @@ $userPr = Auth::user()->sites ?? collect([]); + @can('assignLabels', App\Models\Ticket::class) + + @foreach ($ticket->labels as $label) + {{ $label->name }} + @endforeach + + @endcan {!! clean(nl2br($ticket->text)) !!}
{!! clean(nl2br($ticket->text)) !!}