Merge branch 'macroplanning' into develop
This commit is contained in:
commit
f4412728d6
50 changed files with 4035 additions and 1667 deletions
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
|
|
@ -1,16 +1,16 @@
|
|||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#275782",
|
||||
"activityBar.activeBorder": "#1b1e21",
|
||||
"activityBar.background": "#275782",
|
||||
"activityBar.activeBackground": "#3371a9",
|
||||
"activityBar.activeBorder": "#621e41",
|
||||
"activityBar.background": "#3371a9",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#1b1e21",
|
||||
"activityBarBadge.foreground": "#ffffff",
|
||||
"sash.hoverBorder": "#275782",
|
||||
"activityBarBadge.background": "#621e41",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"sash.hoverBorder": "#3371a9",
|
||||
"statusBar.background": "#275782",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#275782",
|
||||
"statusBarItem.hoverBackground": "#3371a9",
|
||||
"statusBarItem.remoteBackground": "#275782",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#275782",
|
||||
|
|
@ -18,4 +18,5 @@
|
|||
"titleBar.inactiveBackground": "#27578299",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
}
|
||||
"peacock.remoteColor": "#275782",
|
||||
}
|
||||
|
|
|
|||
47
app/Enums/MacroSchedule/Activity.php
Normal file
47
app/Enums/MacroSchedule/Activity.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\MacroSchedule;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self conception()
|
||||
* @method static self development()
|
||||
* @method static self recipe()
|
||||
* @method static self production()
|
||||
* @method static self meeting()
|
||||
*/
|
||||
final class Activity extends BaseEnum
|
||||
{
|
||||
/**
|
||||
* Enum values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'conception' => 0,
|
||||
'development' => 1,
|
||||
'recipe' => 2,
|
||||
'production' => 3,
|
||||
'meeting' => 4
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'conception' => trans('Conception / Rédaction'),
|
||||
'development' => trans('Développement'),
|
||||
'recipe' => trans('Mise en recette'),
|
||||
'production' => trans('Mise en production'),
|
||||
'meeting' => trans('Réunion')
|
||||
];
|
||||
}
|
||||
}
|
||||
53
app/Enums/ProjectStatus.php
Normal file
53
app/Enums/ProjectStatus.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self new()
|
||||
* @method static self specification()
|
||||
* @method static self validated()
|
||||
* @method static self development()
|
||||
* @method static self recipe()
|
||||
* @method static self production()
|
||||
* @method static self finished()
|
||||
*/
|
||||
final class ProjectStatus extends BaseEnum
|
||||
{
|
||||
/**
|
||||
* Enum values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'new' => 0,
|
||||
'specification' => 1,
|
||||
'validated' => 2,
|
||||
'development' => 3,
|
||||
'recipe' => 4,
|
||||
'production' => 5,
|
||||
'finished' => 6
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'new' => trans('Nouveau'),
|
||||
'specification' => trans('Spécifications'),
|
||||
'validated' => trans('Validé'),
|
||||
'development' => trans('En développement'),
|
||||
'recipe' => trans('En recettage'),
|
||||
'production' => trans('En production'),
|
||||
'finished' => trans('Terminé')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -17,22 +17,25 @@ class Controller extends BaseController
|
|||
use DispatchesJobs;
|
||||
use ValidatesRequests;
|
||||
|
||||
/**
|
||||
* Controller
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(function($request, $next) {
|
||||
$this->middleware(function ($request, $next) {
|
||||
$ignore = [
|
||||
// Support Web
|
||||
// * Support Web
|
||||
9,
|
||||
// Gen-Game- Web
|
||||
// * Gen-Game- Web
|
||||
15,
|
||||
// Store LDC
|
||||
// * Store LDC
|
||||
19,
|
||||
// CRM
|
||||
// * CRM
|
||||
27,
|
||||
// LDC - WEB
|
||||
// * LDC - WEB
|
||||
55,
|
||||
];
|
||||
|
||||
|
||||
$ticketsNew = Ticket::where('status', TicketStatus::new());
|
||||
$ticketsWaiting = Ticket::where('status', TicketStatus::waiting());
|
||||
$ticketsInProgress = Ticket::whereNotIn(
|
||||
|
|
@ -48,19 +51,19 @@ class Controller extends BaseController
|
|||
TicketStatus::archived()
|
||||
]
|
||||
);
|
||||
|
||||
/** @var \App\Models\User|null $user */
|
||||
|
||||
/** @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);
|
||||
}
|
||||
|
||||
|
||||
View::share('ticketsNew', $ticketsNew->count());
|
||||
View::share('ticketsWaiting', $ticketsWaiting->count());
|
||||
View::share('ticketsInProgress', $ticketsInProgress->count());
|
||||
|
||||
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,19 @@ class FileController extends Controller
|
|||
/** @var File */
|
||||
public static $modelClass = File::class;
|
||||
|
||||
/**
|
||||
* Crud Layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
/**
|
||||
* Index query middleware
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
$parent = request()->site ?? request()->project;
|
||||
|
|
@ -30,6 +41,12 @@ class FileController extends Controller
|
|||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crud index view
|
||||
*
|
||||
* @param \Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
@ -40,6 +57,12 @@ class FileController extends Controller
|
|||
return view('files.index', $view->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Crud create view
|
||||
*
|
||||
* @param \Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function create(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
@ -47,6 +70,12 @@ class FileController extends Controller
|
|||
return view('files.create', $view->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Before store model middleware
|
||||
*
|
||||
* @param \App\Models\File $file
|
||||
* @return void
|
||||
*/
|
||||
public function storeModel(File &$file)
|
||||
{
|
||||
$file->parent()->associate(request()->site ?? request()->project);
|
||||
|
|
|
|||
69
app/Http/Controllers/MarkerController.php
Normal file
69
app/Http/Controllers/MarkerController.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\MacroSchedule\Marker;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
class MarkerController extends Controller
|
||||
{
|
||||
use CrudController;
|
||||
|
||||
/** @var \App\Models\MacroSchedule\Marker */
|
||||
public static $modelClass = Marker::class;
|
||||
|
||||
/**
|
||||
* Crud Layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
/**
|
||||
* Show create page middleware
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function createModel()
|
||||
{
|
||||
$this->shareLabelsToView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show edit model middleware
|
||||
*
|
||||
* @param Model $model
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function editModel(Model &$model)
|
||||
{
|
||||
$this->shareLabelsToView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Before store model middleware
|
||||
*
|
||||
* @param \App\Models\MacroSchedule\Marker $marker
|
||||
* @return void
|
||||
*/
|
||||
public function storeModel(Marker &$marker)
|
||||
{
|
||||
$marker->project()->associate(request()->project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Share Marker labels to view
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function shareLabelsToView()
|
||||
{
|
||||
View::share('markerLabels', Cache::remember('markers_labels', 20, function () {
|
||||
return Marker::pluck('label');
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -10,13 +10,15 @@ use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
|||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
|
||||
use CrudController {
|
||||
__construct as __CrudConstructor;
|
||||
index as crudIndex;
|
||||
show as crudShow;
|
||||
}
|
||||
|
||||
/**
|
||||
* ProjectController
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
|
@ -24,11 +26,22 @@ class ProjectController extends Controller
|
|||
$this->authorizeResource(Project::class, 'project');
|
||||
}
|
||||
|
||||
/** @var Project */
|
||||
/** @var \App\Models\Project */
|
||||
public static $modelClass = Project::class;
|
||||
|
||||
/**
|
||||
* Crud Layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
/**
|
||||
* Crud index view
|
||||
*
|
||||
* @param \Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
@ -39,6 +52,13 @@ class ProjectController extends Controller
|
|||
return view('projects.index', $view->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Crud show view
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Models\Project $model
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function show(Request $request, Project $model)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
|
|||
120
app/Http/Controllers/ScheduleMacroController.php
Normal file
120
app/Http/Controllers/ScheduleMacroController.php
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\ProjectStatus;
|
||||
use App\Http\Requests\MacroSchedule\PostActivityRequest;
|
||||
use App\Http\Requests\MacroSchedule\ShowMacroScheduleRequest;
|
||||
use App\Models\MacroSchedule\Activity;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ScheduleMacroController extends Controller
|
||||
{
|
||||
/**
|
||||
* View Macro planning
|
||||
*
|
||||
* @param \App\Http\Requests\MacroSchedule\ShowMacroScheduleRequest $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function show(ShowMacroScheduleRequest $request)
|
||||
{
|
||||
$numberOfWeeksToShow = \config('macroschedule.numberOfWeeksToShow');
|
||||
|
||||
$date = Carbon::now();
|
||||
if ($request->date) {
|
||||
$date = Carbon::createFromFormat('d/m/Y', $request->date);
|
||||
}
|
||||
|
||||
/** @var \Illuminate\Support\Carbon $date The date from which to take activities */
|
||||
$date->startOfWeek();
|
||||
|
||||
/** @var \Illuminate\Support\Collection $projects */
|
||||
$projects = Project::where('status', '>', ProjectStatus::new())
|
||||
->where('status', '<', ProjectStatus::finished())
|
||||
->with(['activities' => function ($q) use ($date, $numberOfWeeksToShow) {
|
||||
$q->whereDate('start', '>=', $date->toDateString())
|
||||
->OrWhereDate('end', '<=', $date->clone()->addWeeks($numberOfWeeksToShow)->toDateString());
|
||||
}])->with('markers')->orderBy('status')->orderBy('name')->get()->keyBy('id');
|
||||
|
||||
// * Key by date activities relation
|
||||
$projects->map(function (Project $project) {
|
||||
$project->setRelation('activities', $project->activities->keyBy('id'));
|
||||
});
|
||||
|
||||
// * Map activities by datetime
|
||||
$activities = collect();
|
||||
$projects->pluck('activities')->map(function (Collection $projectActivities) use (&$activities) {
|
||||
$projectActivities->map(function (Activity $activity) use (&$activities) {
|
||||
$activities->push($activity);
|
||||
});
|
||||
});
|
||||
|
||||
// * Disponibility calculation
|
||||
$loadCollection = collect();
|
||||
|
||||
/** @var array<\Illuminate\Support\Carbon> */
|
||||
$period = $date->toPeriod($date->clone()->addWeeks($numberOfWeeksToShow), 12, 'hour')->toArray();
|
||||
|
||||
foreach ($period as $day) {
|
||||
/** @var \Illuminate\Support\Collection */
|
||||
$dayActivities = $activities->filter(function (Activity $activity) use ($day) {
|
||||
return $day->betweenIncluded($activity->start, $activity->end);
|
||||
});
|
||||
/** @var integer total activités / hommes / demi journées */
|
||||
$totalLoad = $dayActivities->sum('load');
|
||||
$loadCollection->put($day->format('Y-m-d H:i:s'), $totalLoad / config('macroschedule.totalAvailableDevs'));
|
||||
}
|
||||
|
||||
return view('macroplanning.view', compact(
|
||||
'date',
|
||||
'projects',
|
||||
'date',
|
||||
'numberOfWeeksToShow',
|
||||
'loadCollection'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save activity.
|
||||
*
|
||||
* @param PostActivityRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function postActivity(PostActivityRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$hasOverLap = false;
|
||||
|
||||
/** @var \App\Models\Project */
|
||||
$project = Project::whereId($request->project_id)
|
||||
->with('activities')->firstOrFail();
|
||||
|
||||
/** @var \App\Models\MacroSchedule\Activity */
|
||||
$activity = Activity::whereId($request->id)->firstOrNew();
|
||||
$activity->fill($request->validated());
|
||||
$activity->project()->associate($project);
|
||||
if (!$activity->saveOrFail()) {
|
||||
response()->json('Une erreur est survenue lors de \'enregistrement');
|
||||
}
|
||||
|
||||
// * Overload activities if necessary
|
||||
$project->activities->map(function (Activity $periodActivity) use ($activity, &$hasOverLap) {
|
||||
if (
|
||||
$activity->id !== $periodActivity->id and
|
||||
$activity->doesPeriodCoverActivity($periodActivity->start, $periodActivity->end)
|
||||
) {
|
||||
$hasOverLap = true;
|
||||
$periodActivity->overlapUsingActivity($activity);
|
||||
}
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'id' => $activity->id,
|
||||
'hasOverlap' => $hasOverLap
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
105
app/Http/Requests/MacroSchedule/PostActivityRequest.php
Normal file
105
app/Http/Requests/MacroSchedule/PostActivityRequest.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\MacroSchedule;
|
||||
|
||||
use App\Enums\MacroSchedule\Activity as EnumActivity;
|
||||
use App\Models\MacroSchedule\Activity as ModelActivity;
|
||||
use App\Models\Project;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
|
||||
class PostActivityRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() and $this->ajax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
/** @var \App\Models\Project|null $project */
|
||||
$project = Project::whereId($this->project_id)->with('activities')->first();
|
||||
if ($project and $this->id) {
|
||||
$project->setRelation(
|
||||
'activities',
|
||||
$project->activities->filter(function (ModelActivity $activity) {
|
||||
return $activity->id !== $this->id;
|
||||
})
|
||||
);
|
||||
}
|
||||
return [
|
||||
'project_id' => 'required|exists:projects,id',
|
||||
'id' => 'nullable|exists:macro_activities,id',
|
||||
'activity' => ['required', new EnumIsValidRule(EnumActivity::class)],
|
||||
'comment' => 'required|min:0|max:255',
|
||||
'start' => [
|
||||
'required',
|
||||
'date_format:Y-m-d\TH:i:s',
|
||||
function ($attribute, $value, $fail) use ($project) {
|
||||
try {
|
||||
$dateTime = Carbon::parse($value);
|
||||
if (in_array($dateTime->dayOfWeek, [0, 6])) {
|
||||
$fail('La date de début ne peut être un jour de weekend.');
|
||||
return;
|
||||
}
|
||||
if (!in_array($dateTime->format('H:i'), ['00:00', '12:00'])) {
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
$fail("$attribute : Il est seulement possible de définir une date le matin ou l'après midi.");
|
||||
}
|
||||
} catch (InvalidFormatException $e) {
|
||||
$fail('Format de date invalide');
|
||||
}
|
||||
}
|
||||
],
|
||||
'end' => [
|
||||
'required',
|
||||
'date_format:Y-m-d\TH:i:s',
|
||||
'after:start',
|
||||
function ($attribute, $value, $fail) use ($project) {
|
||||
try {
|
||||
$dateTime = Carbon::parse($value);
|
||||
if (in_array($dateTime->dayOfWeek, [0, 6])) {
|
||||
$fail('La date de fin ne peut être un jour de weekend.');
|
||||
return;
|
||||
}
|
||||
if (!in_array($dateTime->format('H:i'), ['11:59', '23:59'])) {
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
$fail("$attribute : Il est seulement possible de définir une date le matin ou l'après midi.");
|
||||
}
|
||||
} catch (InvalidFormatException $e) {
|
||||
$fail('Format de date invalide');
|
||||
}
|
||||
}
|
||||
],
|
||||
'load' => 'required|integer|min:1|max:' . config('macroschedule.totalAvailableDevs')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'Date de début',
|
||||
'end' => 'Date de fin',
|
||||
'activity' => 'Activité',
|
||||
'comment' => 'Commentaire',
|
||||
'load' => 'Charge',
|
||||
];
|
||||
}
|
||||
}
|
||||
30
app/Http/Requests/MacroSchedule/ShowMacroScheduleRequest.php
Normal file
30
app/Http/Requests/MacroSchedule/ShowMacroScheduleRequest.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\MacroSchedule;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ShowMacroScheduleRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'date' => 'nullable|date_format:d/m/Y',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -7,10 +7,16 @@ use Illuminate\Support\Facades\View;
|
|||
|
||||
class Utils
|
||||
{
|
||||
public static function plainToHtml(string $text)
|
||||
/**
|
||||
* 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
|
||||
// * 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@:%_\+.~#?&\/\/=]*))/',
|
||||
|
|
@ -41,6 +47,12 @@ class Utils
|
|||
return nl2br($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check uri points to image file
|
||||
*
|
||||
* @param string $url
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isImageFile(string $url): bool
|
||||
{
|
||||
$m = [];
|
||||
|
|
@ -79,7 +91,7 @@ class Utils
|
|||
*
|
||||
* Si nv Charge < 0 => saturation
|
||||
*
|
||||
* @return void
|
||||
* @return \App\Enums\LoadLevel
|
||||
*/
|
||||
public static function computeWorkload(): LoadLevel
|
||||
{
|
||||
|
|
@ -87,25 +99,25 @@ class Utils
|
|||
$Tprogress = View::shared('ticketsInProgress');
|
||||
$Twaiting = View::shared('ticketsWaiting');
|
||||
|
||||
$worked = config('tickets.workedHoursInAWeek');
|
||||
$workedDays = config('tickets.workedDaysInAWeek');
|
||||
$worked = config('tickets.workedHoursInAWeek');
|
||||
$workedDays = config('tickets.workedDaysInAWeek');
|
||||
// * in hours
|
||||
$morningWorkTime = ($worked / $workedDays / 2);
|
||||
// in hours
|
||||
// * Devs handlings tickets per week
|
||||
$workingDevsPerWeek = config('tickets.workingDevsPerWeek');
|
||||
// Devs handlings tickets per week
|
||||
// * Worktime per week to handle tickets
|
||||
$timePerWeekDev = $workingDevsPerWeek * $morningWorkTime;
|
||||
// Worktime per week to handle tickets
|
||||
// * 45min for a ticket
|
||||
$avgTimeForTicket = config('tickets.avgTimeForTicket');
|
||||
// 45min for a ticket
|
||||
// * The time needed to handle new ticket (average)
|
||||
$todoListDelay = $Tnew * $avgTimeForTicket;
|
||||
// The time needed to handle new ticket (average)
|
||||
// * The time needed to handle new ticket (average)
|
||||
$waitingListDelay = $Twaiting * $avgTimeForTicket;
|
||||
// The time needed to handle new ticket (average)
|
||||
// * The time needed to handle new ticket (average)
|
||||
$progressListDelay = $Tprogress * $avgTimeForTicket;
|
||||
// The time needed to handle new ticket (average)
|
||||
$totalTicketsDelay = $todoListDelay + $progressListDelay + $waitingListDelay;
|
||||
$ETAForAnewTicket = $totalTicketsDelay / $timePerWeekDev;
|
||||
// in weeks
|
||||
// * in weeks
|
||||
$ETAForAnewTicket = $totalTicketsDelay / $timePerWeekDev;
|
||||
switch ((int)round($ETAForAnewTicket)) {
|
||||
case LoadLevel::weak()->value:
|
||||
case LoadLevel::stable()->value:
|
||||
|
|
|
|||
264
app/Models/MacroSchedule/Activity.php
Normal file
264
app/Models/MacroSchedule/Activity.php
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\MacroSchedule;
|
||||
|
||||
use App\Enums\MacroSchedule\Activity as MacroScheduleActivity;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Activity extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'macro_activities';
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'start' => 'datetime',
|
||||
'end' => 'datetime',
|
||||
'load' => 'int',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to Enums.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'activity' => MacroScheduleActivity::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'activity',
|
||||
'load',
|
||||
'comment',
|
||||
'start',
|
||||
'end',
|
||||
'project_id'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::updating(function (self $activity) {
|
||||
if (
|
||||
$activity->getRawOriginal('start') !== $activity->start->format('Y-m-d H:i:s') and
|
||||
$activity->start->clone()->setTime(0, 0)->lessThan(now()->setTime(0, 0))
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
'Il n\'est pas possible de modifier la date de début à une date passé'
|
||||
]);
|
||||
}
|
||||
if (
|
||||
$activity->getRawOriginal('end') !== $activity->end->format('Y-m-d H:i:s') and
|
||||
$activity->end->clone()->setTime(0, 0)->lessThan(now()->setTime(0, 0))
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
'Il n\'est pas possible de modifier la date de fin à une date passé'
|
||||
]);
|
||||
}
|
||||
if (Carbon::parse($activity->getRawOriginal('start'))->lessThan(now()->setTime(0, 0))) {
|
||||
throw ValidationException::withMessages([
|
||||
'Il n\'est pas possible de modifier la date de début d\'une activité qui est déjà passée'
|
||||
]);
|
||||
}
|
||||
if (Carbon::parse($activity->getRawOriginal('end'))->lessThan(now()->setTime(0, 0))) {
|
||||
throw ValidationException::withMessages([
|
||||
'Il n\'est pas possible de modifier la date de fin d\'une activité dejà terminée'
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS.
|
||||
|
||||
/**
|
||||
* BelongsTo Project
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Set the start date and specifiy wether it begins in
|
||||
* the morning or in the afternoon.
|
||||
*
|
||||
* @param Carbon $date
|
||||
* @param boolean $isMorning
|
||||
* @return void
|
||||
*/
|
||||
public function setStartDate(Carbon $date, bool $isMorning)
|
||||
{
|
||||
$date = $isMorning ? $date->setTime(0, 0) : $date->setTime(12, 00);
|
||||
|
||||
$this->attributes['start'] = $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the end date and specifiy wether it begins in
|
||||
* the morning or in the afternoon.
|
||||
*
|
||||
* @param Carbon $date
|
||||
* @param boolean $isMorning
|
||||
* @return void
|
||||
*/
|
||||
public function setEndDate(Carbon $date, bool $isMorning)
|
||||
{
|
||||
$date = $isMorning ? $date->setTime(11, 59) : $date->setTime(23, 59);
|
||||
|
||||
$this->attributes['end'] = $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start date attribute.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws \ErrorException This attribute cannot direct be assigned.
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function setStartDateAttribute($value)
|
||||
{
|
||||
throw new \ErrorException('The start date attribute should be assigned only using setDate method');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the end date attribute.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws \ErrorException This attribute cannot direct be assigned.
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function setEndDateAttribute($value)
|
||||
{
|
||||
throw new \ErrorException('The end date attribute should be assigned only using setDate method');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overlap activity using an other activity dates,
|
||||
* over the source activity si completely overlaped,
|
||||
* it will be removed
|
||||
*
|
||||
* @param self $activity
|
||||
* @return void
|
||||
* @throws \Throwable If updating (or removing) this activity failed.
|
||||
* @throws \ErrorException If no overlap action was done even the two activities overlaps.
|
||||
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
*/
|
||||
public function overlapUsingActivity(self $activity)
|
||||
{
|
||||
/** @var \Illuminate\Support\Carbon */
|
||||
$selfStart = $this->start;
|
||||
/** @var \Illuminate\Support\Carbon */
|
||||
$selfEnd = $this->end;
|
||||
/** @var \Illuminate\Support\Carbon */
|
||||
$targetStart = $activity->start;
|
||||
/** @var \Illuminate\Support\Carbon */
|
||||
$targetEnd = $activity->end;
|
||||
|
||||
// ? Does target overlap self ?
|
||||
if (!$activity->doesPeriodCoverActivity($selfStart, $selfEnd)) {
|
||||
return;
|
||||
}
|
||||
// ? Does target completely overlap self ?
|
||||
if ($targetStart->lessThanOrEqualTo($selfStart) and $targetEnd->greaterThanOrEqualTo($selfEnd)) {
|
||||
$this->deleteOrFail();
|
||||
return;
|
||||
}
|
||||
// ? Does target begins inside self ?
|
||||
if ($selfStart->toPeriod($selfEnd)->contains($targetStart)) {
|
||||
$newDate = $targetStart->clone()->subMinute();
|
||||
if ($newDate->isWeekend()) {
|
||||
// * ignore weekend
|
||||
$newDate->previous(Carbon::FRIDAY);
|
||||
$newDate->addMinute();
|
||||
}
|
||||
$this->setEndDate(
|
||||
$newDate,
|
||||
\boolval($newDate->hour < 12 and $newDate->hour >= 0)
|
||||
);
|
||||
$this->saveOrFail();
|
||||
return;
|
||||
}
|
||||
// ? Does target ends inside self ?
|
||||
if ($selfStart->toPeriod($selfEnd)->contains($targetEnd)) {
|
||||
$newDate = $targetEnd->clone()->addMinute();
|
||||
if ($newDate->isWeekend()) {
|
||||
// * ignore weekend
|
||||
$newDate->next(Carbon::MONDAY);
|
||||
}
|
||||
$this->setStartDate(
|
||||
$newDate,
|
||||
\boolval($newDate->hour < 12 and $newDate->hour >= 0)
|
||||
);
|
||||
$this->saveOrFail();
|
||||
return;
|
||||
}
|
||||
throw new \ErrorException('No overlap action was done.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a period covers this activity
|
||||
*
|
||||
* @param \Illuminate\Support\Carbon $start
|
||||
* @param \Illuminate\Support\Carbon $end
|
||||
* @return boolean
|
||||
*/
|
||||
public function doesPeriodCoverActivity(Carbon $start, Carbon $end): bool
|
||||
{
|
||||
if (!$this->start or !$this->end) {
|
||||
return false;
|
||||
}
|
||||
$overlap = $start->toPeriod($end)->overlaps($this->start, $this->end);
|
||||
return $overlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the model instance to an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array_merge(parent::toArray(), [
|
||||
'start' => $this->start->formatLocalized('%Y-%m-%dT%H:%M:%S'),
|
||||
'end' => $this->end->formatLocalized('%Y-%m-%dT%H:%M:%S')
|
||||
]);
|
||||
}
|
||||
}
|
||||
202
app/Models/MacroSchedule/Marker.php
Normal file
202
app/Models/MacroSchedule/Marker.php
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\MacroSchedule;
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
|
||||
class Marker extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
|
||||
/**
|
||||
* Crud label column
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudLabelColumn = 'label';
|
||||
|
||||
/**
|
||||
* Crud value column
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'macro_markers';
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'label',
|
||||
'marker'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'marker' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Maker
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
'label' => [
|
||||
'label' => trans('Nom'),
|
||||
'type' => CrudType::string(),
|
||||
'validate' => [
|
||||
'string',
|
||||
'min:4',
|
||||
'max:255'
|
||||
],
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'marker' => [
|
||||
'label' => trans('Marqueur'),
|
||||
'type' => CrudType::datetime(),
|
||||
'validate' => [
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
]
|
||||
];
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $marker) {
|
||||
if ($marker->markersThisSameDay()) {
|
||||
throw ValidationException::withMessages(['Un marqueur existe déjà pour ce projet ce même jour']);
|
||||
}
|
||||
});
|
||||
static::updating(function (self $marker) {
|
||||
if ($marker->markersThisSameDay()) {
|
||||
throw ValidationException::withMessages(['Un marqueur existe déjà pour ce projet ce même jour']);
|
||||
}
|
||||
});
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
static::created(function (self $marker) {
|
||||
Cache::delete('markerLabels');
|
||||
});
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
static::updated(function (self $marker) {
|
||||
Cache::delete('markerLabels');
|
||||
});
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
static::deleted(function (self $marker) {
|
||||
Cache::delete('markerLabels');
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Get all available labels from cache.
|
||||
* If cache was reset, it will fetch them from DB.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public static function getCachedLabels()
|
||||
{
|
||||
if (!Cache::has('markerLabels')) {
|
||||
self::setCachedLabels();
|
||||
}
|
||||
return Cache::get('markerLabels');
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all available labels into cache.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function setCachedLabels(): void
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $markerLabels */
|
||||
$markerLabels = self::select(DB::raw('LOWER(label) AS label'));
|
||||
$markerLabels->groupBy('label');
|
||||
Cache::put('markerLabels', $markerLabels->get()->pluck('label'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Count markers this same day
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function markersThisSameDay(): int
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder */
|
||||
$query = self::where('marker', '>=', $this->marker->setHour(0, 0)->format('Y-m-d'))
|
||||
->where('marker', '<', $this->marker->setHour(0, 0)->addDay()->format('Y-m-d'))
|
||||
->whereHas('project', function ($q) {
|
||||
$q->whereId($this->project->id);
|
||||
});
|
||||
if ($this->id) {
|
||||
$query->where('id', '!=', $this->id);
|
||||
}
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS.
|
||||
|
||||
/**
|
||||
* BelongsTo Project
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,28 +2,62 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ProjectStatus;
|
||||
use App\Events\ProjectUpdated;
|
||||
use App\Models\MacroSchedule\Activity;
|
||||
use App\Models\MacroSchedule\Marker;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Project extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* Crud label column
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudLabelColumn = 'name';
|
||||
|
||||
/**
|
||||
* Crud value column
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name', 'status',
|
||||
'description', 'todo', 'sites'
|
||||
];
|
||||
|
||||
public function __construct($attributes = [])
|
||||
/**
|
||||
* The attributes that should be cast to Enums.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'status' => ProjectStatus::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Project
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
'name' => [
|
||||
|
|
@ -56,11 +90,8 @@ class Project extends Model
|
|||
],
|
||||
'status' => [
|
||||
'label' => trans('État'),
|
||||
'type' => CrudType::string(),
|
||||
'validate' => [
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'type' => CrudType::enum(),
|
||||
'enum' => ProjectStatus::class,
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
|
|
@ -114,11 +145,26 @@ class Project extends Model
|
|||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
]
|
||||
]
|
||||
],
|
||||
'markers' => [
|
||||
'label' => trans('Marqueurs'),
|
||||
'type' => CrudType::hasMany(),
|
||||
'hasMany' => Marker::class,
|
||||
'hasManyLabel' => trans('Marqueurs'),
|
||||
'nullable' => true,
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
]
|
||||
],
|
||||
];
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::updated(function (Project $project) {
|
||||
|
|
@ -126,18 +172,55 @@ class Project extends Model
|
|||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS.
|
||||
|
||||
/**
|
||||
* BelongsToMany Site
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function sites()
|
||||
{
|
||||
return $this->belongsToMany(Site::class, 'project_site');
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphMany Log
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||
*/
|
||||
public function logs()
|
||||
{
|
||||
return $this->morphMany(Log::class, 'parent');
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphMany File
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return $this->morphMany(File::class, 'parent');
|
||||
}
|
||||
|
||||
/**
|
||||
* HasMany Activity
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function activities()
|
||||
{
|
||||
return $this->hasMany(Activity::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* HasMany Marker
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function markers()
|
||||
{
|
||||
return $this->hasMany(Marker::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
38
app/Policies/MarkerPolicy.php
Normal file
38
app/Policies/MarkerPolicy.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\UserRoleRules as URR;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||
|
||||
class MarkerPolicy extends RootPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
*/
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
* This first rule to return true in a policy action authorises the action
|
||||
*/
|
||||
$this->policies = [
|
||||
'marker' => [
|
||||
'viewAny' => URR::make('userIsAdmin', $user),
|
||||
'view' => URR::make('userIsAdmin', $user),
|
||||
'update' => URR::make('userIsAdmin', $user),
|
||||
'create' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('userIsAdmin', $user)
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -15,11 +15,11 @@ class ProjectPolicy extends RootPolicy
|
|||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
* @param User $user
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\MacroSchedule\Marker;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
|
@ -35,10 +37,20 @@ class AppServiceProvider extends ServiceProvider
|
|||
setlocale(LC_TIME, strtoupper(config('app.locale')) . "_" . config('app.locale') . '.utf8');
|
||||
}
|
||||
|
||||
$appInfos = json_decode(File::get(\app_path('../composer.json')));
|
||||
|
||||
View::share('name', $appInfos->name);
|
||||
View::share('version', $appInfos->version);
|
||||
View::share('license', $appInfos->license);
|
||||
|
||||
if (!app()->runningInConsole()) {
|
||||
$appInfos = Cache::remember('composer', 360, function () {
|
||||
return json_decode(File::get(\app_path('../composer.json')));
|
||||
});
|
||||
View::share('name', $appInfos->name);
|
||||
View::share('version', $appInfos->version);
|
||||
View::share('license', $appInfos->license);
|
||||
|
||||
// * Groups
|
||||
View::share('markerLabels', Cache::rememberForever('markerLabels', function () {
|
||||
return Marker::getCachedLabels() ?? [];
|
||||
}));
|
||||
}//end if
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Providers;
|
|||
|
||||
use App\Models\File;
|
||||
use App\Models\Log;
|
||||
use App\Models\MacroSchedule\Marker;
|
||||
use App\Models\Project;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ticket;
|
||||
|
|
@ -44,6 +45,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
Route::model('marker', Marker::class);
|
||||
Route::model('file', File::class);
|
||||
Route::model('log', Log::class);
|
||||
Route::model('user', User::class);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"fideloper/proxy": "^4.4",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"kwaadpepper/crud-policies": "^0.2.0",
|
||||
"kwaadpepper/crud-policies": "^0.3.6",
|
||||
"kwaadpepper/enum": "^2.1",
|
||||
"kwaadpepper/exception-handler": "^0.0.5",
|
||||
"laravel-lang/lang": "~8.0",
|
||||
|
|
|
|||
237
composer.lock
generated
237
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "78edc488b98b5b88d1345581be7e49a2",
|
||||
"content-hash": "2a618153177d1ef96c1f71551d669f8c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asm89/stack-cors",
|
||||
|
|
@ -223,16 +223,16 @@
|
|||
},
|
||||
{
|
||||
"name": "doctrine/dbal",
|
||||
"version": "3.3.4",
|
||||
"version": "3.3.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/dbal.git",
|
||||
"reference": "83f779beaea1893c0bece093ab2104c6d15a7f26"
|
||||
"reference": "719663b15983278227669c8595151586a2ff3327"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/83f779beaea1893c0bece093ab2104c6d15a7f26",
|
||||
"reference": "83f779beaea1893c0bece093ab2104c6d15a7f26",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/719663b15983278227669c8595151586a2ff3327",
|
||||
"reference": "719663b15983278227669c8595151586a2ff3327",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -247,7 +247,7 @@
|
|||
"require-dev": {
|
||||
"doctrine/coding-standard": "9.0.0",
|
||||
"jetbrains/phpstorm-stubs": "2021.1",
|
||||
"phpstan/phpstan": "1.4.6",
|
||||
"phpstan/phpstan": "1.5.3",
|
||||
"phpstan/phpstan-strict-rules": "^1.1",
|
||||
"phpunit/phpunit": "9.5.16",
|
||||
"psalm/plugin-phpunit": "0.16.1",
|
||||
|
|
@ -314,7 +314,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/dbal/issues",
|
||||
"source": "https://github.com/doctrine/dbal/tree/3.3.4"
|
||||
"source": "https://github.com/doctrine/dbal/tree/3.3.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -330,7 +330,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-20T18:37:29+00:00"
|
||||
"time": "2022-04-05T09:50:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
|
|
@ -1511,23 +1511,24 @@
|
|||
},
|
||||
{
|
||||
"name": "kwaadpepper/crud-policies",
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Kwaadpepper/crud-policies.git",
|
||||
"reference": "b877abff301bdf29195ac21f9e6685c71ce494f6"
|
||||
"reference": "205b8a16c99c362fc78422eeb31c0ba618a3d351"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Kwaadpepper/crud-policies/zipball/b877abff301bdf29195ac21f9e6685c71ce494f6",
|
||||
"reference": "b877abff301bdf29195ac21f9e6685c71ce494f6",
|
||||
"url": "https://api.github.com/repos/Kwaadpepper/crud-policies/zipball/205b8a16c99c362fc78422eeb31c0ba618a3d351",
|
||||
"reference": "205b8a16c99c362fc78422eeb31c0ba618a3d351",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/dbal": "^3.1",
|
||||
"facade/flare-client-php": "^1.8",
|
||||
"illuminate/support": "~7|~8",
|
||||
"illuminate/support": "~8",
|
||||
"kwaadpepper/enum": "^2.0",
|
||||
"laravel/framework": "~7|~8",
|
||||
"laravel/framework": "~8",
|
||||
"mews/purifier": "^3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
@ -1570,9 +1571,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Kwaadpepper/crud-policies/issues",
|
||||
"source": "https://github.com/Kwaadpepper/crud-policies/tree/0.2.1"
|
||||
"source": "https://github.com/Kwaadpepper/crud-policies/tree/0.3.6"
|
||||
},
|
||||
"time": "2021-11-17T11:35:53+00:00"
|
||||
"time": "2022-05-04T13:19:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kwaadpepper/enum",
|
||||
|
|
@ -1750,16 +1751,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v8.83.5",
|
||||
"version": "v8.83.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "33b1b981266e3a19fbc826b60c4a6847e311ac95"
|
||||
"reference": "ac6e16bb56510eda2e373c6365d5c92da3fd559d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/33b1b981266e3a19fbc826b60c4a6847e311ac95",
|
||||
"reference": "33b1b981266e3a19fbc826b60c4a6847e311ac95",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/ac6e16bb56510eda2e373c6365d5c92da3fd559d",
|
||||
"reference": "ac6e16bb56510eda2e373c6365d5c92da3fd559d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1919,7 +1920,7 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2022-03-15T13:37:44+00:00"
|
||||
"time": "2022-04-19T15:01:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
|
|
@ -1982,16 +1983,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/tinker",
|
||||
"version": "v2.7.1",
|
||||
"version": "v2.7.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/tinker.git",
|
||||
"reference": "1e2d500585a4e546346fadd3adc6f9c1a97e15f4"
|
||||
"reference": "dff39b661e827dae6e092412f976658df82dbac5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/tinker/zipball/1e2d500585a4e546346fadd3adc6f9c1a97e15f4",
|
||||
"reference": "1e2d500585a4e546346fadd3adc6f9c1a97e15f4",
|
||||
"url": "https://api.github.com/repos/laravel/tinker/zipball/dff39b661e827dae6e092412f976658df82dbac5",
|
||||
"reference": "dff39b661e827dae6e092412f976658df82dbac5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2044,9 +2045,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/tinker/issues",
|
||||
"source": "https://github.com/laravel/tinker/tree/v2.7.1"
|
||||
"source": "https://github.com/laravel/tinker/tree/v2.7.2"
|
||||
},
|
||||
"time": "2022-03-15T15:25:01+00:00"
|
||||
"time": "2022-03-23T12:38:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/ui",
|
||||
|
|
@ -2298,16 +2299,16 @@
|
|||
},
|
||||
{
|
||||
"name": "league/mime-type-detection",
|
||||
"version": "1.9.0",
|
||||
"version": "1.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/mime-type-detection.git",
|
||||
"reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69"
|
||||
"reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/aa70e813a6ad3d1558fc927863d47309b4c23e69",
|
||||
"reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69",
|
||||
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
|
||||
"reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2338,7 +2339,7 @@
|
|||
"description": "Mime-type detection for Flysystem",
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/mime-type-detection/issues",
|
||||
"source": "https://github.com/thephpleague/mime-type-detection/tree/1.9.0"
|
||||
"source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2350,7 +2351,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-21T11:48:40+00:00"
|
||||
"time": "2022-04-17T13:12:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mews/purifier",
|
||||
|
|
@ -2435,16 +2436,16 @@
|
|||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "2.4.0",
|
||||
"version": "2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "d7fd7450628561ba697b7097d86db72662f54aef"
|
||||
"reference": "4192345e260f1d51b365536199744b987e160edc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7fd7450628561ba697b7097d86db72662f54aef",
|
||||
"reference": "d7fd7450628561ba697b7097d86db72662f54aef",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/4192345e260f1d51b365536199744b987e160edc",
|
||||
"reference": "4192345e260f1d51b365536199744b987e160edc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2518,7 +2519,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||
"source": "https://github.com/Seldaek/monolog/tree/2.4.0"
|
||||
"source": "https://github.com/Seldaek/monolog/tree/2.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2530,7 +2531,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-14T12:44:37+00:00"
|
||||
"time": "2022-04-08T15:43:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
|
|
@ -3605,16 +3606,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.4.5",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "d8111acc99876953f52fe16d4c50eb60940d49ad"
|
||||
"reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/d8111acc99876953f52fe16d4c50eb60940d49ad",
|
||||
"reference": "d8111acc99876953f52fe16d4c50eb60940d49ad",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/900275254f0a1a2afff1ab0e11abd5587a10e1d6",
|
||||
"reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3684,7 +3685,7 @@
|
|||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.5"
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3700,7 +3701,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-24T12:45:35+00:00"
|
||||
"time": "2022-03-31T17:09:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
|
|
@ -3770,16 +3771,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.5.0",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3817,7 +3818,7 @@
|
|||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3833,20 +3834,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-12T14:48:14+00:00"
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/error-handler",
|
||||
"version": "v5.4.3",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/error-handler.git",
|
||||
"reference": "c4ffc2cd919950d13c8c9ce32a70c70214c3ffc5"
|
||||
"reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/c4ffc2cd919950d13c8c9ce32a70c70214c3ffc5",
|
||||
"reference": "c4ffc2cd919950d13c8c9ce32a70c70214c3ffc5",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/060bc01856a1846e3e4385261bc9ed11a1dd7b6a",
|
||||
"reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3888,7 +3889,7 @@
|
|||
"description": "Provides tools to manage errors and ease debugging PHP code",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/error-handler/tree/v5.4.3"
|
||||
"source": "https://github.com/symfony/error-handler/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3904,7 +3905,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
"time": "2022-03-18T16:21:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
|
|
@ -3993,16 +3994,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher-contracts",
|
||||
"version": "v2.5.0",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
|
||||
"reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a"
|
||||
"reference": "f98b54df6ad059855739db6fcbc2d36995283fe1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a",
|
||||
"reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1",
|
||||
"reference": "f98b54df6ad059855739db6fcbc2d36995283fe1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4052,7 +4053,7 @@
|
|||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0"
|
||||
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -4068,7 +4069,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-12T14:48:14+00:00"
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
|
|
@ -4208,16 +4209,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v5.4.6",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "d41f29ae9af1b5f40c7ebcddf09082953229411d"
|
||||
"reference": "509243b9b3656db966284c45dffce9316c1ecc5c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/d41f29ae9af1b5f40c7ebcddf09082953229411d",
|
||||
"reference": "d41f29ae9af1b5f40c7ebcddf09082953229411d",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/509243b9b3656db966284c45dffce9316c1ecc5c",
|
||||
"reference": "509243b9b3656db966284c45dffce9316c1ecc5c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4300,7 +4301,7 @@
|
|||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v5.4.6"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -4316,20 +4317,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-05T21:14:51+00:00"
|
||||
"time": "2022-04-02T06:04:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mime",
|
||||
"version": "v5.4.3",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mime.git",
|
||||
"reference": "e1503cfb5c9a225350f549d3bb99296f4abfb80f"
|
||||
"reference": "92d27a34dea2e199fa9b687e3fff3a7d169b7b1c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/e1503cfb5c9a225350f549d3bb99296f4abfb80f",
|
||||
"reference": "e1503cfb5c9a225350f549d3bb99296f4abfb80f",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/92d27a34dea2e199fa9b687e3fff3a7d169b7b1c",
|
||||
"reference": "92d27a34dea2e199fa9b687e3fff3a7d169b7b1c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4383,7 +4384,7 @@
|
|||
"mime-type"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mime/tree/v5.4.3"
|
||||
"source": "https://github.com/symfony/mime/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -4399,7 +4400,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
"time": "2022-03-11T16:08:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
|
|
@ -5220,16 +5221,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v5.4.5",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "95440409896f90a5f85db07a32b517ecec17fa4c"
|
||||
"reference": "38a44b2517b470a436e1c944bf9b9ba3961137fb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/95440409896f90a5f85db07a32b517ecec17fa4c",
|
||||
"reference": "95440409896f90a5f85db07a32b517ecec17fa4c",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/38a44b2517b470a436e1c944bf9b9ba3961137fb",
|
||||
"reference": "38a44b2517b470a436e1c944bf9b9ba3961137fb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5262,7 +5263,7 @@
|
|||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.5"
|
||||
"source": "https://github.com/symfony/process/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5278,7 +5279,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-30T18:16:22+00:00"
|
||||
"time": "2022-03-18T16:18:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
|
|
@ -5372,22 +5373,22 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v2.5.0",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc"
|
||||
"reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
|
||||
"reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"psr/container": "^1.1",
|
||||
"symfony/deprecation-contracts": "^2.1"
|
||||
"symfony/deprecation-contracts": "^2.1|^3"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-psr": "<1.1|>=2"
|
||||
|
|
@ -5435,7 +5436,7 @@
|
|||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.5.0"
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5451,7 +5452,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-04T16:48:04+00:00"
|
||||
"time": "2022-03-13T20:07:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
|
|
@ -5541,16 +5542,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v5.4.6",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "a7ca9fdfffb0174209440c2ffa1dee228e15d95b"
|
||||
"reference": "e1eb790575202ee3ac2659f55b93b05853726f8e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/a7ca9fdfffb0174209440c2ffa1dee228e15d95b",
|
||||
"reference": "a7ca9fdfffb0174209440c2ffa1dee228e15d95b",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/e1eb790575202ee3ac2659f55b93b05853726f8e",
|
||||
"reference": "e1eb790575202ee3ac2659f55b93b05853726f8e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5618,7 +5619,7 @@
|
|||
"description": "Provides tools to internationalize your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation/tree/v5.4.6"
|
||||
"source": "https://github.com/symfony/translation/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5634,20 +5635,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-02T12:56:28+00:00"
|
||||
"time": "2022-03-24T17:09:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
"version": "v2.5.0",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation-contracts.git",
|
||||
"reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e"
|
||||
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e",
|
||||
"reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1211df0afa701e45a04253110e959d4af4ef0f07",
|
||||
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5696,7 +5697,7 @@
|
|||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v2.5.0"
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v2.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5712,7 +5713,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-17T14:20:01+00:00"
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
|
|
@ -7040,16 +7041,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "05333065c88f9518e08b094bdd10a4a9e538b2d4"
|
||||
"reference": "129a63b3bc7caeb593c224c41f420675e63cfefc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/05333065c88f9518e08b094bdd10a4a9e538b2d4",
|
||||
"reference": "05333065c88f9518e08b094bdd10a4a9e538b2d4",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/129a63b3bc7caeb593c224c41f420675e63cfefc",
|
||||
"reference": "129a63b3bc7caeb593c224c41f420675e63cfefc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7078,9 +7079,9 @@
|
|||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.4.1"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.4.5"
|
||||
},
|
||||
"time": "2022-03-29T09:28:16+00:00"
|
||||
"time": "2022-04-22T11:11:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
|
@ -7402,16 +7403,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.19",
|
||||
"version": "9.5.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "35ea4b7f3acabb26f4bb640f8c30866c401da807"
|
||||
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/35ea4b7f3acabb26f4bb640f8c30866c401da807",
|
||||
"reference": "35ea4b7f3acabb26f4bb640f8c30866c401da807",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
|
||||
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7489,7 +7490,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.19"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7501,7 +7502,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-15T09:57:31+00:00"
|
||||
"time": "2022-04-01T12:37:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
|
@ -7869,16 +7870,16 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
"version": "5.1.3",
|
||||
"version": "5.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||
"reference": "388b6ced16caa751030f6a69e588299fa09200ac"
|
||||
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac",
|
||||
"reference": "388b6ced16caa751030f6a69e588299fa09200ac",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
||||
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7920,7 +7921,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.3"
|
||||
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7928,7 +7929,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-09-28T05:52:38+00:00"
|
||||
"time": "2022-04-03T09:37:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ return [
|
|||
/*
|
||||
* Laravel Framework Service Providers...
|
||||
*/
|
||||
|
||||
Illuminate\Auth\AuthServiceProvider::class,
|
||||
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||
Illuminate\Bus\BusServiceProvider::class,
|
||||
|
|
@ -169,6 +170,7 @@ return [
|
|||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
|
|
@ -226,6 +228,9 @@ return [
|
|||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
'Carbon' => 'Carbon\Carbon',
|
||||
|
||||
// * Application custom
|
||||
'TicketType' => App\Enums\TicketType::class,
|
||||
'TicketStatus' => App\Enums\TicketStatus::class,
|
||||
'Utils' => App\Lib\Utils::class,
|
||||
|
|
@ -238,7 +243,8 @@ return [
|
|||
'CellType' => App\Enums\Planning\CellType::class,
|
||||
'ActivityType' => App\Enums\Planning\ActivityType::class,
|
||||
'TableRow' => App\Enums\Planning\TableRow::class,
|
||||
'Carbon' => 'Carbon\Carbon'
|
||||
'ActivityEnum' => App\Enums\MacroSchedule\Activity::class,
|
||||
'ProjectStatus' => App\Enums\ProjectStatus::class,
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
8
config/macroschedule.php
Normal file
8
config/macroschedule.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'numberOfWeeksToShow' => 3,
|
||||
'totalAvailableDevs' => 6,
|
||||
// * En pourcentage
|
||||
'loadDevThreshold' => 0.5,
|
||||
];
|
||||
30
database/factories/MacroSchedule/ActivityFactory.php
Normal file
30
database/factories/MacroSchedule/ActivityFactory.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories\MacroSchedule;
|
||||
|
||||
use App\Models\MacroSchedule\Activity;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class ActivityFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Activity::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
\mt_srand();
|
||||
return [
|
||||
'load' => \mt_rand(1, 3),
|
||||
'comment' => $this->faker->sentence(mt_rand(2, 6))
|
||||
];
|
||||
}
|
||||
}
|
||||
29
database/factories/MacroSchedule/MarkerFactory.php
Normal file
29
database/factories/MacroSchedule/MarkerFactory.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories\MacroSchedule;
|
||||
|
||||
use App\Models\MacroSchedule\Marker;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class MarkerFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Marker::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
\mt_srand();
|
||||
return [
|
||||
'label' => $this->faker->sentence(mt_rand(2, 6))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
71
database/seeders/MacroScheduleSeeder.php
Normal file
71
database/seeders/MacroScheduleSeeder.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Enums\MacroSchedule\Activity;
|
||||
use App\Models\MacroSchedule\Activity as MacroScheduleActivity;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class MacroScheduleSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
/** Reset all Activities */
|
||||
DB::table((new MacroScheduleActivity())->getTable())->truncate();
|
||||
|
||||
\mt_srand();
|
||||
|
||||
$startDate = Carbon::now()->subWeeks(1)->setTime(0, 0);
|
||||
$activityDays = function () {
|
||||
return \mt_rand(1, 3);
|
||||
};
|
||||
$activityGutter = function () {
|
||||
return mt_rand(0, 6);
|
||||
};
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Project> $projects */
|
||||
$projects = Project::all();
|
||||
|
||||
$projects->map(function (Project $project) use ($startDate, $activityDays, $activityGutter) {
|
||||
|
||||
$projectStart = $startDate->clone();
|
||||
|
||||
/** @var \Kwaadpepper\Enum\BaseEnum[]<\App\Enums\MacroSchedule\Activity> */
|
||||
$activities = Activity::toArray();
|
||||
|
||||
foreach ($activities as $activityEnum) {
|
||||
|
||||
/** @var integer Activity length in days */
|
||||
$activityLength = $activityDays();
|
||||
|
||||
/** @var integer Activity gutter length in days */
|
||||
$activityGutterLength = $activityGutter();
|
||||
|
||||
/** @var \App\Models\MacroSchedule\Activity */
|
||||
$modelPlanning = MacroScheduleActivity::factory(1)->make()->first();
|
||||
|
||||
$activityModel = new MacroScheduleActivity();
|
||||
$activityModel->setStartDate($projectStart, rand(0, 1));
|
||||
$projectStart = $projectStart->addDays($activityLength);
|
||||
$activityModel->setEndDate($projectStart, rand(0, 1));
|
||||
|
||||
$activityModel->comment = $modelPlanning->comment;
|
||||
$activityModel->load = $modelPlanning->load;
|
||||
$activityModel->activity = $activityEnum;
|
||||
$activityModel->project()->associate($project);
|
||||
$activityModel->saveOrFail();
|
||||
|
||||
// * Sub Gutter to period
|
||||
$projectStart = $projectStart->addDays($activityGutterLength);
|
||||
} //end foreach
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Models\User;
|
||||
use App\Models\WeekPlanning;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class WeekPlanningSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
WeekPlanning::truncate();
|
||||
/** \App\Models\User $user */
|
||||
foreach (User::where('role', Role::developper())->get() as $user) {
|
||||
WeekPlanning::factory(13)->create([
|
||||
'start' => Carbon::now()->startOfWeek(),
|
||||
'user_id' => $user->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
jsconfig.json
Normal file
5
jsconfig.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"include": [
|
||||
"./resources/js/**/*"
|
||||
]
|
||||
}
|
||||
2608
package-lock.json
generated
2608
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -20,7 +20,7 @@
|
|||
"laravel-mix": "^6.0.16",
|
||||
"laravel-mix-polyfill": "^3.0.1",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.29.1",
|
||||
"moment": "^2.29.3",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
|
|
|
|||
72
resources/js/app.js
vendored
72
resources/js/app.js
vendored
|
|
@ -3,75 +3,16 @@ require('./session_watch');
|
|||
//window.Vue = require('vue/dist/vue.min.js');
|
||||
import Vue from 'vue'
|
||||
import moment from 'moment'
|
||||
import vSelect from "vue-select"
|
||||
import 'moment/locale/fr'
|
||||
import vSelect from 'vue-select'
|
||||
|
||||
moment.locale('fr')
|
||||
|
||||
// v-select
|
||||
Vue.component('v-select', vSelect)
|
||||
|
||||
// Configure moment
|
||||
moment.locale('fr', {
|
||||
months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
|
||||
monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
|
||||
monthsParseExact : true,
|
||||
weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
|
||||
weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
|
||||
weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
|
||||
weekdaysParseExact : true,
|
||||
longDateFormat : {
|
||||
LT : 'HH:mm',
|
||||
LTS : 'HH:mm:ss',
|
||||
L : 'DD/MM/YYYY',
|
||||
LL : 'D MMMM YYYY',
|
||||
LLL : 'D MMMM YYYY HH:mm',
|
||||
LLLL : 'dddd D MMMM YYYY HH:mm'
|
||||
},
|
||||
calendar : {
|
||||
sameDay : '[Aujourd’hui à] LT',
|
||||
nextDay : '[Demain à] LT',
|
||||
nextWeek : 'dddd [à] LT',
|
||||
lastDay : '[Hier à] LT',
|
||||
lastWeek : 'dddd [dernier à] LT',
|
||||
sameElse : 'L'
|
||||
},
|
||||
relativeTime : {
|
||||
future : 'dans %s',
|
||||
past : 'il y a %s',
|
||||
s : 'quelques secondes',
|
||||
m : 'une minute',
|
||||
mm : '%d minutes',
|
||||
h : 'une heure',
|
||||
hh : '%d heures',
|
||||
d : 'un jour',
|
||||
dd : '%d jours',
|
||||
M : 'un mois',
|
||||
MM : '%d mois',
|
||||
y : 'un an',
|
||||
yy : '%d ans'
|
||||
},
|
||||
dayOfMonthOrdinalParse : /\d{1,2}(er|e)/,
|
||||
ordinal : function (number) {
|
||||
return number + (number === 1 ? 'er' : 'e');
|
||||
},
|
||||
meridiemParse : /PD|MD/,
|
||||
isPM : function (input) {
|
||||
return input.charAt(0) === 'M';
|
||||
},
|
||||
// In case the meridiem units are not separated around 12, then implement
|
||||
// this function (look at locale/id.js for an example).
|
||||
// meridiemHour : function (hour, meridiem) {
|
||||
// return /* 0-23 hour, given meridiem token and hour 1-12 */ ;
|
||||
// },
|
||||
meridiem : function (hours, minutes, isLower) {
|
||||
return hours < 12 ? 'PD' : 'MD';
|
||||
},
|
||||
week : {
|
||||
dow : 1, // Monday is the first day of the week.
|
||||
doy : 4 // Used to determine first week of the year.
|
||||
}
|
||||
});
|
||||
|
||||
moment.locale('fr');
|
||||
Vue.prototype.moment = moment
|
||||
Vue.prototype.bootstrap = bootstrap
|
||||
|
||||
// Register modules
|
||||
const modules = require.context("./modules", true, /\.js$/i);
|
||||
|
|
@ -102,6 +43,9 @@ Vue.filter('str_limit', function (value, size) {
|
|||
}
|
||||
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 });
|
||||
|
|
|
|||
755
resources/js/components/MacroPlanningComponent.vue
Normal file
755
resources/js/components/MacroPlanningComponent.vue
Normal file
|
|
@ -0,0 +1,755 @@
|
|||
<template>
|
||||
<!-- TODO: order by name, status -->
|
||||
<div ref="macroschedule" class="macroschedule">
|
||||
<div class="row mb-3">
|
||||
<div class="col-1">
|
||||
<button class="btn btn-primary" @click.prevent="goPreviousWeek()">←</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h1 class="text-secondary">Plan de charge</h1>
|
||||
<div class="input-group">
|
||||
<label for="scheduleDateSelect" class="input-group-text">Voir date : </label>
|
||||
<input id="scheduleDateSelect" class="form-control" type="date" :value="startMoment.format('YYYY-MM-DD')" @input="showMacroScheduleDate($event.target.value)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<fieldset>
|
||||
<legend>Légende</legend>
|
||||
<template v-if="isAdmin">
|
||||
<span v-for="activityEnum of activitiesEnum" :key="activityEnum.value" :class="'p-1 m-2 border badge badge-activity-' + activityEnum.value">{{ activityEnum.label }}</span>
|
||||
<span class="p-1 m-2 border badge badge-marker">Marqueur</span>
|
||||
<span class="p-1 m-2 border badge badge-today">Aujourd'hui</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="p-1 m-2 border badge badge-activity-light">Libre</span>
|
||||
<span class="p-1 m-2 border badge badge-activity-dark">Occupé</span>
|
||||
</template>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button class="btn btn-primary" @click.prevent="goNextWeek()">→</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" rowspan="3" class="align-middle">{{ isAdmin ? 'Projets' : '' }}</th>
|
||||
<th scope="col" rowspan="3" class="align-middle">{{ isAdmin ? 'Statut' : '' }}</th>
|
||||
<th v-if="daysThisMonth.length" :colspan="daysThisMonth.length" class="text-center">
|
||||
{{ daysThisMonth[0].format('MMMM') | ucfirst }}
|
||||
</th>
|
||||
<th v-if="daysNextMonth.length" :colspan="daysNextMonth.length" class="text-center">
|
||||
{{ daysNextMonth[0].format('MMMM') | ucfirst }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th v-for="weekNumber in macroscheduleConfig.numberOfWeeksToShow" :key="weekNumber" :colspan="7 - 2" class="text-center">
|
||||
SEMAINE {{ currentWeek + weekNumber - 1}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<template v-for="(currentDay, index) of daysToShow">
|
||||
<th scope="col" v-if="dayShouldbeenSeen(currentDay)" :key="index" :class="'dayCell p-0 text-center ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')">
|
||||
{{ currentDay.format('dd') }} {{ String(currentDay.date()).padStart(2, '0') }}
|
||||
</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-if="isAdmin">
|
||||
<tr v-for="project of projects" :key="project.id" class="projectRow">
|
||||
<th scope="col">
|
||||
<span :class="'rounded-1 p-1 project text-nowrap text-truncate overflow-hidden bg-enum-'+project.status">{{ project.name | str_limit(45) }}</span>
|
||||
</th>
|
||||
<td>
|
||||
<span class="fw-bold text-nowrap text-truncate overflow-hidden">{{ projectStatusEnum[project.status].label }}</span>
|
||||
</td>
|
||||
<template v-for="(currentDay, index) of daysToShow">
|
||||
<td :class="'p-0 border border-dark activities ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')" v-if="dayShouldbeenSeen(currentDay)" :key="index">
|
||||
<div
|
||||
v-for="(activity, index) in [getActivity(project, getMorningMoment(currentDay)), getActivity(project, getAfterNoonMoment(currentDay))]"
|
||||
:key="index"
|
||||
:class="getActivityClass(activity)+ ' activity ' + (hoverActivityId === activity.id ? 'hover' : '')"
|
||||
@click="showModale(project, activity, currentDay, index === 0)"
|
||||
@mouseover="setActivityHover(activity)"
|
||||
@mouseleave="setActivityHover(activity, false)">
|
||||
<div :title="getActivityTooltip(activity)" data-bs-toggle="tooltip">
|
||||
|
||||
</div>
|
||||
<template v-if="projectHasMarkersForTheDay(currentDay, project)">
|
||||
<div
|
||||
class="marker"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-html="true"
|
||||
:title="getMarkersTooltip(currentDay, project)"></div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
<tr class="projectRow">
|
||||
<th scope="col" class="border-end-0 d-flex justify-content-between"><span class="fw-bold h5">Disponibilité</span></th>
|
||||
<td class="border-start-0"></td>
|
||||
<template v-for="(currentDay, index) of daysToShow">
|
||||
<td :class="'p-0 border border-dark activities ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')" v-if="dayShouldbeenSeen(currentDay)" :key="index">
|
||||
<template v-if="isAdmin">
|
||||
<div class="activity text-center" :style="{ 'background-color' : 'rgba(0,0,0,'+ getLoadPercent(getMorningMoment(currentDay)) }">
|
||||
{{ getLoad(getMorningMoment(currentDay)) > 1 ? '🔥' : ' ' }}
|
||||
</div>
|
||||
<div class="activity text-center" :style="{ 'background-color' : 'rgba(0,0,0,'+ getLoadPercent(getAfterNoonMoment(currentDay)) }">
|
||||
{{ getLoad(getAfterNoonMoment(currentDay)) > 1 ? '🔥' : ' ' }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div :class="'activity text-center ' +(getLoad(getMorningMoment(currentDay)) > macroscheduleConfig.loadDevThreshold ? 'bg-dark' : 'bg-light')"> </div>
|
||||
<div :class="'activity text-center ' +(getLoad(getAfterNoonMoment(currentDay)) > macroscheduleConfig.loadDevThreshold ? 'bg-dark' : 'bg-light')"> </div>
|
||||
</template>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
- une Date début, et un choix matin/après-midi
|
||||
- une Date Fin, et un choix matin/après-midi
|
||||
- une activité (cf. liste annexe ci-dessous)
|
||||
- une charge (en nombre d’hommes occupés)
|
||||
- un commentaire
|
||||
-->
|
||||
<div ref="editModal" class="modal fade" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true" >
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalLabel" ref="modalLabel">Activité pour {{ modaleProps.name }}</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div v-if="error" class="alert alert-danger" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="form-group mb-3">
|
||||
<label for="categorie" class="col-form-label text-md-right ms-5">Activité</label>
|
||||
<select id="categorie" ref="type" class="form-select" aria-label="Sélectionnez une catégorie" :disabled="modalLoading" v-model="modaleProps.activity" required>
|
||||
<option :value="undefined" disabled>Sélectionnez une activité</option>
|
||||
<option v-for="activityEnum in activitiesEnum" :key="activityEnum.value" :value="activityEnum.value" :class="activityEnum.value">{{ activityEnum.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="start" class="col-form-label text-md-right ms-5">Date début</label>
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" id="start" type="date" name="start" :value="moment(modaleProps.start).format('YYYY-MM-DD')" @input="updateDateField($event, 'start')" :disabled="modalLoading">
|
||||
<select class="form-select" :disabled="modalLoading" v-model="modaleProps.startMorning" @input="updateHourField($event, 'start')">
|
||||
<option value="1" :selected="moment(modaleProps.start).format('HH') === '00'">Matin</option>
|
||||
<option value="0" :selected="moment(modaleProps.start).format('HH') === '12'">Midi</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="end" class="col-form-label text-md-right ms-5">Date fin</label>
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" id="end" type="date" name="end" :value="moment(modaleProps.end).format('YYYY-MM-DD')" @input="updateDateField($event, 'end')" :disabled="modalLoading">
|
||||
<select class="form-select" :disabled="modalLoading" v-model="modaleProps.endMorning" @input="updateHourField($event, 'end')">
|
||||
<option value="1" :selected="moment(modaleProps.end).format('HH') === '11'">Midi</option>
|
||||
<option value="0" :selected="moment(modaleProps.end).format('HH') === '23'">Soir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="comment" class="col-form-label text-md-right ms-5">Commentaire</label>
|
||||
<textarea ref="comment" class="form-control" name="comment" id="comment" rows="2" placeholder="" @keydown="limitLines($event)" v-model="modaleProps.comment" :disabled="modalLoading"></textarea>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="load" class="col-form-label text-md-right ms-5">Charge <i>(nombre de personnes consommées sur la période)</i></label>
|
||||
<input type="number" class="form-control" name="load" id="load" v-model="modaleProps.load" :disabled="modalLoading" min="1"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-8 text-center">
|
||||
<button class="btn btn-primary me-3" type="button" :disabled="modalLoading" @click="saveActivity()">
|
||||
<span v-if="modalLoading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
{{ modalLoading ? 'Chargement...' : 'Valider' }}
|
||||
</button>
|
||||
<button class="btn btn-danger" @click="modale.hide();$nextTick(refreshTooltips)" :disabled="modalLoading">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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)
|
||||
this.$nextTick(this.refreshTooltips)
|
||||
this.modale = new bootstrap.Modal(this.$refs.editModal)
|
||||
this.$refs.editModal.addEventListener('hide.bs.modal', function (e) {
|
||||
if(self.modalLoading) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
textLimit: 254,
|
||||
hoverActivityId: null,
|
||||
error: '',
|
||||
modaleProps: {
|
||||
activity: null,
|
||||
start: '',
|
||||
startMorning: 1,
|
||||
end: '',
|
||||
endMorning: 1,
|
||||
load: 0,
|
||||
comment: '',
|
||||
name: '',
|
||||
project: null
|
||||
},
|
||||
currentActivity: null,
|
||||
projects: [],
|
||||
activitiesEnum: [],
|
||||
projectStatusEnum: [],
|
||||
macroscheduleConfig: [],
|
||||
loadCollection: {},
|
||||
modale: null,
|
||||
modalLoading: false,
|
||||
tooltipTriggerList: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
todayMoment() {
|
||||
return this.moment(this.today)
|
||||
},
|
||||
startMoment() {
|
||||
return this.moment(this.startDay)
|
||||
},
|
||||
selectedMoment() {
|
||||
return this.moment(this.startMoment.week(), 'w').hours(0).minutes(0).seconds(0).milliseconds(0).add(1, 'day').startOf('week')
|
||||
},
|
||||
currentMonthName() {
|
||||
return this.selectedMoment.format('MMMM')
|
||||
},
|
||||
currentWeek() {
|
||||
return this.selectedMoment.week()
|
||||
},
|
||||
daysToShow() {
|
||||
let days = [], startMoment = this.selectedMoment.clone()
|
||||
for (let i = this.macroscheduleConfig.numberOfWeeksToShow; i > 0; i--) {
|
||||
for(let dayNumber = 0; dayNumber < 7; dayNumber++) {
|
||||
days.push(startMoment)
|
||||
startMoment = startMoment.clone().add(1, 'day')
|
||||
}
|
||||
}
|
||||
return days
|
||||
},
|
||||
daysThisMonth() {
|
||||
let days = this.daysToShow, currentMonth = days[0]?.month(),
|
||||
out = []
|
||||
for (let day of days) {
|
||||
if(currentMonth === day.month() && this.dayShouldbeenSeen(day)) {
|
||||
out.push(day)
|
||||
}
|
||||
}
|
||||
return out
|
||||
},
|
||||
daysNextMonth() {
|
||||
let days = this.daysToShow, currentMonth = days[0]?.month(),
|
||||
out = []
|
||||
for (let day of days) {
|
||||
if(currentMonth !== day.month() && this.dayShouldbeenSeen(day)) {
|
||||
out.push(day)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modaleProps: {
|
||||
handler(object) {
|
||||
// * Validate Date fields
|
||||
this.error = ''
|
||||
this.validateDateField(this.moment(this.modaleProps.start), 'start')
|
||||
this.validateDateField(this.moment(this.modaleProps.end), 'end')
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
currentActivity: {
|
||||
handler(activityObject) {
|
||||
if (activityObject.project_id) {
|
||||
let project = this.getProject(activityObject.project_id)
|
||||
// * Insert (or update) activity
|
||||
this.$set(project.activities, activityObject.id, Object.assign({}, activityObject))
|
||||
this.$nextTick(this.refreshTooltips)
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validateDateField(moment, fieldName) {
|
||||
if(!this.dayShouldbeenSeen(moment)) {
|
||||
this.error = 'La date de ' + (fieldName === 'start' ? 'début' : 'fin') + ' ne peut être un jour de weekend.'
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
updateDateField(e, fieldName) {
|
||||
let date = this.moment(this.modaleProps[fieldName]),
|
||||
time = Number.parseInt(this.modaleProps[fieldName + 'Morning']) === 1 ?
|
||||
this.getMorningMoment(date, fieldName === 'start') :
|
||||
this.getAfterNoonMoment(date, fieldName === 'start'),
|
||||
input = this.moment(e.target.value)
|
||||
if(this.validateDateField(input, fieldName)) {
|
||||
this.error = ''
|
||||
this.$set(this.modaleProps, fieldName, input.format('YYYY-MM-DD') + 'T' + time.format('HH:mm:ss'))
|
||||
}
|
||||
},
|
||||
updateHourField(e, fieldName) {
|
||||
let date = this.moment(this.modaleProps[fieldName])
|
||||
if(Number.parseInt(e.target.value) === 1) {
|
||||
this.$set(this.modaleProps, fieldName, this.getMorningMoment(date, fieldName === 'start').format('YYYY-MM-DD\THH:mm:ss'))
|
||||
} else {
|
||||
this.$set(this.modaleProps, fieldName, this.getAfterNoonMoment(date, fieldName === 'start').format('YYYY-MM-DD\THH:mm:ss'))
|
||||
}
|
||||
},
|
||||
saveActivity() {
|
||||
let self = this,
|
||||
activity = Object.assign({}, this.currentActivity)
|
||||
|
||||
if(this.error.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.modalLoading = true
|
||||
|
||||
activity.activity = self.modaleProps.activity
|
||||
activity.start = self.modaleProps.start
|
||||
activity.end = self.modaleProps.end
|
||||
activity.load = self.modaleProps.load
|
||||
activity.comment = self.modaleProps.comment
|
||||
activity.project_id = self.modaleProps.project.id
|
||||
|
||||
axios.post(self.route('macroplanning.activity'), activity)
|
||||
.then(response => {
|
||||
self.error = ''
|
||||
self.sendToast('L\'activité à été mis à jour')
|
||||
self.$nextTick(() => {
|
||||
self.modalLoading = false
|
||||
self.modale.hide()
|
||||
activity.id = response.data.id
|
||||
self.$set(self, 'currentActivity', activity)
|
||||
})
|
||||
if(response.data.hasOverlap) {
|
||||
window.location.reload()
|
||||
}
|
||||
}).catch(error => {
|
||||
if(error.response.status === 429) {
|
||||
location.reload()
|
||||
}
|
||||
if(error.response.status === 422) {
|
||||
if(error.response.data.errors) {
|
||||
self.error = ''
|
||||
for(let id in error.response.data.errors) {
|
||||
let field = error.response.data.errors[id]
|
||||
self.error += field.join('\n') + '\n'
|
||||
}
|
||||
return
|
||||
} else if(error.response.data.message) {
|
||||
self.error = error.response.data.message
|
||||
return
|
||||
}
|
||||
}
|
||||
self.error = error
|
||||
}).then(() => {
|
||||
self.modalLoading = false
|
||||
self.$nextTick(self.refreshTooltips)
|
||||
})
|
||||
},
|
||||
showModale(project, activity, selectedMoment, isMorning = true) {
|
||||
|
||||
selectedMoment = selectedMoment.clone()
|
||||
|
||||
let self = this,
|
||||
startMoment = (activity.start ? this.moment(activity.start) :
|
||||
(isMorning ? this.getMorningMoment(selectedMoment, true) : this.getAfterNoonMoment(selectedMoment, true))),
|
||||
endMoment = (activity.end ? this.moment(activity.end) :
|
||||
(isMorning ? this.getMorningMoment(selectedMoment, false) : this.getAfterNoonMoment(selectedMoment, false)))
|
||||
|
||||
this.modaleProps = {
|
||||
activity: activity.activity,
|
||||
start: activity.start ?? startMoment.format('YYYY-MM-DD\THH:mm:ss'),
|
||||
startMorning: startMoment.format('HH') === '00' ? 1 : 0,
|
||||
end: activity.end ?? endMoment.format('YYYY-MM-DD\THH:mm:ss'),
|
||||
endMorning: endMoment.format('HH') === '11' ? 1 : 0,
|
||||
load: activity.load,
|
||||
comment: activity.comment,
|
||||
name: project.name,
|
||||
project: project
|
||||
}
|
||||
this.currentActivity = activity
|
||||
this.modale.show()
|
||||
|
||||
setTimeout(() => {
|
||||
self.$nextTick(self.refreshTooltips)
|
||||
}, 2000)
|
||||
},
|
||||
dayShouldbeenSeen(moment) {
|
||||
return moment.day() !== 6 && moment.day() !== 0
|
||||
},
|
||||
projectHasMarkersForTheDay(moment, project) {
|
||||
for(let marker of project.markers) {
|
||||
if (moment.isSame(marker.marker, 'day')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
setActivityHover(activity, activate = true) {
|
||||
if(!activate) {
|
||||
this.hoverActivityId = null
|
||||
} else {
|
||||
this.hoverActivityId = activity.activity !== undefined ? activity.id : ''
|
||||
}
|
||||
},
|
||||
|
||||
// * GETTERS
|
||||
getLoadPercent(moment) {
|
||||
let load = this.getLoad(moment)
|
||||
return load > 1 ? 1 : load
|
||||
},
|
||||
getLoad(moment) {
|
||||
return this.loadCollection[moment.format('YYYY-MM-DD 00:00:00')] ?? null
|
||||
},
|
||||
getActivityTooltip(activity) {
|
||||
let activityEnumLabel = activity.activity !== undefined ? (this.activitiesEnum[activity.activity] ? this.activitiesEnum[activity.activity].label : '') : ''
|
||||
return activity.activity !== undefined ? `${activityEnumLabel}, Charge ${activity.load} personnes, ${activity.comment}` : ''
|
||||
},
|
||||
getActivityClass(activity) {
|
||||
return activity ? `bg-enum-${activity.activity}` : 'bg-white'
|
||||
},
|
||||
getProject(project_id) {
|
||||
return this.projects[project_id]
|
||||
},
|
||||
getActivity(project, moment) {
|
||||
for(let activity of Object.values(project.activities)) {
|
||||
if(moment.isBetween(this.moment(activity.start, this.moment.DATETIME_LOCAL_SECONDS), this.moment(activity.end, this.moment.DATETIME_LOCAL_SECONDS), 'hour', '[]')) {
|
||||
return activity
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
getMorningMoment(moment, start = true) {
|
||||
moment = moment.clone()
|
||||
return start ? moment.hours(0).minutes(0).seconds(0) : moment.hours(11).minutes(59).seconds(0)
|
||||
},
|
||||
getAfterNoonMoment(moment, start = true) {
|
||||
moment = moment.clone()
|
||||
return start ? moment.hours(12).minutes(0).seconds(0) : moment.hours(23).minutes(59).seconds(0)
|
||||
},
|
||||
getMarkersTooltip(moment, project) {
|
||||
let tooltip = ''
|
||||
for(let marker of project.markers) {
|
||||
if (moment.isSame(marker.marker, 'day')) {
|
||||
tooltip += `${this.moment(marker.marker).format('DD/MM/YYYY')} ${marker.label}<br>`
|
||||
}
|
||||
}
|
||||
return tooltip
|
||||
},
|
||||
|
||||
// * NAVIGATION
|
||||
goPreviousWeek() {
|
||||
let moment = this.startMoment ?? this.todayMoment
|
||||
moment = this.moment(moment.week(), 'w')
|
||||
this.showMacroScheduleDate('', moment.subtract(1, 'week'))
|
||||
},
|
||||
goNextWeek() {
|
||||
let moment = this.startMoment ?? this.todayMoment
|
||||
moment = this.moment(moment.week(), 'w')
|
||||
this.showMacroScheduleDate('', moment.add(1, 'week'))
|
||||
},
|
||||
showMacroScheduleDate(dateString, moment = null) {
|
||||
let url = new URL(window.location)
|
||||
moment = moment ?? this.moment(dateString)
|
||||
location = url.origin+ url.pathname + '?date='+moment.format('DD/MM/YYYY')
|
||||
},
|
||||
|
||||
sendToast(message) {
|
||||
Toastify({
|
||||
text: message,
|
||||
duration: 3000,
|
||||
gravity: "top",
|
||||
position: "right",
|
||||
offset: {
|
||||
x: '2rem',
|
||||
y: '3rem'
|
||||
},
|
||||
style: {
|
||||
background: "linear-gradient(to right, #00317d, #0058ca)",
|
||||
}
|
||||
}).showToast();
|
||||
},
|
||||
refreshTooltips() {
|
||||
let exisitingToolips = document.querySelectorAll('.tooltip[role="tooltip"]')
|
||||
for(let tooltip of exisitingToolips) {
|
||||
tooltip.remove()
|
||||
}
|
||||
this.tooltipTriggerList = [].slice.call(this.$refs.macroschedule.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
this.tooltipTriggerList.map((tooltipTriggerEl) => {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl, { delay: 500 })
|
||||
})
|
||||
},
|
||||
limitLines(e) {
|
||||
let keynum, lines = this.$refs.comment.value.split('\n').length;
|
||||
// IE
|
||||
if(window.event) {
|
||||
keynum = e.keyCode;
|
||||
// Netscape/Firefox/Opera
|
||||
} else if(e.which) {
|
||||
keynum = e.which;
|
||||
}
|
||||
if(keynum != 8 && this.$refs.comment.value.length >= this.textLimit) {
|
||||
e.preventDefault()
|
||||
return false;
|
||||
}
|
||||
if(keynum == 13 && lines == this.$refs.comment.rows) {
|
||||
e.preventDefault()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './../../sass/variables';
|
||||
@import './../../sass/complement';
|
||||
@import "toastify-js/src/toastify.css";
|
||||
|
||||
.macroschedule {
|
||||
@each $index, $color in $enum-activities {
|
||||
.badge-activity-#{$index} {
|
||||
background-color: $color;
|
||||
color: a11y-color(#353535 ,$color);
|
||||
}
|
||||
}
|
||||
.badge-marker {
|
||||
background-color: rgba(255, 2, 204, 0.6);
|
||||
color: a11y-color(#353535 ,rgba(255, 2, 204, 0.6));
|
||||
}
|
||||
.badge-today {
|
||||
background-color: rgba(0, 255, 0, 0.6);
|
||||
color: a11y-color(#353535 ,rgba(0, 255, 0, 0.6));
|
||||
}
|
||||
.badge-activity-light {
|
||||
background-color: $light;
|
||||
color: a11y-color(#353535 ,$light);
|
||||
}
|
||||
.badge-activity-dark {
|
||||
background-color: $dark;
|
||||
color: a11y-color(#FFF ,$dark);
|
||||
}
|
||||
|
||||
.project {
|
||||
@each $index, $color in $enum-project-status {
|
||||
&.bg-enum-#{$index} {
|
||||
background-color: $color;
|
||||
color: a11y-color(#f6f6f6 ,$color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th.today,td.today > div {
|
||||
position: relative;
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0.2rem;
|
||||
transform: translateY(-50%);
|
||||
height: 110%;
|
||||
width: 0.3rem;
|
||||
background-color: rgba(0, 255, 0, 0.6);
|
||||
|
||||
}
|
||||
}
|
||||
tr:last-child > td.today {
|
||||
position: relative;
|
||||
&:after {
|
||||
content: '↑ Aujourd\'hui';
|
||||
position: absolute;
|
||||
bottom: -2.5rem;
|
||||
left: 0;
|
||||
background-color: rgb(0, 255, 0);
|
||||
padding: 0.2rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
border-radius: 0.2rem;
|
||||
width: 7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dayCell {
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
.projectRow {
|
||||
&:not(:first-child) {
|
||||
border-top: 2px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
.activities .activity {
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
$buttonBgColor: #4d4d4d;
|
||||
|
||||
.activity {
|
||||
position: relative;
|
||||
|
||||
.bg-white {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@each $index, $color in $enum-activities {
|
||||
&.bg-enum-#{$index} {
|
||||
background-color: $color;
|
||||
color: a11y-color(#353535 ,$color);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset button Style to nice one
|
||||
button {
|
||||
display: inline;
|
||||
border: none;
|
||||
padding: 0.6rem 3rem;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
background: $buttonBgColor;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background 250ms ease-in-out, transform 150ms ease;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
button[disabled] {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: darken($buttonBgColor, 10%);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0.1rem;
|
||||
left: 0.1rem;
|
||||
padding: 0;
|
||||
width: 1.8rem;
|
||||
height: 1.8rem;
|
||||
margin: 0;
|
||||
background-color: $buttonBgColor;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: darken($buttonBgColor, 10%);
|
||||
}
|
||||
&.btn-clear {
|
||||
color: darken($color: #fff, $amount: 5);
|
||||
}
|
||||
}
|
||||
|
||||
.infos {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.hover > div {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
& > div.marker {
|
||||
z-index: 200;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0.2rem;
|
||||
transform: translateY(-50%);
|
||||
height: 110%;
|
||||
width: 0.7rem;
|
||||
background-color: rgba(255, 2, 204, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
resources/js/components/MarkerLabelSelectComponent.vue
Normal file
52
resources/js/components/MarkerLabelSelectComponent.vue
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-select v-model="value" :options="labels" @search="addOption"/>
|
||||
<input type="hidden" :value="inputValue" :name="name" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
this.labels = JSON.parse(this.dbLabels)
|
||||
this.inputValue = this.dbValue
|
||||
this.value = {
|
||||
code: this.labels.length,
|
||||
label: this.inputValue
|
||||
}
|
||||
for (let label of this.labels) {
|
||||
label.default = true
|
||||
if (label.label == this.dbValue) {
|
||||
this.value = label
|
||||
}
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'name', 'dbValue', 'dbLabels'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
value: {},
|
||||
inputValue: '',
|
||||
labels: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.inputValue = this.value.label
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addOption(newOption) {
|
||||
if(newOption.length) {
|
||||
for (let key in this.labels) {
|
||||
if(!this.labels[key].default) {
|
||||
this.labels.splice(key, 1)
|
||||
}
|
||||
}
|
||||
this.labels.push({label: newOption, code: this.labels.length})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
37
resources/js/modules/confirm.js
vendored
Normal file
37
resources/js/modules/confirm.js
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
export default {
|
||||
methods: {
|
||||
confirm(title, text = '', self, after, options = {}) {
|
||||
if (!self) {
|
||||
throw Error('self is needed to set "this" on callback');
|
||||
}
|
||||
if (!after) {
|
||||
throw Error('callback is needed when using confirm');
|
||||
}
|
||||
let icon = options.icon ?? 'warning'
|
||||
let confirm = options.confirm ?? 'Oui'
|
||||
let cancel = options.confirm ?? 'Annuler'
|
||||
Swal.fire({
|
||||
title: title,
|
||||
text: text,
|
||||
icon: icon,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: confirm,
|
||||
cancelButtonText: cancel,
|
||||
reverseButtons: true
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
if (after) {
|
||||
after.apply(self, [result])
|
||||
}
|
||||
} else if (
|
||||
/* Read more about handling dismissals below */
|
||||
result.dismiss === Swal.DismissReason.cancel
|
||||
) {
|
||||
Swal.fire('L\'opération n\'as pas été effectuée')
|
||||
}
|
||||
})
|
||||
return false;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
11
resources/lang/en/models.php
Normal file
11
resources/lang/en/models.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'classes' => [
|
||||
'projects' => 'Projects',
|
||||
'markers' => 'Markers',
|
||||
'logs' => 'Logs',
|
||||
'files' => 'Files',
|
||||
'sites' => 'Sites',
|
||||
]
|
||||
];
|
||||
11
resources/lang/fr/models.php
Normal file
11
resources/lang/fr/models.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'classes' => [
|
||||
'projects' => 'Projets',
|
||||
'markers' => 'Marqueurs',
|
||||
'logs' => 'Journaux',
|
||||
'files' => 'Fichiers',
|
||||
'sites' => 'Sites',
|
||||
]
|
||||
];
|
||||
244
resources/sass/_complement.scss
vendored
Normal file
244
resources/sass/_complement.scss
vendored
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
// @function strip-unit($value)
|
||||
// @param {number with unit} $value
|
||||
@function strip-unit($value) {
|
||||
@return ($value / ($value * 0 + 1));
|
||||
}
|
||||
|
||||
// @function pow($base, $exponents)
|
||||
// Helper: Raise a number the power of another number
|
||||
// Adapted from: https://gist.github.com/hail2u/1964056
|
||||
//
|
||||
// @param {number} $base — The base number
|
||||
// @param {number} $exponents — The exponent to which to raise $base
|
||||
@function pow($base, $exponents) {
|
||||
$raised: 1;
|
||||
@for $i from 1 through $exponents {
|
||||
$raised: $raised * $base;
|
||||
}
|
||||
@return $raised;
|
||||
}
|
||||
|
||||
// @function luminance($color)
|
||||
// Helper: Calculate Luminance of a single color
|
||||
// Adapted from: https://medium.com/dev-channel/using-sass-to-automatically-pick-text-colors-4ba7645d2796
|
||||
// White luminance is 1, Black luminance is 0
|
||||
// To be used in other functions or mixins — creates non-standard CSS output:
|
||||
// Usage:
|
||||
// .sample { luminance: luminance(#c00); }
|
||||
// Output:
|
||||
// .sample { luminance: 0.1283736922; }
|
||||
//
|
||||
@function luminance($color) {
|
||||
// Thanks voxpelli for a very concise implementation of luminance measure in sass
|
||||
// Adapted from: https://gist.github.com/voxpelli/6304812
|
||||
$rgba: red($color), green($color), blue($color);
|
||||
$rgba2: ();
|
||||
@for $i from 1 through 3 {
|
||||
$rgb: nth($rgba, $i);
|
||||
$rgb: calc($rgb / 255);
|
||||
$rgb: if($rgb < .03928, calc($rgb / 12.92), pow(calc(($rgb + .055) / 1.055), 2));
|
||||
$rgba2: append($rgba2, $rgb);
|
||||
}
|
||||
@return (.2126 * nth($rgba2, 1) + .7152 * nth($rgba2, 2) + 0.0722 * nth($rgba2, 3))*100;
|
||||
}
|
||||
|
||||
// @function contrast-ratio($fg, $bg)
|
||||
// Helper: Calculate "readability" as defined by WCAG 2.1
|
||||
// Adapted from: https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js
|
||||
// Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
|
||||
// To be used in other functions or mixins — creates non-standard CSS output:
|
||||
// Usage:
|
||||
// .sample { color-contrast: contrast-ratio(#c00, #fff); }
|
||||
// Output:
|
||||
// .sample { color-contrast: 5.89; }
|
||||
//
|
||||
@function contrast-ratio($fg, $bg) {
|
||||
$luminance1: luminance($fg) + 0.05;
|
||||
$luminance2: luminance($bg) + 0.05;
|
||||
$ratio: calc($luminance1 / $luminance2);
|
||||
@if $luminance2 > $luminance1 {
|
||||
$ratio: calc(1 / $ratio);
|
||||
}
|
||||
// Round to a hundreth because 6.96 should not pass a ratio of 7.0
|
||||
$ratio: calc(round($ratio * 100) / 100);
|
||||
@return $ratio;
|
||||
}
|
||||
|
||||
// @function validate-font-size($size)
|
||||
// Helper: Depending on the unit recalculate a font size value into pixels if possible
|
||||
// To be used in other functions or mixins — creates non-standard CSS output:
|
||||
// Usage:
|
||||
// .sample { validate-font-size: validate-font-size(1em); }
|
||||
// Output:
|
||||
// .sample { validate-font-size: 16; }
|
||||
//
|
||||
@function validate-font-size($size) {
|
||||
@if unit($size) == 'em' or unit($size) == 'rem' or unit($size) == 'px' or unit($size) == '' {
|
||||
// Check if a flexible unit
|
||||
@if unit($size) == 'em' or unit($size) == 'rem' {
|
||||
// Need to convert to a pixel value. Let's not overcomplicate it with possible EM inheritence scale factors
|
||||
@return strip-unit($size * 16)
|
||||
}
|
||||
@if unit($size) == 'px' {
|
||||
// We expect PX, so strip the value and return it
|
||||
@return strip-unit($size);
|
||||
}
|
||||
@if unit($size) == '' {
|
||||
@return $size;
|
||||
}
|
||||
} @else {
|
||||
@error 'validate-font-size(): An unexpected font size unit was supplied.';
|
||||
}
|
||||
}
|
||||
|
||||
// @function get-ratio($level: 'AA', $size: 16, $bold: false)
|
||||
// Helper: Determine the correct ratio value to use based on font-size and WCAG Level
|
||||
// To be used in other functions or mixins — creates non-standard CSS output:
|
||||
// Usage:
|
||||
// .sample { get-ratio: get-ratio('AAA', 19, true); }
|
||||
// Output:
|
||||
// .sample { get-ratio: 4.5; }
|
||||
//
|
||||
@function get-ratio($level: 'AA', $size: 16, $bold: false) {
|
||||
// Default ratio
|
||||
$ratio: 4.5;
|
||||
@if $level == 'AAA' {
|
||||
$ratio: 7;
|
||||
}
|
||||
|
||||
// Make sure the size is valid. If the value is not EM, REM, or PX (preferred), we can't help
|
||||
$size: validate-font-size($size);
|
||||
|
||||
// Check font size
|
||||
@if $size < 24 {
|
||||
// Small text, use defaults
|
||||
// But:
|
||||
@if $size >= 19 and $bold == true {
|
||||
// Special case: Small text but also bold
|
||||
@if $level == 'AAA' {
|
||||
$ratio: 4.5;
|
||||
} @else {
|
||||
$ratio: 3;
|
||||
}
|
||||
}
|
||||
} @else {
|
||||
// Larger than 24
|
||||
$ratio: 3;
|
||||
@if $level == 'AAA' {
|
||||
$ratio: 4.5;
|
||||
}
|
||||
}
|
||||
@return $ratio;
|
||||
}
|
||||
|
||||
// @function light-or-dark($color)
|
||||
// Helper: Use contrast against white or black to determine if a color is "light" or "dark"
|
||||
// Adapted from: https://medium.com/dev-channel/using-sass-to-automatically-pick-text-colors-4ba7645d2796
|
||||
// To be used in other functions or mixins — creates non-standard CSS output:
|
||||
// Usage:
|
||||
// .sample { light-or-dark: light-or-dark(#c00); }
|
||||
// Output:
|
||||
// .sample { light-or-dark: "light"; }
|
||||
//
|
||||
@function light-or-dark($color) {
|
||||
$light-contrast: contrast-ratio($color, $white);
|
||||
$dark-contrast: contrast-ratio($color, $black);
|
||||
|
||||
@if $light-contrast > $dark-contrast {
|
||||
// Contrast against white is higher than against black, so, this is a dark color
|
||||
@return "dark";
|
||||
} @else {
|
||||
@return "light";
|
||||
}
|
||||
}
|
||||
|
||||
// @function color-contrast($color)
|
||||
// Helper: Returns black or white compared to a color, whichever produces the highest contrast
|
||||
// Usage:
|
||||
// .sample {
|
||||
// background-color: #c00;
|
||||
// color: color-contrast(#c00);
|
||||
// }
|
||||
// Output:
|
||||
// .sample {
|
||||
// background-color: #c00;
|
||||
// color: #fff;
|
||||
// }
|
||||
//
|
||||
// Named for the forthcoming CSS Module 5 spec function color-contrast();
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-contrast()
|
||||
//
|
||||
@function color-contrast($color) {
|
||||
$color-lod: light-or-dark($color);
|
||||
|
||||
@if ($color-lod == "dark") {
|
||||
@return $white;
|
||||
} @else {
|
||||
@return $black;
|
||||
}
|
||||
}
|
||||
|
||||
// @function a11y-color($fg, $bg, $level: 'AA', $size: 16, $bold: false)
|
||||
// Goal: Return a color that passes for the chosen WCAG level without changing the Hue of the color
|
||||
// Usage:
|
||||
// .sample {
|
||||
// background-color: #000;
|
||||
// color: a11y-color(#c0c, #000);
|
||||
// }
|
||||
// Output:
|
||||
// .sample {
|
||||
// background-color: #000;
|
||||
// color: #d200d2;
|
||||
// }
|
||||
//
|
||||
@function a11y-color($fg, $bg, $level: 'AA', $size: 16, $bold: false) {
|
||||
// Helper: make sure the font size value is acceptable
|
||||
$font-size: validate-font-size($size);
|
||||
// Helper: With the level, font size, and bold boolean, return the proper target ratio. 3.0, 4.5, or 7.0 expected
|
||||
$ratio: get-ratio($level, $font-size, $bold);
|
||||
// Calculate the first contrast ratio of the given pair
|
||||
$original-contrast: contrast-ratio($fg, $bg);
|
||||
|
||||
@if $original-contrast >= $ratio {
|
||||
// If we pass the ratio already, return the original color
|
||||
@return $fg;
|
||||
} @else {
|
||||
// Doesn't pass. Time to get to work
|
||||
// Should the color be lightened or darkened?
|
||||
$fg-lod: light-or-dark($fg);
|
||||
$bg-lod: light-or-dark($bg);
|
||||
|
||||
// Set a "step" value to lighten or darken a color
|
||||
// Note: Higher percentage steps means faster compile time, but we might overstep the required threshold too far with something higher than 4%
|
||||
$step: 1%;
|
||||
|
||||
// Run through some cases where we want to darken, or use a negative step value
|
||||
@if $fg-lod == 'light' and $bg-lod == 'light' {
|
||||
// Both are light colors, darken the fg
|
||||
$step: - $step;
|
||||
} @else if $fg-lod == 'dark' and $bg-lod == 'light' {
|
||||
// bg is light, fg is dark but does not pass, darken more
|
||||
$step: - $step;
|
||||
}
|
||||
// Keeping the rest of the logic here, but our default values do not change, so this logic is not needed
|
||||
//@else if $fg-lod == 'light' and $bg-lod == 'dark' {
|
||||
// // bg is dark, fg is light but does not pass, lighten further
|
||||
// $step: $step;
|
||||
//} @else if $fg-lod == 'dark' and $bg-lod == 'dark' {
|
||||
// // Both are dark, so lighten the fg
|
||||
// $step: $step;
|
||||
//}
|
||||
|
||||
// The magic happens here
|
||||
// Loop through with a @while statement until the color combination passes our required ratio. Scale the color by our step value until the expression is false
|
||||
// This might loop 100 times or more depending on the colors
|
||||
@while contrast-ratio($fg, $bg) < $ratio {
|
||||
$sat-step: 0%;
|
||||
@if saturation($fg) > 10 {
|
||||
$sat-step: $step;
|
||||
}
|
||||
$fg: scale-color($fg, $lightness: $step, $saturation:$sat-step);
|
||||
}
|
||||
@return $fg;
|
||||
}
|
||||
}
|
||||
20
resources/sass/_variables.scss
vendored
20
resources/sass/_variables.scss
vendored
|
|
@ -106,3 +106,23 @@ $modal-content-border-color: rgba($black, .1) !default;
|
|||
$btn-close-color: $white !default;
|
||||
$btn-close-opacity: .4 !default;
|
||||
$btn-close-hover-opacity: 1 !default;
|
||||
|
||||
|
||||
// Variables
|
||||
$enum-activities: (
|
||||
"0": #fff2cc,
|
||||
"1": #be8e00,
|
||||
"2": #a9d08e,
|
||||
"3": #ff0000,
|
||||
"4": #9bc2e6,
|
||||
);
|
||||
|
||||
$enum-project-status: (
|
||||
"0": #950952,
|
||||
"1": #5E0035,
|
||||
"2": #30011C,
|
||||
"3": #020202,
|
||||
"4": #023618,
|
||||
"5": #014941,
|
||||
"6": #005C69,
|
||||
);
|
||||
|
|
|
|||
1
resources/sass/app.scss
vendored
1
resources/sass/app.scss
vendored
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
// Variables
|
||||
@import 'variables';
|
||||
@import 'complement';
|
||||
|
||||
a.col_sort {
|
||||
&:link,:visited,:hover,:active {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
@auth
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
@if (Auth::user()->isAdmin())
|
||||
<ul class="navbar-nav me-auto">
|
||||
@if (Auth::user()->isAdmin())
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if(strpos(request()->route()->getName(), 'ticket') === 0)active @endif" href="{{ route('ticket.index') }}">Tickets</a>
|
||||
</li>
|
||||
|
|
@ -27,8 +27,11 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link @if(strpos(request()->route()->getName(), 'schedule') === 0)active @endif" href="{{ route('schedule.index') }}">{{ __('Planning') }}</a>
|
||||
</li>
|
||||
@endif
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if(strpos(request()->route()->getName(), 'macroplanning') === 0)active @endif" href="{{ route('macroplanning.show') }}">{{ __('Plan de charge') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
@endif
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<span class="nav-link text-info fw-bold">{{ __('Tickets') }}</span>
|
||||
|
|
@ -94,4 +97,4 @@
|
|||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
|
|
|||
26
resources/views/macroplanning/view.blade.php
Normal file
26
resources/views/macroplanning/view.blade.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
@extends('layout.app')
|
||||
|
||||
|
||||
|
||||
@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)'/>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scriptsConstants')
|
||||
<script>
|
||||
window._routes = {
|
||||
"macroplanning.activity": "{{ route('macroplanning.activity') }}",
|
||||
};
|
||||
</script>
|
||||
@endpush
|
||||
9
resources/views/markers/labelField.blade.php
Normal file
9
resources/views/markers/labelField.blade.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<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>
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
@if(request()->project)
|
||||
<a href="{{ route('projects.show', request()->project) }}" class="btn btn-success float-end">{{ __('Projet') }}</a>
|
||||
<a href="{{ route('projects.logs.index', request()->project) }}" class="btn btn-dark float-end">{{ __('Journaux') }}</a>
|
||||
<a href="{{ route('projects.files.index', request()->project) }}" class="btn btn-dark float-end">{{ __('Fichiers') }}</a>
|
||||
<a href="{{ route('projects.macro_markers.index', request()->project) }}" class="btn btn-warning float-end">{{ __('models.classes.markers') }}</a>
|
||||
<a href="{{ route('projects.logs.index', request()->project) }}" class="btn btn-dark float-end">{{ __('models.classes.logs') }}</a>
|
||||
<a href="{{ route('projects.files.index', request()->project) }}" class="btn btn-dark float-end">{{ __('models.classes.files') }}</a>
|
||||
@endif
|
||||
@if(request()->site)
|
||||
<a href="{{ route('sites.show', request()->site) }}" class="btn btn-success float-end">{{ __('Site') }}</a>
|
||||
<a href="{{ route('sites.logs.index', request()->site) }}" class="btn btn-dark float-end">{{ __('Journaux') }}</a>
|
||||
<a href="{{ route('sites.files.index', request()->site) }}" class="btn btn-dark float-end">{{ __('Fichiers') }}</a>
|
||||
<a href="{{ route('sites.show', request()->site) }}" class="btn btn-success float-end">{{ __('models.classes.sites') }}</a>
|
||||
<a href="{{ route('sites.logs.index', request()->site) }}" class="btn btn-dark float-end">{{ __('models.classes.logs') }}</a>
|
||||
<a href="{{ route('sites.files.index', request()->site) }}" class="btn btn-dark float-end">{{ __('models.classes.files') }}</a>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@
|
|||
@if($prop['type']->equals(CrudType::belongsToMany()))
|
||||
@php $models->load("$fieldName"); @endphp
|
||||
@endif
|
||||
@if($prop['type']->equals(CrudType::hasMany()))
|
||||
@php $models->load("$fieldName"); @endphp
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- DISPLAY MODELS --}}
|
||||
|
|
@ -86,11 +89,14 @@
|
|||
<button type="submit" class="btn btn-sm btn-danger">{{ __('crud-policies::crud.delete') }}</button>
|
||||
</form>
|
||||
@endcan
|
||||
@can('viewAny', \App\Models\MacroSchedule\Marker::class)
|
||||
<a href="{{ CrudController::getRoutePrefixed("projects.macro_markers.index", $model) }}" class="btn btn-sm btn-warning">{{ __('models.classes.markers') }}</a>
|
||||
@endcan
|
||||
@can('viewAny', \App\Models\Log::class)
|
||||
<a href="{{ CrudController::getRoutePrefixed("projects.logs.index", $model) }}" class="btn btn-sm btn-dark">{{ __('Journaux') }}</a>
|
||||
<a href="{{ CrudController::getRoutePrefixed("projects.logs.index", $model) }}" class="btn btn-sm btn-dark">{{ __('models.classes.logs') }}</a>
|
||||
@endcan
|
||||
@can('viewAny', \App\Models\File::class)
|
||||
<a href="{{ CrudController::getRoutePrefixed("projects.files.index", $model) }}" class="btn btn-sm btn-dark">{{ __('Fichiers') }}</a>
|
||||
<a href="{{ CrudController::getRoutePrefixed("projects.files.index", $model) }}" class="btn btn-sm btn-dark">{{ __('models.classes.files') }}</a>
|
||||
@endcan
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
@switch($fieldName)
|
||||
@case('description')
|
||||
@case('todo')
|
||||
<td>{!! $model->{$fieldName} !!}</td>
|
||||
<td>{!! clean($model->{$fieldName}) !!}</td>
|
||||
@break
|
||||
@default
|
||||
<td>@include('crud-policies::crud.printfield', [
|
||||
|
|
|
|||
|
|
@ -94,10 +94,10 @@
|
|||
</form>
|
||||
@endcan
|
||||
@can('viewAny', \App\Models\Log::class)
|
||||
<a href="{{ CrudController::getRoutePrefixed("sites.logs.index", $model) }}" class="btn btn-sm btn-dark">{{ __('Journaux') }}</a>
|
||||
<a href="{{ CrudController::getRoutePrefixed("sites.logs.index", $model) }}" class="btn btn-sm btn-dark">{{ __('models.classes.logs') }}</a>
|
||||
@endcan
|
||||
@can('viewAny', \App\Models\File::class)
|
||||
<a href="{{ CrudController::getRoutePrefixed("sites.files.index", $model) }}" class="btn btn-sm btn-dark">{{ __('Fichiers') }}</a>
|
||||
<a href="{{ CrudController::getRoutePrefixed("sites.files.index", $model) }}" class="btn btn-sm btn-dark">{{ __('models.classes.files') }}</a>
|
||||
@endcan
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
|||
69
resources/views/vendor/crud-policies/crud/formfield.blade.php
vendored
Normal file
69
resources/views/vendor/crud-policies/crud/formfield.blade.php
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
@php
|
||||
$readonly = is_callable($prop['readonly']) ? $prop['readonly']() : $prop['readonly'];
|
||||
@endphp
|
||||
@if($modelClass === \App\Models\MacroSchedule\Marker::class and $fieldName === 'label')
|
||||
@include('markers.labelField')
|
||||
@else
|
||||
@switch($prop['type'])
|
||||
@case(CrudType::password())
|
||||
@include('crud-policies::crud.formfields.password')
|
||||
@break
|
||||
@case(CrudType::boolean())
|
||||
@include('crud-policies::crud.formfields.boolean')
|
||||
@break
|
||||
@case(CrudType::int())
|
||||
@include('crud-policies::crud.formfields.int')
|
||||
@break
|
||||
@case(CrudType::float())
|
||||
@include('crud-policies::crud.formfields.float')
|
||||
@break
|
||||
@case(CrudType::unsignedint())
|
||||
@include('crud-policies::crud.formfields.int', ['unsigned' => true])
|
||||
@break
|
||||
@case(CrudType::unsignedfloat())
|
||||
@include('crud-policies::crud.formfields.float', ['unsigned' => true])
|
||||
@break
|
||||
@case(CrudType::email())
|
||||
@include('crud-policies::crud.formfields.email')
|
||||
@break
|
||||
@case(CrudType::text())
|
||||
@include('crud-policies::crud.formfields.text')
|
||||
@break
|
||||
@case(CrudType::date())
|
||||
@include('crud-policies::crud.formfields.date')
|
||||
@break
|
||||
@case(CrudType::datetime())
|
||||
@include('crud-policies::crud.formfields.datetime')
|
||||
@break
|
||||
@case(CrudType::json())
|
||||
@include('crud-policies::crud.formfields.json')
|
||||
@break
|
||||
@case(CrudType::image())
|
||||
@include('crud-policies::crud.formfields.image')
|
||||
@break
|
||||
@case(CrudType::file())
|
||||
@include('crud-policies::crud.formfields.file')
|
||||
@break
|
||||
@case(CrudType::enum())
|
||||
@include('crud-policies::crud.formfields.enum')
|
||||
@break
|
||||
@case(CrudType::order())
|
||||
@include('crud-policies::crud.formfields.order')
|
||||
@break
|
||||
@case(CrudType::color())
|
||||
@include('crud-policies::crud.formfields.color')
|
||||
@break
|
||||
@case(CrudType::belongsTo())
|
||||
@include('crud-policies::crud.formfields.belongsTo')
|
||||
@break
|
||||
@case(CrudType::belongsToMany())
|
||||
@include('crud-policies::crud.formfields.belongsToMany')
|
||||
@break
|
||||
@case(CrudType::hasMany())
|
||||
@include('crud-policies::crud.formfields.hasMany')
|
||||
@break
|
||||
@case(CrudType::string())
|
||||
@default
|
||||
@include('crud-policies::crud.formfields.string')
|
||||
@endswitch
|
||||
@endif
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
use App\Http\Controllers\FileController;
|
||||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\LogController;
|
||||
use App\Http\Controllers\MarkerController;
|
||||
use App\Http\Controllers\ScheduleMacroController;
|
||||
use App\Http\Controllers\ProjectController;
|
||||
use App\Http\Controllers\ScheduleController;
|
||||
use App\Http\Controllers\SiteController;
|
||||
|
|
@ -25,21 +27,21 @@ use Illuminate\Support\Facades\Route;
|
|||
Route::get('/', [HomeController::class, 'home'])->middleware('guest')->name('home');
|
||||
|
||||
Route::middleware(['throttle:credentials'])->group(function () {
|
||||
// guest:web middleware specified in AuthController
|
||||
// * guest:web middleware specified in AuthController
|
||||
Auth::routes([
|
||||
'login' => true,
|
||||
'logout' => true,
|
||||
'register' => false,
|
||||
// For resetting passwords.
|
||||
'reset' => true,
|
||||
// for resetting passwords
|
||||
// For additional password confirmations.
|
||||
'confirm' => true,
|
||||
// for additional password confirmations
|
||||
'verify' => false,
|
||||
// for email verification
|
||||
// For email verification.
|
||||
'verify' => false
|
||||
]);
|
||||
});
|
||||
|
||||
// Session Ping
|
||||
// * Session Ping
|
||||
Route::get('/ping', function () {
|
||||
if (request()->isJson()) {
|
||||
abort(404);
|
||||
|
|
@ -49,18 +51,28 @@ Route::get('/ping', function () {
|
|||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
|
||||
// Logged user actions
|
||||
// * Macro planning
|
||||
Route::get('/macroplanning', [ScheduleMacroController::class, 'show'])->name('macroplanning.show');
|
||||
Route::post('/activity', [ScheduleMacroController::class, 'postActivity'])->name('macroplanning.activity');
|
||||
|
||||
// * Logged user actions
|
||||
Route::get('/users/change-password', [UserController::class, 'passwordReset'])->name('users.passwordReset');
|
||||
Route::post('/users/change-password', [UserController::class, 'passwordResetSubmit'])->name('users.passwordResetSubmit');
|
||||
Route::post('/users/change-password', [UserController::class, 'passwordResetSubmit'])
|
||||
->name('users.passwordResetSubmit');
|
||||
|
||||
Route::get('/users/stats', [UserController::class, 'stats'])->name('users.stats');
|
||||
|
||||
// Ticket
|
||||
// * 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('projects.macro_markers', MarkerController::class)
|
||||
->parameters([
|
||||
'macro_markers' => 'marker'
|
||||
]);
|
||||
|
||||
// LOGS and Files
|
||||
// * LOGS and Files
|
||||
Route::resource('logs', LogController::class);
|
||||
Route::resource('files', FileController::class);
|
||||
Route::resource('projects.logs', LogController::class);
|
||||
|
|
@ -68,6 +80,7 @@ Route::middleware(['auth'])->group(function () {
|
|||
Route::resource('sites.logs', LogController::class);
|
||||
Route::resource('sites.files', FileController::class);
|
||||
|
||||
// * Ticket
|
||||
Route::prefix('/ticket')->name('ticket.')->group(function () {
|
||||
Route::post('/{ticket}/reopen', [TicketController::class, 'reopen'])
|
||||
->middleware('can:reopen,ticket')
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue