Merge branch 'migration' into develop
This commit is contained in:
commit
25b2e42f14
69 changed files with 3944 additions and 777 deletions
22
.vscode/settings.json
vendored
Normal file
22
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#90431a",
|
||||
"activityBar.background": "#90431a",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#1da24b",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"sash.hoverBorder": "#90431a",
|
||||
"statusBar.background": "#652f12",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#90431a",
|
||||
"statusBarItem.remoteBackground": "#652f12",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#652f12",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#652f1299",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
"peacock.remoteColor": "#652f12"
|
||||
}
|
||||
16
app/Enums/Pagination/ItemsPerPaginationEnum.php
Normal file
16
app/Enums/Pagination/ItemsPerPaginationEnum.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Pagination;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum ItemsPerPaginationEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case five = 5;
|
||||
case twelve = 12;
|
||||
case twentyfive = 25;
|
||||
case fifty = 50;
|
||||
case onehundred = 100;
|
||||
}
|
||||
|
|
@ -13,10 +13,10 @@ enum RayEnum : int
|
|||
case freshlyPackaged = 3;
|
||||
case roastery = 4;
|
||||
case caterer = 5;
|
||||
case creamery = 6;
|
||||
case groceryStore = 7;
|
||||
case frozen = 8;
|
||||
case others = 9;
|
||||
case creamery = 7;
|
||||
case groceryStore = 8;
|
||||
case frozen = 9;
|
||||
case others = 10;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
|
|
|
|||
29
app/Http/Controllers/BrandController.php
Normal file
29
app/Http/Controllers/BrandController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Brands\StoreBrandRequest;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BrandController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Brands\StoreBrandRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(StoreBrandRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$brand = new Brand();
|
||||
$brand->fill($request->validated());
|
||||
if ($brand->saveOrFail()) {
|
||||
return \response()->json(['status' => true, 'brand' => $brand]);
|
||||
}
|
||||
return \response()->json(['status' => false, 'message' => __('La marque n\'a pas pu être créée')]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,18 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\Pagination\ItemsPerPaginationEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
|
|
@ -122,4 +127,26 @@ class Controller extends BaseController
|
|||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize pagination with cache or config (default).
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
*/
|
||||
protected function paginate(Builder $query, ItemsPerPaginationEnum $pagination = null): LengthAwarePaginator
|
||||
{
|
||||
$currentRoutePath = Str::slug(request()->route()->getName());
|
||||
$cacheKey = "pagination.{$currentRoutePath}";
|
||||
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve->value;
|
||||
$pagination = ToolboxHelper::getValidatedPaginate(
|
||||
Cache::get($cacheKey, $pagination)
|
||||
);
|
||||
if (Cache::get($cacheKey) !== $pagination) {
|
||||
Cache::put($cacheKey, $pagination);
|
||||
}
|
||||
$query = $query->paginate($pagination);
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use App\Models\Product;
|
|||
use App\Traits\Controllers\HasDocuments;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ class ProductController extends Controller
|
|||
public function index(Request $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $brands */
|
||||
$brands = Brand::get();
|
||||
$brands = Brand::query();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $products */
|
||||
$products = Product::query();
|
||||
|
|
@ -37,16 +38,14 @@ class ProductController extends Controller
|
|||
|
||||
$this->sortQuery($products);
|
||||
|
||||
$products = $products->paginate(10);
|
||||
$products = $this->paginate($products);
|
||||
|
||||
/** @var array $printMultiProduct Array of id */
|
||||
$printMultiProduct = session('printMultiProduct') ?? [];
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $brands */
|
||||
$brands = Brand::get();
|
||||
$brands = $brands->get();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $keyword */
|
||||
$keywords = Keyword::where('keyword_cat', 'LIKE', 'product_%')->get();
|
||||
$keywords = Keyword::query()->where('keyword_cat', 'LIKE', 'product_%')->get();
|
||||
|
||||
return view('pages.products.index', [
|
||||
'products' => $products,
|
||||
|
|
@ -56,49 +55,6 @@ class ProductController extends Controller
|
|||
'queryParameters' => $request->all(),
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $products
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
|
||||
*/
|
||||
private function requestFilters(Builder $products, Request $request): \Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
//phpcs:enable
|
||||
foreach ($request->all() as $key => $queryParameter) {
|
||||
if (request()->sort_col === null && request()->sort_way === null && request()->rst === null) {
|
||||
$products->when(
|
||||
isset($queryParameter) && $queryParameter != 0,
|
||||
function ($q) use ($queryParameter, $key) {
|
||||
if ($key === "name") {
|
||||
$q->where($key, 'LIKE', '%' . $queryParameter . '%');
|
||||
} elseif ($key === "claims" || $key === "origins") {
|
||||
foreach ($queryParameter as $item) {
|
||||
$q->where($key, 'LIKE', '%{"id":"' . $item['value'] . '"}%');
|
||||
}
|
||||
} elseif ($key === "keywords") {
|
||||
foreach ($queryParameter as $keyword) {
|
||||
$q->whereHas('productKeywords', function ($q) use ($keyword, $key) {
|
||||
$q->where($key . '.id', intval($keyword['value']));
|
||||
});
|
||||
}
|
||||
} elseif ($key === "date_before" || $key === "date_after") {
|
||||
if ($key === "date_before") {
|
||||
$q->whereDate('created_at', '>=', $queryParameter);
|
||||
} else {
|
||||
$q->whereDate('created_at', '<=', $queryParameter);
|
||||
}
|
||||
} else {
|
||||
$q->where($key, $queryParameter);
|
||||
} //end if
|
||||
}
|
||||
);
|
||||
} //end if
|
||||
} //end foreach
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
|
|
@ -110,11 +66,11 @@ class ProductController extends Controller
|
|||
public function create(Product $productModel): \Illuminate\Contracts\View\View
|
||||
{
|
||||
//phpcs:enable
|
||||
$brands = Brand::get();
|
||||
$productsFair = Product::pluck('fair');
|
||||
$productsShop = Product::pluck('shop');
|
||||
$productsCatalog = Product::pluck('catalogue');
|
||||
$keywords = Keyword::where('keyword_cat', 'product_pleasure')
|
||||
$brands = Brand::all();
|
||||
$productsFair = Product::query()->pluck('fair');
|
||||
$productsShop = Product::query()->pluck('shop');
|
||||
$productsCatalog = Product::query()->pluck('catalogue');
|
||||
$keywords = Keyword::query()->where('keyword_cat', 'product_pleasure')
|
||||
->orWhere('keyword_cat', 'product_shape')
|
||||
->orWhere('keyword_cat', 'product_practicality')
|
||||
->orWhere('keyword_cat', 'product_health')
|
||||
|
|
@ -170,12 +126,12 @@ class ProductController extends Controller
|
|||
*/
|
||||
public function edit(Product $product)
|
||||
{
|
||||
$brands = Brand::get();
|
||||
$productsFair = Product::pluck('fair');
|
||||
$productsShop = Product::pluck('shop');
|
||||
$productsCatalog = Product::pluck('catalogue');
|
||||
$brands = Brand::all();
|
||||
$productsFair = Product::query()->pluck('fair');
|
||||
$productsShop = Product::query()->pluck('shop');
|
||||
$productsCatalog = Product::query()->pluck('catalogue');
|
||||
$commentModel = $product->productComments;
|
||||
$keywords = Keyword::where('keyword_cat', 'product_pleasure')
|
||||
$keywords = Keyword::query()->where('keyword_cat', 'product_pleasure')
|
||||
->orWhere('keyword_cat', 'product_shape')
|
||||
->orWhere('keyword_cat', 'product_practicality')
|
||||
->orWhere('keyword_cat', 'product_health')
|
||||
|
|
@ -312,20 +268,20 @@ class ProductController extends Controller
|
|||
public function checkGencode(Request $request)
|
||||
{
|
||||
if (
|
||||
isset($request->GENCODE) &&
|
||||
$request->GENCODE !== null &&
|
||||
ctype_digit($request->GENCODE) &&
|
||||
strlen($request->GENCODE) > 0
|
||||
isset($request->GENCOD) &&
|
||||
$request->GENCOD !== null &&
|
||||
ctype_digit($request->GENCOD) &&
|
||||
strlen($request->GENCOD) > 0
|
||||
) {
|
||||
if (
|
||||
isset($request->ID) &&
|
||||
$request->ID !== null &&
|
||||
intval($request->GENCODE) === Product::where('id', $request->ID)->firstOrFail()->gencod
|
||||
intval($request->GENCOD) === Product::where('id', $request->ID)->firstOrFail()->gencod
|
||||
) {
|
||||
$result = null;
|
||||
} else {
|
||||
$result = in_array(intval($request->GENCODE), Product::pluck('gencod')->toArray()) ?
|
||||
Product::orderBy('created_at', 'desc')->where('gencod', intval($request->GENCODE))->first() : null;
|
||||
$result = in_array(intval($request->GENCOD), Product::pluck('gencod')->toArray()) ?
|
||||
Product::orderBy('created_at', 'desc')->where('gencod', intval($request->GENCOD))->first() : null;
|
||||
}
|
||||
} else {
|
||||
$result = null;
|
||||
|
|
@ -427,4 +383,49 @@ class ProductController extends Controller
|
|||
{
|
||||
\dispatch(new ExportProduct($request->id))->onConnection('database');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $products
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
|
||||
*/
|
||||
private function requestFilters(Builder $products, Request $request): \Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
//phpcs:enable
|
||||
$queryFetch = ['name', 'claims', 'origins', 'keywords', 'date_before', 'date_after'];
|
||||
foreach (Arr::only($request->all(), $queryFetch) as $key => $queryParameter) {
|
||||
if ($request->sort_col === null && $request->sort_way === null && $request->rst === null) {
|
||||
$products->when(
|
||||
isset($queryParameter) && $queryParameter != 0,
|
||||
function ($q) use ($queryParameter, $key) {
|
||||
if ($key === "name") {
|
||||
$q->where($key, 'LIKE', '%' . $queryParameter . '%');
|
||||
} elseif ($key === "claims" || $key === "origins") {
|
||||
foreach ($queryParameter as $item) {
|
||||
$q->where($key, 'LIKE', '%{"id":"' . $item['value'] . '"}%');
|
||||
}
|
||||
} elseif ($key === "keywords") {
|
||||
foreach ($queryParameter as $keyword) {
|
||||
$q->whereHas('productKeywords', function ($q) use ($keyword, $key) {
|
||||
$q->where($key . '.id', intval($keyword['value']));
|
||||
});
|
||||
}
|
||||
} elseif ($key === "date_before" || $key === "date_after") {
|
||||
if ($key === "date_before") {
|
||||
$q->whereDate('created_at', '>=', $queryParameter);
|
||||
} else {
|
||||
$q->whereDate('created_at', '<=', $queryParameter);
|
||||
}
|
||||
} else {
|
||||
$q->where($key, $queryParameter);
|
||||
} //end if
|
||||
}
|
||||
);
|
||||
} //end if
|
||||
} //end foreach
|
||||
return $products;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
42
app/Http/Requests/Brands/StoreBrandRequest.php
Normal file
42
app/Http/Requests/Brands/StoreBrandRequest.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Brands;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreBrandRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|min:3|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'name' => trans('nom de la marque'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,9 @@ class StoreProductRequest extends FormRequest
|
|||
'is_tasted' => $this->is_tasted ? true : false,
|
||||
'origins' => $this->origins ? $this->origins : [],
|
||||
'claims' => $this->claims ? $this->claims : [],
|
||||
'picture_alt' => $this->name,
|
||||
'picture_title' => $this->name,
|
||||
'certification' => $this->certification == 0 ? null : $this->certification,
|
||||
]);
|
||||
$this->mergePicture('picture');
|
||||
$this->mergeDocuments('docs');
|
||||
|
|
@ -76,19 +79,17 @@ class StoreProductRequest extends FormRequest
|
|||
}
|
||||
],
|
||||
'certification' => ['nullable', new Enum(CertificationEnum::class)],
|
||||
'origins' => 'nullable|array',
|
||||
'origins.*' => 'present|array',
|
||||
'origins.*.id' => [new Enum(OriginEnum::class)],
|
||||
'claims' => 'nullable|array',
|
||||
'claims.*' => 'present|array',
|
||||
'claims.*.id' => [new Enum(ClaimEnum::class)],
|
||||
'origins' => 'present|array',
|
||||
'origins.*' => [new Enum(OriginEnum::class)],
|
||||
'claims' => 'present|array',
|
||||
'claims.*' => [new Enum(ClaimEnum::class)],
|
||||
'specified_claim' => 'string|min:3|max:255|required_if:claims,' . ClaimEnum::others->value,
|
||||
'distribution_network' => ['required', new Enum(DistributionNetworkEnum::class)],
|
||||
'ray' => ['required', new Enum(RayEnum::class)],
|
||||
'product_range' => ['required', new Enum(RangeEnum::class)],
|
||||
'geo_area' => ['required', new Enum(GeoAreaEnum::class)],
|
||||
'weight' => 'nullable|integer',
|
||||
'pvc' => 'nullable|integer',
|
||||
'pvc' => 'nullable|decimal:0,2',
|
||||
'comment' => 'nullable|string|min:15',
|
||||
'is_tasted' => 'required|boolean',
|
||||
'tasters_number' => 'nullable|integer',
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ProductResource extends JsonResource
|
|||
'user_id' => null,
|
||||
'brand_id' => null,
|
||||
'name' => null,
|
||||
'saw_at' => null,
|
||||
'seen_at' => null,
|
||||
'source' => null,
|
||||
'fair' => null,
|
||||
'shop' => null,
|
||||
|
|
@ -112,7 +112,7 @@ class ProductResource extends JsonResource
|
|||
'user_id' => User::where('id', $this->product->user_id)->firstOrFail(),
|
||||
'brand_id' => Brand::where('id', $this->product->brand_id)->firstOrFail(),
|
||||
'name' => $this->product->name,
|
||||
'saw_at' => Carbon::createFromFormat('Y-m-d H:i:s', $this->product->saw_at)->format('d/m/Y'),
|
||||
'seen_at' => Carbon::createFromFormat('Y-m-d H:i:s', $this->product->seen_at)->format('d/m/Y'),
|
||||
'source' => $this->product->source->label(),
|
||||
'fair' => $this->product->fair,
|
||||
'shop' => $this->product->shop,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Lib\Helpers;
|
||||
|
||||
use App\Enums\Pagination\ItemsPerPaginationEnum;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
|
@ -9,6 +10,7 @@ use Illuminate\Support\Facades\Session;
|
|||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
|
|
@ -288,4 +290,22 @@ class ToolboxHelper
|
|||
{
|
||||
return (array_diff($array1, $array2) == [] && array_diff($array2, $array1) == []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get validated pagination from request
|
||||
*
|
||||
* @param integer $default
|
||||
* @return integer
|
||||
*/
|
||||
public static function getValidatedPaginate(int $default): int
|
||||
{
|
||||
try {
|
||||
return \intval(Validator::make(
|
||||
['pagination' => \request()->get('pagination')],
|
||||
['pagination' => new Enum(ItemsPerPaginationEnum::class)]
|
||||
)->validated()['pagination']);
|
||||
} catch (ValidationException $e) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $brand
|
||||
* @property string $name
|
||||
* @property string $filter
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ use Illuminate\Database\Eloquent\Model;
|
|||
/**
|
||||
* @property integer $id
|
||||
* @property string $label
|
||||
* @property string $keyword_cat
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property \App\Enums\KeywordCategoryEnum $keyword_cat
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property Keywordable[] $keywordables
|
||||
*/
|
||||
class Keyword extends Model
|
||||
|
|
@ -39,7 +39,7 @@ class Keyword extends Model
|
|||
*/
|
||||
public function keywordables()
|
||||
{
|
||||
return $this->hasMany('App\Models\Keywordable');
|
||||
return $this->hasMany(Keywordable::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
|
|||
* @property integer $brand_id
|
||||
* @property string $name
|
||||
* @property string $brand
|
||||
* @property string $saw_at
|
||||
* @property string $seen_at
|
||||
* @property enum $source
|
||||
* @property string $fair
|
||||
* @property string $shop
|
||||
|
|
@ -62,8 +62,9 @@ class Product extends Model
|
|||
'user_id',
|
||||
'brand_id',
|
||||
'name',
|
||||
// ? FIXME: ?why is there brand here, column is not in DB.
|
||||
'brand',
|
||||
'saw_at',
|
||||
'seen_at',
|
||||
'source',
|
||||
'fair',
|
||||
'shop',
|
||||
|
|
@ -108,7 +109,7 @@ class Product extends Model
|
|||
'product_range' => RangeEnum::class,
|
||||
'geo_area' => GeoAreaEnum::class,
|
||||
'is_tasted' => 'boolean',
|
||||
'saw_at' => 'datetime:Y-m-d',
|
||||
'seen_at' => 'datetime:Y-m-d',
|
||||
'created_at' => 'datetime:Y-m-d',
|
||||
'updated_at' => 'datetime:Y-m-d',
|
||||
'deleted_at' => 'datetime:Y-m-d',
|
||||
|
|
@ -131,7 +132,7 @@ class Product extends Model
|
|||
$model->user_id = 1;
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
self::deleteOldDocuments($model);
|
||||
self::deleteOldDocuments($model, true);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
self::updateSource($model);
|
||||
self::updateSpecifiedClaim($model);
|
||||
|
|
@ -170,8 +171,9 @@ class Product extends Model
|
|||
*/
|
||||
private static function updateSpecifiedClaim(Product $model)
|
||||
{
|
||||
foreach ($model->claims as $claim) {
|
||||
$check = (intval($claim['id']) === \App\Enums\Products\ClaimEnum::others->value) ? true : false;
|
||||
$check = false;
|
||||
foreach ($model->claims as $claimValue) {
|
||||
$check = (intval($claimValue) === \App\Enums\Products\ClaimEnum::others->value) ? true : false;
|
||||
}
|
||||
$model->specified_claim = ($check) ? $model->specified_claim : null;
|
||||
}
|
||||
|
|
@ -207,15 +209,23 @@ class Product extends Model
|
|||
/**
|
||||
* Delete old documents.
|
||||
*
|
||||
* @param self $model
|
||||
* @param self $model
|
||||
* @param boolean $onlyIfUpdated
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteOldDocuments(self $model)
|
||||
public static function deleteOldDocuments(self $model, bool $onlyIfUpdated = false)
|
||||
{
|
||||
foreach ($model->getOriginal('docs') as $document) {
|
||||
$original = \collect($model->getOriginal('docs'))->map(fn ($document) => strval($document));
|
||||
$new = \collect($model->docs)->map(fn ($document) => strval($document))->flip();
|
||||
foreach ($original as $document) {
|
||||
// * Ignore if document is to keep.
|
||||
if ($onlyIfUpdated and $new->has($document)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$originalPath = \ltrim(\urldecode($document), '/');
|
||||
if (\strpos($originalPath, 'storage') !== 0) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
$originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath);
|
||||
if (Storage::disk('public')->exists($originalPath)) {
|
||||
|
|
|
|||
|
|
@ -17,12 +17,10 @@ trait HasDocuments
|
|||
*/
|
||||
private function storeDocuments(Request $request, Model $model): void
|
||||
{
|
||||
if (is_array($request->docs)) {
|
||||
$model->docs = [];
|
||||
$model->docs = collect($request->docs)
|
||||
->map(function ($document) use ($model) {
|
||||
return FileStorageHelper::storeFile($model, $document);
|
||||
})->all();
|
||||
}
|
||||
$model->docs = [];
|
||||
$model->docs = collect($request->docs)
|
||||
->map(function ($document) use ($model) {
|
||||
return FileStorageHelper::storeFile($model, $document);
|
||||
})->all();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,8 +62,9 @@ trait HasDocument
|
|||
protected function mergeDocuments(string $name = 'documents'): void
|
||||
{
|
||||
$documents = $this->{$name};
|
||||
$this->merge(['doc' => $this->docs ?? []]);
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
if (is_array($documents)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
foreach ($documents as $key => $documentPath) {
|
||||
// ! Empty document.
|
||||
if ($documentPath === 'false') {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"carlos-meneses/laravel-mpdf": "^2.1",
|
||||
"diglactic/laravel-breadcrumbs": "^8.0",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"kwaadpepper/image-resizer": "^2.1",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/tinker": "^2.8"
|
||||
},
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
"fakerphp/faker": "^1.9.1",
|
||||
"kitloong/laravel-migrations-generator": "^6.8",
|
||||
"krlove/eloquent-model-generator": "dev-l10-compatibility",
|
||||
"laravel-lang/common": "^4.0",
|
||||
"laravel/pint": "^1.0",
|
||||
"laravel/sail": "^1.18",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
|
|
|
|||
1145
composer.lock
generated
1145
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -63,6 +63,18 @@ return [
|
|||
]) : [],
|
||||
],
|
||||
|
||||
'oracle' => [
|
||||
'driver' => 'oracle',
|
||||
'host' => env('DB_OCI_HOST', ''),
|
||||
'port' => env('DB_OCI_PORT', '1521'),
|
||||
'database' => env('DB_OCI_DATABASE', ''),
|
||||
'username' => env('DB_OCI_USERNAME', ''),
|
||||
'password' => env('DB_OCI_PASSWORD', ''),
|
||||
'charset' => env('DB_OCI_CHARSET', 'AL32UTF8'),
|
||||
'prefix' => env('DB_OCI_PREFIX', ''),
|
||||
'prefix_schema' => env('DB_OCI_SCHEMA_PREFIX', ''),
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DATABASE_URL'),
|
||||
|
|
|
|||
69
config/image-resizer.php
Normal file
69
config/image-resizer.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
/**
|
||||
* The php image driver to use
|
||||
* this can be 'imagick' or 'gd'
|
||||
*/
|
||||
'driver' => 'imagick',
|
||||
|
||||
/**
|
||||
* The cache driver to use as defined
|
||||
* in config/cache of your app
|
||||
*/
|
||||
'cache' => 'file',
|
||||
|
||||
/**
|
||||
* The path where to store image in cache
|
||||
*/
|
||||
'cachePath' => 'cache/images',
|
||||
|
||||
/**
|
||||
* Image cache life time
|
||||
* This is given in minutes
|
||||
* Here the cache for images is
|
||||
* one hour
|
||||
*/
|
||||
'lifetime' => 60,
|
||||
|
||||
/**
|
||||
* Format can specifiy to which format save the image
|
||||
* JPEG PNG GIF TIF BMP ICO PSD WebP
|
||||
* GD ✔️ ✔️ ✔️ - - - - ✔️ *
|
||||
* Imagick ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ *
|
||||
* For WebP support GD driver must be used with PHP 5 >= 5.5.0 or PHP 7
|
||||
* in order to use imagewebp(). If Imagick is used, it must be compiled
|
||||
* with libwebp for WebP support.
|
||||
* resize => will resize the image (boolean)
|
||||
* fit => Combine cropping and resizing to format image in a smart way (boolean)
|
||||
* keepRatio => will keep image ratio wile resizing (boolean)
|
||||
* trim => boolean to trim the image using border color
|
||||
* inCanvas => to make sure image boundarie is respected
|
||||
* format => select tha wantd ouput form, yan can just convert images if you want
|
||||
* example :
|
||||
* 'small' => [
|
||||
* 'height' => 500,
|
||||
* 'width' => 250,
|
||||
* 'inCanvas' => true,
|
||||
* 'trim' => ['transparent', null, 10]
|
||||
* ],
|
||||
* 'smallWebp' => [
|
||||
* 'height' => 500,
|
||||
* 'width' => 250,
|
||||
* 'inCanvas' => true,
|
||||
* 'format' => 'webp'
|
||||
* ],
|
||||
* 'justconvertImage' => [
|
||||
* 'format' => 'webp'
|
||||
* ]
|
||||
*/
|
||||
'templates' => [
|
||||
'productTiny' => [
|
||||
'height' => 180,
|
||||
'width' => 180,
|
||||
'resize' => true,
|
||||
'keepRatio' => true,
|
||||
'format' => 'jpg',
|
||||
],
|
||||
]
|
||||
];
|
||||
|
|
@ -25,7 +25,7 @@ final class BrandFactory extends Factory
|
|||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->word,
|
||||
'name' => fake()->unique()->word,
|
||||
'filter' => fake()->word,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ final class ProductFactory extends Factory
|
|||
'user_id' => fake()->randomElement(User::pluck('id')),
|
||||
'brand_id' => fake()->randomElement(Brand::pluck('id')),
|
||||
'name' => fake()->unique()->word . " " . fake()->unique()->word,
|
||||
'saw_at' => fake()->dateTime(),
|
||||
'seen_at' => fake()->dateTime(),
|
||||
'source' => $source,
|
||||
'fair' => $fair,
|
||||
'shop' => $shop,
|
||||
|
|
@ -61,8 +61,8 @@ final class ProductFactory extends Factory
|
|||
'docs' => [],
|
||||
'url' => fake()->url,
|
||||
'certification' => \collect(CertificationEnum::toArray())->random()->value,
|
||||
'origins' => [["id" => strval(\collect(OriginEnum::toArray())->random()->value)]],
|
||||
'claims' => [["id" => strval($claim)]],
|
||||
'origins' => [\collect(OriginEnum::toArray())->random()->value],
|
||||
'claims' => [$claim],
|
||||
'specified_claim' => ($claim == OriginEnum::others->value) ? fake()->text : null,
|
||||
'distribution_network' => \collect(DistributionNetworkEnum::toArray())->random()->value,
|
||||
'ray' => \collect(RayEnum::toArray())->random()->value,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ return new class extends Migration
|
|||
$table->bigInteger('user_id')->index();
|
||||
$table->bigInteger('brand_id')->index()->comment('Marque - Required - Texte libre avec auto-suggestion sur la table elle même');
|
||||
$table->string('name')->comment('Nom du produit - Required');
|
||||
$table->timestamp('saw_at')->useCurrentOnUpdate()->useCurrent()->comment('Produit vue le (champs date)');
|
||||
$table->timestamp('seen_at')->useCurrentOnUpdate()->useCurrent()->comment('Produit vue le (champs date)');
|
||||
$table->unsignedTinyInteger('source')->comment('Source - Required - (valeur de l\'enum SourceEnum) -> champ SELECT');
|
||||
$table->string('fair')->nullable()->comment('Salon - Affiché en front si SourceEnum = Salon Professionnel ou Salon Clients ou Salon Grand Public - Texte libre avec auto-suggestion sur la table elle même');
|
||||
$table->string('shop')->nullable()->comment('Magasin - Affiché en front si SourceEnum = Magasin - Texte libre avec auto-suggestion sur la table elle même');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->decimal('pvc', 12, 2)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->integer('pvc')->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
786
database/migrations/2023_12_06_124752_migrate_oci_tables.php
Normal file
786
database/migrations/2023_12_06_124752_migrate_oci_tables.php
Normal file
|
|
@ -0,0 +1,786 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\KeywordCategoryEnum;
|
||||
use App\Enums\Products\CertificationEnum;
|
||||
use App\Enums\Products\ClaimEnum;
|
||||
use App\Enums\Products\DistributionNetworkEnum;
|
||||
use App\Enums\Products\GeoAreaEnum;
|
||||
use App\Enums\Products\OriginEnum;
|
||||
use App\Enums\Products\RangeEnum;
|
||||
use App\Enums\Products\RayEnum;
|
||||
use App\Enums\Products\SourceEnum;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Keyword;
|
||||
use App\Models\Product;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Database\Query\Builder;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* StartC table prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $tablePrefix = 'STARTC';
|
||||
|
||||
/**
|
||||
* StartC table to migrate
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $tableList = [
|
||||
'FAMILLE', 'CERTIFICATION',
|
||||
'RAYON', 'RESEAU',
|
||||
'SECTEUR', 'SOURCE',
|
||||
'VEILLE_PROD', 'VEILLE_PROD_COMM',
|
||||
];
|
||||
|
||||
/** @var \App\Models\User */
|
||||
private $dummyUser;
|
||||
|
||||
/** @var \App\Models\Brand */
|
||||
private $dummyBrand;
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
* @throws \RuntimeException In case of failure.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// ! DEV: Uncomment this to skip the migration
|
||||
// return;
|
||||
$console = new ConsoleOutput();
|
||||
$section = $console->section();
|
||||
|
||||
// * SEED KEYWORDS As they are needed on runtime.
|
||||
Schema::disableForeignKeyConstraints();
|
||||
DB::table('brands')->truncate();
|
||||
DB::table('keywordables')->truncate();
|
||||
DB::table('keywords')->truncate();
|
||||
DB::table('products')->truncate();
|
||||
// * Remove previously uploaded files.
|
||||
File::deleteDirectory(storage_path('app/public/modelfiles/products'));
|
||||
Schema::enableForeignKeyConstraints();
|
||||
if (Artisan::call('db:seed', ['--class' => 'KeywordInitSeeder']) !== 0) {
|
||||
throw new \RuntimeException('Failed to seed keywords');
|
||||
}
|
||||
|
||||
// * Seed a dummy user.
|
||||
$this->dummyUser = User::factory()->createOne([
|
||||
'firstname' => 'Dummy',
|
||||
'lastname' => 'User'
|
||||
]);
|
||||
// * Seed a dummy brand.
|
||||
$this->dummyBrand = Brand::factory()->createOne([
|
||||
'name' => Str::of('Sans Marque')->slug()->studly(),
|
||||
'filter' => null
|
||||
]);
|
||||
|
||||
// * Apply DB corrections
|
||||
$this->setUniqueOnBrandName();
|
||||
$this->setJsonColumnsAsJsonOnProducts();
|
||||
$this->fixForeignKeyActionOnKeywordables();
|
||||
$this->fixForeignKeyActionOnProcesses();
|
||||
|
||||
// ! DEV: Comment this after running once, to develop a migration without remigrating every temp table.
|
||||
// $this->createAndFillTempTables();
|
||||
|
||||
// * Migrate PRODUCTS, comment all except the one you want to develop here.
|
||||
$this->migrateBrands();
|
||||
$this->migrateProducts();
|
||||
|
||||
// ! DEV: Add a die here to develop migration and prevent droping temp tables
|
||||
// dd('');
|
||||
|
||||
$this->dropTempTables();
|
||||
$section->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Error Wont rollback.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
throw new \Error('Wont rollback');
|
||||
}
|
||||
|
||||
// - MIGRATION METHODS -
|
||||
|
||||
/**
|
||||
* Migrate products
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
* @throws \Error In case of failure.
|
||||
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
|
||||
*/
|
||||
private function migrateProducts(): void
|
||||
{
|
||||
// phpcs:enable
|
||||
$otherAllegations = Str::of(File::get(base_path('database/migrations/other_allegations.txt')))
|
||||
->explode(PHP_EOL)->map(fn (string $other_allegation) => Str::of($other_allegation)->squish())
|
||||
->unique();
|
||||
|
||||
$MOTKS = Keyword::all();
|
||||
/** @var \Illuminate\Support\Collection<string,string> */
|
||||
$START_SOURCES = $this->getQuery('SOURCE')->get();
|
||||
/** @var \Illuminate\Support\Collection<string,string> */
|
||||
$START_CERTIFICATIONS = $this->getQuery('CERTIFICATION')->get();
|
||||
/** @var \Illuminate\Support\Collection<string,string> */
|
||||
$START_FAMILLES = $this->getQuery('FAMILLE')->get();
|
||||
/** @var \Illuminate\Support\Collection<string,string> */
|
||||
$START_SECTEURS = $this->getQuery('SECTEUR')->get();
|
||||
/** @var \Illuminate\Support\Collection<string,string> */
|
||||
$START_RAYONS = $this->getQuery('RAYON')->get();
|
||||
|
||||
// No need to paginate here, column name is unique.
|
||||
$brands = Brand::all();
|
||||
$sources = collect(SourceEnum::cases());
|
||||
$certifications = collect(CertificationEnum::cases());
|
||||
$claims = collect(ClaimEnum::cases());
|
||||
$ranges = collect(RangeEnum::cases());
|
||||
$geo_areas = collect(GeoAreaEnum::cases());
|
||||
$origins = collect(OriginEnum::cases());
|
||||
|
||||
$index = "NUM_VEILLE_PROD";
|
||||
$query = $this->getQuery("VEILLE_PROD")->orderBy($index);
|
||||
$nbRowsToMigrate = (clone $query)->count();
|
||||
$totalRowsToMigrate = $nbRowsToMigrate;
|
||||
|
||||
$console = new ConsoleOutput();
|
||||
$console->writeln('');
|
||||
$progress = new ProgressBar($console, $nbRowsToMigrate);
|
||||
$progress->setMessage(str_pad('Migrate Products', 24, ' '));
|
||||
$progress->setFormat(' - %message% [%bar%] %percent:3s%% %current%/%max% %elapsed:6s%/%estimated:-6s%');
|
||||
|
||||
while ($nbRowsToMigrate > 0) {
|
||||
$progress->display();
|
||||
$progress->advance(1);
|
||||
$row = (clone $query)->skip($totalRowsToMigrate - $nbRowsToMigrate--)->limit(1)->first();
|
||||
|
||||
$resolveAllegation = function (string $alleg) use ($claims, $otherAllegations) {
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
if ($alleg === 'Sans additif, sans colorant, sans conservateur,100% simple, sans polyphosphate,sans sel nutrite') {
|
||||
return ClaimEnum::withoutAdditives;
|
||||
}
|
||||
if (in_array(Str::of($alleg)->squish(), $otherAllegations->all())) {
|
||||
return ClaimEnum::others;
|
||||
}
|
||||
try {
|
||||
return $claims->firstOrFail(
|
||||
fn (ClaimEnum $claim) => Str::of($claim->label())->ascii()->replace(' ', '')
|
||||
== Str::of($alleg)->trim()->ascii()->replace(' ', '')
|
||||
);
|
||||
} catch (\Exception) {
|
||||
dump(
|
||||
$alleg,
|
||||
$claims->map(fn (ClaimEnum $claim) => $claim->label())
|
||||
);
|
||||
throw new \Error("allegation `{$alleg}` no found");
|
||||
}
|
||||
};
|
||||
$resolveOrigin = function (string $ori) use ($origins) {
|
||||
if (
|
||||
in_array(Str::of($ori)->squish(), [
|
||||
'N??,??lev?? et pr??par?? en France',
|
||||
'N???,???lev??? et pr???par??? en France'
|
||||
])
|
||||
) {
|
||||
return ClaimEnum::others;
|
||||
}
|
||||
try {
|
||||
return $origins->firstOrFail(
|
||||
fn (OriginEnum $origin) => Str::of($origin->label())->ascii()->replace(' ', '')
|
||||
== Str::of($ori)->trim()->ascii()->replace(' ', '')
|
||||
);
|
||||
} catch (\Exception) {
|
||||
dump(
|
||||
$ori,
|
||||
$origins->map(fn (OriginEnum $origin) => $origin->label())
|
||||
);
|
||||
throw new \Error("origin `{$ori}` no found");
|
||||
}
|
||||
};
|
||||
$resolveMotk = function (string $motk) use ($MOTKS) {
|
||||
// * Uu OMG !!!
|
||||
if ($motk === 'Gain de temps Nomadisme') {
|
||||
return $MOTKS->where('label', 'Gain de temps')
|
||||
->where('keyword_cat', KeywordCategoryEnum::productPracticality)->firstOrFail();
|
||||
}
|
||||
if ($motk === 'Vari??t?? des sens') {
|
||||
return $MOTKS->where('label', 'Variété des sens')
|
||||
->where('keyword_cat', KeywordCategoryEnum::productPleasure)->firstOrFail();
|
||||
}
|
||||
if ($motk === 'V??g??tal') {
|
||||
return $MOTKS->where('label', 'Végétal')
|
||||
->where('keyword_cat', KeywordCategoryEnum::productHealth)->firstOrFail();
|
||||
}
|
||||
try {
|
||||
return $MOTKS->firstOrFail(
|
||||
fn (Keyword $keyword) => Str::of($keyword->label)->ascii()->replace(' ', '')
|
||||
== Str::of($motk)->trim()->ascii()->replace(' ', '')
|
||||
);
|
||||
} catch (\Exception) {
|
||||
dump(
|
||||
$motk,
|
||||
$MOTKS->pluck('label')
|
||||
);
|
||||
throw new \Error("keyword `{$motk}` no found");
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9740 .
|
||||
if (is_null($row->MARQUE)) {
|
||||
continue;
|
||||
}
|
||||
if (!strlen(trim($row->MARQUE))) {
|
||||
$row->MARQUE = $this->dummyBrand->name;
|
||||
continue;
|
||||
}
|
||||
// NOTE: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9750 .
|
||||
if (empty($row->NUM_RESEAU)) {
|
||||
continue;
|
||||
}
|
||||
// NOTE: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9758 .
|
||||
if (empty($row->NUM_SECTEUR)) {
|
||||
continue;
|
||||
}
|
||||
// NOTE: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9760 .
|
||||
if (empty($row->NUM_RAYON)) {
|
||||
continue;
|
||||
}
|
||||
// NOTE: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9767 .
|
||||
if (empty($row->NUM_FAMILLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$brandName = Str::of($row->MARQUE)->trim()->title();
|
||||
/** name is unique on brand */
|
||||
$brand = null;
|
||||
try {
|
||||
$brand = $brands->where('name', $brandName)->firstOrFail();
|
||||
} catch (\Exception) {
|
||||
throw new \Error("brand` {$brandName}` not found");
|
||||
}
|
||||
$url = $row->URL ? Str::of(url($row->URL))->lower() : null;
|
||||
$date_saisie = $row->DATE_SAISIE ? $this->tryToParseCarbon($row->DATE_SAISIE, true) : null;
|
||||
$is_tasted = Str::of($row->DEGUSTER_O_N)->lower() === 'o' ? true : false;
|
||||
$claimList = collect([
|
||||
$row->ALLEG0, $row->ALLEG1, $row->ALLEG2
|
||||
])->filter()->map(fn (string $alleg) => $resolveAllegation($alleg));
|
||||
$originList = collect([
|
||||
$row->ORI0, $row->ORI1, $row->ORI2
|
||||
])->filter()->map(fn (string $origin) => $resolveOrigin($origin));
|
||||
$keywordsList = collect([
|
||||
$row->MOTK0, $row->MOTK1, $row->MOTK2, $row->MOTK3, $row->MOTK4, $row->MOTK5
|
||||
])->filter()->map(fn (string $motk) => $resolveMotk($motk));
|
||||
|
||||
$sourceEnum = null;
|
||||
if ($row->NUM_SOURCE) {
|
||||
$sourceEnum = $sources->firstOrFail(fn (SourceEnum $source) => $source->value() == $row->NUM_SOURCE);
|
||||
}
|
||||
|
||||
$certificationEnum = null;
|
||||
if ($row->CERTIFICATION) {
|
||||
$certificationEnum = $certifications->firstOrFail(
|
||||
fn (CertificationEnum $certification) => $certification->value() == $row->CERTIFICATION
|
||||
);
|
||||
}
|
||||
|
||||
$distributionNetwork = $row->NUM_RESEAU ? DistributionNetworkEnum::from($row->NUM_RESEAU) : null;
|
||||
$ray = $row->NUM_RAYON ? RayEnum::from($row->NUM_RAYON) : null;
|
||||
$product_range = null;
|
||||
if ($row->NUM_FAMILLE) {
|
||||
$product_range = $ranges->firstOrFail(
|
||||
fn (RangeEnum $range) => $range->value() == $row->NUM_FAMILLE
|
||||
);
|
||||
}
|
||||
$geo_area = null;
|
||||
if ($row->NUM_SECTEUR) {
|
||||
$geo_area = $geo_areas->firstOrFail(
|
||||
fn (GeoAreaEnum $geoArea) => $geoArea->value() == $row->NUM_SECTEUR
|
||||
);
|
||||
}
|
||||
|
||||
$claim = null;
|
||||
if ($row->COMP_ALLEG) {
|
||||
try {
|
||||
if (!$resolveAllegation($row->COMP_ALLEG)->equals(ClaimEnum::others)) {
|
||||
throw new \Error("`{$row->COMP_ALLEG}` is not resolved as `other`");
|
||||
}
|
||||
$claim = $row->COMP_ALLEG;
|
||||
} catch (\Exception) {
|
||||
throw new \Error("`{$row->COMP_ALLEG}` not found");
|
||||
}
|
||||
}
|
||||
|
||||
/** @var \App\Models\Product $product */
|
||||
$product = Product::query()->where('id', $row->{$index})->firstOrNew();
|
||||
$product->id = $row->NUM_VEILLE_PROD;
|
||||
|
||||
// * DOCS
|
||||
$disk = $this->getMigrationStorage('Produit/' . strval($row->{$index}));
|
||||
$files = $disk ? collect($disk->files())->mapWithKeys(function (string $file) use ($disk, $product) {
|
||||
$filePath = FileStorageHelper::storeFile($product, new \SplFileInfo($disk->path($file)));
|
||||
return [pathinfo($filePath, PATHINFO_BASENAME) => $filePath];
|
||||
}) : null;
|
||||
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
$pictureKey = $files ? $files->map(fn (string $filePath, string $fileName) => $fileName)
|
||||
->first(function ($fileName) {
|
||||
return preg_match('/(ref-?[0-9a-z]{0,}.jpg)/', $fileName) === 1;
|
||||
}) : null;
|
||||
$picture = $files ? $files->get($pictureKey) : null;
|
||||
if ($files) {
|
||||
$files->pull($pictureKey);
|
||||
$files = $files->values();
|
||||
}
|
||||
|
||||
$fill = [
|
||||
'brand_id' => $brand->id,
|
||||
'catalogue' => $row->CATALOGUE,
|
||||
'certification' => $certificationEnum,
|
||||
'comment' => $row->COMM_LIBRE,
|
||||
'distribution_network' => $distributionNetwork,
|
||||
'fair' => $row->SALON,
|
||||
'geo_area' => $geo_area,
|
||||
'name' => $row->NOM,
|
||||
'product_range' => $product_range,
|
||||
'ray' => $ray,
|
||||
'shop' => $row->MAGASIN,
|
||||
'source' => $sourceEnum,
|
||||
'pvc' => $row->PVC,
|
||||
'weight' => $row->POIDS,
|
||||
'is_tasted' => $is_tasted,
|
||||
'gencod' => $row->GENCOD,
|
||||
'url' => $url,
|
||||
'claims' => $claimList,
|
||||
'specified_claim' => $claim,
|
||||
'tasters_number' => $row->NB_DEGUST,
|
||||
'origins' => $originList,
|
||||
'docs' => $files,
|
||||
'picture_alt' => "Image de {$row->NOM}",
|
||||
'picture_title' => "Image de {$row->NOM}",
|
||||
'picture' => $picture,
|
||||
// NOTE: https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_10688 .
|
||||
'user_id' => $this->dummyUser->id
|
||||
];
|
||||
if ($date_saisie) {
|
||||
// ? FIXME: IS this pertinent ?
|
||||
$fill['seen_at'] = $date_saisie;
|
||||
$fill['created_at'] = $date_saisie;
|
||||
$fill['updated_at'] = $date_saisie;
|
||||
}
|
||||
if (Str::of($row->SUPPRIMER_O_N)->lower() === 'o') {
|
||||
$fill['deleted_at'] = $date_saisie;
|
||||
}
|
||||
$product->fill($fill);
|
||||
if (!DB::transaction(fn () => $product->saveQuietly())) {
|
||||
throw new \Error("Failed to save product {$row->NUM_VEILLE_PROD}");
|
||||
}
|
||||
|
||||
$product->productKeywords()->sync($keywordsList->pluck('id'));
|
||||
} //end while
|
||||
$progress->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create unique brands using veille products
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function migrateBrands(): void
|
||||
{
|
||||
$query = $this->getQuery("VEILLE_PROD");
|
||||
/** @var \Illuminate\Support\Collection<string,string> */
|
||||
$brands = (clone $query)->pluck('MARQUE')->filter()
|
||||
->map(fn (string $brandName) => Str::of($brandName)->trim()->title())->unique();
|
||||
$console = new ConsoleOutput();
|
||||
$console->writeln('');
|
||||
$progress = new ProgressBar($console, $brands->count());
|
||||
$progress->setMessage(str_pad('Migrate Brands', 24, ' '));
|
||||
$progress->setFormat(' - %message% [%bar%] %percent:3s%% %current%/%max% %elapsed:6s%/%estimated:-6s%');
|
||||
// FIXME: Insert brand->filter and find from where it should be fetched.
|
||||
$brands->each(function ($brandName) use ($progress) {
|
||||
Brand::query()->where('name', $brandName)->firstOrNew()->fill([
|
||||
'name' => $brandName
|
||||
])->saveQuietly();
|
||||
$progress->advance();
|
||||
});
|
||||
$progress->finish();
|
||||
}
|
||||
|
||||
// - END MIGRATION METHODS -
|
||||
// -- DB CORRECTIONS --
|
||||
|
||||
/**
|
||||
* Set unique on brands.name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setUniqueOnBrandName(): void
|
||||
{
|
||||
try {
|
||||
Schema::table('brands', function (Blueprint $table) {
|
||||
$table->dropUnique(['name']);
|
||||
});
|
||||
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
} catch (QueryException) {
|
||||
// * Do nothing
|
||||
} finally {
|
||||
Schema::table('brands', function (Blueprint $table) {
|
||||
$table->unique(['name']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct some columns to effectively json
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setJsonColumnsAsJsonOnProducts(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->json('origins')->nullable()->change()
|
||||
->comment('Origine - Tableau d\'Enum (entier) JSON (valeur de l\'enum OriginEnum)');
|
||||
$table->json('claims')->nullable()->change()
|
||||
->comment('Autres allégations - Tableau d\'Enum (entier) JSON (valeur de l\'enum ClaimEnum)');
|
||||
$table->json('docs')->nullable()->change()
|
||||
->comment('Docs complémentaires - Tableau de path - 0 a 5 docs PDF');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restrict foreign keys on keywordables
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function fixForeignKeyActionOnKeywordables(): void
|
||||
{
|
||||
Schema::table('keywordables', function (Blueprint $table) {
|
||||
$table->dropForeign(['keyword_id']);
|
||||
});
|
||||
Schema::table('keywordables', function (Blueprint $table) {
|
||||
$table->foreign(['keyword_id'])->references(['id'])->on('keywords')
|
||||
->restrictOnUpdate()->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restrict foreign keys on processes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function fixForeignKeyActionOnProcesses(): void
|
||||
{
|
||||
Schema::table('processes', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
});
|
||||
Schema::table('processes', function (Blueprint $table) {
|
||||
$table->foreign(['user_id'])->references(['id'])->on('users')
|
||||
->restrictOnUpdate()->restrictOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
// -- END DB CORRECTION --
|
||||
// --- LIB METHODS ---
|
||||
|
||||
/**
|
||||
* Get migration disk
|
||||
*
|
||||
* @param string|null $path Get a specific path.
|
||||
* @return \Illuminate\Filesystem\FilesystemAdapter|null
|
||||
* @throws \Error If veille folder is missing.
|
||||
*/
|
||||
private function getMigrationStorage(string $path = ''): \Illuminate\Filesystem\FilesystemAdapter|null
|
||||
{
|
||||
$storagePath = storage_path('app/migration');
|
||||
$path = "/Veille/{$path}";
|
||||
|
||||
if (!File::exists("{$storagePath}{$path}")) {
|
||||
return null;
|
||||
}
|
||||
$diskName = 'migration' . Str::of("{$storagePath}{$path}")->slug();
|
||||
|
||||
config()->set("filesystems.disks.{$diskName}", [
|
||||
'driver' => 'local',
|
||||
'root' => "{$storagePath}{$path}",
|
||||
'throw' => false,
|
||||
]);
|
||||
/** @var \Illuminate\Filesystem\FilesystemAdapter */
|
||||
$disk = Storage::disk($diskName);
|
||||
|
||||
|
||||
return $disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Temp DB query builder
|
||||
*
|
||||
* @param string $tableName
|
||||
* @return \Illuminate\Contracts\Database\Query\Builder
|
||||
* @throws \Error If $tableName does not exists.
|
||||
*/
|
||||
private function getQuery(string $tableName): Builder
|
||||
{
|
||||
if (!DB::table($this->getTableName($tableName))->exists()) {
|
||||
throw new \Error("table {$tableName} does not exists");
|
||||
}
|
||||
return DB::table($this->getTableName($tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a table if it exists
|
||||
*
|
||||
* @param string $tableName
|
||||
* @return string
|
||||
* @throws \Error If table is unkown.
|
||||
*/
|
||||
private function getTableName(string $tableName): string
|
||||
{
|
||||
if (!in_array($tableName, $this->tableList)) {
|
||||
throw new Error("Unknown table {$tableName}");
|
||||
}
|
||||
return "{$this->tablePrefix}_{$tableName}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create table in SQL
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param array<object> $tableColumns With name, type, size.
|
||||
* @return void
|
||||
*/
|
||||
private function createTable(string $tableName, array $tableColumns)
|
||||
{
|
||||
Schema::dropIfExists($tableName);
|
||||
Schema::create($tableName, function (Blueprint $table) use ($tableColumns) {
|
||||
foreach ($tableColumns as $tableColumn) {
|
||||
$tableComment = "{$tableColumn->name} {$tableColumn->type} {$tableColumn->size}";
|
||||
switch ($tableColumn->type) {
|
||||
case 'NUMBER':
|
||||
$table->bigInteger($tableColumn->name)
|
||||
->nullable()->comment($tableComment);
|
||||
break;
|
||||
case 'VARCHAR2':
|
||||
case 'CHAR':
|
||||
$table->string($tableColumn->name, $tableColumn->size)
|
||||
->nullable()->comment($tableComment);
|
||||
break;
|
||||
case 'DATE':
|
||||
$table->dateTime($tableColumn->name)
|
||||
->nullable()->comment($tableComment);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unhandled type {$tableColumn->type}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create temp tables and fill them
|
||||
*
|
||||
* @return void
|
||||
* @throws \Error In case of failure.
|
||||
*/
|
||||
private function createAndFillTempTables(): void
|
||||
{
|
||||
$console = new ConsoleOutput();
|
||||
$console->writeln('');
|
||||
foreach ($this->tableList as $tableName) {
|
||||
$section = $console->section();
|
||||
$header = $this->fetchOciTableHeader($tableName);
|
||||
$this->createTable(
|
||||
$this->getTableName($tableName),
|
||||
$header
|
||||
);
|
||||
$message = str_pad("{$this->tablePrefix} {$tableName}", 24, ' ');
|
||||
$section->write(" - {$message} -> Fetching OCI DATA..");
|
||||
$content = $this->fetchOciTableContent($tableName, $header);
|
||||
$section->clear();
|
||||
$progress = new ProgressBar($section, count($content));
|
||||
$progress->setMessage($message);
|
||||
$progress->setFormat(' - %message% [%bar%] %percent:3s%% %current%/%max% %elapsed:6s%/%estimated:-6s%');
|
||||
foreach ($content as $row) {
|
||||
$progress->advance();
|
||||
DB::table($this->getTableName($tableName))->insert((array)$row);
|
||||
}
|
||||
$progress->finish();
|
||||
} //end foreach
|
||||
}
|
||||
|
||||
/**
|
||||
* Create temp tables and fill them
|
||||
*
|
||||
* @return void
|
||||
* @throws \Error In case of failure.
|
||||
*/
|
||||
private function dropTempTables(): void
|
||||
{
|
||||
foreach ($this->tableList as $tableName) {
|
||||
Schema::drop($this->getTableName($tableName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Oci table header
|
||||
*
|
||||
* @param string $tableName
|
||||
* @return array<object> with name, type, size
|
||||
* @throws \Error If connection or query fails.
|
||||
*/
|
||||
private function fetchOciTableHeader(string $tableName): array
|
||||
{
|
||||
$tableName = $this->getTableName($tableName);
|
||||
$conn = $this->getOciConnection();
|
||||
|
||||
// Prepare the statement.
|
||||
$stid = oci_parse($conn, "SELECT * FROM {$tableName}");
|
||||
|
||||
if (!$stid) {
|
||||
$e = oci_error($conn);
|
||||
throw new \Error(htmlentities($e['message'], ENT_QUOTES));
|
||||
}
|
||||
|
||||
// Use OCI_DESCRIBE_ONLY if not fetching rows.
|
||||
oci_execute($stid, OCI_DESCRIBE_ONLY);
|
||||
|
||||
if (!$stid) {
|
||||
$e = oci_error($conn);
|
||||
throw new \Error(htmlentities($e['message'], ENT_QUOTES));
|
||||
}
|
||||
|
||||
$ncols = oci_num_fields($stid);
|
||||
|
||||
$out = [];
|
||||
|
||||
for ($i = 1; $i <= $ncols; $i++) {
|
||||
$column_name = oci_field_name($stid, $i);
|
||||
$column_type = oci_field_type($stid, $i);
|
||||
$column_size = oci_field_size($stid, $i);
|
||||
|
||||
$out[] = (object)[
|
||||
'name' => $column_name,
|
||||
'type' => $column_type,
|
||||
'size' => $column_size
|
||||
];
|
||||
}
|
||||
|
||||
oci_free_statement($stid);
|
||||
oci_close($conn);
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Oci table content
|
||||
*
|
||||
* @param string $tableName Table name.
|
||||
* @param array<object> $header With name, type,size.
|
||||
* @return array<object> With DB content.
|
||||
* @throws \Error If connection or query fails.
|
||||
*/
|
||||
private function fetchOciTableContent(string $tableName, array $header): array
|
||||
{
|
||||
$out = [];
|
||||
$tableName = $this->getTableName($tableName);
|
||||
$conn = $this->getOciConnection();
|
||||
|
||||
// Prepare the statement.
|
||||
$stid = oci_parse($conn, "SELECT * FROM {$tableName}");
|
||||
|
||||
if (!$stid) {
|
||||
$e = oci_error($conn);
|
||||
throw new \Error(htmlentities($e['message'], ENT_QUOTES));
|
||||
}
|
||||
|
||||
$r = oci_execute($stid);
|
||||
if (!$stid) {
|
||||
$e = oci_error($conn);
|
||||
throw new \Error(htmlentities($e['message'], ENT_QUOTES));
|
||||
}
|
||||
|
||||
while ($row = oci_fetch_array($stid, OCI_ASSOC + OCI_RETURN_NULLS)) {
|
||||
$outRow = [];
|
||||
foreach ($row as $k => $item) {
|
||||
/** @var string */
|
||||
$type = collect($header)->where('name', $k)->firstOrFail()->type;
|
||||
if ($type === 'DATE' and $item !== null) {
|
||||
$item = $this->tryToParseCarbon($item);
|
||||
}
|
||||
$outRow[$k] = $item;
|
||||
}
|
||||
$out[] = (object)$outRow;
|
||||
}
|
||||
|
||||
oci_free_statement($stid);
|
||||
oci_close($conn);
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Oci connection
|
||||
*
|
||||
* @return resource oci8 connection resource
|
||||
* @throws \Error If connection fails.
|
||||
*/
|
||||
private function getOciConnection()
|
||||
{
|
||||
$conn = oci_connect('system', 'secret', 'localhost/XE');
|
||||
if (!$conn) {
|
||||
$e = oci_error();
|
||||
throw new \Error(htmlentities($e['message'], ENT_QUOTES));
|
||||
}
|
||||
return $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse freaking Start coq dates
|
||||
*
|
||||
* @param string $date The string date to parse.
|
||||
* @param boolean $asTimestamp To force the return of a valid TIMESTAMP (in epoch).
|
||||
* @return \Carbon\Carbon
|
||||
* @throws \Error If the date cannot fit in a timestamp.
|
||||
*/
|
||||
private function tryToParseCarbon(string $date, bool $asTimestamp = false): Carbon
|
||||
{
|
||||
$utcTimeZone = new DateTimeZone('UTC');
|
||||
$cDate = Carbon::parse($date, $utcTimeZone);
|
||||
if (
|
||||
$asTimestamp and
|
||||
$cDate->lt(Carbon::createFromTimestamp(0, $utcTimeZone)) or
|
||||
$cDate->gt(Carbon::createFromTimestamp(
|
||||
(new DateTime("99999/12/31 00:00:00"))->getTimestamp(),
|
||||
$utcTimeZone
|
||||
))
|
||||
) {
|
||||
throw new \Error("The date {$date} is cannot fit in a Timestamp");
|
||||
}
|
||||
|
||||
if ($cDate->timestamp === 0) {
|
||||
// * Prevent Invalid datetime format: 1292 Incorrect datetime value: '1970-01-01 00:00:00'
|
||||
$cDate->addSecond();
|
||||
}
|
||||
return $cDate;
|
||||
}
|
||||
};
|
||||
185
database/migrations/other_allegations.txt
Normal file
185
database/migrations/other_allegations.txt
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
# EXPORT THIS FROM DB USING
|
||||
# `COMP_ALLEG` NOT IN(
|
||||
# 'Sans additif, sans colorant, sans conservateur,100% simple, sans polyphosphate,sans sel nutrite',
|
||||
# 'Sans additif, sans colorant, sans conservateur, 100% simple, sans polyphosphate, sans sel nitrité',
|
||||
# 'Produit de l\'année',
|
||||
# 'Bleu Blanc Coeur, source d\'Omega 3',
|
||||
# 'Sans OGM',
|
||||
# 'Sans gluten',
|
||||
# 'Sans Antibiotique',
|
||||
# 'Vegan',
|
||||
# 'Allégé',
|
||||
# 'Halal, Casher',
|
||||
# 'Riche en protéines, fibres, calcium…',
|
||||
# 'Autres'
|
||||
# )
|
||||
Agri ethique france commerce equitable
|
||||
Commerce equitable
|
||||
eleveurs engages, alimentation 100% vegetaux, mineraux vitamines, cereales d'origines locales, collecte de proximite, respect du bien-etre animal
|
||||
Emballage produit en Vendee
|
||||
Faible en MG / pauvre en sel
|
||||
producteurs partenaires
|
||||
Recette Vegetarienne
|
||||
Sans colorant
|
||||
Sans pesticides
|
||||
Saumon garanti jamais congele
|
||||
saumon garanti jamais congele
|
||||
Creme fraiche origine france et chablis AOC
|
||||
Elabore a la main, petits pois origine france
|
||||
sans colorant ni conservateur , sans huile de palme
|
||||
SANS COLORANT SANS HUILE DE PALME SANS EXHAUSTEUR DE GOUT
|
||||
sans colorant, conservateur et huile de palme
|
||||
sans colorant, conservatuer et huile de palme
|
||||
VEGETAL
|
||||
vegetarien
|
||||
Antioxydant/ Vitamine C
|
||||
equitable
|
||||
Filiere cacao equitable
|
||||
Ferme engagee
|
||||
Filiere courte des eleveurs aux consommateurs ;
|
||||
meilleur remuneration des producteurs
|
||||
-25% sel
|
||||
-60% de plastique
|
||||
100% filets
|
||||
Barquette en bois issue de cultures durables
|
||||
bien eleve et nourri
|
||||
COLLECTION FESTIVE
|
||||
filet de poulet
|
||||
fume au bois de hetre
|
||||
Origine France
|
||||
Sans additif, Sans conservateur
|
||||
sans conservateur
|
||||
sans conservateur - vegetarien
|
||||
sans conservateur, colorant, arome ajoute,
|
||||
sans conservateur, sans colorant, sans huile de palme, sans exhausteur de gout, sans huile hydrogenee
|
||||
sans huile de palem , sans exhausteur de gout
|
||||
Sans huile de palme, Sans exhausteur de gout
|
||||
Sans sel nitrite ajoute
|
||||
source de proteines vegetales et sources de fibres
|
||||
viande 100% Filet
|
||||
Eleve en plein air
|
||||
certifie
|
||||
Cooperative engagee
|
||||
Faible en matiere grasse
|
||||
Le porc Francais
|
||||
Mache cultivee en Pays de la Loire
|
||||
Melusines Bieres Artisanales
|
||||
Pauvre en sel
|
||||
Porc francais
|
||||
Porc Francais, produit de Bretagne
|
||||
Poulet fermier
|
||||
Poulet Francais, sans conservateur ni colorant
|
||||
SANS ADDITIF
|
||||
Sans colorant, avec du poulet eleve au sol et bien nourri
|
||||
Sans huile de palme / sans exhausteur de gout
|
||||
Sans sel nitrite
|
||||
Source de proteines
|
||||
+ de 40 eleveurs de votre region
|
||||
100 % local
|
||||
100% Alimentation vegetale, minerale er vitamines
|
||||
100% local,
|
||||
100% local, issu de votre region
|
||||
100% viande de volaille
|
||||
5 min a la poele
|
||||
AB
|
||||
Agri cnfiance
|
||||
Alimentation 100% vegetale
|
||||
Alimentation 100% vegetale CLASSE A
|
||||
alimentation 100% vegetale minerale et vitaminee
|
||||
alimentation 100% vegetale minerale et vitamines
|
||||
alimentation 100% vegetale, 50% mais
|
||||
Alimentation 100% vegetale, minerale et vitamines
|
||||
Alimentation 100% vegetale, Minerale et vitamines
|
||||
Alimentation cereales 100% francaises
|
||||
Alimentation cereales entieres 100% francaises
|
||||
alimente avec 100% vegetaux mineraux et vitamines
|
||||
Avec de la dinde bien nourrie
|
||||
avec du poulet bien nourri
|
||||
avec du poulet bien nourri, sans colorant
|
||||
barquette avec 60% de plastique recycle
|
||||
Barquette avec 60% recycle
|
||||
Barquette cuisson
|
||||
Barquettes recyclables a 60 % en carton
|
||||
Bien etre animal
|
||||
Bien-etre animal
|
||||
Bq 100% recyclable issu de 85% de plastique recycle
|
||||
Cereales entieres 100% francaises
|
||||
Cereales produites localement; preparation et abattage local; j'achete local
|
||||
Chair plus gouteuse
|
||||
Clean label
|
||||
Clean Label
|
||||
Confit de France
|
||||
Contribue a l'economie locale, revenu des eleveurs protege, + de 5 900 emplois soutenus en regions
|
||||
cuit a l'etouffee
|
||||
Eleve en France
|
||||
Eleve en plein air et Emballage eco-concu -45% de plastique
|
||||
Eleve en plein air, Emballage eco-concu
|
||||
Elevee en plein air, Emballage eco-concu
|
||||
Emballage eco-concu -45% de plastique
|
||||
Engagee pour la vie de nos eleveurs, Barquette avec 60% de plastique recycle
|
||||
Ethik food
|
||||
Faconne traditionnellement a la main
|
||||
Fait main
|
||||
Faite main
|
||||
Ficele a la main
|
||||
Fromage AOP
|
||||
fume au bois de hetre et de chene
|
||||
Garantie volaille bien elevee et bien nourrie
|
||||
Garantie Volaille bien elevee et bien nourrie et bien
|
||||
Issu de souche rustique
|
||||
J'aime manger local
|
||||
Juste remuneration des eleveurs
|
||||
La moins calorique
|
||||
La viande du quotidien la moins calorique
|
||||
moins de 2% de mG
|
||||
montees a la main
|
||||
Montees a la main
|
||||
montees main
|
||||
naturellement tendres
|
||||
ne et eleve en France abattue en Allemagne
|
||||
NIVEAU BIEN ETRE ANIMAL
|
||||
nourri aux cereales locales
|
||||
Nourri avec 100% de vegetaux, mineraux et vitamines
|
||||
Nourri avec 100% vegetaux, mineraux et vitamines
|
||||
Nourri avec des cereales 100% francaises
|
||||
Nourrie sans OGM (<0,9%) cereales francaises, elevage respectueux, barquette 100% recyclable
|
||||
Origine certifie sud ouest
|
||||
Planete positive
|
||||
PRET A CONSOMMER
|
||||
sans antibio
|
||||
sans huile de palme
|
||||
Sans Huile de palme
|
||||
sans huile de palme sans colorant
|
||||
Sans lactose
|
||||
Sans lactose, Sources de proteines
|
||||
sans traitement antibiotique , aux aromes naturels, sans conservateur
|
||||
Sortant a l'exterieur
|
||||
Sources de proteines
|
||||
Viande francaise
|
||||
Volaille bien nourie
|
||||
volaille francaise
|
||||
volaille nourrie 100% vegetaux mineraux et vitamines
|
||||
Barquette Cuisson
|
||||
Cuisson facile
|
||||
Elabores par nos bouchers
|
||||
Eleve en large enlos
|
||||
En direct de nos ateliers
|
||||
extra tendre
|
||||
Faite mains
|
||||
Filiere responsable engagee pour vous
|
||||
gigot d'agneau
|
||||
les eleveurs cooperateurs; sans colorant ni conservateur
|
||||
Montees main, pic inox, qualite superieur
|
||||
Omega 3
|
||||
Porc Francais, fabrication savoie
|
||||
qualite superieur
|
||||
recette traditionnelle, sans colorant ni conservateur
|
||||
Sans additif
|
||||
sans os
|
||||
Savoir faire boucher francais
|
||||
Selectionne par nos Bouchers
|
||||
Terres d'Eure et Loir
|
||||
Tous engages : nourris aux cereales francaise sans OGM
|
||||
Viande de cheval fraiche
|
||||
100% pur beure
|
||||
100% vegetal
|
||||
197
database/seeders/KeywordInitSeeder.php
Normal file
197
database/seeders/KeywordInitSeeder.php
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Enums\KeywordCategoryEnum;
|
||||
use App\Models\Keyword;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class KeywordInitSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
/**
|
||||
* * INGREDIENTS
|
||||
*/
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0];
|
||||
$keyword->label = "Sophistication";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0];
|
||||
$keyword->label = "Exotisme";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0];
|
||||
$keyword->label = "Fun";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1];
|
||||
$keyword->label = "Clean label/naturalité";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1];
|
||||
$keyword->label = "Végétarien/vegan";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1];
|
||||
$keyword->label = "Bien être";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2];
|
||||
$keyword->label = "Solidarité";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2];
|
||||
$keyword->label = "Écologie";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3];
|
||||
$keyword->label = "Emballage individuel";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3];
|
||||
$keyword->label = "Facilité de mise en oeuvre";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
/**
|
||||
* * PRODUCTS
|
||||
*/
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Sophistication";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Exotisme";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Variété des sens";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Fun";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5];
|
||||
$keyword->label = "Minceur";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5];
|
||||
$keyword->label = "Énergie/Bien-être";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5];
|
||||
$keyword->label = "Cosmétique";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6];
|
||||
$keyword->label = "Manipulation produit";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6];
|
||||
$keyword->label = "Gain de temps";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6];
|
||||
$keyword->label = "Nomadisme";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7];
|
||||
$keyword->label = "Naturalité";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7];
|
||||
$keyword->label = "Végétal";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7];
|
||||
$keyword->label = "Médical";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8];
|
||||
$keyword->label = "Solidarité";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8];
|
||||
$keyword->label = "Écologie";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
/**
|
||||
* * PACKAGING
|
||||
*/
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9];
|
||||
$keyword->label = "Sophistication";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9];
|
||||
$keyword->label = "Exotisme";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9];
|
||||
$keyword->label = "Fun";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10];
|
||||
$keyword->label = "Manipulation produit";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10];
|
||||
$keyword->label = "Gain de temps";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10];
|
||||
$keyword->label = "Nomadisme";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11];
|
||||
$keyword->label = "Décor/forme";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11];
|
||||
$keyword->label = "Matériaux";
|
||||
$keyword->saveOrFail();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[12];
|
||||
$keyword->label = "Écologie";
|
||||
$keyword->saveOrFail();
|
||||
}
|
||||
}
|
||||
|
|
@ -16,186 +16,7 @@ class KeywordSeeder extends Seeder
|
|||
*/
|
||||
public function run()
|
||||
{
|
||||
$id = 0;
|
||||
/**
|
||||
* * INGREDIENTS
|
||||
*/
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0];
|
||||
$keyword->label = "Sophistication";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0];
|
||||
$keyword->label = "Exotisme";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0];
|
||||
$keyword->label = "Fun";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1];
|
||||
$keyword->label = "Clean label/naturalité";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1];
|
||||
$keyword->label = "Végétarien/vegan";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1];
|
||||
$keyword->label = "Bien être";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2];
|
||||
$keyword->label = "Solidarité";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2];
|
||||
$keyword->label = "Écologie";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3];
|
||||
$keyword->label = "Emballage individuel";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3];
|
||||
$keyword->label = "Facilité de mise en oeuvre";
|
||||
$keyword->save();
|
||||
|
||||
/**
|
||||
* * PRODUCTS
|
||||
*/
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Sophistication";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Exotisme";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Variété des sens";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4];
|
||||
$keyword->label = "Fun";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5];
|
||||
$keyword->label = "Minceur";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5];
|
||||
$keyword->label = "Énergie/Bien-être";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5];
|
||||
$keyword->label = "Cosmétique";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6];
|
||||
$keyword->label = "Manipulation produit";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6];
|
||||
$keyword->label = "Gain de temps";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6];
|
||||
$keyword->label = "Nomadisme";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7];
|
||||
$keyword->label = "Naturalité";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7];
|
||||
$keyword->label = "Végétal";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7];
|
||||
$keyword->label = "Médical";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8];
|
||||
$keyword->label = "Solidarité";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8];
|
||||
$keyword->label = "Écologie";
|
||||
$keyword->save();
|
||||
|
||||
/**
|
||||
* * PACKAGING
|
||||
*/
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9];
|
||||
$keyword->label = "Sophistication";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9];
|
||||
$keyword->label = "Exotisme";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9];
|
||||
$keyword->label = "Fun";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10];
|
||||
$keyword->label = "Manipulation produit";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10];
|
||||
$keyword->label = "Gain de temps";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10];
|
||||
$keyword->label = "Nomadisme";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11];
|
||||
$keyword->label = "Décor/forme";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11];
|
||||
$keyword->label = "Matériaux";
|
||||
$keyword->save();
|
||||
|
||||
$keyword = new Keyword();
|
||||
$keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[12];
|
||||
$keyword->label = "Écologie";
|
||||
$keyword->save();
|
||||
|
||||
$this->call(KeywordInitSeeder::class);
|
||||
// Seeder for keywordables table.
|
||||
Keyword::query()->get()->each(function (Keyword $keyword) {
|
||||
Keywordable::factory(mt_rand(1, 3))->make()->each(function (Keywordable $keywordable) use ($keyword) {
|
||||
|
|
@ -226,7 +47,7 @@ class KeywordSeeder extends Seeder
|
|||
$keywordable->keyword()->associate($keyword);
|
||||
$keywordable->keywordable_type = 'App\Models\Packaging';
|
||||
$keywordable->saveOrFail();
|
||||
}
|
||||
}//end if
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
39
resources/lang/fr.json
Normal file
39
resources/lang/fr.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"(and :count more error)": "(et :count erreur en plus)",
|
||||
"(and :count more errors)": "(et :count erreurs en plus)",
|
||||
"All rights reserved.": "Tous droits réservés.",
|
||||
"Forbidden": "Interdit",
|
||||
"Go to page :page": "Aller à la page :page",
|
||||
"Hello!": "Bonjour !",
|
||||
"If you did not create an account, no further action is required.": "Si vous n'avez pas créé de compte, vous pouvez ignorer ce message.",
|
||||
"If you did not request a password reset, no further action is required.": "Si vous n'avez pas demandé de réinitialisation de mot de passe, vous pouvez ignorer ce message.",
|
||||
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Si vous avez des difficultés à cliquer sur le bouton \":actionText\", copiez et collez l'URL ci-dessous\ndans votre navigateur Web :",
|
||||
"Invalid JSON was returned from the route.": "Un JSON non valide a été renvoyé par la route.",
|
||||
"Login": "Connexion",
|
||||
"Logout": "Déconnexion",
|
||||
"Not Found": "Non trouvé",
|
||||
"of": "de",
|
||||
"Page Expired": "Page expirée",
|
||||
"Pagination Navigation": "Pagination",
|
||||
"Payment Required": "Paiement requis",
|
||||
"Please click the button below to verify your email address.": "Veuillez cliquer sur le bouton ci-dessous pour vérifier votre adresse e-mail :",
|
||||
"Regards": "Cordialement",
|
||||
"Register": "Inscription",
|
||||
"Reset Password": "Réinitialisation du mot de passe",
|
||||
"Reset Password Notification": "Notification de réinitialisation du mot de passe",
|
||||
"results": "résultats",
|
||||
"Server Error": "Erreur serveur",
|
||||
"Service Unavailable": "Service indisponible",
|
||||
"Showing": "Montrant",
|
||||
"The given data was invalid.": "La donnée renseignée est incorrecte.",
|
||||
"The response is not a streamed response.": "La réponse n'est pas une réponse diffusée.",
|
||||
"The response is not a view.": "La réponse n'est pas une vue.",
|
||||
"This password reset link will expire in :count minutes.": "Ce lien de réinitialisation du mot de passe expirera dans :count minutes.",
|
||||
"to": "à",
|
||||
"Toggle navigation": "Afficher / masquer le menu de navigation",
|
||||
"Too Many Requests": "Trop de requêtes",
|
||||
"Unauthorized": "Non autorisé",
|
||||
"Verify Email Address": "Vérifier l'adresse e-mail",
|
||||
"Whoops!": "Oups !",
|
||||
"You are receiving this email because we received a password reset request for your account.": "Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte."
|
||||
}
|
||||
9
resources/lang/fr/auth.php
Normal file
9
resources/lang/fr/auth.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'failed' => 'Ces identifiants ne correspondent pas à nos enregistrements.',
|
||||
'password' => 'Le mot de passe est incorrect',
|
||||
'throttle' => 'Tentatives de connexion trop nombreuses. Veuillez essayer de nouveau dans :seconds secondes.',
|
||||
];
|
||||
83
resources/lang/fr/crud.php
Normal file
83
resources/lang/fr/crud.php
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'actions' => [
|
||||
'list' => 'Liste',
|
||||
'view' => 'Voir',
|
||||
'create' => 'Créer',
|
||||
'duplicate' => 'Dupliquer',
|
||||
'update' => 'Éditer',
|
||||
'delete' => 'Supprimer',
|
||||
'save' => 'Sauvegarder',
|
||||
'edit' => 'Mettre à jour',
|
||||
],
|
||||
|
||||
'helpers' => [
|
||||
'list' => 'Liste',
|
||||
'list_all' => 'Lister tous les :model',
|
||||
'create_model' => 'Créer :model',
|
||||
'view_model' => 'Voir :model',
|
||||
'update_model' => 'Éditer :model',
|
||||
'duplicate_model' => 'Dupliquer :model',
|
||||
'delete_model' => 'Supprimer :model',
|
||||
'save_model' => 'Sauvegarder :model',
|
||||
'edit_model' => 'Mettre à jour :model',
|
||||
'no_results' => 'Aucun résultat pour :model',
|
||||
'publish' => 'Publié',
|
||||
'unpublish' => 'Dépublié',
|
||||
],
|
||||
|
||||
'libs' => [
|
||||
'toolbox' => [
|
||||
'assertFieldIsUnique' => 'La :valeur du champ :attribut est déjà utilisée',
|
||||
'assertNoInternalBoUrls' => ':attribute : Il est interdit de coller l\'uri de back-office dans ce champ',
|
||||
'assertNoInternalUrls' => ':attribute : Il est interdit de coller une url de :appname dans ce champ.',
|
||||
]
|
||||
],
|
||||
|
||||
'messages' => [
|
||||
'has_been_created' => ':model a été créé',
|
||||
'cannot_be_created' => ':model ne peut être créé',
|
||||
'has_been_updated' => ':model a été mis à jour',
|
||||
'cannot_be_updated' => ':model ne peut être mis à jour',
|
||||
'has_been_deleted' => ':model a été supprimé',
|
||||
'cannot_be_deleted' => ':model ne peut être supprimé',
|
||||
'order_changed' => 'L\'ordre a été changé !',
|
||||
'order_not_changed' => 'L\'ordre n\'a pas été changé !',
|
||||
'publish_status_saved' => 'Changement de l\'état de publication réussit !',
|
||||
'right' => 'Vous n\'avez pas les droits pour accéder à cette partie !'
|
||||
],
|
||||
|
||||
'minicrud' => [
|
||||
'visibility_label' => 'Visibilité',
|
||||
'visibility_helper' => 'Vérifier qu\'il est visible.',
|
||||
'enum_select' => 'Sélectionnez :enum dans la liste.',
|
||||
'associated_enum' => 'Associé :enum',
|
||||
'dimensions_must_be' => 'Doit être :widthpx (largeur) par :heightpx (hauteur).',
|
||||
'picture_title_helper' => 'Utile pour les utilisateurs, s\'affiche en passant la souris sur l\'image',
|
||||
'picture_title_label' => 'Attribut du titre de l\'image',
|
||||
'picture_alt_helper' => 'Doit décrire votre image pour les moteurs de recherche et les aveugles (5 à 6 mots)',
|
||||
'picture_alt_label' => 'Attribut Alt de l\'image',
|
||||
'text_warning' => 'Relisez votre contenu avant de le publier',
|
||||
],
|
||||
|
||||
'pagination' => [
|
||||
"previous" => "Page précédente",
|
||||
"next" => "Page suivante",
|
||||
"specific_page" => "Accéder à la page n°:id",
|
||||
'paginate_list' => 'Nombre d\'éléments par page',
|
||||
],
|
||||
|
||||
'other' => [
|
||||
'up' => 'Modifier l\'ordre d\'apparition vers le haut',
|
||||
'down' => 'Modifier l\'ordre d\'apparition vers le bas',
|
||||
'no_model_found' => 'Aucun :model trouvés',
|
||||
'publish' => 'Publié',
|
||||
'unpublish' => 'Dépublié',
|
||||
'sort_ascending' => 'Trier :name dans l\'ordre croissant',
|
||||
'sort_descending' => 'Trier :name dans l\'ordre décroissant',
|
||||
'sort_delete' => 'Supprimer le tri',
|
||||
'sort_arrow' => 'Ici, les flèches sont utilisées pour trier et non pour filtrer',
|
||||
'right' => 'Vous n\'avez pas les droits',
|
||||
]
|
||||
];
|
||||
84
resources/lang/fr/http-statuses.php
Normal file
84
resources/lang/fr/http-statuses.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'0' => 'Erreur inconnue',
|
||||
'100' => 'Continuer',
|
||||
'101' => 'Protocoles de commutation',
|
||||
'102' => 'En traitement',
|
||||
'200' => 'D\'accord',
|
||||
'201' => 'Créé',
|
||||
'202' => 'Accepté',
|
||||
'203' => 'Informations non autorisées',
|
||||
'204' => 'Pas content',
|
||||
'205' => 'Réinitialiser le contenu',
|
||||
'206' => 'Contenu partiel',
|
||||
'207' => 'Multi-statut',
|
||||
'208' => 'Déjà rapporté',
|
||||
'226' => 'J\'ai l\'habitude',
|
||||
'300' => 'Choix multiples',
|
||||
'301' => 'Déplacé de façon permanente',
|
||||
'302' => 'A trouvé',
|
||||
'303' => 'Voir autre',
|
||||
'304' => 'Non modifié',
|
||||
'305' => 'Utiliser un proxy',
|
||||
'307' => 'Redirection temporaire',
|
||||
'308' => 'Redirection permanente',
|
||||
'400' => 'Mauvaise Demande',
|
||||
'401' => 'Non autorisé',
|
||||
'402' => 'Paiement Requis',
|
||||
'403' => 'Interdit',
|
||||
'404' => 'Page non trouvée',
|
||||
'405' => 'Méthode Non Autorisée',
|
||||
'406' => 'Pas acceptable',
|
||||
'407' => 'Authentification proxy requise',
|
||||
'408' => 'Délai d\'expiration de la demande',
|
||||
'409' => 'Conflit',
|
||||
'410' => 'Disparu',
|
||||
'411' => 'Longueur requise',
|
||||
'412' => 'La précondition a échoué',
|
||||
'413' => 'Charge utile trop grande',
|
||||
'414' => 'URI trop long',
|
||||
'415' => 'Type de support non supporté',
|
||||
'416' => 'Plage non satisfaisante',
|
||||
'417' => 'Échec de l\'attente',
|
||||
'418' => 'Je suis une théière',
|
||||
'419' => 'La session a expiré',
|
||||
'421' => 'Demande mal dirigée',
|
||||
'422' => 'Entité non traitable',
|
||||
'423' => 'Fermé à clef',
|
||||
'424' => 'Dépendance échouée',
|
||||
'425' => 'Trop tôt',
|
||||
'426' => 'Mise à niveau requise',
|
||||
'428' => 'Condition préalable requise',
|
||||
'429' => 'Trop de demandes',
|
||||
'431' => 'Demander des champs d\'en-tête trop grands',
|
||||
'444' => 'Connexion fermée sans réponse',
|
||||
'449' => 'Réessayer avec',
|
||||
'451' => 'Indisponible pour des raisons légales',
|
||||
'499' => 'Demande fermée du client',
|
||||
'500' => 'Erreur Interne du Serveur',
|
||||
'501' => 'Pas mis en œuvre',
|
||||
'502' => 'Mauvaise passerelle',
|
||||
'503' => 'Mode de Maintenance',
|
||||
'504' => 'Délai d\'attente de la passerelle',
|
||||
'505' => 'Version HTTP non prise en charge',
|
||||
'506' => 'La variante négocie également',
|
||||
'507' => 'Espace insuffisant',
|
||||
'508' => 'Boucle détectée',
|
||||
'509' => 'Limite de bande passante dépassée',
|
||||
'510' => 'Non prolongé',
|
||||
'511' => 'Authentification réseau requise',
|
||||
'520' => 'Erreur inconnue',
|
||||
'521' => 'Le serveur Web est en panne',
|
||||
'522' => 'La connexion a expiré',
|
||||
'523' => 'L\'origine est inaccessible',
|
||||
'524' => 'Un dépassement de délai s\'est produit',
|
||||
'525' => 'Échec de la prise de contact SSL',
|
||||
'526' => 'Certificat SSL invalide',
|
||||
'527' => 'Erreur de canon ferroviaire',
|
||||
'598' => 'Erreur de délai de lecture réseau',
|
||||
'599' => 'Erreur de délai de connexion réseau',
|
||||
'unknownError' => 'Erreur inconnue',
|
||||
];
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
return [
|
||||
'classes' => [
|
||||
'products' => 'Produits',
|
||||
'products_monitoring' => 'Veille produit|Veilles produit',
|
||||
'brands' => 'Marques',
|
||||
]
|
||||
];
|
||||
|
|
|
|||
8
resources/lang/fr/pagination.php
Normal file
8
resources/lang/fr/pagination.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'next' => 'Suivant »',
|
||||
'previous' => '« Précédent',
|
||||
];
|
||||
11
resources/lang/fr/passwords.php
Normal file
11
resources/lang/fr/passwords.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'reset' => 'Votre mot de passe a été réinitialisé !',
|
||||
'sent' => 'Nous vous avons envoyé par email le lien de réinitialisation du mot de passe !',
|
||||
'throttled' => 'Veuillez patienter avant de réessayer.',
|
||||
'token' => 'Ce jeton de réinitialisation du mot de passe n\'est pas valide.',
|
||||
'user' => 'Aucun utilisateur n\'a été trouvé avec cette adresse email.',
|
||||
];
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'accepted' => 'Le champ :attribute doit être accepté.',
|
||||
'accepted_if' => 'Le champ :attribute doit être accepté quand :other a la valeur :value.',
|
||||
|
|
@ -10,6 +12,7 @@ return [
|
|||
'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.',
|
||||
'ascii' => 'Le champ :attribute ne doit contenir que des caractères alphanumériques et des symboles codés sur un octet.',
|
||||
'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' => [
|
||||
|
|
@ -19,11 +22,18 @@ return [
|
|||
'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.',
|
||||
],
|
||||
'boolean' => 'Le champ :attribute doit être vrai ou faux.',
|
||||
'can' => 'Le champ :attribute contient une valeur non autorisée.',
|
||||
'color' => ':attribute doit être un code couleur valide (hexadecimal, RGB, RGBA ou un nom de couleur CSS)',
|
||||
'color_hex' => ':attribute doit être un code couleur hexadecimal valide',
|
||||
'color_name' => ':attribute doit être un nom de couleur CSS valide',
|
||||
'color_rgb' => ':attribute doit être un code couleur RGB valide',
|
||||
'color_rgba' => ':attribute doit être un code couleur RGBA valide',
|
||||
'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.',
|
||||
'current_password' => 'Le mot de passe est incorrect.',
|
||||
'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.',
|
||||
'decimal' => 'Le champ :attribute doit comporter :decimal décimales.',
|
||||
'declined' => 'Le champ :attribute doit être décliné.',
|
||||
'declined_if' => 'Le champ :attribute doit être décliné quand :other a la valeur :value.',
|
||||
'different' => 'Les champs :attribute et :other doivent être différents.',
|
||||
|
|
@ -31,6 +41,8 @@ return [
|
|||
'digits_between' => 'Le champ :attribute doit contenir entre :min et :max chiffres.',
|
||||
'dimensions' => 'La taille de l\'image :attribute n\'est pas conforme.',
|
||||
'distinct' => 'Le champ :attribute a une valeur en double.',
|
||||
'doesnt_end_with' => 'Le champ :attribute ne doit pas finir avec une des valeurs suivantes : :values.',
|
||||
'doesnt_start_with' => 'Le champ :attribute ne doit pas commencer avec une des valeurs suivantes : :values.',
|
||||
'email' => 'Le champ :attribute doit être une adresse e-mail valide.',
|
||||
'ends_with' => 'Le champ :attribute doit se terminer par une des valeurs suivantes : :values',
|
||||
'enum' => 'Le champ :attribute sélectionné est invalide.',
|
||||
|
|
@ -57,6 +69,7 @@ return [
|
|||
'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.',
|
||||
'lowercase' => 'Le champ :attribute doit être en minuscules.',
|
||||
'lt' => [
|
||||
'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.',
|
||||
|
|
@ -71,24 +84,39 @@ return [
|
|||
],
|
||||
'mac_address' => 'Le champ :attribute doit être une adresse MAC valide.',
|
||||
'max' => [
|
||||
'array' => 'Le tableau :attribute ne peut contenir plus de :max éléments.',
|
||||
'array' => 'Le tableau :attribute ne peut pas contenir plus que :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.',
|
||||
'numeric' => 'La valeur de :attribute ne peut pas être supérieure à :max.',
|
||||
'string' => 'Le texte de :attribute ne peut pas contenir plus de :max caractères.',
|
||||
],
|
||||
'max_digits' => 'Le champ :attribute ne doit pas avoir plus de :max chiffres.',
|
||||
'mimes' => 'Le champ :attribute doit être un fichier de type : :values.',
|
||||
'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.',
|
||||
'min' => [
|
||||
'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.',
|
||||
'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :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.',
|
||||
'string' => 'Le texte de :attribute doit contenir au moins :min caractères.',
|
||||
],
|
||||
'min_digits' => 'Le champ :attribute doit avoir au moins :min chiffres.',
|
||||
'missing' => 'Le champ :attribute doit être manquant.',
|
||||
'missing_if' => 'Le champ :attribute doit être manquant quand :other a la valeur :value.',
|
||||
'missing_unless' => 'Le champ :attribute doit être manquant sauf si :other a la valeur :value.',
|
||||
'missing_with' => 'Le champ :attribute doit être manquant quand :values est présent.',
|
||||
'missing_with_all' => 'Le champ :attribute doit être manquant quand :values sont présents.',
|
||||
'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.',
|
||||
'numeric' => 'Le champ :attribute doit contenir un nombre.',
|
||||
'password' => 'Le mot de passe est incorrect',
|
||||
'password' => [
|
||||
'letters' => 'Le champ :attribute doit contenir au moins une lettre.',
|
||||
'mixed' => 'Le champ :attribute doit contenir au moins une majuscule et une minuscule.',
|
||||
'numbers' => 'Le champ :attribute doit contenir au moins un chiffre.',
|
||||
'symbols' => 'Le champ :attribute doit contenir au moins un symbole.',
|
||||
'uncompromised' => 'La valeur du champ :attribute est apparue dans une fuite de données. Veuillez choisir une valeur différente.',
|
||||
],
|
||||
'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide.',
|
||||
'postal_code' => 'Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.',
|
||||
'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.',
|
||||
|
|
@ -98,6 +126,7 @@ return [
|
|||
'required' => 'Le champ :attribute est obligatoire.',
|
||||
'required_array_keys' => 'Le champ :attribute doit contenir des entrées pour : :values.',
|
||||
'required_if' => 'Le champ :attribute est obligatoire quand la valeur de :other est :value.',
|
||||
'required_if_accepted' => 'Le champ :attribute est obligatoire quand le champ :other a été accepté.',
|
||||
'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.',
|
||||
|
|
@ -113,16 +142,83 @@ return [
|
|||
'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.',
|
||||
'ulid' => 'Le champ :attribute doit être un ULID 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é.',
|
||||
'uppercase' => 'Le champ :attribute doit être en majuscules.',
|
||||
'url' => 'Le format de l\'URL de :attribute n\'est pas valide.',
|
||||
'uuid' => 'Le champ :attribute doit être un UUID valide',
|
||||
'custom' => [],
|
||||
'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide.',
|
||||
'color' => ':attribute doit être un code couleur valide (hexadecimal, RGB, RGBA ou un nom de couleur CSS)',
|
||||
'color_hex' => ':attribute doit être un code couleur hexadecimal valide',
|
||||
'color_rgb' => ':attribute doit être un code couleur RGB valide',
|
||||
'color_rgba' => ':attribute doit être un code couleur RGBA valide',
|
||||
'color_name' => ':attribute doit être un nom de couleur CSS valide',
|
||||
'postal_code' => 'Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.'
|
||||
'attributes' => [
|
||||
'address' => 'adresse',
|
||||
'age' => 'âge',
|
||||
'amount' => 'montant',
|
||||
'area' => 'zone',
|
||||
'available' => 'disponible',
|
||||
'birthday' => 'anniversaire',
|
||||
'body' => 'corps',
|
||||
'city' => 'ville',
|
||||
'content' => 'contenu',
|
||||
'country' => 'pays',
|
||||
'created_at' => 'créé à',
|
||||
'creator' => 'créateur',
|
||||
'current_password' => 'mot de passe actuel',
|
||||
'date' => 'Date',
|
||||
'date_of_birth' => 'date de naissance',
|
||||
'day' => 'jour',
|
||||
'deleted_at' => 'supprimé à',
|
||||
'description' => 'la description',
|
||||
'district' => 'quartier',
|
||||
'duration' => 'durée',
|
||||
'email' => 'adresse e-mail',
|
||||
'excerpt' => 'extrait',
|
||||
'filter' => 'filtre',
|
||||
'first_name' => 'prénom',
|
||||
'gender' => 'genre',
|
||||
'group' => 'groupe',
|
||||
'hour' => 'heure',
|
||||
'image' => 'image',
|
||||
'last_name' => 'nom',
|
||||
'lesson' => 'leçon',
|
||||
'line_address_1' => 'ligne d\'adresse 1',
|
||||
'line_address_2' => 'ligne d\'adresse 2',
|
||||
'message' => 'message',
|
||||
'middle_name' => 'deuxième prénom',
|
||||
'minute' => 'minute',
|
||||
'mobile' => 'portable',
|
||||
'month' => 'mois',
|
||||
'name' => 'nom',
|
||||
'national_code' => 'code national',
|
||||
'number' => 'numéro',
|
||||
'password' => 'mot de passe',
|
||||
'password_confirmation' => 'confirmation du mot de passe',
|
||||
'phone' => 'téléphone',
|
||||
'photo' => 'photo',
|
||||
'postal_code' => 'code postal',
|
||||
'price' => 'prix',
|
||||
'province' => 'région',
|
||||
'recaptcha_response_field' => 'champ de réponse recaptcha',
|
||||
'remember' => 'se souvenir',
|
||||
'restored_at' => 'restauré à',
|
||||
'result_text_under_image' => 'texte de résultat sous l\'image',
|
||||
'role' => 'rôle',
|
||||
'second' => 'seconde',
|
||||
'sex' => 'sexe',
|
||||
'short_text' => 'texte court',
|
||||
'size' => 'taille',
|
||||
'state' => 'état',
|
||||
'street' => 'rue',
|
||||
'student' => 'étudiant',
|
||||
'subject' => 'sujet',
|
||||
'teacher' => 'professeur',
|
||||
'terms' => 'conditions',
|
||||
'test_description' => 'description test',
|
||||
'test_locale' => 'localisation test',
|
||||
'test_name' => 'nom test',
|
||||
'text' => 'texte',
|
||||
'time' => 'heure',
|
||||
'title' => 'titre',
|
||||
'updated_at' => 'mis à jour à',
|
||||
'username' => 'nom d\'utilisateur',
|
||||
'year' => 'année',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
}
|
||||
|
||||
.bo-image-preview {
|
||||
max-height: 60px;
|
||||
// max-height: 60px;
|
||||
width: 180px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.text-ellipsis {
|
||||
|
|
@ -13,6 +15,11 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-md {
|
||||
min-width: 38px;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
// Print fiche commerciale
|
||||
@media print {
|
||||
#print-id-product {
|
||||
|
|
|
|||
261
resources/ts/components/BelongsToDropdown.vue
Normal file
261
resources/ts/components/BelongsToDropdown.vue
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
<template>
|
||||
<div class="belongs-to-dropdown">
|
||||
<label
|
||||
:for="name"
|
||||
class="col-form-label"
|
||||
>
|
||||
<b>{{ label }} {{ required ? "*" : "" }}</b>
|
||||
</label>
|
||||
<Transition name="fade">
|
||||
<p
|
||||
v-if="message"
|
||||
class="message"
|
||||
>
|
||||
{{ message }}
|
||||
</p>
|
||||
</Transition>
|
||||
<div class="d-inline-block tagInput w-100">
|
||||
<VueTagsInputCls
|
||||
v-model="item"
|
||||
:tags="selectedItem"
|
||||
:autocomplete-items="filteredItems"
|
||||
:autocomplete-min-length="0"
|
||||
:max-tags="1"
|
||||
:placeholder="placeholder"
|
||||
@before-adding-tag="beforeTagSave"
|
||||
@tags-changed="changedTags"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
v-if="!selectedItem.length"
|
||||
:name="name"
|
||||
type="hidden"
|
||||
value=""
|
||||
>
|
||||
<template
|
||||
v-for="(sItem, index) in selectedItem"
|
||||
:key="index"
|
||||
>
|
||||
<p class="text-danger m-0">
|
||||
{{ parseValidationInput(`${name}`, allErrors) }}
|
||||
</p>
|
||||
<input
|
||||
:name="`${name}`"
|
||||
type="hidden"
|
||||
:value="sItem.id"
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type VueTagsInput from "@sipec/vue3-tags-input";
|
||||
import { VueTagsInput as VueTagsInputCls } from "@sipec/vue3-tags-input";
|
||||
import axios from "axios";
|
||||
import slugify from "slugify";
|
||||
import { defineComponent } from "vue";
|
||||
import parseLaravelErrorMessages from "./../modules/parseLaravelErrorMessages";
|
||||
import route from "./../modules/route";
|
||||
import trans from "./../modules/trans";
|
||||
|
||||
export default defineComponent({
|
||||
name:"BelongsToDropdown",
|
||||
mixins: [route, trans, parseLaravelErrorMessages],
|
||||
components: {
|
||||
VueTagsInputCls,
|
||||
},
|
||||
beforeMount() {
|
||||
if (!this.$attrs.json) {
|
||||
throw new Error("this component requires json");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.name = data.name;
|
||||
this.labelField = data.labelField;
|
||||
this.label = data.label;
|
||||
this.required = data.require;
|
||||
this.allItems = (data.itemsList as CustomTag[]).map(item => {item.text = item[this.labelField] as string; return item;});
|
||||
this.selectedItem = this.parseTags([data.value]);
|
||||
this.intStoreRoute = data.storeRoute ?? null;
|
||||
this.placeholder = data.placeholder;
|
||||
},
|
||||
data(): {
|
||||
selectedItem: CustomTag[];
|
||||
allItems: CustomTag[];
|
||||
name: string;
|
||||
item: string;
|
||||
message: string | null;
|
||||
intStoreRoute: string | null;
|
||||
placeholder: string;
|
||||
label: string;
|
||||
required: boolean;
|
||||
labelField: string;
|
||||
} {
|
||||
return {
|
||||
allItems: [],
|
||||
selectedItem: [],
|
||||
item: "",
|
||||
name: "",
|
||||
message: null,
|
||||
intStoreRoute: null,
|
||||
placeholder: "",
|
||||
label: "",
|
||||
required: false,
|
||||
labelField: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parseTags(intSelectedItem: number[]): CustomTag[] {
|
||||
const out: CustomTag[] = [];
|
||||
intSelectedItem.forEach((intItem: number) => {
|
||||
const selectedItem = this.allItems.find(item => String(item.id) === String(intItem));
|
||||
if (!selectedItem) {
|
||||
console.debug(intItem);
|
||||
throw new Error(`Could not find enum ${intItem}`);
|
||||
}
|
||||
out.push({
|
||||
id: String(selectedItem.id),
|
||||
value: String(selectedItem.id),
|
||||
text: String(selectedItem[`${this.labelField}`])
|
||||
} as LaravelModel as CustomTag);
|
||||
});
|
||||
return out;
|
||||
},
|
||||
parseNewlyCreated(newModel: LaravelModel): CustomTag {
|
||||
let newOut: CustomTag;
|
||||
newOut = {
|
||||
id: String(newModel.id),
|
||||
value: String(newModel.id),
|
||||
text: String(newModel[`${this.labelField}`])
|
||||
} as LaravelModel as CustomTag;
|
||||
return newOut;
|
||||
},
|
||||
/**
|
||||
* Add item
|
||||
* @param obj
|
||||
*/
|
||||
beforeTagSave(obj: VueTagsInput.IAddArgs): void {
|
||||
if (this.selectedItem.length < 1) {
|
||||
let customTag = this.tagExists(this.allItems, obj.tag);
|
||||
if (customTag) {
|
||||
obj.tag = customTag;
|
||||
this.selectedItem.push(customTag);
|
||||
this.item = "";
|
||||
return;
|
||||
} else if (this.intStoreRoute !== null) {
|
||||
const storeRoute = route.methods.route(this.intStoreRoute);
|
||||
if (!storeRoute) {
|
||||
throw new Error("Undefined route " + this.intStoreRoute);
|
||||
}
|
||||
axios
|
||||
.post(storeRoute, {
|
||||
name: obj.tag.text
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.data.status === true) {
|
||||
this.item = "";
|
||||
const brandItem = this.parseNewlyCreated(response.data.brand);
|
||||
this.allItems.push(brandItem);
|
||||
this.selectedItem.push(brandItem);
|
||||
}
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.message = this.__("Nombre d'élements maximal atteint.");
|
||||
setTimeout(() => (this.message = null), 1500);
|
||||
}
|
||||
},
|
||||
tagExists(
|
||||
items: Array<CustomTag>,
|
||||
tagToCheck: VueTagsInput.ITag
|
||||
): CustomTag | null {
|
||||
let customTag: CustomTag | null = null;
|
||||
items.filter((item: CustomTag) => {
|
||||
if (
|
||||
item.id === (tagToCheck as CustomTag).id ||
|
||||
item.slug === slugify(tagToCheck.text)
|
||||
) {
|
||||
customTag = item;
|
||||
}
|
||||
});
|
||||
return customTag;
|
||||
},
|
||||
/**
|
||||
* Delete item.
|
||||
* @param newItems
|
||||
*/
|
||||
changedTags(newItems: Array<CustomTag>): void {
|
||||
this.selectedItem = newItems as Array<CustomTag>;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
filteredItems() {
|
||||
return this.allItems.filter((i) => {
|
||||
return i.text.toLowerCase().indexOf(this.item.toLowerCase()) !== -1;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.vue-tags-input {
|
||||
max-width: 100% !important;
|
||||
.ti- {
|
||||
&autocomplete {
|
||||
max-height: 10rem !important;
|
||||
border-radius: 0.375rem;
|
||||
box-shadow: 0 0 4px var(--bs-gray-400);
|
||||
overflow-y: auto !important;
|
||||
ul {
|
||||
padding: 0.25rem 0 !important;
|
||||
}
|
||||
}
|
||||
&input {
|
||||
padding: 0.375rem !important;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
&tags {
|
||||
line-height: inherit !important;
|
||||
}
|
||||
&tag,
|
||||
&new-tag-input-wrapper {
|
||||
padding: 0.1rem !important;
|
||||
margin: 0.1rem !important;
|
||||
}
|
||||
&new-tag-input-wrapper {
|
||||
font-size: 1em !important;
|
||||
input {
|
||||
min-width: 165px !important;
|
||||
}
|
||||
}
|
||||
&tag {
|
||||
padding: 0.1rem 0.3rem !important;
|
||||
background-color: var(--bs-blue) !important;
|
||||
border-radius: 0.375rem !important;
|
||||
}
|
||||
&selected-item {
|
||||
background-color: var(--bs-gray-300) !important;
|
||||
color: var(--bs-gray-900) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
color: #ff0000;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -63,15 +63,17 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import type VueTagsInput from "@sipec/vue3-tags-input";
|
||||
import { VueTagsInput as VueTagsInputCls } from "@sipec/vue3-tags-input";
|
||||
import axios from "axios";
|
||||
import slugify from "slugify";
|
||||
import { defineComponent } from "vue";
|
||||
import parseLaravelErrorMessages from "./../modules/parseLaravelErrorMessages";
|
||||
import route from "./../modules/route";
|
||||
import trans from "./../modules/trans";
|
||||
import parseLaravelErrorMessages from "./../modules/parseLaravelErrorMessages";
|
||||
|
||||
export default defineComponent({
|
||||
name: "BelongsToManyDropdown",
|
||||
mixins: [route, trans, parseLaravelErrorMessages],
|
||||
components: {
|
||||
VueTagsInputCls,
|
||||
|
|
@ -88,6 +90,7 @@ export default defineComponent({
|
|||
this.selectedItems = this.parseTags(data.value);
|
||||
this.allItems = this.parseTags(data.itemsList);
|
||||
this.placeholder = data.placeholder;
|
||||
this.intStoreRoute = data.storeRoute ?? null;
|
||||
// * Min and max calc.
|
||||
this.min = Number.parseInt((data?.itemLimit ?? [])[0] ?? 0);
|
||||
this.min = this.min < 0 ? 0 : this.min;
|
||||
|
|
@ -108,6 +111,7 @@ export default defineComponent({
|
|||
waitingListObjects: Array<VueTagsInput.IAddArgs>;
|
||||
allErrors: Record<string, Array<string>>;
|
||||
message: string | null;
|
||||
intStoreRoute: string | null;
|
||||
} {
|
||||
return {
|
||||
name: "",
|
||||
|
|
@ -121,6 +125,7 @@ export default defineComponent({
|
|||
waitingListObjects: [],
|
||||
allErrors: {},
|
||||
message: null,
|
||||
intStoreRoute: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -143,6 +148,23 @@ export default defineComponent({
|
|||
this.selectedItems.push(customTag);
|
||||
this.item = "";
|
||||
return;
|
||||
} else if (this.intStoreRoute !== null) {
|
||||
const storeBrandRoute = route.methods.route("brands.store");
|
||||
if (!storeBrandRoute) {
|
||||
throw new Error("Undefined route brands.store");
|
||||
}
|
||||
axios
|
||||
.post(storeBrandRoute, {
|
||||
name: obj.tag.text
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.data.status === true) {
|
||||
obj.addTag();
|
||||
}
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.message = this.__("Nombre d'élements maximal atteint.");
|
||||
|
|
|
|||
|
|
@ -10,22 +10,27 @@
|
|||
>
|
||||
<template v-if="intIsAdded">
|
||||
<FontAwesomeIcon icon="fa-solid fa-minus" />
|
||||
|
||||
<FontAwesomeIcon icon="fa-solid fa-file-pdf" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<FontAwesomeIcon icon="fa-solid fa-plus" />
|
||||
|
||||
<FontAwesomeIcon icon="fa-solid fa-file-pdf" />
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import trans from "./../modules/trans";
|
||||
import route from "./../modules/route";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import Tooltip from "bootstrap/js/dist/tooltip";
|
||||
import { defineComponent } from "vue";
|
||||
import route from "./../modules/route";
|
||||
import trans from "./../modules/trans";
|
||||
|
||||
export default defineComponent({
|
||||
name: "ButtonMultiPdfComponent",
|
||||
mixins: [trans, route],
|
||||
components: {
|
||||
FontAwesomeIcon,
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
</label>
|
||||
<img
|
||||
v-if="intValue"
|
||||
class="img-fluid"
|
||||
class="img-fluid img-preview"
|
||||
:src="intValue"
|
||||
:alt="__('Image placeholder')"
|
||||
:aria-describedby="`PreviewHelp${intId}`"
|
||||
|
|
@ -400,10 +400,10 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import Cropper from "cropperjs";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import Cropper from "cropperjs";
|
||||
import { defineComponent } from "vue";
|
||||
import trans from "./../modules/trans";
|
||||
|
||||
const UNITS = [
|
||||
|
|
@ -417,6 +417,7 @@ const UNITS = [
|
|||
const BYTES_PER_KB = 1024;
|
||||
|
||||
export default defineComponent({
|
||||
name: "ImageInputComponent",
|
||||
mixins: [trans],
|
||||
emits: ["browseFile"],
|
||||
components: {
|
||||
|
|
@ -952,6 +953,10 @@ export default defineComponent({
|
|||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
}
|
||||
.img-preview {
|
||||
width: 180px;
|
||||
height: auto;
|
||||
}
|
||||
.right-aligned {
|
||||
overflow: hidden !important;
|
||||
direction: ltr !important;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import WordCounter from "./WordCounterComponent.vue";
|
|||
const textarea = ref<HTMLTextAreaElement | null>(null);
|
||||
|
||||
export default defineComponent({
|
||||
name: "InputTextAutoComponent",
|
||||
inheritAttrs: false,
|
||||
mixins: [trans],
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
class="col-form-label"
|
||||
for="commentEvaluation"
|
||||
>
|
||||
<b>{{ __("Évaluation du produit") }} *</b>
|
||||
<b>{{ __("Appréciation globale") }} *</b>
|
||||
</label>
|
||||
<InputTextAutoComponent
|
||||
:id="'commentEvaluation' + commentIndex"
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
>
|
||||
{{
|
||||
__(
|
||||
"Doit détailler l'évaluation du produit (3 caractères minimum, 255 maximum)."
|
||||
"Doit détailler l'appéciation globale (3 caractères minimum, 255 maximum)."
|
||||
)
|
||||
}}
|
||||
</small>
|
||||
|
|
@ -227,11 +227,12 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import InputTextAutoComponent from "./InputTextAutoComponent.vue";
|
||||
import parseLaravelErrorMessages from "../modules/parseLaravelErrorMessages";
|
||||
import trans from "../modules/trans";
|
||||
import InputTextAutoComponent from "./InputTextAutoComponent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "MultipleCommentsComponent",
|
||||
components: {
|
||||
InputTextAutoComponent,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -44,19 +44,6 @@
|
|||
:disabled="intValues.length >= intItemLimit[1]"
|
||||
readonly
|
||||
>
|
||||
<button
|
||||
class="btn btn-primary btn-collapse collapsed"
|
||||
type="button"
|
||||
:title="__('Rouler/Dérouler la liste des documents')"
|
||||
data-bs-tooltip="tooltip"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#multiple-documents"
|
||||
aria-expanded="false"
|
||||
aria-controls="multiple-documents"
|
||||
:disabled="!intHasDocuments"
|
||||
>
|
||||
<i class="fa fa-arrow-down" />
|
||||
</button>
|
||||
<button
|
||||
v-if="intValues.length > intItemLimit[0]"
|
||||
@click.prevent="removeFiles"
|
||||
|
|
@ -72,9 +59,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- End main buttons -->
|
||||
<!-- Content collapse -->
|
||||
<!-- Documents list -->
|
||||
<div
|
||||
class="collapse"
|
||||
id="multiple-documents"
|
||||
>
|
||||
<div class="row w-100 mx-auto my-2">
|
||||
|
|
@ -128,12 +114,22 @@
|
|||
<input
|
||||
:id="intId"
|
||||
ref="actualDocument"
|
||||
@change="changedDocument($event, n - 1)"
|
||||
@change="changedDocument($event.target as HTMLInputElement, n - 1)"
|
||||
type="file"
|
||||
class="d-none"
|
||||
:name="intName"
|
||||
:name="intName.replace('[]', '') + `[${n - 1}]`"
|
||||
accept=".pdf"
|
||||
:aria-describedby="`Help${intId}`"
|
||||
:disabled="intPreviousFiles.has(intOriginalFileName[n - 1])"
|
||||
>
|
||||
<input
|
||||
@change="changedDocument(getInput(intId), n - 1)"
|
||||
type="text"
|
||||
class="d-none"
|
||||
:name="intName.replace('[]', '') + `[${n - 1}]`"
|
||||
:aria-describedby="`Help${intId}`"
|
||||
:disabled="!intPreviousFiles.has(intOriginalFileName[n - 1])"
|
||||
:value="intValues[n - 1]"
|
||||
>
|
||||
<button
|
||||
v-if="intValues.length > intItemLimit[0]"
|
||||
|
|
@ -211,7 +207,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End content collapse -->
|
||||
<!-- End documents list -->
|
||||
<small
|
||||
:id="`Help${intId}`"
|
||||
class="form-text text-muted"
|
||||
|
|
@ -222,8 +218,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import { defineComponent } from "vue";
|
||||
import error from "./../modules/parseLaravelErrorMessages";
|
||||
import trans from "./../modules/trans";
|
||||
|
||||
|
|
@ -238,18 +234,19 @@ const UNITS = [
|
|||
const BYTES_PER_KB = 1024;
|
||||
|
||||
export default defineComponent({
|
||||
name: "MultipleDocumentsInputComponent",
|
||||
mixins: [error, trans],
|
||||
data(): {
|
||||
intId: string;
|
||||
intName: string;
|
||||
intHasDocument: boolean[];
|
||||
intHasDocuments: boolean;
|
||||
intPreviousFiles: Set<string>;
|
||||
intOriginalFile: File[];
|
||||
intOriginalFileName: string[];
|
||||
intValues: string[];
|
||||
intHelper: string;
|
||||
intTooltipList: HTMLButtonElement[];
|
||||
intBtnCollapse: HTMLButtonElement | null;
|
||||
intLoopLoadDocuments: number;
|
||||
intViewerLoadDocument: boolean;
|
||||
intInputDocuments: HTMLInputElement[];
|
||||
|
|
@ -261,11 +258,11 @@ export default defineComponent({
|
|||
intName: "",
|
||||
intHasDocument: [],
|
||||
intHasDocuments: false,
|
||||
intPreviousFiles: new Set<string>(),
|
||||
intOriginalFile: [],
|
||||
intOriginalFileName: [],
|
||||
intValues: [],
|
||||
intHelper: "",
|
||||
intBtnCollapse: null,
|
||||
intTooltipList: [],
|
||||
intLoopLoadDocuments: 0,
|
||||
intViewerLoadDocument: false,
|
||||
|
|
@ -294,9 +291,6 @@ export default defineComponent({
|
|||
*/
|
||||
initComponent() {
|
||||
if (this.intValues.length > 0) {
|
||||
this.intBtnCollapse = document.querySelector(
|
||||
"[data-bs-target=\"#multiple-documents\"]"
|
||||
);
|
||||
this.intValues.forEach((element, index) => {
|
||||
element.charAt(0) != "/" ? "/".concat(element) : "";
|
||||
this.intValues.splice(index, 1, element.replace(/^\/|^/, "/"));
|
||||
|
|
@ -314,9 +308,11 @@ export default defineComponent({
|
|||
initDocumentsSaved() {
|
||||
var actualDocuments = this.$refs.actualDocument as HTMLInputElement[];
|
||||
this.intValues.forEach((element, index) => {
|
||||
const documentName = element.substring(element.lastIndexOf("/") + 1);
|
||||
this.intPreviousFiles.add(documentName);
|
||||
this.createFile(
|
||||
element,
|
||||
"document_" + (index + 1) + ".pdf",
|
||||
`${documentName}`,
|
||||
"application/pdf"
|
||||
).then((file: File) => {
|
||||
const container = new DataTransfer();
|
||||
|
|
@ -386,6 +382,9 @@ export default defineComponent({
|
|||
* Remove one specific document.
|
||||
*/
|
||||
removeFile(n: number) {
|
||||
if(this.intPreviousFiles.has(this.intOriginalFileName[n])) {
|
||||
this.intPreviousFiles.delete(this.intOriginalFileName[n]);
|
||||
}
|
||||
this.intValues.splice(n, 1);
|
||||
this.intHasDocument.splice(n, 1);
|
||||
this.intOriginalFile.splice(n, 1);
|
||||
|
|
@ -420,9 +419,8 @@ export default defineComponent({
|
|||
/**
|
||||
* Change one specific document.
|
||||
*/
|
||||
changedDocument(e: Event, n: number) {
|
||||
changedDocument(el: HTMLInputElement, n: number) {
|
||||
const reader = new FileReader();
|
||||
const el = e.target;
|
||||
if (!el || !(el instanceof HTMLInputElement)) {
|
||||
throw new Error(
|
||||
"confirmDoneJS can only be executed on an exising form"
|
||||
|
|
@ -462,9 +460,6 @@ export default defineComponent({
|
|||
this.intLoopLoadDocuments = this.intValues.length;
|
||||
this.loadDocuments([].slice.call(el.files));
|
||||
this.intHasDocuments = true;
|
||||
if (this.intBtnCollapse?.classList.contains("collapsed")) {
|
||||
this.intBtnCollapse?.click();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Update inputs file.
|
||||
|
|
@ -536,6 +531,9 @@ export default defineComponent({
|
|||
maximumFractionDigits: 1,
|
||||
}).format(size);
|
||||
},
|
||||
getInput(intId: string): HTMLInputElement {
|
||||
return document.getElementById(intId) as HTMLInputElement;
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -33,26 +33,16 @@
|
|||
<p class="text-danger m-0">
|
||||
{{
|
||||
parseValidationInput(
|
||||
`${intName}[${String(index)}][name]`,
|
||||
`${intName}[${String(index)}]`,
|
||||
intAllErrors
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<input
|
||||
:name="`${intName}[${String(index)}][id]`"
|
||||
:name="`${intName}[${String(index)}]`"
|
||||
type="hidden"
|
||||
:value="sItem.id"
|
||||
>
|
||||
<input
|
||||
:name="`${intName}[${String(index)}][value]`"
|
||||
type="hidden"
|
||||
:value="sItem.id"
|
||||
>
|
||||
<input
|
||||
:name="`${intName}[${String(index)}][label]`"
|
||||
type="hidden"
|
||||
:value="sItem.text"
|
||||
>
|
||||
</template>
|
||||
<small class="form-text text-muted">{{ intHelper }}</small>
|
||||
</div>
|
||||
|
|
@ -82,15 +72,16 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import type VueTagsInput from "@sipec/vue3-tags-input";
|
||||
import { VueTagsInput as VueTagsInputCls } from "@sipec/vue3-tags-input";
|
||||
import slugify from "slugify";
|
||||
import { defineComponent } from "vue";
|
||||
import parseLaravelErrorMessages from "./../modules/parseLaravelErrorMessages";
|
||||
import route from "./../modules/route";
|
||||
import trans from "./../modules/trans";
|
||||
import parseLaravelErrorMessages from "./../modules/parseLaravelErrorMessages";
|
||||
|
||||
export default defineComponent({
|
||||
name: "SelectMultipleEnumComponent",
|
||||
mixins: [route, trans, parseLaravelErrorMessages],
|
||||
components: {
|
||||
VueTagsInputCls,
|
||||
|
|
@ -144,7 +135,7 @@ export default defineComponent({
|
|||
this.intName = data.name;
|
||||
this.intLabel = data.label;
|
||||
this.intHelper = data.helper;
|
||||
this.intAllItems = this.parseTags(data.itemsList);
|
||||
this.intAllItems = (data.itemsList as CustomTag[]).map(item => {item.text = item.label; return item;});
|
||||
this.intSelectedItem = this.parseTags(data.itemsSelected);
|
||||
this.intPlaceholder = data.placeholder;
|
||||
this.intItemLimit = data.itemLimit;
|
||||
|
|
@ -155,18 +146,21 @@ export default defineComponent({
|
|||
this.checkSpecificItem();
|
||||
},
|
||||
methods: {
|
||||
parseTags(intSelectedItem: CustomTag[]): CustomTag[] {
|
||||
return intSelectedItem.map((intItem: CustomTag) => {
|
||||
if (!intItem.value)
|
||||
intItem = this.intAllItems
|
||||
.filter((intItemEnum) => {
|
||||
return intItemEnum.value == intItem.id;
|
||||
})
|
||||
.shift() as CustomTag;
|
||||
intItem.id = String(intItem.value);
|
||||
intItem.text = String(intItem.label);
|
||||
return intItem;
|
||||
parseTags(intSelectedItem: number[]): CustomTag[] {
|
||||
const out: CustomTag[] = [];
|
||||
intSelectedItem.forEach((intItem: number) => {
|
||||
const selectedItem = this.intAllItems.find(item => String(item.value) === String(intItem));
|
||||
if (!selectedItem) {
|
||||
console.debug(intItem);
|
||||
throw new Error(`Could not find enum ${intItem}`);
|
||||
}
|
||||
out.push({
|
||||
id: String(selectedItem.value),
|
||||
value: String(selectedItem.value),
|
||||
text: String(selectedItem.text)
|
||||
} as LaravelModel as CustomTag);
|
||||
});
|
||||
return out;
|
||||
},
|
||||
/**
|
||||
* Add item
|
||||
|
|
|
|||
|
|
@ -12,13 +12,17 @@
|
|||
class="form-select source-select"
|
||||
:name="intName"
|
||||
:aria-describedby="`${intId}Help`"
|
||||
@change="setSelectedValue()"
|
||||
@change="setSelectedValue(0)"
|
||||
>
|
||||
<option
|
||||
:value="null"
|
||||
:selected="intSelectedValue === 0"
|
||||
/>
|
||||
<option
|
||||
v-for="(itemEnum, indexEnum) in intSourceEnum"
|
||||
:key="indexEnum"
|
||||
:value="itemEnum.value"
|
||||
:selected="itemEnum.value === intModel?.source"
|
||||
:selected="itemEnum.value === intModel?.source || itemEnum.value === intSelectedValue "
|
||||
>
|
||||
{{ itemEnum.label }}
|
||||
</option>
|
||||
|
|
@ -40,9 +44,9 @@
|
|||
class="form-control"
|
||||
name="catalogue"
|
||||
type="text"
|
||||
placeholder="Nom du catalogue"
|
||||
placeholder=""
|
||||
list="productsCatalog"
|
||||
:value="intModel?.catalogue"
|
||||
:value="intModel?.catalogue ?? intOldDetails"
|
||||
>
|
||||
<datalist id="productsCatalog">
|
||||
<option
|
||||
|
|
@ -75,9 +79,9 @@
|
|||
class="form-control"
|
||||
name="shop"
|
||||
type="text"
|
||||
placeholder="Nom du magasin"
|
||||
placeholder=""
|
||||
list="productsShop"
|
||||
:value="intModel?.shop"
|
||||
:value="intModel?.shop ?? intOldDetails"
|
||||
>
|
||||
<datalist id="productsShop">
|
||||
<option
|
||||
|
|
@ -110,9 +114,9 @@
|
|||
class="form-control"
|
||||
name="fair"
|
||||
type="text"
|
||||
placeholder="Nom du salon"
|
||||
placeholder=""
|
||||
list="productsFair"
|
||||
:value="intModel?.fair"
|
||||
:value="intModel?.fair ?? intOldDetails"
|
||||
>
|
||||
<datalist id="productsFair">
|
||||
<option
|
||||
|
|
@ -138,6 +142,7 @@ import { defineComponent } from "vue";
|
|||
import trans from "../modules/trans";
|
||||
|
||||
export default defineComponent({
|
||||
name: "SourceInputsComponent",
|
||||
mixins: [trans],
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}");
|
||||
|
|
@ -151,7 +156,9 @@ export default defineComponent({
|
|||
this.intProductsCatalog = data.productsCatalog;
|
||||
this.intSourceEnum = data.sourceEnum;
|
||||
this.intHelper = data.helper;
|
||||
this.setSelectedValue();
|
||||
this.intSelectedValue = data.oldValue;
|
||||
this.intOldDetails = data.oldDetails;
|
||||
this.setSelectedValue(data.oldValue);
|
||||
},
|
||||
data(): {
|
||||
intId: string;
|
||||
|
|
@ -167,6 +174,7 @@ export default defineComponent({
|
|||
intSourceMagazin: boolean;
|
||||
intSourceShow: boolean;
|
||||
intHelper: string;
|
||||
intOldDetails: string;
|
||||
} {
|
||||
return {
|
||||
intId: "",
|
||||
|
|
@ -182,18 +190,19 @@ export default defineComponent({
|
|||
intSourceMagazin: false,
|
||||
intSourceShow: false,
|
||||
intHelper: "",
|
||||
intOldDetails: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Set selected source variables.
|
||||
*/
|
||||
setSelectedValue() {
|
||||
setSelectedValue(oldValue: number) {
|
||||
this.$nextTick(() => {
|
||||
const select = document.querySelector(
|
||||
".source-select"
|
||||
) as HTMLSelectElement;
|
||||
this.intSelectedValue = Number(select.value);
|
||||
this.intSelectedValue = isNaN(Number(select.value)) ? Number(oldValue) : Number(select.value) ;
|
||||
this.updateVue();
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -75,10 +75,11 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import MultipleCommentsComponent from "./MultipleCommentsComponent.vue";
|
||||
import trans from "../modules/trans";
|
||||
import MultipleCommentsComponent from "./MultipleCommentsComponent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TastedProductComponent",
|
||||
components: {
|
||||
MultipleCommentsComponent,
|
||||
},
|
||||
|
|
@ -150,6 +151,8 @@ export default defineComponent({
|
|||
: false
|
||||
) {
|
||||
this.updateBoolean();
|
||||
} else {
|
||||
this.intIsTasted = data.oldCheck;
|
||||
}
|
||||
this.intCommentModel =
|
||||
data.commentOldModel.length > 0
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
:name="intName"
|
||||
type="text"
|
||||
:value="intValue"
|
||||
:placeholder="__('112233445566')"
|
||||
placeholder=""
|
||||
class="form-control input-gencode"
|
||||
:class="[
|
||||
intResult != undefined ? [intResult ? 'is-invalid' : 'is-valid'] : '',
|
||||
|
|
@ -43,13 +43,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import { defineComponent } from "vue";
|
||||
import route from "./../modules/route";
|
||||
import trans from "./../modules/trans";
|
||||
|
||||
export default defineComponent({
|
||||
name: "VerificationCodeComponent",
|
||||
mixins: [route, trans],
|
||||
components: {
|
||||
FontAwesomeIcon,
|
||||
|
|
@ -161,7 +162,7 @@ export default defineComponent({
|
|||
this.intCode = inputCode.value;
|
||||
window.axios
|
||||
.post(this.getRouteProductGencode(), {
|
||||
GENCODE: this.intCode,
|
||||
GENCOD: this.intCode,
|
||||
ID: this.intModel?.id,
|
||||
})
|
||||
.then((reponse) => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { defineComponent } from "vue";
|
|||
import trans from "../modules/trans";
|
||||
|
||||
export default defineComponent({
|
||||
name: "WordCounterComponent",
|
||||
mixins: [trans],
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}");
|
||||
|
|
|
|||
6
resources/ts/index.d.ts
vendored
6
resources/ts/index.d.ts
vendored
|
|
@ -1,7 +1,7 @@
|
|||
export {};
|
||||
export { };
|
||||
|
||||
import { AxiosStatic } from "axios";
|
||||
import type VueTagsInput from "@sipec/vue3-tags-input";
|
||||
import { AxiosStatic } from "axios";
|
||||
|
||||
// * Recursive List<string>
|
||||
type StringList = Record<PropertyKey, string>;
|
||||
|
|
@ -30,7 +30,7 @@ type NestedNumberList = NestedNumberListInterface | NumberList | number;
|
|||
type ModelList = Array<Model>;
|
||||
|
||||
declare global {
|
||||
type CustomTag = LaravelModel & VueTagsInput.ITag;
|
||||
type CustomTag = LaravelModel & VueTagsInput.ITag & {name:string,label:string, value:number|string};
|
||||
type LaravelModel = Model;
|
||||
type LaravelModelList = ModelList;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,49 @@
|
|||
import { dom, library, config } from "~/@fortawesome/fontawesome-svg-core";
|
||||
import { config, dom, library } from "~/@fortawesome/fontawesome-svg-core";
|
||||
import {
|
||||
faEraser,
|
||||
faArrowLeftLong,
|
||||
faPlus,
|
||||
faMinus,
|
||||
faTrash,
|
||||
faEye,
|
||||
faFolderOpen,
|
||||
faCrop,
|
||||
faQuestionCircle,
|
||||
faCheck,
|
||||
faUndoAlt,
|
||||
faRedoAlt,
|
||||
faSyncAlt,
|
||||
faSearchPlus,
|
||||
faSearchMinus,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
faArrowUp,
|
||||
faArrowDown,
|
||||
faArrowsAltH,
|
||||
faArrowsAltV,
|
||||
faWandMagic,
|
||||
faPencil,
|
||||
faTimes,
|
||||
faBackspace,
|
||||
faSearch,
|
||||
faArrowRightFromBracket,
|
||||
faAnglesRight,
|
||||
faArrowAltCircleDown,
|
||||
faArrowAltCircleUp,
|
||||
faQuestion,
|
||||
faXmark,
|
||||
faArrowDown,
|
||||
faArrowLeft,
|
||||
faArrowLeftLong,
|
||||
faArrowRight,
|
||||
faArrowRightFromBracket,
|
||||
faArrowUp,
|
||||
faArrowsAltH,
|
||||
faArrowsAltV,
|
||||
faBackspace,
|
||||
faCheck,
|
||||
faChevronLeft,
|
||||
faChevronRight,
|
||||
faCircleNotch,
|
||||
faCopy,
|
||||
faFile,
|
||||
faLink,
|
||||
faPrint,
|
||||
faImage,
|
||||
faCrop,
|
||||
faDownload,
|
||||
faEraser,
|
||||
faEye,
|
||||
faFile,
|
||||
faFilePdf,
|
||||
faFolderOpen,
|
||||
faHeading,
|
||||
faImage,
|
||||
faLink,
|
||||
faListUl,
|
||||
faMinus,
|
||||
faPencil,
|
||||
faPlus,
|
||||
faPrint,
|
||||
faQuestion,
|
||||
faQuestionCircle,
|
||||
faRedoAlt,
|
||||
faSearch,
|
||||
faSearchMinus,
|
||||
faSearchPlus,
|
||||
faSyncAlt,
|
||||
faTimes,
|
||||
faTrash,
|
||||
faUndoAlt,
|
||||
faWandMagic,
|
||||
faXmark,
|
||||
} from "~/@fortawesome/free-solid-svg-icons";
|
||||
|
||||
config.autoReplaceSvg = false;
|
||||
|
|
@ -77,5 +84,12 @@ library.add(faLink);
|
|||
library.add(faPrint);
|
||||
library.add(faImage);
|
||||
library.add(faDownload);
|
||||
library.add(faFilePdf);
|
||||
library.add(faListUl);
|
||||
library.add(faHeading);
|
||||
library.add(faAnglesRight);
|
||||
library.add(faChevronLeft);
|
||||
library.add(faChevronRight);
|
||||
library.add(faCircleNotch);
|
||||
|
||||
dom.watch();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@
|
|||
{{-- CONTENT --}}
|
||||
<table width="100%">
|
||||
<tr>
|
||||
@if (isset($product->picture) && !empty($product->picture))
|
||||
<td class="table-cell-text-center" width="30%">
|
||||
@if (isset($product->picture))
|
||||
<img src="{{ public_path($product->picture) }}" alt="{{ $product->picture_alt }}" alt="{{ $product->picture_title }}">
|
||||
@endif
|
||||
</td>
|
||||
<td width="70%">
|
||||
@else
|
||||
<td width="100%">
|
||||
@endif
|
||||
@if (isset($product->brand_id->name))
|
||||
<h2><strong>{{ __('Marque du produit : ') }}</strong><span>{{ $product->brand_id->name }}</span></h2>
|
||||
@endif
|
||||
|
|
@ -21,10 +23,10 @@
|
|||
<p><strong>{{ __('Certification : ') }}</strong>{{ $product->certification }}</p>
|
||||
@endif
|
||||
@if (isset($product->origins))
|
||||
<p><strong>{{ __('Origines : ') }}</strong>{{ $product->origins }}</p>
|
||||
<p><strong>{{ __('Origines : ') }}</strong>{{ Str::replaceLast(', ', '', $product->origins) }}</p>
|
||||
@endif
|
||||
@if (isset($product->claims))
|
||||
<p><strong>{{ __('Allégations : ') }}</strong>{{ $product->claims }}</p>
|
||||
<p><strong>{{ __('Allégations : ') }}</strong>{{ Str::replaceLast(', ', '', $product->claims) }}</p>
|
||||
@endif
|
||||
<table class="table" width="100%">
|
||||
<tr class="table-header">
|
||||
|
|
@ -49,11 +51,14 @@
|
|||
@if (isset($product->pvc))
|
||||
<td class="table-cell-text-center">{{ $product->pvc }}€</td>
|
||||
@endif
|
||||
<td class="table-cell-text-center">--,--€</td>
|
||||
@php
|
||||
$priceKg = 1000 * $product->pvc / $product->weight;
|
||||
@endphp
|
||||
<td class="table-cell-text-center">{{ number_format(round($priceKg, 2), 2, ',', ' ') }}€</td>
|
||||
</tr>
|
||||
</table>
|
||||
@if (isset($product->productKeywords))
|
||||
<p><strong>{{ __('Tendances identifiées : ') }}</strong>{{ $product->productKeywords }}</p>
|
||||
<p><strong>{{ __('Tendances identifiées : ') }}</strong>{{ Str::replaceLast(', ', '', $product->productKeywords) }}</p>
|
||||
@endif
|
||||
@if (isset($product->distribution_network))
|
||||
<p><strong>{{ __('Réseau de distribution : ') }}</strong>{{ $product->distribution_network }}</p>
|
||||
|
|
@ -96,7 +101,7 @@
|
|||
@endif
|
||||
<table width="100%" class="product-saw">
|
||||
<tr>
|
||||
@if (isset($product->saw_at))
|
||||
@if (isset($product->seen_at))
|
||||
<td class="table-cell-text-center" width="50%">
|
||||
<strong>{{ __('Téléchargez les documents :') }}</strong>
|
||||
<a href="{{ route('products.download', $product->id) }}">{{ __('ICI') }}</a>
|
||||
|
|
@ -104,7 +109,7 @@
|
|||
@endif
|
||||
@if (isset($product->source))
|
||||
<td class="table-cell-text-center" width="50%">
|
||||
<p><strong>{{ __('Vu le : ') }}</strong>{{ $product->saw_at }}</p>
|
||||
<p><strong>{{ __('Vu le : ') }}</strong>{{ $product->seen_at }}</p>
|
||||
<p><strong>{{ __('Lieu : ') }}</strong>{{ $product->source }}, {{ $product->fair }}{{ $product->shop }}{{ $product->catalogue }}</p>
|
||||
<p><strong>{{ __('Par : ') }}</strong>{{ $product->user_id->firstname }} {{ $product->user_id->lastname }}</p>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
h2 {
|
||||
font-weight: lighter;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
|
@ -61,11 +60,11 @@
|
|||
}
|
||||
|
||||
.table-header {
|
||||
background-color: black;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.table-header td {
|
||||
color: white;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.table-body td {
|
||||
|
|
@ -75,6 +74,10 @@
|
|||
.table-cell-text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-cell-text-start {
|
||||
text-align: start;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@
|
|||
<li>
|
||||
<a class="dropdown-item @if(request()->routeIs('products.*')) active @endif"
|
||||
href="{{ route('products.index') }}"
|
||||
title="{{ __('Administrer les :model', ['model' => __('models.classes.products')]) }}"
|
||||
title="{{ __('Administrer les :model', ['model' => trans_choice('models.classes.products_monitoring', 2)]) }}"
|
||||
data-bs="tooltip"
|
||||
data-bs-placement="right">
|
||||
{{ __('models.classes.products') }}
|
||||
{{ trans_choice('models.classes.products_monitoring', 2) }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,60 +1,98 @@
|
|||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination d-flex justify-content-center align-items-center m-0 mt-3">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" data-bs="tooltip" data-bs-placement="top"
|
||||
title="{{ __('Page précédente') }}" aria-disabled="true" aria-label="@lang('Page précédente')">
|
||||
<span class="page-link" aria-hidden="true">‹</span>
|
||||
<nav class="d-flex justify-content-between align-items-center my-3">
|
||||
{{-- SELECT ITEMS PER PAGE --}}
|
||||
<div class="dropup-center dropup d-flex justify-content-center align-items-center input-group w-fit">
|
||||
<span class="btn-md input-group-text" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="{{ __('crud.pagination.paginate_list') }}">
|
||||
<i class="fa-solid fa-list-ul"></i>
|
||||
</span>
|
||||
<button class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" type="button" aria-expanded="false">
|
||||
{{ Cache::get('pagination.' .Str::slug(request()->route()->getName())) }}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (\App\Enums\Pagination\ItemsPerPaginationEnum::toArray() as $itemsPerPaginationEnum)
|
||||
<li>
|
||||
<a class="dropdown-item text-center"
|
||||
href="{{ request()->fullUrlWithQuery(['pagination' => $itemsPerPaginationEnum->value]) }}">
|
||||
{{ $itemsPerPaginationEnum->value }}
|
||||
</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" data-bs="tooltip" data-bs-placement="top"
|
||||
title="{{ __('Page précédente') }}" href="{{ $paginator->previousPageUrl() }}"
|
||||
rel="prev" aria-label="@lang('Page précédente')">‹</a>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@if ($paginator->hasPages())
|
||||
{{-- PAGINATION --}}
|
||||
<div class="d-flex justify-content-end align-items-center">
|
||||
<ul class="pagination m-0">
|
||||
{{-- PREVIOUS PAGE --}}
|
||||
<li class="page-item @if ($paginator->onFirstPage()) disabled @endif">
|
||||
<a class="page-link d-flex align-items-center h-100" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="{{ __('crud.pagination.previous') }}" aria-label="@lang('crud.pagination.previous')"
|
||||
@if (!$paginator->onFirstPage()) href="{{ $paginator->previousPageUrl() }}" @endif rel="prev"
|
||||
@if ($paginator->onFirstPage()) aria-hidden="true" @endif>
|
||||
<i class="fa-solid fa-chevron-left fa-2xs mt-1"></i>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<li class="page-item disabled" aria-disabled="true"><span
|
||||
class="page-link">{{ $element }}</span></li>
|
||||
{{-- FIRST PAGE --}}
|
||||
@if ($paginator->currentPage() > 3)
|
||||
<li class="page-item" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="{{ __('crud.pagination.specific_page', ['id' => 1]) }}">
|
||||
<a class="page-link" href="{{ $paginator->url(1) }}">1</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<li class="page-item active" data-bs="tooltip" data-bs-placement="top"
|
||||
title="{{ __('Page actuelle') }}" aria-current="page">
|
||||
<span class="page-link">{{ $page }}</span>
|
||||
{{-- HIDE PAGES TOO FAR --}}
|
||||
@if ($paginator->currentPage() > 4)
|
||||
<li class="page-item disabled"><span class="page-link">...</span></li>
|
||||
@endif
|
||||
{{-- SHOW ACTUAL PAGES + N-2/N+2 --}}
|
||||
@foreach (range(1, $paginator->lastPage()) as $i)
|
||||
@if ($i >= $paginator->currentPage() - 2 && $i <= $paginator->currentPage() + 2)
|
||||
@if ($i == $paginator->currentPage())
|
||||
<li class="page-item">
|
||||
<div class="dropup-center dropup">
|
||||
<button class="btn btn-primary dropdown-toggle rounded-0" data-bs-toggle="dropdown" type="button"
|
||||
aria-expanded="false">
|
||||
{{ $i }}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
@for ($i = 1; $i <= $paginator->lastPage(); $i++)
|
||||
<li>
|
||||
<a class="dropdown-item text-center"
|
||||
href="{{ request()->fullUrlWithQuery(['page' => $i]) }}">
|
||||
{{ $i }}
|
||||
</a>
|
||||
</li>
|
||||
@endfor
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" data-bs="tooltip" data-bs-placement="top"
|
||||
title="{{ __('Page n°:id', ['id' => $page]) }}"
|
||||
href="{{ $url }}">{{ $page }}</a>
|
||||
<li class="page-item" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="{{ __('crud.pagination.specific_page', ['id' => $i]) }}">
|
||||
<a class="page-link" href="{{ $paginator->url($i) }}">{{ $i }}</a>
|
||||
</li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
{{-- HIDE PAGES TOO FAR --}}
|
||||
@if ($paginator->currentPage() < $paginator->lastPage() - 3)
|
||||
<li class="page-item disabled"><span class="page-link">...</span></li>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" data-bs="tooltip" data-bs-placement="top" title="{{ __('Page suivante') }}"
|
||||
href="{{ $paginator->nextPageUrl() }}" rel="next"
|
||||
aria-label="@lang('Page suivante')">›</a>
|
||||
{{-- LAST PAGE --}}
|
||||
@if ($paginator->currentPage() < $paginator->lastPage() - 2)
|
||||
<li class="page-item" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="{{ __('crud.pagination.specific_page', ['id' => $paginator->lastPage()]) }}">
|
||||
<a class="page-link" href="{{ $paginator->url($paginator->lastPage()) }}">{{ $paginator->lastPage() }}</a>
|
||||
</li>
|
||||
@endif
|
||||
{{-- NEXT PAGE --}}
|
||||
<li class="page-item @if (!$paginator->hasMorePages()) disabled @endif">
|
||||
<a class="page-link d-flex align-items-center h-100" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="{{ __('crud.pagination.next') }}" aria-label="@lang('crud.pagination.next')"
|
||||
@if ($paginator->hasMorePages()) href="{{ $paginator->nextPageUrl() }}" @endif rel="next"
|
||||
@if (!$paginator->hasMorePages()) aria-hidden="true" @endif>
|
||||
<i class="fa-solid fa-chevron-right fa-2xs mt-1"></i></a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" data-bs="tooltip" data-bs-placement="top"
|
||||
title="{{ __('Page suivante') }}" aria-disabled="true" aria-label="@lang('Page suivante')">
|
||||
<span class="page-link" aria-hidden="true">›</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -16,4 +16,7 @@
|
|||
multipdf: "{{ route('products.multipdf.update', ['id' => 'ID']) }}",
|
||||
duplicate: "{{ route('products.duplicate', ['product' => 'PRODUCT']) }}"
|
||||
};
|
||||
window.__SYSTEM._routes.brands = {
|
||||
store: "{{ route('brands.store') }}",
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ $ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
|
|||
@endphp
|
||||
{{-- NO Sort for order column --}}
|
||||
@if($col === 'order' or in_array($col, $ignore))
|
||||
@if(!$noOrder or $rst)
|
||||
@if ($col === 'multi_pdf')
|
||||
<th scope="col" class="text-center @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif">
|
||||
<span>{{ $colname }}</span>
|
||||
|
|
@ -35,7 +34,6 @@ $ignore = (isset($ignore) and is_array($ignore)) ? $ignore : [];
|
|||
<span>{{ $colname }}</span>
|
||||
</th>
|
||||
@endif
|
||||
@endif
|
||||
@continue
|
||||
@endif
|
||||
<th scope="col" class="text-center @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif">
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@
|
|||
<div class="card-body">
|
||||
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-center">
|
||||
<div class="text-center text-lg-start pb-2 p-lg-0">
|
||||
<h5 class="card-title">{{ __('models.classes.products') }}</h5>
|
||||
<h5 class="card-title">{{ trans_choice('models.classes.products_monitoring', 2) }}</h5>
|
||||
<p class="card-text text-muted fst-italic">{{ __('- accéder à l\'ensemble des produits enregistrés') }}</p>
|
||||
</div>
|
||||
<a href="{{ route('products.index') }}"
|
||||
class="btn btn-primary"
|
||||
title="{{ __('Accéder à la liste des :model', ['model' => __('models.classes.products')]) }}"
|
||||
title="{{ __('Accéder à la liste des :model', ['model' => trans_choice('models.classes.products_monitoring', 2)]) }}"
|
||||
data-bs="tooltip">
|
||||
{{ __('Voir la liste') }}
|
||||
{{ __('Consulter/Insérer une :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@extends('layout')
|
||||
|
||||
@section('title', sprintf('%s - %s', __('models.classes.products'), Str::ucfirst(__('création'))))
|
||||
@section('title', sprintf('%s - %s', trans_choice('models.classes.products_monitoring', 1), Str::ucfirst(__('création'))))
|
||||
@section('description', __('Création d\'un produit'))
|
||||
@section('breadcrumb', 'no')
|
||||
|
||||
|
|
@ -14,17 +14,17 @@
|
|||
class="btn btn-primary text-decoration-none m-0"
|
||||
data-bs="tooltip"
|
||||
data-bs-placement="top"
|
||||
title="{{ __('Retourner sur la liste des :model', ['model' => __('models.classes.products')]) }}"
|
||||
title="{{ __('Retourner sur la liste des :model', ['model' => trans_choice('models.classes.products_monitoring', 2)]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
<span class="fw-bold">{{ Str::singular(__('models.classes.products')) }}</span> <small class="text-muted">{{ Str::ucfirst(__('création')) }}</small>
|
||||
<span class="fw-bold">{{ trans_choice('models.classes.products_monitoring', 1) }}</span> <small class="text-muted">{{ Str::ucfirst(__('création')) }}</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 d-flex justify-content-center justify-content-md-end align-items-center">
|
||||
<button id="formSubmitClone"
|
||||
class="btn btn-primary pull-end"
|
||||
title="{{ __('Sauvegarder ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
class="btn btn-success pull-end"
|
||||
title="{{ __('Sauvegarder cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
data-bs="tooltip">
|
||||
{{ __('Sauvegarder') }}
|
||||
</button>
|
||||
|
|
@ -39,8 +39,8 @@
|
|||
<div class="col text-center">
|
||||
<button id="formSubmit"
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
title="{{ __('Sauvegarder ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
class="btn btn-success"
|
||||
title="{{ __('Sauvegarder cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
data-bs="tooltip">{{ __('Sauvegarder') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@extends('layout')
|
||||
|
||||
@section('title', sprintf('%s - %s', __('models.classes.products'), Str::ucfirst(__('édition'))))
|
||||
@section('title', sprintf('%s - %s', trans_choice('models.classes.products_monitoring', 1), Str::ucfirst(__('édition'))))
|
||||
@section('description', __('Édition d\'un produit'))
|
||||
@section('breadcrumb', 'no')
|
||||
|
||||
|
|
@ -14,11 +14,11 @@
|
|||
class="btn btn-primary text-decoration-none m-0"
|
||||
data-bs="tooltip"
|
||||
data-bs-placement="top"
|
||||
title="{{ __('Retourner sur la liste des :model', ['model' => __('models.classes.products')]) }}"
|
||||
title="{{ __('Retourner sur la liste des :model', ['model' => trans_choice('models.classes.products_monitoring', 2)]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
<span class="fw-bold">{{ Str::singular(__('models.classes.products')) }}</span> <small class="text-muted">{{ Str::ucfirst(__('édition')) }}</small>
|
||||
<span class="fw-bold">{{ trans_choice('models.classes.products_monitoring', 1) }}</span> <small class="text-muted">{{ Str::ucfirst(__('édition')) }}</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 d-flex justify-content-center justify-content-md-end align-items-center">
|
||||
|
|
@ -28,13 +28,13 @@
|
|||
<div class="btn-group" role="group">
|
||||
<button id="formSubmitClone"
|
||||
type="button"
|
||||
class="btn btn-primary pull-end"
|
||||
title="{{ __('Sauvegarder ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
class="btn btn-success pull-end"
|
||||
title="{{ __('Sauvegarder cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
data-bs="tooltip">
|
||||
{{ __('Sauvegarder') }}
|
||||
</button>
|
||||
<button class="btn btn-danger"
|
||||
title="{{ __('Supprimer ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
title="{{ __('Supprimer cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
|
|
@ -52,8 +52,8 @@
|
|||
<div class="col text-center">
|
||||
<button id="formSubmit"
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
title="{{ __('Sauvegarder ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
class="btn btn-success"
|
||||
title="{{ __('Sauvegarder cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
data-bs="tooltip">
|
||||
{{ __('Sauvegarder') }}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
<div class="row">
|
||||
{{-- TITLE --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<legend>{{ __('Titre') }}</legend>
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Nom') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@include('partials.inputs.varchar', [
|
||||
'id' => 'productName',
|
||||
'label' => __('Titre du :model', ['model' => Str::singular(__('models.classes.products'))]),
|
||||
'label' => __('Nom du :model', ['model' => Str::singular(__('models.classes.products'))]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'name',
|
||||
'required' => true,
|
||||
'model' => $productModel ?? null,
|
||||
'placeholder' => '',
|
||||
'helper' => __('Doit décrire précisément le produit (3 caractères minimum, 255 maximum).'),
|
||||
])
|
||||
</div>
|
||||
|
|
@ -20,17 +21,21 @@
|
|||
</div>
|
||||
{{-- BRANDS --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('models.classes.brands') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@include('partials.inputs.bigint-foreign-id', [
|
||||
'id' => 'productBrand',
|
||||
'label' => trans(':model associé(e)', ['model' => Str::singular(trans('models.classes.brands'))]),
|
||||
@include('partials.inputs.bigint-belongs-to', [
|
||||
'name' => 'brand_id',
|
||||
'model' => $productModel ?? null,
|
||||
'associatedModels' => $brands,
|
||||
'helper' => trans('Selectionnez un(e) :model parmis la liste.', ['model' => Str::singular(trans('models.classes.brands'))]),
|
||||
'itemLimit' => [0, 1],
|
||||
'itemsList' => $brands,
|
||||
'storeRoute' => 'brands.store',
|
||||
'labelField' => 'name',
|
||||
'helper' => __('La marque associée au produit.'),
|
||||
'placeholder' => '',
|
||||
'require' => true,
|
||||
'label' => __('Marque associée'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -38,42 +43,47 @@
|
|||
</div>
|
||||
{{-- SOURCE --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<legend>{{ __("Source") }}</legend>
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Source') }}</legend>
|
||||
@php
|
||||
$data = [
|
||||
'id' => 'productSource',
|
||||
'label' => __('Source associé(e)'),
|
||||
'name' => 'source',
|
||||
'model' => $productModel ?? null,
|
||||
'sourceEnum' => \App\Enums\Products\SourceEnum::toArray(),
|
||||
'productsFair' => $productsFair,
|
||||
'productsShop' => $productsShop,
|
||||
'productsCatalog' => $productsCatalog,
|
||||
'helper' => __('Selectionnez un(e) Source maximum parmis la liste.')
|
||||
];
|
||||
$data = [
|
||||
'id' => 'productSource',
|
||||
'label' => __('Source'),
|
||||
'name' => 'source',
|
||||
'model' => $productModel ?? null,
|
||||
'sourceEnum' => \App\Enums\Products\SourceEnum::toArray(),
|
||||
'productsFair' => $productsFair,
|
||||
'productsShop' => $productsShop,
|
||||
'productsCatalog' => $productsCatalog,
|
||||
'oldValue' => old('source') ?? ($productModel->source ?? 0),
|
||||
'oldDetails' => old('shop', old('fair', old('catalogue'))) ?? '',
|
||||
'helper' => __('Selectionnez un(e) Source maximum parmi la liste.'),
|
||||
];
|
||||
@endphp
|
||||
<div class="source-inputs" data-json='@json($data)'></div>
|
||||
@include('modules.input-error', ['inputName' => 'source'])
|
||||
@include('modules.input-error', ['inputName' => 'catalogue'])
|
||||
@include('modules.input-error', ['inputName' => 'shop'])
|
||||
@include('modules.input-error', ['inputName' => 'fair'])
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- GENCODE --}}
|
||||
{{-- GENCOD --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<legend>{{ __('GENCODE') }}</legend>
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('GENCOD') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@php
|
||||
$data = [
|
||||
'id' => 'productGencod',
|
||||
'label' => __('Code'),
|
||||
'name' => 'gencod',
|
||||
'model' => $productModel ?? null,
|
||||
'value' => old('gencod') ?? $productModel->gencod ?? null,
|
||||
'maxlength' => 13,
|
||||
'required' => false,
|
||||
'helper' => __('Saisir une suite de 13 chiffres maximum.')
|
||||
];
|
||||
$data = [
|
||||
'id' => 'productGencod',
|
||||
'label' => __('Code'),
|
||||
'name' => 'gencod',
|
||||
'model' => $productModel ?? null,
|
||||
'value' => old('gencod') ?? ($productModel->gencod ?? null),
|
||||
'maxlength' => 13,
|
||||
'required' => false,
|
||||
'helper' => __('Saisir une suite de 13 chiffres maximum.'),
|
||||
];
|
||||
@endphp
|
||||
<div class="verification-code" data-json='@json($data)'></div>
|
||||
@include('modules.input-error', ['inputName' => 'gencod'])
|
||||
|
|
@ -83,7 +93,7 @@
|
|||
</div>
|
||||
{{-- URL --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('URL') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
|
|
@ -94,7 +104,7 @@
|
|||
'name' => 'url',
|
||||
'required' => false,
|
||||
'model' => $productModel ?? null,
|
||||
'placeholder' => __('https://google.com'),
|
||||
'placeholder' => '',
|
||||
'helper' => __('Doit être une url valide.'),
|
||||
])
|
||||
</div>
|
||||
|
|
@ -103,33 +113,9 @@
|
|||
</div>
|
||||
{{-- IMAGE --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<legend>{{ __('Image') }}</legend>
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Visuel de référence') }}</legend>
|
||||
<div class="row">
|
||||
{{-- PICTURE_ALT --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
@include('partials.inputs.varchar', [
|
||||
'id' => 'homePart3PictureAlt',
|
||||
'label' => __('Texte alternatif de :model', ['model' => Str::singular(__('models.classes.products'))]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'picture_alt',
|
||||
'required' => true,
|
||||
'model' => $productModel ?? null,
|
||||
'helper' => __('Doit décrire votre image pour les moteurs de recherches et les non voyants (5 à 6 mots, minimum 3 caractères).'),
|
||||
])
|
||||
</div>
|
||||
{{-- PICTURE_TITLE --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
@include('partials.inputs.varchar', [
|
||||
'id' => 'homePart3PictureTitle',
|
||||
'label' => __('Titre de :model', ['model' => Str::singular(__('models.classes.products'))]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'picture_title',
|
||||
'required' => true,
|
||||
'model' => $productModel ?? null,
|
||||
'helper' => __('Utile pour les utilisateurs, s\'affiche en passant la souris sur l\'image (minimum 3 caractères).'),
|
||||
])
|
||||
</div>
|
||||
<div class="col-12">
|
||||
@include('partials.inputs.varchar-picture', [
|
||||
'id' => 'productImage',
|
||||
|
|
@ -147,19 +133,19 @@
|
|||
</div>
|
||||
{{-- DOCUMENTS --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<legend>{{ __('Documents') }}</legend>
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Documents complémentaires') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@php
|
||||
$data = [
|
||||
'id' => 'productDocs',
|
||||
'name' => 'docs[]',
|
||||
'value' => $productModel->docs ?? [],
|
||||
'limit' => [0,5],
|
||||
'helper' => __('Doit respecter le format :format.', ['format' => '.pdf']),
|
||||
'errors' => $errors->getBag('default')->getMessages()
|
||||
];
|
||||
$data = [
|
||||
'id' => 'productDocs',
|
||||
'name' => 'docs[]',
|
||||
'value' => $productModel->docs ?? [],
|
||||
'limit' => [0, 5],
|
||||
'helper' => __('Doit respecter le format :format.', ['format' => '.pdf']),
|
||||
'errors' => $errors->getBag('default')->getMessages(),
|
||||
];
|
||||
@endphp
|
||||
<div class="multiple-documents-input" data-json='@json($data)'></div>
|
||||
</div>
|
||||
|
|
@ -168,7 +154,7 @@
|
|||
</div>
|
||||
{{-- CERTIFICATION --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Certification') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
|
|
@ -179,7 +165,7 @@
|
|||
'model' => $productModel ?? null,
|
||||
'require' => false,
|
||||
'associatedModels' => \App\Enums\Products\CertificationEnum::toArray(),
|
||||
'helper' => trans('Selectionnez une Certification parmis la liste.'),
|
||||
'helper' => trans('Selectionnez une Certification parmi la liste.'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -187,18 +173,18 @@
|
|||
</div>
|
||||
{{-- ORIGINS --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<legend>{{ __('Origines') }}</legend>
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Origine') }}</legend>
|
||||
<div class="row">
|
||||
@include('partials.inputs.select-multiple-enum', [
|
||||
'id' => 'productOrigins',
|
||||
'label' => __('Origines associées'),
|
||||
'label' => __('Origine associée'),
|
||||
'name' => 'origins',
|
||||
'itemsSelected' => old('origins', $productModel->origins ?? []),
|
||||
'itemsList' => collect(\App\Enums\Products\OriginEnum::toArray())->values(),
|
||||
'itemLimit' => [0, count(\App\Enums\Products\OriginEnum::toArray())],
|
||||
'placeholder' => __('Recherchez une Origine a associer.'),
|
||||
'helper' => __('Les Origines associées au produit.'),
|
||||
'placeholder' => '',
|
||||
'helper' => __('L\'origine associée au produit.'),
|
||||
'require' => false,
|
||||
])
|
||||
</div>
|
||||
|
|
@ -206,7 +192,7 @@
|
|||
</div>
|
||||
{{-- CLAIMS --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Allégations') }}</legend>
|
||||
<div class="row">
|
||||
@include('partials.inputs.select-multiple-enum', [
|
||||
|
|
@ -219,13 +205,13 @@
|
|||
'showSpecificItem' => [
|
||||
'name' => 'specified_claim',
|
||||
'label' => 'Précision de l\'allégation',
|
||||
'placeholder' => 'Sans sucre ajouté',
|
||||
'value' => $productModel->specified_claim ?? null,
|
||||
'placeholder' => '',
|
||||
'value' => old('specified_claim', $productModel->specified_claim ?? null),
|
||||
'showId' => 99,
|
||||
'helper' => __('Doit décrire précisément l\'allégation (3 caractères minimum, 255 maximum).')
|
||||
'helper' => __('Doit décrire précisément l\'allégation (3 caractères minimum, 255 maximum).'),
|
||||
],
|
||||
'model' => $productModel ?? null,
|
||||
'placeholder' => __('Recherchez une Allégation a associer.'),
|
||||
'placeholder' => '',
|
||||
'require' => false,
|
||||
'helper' => __('Les Allégations associées au produit.'),
|
||||
])
|
||||
|
|
@ -234,7 +220,7 @@
|
|||
</div>
|
||||
{{-- DISTRIBUTION NETWORK --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Réseau de distribution') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
|
|
@ -243,9 +229,9 @@
|
|||
'label' => __('Réseau de distribution associé'),
|
||||
'name' => 'distribution_network',
|
||||
'model' => $productModel ?? null,
|
||||
'require' => true,
|
||||
'required' => true,
|
||||
'associatedModels' => \App\Enums\Products\DistributionNetworkEnum::toArray(),
|
||||
'helper' => trans('Selectionnez un Réseau de distribution parmis la liste.'),
|
||||
'helper' => trans('Selectionnez un Réseau de distribution parmi la liste.'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -253,7 +239,7 @@
|
|||
</div>
|
||||
{{-- RAY --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Rayon') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
|
|
@ -262,9 +248,9 @@
|
|||
'label' => __('Rayon associé'),
|
||||
'name' => 'ray',
|
||||
'model' => $productModel ?? null,
|
||||
'require' => true,
|
||||
'required' => true,
|
||||
'associatedModels' => \App\Enums\Products\RayEnum::toArray(),
|
||||
'helper' => trans('Selectionnez un Rayon parmis la liste.'),
|
||||
'helper' => trans('Selectionnez un Rayon parmi la liste.'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -272,7 +258,7 @@
|
|||
</div>
|
||||
{{-- RANGE --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Famille de produit') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
|
|
@ -281,9 +267,9 @@
|
|||
'label' => __('Famille de produit associée'),
|
||||
'name' => 'product_range',
|
||||
'model' => $productModel ?? null,
|
||||
'require' => true,
|
||||
'required' => true,
|
||||
'associatedModels' => \App\Enums\Products\RangeEnum::toArray(),
|
||||
'helper' => trans('Selectionnez une Famille de produit parmis la liste.'),
|
||||
'helper' => trans('Selectionnez une Famille de produit parmi la liste.'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -291,7 +277,7 @@
|
|||
</div>
|
||||
{{-- GEO AREA --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Zone géographique') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
|
|
@ -300,9 +286,9 @@
|
|||
'label' => __('Zone géographique associée'),
|
||||
'name' => 'geo_area',
|
||||
'model' => $productModel ?? null,
|
||||
'require' => true,
|
||||
'required' => true,
|
||||
'associatedModels' => \App\Enums\Products\GeoAreaEnum::toArray(),
|
||||
'helper' => trans('Selectionnez une Zone géographique parmis la liste.'),
|
||||
'helper' => trans('Selectionnez une Zone géographique parmi la liste.'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -310,7 +296,7 @@
|
|||
</div>
|
||||
{{-- WEIGHT --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Poids') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
|
|
@ -329,16 +315,17 @@
|
|||
</div>
|
||||
{{-- PVC --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('PVC') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@include('partials.inputs.int-number', [
|
||||
'id' => 'productWeight',
|
||||
'id' => 'productPvc',
|
||||
'label' => __('PVC de l\'UVC'),
|
||||
'name' => 'pvc',
|
||||
'model' => $productModel ?? null,
|
||||
'min' => 0,
|
||||
'step' => 0.01,
|
||||
'required' => false,
|
||||
'helper' => trans('Le prix doit être en euro (€).'),
|
||||
])
|
||||
|
|
@ -346,9 +333,51 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- Prix au kilo --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Prix au kilo') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
@php
|
||||
$priceKg = 0;
|
||||
if ($productModel && $productModel->weight != 0) {
|
||||
$priceKg = (1000 * $productModel->pvc) / $productModel->weight;
|
||||
}
|
||||
@endphp
|
||||
@include('partials.inputs.int-number', [
|
||||
'id' => 'productPriceKg',
|
||||
'label' => __('Prix au kilo du produit'),
|
||||
'name' => 'priceKg',
|
||||
'model' => null,
|
||||
'required' => false,
|
||||
'disabled' => true,
|
||||
'value' => $priceKg,
|
||||
'helper' => trans('Le prix au kilo est calculé automatiquement.'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- KEYWORDS --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Précisez la ou les tendances') }}</legend>
|
||||
<div class="row">
|
||||
@include('partials.inputs.pivot-taggable', [
|
||||
'name' => 'productKeywords',
|
||||
'model' => $productModel ?? null,
|
||||
'itemLimit' => [0, count($keywords)],
|
||||
'itemsList' => $keywords,
|
||||
'helper' => __('Les Tendances associées au produit.'),
|
||||
'placeholder' => '',
|
||||
])
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- COMMENT --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<fieldset class="h-100 border bg-white p-3">
|
||||
<legend>{{ __('Commentaire libre') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
|
|
@ -358,63 +387,49 @@
|
|||
'name' => 'comment',
|
||||
'model' => $productModel ?? null,
|
||||
'required' => false,
|
||||
'helper' => __(' Doit décrire précisément le contenu du produit, requiert entre 50 et 160 caractères, balise <meta> description.'),
|
||||
'placeholder' => __('Une description du contenu du produit pour les moteurs de recherche.'),
|
||||
'helper' => __(
|
||||
' Doit décrire précisément le contenu du produit, requiert entre 50 et 160 caractères, balise <meta> description.'),
|
||||
'placeholder' => '',
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- KEYWORDS --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="border h-100 bg-white p-3">
|
||||
<legend>{{ __('Tendances') }}</legend>
|
||||
<div class="row">
|
||||
@include('partials.inputs.pivot-taggable', [
|
||||
'name' => 'productKeywords',
|
||||
'model' => $productModel ?? null,
|
||||
'itemLimit' => [0, count($keywords)],
|
||||
'itemsList' => $keywords,
|
||||
'helper' => __('Les Tendances associées au produit.'),
|
||||
'placeholder' => __('Recherchez une Tendance a associer.'),
|
||||
])
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- TASTED --}}
|
||||
<div class="col-12 mb-3">
|
||||
@php
|
||||
if (session()->getOldInput()['evaluation'] ?? null) {
|
||||
foreach (session()->getOldInput()['evaluation'] as $key => $value) {
|
||||
$commentOldModel[] = [
|
||||
"evaluation" => session()->getOldInput()['evaluation'][$key] ?? null,
|
||||
"aspect" => session()->getOldInput()['aspect'][$key] ?? null,
|
||||
"taste" => session()->getOldInput()['taste'][$key] ?? null,
|
||||
"odor" => session()->getOldInput()['odor'][$key] ?? null,
|
||||
"texture" => session()->getOldInput()['texture'][$key] ?? null,
|
||||
];
|
||||
if (session()->getOldInput()['evaluation'] ?? null) {
|
||||
foreach (session()->getOldInput()['evaluation'] as $key => $value) {
|
||||
$commentOldModel[] = [
|
||||
'evaluation' => session()->getOldInput()['evaluation'][$key] ?? null,
|
||||
'aspect' => session()->getOldInput()['aspect'][$key] ?? null,
|
||||
'taste' => session()->getOldInput()['taste'][$key] ?? null,
|
||||
'odor' => session()->getOldInput()['odor'][$key] ?? null,
|
||||
'texture' => session()->getOldInput()['texture'][$key] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
$data = [
|
||||
'id' => 'productSource',
|
||||
'title1' => __("Dégustation"),
|
||||
'label1' => __('Produit dégusté ?'),
|
||||
'name1' => 'is_tasted',
|
||||
'model' => $productModel ?? null,
|
||||
'oldModel' => session()->getOldInput(),
|
||||
'max' => 15,
|
||||
'min' => 1,
|
||||
'helper1' => __('Le produit a été dégusté.'),
|
||||
'required1' => false,
|
||||
'title2' => __("Commentaires"),
|
||||
'name2' => 'tasters_number',
|
||||
'label2' => __('Nombre de dégustation'),
|
||||
'helper2' => __('Doit être un chiffre ou un nombre (minimum 1 et maximum 15).'),
|
||||
'required2' => false,
|
||||
'commentModel' => $commentModel ?? [],
|
||||
'commentOldModel' => $commentOldModel ?? [],
|
||||
'errors' => $errors->getBag('default')->getMessages() ?? []
|
||||
];
|
||||
$data = [
|
||||
'id' => 'productSource',
|
||||
'title1' => __('Dégustation'),
|
||||
'label1' => __('Produit dégusté ?'),
|
||||
'name1' => 'is_tasted',
|
||||
'oldCheck' => boolval(old('is_tasted', null)),
|
||||
'model' => $productModel ?? null,
|
||||
'oldModel' => session()->getOldInput(),
|
||||
'max' => 15,
|
||||
'min' => 1,
|
||||
'helper1' => __('Le produit a été dégusté.'),
|
||||
'required1' => false,
|
||||
'title2' => __('Commentaires'),
|
||||
'name2' => 'tasters_number',
|
||||
'label2' => __('Nombre de dégustation'),
|
||||
'helper2' => __('Doit être un chiffre ou un nombre (minimum 1 et maximum 15).'),
|
||||
'required2' => false,
|
||||
'commentModel' => $commentModel ?? [],
|
||||
'commentOldModel' => $commentOldModel ?? [],
|
||||
'errors' => $errors->getBag('default')->getMessages() ?? [],
|
||||
];
|
||||
@endphp
|
||||
<div class="tasted-product" data-json='@json($data)'></div>
|
||||
</div>
|
||||
|
|
@ -429,5 +444,21 @@
|
|||
event.preventDefault();
|
||||
submit.click('');
|
||||
})
|
||||
|
||||
const priceKgInput = document.getElementById('productPriceKg');
|
||||
const priceInput = document.getElementById('productPvc');
|
||||
const weightInput = document.getElementById('productWeight');
|
||||
priceInput.addEventListener("change", updatePriceKg);
|
||||
weightInput.addEventListener("change", updatePriceKg);
|
||||
|
||||
function updatePriceKg() {
|
||||
if (weightInput.value != 0) {
|
||||
$priceKg = 1000 * priceInput.value / weightInput.value;
|
||||
priceKgInput.value = $priceKg.toFixed(2);
|
||||
} else {
|
||||
priceKgInput.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@extends('layout')
|
||||
|
||||
@section('title', sprintf('%s - %s', __('models.classes.products'), Str::ucfirst(__('liste'))))
|
||||
@section('title', sprintf('%s - %s', trans_choice('models.classes.products_monitoring', 2), Str::ucfirst(__('liste'))))
|
||||
@section('description', __('Liste des produits publiés'))
|
||||
@section('breadcrumb', 'no')
|
||||
|
||||
|
|
@ -10,14 +10,14 @@
|
|||
<div class="row">
|
||||
<div class="col">
|
||||
<h1 class="text-center text-md-start h2">
|
||||
<span class="fw-bold">{{ __('models.classes.products') }}</span> <span class="text-muted">{{ Str::ucfirst(__('liste')) }}</span>
|
||||
<span class="fw-bold">{{ trans_choice('models.classes.products_monitoring', 2) }}</span> <span class="text-muted">{{ Str::ucfirst(__('liste')) }}</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="input-group justify-content-end">
|
||||
<a href="{{ route('products.create') }}"
|
||||
class="btn btn-primary"
|
||||
title="{{ __('Ajouter un :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
class="btn btn-success"
|
||||
title="{{ __('Ajouter une :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,115 +1,103 @@
|
|||
<div class="table-responsive">
|
||||
<table class="table fix-action m-0 mt-3">
|
||||
<table class="fix-action m-0 mt-3 table">
|
||||
<thead>
|
||||
@php
|
||||
$cols = [
|
||||
'name' => __('Nom'),
|
||||
'brand_id' => __('Marque'),
|
||||
'picture' => __('Image'),
|
||||
'updated_at' => __('Mis à jour le'),
|
||||
'multi_pdf' => __('Multi .pdf'),
|
||||
];
|
||||
$routeName = request()->route()->getName();
|
||||
$rst = !is_null(request()->rst) || !Session::has("$routeName.sorted");
|
||||
$noOrder = Session::get("$routeName.sort_col") !== 'order' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"));
|
||||
$noOrder = Session::get("$routeName.sort_col") !== '' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"));
|
||||
@endphp
|
||||
@include('modules.table-col-sorter', ['cols' => $cols, 'mobileHide' => [], 'ignore' => ['multi_pdf']])
|
||||
@php
|
||||
$cols = [
|
||||
'name' => __('Nom'),
|
||||
'brand_id' => __('Marque'),
|
||||
'picture' => __('Image'),
|
||||
'multi_pdf' => __('Générer en pdf la sélection'),
|
||||
];
|
||||
$routeName = request()
|
||||
->route()
|
||||
->getName();
|
||||
$rst = !is_null(request()->rst) || !Session::has("$routeName.sorted");
|
||||
($noOrder = Session::get("$routeName.sort_col") !== 'order') and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"));
|
||||
($noOrder = Session::get("$routeName.sort_col") !== '') and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"));
|
||||
@endphp
|
||||
@include('modules.table-col-sorter', ['cols' => $cols, 'mobileHide' => [], 'ignore' => ['multi_pdf']])
|
||||
</thead>
|
||||
<tbody>
|
||||
@if ($products->count())
|
||||
@foreach ($products as $productModel)
|
||||
<tr>
|
||||
<td class="align-middle text-center">
|
||||
<span>{{ $productModel->name }}</span>
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
@foreach($brands as $key => $brand)
|
||||
@if ($brand->id === $productModel->brand_id)
|
||||
<p class="m-0">{{ $brand->name }}</p>
|
||||
@endif
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
@if(isset($productModel->picture))
|
||||
<img class="bo-image-preview" src="{{ asset($productModel->picture) }}">
|
||||
@else
|
||||
<p class="text-muted">{{ __('Aucune image enregistée') }}</p>
|
||||
@endif
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
<span class="badge bg-secondary text-wrap">
|
||||
{{ $productModel->updated_at->formatLocalized('%d/%m/%Y à %I:%M') }}
|
||||
</span>
|
||||
</td>
|
||||
@if(!$noOrder or $rst)
|
||||
<td class="align-middle text-center">
|
||||
@php
|
||||
$data = [
|
||||
'id' => 'productSelectMultiPdf',
|
||||
'model' => $productModel,
|
||||
'printMultiProduct' => $printMultiProduct,
|
||||
];
|
||||
@endphp
|
||||
<div class="button-multi-pdf" data-json='@json($data)'></div>
|
||||
</td>
|
||||
@endif
|
||||
<td class="align-middle text-end">
|
||||
<form action="{{ route('products.destroy', $productModel->id) }}" method="POST" class="d-inline confirmDeleteJS">
|
||||
@method('DELETE')
|
||||
@csrf
|
||||
<div class="btn-group" role="group">
|
||||
<a class="btn btn-sm btn-info glightbox" type="button"
|
||||
href="{{ asset($productModel->picture) }}" target="_blank"
|
||||
data-gallery="product{{ $productModel->id }}"
|
||||
data-bs-title="{{ __('Visionner l\'image du :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-image"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-warning" type="button"
|
||||
href="{{ route('products.download', $productModel) }}"
|
||||
title="{{ __('Télécharger les annexes du :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-secondary btn-printing" type="button"
|
||||
href="{{ route('products.pdf.show', $productModel) }}"
|
||||
target="blank"
|
||||
title="{{ __('Fichier .pdf du :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
data-bs="tooltip"
|
||||
data-id="{{ $productModel->id }}">
|
||||
<i class="fa-solid fa-print"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-success"
|
||||
href="{{ route('products.duplicate', $productModel) }}"
|
||||
title="{{ __('Dupliquer ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-primary"
|
||||
href="{{ route('products.edit', $productModel) }}"
|
||||
title="{{ __('Modifier ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-pencil"></i>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
title="{{ __('Supprimer ce :model', ['model' => Str::singular(__('models.classes.products'))]) }}"
|
||||
data-bs="tooltip">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@foreach ($products as $productModel)
|
||||
<tr>
|
||||
<td class="text-center align-middle">
|
||||
<span>{{ $productModel->name }}</span>
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
@foreach ($brands as $key => $brand)
|
||||
@if ($brand->id === $productModel->brand_id)
|
||||
<p class="m-0">{{ $brand->name }}</p>
|
||||
@endif
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
@if ($productModel->picture)
|
||||
<img class="bo-image-preview" src="{{ asset(resize($productModel->picture, 'productTiny')) }}">
|
||||
@else
|
||||
<p class="text-muted">{{ __('Aucune image enregistée') }}</p>
|
||||
@endif
|
||||
</td>
|
||||
{{-- @if (!$noOrder or $rst) --}}
|
||||
<td class="text-center align-middle">
|
||||
@php
|
||||
$data = [
|
||||
'id' => 'productSelectMultiPdf',
|
||||
'model' => $productModel,
|
||||
'printMultiProduct' => $printMultiProduct,
|
||||
];
|
||||
@endphp
|
||||
<div class="button-multi-pdf" data-json='@json($data)'></div>
|
||||
</td>
|
||||
{{-- @endif --}}
|
||||
<td class="text-end align-middle">
|
||||
<form class="d-inline confirmDeleteJS" action="{{ route('products.destroy', $productModel->id) }}"
|
||||
method="POST">
|
||||
@method('DELETE')
|
||||
@csrf
|
||||
<div class="btn-group" role="group">
|
||||
<a class="btn btn-sm btn-info glightbox" data-gallery="product{{ $productModel->id }}"
|
||||
data-bs-title="{{ __('Visionner l\'image de la :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
data-bs="tooltip" type="button" href="{{ asset($productModel->picture) }}" target="_blank">
|
||||
<i class="fa-solid fa-image"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-warning" data-bs="tooltip" type="button"
|
||||
href="{{ route('products.download', $productModel) }}"
|
||||
title="{{ __('Télécharger les annexes de la :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-secondary btn-printing" data-bs="tooltip" data-id="{{ $productModel->id }}"
|
||||
type="button" href="{{ route('products.pdf.show', $productModel) }}"
|
||||
title="{{ __('Fichier .pdf de la :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}"
|
||||
target="blank">
|
||||
<i class="fa-solid fa-print"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-success" data-bs="tooltip"
|
||||
href="{{ route('products.duplicate', $productModel) }}"
|
||||
title="{{ __('Dupliquer cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-primary" data-bs="tooltip" href="{{ route('products.edit', $productModel) }}"
|
||||
title="{{ __('Modifier cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}">
|
||||
<i class="fa-solid fa-pencil"></i>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-danger" data-bs="tooltip"
|
||||
title="{{ __('Supprimer cette :model', ['model' => trans_choice('models.classes.products_monitoring', 1)]) }}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@else
|
||||
<tr>
|
||||
<td colspan="{{ count($cols)+1 }}" class="text-center">
|
||||
<span
|
||||
class="h3">{{ __('Aucun résultat pour les :models', ['models' => __('models.classes.products')]) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center" colspan="{{ count($cols) + 1 }}">
|
||||
<span
|
||||
class="h3">{{ __('Aucun résultat pour les :models', ['models' => trans_choice('models.classes.products_monitoring', 2)]) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 mt-1">
|
||||
<input name="gencod" type="text" class="form-control" placeholder="GENCODE" value="{{ $queryParameters['gencod'] ?? null }}">
|
||||
<input name="gencod" type="text" class="form-control" placeholder="GENCOD" value="{{ $queryParameters['gencod'] ?? null }}">
|
||||
</div>
|
||||
</div>
|
||||
<!-- SOURCE -->
|
||||
|
|
|
|||
18
resources/views/partials/inputs/bigint-belongs-to.blade.php
Normal file
18
resources/views/partials/inputs/bigint-belongs-to.blade.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
@php
|
||||
$value = old($name) ?? $model->{$name} ?? null;
|
||||
$value = $value ? intval($value) : $value;
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'labelField' => $labelField,
|
||||
'itemLimit' => $itemLimit,
|
||||
'itemsList' => $itemsList,
|
||||
'storeRoute' => $storeRoute ?? null,
|
||||
'placeholder' => $placeholder,
|
||||
'errors' => $errors->getBag('default')->getMessages(),
|
||||
'label' => $label,
|
||||
'require' => $require
|
||||
];
|
||||
@endphp
|
||||
<div class="belongs-to-dropdown" data-json='@json($data)'></div>
|
||||
@include('modules.input-error', ['helper' => $helper, 'inputName' => $name])
|
||||
|
|
@ -2,8 +2,10 @@
|
|||
<label for="{{ $name }}" class="col-form-label"><b>{{ $label }} *</b></label>
|
||||
@endif
|
||||
<select id="{{ $name }}" class="form-select" name="{{ $name }}" aria-describedby="{{ $id }}Help" @if(!empty($label))required @endif>
|
||||
<option value="0" @if (!old($name, $model->{$name}->id ?? $value ?? null)) selected @endif>
|
||||
</option>
|
||||
@foreach ($associatedModels as $associatedModel)
|
||||
<option value="{{ $associatedModel->id }}" @if (old($name, $model->{$name} ?? 1) == $associatedModel->id) selected @endif>
|
||||
<option value="{{ $associatedModel->id }}" @if (old($name, $model->{$name} ?? $value ?? null) == $associatedModel->id) selected @endif>
|
||||
{{ $associatedModel->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
|
|
|
|||
|
|
@ -7,10 +7,14 @@
|
|||
id="{{ $id }}"
|
||||
name="{{ $name }}"
|
||||
type="number"
|
||||
value="{{ old($name, $model->{$name} ?? 0) }}"
|
||||
value="{{ old($name, $model->{$name} ?? $value ?? null) }}"
|
||||
class="form-control"
|
||||
aria-describedby="{{ $id }}Help"
|
||||
@if(!empty($step))
|
||||
step="{{ $step }}"
|
||||
@else
|
||||
step="1"
|
||||
@endif
|
||||
@if(!empty($max))
|
||||
max="{{ $max }}"
|
||||
@else
|
||||
|
|
@ -22,6 +26,8 @@
|
|||
min="{{ !empty($unsigned) ? '0' : '-2147483648' }}"
|
||||
@endif
|
||||
@if(!empty($required))required @endif
|
||||
@if(!empty($disabled))disabled @endif
|
||||
@if(!empty($readonly))readonly @endif
|
||||
>
|
||||
@include('modules.input-error', [
|
||||
'inputName' => $name,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
<label for="{{ $name }}" class="col-form-label"><b>{{ $label }}@if(!empty($required)) * @endif</b></label>
|
||||
@endif
|
||||
<select id="{{ $name }}" class="form-select" name="{{ $name }}" aria-describedby="{{ $id }}Help" @if(!empty($require))required @endif>
|
||||
<option value="0" @if (!old($name, $model->{$name}->value ?? $value ?? null)) selected @endif>
|
||||
</option>
|
||||
@foreach ($associatedModels as $associatedModel)
|
||||
<option value="{{ $associatedModel->value}}" @if (old($name, $model->{$name}->value ?? $value ?? 1) == $associatedModel->value) selected @endif>
|
||||
<option value="{{ $associatedModel->value}}" @if (old($name, $model->{$name}->value ?? $value ?? null) == $associatedModel->value) selected @endif>
|
||||
{{ $associatedModel->label }}
|
||||
</option>
|
||||
@endforeach
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\BrandController;
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\ProductController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
|
@ -9,6 +10,10 @@ Route::get('/', [DashboardController::class, 'show'])->name('dashboard');
|
|||
|
||||
// * PRODUCTS
|
||||
Route::resource('products', ProductController::class)->except(['show']);
|
||||
|
||||
// * BRANDS
|
||||
Route::resource('brands', BrandController::class)->only(['store']);
|
||||
|
||||
Route::prefix('/product')
|
||||
->name('products.')
|
||||
->group(function () {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import legacy from "@vitejs/plugin-legacy";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import laravel from "laravel-vite-plugin";
|
||||
import path from "path";
|
||||
import { defineConfig } from "vite";
|
||||
import checker from "vite-plugin-checker";
|
||||
import eslint from "vite-plugin-eslint";
|
||||
import laravel from "laravel-vite-plugin";
|
||||
import legacy from "@vitejs/plugin-legacy";
|
||||
import path from "path";
|
||||
import stylelint from "vite-plugin-stylelint";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
|
|
@ -41,6 +41,7 @@ export default defineConfig({
|
|||
},
|
||||
plugins: [
|
||||
checker({
|
||||
enableBuild: true,
|
||||
vueTsc: {
|
||||
root: ".",
|
||||
tsconfigPath: "tsconfig.json",
|
||||
|
|
@ -74,7 +75,7 @@ export default defineConfig({
|
|||
"**/vendor/**/*.scss",
|
||||
"**/vendor/**/*.css",
|
||||
],
|
||||
lintOnStart: true,
|
||||
lintOnStart: false,
|
||||
emitError: true,
|
||||
emitWarning: true,
|
||||
emitErrorAsWarning: false,
|
||||
|
|
@ -84,7 +85,7 @@ export default defineConfig({
|
|||
cache: false,
|
||||
extensions: ["js", "ts"],
|
||||
include: "**/*.ts",
|
||||
lintOnStart: true,
|
||||
lintOnStart: false,
|
||||
emitWarning: true,
|
||||
emitError: true,
|
||||
failOnWarning: false,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue