Merge branch 'develop' into trunk
This commit is contained in:
commit
ca7057ce77
27 changed files with 850 additions and 424 deletions
|
|
@ -13,8 +13,10 @@ use App\Http\Requests\Bo\ChangeTicketStatus;
|
|||
use App\Http\Requests\Bo\CreateMessage;
|
||||
use App\Http\Requests\Bo\SortTickets;
|
||||
use App\Http\Requests\Bo\StoreTicket;
|
||||
use App\Http\Requests\Bo\UpdateChangeOrderRequest;
|
||||
use App\Http\Requests\Bo\UpdateMessage;
|
||||
use App\Http\Requests\Bo\UpdateTicket;
|
||||
use App\Http\Requests\Bo\LinkTicket;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Mail\NewTicket;
|
||||
|
|
@ -29,6 +31,7 @@ use Illuminate\Support\Facades\DB;
|
|||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
|
|
@ -74,7 +77,6 @@ class TicketController extends Controller
|
|||
}));
|
||||
} //end if
|
||||
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$tickets = Ticket::with(['site', 'labels']);
|
||||
|
|
@ -97,6 +99,10 @@ class TicketController extends Controller
|
|||
|
||||
$tickets = $this->filterTickets($tickets, $request);
|
||||
$tickets = $this->searchTicket($tickets, $request);
|
||||
|
||||
/** Sort columns with a query */
|
||||
$this->sortQuery($tickets);
|
||||
|
||||
/** Custom pagination */
|
||||
$tickets = $this->customPaginate($tickets, $request->pagination);
|
||||
|
||||
|
|
@ -134,10 +140,13 @@ class TicketController extends Controller
|
|||
abort(403);
|
||||
}
|
||||
|
||||
$linkedTicket = $ticket->linkedTicket()->get();
|
||||
|
||||
$messages = $ticket->messages()->orderBy('created_at', 'DESC')->get();
|
||||
$isSubscribed = (bool)$ticket->subscriptions()->where('user_id', $user->id)->count();
|
||||
return view('bo.pages.tickets.show', [
|
||||
'ticket' => $ticket,
|
||||
'linkedTicket' => $linkedTicket,
|
||||
'messages' => $messages,
|
||||
'isSubscribed' => $isSubscribed
|
||||
]);
|
||||
|
|
@ -484,6 +493,48 @@ class TicketController extends Controller
|
|||
return back()->with('info', trans('Le ticket n\'est plus assigné à personne'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Link two tickets.
|
||||
*
|
||||
* @param LinkTicket $request
|
||||
* @param Ticket $ticket
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function linkTicket(LinkTicket $request, Ticket $ticket)
|
||||
{
|
||||
$linkedTicket = Ticket::findOrFail($request->validated())->first();
|
||||
|
||||
if ($ticket->site_id !== $linkedTicket->site_id) {
|
||||
return back()->with('error', trans('Le ticket que vous avez renseigné ne fait pas partie du même site.'));
|
||||
}
|
||||
|
||||
if (!$ticket->linkedTicket()->where('linked_ticket_id', $linkedTicket->id)->exists()) {
|
||||
$ticket->linkedTicket()->attach($linkedTicket->id);
|
||||
$linkedTicket->linkedTicket()->attach($ticket->id);
|
||||
return back()->with('success', trans('Les tickets ont été liés avec succès.'));
|
||||
}
|
||||
|
||||
return back()->with('error', trans('Les tickets sont déjà liés ensemble.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink two tickets.
|
||||
*
|
||||
* @param Ticket $ticket
|
||||
* @param Ticket $linkedTicket
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function unlinkTicket(Ticket $ticket, Ticket $linkedTicket)
|
||||
{
|
||||
$ticket->linkedTicket()->detach($linkedTicket->id);
|
||||
$linkedTicket->linkedTicket()->detach($ticket->id);
|
||||
|
||||
return back()->with('warning', trans('Le lien entre les tickets à été supprimé.'));
|
||||
}
|
||||
|
||||
// * PRIVATES
|
||||
|
||||
/**
|
||||
|
|
@ -502,8 +553,7 @@ class TicketController extends Controller
|
|||
Session::remove('tickets.st');
|
||||
Session::remove('tickets.pr');
|
||||
Session::remove('tickets.ty');
|
||||
Session::remove('tickets.sort_col');
|
||||
Session::remove('tickets.sort_way');
|
||||
Session::remove('tickets.or');
|
||||
Session::remove('tickets.search');
|
||||
}
|
||||
if ($request->assr) {
|
||||
|
|
@ -518,17 +568,15 @@ class TicketController extends Controller
|
|||
if ($request->tys) {
|
||||
Session::remove('tickets.ty');
|
||||
}
|
||||
if ($request->sort_col) {
|
||||
Session::put('tickets.sort_col', $request->sort_col);
|
||||
}
|
||||
if ($request->sort_way) {
|
||||
Session::put('tickets.sort_way', $request->sort_way);
|
||||
if ($request->ord) {
|
||||
Session::remove('tickets.or');
|
||||
}
|
||||
if ($request->pr) {
|
||||
Session::put('tickets.pr', $request->pr);
|
||||
}
|
||||
if ($request->st) {
|
||||
Session::put('tickets.st', $request->st);
|
||||
Session::remove('tickets.or');
|
||||
}
|
||||
if ($request->ty) {
|
||||
Session::put('tickets.ty', $request->ty);
|
||||
|
|
@ -536,6 +584,9 @@ class TicketController extends Controller
|
|||
if ($request->ass) {
|
||||
Session::put('tickets.ass', $request->ass);
|
||||
}
|
||||
if ($request->or && Session::has('tickets.pr')) {
|
||||
Session::put('tickets.or', $request->or);
|
||||
}
|
||||
Session::save();
|
||||
|
||||
if (Session::has('tickets.pr')) {
|
||||
|
|
@ -556,27 +607,12 @@ class TicketController extends Controller
|
|||
$tickets->where('type', Session::get('tickets.ty'));
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
Session::has('tickets.sort_col') and
|
||||
Session::has('tickets.sort_way') and
|
||||
in_array(Session::get('tickets.sort_col'), [
|
||||
'id',
|
||||
'site_id',
|
||||
'created_at',
|
||||
'created_by',
|
||||
'type',
|
||||
'status',
|
||||
'assigned_to'
|
||||
])
|
||||
) {
|
||||
$tickets->orderBy(
|
||||
Session::get('tickets.sort_col'),
|
||||
Session::get('tickets.sort_way')
|
||||
);
|
||||
if (Session::has('tickets.or')) {
|
||||
$tickets->orderBy('order', 'ASC');
|
||||
} else {
|
||||
$tickets->orderBy('last_activity', 'DESC');
|
||||
}
|
||||
|
||||
return $tickets;
|
||||
}
|
||||
|
||||
|
|
@ -733,4 +769,62 @@ class TicketController extends Controller
|
|||
}
|
||||
return \sprintf("<br><br> Fichier %d : <br>%s<br>", $fileNb, $file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change order.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\UpdateChangeOrderRequest $request
|
||||
* @param mixed ...$params
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \RuntimeException Si le nom du parametre de la route ne correspond pas au params utilisé.
|
||||
*/
|
||||
public function changeOrder(UpdateChangeOrderRequest $request, mixed ...$params): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$paramNames = collect(request()->route()->parameterNames);
|
||||
$className = Str::of($paramNames->get($paramNames->count() - 2))->lower()->studly();
|
||||
$className = "\\App\\Models\\{$className}";
|
||||
$model = Arr::get($params, $paramNames->count() - 2);
|
||||
|
||||
if (!class_exists($className)) {
|
||||
throw new \RuntimeException("The {$className} class doesn't exist.");
|
||||
}
|
||||
|
||||
if (!\is_object($model)) {
|
||||
throw new \RuntimeException("The second last param isn't an object.");
|
||||
}
|
||||
|
||||
if (get_class($model) !== (string)Str::of($className)->replaceStart('\\', '')) {
|
||||
throw new \RuntimeException("The {$className} doesn't match second last binded param.");
|
||||
}
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Model|null */
|
||||
$model = $className::query()->where($model->getRouteKeyName(), $model->getRouteKey())->first();
|
||||
|
||||
if (!$model) {
|
||||
return back()->with('error', trans('crud.messages.order_not_changed'));
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($request, $className, $model) {
|
||||
/** @var \Illuminate\Support\Collection $models */
|
||||
$models = $className::query()
|
||||
->orderBy('order', 'ASC')
|
||||
->where('site_id', $model->site_id)
|
||||
->where('status', '!=', TicketStatusEnum::archived)
|
||||
->where('id', '!=', $model->getKey())
|
||||
->where('order', $request->direction ? '<' : '>', $model->order)
|
||||
->get();
|
||||
/** @var \Illuminate\Database\Eloquent\Model|null $tmp */
|
||||
$tmp = $request->direction ? $models->last() : $models->first();
|
||||
|
||||
if (!$tmp) {
|
||||
return back()->with('error', trans('crud.messages.order_not_changed'));
|
||||
}
|
||||
|
||||
$newOrder = $tmp->getOriginal('order');
|
||||
$tmp->update(['order' => $model->getOriginal('order')]);
|
||||
$model->update(['order' => $newOrder]);
|
||||
|
||||
return redirect()->back()->with('success', trans('crud.messages.order_changed'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,10 +223,6 @@ class Controller extends BaseController
|
|||
) {
|
||||
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
|
||||
}
|
||||
} elseif (Schema::hasColumn($table, 'order')) {
|
||||
$query = $query->orderBy('order', 'ASC');
|
||||
Session::put("$rName.sort_col", 'order');
|
||||
Session::put("$rName.sort_way", 'asc');
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class RedirectIfAuthenticated
|
|||
if (Auth::guard($guard)->check()) {
|
||||
switch ($guard) {
|
||||
case 'backend':
|
||||
if (!$request->routeIs('bo.*')) {
|
||||
if (!$request->routeIs('bo.*') && !$request->routeIs('login')) {
|
||||
continue;
|
||||
}
|
||||
return \redirect()->route('bo.dashboard');
|
||||
|
|
|
|||
36
app/Http/Requests/Bo/LinkTicket.php
Normal file
36
app/Http/Requests/Bo/LinkTicket.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LinkTicket extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$ticket = $this->route('ticket');
|
||||
return [
|
||||
'linked_ticket_id' => [
|
||||
'required',
|
||||
'numeric',
|
||||
'exists:tickets,id',
|
||||
'not_in:' . $ticket->id,
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -96,6 +96,7 @@ class StoreTicket extends FormRequest
|
|||
}
|
||||
}
|
||||
],
|
||||
'date_mep' => 'required|date',
|
||||
'text' => 'required|string|min:40',
|
||||
'title' => 'required|string|between:3,255',
|
||||
'file_tickets' => 'sometimes|array',
|
||||
|
|
|
|||
43
app/Http/Requests/Bo/UpdateChangeOrderRequest.php
Normal file
43
app/Http/Requests/Bo/UpdateChangeOrderRequest.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateChangeOrderRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'direction' => $this->route('direction') === 'up' ? true : false
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'direction' => 'required|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -32,6 +32,8 @@ class Ticket extends Model
|
|||
'site_id',
|
||||
'type',
|
||||
'status',
|
||||
'order',
|
||||
'date_mep',
|
||||
'text',
|
||||
'title',
|
||||
'is_internal',
|
||||
|
|
@ -47,6 +49,7 @@ class Ticket extends Model
|
|||
'is_internal' => 'bool',
|
||||
'type' => TicketTypeEnum::class,
|
||||
'status' => TicketStatusEnum::class,
|
||||
'date_mep' => 'date',
|
||||
'last_activity' => 'timestamp',
|
||||
];
|
||||
|
||||
|
|
@ -59,6 +62,7 @@ class Ticket extends Model
|
|||
{
|
||||
static::creating(function (Ticket $ticket) {
|
||||
// If bug force the ticket status on new. All the others are estimateTodo.
|
||||
self::setOrder($ticket);
|
||||
if ($ticket->type->equals(TicketTypeEnum::bug)) {
|
||||
$ticket->status = TicketStatusEnum::new;
|
||||
} else {
|
||||
|
|
@ -66,7 +70,7 @@ class Ticket extends Model
|
|||
}
|
||||
});
|
||||
static::updating(function (Ticket $ticket) {
|
||||
if (!$ticket->original['status'] !== $ticket->status->value) {
|
||||
if (!$ticket->original['status'] !== $ticket->status->value && $ticket->original['order'] === $ticket->order) {
|
||||
event(new TicketStatusChanged(
|
||||
$ticket,
|
||||
TicketStatusEnum::make($ticket->original['status']),
|
||||
|
|
@ -85,9 +89,25 @@ class Ticket extends Model
|
|||
$ticket->status->label()
|
||||
)));
|
||||
}
|
||||
if ($ticket->status->equals(TicketStatusEnum::archived)) {
|
||||
self::setOrder($ticket, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order after the last element of the list.
|
||||
*
|
||||
* @param self $model
|
||||
* @param boolean $isArchived
|
||||
* @return void
|
||||
*/
|
||||
private static function setOrder(self $model, bool $isArchived = false): void
|
||||
{
|
||||
$model->order = \intval(self::query()->where('status', $isArchived ? '=' : '!=', TicketStatusEnum::archived)->max('order')) + 1;
|
||||
}
|
||||
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
|
|
@ -173,6 +193,16 @@ class Ticket extends Model
|
|||
return $this->belongsTo(TicketType::class, 'type', 'type');
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsToMany Linked Ticket
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function linkedTicket()
|
||||
{
|
||||
return $this->belongsToMany(Ticket::class, 'linked_tickets', 'ticket_id', 'linked_ticket_id');
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -67,6 +67,17 @@ class TicketPolicy
|
|||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user can link ticket .
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return boolean
|
||||
*/
|
||||
public function linkTicket(User $user)
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user can change the internal status of a given ticket.
|
||||
*
|
||||
|
|
|
|||
64
app/Traits/Controllers/ChangesModelOrder.php
Normal file
64
app/Traits/Controllers/ChangesModelOrder.php
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Controllers;
|
||||
|
||||
use App\Http\Requests\Bo\UpdateChangeOrderRequest;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait ChangesModelOrder
|
||||
{
|
||||
/**
|
||||
* Change order.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\UpdateChangeOrderRequest $request
|
||||
* @param mixed ...$params
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \RuntimeException Si le nom du parametre de la route ne correspond pas au params utilisé.
|
||||
*/
|
||||
public function changeOrder(UpdateChangeOrderRequest $request, mixed ...$params): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$paramNames = collect(request()->route()->parameterNames);
|
||||
$className = Str::of($paramNames->get($paramNames->count() - 2))->lower()->studly();
|
||||
$className = "\\App\\Models\\{$className}";
|
||||
$model = Arr::get($params, $paramNames->count() - 2);
|
||||
|
||||
if (!class_exists($className)) {
|
||||
throw new \RuntimeException("The {$className} class doesn't exist.");
|
||||
}
|
||||
|
||||
if (!\is_object($model)) {
|
||||
throw new \RuntimeException("The second last param isn't an object.");
|
||||
}
|
||||
|
||||
if (get_class($model) !== (string)Str::of($className)->replaceStart('\\', '')) {
|
||||
throw new \RuntimeException("The {$className} doesn't match second last binded param.");
|
||||
}
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Model|null */
|
||||
$model = $className::query()->where($model->getRouteKeyName(), $model->getRouteKey())->first();
|
||||
|
||||
if (!$model) {
|
||||
return back()->with('error', trans('crud.messages.order_not_changed'));
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($request, $className, $model) {
|
||||
/** @var \Illuminate\Database\Eloquent\Model|null */
|
||||
$tmp = $className::query()
|
||||
->where('order', $request->direction ? '<' : '>', $model->getOriginal('order'))
|
||||
->orderBy('order', $request->direction ? 'DESC' : 'ASC')
|
||||
->first();
|
||||
|
||||
if (!$tmp) {
|
||||
return back()->with('error', trans('crud.messages.order_not_changed'));
|
||||
}
|
||||
|
||||
$newOrder = $tmp->getOriginal('order');
|
||||
$tmp->update(['order' => $model->getOriginal('order')]);
|
||||
$model->update(['order' => $newOrder]);
|
||||
|
||||
return redirect()->back()->with('success', trans('crud.messages.order_changed'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use App\Models\Site;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->integer('order')->after('status')->comment('Ticket order.');
|
||||
});
|
||||
|
||||
Site::query()->get()->map(function ($site) {
|
||||
$site->tickets->map(function ($ticket, $ticketIndex) {
|
||||
$ticket->order = $ticketIndex + 1;
|
||||
$ticket->saveQuietly();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropColumn('order');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->date('date_mep')->nullable()->after('gitlab_issue')->comment('Date MEP souhaitée');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropColumn('date_mep');
|
||||
});
|
||||
}
|
||||
};
|
||||
35
database/migrations/2025_03_13_151503_tickets_linking.php
Normal file
35
database/migrations/2025_03_13_151503_tickets_linking.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('linked_tickets', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('ticket_id')
|
||||
->constrained()->cascadeOnDelete();
|
||||
$table->unsignedBigInteger('linked_ticket_id')
|
||||
->constrained()->cascadeOnDelete();
|
||||
$table->foreign('ticket_id')->references('id')->on('tickets');
|
||||
$table->foreign('linked_ticket_id')->references('id')->on('tickets');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('linked_tickets');
|
||||
}
|
||||
};
|
||||
|
|
@ -50,5 +50,6 @@
|
|||
"Verify Your Email Address": "Verify Your Email Address",
|
||||
"Whoops!": "Whoops!",
|
||||
"You are logged in!": "You are logged in!",
|
||||
"You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account."
|
||||
}
|
||||
"You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account.",
|
||||
"see_mode": "see :mode mode"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,4 +214,14 @@ return [
|
|||
'username' => 'username',
|
||||
'year' => 'year',
|
||||
],
|
||||
'custom' => [
|
||||
'type' => 'type',
|
||||
'tickets' => 'tickets',
|
||||
'assigned_to' => 'assigned to',
|
||||
'order' => 'order',
|
||||
'priority' => 'priority',
|
||||
'default' => 'default',
|
||||
'date_mep' => 'desired MEP date',
|
||||
'date_mep_empty' => 'no date entered'
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -55,5 +55,6 @@
|
|||
"Verify Your Email Address": "Vérifiez votre adresse e-mail",
|
||||
"Whoops!": "Oups !",
|
||||
"You are logged in!": "Vous êtes connecté·e !",
|
||||
"You are receiving this email because we received a password reset request for your account.": "Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte."
|
||||
}
|
||||
"You are receiving this email because we received a password reset request for your account.": "Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte.",
|
||||
"see_mode": "voir mode :mode"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,5 +89,7 @@ return [
|
|||
'sort_delete' => 'Supprimer le tri',
|
||||
'sort_arrow' => 'Ici, les flèches sont utilisées pour trier et non pour filtrer',
|
||||
'right' => 'Vous n\'avez pas les droits',
|
||||
'view_linked_ticket' => 'voir le ticket :model',
|
||||
'unlink_ticket' => 'supprimer le lien avec le ticket :model'
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Validation Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines contain the default error messages used by
|
||||
| the validator class. Some of these rules have multiple versions such
|
||||
| as the size rules. Feel free to tweak each of these messages here.
|
||||
|
|
||||
*/
|
||||
|
||||
return [
|
||||
'accepted' => 'Ce champ doit être accepté.',
|
||||
'active_url' => 'Ce n\'est pas une URL valide',
|
||||
'after' => 'La date doit être postérieure au :date.',
|
||||
'after_or_equal' => 'La date doit être postérieure ou égale au :date.',
|
||||
'alpha' => 'Ce champ doit contenir uniquement des lettres',
|
||||
'alpha_dash' => 'Ce champ doit contenir uniquement des lettres, des chiffres et des tirets.',
|
||||
'alpha_num' => 'Ce champ doit contenir uniquement des chiffres et des lettres.',
|
||||
'array' => 'Ce champ doit être un tableau.',
|
||||
'attached' => 'Ce champ est déjà attaché.',
|
||||
'before' => 'Ce champ doit être une date antérieure au :date.',
|
||||
'before_or_equal' => 'Ce champ doit être une date antérieure ou égale au :date.',
|
||||
'between' => [
|
||||
'array' => 'Le tableau doit contenir entre :min et :max éléments.',
|
||||
'file' => 'La taille du fichier doit être comprise entre :min et :max kilo-octets.',
|
||||
'numeric' => 'La valeur doit être comprise entre :min et :max.',
|
||||
'string' => 'Le texte doit contenir entre :min et :max caractères.',
|
||||
],
|
||||
'boolean' => 'Ce champ doit être vrai ou faux.',
|
||||
'confirmed' => 'Le champ de confirmation ne correspond pas.',
|
||||
'date' => 'Ce n\'est pas une date valide.',
|
||||
'date_equals' => 'La date doit être égale à :date.',
|
||||
'date_format' => 'Ce champ ne correspond pas au format :format.',
|
||||
'different' => 'Cette valeur doit être différente de :other.',
|
||||
'digits' => 'Ce champ doit contenir :digits chiffres.',
|
||||
'digits_between' => 'Ce champ doit contenir entre :min et :max chiffres.',
|
||||
'dimensions' => 'La taille de l\'image n\'est pas conforme.',
|
||||
'distinct' => 'Ce champ a une valeur en double.',
|
||||
'email' => 'Ce champ doit être une adresse email valide.',
|
||||
'ends_with' => 'Ce champ doit se terminer par une des valeurs suivantes : :values',
|
||||
'exists' => 'Ce champ sélectionné est invalide.',
|
||||
'file' => 'Ce champ doit être un fichier.',
|
||||
'filled' => 'Ce champ doit avoir une valeur.',
|
||||
'gt' => [
|
||||
'array' => 'Le tableau doit contenir plus de :value éléments.',
|
||||
'file' => 'La taille du fichier doit être supérieure à :value kilo-octets.',
|
||||
'numeric' => 'La valeur doit être supérieure à :value.',
|
||||
'string' => 'Le texte doit contenir plus de :value caractères.',
|
||||
],
|
||||
'gte' => [
|
||||
'array' => 'Le tableau doit contenir au moins :value éléments.',
|
||||
'file' => 'La taille du fichier doit être supérieure ou égale à :value kilo-octets.',
|
||||
'numeric' => 'La valeur doit être supérieure ou égale à :value.',
|
||||
'string' => 'Le texte doit contenir au moins :value caractères.',
|
||||
],
|
||||
'image' => 'Ce champ doit être une image.',
|
||||
'in' => 'Ce champ est invalide.',
|
||||
'in_array' => 'Ce champ n\'existe pas dans :other.',
|
||||
'integer' => 'Ce champ doit être un entier.',
|
||||
'ip' => 'Ce champ doit être une adresse IP valide.',
|
||||
'ipv4' => 'Ce champ doit être une adresse IPv4 valide.',
|
||||
'ipv6' => 'Ce champ doit être une adresse IPv6 valide.',
|
||||
'json' => 'Ce champ doit être un document JSON valide.',
|
||||
'lt' => [
|
||||
'array' => 'Le tableau doit contenir moins de :value éléments.',
|
||||
'file' => 'La taille du fichier doit être inférieure à :value kilo-octets.',
|
||||
'numeric' => 'La valeur doit être inférieure à :value.',
|
||||
'string' => 'Le texte doit contenir moins de :value caractères.',
|
||||
],
|
||||
'lte' => [
|
||||
'array' => 'Le tableau doit contenir au plus :value éléments.',
|
||||
'file' => 'La taille du fichier doit être inférieure ou égale à :value kilo-octets.',
|
||||
'numeric' => 'La valeur doit être inférieure ou égale à :value.',
|
||||
'string' => 'Le texte doit contenir au plus :value caractères.',
|
||||
],
|
||||
'max' => [
|
||||
'array' => 'Le tableau ne peut contenir plus de :max éléments.',
|
||||
'file' => 'La taille du fichier ne peut pas dépasser :max kilo-octets.',
|
||||
'numeric' => 'La valeur ne peut être supérieure à :max.',
|
||||
'string' => 'Le texte ne peut contenir plus de :max caractères.',
|
||||
],
|
||||
'mimes' => 'Le fichier doit être de type : :values.',
|
||||
'mimetypes' => 'Le fichier doit être de type : :values.',
|
||||
'min' => [
|
||||
'array' => 'Le tableau doit contenir au moins :min éléments.',
|
||||
'file' => 'La taille du fichier doit être supérieure à :min kilo-octets.',
|
||||
'numeric' => 'La valeur doit être supérieure ou égale à :min.',
|
||||
'string' => 'Le texte doit contenir au moins :min caractères.',
|
||||
],
|
||||
'multiple_of' => 'La valeur doit être un multiple de :value',
|
||||
'not_in' => 'Le champ sélectionné n\'est pas valide.',
|
||||
'not_regex' => 'Le format du champ n\'est pas valide.',
|
||||
'numeric' => 'Ce champ doit contenir un nombre.',
|
||||
'password' => 'Le mot de passe est incorrect',
|
||||
'present' => 'Ce champ doit être présent.',
|
||||
'prohibited' => 'Ce champ est interdit',
|
||||
'prohibited_if' => 'Ce champ est interdit quand :other a la valeur :value.',
|
||||
'prohibited_unless' => 'Ce champ est interdit à moins que :other ait l\'une des valeurs :values.',
|
||||
'regex' => 'Le format du champ est invalide.',
|
||||
'relatable' => 'Ce champ n\'est sans doute pas associé avec cette donnée.',
|
||||
'required' => 'Ce champ est obligatoire.',
|
||||
'required_if' => 'Ce champ est obligatoire quand la valeur de :other est :value.',
|
||||
'required_unless' => 'Ce champ est obligatoire sauf si :other est :values.',
|
||||
'required_with' => 'Ce champ est obligatoire quand :values est présent.',
|
||||
'required_with_all' => 'Ce champ est obligatoire quand :values sont présents.',
|
||||
'required_without' => 'Ce champ est obligatoire quand :values n\'est pas présent.',
|
||||
'required_without_all' => 'Ce champ est requis quand aucun de :values n\'est présent.',
|
||||
'same' => 'Ce champ doit être identique à :other.',
|
||||
'size' => [
|
||||
'array' => 'Le tableau doit contenir :size éléments.',
|
||||
'file' => 'La taille du fichier doit être de :size kilo-octets.',
|
||||
'numeric' => 'La valeur doit être :size.',
|
||||
'string' => 'Le texte doit contenir :size caractères.',
|
||||
],
|
||||
'starts_with' => 'Ce champ doit commencer avec une des valeurs suivantes : :values',
|
||||
'string' => 'Ce champ doit être une chaîne de caractères.',
|
||||
'timezone' => 'Ce champ doit être un fuseau horaire valide.',
|
||||
'unique' => 'La valeur est déjà utilisée.',
|
||||
'uploaded' => 'Le fichier n\'a pu être téléversé.',
|
||||
'url' => 'Le format de l\'URL n\'est pas valide.',
|
||||
'uuid' => 'Ce champ doit être un UUID valide',
|
||||
'custom' => [
|
||||
'attribute-name' => [
|
||||
'rule-name' => 'custom-message',
|
||||
],
|
||||
],
|
||||
'attributes' => [],
|
||||
];
|
||||
|
|
@ -248,5 +248,16 @@ return [
|
|||
'url_re7' => 'Url de RECETTE',
|
||||
'username' => 'nom d\'utilisateur',
|
||||
'year' => 'année',
|
||||
'id' => 'identifiant'
|
||||
],
|
||||
'custom' => [
|
||||
'type' => 'type',
|
||||
'tickets' => 'étiquettes',
|
||||
'assigned_to' => 'assigné à',
|
||||
'order' => 'ordre',
|
||||
'priority' => 'priorité',
|
||||
'default' => 'défaut',
|
||||
'date_mep' => 'date de MEP souhaitée',
|
||||
'date_mep_empty' => 'aucune date renseignée'
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
faDeleteLeft,
|
||||
faDiagramProject,
|
||||
faDownload,
|
||||
faEquals,
|
||||
faEraser,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
|
|
@ -60,10 +61,12 @@ import {
|
|||
faTicket,
|
||||
faTimes,
|
||||
faTrash,
|
||||
faTriangleExclamation,
|
||||
faUndoAlt,
|
||||
faUsers,
|
||||
faWandMagic,
|
||||
faXmark
|
||||
faXmark,
|
||||
faLinkSlash
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
config.autoReplaceSvg = false;
|
||||
|
|
@ -124,6 +127,7 @@ library.add(faCircleExclamation);
|
|||
library.add(faCircleInfo);
|
||||
library.add(faPaintRoller);
|
||||
library.add(faTicket);
|
||||
library.add(faLinkSlash);
|
||||
|
||||
// Front
|
||||
library.add(faChevronLeft);
|
||||
|
|
@ -137,4 +141,6 @@ library.add(faCopy);
|
|||
library.add(faClipboard);
|
||||
library.add(faXmark);
|
||||
library.add(faArrowRightFromBracket);
|
||||
library.add(faTriangleExclamation);
|
||||
library.add(faEquals);
|
||||
dom.watch();
|
||||
|
|
|
|||
42
resources/views/bo/modules/change-model-order.blade.php
Normal file
42
resources/views/bo/modules/change-model-order.blade.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<td class="text-center align-middle">
|
||||
@if (isset($value))
|
||||
{{ $value }}
|
||||
@endif
|
||||
@can('changeOrder', $model)
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<form @class(['invisible' => $loop->first and $models->onFirstPage()])
|
||||
action="{{ route('bo.' . $routeName . '.change-order', [str($routeName)->singular()->value => $model, 'direction' => 'up']) }}"
|
||||
method="POST">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button data-bs-toggle="tooltip" type="submit" title="{{ __('crud.other.up') }}" @class([
|
||||
'btn btn-sm btn-outline-secondary',
|
||||
'disabled' => $loop->first and $models->onFirstPage(),
|
||||
])>
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form @class([
|
||||
'invisible' =>
|
||||
$loop->last and $models->currentPage() === $models->lastPage(),
|
||||
])
|
||||
action="{{ route('bo.' . $routeName . '.change-order', [str($routeName)->singular()->value => $model, 'direction' => 'down']) }}"
|
||||
method="POST">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button data-bs-toggle="tooltip" type="submit" title="{{ __('crud.other.down') }}" @class([
|
||||
'btn btn-sm btn-outline-secondary ms-1',
|
||||
'disabled' =>
|
||||
$loop->last and $models->currentPage() === $models->lastPage(),
|
||||
])>
|
||||
<i class="fas fa-arrow-down"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@elseif(isset($noRightsVisible) && $noRightsVisible)
|
||||
<span class="text-danger" data-bs-toggle="tooltip" title="{{ __('crud.other.right') }}">
|
||||
<i class="fa-solid fa-ban"></i>
|
||||
</span>
|
||||
@endcan
|
||||
</td>
|
||||
|
||||
54
resources/views/bo/modules/table-col-sorter-cell.blade.php
Normal file
54
resources/views/bo/modules/table-col-sorter-cell.blade.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
@php
|
||||
$iArgs = ['sort_col' => $col];
|
||||
if (request()->search) {
|
||||
$iArgs['search'] = request()->search;
|
||||
}
|
||||
@endphp
|
||||
{{-- NO Sort for order column --}}
|
||||
@if ($col === 'order' or in_array($col, $ignore))
|
||||
<th @class([
|
||||
'h-100 text-center',
|
||||
'd-none d-md-table-cell' =>
|
||||
isset($mobileHide) and
|
||||
is_array($mobileHide) and
|
||||
in_array($col, $mobileHide),
|
||||
]) scope="col">
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
<span class="text-nowrap">{{ $colname }}</span>
|
||||
</div>
|
||||
</th>
|
||||
@else
|
||||
<th @class([
|
||||
'h-100 text-center',
|
||||
'd-none d-md-table-cell' =>
|
||||
isset($mobileHide) and
|
||||
is_array($mobileHide) and
|
||||
in_array($col, $mobileHide),
|
||||
]) scope="col">
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
<span class="text-nowrap">
|
||||
{{ $colname }}
|
||||
</span>
|
||||
@if (
|
||||
!session("$routeName.sort_way") or
|
||||
session("$routeName.sort_col") !== $col or
|
||||
session("$routeName.sort_way") === 'desc' and session("$routeName.sort_col") === $col)
|
||||
<a class="btn btn-sm btn-outline-secondary text-decoration-none ms-1" data-bs-toggle="tooltip"
|
||||
href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'asc', 'rst' => null])) }}"
|
||||
title="{{ __('crud.other.sort_descending', ['name' => str($colname)->lower()]) }}">
|
||||
<i class="fas fa-arrow-down 2xs"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if (
|
||||
!session("$routeName.sort_way") or
|
||||
session("$routeName.sort_col") !== $col or
|
||||
session("$routeName.sort_way") === 'asc' and session("$routeName.sort_col") === $col)
|
||||
<a class="btn btn-sm btn-outline-secondary text-decoration-none ms-1"
|
||||
href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'desc', 'rst' => null])) }}"data-bs-toggle="tooltip"
|
||||
title="{{ __('crud.other.sort_ascending', ['name' => str($colname)->lower()]) }}">
|
||||
<i class="fas fa-arrow-up 2xs"></i>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</th>
|
||||
@endif
|
||||
|
|
@ -1,73 +1,31 @@
|
|||
@php
|
||||
$route = request()->route();
|
||||
$routeName = $route->getName();
|
||||
$rst = !is_null(request()->rst) || !Session::has("$routeName.sorted");
|
||||
$noOrder = !empty($noOrder);
|
||||
$noOrder = $noOrder || (Session::get("$routeName.sort_col") !== 'order' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way")));
|
||||
$noOrder = $noOrder || !empty(request()->search);
|
||||
$ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
|
||||
$route = request()->route();
|
||||
$routeName = $route->getName();
|
||||
$rst = !is_null(request()->rst) || !session()->has("$routeName.sorted");
|
||||
$noOrder = !empty($noOrder);
|
||||
$noOrder =
|
||||
$noOrder ||
|
||||
(session()->get("$routeName.sort_col") !== 'order' and
|
||||
(session()->has("$routeName.sort_col") or session()->has("$routeName.sort_way")));
|
||||
$noOrder = $noOrder || !empty(request()->search);
|
||||
$ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
|
||||
@endphp
|
||||
<tr class="table-col-sorter">
|
||||
@foreach($cols as $col => $colname)
|
||||
@php
|
||||
$iArgs = ['sort_col' => $col];
|
||||
if(request()->search) {
|
||||
$iArgs['search'] = request()->search;
|
||||
}
|
||||
@endphp
|
||||
{{-- NO Sort for order column --}}
|
||||
@if($col === 'order' or in_array($col, $ignore))
|
||||
@if($noOrder and !in_array($col, $ignore)) @continue @endif
|
||||
<th scope="col" class="text-center h-100 @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif">
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
<span class="text-nowrap">{{ $colname }}</span>
|
||||
</div>
|
||||
</th>
|
||||
@continue
|
||||
@endif
|
||||
<th scope="col" class="text-center h-100 @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif">
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
<span class="text-nowrap">
|
||||
{{ $colname }}
|
||||
</span>
|
||||
@if(!session("$routeName.sort_way") or session("$routeName.sort_col") !== $col or (session("$routeName.sort_way") === 'desc' and session("$routeName.sort_col") === $col))
|
||||
<a class="text-decoration-none ms-1"
|
||||
href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'asc', 'rst' => null])) }}">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-dark"
|
||||
title="{{ __('crud.other.sort_descending', ['name' => Str::lower($colname)]) }}"
|
||||
data-bs-toggle="tooltip">
|
||||
<i class="fas fa-arrow-down 2xs"></i>
|
||||
</button>
|
||||
</a>
|
||||
@endif
|
||||
@if(!session("$routeName.sort_way") or session("$routeName.sort_col") !== $col or (session("$routeName.sort_way") === 'asc' and session("$routeName.sort_col") === $col))
|
||||
<a class="text-decoration-none ms-1"
|
||||
href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'desc', 'rst' => null])) }}">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-dark"
|
||||
title="{{ __('crud.other.sort_ascending', ['name' => Str::lower($colname)]) }}"
|
||||
data-bs-toggle="tooltip">
|
||||
<i class="fas fa-arrow-up 2xs"></i>
|
||||
</button>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</th>
|
||||
<tr class="table-col-sorter border-start-0 border-end-0 border-top-0 border-secondary border border-2">
|
||||
@foreach ($cols as $col => $colname)
|
||||
@include('bo.modules.table-col-sorter-cell')
|
||||
@endforeach
|
||||
{{-- Buttons Col --}}
|
||||
<th scope="col" class="text-end">
|
||||
@if(!$rst)
|
||||
<a class="btn btn-sm btn-danger"
|
||||
href="{{ request()->fullUrlWithQuery(['sort_col' => null, 'sort_way' => null, 'rst' => true, 'search' => null]) }}"
|
||||
data-bs-toggle="tooltip"
|
||||
title="{{ __('crud.other.sort_delete') }}">
|
||||
<i class="fa fa-eraser"></i>
|
||||
</a>
|
||||
<th class="text-end" scope="col">
|
||||
@if (!$rst)
|
||||
<a class="btn btn-sm btn-danger" data-bs-toggle="tooltip"
|
||||
href="{{ request()->fullUrlWithQuery(['sort_col' => null, 'sort_way' => null, 'rst' => true, 'search' => null]) }}"
|
||||
title="{{ __('crud.other.sort_delete') }}">
|
||||
<i class="fa fa-eraser"></i>
|
||||
</a>
|
||||
@else
|
||||
<span data-bs-toggle="tooltip" data-bs-placement="top" title="{{ __('crud.other.sort_arrow') }}">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</span>
|
||||
<span data-bs-toggle="tooltip" title="{{ __('crud.other.sort_arrow') }}">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</span>
|
||||
@endif
|
||||
</th>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -93,6 +93,11 @@
|
|||
|
||||
</div>
|
||||
@endcan
|
||||
|
||||
<div class="form-group col">
|
||||
<label class="col-form-label" for="date_mep">{{ __('Date de mise en production souhaitée') }}</label>
|
||||
<input class="form-control" id="date_mep" name="date_mep" type="date" min="{{ date('Y-m-j') }}" value="{{ old('date_mep') ?? (isset($ticket) && !is_null($ticket->date_mep) ? $ticket->date_mep->format('Y-m-j') : date('Y-m-j')) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@
|
|||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="d-inline">{{ __('Gestion des tickets') }} @if (Session::get('tickets.st') == \App\Enums\Tickets\TicketStatusEnum::archived->value)
|
||||
<h2 class="d-inline">
|
||||
{{ __('Gestion des tickets') }}
|
||||
@if (Session::get('tickets.st') == \App\Enums\Tickets\TicketStatusEnum::archived->value)
|
||||
(Archivés)
|
||||
@endif
|
||||
</h2>
|
||||
|
|
@ -24,23 +26,39 @@
|
|||
@can('isAdmin')
|
||||
<a class="btn btn-info" href="{{ route('bo.labels.index') }}"><i class="fa-solid fa-tags"></i></a>
|
||||
@endcan
|
||||
<a class="btn btn-primary" href="{{ route('bo.ticket.create') }}"><i
|
||||
class="fa-solid fa-square-plus"></i> {{ __('ticket') }}</a>
|
||||
<a class="btn btn-primary" href="{{ route('bo.ticket.create') }}">
|
||||
<i class="fa-solid fa-square-plus"></i> {{ __('ticket') }}
|
||||
</a>
|
||||
@if (session('tickets.pr') && session('tickets.st') != \App\Enums\Tickets\TicketStatusEnum::archived->value)
|
||||
@if (session('tickets.or'))
|
||||
<a class="btn btn-info float-end" href="{{ route('bo.ticket.index', ['ord' => -1]) }}">
|
||||
<i class="fa-solid fa-equals"></i> {{ __('see_mode', ['mode' => __('validation.custom.default')]) }}
|
||||
</a>
|
||||
@else
|
||||
<a class="btn btn-danger" href="{{ route('bo.ticket.index', ['or' => 1]) }}">
|
||||
<i class="fa-solid fa-triangle-exclamation"></i> {{ __('see_mode', ['mode' => __('validation.custom.priority')]) }}
|
||||
</a>
|
||||
@endif
|
||||
@endif
|
||||
@if (session('tickets.st') == \App\Enums\Tickets\TicketStatusEnum::archived->value)
|
||||
<a class="btn btn-success" href="{{ route('bo.ticket.index', ['sts' => -1]) }}"><i
|
||||
class="fa-solid fa-inbox"></i></a>
|
||||
<a class="btn btn-success" href="{{ route('bo.ticket.index', ['sts' => -1]) }}">
|
||||
<i class="fa-solid fa-inbox"></i>
|
||||
</a>
|
||||
@else
|
||||
<a class="btn btn-secondary float-end"
|
||||
href="{{ route('bo.ticket.index', ['st' => \App\Enums\Tickets\TicketStatusEnum::archived]) }}"><i
|
||||
class="fa-solid fa-box-archive"></i></a>
|
||||
href="{{ route('bo.ticket.index', ['st' => \App\Enums\Tickets\TicketStatusEnum::archived]) }}">
|
||||
<i class="fa-solid fa-box-archive"></i>
|
||||
</a>
|
||||
@endif
|
||||
<a class="btn btn-dark" href="{{ route('bo.ticket.index', ['rst' => 1]) }}"><i
|
||||
class="fa-solid fa-eraser"></i> {{ __(' le tri') }}</a>
|
||||
<a class="btn btn-dark" href="{{ route('bo.ticket.index', ['rst' => 1]) }}">
|
||||
<i class="fa-solid fa-eraser"></i> {{ __(' le tri') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="float-end me-2">
|
||||
<div class="input-group">
|
||||
<label class="input-group-text" for="selectProject"><i
|
||||
class="fa-solid fa-diagram-project"></i></label>
|
||||
<label class="input-group-text" for="selectProject">
|
||||
<i class="fa-solid fa-diagram-project"></i>
|
||||
</label>
|
||||
<select class="form-select" id="selectProject">
|
||||
<option value="-1">{{ __('Tous') }}</option>
|
||||
@foreach ($sites as $site)
|
||||
|
|
@ -137,12 +155,12 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<form class="w-75 float-end mt-3" id="searchTicketForm"
|
||||
<form class="float-end mt-3" id="searchTicketForm"
|
||||
action="{{ request()->fullUrlWithQuery(['search' => '']) }}">
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" id="searchTicket" name="search" type="text"
|
||||
value="{{ session()->get('tickets.search') }}" aria-label="{{ __('Recherche de ticket..') }}"
|
||||
aria-describedby="searchTicket" placeholder="{{ __('Recherche de ticket..') }}">
|
||||
value="{{ session()->get('tickets.search') }}" aria-label="{{ __('Recherche de ticket...') }}"
|
||||
aria-describedby="searchTicket" placeholder="{{ __('Recherche de ticket...') }}">
|
||||
<button class="btn btn-outline-danger" id="searchDelete" type="button"><i
|
||||
class="fa-solid fa-delete-left"></i></button>
|
||||
<button class="btn btn-outline-primary" type="submit"><i
|
||||
|
|
@ -162,184 +180,207 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table-hover table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
<span># </span>
|
||||
<span>@include('modules.sorter', ['colname' => 'id'])</span>
|
||||
</th>
|
||||
<th scope="col">
|
||||
<span>{{ __('Titre') }} </span>
|
||||
<span>@include('modules.sorter', ['colname' => 'title'])</span>
|
||||
</th>
|
||||
<th scope="col">
|
||||
<span>{{ __('Type') }} </span>
|
||||
<span>@include('modules.sorter', ['colname' => 'type'])</span>
|
||||
</th>
|
||||
@can('assignLabels', App\Models\Ticket::class)
|
||||
<th scope="col">{{ __('Étiquettes') }}</th>
|
||||
@endcan
|
||||
@if (Auth::user()->isAdmin())
|
||||
<th scope="col">
|
||||
<span>{{ __('Assigné à') }} </span>
|
||||
<span>@include('modules.sorter', ['colname' => 'assigned_to'])</span>
|
||||
</th>
|
||||
@endif
|
||||
<th scope="col">
|
||||
<span>{{ __('Le') }} </span>
|
||||
<span>@include('modules.sorter', ['colname' => 'created_at'])</span>
|
||||
</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (!count($tickets))
|
||||
<tr>
|
||||
<td class="text-center" colspan="@if (Auth::user()->isAdmin()) 6 @else 5 @endif">
|
||||
<h2>{{ __('Aucun ticket') }}</h2>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@foreach ($tickets as $ticket)
|
||||
<tr class="position-relative">
|
||||
<td scope="row">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="me-1">{{ $ticket->id }} @if ($ticket->is_internal)
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
@endif
|
||||
</span>
|
||||
<span class="d-flex flex-column text-end">
|
||||
@if (Auth::user()->isAdmin())
|
||||
<a href="{{ route('bo.sites.show', $ticket->site) }}"
|
||||
title="{{ __('Voir le site :sitename', [
|
||||
'sitename' => $ticket->site->name,
|
||||
]) }}">{{ $ticket->site->name }}</a>
|
||||
@else
|
||||
<span class="text-decoration-underline">{{ $ticket->site->name }}</span>
|
||||
@endif
|
||||
<span
|
||||
class="{{ $ticket->status->class() }} rounded text-center">{{ $ticket->status->label() }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="me-1">{{ $ticket->title }}</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="badge {{ $ticket->type->class() }}">{{ $ticket->type->label() }}</span>
|
||||
</td>
|
||||
@can('assignLabels', App\Models\Ticket::class)
|
||||
<td>
|
||||
<span class="ticket-index-labels">
|
||||
@foreach ($ticket->labels as $label)
|
||||
<span class="badge label colorize"
|
||||
data-color="{{ $label->color }}">{{ $label->name }}</span>
|
||||
@endforeach
|
||||
</span>
|
||||
<div class="overflow-scroll w-100">
|
||||
<table class="table-hover table">
|
||||
<thead>
|
||||
@php
|
||||
$cols = [];
|
||||
if (session('tickets.or')) {
|
||||
$cols = array_merge($cols, [
|
||||
'order' => Str::ucfirst(__('validation.custom.priority'))
|
||||
]);
|
||||
}
|
||||
$cols = array_merge($cols, [
|
||||
'id' => Str::ucfirst(__('validation.attributes.id')),
|
||||
'title' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'type' => Str::ucfirst(__('validation.custom.type')),
|
||||
]);
|
||||
if (Auth::user()->isAdmin()) {
|
||||
$cols = array_merge($cols, [
|
||||
'tickets' => Str::ucfirst(__('validation.custom.tickets')),
|
||||
'assigned_to' => Str::ucfirst(__('validation.custom.assigned_to')),
|
||||
]);
|
||||
}
|
||||
$cols = array_merge($cols, [
|
||||
'date_mep' => Str::ucfirst(__('validation.custom.date_mep')),
|
||||
'created_at' => Str::ucfirst(__('validation.attributes.created_at')),
|
||||
]);
|
||||
@endphp
|
||||
@include('bo.modules.table-col-sorter', [
|
||||
'cols' => $cols,
|
||||
'ignore' => ['id', 'tickets'],
|
||||
])
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (!count($tickets))
|
||||
<tr>
|
||||
<td class="text-center" colspan="@if (Auth::user()->isAdmin()) 6 @else 5 @endif">
|
||||
<h2>{{ __('Aucun ticket') }}</h2>
|
||||
</td>
|
||||
@endcan
|
||||
@if (Auth::user()->isAdmin())
|
||||
<td>{{ $ticket->assignedUser ? $ticket->assignedUser->name : __('Personne') }}</td>
|
||||
@endif
|
||||
<td>{{ Carbon\Carbon::parse($ticket->created_at)->format('d/m/Y à H:i') }}</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<div>
|
||||
<div class="btn-group" role="group">
|
||||
@php
|
||||
$ticket_files = $ticket->getFiles();
|
||||
@endphp
|
||||
@foreach ($ticket_files as $key => $file)
|
||||
@if ($file !== null && (\App\Lib\Helpers\ToolboxHelper::isImageFile($file) || \App\Lib\Helpers\ToolboxHelper::isPdfFile($file)))
|
||||
<a class="glightbox btn btn-outline-dark"
|
||||
data-gallery="gallery{{ $ticket->id }}" href="{{ asset($file) }}"
|
||||
target="_blank"><i class="fa-solid fa-file-image"></i>
|
||||
</a>
|
||||
@break
|
||||
</tr>
|
||||
@endif
|
||||
@foreach ($tickets as $key => $ticket)
|
||||
<tr class="position-relative">
|
||||
{{-- order --}}
|
||||
@if (session('tickets.or'))
|
||||
@include('bo.modules.change-model-order', [
|
||||
'routeName' => 'ticket',
|
||||
'model' => $ticket,
|
||||
'models' => $tickets,
|
||||
'value' => $key + 1,
|
||||
'noRightsVisible' => false,
|
||||
])
|
||||
@endif
|
||||
{{-- id --}}
|
||||
<td scope="row">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="me-1">{{ $ticket->id }} @if ($ticket->is_internal)
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
@endif
|
||||
@endforeach
|
||||
{{-- Modal du ticket --}}
|
||||
<!-- Button trigger modal -->
|
||||
<button class="btn btn-outline-info" data-bs-toggle="modal"
|
||||
data-bs-target="#ticket{{ $ticket->id }}Modal" type="button"><i
|
||||
class="fa-solid fa-eye"></i></button>
|
||||
<a class="btn btn-outline-success"
|
||||
href="{{ route('bo.ticket.show', $ticket->id) }}"><i
|
||||
class="fa-solid fa-comment"></i></a>
|
||||
@if (auth()->user()->isAdmin())
|
||||
<a class="btn btn-outline-primary"
|
||||
href="{{ route('bo.ticket.edit', $ticket) }}"><i
|
||||
class="fa-solid fa-pen"></i></a>
|
||||
@endif
|
||||
@can('changeStatus', $ticket)
|
||||
@if (!$ticket->status->equals(\App\Enums\Tickets\TicketStatusEnum::archived))
|
||||
<form class="confirmJS" method="POST"
|
||||
action="{{ route('bo.ticket.archive', $ticket) }}">
|
||||
@method('PUT')
|
||||
@csrf
|
||||
<button class="btn btn-outline-danger rounded-0 rounded-end" type="submit"><i
|
||||
class="fa-solid fa-box-archive"></i></button>
|
||||
</form>
|
||||
</span>
|
||||
<span class="d-flex flex-column text-end">
|
||||
@if (Auth::user()->isAdmin())
|
||||
<a href="{{ route('bo.sites.show', $ticket->site) }}"
|
||||
title="{{ __('Voir le site :sitename', [
|
||||
'sitename' => $ticket->site->name,
|
||||
]) }}">{{ $ticket->site->name }}</a>
|
||||
@else
|
||||
<span class="text-decoration-underline">{{ $ticket->site->name }}</span>
|
||||
@endif
|
||||
@endcan
|
||||
<span class="{{ $ticket->status->class() }} rounded text-center">
|
||||
{{ $ticket->status->label() }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="ticket{{ $ticket->id }}Modal"
|
||||
aria-labelledby="ticket{{ $ticket->id }}ModalLabel" aria-hidden="true" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="ticket{{ $ticket->id }}ModalLabel">
|
||||
@can('isAdmin')
|
||||
<a href="{{ route('bo.sites.show', $ticket->site) }}"
|
||||
title="{{ __('Voir le site :sitename', [
|
||||
'sitename' => $ticket->site->name,
|
||||
]) }}"><b>{{ $ticket->site->name }}</b></a>
|
||||
@else
|
||||
<b>{{ $ticket->site->name }}</b>
|
||||
@endcan -
|
||||
<u>
|
||||
{{ __('Ticket n°') }} {{ $ticket->id }}
|
||||
@if ($ticket->is_internal)
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
@endif
|
||||
</u> -
|
||||
<span class="badge {{ $ticket->type->class() }}">
|
||||
{{ $ticket->type->label() }}
|
||||
</span>
|
||||
</h5>
|
||||
<button class="btn btn-sm btn-primary bg-primary" data-bs-dismiss="modal"
|
||||
type="button" aria-label="Close">
|
||||
<i class="fa-solid fa-xmark text-white"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@can('assignLabels', App\Models\Ticket::class)
|
||||
<div class="mb-3 mt-1">
|
||||
@foreach ($ticket->labels as $label)
|
||||
<span class="badge label colorize"
|
||||
data-color="{{ $label->color }}">{{ $label->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</td>
|
||||
{{-- title --}}
|
||||
<td class="text-center">
|
||||
<span class="me-1">{{ $ticket->title }}</span>
|
||||
</td>
|
||||
{{-- type --}}
|
||||
<td class="text-center">
|
||||
<span class="badge {{ $ticket->type->class() }}">{{ $ticket->type->label() }}</span>
|
||||
</td>
|
||||
{{-- tag --}}
|
||||
@php
|
||||
$routeName = request()->route()->getName();
|
||||
@endphp
|
||||
@can('assignLabels', App\Models\Ticket::class)
|
||||
<td>
|
||||
<span class="ticket-index-labels">
|
||||
@foreach ($ticket->labels as $label)
|
||||
<span class="badge label colorize"
|
||||
data-color="{{ $label->color }}">{{ $label->name }}</span>
|
||||
@endforeach
|
||||
</span>
|
||||
</td>
|
||||
@endcan
|
||||
{{-- assigned_to / date_mep --}}
|
||||
@if (Auth::user()->isAdmin())
|
||||
<td>{{ $ticket->assignedUser ? $ticket->assignedUser->name : __('Personne') }}</td>
|
||||
@endif
|
||||
<td class="text-center">
|
||||
{{ is_null($ticket->date_mep) ? Str::ucfirst(__("validation.custom.date_mep_empty")) : $ticket->date_mep->format('d/m/Y') }}
|
||||
</td>
|
||||
<td>{{ Carbon\Carbon::parse($ticket->created_at)->format('d/m/Y à H:i') }}</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<div>
|
||||
<div class="btn-group" role="group">
|
||||
@php
|
||||
$ticket_files = $ticket->getFiles();
|
||||
@endphp
|
||||
@foreach ($ticket_files as $key => $file)
|
||||
@if ($file !== null && (\App\Lib\Helpers\ToolboxHelper::isImageFile($file) || \App\Lib\Helpers\ToolboxHelper::isPdfFile($file)))
|
||||
<a class="glightbox btn btn-outline-dark"
|
||||
data-gallery="gallery{{ $ticket->id }}" href="{{ asset($file) }}"
|
||||
target="_blank"><i class="fa-solid fa-file-image"></i>
|
||||
</a>
|
||||
@break
|
||||
@endif
|
||||
@endforeach
|
||||
{{-- Modal du ticket --}}
|
||||
<!-- Button trigger modal -->
|
||||
<button class="btn btn-outline-info" data-bs-toggle="modal"
|
||||
data-bs-target="#ticket{{ $ticket->id }}Modal" type="button"><i
|
||||
class="fa-solid fa-eye"></i></button>
|
||||
<a class="btn btn-outline-success"
|
||||
href="{{ route('bo.ticket.show', $ticket->id) }}"><i
|
||||
class="fa-solid fa-comment"></i></a>
|
||||
@if (auth()->user()->isAdmin())
|
||||
<a class="btn btn-outline-primary"
|
||||
href="{{ route('bo.ticket.edit', $ticket) }}"><i
|
||||
class="fa-solid fa-pen"></i></a>
|
||||
@endif
|
||||
@can('changeStatus', $ticket)
|
||||
@if (!$ticket->status->equals(\App\Enums\Tickets\TicketStatusEnum::archived))
|
||||
<form class="confirmJS" method="POST"
|
||||
action="{{ route('bo.ticket.archive', $ticket) }}">
|
||||
@method('PUT')
|
||||
@csrf
|
||||
<button class="btn btn-outline-danger rounded-0 rounded-end" type="submit"><i
|
||||
class="fa-solid fa-box-archive"></i></button>
|
||||
</form>
|
||||
@endif
|
||||
@endcan
|
||||
<p>{!! clean(nl2br($ticket->text)) !!}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-sm btn-primary" data-bs-dismiss="modal"
|
||||
type="button">{{ __('Fermer') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
{{ $tickets->withQueryString()->links() }}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="ticket{{ $ticket->id }}Modal"
|
||||
aria-labelledby="ticket{{ $ticket->id }}ModalLabel" aria-hidden="true" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="ticket{{ $ticket->id }}ModalLabel">
|
||||
@can('isAdmin')
|
||||
<a href="{{ route('bo.sites.show', $ticket->site) }}"
|
||||
title="{{ __('Voir le site :sitename', [
|
||||
'sitename' => $ticket->site->name,
|
||||
]) }}"><b>{{ $ticket->site->name }}</b></a>
|
||||
@else
|
||||
<b>{{ $ticket->site->name }}</b>
|
||||
@endcan -
|
||||
<u>
|
||||
{{ __('Ticket n°') }} {{ $ticket->id }}
|
||||
@if ($ticket->is_internal)
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
@endif
|
||||
</u> -
|
||||
<span class="badge {{ $ticket->type->class() }}">
|
||||
{{ $ticket->type->label() }}
|
||||
</span>
|
||||
</h5>
|
||||
<button class="btn btn-sm btn-primary bg-primary" data-bs-dismiss="modal"
|
||||
type="button" aria-label="Close">
|
||||
<i class="fa-solid fa-xmark text-white"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@can('assignLabels', App\Models\Ticket::class)
|
||||
<div class="mb-3 mt-1">
|
||||
@foreach ($ticket->labels as $label)
|
||||
<span class="badge label colorize"
|
||||
data-color="{{ $label->color }}">{{ $label->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
@endcan
|
||||
<p>{!! clean(nl2br($ticket->text)) !!}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-sm btn-primary" data-bs-dismiss="modal"
|
||||
type="button">{{ __('Fermer') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $tickets->withQueryString()->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -273,6 +273,46 @@
|
|||
</div>
|
||||
@endif
|
||||
<hr>
|
||||
<div class="flex-col">
|
||||
<div>
|
||||
<h4>{{ __('Tickets liés') }}</h4>
|
||||
</div>
|
||||
@can('linkTicket', $ticket)
|
||||
<div class="row">
|
||||
<form class="col" id="form_link_ticket" action="{{ route('bo.ticket.link', ['ticket' => $ticket]) }}" method="POST">
|
||||
@csrf
|
||||
<div class="input-group">
|
||||
<label class="input-group-text" for="link-ticket"><i class="fa-solid fa-ticket"></i></label>
|
||||
<input class="form-control" id="linked_ticket_id" name="linked_ticket_id" type="text" placeholder="Numéro du ticket">
|
||||
<button class="btn btn-sm btn-success float-end" type="submit">
|
||||
{{ __('Lier') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endcan
|
||||
<div class="row row-cols-auto gap-2">
|
||||
@foreach ($linkedTicket as $linked)
|
||||
<div class="col">
|
||||
@can('linkTicket', $ticket)
|
||||
<form id="form_unlink_ticket" action="{{ route('bo.ticket.unlink', ['ticket' => $ticket, 'linkedTicket' => $linked]) }}" method="POST">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<div class="input-group mt-2">
|
||||
@endcan
|
||||
<a class="btn btn-xl btn-outline-primary" href="{{ route('bo.ticket.show', $linked->id) }}" title="{{ Str::ucfirst(__('crud.other.view_linked_ticket', ['model' => $linked->id])) }}">
|
||||
TICKET - {{ $linked->id }}
|
||||
</a>
|
||||
@can('linkTicket', $ticket)
|
||||
<button class="btn btn-xl btn-outline-danger" type="submit" title="{{ Str::ucfirst(__('crud.other.unlink_ticket', ['model' => $linked->id])) }}"><i class="fa-solid fa-link-slash"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
@endcan
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
@can('createGitlabIssue', $ticket)
|
||||
<form class="col d-flex justify-content-end" id="form_issue" action="{{ route('bo.ticket.create-issue', $ticket) }}" method="POST">
|
||||
@csrf
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
@if(!session('tickets.sort_way') or session('tickets.sort_col') !== $colname or (session('tickets.sort_way') === 'desc' and session('tickets.sort_col') === $colname))
|
||||
<a class="col_sort text-secondary lead asc" href="{{ request()->fullUrlWithQuery(['sort_col' => $colname, 'sort_way' => 'asc']) }}"> ↓ </a>
|
||||
@endif
|
||||
@if(!session('tickets.sort_way') or session('tickets.sort_col') !== $colname or (session('tickets.sort_way') === 'asc' and session('tickets.sort_col') === $colname))
|
||||
<a class="col_sort text-secondary lead desc" href="{{ request()->fullUrlWithQuery(['sort_col' => $colname, 'sort_way' => 'desc']) }}"> ↑ </a>
|
||||
@endif
|
||||
|
|
@ -95,6 +95,10 @@ Route::middleware('admin-secure')
|
|||
)
|
||||
->middleware('can:createGitlabIssue,ticket')
|
||||
->name('ticket.create-issue');
|
||||
Route::patch('/tickets/{ticket}/change-order/{direction}', [
|
||||
TicketController::class,
|
||||
'changeOrder'
|
||||
])->where('direction', 'up|down')->name('ticket.change-order');
|
||||
});
|
||||
Route::resource('credit_accounts', AccountController::class)
|
||||
->only(['index'])
|
||||
|
|
@ -150,6 +154,12 @@ Route::middleware('admin-secure')
|
|||
Route::post('/{ticket}/subscribe', [TicketController::class, 'subscribe'])
|
||||
->middleware('can:suscribeToTicket,ticket')
|
||||
->name('subscribe');
|
||||
Route::post('/{ticket}/link', [TicketController::class, 'linkTicket'])
|
||||
->middleware('can:linkTicket,ticket')
|
||||
->name('link');
|
||||
Route::delete('/{ticket}/unlink/{linkedTicket}', [TicketController::class, 'unlinkTicket'])
|
||||
->middleware('can:linkTicket,ticket')
|
||||
->name('unlink');
|
||||
Route::put('/{ticket}/archive', [TicketController::class, 'setToArchive'])
|
||||
->middleware('can:changeStatus,ticket')
|
||||
->name('archive');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue