Compare commits
1 Commits
master
...
1.0.0-rc.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ec13e802e |
18
AGENTS.md
18
AGENTS.md
@@ -52,23 +52,19 @@ migrations or service integrations, include coverage that exercises
|
|||||||
failure paths. For granular checks, `./vendor/bin/phpunit --filter
|
failure paths. For granular checks, `./vendor/bin/phpunit --filter
|
||||||
FooTest` is acceptable, but always run the full suite before pushing.
|
FooTest` is acceptable, but always run the full suite before pushing.
|
||||||
|
|
||||||
## Pull Request Guidelines
|
## Commit & Pull Request Guidelines
|
||||||
|
|
||||||
|
Commits in this repo mix Conventional Commit prefixes (`new:`, `fix:`,
|
||||||
|
`chg:`); `fix: prevent null totals`. Keep messages in the imperative
|
||||||
|
mood and reference ticket IDs when available.
|
||||||
|
|
||||||
Pull requests must describe scope, list schema or configuration
|
Pull requests must describe scope, list schema or configuration
|
||||||
changes, and note any manual follow-up (cron, storage links, queues).
|
changes, and note any manual follow-up (cron, storage links,
|
||||||
|
queues).
|
||||||
|
|
||||||
Attach screenshots or terminal logs when touching UI or console
|
Attach screenshots or terminal logs when touching UI or console
|
||||||
output, and ensure CI scripts (when available) pass.
|
output, and ensure CI scripts (when available) pass.
|
||||||
|
|
||||||
## Remote Server Access
|
|
||||||
|
|
||||||
- **BLOCKING**: NEVER execute state-changing commands on remote
|
|
||||||
servers without explicit per-command user approval. Only READ
|
|
||||||
operations are allowed by default (logs, status, select queries,
|
|
||||||
configuration inspection). Write operations include but are not
|
|
||||||
limited to: `migrate`, `seed`, write queries, file modifications,
|
|
||||||
service restarts, deployments. No exceptions.
|
|
||||||
|
|
||||||
## Environment & Security Notes
|
## Environment & Security Notes
|
||||||
|
|
||||||
Copy `.env.example` to `.env` and run `php artisan key:generate`
|
Copy `.env.example` to `.env` and run `php artisan key:generate`
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ class CustomerOrdersDataTable extends DataTable
|
|||||||
{
|
{
|
||||||
$datatables
|
$datatables
|
||||||
->editColumn('status', function (Order $order) {
|
->editColumn('status', function (Order $order) {
|
||||||
return Orders::getStatusBadge($order->status);
|
return Orders::getStatus($order->status);
|
||||||
})
|
})
|
||||||
->editColumn('created_at', function (Order $order) {
|
->editColumn('created_at', function (Order $order) {
|
||||||
return $order->created_at->isoFormat('DD/MM/YY HH:mm');
|
return $order->created_at->isoFormat('DD/MM/YY HH:mm');
|
||||||
})
|
})
|
||||||
->rawColumns(['status', 'action']);
|
->rawColumns(['action']);
|
||||||
|
|
||||||
return parent::modifier($datatables);
|
return parent::modifier($datatables);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,20 +29,18 @@ class OrdersDataTable extends DataTable
|
|||||||
{
|
{
|
||||||
$datatables
|
$datatables
|
||||||
->editColumn('status', function (Order $order) {
|
->editColumn('status', function (Order $order) {
|
||||||
return Orders::getStatusBadge($order->status);
|
return Orders::getStatus($order->status);
|
||||||
})
|
})
|
||||||
->editColumn('created_at', function (Order $order) {
|
->editColumn('created_at', function (Order $order) {
|
||||||
return $order->created_at->format('d/m/Y H:i:s');
|
return $order->created_at->format('d/m/Y H:i:s');
|
||||||
})
|
})
|
||||||
->editColumn('customer.last_name', function (Order $order) {
|
->editColumn('customer.last_name', function (Order $order) {
|
||||||
return $order->customer
|
return $order->customer->last_name.' '.$order->customer->first_name;
|
||||||
? $order->customer->last_name.' '.$order->customer->first_name
|
|
||||||
: 'Client supprimé';
|
|
||||||
})
|
})
|
||||||
->editColumn('payment_type', function (Order $order) {
|
->editColumn('payment_type', function (Order $order) {
|
||||||
return InvoicePayments::getPaymentType($order->payment_type);
|
return InvoicePayments::getPaymentType($order->payment_type);
|
||||||
})
|
})
|
||||||
->rawColumns(['status', 'action']);
|
->rawColumns(['action']);
|
||||||
|
|
||||||
return parent::modifier($datatables);
|
return parent::modifier($datatables);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class TariffsDataTable extends DataTable
|
|||||||
{
|
{
|
||||||
$datatables
|
$datatables
|
||||||
->editColumn('status', function (Tariff $tariff) {
|
->editColumn('status', function (Tariff $tariff) {
|
||||||
return Tariffs::getStatusBadge($tariff['status_id']);
|
return Tariffs::getStatus($tariff['status_id']);
|
||||||
})
|
})
|
||||||
->editColumn('sale_channels2', function (Tariff $tariff) {
|
->editColumn('sale_channels2', function (Tariff $tariff) {
|
||||||
$html = '';
|
$html = '';
|
||||||
@@ -32,7 +32,7 @@ class TariffsDataTable extends DataTable
|
|||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
})
|
})
|
||||||
->rawColumns(['status', 'sale_channels2', 'action']);
|
->rawColumns(['sale_channels2', 'action']);
|
||||||
|
|
||||||
return parent::modifier($datatables);
|
return parent::modifier($datatables);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,16 +47,12 @@ class CustomerOrdersDataTable extends DataTable
|
|||||||
{
|
{
|
||||||
$datatables
|
$datatables
|
||||||
->editColumn('status', function (Order $order) {
|
->editColumn('status', function (Order $order) {
|
||||||
if ($order->status == 0 && in_array($order->payment_type, [2, 3])) {
|
return Orders::getStatus($order->status);
|
||||||
return '<span class="badge badge-warning">En attente de règlement</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return Orders::getStatusBadge($order->status);
|
|
||||||
})
|
})
|
||||||
->editColumn('created_at', function (Order $order) {
|
->editColumn('created_at', function (Order $order) {
|
||||||
return $order->created_at->isoFormat('DD/MM/YY HH:mm');
|
return $order->created_at->isoFormat('DD/MM/YY HH:mm');
|
||||||
})
|
})
|
||||||
->rawColumns(['status', 'action']);
|
->rawColumns(['action']);
|
||||||
|
|
||||||
return parent::modifier($datatables);
|
return parent::modifier($datatables);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class OrdersDataTable extends DataTable
|
|||||||
{
|
{
|
||||||
$datatables
|
$datatables
|
||||||
->editColumn('status', function (Order $order) {
|
->editColumn('status', function (Order $order) {
|
||||||
return Orders::getStatusBadge($order->status);
|
return Orders::getStatus($order->status);
|
||||||
})
|
})
|
||||||
->editColumn('created_at', function (Order $order) {
|
->editColumn('created_at', function (Order $order) {
|
||||||
return $order->created_at->toDateTimeString();
|
return $order->created_at->toDateTimeString();
|
||||||
@@ -40,7 +40,7 @@ class OrdersDataTable extends DataTable
|
|||||||
->editColumn('payment_type', function (Order $order) {
|
->editColumn('payment_type', function (Order $order) {
|
||||||
return InvoicePayments::getPaymentType($order->payment_type);
|
return InvoicePayments::getPaymentType($order->payment_type);
|
||||||
})
|
})
|
||||||
->rawColumns(['status', 'action']);
|
->rawColumns(['action']);
|
||||||
|
|
||||||
return parent::modifier($datatables);
|
return parent::modifier($datatables);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class ArticleController extends Controller
|
|||||||
'article_natures' => ArticleNatures::getOptions(),
|
'article_natures' => ArticleNatures::getOptions(),
|
||||||
'categories' => Categories::getOptions(),
|
'categories' => Categories::getOptions(),
|
||||||
'tags' => Tags::getOptionsFullName(),
|
'tags' => Tags::getOptionsFullName(),
|
||||||
'filters' => request()->only(['article_nature_id', 'category_id', 'tag_id']),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return $dataTable->render('Admin.Shop.Articles.list', $data);
|
return $dataTable->render('Admin.Shop.Articles.list', $data);
|
||||||
@@ -64,17 +63,6 @@ class ArticleController extends Controller
|
|||||||
return view('Admin.Shop.Articles.edit', $data);
|
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)
|
public function destroy($id)
|
||||||
{
|
{
|
||||||
return Articles::destroy($id);
|
return Articles::destroy($id);
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ namespace App\Http\Controllers\Admin\Shop;
|
|||||||
|
|
||||||
use App\Datatables\Admin\Shop\OrdersDataTable;
|
use App\Datatables\Admin\Shop\OrdersDataTable;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Repositories\Shop\Invoices;
|
|
||||||
use App\Repositories\Shop\OfferStocks;
|
|
||||||
use App\Repositories\Shop\OrderMails;
|
use App\Repositories\Shop\OrderMails;
|
||||||
use App\Repositories\Shop\Orders;
|
use App\Repositories\Shop\Orders;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -38,43 +36,6 @@ class OrderController extends Controller
|
|||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$previousStatus = null;
|
|
||||||
if ($request->has('id')) {
|
|
||||||
$previousStatus = Orders::get($request->input('id'))->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
$newStatus = $request->input('status');
|
|
||||||
|
|
||||||
// Interdire l'annulation si la facture a des paiements
|
|
||||||
if ($previousStatus != 4 && $newStatus == 4 && $request->has('id')) {
|
|
||||||
$order = Orders::get($request->input('id'), ['invoice']);
|
|
||||||
if ($order->invoice) {
|
|
||||||
$totalPaid = Invoices::getPayments($order->invoice->id);
|
|
||||||
if ($totalPaid > 0) {
|
|
||||||
return redirect()->back()->withInput()->with(
|
|
||||||
'growl',
|
|
||||||
['Impossible d\'annuler cette commande : la facture a déjà été réglée ('.number_format($totalPaid, 2, ',', ' ').' €). Veuillez d\'abord gérer le remboursement.', 'danger']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier le stock avant de dés-annuler une commande
|
|
||||||
if ($previousStatus == 4 && $newStatus != 4) {
|
|
||||||
$insufficients = OfferStocks::checkStockForOrder($request->input('id'));
|
|
||||||
if (! empty($insufficients)) {
|
|
||||||
$messages = [];
|
|
||||||
foreach ($insufficients as $item) {
|
|
||||||
$messages[] = $item['name'].' (stock : '.$item['stock'].', requis : '.$item['quantity'].')';
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->back()->withInput()->with(
|
|
||||||
'growl',
|
|
||||||
['Impossible de réactiver cette commande, stock insuffisant : '.implode(' ; ', $messages), 'danger']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$order = Orders::store($request->all());
|
$order = Orders::store($request->all());
|
||||||
if ($order->wasChanged('status')) {
|
if ($order->wasChanged('status')) {
|
||||||
switch ($order->status) {
|
switch ($order->status) {
|
||||||
@@ -84,13 +45,7 @@ class OrderController extends Controller
|
|||||||
case 2:
|
case 2:
|
||||||
OrderMails::sendShipping($order->id);
|
OrderMails::sendShipping($order->id);
|
||||||
break;
|
break;
|
||||||
case 4:
|
|
||||||
OfferStocks::restoreStock($order->id);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
if ($previousStatus == 4) {
|
|
||||||
OfferStocks::decreaseStockForOrder($order->id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,10 +68,8 @@ class BasketController extends Controller
|
|||||||
|
|
||||||
public function getBasketTotal($deliveryId = false, $deliveryTypeId = false)
|
public function getBasketTotal($deliveryId = false, $deliveryTypeId = false)
|
||||||
{
|
{
|
||||||
$basket = Baskets::getBasketTotal($deliveryId, $deliveryTypeId);
|
|
||||||
$data = [
|
$data = [
|
||||||
'basket' => $basket,
|
'basket' => Baskets::getBasketTotal($deliveryId, $deliveryTypeId),
|
||||||
'sale_channel' => $basket['sale_channel'] ?? null,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return view('Shop.Baskets.partials.basketTotal', $data);
|
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\Customers;
|
||||||
use App\Repositories\Shop\Offers;
|
use App\Repositories\Shop\Offers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
@@ -118,82 +117,9 @@ class CustomerController extends Controller
|
|||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$data = $request->all();
|
$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);
|
$customer = Customers::storeFull($data);
|
||||||
|
|
||||||
$growl = $request->filled('new-password')
|
return redirect()->route('Shop.Customers.edit');
|
||||||
? [__('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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function storeAddress(Request $request)
|
public function storeAddress(Request $request)
|
||||||
@@ -245,7 +171,6 @@ class CustomerController extends Controller
|
|||||||
$html = view('Shop.Customers.partials.address_item', [
|
$html = view('Shop.Customers.partials.address_item', [
|
||||||
'address' => $address->toArray(),
|
'address' => $address->toArray(),
|
||||||
'prefix' => $prefix,
|
'prefix' => $prefix,
|
||||||
'inputName' => $request->input('input_name'),
|
|
||||||
'with_name' => true,
|
'with_name' => true,
|
||||||
'selected' => $address->id,
|
'selected' => $address->id,
|
||||||
])->render();
|
])->render();
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ use App\Repositories\Core\User\ShopCart;
|
|||||||
use App\Repositories\Shop\Baskets;
|
use App\Repositories\Shop\Baskets;
|
||||||
use App\Repositories\Shop\Contents;
|
use App\Repositories\Shop\Contents;
|
||||||
use App\Repositories\Shop\Customers;
|
use App\Repositories\Shop\Customers;
|
||||||
use App\Repositories\Shop\CustomerAddresses;
|
|
||||||
use App\Repositories\Shop\Deliveries;
|
use App\Repositories\Shop\Deliveries;
|
||||||
use App\Repositories\Shop\DeliveryTypes;
|
use App\Repositories\Shop\DeliveryTypes;
|
||||||
use App\Repositories\Shop\Offers;
|
|
||||||
use App\Repositories\Shop\OrderMails;
|
use App\Repositories\Shop\OrderMails;
|
||||||
use App\Repositories\Shop\Orders;
|
use App\Repositories\Shop\Orders;
|
||||||
use App\Repositories\Shop\Paybox;
|
use App\Repositories\Shop\Paybox;
|
||||||
@@ -59,21 +57,8 @@ class OrderController extends Controller
|
|||||||
$deliveries = $deliveries ? $deliveries->values() : collect();
|
$deliveries = $deliveries ? $deliveries->values() : collect();
|
||||||
|
|
||||||
$customerData = $customer ? $customer->toArray() : false;
|
$customerData = $customer ? $customer->toArray() : false;
|
||||||
if ($customerData) {
|
if ($customerData && $defaultSaleChannelId) {
|
||||||
$customerData['delivery_address_id'] = optional(CustomerAddresses::getDeliveryAddress($customerId))->id;
|
$customerData['default_sale_channel_id'] = $defaultSaleChannelId;
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
@@ -95,25 +80,6 @@ class OrderController extends Controller
|
|||||||
$data['customer_id'] = Customers::getId();
|
$data['customer_id'] = Customers::getId();
|
||||||
$data['sale_channel_id'] = $data['sale_channel_id'] ?? SaleChannels::getDefaultID();
|
$data['sale_channel_id'] = $data['sale_channel_id'] ?? SaleChannels::getDefaultID();
|
||||||
$data['basket'] = Baskets::getBasketSummary($data['sale_channel_id'], $data['delivery_type_id'] ?? false);
|
$data['basket'] = Baskets::getBasketSummary($data['sale_channel_id'], $data['delivery_type_id'] ?? false);
|
||||||
|
|
||||||
// Vérifier le stock avant de valider la commande
|
|
||||||
$insufficients = [];
|
|
||||||
foreach ($data['basket']['detail'] as $item) {
|
|
||||||
$offer = Offers::get($item['offer_id']);
|
|
||||||
if ($offer && $offer->stock_current !== null && $item['quantity'] > $offer->stock_current) {
|
|
||||||
$offer->load('article');
|
|
||||||
$insufficients[] = ($offer->article->name ?? 'Offre #'.$offer->id)
|
|
||||||
.' (stock : '.(int) $offer->stock_current.', demandé : '.$item['quantity'].')';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! empty($insufficients)) {
|
|
||||||
return redirect()->back()->withInput()->with(
|
|
||||||
'growl',
|
|
||||||
['Stock insuffisant pour : '.implode(' ; ', $insufficients).'. Veuillez ajuster les quantités.', 'danger']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$order = Orders::saveOrder($data);
|
$order = Orders::saveOrder($data);
|
||||||
|
|
||||||
if ($order) {
|
if ($order) {
|
||||||
@@ -122,9 +88,7 @@ class OrderController extends Controller
|
|||||||
}
|
}
|
||||||
OrderMails::sendOrderConfirmed($order->id);
|
OrderMails::sendOrderConfirmed($order->id);
|
||||||
|
|
||||||
return redirect()->route('Shop.Orders.confirmed', [
|
return redirect()->route('Shop.Orders.confirmed');
|
||||||
'payment_type' => $data['payment_type'],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('Shop.Orders.order');
|
return view('Shop.Orders.order');
|
||||||
@@ -133,18 +97,9 @@ class OrderController extends Controller
|
|||||||
public function confirmed()
|
public function confirmed()
|
||||||
{
|
{
|
||||||
ShopCart::clear();
|
ShopCart::clear();
|
||||||
$paymentType = request('payment_type');
|
|
||||||
$content = Contents::getOrderConfirmedContent();
|
$content = Contents::getOrderConfirmedContent();
|
||||||
$paymentLabel = match ($paymentType) {
|
|
||||||
'2' => 'chèque',
|
|
||||||
'3' => 'virement',
|
|
||||||
default => null,
|
|
||||||
};
|
|
||||||
|
|
||||||
return view('Shop.Orders.confirmed', [
|
return view('Shop.Orders.confirmed', ['content' => $content]);
|
||||||
'content' => $content,
|
|
||||||
'payment_label' => $paymentLabel,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPdf($uuid)
|
public function getPdf($uuid)
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ class RegisterCustomer extends FormRequest
|
|||||||
'last_name' => 'required|max:255',
|
'last_name' => 'required|max:255',
|
||||||
'first_name' => 'required|max:255',
|
'first_name' => 'required|max:255',
|
||||||
'email' => 'required|email|max:255|unique:shop_customers,email,NULL,id,deleted_at,NULL',
|
'email' => 'required|email|max:255|unique:shop_customers,email,NULL,id,deleted_at,NULL',
|
||||||
'phone' => 'required|max:30',
|
|
||||||
'password' => ['required', 'confirmed', new Password()],
|
'password' => ['required', 'confirmed', new Password()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Models\Core\Mail\MailTemplate;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Mail\Mailables\Address;
|
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Spatie\MailTemplates\TemplateMailable;
|
|
||||||
|
|
||||||
class AlertePaiementAnnule extends TemplateMailable
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public $subject;
|
|
||||||
|
|
||||||
public $numero_commande;
|
|
||||||
|
|
||||||
public $date_commande;
|
|
||||||
|
|
||||||
public $montant;
|
|
||||||
|
|
||||||
public $client_nom;
|
|
||||||
|
|
||||||
public $client_email;
|
|
||||||
|
|
||||||
public $reference_paiement;
|
|
||||||
|
|
||||||
protected static $templateModelClass = MailTemplate::class;
|
|
||||||
|
|
||||||
public function __construct($order, $reference)
|
|
||||||
{
|
|
||||||
$this->numero_commande = $order->ref;
|
|
||||||
$this->date_commande = $order->created_at->format('d/m/Y H:i');
|
|
||||||
$this->montant = number_format($order->total_shipped, 2, ',', ' ').' €';
|
|
||||||
$this->client_nom = $order->customer
|
|
||||||
? $order->customer->last_name.' '.$order->customer->first_name
|
|
||||||
: 'Client supprimé';
|
|
||||||
$this->client_email = $order->customer->email ?? 'inconnu';
|
|
||||||
$this->reference_paiement = $reference;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function envelope()
|
|
||||||
{
|
|
||||||
return new Envelope(
|
|
||||||
from: new Address('boutique@jardinenvie.com', 'Jardin\'en\'Vie'),
|
|
||||||
subject: $this->subject,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Models\Core\Mail\MailTemplate;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Mail\Mailables\Address;
|
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Spatie\MailTemplates\TemplateMailable;
|
|
||||||
|
|
||||||
class AlerteStock extends TemplateMailable
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public $subject;
|
|
||||||
|
|
||||||
public $article;
|
|
||||||
|
|
||||||
public $offre;
|
|
||||||
|
|
||||||
public $stock_restant;
|
|
||||||
|
|
||||||
public $seuil;
|
|
||||||
|
|
||||||
public $lien_article;
|
|
||||||
|
|
||||||
public $lien_offre;
|
|
||||||
|
|
||||||
protected static $templateModelClass = MailTemplate::class;
|
|
||||||
|
|
||||||
public function __construct($offer)
|
|
||||||
{
|
|
||||||
$this->article = $offer->article->name ?? 'Article #'.$offer->article_id;
|
|
||||||
$this->offre = $offer->id;
|
|
||||||
$this->stock_restant = $offer->stock_current;
|
|
||||||
$this->seuil = $offer->minimum_ondemand;
|
|
||||||
$this->lien_article = url('/Admin/Shop/Articles/edit/'.$offer->article_id);
|
|
||||||
$this->lien_offre = url('/Admin/Shop/Offers/edit/'.$offer->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function envelope()
|
|
||||||
{
|
|
||||||
return new Envelope(
|
|
||||||
from: new Address('boutique@jardinenvie.com', 'Jardin\'en\'Vie'),
|
|
||||||
subject: $this->subject,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Mail;
|
namespace App\Mail;
|
||||||
|
|
||||||
use App\Models\Core\Mail\MailTemplate;
|
use App\Models\Core\Mail\MailTemplate;
|
||||||
use App\Repositories\Shop\Orders;
|
|
||||||
use App\Repositories\Shop\Traits\MailCustomers;
|
use App\Repositories\Shop\Traits\MailCustomers;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
@@ -33,44 +32,29 @@ class ConfirmationCommande extends TemplateMailable
|
|||||||
|
|
||||||
public $facturation_ville;
|
public $facturation_ville;
|
||||||
|
|
||||||
public $livraison_nom;
|
|
||||||
|
|
||||||
public $livraison_adresse;
|
public $livraison_adresse;
|
||||||
|
|
||||||
public $livraison_adresse2;
|
|
||||||
|
|
||||||
public $livraison_cp;
|
public $livraison_cp;
|
||||||
|
|
||||||
public $livraison_ville;
|
public $livraison_ville;
|
||||||
|
|
||||||
public $facturation_nom;
|
|
||||||
|
|
||||||
public $facturation_adresse2;
|
|
||||||
|
|
||||||
public $mode_paiement;
|
|
||||||
|
|
||||||
protected static $templateModelClass = MailTemplate::class;
|
protected static $templateModelClass = MailTemplate::class;
|
||||||
|
|
||||||
public function __construct($order)
|
public function __construct($order)
|
||||||
{
|
{
|
||||||
$facturation_address = $order->invoice->address;
|
$facturation_address = $order->invoice->address;
|
||||||
$delivery_address = $order->delivery_address;
|
$delivery_address = $order->delivery_address;
|
||||||
$this->prenom = $order->customer->first_name;
|
$this->prenom = $order->customer->first_name;
|
||||||
$this->nom = $order->customer->last_name;
|
$this->nom = $order->customer->last_name;
|
||||||
$this->facturation_nom = $facturation_address->name;
|
|
||||||
$this->facturation_adresse = $facturation_address->address;
|
$this->facturation_adresse = $facturation_address->address;
|
||||||
$this->facturation_adresse2 = $facturation_address->address2;
|
|
||||||
$this->facturation_cp = $facturation_address->zipcode;
|
$this->facturation_cp = $facturation_address->zipcode;
|
||||||
$this->facturation_ville = $facturation_address->city;
|
$this->facturation_ville = $facturation_address->city;
|
||||||
$this->livraison_nom = $delivery_address->name;
|
|
||||||
$this->livraison_adresse = $delivery_address->address;
|
$this->livraison_adresse = $delivery_address->address;
|
||||||
$this->livraison_adresse2 = $delivery_address->address2;
|
|
||||||
$this->livraison_cp = $delivery_address->zipcode;
|
$this->livraison_cp = $delivery_address->zipcode;
|
||||||
$this->livraison_ville = $delivery_address->city;
|
$this->livraison_ville = $delivery_address->city;
|
||||||
$this->societe = $order->customer->company;
|
$this->societe = $order->customer->company;
|
||||||
$this->email = $order->customer->email;
|
$this->email = $order->customer->email;
|
||||||
$this->numero_commande = $order->ref;
|
$this->numero_commande = $order->ref;
|
||||||
$this->date_commande = $order->created_at;
|
$this->date_commande = $order->created_at;
|
||||||
$this->mode_paiement = Orders::getPaymentType($order->payment_type);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ class Article extends Model implements HasMedia
|
|||||||
|
|
||||||
public function scopeWithAvailableOffers($query, $saleChannelId = false)
|
public function scopeWithAvailableOffers($query, $saleChannelId = false)
|
||||||
{
|
{
|
||||||
return $query->visible()->whereHas('offers', function ($query) use ($saleChannelId) {
|
return $query->whereHas('offers', function ($query) use ($saleChannelId) {
|
||||||
$query->active()->byStockAvailable();
|
$query->active()->byStockAvailable();
|
||||||
|
|
||||||
if ($saleChannelId) {
|
if ($saleChannelId) {
|
||||||
|
|||||||
@@ -92,11 +92,6 @@ class Order extends Model
|
|||||||
return $query->byStatus(3);
|
return $query->byStatus(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeNotCancelled($query)
|
|
||||||
{
|
|
||||||
return $query->where('status', '!=', 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeByStatus($query, $status)
|
public function scopeByStatus($query, $status)
|
||||||
{
|
{
|
||||||
return $query->where('status', $status);
|
return $query->where('status', $status);
|
||||||
|
|||||||
@@ -67,15 +67,17 @@ class Categories
|
|||||||
$category_target = self::getNode($target_id);
|
$category_target = self::getNode($target_id);
|
||||||
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'before':
|
|
||||||
return $category->insertBeforeNode($category_target);
|
|
||||||
case 'after':
|
case 'after':
|
||||||
return $category->insertAfterNode($category_target);
|
$category->afterNode($category_target);
|
||||||
|
break;
|
||||||
case 'inside':
|
case 'inside':
|
||||||
return $category_target->prependNode($category);
|
$category_target->appendNode($category);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return $category->insertAfterNode($category_target);
|
$category->afterNode($category_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $category->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function create($data)
|
public static function create($data)
|
||||||
|
|||||||
@@ -31,11 +31,7 @@ class ArticleImages
|
|||||||
|
|
||||||
public static function getFullImagesByArticle($article)
|
public static function getFullImagesByArticle($article)
|
||||||
{
|
{
|
||||||
if (! $article) {
|
$images = count($article->images) ? $article->images : collect([]);
|
||||||
return collect([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$images = count($article->images ?? []) ? $article->images : collect([]);
|
|
||||||
switch ($article->product_type) {
|
switch ($article->product_type) {
|
||||||
case 'App\Models\Botanic\Variety':
|
case 'App\Models\Botanic\Variety':
|
||||||
$variety = $article->product ?? false;
|
$variety = $article->product ?? false;
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ class Articles
|
|||||||
'id' => $offer->id,
|
'id' => $offer->id,
|
||||||
'name' => $offer->variation->name,
|
'name' => $offer->variation->name,
|
||||||
'prices' => $offer->tariff->price_lists->first()->price_list_values->toArray(),
|
'prices' => $offer->tariff->price_lists->first()->price_list_values->toArray(),
|
||||||
'stock' => $offer->stock_current !== null ? (int) $offer->stock_current : null,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,11 +66,6 @@ class Articles
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getVisibilityMap()
|
|
||||||
{
|
|
||||||
return Article::pluck('visible', 'id')->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getAll()
|
public static function getAll()
|
||||||
{
|
{
|
||||||
return Article::orderBy('name', 'asc')->get();
|
return Article::orderBy('name', 'asc')->get();
|
||||||
@@ -184,18 +178,10 @@ class Articles
|
|||||||
$articles = self::getArticlesWithOffers($options);
|
$articles = self::getArticlesWithOffers($options);
|
||||||
$searchOrder = $options['ids'] ?? false ? array_flip($options['ids']->toArray()) : false;
|
$searchOrder = $options['ids'] ?? false ? array_flip($options['ids']->toArray()) : false;
|
||||||
foreach ($articles as $article) {
|
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();
|
$price_lists = $article->offers[0]->tariff->price_lists->toArray();
|
||||||
if (! count($price_lists)) {
|
if (! count($price_lists)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (empty($price_lists[0]['price_list_values'][0] ?? null)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (! is_array($data[$article->name] ?? false)) {
|
if (! is_array($data[$article->name] ?? false)) {
|
||||||
$data[$article->name] = self::getDataForSale($article);
|
$data[$article->name] = self::getDataForSale($article);
|
||||||
if ($searchOrder) {
|
if ($searchOrder) {
|
||||||
@@ -210,7 +196,7 @@ class Articles
|
|||||||
ksort($data);
|
ksort($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data ?? [];
|
return $data ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getDataForSale($article)
|
public static function getDataForSale($article)
|
||||||
@@ -317,6 +303,8 @@ class Articles
|
|||||||
case 'merchandise':
|
case 'merchandise':
|
||||||
$model = $model->merchandise();
|
$model = $model->merchandise();
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
$model = $model->botanic();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $model;
|
return $model;
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ class Baskets
|
|||||||
'variation' => $offer->variation->name,
|
'variation' => $offer->variation->name,
|
||||||
'image' => Articles::getPreviewSrc(ArticleImages::getFullImageByArticle($offer->article)),
|
'image' => Articles::getPreviewSrc(ArticleImages::getFullImageByArticle($offer->article)),
|
||||||
'latin' => $offer->article->product->specie->latin ?? false,
|
'latin' => $offer->article->product->specie->latin ?? false,
|
||||||
'stock' => $offer->stock_current !== null ? (int) $offer->stock_current : null,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,13 +144,6 @@ class Baskets
|
|||||||
{
|
{
|
||||||
$offer = Offers::get($id, ['article', 'variation']);
|
$offer = Offers::get($id, ['article', 'variation']);
|
||||||
|
|
||||||
if ($offer && $offer->stock_current !== null) {
|
|
||||||
$quantity = min($quantity, max(0, (int) $offer->stock_current));
|
|
||||||
if ($quantity <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'name' => self::getArticleName($offer),
|
'name' => self::getArticleName($offer),
|
||||||
|
|||||||
@@ -41,16 +41,6 @@ class Contents
|
|||||||
return self::get(5)->text ?? 'Votre commande a été confirmée';
|
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()
|
public static function getPayboxConfirmedContent()
|
||||||
{
|
{
|
||||||
return self::get(6)->text ?? 'Merci pour votre règlement. Votre commande sera traitée sous peu.';
|
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);
|
$invoice = Invoices::getFull($id);
|
||||||
$customFields = [];
|
$customFields = [];
|
||||||
if ($orderRef = optional($invoice->order)->ref) {
|
if ($orderRef = optional($invoice->order)->ref) {
|
||||||
$customFields['Numéro de commande'] = $orderRef;
|
$customFields['order number'] = $orderRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
$customer = new Party([
|
$customer = new Party([
|
||||||
@@ -61,7 +61,7 @@ class InvoicePDF
|
|||||||
trim(($address->zipcode ?? '').' '.($address->city ?? '')),
|
trim(($address->zipcode ?? '').' '.($address->city ?? '')),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return implode("\n", $lines);
|
return implode('<br>', $lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function makeItems($details)
|
public static function makeItems($details)
|
||||||
|
|||||||
@@ -2,108 +2,19 @@
|
|||||||
|
|
||||||
namespace App\Repositories\Shop;
|
namespace App\Repositories\Shop;
|
||||||
|
|
||||||
use App\Mail\AlerteStock;
|
|
||||||
use App\Models\Shop\Offer;
|
use App\Models\Shop\Offer;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
|
|
||||||
class OfferStocks
|
class OfferStocks
|
||||||
{
|
{
|
||||||
public static function decreaseStock($item)
|
public static function decreaseStock($item)
|
||||||
{
|
{
|
||||||
$offer = Offers::get($item['offer_id']);
|
$offer = Offers::get($item['offer_id']);
|
||||||
$previousStock = $offer->stock_current;
|
|
||||||
$offer->stock_current = $offer->stock_current - $item['quantity'];
|
$offer->stock_current = $offer->stock_current - $item['quantity'];
|
||||||
if ($offer->stock_current <= 0) {
|
if ($offer->stock_current <= 0) {
|
||||||
$offer->stock_current = 0;
|
$offer->stock_current = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$saved = $offer->save();
|
return $offer->save();
|
||||||
|
|
||||||
if ($saved) {
|
|
||||||
self::checkStockAlert($offer, $previousStock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $saved;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function checkStockAlert($offer, $previousStock)
|
|
||||||
{
|
|
||||||
$threshold = (float) $offer->minimum_ondemand;
|
|
||||||
if ($threshold <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$crossedThreshold = $previousStock > $threshold
|
|
||||||
&& $offer->stock_current <= $threshold;
|
|
||||||
|
|
||||||
if (! $crossedThreshold) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$offer->load('article');
|
|
||||||
Mail::to('commande@jardinenvie.com')
|
|
||||||
->send(new AlerteStock($offer));
|
|
||||||
Log::info('Stock alert email sent', [
|
|
||||||
'offer_id' => $offer->id,
|
|
||||||
'article' => $offer->article->name ?? $offer->article_id,
|
|
||||||
'stock_current' => $offer->stock_current,
|
|
||||||
'threshold' => $threshold,
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Failed to send stock alert email', [
|
|
||||||
'offer_id' => $offer->id,
|
|
||||||
'stock_current' => $offer->stock_current,
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function restoreStock($orderId)
|
|
||||||
{
|
|
||||||
$details = \App\Models\Shop\OrderDetail::where('order_id', $orderId)->get();
|
|
||||||
|
|
||||||
foreach ($details as $detail) {
|
|
||||||
$offer = Offers::get($detail->offer_id);
|
|
||||||
if ($offer) {
|
|
||||||
$offer->stock_current = $offer->stock_current + $detail->quantity;
|
|
||||||
$offer->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function checkStockForOrder($orderId)
|
|
||||||
{
|
|
||||||
$details = \App\Models\Shop\OrderDetail::where('order_id', $orderId)->get();
|
|
||||||
$insufficients = [];
|
|
||||||
|
|
||||||
foreach ($details as $detail) {
|
|
||||||
$offer = Offers::get($detail->offer_id);
|
|
||||||
if ($offer && $offer->stock_current < $detail->quantity) {
|
|
||||||
$offer->load('article');
|
|
||||||
$insufficients[] = [
|
|
||||||
'name' => $offer->article->name ?? 'Offre #'.$offer->id,
|
|
||||||
'stock' => $offer->stock_current,
|
|
||||||
'quantity' => $detail->quantity,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $insufficients;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function decreaseStockForOrder($orderId)
|
|
||||||
{
|
|
||||||
$details = \App\Models\Shop\OrderDetail::where('order_id', $orderId)->get();
|
|
||||||
|
|
||||||
foreach ($details as $detail) {
|
|
||||||
$offer = Offers::get($detail->offer_id);
|
|
||||||
if ($offer) {
|
|
||||||
$offer->stock_current = max(0, $offer->stock_current - $detail->quantity);
|
|
||||||
$offer->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getStockCurrent($id)
|
public static function getStockCurrent($id)
|
||||||
|
|||||||
@@ -16,11 +16,7 @@ class Offers
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'articles' => Articles::getOptionsWithNature(),
|
'articles' => Articles::getOptionsWithNature(),
|
||||||
'article_visibilities' => Articles::getVisibilityMap(),
|
|
||||||
'tariffs' => Tariffs::getOptions(),
|
'tariffs' => Tariffs::getOptions(),
|
||||||
'tariff_statuses' => Tariffs::getStatusMap(),
|
|
||||||
'tariff_status_labels' => Tariffs::getStatuses(),
|
|
||||||
'tariff_pricelist_counts' => Tariffs::getPriceListCountMap(),
|
|
||||||
'variations' => Variations::getOptions(),
|
'variations' => Variations::getOptions(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
namespace App\Repositories\Shop;
|
namespace App\Repositories\Shop;
|
||||||
|
|
||||||
use App\Mail\Acheminement;
|
use App\Mail\Acheminement;
|
||||||
use App\Mail\AlertePaiementAnnule;
|
|
||||||
use App\Mail\ConfirmationCommande;
|
use App\Mail\ConfirmationCommande;
|
||||||
use App\Mail\Preparation;
|
use App\Mail\Preparation;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class OrderMails
|
class OrderMails
|
||||||
{
|
{
|
||||||
@@ -39,25 +37,4 @@ class OrderMails
|
|||||||
|
|
||||||
return Mail::to($order->customer->email)->send($mail);
|
return Mail::to($order->customer->email)->send($mail);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function sendCancelledOrderPaymentAlert($orderId, $reference)
|
|
||||||
{
|
|
||||||
$order = Orders::get($orderId, ['customer']);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Mail::to('commande@jardinenvie.com')
|
|
||||||
->send(new AlertePaiementAnnule($order, $reference));
|
|
||||||
Log::info('Cancelled order payment alert sent', [
|
|
||||||
'order_id' => $orderId,
|
|
||||||
'order_ref' => $order->ref,
|
|
||||||
'reference' => $reference,
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Failed to send cancelled order payment alert', [
|
|
||||||
'order_id' => $orderId,
|
|
||||||
'order_ref' => $order->ref,
|
|
||||||
'exception' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,36 +48,36 @@ class OrderMetrics
|
|||||||
|
|
||||||
public static function countOfToday()
|
public static function countOfToday()
|
||||||
{
|
{
|
||||||
return Order::notCancelled()->ofToday()->count();
|
return Order::ofToday()->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function countOfLastWeek()
|
public static function countOfLastWeek()
|
||||||
{
|
{
|
||||||
return Order::notCancelled()->ofLastWeek()->count();
|
return Order::ofLastWeek()->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function countOfLastMonth()
|
public static function countOfLastMonth()
|
||||||
{
|
{
|
||||||
return Order::notCancelled()->ofLastMonth()->count();
|
return Order::ofLastMonth()->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTotalOfToday()
|
public static function getTotalOfToday()
|
||||||
{
|
{
|
||||||
return Order::notCancelled()->ofToday()->sum('total_taxed');
|
return Order::ofToday()->sum('total_taxed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTotalOfLastWeek()
|
public static function getTotalOfLastWeek()
|
||||||
{
|
{
|
||||||
return Order::notCancelled()->ofLastWeek()->sum('total_taxed');
|
return Order::ofLastWeek()->sum('total_taxed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTotalOfLastMonth()
|
public static function getTotalOfLastMonth()
|
||||||
{
|
{
|
||||||
return Order::notCancelled()->ofLastMonth()->sum('total_taxed');
|
return Order::ofLastMonth()->sum('total_taxed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getModel()
|
public static function getModel()
|
||||||
{
|
{
|
||||||
return Order::notCancelled();
|
return Order::query();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,26 +126,6 @@ class Orders
|
|||||||
return self::statuses()[$id] ?? false;
|
return self::statuses()[$id] ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getStatusBadge($id)
|
|
||||||
{
|
|
||||||
$label = self::getStatus($id);
|
|
||||||
if ($label === false) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$classes = [
|
|
||||||
0 => 'badge-warning',
|
|
||||||
1 => 'badge-info',
|
|
||||||
2 => 'badge-primary',
|
|
||||||
3 => 'badge-success',
|
|
||||||
4 => 'badge-danger',
|
|
||||||
];
|
|
||||||
|
|
||||||
$class = $classes[$id] ?? 'badge-secondary';
|
|
||||||
|
|
||||||
return '<span class="badge '.$class.'">'.$label.'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getStatusByName($name)
|
public static function getStatusByName($name)
|
||||||
{
|
{
|
||||||
$data = array_flip(self::statuses());
|
$data = array_flip(self::statuses());
|
||||||
@@ -155,7 +135,7 @@ class Orders
|
|||||||
|
|
||||||
public static function statuses()
|
public static function statuses()
|
||||||
{
|
{
|
||||||
return ['En attente', 'Préparation', 'Expédié', 'Livré', 'Annulé'];
|
return ['En attente', 'Préparation', 'Expédié', 'Livré'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPaymentType($id)
|
public static function getPaymentType($id)
|
||||||
|
|||||||
@@ -104,9 +104,7 @@ class Paybox
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$isCancelled = (int) $order->status === 4;
|
DB::transaction(function () use ($invoice, $order, $reference, $payload, $existingPayment, &$shouldNotify) {
|
||||||
|
|
||||||
DB::transaction(function () use ($invoice, $order, $reference, $payload, $existingPayment, &$shouldNotify, $isCancelled) {
|
|
||||||
$attributes = [
|
$attributes = [
|
||||||
'payment_type' => 1,
|
'payment_type' => 1,
|
||||||
'amount' => $invoice->total_shipped,
|
'amount' => $invoice->total_shipped,
|
||||||
@@ -136,24 +134,14 @@ class Paybox
|
|||||||
|
|
||||||
Invoices::checkPayments($invoice->id);
|
Invoices::checkPayments($invoice->id);
|
||||||
|
|
||||||
if (! $isCancelled) {
|
$paidStatus = Orders::getStatusByName('Préparation');
|
||||||
$paidStatus = Orders::getStatusByName('Préparation');
|
if ($paidStatus !== '' && (int) $order->status !== (int) $paidStatus) {
|
||||||
if ($paidStatus !== '' && (int) $order->status !== (int) $paidStatus) {
|
$order->status = $paidStatus;
|
||||||
$order->status = $paidStatus;
|
$order->save();
|
||||||
$order->save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($isCancelled && $shouldNotify) {
|
if ($shouldNotify) {
|
||||||
Log::warning('Paybox payment received on cancelled order', [
|
|
||||||
'order_id' => $order->id,
|
|
||||||
'order_ref' => $order->ref,
|
|
||||||
'invoice_id' => $invoice->id,
|
|
||||||
'reference' => $reference,
|
|
||||||
]);
|
|
||||||
OrderMails::sendCancelledOrderPaymentAlert($order->id, $reference);
|
|
||||||
} elseif ($shouldNotify) {
|
|
||||||
try {
|
try {
|
||||||
OrderMails::sendOrderConfirmed($order->id);
|
OrderMails::sendOrderConfirmed($order->id);
|
||||||
} catch (\Throwable $exception) {
|
} catch (\Throwable $exception) {
|
||||||
|
|||||||
@@ -50,30 +50,11 @@ class Tariffs
|
|||||||
return self::getStatuses()[$status_id];
|
return self::getStatuses()[$status_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getStatusBadge($status_id)
|
|
||||||
{
|
|
||||||
$colors = ['success', 'warning', 'secondary', 'danger'];
|
|
||||||
$label = self::getStatus($status_id);
|
|
||||||
$color = $colors[$status_id] ?? 'secondary';
|
|
||||||
|
|
||||||
return '<span class="badge badge-'.$color.'">'.$label.'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getStatuses()
|
public static function getStatuses()
|
||||||
{
|
{
|
||||||
return ['Actif', 'Suspendu', 'Invisible', 'Obsolete'];
|
return ['Actif', 'Suspendu', 'Invisible', 'Obsolete'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getStatusMap()
|
|
||||||
{
|
|
||||||
return Tariff::pluck('status_id', 'id')->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getPriceListCountMap()
|
|
||||||
{
|
|
||||||
return Tariff::withCount('price_lists')->pluck('price_lists_count', 'id')->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getModel()
|
public static function getModel()
|
||||||
{
|
{
|
||||||
return Tariff::query();
|
return Tariff::query();
|
||||||
|
|||||||
@@ -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,75 +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('mail_templates')->insert([
|
|
||||||
'mailable' => 'App\\Mail\\AlerteStock',
|
|
||||||
'subject' => json_encode(['fr' => '[Stock bas] {{article}} — {{stock_restant}} unités restantes'], JSON_UNESCAPED_UNICODE),
|
|
||||||
'html_template' => json_encode(['fr' => $this->getHtmlTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'text_template' => json_encode(['fr' => $this->getTextTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*/
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
DB::table('mail_templates')
|
|
||||||
->where('mailable', 'App\\Mail\\AlerteStock')
|
|
||||||
->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getHtmlTemplate(): string
|
|
||||||
{
|
|
||||||
return '<table style="min-width: 100%;" width="100%" cellspacing="0" cellpadding="0">'
|
|
||||||
.'<tbody><tr><td>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr>'
|
|
||||||
.'<td style="margin: 0px auto; padding: 32px 0px 24px 4px; vertical-align: top;" align="center">'
|
|
||||||
.'<a href="https://www.jardinenvie.com/" style="text-decoration: none; color: #000000;">'
|
|
||||||
.'<img alt="Jardin\'enVie" src="https://boutique.jardinenvie.com/img/logo.png" style="margin: 0px auto; display: block;" width="300" height="138" border="0" /></a></td>'
|
|
||||||
.'</tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="margin: 0 auto; padding: 0px; vertical-align: top;">'
|
|
||||||
.'<table style="border-collapse: collapse; margin: 0 auto; width: 512px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 40px; color: #000000; font-size: 28px; padding: 20px 0px 10px 0px; font-weight: 800; text-align: center;">'
|
|
||||||
.'Alerte stock bas</td></tr>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 28px; color: #000000; font-size: 16px; text-align: center; padding: 10px 0 30px 0;">'
|
|
||||||
.'<p>Le stock de l\'article <strong>{{article}}</strong> (offre n°{{offre}}) '
|
|
||||||
.'a atteint le seuil d\'alerte.</p>'
|
|
||||||
.'<p style="font-size: 24px; font-weight: bold; color: #c0392b; padding: 10px 0;">{{stock_restant}} unités restantes</p>'
|
|
||||||
.'<p>Seuil d\'alerte configuré : {{seuil}} unités</p>'
|
|
||||||
.'<p style="padding-top: 15px; color: #666;">Pensez à réapprovisionner cet article.</p>'
|
|
||||||
.'</td></tr>'
|
|
||||||
.'</tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px; text-align: center;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; font-size: 12px; font-weight: 600; line-height: 20px; padding: 16px; color: #999999;">'
|
|
||||||
.'Jardin\'enVie Artisan Semencier<br />429 route des chaux, 26500 Bourg les Valence - Drôme'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getTextTemplate(): string
|
|
||||||
{
|
|
||||||
return "ALERTE STOCK BAS\n\n"
|
|
||||||
."Article : {{article}} (offre n°{{offre}})\n"
|
|
||||||
."Stock restant : {{stock_restant}} unités\n"
|
|
||||||
."Seuil d'alerte : {{seuil}} unités\n\n"
|
|
||||||
."Pensez à réapprovisionner cet article.\n\n"
|
|
||||||
."Jardin'enVie Artisan Semencier\n"
|
|
||||||
.'429 route des chaux, 26500 Bourg les Valence - Drôme';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,86 +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('mail_templates')->insert([
|
|
||||||
'mailable' => 'App\\Mail\\AlertePaiementAnnule',
|
|
||||||
'subject' => json_encode(['fr' => '[URGENT] Paiement reçu sur commande annulée {{numero_commande}}'], JSON_UNESCAPED_UNICODE),
|
|
||||||
'html_template' => json_encode(['fr' => $this->getHtmlTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'text_template' => json_encode(['fr' => $this->getTextTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*/
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
DB::table('mail_templates')
|
|
||||||
->where('mailable', 'App\\Mail\\AlertePaiementAnnule')
|
|
||||||
->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getHtmlTemplate(): string
|
|
||||||
{
|
|
||||||
return '<table style="min-width: 100%;" width="100%" cellspacing="0" cellpadding="0">'
|
|
||||||
.'<tbody><tr><td>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr>'
|
|
||||||
.'<td style="margin: 0px auto; padding: 32px 0px 24px 4px; vertical-align: top;" align="center">'
|
|
||||||
.'<a href="https://www.jardinenvie.com/" style="text-decoration: none; color: #000000;">'
|
|
||||||
.'<img alt="Jardin\'enVie" src="https://boutique.jardinenvie.com/img/logo.png" style="margin: 0px auto; display: block;" width="300" height="138" border="0" /></a></td>'
|
|
||||||
.'</tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="margin: 0 auto; padding: 0px; vertical-align: top;">'
|
|
||||||
.'<table style="border-collapse: collapse; margin: 0 auto; width: 512px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 40px; color: #c0392b; font-size: 28px; padding: 20px 0px 10px 0px; font-weight: 800; text-align: center;">'
|
|
||||||
.'⚠ Paiement sur commande annulée</td></tr>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 28px; color: #000000; font-size: 16px; text-align: center; padding: 10px 0 30px 0;">'
|
|
||||||
.'<p>Un paiement Paybox a été reçu sur une commande <strong>annulée</strong>.</p>'
|
|
||||||
.'<p style="font-size: 20px; font-weight: bold; color: #c0392b; padding: 10px 0;">Un remboursement est probablement nécessaire.</p>'
|
|
||||||
.'<table style="margin: 15px auto; text-align: left; border-collapse: collapse;" cellpadding="8">'
|
|
||||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Commande :</td><td>{{numero_commande}}</td></tr>'
|
|
||||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Date :</td><td>{{date_commande}}</td></tr>'
|
|
||||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Montant :</td><td style="font-size: 18px; font-weight: bold; color: #c0392b;">{{montant}}</td></tr>'
|
|
||||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Client :</td><td>{{client_nom}}</td></tr>'
|
|
||||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Email :</td><td>{{client_email}}</td></tr>'
|
|
||||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Réf. paiement :</td><td>{{reference_paiement}}</td></tr>'
|
|
||||||
.'</table>'
|
|
||||||
.'<p style="padding-top: 15px; color: #666;">Veuillez procéder au remboursement du client dans les plus brefs délais.</p>'
|
|
||||||
.'</td></tr>'
|
|
||||||
.'</tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px; text-align: center;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; font-size: 12px; font-weight: 600; line-height: 20px; padding: 16px; color: #999999;">'
|
|
||||||
.'Jardin\'enVie Artisan Semencier<br />429 route des chaux, 26500 Bourg les Valence - Drôme'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getTextTemplate(): string
|
|
||||||
{
|
|
||||||
return "⚠ PAIEMENT SUR COMMANDE ANNULÉE\n\n"
|
|
||||||
."Un paiement Paybox a été reçu sur une commande annulée.\n"
|
|
||||||
."Un remboursement est probablement nécessaire.\n\n"
|
|
||||||
."Commande : {{numero_commande}}\n"
|
|
||||||
."Date : {{date_commande}}\n"
|
|
||||||
."Montant : {{montant}}\n"
|
|
||||||
."Client : {{client_nom}}\n"
|
|
||||||
."Email : {{client_email}}\n"
|
|
||||||
."Réf. paiement : {{reference_paiement}}\n\n"
|
|
||||||
."Veuillez procéder au remboursement du client dans les plus brefs délais.\n\n"
|
|
||||||
."Jardin'enVie Artisan Semencier\n"
|
|
||||||
.'429 route des chaux, 26500 Bourg les Valence - Drôme';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,125 +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('mail_templates')
|
|
||||||
->where('mailable', 'App\\Mail\\AlerteStock')
|
|
||||||
->update([
|
|
||||||
'html_template' => json_encode(['fr' => $this->getHtmlTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'text_template' => json_encode(['fr' => $this->getTextTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*/
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
DB::table('mail_templates')
|
|
||||||
->where('mailable', 'App\\Mail\\AlerteStock')
|
|
||||||
->update([
|
|
||||||
'html_template' => json_encode(['fr' => $this->getOldHtmlTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'text_template' => json_encode(['fr' => $this->getOldTextTemplate()], JSON_UNESCAPED_UNICODE),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getHtmlTemplate(): string
|
|
||||||
{
|
|
||||||
return '<table style="min-width: 100%;" width="100%" cellspacing="0" cellpadding="0">'
|
|
||||||
.'<tbody><tr><td>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr>'
|
|
||||||
.'<td style="margin: 0px auto; padding: 32px 0px 24px 4px; vertical-align: top;" align="center">'
|
|
||||||
.'<a href="https://www.jardinenvie.com/" style="text-decoration: none; color: #000000;">'
|
|
||||||
.'<img alt="Jardin\'enVie" src="https://boutique.jardinenvie.com/img/logo.png" style="margin: 0px auto; display: block;" width="300" height="138" border="0" /></a></td>'
|
|
||||||
.'</tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="margin: 0 auto; padding: 0px; vertical-align: top;">'
|
|
||||||
.'<table style="border-collapse: collapse; margin: 0 auto; width: 512px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 40px; color: #000000; font-size: 28px; padding: 20px 0px 10px 0px; font-weight: 800; text-align: center;">'
|
|
||||||
.'Alerte stock bas</td></tr>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 28px; color: #000000; font-size: 16px; text-align: center; padding: 10px 0 30px 0;">'
|
|
||||||
.'<p>Le stock de l\'article <strong>{{article}}</strong> (offre n°{{offre}}) '
|
|
||||||
.'a atteint le seuil d\'alerte.</p>'
|
|
||||||
.'<p style="font-size: 24px; font-weight: bold; color: #c0392b; padding: 10px 0;">{{stock_restant}} unités restantes</p>'
|
|
||||||
.'<p>Seuil d\'alerte configuré : {{seuil}} unités</p>'
|
|
||||||
.'<p style="padding-top: 15px;"><a href="{{lien_article}}" style="display: inline-block; padding: 10px 20px; background-color: #3498db; color: #ffffff; text-decoration: none; border-radius: 4px; font-weight: bold;">Voir l\'article</a>'
|
|
||||||
.' <a href="{{lien_offre}}" style="display: inline-block; padding: 10px 20px; background-color: #27ae60; color: #ffffff; text-decoration: none; border-radius: 4px; font-weight: bold;">Voir l\'offre</a></p>'
|
|
||||||
.'<p style="padding-top: 10px; color: #666;">Pensez à réapprovisionner cet article.</p>'
|
|
||||||
.'</td></tr>'
|
|
||||||
.'</tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px; text-align: center;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; font-size: 12px; font-weight: 600; line-height: 20px; padding: 16px; color: #999999;">'
|
|
||||||
.'Jardin\'enVie Artisan Semencier<br />429 route des chaux, 26500 Bourg les Valence - Drôme'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getTextTemplate(): string
|
|
||||||
{
|
|
||||||
return "ALERTE STOCK BAS\n\n"
|
|
||||||
."Article : {{article}} (offre n°{{offre}})\n"
|
|
||||||
."Stock restant : {{stock_restant}} unités\n"
|
|
||||||
."Seuil d'alerte : {{seuil}} unités\n\n"
|
|
||||||
."Voir l'article dans l'admin : {{lien_article}}\n"
|
|
||||||
."Voir l'offre dans l'admin : {{lien_offre}}\n\n"
|
|
||||||
."Pensez à réapprovisionner cet article.\n\n"
|
|
||||||
."Jardin'enVie Artisan Semencier\n"
|
|
||||||
.'429 route des chaux, 26500 Bourg les Valence - Drôme';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getOldHtmlTemplate(): string
|
|
||||||
{
|
|
||||||
return '<table style="min-width: 100%;" width="100%" cellspacing="0" cellpadding="0">'
|
|
||||||
.'<tbody><tr><td>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr>'
|
|
||||||
.'<td style="margin: 0px auto; padding: 32px 0px 24px 4px; vertical-align: top;" align="center">'
|
|
||||||
.'<a href="https://www.jardinenvie.com/" style="text-decoration: none; color: #000000;">'
|
|
||||||
.'<img alt="Jardin\'enVie" src="https://boutique.jardinenvie.com/img/logo.png" style="margin: 0px auto; display: block;" width="300" height="138" border="0" /></a></td>'
|
|
||||||
.'</tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="margin: 0 auto; padding: 0px; vertical-align: top;">'
|
|
||||||
.'<table style="border-collapse: collapse; margin: 0 auto; width: 512px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 40px; color: #000000; font-size: 28px; padding: 20px 0px 10px 0px; font-weight: 800; text-align: center;">'
|
|
||||||
.'Alerte stock bas</td></tr>'
|
|
||||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 28px; color: #000000; font-size: 16px; text-align: center; padding: 10px 0 30px 0;">'
|
|
||||||
.'<p>Le stock de l\'article <strong>{{article}}</strong> (offre n°{{offre}}) '
|
|
||||||
.'a atteint le seuil d\'alerte.</p>'
|
|
||||||
.'<p style="font-size: 24px; font-weight: bold; color: #c0392b; padding: 10px 0;">{{stock_restant}} unités restantes</p>'
|
|
||||||
.'<p>Seuil d\'alerte configuré : {{seuil}} unités</p>'
|
|
||||||
.'<p style="padding-top: 15px; color: #666;">Pensez à réapprovisionner cet article.</p>'
|
|
||||||
.'</td></tr>'
|
|
||||||
.'</tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px; text-align: center;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
|
||||||
.'<tbody><tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; font-size: 12px; font-weight: 600; line-height: 20px; padding: 16px; color: #999999;">'
|
|
||||||
.'Jardin\'enVie Artisan Semencier<br />429 route des chaux, 26500 Bourg les Valence - Drôme'
|
|
||||||
.'</td></tr></tbody></table>'
|
|
||||||
.'</td></tr></tbody></table>';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getOldTextTemplate(): string
|
|
||||||
{
|
|
||||||
return "ALERTE STOCK BAS\n\n"
|
|
||||||
."Article : {{article}} (offre n°{{offre}})\n"
|
|
||||||
."Stock restant : {{stock_restant}} unités\n"
|
|
||||||
."Seuil d'alerte : {{seuil}} unités\n\n"
|
|
||||||
."Pensez à réapprovisionner cet article.\n\n"
|
|
||||||
."Jardin'enVie Artisan Semencier\n"
|
|
||||||
.'429 route des chaux, 26500 Bourg les Valence - Drôme';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -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 {
|
.bg-darker {
|
||||||
background-color: rgba(0,0,0,0.05)!important;
|
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-face {
|
||||||
font-family: 'noto_sanscondensed';
|
font-family: 'noto_sanscondensed';
|
||||||
src: url('/fonts/notosans-condensed/notosans-condensed-webfont.eot');
|
src: url('/fonts/notosans-condensed/notosans-condensed-webfont.eot');
|
||||||
@@ -397,11 +351,6 @@ div.megamenu ul.megamenu li.megamenu.level1
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 991.98px){
|
@media (max-width: 991.98px){
|
||||||
#navbarContentMobile {
|
|
||||||
max-height: calc(100vh - 60px);
|
|
||||||
overflow-y: auto;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
}
|
|
||||||
#navbarContentMobile .navbar-nav {
|
#navbarContentMobile .navbar-nav {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@@ -426,73 +375,4 @@ div.megamenu ul.megamenu li.megamenu.level1
|
|||||||
#navbarContentMobile .dropdown-menu .shadow {
|
#navbarContentMobile .dropdown-menu .shadow {
|
||||||
box-shadow: none !important;
|
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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- Titres des rayons -- */
|
|
||||||
|
|
||||||
.shelve-title {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shelve-article-label {
|
|
||||||
height: 48px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- Responsive: très petites résolutions (< 430px) -- */
|
|
||||||
|
|
||||||
@media (max-width: 429.98px) {
|
|
||||||
.shelve-title {
|
|
||||||
font-size: 1.4em;
|
|
||||||
}
|
|
||||||
.shelve-btn {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
.shelve-btn-suffix {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.shelve-article-label {
|
|
||||||
font-size: 0.8em;
|
|
||||||
height: 36px;
|
|
||||||
}
|
|
||||||
.shelve-slide {
|
|
||||||
padding-left: 0.15rem !important;
|
|
||||||
padding-right: 0.15rem !important;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
.homepage-text {
|
|
||||||
font-size: 1em !important;
|
|
||||||
}
|
|
||||||
.homepage-text h1 {
|
|
||||||
font-size: 1.3em !important;
|
|
||||||
}
|
|
||||||
.homepage-text h5 {
|
|
||||||
font-size: 0.85em !important;
|
|
||||||
}
|
|
||||||
.homepage-text p {
|
|
||||||
font-size: 0.85em;
|
|
||||||
}
|
|
||||||
.homepage-text .home-nav .auto {
|
|
||||||
min-width: 100% !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
}
|
|
||||||
#navbarContentMobile .nav-link,
|
|
||||||
#navbarContentMobile .dropdown-menu a {
|
|
||||||
font-size: 0.85em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,5 @@
|
|||||||
])
|
])
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@include('Admin.Shop.Articles.form', [
|
@include('Admin.Shop.Articles.form')
|
||||||
'cancel_url' => route('Admin.Shop.Articles.index'),
|
|
||||||
])
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -5,13 +5,5 @@
|
|||||||
])
|
])
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@php
|
@include('Admin.Shop.Articles.form')
|
||||||
$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'),
|
|
||||||
])
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -5,29 +5,10 @@
|
|||||||
'files' => true,
|
'files' => true,
|
||||||
]) }}
|
]) }}
|
||||||
<input type="hidden" name="id" id="id" value="{{ $article['id'] ?? null }}">
|
<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')
|
@include('Admin.Shop.Articles.partials.characteristics')
|
||||||
{{ Form::close() }}
|
{{ Form::close() }}
|
||||||
|
|
||||||
<x-save :cancel-url="$cancel_url ?? null" :duplicate-url="$duplicate_url ?? null" />
|
<x-save />
|
||||||
|
|
||||||
@include('load.form.appender')
|
@include('load.form.appender')
|
||||||
@include('load.form.editor')
|
@include('load.form.editor')
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@component('components.layout.box-collapse', [
|
@component('components.layout.box-collapse', [
|
||||||
'id' => 'product_description_box',
|
'id' => 'product_description_box',
|
||||||
'title' => 'Informations héritées',
|
'title' => 'Informations héritées',
|
||||||
'collapsed' => $collapsed ?? true,
|
'collapsed' => $collapsed ?? false,
|
||||||
])
|
])
|
||||||
@foreach ($article['inherited'] as $inherited)
|
@foreach ($article['inherited'] as $inherited)
|
||||||
@component('components.card', ['title' => $inherited['name'], 'class' => 'mb-3'])
|
@component('components.card', ['title' => $inherited['name'], 'class' => 'mb-3'])
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
@if ($category['id'] ?? false)
|
|
||||||
<div class="d-flex justify-content-end mb-3">
|
|
||||||
<a href="{{ route('Admin.Shop.Articles.index', ['category_id' => $category['id']]) }}" class="btn btn-outline-primary">
|
|
||||||
Voir les articles de ce rayon
|
|
||||||
<i class="fa fa-external-link"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
|
|||||||
@@ -14,30 +14,57 @@
|
|||||||
|
|
||||||
@push('js')
|
@push('js')
|
||||||
<script>
|
<script>
|
||||||
|
var position = '';
|
||||||
|
var target_node = '';
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
var $tree = $('#tree1').tree({
|
var $tree = $('#tree1').tree({
|
||||||
dragAndDrop: true,
|
dragAndDrop: true,
|
||||||
|
onDragStop: handleMove,
|
||||||
autoOpen: 0
|
autoOpen: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
$tree.on('tree.move', function(e) {
|
|
||||||
var position = e.move_info.position;
|
|
||||||
var target_node = e.move_info.target_node;
|
|
||||||
var moved_node = e.move_info.moved_node;
|
|
||||||
|
|
||||||
$.ajax({
|
$tree.on('tree.move', function(e) {
|
||||||
method: "POST",
|
// e.preventDefault();
|
||||||
url: "{{ route('Admin.Shop.Categories.moveTree') }}",
|
|
||||||
data: {
|
position = e.move_info.position;
|
||||||
node_id: moved_node.id,
|
target_node = e.move_info.target_node;
|
||||||
type: position,
|
|
||||||
target_id: target_node.id
|
function getNewParentNode() {
|
||||||
}
|
if (position == 'inside') {
|
||||||
});
|
return target_node;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// before or after
|
||||||
|
return target_node.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent_node = getNewParentNode();
|
||||||
|
console.log("Parent node", parent_node);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleMove(node, e) {
|
||||||
|
console.log(node);
|
||||||
|
node_id = node.id;
|
||||||
|
console.log(node_id);
|
||||||
|
console.log(position);
|
||||||
|
console.log(target_node);
|
||||||
|
target_node_id = target_node.id;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: "POST",
|
||||||
|
url: "{{ route('Admin.Shop.Categories.moveTree') }}",
|
||||||
|
data: { node_id: node.id, type: position, target_id: target_node.id }
|
||||||
|
});
|
||||||
|
// console.log(e);
|
||||||
|
// console.log($('#tree1').tree('getTree'));
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
])
|
])
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
{{ Form::open(['route' => 'Admin.Shop.Contents.store', 'id' => 'homepage-form', 'autocomplete' => 'off']) }}
|
{{ Form::open(['route' => 'Admin.Shop.Contents.store', 'id' => 'content-form', 'autocomplete' => 'off']) }}
|
||||||
<input type="hidden" name="id" value="{{ $content['id'] }}">
|
<input type="hidden" name="id" value="{{ $content['id'] }}">
|
||||||
@include('Admin.Shop.Contents.form')
|
@include('Admin.Shop.Contents.form')
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -25,21 +25,13 @@
|
|||||||
@foreach ($lastOrders as $order)
|
@foreach ($lastOrders as $order)
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
@if ($order->customer)
|
<a href="{{ route('Admin.Shop.Customers.edit', ['id' => $order->customer->id]) }}"
|
||||||
<a href="{{ route('Admin.Shop.Customers.edit', ['id' => $order->customer->id]) }}"
|
class="alert-link green">
|
||||||
class="alert-link green">
|
{{ $order->customer->first_name }}
|
||||||
{{ $order->customer->first_name }}
|
{{ $order->customer->last_name }}
|
||||||
{{ $order->customer->last_name }}
|
</a>
|
||||||
</a>
|
|
||||||
@else
|
|
||||||
<span class="text-muted">Client supprimé</span>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@if ($order->customer)
|
|
||||||
{{ $order->customer->city }} ({{ substr($order->customer->zipcode, 0, 2) }})
|
|
||||||
@endif
|
|
||||||
</td>
|
</td>
|
||||||
|
<td>{{ $order->customer->city }} ({{ substr($order->customer->zipcode, 0, 2) }})</td>
|
||||||
<td>{{ Carbon\Carbon::parse($order->created_at)->format('d/m/Y H:i') }}</td>
|
<td>{{ Carbon\Carbon::parse($order->created_at)->format('d/m/Y H:i') }}</td>
|
||||||
<td class="text-right font-weight-bold">
|
<td class="text-right font-weight-bold">
|
||||||
{{ $order->total_shipped }} €
|
{{ $order->total_shipped }} €
|
||||||
|
|||||||
@@ -1,79 +1,49 @@
|
|||||||
{{ Form::open(['route' => 'Admin.Shop.Offers.store', 'id' => 'offer-form', 'autocomplete' => 'off']) }}
|
{{ Form::open(['route' => 'Admin.Shop.Offers.store', 'id' => 'offer-form', 'autocomplete' => 'off']) }}
|
||||||
<input type="hidden" name="id" value="{{ $offer['id'] ?? false }}">
|
<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="row mb-3">
|
||||||
<div class="col-12">
|
<div class="col-8">
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex align-items-end">
|
@include('components.form.select', [
|
||||||
<div class="flex-grow-1">
|
'name' => 'article_id',
|
||||||
@include('components.form.select', [
|
'id_name' => 'article_id',
|
||||||
'name' => 'article_id',
|
'list' => $articles ?? null,
|
||||||
'id_name' => 'article_id',
|
'value' => $offer['article_id'] ?? null,
|
||||||
'list' => $articles ?? null,
|
'with_empty' => '',
|
||||||
'value' => $offer['article_id'] ?? null,
|
'class' => 'select2 select_article',
|
||||||
'with_empty' => '',
|
'label' => 'Article',
|
||||||
'class' => 'select2 select_article',
|
'required' => true,
|
||||||
'label' => 'Article',
|
])
|
||||||
'required' => true,
|
|
||||||
])
|
|
||||||
</div>
|
|
||||||
<a id="edit-article-link" href="#" class="btn btn-sm btn-outline-secondary ml-2 mb-1" title="Modifier l'article" style="display:none;">
|
|
||||||
<i class="fa fa-external-link"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<div class="d-flex align-items-end">
|
@include('components.form.select', [
|
||||||
<div class="flex-grow-1">
|
'name' => 'variation_id',
|
||||||
@include('components.form.select', [
|
'id_name' => 'variation_id',
|
||||||
'name' => 'variation_id',
|
'list' => $variations ?? null,
|
||||||
'id_name' => 'variation_id',
|
'value' => $offer['variation_id'] ?? null,
|
||||||
'list' => $variations ?? null,
|
'with_empty' => '',
|
||||||
'value' => $offer['variation_id'] ?? null,
|
'class' => 'select2 select_variation',
|
||||||
'with_empty' => '',
|
'label' => __('shop.packages.name'),
|
||||||
'class' => 'select2 select_variation',
|
'required' => true,
|
||||||
'label' => __('shop.packages.name'),
|
])
|
||||||
'required' => true,
|
|
||||||
])
|
|
||||||
</div>
|
|
||||||
<a id="edit-variation-link" href="#" class="btn btn-sm btn-outline-secondary ml-2 mb-1" title="Modifier la déclinaison" style="display:none;">
|
|
||||||
<i class="fa fa-external-link"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<div class="d-flex align-items-end">
|
@include('components.form.select', [
|
||||||
<div class="flex-grow-1">
|
'name' => 'tariff_id',
|
||||||
@include('components.form.select', [
|
'id_name' => 'tariff_id',
|
||||||
'name' => 'tariff_id',
|
'list' => $tariffs ?? null,
|
||||||
'id_name' => 'tariff_id',
|
'value' => $offer['tariff_id'] ?? null,
|
||||||
'list' => $tariffs ?? null,
|
'with_empty' => '',
|
||||||
'value' => $offer['tariff_id'] ?? null,
|
'class' => 'select2 select_tariffs',
|
||||||
'with_empty' => '',
|
'label' => 'Tarif',
|
||||||
'class' => 'select2 select_tariffs',
|
'required' => true,
|
||||||
'label' => 'Tarif',
|
])
|
||||||
'required' => true,
|
|
||||||
])
|
|
||||||
</div>
|
|
||||||
<a id="edit-tariff-link" href="#" class="btn btn-sm btn-outline-secondary ml-2 mb-1" title="Modifier le tarif" style="display:none;">
|
|
||||||
<i class="fa fa-external-link"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-4">
|
||||||
@include('components.form.input', [
|
@include('components.form.input', [
|
||||||
'name' => 'weight',
|
'name' => 'weight',
|
||||||
'value' => $offer['weight'] ?? null,
|
'value' => $offer['weight'] ?? null,
|
||||||
@@ -81,15 +51,6 @@
|
|||||||
'required' => true,
|
'required' => true,
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
|
||||||
<input type="hidden" name="status_id" value="0">
|
|
||||||
@include('components.form.toggle', [
|
|
||||||
'name' => 'status_id',
|
|
||||||
'value' => $offer['status_id'] ?? 0,
|
|
||||||
'label' => 'Actif',
|
|
||||||
'size' => 'md',
|
|
||||||
])
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@component('components.card', ['title' => 'Disponibilité', 'class' => 'mt-5'])
|
@component('components.card', ['title' => 'Disponibilité', 'class' => 'mt-5'])
|
||||||
@@ -135,6 +96,13 @@
|
|||||||
</div>
|
</div>
|
||||||
@endcomponent
|
@endcomponent
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
@@ -149,83 +117,59 @@
|
|||||||
{!! JsValidator::formRequest('App\Http\Requests\Admin\Shop\StoreOfferPost', '#offer-form') !!}
|
{!! JsValidator::formRequest('App\Http\Requests\Admin\Shop\StoreOfferPost', '#offer-form') !!}
|
||||||
|
|
||||||
<script>
|
<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();
|
initChevron();
|
||||||
initSaveForm('#offer-form');
|
initSaveForm('#offer-form');
|
||||||
initSelect2();
|
initSelect2();
|
||||||
$('#status_id').bootstrapToggle();
|
@if ($offer['id'] ?? false)
|
||||||
|
initPreview();
|
||||||
// Article visibility badges in select2
|
@endif
|
||||||
var articleVisibilities = {!! json_encode($article_visibilities ?? (object)[]) !!};
|
|
||||||
|
|
||||||
function formatArticle(item) {
|
|
||||||
if (!item.id) return item.text;
|
|
||||||
var visible = articleVisibilities[item.id];
|
|
||||||
var badge = (visible == 1)
|
|
||||||
? '<span class="badge badge-success" style="font-size:0.75em;margin-left:4px;">Visible</span>'
|
|
||||||
: '<span class="badge badge-warning" style="font-size:0.75em;margin-left:4px;">Invisible</span>';
|
|
||||||
return $('<span style="display:flex;justify-content:space-between;align-items:center;width:100%;">' + item.text + badge + '</span>');
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#article_id').select2('destroy').select2({
|
|
||||||
placeholder: "{{ __('select_a_value') }}",
|
|
||||||
allowClear: false,
|
|
||||||
width: { value: '100%' },
|
|
||||||
templateResult: formatArticle,
|
|
||||||
templateSelection: formatArticle
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tariff status badges in select2
|
|
||||||
var tariffStatuses = {!! json_encode($tariff_statuses ?? (object)[]) !!};
|
|
||||||
var tariffStatusLabels = {!! json_encode($tariff_status_labels ?? []) !!};
|
|
||||||
var tariffStatusColors = {0: '#28a745', 1: '#ffc107', 2: '#6c757d', 3: '#dc3545'};
|
|
||||||
var tariffPLCounts = {!! json_encode($tariff_pricelist_counts ?? (object)[]) !!};
|
|
||||||
|
|
||||||
function formatTariff(item) {
|
|
||||||
if (!item.id) return item.text;
|
|
||||||
var statusId = tariffStatuses[item.id];
|
|
||||||
var color = tariffStatusColors[statusId] || '#6c757d';
|
|
||||||
var label = tariffStatusLabels[statusId] || '';
|
|
||||||
var plCount = tariffPLCounts[item.id] || 0;
|
|
||||||
var warning = plCount == 0 ? '<span style="margin-left:4px;cursor:help;" title="Aucune liste de prix"><i class="fas fa-exclamation-triangle text-danger" style="font-size:1.4em;vertical-align:-2px;"></i></span>' : '';
|
|
||||||
return $('<span style="display:flex;justify-content:space-between;align-items:center;width:100%;">' + item.text + '<span>' + warning + '<span class="badge" style="background:' + color + ';color:#fff;font-size:0.75em;margin-left:4px;">' + label + '</span></span></span>');
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#tariff_id').select2('destroy').select2({
|
|
||||||
placeholder: "{{ __('select_a_value') }}",
|
|
||||||
allowClear: false,
|
|
||||||
width: { value: '100%' },
|
|
||||||
templateResult: formatTariff,
|
|
||||||
templateSelection: formatTariff
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateEditLink(selectId, linkId, routeTemplate) {
|
|
||||||
var val = $('#' + selectId).val();
|
|
||||||
var $link = $('#' + linkId);
|
|
||||||
if (val) {
|
|
||||||
$link.attr('href', routeTemplate.replace('__ID__', val)).show();
|
|
||||||
} else {
|
|
||||||
$link.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var articleRoute = '{{ route('Admin.Shop.Articles.edit', ['id' => '__ID__']) }}';
|
|
||||||
var variationRoute = '{{ route('Admin.Shop.Variations.edit', ['id' => '__ID__']) }}';
|
|
||||||
var tariffRoute = '{{ route('Admin.Shop.Tariffs.edit', ['id' => '__ID__']) }}';
|
|
||||||
|
|
||||||
// Init on page load
|
|
||||||
updateEditLink('article_id', 'edit-article-link', articleRoute);
|
|
||||||
updateEditLink('variation_id', 'edit-variation-link', variationRoute);
|
|
||||||
updateEditLink('tariff_id', 'edit-tariff-link', tariffRoute);
|
|
||||||
|
|
||||||
// Update on change
|
|
||||||
$('#article_id').on('change', function() {
|
|
||||||
updateEditLink('article_id', 'edit-article-link', articleRoute);
|
|
||||||
});
|
|
||||||
$('#variation_id').on('change', function() {
|
|
||||||
updateEditLink('variation_id', 'edit-variation-link', variationRoute);
|
|
||||||
});
|
|
||||||
$('#tariff_id').on('change', function() {
|
|
||||||
updateEditLink('tariff_id', 'edit-tariff-link', tariffRoute);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('load.form.password_toggle')
|
<div class="modal-body">
|
||||||
<div class="modal-body">
|
|
||||||
<div class="row" style="padding: 10px 20px;">
|
<div class="row" style="padding: 10px 20px;">
|
||||||
<div class="col-xs-12 text-center" id="changePasswordMessage"></div>
|
<div class="col-xs-12 text-center" id="changePasswordMessage"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
@php
|
@php
|
||||||
// Check if article is not visible OR has no offers at all
|
// Check if article is not visible OR has no offers at all
|
||||||
$hasNoOffers = empty($article['offers'] ?? false) || !array_filter($article['offers']);
|
$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;
|
$shouldShowComingSoon = !($article['visible'] ?? true) || $hasNoOffers;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@@ -16,16 +19,41 @@
|
|||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
{{-- Display normal offers for visible articles with available offers --}}
|
{{-- Display normal offers for visible articles with available offers --}}
|
||||||
@foreach ($article['offers'] as $natureKey => $natureOffers)
|
@if ($article['offers']['semences'] ?? false)
|
||||||
@if (!empty($natureOffers))
|
@include('Shop.Articles.partials.addBasket', [
|
||||||
@include('Shop.Articles.partials.addBasket', [
|
'data' => $article['offers']['semences'],
|
||||||
'data' => $natureOffers,
|
'title' => 'Semences',
|
||||||
'title' => ucfirst($natureKey),
|
'model' => 'semences',
|
||||||
'model' => $natureKey,
|
'bgClass' => 'bg-green-light',
|
||||||
'bgClass' => 'bg-green-light',
|
])
|
||||||
])
|
@endif
|
||||||
@endif
|
|
||||||
@endforeach
|
@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']['marchandise'] ?? false)
|
||||||
|
@include('Shop.Articles.partials.addBasket', [
|
||||||
|
'data' => $article['offers']['marchandise'],
|
||||||
|
'title' => 'Marchandises',
|
||||||
|
'model' => 'marchandise',
|
||||||
|
'bgClass' => 'bg-green-light',
|
||||||
|
])
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@include('load.basket')
|
@include('load.basket')
|
||||||
|
|||||||
@@ -18,9 +18,8 @@
|
|||||||
'name' => 'quantity',
|
'name' => 'quantity',
|
||||||
'class' => 'quantity',
|
'class' => 'quantity',
|
||||||
'id_name' => $model . '-quantity',
|
'id_name' => $model . '-quantity',
|
||||||
'value' => 1,
|
'value' => (int) $data[0]['prices'][0]['quantity'],
|
||||||
'min' => 1,
|
'min' => $data[0]['prices'][0]['quantity'],
|
||||||
'max' => $data[0]['stock'] ?? false,
|
|
||||||
'step' => 1,
|
'step' => 1,
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
@@ -45,35 +44,10 @@
|
|||||||
|
|
||||||
@push('js')
|
@push('js')
|
||||||
<script>
|
<script>
|
||||||
var {{ $model }}Stocks = {
|
|
||||||
@foreach ($data as $offer)
|
|
||||||
{{ $offer['id'] }}: {{ $offer['stock'] !== null ? $offer['stock'] : 'null' }},
|
|
||||||
@endforeach
|
|
||||||
};
|
|
||||||
|
|
||||||
function update{{ ucfirst($model) }}Max() {
|
|
||||||
var offerId = $('#{{ $model }}-offer_id').find('option:selected').val();
|
|
||||||
var stock = {{ $model }}Stocks[offerId];
|
|
||||||
var input = $('#{{ $model }}-quantity');
|
|
||||||
if (stock !== null && stock !== undefined) {
|
|
||||||
input.attr('max', stock);
|
|
||||||
if (parseInt(input.val()) > stock) {
|
|
||||||
input.val(stock);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
input.removeAttr('max');
|
|
||||||
}
|
|
||||||
if (parseInt(input.val()) < 1) {
|
|
||||||
input.val(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#{{ $model }}-quantity').change(function() {
|
$('#{{ $model }}-quantity').change(function() {
|
||||||
update{{ ucfirst($model) }}Max();
|
|
||||||
setPrice('{{ $model }}');
|
setPrice('{{ $model }}');
|
||||||
});
|
});
|
||||||
$('#{{ $model }}-offer_id').change(function() {
|
$('#{{ $model }}-offer_id').change(function() {
|
||||||
update{{ ucfirst($model) }}Max();
|
|
||||||
setPrice('{{ $model }}');
|
setPrice('{{ $model }}');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -19,8 +19,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-lg-5 col-xs-12 text-justify">
|
<div class="col-lg-5 col-xs-12 text-justify">
|
||||||
{!! $article['description']['variety'] ?? null !!}
|
{!! $article['description']['variety'] ?? null !!}
|
||||||
|
{!! $article['description']['semences'] ?? null !!}
|
||||||
|
{!! $article['description']['plants'] ?? null !!}
|
||||||
{!! $article['description']['merchandise'] ?? null !!}
|
{!! $article['description']['merchandise'] ?? null !!}
|
||||||
{!! $article['description']['description'] ?? null !!}
|
|
||||||
|
|
||||||
@if ($article['description']['plus'] ?? false)
|
@if ($article['description']['plus'] ?? false)
|
||||||
<h3>Spécificités</h3>
|
<h3>Spécificités</h3>
|
||||||
@@ -49,16 +50,7 @@
|
|||||||
<div class="col-lg-3 col-xs-12">
|
<div class="col-lg-3 col-xs-12">
|
||||||
@if (auth('web')->check() && !empty($article['available_sale_channels']))
|
@if (auth('web')->check() && !empty($article['available_sale_channels']))
|
||||||
<div id="article-admin-offers" class="alert alert-info p-2 mb-3">
|
<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">Offres :</strong>
|
||||||
<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>
|
|
||||||
<ul class="list-unstyled mb-0 small">
|
<ul class="list-unstyled mb-0 small">
|
||||||
@php
|
@php
|
||||||
$currentSaleChannelId = $article['current_sale_channel']['id'] ?? null;
|
$currentSaleChannelId = $article['current_sale_channel']['id'] ?? null;
|
||||||
@@ -94,7 +86,7 @@
|
|||||||
$tariffId = $channel['tariff_id'] ?? null;
|
$tariffId = $channel['tariff_id'] ?? null;
|
||||||
@endphp
|
@endphp
|
||||||
@if ($tariffId)
|
@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">
|
<a href="{{ route('Admin.Shop.Tariffs.edit', $tariffId) }}" target="_blank" rel="noopener" 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
|
{{ number_format($priceTaxed, 2, ',', ' ') }} € TTC
|
||||||
@if (! empty($quantity))
|
@if (! empty($quantity))
|
||||||
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
||||||
@@ -121,7 +113,7 @@
|
|||||||
$stockClass = $offer['stock_current'] > 0 ? 'text-success' : 'text-danger';
|
$stockClass = $offer['stock_current'] > 0 ? 'text-success' : 'text-danger';
|
||||||
@endphp
|
@endphp
|
||||||
<li class="small {{ $offerClass }}" style="font-size: 0.85em;">
|
<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">
|
<a href="{{ route('Admin.Shop.Offers.edit', $offer['id']) }}" target="_blank" rel="noopener" class="text-decoration-none {{ $offerClass }} admin-link-group admin-offer-link">
|
||||||
<div class="d-flex justify-content-between align-items-start">
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
<div>
|
<div>
|
||||||
<span style="opacity: 0.5;">{{ $isSelectedOffer ? '▸' : '○' }}</span>
|
<span style="opacity: 0.5;">{{ $isSelectedOffer ? '▸' : '○' }}</span>
|
||||||
|
|||||||
@@ -53,12 +53,7 @@
|
|||||||
|
|
||||||
$('.basket-quantity').change(function() {
|
$('.basket-quantity').change(function() {
|
||||||
var offer_id = $(this).data('id');
|
var offer_id = $(this).data('id');
|
||||||
var quantity = parseInt($(this).val()) || 1;
|
var quantity = $(this).val();
|
||||||
var min = parseInt($(this).attr('min')) || 1;
|
|
||||||
var max = parseInt($(this).attr('max'));
|
|
||||||
if (quantity < min) quantity = min;
|
|
||||||
if (max && quantity > max) quantity = max;
|
|
||||||
$(this).val(quantity);
|
|
||||||
var $row = $(this).closest('.row');
|
var $row = $(this).closest('.row');
|
||||||
updateBasket(offer_id, quantity, function() {
|
updateBasket(offer_id, quantity, function() {
|
||||||
calculatePrice($row);
|
calculatePrice($row);
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
'value' => $item['quantity'],
|
'value' => $item['quantity'],
|
||||||
'class' => 'basket-quantity',
|
'class' => 'basket-quantity',
|
||||||
'data_id' => $item['id'],
|
'data_id' => $item['id'],
|
||||||
'min' => 1,
|
|
||||||
'max' => $item['stock'] ?? false,
|
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 text-right" style="font-size: 2em;" id="basket_total-{{ $item['id'] }}">
|
<div class="col-4 text-right" style="font-size: 2em;" id="basket_total-{{ $item['id'] }}">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="row mt-3 address-row" data-address-id="{{ $address['id'] }}">
|
<div class="row mt-3 address-row" data-address-id="{{ $address['id'] }}">
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
@php
|
@php
|
||||||
$inputName = $inputName ?? (isset($prefix) && $prefix ? $prefix.'[address_id]' : 'address_id');
|
$inputName = isset($prefix) && $prefix ? $prefix.'[address_id]' : 'address_id';
|
||||||
$currentValue = $selected ?? null;
|
$currentValue = $selected ?? null;
|
||||||
@endphp
|
@endphp
|
||||||
<x-form.radios.icheck name="{{ $inputName }}" val="{{ $address['id'] }}"
|
<x-form.radios.icheck name="{{ $inputName }}" val="{{ $address['id'] }}"
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
@include('Shop.Customers.partials.address_item', [
|
@include('Shop.Customers.partials.address_item', [
|
||||||
'address' => $address,
|
'address' => $address,
|
||||||
'prefix' => $prefix ?? null,
|
'prefix' => $prefix ?? null,
|
||||||
'inputName' => $inputName ?? null,
|
|
||||||
'with_name' => $with_name ?? false,
|
'with_name' => $with_name ?? false,
|
||||||
'selected' => $selected ?? null,
|
'selected' => $selected ?? null,
|
||||||
])
|
])
|
||||||
@@ -45,7 +44,6 @@
|
|||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var prefix = '{{ $prefix }}';
|
var prefix = '{{ $prefix }}';
|
||||||
var inputName = '{{ $inputName ?? '' }}';
|
|
||||||
var $formContainer = $('#add_address_container_{{ $prefix }}');
|
var $formContainer = $('#add_address_container_{{ $prefix }}');
|
||||||
var $list = $('#addresses_list_{{ $prefix }}');
|
var $list = $('#addresses_list_{{ $prefix }}');
|
||||||
var storeUrl = '{{ route('Shop.Customers.address.store') }}';
|
var storeUrl = '{{ route('Shop.Customers.address.store') }}';
|
||||||
@@ -71,7 +69,7 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: storeUrl,
|
url: storeUrl,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: data + '&prefix=' + prefix + (inputName ? '&input_name=' + inputName : ''),
|
data: data + '&prefix=' + prefix,
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.html) {
|
if (response.html) {
|
||||||
$list.append(response.html);
|
$list.append(response.html);
|
||||||
|
|||||||
@@ -57,7 +57,6 @@
|
|||||||
'name' => 'phone',
|
'name' => 'phone',
|
||||||
'value' => $customer['phone'] ?? (old('phone') ?? ''),
|
'value' => $customer['phone'] ?? (old('phone') ?? ''),
|
||||||
'label' => 'Téléphone',
|
'label' => 'Téléphone',
|
||||||
'required' => true,
|
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
<div class="mb-5 bg-green-light shadow2">
|
<div class="mb-5 bg-green-light shadow2">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<h1 class="p-2 green shelve-title">{{ $shelve['name'] }}</h1>
|
<h1 class="p-2 green" style="font-size: 2em;">{{ $shelve['name'] }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 text-right">
|
<div class="col-6 text-right">
|
||||||
<a href="{{ route('Shop.Categories.show', ['id' => $shelve['id']]) }}"
|
<a href="{{ route('Shop.Categories.show', ['id' => $shelve['id']]) }}"
|
||||||
class="mt-2 mr-2 btn btn-green-dark shelve-btn">
|
class="mt-2 mr-2 btn btn-green-dark">
|
||||||
Découvrir<span class="shelve-btn-suffix"> la sélection</span>
|
Découvrir la sélection
|
||||||
</a>
|
</a>
|
||||||
<!--
|
<!--
|
||||||
<a class="mt-2 green-dark btn" href="{{ route('Shop.Categories.show', ['id' => $shelve['id']]) }}">Tout
|
<a class="mt-2 green-dark btn" href="{{ route('Shop.Categories.show', ['id' => $shelve['id']]) }}">Tout
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-11 mx-auto shelve_slider_{{ $shelve['id'] }} slider">
|
<div class="col-11 mx-auto shelve_slider_{{ $shelve['id'] }} slider">
|
||||||
@foreach ($shelve['articles'] as $name => $article)
|
@foreach ($shelve['articles'] as $name => $article)
|
||||||
<div class="text-center pr-2 pl-2 shelve-slide">
|
<div class="text-center pr-2 pl-2">
|
||||||
<a class="green" href="{{ route('Shop.Articles.show', ['id' => $article['id']]) }}">
|
<a class="green" href="{{ route('Shop.Articles.show', ['id' => $article['id']]) }}">
|
||||||
<img data-lazy="{{ App\Repositories\Shop\Articles::getPreviewSrc($article['image'] ?? false) }}"
|
<img data-lazy="{{ App\Repositories\Shop\Articles::getPreviewSrc($article['image'] ?? false) }}"
|
||||||
class="d-block w-100 rounded" alt="{{ $name }}" />
|
class="d-block w-100 rounded" alt="{{ $name }}" />
|
||||||
<div class="shelve-article-label">
|
<div style="height: 48px;">
|
||||||
{{ $name }}
|
{{ $name }}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -4,20 +4,8 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 text-center py-5">
|
<div class="col-12">
|
||||||
<i class="fa fa-check-circle text-success" style="font-size: 5rem;"></i>
|
{!! $content !!}
|
||||||
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -40,10 +40,7 @@
|
|||||||
<div class="col-sm-12 col-lg-4">
|
<div class="col-sm-12 col-lg-4">
|
||||||
<x-card class='shadow'>
|
<x-card class='shadow'>
|
||||||
<div id="basketTotal">
|
<div id="basketTotal">
|
||||||
@include('Shop.Baskets.partials.basketTotal', [
|
@include('Shop.Baskets.partials.basketTotal', ['basket' => $basket])
|
||||||
'basket' => $basket,
|
|
||||||
'sale_channel' => $basket['sale_channel'] ?? null,
|
|
||||||
])
|
|
||||||
</div>
|
</div>
|
||||||
</x-card>
|
</x-card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
<div id="registred">
|
<div id="registred">
|
||||||
|
<x-layout.collapse id="invoice_addresses" title="Adresse de facturation" class="rounded-lg mb-3" uncollapsed=true>
|
||||||
|
@include('Shop.Orders.partials.addresses', [
|
||||||
|
'addresses' => $customer['invoice_addresses'] ?? false,
|
||||||
|
'prefix' => 'invoice',
|
||||||
|
'name' => 'invoice[invoice_address_id]',
|
||||||
|
])
|
||||||
|
</x-layout.collapse>
|
||||||
|
|
||||||
<x-layout.collapse id="delivery_mode" title="Mode de livraison" class="rounded-lg mb-3" uncollapsed=true>
|
<x-layout.collapse id="delivery_mode" title="Mode de livraison" class="rounded-lg mb-3" uncollapsed=true>
|
||||||
@include('Shop.Orders.partials.deliveries')
|
@include('Shop.Orders.partials.deliveries')
|
||||||
</x-layout.collapse>
|
</x-layout.collapse>
|
||||||
|
|
||||||
<x-layout.collapse id="delivery_addresses" title="Adresse de livraison" class="rounded-lg mb-3 d-none"
|
<x-layout.collapse id="delivery_addresses" title="Adresse de livraison" class="rounded-lg mb-3 d-none"
|
||||||
uncollapsed=true>
|
uncollapsed=true>
|
||||||
@include('Shop.Customers.partials.addresses', [
|
@include('Shop.Orders.partials.addresses', [
|
||||||
'addresses' => $customer['delivery_addresses'] ?? [],
|
'addresses' => $customer['delivery_addresses'] ?? false,
|
||||||
'prefix' => 'deliveries',
|
'prefix' => 'delivery',
|
||||||
'inputName' => 'delivery_address_id',
|
'name' => 'delivery_address_id',
|
||||||
'with_name' => true,
|
|
||||||
'selected' => $customer['delivery_address_id'] ?? null,
|
|
||||||
])
|
])
|
||||||
@include('Shop.Orders.partials.shipping')
|
@include('Shop.Orders.partials.shipping')
|
||||||
</x-layout.collapse>
|
</x-layout.collapse>
|
||||||
|
|
||||||
<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,
|
|
||||||
])
|
|
||||||
</x-layout.collapse>
|
|
||||||
|
|
||||||
<x-layout.collapse id="payment" title="Paiement" class="rounded-lg mb-3" uncollapsed=true>
|
<x-layout.collapse id="payment" title="Paiement" class="rounded-lg mb-3" uncollapsed=true>
|
||||||
@include('Shop.Orders.partials.payments')
|
@include('Shop.Orders.partials.payments')
|
||||||
</x-layout.collapse>
|
</x-layout.collapse>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<h1 class="breadcrumb-title">
|
<h1 style="font-size: 1.5em;">
|
||||||
@foreach($breadcrumb ?? [] as $parent)
|
@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
|
@endforeach
|
||||||
<span class="breadcrumb-current">{{ $category['name'] }}</span>
|
<span style="font-size: 1.4em;">
|
||||||
|
{{ $category['name'] }}
|
||||||
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<h1 class="category-title">{{ $category['name'] }}</h1>
|
<h1 style="font-size: 2em;">{{ $category['name'] }}</h1>
|
||||||
<h3 class="category-description">{!! $category['description'] !!}</h3>
|
<h3 style="font-size: 1.2em;">{!! $category['description'] !!}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
@include('Shop.layout.partials.category_add')
|
@include('Shop.layout.partials.category_add')
|
||||||
@@ -12,4 +12,4 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@include('Shop.layout.partials.category_articles')
|
@include('Shop.layout.partials.category_articles')
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="row mx-n1">
|
<div class="row">
|
||||||
@if ($articles ?? false)
|
@if ($articles ?? false)
|
||||||
@foreach ($articles as $product_name => $article)
|
@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')
|
@include('Shop.Articles.partials.article')
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -46,3 +46,4 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<p>{{ __('boilerplate::auth.firstlogin.intro') }}</p>
|
<p>{{ __('boilerplate::auth.firstlogin.intro') }}</p>
|
||||||
</div>
|
</div>
|
||||||
@include('load.form.password_toggle')
|
|
||||||
<div class="form-group {{ $errors->has('password') ? 'has-error' : '' }}">
|
<div class="form-group {{ $errors->has('password') ? 'has-error' : '' }}">
|
||||||
{{ Form::label('password', __('boilerplate::auth.fields.password')) }}
|
{{ Form::label('password', __('boilerplate::auth.fields.password')) }}
|
||||||
{{ Form::input('password', 'password', Request::old('password'), ['class' => 'form-control', 'autofocus']) }}
|
{{ Form::input('password', 'password', Request::old('password'), ['class' => 'form-control', 'autofocus']) }}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
<div class="input-group form-group {{ $errors->has('password') ? 'has-error' : '' }}">
|
<div class="input-group form-group {{ $errors->has('password') ? 'has-error' : '' }}">
|
||||||
{{ Form::password('password', ['class' => 'form-control', 'placeholder' => __('boilerplate::auth.fields.password'), 'required']) }}
|
{{ Form::password('password', ['class' => 'form-control', 'placeholder' => __('boilerplate::auth.fields.password'), 'required']) }}
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-outline-secondary password-toggle" type="button" tabindex="-1">
|
<div class="btn btn-outline-secondary">
|
||||||
<i class="fa fa-eye"></i>
|
<i class="fa fa-lock"></i>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!! $errors->first('password', '<p class="text-danger"><strong>:message</strong></p>') !!}
|
{!! $errors->first('password', '<p class="text-danger"><strong>:message</strong></p>') !!}
|
||||||
@@ -46,5 +46,4 @@
|
|||||||
-->
|
-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@include('load.form.password_toggle')
|
|
||||||
{!! Form::close() !!}
|
{!! Form::close() !!}
|
||||||
|
|||||||
@@ -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>
|
<label>Mot de passe *</label>
|
||||||
{{ Form::password('password', [
|
{{ Form::password('password', [
|
||||||
'class' => 'form-control',
|
'class' => 'form-control',
|
||||||
'id' => 'password',
|
|
||||||
'placeholder' => __('boilerplate::auth.fields.password'),
|
'placeholder' => __('boilerplate::auth.fields.password'),
|
||||||
'required',
|
'required',
|
||||||
]) }}
|
]) }}
|
||||||
{!! $errors->registration->first('password', '<p class="text-danger"><strong>:message</strong></p>') !!}
|
{!! $errors->registration->first('password', '<p class="text-danger"><strong>:message</strong></p>') !!}
|
||||||
@include('Shop.auth.partials.password_rules', ['passwordInputId' => 'password'])
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
@@ -68,8 +66,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@include('load.form.password_toggle')
|
|
||||||
|
|
||||||
@push('js')
|
@push('js')
|
||||||
<script>
|
<script>
|
||||||
$('#use_for_delivery').click(function() {
|
$('#use_for_delivery').click(function() {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@include('load.form.password_toggle')
|
|
||||||
|
|
||||||
<div class="row mb-3 mt-3">
|
<div class="row mb-3 mt-3">
|
||||||
<label for="new-password" class="col-md-6 control-label text-right">Mot de passe actuel</label>
|
<label for="new-password" class="col-md-6 control-label text-right">Mot de passe actuel</label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -15,8 +13,7 @@
|
|||||||
<label for="new-password" class="col-md-6 control-label text-right">Nouveau mot de passe</label>
|
<label for="new-password" class="col-md-6 control-label text-right">Nouveau mot de passe</label>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<input id="new-password" type="password" class="form-control" name="new-password">
|
<input id="new-password" type="password" class="form-control" name="new-password" required>
|
||||||
@include('Shop.auth.partials.password_rules', ['passwordInputId' => 'new-password'])
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -24,6 +21,6 @@
|
|||||||
<label for="new-password-confirm" class="col-md-6 control-label text-right">Confirmez votre mot de passe</label>
|
<label for="new-password-confirm" class="col-md-6 control-label text-right">Confirmez votre mot de passe</label>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
{{ Form::email('email', old('email', $email), ['class' => 'form-control', 'placeholder' => __('boilerplate::auth.fields.email'), 'required', 'autofocus']) }}
|
{{ Form::email('email', old('email', $email), ['class' => 'form-control', 'placeholder' => __('boilerplate::auth.fields.email'), 'required', 'autofocus']) }}
|
||||||
{!! $errors->first('email','<p class="text-danger"><strong>:message</strong></p>') !!}
|
{!! $errors->first('email','<p class="text-danger"><strong>:message</strong></p>') !!}
|
||||||
</div>
|
</div>
|
||||||
@include('load.form.password_toggle')
|
|
||||||
<div class="form-group {{ $errors->has('password') ? 'has-error' : '' }}">
|
<div class="form-group {{ $errors->has('password') ? 'has-error' : '' }}">
|
||||||
{{ Form::password('password', ['class' => 'form-control', 'placeholder' => __('boilerplate::auth.fields.password'), 'required']) }}
|
{{ Form::password('password', ['class' => 'form-control', 'placeholder' => __('boilerplate::auth.fields.password'), 'required']) }}
|
||||||
{!! $errors->first('password','<p class="text-danger"><strong>:message</strong></p>') !!}
|
{!! $errors->first('password','<p class="text-danger"><strong>:message</strong></p>') !!}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
@if (!empty($text))
|
@if (!empty($text))
|
||||||
<div class="row m-0 mb-3">
|
<div class="row m-0 mb-3">
|
||||||
<div class="col-12 p-3 green-dark homepage-text" style="font-size: 1.2em;">{!! $text !!}</div>
|
<div class="col-12 p-3 green-dark" style="font-size: 1.2em;">{!! $text !!}</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -15,20 +15,7 @@
|
|||||||
@isset($trigger) data-trigger="{{ $trigger }}" @endisset
|
@isset($trigger) data-trigger="{{ $trigger }}" @endisset
|
||||||
@isset($container) data-container="{{ $container }}" @endisset
|
@isset($container) data-container="{{ $container }}" @endisset
|
||||||
@isset($html) data-html="true" @endisset
|
@isset($html) data-html="true" @endisset
|
||||||
@isset($metadata) {{ $metadata }} @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>
|
|
||||||
<i class="fa fa-fw {{ $icon ?? '' }}"></i>
|
<i class="fa fa-fw {{ $icon ?? '' }}"></i>
|
||||||
{{ $txt ?? '' }}
|
{{ $txt ?? '' }}
|
||||||
</button>
|
</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="row pt-0 pb-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="text-right form-buttons">
|
<div class="text-right form-buttons">
|
||||||
@include('components.form.buttons.button-cancel', ['url' => $cancelUrl])
|
@include('components.form.buttons.button-cancel')
|
||||||
@if($duplicateUrl)
|
|
||||||
@include('components.form.buttons.button-duplicate', ['duplicate_url' => $duplicateUrl])
|
|
||||||
@endif
|
|
||||||
@include('components.form.buttons.button-save')
|
@include('components.form.buttons.button-save')
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
<div class="content-header pt-2 pb-1">
|
<div class="content-header pt-2 pb-1">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row mb-2 align-items-center">
|
<div class="row mb-2 align-items-end">
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-6">
|
||||||
<div class="d-flex align-items-center flex-wrap">
|
<h1 class="m-0 text-dark">
|
||||||
<h1 class="m-0 text-dark d-flex align-items-center flex-grow-1">
|
{{ $title ?? null}}
|
||||||
{{ $title ?? null}}
|
@isset($subtitle)
|
||||||
@isset($subtitle)
|
<small class="font-weight-light ml-1 text-md">{{ $subtitle }}</small>
|
||||||
<small class="font-weight-light ml-1 text-md">{{ $subtitle }}</small>
|
@endisset
|
||||||
@endisset
|
</h1>
|
||||||
</h1>
|
|
||||||
@stack('header-actions')
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-6">
|
||||||
<ol class="breadcrumb float-sm-right text-sm">
|
<ol class="breadcrumb float-sm-right text-sm">
|
||||||
<li class="breadcrumb-item">
|
<li class="breadcrumb-item">
|
||||||
<a href="{{ route('boilerplate.dashboard') }}">
|
<a href="{{ route('boilerplate.dashboard') }}">
|
||||||
|
|||||||
@@ -94,18 +94,6 @@
|
|||||||
|
|
||||||
@stack('scripts')
|
@stack('scripts')
|
||||||
@stack('js')
|
@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>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
@if (!defined('LOAD_PASSWORD_TOGGLE'))
|
|
||||||
@push('js')
|
|
||||||
<script>
|
|
||||||
function initPasswordToggle(context) {
|
|
||||||
var $ctx = $(context || document);
|
|
||||||
$ctx.find('input[type="password"]').each(function() {
|
|
||||||
var $input = $(this);
|
|
||||||
if ($input.closest('.input-group').find('.password-toggle').length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!$input.parent().hasClass('input-group')) {
|
|
||||||
$input.wrap('<div class="input-group"></div>');
|
|
||||||
}
|
|
||||||
var $btn = $('<div class="input-group-append">' +
|
|
||||||
'<button class="btn btn-outline-secondary password-toggle" type="button" tabindex="-1">' +
|
|
||||||
'<i class="fa fa-eye"></i>' +
|
|
||||||
'</button></div>');
|
|
||||||
$input.after($btn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
initPasswordToggle();
|
|
||||||
|
|
||||||
$(document).on('shown.bs.modal', function(e) {
|
|
||||||
initPasswordToggle(e.target);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('click', '.password-toggle', function() {
|
|
||||||
var $btn = $(this);
|
|
||||||
var $input = $btn.closest('.input-group').find('input');
|
|
||||||
if ($input.attr('type') === 'password') {
|
|
||||||
$input.attr('type', 'text');
|
|
||||||
$btn.find('i').removeClass('fa-eye').addClass('fa-eye-slash');
|
|
||||||
} else {
|
|
||||||
$input.attr('type', 'password');
|
|
||||||
$btn.find('i').removeClass('fa-eye-slash').addClass('fa-eye');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@endpush
|
|
||||||
@php(define('LOAD_PASSWORD_TOGGLE', true))
|
|
||||||
@endif
|
|
||||||
@@ -14,5 +14,4 @@ Route::prefix('Articles')->name('Articles.')->group(function () {
|
|||||||
Route::get('getProductImages/{product_id?}/{model?}', 'ArticleController@getProductImages')->name('getProductImages');
|
Route::get('getProductImages/{product_id?}/{model?}', 'ArticleController@getProductImages')->name('getProductImages');
|
||||||
Route::post('toggleVisible', 'ArticleController@toggleVisible')->name('toggleVisible');
|
Route::post('toggleVisible', 'ArticleController@toggleVisible')->name('toggleVisible');
|
||||||
Route::post('toggleHomepage', 'ArticleController@toggleHomepage')->name('toggleHomepage');
|
Route::post('toggleHomepage', 'ArticleController@toggleHomepage')->name('toggleHomepage');
|
||||||
Route::get('duplicate/{id}', 'ArticleController@duplicate')->name('duplicate');
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
Route::middleware('auth')->prefix('Admin')->namespace('Admin')->name('Admin.')->group(function () {
|
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__.'/Botanic/route.php';
|
||||||
include __DIR__.'/Core/route.php';
|
include __DIR__.'/Core/route.php';
|
||||||
include __DIR__.'/Shop/route.php';
|
include __DIR__.'/Shop/route.php';
|
||||||
Route::get('{period?}', 'HomeController@index')->name('home');
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
Route::prefix('Offres')->name('Offers.')->group(function () {
|
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}', 'OfferController@show')->name('show');
|
||||||
Route::get('show/{id}', function () {
|
|
||||||
abort(404);
|
|
||||||
})->name('show');
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user