Compare commits
376 Commits
1.0.0-rc.1
...
ccb0399d91
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccb0399d91 | ||
|
|
062d3a9141 | ||
|
|
9ba8f4acf4 | ||
|
|
e080d98e81 | ||
|
|
bd63a92604 | ||
|
|
dc05eb31ac | ||
|
|
5bee7c5e50 | ||
|
|
31500f3386 | ||
|
|
0f58dfe1f4 | ||
|
|
c0737e3bce | ||
|
|
7913576f1a | ||
|
|
d88b8c8a70 | ||
|
|
f2a87f4d12 | ||
|
|
c338a8afc7 | ||
|
|
29c21367d7 | ||
|
|
09b6c3cf28 | ||
|
|
a34905f34e | ||
|
|
8d8528f6fb | ||
|
|
e69487f73e | ||
|
|
a2da0a8a12 | ||
|
|
b7634e07c0 | ||
|
|
ece0b433fd | ||
|
|
c94d815e5a | ||
|
|
0d0e7c4652 | ||
|
|
0e9d758e6b | ||
|
|
883d2d634d | ||
|
|
1503f21b3b | ||
|
|
17fa80b96b | ||
|
|
92c9975e81 | ||
|
|
b214d8dc74 | ||
|
|
08fc49cb61 | ||
|
|
0f32386406 | ||
|
|
4023fd9ee4 | ||
|
|
1e32fd7cf1 | ||
|
|
870fc6c806 | ||
|
|
045641e687 | ||
|
|
38e477ee5f | ||
|
|
561cd2248c | ||
|
|
0a538ee1a2 | ||
|
|
e490c27837 | ||
|
|
e92f38f6c3 | ||
|
|
296dc4fde2 | ||
|
|
e4e2126ada | ||
|
|
44391e817c | ||
|
|
5359375210 | ||
|
|
c15a6e380f | ||
|
|
3acb69abe4 | ||
|
|
00fc978217 | ||
|
|
7a33245c1e | ||
|
|
927be4ea77 | ||
|
|
b0b87db86d | ||
|
|
53448d7907 | ||
|
|
e920157c0b | ||
|
|
8f905ce1eb | ||
|
|
b95da54240 | ||
|
|
e6ad147790 | ||
|
|
5fc8d3dc57 | ||
|
|
4caf069011 | ||
|
|
d0e97ccd08 | ||
|
|
38ce69b525 | ||
|
|
b67e91d228 | ||
|
|
94f84a7a85 | ||
|
|
54ab6b0231 | ||
|
|
9e30118088 | ||
|
|
6c88e43b74 | ||
|
|
5c20e6d5d0 | ||
|
|
79af996c63 | ||
|
|
c0c4146c9c | ||
|
|
84bc5f2e67 | ||
|
|
72f5da4555 | ||
|
|
349204307f | ||
|
|
36459de793 | ||
|
|
d432827bab | ||
|
|
930695166d | ||
|
|
8c39035cdc | ||
|
|
6a1d8ba80a | ||
|
|
6f8340b90b | ||
|
|
c5f06a608c | ||
|
|
50111787aa | ||
|
|
b80c2a8d41 | ||
|
|
2eea4ec73d | ||
|
|
88a57a9c32 | ||
|
|
7e5b1fea89 | ||
|
|
c053221662 | ||
|
|
25b8fc40c5 | ||
|
|
d33dd6e755 | ||
|
|
04f92c9695 | ||
|
|
7c8546e450 | ||
|
|
585d42264f | ||
|
|
9f4ef99ce2 | ||
|
|
501f3a5ab9 | ||
|
|
0ff39c1401 | ||
|
|
71ecb13af0 | ||
|
|
7561a8e8ba | ||
|
|
bbf4bc4232 | ||
|
|
5d5ea92127 | ||
|
|
a84995e0ce | ||
|
|
fcc564b6d8 | ||
|
|
be468304c9 | ||
|
|
b812a78f9f | ||
|
|
0ec0b8e85b | ||
|
|
9d520abfcf | ||
|
|
174c4ca0e2 | ||
|
|
216c408596 | ||
|
|
3cdb30a0dc | ||
|
|
5bcf265cda | ||
|
|
7ec1d3e89b | ||
|
|
741f389620 | ||
|
|
37f1ebbb59 | ||
|
|
81974d38ab | ||
|
|
cc9672ddca | ||
|
|
12903b6029 | ||
|
|
e6b15e2438 | ||
|
|
002644cb97 | ||
|
|
dfe7625995 | ||
|
|
53feef282f | ||
|
|
69478e3c74 | ||
|
|
ab8ab8eba1 | ||
|
|
de5f3d665d | ||
|
|
afaf0cc629 | ||
|
|
419a4c2b9a | ||
|
|
d750cdced7 | ||
|
|
01f9c9ae7c | ||
|
|
043b7d7215 | ||
|
|
f85df32c67 | ||
|
|
6f2a985edc | ||
|
|
5f1ff26196 | ||
|
|
ddb4520621 | ||
|
|
c619f540f8 | ||
|
|
39c80ce6d1 | ||
|
|
297dcc62d2 | ||
|
|
b011f40b2f | ||
|
|
fdbf819bf5 | ||
|
|
f4738377c7 | ||
|
|
99bdf09be7 | ||
|
|
b283363543 | ||
|
|
6649c32501 | ||
|
|
bc1cf1190b | ||
|
|
3dc6c70c4d | ||
|
|
6e4f93dd65 | ||
|
|
ba8f87cff4 | ||
|
|
efb3fe9670 | ||
|
|
23e6ca35ca | ||
|
|
695d23a139 | ||
|
|
3943fc033f | ||
|
|
bb77a199eb | ||
|
|
808e60e41b | ||
|
|
260eccc1f1 | ||
|
|
abed17b3f0 | ||
|
|
3ad83b870a | ||
|
|
241b255b1c | ||
|
|
d2ec87c374 | ||
|
|
24b86652aa | ||
|
|
923f988aba | ||
|
|
0778fdc10a | ||
|
|
900aa413a2 | ||
|
|
820a200e88 | ||
|
|
186e3de863 | ||
|
|
7722b73be9 | ||
|
|
685160ddf5 | ||
|
|
7449229ff7 | ||
|
|
c5ae71544a | ||
|
|
926996166b | ||
|
|
fd8e89e33c | ||
|
|
503efc6f25 | ||
|
|
e8ae460bab | ||
|
|
bb1e1a6b18 | ||
|
|
02b2d3e410 | ||
|
|
72870680cf | ||
|
|
f3289334f3 | ||
|
|
12e5dbb721 | ||
|
|
b42ae14428 | ||
|
|
ea5350a0a6 | ||
|
|
90d78bc3e3 | ||
|
|
a85e5f2921 | ||
|
|
5e6af2e5f8 | ||
|
|
11aaaa024c | ||
|
|
fd1ab5cf04 | ||
|
|
a03befbf44 | ||
|
|
ce7755ffe3 | ||
|
|
17792fce40 | ||
|
|
9698ba54d0 | ||
|
|
ae39681cb0 | ||
|
|
9895266798 | ||
|
|
c1a9c21e52 | ||
|
|
3a18679a1c | ||
|
|
6ef31f286e | ||
|
|
52193994a3 | ||
|
|
71c0489862 | ||
|
|
d33b5eea52 | ||
|
|
782809829b | ||
|
|
ebe7ba5f6c | ||
|
|
dae8156164 | ||
|
|
1880b25407 | ||
|
|
5819f51f79 | ||
|
|
01f56204b7 | ||
|
|
eadea3958d | ||
|
|
719e4481d7 | ||
|
|
eae2cdf345 | ||
|
|
ab145dd60f | ||
|
|
8054bffb43 | ||
|
|
d50ecd674e | ||
|
|
1703082b3e | ||
|
|
35310b049e | ||
|
|
4801ff4338 | ||
|
|
b2987e5eed | ||
|
|
8b334bc8ed | ||
|
|
79c717ae6c | ||
|
|
8abf391a53 | ||
|
|
cc2798a78e | ||
|
|
ce9f613b66 | ||
|
|
3370b8061c | ||
|
|
8c898bf63b | ||
|
|
fedf463f15 | ||
|
|
73508f6b46 | ||
|
|
c79facd0ca | ||
|
|
3ba5a833b2 | ||
|
|
c77db883c2 | ||
|
|
904110d10f | ||
|
|
3c8fab27da | ||
|
|
ee0954931f | ||
|
|
d399b72120 | ||
|
|
6fc6451f6f | ||
|
|
f3e9ccaa46 | ||
|
|
1fc860715f | ||
|
|
6837954fc9 | ||
|
|
483aa59750 | ||
|
|
f460865a57 | ||
|
|
4d31b1682c | ||
|
|
ee148a27ed | ||
|
|
79e5a6388a | ||
|
|
165262abfa | ||
|
|
2f77b5fc23 | ||
|
|
8afb3467f8 | ||
|
|
e3c60e7cde | ||
|
|
7187a312eb | ||
|
|
e208dc910b | ||
|
|
d4bb94a487 | ||
|
|
e77ed62066 | ||
|
|
8dddd4d99b | ||
|
|
edf43ff270 | ||
|
|
36c6402f04 | ||
|
|
8ba8d9a276 | ||
|
|
cdce15a29f | ||
|
|
c12b45f8ad | ||
|
|
081df4d5d0 | ||
|
|
2d86f162a0 | ||
|
|
c2b7315abf | ||
|
|
7b265d3b47 | ||
|
|
c90dd98319 | ||
|
|
193f5cf4ad | ||
|
|
930eeaede8 | ||
|
|
36a63f3b14 | ||
|
|
d9dba1c515 | ||
|
|
411b47f78d | ||
|
|
a550278f39 | ||
|
|
d715d726db | ||
|
|
60acbc7939 | ||
|
|
6a634c10ca | ||
|
|
308f226ca0 | ||
|
|
c0aeda7554 | ||
|
|
3633581ebf | ||
|
|
15539f27a6 | ||
|
|
0a9d03e214 | ||
|
|
36f16921bb | ||
|
|
cbe397be6a | ||
|
|
8dc409bd78 | ||
|
|
56659c73bb | ||
|
|
accc94f56b | ||
|
|
64f5663da5 | ||
|
|
c6ba4aaa24 | ||
|
|
020954a7cc | ||
|
|
61be4a6769 | ||
|
|
cf9439852f | ||
|
|
5bf3b7ec7b | ||
|
|
715e228b41 | ||
|
|
91675dc83b | ||
|
|
e0addd61ce | ||
|
|
66cc7a6054 | ||
|
|
2d6b092b64 | ||
|
|
9ab19b0e4d | ||
|
|
6110b42ce2 | ||
|
|
82a345c4c7 | ||
|
|
c0a80aa62b | ||
|
|
b325a44ee2 | ||
|
|
88d87ceaab | ||
|
|
ba04007f86 | ||
|
|
81dcd273ce | ||
|
|
d899d3dcaa | ||
|
|
9f6d21ef04 | ||
|
|
bdefa235eb | ||
|
|
545c242bba | ||
|
|
e241486143 | ||
|
|
2f81f1d61d | ||
|
|
4d3ffa2ca3 | ||
|
|
f237882757 | ||
|
|
090dcd6f24 | ||
|
|
6db343c6b2 | ||
|
|
d46347a66f | ||
|
|
ab8f68e4bc | ||
|
|
8c9fc66b89 | ||
|
|
2be53d581e | ||
|
|
26f3fc3d2c | ||
|
|
efff4f0341 | ||
|
|
b3f2aa36be | ||
|
|
cdc88b43df | ||
|
|
ecbb7b62c9 | ||
|
|
b1a2e70d12 | ||
|
|
b2f5cc4a45 | ||
|
|
8a1573d425 | ||
|
|
cb0b2e4aa0 | ||
|
|
7092cf23fc | ||
|
|
e434220c16 | ||
|
|
0858804095 | ||
|
|
5f7e90d22d | ||
|
|
6dce60d227 | ||
|
|
f6668a6dd3 | ||
|
|
dd396a86aa | ||
|
|
f3b6504723 | ||
|
|
41cab61bda | ||
|
|
c76996db86 | ||
|
|
f5716c6530 | ||
|
|
459d35e8a1 | ||
|
|
e9ce44481a | ||
|
|
18f1f8a13a | ||
|
|
e97f54f126 | ||
|
|
0d0e4deb16 | ||
|
|
da51da2530 | ||
|
|
c024bdc31e | ||
|
|
4ec0558cd8 | ||
|
|
48d89d338c | ||
|
|
0f23d5cd56 | ||
|
|
9380d99688 | ||
|
|
06fb42e7c7 | ||
|
|
66a830eec0 | ||
|
|
0e73c8109b | ||
|
|
5e4c9963c6 | ||
|
|
f9070b2ec8 | ||
|
|
8107078ea7 | ||
|
|
be3b6bc0a8 | ||
|
|
290602057a | ||
|
|
a1a3ccb227 | ||
|
|
e9002fb494 | ||
|
|
cffec143e0 | ||
|
|
385873a0cf | ||
|
|
8842b7eea7 | ||
|
|
0cd0e1f126 | ||
|
|
7c01366281 | ||
|
|
fa193a8089 | ||
|
|
27c5663ba1 | ||
|
|
cfd8086586 | ||
|
|
c3a05640ab | ||
|
|
ca691c8fdd | ||
|
|
47ff07451f | ||
|
|
5bbf63b33f | ||
|
|
768c7e88f9 | ||
|
|
23b8914187 | ||
|
|
aee50813e4 | ||
|
|
331b7647de | ||
|
|
b1494477a2 | ||
|
|
31b671bcdd | ||
|
|
9e2226a776 | ||
|
|
9ca510086b | ||
|
|
7ec40145de | ||
|
|
734ec87b89 | ||
|
|
0d421226fa | ||
|
|
d174fe1c81 | ||
|
|
64abc46d99 | ||
|
|
f4ab8e71a8 | ||
|
|
763577bf87 | ||
|
|
226153f744 | ||
|
|
e298320119 | ||
|
|
b90d633e6e | ||
|
|
31e60dee45 | ||
|
|
0dd3df2984 | ||
|
|
d53e9f5a4f |
@@ -43,7 +43,6 @@ COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN mkdir -p /app/bootstrap/cache \
|
||||
/app/storage/media-library/temp \
|
||||
/app/storage/framework/cache \
|
||||
/app/storage/framework/views \
|
||||
/app/storage/framework/sessions \
|
||||
@@ -57,7 +56,6 @@ RUN chmod +x artisan
|
||||
RUN ./artisan vendor:publish --tag=public --force ## creates public/vendor/jsvalidation
|
||||
RUN ./artisan vendor:publish --tag=boilerplate-public --force --ansi ## creates public/vendor/boilerplate
|
||||
RUN ./artisan vendor:publish --tag=datatables-buttons --force --ansi ## creates public/vendor/datatables/buttons
|
||||
RUN ./artisan vendor:publish --tag=lfm_public --force --ansi
|
||||
|
||||
## XXXvlab: 2025-09-25 these migration files are breaking first
|
||||
## install, but we had to resolve to not install from scratch and use
|
||||
@@ -86,6 +84,8 @@ RUN apk add --no-cache xz
|
||||
# bring PHP app with vendor
|
||||
COPY --from=phpdeps /app /app
|
||||
|
||||
# ensure required runtime dirs exist (empty is fine)
|
||||
RUN mkdir -p storage/framework/{cache,views,sessions} bootstrap/cache
|
||||
# create artifact (use tar + xz so we don't depend on GNU tar -J)
|
||||
RUN mkdir -p /out \
|
||||
&& tar -C /app -cf /out/app.tar \
|
||||
|
||||
@@ -47,10 +47,6 @@ class CustomerOrdersDataTable extends DataTable
|
||||
{
|
||||
$datatables
|
||||
->editColumn('status', function (Order $order) {
|
||||
if ($order->status == 0 && in_array($order->payment_type, [2, 3])) {
|
||||
return 'En attente de règlement';
|
||||
}
|
||||
|
||||
return Orders::getStatus($order->status);
|
||||
})
|
||||
->editColumn('created_at', function (Order $order) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Admin\Botanic;
|
||||
|
||||
use App\Datatables\Botanic\SpeciesDataTable;
|
||||
use App\Repositories\Botanic\Genres;
|
||||
use App\Repositories\Botanic\Species;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -20,7 +21,7 @@ class SpecieController extends Controller
|
||||
|
||||
public function create()
|
||||
{
|
||||
$data = Species::init();
|
||||
$data = Genres::init();
|
||||
|
||||
return view('Admin.Botanic.Species.create', $data);
|
||||
}
|
||||
@@ -35,7 +36,7 @@ class SpecieController extends Controller
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$data = Species::init();
|
||||
$data = Genres::init();
|
||||
$data['specie'] = Species::getFull($id);
|
||||
|
||||
return view('Admin.Botanic.Species.edit', $data);
|
||||
|
||||
@@ -63,17 +63,6 @@ class ArticleController extends Controller
|
||||
return view('Admin.Shop.Articles.edit', $data);
|
||||
}
|
||||
|
||||
public function duplicate($id)
|
||||
{
|
||||
$data = Articles::getFull($id);
|
||||
// Prepare for creation: blank id/slug, tweak name to indicate copy
|
||||
$data['article']['id'] = null;
|
||||
$data['article']['slug'] = null;
|
||||
$data['article']['name'] = ($data['article']['name'] ?? '').' (copie)';
|
||||
|
||||
return view('Admin.Shop.Articles.create', $data);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
return Articles::destroy($id);
|
||||
|
||||
@@ -71,10 +71,7 @@ class PriceListValueController extends Controller
|
||||
|
||||
public function addPrice($index)
|
||||
{
|
||||
$data = [
|
||||
'index' => $index,
|
||||
'taxes' => Taxes::getOptions(),
|
||||
];
|
||||
$data['index'] = $index;
|
||||
|
||||
return view('Admin.Shop.PriceListValues.partials.row_price', $data);
|
||||
}
|
||||
|
||||
@@ -68,10 +68,8 @@ class BasketController extends Controller
|
||||
|
||||
public function getBasketTotal($deliveryId = false, $deliveryTypeId = false)
|
||||
{
|
||||
$basket = Baskets::getBasketTotal($deliveryId, $deliveryTypeId);
|
||||
$data = [
|
||||
'basket' => $basket,
|
||||
'sale_channel' => $basket['sale_channel'] ?? null,
|
||||
'basket' => Baskets::getBasketTotal($deliveryId, $deliveryTypeId),
|
||||
];
|
||||
|
||||
return view('Shop.Baskets.partials.basketTotal', $data);
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Repositories\Shop\CustomerAddresses;
|
||||
use App\Repositories\Shop\Customers;
|
||||
use App\Repositories\Shop\Offers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
@@ -118,82 +117,9 @@ class CustomerController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->all();
|
||||
|
||||
$validator = Validator::make($data, [
|
||||
'phone' => 'required|max:30',
|
||||
], [
|
||||
'phone.required' => __('Le numéro de téléphone est obligatoire.'),
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->route('Shop.Customers.edit')
|
||||
->withInput()
|
||||
->withErrors($validator->errors(), 'registration');
|
||||
}
|
||||
|
||||
$passwordError = $this->handlePasswordChange($request);
|
||||
if ($passwordError) {
|
||||
return redirect()->route('Shop.Customers.edit')
|
||||
->with('growl', [$passwordError, 'danger']);
|
||||
}
|
||||
|
||||
unset($data['current-password'], $data['new-password'], $data['new-password_confirmation']);
|
||||
|
||||
$customer = Customers::storeFull($data);
|
||||
|
||||
$growl = $request->filled('new-password')
|
||||
? [__('Profil et mot de passe mis à jour.'), 'success']
|
||||
: [__('Profil mis à jour.'), 'success'];
|
||||
|
||||
return redirect()->route('Shop.Customers.edit')->with('growl', $growl);
|
||||
}
|
||||
|
||||
protected function handlePasswordChange(Request $request)
|
||||
{
|
||||
if (! $request->filled('new-password')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$customer = Customers::get(Customers::getId());
|
||||
|
||||
if (! $customer) {
|
||||
return __('Impossible de modifier le mot de passe.');
|
||||
}
|
||||
|
||||
if (! Hash::check($request->input('current-password'), $customer->password)) {
|
||||
return __('Le mot de passe actuel est incorrect.');
|
||||
}
|
||||
|
||||
if ($request->input('new-password') !== $request->input('new-password_confirmation')) {
|
||||
return __('Les mots de passe ne correspondent pas.');
|
||||
}
|
||||
|
||||
$newPassword = $request->input('new-password');
|
||||
|
||||
if (strlen($newPassword) < 8) {
|
||||
return __('Le mot de passe doit contenir au moins 8 caractères.');
|
||||
}
|
||||
|
||||
if (! preg_match('/[a-z]/', $newPassword)) {
|
||||
return __('Le mot de passe doit contenir au moins une lettre minuscule.');
|
||||
}
|
||||
|
||||
if (! preg_match('/[A-Z]/', $newPassword)) {
|
||||
return __('Le mot de passe doit contenir au moins une lettre majuscule.');
|
||||
}
|
||||
|
||||
if (! preg_match('/[0-9]/', $newPassword)) {
|
||||
return __('Le mot de passe doit contenir au moins un chiffre.');
|
||||
}
|
||||
|
||||
if (! preg_match('/[^A-Za-z0-9]/', $newPassword)) {
|
||||
return __('Le mot de passe doit contenir au moins un caractère spécial.');
|
||||
}
|
||||
|
||||
$customer->password = Hash::make($request->input('new-password'));
|
||||
$customer->save();
|
||||
|
||||
return null;
|
||||
return redirect()->route('Shop.Customers.edit');
|
||||
}
|
||||
|
||||
public function storeAddress(Request $request)
|
||||
@@ -245,7 +171,6 @@ class CustomerController extends Controller
|
||||
$html = view('Shop.Customers.partials.address_item', [
|
||||
'address' => $address->toArray(),
|
||||
'prefix' => $prefix,
|
||||
'inputName' => $request->input('input_name'),
|
||||
'with_name' => true,
|
||||
'selected' => $address->id,
|
||||
])->render();
|
||||
|
||||
@@ -9,7 +9,6 @@ use App\Repositories\Core\User\ShopCart;
|
||||
use App\Repositories\Shop\Baskets;
|
||||
use App\Repositories\Shop\Contents;
|
||||
use App\Repositories\Shop\Customers;
|
||||
use App\Repositories\Shop\CustomerAddresses;
|
||||
use App\Repositories\Shop\Deliveries;
|
||||
use App\Repositories\Shop\DeliveryTypes;
|
||||
use App\Repositories\Shop\OrderMails;
|
||||
@@ -58,21 +57,8 @@ class OrderController extends Controller
|
||||
$deliveries = $deliveries ? $deliveries->values() : collect();
|
||||
|
||||
$customerData = $customer ? $customer->toArray() : false;
|
||||
if ($customerData) {
|
||||
$customerData['delivery_address_id'] = optional(CustomerAddresses::getDeliveryAddress($customerId))->id;
|
||||
$customerData['invoice_address_id'] = optional(CustomerAddresses::getInvoiceAddress($customerId))->id;
|
||||
|
||||
if (! $customerData['delivery_address_id'] && ! empty($customerData['delivery_addresses'])) {
|
||||
$customerData['delivery_address_id'] = $customerData['delivery_addresses'][0]['id'] ?? null;
|
||||
}
|
||||
|
||||
if (! $customerData['invoice_address_id'] && ! empty($customerData['invoice_addresses'])) {
|
||||
$customerData['invoice_address_id'] = $customerData['invoice_addresses'][0]['id'] ?? null;
|
||||
}
|
||||
|
||||
if ($defaultSaleChannelId) {
|
||||
$customerData['default_sale_channel_id'] = $defaultSaleChannelId;
|
||||
}
|
||||
if ($customerData && $defaultSaleChannelId) {
|
||||
$customerData['default_sale_channel_id'] = $defaultSaleChannelId;
|
||||
}
|
||||
|
||||
$data = [
|
||||
@@ -102,9 +88,7 @@ class OrderController extends Controller
|
||||
}
|
||||
OrderMails::sendOrderConfirmed($order->id);
|
||||
|
||||
return redirect()->route('Shop.Orders.confirmed', [
|
||||
'payment_type' => $data['payment_type'],
|
||||
]);
|
||||
return redirect()->route('Shop.Orders.confirmed');
|
||||
}
|
||||
|
||||
return view('Shop.Orders.order');
|
||||
@@ -113,18 +97,9 @@ class OrderController extends Controller
|
||||
public function confirmed()
|
||||
{
|
||||
ShopCart::clear();
|
||||
$paymentType = request('payment_type');
|
||||
$content = Contents::getOrderConfirmedContent();
|
||||
$paymentLabel = match ($paymentType) {
|
||||
'2' => 'chèque',
|
||||
'3' => 'virement',
|
||||
default => null,
|
||||
};
|
||||
|
||||
return view('Shop.Orders.confirmed', [
|
||||
'content' => $content,
|
||||
'payment_label' => $paymentLabel,
|
||||
]);
|
||||
return view('Shop.Orders.confirmed', ['content' => $content]);
|
||||
}
|
||||
|
||||
public function getPdf($uuid)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Http\Requests\Admin\Shop;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreArticlePost extends FormRequest
|
||||
{
|
||||
@@ -14,13 +13,8 @@ class StoreArticlePost extends FormRequest
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$articleId = $this->input('id');
|
||||
|
||||
return [
|
||||
'ref' => [
|
||||
'required',
|
||||
Rule::unique('shop_articles', 'ref')->ignore($articleId)->whereNull('deleted_at'),
|
||||
],
|
||||
'ref' => 'required|unique:shop_articles',
|
||||
'product_type' => 'required',
|
||||
'product_id' => 'required',
|
||||
'article_nature_id' => 'required',
|
||||
|
||||
@@ -20,7 +20,6 @@ class RegisterCustomer extends FormRequest
|
||||
'last_name' => 'required|max:255',
|
||||
'first_name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255|unique:shop_customers,email,NULL,id,deleted_at,NULL',
|
||||
'phone' => 'required|max:30',
|
||||
'password' => ['required', 'confirmed', new Password()],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Core\Mail\MailTemplate;
|
||||
use App\Repositories\Shop\Orders;
|
||||
use App\Repositories\Shop\Traits\MailCustomers;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
@@ -33,44 +32,29 @@ class ConfirmationCommande extends TemplateMailable
|
||||
|
||||
public $facturation_ville;
|
||||
|
||||
public $livraison_nom;
|
||||
|
||||
public $livraison_adresse;
|
||||
|
||||
public $livraison_adresse2;
|
||||
|
||||
public $livraison_cp;
|
||||
|
||||
public $livraison_ville;
|
||||
|
||||
public $facturation_nom;
|
||||
|
||||
public $facturation_adresse2;
|
||||
|
||||
public $mode_paiement;
|
||||
|
||||
protected static $templateModelClass = MailTemplate::class;
|
||||
|
||||
public function __construct($order)
|
||||
{
|
||||
$facturation_address = $order->invoice->address;
|
||||
$facturation_address = $order->invoice->address;
|
||||
$delivery_address = $order->delivery_address;
|
||||
$this->prenom = $order->customer->first_name;
|
||||
$this->nom = $order->customer->last_name;
|
||||
$this->facturation_nom = $facturation_address->name;
|
||||
$this->facturation_adresse = $facturation_address->address;
|
||||
$this->facturation_adresse2 = $facturation_address->address2;
|
||||
$this->facturation_cp = $facturation_address->zipcode;
|
||||
$this->facturation_ville = $facturation_address->city;
|
||||
$this->livraison_nom = $delivery_address->name;
|
||||
$this->livraison_adresse = $delivery_address->address;
|
||||
$this->livraison_adresse2 = $delivery_address->address2;
|
||||
$this->livraison_cp = $delivery_address->zipcode;
|
||||
$this->livraison_ville = $delivery_address->city;
|
||||
$this->societe = $order->customer->company;
|
||||
$this->email = $order->customer->email;
|
||||
$this->numero_commande = $order->ref;
|
||||
$this->date_commande = $order->created_at;
|
||||
$this->mode_paiement = Orders::getPaymentType($order->payment_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
if (config('app.env') === 'production') {
|
||||
\URL::forceScheme('https');
|
||||
}
|
||||
Schema::defaultStringLength(191);
|
||||
View::composer('Shop.layout.layout', LayoutComposer::class);
|
||||
}
|
||||
|
||||
@@ -65,9 +65,6 @@ class Varieties
|
||||
{
|
||||
$images = $data['images'] ?? false;
|
||||
$tags = $data['tags'] ?? false;
|
||||
if (! array_key_exists('plus', $data) || $data['plus'] === null) {
|
||||
$data['plus'] = '';
|
||||
}
|
||||
unset($data['images']);
|
||||
unset($data['tags']);
|
||||
$variety = self::store($data);
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Repositories\Core;
|
||||
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media as MediaModel;
|
||||
|
||||
class Medias
|
||||
{
|
||||
public static function getImage($model, $conversion = 'normal', $collection = 'images')
|
||||
@@ -81,9 +79,13 @@ class Medias
|
||||
|
||||
public static function getImageSrc($image)
|
||||
{
|
||||
$media = self::resolveMedia($image);
|
||||
if (! $image) {
|
||||
return null;
|
||||
}
|
||||
$id = $image['id'];
|
||||
$filename = self::getFilename($image);
|
||||
|
||||
return $media ? $media->getUrl() : null;
|
||||
return "/storage/{$id}/{$filename}";
|
||||
}
|
||||
|
||||
public static function getThumbSrc($image)
|
||||
@@ -108,12 +110,6 @@ class Medias
|
||||
|
||||
public static function getSrcByType($image, $type)
|
||||
{
|
||||
$media = self::resolveMedia($image);
|
||||
|
||||
if ($media) {
|
||||
return $type ? $media->getUrl($type) : $media->getUrl();
|
||||
}
|
||||
|
||||
return $image ? '/storage/'.$image['id'].'/conversions/'.self::getFilename($image, $type) : false;
|
||||
}
|
||||
|
||||
@@ -128,48 +124,4 @@ class Medias
|
||||
{
|
||||
return str_replace(['#', '/', '\\', ' '], '-', $name);
|
||||
}
|
||||
|
||||
protected static function resolveMedia($image): ?MediaModel
|
||||
{
|
||||
if ($image instanceof MediaModel) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
if (is_null($image)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_array($image)) {
|
||||
return self::hydrateMedia($image);
|
||||
}
|
||||
|
||||
if (is_object($image)) {
|
||||
if ($image instanceof \ArrayAccess) {
|
||||
return self::hydrateMedia((array) $image);
|
||||
}
|
||||
|
||||
$array = method_exists($image, 'toArray') ? $image->toArray() : (array) $image;
|
||||
|
||||
return self::hydrateMedia($array);
|
||||
}
|
||||
|
||||
$id = data_get($image, 'id');
|
||||
|
||||
return $id ? MediaModel::query()->withoutGlobalScopes()->find($id) : null;
|
||||
}
|
||||
|
||||
protected static function hydrateMedia(array $attributes): ?MediaModel
|
||||
{
|
||||
$id = data_get($attributes, 'id');
|
||||
|
||||
if (! $id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$media = new MediaModel();
|
||||
$media->forceFill($attributes);
|
||||
$media->exists = true;
|
||||
|
||||
return $media;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,7 @@ class ArticleImages
|
||||
|
||||
public static function getFullImagesByArticle($article)
|
||||
{
|
||||
if (! $article) {
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
$images = count($article->images ?? []) ? $article->images : collect([]);
|
||||
$images = count($article->images) ? $article->images : collect([]);
|
||||
switch ($article->product_type) {
|
||||
case 'App\Models\Botanic\Variety':
|
||||
$variety = $article->product ?? false;
|
||||
|
||||
@@ -27,34 +27,21 @@ class ArticleTags
|
||||
|
||||
switch ($article->product_type) {
|
||||
case 'App\Models\Botanic\Variety':
|
||||
$variety = $article->product;
|
||||
if ($variety && $variety->tags) {
|
||||
$data = array_merge($data, $variety->tags->toArray());
|
||||
}
|
||||
if ($variety && $variety->specie && $variety->specie->tags) {
|
||||
$data = array_merge($data, $variety->specie->tags->toArray());
|
||||
$data += $article->product->tags->toArray();
|
||||
if ($article->product->specie ?? false) {
|
||||
$data += $article->product->specie->tags->toArray();
|
||||
}
|
||||
break;
|
||||
case 'App\Models\Botanic\Specie':
|
||||
$specie = $article->product;
|
||||
if ($specie && $specie->tags) {
|
||||
$data = array_merge($data, $specie->tags->toArray());
|
||||
}
|
||||
$data += $article->product->tags->toArray();
|
||||
break;
|
||||
case 'App\Models\Shop\Merchandise':
|
||||
$merchandise = $article->product;
|
||||
if ($merchandise && $merchandise->tags) {
|
||||
$data = array_merge($data, $merchandise->tags->toArray());
|
||||
}
|
||||
if ($merchandise && $merchandise->producer && $merchandise->producer->tags) {
|
||||
$data = array_merge($data, $merchandise->producer->tags->toArray());
|
||||
}
|
||||
$data += $article->product->tags->toArray();
|
||||
$data += $article->product->producer->tags->toArray();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
if ($article->tags) {
|
||||
$data = array_merge($data, $article->tags->toArray());
|
||||
}
|
||||
$data += $article->tags->toArray();
|
||||
|
||||
foreach ($data as $tag) {
|
||||
if (! isset($tags[$tag['group']][$tag['name']])) {
|
||||
|
||||
@@ -19,7 +19,7 @@ class Articles
|
||||
|
||||
public static function autocomplete($str)
|
||||
{
|
||||
$data = Article::byAutocomplete($str)->visible()->orderBy('name')->limit(20)->pluck('name', 'id');
|
||||
$data = Article::byAutocomplete($str)->orderBy('name')->limit(20)->pluck('name', 'id');
|
||||
$export = [];
|
||||
foreach ($data as $key => $name) {
|
||||
$export[] = ['value' => $key, 'text' => $name];
|
||||
@@ -135,11 +135,8 @@ class Articles
|
||||
$data['specie'] = $article->product ? $article->product->description : '';
|
||||
break;
|
||||
case 'App\Models\Shop\Merchandise':
|
||||
$merchandise = $article->product;
|
||||
$data['merchandise'] = $merchandise ? ($merchandise->description ?? '') : '';
|
||||
if ($merchandise && $merchandise->producer) {
|
||||
$data['producer'] = $merchandise->producer->description ?? '';
|
||||
}
|
||||
$data['merchandise'] = $article->product ? $article->product->description : '';
|
||||
$data['producer'] = $article->product->producer->description;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
@@ -178,18 +175,10 @@ class Articles
|
||||
$articles = self::getArticlesWithOffers($options);
|
||||
$searchOrder = $options['ids'] ?? false ? array_flip($options['ids']->toArray()) : false;
|
||||
foreach ($articles as $article) {
|
||||
// Skip articles without an offer/tariff/price list for the resolved sale channel
|
||||
if (!isset($article->offers[0]) || ! $article->offers[0]->tariff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$price_lists = $article->offers[0]->tariff->price_lists->toArray();
|
||||
if (! count($price_lists)) {
|
||||
continue;
|
||||
}
|
||||
if (empty($price_lists[0]['price_list_values'][0] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
if (! is_array($data[$article->name] ?? false)) {
|
||||
$data[$article->name] = self::getDataForSale($article);
|
||||
if ($searchOrder) {
|
||||
@@ -204,7 +193,7 @@ class Articles
|
||||
ksort($data);
|
||||
}
|
||||
|
||||
return $data ?? [];
|
||||
return $data ?? false;
|
||||
}
|
||||
|
||||
public static function getDataForSale($article)
|
||||
|
||||
@@ -41,16 +41,6 @@ class Contents
|
||||
return self::get(5)->text ?? 'Votre commande a été confirmée';
|
||||
}
|
||||
|
||||
public static function getOrderConfirmedByCheckContent()
|
||||
{
|
||||
return self::get(10)->text ?? 'Votre commande a bien été enregistrée, elle vous sera expédiée dès réception de votre chèque.';
|
||||
}
|
||||
|
||||
public static function getOrderConfirmedByWireContent()
|
||||
{
|
||||
return self::get(11)->text ?? 'Votre commande a bien été enregistrée, elle vous sera expédiée dès réception de votre virement.';
|
||||
}
|
||||
|
||||
public static function getPayboxConfirmedContent()
|
||||
{
|
||||
return self::get(6)->text ?? 'Merci pour votre règlement. Votre commande sera traitée sous peu.';
|
||||
|
||||
@@ -19,7 +19,7 @@ class InvoicePDF
|
||||
$invoice = Invoices::getFull($id);
|
||||
$customFields = [];
|
||||
if ($orderRef = optional($invoice->order)->ref) {
|
||||
$customFields['Numéro de commande'] = $orderRef;
|
||||
$customFields['order number'] = $orderRef;
|
||||
}
|
||||
|
||||
$customer = new Party([
|
||||
@@ -61,7 +61,7 @@ class InvoicePDF
|
||||
trim(($address->zipcode ?? '').' '.($address->city ?? '')),
|
||||
]);
|
||||
|
||||
return implode("\n", $lines);
|
||||
return implode('<br>', $lines);
|
||||
}
|
||||
|
||||
public static function makeItems($details)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Repositories\Shop;
|
||||
|
||||
use App\Models\Shop\Offer;
|
||||
use App\Models\Shop\PriceList;
|
||||
use App\Models\Shop\PriceListValue;
|
||||
use App\Models\Shop\SaleChannel;
|
||||
use App\Traits\Model\Basic;
|
||||
@@ -186,44 +185,20 @@ class Offers
|
||||
->with([
|
||||
'article',
|
||||
'tariff:id,status_id',
|
||||
'variation',
|
||||
])
|
||||
->get();
|
||||
|
||||
return $channels->map(function ($channel) use ($offers) {
|
||||
$priceValue = null;
|
||||
$candidateOffer = null;
|
||||
$allOffersForChannel = [];
|
||||
|
||||
foreach ($offers as $offer) {
|
||||
$priceCandidate = self::getPrice($offer->id, 1, $channel->id);
|
||||
|
||||
if ($priceCandidate && (float) $priceCandidate->price_taxed > 0) {
|
||||
// Get price list name
|
||||
$priceListName = null;
|
||||
if ($priceCandidate) {
|
||||
$priceListModel = PriceList::find($priceCandidate->price_list_id);
|
||||
$priceListName = $priceListModel ? $priceListModel->name : null;
|
||||
}
|
||||
|
||||
// Collect all offers with their details
|
||||
$allOffersForChannel[] = [
|
||||
'id' => $offer->id,
|
||||
'variation_name' => $offer->variation ? $offer->variation->name : null,
|
||||
'stock_current' => (int) $offer->stock_current,
|
||||
'status_id' => (int) $offer->status_id,
|
||||
'is_active' => (int) $offer->status_id === 1,
|
||||
'tariff_id' => $offer->tariff_id ? (int) $offer->tariff_id : null,
|
||||
'price_taxed' => (float) $priceCandidate->price_taxed,
|
||||
'quantity' => (int) $priceCandidate->quantity,
|
||||
'price_list_name' => $priceListName,
|
||||
];
|
||||
|
||||
// Keep first valid offer as the main candidate
|
||||
if (!$candidateOffer) {
|
||||
$priceValue = $priceCandidate;
|
||||
$candidateOffer = $offer;
|
||||
}
|
||||
$priceValue = $priceCandidate;
|
||||
$candidateOffer = $offer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +209,6 @@ class Offers
|
||||
$offerHasStock = $candidateOffer && $candidateOffer->stock_current !== null
|
||||
? (float) $candidateOffer->stock_current > 0
|
||||
: null;
|
||||
$offerTariffId = $candidateOffer && $candidateOffer->tariff_id ? (int) $candidateOffer->tariff_id : null;
|
||||
|
||||
return [
|
||||
'id' => $channel->id,
|
||||
@@ -247,8 +221,6 @@ class Offers
|
||||
'offer_stock_current' => $offerStock,
|
||||
'offer_has_stock' => $offerHasStock,
|
||||
'tariff_status_id' => $offerTariffStatus,
|
||||
'tariff_id' => $offerTariffId,
|
||||
'all_offers' => $allOffersForChannel,
|
||||
];
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
@@ -46,34 +46,12 @@ class PriceListValues
|
||||
{
|
||||
foreach ($values as $value) {
|
||||
$value['price_list_id'] = $price_list_id;
|
||||
if (self::hasPrice($value)) {
|
||||
if ($value['price']) {
|
||||
self::store($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function purgeRemovedValues($price_list_id, array $ids)
|
||||
{
|
||||
if (! count($ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PriceListValue::byPriceList($price_list_id)
|
||||
->whereIn('id', $ids)
|
||||
->delete();
|
||||
}
|
||||
|
||||
protected static function hasPrice($value): bool
|
||||
{
|
||||
if (! array_key_exists('price', $value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$price = $value['price'];
|
||||
|
||||
return $price !== null && $price !== '';
|
||||
}
|
||||
|
||||
public static function getModel()
|
||||
{
|
||||
return PriceListValue::query();
|
||||
|
||||
@@ -17,7 +17,7 @@ class PriceLists
|
||||
'taxes' => Taxes::getOptions(),
|
||||
'price_list' => [
|
||||
'tariff_id' => $tariffId,
|
||||
'price_list_values' => [],
|
||||
'price_list_values' => array_fill(0, 3, ''),
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -50,8 +50,9 @@ class PriceLists
|
||||
public static function edit($id)
|
||||
{
|
||||
$price_list = self::getFull($id)->toArray();
|
||||
if (count($price_list['price_list_values']) === 0) {
|
||||
$price_list['price_list_values'][] = [];
|
||||
$n = count($price_list['price_list_values']);
|
||||
if ($n <= 3) {
|
||||
$price_list['price_list_values'] += array_fill($n, 3 - $n, '');
|
||||
}
|
||||
|
||||
return $price_list;
|
||||
@@ -70,14 +71,9 @@ class PriceLists
|
||||
public static function store($data)
|
||||
{
|
||||
$id = $data['id'] ?? false;
|
||||
$price_list_values = $data['price_list_values'] ?? [];
|
||||
$deleted_values = array_map('intval', array_filter($data['deleted_price_list_value_ids'] ?? [], function ($value) {
|
||||
return $value !== null && $value !== '';
|
||||
}));
|
||||
$price_list_values = $data['price_list_values'] ?? false;
|
||||
unset($data['price_list_values']);
|
||||
unset($data['deleted_price_list_value_ids']);
|
||||
$price_list = $id ? self::update($data) : self::create($data);
|
||||
PriceListValues::purgeRemovedValues($price_list->id, $deleted_values);
|
||||
PriceListValues::storePrices($price_list->id, $price_list_values);
|
||||
|
||||
return $price_list;
|
||||
|
||||
@@ -49,7 +49,7 @@ class SaleChannels
|
||||
}
|
||||
}
|
||||
|
||||
return self::getByCode('POSTE');
|
||||
return self::getByCode('EXP');
|
||||
}
|
||||
|
||||
public static function getByCode($code)
|
||||
|
||||
@@ -8,14 +8,8 @@ class Searches
|
||||
{
|
||||
public static function search($options)
|
||||
{
|
||||
// Get article IDs from Scout search
|
||||
$searchResults = Article::search($options['search_name'])->get()->pluck('id');
|
||||
|
||||
// Filter to only include visible articles
|
||||
$visibleArticleIds = Article::whereIn('id', $searchResults)->visible()->pluck('id');
|
||||
|
||||
return collect(Articles::getArticlesToSell([
|
||||
'ids' => $visibleArticleIds,
|
||||
'ids' => Article::search($options['search_name'])->get()->pluck('id'),
|
||||
]))->sortBy('searchOrder')->toArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Default configuration group for Gravatar
|
||||
'default' => [
|
||||
// --- The default avatar size
|
||||
'size' => 80,
|
||||
// --- The default avatar size
|
||||
'size' => 80,
|
||||
|
||||
// --- The default avatar to display if we have no results
|
||||
// (bool) false
|
||||
// (string) 404
|
||||
// (string) mm: (mystery-man) a simple, cartoon-style silhouetted outline of a person (does not vary by email hash).
|
||||
// (string) identicon: a geometric pattern based on an email hash.
|
||||
// (string) monsterid: a generated 'monster' with different colors, faces, etc.
|
||||
// (string) wavatar: generated faces with differing features and backgrounds.
|
||||
// (string) retro: awesome generated, 8-bit arcade-style pixelated faces.
|
||||
'fallback' => 'identicon',
|
||||
// --- The default avatar to display if we have no results
|
||||
// (bool) false
|
||||
// (string) 404
|
||||
// (string) mm: (mystery-man) a simple, cartoon-style silhouetted outline of a person (does not vary by email hash).
|
||||
// (string) identicon: a geometric pattern based on an email hash.
|
||||
// (string) monsterid: a generated 'monster' with different colors, faces, etc.
|
||||
// (string) wavatar: generated faces with differing features and backgrounds.
|
||||
// (string) retro: awesome generated, 8-bit arcade-style pixelated faces.
|
||||
'default' => 'identicon',
|
||||
|
||||
// --- Whether to use HTTPS protocol
|
||||
'secure' => false,
|
||||
|
||||
// --- Set the type of avatars we allow to show
|
||||
// - g: suitable for display on all websites with any audience type.
|
||||
// - pg: may contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence.
|
||||
// - r: may contain such things as harsh profanity, intense violence, nudity, or hard drug use.
|
||||
// - x: may contain hardcore sexual imagery or extremely disturbing violence.
|
||||
'maximumRating' => 'g',
|
||||
|
||||
// --- Force default image to always display
|
||||
'forceDefault' => false,
|
||||
|
||||
// --- Optional file extension appended to URL
|
||||
'forceExtension' => 'jpg',
|
||||
]
|
||||
// --- Set the type of avatars we allow to show
|
||||
// - g: suitable for display on all websites with any audience type.
|
||||
// - pg: may contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence.
|
||||
// - r: may contain such things as harsh profanity, intense violence, nudity, or hard drug use.
|
||||
// - x: may contain hardcore sexual imagery or extremely disturbing violence.
|
||||
'maxRating' => 'g',
|
||||
];
|
||||
|
||||
@@ -126,7 +126,7 @@ return [
|
||||
* The path where to store temporary files while performing image conversions.
|
||||
* If set to null, storage_path('media-library/temp') will be used.
|
||||
*/
|
||||
'temporary_directory_path' => env('MEDIA_LIBRARY_TEMP_PATH', storage_path('media-library/temp')),
|
||||
'temporary_directory_path' => null,
|
||||
|
||||
/*
|
||||
* The engine that should perform the image conversions.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('shop_articles', function (Blueprint $table) {
|
||||
$table->dropUnique('ref');
|
||||
$table->unique(['ref', 'deleted_at'], 'shop_articles_ref_deleted_at_unique');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('shop_articles', function (Blueprint $table) {
|
||||
$table->dropUnique('shop_articles_ref_deleted_at_unique');
|
||||
$table->unique('ref');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
DB::table('shop_contents')->insert([
|
||||
[
|
||||
'id' => 10,
|
||||
'text' => '<p>Votre commande a bien été enregistrée, elle vous sera expédiée dès réception de votre chèque.</p><p class="mt-3 text-warning"><i class="fa fa-exclamation-triangle mr-1"></i> Sans réception de votre paiement au bout de 30 jours, votre commande sera annulée.</p>',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'id' => 11,
|
||||
'text' => '<p>Votre commande a bien été enregistrée, elle vous sera expédiée dès réception de votre virement.</p><p class="mt-3 text-warning"><i class="fa fa-exclamation-triangle mr-1"></i> Sans réception de votre paiement au bout de 30 jours, votre commande sera annulée.</p>',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
DB::table('shop_contents')->whereIn('id', [10, 11])->delete();
|
||||
}
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$this->transformTemplate(function ($html) {
|
||||
// Replace hardcoded "Carte de crédit" with the template variable
|
||||
$html = str_replace(
|
||||
'Carte de crédit',
|
||||
'{{mode_paiement}}',
|
||||
$html
|
||||
);
|
||||
|
||||
// Add address2 to delivery address
|
||||
$html = str_replace(
|
||||
'{{livraison_adresse}}<br />{{livraison_cp}} {{livraison_ville}}',
|
||||
'{{livraison_adresse}}{{#livraison_adresse2}}<br />{{livraison_adresse2}}{{/livraison_adresse2}}<br />{{livraison_cp}} {{livraison_ville}}',
|
||||
$html
|
||||
);
|
||||
|
||||
// Add address2 to billing address
|
||||
$html = str_replace(
|
||||
'{{facturation_adresse}}<br />{{facturation_cp}} {{facturation_ville}}',
|
||||
'{{facturation_adresse}}{{#facturation_adresse2}}<br />{{facturation_adresse2}}{{/facturation_adresse2}}<br />{{facturation_cp}} {{facturation_ville}}',
|
||||
$html
|
||||
);
|
||||
|
||||
return $html;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
$this->transformTemplate(function ($html) {
|
||||
$html = str_replace(
|
||||
'{{mode_paiement}}',
|
||||
'Carte de crédit',
|
||||
$html
|
||||
);
|
||||
|
||||
$html = str_replace(
|
||||
'{{livraison_adresse}}{{#livraison_adresse2}}<br />{{livraison_adresse2}}{{/livraison_adresse2}}<br />{{livraison_cp}} {{livraison_ville}}',
|
||||
'{{livraison_adresse}}<br />{{livraison_cp}} {{livraison_ville}}',
|
||||
$html
|
||||
);
|
||||
|
||||
$html = str_replace(
|
||||
'{{facturation_adresse}}{{#facturation_adresse2}}<br />{{facturation_adresse2}}{{/facturation_adresse2}}<br />{{facturation_cp}} {{facturation_ville}}',
|
||||
'{{facturation_adresse}}<br />{{facturation_cp}} {{facturation_ville}}',
|
||||
$html
|
||||
);
|
||||
|
||||
return $html;
|
||||
});
|
||||
}
|
||||
|
||||
private function transformTemplate(callable $transform): void
|
||||
{
|
||||
$template = DB::table('mail_templates')
|
||||
->where('mailable', 'App\\Mail\\ConfirmationCommande')
|
||||
->first();
|
||||
|
||||
if (! $template) {
|
||||
return;
|
||||
}
|
||||
|
||||
$translations = json_decode($template->html_template, true);
|
||||
|
||||
foreach ($translations as $lang => $html) {
|
||||
$translations[$lang] = $transform($html);
|
||||
}
|
||||
|
||||
DB::table('mail_templates')
|
||||
->where('id', $template->id)
|
||||
->update(['html_template' => json_encode($translations, JSON_UNESCAPED_UNICODE)]);
|
||||
}
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$this->transformTemplate(function ($html) {
|
||||
// Add name before delivery address
|
||||
$html = str_replace(
|
||||
'{{livraison_adresse}}',
|
||||
'{{#livraison_nom}}{{livraison_nom}}<br />{{/livraison_nom}}{{livraison_adresse}}',
|
||||
$html
|
||||
);
|
||||
|
||||
// Add name before billing address
|
||||
$html = str_replace(
|
||||
'{{facturation_adresse}}',
|
||||
'{{#facturation_nom}}{{facturation_nom}}<br />{{/facturation_nom}}{{facturation_adresse}}',
|
||||
$html
|
||||
);
|
||||
|
||||
return $html;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
$this->transformTemplate(function ($html) {
|
||||
$html = str_replace(
|
||||
'{{#livraison_nom}}{{livraison_nom}}<br />{{/livraison_nom}}{{livraison_adresse}}',
|
||||
'{{livraison_adresse}}',
|
||||
$html
|
||||
);
|
||||
|
||||
$html = str_replace(
|
||||
'{{#facturation_nom}}{{facturation_nom}}<br />{{/facturation_nom}}{{facturation_adresse}}',
|
||||
'{{facturation_adresse}}',
|
||||
$html
|
||||
);
|
||||
|
||||
return $html;
|
||||
});
|
||||
}
|
||||
|
||||
private function transformTemplate(callable $transform): void
|
||||
{
|
||||
$template = DB::table('mail_templates')
|
||||
->where('mailable', 'App\\Mail\\ConfirmationCommande')
|
||||
->first();
|
||||
|
||||
if (! $template) {
|
||||
return;
|
||||
}
|
||||
|
||||
$translations = json_decode($template->html_template, true);
|
||||
|
||||
foreach ($translations as $lang => $html) {
|
||||
$translations[$lang] = $transform($html);
|
||||
}
|
||||
|
||||
DB::table('mail_templates')
|
||||
->where('id', $template->id)
|
||||
->update(['html_template' => json_encode($translations, JSON_UNESCAPED_UNICODE)]);
|
||||
}
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
private $oldSrc = '/storage/photos/shares/logo.png';
|
||||
|
||||
private $newSrc = 'https://boutique.jardinenvie.com/img/logo.png';
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$this->replaceLogoInAllTemplates($this->oldSrc, $this->newSrc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
$this->replaceLogoInAllTemplates($this->newSrc, $this->oldSrc);
|
||||
}
|
||||
|
||||
private function replaceLogoInAllTemplates(string $from, string $to): void
|
||||
{
|
||||
$templates = DB::table('mail_templates')->get();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
$translations = json_decode($template->html_template, true);
|
||||
|
||||
if (! $translations) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$changed = false;
|
||||
|
||||
foreach ($translations as $lang => $html) {
|
||||
$updated = str_replace($from, $to, $html);
|
||||
if ($updated !== $html) {
|
||||
$translations[$lang] = $updated;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
DB::table('mail_templates')
|
||||
->where('id', $template->id)
|
||||
->update(['html_template' => json_encode($translations, JSON_UNESCAPED_UNICODE)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'confirmdelete' => 'Do you confirm the deletion?',
|
||||
'deletesuccess' => 'Deleted successfully.',
|
||||
'mail_the_selection' => 'Mail the selection',
|
||||
'mail_the_complete_list' => 'Mail the complete list',
|
||||
];
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'confirmdelete' => 'Confirmez-vous la suppression ?',
|
||||
'deletesuccess' => 'La suppression a été effectuée.',
|
||||
'mail_the_selection' => 'Envoyer la sélection',
|
||||
'mail_the_complete_list' => 'Envoyer toute la liste',
|
||||
];
|
||||
|
||||
@@ -112,33 +112,3 @@ body {
|
||||
.bg-darker {
|
||||
background-color: rgba(0,0,0,0.05)!important;
|
||||
}
|
||||
|
||||
/* Header action buttons aligned with page title */
|
||||
.content-header .form-buttons {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.content-header .form-buttons .btn {
|
||||
height: 32px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.content-header .form-buttons {
|
||||
margin-left: 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.content-header .form-buttons .btn {
|
||||
height: 28px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,52 +311,6 @@ div.megamenu ul.megamenu li.megamenu.level1
|
||||
}
|
||||
|
||||
|
||||
.category-title {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.category-description {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.breadcrumb-title {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
.breadcrumb-current {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px){
|
||||
.category-title {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.category-description {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
.breadcrumb-title {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
.breadcrumb-current {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px){
|
||||
.category-title {
|
||||
font-size: 1.35em;
|
||||
}
|
||||
.category-description {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
.breadcrumb-title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.breadcrumb-current {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'noto_sanscondensed';
|
||||
src: url('/fonts/notosans-condensed/notosans-condensed-webfont.eot');
|
||||
@@ -394,45 +348,4 @@ div.megamenu ul.megamenu li.megamenu.level1
|
||||
.dropdown-menu > li:hover > .submenu{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px){
|
||||
#navbarContentMobile {
|
||||
max-height: calc(100vh - 60px);
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
#navbarContentMobile .navbar-nav {
|
||||
flex-direction: column;
|
||||
}
|
||||
#navbarContentMobile .navbar-nav .col {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
#navbarContentMobile .dropdown-menu {
|
||||
display: block;
|
||||
position: static;
|
||||
float: none;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
#navbarContentMobile .dropdown-toggle::after {
|
||||
display: none;
|
||||
}
|
||||
#navbarContentMobile .dropdown-menu .container,
|
||||
#navbarContentMobile .dropdown-menu .row {
|
||||
margin: 0;
|
||||
}
|
||||
#navbarContentMobile .dropdown-menu .shadow {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.category-card .card-body {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Supprimer les grandes marges du container en affichage mobile/tablette */
|
||||
.container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,14 +32,12 @@ $(document).on('click', '.dropdown-menu', function (e) {
|
||||
// make it as accordion for smaller screens
|
||||
if ($(window).width() < 992) {
|
||||
$('.dropdown-menu a').click(function(e) {
|
||||
var $submenu = $(this).next('.submenu');
|
||||
if ($submenu.length) {
|
||||
e.preventDefault();
|
||||
$submenu.toggle();
|
||||
e.preventDefault();
|
||||
if ($(this).next('.submenu').length) {
|
||||
$(this).next('.submenu').toggle();
|
||||
}
|
||||
});
|
||||
|
||||
$('.dropdown').on('hide.bs.dropdown', function () {
|
||||
$(this).find('.submenu').hide();
|
||||
$('.dropdown').on('hide.bs.dropdown', function () {
|
||||
$(this).find('.submenu').hide();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,5 @@
|
||||
])
|
||||
|
||||
@section('content')
|
||||
@include('Admin.Shop.Articles.form', [
|
||||
'cancel_url' => route('Admin.Shop.Articles.index'),
|
||||
])
|
||||
@include('Admin.Shop.Articles.form')
|
||||
@endsection
|
||||
|
||||
@@ -5,13 +5,5 @@
|
||||
])
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$duplicateUrl = \Route::has('Admin.Shop.Articles.duplicate')
|
||||
? route('Admin.Shop.Articles.duplicate', $article['id'] ?? null)
|
||||
: null;
|
||||
@endphp
|
||||
@include('Admin.Shop.Articles.form', [
|
||||
'duplicate_url' => $duplicateUrl,
|
||||
'cancel_url' => route('Admin.Shop.Articles.index'),
|
||||
])
|
||||
@include('Admin.Shop.Articles.form')
|
||||
@endsection
|
||||
|
||||
@@ -5,29 +5,10 @@
|
||||
'files' => true,
|
||||
]) }}
|
||||
<input type="hidden" name="id" id="id" value="{{ $article['id'] ?? null }}">
|
||||
|
||||
@php
|
||||
$articlePublicUrl = null;
|
||||
if (!empty($article['slug'] ?? null)) {
|
||||
$articlePublicUrl = route('Shop.Articles.slug', ['slug' => $article['slug']]);
|
||||
} elseif (!empty($article['id'] ?? null)) {
|
||||
$articlePublicUrl = route('Shop.Articles.show', ['id' => $article['id']]);
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if ($articlePublicUrl)
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<a href="{{ $articlePublicUrl }}" class="btn btn-outline-primary" target="_blank" rel="noopener">
|
||||
Voir la page publique
|
||||
<i class="fa fa-external-link"></i>
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@include('Admin.Shop.Articles.partials.characteristics')
|
||||
{{ Form::close() }}
|
||||
|
||||
<x-save :cancel-url="$cancel_url ?? null" :duplicate-url="$duplicate_url ?? null" />
|
||||
<x-save />
|
||||
|
||||
@include('load.form.appender')
|
||||
@include('load.form.editor')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@component('components.layout.box-collapse', [
|
||||
'id' => 'product_description_box',
|
||||
'title' => 'Informations héritées',
|
||||
'collapsed' => $collapsed ?? true,
|
||||
'collapsed' => $collapsed ?? false,
|
||||
])
|
||||
@foreach ($article['inherited'] as $inherited)
|
||||
@component('components.card', ['title' => $inherited['name'], 'class' => 'mb-3'])
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
{{ Form::open(['route' => 'Admin.Shop.Offers.store', 'id' => 'offer-form', 'autocomplete' => 'off']) }}
|
||||
<input type="hidden" name="id" value="{{ $offer['id'] ?? false }}">
|
||||
|
||||
@if (($offer['id'] ?? false) && ($offer['article_id'] ?? false))
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<a href="{{ route('Shop.Articles.show', ['id' => $offer['article_id']]) }}" class="btn btn-outline-primary" target="_blank" rel="noopener">
|
||||
Voir la page publique de l'article
|
||||
<i class="fa fa-external-link"></i>
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="col-8">
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
@include('components.form.select', [
|
||||
@@ -105,6 +96,13 @@
|
||||
</div>
|
||||
@endcomponent
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@component('components.card', ['title' => 'Previsualisation'])
|
||||
<div id="preview-article"></div>
|
||||
<div id="preview-variation"></div>
|
||||
<div id="preview-tariff"></div>
|
||||
@endcomponent
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -119,8 +117,59 @@
|
||||
{!! JsValidator::formRequest('App\Http\Requests\Admin\Shop\StoreOfferPost', '#offer-form') !!}
|
||||
|
||||
<script>
|
||||
function handleArticle() {
|
||||
$('.select_article').change(function() {
|
||||
previewArticle($(this).val());
|
||||
})
|
||||
}
|
||||
|
||||
function previewArticle(id) {
|
||||
var url = '{{ route('Admin.Shop.Offers.previewArticle') }}/' + id;
|
||||
$('#preview-article').load(url, function() {
|
||||
initChevron();
|
||||
});
|
||||
}
|
||||
|
||||
function handleVariation() {
|
||||
$('.select_variation').change(function() {
|
||||
previewVariation($(this).val());
|
||||
})
|
||||
}
|
||||
|
||||
function previewVariation(id) {
|
||||
var url = '{{ route('Admin.Shop.Offers.previewVariation') }}/' + id;
|
||||
$('#preview-variation').load(url, function() {
|
||||
initChevron();
|
||||
});
|
||||
}
|
||||
|
||||
function handleTariff() {
|
||||
$('.select_tariffs').change(function() {
|
||||
previewTariff($(this).val());
|
||||
})
|
||||
}
|
||||
|
||||
function previewTariff(id) {
|
||||
var url = '{{ route('Admin.Shop.Offers.previewTariff') }}/' + id;
|
||||
$('#preview-tariff').load(url, function() {
|
||||
initChevron();
|
||||
});
|
||||
}
|
||||
|
||||
function initPreview() {
|
||||
previewArticle("{{ $offer['article_id'] ?? null }}");
|
||||
previewVariation("{{ $offer['variation_id'] ?? null }}");
|
||||
previewTariff("{{ $offer['tariff_id'] ?? null }}");
|
||||
}
|
||||
|
||||
handleArticle();
|
||||
handleVariation();
|
||||
handleTariff();
|
||||
initChevron();
|
||||
initSaveForm('#offer-form');
|
||||
initSelect2();
|
||||
@if ($offer['id'] ?? false)
|
||||
initPreview();
|
||||
@endif
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@@ -15,9 +15,4 @@
|
||||
<td>
|
||||
@include('components.form.inputs.money', ['name' => 'price_list_values[' . $index . '][price_taxed]', 'value' => $price_list_value['price_taxed'] ?? null, 'required' => true, 'class' => 'price_taxed'])
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
<button type="button" class="btn btn-outline-danger btn-xs remove-price" title="{{ __('Supprimer') }}">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
@@ -30,18 +30,12 @@
|
||||
<th>Unit. HT</th>
|
||||
<th>TVA</th>
|
||||
<th>Unit. TTC</th>
|
||||
<th class="text-center" style="width: 60px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@php($priceListValues = $price_list['price_list_values'] ?? [])
|
||||
@php($nextIndex = count($priceListValues))
|
||||
|
||||
@foreach ($priceListValues as $index => $price_list_value)
|
||||
@include('Admin.Shop.PriceListValues.partials.row_price', ['index' => $index, 'price_list_value' => $price_list_value])
|
||||
@foreach ($price_list['price_list_values'] as $price_list_value)
|
||||
@include('Admin.Shop.PriceListValues.partials.row_price', ['index' => $loop->index])
|
||||
@endforeach
|
||||
|
||||
@include('Admin.Shop.PriceListValues.partials.row_price', ['index' => $nextIndex, 'price_list_value' => []])
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
@@ -55,28 +49,19 @@
|
||||
</tfoot>
|
||||
</table>
|
||||
@endcomponent
|
||||
<div id="deleted-price-list-values"></div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<script>
|
||||
var priceRowIndex = 0;
|
||||
var lastRemovedIndex = null;
|
||||
|
||||
function handleAddPrice() {
|
||||
$('#add_price').click( function () {
|
||||
addEmptyPriceRow();
|
||||
})
|
||||
}
|
||||
|
||||
function addEmptyPriceRow() {
|
||||
var index = nextPriceRowIndex();
|
||||
$.get("{{ route('Admin.Shop.PriceListValues.addPrice') }}/" + index, function(data) {
|
||||
$("#prices-table tbody").append(data);
|
||||
var index = $('#prices-table tbody tr').length;
|
||||
$.get("{{ route('Admin.Shop.PriceListValues.addPrice') }}/" + index, function(data) {
|
||||
$("#prices-table").append(data);
|
||||
})
|
||||
handlePrices();
|
||||
handleRemovePrice();
|
||||
lastRemovedIndex = null;
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function handle_prices() {
|
||||
@@ -116,82 +101,9 @@
|
||||
handle_prices_taxed();
|
||||
}
|
||||
|
||||
function handleRemovePrice() {
|
||||
$('#prices-table').off('click', '.remove-price').on('click', '.remove-price', function() {
|
||||
var $row = $(this).closest('tr');
|
||||
var idx = extractRowIndex($row);
|
||||
var id = $row.find('input[name$="[id]"]').val();
|
||||
if (id) {
|
||||
registerDeletedPrice(id);
|
||||
}
|
||||
$row.remove();
|
||||
lastRemovedIndex = idx;
|
||||
ensureAtLeastOneRow();
|
||||
});
|
||||
}
|
||||
|
||||
function registerDeletedPrice(id) {
|
||||
var $container = $('#deleted-price-list-values');
|
||||
if ($container.find('input[value="' + id + '"]').length === 0) {
|
||||
$container.append('<input type="hidden" name="deleted_price_list_value_ids[]" value="' + id + '">');
|
||||
}
|
||||
}
|
||||
|
||||
function ensureAtLeastOneRow() {
|
||||
if ($('#prices-table tbody tr').length === 0) {
|
||||
if (lastRemovedIndex !== null) {
|
||||
$.get("{{ route('Admin.Shop.PriceListValues.addPrice') }}/" + lastRemovedIndex, function(data) {
|
||||
$("#prices-table tbody").append(data);
|
||||
handlePrices();
|
||||
handleRemovePrice();
|
||||
});
|
||||
} else {
|
||||
addEmptyPriceRow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function computeStartingIndex() {
|
||||
var maxIndex = -1;
|
||||
$('#prices-table tbody tr').each(function() {
|
||||
var $idInput = $(this).find('input[name$="[id]"]');
|
||||
var name = $idInput.attr('name');
|
||||
if (name) {
|
||||
var matches = name.match(/price_list_values\[(\d+)\]\[id\]/);
|
||||
if (matches && matches[1]) {
|
||||
var idx = parseInt(matches[1], 10);
|
||||
if (!isNaN(idx) && idx > maxIndex) {
|
||||
maxIndex = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return maxIndex + 1;
|
||||
}
|
||||
|
||||
function nextPriceRowIndex() {
|
||||
return priceRowIndex++;
|
||||
}
|
||||
|
||||
function extractRowIndex($row) {
|
||||
var $idInput = $row.find('input[name$="[id]"]');
|
||||
var name = $idInput.attr('name');
|
||||
if (!name) {
|
||||
return priceRowIndex;
|
||||
}
|
||||
|
||||
var matches = name.match(/price_list_values\[(\d+)\]\[id\]/);
|
||||
|
||||
return matches && matches[1] ? parseInt(matches[1], 10) : priceRowIndex;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
priceRowIndex = computeStartingIndex();
|
||||
handleAddPrice();
|
||||
handlePrices();
|
||||
handleRemovePrice();
|
||||
ensureAtLeastOneRow();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,59 +1,37 @@
|
||||
@php
|
||||
// Check if article is not visible OR has no offers at all
|
||||
$hasNoOffers = empty($article['offers']['semences'] ?? false)
|
||||
&& empty($article['offers']['plants'] ?? false)
|
||||
&& empty($article['offers']['legumes'] ?? false)
|
||||
&& empty($article['offers']['marchandise'] ?? false);
|
||||
$shouldShowComingSoon = !($article['visible'] ?? true) || $hasNoOffers;
|
||||
@endphp
|
||||
@if ($article['offers']['semences'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['semences'],
|
||||
'title' => 'Semences',
|
||||
'model' => 'semences',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
|
||||
@if ($shouldShowComingSoon)
|
||||
{{-- Display "Coming Soon" box when article is not visible or has no offers --}}
|
||||
<div class="card border-info">
|
||||
<div class="card-body text-center p-4">
|
||||
<h4 class="text-info mb-3">
|
||||
<i class="fas fa-clock"></i>
|
||||
</h4>
|
||||
<h5 class="card-title mb-0">Bientôt disponible</h5>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
{{-- Display normal offers for visible articles with available offers --}}
|
||||
@if ($article['offers']['semences'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['semences'],
|
||||
'title' => 'Semences',
|
||||
'model' => 'semences',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
@if ($article['offers']['plants'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['plants'],
|
||||
'title' => 'Plants',
|
||||
'model' => 'plants',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
|
||||
@if ($article['offers']['plants'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['plants'],
|
||||
'title' => 'Plants',
|
||||
'model' => 'plants',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
@if ($article['offers']['legumes'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['legumes'],
|
||||
'title' => 'Légumes',
|
||||
'model' => 'legumes',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
|
||||
@if ($article['offers']['legumes'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['legumes'],
|
||||
'title' => 'Légumes',
|
||||
'model' => 'legumes',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
|
||||
@if ($article['offers']['marchandise'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['marchandise'],
|
||||
'title' => 'Marchandises',
|
||||
'model' => 'marchandise',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
@if ($article['offers']['marchandise'] ?? false)
|
||||
@include('Shop.Articles.partials.addBasket', [
|
||||
'data' => $article['offers']['marchandise'],
|
||||
'title' => 'Marchandises',
|
||||
'model' => 'marchandise',
|
||||
'bgClass' => 'bg-green-light',
|
||||
])
|
||||
@endif
|
||||
|
||||
@include('load.basket')
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5 col-xs-12 text-justify">
|
||||
{!! $article['description']['variety'] ?? null !!}
|
||||
{!! $article['description']['semences'] ?? null !!}
|
||||
{!! $article['description']['plants'] ?? null !!}
|
||||
{!! $article['description']['variety'] ?? null !!}
|
||||
{!! $article['description']['merchandise'] ?? null !!}
|
||||
|
||||
@if ($article['description']['plus'] ?? false)
|
||||
@@ -48,18 +48,9 @@
|
||||
|
||||
</div>
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
@if (auth('web')->check() && !empty($article['available_sale_channels']))
|
||||
<div id="article-admin-offers" class="alert alert-info p-2 mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<strong class="d-block mb-0">Offres :</strong>
|
||||
<a href="{{ route('Admin.Shop.Articles.edit', $article['id']) }}" class="text-dark d-inline-flex align-items-center gap-1" style="font-size: 0.95rem;" title="Ouvrir la fiche article en admin" target="_blank" rel="noopener">
|
||||
<svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 20h9" />
|
||||
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z" />
|
||||
</svg>
|
||||
<span class="sr-only">Éditer l'article</span>
|
||||
</a>
|
||||
</div>
|
||||
@if (config('app.debug') && !empty($article['available_sale_channels']))
|
||||
<div class="alert alert-info p-2 mb-3">
|
||||
<strong class="d-block">Offres :</strong>
|
||||
<ul class="list-unstyled mb-0 small">
|
||||
@php
|
||||
$currentSaleChannelId = $article['current_sale_channel']['id'] ?? null;
|
||||
@@ -91,61 +82,16 @@
|
||||
</span>
|
||||
</span>
|
||||
@if ($priceTaxed !== null)
|
||||
@php
|
||||
$tariffId = $channel['tariff_id'] ?? null;
|
||||
@endphp
|
||||
@if ($tariffId)
|
||||
<a href="{{ route('Admin.Shop.Tariffs.edit', $tariffId) }}" target="_blank" rel="noopener" title="Ouvrir le tarif" class="ml-2 text-nowrap text-right {{ $nameClass }} text-decoration-none text-reset d-inline-block admin-link-group admin-price-link">
|
||||
{{ number_format($priceTaxed, 2, ',', ' ') }} € TTC
|
||||
@if (! empty($quantity))
|
||||
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
||||
@endif
|
||||
</a>
|
||||
@else
|
||||
<span class="ml-2 text-nowrap text-right {{ $nameClass }}">
|
||||
{{ number_format($priceTaxed, 2, ',', ' ') }} € TTC
|
||||
@if (! empty($quantity))
|
||||
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
<span class="ml-2 text-nowrap text-right {{ $nameClass }}">
|
||||
{{ number_format($priceTaxed, 2, ',', ' ') }} € TTC
|
||||
@if (! empty($quantity))
|
||||
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
||||
@endif
|
||||
</span>
|
||||
@else
|
||||
<span class="ml-2 text-muted">–</span>
|
||||
@endif
|
||||
</div>
|
||||
@if (!empty($channel['all_offers']))
|
||||
<ul class="list-unstyled mb-0 mt-1" style="padding-left: 0.75em;">
|
||||
@foreach ($channel['all_offers'] as $offer)
|
||||
@php
|
||||
$isSelectedOffer = $offer['id'] === $channel['offer_id'];
|
||||
$offerClass = $offer['is_active'] ? 'text-dark' : 'text-muted';
|
||||
$stockClass = $offer['stock_current'] > 0 ? 'text-success' : 'text-danger';
|
||||
@endphp
|
||||
<li class="small {{ $offerClass }}" style="font-size: 0.85em;">
|
||||
<a href="{{ route('Admin.Shop.Offers.edit', $offer['id']) }}" target="_blank" rel="noopener" title="Ouvrir l'offre" class="text-decoration-none {{ $offerClass }} admin-link-group admin-offer-link">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span style="opacity: 0.5;">{{ $isSelectedOffer ? '▸' : '○' }}</span>
|
||||
@if ($offer['variation_name'])
|
||||
{{ $offer['variation_name'] }}
|
||||
@endif
|
||||
- Stock: <strong class="{{ $stockClass }}">{{ $offer['stock_current'] }}</strong>
|
||||
@if (!$offer['is_active'])
|
||||
<span class="text-muted">(inactive)</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="text-right text-nowrap ml-2">
|
||||
{{ number_format($offer['price_taxed'], 2, ',', ' ') }} €
|
||||
@if ($offer['quantity'] > 1)
|
||||
<span class="text-muted">(min {{ $offer['quantity'] }})</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@@ -157,67 +103,3 @@
|
||||
@endsection
|
||||
|
||||
@include('load.layout.modal')
|
||||
|
||||
@if (auth('web')->check() && !empty($article['available_sale_channels']))
|
||||
@push('styles')
|
||||
<style>
|
||||
#article-admin-offers .admin-link-group {
|
||||
transition: background-color 0.15s ease;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#article-admin-offers .admin-price-link {
|
||||
display: inline-block;
|
||||
padding: 2px 4px;
|
||||
margin: -2px -4px;
|
||||
}
|
||||
|
||||
#article-admin-offers .admin-offer-link {
|
||||
display: block;
|
||||
padding: 2px 4px;
|
||||
margin: -2px -4px;
|
||||
}
|
||||
|
||||
#article-admin-offers .admin-link-group:hover,
|
||||
#article-admin-offers .admin-link-group:focus,
|
||||
#article-admin-offers .admin-link-group.linked-hover {
|
||||
background-color: rgba(0, 123, 255, 0.1);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const container = document.getElementById('article-admin-offers');
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const links = Array.from(container.querySelectorAll('a.admin-link-group[href]'));
|
||||
const grouped = new Map();
|
||||
|
||||
links.forEach((link) => {
|
||||
const href = link.getAttribute('href');
|
||||
if (!grouped.has(href)) {
|
||||
grouped.set(href, []);
|
||||
}
|
||||
grouped.get(href).push(link);
|
||||
});
|
||||
|
||||
grouped.forEach((group) => {
|
||||
group.forEach((link) => {
|
||||
const addHighlight = () => group.forEach((item) => item.classList.add('linked-hover'));
|
||||
const removeHighlight = () => group.forEach((item) => item.classList.remove('linked-hover'));
|
||||
|
||||
link.addEventListener('mouseenter', addHighlight);
|
||||
link.addEventListener('mouseleave', removeHighlight);
|
||||
link.addEventListener('focus', addHighlight);
|
||||
link.addEventListener('blur', removeHighlight);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="row mt-3 address-row" data-address-id="{{ $address['id'] }}">
|
||||
<div class="col-1">
|
||||
@php
|
||||
$inputName = $inputName ?? (isset($prefix) && $prefix ? $prefix.'[address_id]' : 'address_id');
|
||||
$inputName = isset($prefix) && $prefix ? $prefix.'[address_id]' : 'address_id';
|
||||
$currentValue = $selected ?? null;
|
||||
@endphp
|
||||
<x-form.radios.icheck name="{{ $inputName }}" val="{{ $address['id'] }}"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
@include('Shop.Customers.partials.address_item', [
|
||||
'address' => $address,
|
||||
'prefix' => $prefix ?? null,
|
||||
'inputName' => $inputName ?? null,
|
||||
'with_name' => $with_name ?? false,
|
||||
'selected' => $selected ?? null,
|
||||
])
|
||||
@@ -45,7 +44,6 @@
|
||||
<script>
|
||||
(function() {
|
||||
var prefix = '{{ $prefix }}';
|
||||
var inputName = '{{ $inputName ?? '' }}';
|
||||
var $formContainer = $('#add_address_container_{{ $prefix }}');
|
||||
var $list = $('#addresses_list_{{ $prefix }}');
|
||||
var storeUrl = '{{ route('Shop.Customers.address.store') }}';
|
||||
@@ -71,7 +69,7 @@
|
||||
$.ajax({
|
||||
url: storeUrl,
|
||||
method: 'POST',
|
||||
data: data + '&prefix=' + prefix + (inputName ? '&input_name=' + inputName : ''),
|
||||
data: data + '&prefix=' + prefix,
|
||||
success: function(response) {
|
||||
if (response.html) {
|
||||
$list.append(response.html);
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
'name' => 'phone',
|
||||
'value' => $customer['phone'] ?? (old('phone') ?? ''),
|
||||
'label' => 'Téléphone',
|
||||
'required' => true,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,20 +4,8 @@
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fa fa-check-circle text-success" style="font-size: 5rem;"></i>
|
||||
<div class="mt-4" style="font-size: 1.2rem;">
|
||||
{!! $content !!}
|
||||
</div>
|
||||
@if($payment_label ?? false)
|
||||
<div class="mt-3" style="font-size: 1.1rem;">
|
||||
Votre commande a bien été enregistrée, elle vous sera expédiée dès réception de votre {{ $payment_label }}.
|
||||
</div>
|
||||
<div class="mt-3" style="font-size: 1.1rem;">
|
||||
<i class="fa fa-exclamation-triangle text-warning mr-1"></i>
|
||||
Sans réception de votre paiement au bout de 30 jours, votre commande sera annulée.
|
||||
</div>
|
||||
@endif
|
||||
<div class="col-12">
|
||||
{!! $content !!}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -40,10 +40,7 @@
|
||||
<div class="col-sm-12 col-lg-4">
|
||||
<x-card class='shadow'>
|
||||
<div id="basketTotal">
|
||||
@include('Shop.Baskets.partials.basketTotal', [
|
||||
'basket' => $basket,
|
||||
'sale_channel' => $basket['sale_channel'] ?? null,
|
||||
])
|
||||
@include('Shop.Baskets.partials.basketTotal', ['basket' => $basket])
|
||||
</div>
|
||||
</x-card>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<div id="registred">
|
||||
<x-layout.collapse id="invoice_addresses" title="Adresse de facturation" class="rounded-lg mb-3" uncollapsed=true>
|
||||
@include('Shop.Customers.partials.addresses', [
|
||||
'addresses' => $customer['invoice_addresses'] ?? [],
|
||||
'prefix' => 'invoices',
|
||||
'inputName' => 'invoice[invoice_address_id]',
|
||||
'with_name' => true,
|
||||
'selected' => $customer['invoice_address_id'] ?? null,
|
||||
@include('Shop.Orders.partials.addresses', [
|
||||
'addresses' => $customer['invoice_addresses'] ?? false,
|
||||
'prefix' => 'invoice',
|
||||
'name' => 'invoice[invoice_address_id]',
|
||||
])
|
||||
</x-layout.collapse>
|
||||
|
||||
@@ -15,12 +13,10 @@
|
||||
|
||||
<x-layout.collapse id="delivery_addresses" title="Adresse de livraison" class="rounded-lg mb-3 d-none"
|
||||
uncollapsed=true>
|
||||
@include('Shop.Customers.partials.addresses', [
|
||||
'addresses' => $customer['delivery_addresses'] ?? [],
|
||||
'prefix' => 'deliveries',
|
||||
'inputName' => 'delivery_address_id',
|
||||
'with_name' => true,
|
||||
'selected' => $customer['delivery_address_id'] ?? null,
|
||||
@include('Shop.Orders.partials.addresses', [
|
||||
'addresses' => $customer['delivery_addresses'] ?? false,
|
||||
'prefix' => 'delivery',
|
||||
'name' => 'delivery_address_id',
|
||||
])
|
||||
@include('Shop.Orders.partials.shipping')
|
||||
</x-layout.collapse>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<h1 class="breadcrumb-title">
|
||||
<h1 style="font-size: 1.5em;">
|
||||
@foreach($breadcrumb ?? [] as $parent)
|
||||
<a href="{{ route('Shop.Categories.show', ['id' => $parent['id']]) }}" class="breadcrumb-link">{{ $parent['name'] }}</a> /
|
||||
<a href="{{ route('Shop.Categories.show', ['id' => $parent['id']]) }}" style="text-decoration: none; color: inherit;">{{ $parent['name'] }}</a> /
|
||||
@endforeach
|
||||
<span class="breadcrumb-current">{{ $category['name'] }}</span>
|
||||
<span style="font-size: 1.4em;">
|
||||
{{ $category['name'] }}
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<h1 class="category-title">{{ $category['name'] }}</h1>
|
||||
<h3 class="category-description">{!! $category['description'] !!}</h3>
|
||||
<h1 style="font-size: 2em;">{{ $category['name'] }}</h1>
|
||||
<h3 style="font-size: 1.2em;">{!! $category['description'] !!}</h3>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@include('Shop.layout.partials.category_add')
|
||||
@@ -12,4 +12,4 @@
|
||||
<div class="col-12">
|
||||
@include('Shop.layout.partials.category_articles')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="row mx-n1">
|
||||
<div class="row">
|
||||
@if ($articles ?? false)
|
||||
@foreach ($articles as $product_name => $article)
|
||||
<div class="category-card col-6 col-md-4 col-lg-3 mb-2 px-1">
|
||||
<div class="col-lg-3 col-xs-12 mb-3">
|
||||
@include('Shop.Articles.partials.article')
|
||||
</div>
|
||||
@endforeach
|
||||
@@ -46,3 +46,4 @@
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
@php
|
||||
$passwordInputId = $passwordInputId ?? 'password';
|
||||
@endphp
|
||||
|
||||
<ul class="list-unstyled small mt-1 mb-0 password-rules" data-input="#{{ $passwordInputId }}" style="display: none;">
|
||||
<li data-rule="length"><i class="fa fa-fw fa-times"></i> Au moins 8 caractères</li>
|
||||
<li data-rule="lowercase"><i class="fa fa-fw fa-times"></i> Au moins une lettre minuscule</li>
|
||||
<li data-rule="uppercase"><i class="fa fa-fw fa-times"></i> Au moins une lettre majuscule</li>
|
||||
<li data-rule="number"><i class="fa fa-fw fa-times"></i> Au moins un chiffre</li>
|
||||
<li data-rule="special"><i class="fa fa-fw fa-times"></i> Au moins un caractère spécial</li>
|
||||
</ul>
|
||||
|
||||
@once
|
||||
@push('css')
|
||||
<style>
|
||||
.password-rules li {
|
||||
color: #dc3545;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.password-rules li.valid {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@push('js')
|
||||
<script>
|
||||
$(function() {
|
||||
$('.password-rules').each(function() {
|
||||
var $rules = $(this);
|
||||
var inputSelector = $rules.data('input');
|
||||
var $input = $(inputSelector);
|
||||
|
||||
if (!$input.length) return;
|
||||
|
||||
var checks = {
|
||||
length: function(v) { return v.length >= 8; },
|
||||
lowercase: function(v) { return /[a-z]/.test(v); },
|
||||
uppercase: function(v) { return /[A-Z]/.test(v); },
|
||||
number: function(v) { return /[0-9]/.test(v); },
|
||||
special: function(v) { return /[^A-Za-z0-9]/.test(v); }
|
||||
};
|
||||
|
||||
$input.on('input keyup', function() {
|
||||
var val = $(this).val();
|
||||
if (val.length === 0) {
|
||||
$rules.hide();
|
||||
return;
|
||||
}
|
||||
$rules.find('li').each(function() {
|
||||
var rule = $(this).data('rule');
|
||||
if (checks[rule]) {
|
||||
$(this).toggleClass('valid', checks[rule](val));
|
||||
}
|
||||
});
|
||||
$rules.show();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endonce
|
||||
@@ -11,12 +11,10 @@
|
||||
<label>Mot de passe *</label>
|
||||
{{ Form::password('password', [
|
||||
'class' => 'form-control',
|
||||
'id' => 'password',
|
||||
'placeholder' => __('boilerplate::auth.fields.password'),
|
||||
'required',
|
||||
]) }}
|
||||
{!! $errors->registration->first('password', '<p class="text-danger"><strong>:message</strong></p>') !!}
|
||||
@include('Shop.auth.partials.password_rules', ['passwordInputId' => 'password'])
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
<label for="new-password" class="col-md-6 control-label text-right">Nouveau mot de passe</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<input id="new-password" type="password" class="form-control" name="new-password">
|
||||
@include('Shop.auth.partials.password_rules', ['passwordInputId' => 'new-password'])
|
||||
<input id="new-password" type="password" class="form-control" name="new-password" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +21,6 @@
|
||||
<label for="new-password-confirm" class="col-md-6 control-label text-right">Confirmez votre mot de passe</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<input id="new-password-confirm" type="password" class="form-control" name="new-password_confirmation">
|
||||
<input id="new-password-confirm" type="password" class="form-control" name="new-password_confirmation" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
<div class="row bg-light align-items-center">
|
||||
<div class="row bg-light">
|
||||
|
||||
<div class="col-6 col-lg-5 d-flex align-items-center">
|
||||
<div class="col-sm-12 col-lg-5">
|
||||
<a href="/"><img src="/img/logo.png" height="52" alt="Jardin'Envie"></a>
|
||||
<span class="green ml-3 d-none d-md-inline">Variétés Paysannes de la Semence à l'Assiette</span>
|
||||
<span class="green ml-3">Variétés Paysannes de la Semence à l'Assiette</span>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-4 pt-2 order-3 order-lg-2">
|
||||
<div class="col-sm-12 col-lg-4 pt-2">
|
||||
@include('Shop.layout.partials.search')
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-lg-3 pt-2 text-right order-2 order-lg-3 d-flex justify-content-end align-items-center">
|
||||
<div class="col-sm-12 col-lg-3 pt-2 text-right">
|
||||
@include('Shop.layout.partials.header-catalog')
|
||||
@include('Shop.layout.partials.header-profile')
|
||||
@include('Shop.layout.partials.header-basket')
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row d-lg-none bg-green-dark">
|
||||
<div class="col-12 p-0">
|
||||
<div class="collapse" id="navbarContentMobile">
|
||||
<nav class="navbar navbar-dark p-0">
|
||||
@include('Shop.layout.partials.sections-menu-list')
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,13 +10,6 @@
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="btn btn-sm fa fa-search"></i></span>
|
||||
</div>
|
||||
<div class="input-group-append d-lg-none">
|
||||
<button class="navbar-toggler navbar-light" type="button" data-toggle="collapse"
|
||||
data-target="#navbarContentMobile" aria-controls="navbarContentMobile" aria-expanded="false"
|
||||
aria-label="Menu catégories">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<ul class="navbar-nav w-100">
|
||||
@foreach ($categories as $menu)
|
||||
<li class="nav-item dropdown megamenu p-2 col
|
||||
@if (in_array($menu['id'], [$category['id'] ?? false, $category['parent_id'] ?? false, $category['parent']['parent_id'] ?? false])) active @endif">
|
||||
@if ($menu['children'] ?? false)
|
||||
<a id="megamenu_{{ $menu['id'] }}" href="{{ route('Shop.Categories.show', ['id' => $menu['id']]) }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle text-uppercase">
|
||||
{{ $menu['name'] }}
|
||||
</a>
|
||||
<div aria-labelledby="megamenu_{{ $menu['id'] }}" class="dropdown-menu border-0 p-0 m-0">
|
||||
@include('Shop.layout.partials.megamenu')
|
||||
</div>
|
||||
@else
|
||||
<a href="{{ route('Shop.Categories.show', ['id' => $menu['id']]) }}" class="nav-link text-uppercase text-white">
|
||||
{{ $menu['name'] }}
|
||||
</a>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@@ -1,9 +1,27 @@
|
||||
<div class="row mb-3 bg-green-dark d-none d-lg-block">
|
||||
<div class="row mb-3 bg-green-dark">
|
||||
<div class="col-12 pl-0 pr-0">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark p-0">
|
||||
<div class="navbar-collapse show" id="navbarContent">
|
||||
@include('Shop.layout.partials.sections-menu-list')
|
||||
<nav class="navbar navbar-expand-lg p-0">
|
||||
<div class="collapse navbar-collapse" id="navbarContent">
|
||||
<ul class="navbar-nav w-100">
|
||||
@foreach ($categories as $menu)
|
||||
<li class="nav-item dropdown megamenu p-2 col
|
||||
@if (in_array($menu['id'], [$category['id'] ?? false, $category['parent_id'] ?? false, $category['parent']['parent_id'] ?? false])) active @endif">
|
||||
@if ($menu['children'] ?? false)
|
||||
<a id="megamenu_{{ $menu['id'] }}" href="{{ route('Shop.Categories.show', ['id' => $menu['id']]) }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle text-uppercase">
|
||||
{{ $menu['name'] }}
|
||||
</a>
|
||||
<div aria-labelledby="megamenu_{{ $menu['id'] }}" class="dropdown-menu border-0 p-0 m-0">
|
||||
@include('Shop.layout.partials.megamenu')
|
||||
</div>
|
||||
@else
|
||||
<a href="{{ route('Shop.Categories.show', ['id' => $menu['id']]) }}" class="nav-link text-uppercase text-white">
|
||||
{{ $menu['name'] }}
|
||||
</a>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -15,20 +15,7 @@
|
||||
@isset($trigger) data-trigger="{{ $trigger }}" @endisset
|
||||
@isset($container) data-container="{{ $container }}" @endisset
|
||||
@isset($html) data-html="true" @endisset
|
||||
@isset($metadata) {{ $metadata }} @endisset
|
||||
@isset($attr)
|
||||
@if (is_array($attr))
|
||||
@foreach ($attr as $key => $value)
|
||||
@if ($value === true)
|
||||
{{ $key }}
|
||||
@elseif ($value !== false && $value !== null)
|
||||
{{ $key }}="{{ $value }}"
|
||||
@endif
|
||||
@endforeach
|
||||
@else
|
||||
{{ $attr }}
|
||||
@endif
|
||||
@endisset>
|
||||
@isset($metadata) {{ $metadata }} @endisset>
|
||||
<i class="fa fa-fw {{ $icon ?? '' }}"></i>
|
||||
{{ $txt ?? '' }}
|
||||
</button>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
@include('components.form.button', [
|
||||
'class' => 'btn-info duplicate ' . ($class ?? ''),
|
||||
'icon' => 'fa-copy',
|
||||
'txt' => __('Dupliquer'),
|
||||
'attr' => ['data-url' => $duplicate_url ?? $duplicateUrl ?? null],
|
||||
])
|
||||
@@ -1,33 +1,7 @@
|
||||
@php
|
||||
$cancelUrl = $cancel_url ?? $cancelUrl ?? null;
|
||||
$duplicateUrl = $duplicate_url ?? $duplicateUrl ?? null;
|
||||
@endphp
|
||||
|
||||
@push('header-actions')
|
||||
<div class="form-buttons d-flex align-items-center ml-3">
|
||||
@include('components.form.buttons.button-cancel', [
|
||||
'class' => 'btn-sm mr-2',
|
||||
'url' => $cancelUrl,
|
||||
])
|
||||
@if($duplicateUrl)
|
||||
@include('components.form.buttons.button-duplicate', [
|
||||
'class' => 'btn-sm mr-2',
|
||||
'duplicate_url' => $duplicateUrl,
|
||||
])
|
||||
@endif
|
||||
@include('components.form.buttons.button-save', [
|
||||
'class' => 'btn-sm',
|
||||
])
|
||||
</div>
|
||||
@endpush
|
||||
|
||||
<div class="row pt-0 pb-3">
|
||||
<div class="col-12">
|
||||
<div class="text-right form-buttons">
|
||||
@include('components.form.buttons.button-cancel', ['url' => $cancelUrl])
|
||||
@if($duplicateUrl)
|
||||
@include('components.form.buttons.button-duplicate', ['duplicate_url' => $duplicateUrl])
|
||||
@endif
|
||||
@include('components.form.buttons.button-cancel')
|
||||
@include('components.form.buttons.button-save')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
<div class="content-header pt-2 pb-1">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2 align-items-center">
|
||||
<div class="col-sm-8">
|
||||
<div class="d-flex align-items-center flex-wrap">
|
||||
<h1 class="m-0 text-dark d-flex align-items-center flex-grow-1">
|
||||
{{ $title ?? null}}
|
||||
@isset($subtitle)
|
||||
<small class="font-weight-light ml-1 text-md">{{ $subtitle }}</small>
|
||||
@endisset
|
||||
</h1>
|
||||
@stack('header-actions')
|
||||
</div>
|
||||
<div class="row mb-2 align-items-end">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark">
|
||||
{{ $title ?? null}}
|
||||
@isset($subtitle)
|
||||
<small class="font-weight-light ml-1 text-md">{{ $subtitle }}</small>
|
||||
@endisset
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right text-sm">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{{ route('boilerplate.dashboard') }}">
|
||||
|
||||
@@ -94,18 +94,6 @@
|
||||
|
||||
@stack('scripts')
|
||||
@stack('js')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.form-buttons .duplicate').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var url = this.dataset.url || this.getAttribute('data-url');
|
||||
if (url) {
|
||||
window.location = url;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
''); // Empty tags
|
||||
},
|
||||
skin: "oxide",
|
||||
content_css: 'default',
|
||||
content_css: 'oxide',
|
||||
language: '{{ App::getLocale() }}',
|
||||
file_picker_callback: function(callback, value, meta) {
|
||||
var x = window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName(
|
||||
|
||||
@@ -9,16 +9,14 @@
|
||||
var selector = (typeof(sel) == 'undefined') ? '.save' : sel;
|
||||
$(selector).off().click(function(e) {
|
||||
if (typeof initValidator === 'function') {
|
||||
console.log('click');
|
||||
e.preventDefault();
|
||||
if ($(form).valid()) {
|
||||
$(this).prop("disabled", true);
|
||||
$(this).html($(this).data('loading-text'));
|
||||
$(form).submit();
|
||||
} else {
|
||||
const $firstInvalid = $(form).find(':invalid, .error').first();
|
||||
if ($firstInvalid.length) {
|
||||
$firstInvalid.focus();
|
||||
}
|
||||
console.log('erreur');
|
||||
}
|
||||
} else {
|
||||
$(form).submit();
|
||||
|
||||
@@ -75,12 +75,12 @@
|
||||
if (typeof(tinyMCE) != 'undefined') {
|
||||
tinyMCE.triggerSave();
|
||||
}
|
||||
getModalForm(form_id).trigger('submit');
|
||||
$('form ' + form_id).submit();
|
||||
status = 1;
|
||||
}
|
||||
|
||||
function handlePostModal(form_id, url_save, callback) {
|
||||
getModalForm(form_id).off('submit').on('submit', function(e) {
|
||||
$('form ' + form_id).submit(function(e) {
|
||||
e.preventDefault();
|
||||
var formData = new FormData(this);
|
||||
$.ajax({
|
||||
@@ -98,17 +98,8 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getModalForm(form_id) {
|
||||
var $form = $(form_id);
|
||||
if (! $form.length) {
|
||||
$form = $('form' + form_id);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@php(define('LOAD_MODAL', true))
|
||||
@endif
|
||||
@endif
|
||||
@@ -14,5 +14,4 @@ Route::prefix('Articles')->name('Articles.')->group(function () {
|
||||
Route::get('getProductImages/{product_id?}/{model?}', 'ArticleController@getProductImages')->name('getProductImages');
|
||||
Route::post('toggleVisible', 'ArticleController@toggleVisible')->name('toggleVisible');
|
||||
Route::post('toggleHomepage', 'ArticleController@toggleHomepage')->name('toggleHomepage');
|
||||
Route::get('duplicate/{id}', 'ArticleController@duplicate')->name('duplicate');
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
Route::middleware('auth')->prefix('Admin')->namespace('Admin')->name('Admin.')->group(function () {
|
||||
Route::get('{period?}', 'HomeController@index')->name('home');
|
||||
include __DIR__.'/Botanic/route.php';
|
||||
include __DIR__.'/Core/route.php';
|
||||
include __DIR__.'/Shop/route.php';
|
||||
Route::get('{period?}', 'HomeController@index')->name('home');
|
||||
});
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<?php
|
||||
|
||||
Route::prefix('Offres')->name('Offers.')->group(function () {
|
||||
// Public offer pages are not exposed; keep the route returning 404 to avoid leaking data.
|
||||
Route::get('show/{id}', function () {
|
||||
abort(404);
|
||||
})->name('show');
|
||||
Route::get('show/{id}', 'OfferController@show')->name('show');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user