new: add channel management

This commit is contained in:
Valentin Lab
2025-10-05 09:39:27 +02:00
parent 2d7436a12b
commit 8d130b9741
16 changed files with 541 additions and 65 deletions

View File

@@ -48,6 +48,37 @@
</div>
<div class="col-lg-3 col-xs-12">
@if (config('app.debug') && ($article['current_sale_channel'] ?? false))
<div class="alert alert-info p-2 mb-3">
<strong>Canal actif :</strong>
{{ $article['current_sale_channel']['name'] ?? 'N/A' }}
<span class="d-block small text-muted">
ID {{ $article['current_sale_channel']['id'] ?? '' }} · Code {{ $article['current_sale_channel']['code'] ?? '' }}
</span>
@if (!empty($article['available_sale_channels']))
<hr class="my-2">
<strong class="d-block">Offres disponibles dans :</strong>
<ul class="list-unstyled mb-0 small">
@foreach ($article['available_sale_channels'] as $channel)
<li class="d-flex justify-content-between align-items-start">
<span>
{{ $channel['name'] }}
<span class="d-block text-muted" style="font-size: 0.85em; padding-left: 0.9em;">code {{ $channel['code'] }}</span>
</span>
@if (isset($channel['price_taxed']))
<span class="ml-2 text-nowrap text-right">
{{ number_format($channel['price_taxed'], 2, ',', ' ') }} TTC
@if (! empty($channel['quantity']))
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $channel['quantity'] }}</span>
@endif
</span>
@endif
</li>
@endforeach
</ul>
@endif
</div>
@endif
@include('Shop.Articles.partials.ArticleAddBasket')
</div>
</div>

View File

@@ -58,7 +58,7 @@
updateBasket(offer_id, quantity, function() {
calculatePrice($row);
calculateTotal();
});
}, $row);
});
$('.basket-delete').click(function() {
@@ -70,13 +70,20 @@
});
}
function updateBasket(offer_id, quantity, callback) {
function updateBasket(offer_id, quantity, callback, $row) {
var data = {
offer_id: offer_id,
quantity: quantity,
update: true
};
$.post("{{ route('Shop.Basket.addBasket') }}", data, callback);
$.post("{{ route('Shop.Basket.addBasket') }}", data, function(response) {
if ($row && response && response.added && typeof response.added.price !== 'undefined') {
$row.find('.basket-price').text(fixNumber(response.added.price));
$row.find('.basket-total-row').text(fixNumber(response.added.price * $row.find('.basket-quantity').val()));
}
callback(response);
refreshBasketTop();
});
}
function calculatePrice($that) {

View File

@@ -1,25 +1,168 @@
@foreach ($deliveries as $delivery)
<div class="row">
<div class="col-1 text-right pt-1">
@push('styles')
<style>
.sale-channel-wrapper {
border: none;
background-color: transparent;
}
.sale-channel-wrapper:not(.blocked) .card-body {
border: 1px solid #e5e7eb;
border-radius: .75rem;
background-color: #ffffff;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.sale-channel-wrapper:not(.blocked) .card-body:hover {
border-color: #3b82f6;
box-shadow: 0 0.35rem 0.8rem rgba(37, 99, 235, 0.12);
}
.sale-channel-wrapper.blocked .card-body {
border: 1px solid #d1d5db;
border-radius: .75rem;
background-color: #f3f4f6;
}
.sale-channel-wrapper .card-body {
padding: 1.25rem;
}
.sale-channel-toggle {
display: flex;
align-items: flex-start;
justify-content: center;
padding-top: 0.25rem;
}
.sale-channel-content strong {
font-size: 1.05rem;
}
.sale-channel-warning {
font-size: 0.85rem;
}
.sale-channel-wrapper .icheck-success > input:first-child + label::before,
.sale-channel-wrapper .icheck-primary > input:first-child + label::before,
.sale-channel-wrapper .icheck-danger > input:first-child + label::before {
opacity: 1;
border-width: 2px;
border-color: #9ca3af;
}
.sale-channel-wrapper.blocked .icheck-success > input:first-child + label::before,
.sale-channel-wrapper.blocked .icheck-primary > input:first-child + label::before,
.sale-channel-wrapper.blocked .icheck-danger > input:first-child + label::before {
border-color: #cbd5f5;
background-color: #f8fafc;
}
.sale-channel-wrapper .icheck-success > input:first-child + label,
.sale-channel-wrapper .icheck-primary > input:first-child + label,
.sale-channel-wrapper .icheck-danger > input:first-child + label {
opacity: 1;
}
.sale-channel-wrapper [class*="icheck-"] > input:first-child:disabled + label {
opacity: 1;
}
</style>
@endpush
@php
$saleChannelsCollection = collect($sale_channels);
$firstSaleChannel = $saleChannelsCollection->first();
$selectedSaleChannelId = $customer['default_sale_channel_id'] ?? ($firstSaleChannel['id'] ?? null);
$cartCount = app('App\\Repositories\\Core\\User\\ShopCart')::count();
@endphp
@if ($cartCount > 0)
<div class="alert alert-warning">
<strong>Note :</strong> en changeant votre mode d'achat, les articles de votre panier seront transférés sur la liste de prix correspondant au nouveau canal de vente sélectionné.
</div>
@endif
@foreach ($saleChannelsCollection as $saleChannel)
@php
$check = $sale_channel_checks[$saleChannel['id']] ?? null;
$isBlocked = $check && $saleChannel['id'] !== $selectedSaleChannelId;
@endphp
<div class="card sale-channel-wrapper mb-3 @if($isBlocked) blocked @endif">
<div class="card-body py-3">
<div class="row align-items-start">
<div class="col-1 sale-channel-toggle">
@include('components.form.radios.icheck', [
'name' => 'delivery_id',
'id_name' => 'delivery_id_' . $delivery['id'],
'value' => $delivery['id'],
'checked' => $customer['sale_delivery_id'] ?? false,
'class' => 'delivery',
'name' => 'sale_channel_id',
'id_name' => 'sale_channel_id_' . $saleChannel['id'],
'val' => $saleChannel['id'],
'value' => $selectedSaleChannelId,
'class' => 'sale-channel',
'disabled' => $isBlocked,
])
</div>
<div class="col-11 pt-3">
<strong>{{ $delivery['name'] }} - {{ $delivery['sale_channel']['name'] }}</strong><br />
<p>{{ $delivery['description'] }}</p>
</div>
<div class="col-11 sale-channel-content @if($isBlocked) text-muted @endif">
<strong>{{ $saleChannel['name'] }}</strong><br />
<p class="mb-2">{!! $saleChannel['description'] ?? '' !!}</p>
@if ($check)
<div class="text-danger small mb-0 sale-channel-warning">
@php $missingCount = $check['full_count'] ?? count($check['names']); @endphp
@if ($cartCount > 0 && $missingCount >= $cartCount)
{{ __('shop.sale_channels.missing_offers_all') }}
@else
{{ trans_choice('shop.sale_channels.missing_offers', $missingCount, ['count' => $missingCount]) }}
<br>
@if ($missingCount > 3)
<span class="d-block">{{ implode(', ', array_slice($check['names'], 0, 3)) }}, …</span>
@else
<span class="d-block">{{ implode(', ', $check['names']) }}</span>
@endif
@endif
<div class="d-flex align-items-start mt-1">
<span class="mr-1">⚠️</span>
<span>{{ __('shop.sale_channels.cannot_select_with_cart') }}</span>
</div>
</div>
@endif
</div>
</div>
</div>
</div>
@endforeach
@push('js')
<script>
$('.delivery').off().change(function() {
console.log($(this).val());
const $saleChannels = $('.sale-channel');
const updateUrl = '{{ route('Shop.Customers.storeProfileAjax') }}';
const token = '{{ csrf_token() }}';
const customerId = {{ $customer['id'] ?? 'null' }};
let currentSaleChannelId = '{{ $selectedSaleChannelId }}';
$saleChannels.off().change(function() {
if (!customerId) {
return;
}
const selectedSaleChannel = $(this).val();
$.post(updateUrl, {
_token: token,
id: customerId,
default_sale_channel_id: selectedSaleChannel,
}).done(function() {
currentSaleChannelId = selectedSaleChannel;
window.location.reload();
}).fail(function(xhr) {
const message = xhr.responseJSON && xhr.responseJSON.message
? xhr.responseJSON.message
: "{{ __('Une erreur est survenue lors de l\'enregistrement du canal de vente préféré.') }}";
alert(message);
if (currentSaleChannelId) {
$saleChannels.filter('[value="' + currentSaleChannelId + '"]').prop('checked', true);
}
});
});
</script>
@endpush

View File

@@ -1,35 +1,69 @@
<nav>
<div class="nav nav-tabs pl-2">
<a href="#deliveriesTab" data-toggle="tab" class="nav-item nav-link active" role="tab" aria-selected="true">
MON MODE D'ACHAT
</a>
<a href="#ordersTab" data-toggle="tab" class="nav-item nav-link" role="tab" aria-selected="false">
SUIVI DE COMMANDES
</a>
<a href="#invoicesTab" data-toggle="tab" class="nav-item nav-link" role="tab" aria-selected="false">
FACTURES
</a>
</div>
</nav>
@php
$saleChannels = $sale_channels ?? [];
@endphp
<div class="tab-content">
<div class="tab-pane fade show active pt-0 pb-0" id="deliveriesTab">
<x-card classBody="bg-light">
@include('Shop.Customers.partials.deliveries')
</x-card>
@if (count($saleChannels) > 1)
<nav>
<div class="nav nav-tabs pl-2">
<a href="#deliveriesTab" data-toggle="tab" class="nav-item nav-link active" role="tab" aria-selected="true">
MON MODE D'ACHAT
</a>
<a href="#ordersTab" data-toggle="tab" class="nav-item nav-link" role="tab" aria-selected="false">
SUIVI DE COMMANDES
</a>
<a href="#invoicesTab" data-toggle="tab" class="nav-item nav-link" role="tab" aria-selected="false">
FACTURES
</a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active pt-0 pb-0" id="deliveriesTab">
<x-card classBody="bg-light">
@include('Shop.Customers.partials.deliveries')
</x-card>
</div>
<div class="tab-pane fade show pt-0 pb-0" id="ordersTab">
<x-card classBody="bg-light">
@include('Shop.Orders.partials.list', [
'dataTable' => $orders,
])
</x-card>
</div>
<div class="tab-pane fade show pt-0 pb-0" id="invoicesTab">
<x-card classBody="bg-light">
@include('Shop.Invoices.partials.list', [
'dataTable' => $invoices,
])
</x-card>
</div>
</div>
<div class="tab-pane fade show pt-0 pb-0" id="ordersTab">
<x-card classBody="bg-light">
@include('Shop.Orders.partials.list', [
'dataTable' => $orders,
])
</x-card>
@else
<nav>
<div class="nav nav-tabs pl-2">
<a href="#ordersTab" data-toggle="tab" class="nav-item nav-link active" role="tab" aria-selected="true">
SUIVI DE COMMANDES
</a>
<a href="#invoicesTab" data-toggle="tab" class="nav-item nav-link" role="tab" aria-selected="false">
FACTURES
</a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active pt-0 pb-0" id="ordersTab">
<x-card classBody="bg-light">
@include('Shop.Orders.partials.list', [
'dataTable' => $orders,
])
</x-card>
</div>
<div class="tab-pane fade show pt-0 pb-0" id="invoicesTab">
<x-card classBody="bg-light">
@include('Shop.Invoices.partials.list', [
'dataTable' => $invoices,
])
</x-card>
</div>
</div>
<div class="tab-pane fade show pt-0 pb-0" id="invoicesTab">
<x-card classBody="bg-light">
@include('Shop.Invoices.partials.list', [
'dataTable' => $invoices,
])
</x-card>
</div>
</div>
@endif

View File

@@ -1,10 +1,20 @@
@php
$defaultSaleChannelId = $customer['default_sale_channel_id'] ?? null;
$preselectedDeliveryId = old('delivery_id');
if (! $preselectedDeliveryId && $defaultSaleChannelId) {
$match = collect($deliveries)->firstWhere('sale_channel_id', $defaultSaleChannelId);
$preselectedDeliveryId = $match['id'] ?? null;
}
@endphp
@foreach ($deliveries as $delivery)
<div class="row">
<div class="col-1">
@include('components.form.radios.icheck', [
'name' => 'delivery_id',
'val' => $delivery['id'],
'value' => (int) old('delivery_id') === $delivery['id'] ? $delivery['id'] : null,
'value' => $preselectedDeliveryId,
'id' => 'delivery_' . $delivery['id'],
'class' => 'delivery_mode' . ($delivery['at_house'] ? ' at_house' : ''),
])

View File

@@ -17,6 +17,7 @@
<link rel="shortcut icon" type="image/x-icon" href="{{ asset('img/favicon.ico') }}">
<link rel="stylesheet" href="/css/site.min.css?{{ date('Ymd') }}" type="text/css" media="all">
@stack('styles')
@stack('css')
</head>