Brands Ranges and Products CRUDS full ok
This commit is contained in:
parent
9bf55c411d
commit
d657296693
56 changed files with 2942 additions and 59 deletions
55
app/Casts/HtmlColor.php
Normal file
55
app/Casts/HtmlColor.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class HtmlColor implements CastsAttributes
|
||||
{
|
||||
/**
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array<string,mixed> $attributes
|
||||
* @return mixed
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
|
||||
*/
|
||||
public function get(Model $model, string $key, mixed $value, array $attributes): mixed
|
||||
{
|
||||
// phpcs:enable
|
||||
return '#' . $this->zeropad(dechex($value), 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param mixed|null $value
|
||||
* @param array<string,mixed> $attributes
|
||||
* @return mixed
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
|
||||
*/
|
||||
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
|
||||
{
|
||||
// phpcs:enable
|
||||
return \intval(hexdec(ltrim($value, '#')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad with zeros a numeric string
|
||||
*
|
||||
* @param string $num
|
||||
* @param integer $lim
|
||||
* @return string
|
||||
*/
|
||||
private function zeropad(string $num, int $lim): string
|
||||
{
|
||||
return (strlen($num) >= $lim) ? $num : $this->zeropad("0{$num}", $lim);
|
||||
}
|
||||
}
|
||||
149
app/Http/Controllers/Bo/BrandController.php
Normal file
149
app/Http/Controllers/Bo/BrandController.php
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Brands\StoreBrandRequest;
|
||||
use App\Http\Requests\Bo\Brands\UpdateBrandRequest;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BrandController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create the controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->authorizeResource(Brand::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $query */
|
||||
$query = Brand::query();
|
||||
|
||||
/** @var string $search Search field */
|
||||
$search = $request->input('search');
|
||||
if ($search) {
|
||||
$this->searchQuery(
|
||||
$query,
|
||||
$search,
|
||||
null,
|
||||
'name',
|
||||
);
|
||||
}
|
||||
$searchFields = \implode(', ', [
|
||||
trans('validation.attributes.name'),
|
||||
]);
|
||||
|
||||
/** Sort columns with a query */
|
||||
$this->sortQuery($query);
|
||||
|
||||
/** Custom pagination */
|
||||
$brandModelList = $this->paginate($query);
|
||||
|
||||
return view('bo.pages.brands.index', compact('brandModelList', 'search', 'searchFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create(): \Illuminate\Contracts\View\View
|
||||
{
|
||||
return view('bo.pages.brands.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Brands\StoreBrandRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreBrandRequest $request): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$brand = new Brand();
|
||||
$brand->fill($request->validated());
|
||||
|
||||
if ($brand->saveOrFail()) {
|
||||
return redirect()->route('bo.brand.edit', $brand)
|
||||
->with('success', __('crud.messages.has_been_created', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.brand', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->back()
|
||||
->with('error', __('crud.messages.cannot_be_created', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.brand', 1))
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit(Brand $brand): View
|
||||
{
|
||||
return view('bo.pages.brands.edit', ['brandModel' => $brand]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Brands\UpdateBrandRequest $request
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(UpdateBrandRequest $request, Brand $brand): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request, $brand) {
|
||||
$brand->fill($request->validated());
|
||||
|
||||
if ($brand->saveOrFail()) {
|
||||
return redirect()->route('bo.brands.edit', $brand)
|
||||
->with('success', __('crud.messages.has_been_updated', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.brand', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->route('bo.brands.edit', $brand)
|
||||
->with('error', __('crud.messages.cannot_be_updated', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.brand', 1))
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(Brand $brand): RedirectResponse
|
||||
{
|
||||
if ($brand->deleteOrFail()) {
|
||||
return redirect()->route('bo.brands.index')
|
||||
->with('success', __('crud.messages.has_been_deleted', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.brand', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->back()
|
||||
->with('error', __('crud.messages.cannot_be_deleted', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.brand', 1))
|
||||
]));
|
||||
}
|
||||
}
|
||||
155
app/Http/Controllers/Bo/ProductController.php
Normal file
155
app/Http/Controllers/Bo/ProductController.php
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Products\StoreProductRequest;
|
||||
use App\Http\Requests\Bo\Products\UpdateProductRequest;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create the controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->authorizeResource(Product::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Client\Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $query */
|
||||
$query = Product::query()->with('range');
|
||||
|
||||
/** @var string $search Search field */
|
||||
$search = $request->input('search');
|
||||
if ($search) {
|
||||
$this->searchQuery(
|
||||
$query,
|
||||
$search,
|
||||
null,
|
||||
'name',
|
||||
);
|
||||
}
|
||||
$searchFields = \implode(', ', [
|
||||
trans('validation.attributes.name'),
|
||||
]);
|
||||
|
||||
/** Sort columns with a query */
|
||||
$this->sortQuery($query);
|
||||
|
||||
/** Custom pagination */
|
||||
$productModelList = $this->paginate($query);
|
||||
|
||||
return view('bo.pages.products.index', compact('productModelList', 'search', 'searchFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create(): \Illuminate\Contracts\View\View
|
||||
{
|
||||
$rangeModelList = Range::all();
|
||||
return view('bo.pages.products.create', ['rangeModelList' => $rangeModelList]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Products\StoreProductRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreProductRequest $request): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$product = new Product();
|
||||
$product->fill($request->validated());
|
||||
|
||||
if ($product->saveOrFail()) {
|
||||
return redirect()->route('bo.product.edit', $product)
|
||||
->with('success', __('crud.messages.has_been_created', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.product', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->back()
|
||||
->with('error', __('crud.messages.cannot_be_created', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.product', 1))
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit(Product $product): View
|
||||
{
|
||||
$rangeModelList = Range::all();
|
||||
return view('bo.pages.products.edit', [
|
||||
'rangeModelList' => $rangeModelList,
|
||||
'productModel' => $product
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Products\UpdateProductRequest $request
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(UpdateProductRequest $request, Product $product): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request, $product) {
|
||||
$product->fill($request->validated());
|
||||
|
||||
if ($product->saveOrFail()) {
|
||||
return redirect()->route('bo.products.edit', $product)
|
||||
->with('success', __('crud.messages.has_been_updated', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.product', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->route('bo.products.edit', $product)
|
||||
->with('error', __('crud.messages.cannot_be_updated', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.product', 1))
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(Product $product): RedirectResponse
|
||||
{
|
||||
if ($product->deleteOrFail()) {
|
||||
return redirect()->route('bo.products.index')
|
||||
->with('success', __('crud.messages.has_been_deleted', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.product', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->back()
|
||||
->with('error', __('crud.messages.cannot_be_deleted', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.product', 1))
|
||||
]));
|
||||
}
|
||||
}
|
||||
155
app/Http/Controllers/Bo/RangeController.php
Normal file
155
app/Http/Controllers/Bo/RangeController.php
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Ranges\StoreRangeRequest;
|
||||
use App\Http\Requests\Bo\Ranges\UpdateRangeRequest;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Range;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RangeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create the controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->authorizeResource(Range::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Client\Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $query */
|
||||
$query = Range::query()->with('brand');
|
||||
|
||||
/** @var string $search Search field */
|
||||
$search = $request->input('search');
|
||||
if ($search) {
|
||||
$this->searchQuery(
|
||||
$query,
|
||||
$search,
|
||||
null,
|
||||
'name',
|
||||
);
|
||||
}
|
||||
$searchFields = \implode(', ', [
|
||||
trans('validation.attributes.name'),
|
||||
]);
|
||||
|
||||
/** Sort columns with a query */
|
||||
$this->sortQuery($query);
|
||||
|
||||
/** Custom pagination */
|
||||
$rangeModelList = $this->paginate($query);
|
||||
|
||||
return view('bo.pages.ranges.index', compact('rangeModelList', 'search', 'searchFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create(): \Illuminate\Contracts\View\View
|
||||
{
|
||||
$brandModelList = Brand::all();
|
||||
return view('bo.pages.ranges.create', ['brandModelList' => $brandModelList]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Ranges\StoreRangeRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreRangeRequest $request): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$range = new Range();
|
||||
$range->fill($request->validated());
|
||||
|
||||
if ($range->saveOrFail()) {
|
||||
return redirect()->route('bo.range.edit', $range)
|
||||
->with('success', __('crud.messages.has_been_created', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.range', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->back()
|
||||
->with('error', __('crud.messages.cannot_be_created', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.range', 1))
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Range $range
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit(Range $range): View
|
||||
{
|
||||
$brandModelList = Brand::all();
|
||||
return view('bo.pages.ranges.edit', [
|
||||
'brandModelList' => $brandModelList,
|
||||
'rangeModel' => $range
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Ranges\UpdateRangeRequest $request
|
||||
* @param \App\Models\Range $range
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(UpdateRangeRequest $request, Range $range): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request, $range) {
|
||||
$range->fill($request->validated());
|
||||
|
||||
if ($range->saveOrFail()) {
|
||||
return redirect()->route('bo.ranges.edit', $range)
|
||||
->with('success', __('crud.messages.has_been_updated', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.range', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->route('bo.ranges.edit', $range)
|
||||
->with('error', __('crud.messages.cannot_be_updated', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.range', 1))
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Range $range
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(Range $range): RedirectResponse
|
||||
{
|
||||
if ($range->deleteOrFail()) {
|
||||
return redirect()->route('bo.ranges.index')
|
||||
->with('success', __('crud.messages.has_been_deleted', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.range', 1))
|
||||
]));
|
||||
}
|
||||
return redirect()->back()
|
||||
->with('error', __('crud.messages.cannot_be_deleted', [
|
||||
'model' => Str::ucfirst(\trans_choice('models.classes.range', 1))
|
||||
]));
|
||||
}
|
||||
}
|
||||
51
app/Http/Requests/Bo/Brands/StoreBrandRequest.php
Normal file
51
app/Http/Requests/Bo/Brands/StoreBrandRequest.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Brands;
|
||||
|
||||
use App\Models\Brand;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreBrandRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', Brand::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->mergePicture('picture');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string,\Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|min:2|max:60',
|
||||
'color' => 'required|color_hex'
|
||||
];
|
||||
|
||||
return \array_merge(
|
||||
$rules,
|
||||
$this->pictureRules('picture', 500, 350, 800, 350, false)
|
||||
);
|
||||
}
|
||||
}
|
||||
18
app/Http/Requests/Bo/Brands/UpdateBrandRequest.php
Normal file
18
app/Http/Requests/Bo/Brands/UpdateBrandRequest.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Brands;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class UpdateBrandRequest extends StoreBrandRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('update', $this->route('brand'));
|
||||
}
|
||||
}
|
||||
56
app/Http/Requests/Bo/Products/StoreProductRequest.php
Normal file
56
app/Http/Requests/Bo/Products/StoreProductRequest.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Products;
|
||||
|
||||
use App\Models\Product;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class StoreProductRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', Product::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'description' => (string)Str::of($this->input('description', ''))
|
||||
]);
|
||||
$this->mergePicture('picture');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|min:2|max:130',
|
||||
'description' => 'present',
|
||||
'range_id' => 'required|exists:ranges,id'
|
||||
];
|
||||
|
||||
return \array_merge(
|
||||
$rules,
|
||||
$this->pictureRules('picture', 800, 800, 800, 800, false)
|
||||
);
|
||||
}
|
||||
}
|
||||
18
app/Http/Requests/Bo/Products/UpdateProductRequest.php
Normal file
18
app/Http/Requests/Bo/Products/UpdateProductRequest.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Products;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class UpdateProductRequest extends StoreProductRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('update', $this->route('product'));
|
||||
}
|
||||
}
|
||||
51
app/Http/Requests/Bo/Ranges/StoreRangeRequest.php
Normal file
51
app/Http/Requests/Bo/Ranges/StoreRangeRequest.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Ranges;
|
||||
|
||||
use App\Models\Range;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreRangeRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', Range::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->mergePicture('picture');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|min:2|max:90',
|
||||
'brand_id' => 'required|exists:brands,id'
|
||||
];
|
||||
|
||||
return \array_merge(
|
||||
$rules,
|
||||
$this->pictureRules('picture', 500, 500, 800, 500, false)
|
||||
);
|
||||
}
|
||||
}
|
||||
18
app/Http/Requests/Bo/Ranges/UpdateRangeRequest.php
Normal file
18
app/Http/Requests/Bo/Ranges/UpdateRangeRequest.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Ranges;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class UpdateRangeRequest extends StoreRangeRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('update', $this->route('range'));
|
||||
}
|
||||
}
|
||||
|
|
@ -471,19 +471,19 @@ class ToolboxHelper
|
|||
$height = intval($dimensions[1]);
|
||||
|
||||
$xMessage = trans(
|
||||
":attribute la largeur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
":attribute la largeur doit être comprise entre {$sizes->minWidth}px et {$sizes->maxWidth}px"
|
||||
);
|
||||
if ($sizes->minWith === $sizes->maxWith) {
|
||||
$xMessage = trans(":attribute la largeur doit être de {$sizes->minWith}px");
|
||||
if ($sizes->minWidth === $sizes->maxWidth) {
|
||||
$xMessage = trans(":attribute la largeur doit être de {$sizes->minWidth}px");
|
||||
}
|
||||
$yMessage = trans(
|
||||
":attribute la hauteur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
":attribute la hauteur doit être comprise entre {$sizes->minWidth}px et {$sizes->maxWidth}px"
|
||||
);
|
||||
if ($sizes->minHeight === $sizes->maxHeight) {
|
||||
$yMessage = trans(":attribute la hauteur doit être de {$sizes->minHeight}px");
|
||||
}
|
||||
|
||||
if (($width < $sizes->minWith) or ($width > $sizes->maxWith)) {
|
||||
if (($width < $sizes->minWidth) or ($width > $sizes->maxWidth)) {
|
||||
$fail($xMessage);
|
||||
}
|
||||
if (($height < $sizes->minHeight) or ($height > $sizes->maxHeight)) {
|
||||
|
|
|
|||
105
app/Models/Brand.php
Normal file
105
app/Models/Brand.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\HtmlColor;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property string $name Name of the brand.
|
||||
* @property string $slug Slug of the name for the brand.
|
||||
* @property string $color HTML Color string.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
||||
*
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Range> $ranges Ranges Has-many relationship.
|
||||
*/
|
||||
class Brand extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'color',
|
||||
'picture',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'color' => HtmlColor::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
// * ASSERTIONS
|
||||
static::assertNameIsUnique($model->name);
|
||||
|
||||
// * TRANSFORMATIONS
|
||||
$model->slug = Str::slug($model->name);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
// * ASSERTIONS
|
||||
static::assertNameIsUnique($model->name, $model->id);
|
||||
|
||||
// * TRANSFORMATIONS
|
||||
$model->slug = Str::slug($model->name);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
FileStorageHelper::removeOldFile($model, 'picture');
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* Get all of the ranges for the Brand
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function ranges(): HasMany
|
||||
{
|
||||
return $this->hasMany(Range::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the name is unique
|
||||
*
|
||||
* @param string|null $name
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If name already exists.
|
||||
*/
|
||||
private static function assertNameIsUnique(string|null $name, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'name', $name, $id);
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($name), $id);
|
||||
}
|
||||
}
|
||||
96
app/Models/Product.php
Normal file
96
app/Models/Product.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property string $name Name of the product.
|
||||
* @property string $slug Slug of the name for the product.
|
||||
* @property string $description WYSIWYG text description using ckeditor.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
||||
*
|
||||
* @property-read \App\Models\Range $range Range Belongs-to.
|
||||
*/
|
||||
class Product extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'picture',
|
||||
'range_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
// * ASSERTIONS
|
||||
static::assertNameIsUnique($model->name);
|
||||
|
||||
// * TRANSFORMATIONS
|
||||
$model->slug = Str::slug($model->name);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
// * ASSERTIONS
|
||||
static::assertNameIsUnique($model->name, $model->id);
|
||||
|
||||
// * TRANSFORMATIONS
|
||||
$model->slug = Str::slug($model->name);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
FileStorageHelper::removeOldFile($model, 'picture');
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* Get the range that owns the Product
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function range(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Range::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the name is unique
|
||||
*
|
||||
* @param string|null $name
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If name already exists.
|
||||
*/
|
||||
private static function assertNameIsUnique(string|null $name, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'name', $name, $id);
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($name), $id);
|
||||
}
|
||||
}
|
||||
106
app/Models/Range.php
Normal file
106
app/Models/Range.php
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property string $name Name of the range.
|
||||
* @property string $slug Slug of the name for the range.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
||||
*
|
||||
* @property-read \App\Models\Brand $brand Brand Belongs-to.
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Product> $products Products Has-many.
|
||||
*/
|
||||
class Range extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'picture',
|
||||
'brand_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
// * ASSERTIONS
|
||||
static::assertNameIsUnique($model->name);
|
||||
|
||||
// * TRANSFORMATIONS
|
||||
$model->slug = Str::slug($model->name);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
// * ASSERTIONS
|
||||
static::assertNameIsUnique($model->name, $model->id);
|
||||
|
||||
// * TRANSFORMATIONS
|
||||
$model->slug = Str::slug($model->name);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
FileStorageHelper::removeOldFile($model, 'picture');
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* Get the brand that owns the Product
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function brand(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Brand::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the products for the Brand
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function products(): HasMany
|
||||
{
|
||||
return $this->hasMany(Product::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the name is unique
|
||||
*
|
||||
* @param string|null $name
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If name already exists.
|
||||
*/
|
||||
private static function assertNameIsUnique(string|null $name, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'name', $name, $id);
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($name), $id);
|
||||
}
|
||||
}
|
||||
103
app/Policies/BrandPolicy.php
Normal file
103
app/Policies/BrandPolicy.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Enums\Users\RoleEnum;
|
||||
use App\Models\Brand;
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\UserStaticRules;
|
||||
|
||||
class BrandPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @return boolean
|
||||
*/
|
||||
public function viewAny(User $authUser): bool
|
||||
{
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function view(User $authUser, Brand $brand): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @return boolean
|
||||
*/
|
||||
public function create(User $authUser): bool
|
||||
{
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function update(User $authUser, Brand $brand): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function delete(User $authUser, Brand $brand): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function restore(User $authUser, Brand $brand): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Brand $brand
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function forceDelete(User $authUser, Brand $brand): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return false;
|
||||
}
|
||||
}
|
||||
103
app/Policies/ProductPolicy.php
Normal file
103
app/Policies/ProductPolicy.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Enums\Users\RoleEnum;
|
||||
use App\Models\Product;
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\UserStaticRules;
|
||||
|
||||
class ProductPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @return boolean
|
||||
*/
|
||||
public function viewAny(User $authUser): bool
|
||||
{
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Product $product
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function view(User $authUser, Product $product): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @return boolean
|
||||
*/
|
||||
public function create(User $authUser): bool
|
||||
{
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Product $product
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function update(User $authUser, Product $product): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Product $product
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function delete(User $authUser, Product $product): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Product $product
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function restore(User $authUser, Product $product): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Product $product
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function forceDelete(User $authUser, Product $product): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return false;
|
||||
}
|
||||
}
|
||||
103
app/Policies/RangePolicy.php
Normal file
103
app/Policies/RangePolicy.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Enums\Users\RoleEnum;
|
||||
use App\Models\Range;
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\UserStaticRules;
|
||||
|
||||
class RangePolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @return boolean
|
||||
*/
|
||||
public function viewAny(User $authUser): bool
|
||||
{
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Range $range
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function view(User $authUser, Range $range): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @return boolean
|
||||
*/
|
||||
public function create(User $authUser): bool
|
||||
{
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Range $range
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function update(User $authUser, Range $range): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Range $range
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function delete(User $authUser, Range $range): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Range $range
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function restore(User $authUser, Range $range): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @param \App\Models\User $authUser
|
||||
* @param \App\Models\Range $range
|
||||
* @return boolean
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
*/
|
||||
public function forceDelete(User $authUser, Range $range): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
return false;
|
||||
}
|
||||
}
|
||||
183
app/Traits/Requests/HasPicture.php
Normal file
183
app/Traits/Requests/HasPicture.php
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Requests;
|
||||
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
|
||||
/**
|
||||
* ! This assumes file system is using local storage
|
||||
*/
|
||||
trait HasPicture
|
||||
{
|
||||
/**
|
||||
* Minimum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minWidth = 400;
|
||||
|
||||
/**
|
||||
* Maximum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxWidth = 1800;
|
||||
|
||||
/**
|
||||
* Minimum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minHeight = 300;
|
||||
|
||||
/**
|
||||
* Maximum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxHeight = 1800;
|
||||
|
||||
/**
|
||||
* Image rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param integer|null $minWidth
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWidth
|
||||
* @param integer|null $maxHeight
|
||||
* @param boolean $nullable
|
||||
* @param integer|null $size
|
||||
* @param string|null $requiredWith
|
||||
* @param string|null $requiredWithout
|
||||
* @return array
|
||||
*/
|
||||
public function pictureRules(
|
||||
string $name = 'picture',
|
||||
?int $minWidth = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWidth = null,
|
||||
?int $maxHeight = null,
|
||||
bool $nullable = false,
|
||||
?int $size = null,
|
||||
?string $requiredWith = null,
|
||||
?string $requiredWithout = null,
|
||||
): array {
|
||||
$dimensions = (object)[
|
||||
'minWidth' => $minWidth ?? $this->minWidth,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWidth' => $maxWidth ?? $this->maxWidth,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight
|
||||
];
|
||||
return [
|
||||
$name => [
|
||||
!$nullable ? 'required' : 'nullable',
|
||||
(strlen($requiredWith)) ? 'required_with:' . $requiredWith : '',
|
||||
(strlen($requiredWithout)) ? 'required_without:' . $requiredWithout : '',
|
||||
($size) ? 'max:' . $size : '',
|
||||
function (string $attribute, $value, callable $fail) use ($dimensions) {
|
||||
return ToolboxHelper::validatePicture($attribute, $value, $fail, $dimensions);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param integer|null $minWidth
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWidth
|
||||
* @param integer|null $maxHeight
|
||||
* @return array
|
||||
*/
|
||||
public function picturesRules(
|
||||
string $name = 'pictures',
|
||||
?int $minWidth = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWidth = null,
|
||||
?int $maxHeight = null
|
||||
): array {
|
||||
$sizes = (object)[
|
||||
'minWidth' => $minWidth ?? $this->minWidth,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWidth' => $maxWidth ?? $this->maxWidth,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight
|
||||
];
|
||||
return [
|
||||
$name => 'sometimes|array',
|
||||
"{$name}Validate" => "required_with:{$name}|array",
|
||||
"{$name}Validate.*" => [
|
||||
'required',
|
||||
function (string $attribute, $value, callable $fail) use ($sizes) {
|
||||
return $this->validatePicture($attribute, $value, $fail, $sizes);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges products pictures fields with request data
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePicture(string $name = 'picture'): void
|
||||
{
|
||||
$picturePath = $this->{$name};
|
||||
if (is_string($picturePath)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedPicture
|
||||
]);
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges products pictures fields with request data
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePictures(string $name = 'pictures'): void
|
||||
{
|
||||
$pictures = $this->{$name};
|
||||
if (is_array($pictures)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
foreach ($pictures as $key => $picturePath) {
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = $this->{$name};
|
||||
$changedPicture[$key] = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedPicture
|
||||
]);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
"php": "^8.2",
|
||||
"diglactic/laravel-breadcrumbs": "^8.0",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"kaishiyoku/laravel-color-validation": "^2.1",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/tinker": "^2.8",
|
||||
"laravel/ui": "^4.2",
|
||||
|
|
|
|||
128
composer.lock
generated
128
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f6f8667757d8be0e48544e0bc4e250e5",
|
||||
"content-hash": "be23935d916f62e36467d9c3fa5f5917",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
|
@ -1338,6 +1338,69 @@
|
|||
},
|
||||
"time": "2021-10-08T21:21:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kaishiyoku/laravel-color-validation",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Kaishiyoku/laravel-color-validation.git",
|
||||
"reference": "4a77492335e37d18055ec9547332d224186d074b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Kaishiyoku/laravel-color-validation/zipball/4a77492335e37d18055ec9547332d224186d074b",
|
||||
"reference": "4a77492335e37d18055ec9547332d224186d074b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0",
|
||||
"illuminate/validation": "^6.0|^7.0|^8.0|^9.0|^10.0",
|
||||
"php": "^7.4|^8.0",
|
||||
"spatie/regex": "^1.4|^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^6.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Kaishiyoku\\Validation\\Color\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Kaishiyoku\\": "src/Kaishiyoku"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kaishiyoku",
|
||||
"email": "dev@andreas-wiedel.de",
|
||||
"homepage": "https://andreas-wiedel.de",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "Laravel color validation rules",
|
||||
"homepage": "https://github.com/Kaishiyoku/laravel-color-validation",
|
||||
"keywords": [
|
||||
"color",
|
||||
"laravel",
|
||||
"validation rules",
|
||||
"validator"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Kaishiyoku/laravel-color-validation/issues",
|
||||
"source": "https://github.com/Kaishiyoku/laravel-color-validation/tree/2.1.1"
|
||||
},
|
||||
"time": "2023-02-13T20:23:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v10.23.1",
|
||||
|
|
@ -4247,6 +4310,69 @@
|
|||
],
|
||||
"time": "2023-09-05T11:02:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/regex",
|
||||
"version": "3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/regex.git",
|
||||
"reference": "d543de2019a0068e7b80da0ba24f1c51c7469303"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/regex/zipball/d543de2019a0068e7b80da0ba24f1c51c7469303",
|
||||
"reference": "d543de2019a0068e7b80da0ba24f1c51c7469303",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0|^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\Regex\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian De Deyne",
|
||||
"email": "sebastian@spatie.be",
|
||||
"homepage": "https://spatie.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A sane interface for php's built in preg_* functions",
|
||||
"homepage": "https://github.com/spatie/regex",
|
||||
"keywords": [
|
||||
"expression",
|
||||
"expressions",
|
||||
"regex",
|
||||
"regular",
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/regex/issues",
|
||||
"source": "https://github.com/spatie/regex/tree/3.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://spatie.be/open-source/support-us",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/spatie",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-30T21:13:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v6.3.4",
|
||||
|
|
|
|||
24
database/factories/BrandFactory.php
Normal file
24
database/factories/BrandFactory.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Brand>
|
||||
*/
|
||||
class BrandFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'product ' . fake()->unique()->safeColorName(),
|
||||
'color' => fake()->unique()->hexColor()
|
||||
];
|
||||
}
|
||||
}
|
||||
24
database/factories/ProductFactory.php
Normal file
24
database/factories/ProductFactory.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Product>
|
||||
*/
|
||||
class ProductFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'product' . fake()->unique()->words(3, true),
|
||||
'description' => '<strong>TEXT</strong>' . fake()->realText()
|
||||
];
|
||||
}
|
||||
}
|
||||
23
database/factories/RangeFactory.php
Normal file
23
database/factories/RangeFactory.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Range>
|
||||
*/
|
||||
class RangeFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'gamme ' . fake()->unique()->streetName(),
|
||||
];
|
||||
}
|
||||
}
|
||||
BIN
database/factories/assets/brands/default-picture-600x350.jpg
Normal file
BIN
database/factories/assets/brands/default-picture-600x350.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
database/factories/assets/products/default-picture-800x800.jpg
Normal file
BIN
database/factories/assets/products/default-picture-800x800.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
database/factories/assets/ranges/default-picture-600x500.jpg
Normal file
BIN
database/factories/assets/ranges/default-picture-600x500.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
|
|
@ -0,0 +1,39 @@
|
|||
<?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::create('brands', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('name')->comment('Nom de la marque');
|
||||
$table->string('slug')->comment('Slug de la marque');
|
||||
|
||||
$table->unsignedInteger('color')->comment('Couleur de la marque');
|
||||
|
||||
$table->string('picture')->comment('Logo de la marque');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('brands');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?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::create('ranges', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('name')->comment('Nom de la gamme');
|
||||
$table->string('slug')->comment('Slug de la gamme');
|
||||
|
||||
$table->string('picture')->comment('Logo de la marque');
|
||||
|
||||
$table->foreignId('brand_id');
|
||||
$table->foreign('brand_id')->references('id')->on('brands')->cascadeOnDelete();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ranges');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?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::create('products', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('name')->comment('Nom du produit');
|
||||
$table->string('slug')->comment('Slug du produit');
|
||||
|
||||
$table->text('description')->comment('Description du produit');
|
||||
|
||||
$table->string('picture')->comment('Logo du produit');
|
||||
|
||||
$table->foreignId('range_id');
|
||||
$table->foreign('range_id')->references('id')->on('ranges')->cascadeOnDelete();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('products');
|
||||
}
|
||||
};
|
||||
36
database/seeders/BrandSeeder.php
Normal file
36
database/seeders/BrandSeeder.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class BrandSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Schema::disableForeignKeyConstraints();
|
||||
DB::table((new Brand())->getTable())->truncate();
|
||||
Schema::enableForeignKeyConstraints();
|
||||
File::deleteDirectory(\storage_path('app/public/modelfiles/' . (new Brand())->getTable()));
|
||||
|
||||
Brand::factory(fake()->randomDigitNot([0, 1, 2, 3]))->make()->each(function (Brand $brand) {
|
||||
$brand->picture = FileStorageHelper::storeFile(
|
||||
new Brand(),
|
||||
new \SplFileInfo(
|
||||
\resource_path('../database/factories/assets/brands/default-picture-600x350.jpg')
|
||||
)
|
||||
);
|
||||
$brand->save();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,10 @@ class DatabaseSeeder extends Seeder
|
|||
{
|
||||
$this->call([
|
||||
UserSeeder::class,
|
||||
StaticPageSeeder::class
|
||||
StaticPageSeeder::class,
|
||||
BrandSeeder::class,
|
||||
RangeSeeder::class,
|
||||
ProductSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
41
database/seeders/ProductSeeder.php
Normal file
41
database/seeders/ProductSeeder.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProductSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Schema::disableForeignKeyConstraints();
|
||||
DB::table((new Product())->getTable())->truncate();
|
||||
Schema::enableForeignKeyConstraints();
|
||||
File::deleteDirectory(\storage_path('app/public/modelfiles/' . (new Product())->getTable()));
|
||||
|
||||
Range::all()->each(
|
||||
fn (Range $range) =>
|
||||
Product::factory(\random_int(6, 10))->make()->each(function (Product $product) use ($range) {
|
||||
$product->picture = FileStorageHelper::storeFile(
|
||||
new Product(),
|
||||
new \SplFileInfo(
|
||||
\resource_path('../database/factories/assets/products/default-picture-800x800.jpg')
|
||||
)
|
||||
);
|
||||
$product->range()->associate($range);
|
||||
$product->save();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
42
database/seeders/RangeSeeder.php
Normal file
42
database/seeders/RangeSeeder.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Range;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class RangeSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Schema::disableForeignKeyConstraints();
|
||||
DB::table((new Range())->getTable())->truncate();
|
||||
Schema::enableForeignKeyConstraints();
|
||||
File::deleteDirectory(\storage_path('app/public/modelfiles/' . (new Range())->getTable()));
|
||||
|
||||
|
||||
Brand::all()->each(
|
||||
fn (Brand $brand) =>
|
||||
Range::factory(\random_int(6, 16))->make()->each(function (Range $range) use ($brand) {
|
||||
$range->picture = FileStorageHelper::storeFile(
|
||||
new Range(),
|
||||
new \SplFileInfo(
|
||||
\resource_path('../database/factories/assets/ranges/default-picture-600x500.jpg')
|
||||
)
|
||||
);
|
||||
$range->brand()->associate($brand);
|
||||
$range->save();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -87,5 +87,21 @@ return [
|
|||
'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',
|
||||
],
|
||||
|
||||
'meta' => [
|
||||
'title' => [
|
||||
'create' => '{0} Création d\'un :model|{1} Création d\'une :model',
|
||||
'edit' => '{0} Edition d\'un :model|{1} Edition d\'une :model',
|
||||
'show' => '{0} Visualisation d\'un :model|{1} Visualisation d\'une :model',
|
||||
],
|
||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
||||
'description' => [
|
||||
'index' => '{0} Liste de tous les :model|{1} Liste de toutes les :model',
|
||||
'create' => '{0} Création d\'un tout nouveau :model|{1} Création d\'une toute nouvelle :model|{2} Création d\'un tout nouvel :model',
|
||||
'edit' => '{0} Edition d\'un :model déjà existant|{1} Edition d\'une :model déjà existante',
|
||||
'show' => '{0} Visualisation d\'un :model déjà existant|{1} Visualisation d\'une :model déjà existante',
|
||||
],
|
||||
// phpcs:enable
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ return [
|
|||
'classes' => [
|
||||
'user' => 'utilisateur',
|
||||
'activity_log' => 'journal d\'activité|journaux d\'activité',
|
||||
'static_page' => 'page statique|pages statiques'
|
||||
'static_page' => 'page statique|pages statiques',
|
||||
'brand' => 'marque|marques',
|
||||
'range' => 'gamme|gammes',
|
||||
'product' => 'produit|produits',
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -229,5 +229,6 @@ return [
|
|||
'static_page_type' => 'type de page',
|
||||
'seo_title' => 'titre SEO',
|
||||
'seo_description' => 'description SEO',
|
||||
'color' => 'couleur',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -6,6 +6,30 @@
|
|||
{{ __('Accueil') }}
|
||||
</a>
|
||||
</li>
|
||||
@can('viewAny', \App\Models\Brand::class)
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if (request()->routeIs('bo.brands.*')) active @endif" data-bs-toggle="tooltip" href="{{ route('bo.brands.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => Str::of(trans_choice('models.classes.brand', '*'))->title()]) }}">
|
||||
{{ Str::of(trans_choice('models.classes.brand', '*'))->title() }}
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
@can('viewAny', \App\Models\Range::class)
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if (request()->routeIs('bo.ranges.*')) active @endif" data-bs-toggle="tooltip" href="{{ route('bo.ranges.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => Str::of(trans_choice('models.classes.range', '*'))->title()]) }}">
|
||||
{{ Str::of(trans_choice('models.classes.range', '*'))->title() }}
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
@can('viewAny', \App\Models\Product::class)
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if (request()->routeIs('bo.products.*')) active @endif" data-bs-toggle="tooltip" href="{{ route('bo.products.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => Str::of(trans_choice('models.classes.product', '*'))->title()]) }}">
|
||||
{{ Str::of(trans_choice('models.classes.product', '*'))->title() }}
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
{{-- @can('viewAny', \App\Models\StaticPage::class)
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if (request()->routeIs('bo.static_pages.*')) active @endif" data-bs-toggle="tooltip"
|
||||
|
|
|
|||
36
resources/views/bo/pages/brands/create.blade.php
Normal file
36
resources/views/bo/pages/brands/create.blade.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
@extends('bo.layout', ['brParam' => ''])
|
||||
|
||||
@section('title', trans_choice('crud.meta.title.create', 1, ['model' => trans_choice('models.classes.brand', 1)]))
|
||||
@section('description', trans_choice('crud.meta.description.create', 1, ['model' => trans_choice('models.classes.brand', 1)]))
|
||||
@section('breadcrumb',
|
||||
request()->route()->getName())
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3">
|
||||
<div class="d-flex align-items-start flex-row">
|
||||
<a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.brands.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.brand', 1)]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => ''])
|
||||
</div>
|
||||
<div class="mb-md-0 mb-2">
|
||||
<button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.brand', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('bo.brands.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@include('bo.pages.brands.form')
|
||||
<div class="row mt-3">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.brand', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
52
resources/views/bo/pages/brands/edit.blade.php
Normal file
52
resources/views/bo/pages/brands/edit.blade.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
@extends('bo.layout', ['brParam' => $brandModel])
|
||||
|
||||
@section('title', trans_choice('crud.meta.title.edit', 1, ['model' => trans_choice('models.classes.brand', 1)]))
|
||||
@section('description', trans_choice('crud.meta.description.edit', 1, ['model' => trans_choice('models.classes.brand', 1)]))
|
||||
@section('breadcrumb',
|
||||
request()->route()->getName())
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3">
|
||||
<div class="d-flex align-items-start flex-row">
|
||||
<a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.brands.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.brand', '*')]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $brandModel])
|
||||
</div>
|
||||
<div class="mb-md-0 mb-2">
|
||||
<form class="btn-group confirmDeleteTS" action="{{ route('bo.brands.destroy', $brandModel) }}" method="POST" novalidate>
|
||||
@can('duplicate', $brandModel)
|
||||
<a class="btn btn-secondary" data-bs-toggle="tooltip" href="{{ route('bo.brands.duplicate', ['brands' => $brandModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
@endcan
|
||||
<button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.brand', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
@can('delete', $brandModel)
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-danger" data-bs-toggle="tooltip" type="submit" title="{{ Str::ucfirst(__('crud.actions.delete')) }}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@endcan
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('bo.brands.update', $brandModel) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('bo.pages.brands.form')
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.brand', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
107
resources/views/bo/pages/brands/form.blade.php
Normal file
107
resources/views/bo/pages/brands/form.blade.php
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<div class="row">
|
||||
{{-- NAME --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar', [
|
||||
'id' => 'brandText',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.brand', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'name',
|
||||
'required' => true,
|
||||
'model' => $brandModel ?? null,
|
||||
'helper' => __('validation.string', [
|
||||
'attribute' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.brand', 1),
|
||||
]),
|
||||
]),
|
||||
'placeholder' => Str::ucfirst(__('validation.attributes.name')),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
{{-- COLOR --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.custom.color')) }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.int-color', [
|
||||
'id' => 'brandCouleur',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.custom.color')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.brand', 1),
|
||||
]),
|
||||
'name' => 'color',
|
||||
'required' => true,
|
||||
'model' => $brandModel ?? null,
|
||||
'helper' => __('validation.string', [
|
||||
'attribute' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.custom.color')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.brand', 1),
|
||||
]),
|
||||
]),
|
||||
'placeholder' => Str::ucfirst(__('validation.attributes.name')),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
{{-- PICTURE --}}
|
||||
<div class="col-12 pb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.image')) }} *</legend>
|
||||
<div class="row mb-3">
|
||||
{{-- PICTURE --}}
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar-picture', [
|
||||
'id' => 'brandPicture',
|
||||
'name' => 'picture',
|
||||
'width' => 600,
|
||||
'height' => 350,
|
||||
'model' => $brandModel ?? null,
|
||||
'helper' => __('crud.minicrud.dimensions_must_be', [
|
||||
'widthpx' => '600px',
|
||||
'heightpx' => '350px',
|
||||
]),
|
||||
'required' => true,
|
||||
'preview' => true,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<p><b>{{ __('* Champs obligatoires') }}</b></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script nonce="{{ $nonce }}">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Submit button clone.
|
||||
const submit = document.getElementById('formSubmit')
|
||||
submitClone = document.getElementById('formSubmitClone');
|
||||
submitClone.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
submit.click('');
|
||||
})
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
67
resources/views/bo/pages/brands/index-list.blade.php
Normal file
67
resources/views/bo/pages/brands/index-list.blade.php
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<table class="table-hover table-fix-action m-0 table">
|
||||
@if (count($brandModelList) > 0)
|
||||
<thead>
|
||||
@include('bo.modules.table-col-sorter', [
|
||||
'cols' => [
|
||||
'name' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'color' => Str::ucfirst(__('validation.custom.color')),
|
||||
'updated_at' => Str::ucfirst(__('validation.attributes.updated_at')),
|
||||
],
|
||||
])
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($brandModelList as $brandModel)
|
||||
<tr class="border-bottom list-item">
|
||||
<td class="text-center align-middle">{{ $brandModel->name }}</td>
|
||||
<td class="text-center align-middle">
|
||||
<span class="badge" style="background-color: {{ $brandModel->color }};">
|
||||
{{ $brandModel->color }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
<span class="badge bg-secondary">{{ $brandModel->updated_at->isoFormat('LLLL') }}</span>
|
||||
</td>
|
||||
<td class="text-end align-middle">
|
||||
@canAny(['delete', 'duplicate', 'update'], $brandModel)
|
||||
<form class="btn-group confirmDeleteTS" action="{{ route('bo.brands.destroy', $brandModel) }}" method="POST"
|
||||
novalidate>
|
||||
@can('duplicate', $brandModel)
|
||||
<a class="btn btn-sm btn-secondary" data-bs-toggle="tooltip"
|
||||
href="{{ route('bo.brands.duplicate', ['brand' => $brandModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('update', $brandModel)
|
||||
<a class="btn btn-sm btn-primary" data-bs-toggle="tooltip"
|
||||
href="{{ route('bo.brands.edit', ['brand' => $brandModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.update')) }}">
|
||||
<i class="fa-solid fa-pencil"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('delete', $brandModel)
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-sm btn-danger" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ Str::ucfirst(__('crud.actions.delete')) }}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@endcan
|
||||
</form>
|
||||
@else
|
||||
<span class="text-danger" data-bs-toggle="tooltip" title="{{ __('list.right') }}">
|
||||
<i class="fa-solid fa-ban"></i>
|
||||
</span>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@else
|
||||
<tr>
|
||||
<td class="border-0">
|
||||
{{ trans_choice('crud.other.no_model_found', 1, ['model' => trans_choice('models.classes.brand', '*')]) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
21
resources/views/bo/pages/brands/index.blade.php
Normal file
21
resources/views/bo/pages/brands/index.blade.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
@extends('bo.layout')
|
||||
|
||||
@section('title', Str::ucfirst(trans_choice('models.classes.brand', '*')))
|
||||
@section('description', __('Liste de tous les :model', ['model' => trans_choice('models.classes.brand', '*')]))
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
|
||||
@include('modules.breadcrumbs.breadcrumb-body')
|
||||
@can('create', \App\Models\Brand::class)
|
||||
<a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.brands.create') }}"
|
||||
title="{{ __('crud.helpers.create_model', ['model' => trans_choice('models.classes.brand', 1)]) }}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</a>
|
||||
@endcan
|
||||
</div>
|
||||
@include('bo.modules.search-bar')
|
||||
<div class="table-responsive mb-3">
|
||||
@include('bo.pages.brands.index-list')
|
||||
</div>
|
||||
{{ $brandModelList->links() }}
|
||||
@endsection
|
||||
36
resources/views/bo/pages/products/create.blade.php
Normal file
36
resources/views/bo/pages/products/create.blade.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
@extends('bo.layout', ['brParam' => ''])
|
||||
|
||||
@section('title', trans_choice('crud.meta.title.create', 1, ['model' => trans_choice('models.classes.product', 1)]))
|
||||
@section('description', trans_choice('crud.meta.description.create', 1, ['model' => trans_choice('models.classes.product', 1)]))
|
||||
@section('breadcrumb',
|
||||
request()->route()->getName())
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3">
|
||||
<div class="d-flex align-items-start flex-row">
|
||||
<a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.products.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.product', '*')]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => ''])
|
||||
</div>
|
||||
<div class="mb-md-0 mb-2">
|
||||
<button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.product', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('bo.products.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@include('bo.pages.products.form')
|
||||
<div class="row mt-3">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.product', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
52
resources/views/bo/pages/products/edit.blade.php
Normal file
52
resources/views/bo/pages/products/edit.blade.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
@extends('bo.layout', ['brParam' => $productModel])
|
||||
|
||||
@section('title', trans_choice('crud.meta.title.edit', 1, ['model' => trans_choice('models.classes.product', 1)]))
|
||||
@section('description', trans_choice('crud.meta.description.edit', 1, ['model' => trans_choice('models.classes.product', 1)]))
|
||||
@section('breadcrumb',
|
||||
request()->route()->getName())
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3">
|
||||
<div class="d-flex align-items-start flex-row">
|
||||
<a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.products.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.product', '*')]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $productModel])
|
||||
</div>
|
||||
<div class="mb-md-0 mb-2">
|
||||
<form class="btn-group confirmDeleteTS" action="{{ route('bo.products.destroy', $productModel) }}" method="POST" novalidate>
|
||||
@can('duplicate', $productModel)
|
||||
<a class="btn btn-secondary" data-bs-toggle="tooltip" href="{{ route('bo.products.duplicate', ['products' => $productModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
@endcan
|
||||
<button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.product', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
@can('delete', $productModel)
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-danger" data-bs-toggle="tooltip" type="submit" title="{{ Str::ucfirst(__('crud.actions.delete')) }}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@endcan
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('bo.products.update', $productModel) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('bo.pages.products.form')
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.product', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
124
resources/views/bo/pages/products/form.blade.php
Normal file
124
resources/views/bo/pages/products/form.blade.php
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<div class="row">
|
||||
{{-- NAME --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar', [
|
||||
'id' => 'productText',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'inter' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.product', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'name',
|
||||
'required' => true,
|
||||
'model' => $productModel ?? null,
|
||||
'helper' => __('validation.string', [
|
||||
'attribute' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'inter' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.product', 1),
|
||||
]),
|
||||
]),
|
||||
'placeholder' => Str::ucfirst(__('validation.attributes.name')),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- RANGE_ID --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(trans_choice('models.classes.range', 1)) }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.bigint-foreign-id', [
|
||||
'id' => 'productRange',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(trans_choice('models.classes.range', 1)),
|
||||
'inter' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.product', 1),
|
||||
]),
|
||||
'name' => 'range_id',
|
||||
'required' => true,
|
||||
'model' => $productModel ?? null,
|
||||
'associatedModels' => $rangeModelList ?? null,
|
||||
'foreignLabel' => 'name',
|
||||
'helper' => __('crud.minicrud.enum_select', [
|
||||
'enum' => trans_choice('models.classes.range', 1),
|
||||
]),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- PICTURE --}}
|
||||
<div class="col-12 pb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.image')) }} *</legend>
|
||||
<div class="row mb-3">
|
||||
{{-- PICTURE --}}
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar-picture', [
|
||||
'id' => 'productPicture',
|
||||
'name' => 'picture',
|
||||
'width' => 800,
|
||||
'height' => 800,
|
||||
'model' => $productModel ?? null,
|
||||
'helper' => __('crud.minicrud.dimensions_must_be', [
|
||||
'widthpx' => '800px',
|
||||
'heightpx' => '800px',
|
||||
]),
|
||||
'required' => true,
|
||||
'preview' => true,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- DESCRIPTION --}}
|
||||
<div class="col-12 mb-3">
|
||||
<fieldset class="border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.description')) }}</legend>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.text-html', [
|
||||
'id' => 'productDescription',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.description')),
|
||||
'inter' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.product', 1),
|
||||
]),
|
||||
'name' => 'description',
|
||||
'model' => $productModel ?? null,
|
||||
'required' => true,
|
||||
'helper' => __('crud.minicrud.text_warning'),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<p><b>{{ __('* Champs obligatoires') }}</b></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script nonce="{{ $nonce }}">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Submit button clone.
|
||||
const submit = document.getElementById('formSubmit')
|
||||
submitClone = document.getElementById('formSubmitClone');
|
||||
submitClone.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
submit.click('');
|
||||
})
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
68
resources/views/bo/pages/products/index-list.blade.php
Normal file
68
resources/views/bo/pages/products/index-list.blade.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<table class="table-hover table-fix-action m-0 table">
|
||||
@if (count($productModelList) > 0)
|
||||
<thead>
|
||||
@include('bo.modules.table-col-sorter', [
|
||||
'cols' => [
|
||||
'name' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'range_id' => Str::ucfirst(trans_choice('models.classes.range', 1)),
|
||||
'updated_at' => Str::ucfirst(__('validation.attributes.updated_at')),
|
||||
],
|
||||
])
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($productModelList as $productModel)
|
||||
<tr class="border-bottom list-item">
|
||||
<td class="text-center align-middle">{{ $productModel->name }}</td>
|
||||
<td class="text-center align-middle">
|
||||
<a class="btn btn-link" data-bs-toggle="tooltip" href="{{ route('bo.ranges.edit', $productModel->range) }}"
|
||||
title="{{ __('Voir la gamme') }}">
|
||||
{{ Str::limit($productModel->range->name, 35) }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
<span class="badge bg-secondary">{{ $productModel->updated_at->isoFormat('LLLL') }}</span>
|
||||
</td>
|
||||
<td class="text-end align-middle">
|
||||
@canAny(['delete', 'duplicate', 'update'], $productModel)
|
||||
<form class="btn-group confirmDeleteTS" action="{{ route('bo.products.destroy', $productModel) }}" method="POST"
|
||||
novalidate>
|
||||
@can('duplicate', $productModel)
|
||||
<a class="btn btn-sm btn-secondary" data-bs-toggle="tooltip"
|
||||
href="{{ route('bo.products.duplicate', ['product' => $productModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('update', $productModel)
|
||||
<a class="btn btn-sm btn-primary" data-bs-toggle="tooltip"
|
||||
href="{{ route('bo.products.edit', ['product' => $productModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.update')) }}">
|
||||
<i class="fa-solid fa-pencil"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('delete', $productModel)
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-sm btn-danger" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ Str::ucfirst(__('crud.actions.delete')) }}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@endcan
|
||||
</form>
|
||||
@else
|
||||
<span class="text-danger" data-bs-toggle="tooltip" title="{{ __('list.right') }}">
|
||||
<i class="fa-solid fa-ban"></i>
|
||||
</span>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@else
|
||||
<tr>
|
||||
<td class="border-0">
|
||||
{{ trans_choice('crud.other.no_model_found', 1, ['model' => trans_choice('models.classes.product', '*')]) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
21
resources/views/bo/pages/products/index.blade.php
Normal file
21
resources/views/bo/pages/products/index.blade.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
@extends('bo.layout')
|
||||
|
||||
@section('title', Str::ucfirst(trans_choice('models.classes.product', '*')))
|
||||
@section('description', __('Liste de tous les :model', ['model' => trans_choice('models.classes.product', '*')]))
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
|
||||
@include('modules.breadcrumbs.breadcrumb-body')
|
||||
@can('create', \App\Models\Product::class)
|
||||
<a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.products.create') }}"
|
||||
title="{{ __('crud.helpers.create_model', ['model' => trans_choice('models.classes.product', 1)]) }}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</a>
|
||||
@endcan
|
||||
</div>
|
||||
@include('bo.modules.search-bar')
|
||||
<div class="table-responsive mb-3">
|
||||
@include('bo.pages.products.index-list')
|
||||
</div>
|
||||
{{ $productModelList->links() }}
|
||||
@endsection
|
||||
36
resources/views/bo/pages/ranges/create.blade.php
Normal file
36
resources/views/bo/pages/ranges/create.blade.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
@extends('bo.layout', ['brParam' => ''])
|
||||
|
||||
@section('title', trans_choice('crud.meta.title.create', 1, ['model' => trans_choice('models.classes.range', 1)]))
|
||||
@section('description', trans_choice('crud.meta.description.create', 1, ['model' => trans_choice('models.classes.range', 1)]))
|
||||
@section('breadcrumb',
|
||||
request()->route()->getName())
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3">
|
||||
<div class="d-flex align-items-start flex-row">
|
||||
<a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.ranges.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.range', '*')]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => ''])
|
||||
</div>
|
||||
<div class="mb-md-0 mb-2">
|
||||
<button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.range', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('bo.ranges.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@include('bo.pages.ranges.form')
|
||||
<div class="row mt-3">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.range', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
52
resources/views/bo/pages/ranges/edit.blade.php
Normal file
52
resources/views/bo/pages/ranges/edit.blade.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
@extends('bo.layout', ['brParam' => $rangeModel])
|
||||
|
||||
@section('title', trans_choice('crud.meta.title.edit', 1, ['model' => trans_choice('models.classes.range', 1)]))
|
||||
@section('description', trans_choice('crud.meta.description.edit', 1, ['model' => trans_choice('models.classe.ranges', 1)]))
|
||||
@section('breadcrumb',
|
||||
request()->route()->getName())
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3">
|
||||
<div class="d-flex align-items-start flex-row">
|
||||
<a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.ranges.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.range', '*')]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $rangeModel])
|
||||
</div>
|
||||
<div class="mb-md-0 mb-2">
|
||||
<form class="btn-group confirmDeleteTS" action="{{ route('bo.ranges.destroy', $rangeModel) }}" method="POST" novalidate>
|
||||
@can('duplicate', $rangeModel)
|
||||
<a class="btn btn-secondary" data-bs-toggle="tooltip" href="{{ route('bo.ranges.duplicate', ['ranges' => $rangeModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
@endcan
|
||||
<button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.range', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
@can('delete', $rangeModel)
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-danger" data-bs-toggle="tooltip" type="submit" title="{{ Str::ucfirst(__('crud.actions.delete')) }}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@endcan
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('bo.ranges.update', $rangeModel) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('bo.pages.ranges.form')
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.range', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
102
resources/views/bo/pages/ranges/form.blade.php
Normal file
102
resources/views/bo/pages/ranges/form.blade.php
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<div class="row">
|
||||
{{-- NAME --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar', [
|
||||
'id' => 'rangeText',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.range', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'name',
|
||||
'required' => true,
|
||||
'model' => $rangeModel ?? null,
|
||||
'helper' => __('validation.string', [
|
||||
'attribute' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.range', 1),
|
||||
]),
|
||||
]),
|
||||
'placeholder' => Str::ucfirst(__('validation.attributes.name')),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- BRAND_ID --}}
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(trans_choice('models.classes.brand', 1)) }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.bigint-foreign-id', [
|
||||
'id' => 'rangeBrand',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(trans_choice('models.classes.brand', 1)),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.range', 1),
|
||||
]),
|
||||
'name' => 'brand_id',
|
||||
'required' => true,
|
||||
'model' => $rangeModel ?? null,
|
||||
'associatedModels' => $brandModelList ?? null,
|
||||
'foreignLabel' => 'name',
|
||||
'helper' => __('crud.minicrud.enum_select', [
|
||||
'enum' => trans_choice('models.classes.brand', 1),
|
||||
]),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{-- PICTURE --}}
|
||||
<div class="col-12 pb-3">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.image')) }} *</legend>
|
||||
<div class="row mb-3">
|
||||
{{-- PICTURE --}}
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar-picture', [
|
||||
'id' => 'rangePicture',
|
||||
'name' => 'picture',
|
||||
'width' => 600,
|
||||
'height' => 500,
|
||||
'model' => $rangeModel ?? null,
|
||||
'helper' => __('crud.minicrud.dimensions_must_be', [
|
||||
'widthpx' => '600px',
|
||||
'heightpx' => '500px',
|
||||
]),
|
||||
'required' => true,
|
||||
'preview' => true,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<p><b>{{ __('* Champs obligatoires') }}</b></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script nonce="{{ $nonce }}">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Submit button clone.
|
||||
const submit = document.getElementById('formSubmit')
|
||||
submitClone = document.getElementById('formSubmitClone');
|
||||
submitClone.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
submit.click('');
|
||||
})
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
68
resources/views/bo/pages/ranges/index-list.blade.php
Normal file
68
resources/views/bo/pages/ranges/index-list.blade.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<table class="table-hover table-fix-action m-0 table">
|
||||
@if (count($rangeModelList) > 0)
|
||||
<thead>
|
||||
@include('bo.modules.table-col-sorter', [
|
||||
'cols' => [
|
||||
'name' => Str::ucfirst(__('validation.attributes.name')),
|
||||
'brand_id' => Str::ucfirst(trans_choice('models.classes.brand', 1)),
|
||||
'updated_at' => Str::ucfirst(__('validation.attributes.updated_at')),
|
||||
],
|
||||
])
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($rangeModelList as $rangeModel)
|
||||
<tr class="border-bottom list-item">
|
||||
<td class="text-center align-middle">{{ $rangeModel->name }}</td>
|
||||
<td class="text-center align-middle">
|
||||
<a class="btn btn-link" data-bs-toggle="tooltip" href="{{ route('bo.brands.edit', $rangeModel->brand) }}"
|
||||
title="{{ __('Voir la marque') }}">
|
||||
{{ Str::limit($rangeModel->brand->name, 35) }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
<span class="badge bg-secondary">{{ $rangeModel->updated_at->isoFormat('LLLL') }}</span>
|
||||
</td>
|
||||
<td class="text-end align-middle">
|
||||
@canAny(['delete', 'duplicate', 'update'], $rangeModel)
|
||||
<form class="btn-group confirmDeleteTS" action="{{ route('bo.ranges.destroy', $rangeModel) }}" method="POST"
|
||||
novalidate>
|
||||
@can('duplicate', $rangeModel)
|
||||
<a class="btn btn-sm btn-secondary" data-bs-toggle="tooltip"
|
||||
href="{{ route('bo.ranges.duplicate', ['range' => $rangeModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('update', $rangeModel)
|
||||
<a class="btn btn-sm btn-primary" data-bs-toggle="tooltip"
|
||||
href="{{ route('bo.ranges.edit', ['range' => $rangeModel]) }}"
|
||||
title="{{ Str::ucfirst(__('crud.actions.update')) }}">
|
||||
<i class="fa-solid fa-pencil"></i>
|
||||
</a>
|
||||
@endcan
|
||||
@can('delete', $rangeModel)
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-sm btn-danger" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ Str::ucfirst(__('crud.actions.delete')) }}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@endcan
|
||||
</form>
|
||||
@else
|
||||
<span class="text-danger" data-bs-toggle="tooltip" title="{{ __('list.right') }}">
|
||||
<i class="fa-solid fa-ban"></i>
|
||||
</span>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@else
|
||||
<tr>
|
||||
<td class="border-0">
|
||||
{{ trans_choice('crud.other.no_model_found', 1, ['model' => trans_choice('models.classes.range', '*')]) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
21
resources/views/bo/pages/ranges/index.blade.php
Normal file
21
resources/views/bo/pages/ranges/index.blade.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
@extends('bo.layout')
|
||||
|
||||
@section('title', Str::ucfirst(trans_choice('models.classes.range', '*')))
|
||||
@section('description', __('Liste de tous les :model', ['model' => trans_choice('models.classes.range', '*')]))
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
|
||||
@include('modules.breadcrumbs.breadcrumb-body')
|
||||
@can('create', \App\Models\Range::class)
|
||||
<a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.ranges.create') }}"
|
||||
title="{{ __('crud.helpers.create_model', ['model' => trans_choice('models.classes.range', 1)]) }}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</a>
|
||||
@endcan
|
||||
</div>
|
||||
@include('bo.modules.search-bar')
|
||||
<div class="table-responsive mb-3">
|
||||
@include('bo.pages.ranges.index-list')
|
||||
</div>
|
||||
{{ $rangeModelList->links() }}
|
||||
@endsection
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<input
|
||||
id="{{ $id }}"
|
||||
name="{{ $name }}"
|
||||
type="text"
|
||||
type="color"
|
||||
data-jscolor=""
|
||||
class="form-control"
|
||||
aria-describedby="{{ $id }}Help"
|
||||
|
|
@ -27,45 +27,3 @@
|
|||
'helperId' => "{$id}Help",
|
||||
'helper' => $helper
|
||||
])
|
||||
|
||||
@push('scripts')
|
||||
<script @if(isset($nonce) and is_string($nonce)) nonce="{{ $nonce }}" @endif>
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
// https://stackoverflow.com/a/35970186/4355295
|
||||
let __invertColor = function(hex, bw) {
|
||||
if (hex.indexOf('#') === 0) {
|
||||
hex = hex.slice(1);
|
||||
}
|
||||
// convert 3-digit hex to 6-digits.
|
||||
if (hex.length === 3) {
|
||||
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
||||
}
|
||||
if (hex.length !== 6) {
|
||||
throw new Error('Invalid HEX color.');
|
||||
}
|
||||
var r = parseInt(hex.slice(0, 2), 16),
|
||||
g = parseInt(hex.slice(2, 4), 16),
|
||||
b = parseInt(hex.slice(4, 6), 16);
|
||||
if (bw) {
|
||||
// https://stackoverflow.com/a/3943023/112731
|
||||
return (r * 0.299 + g * 0.587 + b * 0.114) > 186
|
||||
? '#000000'
|
||||
: '#FFFFFF';
|
||||
}
|
||||
// invert color components
|
||||
r = (255 - r).toString(16);
|
||||
g = (255 - g).toString(16);
|
||||
b = (255 - b).toString(16);
|
||||
// pad each with zeros and return
|
||||
return "#" + r.padStart(1, '0') + g.padStart(1, '0') + b.padStart(1, '0');
|
||||
}
|
||||
let colorSpans = document.getElementsByClassName('colorize');
|
||||
if(colorSpans.length) {
|
||||
for(let colorSpan of colorSpans) {
|
||||
colorSpan.style.color = __invertColor(colorSpan.textContent, true);
|
||||
colorSpan.style.backgroundColor = colorSpan.textContent;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<?php
|
||||
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\StaticPage;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use App\Models\User;
|
||||
use Diglactic\Breadcrumbs\Breadcrumbs;
|
||||
use Diglactic\Breadcrumbs\Generator;
|
||||
|
|
@ -39,14 +41,50 @@ Breadcrumbs::for('bo.activity_logs.show', function (Generator $trail, ActivityLo
|
|||
$trail->push(Str::ucfirst(trans('crud.actions.view')), route('bo.activity_logs.show', $activity_log));
|
||||
});
|
||||
|
||||
// * STATIC PAGES
|
||||
Breadcrumbs::for('bo.static_pages.index', function (Generator $trail) {
|
||||
// * BRANDS
|
||||
Breadcrumbs::for('bo.brands.index', function (Generator $trail) {
|
||||
$trail->push(
|
||||
Str::of(trans_choice('models.classes.static_page', '*'))->ucfirst(),
|
||||
route('bo.static_pages.index', ['sort_col' => 'updated_at', 'sort_way' => 'desc'])
|
||||
Str::of(trans_choice('models.classes.brand', '*'))->ucfirst(),
|
||||
route('bo.brands.index', ['sort_col' => 'updated_at', 'sort_way' => 'desc'])
|
||||
);
|
||||
});
|
||||
Breadcrumbs::for('bo.static_pages.edit', function (Generator $trail, StaticPage $static_page) {
|
||||
$trail->parent('bo.static_pages.index');
|
||||
$trail->push(Str::ucfirst(trans('crud.actions.edit')), route('bo.static_pages.edit', $static_page));
|
||||
Breadcrumbs::for('bo.brands.create', function (Generator $trail) {
|
||||
$trail->parent('bo.brands.index');
|
||||
$trail->push(Str::ucfirst(trans('crud.actions.create')), route('bo.brands.create'));
|
||||
});
|
||||
Breadcrumbs::for('bo.brands.edit', function (Generator $trail, Brand $brand) {
|
||||
$trail->parent('bo.brands.index');
|
||||
$trail->push(Str::ucfirst(trans('crud.actions.edit')), route('bo.brands.edit', $brand));
|
||||
});
|
||||
|
||||
// * RANGES
|
||||
Breadcrumbs::for('bo.ranges.index', function (Generator $trail) {
|
||||
$trail->push(
|
||||
Str::of(trans_choice('models.classes.range', '*'))->ucfirst(),
|
||||
route('bo.ranges.index', ['sort_col' => 'updated_at', 'sort_way' => 'desc'])
|
||||
);
|
||||
});
|
||||
Breadcrumbs::for('bo.ranges.create', function (Generator $trail) {
|
||||
$trail->parent('bo.ranges.index');
|
||||
$trail->push(Str::ucfirst(trans('crud.actions.create')), route('bo.ranges.create'));
|
||||
});
|
||||
Breadcrumbs::for('bo.ranges.edit', function (Generator $trail, Range $range) {
|
||||
$trail->parent('bo.ranges.index');
|
||||
$trail->push(Str::ucfirst(trans('crud.actions.edit')), route('bo.ranges.edit', $range));
|
||||
});
|
||||
|
||||
// * PRODUCTS
|
||||
Breadcrumbs::for('bo.products.index', function (Generator $trail) {
|
||||
$trail->push(
|
||||
Str::of(trans_choice('models.classes.product', '*'))->ucfirst(),
|
||||
route('bo.products.index', ['sort_col' => 'updated_at', 'sort_way' => 'desc'])
|
||||
);
|
||||
});
|
||||
Breadcrumbs::for('bo.products.create', function (Generator $trail) {
|
||||
$trail->parent('bo.products.index');
|
||||
$trail->push(Str::ucfirst(trans('crud.actions.create')), route('bo.products.create'));
|
||||
});
|
||||
Breadcrumbs::for('bo.products.edit', function (Generator $trail, Product $product) {
|
||||
$trail->parent('bo.products.index');
|
||||
$trail->push(Str::ucfirst(trans('crud.actions.edit')), route('bo.products.edit', $product));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Bo\ActivityLogController;
|
||||
use App\Http\Controllers\Bo\BrandController;
|
||||
use App\Http\Controllers\Bo\DashboardController;
|
||||
use App\Http\Controllers\Bo\ProductController;
|
||||
use App\Http\Controllers\Bo\RangeController;
|
||||
use App\Http\Controllers\Bo\UserController;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
|
@ -36,6 +39,17 @@ Route::prefix('bo')
|
|||
UserController::class, 'duplicate'
|
||||
])->name('users.duplicate');
|
||||
|
||||
// * BRANDS
|
||||
Route::resource('brands', BrandController::class)->except('show');
|
||||
|
||||
// * RANGES
|
||||
Route::resource('ranges', RangeController::class)->except('show');
|
||||
|
||||
// * PRODUCTS
|
||||
Route::resource('products', ProductController::class)->except('show')
|
||||
->withoutMiddleware('htmlsanitize')
|
||||
->middleware('htmlsanitize:description');
|
||||
|
||||
Route::middleware('can:isConceptor')->group(function () {
|
||||
// * ACTIVITY LOGS.
|
||||
Route::resource('activity_logs', ActivityLogController::class)->only(['index', 'show']);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue