Merge branch 'trunk'

This commit is contained in:
Jérémy Munsch 2021-08-02 08:38:08 +02:00
commit 710bd25f79
47 changed files with 1959 additions and 446 deletions

View file

@ -1,3 +1,9 @@
### 01.08 -> 0.0.1
- Ajout d'un algo de charge
- Ajout changement de mot de passe
- Ajout messages http explicites
- Correction couleurs
#### 30.07
- Ajout changelog
- Ajout footer infos

View file

@ -2,5 +2,41 @@
- ~~permettre d'insérer des @user dans le texte avec lien vers user~~
~~pas de liens vers user mais mise en form~~
- ~~parse de @user + envoie de mail si un seul user est trouvé~~
- permettre de modifier son mot de passe ? (je crois que ça n'as pas été mis exprès)
- ~~permettre de modifier son mot de passe ? (je crois que ça n'as pas été mis exprès)~~
- ~~parser les liens http~~
Algorithme charge de travail ticket
delais 7 jours 168h
travaillé 5 jours 37h
temps tickets 1 jours 7.4h
personnes 4 (TpP) 3.7h / semaine * prs
délais moyen ticket (Dmt) 0.75h
délais max attente pour 1 ticket (Dam) 7jours
niveaux de charge 4: - faible
- stable
- élevée
- saturation
états tickets(Et) : - nouveaux (non traités)
- en attente (charge pendante)
- traités
Algo : linéaire
TpP : 3.7h
P : 4
Et(nvx) * Dam
Nv Charge = (P * TpP) / (Et(nvx) * Dam) - (P * TpP) / (Et(nvx) * DmT) - (Et(att) * DmT)
Si nv Charge < 0 => saturation

35
app/Enums/LoadLevel.php Normal file
View file

@ -0,0 +1,35 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnum;
/**
* @method static self weak()
* @method static self stable()
* @method static self high()
* @method static self overload()
*/
class LoadLevel extends BaseEnum
{
protected static function values(): array
{
return [
'weak' => 0,
'stable' => 1,
'high' => 2,
'overload' => 3
];
}
protected static function labels(): array
{
return [
'weak' => trans('Faible'),
'stable' => trans('Stable'),
'high' => trans('Élevée'),
'overload' => trans('Surcharge')
];
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
class ConfirmPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Confirm Password Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password confirmations and
| uses a simple trait to include the behavior. You're free to explore
| this trait and override any functions that require customization.
|
*/
use ConfirmsPasswords;
/**
* Where to redirect users when the intended url fails.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
public function __construct()
{
$this->middleware('guest:web');
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:web')->except('logout');
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Validation\Rules\Password;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Get the password reset validation rules.
*
* @return array
*/
protected function rules()
{
return [
'token' => 'required',
'email' => 'required|email',
'password' => [
'required', 'confirmed', Password::min(8)
->mixedCase()
->letters()
->numbers()
->symbols()
->uncompromised()
],
];
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
public function index()
{
return view('login');
}
public function authenticate(Request $request)
{
if (
Auth::attempt([
"email" => $request->email, "password" => $request->password
], $request->remember !== null ? true : false)
) {
return redirect()->route('home');
} else {
return back()->withInput($request->except("password"))
->withErrors("Les identifiants entrés sont incorrects, veuillez réessayer.");
}
}
public function logout()
{
Auth::logout();
return redirect()->route('home');
}
}

View file

@ -2,8 +2,10 @@
namespace App\Http\Controllers;
use App\Http\Controllers\Auth\ForgotPasswordController;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
class UserController extends Controller
@ -21,4 +23,12 @@ class UserController extends Controller
$model = $model;
$model->sendMailConfirm();
}
public function passwordResetSubmit()
{
/** @var \App\Models\User */
$user = auth()->user();
$request = new Request(['email' => $user->email]);
return app(ForgotPasswordController::class)->sendResetLinkEmail($request);
}
}

View file

@ -2,6 +2,9 @@
namespace App\Lib;
use App\Enums\LoadLevel;
use Illuminate\Support\Facades\View;
class Utils
{
public static function plainToHtml(string $text)
@ -39,4 +42,69 @@ class Utils
$m = [];
return \preg_match('/(\.png|\.jpg|\.jpeg|\.gif)$/', $url, $m) === 1;
}
/**
* delais 7 jours 168h
* travaillé 5 jours 37h
* temps tickets 1 jours 7.4h
*
* personnes 4 (TpP) 3.7h / semaine * prs
*
* délais moyen ticket (Dmt) 0.75h
*
* délais max attente pour 1 ticket (Dam) 7jours
*
* niveaux de charge 4: - faible
* - stable
* - élevée
* - saturation
*
* états tickets(Et) : - nouveaux (non traités)
* - en attente (charge pendante)
* - traités
*
* Algo : linéaire
*
* TpP : 3.7h
* P : 4
*
* Et(nvx) * Dam
*
* Nv Charge = (P * TpP) / (Et(nvx) * Dam) - (P * TpP) / (Et(nvx) * DmT) - (Et(att) * DmT)
*
*
* Si nv Charge < 0 => saturation
*
* @return void
*/
public static function computeWorkload(): LoadLevel
{
$Tnew = View::shared('ticketsNew');
$Tprogress = View::shared('ticketsInProgress');
$Twaiting = View::shared('ticketsWaiting');
$worked = config('tickets.workedHoursInAWeek');
$workedDays = config('tickets.workedDaysInAWeek');
$morningWorkTime = ($worked / $workedDays / 2); // in hours
$workingDevsPerWeek = config('tickets.workingDevsPerWeek'); // Devs handlings tickets per week
$timePerWeekDev = $workingDevsPerWeek * $morningWorkTime; // Worktime per week to handle tickets
$avgTimeForTicket = config('tickets.avgTimeForTicket'); // 45min for a ticket
$todoListDelay = $Tnew * $avgTimeForTicket; // The time needed to handle new ticket (average)
$waitingListDelay = $Twaiting * $avgTimeForTicket; // The time needed to handle new ticket (average)
$progressListDelay = $Tprogress * $avgTimeForTicket; // The time needed to handle new ticket (average)
$totalTicketsDelay = $todoListDelay + $progressListDelay + $waitingListDelay;
$ETAForAnewTicket = $totalTicketsDelay / $timePerWeekDev; // in weeks
switch ((int)round($ETAForAnewTicket)) {
case LoadLevel::weak()->value:
case LoadLevel::stable()->value:
case LoadLevel::high()->value:
return LoadLevel::make((int)round($ETAForAnewTicket));
default:
return LoadLevel::overload();
}
}
}

View file

@ -4,6 +4,7 @@ namespace App\Models;
use App\Enums\Role;
use App\Jobs\SendUserCreatedEmail;
use App\Notifications\ResetPassword;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
@ -190,6 +191,11 @@ class User extends Authenticatable
dispatch(new SendUserCreatedEmail($this));
}
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPassword($token));
}
/**
* Is the user an admin or a developper ?
*

View file

@ -0,0 +1,30 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
class ResetPassword extends ResetPasswordNotification
{
use Queueable;
/**
* Get the reset password notification mail message for the given URL.
*
* @param string $url
* @return \Illuminate\Notifications\Messages\MailMessage
*/
protected function buildMailMessage($url)
{
$message = (new MailMessage())
->subject(trans('Reinitialisation de mot de passe'))
->line(trans('Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte.'))
->action(trans('Réinitialiser le mot de passe'), $url)
->line(trans('Ce lien de réinitialisation de mot de passe expirera dans :count minutes. ', ['count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]))
->line(trans('Si vous n\'avez pas demandé de réinitialisation de mot de passe, aucune autre action n\'est requise.'));
$message->viewData['greeting'] = 'Bonjour,';
return $message;
}
}

View file

@ -41,6 +41,7 @@ class AppServiceProvider extends ServiceProvider
View::share('license', $appInfos->license);
View::share('ticketsNew', Ticket::where('status', TicketStatus::new())->count());
View::share('ticketsWaiting', Ticket::where('status', TicketStatus::waiting())->count());
View::share('ticketsInProgress', Ticket::whereNotIn(
'status',
[
@ -49,6 +50,7 @@ class AppServiceProvider extends ServiceProvider
TicketStatus::estimateValidated(),
TicketStatus::toPlan(),
TicketStatus::planned(),
TicketStatus::waiting(),
TicketStatus::production(),
TicketStatus::archived()
]

View file

@ -2,9 +2,6 @@
namespace App\Providers;
use App\Models\Ticket;
use App\Policies\SitePolicy;
use App\Policies\TicketPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider

View file

@ -61,6 +61,9 @@ class RouteServiceProvider extends ServiceProvider
*/
protected function configureRateLimiting()
{
RateLimiter::for('credentials', function (Request $request) {
return Limit::perMinute(30)->by(optional($request->user())->id ?: $request->ip());
});
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});

View file

@ -1,6 +1,6 @@
{
"name": "ldcweb/supportweb",
"version": "1.0.0",
"version": "1.0.1",
"type": "project",
"description": "The Laravel Framework.",
"keywords": [
@ -17,9 +17,10 @@
"guzzlehttp/guzzle": "^7.0.1",
"kwaadpepper/crud-policies": "^0.1.9",
"kwaadpepper/enum": "^0.1.4",
"laravel-lang/lang": "~8.0",
"laravel/framework": "^8.12",
"laravel/tinker": "^2.5",
"laravel/ui": "^3.1"
"laravel/ui": "^3.3"
},
"require-dev": {
"facade/ignition": "^2.5",

55
composer.lock generated
View file

@ -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": "03b4961bbf5b8266d0317365ce892a88",
"content-hash": "0da9cba25dcff87fac2343ef3effa795",
"packages": [
{
"name": "asm89/stack-cors",
@ -1569,6 +1569,59 @@
},
"time": "2021-04-06T14:35:59+00:00"
},
{
"name": "laravel-lang/lang",
"version": "8.1.3",
"source": {
"type": "git",
"url": "https://github.com/Laravel-Lang/lang.git",
"reference": "54048d1319b211daa8ab73655b698feb09588876"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/54048d1319b211daa8ab73655b698feb09588876",
"reference": "54048d1319b211daa8ab73655b698feb09588876",
"shasum": ""
},
"require": {
"ext-json": "*"
},
"require-dev": {
"andrey-helldar/pretty-array": "^2.3",
"andrey-helldar/support": "^3.4",
"php": "^8.0",
"phpunit/phpunit": "^9.5",
"symfony/var-dumper": "^5.2"
},
"suggest": {
"andrey-helldar/laravel-lang-publisher": "Easy installation and update of translation files for your project",
"arcanedev/laravel-lang": "Translations manager and checker for Laravel 5",
"overtrue/laravel-lang": "Command to add languages in your project"
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laravel-Lang Team",
"homepage": "https://github.com/Laravel-Lang"
}
],
"description": "Languages for Laravel",
"keywords": [
"lang",
"languages",
"laravel",
"lpm"
],
"support": {
"issues": "https://github.com/Laravel-Lang/lang/issues",
"source": "https://github.com/Laravel-Lang/lang"
},
"time": "2021-04-14T17:41:45+00:00"
},
{
"name": "laravel/framework",
"version": "v8.52.0",

View file

@ -233,6 +233,7 @@ return [
'CrudType' => App\Enums\CrudType::class,
'Role' => App\Enums\Role::class,
'FileType' => App\Enums\FileType::class,
'LoadLevel' => App\Enums\LoadLevel::class,
],
];

8
config/tickets.php Normal file
View file

@ -0,0 +1,8 @@
<?php
return [
'workedHoursInAWeek' => 37,
'workedDaysInAWeek' => 5,
'workingDevsPerWeek' => 2,
'avgTimeForTicket' => 0.95, // 0.95 heure
];

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePasswordResetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
}

10
resources/lang/fr.json Normal file
View file

@ -0,0 +1,10 @@
{
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Si vous ne parvenez pas à cliquer sur le bouton \":actionText\", copiez et collez l'URL ci-dessous\ndans votre navigateur Web :",
"Regards": "Cordialement",
"All rights reserved.": "Tous droits réservés.",
"The :attribute must contain at least one uppercase and one lowercase letter.": ":attribute doit contenir au moins une lettre majuscule et une lettre minuscule.",
"The :attribute must contain at least one letter.": ":attribute doit contenir au moins une lettre.",
"The :attribute must contain at least one symbol.": ":attribute doit contenir au moins un caractère spécial.",
"The :attribute must contain at least one number.": ":attribute doit contenir au moins un nombre.",
"The given :attribute has appeared in a data leak. Please choose a different :attribute.": ":attribute apparait dans une fuite de données sur le web. Merci de choisir un :attribute différent."
}

View file

@ -0,0 +1,131 @@
<?php
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
return [
'accepted' => 'Ce champ doit être accepté.',
'active_url' => 'Ce n\'est pas une URL valide',
'after' => 'La date doit être postérieure au :date.',
'after_or_equal' => 'La date doit être postérieure ou égale au :date.',
'alpha' => 'Ce champ doit contenir uniquement des lettres',
'alpha_dash' => 'Ce champ doit contenir uniquement des lettres, des chiffres et des tirets.',
'alpha_num' => 'Ce champ doit contenir uniquement des chiffres et des lettres.',
'array' => 'Ce champ doit être un tableau.',
'attached' => 'Ce champ est déjà attaché.',
'before' => 'Ce champ doit être une date antérieure au :date.',
'before_or_equal' => 'Ce champ doit être une date antérieure ou égale au :date.',
'between' => [
'array' => 'Le tableau doit contenir entre :min et :max éléments.',
'file' => 'La taille du fichier doit être comprise entre :min et :max kilo-octets.',
'numeric' => 'La valeur doit être comprise entre :min et :max.',
'string' => 'Le texte doit contenir entre :min et :max caractères.',
],
'boolean' => 'Ce champ doit être vrai ou faux.',
'confirmed' => 'Le champ de confirmation ne correspond pas.',
'date' => 'Ce n\'est pas une date valide.',
'date_equals' => 'La date doit être égale à :date.',
'date_format' => 'Ce champ ne correspond pas au format :format.',
'different' => 'Cette valeur doit être différente de :other.',
'digits' => 'Ce champ doit contenir :digits chiffres.',
'digits_between' => 'Ce champ doit contenir entre :min et :max chiffres.',
'dimensions' => 'La taille de l\'image n\'est pas conforme.',
'distinct' => 'Ce champ a une valeur en double.',
'email' => 'Ce champ doit être une adresse email valide.',
'ends_with' => 'Ce champ doit se terminer par une des valeurs suivantes : :values',
'exists' => 'Ce champ sélectionné est invalide.',
'file' => 'Ce champ doit être un fichier.',
'filled' => 'Ce champ doit avoir une valeur.',
'gt' => [
'array' => 'Le tableau doit contenir plus de :value éléments.',
'file' => 'La taille du fichier doit être supérieure à :value kilo-octets.',
'numeric' => 'La valeur doit être supérieure à :value.',
'string' => 'Le texte doit contenir plus de :value caractères.',
],
'gte' => [
'array' => 'Le tableau doit contenir au moins :value éléments.',
'file' => 'La taille du fichier doit être supérieure ou égale à :value kilo-octets.',
'numeric' => 'La valeur doit être supérieure ou égale à :value.',
'string' => 'Le texte doit contenir au moins :value caractères.',
],
'image' => 'Ce champ doit être une image.',
'in' => 'Ce champ est invalide.',
'in_array' => 'Ce champ n\'existe pas dans :other.',
'integer' => 'Ce champ doit être un entier.',
'ip' => 'Ce champ doit être une adresse IP valide.',
'ipv4' => 'Ce champ doit être une adresse IPv4 valide.',
'ipv6' => 'Ce champ doit être une adresse IPv6 valide.',
'json' => 'Ce champ doit être un document JSON valide.',
'lt' => [
'array' => 'Le tableau doit contenir moins de :value éléments.',
'file' => 'La taille du fichier doit être inférieure à :value kilo-octets.',
'numeric' => 'La valeur doit être inférieure à :value.',
'string' => 'Le texte doit contenir moins de :value caractères.',
],
'lte' => [
'array' => 'Le tableau doit contenir au plus :value éléments.',
'file' => 'La taille du fichier doit être inférieure ou égale à :value kilo-octets.',
'numeric' => 'La valeur doit être inférieure ou égale à :value.',
'string' => 'Le texte doit contenir au plus :value caractères.',
],
'max' => [
'array' => 'Le tableau ne peut contenir plus de :max éléments.',
'file' => 'La taille du fichier ne peut pas dépasser :max kilo-octets.',
'numeric' => 'La valeur ne peut être supérieure à :max.',
'string' => 'Le texte ne peut contenir plus de :max caractères.',
],
'mimes' => 'Le fichier doit être de type : :values.',
'mimetypes' => 'Le fichier doit être de type : :values.',
'min' => [
'array' => 'Le tableau doit contenir au moins :min éléments.',
'file' => 'La taille du fichier doit être supérieure à :min kilo-octets.',
'numeric' => 'La valeur doit être supérieure ou égale à :min.',
'string' => 'Le texte doit contenir au moins :min caractères.',
],
'multiple_of' => 'La valeur doit être un multiple de :value',
'not_in' => 'Le champ sélectionné n\'est pas valide.',
'not_regex' => 'Le format du champ n\'est pas valide.',
'numeric' => 'Ce champ doit contenir un nombre.',
'password' => 'Le mot de passe est incorrect',
'present' => 'Ce champ doit être présent.',
'prohibited' => 'Ce champ est interdit',
'prohibited_if' => 'Ce champ est interdit quand :other a la valeur :value.',
'prohibited_unless' => 'Ce champ est interdit à moins que :other ait l\'une des valeurs :values.',
'regex' => 'Le format du champ est invalide.',
'relatable' => 'Ce champ n\'est sans doute pas associé avec cette donnée.',
'required' => 'Ce champ est obligatoire.',
'required_if' => 'Ce champ est obligatoire quand la valeur de :other est :value.',
'required_unless' => 'Ce champ est obligatoire sauf si :other est :values.',
'required_with' => 'Ce champ est obligatoire quand :values est présent.',
'required_with_all' => 'Ce champ est obligatoire quand :values sont présents.',
'required_without' => 'Ce champ est obligatoire quand :values n\'est pas présent.',
'required_without_all' => 'Ce champ est requis quand aucun de :values n\'est présent.',
'same' => 'Ce champ doit être identique à :other.',
'size' => [
'array' => 'Le tableau doit contenir :size éléments.',
'file' => 'La taille du fichier doit être de :size kilo-octets.',
'numeric' => 'La valeur doit être :size.',
'string' => 'Le texte doit contenir :size caractères.',
],
'starts_with' => 'Ce champ doit commencer avec une des valeurs suivantes : :values',
'string' => 'Ce champ doit être une chaîne de caractères.',
'timezone' => 'Ce champ doit être un fuseau horaire valide.',
'unique' => 'La valeur est déjà utilisée.',
'uploaded' => 'Le fichier n\'a pu être téléversé.',
'url' => 'Le format de l\'URL n\'est pas valide.',
'uuid' => 'Ce champ doit être un UUID valide',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];

View file

@ -1,42 +1,43 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages.
|
*/
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
return [
'accepted' => 'Le champ :attribute doit être accepté.',
'active_url' => "Le champ :attribute n'est pas une URL valide.",
'active_url' => 'Le champ :attribute n\'est pas une URL valide.',
'after' => 'Le champ :attribute doit être une date postérieure au :date.',
'after_or_equal' => 'Le champ :attribute doit être une date postérieure ou égale au :date.',
'alpha' => 'Le champ :attribute doit contenir uniquement des lettres.',
'alpha_dash' => 'Le champ :attribute doit contenir uniquement des lettres, des chiffres et des tirets.',
'alpha_num' => 'Le champ :attribute doit contenir uniquement des chiffres et des lettres.',
'array' => 'Le champ :attribute doit être un tableau.',
'attached' => ':attribute est déjà attaché(e).',
'before' => 'Le champ :attribute doit être une date antérieure au :date.',
'before_or_equal' => 'Le champ :attribute doit être une date antérieure ou égale au :date.',
'between' => [
'numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.',
'file' => 'La taille du fichier de :attribute doit être comprise entre :min et :max kilo-octets.',
'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.',
'array' => 'Le tableau :attribute doit contenir entre :min et :max éléments.',
'file' => 'La taille du fichier de :attribute doit être comprise entre :min et :max kilo-octets.',
'numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.',
'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.',
],
'boolean' => 'Le champ :attribute doit être vrai ou faux.',
'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.',
'date' => "Le champ :attribute n'est pas une date valide.",
'date' => 'Le champ :attribute n\'est pas une date valide.',
'date_equals' => 'Le champ :attribute doit être une date égale à :date.',
'date_format' => 'Le champ :attribute ne correspond pas au format :format.',
'different' => 'Les champs :attribute et :other doivent être différents.',
'digits' => 'Le champ :attribute doit contenir :digits chiffres.',
'digits_between' => 'Le champ :attribute doit contenir entre :min et :max chiffres.',
'dimensions' => "La taille de l'image :attribute n'est pas conforme.",
'dimensions' => 'La taille de l\'image :attribute n\'est pas conforme.',
'distinct' => 'Le champ :attribute a une valeur en double.',
'email' => 'Le champ :attribute doit être une adresse email valide.',
'ends_with' => 'Le champ :attribute doit se terminer par une des valeurs suivantes : :values',
@ -44,137 +45,118 @@ return [
'file' => 'Le champ :attribute doit être un fichier.',
'filled' => 'Le champ :attribute doit avoir une valeur.',
'gt' => [
'numeric' => 'La valeur de :attribute doit être supérieure à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir plus de :value caractères.',
'array' => 'Le tableau :attribute doit contenir plus de :value éléments.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :value kilo-octets.',
'numeric' => 'La valeur de :attribute doit être supérieure à :value.',
'string' => 'Le texte :attribute doit contenir plus de :value caractères.',
],
'gte' => [
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir au moins :value caractères.',
'array' => 'Le tableau :attribute doit contenir au moins :value éléments.',
'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :value kilo-octets.',
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :value.',
'string' => 'Le texte :attribute doit contenir au moins :value caractères.',
],
'image' => 'Le champ :attribute doit être une image.',
'in' => 'Le champ :attribute est invalide.',
'in_array' => "Le champ :attribute n'existe pas dans :other.",
'in_array' => 'Le champ :attribute n\'existe pas dans :other.',
'integer' => 'Le champ :attribute doit être un entier.',
'ip' => 'Le champ :attribute doit être une adresse IP valide.',
'ipv4' => 'Le champ :attribute doit être une adresse IPv4 valide.',
'ipv6' => 'Le champ :attribute doit être une adresse IPv6 valide.',
'json' => 'Le champ :attribute doit être un document JSON valide.',
'lt' => [
'numeric' => 'La valeur de :attribute doit être inférieure à :value.',
'file' => 'La taille du fichier de :attribute doit être inférieure à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir moins de :value caractères.',
'array' => 'Le tableau :attribute doit contenir moins de :value éléments.',
'file' => 'La taille du fichier de :attribute doit être inférieure à :value kilo-octets.',
'numeric' => 'La valeur de :attribute doit être inférieure à :value.',
'string' => 'Le texte :attribute doit contenir moins de :value caractères.',
],
'lte' => [
'numeric' => 'La valeur de :attribute doit être inférieure ou égale à :value.',
'file' => 'La taille du fichier de :attribute doit être inférieure ou égale à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir au plus :value caractères.',
'array' => 'Le tableau :attribute doit contenir au plus :value éléments.',
'file' => 'La taille du fichier de :attribute doit être inférieure ou égale à :value kilo-octets.',
'numeric' => 'La valeur de :attribute doit être inférieure ou égale à :value.',
'string' => 'Le texte :attribute doit contenir au plus :value caractères.',
],
'max' => [
'numeric' => 'La valeur de :attribute ne peut être supérieure à :max.',
'file' => 'La taille du fichier de :attribute ne peut pas dépasser :max kilo-octets.',
'string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.',
'array' => 'Le tableau :attribute ne peut contenir plus de :max éléments.',
'file' => 'La taille du fichier de :attribute ne peut pas dépasser :max kilo-octets.',
'numeric' => 'La valeur de :attribute ne peut être supérieure à :max.',
'string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.',
],
'mimes' => 'Le champ :attribute doit être un fichier de type : :values.',
'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.',
'min' => [
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :min.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :min kilo-octets.',
'string' => 'Le texte :attribute doit contenir au moins :min caractères.',
'array' => 'Le tableau :attribute doit contenir au moins :min éléments.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :min kilo-octets.',
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :min.',
'string' => 'Le texte :attribute doit contenir au moins :min caractères.',
],
'multiple_of' => 'La valeur de :attribute doit être un multiple de :value',
'not_in' => "Le champ :attribute sélectionné n'est pas valide.",
'not_regex' => "Le format du champ :attribute n'est pas valide.",
'not_in' => 'Le champ :attribute sélectionné n\'est pas valide.',
'not_regex' => 'Le format du champ :attribute n\'est pas valide.',
'numeric' => 'Le champ :attribute doit contenir un nombre.',
'password' => 'Le mot de passe est incorrect',
'present' => 'Le champ :attribute doit être présent.',
'prohibited' => 'Le champ :attribute est interdit.',
'prohibited_if' => 'Le champ :attribute est interdit quand :other a la valeur :value.',
'prohibited_unless' => 'Le champ :attribute est interdit à moins que :other est l\'une des valeurs :values.',
'regex' => 'Le format du champ :attribute est invalide.',
'relatable' => ':attribute n\'est sans doute pas associé(e) avec cette donnée.',
'required' => 'Le champ :attribute est obligatoire.',
'required_if' => 'Le champ :attribute est obligatoire quand la valeur de :other est :value.',
'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est :values.',
'required_with' => 'Le champ :attribute est obligatoire quand :values est présent.',
'required_with_all' => 'Le champ :attribute est obligatoire quand :values sont présents.',
'required_without' => "Le champ :attribute est obligatoire quand :values n'est pas présent.",
'required_without_all' => "Le champ :attribute est requis quand aucun de :values n'est présent.",
'required_without' => 'Le champ :attribute est obligatoire quand :values n\'est pas présent.',
'required_without_all' => 'Le champ :attribute est requis quand aucun de :values n\'est présent.',
'same' => 'Les champs :attribute et :other doivent être identiques.',
'size' => [
'numeric' => 'La valeur de :attribute doit être :size.',
'file' => 'La taille du fichier de :attribute doit être de :size kilo-octets.',
'string' => 'Le texte de :attribute doit contenir :size caractères.',
'array' => 'Le tableau :attribute doit contenir :size éléments.',
'file' => 'La taille du fichier de :attribute doit être de :size kilo-octets.',
'numeric' => 'La valeur de :attribute doit être :size.',
'string' => 'Le texte de :attribute doit contenir :size caractères.',
],
'starts_with' => 'Le champ :attribute doit commencer avec une des valeurs suivantes : :values',
'string' => 'Le champ :attribute doit être une chaîne de caractères.',
'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.',
'unique' => 'La valeur du champ :attribute est déjà utilisée.',
'uploaded' => "Le fichier du champ :attribute n'a pu être téléversé.",
'url' => "Le format de l'URL de :attribute n'est pas valide.",
'uploaded' => 'Le fichier du champ :attribute n\'a pu être téléversé.',
'url' => 'Le format de l\'URL de :attribute n\'est pas valide.',
'uuid' => 'Le champ :attribute doit être un UUID valide',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [
'name' => 'nom',
'username' => "nom d'utilisateur",
'attributes' => [
'address' => 'adresse',
'age' => 'âge',
'available' => 'disponible',
'city' => 'ville',
'content' => 'contenu',
'country' => 'pays',
'current_password' => 'mot de passe actuel',
'date' => 'date',
'day' => 'jour',
'description' => 'description',
'email' => 'adresse email',
'excerpt' => 'extrait',
'first_name' => 'prénom',
'gender' => 'genre',
'hour' => 'heure',
'last_name' => 'nom',
'minute' => 'minute',
'mobile' => 'portable',
'month' => 'mois',
'name' => 'nom',
'password' => 'mot de passe',
'password_confirmation' => 'confirmation du mot de passe',
'city' => 'ville',
'country' => 'pays',
'address' => 'adresse',
'phone' => 'téléphone',
'mobile' => 'portable',
'age' => 'âge',
'sex' => 'sexe',
'gender' => 'genre',
'day' => 'jour',
'month' => 'mois',
'year' => 'année',
'hour' => 'heure',
'minute' => 'minute',
'second' => 'seconde',
'title' => 'titre',
'content' => 'contenu',
'description' => 'description',
'excerpt' => 'extrait',
'date' => 'date',
'time' => 'heure',
'available' => 'disponible',
'sex' => 'sexe',
'size' => 'taille',
'time' => 'heure',
'title' => 'titre',
'username' => 'nom d\'utilisateur',
'year' => 'année',
],
];

View file

@ -1,331 +1,331 @@
// Lumen 5.0.2
// Bootswatch
// Variables
@import 'variables';
@import "variables";
$web-font-path: "https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;1,400&display=swap" !default;
@if $web-font-path {
@import url($web-font-path);
@import url($web-font-path);
}
// Mixins
@mixin shadow($width: 4px){
border-style: solid;
border-width: 0 1px $width 1px;
@mixin shadow($width: 4px) {
border-style: solid;
border-width: 0 1px $width 1px;
}
// Navbar
.navbar {
@include shadow();
@include shadow();
@each $color, $value in $theme-colors {
&.bg-#{$color} {
border-color: shade-color($value, 10%);
@each $color, $value in $theme-colors {
&.bg-#{$color} {
border-color: shade-color($value, 10%);
}
}
}
}
// Buttons
.btn {
@include shadow();
text-transform: uppercase;
@include shadow();
text-transform: uppercase;
&:not(.disabled):hover {
margin-top: 1px;
border-bottom-width: 3px;
}
&:not(.disabled):active {
margin-top: 2px;
border-bottom-width: 2px;
@include box-shadow(none);
}
@each $color, $value in $theme-colors {
&-#{$color} {
&:hover,
&:active,
&:focus {
background-color: $value;
}
&,
&:not(.disabled):hover,
&:not(.disabled):active,
&:focus {
border-color: shade-color($value, 10%);
}
&:not(.disabled):hover {
margin-top: 1px;
border-bottom-width: 3px;
}
&:not(.disabled):active {
margin-top: 2px;
border-bottom-width: 2px;
@include box-shadow(none);
}
@each $color, $value in $theme-colors {
&-#{$color} {
&:hover,
&:active,
&:focus {
background-color: $value;
}
&,
&:not(.disabled):hover,
&:not(.disabled):active,
&:focus {
border-color: shade-color($value, 10%);
}
}
}
}
}
[class*="btn-outline"] {
border-top-width: 1px;
border-top-width: 1px;
}
.btn-group-vertical {
.btn + .btn {
&:hover {
margin-top: -1px;
border-top-width: 1px;
}
.btn + .btn {
&:hover {
margin-top: -1px;
border-top-width: 1px;
}
&:active {
margin-top: -1px;
border-top-width: 2px;
&:active {
margin-top: -1px;
border-top-width: 2px;
}
}
}
}
// Typography
.text-secondary {
color: $gray-700 !important;
color: $gray-700 !important;
}
.blockquote-footer {
color: $gray-600;
color: $gray-600;
}
// Forms
.form-control {
box-shadow: inset 0 2px 0 rgba(0, 0, 0, .075);
box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.075);
}
.input-group-sm {
> .input-group-prepend,
> .input-group-append {
.btn {
font-size: $btn-font-size-sm;
> .input-group-prepend,
> .input-group-append {
.btn {
font-size: $btn-font-size-sm;
}
}
}
}
// Navs
.nav {
.open > a,
.open > a:hover,
.open > a:focus {
border-color: transparent;
}
.open > a,
.open > a:hover,
.open > a:focus {
border-color: transparent;
}
}
.nav-tabs {
.nav-link {
color: $body-color;
.nav-link {
color: $body-color;
&,
&.disabled,
&.disabled:hover,
&.disabled:focus {
margin-top: 6px;
border-color: $nav-tabs-border-color;
transition: padding-bottom .2s ease-in-out, margin-top .2s ease-in-out, border-bottom .2s ease-in-out;
&,
&.disabled,
&.disabled:hover,
&.disabled:focus {
margin-top: 6px;
border-color: $nav-tabs-border-color;
transition: padding-bottom 0.2s ease-in-out,
margin-top 0.2s ease-in-out, border-bottom 0.2s ease-in-out;
}
&:not(.disabled):hover,
&:not(.disabled):focus,
&.active {
padding-bottom: add(0.5rem, 6px);
border-bottom-color: transparent;
margin-top: 0;
}
}
&:not(.disabled):hover,
&:not(.disabled):focus,
&.active {
padding-bottom: add(.5rem, 6px);
border-bottom-color: transparent;
margin-top: 0;
&.nav-justified > li {
vertical-align: bottom;
}
}
&.nav-justified > li {
vertical-align: bottom;
}
}
.dropdown-menu {
margin-top: 0;
@include shadow();
border-top-width: 1px;
@include box-shadow(none);
margin-top: 0;
@include shadow();
border-top-width: 1px;
@include box-shadow(none);
}
.breadcrumb {
border-color: shade-color($breadcrumb-bg, 10%);
@include shadow();
border-color: shade-color($breadcrumb-bg, 10%);
@include shadow();
}
.pagination {
> li > a,
> li > span {
position: relative;
top: 0;
@include shadow();
color: $pagination-color;
font-weight: 700;
text-transform: uppercase;
> li > a,
> li > span {
position: relative;
top: 0;
@include shadow();
color: $pagination-color;
font-weight: 700;
text-transform: uppercase;
&:hover,
&:focus {
top: 1px;
border-bottom-width: 3px;
text-decoration: none;
&:hover,
&:focus {
top: 1px;
border-bottom-width: 3px;
text-decoration: none;
}
&:active {
top: 2px;
border-bottom-width: 2px;
}
}
&:active {
top: 2px;
border-bottom-width: 2px;
}
}
> .disabled > a,
> .disabled > span {
&:hover {
top: 0;
@include shadow();
}
> .disabled > a,
> .disabled > span {
&:hover {
top: 0;
@include shadow();
&:active {
top: 0;
@include shadow();
}
}
&:active {
top: 0;
@include shadow();
}
}
}
.pager {
> li > a,
> li > span,
> .disabled > a,
> .disabled > span {
&,
&:hover,
&:active {
border-left-width: 2px;
border-right-width: 2px;
> li > a,
> li > span,
> .disabled > a,
> .disabled > span {
&,
&:hover,
&:active {
border-left-width: 2px;
border-right-width: 2px;
}
}
}
}
// Indicators
.btn-close {
text-decoration: none;
opacity: .4;
text-decoration: none;
opacity: 0.4;
&:hover,
&:focus {
opacity: 1;
}
&:hover,
&:focus {
opacity: 1;
}
}
.alert {
color: $white;
@include shadow();
&-primary {
background-color: $primary;
border-color: shade-color($primary, 10%);
}
&-secondary {
background-color: $secondary;
border-color: shade-color($secondary, 10%);
}
&-success {
background-color: $success;
border-color: shade-color($success, 10%);
}
&-info {
background-color: $info;
border-color: shade-color($info, 10%);
}
&-danger {
background-color: $danger;
border-color: shade-color($danger, 10%);
}
&-warning {
background-color: $warning;
border-color: shade-color($warning, 10%);
}
&-dark {
background-color: $dark;
border-color: shade-color($dark, 10%);
}
&-light {
background-color: $light;
border-color: shade-color($light, 10%);
}
.alert-link {
font-weight: 400;
color: $white;
text-decoration: underline;
}
@include shadow();
&-secondary,
&-light {
&,
a,
.alert-link {
color: $body-color;
&-primary {
background-color: $primary;
border-color: shade-color($primary, 10%);
}
&-secondary {
background-color: $secondary;
border-color: shade-color($secondary, 10%);
}
&-success {
background-color: $success;
border-color: shade-color($success, 10%);
}
&-info {
background-color: $info;
border-color: shade-color($info, 10%);
}
&-danger {
background-color: $danger;
border-color: shade-color($danger, 10%);
}
&-warning {
background-color: $warning;
border-color: shade-color($warning, 10%);
}
&-dark {
background-color: $dark;
border-color: shade-color($dark, 10%);
}
&-light {
background-color: $light;
border-color: shade-color($light, 10%);
}
.alert-link {
font-weight: 400;
color: $white;
text-decoration: underline;
}
&-secondary,
&-light {
&,
a,
.alert-link {
color: $body-color;
}
}
}
}
.badge {
&.bg-secondary,
&.bg-light {
color: $dark;
}
&.bg-secondary,
&.bg-light {
color: $dark;
}
}
// Containers
a.list-group-item {
&-success {
&.active {
background-color: $success;
&-success {
&.active {
background-color: $success;
}
&.active:hover,
&.active:focus {
background-color: shade-color($success, 10%);
}
}
&.active:hover,
&.active:focus {
background-color: shade-color($success, 10%);
}
}
&-warning {
&.active {
background-color: $warning;
}
&-warning {
&.active {
background-color: $warning;
&.active:hover,
&.active:focus {
background-color: shade-color($warning, 10%);
}
}
&.active:hover,
&.active:focus {
background-color: shade-color($warning, 10%);
}
}
&-danger {
&.active {
background-color: $danger;
}
&-danger {
&.active {
background-color: $danger;
&.active:hover,
&.active:focus {
background-color: shade-color($danger, 10%);
}
}
&.active:hover,
&.active:focus {
background-color: shade-color($danger, 10%);
}
}
}
.modal,
.toast {
.btn-close {
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
}
.btn-close {
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
}
}

View file

@ -9,48 +9,50 @@ $theme: "lumen" !default;
//
// Color system
//
$lightness: -30%;
$saturation: -9%;
$lightness: -20%;
$saturation: -5%;
@function lowerColor($color) {
@return color.scale($color, $lightness: $lightness, $saturation: $saturation);
}
@function lowerBrightness($color) {
@return color.scale($color, $lightness: $lightness - 20%);
@return color.scale($color, $lightness: $lightness + 10%);
}
$white: #fff !default;
$gray-100: lowerBrightness(#f6f6f6) !default;
$gray-200: lowerBrightness(#f0f0f0) !default;
$white: lowerBrightness(#f1efec) !default;
$gray-100: lowerBrightness(#f8f9fa) !default;
$gray-200: lowerBrightness(#e9ecef) !default;
$gray-300: lowerBrightness(#dee2e6) !default;
$gray-400: lowerBrightness(#ced4da) !default;
$gray-500: lowerBrightness(#adb5bd) !default;
$gray-600: lowerBrightness(#999) !default;
$gray-700: lowerBrightness(#555) !default;
$gray-800: lowerBrightness(#333) !default;
$gray-900: lowerBrightness(#222) !default;
$gray-600: lowerBrightness(#6c757d) !default;
$gray-700: lowerBrightness(#495057) !default;
$gray-800: lowerBrightness(#343a40) !default;
$gray-900: lowerBrightness(#212529) !default;
$black: #000 !default;
$blue: lowerColor(#158cba) !default;
$blue: lowerColor(#337ab7) !default;
$indigo: lowerColor(#6610f2) !default;
$purple: lowerColor(#6f42c1) !default;
$pink: lowerColor(#e83e8c) !default;
$red: lowerColor(#ff4136) !default;
$pink: lowerColor(#B7337A) !default;
$red: lowerColor(#d9534f) !default;
$orange: lowerColor(#fd7e14) !default;
$yellow: lowerColor(#ff851b) !default;
$green: lowerColor(#28b62c) !default;
$yellow: lowerColor(#f0ad4e) !default;
$green: lowerColor(#5cb85c) !default;
$teal: lowerColor(#20c997) !default;
$cyan: lowerColor(#75caeb) !default;
$cyan: lowerColor(#5bc0de) !default;
$primary: $blue !default;
$secondary: $gray-200 !default;
$secondary: $gray-600 !default;
$success: $green !default;
$info: $cyan !default;
$info: $teal !default;
$warning: $yellow !default;
$danger: $red !default;
$light: $gray-100 !default;
$dark: $gray-700 !default;
$dark: $gray-900 !default;
$body-bg: $white;
$min-contrast-ratio: 1.95 !default;

View file

@ -16,6 +16,11 @@ a.col_sort {
max-width: 1340px !important;
}
// background color
body {
background-color: $body-bg;
}
// FOOTER
body {
position: relative;

View file

@ -0,0 +1,73 @@
@extends('layout.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col">
<div class="card">
<div class="card-header">{{ __('Connexion') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-4 col-form-label text-end">{{ __('Adresse email') }}</label>
<div class="col-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mt-4 ">
<label for="password" class="col-4 col-form-label text-end">{{ __('Mot de passe') }}</label>
<div class="col-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mt-4">
<div class="col-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Se souvenir de moi') }}
</label>
</div>
</div>
</div>
<div class="form-group row mb-0 mt-4">
<div class="col-8 offset-4">
<button type="submit" class="btn btn-primary">
{{ __('Connexion') }}
</button>
@if (Route::has('password.request'))
<a class="ms-2 btn btn-link" href="{{ route('password.request') }}">
{{ __('Mot de passe oublié ?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,49 @@
@extends('layout.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Confirmez votre mot de passe') }}</div>
<div class="card-body">
{{ __('Veuillez confirmer votre mot de passe pour continuer') }}
<form method="POST" action="{{ route('password.confirm') }}">
@csrf
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Mot de passe') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Confirmer votre mot de passe') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Mot de passe oublié ?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,47 @@
@extends('layout.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Réinitialisation de mot de passe') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('Adresse email') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0 mt-3">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Envoyer un lien de réinitialisation de mot de passe') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,65 @@
@extends('layout.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Réinitialisation de mot de passe') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('Adresse email') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Mot de passe') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirmation Mot de passe') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Réinitialiser le mot de passe') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,5 @@
@extends('errors::layout')
@section('title', __('Unauthorized'))
@section('code', '401')
@section('message', __('Unauthorized'))

View file

@ -0,0 +1,5 @@
@extends('errors::layout')
@section('title', __('Accès interdit'))
@section('code', '403')
@section('message', __($exception->getMessage() ?: 'Vous n\'êtes pas autorisé à acceder à cette page.'))

View file

@ -0,0 +1,6 @@
@extends('errors::layout')
@section('title', __('Page non trouvée'))
@section('code', '404')
@section('message', __('Oups.. Cette page n\'est pas ou plus disponible.'))
@section('redirect', true)

View file

@ -0,0 +1,6 @@
@extends('errors::layout')
@section('title', __('Page expirée'))
@section('code', '419')
@section('message', __('Cette page à expiré, vous devrez peut être vous reconnecter afin d\'y accéder.'))
@section('redirect', true)

View file

@ -0,0 +1,5 @@
@extends('errors::layout')
@section('title', __('Trop de requêtes'))
@section('code', '429')
@section('message', __('Il semblerait que votre navigateur ai essayé de charge cette page trop de fois dans un temps rapproché. Merci de réessayer plus tard.'))

View file

@ -0,0 +1,5 @@
@extends('errors::layout')
@section('title', __('Erreur serveur'))
@section('code', '500')
@section('message', __('Oups, une erreur est survenue. L\'équipe est déjà prévenu par mail. Inutile d\'appeler quelqu\'un est déjà dessus. Merci de réessayer dans un moment.'))

View file

@ -0,0 +1,5 @@
@extends('errors::layout')
@section('title', __('Service non disponible'))
@section('code', '503')
@section('message', __('Veuillez nous excuser pour la gêne occasionnée, le service est en maintenance. Inutile d\'appeler quelqu\'un est déjà dessus. Merci de réessayer dans un moment.'))

View file

@ -0,0 +1,486 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>@yield('title')</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<!-- Styles -->
<style>
html {
line-height: 1.15;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
header,
nav,
section {
display: block;
}
figcaption,
main {
display: block;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
strong {
font-weight: inherit;
}
strong {
font-weight: bolder;
}
code {
font-family: monospace, monospace;
font-size: 1em;
}
dfn {
font-style: italic;
}
svg:not(:root) {
overflow: hidden;
}
button,
input {
font-family: sans-serif;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
button,
input {
overflow: visible;
}
button {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
legend {
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
[type="checkbox"],
[type="radio"] {
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
menu {
display: block;
}
canvas {
display: inline-block;
}
template {
display: none;
}
[hidden] {
display: none;
}
html {
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: sans-serif;
}
*,
*::before,
*::after {
-webkit-box-sizing: inherit;
box-sizing: inherit;
}
p {
margin: 0;
}
button {
background: transparent;
padding: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
*,
*::before,
*::after {
border-width: 0;
border-style: solid;
border-color: #dae1e7;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
border-radius: 0;
}
button,
input {
font-family: inherit;
}
input::-webkit-input-placeholder {
color: inherit;
opacity: .5;
}
input:-ms-input-placeholder {
color: inherit;
opacity: .5;
}
input::-ms-input-placeholder {
color: inherit;
opacity: .5;
}
input::placeholder {
color: inherit;
opacity: .5;
}
button,
[role=button] {
cursor: pointer;
}
.bg-transparent {
background-color: transparent;
}
.bg-white {
background-color: #fff;
}
.bg-teal-light {
background-color: #64d5ca;
}
.bg-blue-dark {
background-color: #2779bd;
}
.bg-indigo-light {
background-color: #7886d7;
}
.bg-purple-light {
background-color: #a779e9;
}
.bg-no-repeat {
background-repeat: no-repeat;
}
.bg-cover {
background-size: cover;
}
.border-grey-light {
border-color: #dae1e7;
}
.hover\:border-grey:hover {
border-color: #b8c2cc;
}
.rounded-lg {
border-radius: .5rem;
}
.border-2 {
border-width: 2px;
}
.hidden {
display: none;
}
.flex {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.items-center {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.justify-center {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.font-sans {
font-family: Nunito, sans-serif;
}
.font-light {
font-weight: 300;
}
.font-bold {
font-weight: 700;
}
.font-black {
font-weight: 900;
}
.h-1 {
height: .25rem;
}
.leading-normal {
line-height: 1.5;
}
.m-8 {
margin: 2rem;
}
.my-3 {
margin-top: .75rem;
margin-bottom: .75rem;
}
.mb-8 {
margin-bottom: 2rem;
}
.max-w-sm {
max-width: 30rem;
}
.min-h-screen {
min-height: 100vh;
}
.py-3 {
padding-top: .75rem;
padding-bottom: .75rem;
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.pb-full {
padding-bottom: 100%;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.pin {
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.text-black {
color: #22292f;
}
.text-grey-darkest {
color: #3d4852;
}
.text-grey-darker {
color: #606f7b;
}
.text-2xl {
font-size: 1.5rem;
}
.text-5xl {
font-size: 3rem;
}
.uppercase {
text-transform: uppercase;
}
.antialiased {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.tracking-wide {
letter-spacing: .05em;
}
.w-16 {
width: 4rem;
}
.w-full {
width: 100%;
}
@media (min-width: 768px) {
.md\:bg-left {
background-position: left;
}
.md\:bg-right {
background-position: right;
}
.md\:flex {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.md\:my-6 {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.md\:min-h-screen {
min-height: 100vh;
}
.md\:pb-0 {
padding-bottom: 0;
}
.md\:text-3xl {
font-size: 1.875rem;
}
.md\:text-15xl {
font-size: 9rem;
}
.md\:w-1\/2 {
width: 50%;
}
}
@media (min-width: 992px) {
.lg\:bg-center {
background-position: center;
}
}
</style>
</head>
<body class="antialiased font-sans">
<div class="md:flex min-h-screen">
<div class="w-full md:w-1/2 bg-white flex items-center justify-center">
<div class="max-w-sm m-8">
<div class="text-black text-5xl md:text-15xl font-black">
@yield('code', __('Oh no'))
</div>
<div class="w-16 h-1 bg-purple-light my-3 md:my-6"></div>
<p class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal">
@yield('message')
</p>
<a href="{{ app('router')->has('home') ? route('home') : url('/') }}">
<button class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg">
{{ __('Go Home') }}
</button>
</a>
</div>
</div>
<div class="relative pb-full md:flex md:pb-0 md:min-h-screen w-full md:w-1/2">
@yield('image')
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,168 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title')</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<!-- Styles -->
<style>
html, body {
background-color: #ECF6FF;
color: #1e2125;
font-family: 'Nunito', sans-serif;
font-weight: 100;
height: 100vh;
margin: 0;
}
*,
*::before,
*::after {
-webkit-box-sizing: inherit;
box-sizing: inherit;
}
button,
input {
font-family: sans-serif;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
button,
input {
overflow: visible;
}
button {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
button {
background: transparent;
padding: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
*,
*::before,
*::after {
border-width: 0;
border-style: solid;
border-color: #dae1e7;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
border-radius: 0;
}
button,
input {
font-family: inherit;
}
button,
[role=button] {
cursor: pointer;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
flex-direction: column;
}
.position-ref {
position: relative;
}
.content {
text-align: center;
}
.title {
font-size: 36px;
padding: 20px;
}
.bg-transparent {
background-color: transparent;
}
.text-grey-darkest {
color: #3d4852;
}
.font-bold {
font-weight: 700;
}
.uppercase {
text-transform: uppercase;
}
.tracking-wide {
letter-spacing: .05em;
}
.border-grey-light {
border-color: #dae1e7;
}
.hover\:border-grey:hover {
border-color: #b8c2cc;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
<img src="{{ asset('http://supportweb.local/images/supportweb_128.png') }}" alt="{{ config('app.name') }}">
<div class="content">
<div class="title">
@yield('message')
</div>
@hasSection('redirect')
<a href="{{ app('router')->has('home') ? route('home') : url('/') }}">
<button class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg">
{{ __('Retourner à l\'accueil') }}
</button>
</a>
@endif
</div>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -28,7 +28,7 @@
@endif
<ul class="navbar-nav me-auto">
<li class="nav-item">
<span class="nav-link text-primary fw-bold">{{ __('Tickets') }}</span>
<span class="nav-link text-info fw-bold">{{ __('Tickets') }}</span>
</li>
<li class="nav-item">
<span class="nav-link text-light">{{ __('Nouveau') }}&nbsp;:&nbsp;<span class="text-info">{{ $ticketsNew }}</span></span>
@ -37,9 +37,7 @@
<span class="nav-link text-light">{{ __('En cours') }}&nbsp;:&nbsp;<span class="text-info">{{ $ticketsInProgress }}</span></span>
</li>
<li class="nav-item">
{{-- Mouhahahaha --}}
<span class="nav-link text-light">{{ __('Charge') }}&nbsp;:&nbsp;<span class="text-danger">{{ __('Elevée') }}</span></span>
{{-- <span class="nav-link text-dark">Charge&nbsp;:&nbsp;<span class="text-info">Stable</span></span> --}}
@include('modules.loadLevel')
</li>
</ul>
</div>
@ -64,6 +62,9 @@
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('users.passwordReset') }}">
{{ __('Identifiants') }}
</a>
<a class="dropdown-item" href="#"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">

View file

@ -1,67 +0,0 @@
@extends('layout.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Connexion</div>
<div class="card-body">
<form method="POST" action="{{ route('authenticate') }}">
@csrf
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{!! $error !!}</li>
@endforeach
</ul>
</div>
@endif
@if (session('success'))
<div class="alert alert-success">
{!! session('success') !!}
</div>
@endif
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">Adresse email</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">Mot de passe</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
</div>
</div>
<div class="form-group row">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
Se souvenir de moi
</label>
</div>
</div>
</div>
<div class="form-group text-center">
<div class="col-md-8 offset-md-4">
<input type="submit" class="btn btn-primary" value="Connexion">
{{-- <a class="btn btn-link" href="{{route('forgot-password')}}">Mot de passe oublié ?</a> --}}
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,16 @@
<span class="nav-link">{{ __('Charge') }}&nbsp;:&nbsp;
@switch(Utils::computeWorkload())
@case(LoadLevel::weak())
<span class="p-1 rounded bg-dark fw-bold text-light">{{ Utils::computeWorkload()->label }}</span>
@break
@case(LoadLevel::stable())
<span class="p-1 rounded bg-success fw-bold text-dark">{{ Utils::computeWorkload()->label }}</span>
@break
@case(LoadLevel::high())
<span class="p-1 rounded bg-warning fw-bold text-dark">{{ Utils::computeWorkload()->label }}</span>
@break
@case(LoadLevel::overload())
<span class="p-1 rounded bg-danger fw-bold text-dark">{{ Utils::computeWorkload()->label }}</span>
@break
@endswitch
</span>

View file

@ -116,7 +116,7 @@ $userPr = Auth::user()->sites ?? collect([]);
$cls = (!$cls && $ticket->status == TicketStatus::inprogress()) ? 'text-dark' : $cls;
$cls = (!$cls && $ticket->status == TicketStatus::waiting()) ? 'text-white bg-danger' : $cls;
@endphp
<td><span class="{{ $cls }}">{{ $ticket->status->label }}</span></td>
<td><span class="rounded {{ $cls }}">{{ $ticket->status->label }}</span></td>
<td>
{{-- Modal du ticket --}}
<!-- Button trigger modal -->

View file

@ -0,0 +1,37 @@
@extends('layout.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Réinitialisation de mot de passe') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
@error('email')
<div class="alert alert-warning" role="alert">
{{ $message }}
</div>
@endif
<form method="POST" action="{{ route('users.passwordResetSubmit') }}">
@csrf
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Réinitialiser le mot de passe') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,7 @@
<tr>
<td class="header">
<a href="{{ $url }}" style="display: inline-block;">
<img src="{{ asset('images/supportweb_128.png') }}" class="logo" alt="{{ config('app.name') }} Logo">
</a>
</td>
</tr>

View file

@ -8,6 +8,7 @@ use App\Http\Controllers\ProjectController;
use App\Http\Controllers\SiteController;
use App\Http\Controllers\TicketController;
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
/*
@ -22,10 +23,26 @@ use Illuminate\Support\Facades\Route;
*/
Route::get('/', [HomeController::class, 'home'])->middleware('guest')->name('home');
Route::get('/login', [LoginController::class, 'index'])->name('login');
Route::post('/login', [LoginController::class, 'authenticate'])->name('authenticate');
Route::middleware(['throttle:credentials'])->group(function () {
// guest:web middleware specified in AuthController
Auth::routes([
'login' => true,
'logout' => true,
'register' => false,
'reset' => true, // for resetting passwords
'confirm' => true, // for additional password confirmations
'verify' => false, // for email verification
]);
});
Route::middleware(['auth'])->group(function () {
Route::post('/logout', [LoginController::class, 'logout'])->name('logout');
// Logged user actions
Route::view('/users/change-password', 'users.passwordReset')->name('users.passwordReset');
Route::post('/users/change-password', [UserController::class, 'passwordResetSubmit'])
->name('users.passwordResetSubmit');
// Ticket
Route::resource('ticket', TicketController::class)->except(['destroy']);
Route::resource('users', UserController::class);