new: make the debug info available to all backoffice users with helpful links
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Repositories\Shop;
|
namespace App\Repositories\Shop;
|
||||||
|
|
||||||
use App\Models\Shop\Offer;
|
use App\Models\Shop\Offer;
|
||||||
|
use App\Models\Shop\PriceList;
|
||||||
use App\Models\Shop\PriceListValue;
|
use App\Models\Shop\PriceListValue;
|
||||||
use App\Models\Shop\SaleChannel;
|
use App\Models\Shop\SaleChannel;
|
||||||
use App\Traits\Model\Basic;
|
use App\Traits\Model\Basic;
|
||||||
@@ -185,20 +186,44 @@ class Offers
|
|||||||
->with([
|
->with([
|
||||||
'article',
|
'article',
|
||||||
'tariff:id,status_id',
|
'tariff:id,status_id',
|
||||||
|
'variation',
|
||||||
])
|
])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
return $channels->map(function ($channel) use ($offers) {
|
return $channels->map(function ($channel) use ($offers) {
|
||||||
$priceValue = null;
|
$priceValue = null;
|
||||||
$candidateOffer = null;
|
$candidateOffer = null;
|
||||||
|
$allOffersForChannel = [];
|
||||||
|
|
||||||
foreach ($offers as $offer) {
|
foreach ($offers as $offer) {
|
||||||
$priceCandidate = self::getPrice($offer->id, 1, $channel->id);
|
$priceCandidate = self::getPrice($offer->id, 1, $channel->id);
|
||||||
|
|
||||||
if ($priceCandidate && (float) $priceCandidate->price_taxed > 0) {
|
if ($priceCandidate && (float) $priceCandidate->price_taxed > 0) {
|
||||||
$priceValue = $priceCandidate;
|
// Get price list name
|
||||||
$candidateOffer = $offer;
|
$priceListName = null;
|
||||||
break;
|
if ($priceCandidate) {
|
||||||
|
$priceListModel = PriceList::find($priceCandidate->price_list_id);
|
||||||
|
$priceListName = $priceListModel ? $priceListModel->name : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all offers with their details
|
||||||
|
$allOffersForChannel[] = [
|
||||||
|
'id' => $offer->id,
|
||||||
|
'variation_name' => $offer->variation ? $offer->variation->name : null,
|
||||||
|
'stock_current' => (int) $offer->stock_current,
|
||||||
|
'status_id' => (int) $offer->status_id,
|
||||||
|
'is_active' => (int) $offer->status_id === 1,
|
||||||
|
'tariff_id' => $offer->tariff_id ? (int) $offer->tariff_id : null,
|
||||||
|
'price_taxed' => (float) $priceCandidate->price_taxed,
|
||||||
|
'quantity' => (int) $priceCandidate->quantity,
|
||||||
|
'price_list_name' => $priceListName,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Keep first valid offer as the main candidate
|
||||||
|
if (!$candidateOffer) {
|
||||||
|
$priceValue = $priceCandidate;
|
||||||
|
$candidateOffer = $offer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +234,7 @@ class Offers
|
|||||||
$offerHasStock = $candidateOffer && $candidateOffer->stock_current !== null
|
$offerHasStock = $candidateOffer && $candidateOffer->stock_current !== null
|
||||||
? (float) $candidateOffer->stock_current > 0
|
? (float) $candidateOffer->stock_current > 0
|
||||||
: null;
|
: null;
|
||||||
|
$offerTariffId = $candidateOffer && $candidateOffer->tariff_id ? (int) $candidateOffer->tariff_id : null;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => $channel->id,
|
'id' => $channel->id,
|
||||||
@@ -221,6 +247,8 @@ class Offers
|
|||||||
'offer_stock_current' => $offerStock,
|
'offer_stock_current' => $offerStock,
|
||||||
'offer_has_stock' => $offerHasStock,
|
'offer_has_stock' => $offerHasStock,
|
||||||
'tariff_status_id' => $offerTariffStatus,
|
'tariff_status_id' => $offerTariffStatus,
|
||||||
|
'tariff_id' => $offerTariffId,
|
||||||
|
'all_offers' => $allOffersForChannel,
|
||||||
];
|
];
|
||||||
})->toArray();
|
})->toArray();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3 col-xs-12">
|
<div class="col-lg-3 col-xs-12">
|
||||||
@if (config('app.debug') && !empty($article['available_sale_channels']))
|
@if (auth('web')->check() && !empty($article['available_sale_channels']))
|
||||||
<div class="alert alert-info p-2 mb-3">
|
<div id="article-admin-offers" class="alert alert-info p-2 mb-3">
|
||||||
<strong class="d-block">Offres :</strong>
|
<strong class="d-block">Offres :</strong>
|
||||||
<ul class="list-unstyled mb-0 small">
|
<ul class="list-unstyled mb-0 small">
|
||||||
@php
|
@php
|
||||||
@@ -82,16 +82,61 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@if ($priceTaxed !== null)
|
@if ($priceTaxed !== null)
|
||||||
<span class="ml-2 text-nowrap text-right {{ $nameClass }}">
|
@php
|
||||||
{{ number_format($priceTaxed, 2, ',', ' ') }} € TTC
|
$tariffId = $channel['tariff_id'] ?? null;
|
||||||
@if (! empty($quantity))
|
@endphp
|
||||||
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
@if ($tariffId)
|
||||||
@endif
|
<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">
|
||||||
</span>
|
{{ number_format($priceTaxed, 2, ',', ' ') }} € TTC
|
||||||
|
@if (! empty($quantity))
|
||||||
|
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<span class="ml-2 text-nowrap text-right {{ $nameClass }}">
|
||||||
|
{{ number_format($priceTaxed, 2, ',', ' ') }} € TTC
|
||||||
|
@if (! empty($quantity))
|
||||||
|
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $quantity }}</span>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
@else
|
@else
|
||||||
<span class="ml-2 text-muted">–</span>
|
<span class="ml-2 text-muted">–</span>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@if (!empty($channel['all_offers']))
|
||||||
|
<ul class="list-unstyled mb-0 mt-1" style="padding-left: 0.75em;">
|
||||||
|
@foreach ($channel['all_offers'] as $offer)
|
||||||
|
@php
|
||||||
|
$isSelectedOffer = $offer['id'] === $channel['offer_id'];
|
||||||
|
$offerClass = $offer['is_active'] ? 'text-dark' : 'text-muted';
|
||||||
|
$stockClass = $offer['stock_current'] > 0 ? 'text-success' : 'text-danger';
|
||||||
|
@endphp
|
||||||
|
<li class="small {{ $offerClass }}" style="font-size: 0.85em;">
|
||||||
|
<a href="{{ route('Admin.Shop.Offers.edit', $offer['id']) }}" target="_blank" rel="noopener" class="text-decoration-none {{ $offerClass }} admin-link-group admin-offer-link">
|
||||||
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
|
<div>
|
||||||
|
<span style="opacity: 0.5;">{{ $isSelectedOffer ? '▸' : '○' }}</span>
|
||||||
|
@if ($offer['variation_name'])
|
||||||
|
{{ $offer['variation_name'] }}
|
||||||
|
@endif
|
||||||
|
- Stock: <strong class="{{ $stockClass }}">{{ $offer['stock_current'] }}</strong>
|
||||||
|
@if (!$offer['is_active'])
|
||||||
|
<span class="text-muted">(inactive)</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="text-right text-nowrap ml-2">
|
||||||
|
{{ number_format($offer['price_taxed'], 2, ',', ' ') }} €
|
||||||
|
@if ($offer['quantity'] > 1)
|
||||||
|
<span class="text-muted">(min {{ $offer['quantity'] }})</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
@endif
|
||||||
</li>
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
</ul>
|
</ul>
|
||||||
@@ -103,3 +148,67 @@
|
|||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@include('load.layout.modal')
|
@include('load.layout.modal')
|
||||||
|
|
||||||
|
@if (auth('web')->check() && !empty($article['available_sale_channels']))
|
||||||
|
@push('styles')
|
||||||
|
<style>
|
||||||
|
#article-admin-offers .admin-link-group {
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#article-admin-offers .admin-price-link {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin: -2px -4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#article-admin-offers .admin-offer-link {
|
||||||
|
display: block;
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin: -2px -4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#article-admin-offers .admin-link-group:hover,
|
||||||
|
#article-admin-offers .admin-link-group:focus,
|
||||||
|
#article-admin-offers .admin-link-group.linked-hover {
|
||||||
|
background-color: rgba(0, 123, 255, 0.1);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@endpush
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const container = document.getElementById('article-admin-offers');
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const links = Array.from(container.querySelectorAll('a.admin-link-group[href]'));
|
||||||
|
const grouped = new Map();
|
||||||
|
|
||||||
|
links.forEach((link) => {
|
||||||
|
const href = link.getAttribute('href');
|
||||||
|
if (!grouped.has(href)) {
|
||||||
|
grouped.set(href, []);
|
||||||
|
}
|
||||||
|
grouped.get(href).push(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
grouped.forEach((group) => {
|
||||||
|
group.forEach((link) => {
|
||||||
|
const addHighlight = () => group.forEach((item) => item.classList.add('linked-hover'));
|
||||||
|
const removeHighlight = () => group.forEach((item) => item.classList.remove('linked-hover'));
|
||||||
|
|
||||||
|
link.addEventListener('mouseenter', addHighlight);
|
||||||
|
link.addEventListener('mouseleave', removeHighlight);
|
||||||
|
link.addEventListener('focus', addHighlight);
|
||||||
|
link.addEventListener('blur', removeHighlight);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
@endif
|
||||||
|
|||||||
Reference in New Issue
Block a user