Merge branch 'develop' into trunk
This commit is contained in:
commit
aad7dc9fcc
96 changed files with 2925 additions and 1996 deletions
|
|
@ -8,6 +8,7 @@ use Illuminate\Foundation\Validation\ValidatesRequests;
|
|||
use Illuminate\Routing\Controller as BaseController;
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
|
|
@ -55,9 +56,9 @@ class Controller extends BaseController
|
|||
/** @var \App\Models\User|null */
|
||||
$user = Auth::user();
|
||||
if (!is_null($user) and !$user->isAdmin()) {
|
||||
$ticketsNew = $ticketsNew->where('is_internal', 0)->whereNotIn('site_id', $ignore);
|
||||
$ticketsWaiting = $ticketsWaiting->where('is_internal', 0)->whereNotIn('site_id', $ignore);
|
||||
$ticketsInProgress = $ticketsInProgress->where('is_internal', 0)->whereNotIn('site_id', $ignore);
|
||||
$ticketsNew = $ticketsNew->where('is_internal', false)->whereNotIn('site_id', $ignore);
|
||||
$ticketsWaiting = $ticketsWaiting->where('is_internal', false)->whereNotIn('site_id', $ignore);
|
||||
$ticketsInProgress = $ticketsInProgress->where('is_internal', false)->whereNotIn('site_id', $ignore);
|
||||
}
|
||||
|
||||
View::share('ticketsNew', $ticketsNew->count());
|
||||
|
|
@ -67,4 +68,68 @@ class Controller extends BaseController
|
|||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Search query on specified fields
|
||||
* splitting search into words or using whole sentence
|
||||
*
|
||||
* @param Builder $query The eloquent query builder.
|
||||
* @param string $search The query string.
|
||||
* @param callable $searchQuery The query string.
|
||||
* @param string ...$fields Either a simple array or a string
|
||||
* in case of a string the two fields would be concatenate to make the search.
|
||||
* @return Builder
|
||||
*/
|
||||
protected function searchQuery(
|
||||
Builder $query,
|
||||
string $search,
|
||||
callable $searchQuery = null,
|
||||
string ...$fields
|
||||
): Builder {
|
||||
if (count($fields)) {
|
||||
// Search using words.
|
||||
$words = explode(' ', $search);
|
||||
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
|
||||
foreach ($words as $word) {
|
||||
if (!strlen($word)) {
|
||||
continue;
|
||||
}
|
||||
$this->searchOnFields($query, $word, $fields);
|
||||
} //end foreach
|
||||
// Search whole using whole sentence.
|
||||
$this->searchOnFields($query, $search, $fields);
|
||||
if ($searchQuery) {
|
||||
$searchQuery($query);
|
||||
}
|
||||
});
|
||||
} //end if
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build query search on all fields
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $search
|
||||
* @param array $fields
|
||||
* @return Builder
|
||||
*/
|
||||
private function searchOnFields(Builder $query, string $search, array $fields): Builder
|
||||
{
|
||||
$table = $query->getModel()->getTable();
|
||||
foreach ($fields as $f) {
|
||||
$f = str_replace('?', '\?', $f);
|
||||
if (is_array($f)) {
|
||||
$query->orWhereRaw(
|
||||
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($f)->map(function ($q) use ($table) {
|
||||
return "`$table`.`$q`";
|
||||
})->implode(', \' \', ')),
|
||||
["%$search%"]
|
||||
);
|
||||
} else {
|
||||
$query->orWhereRaw("UPPER(`$table`.`$f`) LIKE UPPER(?)", ["%$search%"]);
|
||||
}
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ class SiteController extends Controller
|
|||
index as crudIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* SiteController
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
|
@ -23,8 +26,19 @@ class SiteController extends Controller
|
|||
/** @var Site */
|
||||
public static $modelClass = Site::class;
|
||||
|
||||
/**
|
||||
* Crud Layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
/**
|
||||
* Site.index crud
|
||||
*
|
||||
* @param CrudIndexRequest $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
|
|||
|
|
@ -6,39 +6,48 @@ use App\Enums\TicketStatus;
|
|||
use App\Enums\TicketType;
|
||||
use App\Events\NotifySubscriptions;
|
||||
use App\Events\TicketStatusChanged;
|
||||
use App\Http\Requests\AssignTicketToUserRequest;
|
||||
use App\Http\Requests\ChangeTicketStatus;
|
||||
use App\Http\Requests\CreateMessage;
|
||||
use App\Http\Requests\UpdateMessage;
|
||||
use App\Http\Requests\SortTickets;
|
||||
use App\Http\Requests\StoreTicket;
|
||||
use App\Http\Requests\UpdateMessage;
|
||||
use App\Http\Requests\UpdateTicket;
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Mail\NewTicket;
|
||||
use App\Models\Message;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
/**
|
||||
* TicketController
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->authorizeResource(Ticket::class, 'ticket');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tickets List
|
||||
*
|
||||
* @param SortTickets $request
|
||||
* @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index(SortTickets $request)
|
||||
{
|
||||
// handle ajax
|
||||
// * Handle Ajax
|
||||
if ($request->ajax()) {
|
||||
$tickets = Ticket::with('user');
|
||||
$tickets = Ticket::with(['user', 'assignedUser']);
|
||||
if ($request->s) {
|
||||
$tickets = $tickets->where('text', 'LIKE', "%$request->s%")
|
||||
->orWhere('id', $request->s)
|
||||
|
|
@ -71,13 +80,12 @@ class TicketController extends Controller
|
|||
$q->whereId($user->id);
|
||||
});
|
||||
});
|
||||
$tickets = $tickets->where('is_internal', 0);
|
||||
$tickets = $tickets->where('is_internal', false);
|
||||
}
|
||||
$tickets = $tickets->where('status', ($request->st == TicketStatus::archived()) ?
|
||||
'=' : '<>', TicketStatus::archived());
|
||||
if ($request->search) {
|
||||
$tickets = $this->searchTicket($tickets, $request);
|
||||
}
|
||||
|
||||
$tickets = $this->searchTicket($tickets, $request);
|
||||
$tickets = $this->filterTickets($tickets, $request);
|
||||
$tickets = $tickets->paginate();
|
||||
|
||||
|
|
@ -87,14 +95,387 @@ class TicketController extends Controller
|
|||
return view('ticket.index', compact('tickets', 'sites', 'status'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Ticket
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function show(Request $request, Ticket $ticket)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
return \response()->json([
|
||||
'label' => sprintf(
|
||||
'Ticket %d (%s): %s',
|
||||
$ticket->id,
|
||||
$ticket->user ? $ticket->user->email : __('Utilisateur supprimé'),
|
||||
Str::limit(\preg_replace('/[[:cntrl:]]+/', '', $ticket->text), 35)
|
||||
),
|
||||
'value' => $ticket->id
|
||||
]);
|
||||
}
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
if (!$user->can('update', $ticket)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if ($ticket->is_internal && !$user->can('changeInternalStatus', $ticket)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$messages = $ticket->messages()->orderBy('created_at', 'DESC')->get();
|
||||
$isSubscribed = (bool)$ticket->subscriptions()->where('user_id', $user->id)->count();
|
||||
return view('ticket.show', compact('ticket', 'messages', 'isSubscribed'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show create Form
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
$sites = Site::orderBy('name')->get();
|
||||
} else {
|
||||
$sites = $user->sites;
|
||||
}
|
||||
|
||||
if (!count($sites)) {
|
||||
return redirect()->route('sites.create')
|
||||
->with('warning', trans('Vous devez tout d\'abord créer un site pour y ajouter un ticket'));
|
||||
}
|
||||
return view('ticket.create', compact('sites'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ticket
|
||||
*
|
||||
* @param StoreTicket $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreTicket $request)
|
||||
{
|
||||
try {
|
||||
return DB::transaction(function () use ($request) {
|
||||
/** @var \App\Models\Site */
|
||||
$site = Site::findOrFail($request->site_id);
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
$ticket = new Ticket($request->validated());
|
||||
|
||||
/** @var \Illuminate\Http\UploadedFile|null */
|
||||
$fileTicket = $request->file_ticket;
|
||||
/** @var \Illuminate\Http\UploadedFile|null */
|
||||
$fileTicket2 = $request->file_ticket2;
|
||||
|
||||
if ($fileTicket) {
|
||||
$ticket->file = ToolboxHelper::storeImage($ticket->getTable(), $fileTicket);
|
||||
}
|
||||
if ($fileTicket2) {
|
||||
$ticket->file2 = ToolboxHelper::storeImage($ticket->getTable(), $fileTicket2);
|
||||
}
|
||||
|
||||
$ticket->user()->associate(auth()->user());
|
||||
$ticket->site()->associate($site);
|
||||
$ticket->saveOrFail();
|
||||
|
||||
$user->subscriptions()->toggle($ticket->id);
|
||||
|
||||
if (config('mail.MAIL_ADMIN')) {
|
||||
Mail::to(config('mail.MAIL_ADMIN'))->send(new NewTicket($ticket));
|
||||
} else {
|
||||
throw new \ErrorException('Missing MAIL_ADMIN in .env');
|
||||
}
|
||||
|
||||
return redirect()->route('ticket.show', $ticket)
|
||||
->with('success', trans('Ticket enregistré !'));
|
||||
});
|
||||
} catch (\ErrorException $e) {
|
||||
report($e);
|
||||
Session::flash('error', trans('Une erreur est survenue'));
|
||||
} //end try
|
||||
|
||||
return \back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show update form
|
||||
*
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function edit(Ticket $ticket)
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
$sites = Site::orderBy('name')->get();
|
||||
} else {
|
||||
$sites = $user->sites;
|
||||
}
|
||||
return view('ticket.edit', compact('ticket', 'sites'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Model
|
||||
*
|
||||
* @param UpdateTicket $request
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(UpdateTicket $request, Ticket $ticket)
|
||||
{
|
||||
$ticket->fill($request->validated());
|
||||
|
||||
try {
|
||||
return DB::transaction(function () use ($request, $ticket) {
|
||||
/** @var \App\Models\Site */
|
||||
$site = Site::findOrFail($request->site_id);
|
||||
|
||||
/** @var \Illuminate\Http\UploadedFile|null */
|
||||
$fileTicket = $request->file_ticket;
|
||||
/** @var \Illuminate\Http\UploadedFile|null */
|
||||
$fileTicket2 = $request->file_ticket2;
|
||||
|
||||
if ($fileTicket) {
|
||||
$ticket->file = ToolboxHelper::storeImage($ticket->getTable(), $fileTicket);
|
||||
}
|
||||
if ($fileTicket2) {
|
||||
$ticket->file2 = ToolboxHelper::storeImage($ticket->getTable(), $fileTicket2);
|
||||
}
|
||||
|
||||
$ticket->site()->associate($site);
|
||||
$ticket->saveOrFail();
|
||||
|
||||
return redirect()->route('ticket.edit', $ticket->id)
|
||||
->with('success', 'Modifications enregistrées !');
|
||||
});
|
||||
} catch (\ErrorException $e) {
|
||||
report($e);
|
||||
Session::flash('error', trans('Une erreur est survenue'));
|
||||
} //end try
|
||||
|
||||
return \back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change ticket Status
|
||||
*
|
||||
* @param ChangeTicketStatus $request
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function changeStatus(ChangeTicketStatus $request, Ticket $ticket)
|
||||
{
|
||||
$ticket->fill($request->validated());
|
||||
$ticket->saveOrFail();
|
||||
|
||||
return back()
|
||||
->with('warning', trans('Le status à été modifié'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new Message to the Ticket
|
||||
*
|
||||
* @param CreateMessage $request
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function createMessage(CreateMessage $request, Ticket $ticket)
|
||||
{
|
||||
try {
|
||||
return DB::transaction(function () use ($request, $ticket) {
|
||||
/** @var \Illuminate\Http\UploadedFile|null */
|
||||
$fileMessage = $request->file_message;
|
||||
|
||||
$message = new Message($request->validated());
|
||||
$message->user()->associate(auth()->user());
|
||||
$message->ticket()->associate($ticket);
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
if ($request->subscribe) {
|
||||
if (!$user->subscriptions()->find($ticket->id)) {
|
||||
$user->subscriptions()->attach($ticket->id);
|
||||
}
|
||||
} elseif ($user->subscriptions()->find($ticket->id)) {
|
||||
$user->subscriptions()->detach($ticket->id);
|
||||
}
|
||||
|
||||
$message->saveOrFail();
|
||||
|
||||
if ($fileMessage) {
|
||||
$message->file = ToolboxHelper::storeImage($message->getTable(), $fileMessage);
|
||||
$message->saveOrFail();
|
||||
}
|
||||
return back()
|
||||
->with('success', trans('Votre message à été ajouté'));
|
||||
});
|
||||
} catch (\ErrorException $e) {
|
||||
report($e);
|
||||
Session::flash('error', trans('Une erreur est survenue'));
|
||||
} //end try
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Message
|
||||
*
|
||||
* @param UpdateMessage $request
|
||||
* @param Ticket $ticket
|
||||
* @param Message $message
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
*/
|
||||
public function updateMessage(UpdateMessage $request, Ticket $ticket, Message $message)
|
||||
{
|
||||
$message->message = $request->message;
|
||||
$message->saveOrFail();
|
||||
|
||||
return back()
|
||||
->with('success', trans('Votre message à bien été modifié'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change ticket status to archived
|
||||
*
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function setToArchive(Ticket $ticket)
|
||||
{
|
||||
$ticket->status = TicketStatus::archived();
|
||||
$ticket->saveOrFail();
|
||||
return redirect()->route('ticket.index')
|
||||
->with('warning', trans('Le ticket est archivé'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to ticket
|
||||
*
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function subscribe(Ticket $ticket)
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$user->subscriptions()->toggle($ticket->id);
|
||||
if ($user->subscriptions()->where('ticket_id', $ticket->id)->exists()) {
|
||||
return back()
|
||||
->with('success', trans('Vous avez été inscrit'));
|
||||
}
|
||||
return back()->with('warning', trans('Vous avez été désinscrit'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reopen ticket
|
||||
*
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function reopen(Ticket $ticket)
|
||||
{
|
||||
$message = trans(':ticketName à réouvert le ticket n°:ticketNumber', [
|
||||
'ticketName' => $ticket->user->name,
|
||||
'ticketNumber' => $ticket->id,
|
||||
]);
|
||||
$oldStatus = $ticket->status;
|
||||
$ticket->status = TicketStatus::inprogress();
|
||||
$ticket->saveQuietly();
|
||||
event(new TicketStatusChanged($ticket, $oldStatus, null, $message));
|
||||
event(new NotifySubscriptions($ticket, $message, collect([$ticket->user])));
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$user->subscriptions()->toggle($ticket->id);
|
||||
|
||||
return back()->with('warning', trans('Votre ticket à été réouvert.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign ticket to user ticket
|
||||
*
|
||||
* @param AssignTicketToUserRequest $request
|
||||
* @param Ticket $ticket
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function assignTo(AssignTicketToUserRequest $request, Ticket $ticket)
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var \App\Models\User|null */
|
||||
if ($assignedUser = User::whereId($request->assign)->first()) {
|
||||
$message = trans(':admin à assigné le ticket n°:ticketNumber à :assignedAdmin', [
|
||||
'admin' => $user->name,
|
||||
'ticketNumber' => $ticket->id,
|
||||
'assignedAdmin' => $assignedUser->name,
|
||||
]);
|
||||
$ticket->assignedUser()->associate($assignedUser);
|
||||
} else {
|
||||
$message = trans(
|
||||
':admin modifié l\'assignation du ticket n°:ticketNumber, il n\'est plus assigné à quelqu\'un.',
|
||||
[
|
||||
'admin' => $user->name,
|
||||
'ticketNumber' => $ticket->id,
|
||||
]
|
||||
);
|
||||
$ticket->assignedUser()->dissociate();
|
||||
}
|
||||
$ticket->saveQuietly();
|
||||
|
||||
|
||||
/** @var \App\Enums\TicketStatus */
|
||||
$oldStatus = $ticket->status;
|
||||
event(new TicketStatusChanged($ticket, $oldStatus, null, $message));
|
||||
event(new NotifySubscriptions($ticket, $message, collect([$ticket->user])));
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$user->subscriptions()->toggle($ticket->id);
|
||||
|
||||
if ($assignedUser) {
|
||||
return back()->with('info', trans('Le ticket à été assigné à :assignedUser', [
|
||||
'assignedUser' => $assignedUser->name
|
||||
]));
|
||||
}
|
||||
return back()->with('info', trans('Le ticket n\'est plus assigné à personne'));
|
||||
}
|
||||
|
||||
// * PRIVATES
|
||||
|
||||
/**
|
||||
* Filter Tickets request constructor
|
||||
*
|
||||
* @param Builder $tickets
|
||||
* @param Request $request
|
||||
* @return Builder
|
||||
*/
|
||||
private function filterTickets(Builder $tickets, Request $request): Builder
|
||||
{
|
||||
if ($request->rst) {
|
||||
Session::remove('tickets.ass');
|
||||
Session::remove('tickets.st');
|
||||
Session::remove('tickets.pr');
|
||||
Session::remove('tickets.sort_col');
|
||||
Session::remove('tickets.sort_way');
|
||||
}
|
||||
if ($request->assr) {
|
||||
Session::remove('tickets.ass');
|
||||
}
|
||||
if ($request->prs) {
|
||||
Session::remove('tickets.pr');
|
||||
}
|
||||
|
|
@ -113,14 +494,23 @@ class TicketController extends Controller
|
|||
if ($request->st) {
|
||||
Session::put('tickets.st', $request->st);
|
||||
}
|
||||
if ($request->ass) {
|
||||
Session::put('tickets.ass', $request->ass);
|
||||
}
|
||||
Session::save();
|
||||
|
||||
if (Session::has('tickets.pr')) {
|
||||
$tickets = $tickets->where('site_id', Session::get('tickets.pr'));
|
||||
$tickets->where('site_id', Session::get('tickets.pr'));
|
||||
}
|
||||
|
||||
if (Session::has('tickets.ass')) {
|
||||
$tickets->whereHas('assignedUser', function ($q) {
|
||||
$q->whereId(Session::get('tickets.ass'));
|
||||
});
|
||||
}
|
||||
|
||||
if (Session::has('tickets.st')) {
|
||||
$tickets = $tickets->where('status', Session::get('tickets.st'));
|
||||
$tickets->where('status', Session::get('tickets.st'));
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
@ -128,264 +518,65 @@ class TicketController extends Controller
|
|||
Session::has('tickets.sort_way') and
|
||||
in_array(Session::get('tickets.sort_col'), [
|
||||
'id', 'site_id', 'created_at', 'created_by',
|
||||
'type', 'status'
|
||||
'type', 'status', 'assigned_to'
|
||||
])
|
||||
) {
|
||||
$tickets = $tickets->orderBy(
|
||||
$tickets->orderBy(
|
||||
Session::get('tickets.sort_col'),
|
||||
Session::get('tickets.sort_way')
|
||||
);
|
||||
} else {
|
||||
$tickets = $tickets->orderBy('id', 'DESC');
|
||||
$tickets->orderBy('id', 'DESC');
|
||||
}
|
||||
return $tickets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search ticket sql request constructor
|
||||
*
|
||||
* @param Builder $tickets
|
||||
* @param Request $request
|
||||
* @return Builder
|
||||
*/
|
||||
private function searchTicket(Builder $tickets, Request $request): Builder
|
||||
{
|
||||
$words = explode(' ', $request->search);
|
||||
return $tickets->where(function ($q) use ($words) {
|
||||
foreach ($words as $word) {
|
||||
if (!strlen($word)) {
|
||||
continue;
|
||||
}
|
||||
$q = $q->where(function ($q) use ($word) {
|
||||
if ($request->exists('search')) {
|
||||
if (empty($request->search)) {
|
||||
Session::remove('tickets.search');
|
||||
} else {
|
||||
Session::put('tickets.search', $request->search);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Session::has('tickets.search')) {
|
||||
return $tickets;
|
||||
}
|
||||
|
||||
return $this->searchQuery($tickets, Session::get('tickets.search'), function ($q) {
|
||||
$words = explode(' ', Session::get('tickets.search'));
|
||||
if (\is_array($words)) {
|
||||
foreach ($words as $word) {
|
||||
if (!strlen($word)) {
|
||||
continue;
|
||||
}
|
||||
$s = collect(TicketStatus::toArray())->filter(function (TicketStatus $t) use ($word) {
|
||||
return strpos(strtolower($t->label), $word) !== false;
|
||||
return Str::slug($t->label) === Str::slug($word);
|
||||
})->map(function (TicketStatus $ticket) {
|
||||
return $ticket->value;
|
||||
});
|
||||
$t = collect(TicketType::toArray())->filter(function (TicketType $t) use ($word) {
|
||||
return strpos(strtolower($t->label), $word) !== false;
|
||||
$t = collect(TicketType::toArray())->filter(function (TicketType $s) use ($word) {
|
||||
return Str::slug($s->label) === Str::slug($word);
|
||||
})->map(function (TicketType $ticket) {
|
||||
return $ticket->value;
|
||||
});
|
||||
$q->where('id', $word)
|
||||
->orwhere('text', 'LIKE', "%$word%")
|
||||
$q->orWhere('id', $word)
|
||||
->orWhereIn('status', $s)
|
||||
->orWhereIn('type', $t)
|
||||
->orWhere('file', 'LIKE', "%$word%")
|
||||
->orWhere('file2', 'LIKE', "%$word%")
|
||||
->orWhereHas('user', function ($q) use ($word) {
|
||||
$q->where('name', 'LIKE', "%$word%");
|
||||
});
|
||||
});
|
||||
}//end foreach
|
||||
});
|
||||
}
|
||||
|
||||
public function show(Request $request, Ticket $ticket)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
return \response()->json([
|
||||
'label' => sprintf(
|
||||
'Ticket %d (%s): %s',
|
||||
$ticket->id,
|
||||
$ticket->user ? $ticket->user->email : __('Utilisateur supprimé'),
|
||||
Str::limit(\preg_replace('/[[:cntrl:]]+/', '', $ticket->text), 35)
|
||||
),
|
||||
'value' => $ticket->id
|
||||
]);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user->can('update', $ticket)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if ($ticket->is_internal === 1 && !$user->can('changeInternalStatus', $ticket)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$messages = $ticket->messages()->orderBy('created_at', 'DESC')->get();
|
||||
$isSubscribed = (bool)$ticket->subscriptions()->where('user_id', $user->id)->count();
|
||||
return view('ticket.show', compact('ticket', 'messages', 'isSubscribed'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
$sites = Site::orderBy('name')->get();
|
||||
} else {
|
||||
$sites = $user->sites;
|
||||
}
|
||||
|
||||
if (!count($sites)) {
|
||||
return redirect()->route('sites.create')
|
||||
->with('warning', 'Vous devez tout d\'abord créer un site pour y ajouter un ticket');
|
||||
}
|
||||
return view('ticket.create', compact('sites'));
|
||||
}
|
||||
|
||||
public function store(StoreTicket $request)
|
||||
{
|
||||
$site = Site::findOrFail($request->site_id);
|
||||
|
||||
$ticket = new Ticket($request->validated());
|
||||
|
||||
if ($request->file_ticket) {
|
||||
$ticket->file = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket);
|
||||
}
|
||||
if ($request->file_ticket2) {
|
||||
$ticket->file2 = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket2);
|
||||
}
|
||||
|
||||
if ($request->has('is_internal')) {
|
||||
$ticket->is_internal = 1;
|
||||
} else {
|
||||
$ticket->is_internal = 0;
|
||||
}
|
||||
|
||||
$ticket->user()->associate(auth()->user());
|
||||
$ticket->saveOrFail();
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$user->subscriptions()->toggle($ticket->id);
|
||||
|
||||
if (config('mail.MAIL_ADMIN')) {
|
||||
Mail::to(config('mail.MAIL_ADMIN'))->send(new NewTicket($ticket));
|
||||
} else {
|
||||
throw new Exception('Missing MAIL_ADMIN in .env');
|
||||
}
|
||||
|
||||
return redirect()->route('ticket.show', $ticket)
|
||||
->with('success', 'Ticket enregistré !');
|
||||
}
|
||||
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
public function edit(Ticket $ticket)
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
$sites = Site::orderBy('name')->get();
|
||||
} else {
|
||||
$sites = $user->sites;
|
||||
}
|
||||
return view('ticket.edit', compact('ticket', 'sites'));
|
||||
}
|
||||
|
||||
public function update(UpdateTicket $request, Ticket $ticket)
|
||||
{
|
||||
$site = Site::findOrFail($request->site_id);
|
||||
$ticket->fill($request->validated());
|
||||
|
||||
if ($request->file_ticket) {
|
||||
if (File::exists($ticket->file)) {
|
||||
File::delete($ticket->file);
|
||||
}
|
||||
$ticket->file = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket);
|
||||
}
|
||||
if ($request->file_ticket2) {
|
||||
if (File::exists($ticket->file2)) {
|
||||
File::delete($ticket->file2);
|
||||
}
|
||||
$ticket->file2 = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket2);
|
||||
}
|
||||
|
||||
if ($request->has('is_internal')) {
|
||||
$ticket->is_internal = 1;
|
||||
} else {
|
||||
$ticket->is_internal = 0;
|
||||
}
|
||||
|
||||
$ticket->save();
|
||||
|
||||
return redirect()->route('ticket.edit', $ticket->id)
|
||||
->with('success', 'Modifications enregistrées !');
|
||||
}
|
||||
|
||||
public function changeStatus(ChangeTicketStatus $request, Ticket $ticket)
|
||||
{
|
||||
$ticket->fill($request->validated());
|
||||
$ticket->save();
|
||||
|
||||
return back()
|
||||
->with('warning', trans('Le status à été modifié'));
|
||||
}
|
||||
|
||||
public function createMessage(CreateMessage $request, Ticket $ticket)
|
||||
{
|
||||
$fileMessage = $request->file_message;
|
||||
|
||||
$message = new Message($request->validated());
|
||||
$message->user()->associate(auth()->user());
|
||||
$message->ticket()->associate($ticket);
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
// Toggle auth user subscription
|
||||
if ($request->subscribe) {
|
||||
if (!$user->subscriptions()->find($ticket->id)) {
|
||||
$user->subscriptions()->attach($ticket->id);
|
||||
}
|
||||
} elseif ($user->subscriptions()->find($ticket->id)) {
|
||||
$user->subscriptions()->detach($ticket->id);
|
||||
}
|
||||
|
||||
$message->saveOrFail();
|
||||
|
||||
// ici $message->id n'est pas connu lors de la création du message.
|
||||
// on doit donc sauvegarder cette info après le create initial
|
||||
if ($request->file_message) {
|
||||
$siteSlug = Str::slug($ticket->site->name);
|
||||
$fileMessage = $request->file_message;
|
||||
$fileName = $siteSlug . '-ticket-' . $ticket->id .
|
||||
'-message-' . $message->id . '.' . $fileMessage->extension();
|
||||
$fileMessage->move(public_path('fichiers/' . $siteSlug . '/' . $ticket->id . '/'), $fileName);
|
||||
$filePath = 'fichiers/' . $siteSlug . '/' . $ticket->id . '/' . $fileName;
|
||||
$message->file = $filePath;
|
||||
$message->saveOrFail();
|
||||
}
|
||||
|
||||
|
||||
return back()
|
||||
->with('success', trans('Votre message à été ajouté'));
|
||||
}
|
||||
|
||||
public function updateMessage(UpdateMessage $request, Ticket $ticket, Message $message)
|
||||
{
|
||||
$message->message = $request->message;
|
||||
$message->saveOrFail();
|
||||
|
||||
return back()
|
||||
->with('success', trans('Votre message à bien été modifié'));
|
||||
}
|
||||
|
||||
public function setToArchive(Ticket $ticket)
|
||||
{
|
||||
$ticket->status = TicketStatus::archived();
|
||||
$ticket->saveOrFail();
|
||||
return redirect()->route('ticket.index')
|
||||
->with('warning', trans('Le ticket est archivé'));
|
||||
}
|
||||
|
||||
public function subscribe(Ticket $ticket)
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
User::find(Auth::user()->id)->subscriptions()->toggle($ticket->id);
|
||||
$status = $user->subscriptions()->where('ticket_id', $ticket->id)->exists();
|
||||
if ($status) {
|
||||
return back()
|
||||
->with('success', trans('Vous avez été inscrit'));
|
||||
}
|
||||
return back()->with('warning', trans('Vous avez été désinscrit'));
|
||||
}
|
||||
|
||||
public function reopen(Ticket $ticket)
|
||||
{
|
||||
$message = "{$ticket->user->name} à réouvert le ticket n°{$ticket->id}";
|
||||
$oldStatus = $ticket->status;
|
||||
$ticket->status = TicketStatus::inprogress();
|
||||
$ticket->saveQuietly();
|
||||
event(new TicketStatusChanged($ticket, $oldStatus, null, $message));
|
||||
event(new NotifySubscriptions($ticket, $message, collect([$ticket->user])));
|
||||
User::find(Auth::user()->id)->subscriptions()->toggle($ticket->id);
|
||||
return back()->with('warning', trans('Votre ticket à été réouvert.'));
|
||||
} //end foreach
|
||||
} //end if
|
||||
}, 'text', 'file', 'file2');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class Kernel extends HttpKernel
|
|||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\App\Http\Middleware\StartSession::class,
|
||||
\App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Fruitcake\Cors\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
|
|
@ -32,9 +32,12 @@ class Kernel extends HttpKernel
|
|||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\App\Http\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\SecureHeaders::class,
|
||||
'xss'
|
||||
],
|
||||
|
||||
'api' => [
|
||||
|
|
@ -61,5 +64,6 @@ class Kernel extends HttpKernel
|
|||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'xss' => \App\Http\Middleware\XssSanitizer::class,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@ class Authenticate extends Middleware
|
|||
/**
|
||||
* Get the path the user should be redirected to when they are not authenticated.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return string|null
|
||||
*/
|
||||
protected function redirectTo($request)
|
||||
{
|
||||
/** @var \Illuminate\Http\Request $request */
|
||||
if (! $request->expectsJson()) {
|
||||
return route('login');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,5 @@ class PreventRequestsDuringMaintenance extends Middleware
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
];
|
||||
protected $except = [];
|
||||
}
|
||||
|
|
|
|||
71
app/Http/Middleware/SecureHeaders.php
Normal file
71
app/Http/Middleware/SecureHeaders.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
class SecureHeaders
|
||||
{
|
||||
/**
|
||||
* Enumerate headers which you do not want in your application's responses.
|
||||
* Great starting point would be to go check out @Scott_Helme's:
|
||||
* https://securityheaders.com/
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $unwantedHeaderList = [
|
||||
'X-Powered-By',
|
||||
'Server',
|
||||
];
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
View::share('nonce', $nonce = \uniqid());
|
||||
// * For VueJS dev tool to execute
|
||||
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-$nonce'";
|
||||
$reportUri = Route::has('cspReportUri') ?
|
||||
\sprintf('report-uri %s', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
|
||||
$this->removeUnwantedHeaders($this->unwantedHeaderList);
|
||||
$response = $next($request);
|
||||
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
|
||||
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||
$response->headers->set('X-XSS-Protection', '1; mode=block');
|
||||
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
|
||||
$cspPolicies = [
|
||||
// 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;"
|
||||
];
|
||||
$response->headers->set(
|
||||
'Content-Security-Policy',
|
||||
implode(' ', $cspPolicies) . $reportUri
|
||||
);
|
||||
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unwanted headers
|
||||
*
|
||||
* @param array $headerList
|
||||
* @return void
|
||||
*/
|
||||
private function removeUnwantedHeaders(array $headerList)
|
||||
{
|
||||
foreach ($headerList as $header) {
|
||||
header_remove($header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,11 +9,12 @@ class StartSession extends MiddlewareStartSession
|
|||
/**
|
||||
* Save the session data to storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return void
|
||||
*/
|
||||
protected function saveSession($request)
|
||||
{
|
||||
/** @var \Illuminate\Http\Request $request */
|
||||
if (!$request->is('/ping')) {
|
||||
parent::saveSession($request);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class TrimStrings extends Middleware
|
|||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
'password',
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class TrustHosts extends Middleware
|
|||
/**
|
||||
* Get the host patterns that should be trusted.
|
||||
*
|
||||
* @return array
|
||||
* @return array<int, string|null>
|
||||
*/
|
||||
public function hosts()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,5 +19,10 @@ class TrustProxies extends Middleware
|
|||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ class VerifyCsrfToken extends Middleware
|
|||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
];
|
||||
protected $except = [];
|
||||
}
|
||||
|
|
|
|||
46
app/Http/Middleware/XssSanitizer.php
Normal file
46
app/Http/Middleware/XssSanitizer.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Lib\ToolboxHelper;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class XssSanitizer
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param mixed ...$params
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, \Closure $next, ...$params)
|
||||
{
|
||||
$inputs = $request->all();
|
||||
array_walk_recursive($inputs, function (&$input, $name) use ($params) {
|
||||
if (!is_string($input)) {
|
||||
return;
|
||||
}
|
||||
if (in_array($name, $params)) {
|
||||
// ? Is a json input ?
|
||||
if ($isJson = \json_decode($input, true) and is_array($isJson)) {
|
||||
// ! Decode each json field recursive.
|
||||
\array_walk_recursive($isJson, function (&$item) {
|
||||
if (\is_string($item)) {
|
||||
$item = clean($item);
|
||||
$item = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$input = clean($input);
|
||||
$input = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($input);
|
||||
}
|
||||
} else {
|
||||
$input = strip_tags($input);
|
||||
}
|
||||
});
|
||||
$request->merge($inputs);
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
42
app/Http/Requests/AssignTicketToUserRequest.php
Normal file
42
app/Http/Requests/AssignTicketToUserRequest.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AssignTicketToUserRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return auth()->check() and $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'assign' => [
|
||||
'nullable', 'exists:users,id',
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
/** @var \App\Models\User $user */
|
||||
if ($user = User::whereId($value)->first() and !$user->isAdmin()) {
|
||||
$fail(trans('Cet utilisateur ne peut se voir assigner un ticket'));
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -13,13 +13,28 @@ class CreateMessage extends FormRequest
|
|||
*/
|
||||
public function authorize()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
/** @var \App\Models\User|null */
|
||||
if (!($user = auth()->user())) {
|
||||
return false;
|
||||
}
|
||||
return auth()->check() and auth()->user()->sites and (
|
||||
$user->isAdmin() or
|
||||
auth()->user()->sites->contains(request()->route()->ticket->site_id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
|
||||
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'subscribe' => $this->subscribe ? true : false
|
||||
]);
|
||||
return auth()->check() and ($user->isAdmin() or
|
||||
auth()->user()->sites->contains(request()->route()->ticket->site_id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,16 +15,34 @@ class StoreTicket extends FormRequest
|
|||
*/
|
||||
public function authorize()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if (!$user->isAdmin()) {
|
||||
$this->merge([
|
||||
'status' => TicketStatus::new()->value,
|
||||
]);
|
||||
}
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
/** @var \App\Models\User|null */
|
||||
$user = auth()->user();
|
||||
if ($user) {
|
||||
if (!$user->isAdmin()) {
|
||||
$this->merge([
|
||||
'status' => $this->site->status->value,
|
||||
'is_internal' => false,
|
||||
]);
|
||||
} else {
|
||||
$this->merge([
|
||||
'is_internal' => $this->is_internal ? true : false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
|
|
@ -75,6 +93,7 @@ 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'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,19 @@ namespace App\Http\Requests;
|
|||
|
||||
class UpdateTicket extends StoreTicket
|
||||
{
|
||||
public function authorize()
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if (!$user->isAdmin()) {
|
||||
$this->merge(['status' => $this->site->status->value,
|
||||
]);
|
||||
/** @var \App\Models\User|null */
|
||||
if (!($user = auth()->user())) {
|
||||
return false;
|
||||
}
|
||||
return auth()->check() and ($user->isAdmin() or
|
||||
request()->route()->ticket->created_by === auth()->user()->id);
|
||||
optional(request()->route()->ticket)->created_by === $user->id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
255
app/Lib/ToolboxHelper.php
Normal file
255
app/Lib/ToolboxHelper.php
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib;
|
||||
|
||||
use App\Enums\LoadLevel;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class ToolboxHelper
|
||||
{
|
||||
/**
|
||||
* Compute plain text to html representation.
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public static function plainToHtml(string $text): string
|
||||
{
|
||||
$text = e($text);
|
||||
// * url replace and email replace
|
||||
$text = preg_replace_callback(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
'/(?P<email>[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,})|(?P<user>@\w*)|(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
|
||||
function ($matches) {
|
||||
$match = collect($matches)->first() ?? '';
|
||||
if (filter_var($match, FILTER_VALIDATE_EMAIL)) {
|
||||
return strlen($match) ? "<a href=\"mailto:$match\">$match</a>" : $match;
|
||||
} elseif (isset($matches['user']) and strlen($matches['user'])) {
|
||||
return sprintf(
|
||||
'<span class="text-primary fw-bold font-weight-bold">%s</span>',
|
||||
$matches['user']
|
||||
);
|
||||
} else {
|
||||
$url = $match;
|
||||
if (\strpos($url, 'http') === false and \strpos($url, 'https') === false) {
|
||||
$url = sprintf(
|
||||
'%s://%s',
|
||||
config('app.env') !== 'local' ? 'https' : 'http',
|
||||
$url
|
||||
);
|
||||
}
|
||||
$fsligthbox = self::isImageFile($url) ? 'data-fslightbox="gallery"' : '';
|
||||
return strlen($match) ? "<a $fsligthbox href=\"$url\" target=\"_blank\">$match</a>" : $match;
|
||||
}
|
||||
},
|
||||
$text
|
||||
);
|
||||
return nl2br($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check uri points to image file
|
||||
*
|
||||
* @param string $url
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isImageFile(string $url): bool
|
||||
{
|
||||
$m = [];
|
||||
return \preg_match('/(\.png|\.jpg|\.jpeg|\.gif)$/', $url, $m) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* delais 7 jours 168h
|
||||
* travaillé 5 jours 37h
|
||||
* temps tickets 1 jours 7.4h
|
||||
*
|
||||
* personnes 4 (TpP) 3.7h / semaine * prs
|
||||
*
|
||||
* délais moyen ticket (Dmt) 0.75h
|
||||
*
|
||||
* délais max attente pour 1 ticket (Dam) 7jours
|
||||
*
|
||||
* niveaux de charge 4: - faible
|
||||
* - stable
|
||||
* - élevée
|
||||
* - saturation
|
||||
*
|
||||
* états tickets(Et) : - nouveaux (non traités)
|
||||
* - en attente (charge pendante)
|
||||
* - traités
|
||||
*
|
||||
* Algo : linéaire
|
||||
*
|
||||
* TpP : 3.7h
|
||||
* P : 4
|
||||
*
|
||||
* Et(nvx) * Dam
|
||||
*
|
||||
* Nv Charge = (P * TpP) / (Et(nvx) * Dam) - (P * TpP) / (Et(nvx) * DmT) - (Et(att) * DmT)
|
||||
*
|
||||
*
|
||||
* Si nv Charge < 0 => saturation
|
||||
*
|
||||
* @return \App\Enums\LoadLevel
|
||||
*/
|
||||
public static function computeWorkload(): LoadLevel
|
||||
{
|
||||
$Tnew = View::shared('ticketsNew');
|
||||
$Tprogress = View::shared('ticketsInProgress');
|
||||
$Twaiting = View::shared('ticketsWaiting');
|
||||
|
||||
$worked = config('tickets.workedHoursInAWeek');
|
||||
$workedDays = config('tickets.workedDaysInAWeek');
|
||||
// * in hours
|
||||
$morningWorkTime = ($worked / $workedDays / 2);
|
||||
// * Devs handlings tickets per week
|
||||
$workingDevsPerWeek = config('tickets.workingDevsPerWeek');
|
||||
// * Worktime per week to handle tickets
|
||||
$timePerWeekDev = $workingDevsPerWeek * $morningWorkTime;
|
||||
// * 45min for a ticket
|
||||
$avgTimeForTicket = config('tickets.avgTimeForTicket');
|
||||
// * The time needed to handle new ticket (average)
|
||||
$todoListDelay = $Tnew * $avgTimeForTicket;
|
||||
// * The time needed to handle new ticket (average)
|
||||
$waitingListDelay = $Twaiting * $avgTimeForTicket;
|
||||
// * The time needed to handle new ticket (average)
|
||||
$progressListDelay = $Tprogress * $avgTimeForTicket;
|
||||
$totalTicketsDelay = $todoListDelay + $progressListDelay + $waitingListDelay;
|
||||
// * in weeks
|
||||
$ETAForAnewTicket = $totalTicketsDelay / $timePerWeekDev;
|
||||
switch ((int)round($ETAForAnewTicket)) {
|
||||
case LoadLevel::weak()->value:
|
||||
case LoadLevel::stable()->value:
|
||||
case LoadLevel::high()->value:
|
||||
return LoadLevel::make((int)round($ETAForAnewTicket));
|
||||
default:
|
||||
return LoadLevel::overload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip scheme (https) on oembed urls
|
||||
* Only embed allowed are for MP4 self hosted videos right now
|
||||
*
|
||||
* https is force by ckeditor MediaElements which natively supports
|
||||
* only cloud hosted medias (youtube, spotify etc..)
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
public static function stripCkEditorSchemesOnMediaInputs(string $input): string
|
||||
{
|
||||
$input = \preg_replace('/(?<=<oembed url=")(https:\/\/)(?=.*?">)/m', '', $input);
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert That no Back Office url is present on txt
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $text
|
||||
* @return string
|
||||
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
|
||||
*/
|
||||
public static function assertNoInternalBoUrls(string $field, string $text): string
|
||||
{
|
||||
$routes = Route::getRoutes();
|
||||
// * Url replace and email replace.
|
||||
$text = preg_replace_callback(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
'/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
|
||||
function ($matches) use ($routes, $field) {
|
||||
$match = collect($matches)->first() ?? '';
|
||||
$url = $match;
|
||||
try {
|
||||
$request = Request::create($url);
|
||||
// If a route is match then it do not throws exception.
|
||||
if (
|
||||
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) &&
|
||||
trim($routes->match($request)->getPrefix() ?? '', '/') === 'bo'
|
||||
) {
|
||||
// Removes the matched url.
|
||||
throw ValidationException::withMessages([
|
||||
$field => sprintf(
|
||||
'%s : Il est interdit de coller une url du back office dans ce champ',
|
||||
trans($field)
|
||||
)
|
||||
]);
|
||||
}
|
||||
return $match;
|
||||
} catch (NotFoundHttpException $e) {
|
||||
return $match;
|
||||
}
|
||||
},
|
||||
$text
|
||||
);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an image of different types
|
||||
*
|
||||
* @param string $table
|
||||
* @param \Illuminate\Http\UploadedFile|\SplFileInfo|string $image
|
||||
* @return string
|
||||
* @throws \ErrorException If file copy fails.
|
||||
*/
|
||||
public static function storeImage(string $table, $image): string
|
||||
{
|
||||
/** @var \Illuminate\Http\UploadedFile $image */
|
||||
if (\is_object($image) and get_class($image) === UploadedFile::class) {
|
||||
$newPath = self::prepareMoveFile($table, $image->getClientOriginalName());
|
||||
$newPath = str_replace(storage_path('app'), '', $newPath);
|
||||
$image->storePubliclyAs(
|
||||
pathinfo($newPath, \PATHINFO_DIRNAME),
|
||||
pathinfo($newPath, \PATHINFO_BASENAME)
|
||||
);
|
||||
return 'storage' . str_replace('/public', '', $newPath);
|
||||
}
|
||||
/** @var \SplFileInfo $image */
|
||||
if (\is_object($image) and get_class($image) === \SplFileInfo::class) {
|
||||
$newPath = self::prepareMoveFile($table, $image->getBaseName());
|
||||
if (!File::copy($image->getRealPath(), $newPath)) {
|
||||
throw new \ErrorException(sprintf(
|
||||
'Failed to copy file %s to %s',
|
||||
$image->getRealPath(),
|
||||
$newPath
|
||||
));
|
||||
}
|
||||
return \ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/');
|
||||
}
|
||||
if (\is_string($image)) {
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare storage path and return
|
||||
* the image full path
|
||||
*
|
||||
* @param string $table Model table name.
|
||||
* @param string $filename The basename for the file.
|
||||
* @return string
|
||||
*/
|
||||
private static function prepareMoveFile(string $table, string $filename): string
|
||||
{
|
||||
$pathFolder = \storage_path(sprintf('app/public/images/%s', $table));
|
||||
if (!File::exists($pathFolder)) {
|
||||
File::makeDirectory($pathFolder, 0755, true);
|
||||
}
|
||||
$filename = sprintf('%s-%s', Str::uuid(), $filename);
|
||||
$fullPath = sprintf('%s/%s', $pathFolder, $filename);
|
||||
if (File::exists($fullPath)) {
|
||||
File::delete($fullPath);
|
||||
}
|
||||
return $fullPath;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib;
|
||||
|
||||
use App\Enums\LoadLevel;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
class Utils
|
||||
{
|
||||
/**
|
||||
* Compute plain text to html representation.
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public static function plainToHtml(string $text): string
|
||||
{
|
||||
$text = e($text);
|
||||
// * url replace and email replace
|
||||
$text = preg_replace_callback(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
'/(?P<email>[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,})|(?P<user>@\w*)|(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
|
||||
function ($matches) {
|
||||
$match = collect($matches)->first() ?? '';
|
||||
if (filter_var($match, FILTER_VALIDATE_EMAIL)) {
|
||||
return strlen($match) ? "<a href=\"mailto:$match\">$match</a>" : $match;
|
||||
} elseif (isset($matches['user']) and strlen($matches['user'])) {
|
||||
return sprintf(
|
||||
'<span class="text-primary fw-bold font-weight-bold">%s</span>',
|
||||
$matches['user']
|
||||
);
|
||||
} else {
|
||||
$url = $match;
|
||||
if (\strpos($url, 'http') === false and \strpos($url, 'https') === false) {
|
||||
$url = sprintf(
|
||||
'%s://%s',
|
||||
config('app.env') !== 'local' ? 'https' : 'http',
|
||||
$url
|
||||
);
|
||||
}
|
||||
$fsligthbox = self::isImageFile($url) ? 'data-fslightbox="gallery"' : '';
|
||||
return strlen($match) ? "<a $fsligthbox href=\"$url\" target=\"_blank\">$match</a>" : $match;
|
||||
}
|
||||
},
|
||||
$text
|
||||
);
|
||||
return nl2br($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check uri points to image file
|
||||
*
|
||||
* @param string $url
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isImageFile(string $url): bool
|
||||
{
|
||||
$m = [];
|
||||
return \preg_match('/(\.png|\.jpg|\.jpeg|\.gif)$/', $url, $m) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* delais 7 jours 168h
|
||||
* travaillé 5 jours 37h
|
||||
* temps tickets 1 jours 7.4h
|
||||
*
|
||||
* personnes 4 (TpP) 3.7h / semaine * prs
|
||||
*
|
||||
* délais moyen ticket (Dmt) 0.75h
|
||||
*
|
||||
* délais max attente pour 1 ticket (Dam) 7jours
|
||||
*
|
||||
* niveaux de charge 4: - faible
|
||||
* - stable
|
||||
* - élevée
|
||||
* - saturation
|
||||
*
|
||||
* états tickets(Et) : - nouveaux (non traités)
|
||||
* - en attente (charge pendante)
|
||||
* - traités
|
||||
*
|
||||
* Algo : linéaire
|
||||
*
|
||||
* TpP : 3.7h
|
||||
* P : 4
|
||||
*
|
||||
* Et(nvx) * Dam
|
||||
*
|
||||
* Nv Charge = (P * TpP) / (Et(nvx) * Dam) - (P * TpP) / (Et(nvx) * DmT) - (Et(att) * DmT)
|
||||
*
|
||||
*
|
||||
* Si nv Charge < 0 => saturation
|
||||
*
|
||||
* @return \App\Enums\LoadLevel
|
||||
*/
|
||||
public static function computeWorkload(): LoadLevel
|
||||
{
|
||||
$Tnew = View::shared('ticketsNew');
|
||||
$Tprogress = View::shared('ticketsInProgress');
|
||||
$Twaiting = View::shared('ticketsWaiting');
|
||||
|
||||
$worked = config('tickets.workedHoursInAWeek');
|
||||
$workedDays = config('tickets.workedDaysInAWeek');
|
||||
// * in hours
|
||||
$morningWorkTime = ($worked / $workedDays / 2);
|
||||
// * Devs handlings tickets per week
|
||||
$workingDevsPerWeek = config('tickets.workingDevsPerWeek');
|
||||
// * Worktime per week to handle tickets
|
||||
$timePerWeekDev = $workingDevsPerWeek * $morningWorkTime;
|
||||
// * 45min for a ticket
|
||||
$avgTimeForTicket = config('tickets.avgTimeForTicket');
|
||||
// * The time needed to handle new ticket (average)
|
||||
$todoListDelay = $Tnew * $avgTimeForTicket;
|
||||
// * The time needed to handle new ticket (average)
|
||||
$waitingListDelay = $Twaiting * $avgTimeForTicket;
|
||||
// * The time needed to handle new ticket (average)
|
||||
$progressListDelay = $Tprogress * $avgTimeForTicket;
|
||||
$totalTicketsDelay = $todoListDelay + $progressListDelay + $waitingListDelay;
|
||||
// * in weeks
|
||||
$ETAForAnewTicket = $totalTicketsDelay / $timePerWeekDev;
|
||||
switch ((int)round($ETAForAnewTicket)) {
|
||||
case LoadLevel::weak()->value:
|
||||
case LoadLevel::stable()->value:
|
||||
case LoadLevel::high()->value:
|
||||
return LoadLevel::make((int)round($ETAForAnewTicket));
|
||||
default:
|
||||
return LoadLevel::overload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Lib\Utils;
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
|
|
@ -38,7 +38,7 @@ class TicketMention extends Mailable
|
|||
public function build()
|
||||
{
|
||||
$ticket = $this->ticket;
|
||||
$corpsMaj = Utils::plainToHtml($this->corpsMaj);
|
||||
$corpsMaj = ToolboxHelper::plainToHtml($this->corpsMaj);
|
||||
return $this->subject("[SupportWeb] Vous avez été mentionné sur le ticket n° $ticket->id")
|
||||
->view('emails.ticket-mention', compact('ticket', 'corpsMaj'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Lib\Utils;
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
|
|
@ -38,7 +38,7 @@ class TicketUpdate extends Mailable
|
|||
public function build()
|
||||
{
|
||||
$ticket = $this->ticket;
|
||||
$corpsMaj = \html_entity_decode(Utils::plainToHtml($this->corpsMaj));
|
||||
$corpsMaj = \html_entity_decode(ToolboxHelper::plainToHtml($this->corpsMaj));
|
||||
return $this->subject("[SupportWeb][{$ticket->site->name}] Ticket n°$ticket->id")
|
||||
->view('emails.ticket-update', compact('ticket', 'corpsMaj'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,25 @@ use App\Events\NotifySubscriptions;
|
|||
use App\Events\NotifyTicketCreator;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
||||
class Message extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'message'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (Message $message) {
|
||||
|
|
@ -50,7 +59,7 @@ class Message extends Model
|
|||
// https://supportweb.ldc.fr/ticket/583/message/create
|
||||
// SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '583-8' for key 'PRIMARY'
|
||||
// $ticket->subscriptions()->attach($messageUser);
|
||||
// Add subscription
|
||||
// Add subscription.
|
||||
if (!$messageUser->subscriptions()->find($ticket->id)) {
|
||||
$messageUser->subscriptions()->attach($ticket->id);
|
||||
\session()->flash('info', trans('Vous avez été inscrit.'));
|
||||
|
|
@ -69,28 +78,25 @@ class Message extends Model
|
|||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* BelongsTo Ticket relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function ticket()
|
||||
{
|
||||
return $this->belongsTo(Ticket::class, 'ticket_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsTo User relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
public function saveFile(Site $site, Ticket $ticket, UploadedFile $file)
|
||||
{
|
||||
$siteAssetFullPath = Site::ticketAssetsFullPath($site->slug_name);
|
||||
$fileAssetPath = Site::ticketAssetsPath($site->slug_name);
|
||||
$fileName = sprintf(
|
||||
'%s-%s-%d-%d.%s',
|
||||
$site->slug_name,
|
||||
$ticket->id,
|
||||
rand(),
|
||||
$file->extension()
|
||||
);
|
||||
$file->move(sprintf('%s/messages/%d', $siteAssetFullPath, $ticket->id), $fileName);
|
||||
return sprintf('%s/%d/%s', $fileAssetPath, $ticket->id, $fileName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,20 +18,45 @@ class Site extends Model
|
|||
use IsCrudModel;
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* Crud Label
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudLabelColumn = 'name';
|
||||
|
||||
/**
|
||||
* Crud Value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name', 'status',
|
||||
'description', 'todo'
|
||||
];
|
||||
|
||||
/**
|
||||
* The model's attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'status' => '',
|
||||
];
|
||||
|
||||
public function __construct($attributes = [])
|
||||
/**
|
||||
* Site
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
'name' => [
|
||||
|
|
@ -142,6 +167,11 @@ class Site extends Model
|
|||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (Site $site) {
|
||||
|
|
@ -171,32 +201,67 @@ class Site extends Model
|
|||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* HasMany Message relation
|
||||
* The users of type client that would have access to this site tickets
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* HasMany Ticket relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function tickets()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'site_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsToMany Project relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function projects()
|
||||
{
|
||||
return $this->belongsToMany(Project::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphMany Log relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||
*/
|
||||
public function logs()
|
||||
{
|
||||
return $this->morphMany(Log::class, 'parent');
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphMany File relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return $this->morphMany(File::class, 'parent');
|
||||
}
|
||||
|
||||
public function getSlugNameAttribute()
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Getter slugName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSlugNameAttribute(): string
|
||||
{
|
||||
return Str::slug($this->name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use App\Mail\TicketMention;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Collection;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
|
|
@ -22,11 +21,30 @@ class Ticket extends Model
|
|||
use HasFactory;
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to Enums.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'type' => TicketType::class,
|
||||
'status' => TicketStatus::class
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_internal' => 'bool'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'type',
|
||||
|
|
@ -35,10 +53,15 @@ class Ticket extends Model
|
|||
'is_internal'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (Ticket $ticket) {
|
||||
// If evolution force the ticket status on estimateTodo
|
||||
// If evolution force the ticket status on estimateTodo.
|
||||
if ($ticket->type->equals(TicketType::evolution())) {
|
||||
$ticket->status = TicketStatus::estimateTodo();
|
||||
}
|
||||
|
|
@ -64,41 +87,62 @@ class Ticket extends Model
|
|||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* BelongsTo Site relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function site()
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsTo User relation
|
||||
* The user that created the ticket
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsTo User relation
|
||||
* The user that is in charge of the ticket
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function assignedUser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assigned_to');
|
||||
}
|
||||
|
||||
/**
|
||||
* HasMany Message relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return $this->hasMany(Message::class, 'ticket_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsToMany User relation
|
||||
* The Users that are following this ticket
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function subscriptions()
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'subscriptions');
|
||||
}
|
||||
|
||||
public static function saveFile(Site $site, Ticket $ticket, TicketType $type, UploadedFile $file)
|
||||
{
|
||||
$siteAssetFullPath = Site::ticketAssetsFullPath($site->slug_name);
|
||||
$fileAssetPath = Site::ticketAssetsPath($site->slug_name);
|
||||
$fileName = sprintf(
|
||||
'%s-%s-%d-%d.%s',
|
||||
$site->slug_name,
|
||||
$type->getDefinition(),
|
||||
$ticket->id,
|
||||
rand(),
|
||||
$file->extension()
|
||||
);
|
||||
$file->move(sprintf('%s/%d', $siteAssetFullPath, $ticket->id), $fileName);
|
||||
return sprintf('%s/%d/%s', $fileAssetPath, $ticket->id, $fileName);
|
||||
}
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Parse message @user tags and send emails to concerned users
|
||||
|
|
@ -110,7 +154,7 @@ class Ticket extends Model
|
|||
public function sendBeenMentionnedToUser(Message $message): Collection
|
||||
{
|
||||
$ticket = $this;
|
||||
// find @user + send mail si un seul user est trouvé
|
||||
// Find @user + send mail si un seul user est trouvé.
|
||||
$usersNotified = collect([]);
|
||||
$matches = [];
|
||||
$ticket->load('site');
|
||||
|
|
@ -128,7 +172,7 @@ class Ticket extends Model
|
|||
->orWhere('role', Role::developper()->value);
|
||||
})->firstOrFail();
|
||||
|
||||
if ($ticket->is_internal === 1) {
|
||||
if ($ticket->is_internal) {
|
||||
if ($user->isAdmin()) {
|
||||
$usersNotified->add($user);
|
||||
SendTicketMentionMail::dispatchAfterResponse(
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ namespace App\Models;
|
|||
use App\Enums\Role;
|
||||
use App\Jobs\SendUserCreatedEmail;
|
||||
use App\Notifications\ResetPassword;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
|
|
@ -24,17 +26,29 @@ class User extends Authenticatable
|
|||
use Notifiable;
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to Enums.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'role' => Role::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Crud Label
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudLabelColumn = 'name';
|
||||
|
||||
/**
|
||||
* Crud Value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
// stores unhashed pwd
|
||||
public $tmpPass = null;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
|
|
@ -67,7 +81,12 @@ class User extends Authenticatable
|
|||
'email_verified_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function __construct($attributes = [])
|
||||
/**
|
||||
* User
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
'name' => [
|
||||
|
|
@ -148,6 +167,11 @@ class User extends Authenticatable
|
|||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (User $user) {
|
||||
|
|
@ -158,46 +182,89 @@ class User extends Authenticatable
|
|||
if (Hash::needsRehash($user->password)) {
|
||||
$user->password = Hash::make($user->password);
|
||||
}
|
||||
Cache::flush('AdminUsers');
|
||||
});
|
||||
static::updating(function (User $user) {
|
||||
if (Hash::needsRehash($user->password)) {
|
||||
$user->password = Hash::make($user->password);
|
||||
}
|
||||
Cache::flush('AdminUsers');
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* HasMany Ticket relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function tickets()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* HasMany Message relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return $this->hasMany(Messages::class, 'user_id');
|
||||
return $this->hasMany(Message::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsToMany Ticket relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function subscriptions()
|
||||
{
|
||||
return $this->belongsToMany(Ticket::class, 'subscriptions');
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsToMany Site relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function sites()
|
||||
{
|
||||
return $this->belongsToMany(Site::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* HasMany Schedule relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function schedules()
|
||||
{
|
||||
return $this->hasMany(Schedule::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Send User creation confirm mail
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendMailConfirm()
|
||||
{
|
||||
dispatch(new SendUserCreatedEmail($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send User reset password mail
|
||||
*
|
||||
* @param mixed $token
|
||||
* @return void
|
||||
*/
|
||||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
/** @var string $token */
|
||||
$this->notify(new ResetPassword($token));
|
||||
}
|
||||
|
||||
|
|
@ -211,4 +278,16 @@ class User extends Authenticatable
|
|||
return ($this->role->equals(Role::developper()) or
|
||||
$this->role->equals(Role::admin()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get All Admin users
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public static function adminUsers(): Collection
|
||||
{
|
||||
return Cache::rememberForever('AdminUsers', function () {
|
||||
return self::whereIn('role', [Role::admin(), Role::developper()])->get();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class ResetPassword extends ResetPasswordNotification
|
|||
/**
|
||||
* Get the reset password notification mail message for the given URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @param mixed $url
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
protected function buildMailMessage($url)
|
||||
|
|
@ -22,6 +22,7 @@ class ResetPassword extends ResetPasswordNotification
|
|||
->subject(trans('Reinitialisation de mot de passe'))
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
->line(trans('Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte.'))
|
||||
/** @var string $url */
|
||||
->action(trans('Réinitialiser le mot de passe'), $url)
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
->line(trans('Ce lien de réinitialisation de mot de passe expirera dans :count minutes. ', ['count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]))
|
||||
|
|
|
|||
|
|
@ -12,7 +12,24 @@ use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
|||
*/
|
||||
abstract class TicketStaticRules extends StaticRules
|
||||
{
|
||||
protected static function ticketIsNotArchived(Ticket $ticket)
|
||||
/**
|
||||
* Ticket is not in production
|
||||
*
|
||||
* @param Ticket $ticket
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function ticketIsNotInProduction(Ticket $ticket): bool
|
||||
{
|
||||
return !$ticket->status->equals(TicketStatus::production());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ticket is not archived
|
||||
*
|
||||
* @param Ticket $ticket
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function ticketIsNotArchived(Ticket $ticket): bool
|
||||
{
|
||||
return !$ticket->status->equals(TicketStatus::archived());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@ class TicketPolicy extends RootPolicy
|
|||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
* @param User $user
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
|
|
@ -51,7 +50,12 @@ class TicketPolicy extends RootPolicy
|
|||
URR::make('userIsAdmin', $user),
|
||||
TicketStaticRules::make('ticketIsNotArchived', $model),
|
||||
]),
|
||||
'reopen' => URR::make('userIsClient', $user)
|
||||
'reopen' => URR::make('userIsClient', $user),
|
||||
'assignTo' => StaticRules::and([
|
||||
URR::make('userIsAdmin', $user),
|
||||
TicketStaticRules::make('ticketIsNotInProduction', $model),
|
||||
TicketStaticRules::make('ticketIsNotArchived', $model)
|
||||
])
|
||||
]
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ class RouteServiceProvider extends ServiceProvider
|
|||
{
|
||||
$this->configureRateLimiting();
|
||||
|
||||
$this->cspReportRoute();
|
||||
|
||||
$this->routes(function () {
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
|
|
@ -70,4 +72,42 @@ class RouteServiceProvider extends ServiceProvider
|
|||
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* * Register CSP report URI
|
||||
*
|
||||
* Put this function inside RouteServiceProvider,
|
||||
* and call it in RouteServiceProvider::boot
|
||||
*
|
||||
* ! Use this in conjonction with kwaadpepper/exception-handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cspReportRoute()
|
||||
{
|
||||
// * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware
|
||||
\Illuminate\Support\Facades\Route::post('/csp-report', function () {
|
||||
$isCspRequest = request()->header('Content-Type') === 'application/csp-report';
|
||||
if (!$isCspRequest or !request()->json('csp-report') or !is_array(request()->json('csp-report'))) {
|
||||
abort(401);
|
||||
}
|
||||
// * Flatten if needed
|
||||
$cspReport = collect(request()->json('csp-report'))
|
||||
->flatMap(function ($v, $k) {
|
||||
return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v];
|
||||
})->all();
|
||||
// * Filter array keys
|
||||
$cspReport = collect(array_intersect_key($cspReport, [
|
||||
'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '',
|
||||
'referrer' => '', 'violated-directive' => ''
|
||||
]));
|
||||
report(new \Illuminate\Http\Client\HttpClientException(sprintf(
|
||||
"CSP REPORT for %s : \n %s",
|
||||
$cspReport->get('document-uri'),
|
||||
$cspReport->implode("\n")
|
||||
)));
|
||||
})->middleware('throttle:5,2')
|
||||
->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class)
|
||||
->name('cspReportUri');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ return [
|
|||
// * Application custom
|
||||
'TicketType' => App\Enums\TicketType::class,
|
||||
'TicketStatus' => App\Enums\TicketStatus::class,
|
||||
'Utils' => App\Lib\Utils::class,
|
||||
'ToolboxHelper' => App\Lib\ToolboxHelper::class,
|
||||
'CrudAction' => App\Enums\CrudAction::class,
|
||||
'CrudType' => App\Enums\CrudType::class,
|
||||
'Role' => App\Enums\Role::class,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
* if ( ! $this->config->get('purifier.finalize')) {
|
||||
* $config->autoFinalize = false;
|
||||
* }
|
||||
* $config->loadArray($this->getConfig());
|
||||
* $config->load [$this->getConfig());
|
||||
*
|
||||
* You must NOT delete the default settings
|
||||
* anything in settings should be compacted with params that needed to instance HTMLPurifier_Config.
|
||||
|
|
@ -25,26 +25,26 @@ return [
|
|||
'cacheFileMode' => 0755,
|
||||
'settings' => [
|
||||
'default' => [
|
||||
'HTML.SafeIframe' => true,
|
||||
// * For iframes
|
||||
'URI.SafeIframeRegexp' => "%^(http://|https://|//)(vimeo.com)%",
|
||||
'HTML.Doctype' => 'HTML 4.01 Transitional',
|
||||
'HTML.Allowed' => 'h1,h2,h3,h4,h5,h6,div,b,strong,i,em,u,a[href|title],ul,ol,li,p[style],br,span[style],img[width|height|alt|src]',
|
||||
'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align',
|
||||
'HTML.Allowed' => 'h1[style],h2[style],h3[style],h4[style],h5[style],h6[style],figure[class|style],oembed[url],div[style],b,strong,i,em,u,a[href|title|target|rel],ul[style],ol[style],dl[style],li,p[style],br,span[style|class],img[width|height|alt|src|style|class|title],iframe[src|width|height|class|frameborder|style],table[style|align|border|cellspacing|cellpadding|width],thead,tbody,th[colspan|align|valign|style],tr[align|valign|style],td[style|colspan|rowspan|scope|align|valign],hr,dl,dt',
|
||||
'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align,list-style-type,float,width,height,vertical-align,border-color',
|
||||
'AutoFormat.AutoParagraph' => false,
|
||||
'AutoFormat.RemoveEmpty' => false,
|
||||
'AutoFormat.RemoveEmpty.RemoveNbsp' => false,
|
||||
],
|
||||
'test' => [
|
||||
'Attr.EnableID' => 'true',
|
||||
],
|
||||
"youtube" => [
|
||||
"HTML.SafeIframe" => 'true',
|
||||
"URI.SafeIframeRegexp" => "%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/)%",
|
||||
"URI.SafeIframeRegexp" => "%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/|vimeo.com/)%",
|
||||
],
|
||||
'custom_definition' => [
|
||||
'id' => 'html5-definitions',
|
||||
'rev' => 1,
|
||||
'debug' => false,
|
||||
'debug' => true,
|
||||
'elements' => [
|
||||
// http://developers.whatwg.org/sections.html
|
||||
// * http://developers.whatwg.org/sections.html
|
||||
['section', 'Block', 'Flow', 'Common'],
|
||||
['nav', 'Block', 'Flow', 'Common'],
|
||||
['article', 'Block', 'Flow', 'Common'],
|
||||
|
|
@ -52,15 +52,16 @@ return [
|
|||
['header', 'Block', 'Flow', 'Common'],
|
||||
['footer', 'Block', 'Flow', 'Common'],
|
||||
|
||||
// Content model actually excludes several tags, not modelled here
|
||||
// * Content model actually excludes several tags, not modelled here
|
||||
['address', 'Block', 'Flow', 'Common'],
|
||||
['hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common'],
|
||||
|
||||
// http://developers.whatwg.org/grouping-content.html
|
||||
// * http://developers.whatwg.org/grouping-content.html
|
||||
['figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'],
|
||||
['figcaption', 'Inline', 'Flow', 'Common'],
|
||||
['oembed', 'Inline', 'Flow', 'Common'],
|
||||
|
||||
// http://developers.whatwg.org/the-video-element.html#the-video-element
|
||||
// * http://developers.whatwg.org/the-video-element.html#the-video-element
|
||||
['video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
|
||||
'src' => 'URI',
|
||||
'type' => 'Text',
|
||||
|
|
@ -75,7 +76,7 @@ return [
|
|||
'type' => 'Text',
|
||||
]],
|
||||
|
||||
// http://developers.whatwg.org/text-level-semantics.html
|
||||
// * http://developers.whatwg.org/text-level-semantics.html
|
||||
['s', 'Inline', 'Inline', 'Common'],
|
||||
['var', 'Inline', 'Inline', 'Common'],
|
||||
['sub', 'Inline', 'Inline', 'Common'],
|
||||
|
|
@ -83,7 +84,7 @@ return [
|
|||
['mark', 'Inline', 'Inline', 'Common'],
|
||||
['wbr', 'Inline', 'Empty', 'Core'],
|
||||
|
||||
// http://developers.whatwg.org/edits.html
|
||||
// * http://developers.whatwg.org/edits.html
|
||||
['ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']],
|
||||
['del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']],
|
||||
],
|
||||
|
|
@ -95,14 +96,24 @@ return [
|
|||
['tr', 'width', 'Text'],
|
||||
['tr', 'height', 'Text'],
|
||||
['tr', 'border', 'Text'],
|
||||
['figure', 'class', 'Text'],
|
||||
['figure', 'style', 'Text'],
|
||||
['oembed', 'url', 'Text'],
|
||||
['span', 'style', 'Text'],
|
||||
['img', 'style', 'Text'],
|
||||
],
|
||||
],
|
||||
'custom_attributes' => [
|
||||
['a', 'target', 'Enum#_blank,_self,_target,_top'],
|
||||
['ol', 'style', 'Text'],
|
||||
['figure', 'style', 'Text'],
|
||||
['img', 'style', 'Text'],
|
||||
['span', 'style', 'Text'],
|
||||
['oembed', 'url', 'Text'],
|
||||
],
|
||||
'custom_elements' => [
|
||||
['u', 'Inline', 'Inline', 'Common'],
|
||||
['oembed', 'Inline', 'Inline', 'Common'],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->string('password');
|
||||
$table->integer('role')->default(2);
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjects extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('projects', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('projects');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateTickets extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('tickets', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('created_by')->constrained('users');
|
||||
$table->foreignId('project_id')->constrained('projects');
|
||||
$table->integer('type');
|
||||
$table->text('text');
|
||||
$table->string('file')->nullable();
|
||||
$table->integer('status')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('tickets');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateUsers extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->foreignId('project_id')->nullable()->after('role')->constrained('projects');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropConstrainedForeignId('project_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateMessages extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('messages', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained('tickets');
|
||||
$table->foreignId('user_id')->nullable()->constrained('users');
|
||||
$table->text('message');
|
||||
$table->string('file')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('messages');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AlterTickets extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('tickets', function(Blueprint $table) {
|
||||
$table->string('file2')->nullable()->after('file');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('tickets', function(Blueprint $table) {
|
||||
$table->dropColumn('file2');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSubscriptions extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('subscriptions', function (Blueprint $table) {
|
||||
$table->foreignId('ticket_id')->constrained('tickets');
|
||||
$table->foreignId('user_id')->constrained('users');
|
||||
$table->primary(['ticket_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('subscriptions');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateTicketsStatusWithEnumStatus extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
foreach (Ticket::where('status', 4)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::archived();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateTicketsStatusWithEnumStatus2 extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
foreach (Ticket::where('status', 5)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::production();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
foreach (Ticket::where('status', 4)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::recette();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
foreach (Ticket::where('status', 3)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::processed();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateTicketsStatusWithEnumStatus3 extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
//save saveQuietly (without events)
|
||||
foreach (Ticket::where('status', 9)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::archived();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
foreach (Ticket::where('status', 6)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::production();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
foreach (Ticket::where('status', 5)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::recette();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
foreach (Ticket::where('status', 4)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::processed();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
foreach (Ticket::where('status', 3)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::waiting();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
foreach (Ticket::where('status', 2)->get() as $ticket) {
|
||||
$ticket->status = TicketStatus::inprogress();
|
||||
$ticket->saveQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\SiteUser;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectUserTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_user', function (Blueprint $table) {
|
||||
$table->foreignId('project_id')->constrained('projects');
|
||||
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->unique(['project_id', 'user_id']);
|
||||
});
|
||||
/** @var \App\Models\User */
|
||||
foreach (User::all() as $user) {
|
||||
if ($user->project_id) {
|
||||
$uP = new SiteUser();
|
||||
$uP->project_id = $user->project_id;
|
||||
$uP->user_id = $user->id;
|
||||
$uP->saveOrFail();
|
||||
}
|
||||
}
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropForeign(['project_id']);
|
||||
$table->dropColumn('project_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->foreignId('project_id')->nullable()
|
||||
->after('role')->constrained('projects');
|
||||
});
|
||||
/** @var \App\Models\SiteUser */
|
||||
foreach (SiteUser::all() as $uP) {
|
||||
try {
|
||||
/** @var \App\Models\User */
|
||||
$user = User::whereId($uP->user_id)->firstOrFail();
|
||||
$user->project_id = $uP->project_id;
|
||||
$user->saveOrFail();
|
||||
//phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
} catch (ModelNotFoundException $e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
Schema::dropIfExists('project_user');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddSoftDeleteOnUsers extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddSoftDeleteOnProjects extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('projects', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('projects', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class RenameProjectsTableIntoSitesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::rename('projects', 'sites');
|
||||
Schema::table('project_user', function (Blueprint $table) {
|
||||
$table->dropForeign('project_user_project_id_foreign');
|
||||
$table->dropUnique(['project_id', 'user_id']);
|
||||
});
|
||||
Schema::rename('project_user', 'site_user');
|
||||
Schema::table('site_user', function (Blueprint $table) {
|
||||
$table->renameColumn('project_id', 'site_id');
|
||||
$table->foreign('site_id')->references('id')->on('sites');
|
||||
$table->unique(['site_id', 'user_id']);
|
||||
});
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropForeign('tickets_project_id_foreign');
|
||||
$table->renameColumn('project_id', 'site_id');
|
||||
$table->foreign('site_id')->references('id')->on('sites');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::rename('sites', 'projects');
|
||||
Schema::table('site_user', function (Blueprint $table) {
|
||||
$table->dropForeign('site_user_site_id_foreign');
|
||||
$table->dropUnique(['site_id', 'user_id']);
|
||||
});
|
||||
Schema::rename('site_user', 'project_user');
|
||||
Schema::table('project_user', function (Blueprint $table) {
|
||||
$table->renameColumn('site_id', 'project_id');
|
||||
$table->foreign('project_id')->references('id')->on('projects');
|
||||
$table->unique(['project_id', 'user_id']);
|
||||
});
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropForeign('tickets_site_id_foreign');
|
||||
$table->renameColumn('site_id', 'project_id');
|
||||
$table->foreign('project_id')->references('id')->on('projects');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('projects', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('status');
|
||||
$table->text('description');
|
||||
$table->text('todo')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('projects');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectSiteTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_site', function (Blueprint $table) {
|
||||
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
|
||||
$table->foreignId('site_id')->constrained('sites');
|
||||
$table->unique(['project_id', 'site_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('project_site');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateLogsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->text('entry');
|
||||
$table->foreignId('user_id')->constrained('users');
|
||||
$table->morphs('parent'); // can be Site or Projet
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('logs');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateFilesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('files', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('uri', 2000)->nullable(); // max uri length is 2k
|
||||
$table->string('file')->nullable();
|
||||
$table->smallInteger('type');
|
||||
$table->smallInteger('category');
|
||||
$table->morphs('parent'); // can be Site or Projet
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('files');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddTodoOnSitesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('sites', function (Blueprint $table) {
|
||||
$table->string('status')->after('name');
|
||||
$table->text('description')->after('status');
|
||||
$table->text('todo')->after('description')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('sites', function (Blueprint $table) {
|
||||
$table->dropColumn('status');
|
||||
$table->dropColumn('description');
|
||||
$table->dropColumn('todo');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePasswordResetsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('password_resets', function (Blueprint $table) {
|
||||
$table->string('email')->index();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('password_resets');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateWeekPlanningsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('week_plannings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->date('start');
|
||||
$table->text('planning');
|
||||
|
||||
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('week_plannings');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Schedule;
|
||||
use App\Models\WeekPlanning;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSchedulesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('schedules', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained('users');
|
||||
$table->date('date');
|
||||
$table->smallInteger('slot');
|
||||
$table->string('activity')->nullable();
|
||||
$table->text('description');
|
||||
$table->bigInteger('id_lie')->nullable();
|
||||
$table->string('model_lie')->nullable();
|
||||
$table->timestamps();
|
||||
$table->unique(['user_id','date','slot'], 'user_day_slot_unique');
|
||||
});
|
||||
|
||||
//migrate data from week plannings
|
||||
$week_plannings = WeekPlanning::all();
|
||||
|
||||
foreach ($week_plannings as $oneweek) {
|
||||
$user_id = $oneweek->user_id;
|
||||
$plan = $oneweek->planning;
|
||||
foreach ($plan as $time => $data) {
|
||||
$date = Carbon::createFromTimestamp($time);
|
||||
foreach ($data as $dayslot => $cell) {
|
||||
$activite = $this->convertColorToType($cell["type"]);
|
||||
$description = $cell["text"];
|
||||
$id_objetlie = $cell["foreign"];
|
||||
$type_objetlie = $cell["cellModel"]; //c'est une table d'après l'Enum CellModel
|
||||
|
||||
$collected = [
|
||||
'user_id' => $user_id,
|
||||
'date' => $date,
|
||||
'slot' => $dayslot,
|
||||
'activity' => $activite,
|
||||
'description' => $description,
|
||||
'id_lie' => $id_objetlie,
|
||||
'model_lie' => $type_objetlie
|
||||
];
|
||||
//si activite null et description null ne pas enregistrer
|
||||
if (!is_null($activite) && !is_null($description)) {
|
||||
$schedule = new Schedule();
|
||||
$schedule->fill($collected);
|
||||
$schedule->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('schedules');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* convertir une value de couleur en type
|
||||
*/
|
||||
private function convertColorToType($value) {
|
||||
$converter = array(
|
||||
'#c6e0b4' => 'support',
|
||||
'#b4c6e7' => 'rnd',
|
||||
'#00b0f0' => 'dev',
|
||||
'#ffe699' => 'projects',
|
||||
'#f4b084' => 'management',
|
||||
'#aeaaaa' => 'absence',
|
||||
'abscence' => 'absence'
|
||||
);
|
||||
return $converter[$value] ?? null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Schedule;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class MigrateSchedulesToEightSlotsPerDay extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
//migrate data from week plannings
|
||||
for ($i = 4; $i > 0; $i--) {
|
||||
$schedules = Schedule::where('slot', $i)->get();
|
||||
|
||||
foreach ($schedules as $oneschedule) {
|
||||
$newschedule = $oneschedule->replicate();
|
||||
$newschedule->slot = $i * 2;
|
||||
$newschedule->save();
|
||||
$oneschedule->slot = ($i * 2) - 1;
|
||||
$oneschedule->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//not reversible
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class DropWeekPlanningsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::dropIfExists('week_plannings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::create('week_plannings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->date('start');
|
||||
$table->text('planning');
|
||||
|
||||
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateMarkersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('macro_markers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('label')->comment('Le libellé du marqueur.');
|
||||
$table->dateTime('marker')->comment('Le point dans le temps du marqueur.');
|
||||
|
||||
$table->foreignIdFor(Project::class)->constrained()
|
||||
->onUpdate('cascade')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('macro_markers');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateActivitiesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('macro_activities', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->dateTime('start')->comment('La date de début de l\'activité.');
|
||||
$table->dateTime('end')->comment('La date de fin de l\'activité.');
|
||||
$table->unsignedSmallInteger('activity')->comment('Enum MacroSchedule/Activity');
|
||||
$table->unsignedSmallInteger('load')->comment('Le nombre d\'hommes qui vont être mobilisé pour la tâche');
|
||||
$table->string('comment')->comment('Commentaire sur la tâche, (255 chars max)');
|
||||
|
||||
$table->foreignIdFor(Project::class)->constrained()
|
||||
->onUpdate('cascade')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('macro_activities');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ChangeStatusOnProjectsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Project::query()->update(['status' => '0']);
|
||||
Schema::table('projects', function (Blueprint $table) {
|
||||
$table->unsignedSmallInteger('status')->default(0)
|
||||
->comment('Enum ProjectStatus')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('projects', function (Blueprint $table) {
|
||||
$table->string('status')->default('')->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
|
|||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddIsInternalOnTicketsTable extends Migration
|
||||
class AssignUserToTickets extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
|
|
@ -14,7 +14,12 @@ class AddIsInternalOnTicketsTable extends Migration
|
|||
public function up()
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->tinyInteger('is_internal')->default(0)->after('status');
|
||||
$table->foreignId('assigned_to')
|
||||
->comment('La personne a qui est assigné le ticket.')
|
||||
->nullable()
|
||||
->after('created_by')
|
||||
->constrained('users')
|
||||
->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +31,8 @@ class AddIsInternalOnTicketsTable extends Migration
|
|||
public function down()
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropColumn('is_internal')->default(0);
|
||||
$table->dropForeign(['assigned_to']);
|
||||
$table->dropColumn('assigned_to');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
use App\Models\File as ModelsFile;
|
||||
use App\Models\Message;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class MoveImagesToStorage extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
Schema::table('storage', function (Blueprint $table) {
|
||||
if (File::exists(public_path('fichiers'))) {
|
||||
File::moveDirectory(public_path('fichiers'), public_path('storage/fichiers'));
|
||||
Ticket::all()->map(function (Ticket $ticket) {
|
||||
if ($ticket->file) {
|
||||
$ticket->file = "storage/{$ticket->file}";
|
||||
}
|
||||
if ($ticket->file2) {
|
||||
$ticket->file2 = "storage/{$ticket->file2}";
|
||||
}
|
||||
$ticket->saveQuietly();
|
||||
});
|
||||
Message::all()->map(function (Message $message) {
|
||||
if ($message->file) {
|
||||
$message->file = "storage/{$message->file}";
|
||||
}
|
||||
$message->saveQuietly();
|
||||
});
|
||||
ModelsFile::all()->map(function (ModelsFile $fichier) {
|
||||
if ($fichier->file) {
|
||||
$fichier->file = "storage/{$fichier->file}";
|
||||
}
|
||||
$fichier->saveQuietly();
|
||||
});
|
||||
} //end if
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('storage', function (Blueprint $table) {
|
||||
});
|
||||
}
|
||||
}
|
||||
266
database/schema/mysql-schema.dump
Normal file
266
database/schema/mysql-schema.dump
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
DROP TABLE IF EXISTS `files`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `files` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`uri` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`file` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`type` smallint(6) NOT NULL,
|
||||
`category` smallint(6) NOT NULL,
|
||||
`parent_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`parent_id` bigint(20) unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `files_parent_type_parent_id_index` (`parent_type`,`parent_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `logs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `logs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`entry` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`parent_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`parent_id` bigint(20) unsigned NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `logs_user_id_foreign` (`user_id`),
|
||||
KEY `logs_parent_type_parent_id_index` (`parent_type`,`parent_id`),
|
||||
CONSTRAINT `logs_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `macro_activities`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `macro_activities` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`start` datetime NOT NULL COMMENT 'La date de début de l''activité.',
|
||||
`end` datetime NOT NULL COMMENT 'La date de fin de l''activité.',
|
||||
`activity` smallint(5) unsigned NOT NULL COMMENT 'Enum MacroSchedule/Activity',
|
||||
`load` smallint(5) unsigned NOT NULL COMMENT 'Le nombre d''hommes qui vont être mobilisé pour la tâche',
|
||||
`comment` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Commentaire sur la tâche, (255 chars max)',
|
||||
`project_id` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `macro_activities_project_id_foreign` (`project_id`),
|
||||
CONSTRAINT `macro_activities_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `macro_markers`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `macro_markers` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`label` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Le libellé du marqueur.',
|
||||
`marker` datetime NOT NULL COMMENT 'Le point dans le temps du marqueur.',
|
||||
`project_id` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `macro_markers_project_id_foreign` (`project_id`),
|
||||
CONSTRAINT `macro_markers_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `messages`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `messages` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`ticket_id` bigint(20) unsigned NOT NULL,
|
||||
`user_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`message` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`file` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `messages_ticket_id_foreign` (`ticket_id`),
|
||||
KEY `messages_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `messages_ticket_id_foreign` FOREIGN KEY (`ticket_id`) REFERENCES `tickets` (`id`),
|
||||
CONSTRAINT `messages_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `migrations`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `migrations` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`batch` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `password_resets`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `password_resets` (
|
||||
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
KEY `password_resets_email_index` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `project_site`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `project_site` (
|
||||
`project_id` bigint(20) unsigned NOT NULL,
|
||||
`site_id` bigint(20) unsigned NOT NULL,
|
||||
UNIQUE KEY `project_site_project_id_site_id_unique` (`project_id`,`site_id`),
|
||||
KEY `project_site_site_id_foreign` (`site_id`),
|
||||
CONSTRAINT `project_site_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `project_site_site_id_foreign` FOREIGN KEY (`site_id`) REFERENCES `sites` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `projects`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `projects` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`status` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Enum ProjectStatus',
|
||||
`description` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`todo` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `schedules`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `schedules` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`date` date NOT NULL,
|
||||
`slot` smallint(6) NOT NULL,
|
||||
`activity` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`description` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`id_lie` bigint(20) DEFAULT NULL,
|
||||
`model_lie` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_day_slot_unique` (`user_id`,`date`,`slot`),
|
||||
CONSTRAINT `schedules_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `site_user`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `site_user` (
|
||||
`site_id` bigint(20) unsigned NOT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
UNIQUE KEY `site_user_site_id_user_id_unique` (`site_id`,`user_id`),
|
||||
KEY `project_user_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `project_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `site_user_site_id_foreign` FOREIGN KEY (`site_id`) REFERENCES `sites` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `sites`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `sites` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`description` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`todo` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`deleted_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `subscriptions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `subscriptions` (
|
||||
`ticket_id` bigint(20) unsigned NOT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (`ticket_id`,`user_id`),
|
||||
KEY `subscriptions_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `subscriptions_ticket_id_foreign` FOREIGN KEY (`ticket_id`) REFERENCES `tickets` (`id`),
|
||||
CONSTRAINT `subscriptions_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `tickets`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `tickets` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`created_by` bigint(20) unsigned NOT NULL,
|
||||
`site_id` bigint(20) unsigned NOT NULL,
|
||||
`type` int(11) NOT NULL,
|
||||
`text` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`file` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`file2` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`status` int(11) NOT NULL DEFAULT 1,
|
||||
`is_internal` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `tickets_created_by_foreign` (`created_by`),
|
||||
KEY `tickets_project_id_foreign` (`site_id`),
|
||||
CONSTRAINT `tickets_created_by_foreign` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`),
|
||||
CONSTRAINT `tickets_site_id_foreign` FOREIGN KEY (`site_id`) REFERENCES `sites` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `users` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`role` int(11) NOT NULL DEFAULT 2,
|
||||
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`deleted_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `users_email_unique` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
INSERT INTO `migrations` VALUES (1,'2014_10_12_000000_create_users_table',1);
|
||||
INSERT INTO `migrations` VALUES (2,'2020_11_13_152248_create_projects',1);
|
||||
INSERT INTO `migrations` VALUES (3,'2020_11_13_152321_create_tickets',1);
|
||||
INSERT INTO `migrations` VALUES (4,'2020_11_17_140637_update_users',1);
|
||||
INSERT INTO `migrations` VALUES (5,'2020_11_25_104931_create_messages',2);
|
||||
INSERT INTO `migrations` VALUES (6,'2020_11_25_141438_alter_tickets',2);
|
||||
INSERT INTO `migrations` VALUES (7,'2020_11_26_132557_create_subscriptions',3);
|
||||
INSERT INTO `migrations` VALUES (8,'2021_04_21_132132_update_tickets_status_with_enum_status',4);
|
||||
INSERT INTO `migrations` VALUES (9,'2021_04_21_132140_update_tickets_status_with_enum_status2',5);
|
||||
INSERT INTO `migrations` VALUES (10,'2021_04_21_132140_update_tickets_status_with_enum_status3',6);
|
||||
INSERT INTO `migrations` VALUES (11,'2021_06_17_173912_create_project_user_table',7);
|
||||
INSERT INTO `migrations` VALUES (12,'2021_06_28_163754_add_soft_delete_on_users',8);
|
||||
INSERT INTO `migrations` VALUES (13,'2021_06_28_163755_add_soft_delete_on_projects',9);
|
||||
INSERT INTO `migrations` VALUES (14,'2021_07_23_111336_rename_projects_table_into_sites_table',10);
|
||||
INSERT INTO `migrations` VALUES (15,'2021_07_23_150120_create_projects_table',11);
|
||||
INSERT INTO `migrations` VALUES (16,'2021_07_23_152102_create_project_site_table',12);
|
||||
INSERT INTO `migrations` VALUES (17,'2021_07_23_152810_create_logs_table',13);
|
||||
INSERT INTO `migrations` VALUES (18,'2021_07_23_153446_create_files_table',14);
|
||||
INSERT INTO `migrations` VALUES (19,'2021_07_27_144557_add_todo_on_sites_table',15);
|
||||
INSERT INTO `migrations` VALUES (20,'2021_08_01_140823_create_password_resets_table',16);
|
||||
INSERT INTO `migrations` VALUES (21,'2021_10_01_090053_create_week_plannings_table',17);
|
||||
INSERT INTO `migrations` VALUES (22,'2021_12_03_195755_create_schedules_table',18);
|
||||
INSERT INTO `migrations` VALUES (23,'2022_02_23_121545_add_is_internal_on_tickets_table',19);
|
||||
INSERT INTO `migrations` VALUES (24,'2022_03_08_101614_migrate_schedules_to_eight_slots_per_day',20);
|
||||
INSERT INTO `migrations` VALUES (25,'2022_03_15_164122_drop_week_plannings_table',21);
|
||||
INSERT INTO `migrations` VALUES (26,'2022_04_26_095605_create_markers_table',22);
|
||||
INSERT INTO `migrations` VALUES (27,'2022_04_26_095636_create_activities_table',23);
|
||||
INSERT INTO `migrations` VALUES (28,'2022_04_26_102155_change_status_on_projects_table',24);
|
||||
601
package-lock.json
generated
601
package-lock.json
generated
|
|
@ -6,18 +6,22 @@
|
|||
"": {
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-async-to-generator": "^7.16.8",
|
||||
"autoprefixer": "10.4.5",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "^5.0.2",
|
||||
"bootstrap": "^5.1.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"cross-env": "^7.0",
|
||||
"del-cli": "^4.0.1",
|
||||
"fslightbox": "^3.2.3",
|
||||
"laravel-mix": "^6.0.16",
|
||||
"laravel-mix-clean": "^0.1.0",
|
||||
"laravel-mix-polyfill": "^3.0.1",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.29.3",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"sweetalert2": "^11.4.16",
|
||||
"toastify-js": "^1.11.2",
|
||||
"vue": "^2.6.14",
|
||||
"vue-loader": "^15.9.7",
|
||||
|
|
@ -2378,6 +2382,15 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/aggregate-error/node_modules/indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
|
|
@ -2501,12 +2514,24 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
|
||||
"integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"array-uniq": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/array-uniq": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
|
||||
"integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/arrify": {
|
||||
|
|
@ -2574,9 +2599,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.7",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz",
|
||||
"integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==",
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
|
||||
"integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2589,8 +2614,8 @@
|
|||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"browserslist": "^4.20.3",
|
||||
"caniuse-lite": "^1.0.30001335",
|
||||
"browserslist": "^4.20.2",
|
||||
"caniuse-lite": "^1.0.30001332",
|
||||
"fraction.js": "^4.2.0",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.0",
|
||||
|
|
@ -3205,6 +3230,21 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-webpack-plugin": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz",
|
||||
"integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"del": "^4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": ">=4.0.0 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-table3": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz",
|
||||
|
|
@ -3986,6 +4026,53 @@
|
|||
}
|
||||
},
|
||||
"node_modules/del": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
|
||||
"integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/glob": "^7.1.1",
|
||||
"globby": "^6.1.0",
|
||||
"is-path-cwd": "^2.0.0",
|
||||
"is-path-in-cwd": "^2.0.0",
|
||||
"p-map": "^2.0.0",
|
||||
"pify": "^4.0.1",
|
||||
"rimraf": "^2.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/del-cli": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/del-cli/-/del-cli-4.0.1.tgz",
|
||||
"integrity": "sha512-KtR/6cBfZkGDAP2NA7z+bP4p1OMob3wjN9mq13+SWvExx6jT9gFWfLgXEeX8J2B47OKeNCq9yTONmtryQ+m+6g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"del": "^6.0.0",
|
||||
"meow": "^10.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"del": "cli.js",
|
||||
"del-cli": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/del-cli/node_modules/array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/del-cli/node_modules/del": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/del/-/del-6.1.0.tgz",
|
||||
"integrity": "sha512-OpcRktOt7G7HBfyxP0srBH4Djg4824EQORX8E1qvIhIzthNNArxxhrB/Mm7dRMiLi1nvFyUpDhzD2cTtbBhV8A==",
|
||||
|
|
@ -4007,26 +4094,65 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/del-cli": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/del-cli/-/del-cli-4.0.1.tgz",
|
||||
"integrity": "sha512-KtR/6cBfZkGDAP2NA7z+bP4p1OMob3wjN9mq13+SWvExx6jT9gFWfLgXEeX8J2B47OKeNCq9yTONmtryQ+m+6g==",
|
||||
"node_modules/del-cli/node_modules/globby": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
|
||||
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"del": "^6.0.0",
|
||||
"meow": "^10.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"del": "cli.js",
|
||||
"del-cli": "cli.js"
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.2.9",
|
||||
"ignore": "^5.2.0",
|
||||
"merge2": "^1.4.1",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/del-cli/node_modules/is-path-inside": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
|
||||
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/del-cli/node_modules/p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"aggregate-error": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/del-cli/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
|
|
@ -4974,23 +5100,28 @@
|
|||
}
|
||||
},
|
||||
"node_modules/globby": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
|
||||
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
||||
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.2.9",
|
||||
"ignore": "^5.2.0",
|
||||
"merge2": "^1.4.1",
|
||||
"slash": "^3.0.0"
|
||||
"array-union": "^1.0.1",
|
||||
"glob": "^7.0.3",
|
||||
"object-assign": "^4.0.1",
|
||||
"pify": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/globby/node_modules/pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
|
|
@ -5460,6 +5591,15 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/imagemin/node_modules/array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/imagemin/node_modules/globby": {
|
||||
"version": "10.0.2",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz",
|
||||
|
|
@ -5562,12 +5702,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
|
||||
"integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
|
|
@ -5703,13 +5846,28 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/is-path-inside": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
|
||||
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
|
||||
"node_modules/is-path-in-cwd": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
|
||||
"integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-path-inside": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/is-path-inside": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
|
||||
"integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-is-inside": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/is-plain-obj": {
|
||||
|
|
@ -5972,6 +6130,12 @@
|
|||
"webpack-cli": "^4.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/laravel-mix-clean": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/laravel-mix-clean/-/laravel-mix-clean-0.1.0.tgz",
|
||||
"integrity": "sha512-iOgVELzL0zJ2SNEVEmzCoduZ7tBQNWa/ViWXSTg9Z2QVK05Sld2efzTybR82Lu6ohZaA4btr+KsBBw7kt8aqTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/laravel-mix-polyfill": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/laravel-mix-polyfill/-/laravel-mix-polyfill-3.0.1.tgz",
|
||||
|
|
@ -6901,18 +7065,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
|
||||
"integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"aggregate-error": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/p-pipe": {
|
||||
|
|
@ -7051,6 +7209,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-inside": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
|
||||
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
|
|
@ -7115,6 +7279,36 @@
|
|||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pinkie": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
|
||||
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pinkie-promise": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
||||
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pinkie": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
|
|
@ -8072,18 +8266,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/redent/node_modules/indent-string": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
|
||||
"integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
|
|
@ -8409,18 +8591,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ripemd160": {
|
||||
|
|
@ -9197,6 +9376,16 @@
|
|||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sweetalert2": {
|
||||
"version": "11.4.16",
|
||||
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.16.tgz",
|
||||
"integrity": "sha512-6xXd7kQps2M0okOJp5D9kR/TVL+DPWiQGQFoUIwkRPN/FAF/RrWkV/VUT0/a+VXjjkxDQDedEs/PK0/ptEo/7A==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://sweetalert2.github.io/#donations"
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
|
|
@ -9992,6 +10181,21 @@
|
|||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/schema-utils": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
|
||||
|
|
@ -12177,6 +12381,14 @@
|
|||
"requires": {
|
||||
"clean-stack": "^2.0.0",
|
||||
"indent-string": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
|
|
@ -12271,9 +12483,18 @@
|
|||
"dev": true
|
||||
},
|
||||
"array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
|
||||
"integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-uniq": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"array-uniq": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
|
||||
"integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
|
||||
"dev": true
|
||||
},
|
||||
"arrify": {
|
||||
|
|
@ -12336,13 +12557,13 @@
|
|||
"dev": true
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "10.4.7",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz",
|
||||
"integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==",
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.5.tgz",
|
||||
"integrity": "sha512-Fvd8yCoA7lNX/OUllvS+aS1I7WRBclGXsepbvT8ZaPgrH24rgXpZzF0/6Hh3ZEkwg+0AES/Osd196VZmYoEFtw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.20.3",
|
||||
"caniuse-lite": "^1.0.30001335",
|
||||
"browserslist": "^4.20.2",
|
||||
"caniuse-lite": "^1.0.30001332",
|
||||
"fraction.js": "^4.2.0",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.0",
|
||||
|
|
@ -12810,6 +13031,15 @@
|
|||
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
|
||||
"dev": true
|
||||
},
|
||||
"clean-webpack-plugin": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz",
|
||||
"integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"del": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"cli-table3": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz",
|
||||
|
|
@ -13415,19 +13645,18 @@
|
|||
}
|
||||
},
|
||||
"del": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/del/-/del-6.1.0.tgz",
|
||||
"integrity": "sha512-OpcRktOt7G7HBfyxP0srBH4Djg4824EQORX8E1qvIhIzthNNArxxhrB/Mm7dRMiLi1nvFyUpDhzD2cTtbBhV8A==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
|
||||
"integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"globby": "^11.0.1",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"is-glob": "^4.0.1",
|
||||
"is-path-cwd": "^2.2.0",
|
||||
"is-path-inside": "^3.0.2",
|
||||
"p-map": "^4.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"slash": "^3.0.0"
|
||||
"@types/glob": "^7.1.1",
|
||||
"globby": "^6.1.0",
|
||||
"is-path-cwd": "^2.0.0",
|
||||
"is-path-in-cwd": "^2.0.0",
|
||||
"p-map": "^2.0.0",
|
||||
"pify": "^4.0.1",
|
||||
"rimraf": "^2.6.3"
|
||||
}
|
||||
},
|
||||
"del-cli": {
|
||||
|
|
@ -13438,6 +13667,68 @@
|
|||
"requires": {
|
||||
"del": "^6.0.0",
|
||||
"meow": "^10.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"dev": true
|
||||
},
|
||||
"del": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/del/-/del-6.1.0.tgz",
|
||||
"integrity": "sha512-OpcRktOt7G7HBfyxP0srBH4Djg4824EQORX8E1qvIhIzthNNArxxhrB/Mm7dRMiLi1nvFyUpDhzD2cTtbBhV8A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"globby": "^11.0.1",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"is-glob": "^4.0.1",
|
||||
"is-path-cwd": "^2.2.0",
|
||||
"is-path-inside": "^3.0.2",
|
||||
"p-map": "^4.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"globby": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
|
||||
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.2.9",
|
||||
"ignore": "^5.2.0",
|
||||
"merge2": "^1.4.1",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"is-path-inside": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
|
||||
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aggregate-error": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"depd": {
|
||||
|
|
@ -14178,17 +14469,24 @@
|
|||
"dev": true
|
||||
},
|
||||
"globby": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
|
||||
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
||||
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-union": "^2.1.0",
|
||||
"dir-glob": "^3.0.1",
|
||||
"fast-glob": "^3.2.9",
|
||||
"ignore": "^5.2.0",
|
||||
"merge2": "^1.4.1",
|
||||
"slash": "^3.0.0"
|
||||
"array-union": "^1.0.1",
|
||||
"glob": "^7.0.3",
|
||||
"object-assign": "^4.0.1",
|
||||
"pify": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
|
|
@ -14535,6 +14833,12 @@
|
|||
"replace-ext": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
||||
"dev": true
|
||||
},
|
||||
"globby": {
|
||||
"version": "10.0.2",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz",
|
||||
|
|
@ -14611,9 +14915,9 @@
|
|||
}
|
||||
},
|
||||
"indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
|
||||
"integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
|
|
@ -14713,11 +15017,23 @@
|
|||
"integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-path-in-cwd": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
|
||||
"integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-path-inside": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"is-path-inside": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
|
||||
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
|
||||
"dev": true
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
|
||||
"integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-is-inside": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"is-plain-obj": {
|
||||
"version": "1.1.0",
|
||||
|
|
@ -15000,6 +15316,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"laravel-mix-clean": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/laravel-mix-clean/-/laravel-mix-clean-0.1.0.tgz",
|
||||
"integrity": "sha512-iOgVELzL0zJ2SNEVEmzCoduZ7tBQNWa/ViWXSTg9Z2QVK05Sld2efzTybR82Lu6ohZaA4btr+KsBBw7kt8aqTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"laravel-mix-polyfill": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/laravel-mix-polyfill/-/laravel-mix-polyfill-3.0.1.tgz",
|
||||
|
|
@ -15618,13 +15940,10 @@
|
|||
}
|
||||
},
|
||||
"p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aggregate-error": "^3.0.0"
|
||||
}
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
|
||||
"integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
|
||||
"dev": true
|
||||
},
|
||||
"p-pipe": {
|
||||
"version": "3.1.0",
|
||||
|
|
@ -15732,6 +16051,12 @@
|
|||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-inside": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
|
||||
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
|
||||
"dev": true
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
|
|
@ -15781,6 +16106,27 @@
|
|||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||
"dev": true
|
||||
},
|
||||
"pinkie": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
|
||||
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
|
||||
"dev": true
|
||||
},
|
||||
"pinkie-promise": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
||||
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pinkie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
|
|
@ -16405,14 +16751,6 @@
|
|||
"requires": {
|
||||
"indent-string": "^5.0.0",
|
||||
"strip-indent": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"indent-string": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
|
||||
"integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"regenerate": {
|
||||
|
|
@ -16676,9 +17014,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
|
|
@ -17289,6 +17627,12 @@
|
|||
"stable": "^0.1.8"
|
||||
}
|
||||
},
|
||||
"sweetalert2": {
|
||||
"version": "11.4.16",
|
||||
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.16.tgz",
|
||||
"integrity": "sha512-6xXd7kQps2M0okOJp5D9kR/TVL+DPWiQGQFoUIwkRPN/FAF/RrWkV/VUT0/a+VXjjkxDQDedEs/PK0/ptEo/7A==",
|
||||
"dev": true
|
||||
},
|
||||
"tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
|
|
@ -17908,6 +18252,15 @@
|
|||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -12,18 +12,22 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-async-to-generator": "^7.16.8",
|
||||
"autoprefixer": "10.4.5",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "^5.0.2",
|
||||
"bootstrap": "^5.1.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"cross-env": "^7.0",
|
||||
"del-cli": "^4.0.1",
|
||||
"fslightbox": "^3.2.3",
|
||||
"laravel-mix": "^6.0.16",
|
||||
"laravel-mix-clean": "^0.1.0",
|
||||
"laravel-mix-polyfill": "^3.0.1",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.29.3",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"sweetalert2": "^11.4.16",
|
||||
"toastify-js": "^1.11.2",
|
||||
"vue": "^2.6.14",
|
||||
"vue-loader": "^15.9.7",
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
57
resources/js/app.js
vendored
57
resources/js/app.js
vendored
|
|
@ -1,53 +1,4 @@
|
|||
require('./bootstrap');
|
||||
require('./session_watch');
|
||||
//window.Vue = require('vue/dist/vue.min.js');
|
||||
import Vue from 'vue'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/fr'
|
||||
import vSelect from 'vue-select'
|
||||
|
||||
moment.locale('fr')
|
||||
|
||||
// v-select
|
||||
Vue.component('v-select', vSelect)
|
||||
|
||||
Vue.prototype.moment = moment
|
||||
Vue.prototype.bootstrap = bootstrap
|
||||
|
||||
// Register modules
|
||||
const modules = require.context("./modules", true, /\.js$/i);
|
||||
modules.keys().map(key => Vue.mixin(modules(key).default));
|
||||
|
||||
// Register components
|
||||
const vues = require.context("./components", true, /\.vue$/i);
|
||||
vues.keys().map(key =>
|
||||
Vue.component(
|
||||
key
|
||||
.split("/")
|
||||
.pop()
|
||||
.split(".")[0]
|
||||
.replace(/\.?([A-Z])/g, function (x, y) {
|
||||
return "-" + y.toLowerCase();
|
||||
})
|
||||
.replace(/^-/, ""),
|
||||
vues(key).default
|
||||
)
|
||||
);
|
||||
|
||||
// Register vue function
|
||||
Vue.filter('str_limit', function (value, size) {
|
||||
if (!value) return '';
|
||||
value = value.toString();
|
||||
if (value.length <= size) {
|
||||
return value;
|
||||
}
|
||||
return value.substr(0, size) + '...';
|
||||
});
|
||||
Vue.filter('ucfirst', function (value) {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1)
|
||||
});
|
||||
|
||||
for (let el of document.getElementsByClassName('vueComponent')) {
|
||||
new Vue({ el: el });
|
||||
}
|
||||
|
||||
require('./bootstrap')
|
||||
require('./session_watch')
|
||||
require('./vue')
|
||||
require('./files_add')
|
||||
|
|
|
|||
40
resources/js/bootstrap.js
vendored
40
resources/js/bootstrap.js
vendored
|
|
@ -1,10 +1,16 @@
|
|||
window._ = require('lodash');
|
||||
// SweetAlert
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
/**
|
||||
* We'll load jQuery and the Bootstrap jQuery plugin which provides support
|
||||
* for JavaScript based Bootstrap features such as modals and tabs. This
|
||||
* code may be modified to fit the specific needs of your application.
|
||||
*/
|
||||
// bootstrap styling
|
||||
window.Swal = Swal.mixin({
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-lg btn-success ms-3 me-3',
|
||||
cancelButton: 'btn btn-lg btn-danger ms-3 me-3'
|
||||
},
|
||||
buttonsStyling: false
|
||||
})
|
||||
|
||||
window._ = require('lodash')
|
||||
|
||||
/**
|
||||
* We'll load the axios HTTP library which allows us to easily issue requests
|
||||
|
|
@ -12,26 +18,10 @@ window._ = require('lodash');
|
|||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||
*/
|
||||
|
||||
window.axios = require('axios');
|
||||
window.axios = require('axios')
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||
* allows your team to easily build robust real-time web applications.
|
||||
*/
|
||||
|
||||
// import Echo from 'laravel-echo';
|
||||
|
||||
// window.Pusher = require('pusher-js');
|
||||
|
||||
// window.Echo = new Echo({
|
||||
// broadcaster: 'pusher',
|
||||
// key: process.env.MIX_PUSHER_APP_KEY,
|
||||
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
|
||||
// forceTLS: true
|
||||
// });
|
||||
|
||||
require('fslightbox');
|
||||
require('fslightbox')
|
||||
|
|
|
|||
|
|
@ -193,42 +193,21 @@ import Toastify from 'toastify-js'
|
|||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
isAdmin: {
|
||||
type: Number,
|
||||
},
|
||||
dbLoadCollection: {
|
||||
type: String,
|
||||
},
|
||||
dbMacroscheduleConfig: {
|
||||
type: String,
|
||||
},
|
||||
dbProjects: {
|
||||
type: String,
|
||||
},
|
||||
dbActivitiesEnum: {
|
||||
type: String,
|
||||
},
|
||||
dbProjectStatusEnum: {
|
||||
type: String,
|
||||
},
|
||||
today: {
|
||||
type: String,
|
||||
},
|
||||
startDay: {
|
||||
type: String,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let self = this
|
||||
window.Toastify = Toastify
|
||||
this.projects = JSON.parse(this.dbProjects)
|
||||
this.loadCollection = JSON.parse(this.dbLoadCollection)
|
||||
this.macroscheduleConfig = JSON.parse(this.dbMacroscheduleConfig)
|
||||
this.activitiesEnum = JSON.parse(this.dbActivitiesEnum)
|
||||
this.projectStatusEnum = JSON.parse(this.dbProjectStatusEnum)
|
||||
|
||||
let data = JSON.parse(this.$parent.$options.json)
|
||||
this.projects = data.projects
|
||||
this.loadCollection = data.loadCollection
|
||||
this.macroscheduleConfig = data.macroscheduleConfig
|
||||
this.activitiesEnum = data.activitiesEnum
|
||||
this.projectStatusEnum = data.projectStatusEnum
|
||||
this.today = data.today
|
||||
this.startDay = data.startDay
|
||||
this.isAdmin = data.isAdmin
|
||||
this.$nextTick(this.refreshTooltips)
|
||||
this.modale = new bootstrap.Modal(this.$refs.editModal)
|
||||
this.modale = new this.bootstrap.Modal(this.$refs.editModal)
|
||||
this.$refs.editModal.addEventListener('hide.bs.modal', function (e) {
|
||||
if(self.modalLoading) {
|
||||
e.preventDefault()
|
||||
|
|
@ -238,6 +217,9 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
isAdmin: false,
|
||||
today: '',
|
||||
startDay: '',
|
||||
textLimit: 254,
|
||||
hoverActivityId: null,
|
||||
error: '',
|
||||
|
|
@ -544,7 +526,7 @@ export default {
|
|||
}
|
||||
this.tooltipTriggerList = [].slice.call(this.$refs.macroschedule.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
this.tooltipTriggerList.map((tooltipTriggerEl) => {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl, { delay: 500 })
|
||||
return new this.bootstrap.Tooltip(tooltipTriggerEl, { delay: 500 })
|
||||
})
|
||||
},
|
||||
limitLines(e) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
this.labels = JSON.parse(this.dbLabels)
|
||||
this.inputValue = this.dbValue
|
||||
let data = JSON.parse(this.$parent.$options.json)
|
||||
this.name = data.name
|
||||
this.labels = data.labels
|
||||
this.inputValue = data.value
|
||||
this.value = {
|
||||
code: this.labels.length,
|
||||
label: this.inputValue
|
||||
|
|
@ -21,11 +23,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'name', 'dbValue', 'dbLabels'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
value: {},
|
||||
inputValue: '',
|
||||
labels: []
|
||||
|
|
|
|||
|
|
@ -147,50 +147,25 @@
|
|||
<script>
|
||||
import Toastify from 'toastify-js'
|
||||
export default {
|
||||
props: {
|
||||
dbSchedules: {
|
||||
type: String,
|
||||
},
|
||||
dbActivities: {
|
||||
type: String,
|
||||
},
|
||||
dbModels: {
|
||||
type: String,
|
||||
},
|
||||
dbUsers: {
|
||||
type: String,
|
||||
},
|
||||
weeks: {
|
||||
type: String,
|
||||
},
|
||||
daySlots: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
browseDate: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return new Date();
|
||||
}
|
||||
},
|
||||
showWeekends: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.Toastify = Toastify
|
||||
this.currentDate = JSON.parse(this.browseDate);
|
||||
this.weeksCells = JSON.parse(this.weeks);
|
||||
this.numweeks = this.weeksCells.length;
|
||||
|
||||
let data = JSON.parse(this.$parent.$options.json)
|
||||
this.daySlots = Number.parseInt(data.daySlots) !== NaN ? Number.parseInt(data.daySlots) : 4
|
||||
this.showWeekends = data.daySlots == 'true' ? true : false
|
||||
this.currentDate = data.browseDate ?? new Date()
|
||||
this.weeksCells = data.weeks
|
||||
this.numweeks = this.weeksCells.length
|
||||
this.days = this.prepareDays();
|
||||
this.users = JSON.parse(this.dbUsers);
|
||||
this.schedules = this.prepareSched(JSON.parse(this.dbSchedules));
|
||||
this.activities = JSON.parse(this.dbActivities);
|
||||
this.models = JSON.parse(this.dbModels);
|
||||
this.users = data.users
|
||||
this.schedules = this.prepareSched(data.schedules)
|
||||
this.activities = data.activities
|
||||
this.models = data.models
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showWeekends: false,
|
||||
daySlots: 4,
|
||||
currentDate: null,
|
||||
weeksCells: [],
|
||||
days: [],
|
||||
|
|
|
|||
31
resources/js/files_add.js
vendored
Normal file
31
resources/js/files_add.js
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Page Project File Add
|
||||
if (document.querySelector('.content.pageFile')) {
|
||||
let selectFileType = document.getElementById('type'),
|
||||
inputUri = document.getElementById('uri'),
|
||||
inputFile = document.getElementById('file')
|
||||
|
||||
|
||||
let changeFields = () => {
|
||||
if (selectFileType.value == 0) {
|
||||
inputUri.attributes['required'] = true
|
||||
inputUri.attributes['disabled'] = false
|
||||
inputUri.parentElement.parentElement.classList.remove('d-none')
|
||||
inputFile.attributes['required'] = false
|
||||
inputFile.attributes['disabled'] = true
|
||||
inputFile.parentElement.parentElement.classList.add('d-none')
|
||||
} else {
|
||||
inputUri.attributes['required'] = false
|
||||
inputUri.attributes['disabled'] = true
|
||||
inputUri.parentElement.parentElement.classList.add('d-none')
|
||||
inputFile.attributes['required'] = true
|
||||
inputFile.attributes['disabled'] = false
|
||||
inputFile.parentElement.parentElement.classList.remove('d-none')
|
||||
}
|
||||
}
|
||||
|
||||
changeFields()
|
||||
selectFileType.addEventListener('change', changeFields)
|
||||
}
|
||||
})
|
||||
85
resources/js/vue.js
vendored
Normal file
85
resources/js/vue.js
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import Vue from 'vue'
|
||||
import vSelect from 'vue-select'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/fr'
|
||||
|
||||
let bootstrap = require('bootstrap')
|
||||
|
||||
// enable bs tooltips
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
})
|
||||
|
||||
moment.locale('fr')
|
||||
|
||||
window.Vue = Vue
|
||||
|
||||
if (window.vueDebug) {
|
||||
Vue.config.devtools = true;
|
||||
Vue.config.debug = true;
|
||||
}
|
||||
|
||||
Vue.prototype.moment = moment
|
||||
Vue.prototype.bootstrap = bootstrap
|
||||
|
||||
// v-select
|
||||
Vue.component('v-select', vSelect)
|
||||
|
||||
// Register modules
|
||||
const modules = require.context("./modules", true, /\.js$/i);
|
||||
modules.keys().map(key => {
|
||||
Vue.mixin(modules(key).default)
|
||||
})
|
||||
|
||||
window.confirmJS = modules('./confirm.js').default.methods.confirm
|
||||
|
||||
// Register vue function
|
||||
Vue.filter('str_limit', function (value, size) {
|
||||
if (!value) return ''
|
||||
value = value.toString()
|
||||
if (value.length <= size) {
|
||||
return value;
|
||||
}
|
||||
return value.substr(0, size) + '...';
|
||||
})
|
||||
Vue.filter('ucfirst', function (value) {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1)
|
||||
})
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Register components
|
||||
const vues = require.context("./components", true, /\.vue$/i)
|
||||
vues.keys().map(key => {
|
||||
let htmlKey = key.split('/')
|
||||
.pop()
|
||||
.split('.')[0]
|
||||
.replace(/\.?([A-Z])/g, function (x, y) {
|
||||
return '-' + y.toLowerCase();
|
||||
})
|
||||
.replace(/^-/, '')
|
||||
.replace('-component', '')
|
||||
|
||||
let el = document.getElementById(htmlKey),
|
||||
elGroup = document.getElementsByClassName(htmlKey)
|
||||
if (el) {
|
||||
new Vue({
|
||||
el: el,
|
||||
json: el.dataset.json ?? {},
|
||||
// * For runtime vuejs
|
||||
render: createElement => createElement(vues(key).default)
|
||||
})
|
||||
}
|
||||
for (let grpEl of elGroup) {
|
||||
// * Hack to prevent loop weirdness
|
||||
setTimeout(() => {
|
||||
new Vue({
|
||||
el: grpEl,
|
||||
json: grpEl.dataset.json,
|
||||
// * For runtime vuejs
|
||||
render: createElement => createElement(vues(key).default)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
})
|
||||
17
resources/sass/_bootswatch.scss
vendored
17
resources/sass/_bootswatch.scss
vendored
|
|
@ -1,4 +1,4 @@
|
|||
// Lumen 5.0.2
|
||||
// Lumen 5.1.3
|
||||
// Bootswatch
|
||||
|
||||
// Variables
|
||||
|
|
@ -97,15 +97,6 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,w
|
|||
box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.input-group-sm {
|
||||
> .input-group-prepend,
|
||||
> .input-group-append {
|
||||
.btn {
|
||||
font-size: $btn-font-size-sm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Navs
|
||||
|
||||
.nav {
|
||||
|
|
@ -151,10 +142,6 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,w
|
|||
@include box-shadow(none);
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
border-color: shade-color($breadcrumb-bg, 10%);
|
||||
@include shadow();
|
||||
|
|
@ -334,4 +321,4 @@ a.list-group-item {
|
|||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
418
resources/sass/_ckEditorContent.scss
vendored
Normal file
418
resources/sass/_ckEditorContent.scss
vendored
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
// CKeditor fix in alert
|
||||
.ck-editor__main {
|
||||
color: initial;
|
||||
}
|
||||
|
||||
// CK Content
|
||||
/*
|
||||
* CKEditor 5 (v32.0.0) content styles.
|
||||
* Generated on Thu, 17 Feb 2022 10:21:25 GMT.
|
||||
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
|
||||
*/
|
||||
|
||||
:root {
|
||||
--ck-color-image-caption-background: hsl(0, 0%, 97%);
|
||||
--ck-color-image-caption-text: hsl(0, 0%, 20%);
|
||||
--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);
|
||||
--ck-color-mention-text: hsl(341, 100%, 30%);
|
||||
--ck-color-table-caption-background: hsl(0, 0%, 97%);
|
||||
--ck-color-table-caption-text: hsl(0, 0%, 20%);
|
||||
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
|
||||
--ck-highlight-marker-green: hsl(120, 93%, 68%);
|
||||
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
|
||||
--ck-highlight-marker-yellow: hsl(60, 97%, 73%);
|
||||
--ck-highlight-pen-green: hsl(112, 100%, 27%);
|
||||
--ck-highlight-pen-red: hsl(0, 85%, 49%);
|
||||
--ck-image-style-spacing: 1.5em;
|
||||
--ck-inline-image-style-spacing: calc(var(--ck-image-style-spacing) / 2);
|
||||
--ck-todo-list-checkmark-size: 16px;
|
||||
}
|
||||
|
||||
/* ckeditor5-block-quote/theme/blockquote.css */
|
||||
.ck-content blockquote {
|
||||
overflow: hidden;
|
||||
padding-right: 1.5em;
|
||||
padding-left: 1.5em;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
font-style: italic;
|
||||
border-left: solid 5px hsl(0, 0%, 80%);
|
||||
}
|
||||
/* ckeditor5-block-quote/theme/blockquote.css */
|
||||
.ck-content[dir="rtl"] blockquote {
|
||||
border-left: 0;
|
||||
border-right: solid 5px hsl(0, 0%, 80%);
|
||||
}
|
||||
/* ckeditor5-highlight/theme/highlight.css */
|
||||
.ck-content .marker-yellow {
|
||||
background-color: var(--ck-highlight-marker-yellow);
|
||||
}
|
||||
/* ckeditor5-highlight/theme/highlight.css */
|
||||
.ck-content .marker-green {
|
||||
background-color: var(--ck-highlight-marker-green);
|
||||
}
|
||||
/* ckeditor5-highlight/theme/highlight.css */
|
||||
.ck-content .marker-pink {
|
||||
background-color: var(--ck-highlight-marker-pink);
|
||||
}
|
||||
/* ckeditor5-highlight/theme/highlight.css */
|
||||
.ck-content .marker-blue {
|
||||
background-color: var(--ck-highlight-marker-blue);
|
||||
}
|
||||
/* ckeditor5-highlight/theme/highlight.css */
|
||||
.ck-content .pen-red {
|
||||
color: var(--ck-highlight-pen-red);
|
||||
background-color: transparent;
|
||||
}
|
||||
/* ckeditor5-highlight/theme/highlight.css */
|
||||
.ck-content .pen-green {
|
||||
color: var(--ck-highlight-pen-green);
|
||||
background-color: transparent;
|
||||
}
|
||||
/* ckeditor5-font/theme/fontsize.css */
|
||||
.ck-content .text-tiny {
|
||||
font-size: .7em;
|
||||
}
|
||||
/* ckeditor5-font/theme/fontsize.css */
|
||||
.ck-content .text-small {
|
||||
font-size: .85em;
|
||||
}
|
||||
/* ckeditor5-font/theme/fontsize.css */
|
||||
.ck-content .text-big {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
/* ckeditor5-font/theme/fontsize.css */
|
||||
.ck-content .text-huge {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
/* ckeditor5-basic-styles/theme/code.css */
|
||||
.ck-content code {
|
||||
background-color: hsla(0, 0%, 78%, 0.3);
|
||||
padding: .15em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
/* ckeditor5-image/theme/image.css */
|
||||
.ck-content .image {
|
||||
display: table;
|
||||
clear: both;
|
||||
text-align: center;
|
||||
margin: 0.9em auto;
|
||||
min-width: 50px;
|
||||
}
|
||||
/* ckeditor5-image/theme/image.css */
|
||||
.ck-content .image img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
/* ckeditor5-image/theme/image.css */
|
||||
.ck-content .image-inline {
|
||||
/*
|
||||
* Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).;
|
||||
* Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root.
|
||||
* This strange behavior does not happen with inline-flex.
|
||||
*/
|
||||
display: inline-flex;
|
||||
max-width: 100%;
|
||||
align-items: flex-start;
|
||||
}
|
||||
/* ckeditor5-image/theme/image.css */
|
||||
.ck-content .image-inline picture {
|
||||
display: flex;
|
||||
}
|
||||
/* ckeditor5-image/theme/image.css */
|
||||
.ck-content .image-inline picture,
|
||||
.ck-content .image-inline img {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
max-width: 100%;
|
||||
}
|
||||
/* ckeditor5-image/theme/imageresize.css */
|
||||
.ck-content .image.image_resized {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* ckeditor5-image/theme/imageresize.css */
|
||||
.ck-content .image.image_resized img {
|
||||
width: 100%;
|
||||
}
|
||||
/* ckeditor5-image/theme/imageresize.css */
|
||||
.ck-content .image.image_resized > figcaption {
|
||||
display: block;
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-block-align-left,
|
||||
.ck-content .image-style-block-align-right {
|
||||
max-width: calc(100% - var(--ck-image-style-spacing));
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-align-left,
|
||||
.ck-content .image-style-align-right {
|
||||
clear: none;
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-side {
|
||||
float: right;
|
||||
margin-left: var(--ck-image-style-spacing);
|
||||
max-width: 50%;
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-align-left {
|
||||
float: left;
|
||||
margin-right: var(--ck-image-style-spacing);
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-align-right {
|
||||
float: right;
|
||||
margin-left: var(--ck-image-style-spacing);
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-block-align-right {
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-style-block-align-left {
|
||||
margin-left: 0;
|
||||
margin-right: auto;
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content p + .image-style-align-left,
|
||||
.ck-content p + .image-style-align-right,
|
||||
.ck-content p + .image-style-side {
|
||||
margin-top: 0;
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-inline.image-style-align-left,
|
||||
.ck-content .image-inline.image-style-align-right {
|
||||
margin-top: var(--ck-inline-image-style-spacing);
|
||||
margin-bottom: var(--ck-inline-image-style-spacing);
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-inline.image-style-align-left {
|
||||
margin-right: var(--ck-inline-image-style-spacing);
|
||||
}
|
||||
/* ckeditor5-image/theme/imagestyle.css */
|
||||
.ck-content .image-inline.image-style-align-right {
|
||||
margin-left: var(--ck-inline-image-style-spacing);
|
||||
}
|
||||
/* ckeditor5-image/theme/imagecaption.css */
|
||||
.ck-content .image > figcaption {
|
||||
display: table-caption;
|
||||
caption-side: bottom;
|
||||
word-break: break-word;
|
||||
color: var(--ck-color-image-caption-text);
|
||||
background-color: var(--ck-color-image-caption-background);
|
||||
padding: .6em;
|
||||
font-size: .75em;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
/* ckeditor5-language/theme/language.css */
|
||||
.ck-content span[lang] {
|
||||
font-style: italic;
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list {
|
||||
list-style: none;
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list li .todo-list {
|
||||
margin-top: 5px;
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list .todo-list__label > input {
|
||||
-webkit-appearance: none;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: var(--ck-todo-list-checkmark-size);
|
||||
height: var(--ck-todo-list-checkmark-size);
|
||||
vertical-align: middle;
|
||||
border: 0;
|
||||
left: -25px;
|
||||
margin-right: -15px;
|
||||
right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list .todo-list__label > input::before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid hsl(0, 0%, 20%);
|
||||
border-radius: 2px;
|
||||
transition: 250ms ease-in-out box-shadow, 250ms ease-in-out background, 250ms ease-in-out border;
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list .todo-list__label > input::after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
box-sizing: content-box;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
left: calc( var(--ck-todo-list-checkmark-size) / 3 );
|
||||
top: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
|
||||
width: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
|
||||
height: calc( var(--ck-todo-list-checkmark-size) / 2.6 );
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-width: 0 calc( var(--ck-todo-list-checkmark-size) / 8 ) calc( var(--ck-todo-list-checkmark-size) / 8 ) 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list .todo-list__label > input[checked]::before {
|
||||
background: hsl(126, 64%, 41%);
|
||||
border-color: hsl(126, 64%, 41%);
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list .todo-list__label > input[checked]::after {
|
||||
border-color: hsl(0, 0%, 100%);
|
||||
}
|
||||
/* ckeditor5-list/theme/todolist.css */
|
||||
.ck-content .todo-list .todo-list__label .todo-list__label__description {
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||
.ck-content .page-break {
|
||||
position: relative;
|
||||
clear: both;
|
||||
padding: 5px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||
.ck-content .page-break::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-bottom: 2px dashed hsl(0, 0%, 77%);
|
||||
width: 100%;
|
||||
}
|
||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||
.ck-content .page-break__label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: .3em .6em;
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid hsl(0, 0%, 77%);
|
||||
border-radius: 2px;
|
||||
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
|
||||
font-size: 0.75em;
|
||||
font-weight: bold;
|
||||
color: hsl(0, 0%, 20%);
|
||||
background: hsl(0, 0%, 100%);
|
||||
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
/* ckeditor5-table/theme/tablecaption.css */
|
||||
.ck-content .table > figcaption {
|
||||
display: table-caption;
|
||||
caption-side: top;
|
||||
word-break: break-word;
|
||||
text-align: center;
|
||||
color: var(--ck-color-table-caption-text);
|
||||
background-color: var(--ck-color-table-caption-background);
|
||||
padding: .6em;
|
||||
font-size: .75em;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
/* ckeditor5-media-embed/theme/mediaembed.css */
|
||||
.ck-content .media {
|
||||
clear: both;
|
||||
margin: 0.9em 0;
|
||||
display: block;
|
||||
min-width: 15em;
|
||||
}
|
||||
/* ckeditor5-table/theme/table.css */
|
||||
.ck-content .table {
|
||||
margin: 0.9em auto;
|
||||
display: table;
|
||||
}
|
||||
/* ckeditor5-table/theme/table.css */
|
||||
.ck-content .table table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px double hsl(0, 0%, 70%);
|
||||
}
|
||||
/* ckeditor5-table/theme/table.css */
|
||||
.ck-content .table table td,
|
||||
.ck-content .table table th {
|
||||
min-width: 2em;
|
||||
padding: .4em;
|
||||
border: 1px solid hsl(0, 0%, 75%);
|
||||
}
|
||||
/* ckeditor5-table/theme/table.css */
|
||||
.ck-content .table table th {
|
||||
font-weight: bold;
|
||||
background: hsla(0, 0%, 0%, 5%);
|
||||
}
|
||||
/* ckeditor5-table/theme/table.css */
|
||||
.ck-content[dir="rtl"] .table th {
|
||||
text-align: right;
|
||||
}
|
||||
/* ckeditor5-table/theme/table.css */
|
||||
.ck-content[dir="ltr"] .table th {
|
||||
text-align: left;
|
||||
}
|
||||
/* ckeditor5-horizontal-line/theme/horizontalline.css */
|
||||
.ck-content hr {
|
||||
margin: 15px 0;
|
||||
height: 4px;
|
||||
background: hsl(0, 0%, 87%);
|
||||
border: 0;
|
||||
}
|
||||
/* ckeditor5-code-block/theme/codeblock.css */
|
||||
.ck-content pre {
|
||||
padding: 1em;
|
||||
color: hsl(0, 0%, 20.8%);
|
||||
background: hsla(0, 0%, 78%, 0.3);
|
||||
border: 1px solid hsl(0, 0%, 77%);
|
||||
border-radius: 2px;
|
||||
text-align: left;
|
||||
direction: ltr;
|
||||
tab-size: 4;
|
||||
white-space: pre-wrap;
|
||||
font-style: normal;
|
||||
min-width: 200px;
|
||||
}
|
||||
/* ckeditor5-code-block/theme/codeblock.css */
|
||||
.ck-content pre code {
|
||||
background: unset;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
/* ckeditor5-mention/theme/mention.css */
|
||||
.ck-content .mention {
|
||||
background: var(--ck-color-mention-background);
|
||||
color: var(--ck-color-mention-text);
|
||||
}
|
||||
@media print {
|
||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||
.ck-content .page-break {
|
||||
padding: 0;
|
||||
}
|
||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||
.ck-content .page-break::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
3
resources/sass/_variables.scss
vendored
3
resources/sass/_variables.scss
vendored
|
|
@ -1,11 +1,10 @@
|
|||
@use "sass:color";
|
||||
|
||||
// Lumen 5.0.2
|
||||
// Lumen 5.1.3
|
||||
// Bootswatch
|
||||
|
||||
$theme: "lumen" !default;
|
||||
|
||||
|
||||
//
|
||||
// Color system
|
||||
//
|
||||
|
|
|
|||
6
resources/sass/app.scss
vendored
6
resources/sass/app.scss
vendored
|
|
@ -1,11 +1,7 @@
|
|||
@use "sass:color";
|
||||
|
||||
// Fonts
|
||||
@import url('https://fonts.googleapis.com/css?family=Nunito');
|
||||
|
||||
// Variables
|
||||
@import 'variables';
|
||||
@import 'complement';
|
||||
@import 'ckEditorContent';
|
||||
|
||||
a.col_sort {
|
||||
&:link,:visited,:hover,:active {
|
||||
|
|
|
|||
6
resources/sass/bootstrap.scss
vendored
6
resources/sass/bootstrap.scss
vendored
|
|
@ -1,3 +1,3 @@
|
|||
@import "variables";
|
||||
@import "~bootstrap/scss/bootstrap";
|
||||
@import "bootswatch";
|
||||
@import 'variables';
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
@import 'bootswatch';
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="flex-center position-ref full-height">
|
||||
<img src="{{ asset('http://supportweb.local/images/supportweb_128.png') }}" alt="{{ config('app.name') }}">
|
||||
<img src="{{ asset('assets/images/supportweb_128.png') }}" alt="{{ config('app.name') }}">
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
@yield('message')
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -4,6 +4,8 @@
|
|||
@section('description', sprintf('%s %s', __('crud-policies::crud.create'), class_basename($modelClass)))
|
||||
@section('metaIndex', 'noindex,nofollow')
|
||||
|
||||
@section('bodyClass', 'content pageFile')
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
@include('crud-policies::crud.modules.flashMessage')
|
||||
|
|
@ -43,35 +45,3 @@
|
|||
</div>
|
||||
@include('crud-policies::crud.modules.scriptConstants')
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
let changeFields = () => {
|
||||
let $iUri = $('#uri');
|
||||
let $iFile = $('#file');
|
||||
let $iUriLine = $iUri.closest('tr');
|
||||
let $iFileLine = $iFile.closest('tr');
|
||||
let val = $('#type').val();
|
||||
console.log(val)
|
||||
if(val == 0) {
|
||||
$iUri.prop('required', true);
|
||||
$iUri.prop('disabled', false);
|
||||
$iUriLine.removeClass('d-none');
|
||||
$iFile.prop('required', false);
|
||||
$iFile.prop('disabled', true);
|
||||
$iFileLine.addClass('d-none');
|
||||
} else {
|
||||
$iUri.prop('required', false);
|
||||
$iUri.prop('disabled', true);
|
||||
$iUriLine.addClass('d-none');
|
||||
$iFile.prop('required', true);
|
||||
$iFile.prop('disabled', false);
|
||||
$iFileLine.removeClass('d-none');
|
||||
}
|
||||
};
|
||||
changeFields();
|
||||
$('#type').change(changeFields);
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
|
|
@ -17,12 +17,9 @@
|
|||
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.16/dist/sweetalert2.all.min.js" integrity="sha256-551HBsteMvKOSqjUXSmRy/EOF0bBNbWB96b5L3Deh8A=" crossorigin="anonymous"></script>
|
||||
|
||||
<!-- Styles -->
|
||||
<link href="{{ asset('css/bootstrap.css') }}" rel="stylesheet">
|
||||
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.16/dist/sweetalert2.min.css" integrity="sha256-qpXwccFYncH5gTqXFe9EXZ8QeDw1Re68bbNVfFZzIzg=" crossorigin="anonymous">
|
||||
@crudPoliciesCSS
|
||||
@stack('customCss')
|
||||
</head>
|
||||
|
|
@ -42,15 +39,13 @@
|
|||
</div>
|
||||
</div>
|
||||
@include('layout.footer')
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://bootswatch.com/_vendor/bootstrap/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
@stack('scriptsConstants')
|
||||
<script src="{{ asset('js/app.js') }}"></script>
|
||||
@stack('scripts')
|
||||
@crudPoliciesJS()
|
||||
@auth
|
||||
{{-- JS SESSION ALERT IF SESSION TIMEOUT --}}
|
||||
<script>var __SESSION_WATCH = true;</script>
|
||||
<script nonce="{{ $nonce }}">var __SESSION_WATCH = true;</script>
|
||||
@endauth
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<footer id="footer" class="text-center text-lg-start">
|
||||
<div class="text-center p-2" style="background-color: rgba(0, 0, 0, 0.05);">
|
||||
<img src="{{ asset('images/supportweb_128.png') }}" alt="{{ config('app.name', 'Laravel') }}" width="56">
|
||||
<img src="{{ asset('assets/images/supportweb_128.png') }}" alt="{{ config('app.name', 'Laravel') }}" width="56">
|
||||
<span class="text-reset">{{ $license }}</span>
|
||||
-
|
||||
<span class="text-reset">{{ $name }} - {{ $version }}</span>
|
||||
|
|
|
|||
|
|
@ -4,21 +4,26 @@
|
|||
|
||||
@section('content')
|
||||
<div class="container container-xxl vueComponent">
|
||||
<macro-planning-component
|
||||
:is-admin='{{ Auth::user()->isAdmin() ? '1' : '0' }}'
|
||||
today="{{ now() }}"
|
||||
start-day="{{ $date }}"
|
||||
db-load-collection='@json($loadCollection)'
|
||||
db-macroschedule-config='@json(config('macroschedule'))'
|
||||
db-load-collection='@json($loadCollection)'
|
||||
db-activities-enum='@json(ActivityEnum::toArray())'
|
||||
db-project-status-enum='@json(ProjectStatus::toArray())'
|
||||
db-projects='@json($projects)'/>
|
||||
@php
|
||||
$dataVue = [
|
||||
'isAdmin' => Auth::user()->isAdmin() ? '1' : '0',
|
||||
'today' => now(),
|
||||
'startDay' => $date,
|
||||
'loadCollection' => $loadCollection,
|
||||
'projects' => $projects,
|
||||
'projectStatusEnum' => ProjectStatus::toArray(),
|
||||
'activitiesEnum' => ActivityEnum::toArray(),
|
||||
'macroscheduleConfig' => config('macroschedule'),
|
||||
];
|
||||
@endphp
|
||||
<div
|
||||
id="macro-planning"
|
||||
data-json='@json($dataVue)'></div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scriptsConstants')
|
||||
<script>
|
||||
<script nonce="{{ $nonce }}">
|
||||
window._routes = {
|
||||
"macroplanning.activity": "{{ route('macroplanning.activity') }}",
|
||||
"projects.show": "{{ route('projects.show', ['project' => 'PROJECT']) }}",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
<td>
|
||||
<label class="form-label">{{ $prop['label'] }}</label>
|
||||
</td>
|
||||
<td class="vueComponent">
|
||||
<marker-label-select-component
|
||||
name="{{ $fieldName }}"
|
||||
db-value="{{ old($fieldName, $model->{$fieldName} ?? $prop['default'] ?? '') }}"
|
||||
db-labels='@json($markerLabels->mapWithKeys(function($value, $key) { return [$key => ['code' => $key, 'label' => $value]]; }))'/>
|
||||
<td>
|
||||
@php
|
||||
$dataVue = [
|
||||
'name' => 'label',
|
||||
'value' => old($fieldName, $model->{$fieldName} ?? $prop['default'] ?? ''),
|
||||
'labels' => $markerLabels->mapWithKeys(function($value, $key) { return [$key => ['code' => $key, 'label' => $value]]; })
|
||||
];
|
||||
@endphp
|
||||
<div
|
||||
id="marker-label-select"
|
||||
class="sticky-top"
|
||||
data-json='@json($dataVue)'></div>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
<span class="nav-link">{{ __('Charge') }} :
|
||||
@switch(Utils::computeWorkload())
|
||||
@switch(ToolboxHelper::computeWorkload())
|
||||
@case(LoadLevel::weak())
|
||||
<span class="p-1 rounded bg-dark fw-bold text-light">{{ Utils::computeWorkload()->label }}</span>
|
||||
<span class="p-1 rounded bg-dark fw-bold text-light">{{ ToolboxHelper::computeWorkload()->label }}</span>
|
||||
@break
|
||||
@case(LoadLevel::stable())
|
||||
<span class="p-1 rounded bg-success fw-bold text-dark">{{ Utils::computeWorkload()->label }}</span>
|
||||
<span class="p-1 rounded bg-success fw-bold text-dark">{{ ToolboxHelper::computeWorkload()->label }}</span>
|
||||
@break
|
||||
@case(LoadLevel::high())
|
||||
<span class="p-1 rounded bg-warning fw-bold text-dark">{{ Utils::computeWorkload()->label }}</span>
|
||||
<span class="p-1 rounded bg-warning fw-bold text-dark">{{ ToolboxHelper::computeWorkload()->label }}</span>
|
||||
@break
|
||||
@case(LoadLevel::overload())
|
||||
<span class="p-1 rounded bg-danger fw-bold text-dark">{{ Utils::computeWorkload()->label }}</span>
|
||||
<span class="p-1 rounded bg-danger fw-bold text-dark">{{ ToolboxHelper::computeWorkload()->label }}</span>
|
||||
@break
|
||||
@endswitch
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -17,20 +17,26 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<schedule-component
|
||||
browse-date='@json($date)'
|
||||
db-schedules='@json($arSchedules)'
|
||||
db-activities='@json(ActivityType::toArray())'
|
||||
db-models='@json(CellModel::toArray())'
|
||||
db-users='@json($users)'
|
||||
weeks='@json($weekCells)'
|
||||
v-bind:day-slots='{{ Config::get('schedules.daySlots') }}'
|
||||
v-bind:show-weekends='false'></schedule-component>
|
||||
@php
|
||||
$dataVue = [
|
||||
'browseDate' => $date,
|
||||
'schedules' => $arSchedules,
|
||||
'activities' => ActivityType::toArray(),
|
||||
'models' => CellModel::toArray(),
|
||||
'users' => $users,
|
||||
'weeks' => $weekCells,
|
||||
'daySlots' => Config::get('schedules.daySlots'),
|
||||
'showWeekends' => 'false',
|
||||
];
|
||||
@endphp
|
||||
<div
|
||||
id="schedule"
|
||||
data-json='@json($dataVue)'></div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scriptsConstants')
|
||||
<script>
|
||||
<script nonce="{{ $nonce }}">
|
||||
window._routes = {
|
||||
"schedule.index": "{{ route('schedule.index') }}",
|
||||
"ticket.show": "{{ route('ticket.show', 'TICKET') }}",
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@
|
|||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="card-header">Créer un ticket<a href="{{ route('home') }}" class="float-end btn btn-sm btn-primary">Retour</a></div>
|
||||
<div class="card-header">{{ __('Créer un ticket') }}<a href="{{ route('home') }}" class="float-end btn btn-sm btn-primary">{{ __('Retour') }}</a></div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('ticket.store') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
@include('ticket.form-inputs')
|
||||
|
||||
<div class="form-group text-center">
|
||||
<input type="submit" class="btn btn-primary" value="Enregistrer">
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,17 +4,12 @@
|
|||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="card-header">Modifier un ticket<a href="{{ route('home') }}" class="float-end btn btn-sm btn-primary">Retour</a></div>
|
||||
<div class="card-header">{{ __('Modifier un ticket') }}<a href="{{ route('home') }}" class="float-end btn btn-sm btn-primary">{{ __('Retour') }}</a></div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('ticket.update', $ticket->id) }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@if (session('success'))
|
||||
<div class="alert alert-success">{!! session('success') !!}</div>
|
||||
@endif
|
||||
|
||||
@include('ticket.form-inputs')
|
||||
|
||||
<div class="form-group text-center">
|
||||
<input type="submit" class="btn btn-primary" value="Modifier">
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ $userPr = Auth::user()->sites ?? collect([]);
|
|||
@endphp
|
||||
<div class="form-row">
|
||||
<div class="form-group col">
|
||||
<label for="site" class="col-form-label">{{ __('Projet') }}</label>
|
||||
<label for="site" class="col-form-label">{{ __('Site') }}</label>
|
||||
<select class="form-select" name="site_id" id="site">
|
||||
@foreach($sites as $site)
|
||||
@if (Auth::user()->isAdmin())
|
||||
|
|
@ -62,7 +62,7 @@ $userPr = Auth::user()->sites ?? collect([]);
|
|||
</div>
|
||||
@can('changeInternalStatus', App\Model\Ticket::class)
|
||||
<div class="form-group text-center">
|
||||
<input type="checkbox" name="is_internal" id="is_internal" @if ((old('is_internal') && old('is_internal')===1) || (isset($ticket) && $ticket->is_internal === 1)) checked @endif>
|
||||
<input type="checkbox" name="is_internal" id="is_internal" @if(old('is_internal', isset($ticket) and $ticket->is_internal)) checked @endif>
|
||||
<label for="is_internal" class="col-form-label">Ticket interne</label>
|
||||
</div>
|
||||
@endcan
|
||||
@endcan
|
||||
|
|
|
|||
|
|
@ -42,11 +42,21 @@ $userPr = Auth::user()->sites ?? collect([]);
|
|||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="float-end mt-3 me-2">
|
||||
<select class="form-select" onchange="window.location='{{ route('ticket.index') }}?'+(this.value == -1 ? 'assr=-1' : 'ass='+this.value);">
|
||||
<option value="-1">{{ __('Tous') }}</option>
|
||||
@foreach(\App\Models\User::adminUsers() as $user)
|
||||
<option value="{{ $user->id }}" @if(intval(session('tickets.ass'))==$user->id)selected="selected" @endif>{{ $user->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<form id="searchTicketForm" class="float-end mt-3 w-75" action="{{ request()->fullUrlWithQuery(['search' => '']) }}">
|
||||
<div class="input-group mb-3">
|
||||
<input id="searchTicket" type="text" name="search" class="form-control" placeholder="{{ __('Recherche de ticket..') }}" aria-label="{{ __('Recherche de ticket..') }}" aria-describedby="searchTicket" value="{{ request()->search }}">
|
||||
<button class="btn btn-outline-danger" type="button" onclick="$('#searchTicket').val('');$('#searchTicketForm').submit();">⌫</button>
|
||||
<input id="searchTicket" type="text" name="search" class="form-control" placeholder="{{ __('Recherche de ticket..') }}" aria-label="{{ __('Recherche de ticket..') }}" aria-describedby="searchTicket" value="{{ session()->get('tickets.search') }}">
|
||||
<button class="btn btn-outline-danger" type="button" onclick="document.getElementById('searchTicket').value = '';document.getElementById('searchTicketForm').submit();">⌫</button>
|
||||
<button class="btn btn-outline-primary" type="submit">🔍</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -61,20 +71,21 @@ $userPr = Auth::user()->sites ?? collect([]);
|
|||
#
|
||||
@include('modules.sorter', ['colname' => 'id'])
|
||||
</th>
|
||||
<th scope="col">
|
||||
{{__('Projet') }}
|
||||
@include('modules.sorter', ['colname' => 'site_id'])
|
||||
</th>
|
||||
<th scope="col">
|
||||
{{__('Type') }}
|
||||
@include('modules.sorter', ['colname' => 'type'])
|
||||
</th>
|
||||
<th scope="col">{{__('Fichier') }}</th>
|
||||
<th scope="col">{{__('Fichier 2') }}</th>
|
||||
<th scope="col">{{__('Fichiers') }}</th>
|
||||
<th scope="col">
|
||||
{{__('Date de création') }}
|
||||
{{__('Crée le') }}
|
||||
@include('modules.sorter', ['colname' => 'created_at'])
|
||||
</th>
|
||||
@if(Auth::user()->isAdmin())
|
||||
<th scope="col">
|
||||
{{__('Assigné à') }}
|
||||
@include('modules.sorter', ['colname' => 'assigned_to'])
|
||||
</th>
|
||||
@endif
|
||||
<th scope="col">
|
||||
{{__('Créé par') }}
|
||||
@include('modules.sorter', ['colname' => 'created_by'])
|
||||
|
|
@ -119,12 +130,11 @@ $userPr = Auth::user()->sites ?? collect([]);
|
|||
@endphp
|
||||
<tr>
|
||||
<td scope="row">
|
||||
{{ $ticket->id }}
|
||||
@if ($ticket->is_internal === 1)
|
||||
🔒
|
||||
@endif
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="me-1">{{ $ticket->id }} @if($ticket->is_internal)🔒@endif</span>
|
||||
<span class="text-end">{{ $ticket->site->name }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ $ticket->site->name }}</td>
|
||||
<td>
|
||||
<span class="badge {{ $ticketClass }}">{{ $ticket->type->label }}</span>
|
||||
</td>
|
||||
|
|
@ -132,13 +142,14 @@ $userPr = Auth::user()->sites ?? collect([]);
|
|||
@if ($ticket->file !== null)
|
||||
<a data-fslightbox="gallery" href="{{ asset($ticket->file) }}" class="btn btn-sm btn-outline-primary" target="_blank">Fichier</a>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($ticket->file2 !== null)
|
||||
<a data-fslightbox="gallery" href="{{ asset($ticket->file2) }}" class="btn btn-sm btn-outline-primary" target="_blank">Fichier 2</a>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ Carbon\Carbon::parse($ticket->created_at)->format('d/m/Y à H:i') }}</td>
|
||||
@if(Auth::user()->isAdmin())
|
||||
<td>{{ $ticket->assignedUser ? $ticket->assignedUser->name : __('Personne') }}</td>
|
||||
@endif
|
||||
<td>{{ $ticket->user ? $ticket->user->name : __('Utilisateur supprimé') }}</td>
|
||||
<td><span class="rounded {{ $cls }}">{{ $ticket->status->label }}</span></td>
|
||||
<td>
|
||||
|
|
@ -157,7 +168,7 @@ $userPr = Auth::user()->sites ?? collect([]);
|
|||
<b>{{ $ticket->site->name }}</b> -
|
||||
<u>
|
||||
{{__('Ticket n°') }} {{ $ticket->id }}
|
||||
@if ($ticket->is_internal === 1)
|
||||
@if ($ticket->is_internal)
|
||||
🔒
|
||||
@endif
|
||||
</u> -
|
||||
|
|
|
|||
|
|
@ -3,31 +3,13 @@
|
|||
@section('content')
|
||||
<div class="form-row row">
|
||||
<h2 class="col">
|
||||
@php
|
||||
$back = route('ticket.index');
|
||||
$url = parse_url(url()->previous());
|
||||
if (
|
||||
($url['host'] ?? '') === parse_url(config('app.url'), PHP_URL_HOST) and
|
||||
($url['path'] ?? '') === '/ticket'
|
||||
) {
|
||||
$queries = explode('&', $url['query'] ?? '');
|
||||
foreach($queries as $q) {
|
||||
$q = explode('=', $q);
|
||||
if(($q[0] ?? '') === 'pr' and ctype_digit(($q[1] ?? ''))) {
|
||||
$back = route('ticket.index', ['pr' => $q[1]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
<a href="{{ $back }}" class="text-decoration-none">←</a>
|
||||
<a href="{{ route('ticket.index') }}" class="text-decoration-none">←</a>
|
||||
{{ __('Ticket n° :ticketId : :ticketSiteName - :ticketTypeLabel', [
|
||||
'ticketId' => $ticket->id,
|
||||
'ticketSiteName' => $ticket->site->name,
|
||||
'ticketTypeLabel' => $ticket->type->label
|
||||
]) }}
|
||||
@if ($ticket->is_internal === 1)
|
||||
🔒
|
||||
@endif
|
||||
@if ($ticket->is_internal)🔒@endif
|
||||
<span class="badge bg-info">{{ $ticket->status->label }}</span>
|
||||
</h2>
|
||||
<div class="col d-flex flex-row text-end">
|
||||
|
|
@ -35,15 +17,26 @@
|
|||
<form action="{{ route('ticket.subscribe', $ticket->id) }}" method="POST" id="form_abonnement" class="col">
|
||||
@csrf
|
||||
<div class="custom-control custom-checkbox text-right p-2">
|
||||
<input type="checkbox" name="subscription" class="custom-control-input" id="subscription" @if ($isSubscribed)checked @endif onchange="$('#form_abonnement').submit()">
|
||||
<input type="checkbox" name="subscription" class="custom-control-input" id="subscription" @if ($isSubscribed)checked @endif onchange="document.getElementById('form_abonnement').submit()">
|
||||
<label class="custom-control-label" for="subscription">{{ __('Suivre le ticket') }}</label>
|
||||
</div>
|
||||
</form>
|
||||
@endcan
|
||||
@can('assignTo', $ticket)
|
||||
<form action="{{ route('ticket.assignTo', ['ticket' => $ticket]) }}" method="POST" id="form_assign" class="col me-2">
|
||||
@csrf
|
||||
<select class="form-select" name="assign" id="assign" onchange="document.getElementById('form_assign').submit();">
|
||||
<option value="" @if(!$ticket->assignedUser)selected @endif>{{ __('Personne n\'est assigné') }}</option>
|
||||
@foreach(\App\Models\User::adminUsers() as $user)
|
||||
<option value="{{ $user->id }}" @if(optional($ticket->assignedUser)->id == $user->id)selected @endif>{{ $user->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</form>
|
||||
@endcan
|
||||
@can('changeStatus', $ticket)
|
||||
<form action="{{ route('ticket.changeStatus', $ticket->id) }}" method="POST" id="form_status" class="col">
|
||||
@csrf
|
||||
<select class="form-select" name="status" id="status" onchange="$('#form_status').submit();">
|
||||
<select class="form-select" name="status" id="status" onchange="document.getElementById('form_status').submit();">
|
||||
@foreach(TicketStatus::toArray() as $ticketStatus)
|
||||
<option value="{{ $ticketStatus->value }}" @if($ticketStatus->equals($ticket->status))selected @endif>{{ $ticketStatus->label }}</option>
|
||||
@endforeach
|
||||
|
|
@ -120,9 +113,9 @@
|
|||
</div>
|
||||
@else
|
||||
<div class="@if($message->user_id === Auth::user()->id)offset-md-4 alert-dark @else alert-light @endif col-md-8 alert">
|
||||
{!! Utils::plainToHtml($message->message) !!}
|
||||
{!! ToolboxHelper::plainToHtml($message->message) !!}
|
||||
@if ($message->file !== null)
|
||||
<br><a @if(Utils::isImageFile($message->file))data-fslightbox="gallery" @endif href="{{ asset($message->file) }}" target="_blank">Ouvrir la pièce jointe</a><br>
|
||||
<br><a @if(ToolboxHelper::isImageFile($message->file))data-fslightbox="gallery" @endif href="{{ asset($message->file) }}" target="_blank">Ouvrir la pièce jointe</a><br>
|
||||
@endif<br><br>
|
||||
<small class="text-muted fw-bold">
|
||||
{!! __('Créé le :date par <b>:userName</b>', [
|
||||
|
|
@ -172,7 +165,7 @@
|
|||
<hr>
|
||||
<div>
|
||||
<h4>{{__('Description') }}</h4>
|
||||
<p>{!! clean(Utils::plainToHtml($ticket->text)) !!}</p>
|
||||
<p>{!! clean(ToolboxHelper::plainToHtml($ticket->text)) !!}</p>
|
||||
<small class="text-muted">{!! __('Créé le :date par <b>:userName</b>', [
|
||||
'date' => \Carbon\Carbon::parse($ticket->created_at)->format('d/m/Y à H:i'),
|
||||
'userName' => $ticket->user ? $ticket->user->name : __('Utilisateur supprimé')
|
||||
|
|
@ -186,13 +179,13 @@
|
|||
@if (!is_null($ticket->file))
|
||||
<li>
|
||||
{{ __('Pièce jointe n°1') }} :
|
||||
<a @if(Utils::isImageFile($ticket->file))data-fslightbox="gallery" @endif href="{{ asset($ticket->file) }}" target="_blank">{{ __('Ouvrir la pièce jointe') }}</a>
|
||||
<a @if(ToolboxHelper::isImageFile($ticket->file))data-fslightbox="gallery" @endif href="{{ asset($ticket->file) }}" target="_blank">{{ __('Ouvrir la pièce jointe') }}</a>
|
||||
</li>
|
||||
@endif
|
||||
@if (!is_null($ticket->file2))
|
||||
<li>
|
||||
{{ __('Pièce jointe n°2') }} :
|
||||
<a @if(Utils::isImageFile($ticket->file2))data-fslightbox="gallery" @endif href="{{ asset($ticket->file2) }}" target="_blank">{{ __('Ouvrir la pièce jointe') }}</a>
|
||||
<a @if(ToolboxHelper::isImageFile($ticket->file2))data-fslightbox="gallery" @endif href="{{ asset($ticket->file2) }}" target="_blank">{{ __('Ouvrir la pièce jointe') }}</a>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<tr>
|
||||
<td class="header">
|
||||
<a href="{{ $url }}" style="display: inline-block;">
|
||||
<img src="{{ asset('images/supportweb_128.png') }}" class="logo" alt="{{ config('app.name') }} Logo">
|
||||
<img src="{{ asset('assets/images/supportweb_128.png') }}" class="logo" alt="{{ config('app.name') }} Logo">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -65,8 +65,12 @@ Route::middleware(['auth'])->group(function () {
|
|||
// * Resources
|
||||
Route::resource('ticket', TicketController::class)->except(['destroy']);
|
||||
Route::resource('users', UserController::class);
|
||||
Route::resource('sites', SiteController::class);
|
||||
Route::resource('projects', ProjectController::class);
|
||||
Route::resource('sites', SiteController::class)
|
||||
->withoutMiddleware('xss')
|
||||
->middleware('xss:description,todo');
|
||||
Route::resource('projects', ProjectController::class)
|
||||
->withoutMiddleware('xss')
|
||||
->middleware('xss:description,todo');
|
||||
Route::resource('projects.macro_markers', MarkerController::class)
|
||||
->parameters([
|
||||
'macro_markers' => 'marker'
|
||||
|
|
@ -82,6 +86,9 @@ Route::middleware(['auth'])->group(function () {
|
|||
|
||||
// * Ticket
|
||||
Route::prefix('/ticket')->name('ticket.')->group(function () {
|
||||
Route::post('/{ticket}/assignTo', [TicketController::class, 'assignTo'])
|
||||
->middleware('can:assignTo,ticket')
|
||||
->name('assignTo');
|
||||
Route::post('/{ticket}/reopen', [TicketController::class, 'reopen'])
|
||||
->middleware('can:reopen,ticket')
|
||||
->name('reopen');
|
||||
|
|
|
|||
59
webpack.mix.js
vendored
59
webpack.mix.js
vendored
|
|
@ -1,13 +1,58 @@
|
|||
const mix = require('laravel-mix');
|
||||
require('laravel-mix-polyfill');
|
||||
const path = require('path')
|
||||
const mix = require('laravel-mix')
|
||||
require('laravel-mix-polyfill')
|
||||
require('laravel-mix-clean')
|
||||
|
||||
let assetDir = 'assets'
|
||||
let polyfill = {
|
||||
enabled: true,
|
||||
useBuiltIns: "usage",
|
||||
// legacy targets from gulp file
|
||||
targets: ['last 3 version', 'safari 5', 'ie 8', 'opera 12.1', 'ios 6', 'android 4']
|
||||
}
|
||||
|
||||
mix.webpackConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve('resources/assets'),
|
||||
'vue$': process.env.NODE_ENV == 'production' ? 'vue/dist/vue.runtime.min.js' : 'vue/dist/vue.runtime.js',
|
||||
}
|
||||
},
|
||||
// Pour debug compilation errors
|
||||
// stats: {
|
||||
// children: true,
|
||||
// warningsFilter: [
|
||||
// /\-\-underline\-color/,
|
||||
// ]
|
||||
// },
|
||||
})
|
||||
|
||||
mix.options({
|
||||
fileLoaderDirs: {
|
||||
fonts: `${assetDir}/fonts`,
|
||||
images: `${assetDir}/images`
|
||||
},
|
||||
postCss: [
|
||||
require('postcss-discard-comments')({
|
||||
removeAll: true
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
mix.clean({
|
||||
cleanOnceBeforeBuildPatterns: [
|
||||
'css/*',
|
||||
'js/*',
|
||||
'assets/*',
|
||||
]
|
||||
})
|
||||
|
||||
// mix Backend
|
||||
mix.js('resources/js/app.js', 'public/js')
|
||||
.sass('resources/sass/app.scss', 'public/css')
|
||||
.sass('resources/sass/bootstrap.scss', 'public/css')
|
||||
.vue()
|
||||
.polyfill({
|
||||
enabled: true,
|
||||
useBuiltIns: "usage",
|
||||
targets: { "firefox": "50", "ie": 11 }
|
||||
});
|
||||
.polyfill(polyfill)
|
||||
|
||||
// Copy Blade Images Assets
|
||||
mix.copyDirectory('resources/assets', 'public/assets')
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue