Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Jérémy Munsch
b44d3bb07a
implements #11 Ticket type templating 2023-04-19 17:35:57 +02:00
Jérémy Munsch
22a3f7bf27
npm audit fix 2023-04-19 15:52:10 +02:00
15 changed files with 2016 additions and 1593 deletions

View file

@ -12,7 +12,7 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
* @method static self qrcode()
* @method static self advice()
*/
class TicketType extends BaseEnumRoutable
class TicketTypeEnum extends BaseEnumRoutable
{
/**
* Enum values

View file

@ -3,7 +3,7 @@
namespace App\Http\Controllers;
use App\Enums\TicketStatus;
use App\Enums\TicketType;
use App\Enums\TicketTypeEnum;
use App\Events\NotifySubscriptions;
use App\Events\TicketStatusChanged;
use App\Http\Requests\AssignTicketToUserRequest;
@ -578,9 +578,9 @@ class TicketController extends Controller
})->map(function (TicketStatus $ticket) {
return $ticket->value;
});
$t = collect(TicketType::toArray())->filter(function (TicketType $s) use ($word) {
$t = collect(TicketTypeEnum::toArray())->filter(function (TicketTypeEnum $s) use ($word) {
return Str::slug($s->label) === Str::slug($word);
})->map(function (TicketType $ticket) {
})->map(function (TicketTypeEnum $ticket) {
return $ticket->value;
});
$q->orWhere('id', $word)

View file

@ -0,0 +1,37 @@
<?php
namespace App\Http\Controllers;
use App\Models\Ticket\TicketType;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
class TicketTypeController extends Controller
{
use CrudController {
edit as crudEdit;
}
/** @var \App\Models\MacroSchedule\Marker */
public static $modelClass = TicketType::class;
/**
* Crud Layout
*
* @var string
*/
public static $layout = 'layout.app';
/**
* Edit model action
*
* @param mixed ...$params
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function edit(...$params)
{
/** @var \Illuminate\View\View */
$view = call_user_func_array([$this, 'crudEdit'], $params);
return view('ticket_types.edit', \array_merge($view->getData()));
}
}

View file

@ -3,7 +3,7 @@
namespace App\Http\Requests;
use App\Enums\TicketStatus;
use App\Enums\TicketType;
use App\Enums\TicketTypeEnum;
use App\Models\Ticket;
use Illuminate\Foundation\Http\FormRequest;
@ -78,9 +78,9 @@ class StoreTicket extends FormRequest
'required',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if (!in_array($value, TicketType::toValues())) {
if (!in_array($value, TicketTypeEnum::toValues())) {
$fail(__('Veuillez choisir entre :values', [
'value' => implode(',', TicketType::toLabels())
'value' => implode(',', TicketTypeEnum::toLabels())
]));
}
}

View file

@ -4,7 +4,7 @@ namespace App\Models;
use App\Enums\Role;
use App\Enums\TicketStatus;
use App\Enums\TicketType;
use App\Enums\TicketTypeEnum;
use App\Events\NotifyAdmin;
use App\Events\NotifySubscriptions;
use App\Events\TicketStatusChanged;
@ -12,6 +12,7 @@ use App\Jobs\SendTicketMentionMail;
use App\Mail\TicketMention;
use App\Models\Credit\Account;
use App\Models\Credit\Estimation;
use App\Models\Ticket\TicketType;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@ -29,7 +30,7 @@ class Ticket extends Model
* @var array
*/
protected $enumCasts = [
'type' => TicketType::class,
'type' => TicketTypeEnum::class,
'status' => TicketStatus::class
];
@ -64,7 +65,7 @@ class Ticket extends Model
{
static::creating(function (Ticket $ticket) {
// If bug force the ticket status on new. All the others are estimateTodo.
if ($ticket->type->equals(TicketType::bug())) {
if ($ticket->type->equals(TicketTypeEnum::bug())) {
$ticket->status = TicketStatus::new();
} else {
$ticket->status = TicketStatus::estimateTodo();
@ -168,6 +169,16 @@ class Ticket extends Model
return $this->belongsToMany(Label::class, 'label_ticket');
}
/**
* BelongsTo ticket_type
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function ticketType()
{
return $this->belongsTo(TicketType::class, 'type', 'type');
}
// * METHODS
/**

View file

@ -0,0 +1,109 @@
<?php
namespace App\Models\Ticket;
use App\Enums\TicketTypeEnum;
use App\Models\Ticket;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
use Kwaadpepper\Enum\Traits\CastsEnums;
class TicketType extends Model
{
use CastsEnums;
use HasFactory;
use IsCrudModel;
/**
* Crud Label
*
* @var string
*/
protected $crudLabelColumn = 'type';
/**
* Crud Value
*
* @var string
*/
protected $crudValueColumn = 'id';
/**
* The attributes that should be cast to Enums.
*
* @var array
*/
protected $enumCasts = [
'type' => TicketTypeEnum::class,
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'type',
'template'
];
/**
* TicketType
*
* @param array $attributes
*/
public function __construct(array $attributes = [])
{
self::$editableProperties = [
'type' => [
'label' => trans('Type'),
'type' => CrudType::enum(),
'enum' => TicketTypeEnum::class,
'required' => true,
'readonly' => true,
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::update()
]
],
'template' => [
'label' => trans('Template'),
'type' => CrudType::text(),
'required' => true,
'readonly' => false,
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::update()
]
],
];
parent::__construct($attributes);
}
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted()
{
static::creating(function () {
throw new \Error('Denied');
});
}
/**
* BelongsToMany tickets
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tickets()
{
return $this->hasMany(Ticket::class, 'type', 'type');
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Policies\Rules\UserRoleRules as URR;
use Illuminate\Auth\Access\HandlesAuthorization;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
class TicketTypePolicy extends RootPolicy
{
use HandlesAuthorization;
/**
* Init policies on call
*
* @param \App\Models\User $user
* @return void
*/
public function setPolicies(User $user)
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'tickettype' => [
'viewAny' => URR::make('userIsAdmin', $user),
'update' => URR::make('userIsAdmin', $user)
]
];
}
}

View file

@ -17,6 +17,7 @@ use App\Models\Package;
use App\Models\Project;
use App\Models\Site;
use App\Models\Ticket;
use App\Models\Ticket\TicketType;
use App\Models\User;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
@ -102,6 +103,7 @@ class RouteServiceProvider extends ServiceProvider
Route::model('user', User::class);
Route::model('site', Site::class);
Route::model('project', Project::class);
Route::model('ticket_type', TicketType::class);
Route::bind('ticket', function ($value) {
return Ticket::where('id', $value)->has('site')->firstOrFail();
});

View file

@ -231,7 +231,7 @@ return [
'Carbon' => 'Carbon\Carbon',
// * Application custom
'TicketType' => App\Enums\TicketType::class,
'TicketTypeEnum' => App\Enums\TicketTypeEnum::class,
'TicketStatus' => App\Enums\TicketStatus::class,
'ToolboxHelper' => App\Lib\ToolboxHelper::class,
'CrudAction' => App\Enums\CrudAction::class,

View file

@ -0,0 +1,44 @@
<?php
use App\Enums\TicketTypeEnum;
use App\Models\Ticket\TicketType;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTicketTypesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('ticket_types', function (Blueprint $table) {
$table->id();
$table->unsignedTinyInteger('type')->unique()->comment('TicketType Enum');
$table->text('template')->comment('Le template du type de ticket');
$table->timestamps();
});
collect(TicketTypeEnum::toArray())->each(function (TicketTypeEnum $ticketTypeEnum) {
(new TicketType([
'type' => $ticketTypeEnum,
'template' => "Problème recontré :\n\nUrl pertinents :\n\nCi-joint une capture d'écran.\nCordialement.",
]))->saveQuietly();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('ticket_types');
}
}

3263
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,7 @@ $userPr = Auth::user()->sites ?? collect([]);
<div class="form-group col">
<label for="type" class="col-form-label">{{ __('Type') }}</label>
<select class="form-select" name="type" id="type">
@foreach(TicketType::toArray() as $ticketType)
@foreach(\App\Enums\TicketTypeEnum::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>
@endforeach
</select>
@ -31,6 +31,41 @@ $userPr = Auth::user()->sites ?? collect([]);
@endforeach
</select>
</div>
@push('scripts')
<script nonce="{{ $nonce ?? null }}">
document.addEventListener('DOMContentLoaded', function () {
const typeSwitch = document.getElementById('type');
const textArea = document.getElementById('text');
const templates = {
@foreach(\App\Models\Ticket\TicketType::query()->get() as $ticketTypeModel)
@php $value = str_replace(["\r\n", "\n", "\r"], '\n', str_replace("<br />", '\n', strip_tags(nl2br($ticketTypeModel->template), '<br />'))); @endphp
"{{ $ticketTypeModel->type->value }}" : "{!! $value !!}",
@endforeach
};
const changeTextValue = () => {
switch(typeSwitch.value) {
@foreach(\App\Models\Ticket\TicketType::query()->get() as $ticketTypeModel)
case "{{ $ticketTypeModel->type->value }}":
// * Ignore if template already changed.
if(textArea.value !== (templates[textArea.template] ?? null) && textArea.value.length) {
return;
}
// Save actual template
textArea.template = typeSwitch.value;
// apply template
textArea.value = templates[typeSwitch.value];
break;
@endforeach
default:
console.error(`Unkown value ${typeSwitch.value}`);
}
};
typeSwitch.addEventListener('change', changeTextValue);
changeTextValue();
});
</script>
@endpush
@else
<input type="hidden" name="status" value="{{ \App\Enums\TicketStatus::new() }}" required>
@endcan

View file

@ -0,0 +1,24 @@
@extends('layout.app')
@section('bodyClass', 'col-md-8')
@section('content')
<div class="card">
<div class="card-header">
<h2 class="col h4">
<a href="{{ route('ticket_types.index') }}" class="text-decoration-none btn btn-sm btn-primary" role="button" aria-roledescription="{{ _('Revenir en arrière') }}"><i class="fa-solid fa-arrow-left-long"></i></a>
<span>&nbsp;{{ __('Modifier un type de ticket') }}</span> - {{ $model->type->label }}
</h2>
</div>
<div class="card-body">
<form method="POST" action="{{ route('ticket_types.update', $model->id) }}" enctype="multipart/form-data">
@csrf
@method('PUT')
@include('ticket_types.form-inputs')
<div class="form-group text-center">
<input type="submit" class="btn btn-primary" value="Modifier">
</div>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,19 @@
<div class="form-group mb-5">
<label for="type"><b>{{ __('Type') }}</b></label>
<select class="form-select" name="type" id="type" readonly>
<option value="" @if(!old('credit_account_id')) selected @endif disabled>{{ __('Veuillez sélectionner un compte à débiter') }}</option>
@foreach(\App\Enums\TicketTypeEnum::toArray() as $ticketTypeEnum)
<option value="{{ $ticketTypeEnum->value }}" @if($model->type->value === $ticketTypeEnum->value or old('type') == $ticketTypeEnum->value) selected @endif>{{ $ticketTypeEnum->label }}</option>
@endforeach
</select>
@include('modules.input-error', [
'inputName' => 'type'
])
</div>
<div class="form-group mb-5">
<label for="template" class="col-form-label text-md-right">{{ __('Template') }}</label>
<textarea rows="15" class="form-control" name="template" id="text" placeholder="{{ __('Template du type de ticket') }} *">{{ old('template') ?? $model->template ?? ''}}</textarea>
@include('modules.input-error', [
'inputName' => 'template'
])
</div>

View file

@ -19,6 +19,7 @@ use App\Http\Controllers\ProjectController;
use App\Http\Controllers\ScheduleController;
use App\Http\Controllers\SiteController;
use App\Http\Controllers\TicketController;
use App\Http\Controllers\TicketTypeController;
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
@ -93,6 +94,14 @@ Route::middleware('auth:web')->group(function () {
'macro_markers' => 'marker'
]);
// * Types de tickets
Route::resource('ticket_types', TicketTypeController::class)->only(['index', 'edit', 'update'])
->withoutMiddleware('htmlsanitize')
->middleware('htmlsanitize:template')
->parameters([
'ticket_types' => 'ticket_type'
]);
// * Brands and Accounts
Route::resource('credit_accounts', AccountController::class)->only(['index'])
->parameters([