10.April 2026

This commit is contained in:
Kevin Adametz 2026-04-10 17:15:27 +02:00
parent a00c42e770
commit f58c709945
208 changed files with 19280 additions and 2914 deletions

View file

@ -0,0 +1,111 @@
@php
$chartId = 'aboChart_' . str_replace('.', '', microtime(true));
$chartDataJson = json_encode(array_values($chartData));
$chartLabelsJson = json_encode(array_values($chartMonths));
$nonNullValues = array_filter($chartData, fn($v) => $v !== null);
$maxVal = count($nonNullValues) ? max($nonNullValues) : 0;
// Großzügige Schritte für die Y-Achse
if ($maxVal <= 5) { $stepSize = 1; }
elseif ($maxVal <= 10) { $stepSize = 2; }
elseif ($maxVal <= 20) { $stepSize = 5; }
elseif ($maxVal <= 50) { $stepSize = 10; }
elseif ($maxVal <= 100) { $stepSize = 20; }
elseif ($maxVal <= 200) { $stepSize = 50; }
elseif ($maxVal <= 500) { $stepSize = 100; }
elseif ($maxVal <= 1000) { $stepSize = 200; }
else { $stepSize = 500; }
// Genug Platz nach oben, damit das Label nicht abgeschnitten wird
$suggestedMax = $maxVal + $stepSize;
@endphp
<div class="card mb-4">
<div class="card-body pb-2">
<div class="d-flex justify-content-between align-items-center flex-wrap">
<h6 class="font-weight-bold mb-2">
{{ __('abo.chart_monthly_abos') }}
<span class="text-muted font-weight-normal ml-1">{{ $chartYear }}</span>
</h6>
<div class="mb-2">
@foreach($chartYears as $y)
<a href="{{ url()->current() }}?year={{ $y }}"
class="btn btn-xs btn-sm {{ $chartYear == $y ? 'btn-secondary' : 'btn-outline-secondary' }} mr-1 mb-1">
{{ $y }}
</a>
@endforeach
</div>
</div>
</div>
<hr class="m-0">
<div class="card-body pt-3 pb-2">
<canvas id="{{ $chartId }}" height="80"></canvas>
</div>
</div>
<script src="/vendor/libs/chartjs/chartjs.js"></script>
<script>
(function () {
// Inline data-labels plugin für Chart.js v2
var dataLabelsPlugin = {
afterDatasetsDraw: function (chart) {
var ctx = chart.ctx;
chart.data.datasets.forEach(function (dataset, i) {
var meta = chart.getDatasetMeta(i);
if (meta.hidden) { return; }
meta.data.forEach(function (bar, index) {
var value = dataset.data[index];
if (value === null || value === undefined || value === 0) { return; }
ctx.save();
ctx.fillStyle = '#495057';
ctx.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillText(value, bar._model.x, bar._model.y - 3);
ctx.restore();
});
});
}
};
var ctx = document.getElementById('{{ $chartId }}').getContext('2d');
new Chart(ctx, {
type: 'bar',
plugins: [dataLabelsPlugin],
data: {
labels: {!! $chartLabelsJson !!},
datasets: [{
label: '{{ __('abo.chart_active_abos') }}',
data: {!! $chartDataJson !!},
backgroundColor: 'rgba(102, 110, 232, 0.2)',
borderColor: 'rgba(102, 110, 232, 0.8)',
borderWidth: 1
}]
},
options: {
responsive: true,
legend: { display: false },
tooltips: { enabled: false },
layout: {
padding: { top: 20 }
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stepSize: {{ $stepSize }},
suggestedMax: {{ $suggestedMax }},
callback: function (value) {
return Number.isInteger(value) ? value : null;
}
},
gridLines: { color: 'rgba(0,0,0,0.05)' }
}],
xAxes: [{
gridLines: { display: false }
}]
}
}
});
}());
</script>

View file

@ -1,27 +1,39 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
<a href="{{route('user_abos', [$view])}}" class="btn btn-sm btn-default float-right">{{ __('back') }}</a>
<a href="{{ route('user_abos', [$view]) }}" class="btn btn-sm btn-default float-right">{{ __('back') }}</a>
<div>
@if($view === 'ot') {{ __('navigation.customerabo') }} @endif
@if($view === 'me') {{ __('navigation.myabo') }} @endif
<span class="text-muted">{{ '#'.$user_abo->payone_userid }}</span>
@if ($view === 'ot')
{{ __('navigation.customerabo') }}
@endif
@if ($view === 'me')
{{ __('navigation.myabo') }}
@endif
<span class="text-muted">{{ '#' . $user_abo->payone_userid }}</span>
</div>
</h4>
@if(Session::has('alert-error'))
<div class="col-sm-12">
<div class="alert alert-danger p-2 mt-2">
<ul>
<li>{{ Session::get('alert-error') }}</li>
</ul>
</div>
</div>
@endif
@if (Session::has('alert-error'))
<div class="col-sm-12">
<div class="alert alert-danger p-2 mt-2">
<ul>
<li>{{ Session::get('alert-error') }}</li>
</ul>
</div>
</div>
@endif
@if (Session::has('alert-warning'))
<div class="col-sm-12">
<div class="alert alert-warning p-2 mt-2">
<ul>
<li>{{ Session::get('alert-warning') }}</li>
</ul>
</div>
</div>
@endif
<div class="card">
@include('admin.abo._detail')
</div>
@ -34,13 +46,18 @@
@php
$addOnlyMode = App\Services\AboHelper::isAddOnlyMode($user_abo, $view);
@endphp
{!! Form::open(['action' => route('user_abos_update', [$view, $user_abo->id]), 'class' => 'form-horizontal', 'id'=>'cart-order-form', 'data-add-only-mode' => $addOnlyMode ? '1' : '0']) !!}
<input type="hidden" name="is_for" value="{{ $user_abo->is_for }}">
{!! Form::open([
'action' => route('user_abos_update', [$view, $user_abo->id]),
'class' => 'form-horizontal',
'id' => 'cart-order-form',
'data-add-only-mode' => $addOnlyMode ? '1' : '0',
]) !!}
<input type="hidden" name="is_for" value="{{ $user_abo->is_for }}">
<div class="card mt-3">
@include('admin.abo._order_abo', ['add_only_mode' => $addOnlyMode])
</div>
@if($comp_products && Yard::instance('shopping')->getNumComp() > 0)
@if ($comp_products && Yard::instance('shopping')->getNumComp() > 0)
<div id="holder_html_view_comp_product">
@include('user.order.comp_product')
</div>
@ -52,48 +69,49 @@
</div>
<a href="{{route('user_abos', [$view])}}" class="btn btn-sm btn-default float-right">{{ __('back') }}</a>
<a href="{{ route('user_abos', [$view]) }}" class="btn btn-sm btn-default float-right">{{ __('back') }}</a>
<div class="modal fade" id="modal-confirm-add" tabindex="-1" role="dialog" aria-labelledby="modal-confirm-add-label" aria-hidden="true"
data-title-add-only="{{ __('abo.confirm_add_title') }}"
data-title-normal="{{ __('abo.confirm_add_title_normal') }}"
data-warning-add-only="{{ __('abo.confirm_add_warning') }}"
data-warning-normal="{{ __('abo.confirm_add_warning_normal') }}">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-confirm-add-label"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="alert alert-warning mb-3">
<i class="fa fa-exclamation-triangle"></i> <span id="confirm-add-warning-text"></span>
<div class="modal fade" id="modal-confirm-add" tabindex="-1" role="dialog" aria-labelledby="modal-confirm-add-label"
aria-hidden="true" data-title-add-only="{{ __('abo.confirm_add_title') }}"
data-title-normal="{{ __('abo.confirm_add_title_normal') }}"
data-warning-add-only="{{ __('abo.confirm_add_warning') }}"
data-warning-normal="{{ __('abo.confirm_add_warning_normal') }}">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-confirm-add-label"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="alert alert-warning mb-3">
<i class="fa fa-exclamation-triangle"></i> <span id="confirm-add-warning-text"></span>
</div>
<table class="table table-sm mb-0">
<tr>
<td class="font-weight-bold">{{ __('order.article') }}:</td>
<td id="confirm-add-product-name"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('tables.price') }}:</td>
<td id="confirm-add-product-price"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('tables.quantity') }}:</td>
<td id="confirm-add-qty-info"></td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">{{ __('abo.confirm_add_cancel') }}</button>
<button type="button" class="btn btn-primary"
id="confirm-add-btn">{{ __('abo.confirm_add_ok') }}</button>
</div>
<table class="table table-sm mb-0">
<tr>
<td class="font-weight-bold">{{ __('order.article') }}:</td>
<td id="confirm-add-product-name"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('tables.price') }}:</td>
<td id="confirm-add-product-price"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('tables.quantity') }}:</td>
<td id="confirm-add-qty-info"></td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ __('abo.confirm_add_cancel') }}</button>
<button type="button" class="btn btn-primary" id="confirm-add-btn">{{ __('abo.confirm_add_ok') }}</button>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@ -105,4 +123,4 @@
</script>
@endsection
@endsection

View file

@ -9,6 +9,10 @@
</div>
</h4>
@if(isset($chartData))
@include('user.abo._abo_chart')
@endif
<div class="row">
<div class="col-12">
@ -82,8 +86,13 @@
{{ $user_abo->getCountOrders() }}
</div>
<div class="col-md-3 mb-3">
<div class="text-muted small">{{ __('tables.amount') }}</div>
{{ $user_abo->getFormattedAmount() }}
@if($view === 'ot')
<div class="text-muted small">{{ __('navigation.points') }}</div>
<strong class="text-primary">{{ $user_abo->getFormattedTotalPoints() }} Pkt.</strong>
@else
<div class="text-muted small">{{ __('tables.amount') }}</div>
{{ $user_abo->getFormattedAmount() }}
@endif
</div>
<div class="col-md-3 mb-3">
<div class="text-muted small">{{ __('tables.payment') }}</div>

View file

@ -0,0 +1,98 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
{{ $incentive->name }} - {{ __('incentive.my_calculation') }}
<a href="{{ route('user_incentive_show', [$incentive->slug]) }}" class="btn btn-sm btn-outline-primary float-right">
<span class="fa fa-arrow-left"></span> {{ __('incentive.back_to_ranking') }}
</a>
</h4>
{{-- Zusammenfassung --}}
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.total_points') }}</h5>
<h2 class="font-weight-bold">{{ number_format($participant->total_points, 0, ',', '.') }}</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.rank') }}</h5>
<h2 class="font-weight-bold">{{ $participant->rank ?? '-' }}</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.partners') }}</h5>
<h2 class="font-weight-bold {{ $participant->qualified_partners >= $incentive->min_direct_partners ? 'text-success' : '' }}">
{{ $participant->qualified_partners }}/{{ $incentive->min_direct_partners }}
</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.abos') }}</h5>
<h2 class="font-weight-bold {{ $participant->qualified_abos >= $incentive->min_customer_abos ? 'text-success' : '' }}">
{{ $participant->qualified_abos }}/{{ $incentive->min_customer_abos }}
</h2>
</div>
</div>
</div>
</div>
{{-- Sektion A: Neupartner-Punkte --}}
<div class="card mb-4">
<div class="card-header">
<strong>{{ __('incentive.section_partners') }}</strong>
</div>
<div class="card-body p-0">
@include('partials.incentive._source_table', [
'sources' => $partner_sources,
'type' => 'partner',
'label_header' => __('incentive.new_partner'),
'date_header' => __('incentive.entry_date'),
'empty_message' => __('incentive.no_partners_yet'),
])
</div>
</div>
{{-- Sektion B: Kundenabo-Punkte --}}
<div class="card mb-4">
<div class="card-header">
<strong>{{ __('incentive.section_abos') }}</strong>
</div>
<div class="card-body p-0">
@include('partials.incentive._source_table', [
'sources' => $abo_sources,
'type' => 'abo',
'label_header' => __('incentive.customer_abo'),
'date_header' => __('incentive.abo_date'),
'empty_message' => __('incentive.no_abos_yet'),
])
</div>
</div>
{{-- Gesamtpunkte --}}
<div class="card border-primary">
<div class="card-body text-center">
<h4>{{ __('incentive.total_points') }}:
<strong>{{ number_format($participant->total_points, 0, ',', '.') }}</strong></h4>
@if ($participant->is_qualified)
<span class="badge badge-success badge-lg p-2">{{ __('incentive.qualified') }}</span>
@if ($participant->isWinner())
<span class="badge badge-warning badge-lg p-2">{{ __('incentive.winner') }}</span>
@endif
@else
<span class="badge badge-secondary badge-lg p-2">{{ __('incentive.not_yet_qualified') }}</span>
@endif
</div>
</div>
@endsection

View file

@ -0,0 +1,953 @@
@extends('layouts.layout-2')
@section('styles')
<style>
.inc-header {
background: linear-gradient(135deg, #6b7758 0%, #4a5340 100%);
border-radius: .75rem;
color: #fff;
padding: 1.5rem 2rem;
position: relative;
overflow: hidden;
}
.inc-header::before {
content: '';
position: absolute;
top: -60%;
right: -20%;
width: 300px;
height: 300px;
background: rgba(215, 215, 0, 0.08);
border-radius: 50%;
}
.inc-header h5 {
margin: 0;
font-weight: 700;
}
.inc-header .stat-box {
text-align: center;
padding: 0 1rem;
}
.inc-header .stat-label {
font-size: .68rem;
text-transform: uppercase;
letter-spacing: .05em;
opacity: .75;
}
.inc-header .stat-value {
font-size: 1.5rem;
font-weight: 800;
line-height: 1.1;
}
.inc-header .badge-qual {
background: rgba(215, 215, 0, 0.85);
color: #333;
font-weight: 700;
padding: .35rem .8rem;
border-radius: 50px;
font-size: .8rem;
}
.inc-header .badge-open {
background: rgba(255, 255, 255, 0.2);
color: #fff;
font-weight: 600;
padding: .35rem .8rem;
border-radius: 50px;
font-size: .8rem;
}
.inc-header .btn-details {
background: rgba(255, 255, 255, 0.15);
color: #fff;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 50px;
padding: .4rem 1.2rem;
font-weight: 600;
font-size: .85rem;
transition: background .2s;
}
.inc-header .btn-details:hover {
background: rgba(255, 255, 255, 0.25);
color: #fff;
text-decoration: none;
}
.inc-toggle-bar {
background: #f4f5f0;
border-radius: 0 0 .75rem .75rem;
margin-top: -0.75rem;
padding-top: .75rem;
}
.inc-toggle-bar .btn {
border: none;
color: #6b7758;
font-weight: 600;
}
.inc-info-tile {
border: none;
border-radius: .75rem;
overflow: hidden;
transition: transform .2s ease, box-shadow .2s ease;
}
.inc-info-tile:hover {
transform: translateY(-3px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}
.inc-tile-icon {
width: 48px;
height: 48px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3rem;
color: #fff;
flex-shrink: 0;
}
.inc-tile-icon-calendar {
background: linear-gradient(135deg, #6b7758, #4a5340);
}
.inc-tile-icon-plane {
background: linear-gradient(135deg, #6b7758, #8a9a70);
}
.inc-tile-icon-star {
background: linear-gradient(135deg, #b8b800, #d7d700);
}
.inc-hero-img {
border-radius: .75rem;
overflow: hidden;
position: relative;
}
.inc-hero-img img {
width: 100%;
max-height: 350px;
object-fit: cover;
display: block;
}
.inc-hero-img .hero-gradient {
position: absolute;
inset: 0;
background: linear-gradient(to bottom, transparent 50%, rgba(0, 0, 0, 0.4) 100%);
}
.inc-intro {
background: linear-gradient(135deg, #6b7758 0%, #4a5340 100%);
color: #fff;
border-radius: .75rem;
padding: 1.5rem 2rem;
font-size: 1rem;
line-height: 1.65;
position: relative;
overflow: hidden;
}
.inc-intro::before {
content: '\201C';
position: absolute;
top: -10px;
left: 12px;
font-size: 6rem;
opacity: .1;
font-family: Georgia, serif;
line-height: 1;
}
.inc-points-section {
background: #f4f5f0;
border-radius: .75rem;
padding: 1.5rem;
}
.inc-point-card {
background: #fff;
border-radius: .75rem;
padding: 1.2rem;
height: 100%;
border-left: 4px solid;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.inc-point-card-partner {
border-left-color: #6b7758;
}
.inc-point-card-abo {
border-left-color: #d7d700;
}
.inc-point-card .inc-point-val {
font-size: 1.4rem;
font-weight: 800;
line-height: 1;
}
.inc-point-card-partner .inc-point-val {
color: #6b7758;
}
.inc-point-card-abo .inc-point-val {
color: #9a9a00;
}
.inc-point-card li {
font-size: .88rem;
color: #555;
margin-bottom: .25rem;
}
.inc-participate {
background: linear-gradient(135deg, #6b7758 0%, #4a5340 100%);
border-radius: .75rem;
color: #fff;
padding: 2rem;
position: relative;
overflow: hidden;
}
.inc-participate::before {
content: '';
position: absolute;
bottom: -40%;
left: -20%;
width: 250px;
height: 250px;
background: rgba(215, 215, 0, 0.08);
border-radius: 50%;
}
.inc-participate h5 {
font-weight: 700;
}
.inc-participate .btn-participate {
background: #d7d700;
color: #333;
border: none;
border-radius: 50px;
font-weight: 700;
padding: .6rem 1.5rem;
transition: transform .2s, box-shadow .2s;
}
.inc-participate .btn-participate:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.inc-participate .custom-control-label {
color: rgba(255, 255, 255, 0.9);
font-size: .9rem;
}
.inc-participate a {
color: #d7d700;
}
.inc-terms-toggle {
border: none;
border-radius: .75rem;
overflow: hidden;
}
.inc-terms-toggle .card-header {
background: #f4f5f0;
border: none;
padding: .8rem 1.2rem;
}
.inc-terms-toggle .card-header a {
text-decoration: none;
font-weight: 600;
color: #555;
}
.inc-ranking-card {
border: none;
border-radius: .75rem;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.inc-ranking-card .card-header {
background: #f4f5f0;
border: none;
padding: 1.2rem 1.5rem;
}
.inc-ranking-card .ranking-title {
font-weight: 700;
font-size: 1.1rem;
color: #333;
}
.inc-ranking-card .badge-top {
background: linear-gradient(135deg, #6b7758, #4a5340);
color: #fff;
border-radius: 50px;
padding: .3rem .7rem;
font-size: .75rem;
font-weight: 700;
}
.inc-ranking-card .hint-text {
font-size: .8rem;
color: #888;
line-height: 1.45;
}
.inc-ranking-table {
margin: 0;
}
.inc-ranking-table thead th {
background: #fafaf6;
border-top: none;
font-size: .78rem;
text-transform: uppercase;
letter-spacing: .04em;
color: #6b7758;
padding: .75rem 1rem;
}
.inc-ranking-table tbody td {
padding: .75rem 1rem;
vertical-align: middle;
}
.inc-ranking-table .row-winner {
background: rgba(215, 215, 0, 0.08);
}
.inc-ranking-table .row-me {
background: rgba(107, 119, 88, 0.1);
}
.inc-ranking-table .rank-trophy {
color: #d7d700;
font-size: 1.2rem;
}
.inc-ranking-table .badge-winner {
background: linear-gradient(135deg, #b8b800, #d7d700);
color: #333;
font-weight: 700;
border-radius: 50px;
padding: .25rem .6rem;
font-size: .72rem;
}
.inc-ranking-table .badge-qualified {
background: #6b7758;
color: #fff;
font-weight: 600;
border-radius: 50px;
padding: .25rem .6rem;
font-size: .72rem;
}
.inc-ranking-table .badge-open-status {
background: #e8e8e8;
color: #777;
font-weight: 600;
border-radius: 50px;
padding: .25rem .6rem;
font-size: .72rem;
}
.inc-ranking-table .check-ok {
color: #6b7758;
font-weight: 700;
}
.inc-ranking-table .badge-me {
background: #6b7758;
color: #fff;
border-radius: 50px;
padding: .15rem .5rem;
font-size: .7rem;
font-weight: 600;
}
.pending-banner {
background: rgba(215, 215, 0, 0.12);
border: 1px solid rgba(215, 215, 0, 0.3);
border-radius: .75rem;
padding: 1rem 1.5rem;
color: #555;
}
.pending-banner i {
color: #9a9a00;
}
@media (max-width: 767px) {
.inc-header {
padding: 1.2rem;
}
.inc-header .stat-box {
padding: 0 .5rem;
}
.inc-participate {
padding: 1.5rem;
}
}
</style>
@endsection
@section('content')
{{-- Flash-Nachrichten --}}
@if (Session::has('alert-success'))
<div class="alert alert-success">{{ Session::get('alert-success') }}</div>
@endif
@if (Session::has('alert-error'))
<div class="alert alert-danger">{{ Session::get('alert-error') }}</div>
@endif
@if (Session::has('alert-info'))
<div class="alert alert-info">{{ Session::get('alert-info') }}</div>
@endif
@if ($hasConfirmedParticipation)
{{-- ============================================================
ANSICHT: TEILNEHMER kompakter Header, Info eingeklappt
============================================================ --}}
<div class="inc-header mb-0">
<div class="d-flex align-items-center justify-content-between flex-wrap">
<div class="d-flex align-items-center mr-3 mb-2 mb-md-0">
<i class="ion ion-md-trophy mr-2" style="font-size: 1.5rem; color: #d7d700;"></i>
<h5>{{ $incentive->name }}</h5>
</div>
<div class="d-flex align-items-center flex-wrap">
@if ($participant->rank)
<div class="stat-box">
<div class="stat-label">{{ __('incentive.your_rank') }}</div>
<div class="stat-value">{{ $participant->rank }}</div>
</div>
@endif
<div class="stat-box">
<div class="stat-label">{{ __('incentive.total_points') }}</div>
<div class="stat-value">{{ number_format($participant->total_points, 0, ',', '.') }}</div>
</div>
<div class="mr-3">
@if ($participant->is_qualified)
<span class="badge-qual">{{ __('incentive.qualified') }}</span>
@else
<span class="badge-open">{{ __('incentive.not_yet_qualified') }}</span>
@endif
</div>
<a href="{{ route('user_incentive_details', [$incentive->slug]) }}" class="btn-details">
<i class="ion ion-md-stats mr-1"></i>{{ __('incentive.my_details') }}
</a>
</div>
</div>
</div>
<div class="inc-toggle-bar mb-3">
<button type="button" id="incentive-info-toggle" class="btn btn-block py-2 text-center" data-toggle="collapse"
data-target="#incentive-info" aria-expanded="false" aria-controls="incentive-info">
<i class="ion ion-md-arrow-dropdown mr-1" id="incentive-info-icon"></i>
<span id="incentive-info-label">{{ __('incentive.read_more') }}</span>
</button>
</div>
{{-- Eingeklappter Info-Bereich --}}
<div id="incentive-info" class="collapse mb-3">
@if ($incentive->image)
<div class="inc-hero-img mb-4">
<img src="{{ asset('img/incentive/' . $incentive->image) }}" alt="{{ $incentive->name }}">
<div class="hero-gradient"></div>
</div>
@endif
@if ($incentive->getLang('description'))
<div class="inc-intro mb-4">
{!! $incentive->getLang('description') !!}
</div>
@endif
<div class="row">
<div class="col-lg-8">
{{-- Qualifikationszeitraum --}}
<div class="card inc-info-tile shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-start">
<div class="inc-tile-icon inc-tile-icon-calendar mr-3">
<i class="ion ion-md-calendar"></i>
</div>
<div>
<h6 class="font-weight-bold mb-2">{{ __('incentive.section_period') }}</h6>
<ul class="list-unstyled mb-0">
<li class="mb-1">
<strong>{{ __('incentive.qualification_period') }}:</strong>
{{ $incentive->qualification_start->format('d.m.Y') }}
{{ $incentive->qualification_end->format('d.m.Y') }}
</li>
<li class="text-muted">
<strong>{{ __('incentive.calculation_period') }}:</strong>
{{ __('incentive.calculation_period_hint', ['date' => $incentive->calculation_end->format('d.m.Y')]) }}
</li>
</ul>
</div>
</div>
</div>
</div>
{{-- Mindestqualifikation --}}
<div class="card inc-info-tile shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-start">
<div class="inc-tile-icon inc-tile-icon-plane mr-3">
<i class="ion ion-md-airplane"></i>
</div>
<div>
<h6 class="font-weight-bold mb-2">{{ __('incentive.section_min_qual') }}</h6>
<p class="text-muted small mb-2">{{ __('incentive.min_qual_intro') }}</p>
<ul class="list-unstyled mb-2">
<li class="mb-1">
<strong style="color: #6b7758;">{{ $incentive->min_direct_partners }}</strong>
{{ __('incentive.min_partners_label') }}
</li>
<li>
<strong style="color: #6b7758;">{{ $incentive->min_customer_abos }}</strong>
{{ __('incentive.min_abos_label') }}
</li>
</ul>
<p class="text-muted small mb-0">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.min_qual_ranking_hint') }}
</p>
</div>
</div>
</div>
</div>
{{-- Punkte-System --}}
<div class="inc-points-section mb-4">
<h6 class="font-weight-bold mb-3">
<i class="ion ion-md-star mr-1" style="color: #d7d700;"></i>
{{ __('incentive.section_points') }}
</h6>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="inc-point-card inc-point-card-partner">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
<i class="ion ion-md-person-add mr-1"></i>
{{ __('incentive.points_partners_title') }}
</h6>
<div class="inc-point-val mb-2">
{{ number_format($incentive->points_partner_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</div>
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_starter_package_label') }}</li>
<li>{{ __('incentive.points_partner_boost') }}</li>
</ul>
</div>
</div>
<div class="col-md-6">
<div class="inc-point-card inc-point-card-abo">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
<i class="ion ion-md-ribbon mr-1"></i>
{{ __('incentive.points_abos_title') }}
</h6>
<div class="inc-point-val mb-2">
{{ number_format($incentive->points_abo_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</div>
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_abo_direct') }}</li>
<li>{{ __('incentive.points_abo_boost') }}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
@if ($incentive->getLang('terms'))
<div class="card inc-terms-toggle mb-4">
<div class="card-header">
<a href="#terms" data-toggle="collapse" class="d-flex align-items-center">
<i class="ion ion-md-document mr-2"></i>
<strong>{{ __('incentive.terms') }}</strong>
<i class="ion ion-md-chevron-down ml-auto"></i>
</a>
</div>
<div id="terms" class="collapse">
<div class="card-body">
<div class="small">{!! $incentive->getLang('terms') !!}</div>
</div>
</div>
</div>
@endif
</div>
</div>
</div>
<script>
(function() {
var collapse = document.getElementById('incentive-info');
var icon = document.getElementById('incentive-info-icon');
var label = document.getElementById('incentive-info-label');
if (collapse) {
collapse.addEventListener('show.bs.collapse', function() {
icon.className = 'ion ion-md-arrow-dropup mr-1';
label.textContent = '{{ __('incentive.read_less') }}';
});
collapse.addEventListener('hide.bs.collapse', function() {
icon.className = 'ion ion-md-arrow-dropdown mr-1';
label.textContent = '{{ __('incentive.read_more') }}';
});
}
})();
</script>
@else
{{-- ============================================================
ANSICHT: NOCH KEINE ZUSTIMMUNG (Punkte laufen) ODER KEIN TEILNEHMER
============================================================ --}}
@if ($participant)
<div class="inc-header mb-3">
<div class="d-flex align-items-center justify-content-between flex-wrap">
<div class="d-flex align-items-center mr-3 mb-2 mb-md-0">
<i class="ion ion-md-trophy mr-2" style="font-size: 1.5rem; color: #d7d700;"></i>
<h5>{{ $incentive->name }}</h5>
</div>
<div class="d-flex align-items-center flex-wrap">
@if ($participant->rank)
<div class="stat-box">
<div class="stat-label">{{ __('incentive.your_rank') }}</div>
<div class="stat-value">{{ $participant->rank }}</div>
</div>
@endif
<div class="stat-box">
<div class="stat-label">{{ __('incentive.total_points') }}</div>
<div class="stat-value">{{ number_format($participant->total_points, 0, ',', '.') }}</div>
</div>
<div>
@if ($participant->is_qualified)
<span class="badge-qual">{{ __('incentive.qualified') }}</span>
@else
<span class="badge-open">{{ __('incentive.not_yet_qualified') }}</span>
@endif
</div>
</div>
</div>
</div>
<div class="pending-banner mb-4">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.pending_confirmation_banner') }}
</div>
@endif
@if ($incentive->image)
<div class="inc-hero-img mb-4">
<img src="{{ asset('img/incentive/' . $incentive->image) }}" alt="{{ $incentive->name }}"
style="max-height: 450px;">
<div class="hero-gradient"></div>
</div>
@endif
<h3 class="font-weight-bold mb-3">
<i class="ion ion-md-trophy mr-2" style="color: #d7d700;"></i>{{ $incentive->name }}
</h3>
@if ($incentive->getLang('description'))
<div class="inc-intro mb-4">
{!! $incentive->getLang('description') !!}
</div>
@endif
<div class="row">
<div class="col-lg-8">
{{-- Qualifikationszeitraum --}}
<div class="card inc-info-tile shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-start">
<div class="inc-tile-icon inc-tile-icon-calendar mr-3">
<i class="ion ion-md-calendar"></i>
</div>
<div>
<h6 class="font-weight-bold mb-2">{{ __('incentive.section_period') }}</h6>
<ul class="list-unstyled mb-0">
<li class="mb-1">
<strong>{{ __('incentive.qualification_period') }}:</strong>
{{ $incentive->qualification_start->format('d.m.Y') }}
{{ $incentive->qualification_end->format('d.m.Y') }}
</li>
<li class="text-muted">
<strong>{{ __('incentive.calculation_period') }}:</strong>
{{ __('incentive.calculation_period_hint', ['date' => $incentive->calculation_end->format('d.m.Y')]) }}
</li>
</ul>
</div>
</div>
</div>
</div>
{{-- Mindestqualifikation --}}
<div class="card inc-info-tile shadow-sm mb-4">
<div class="card-body">
<div class="d-flex align-items-start">
<div class="inc-tile-icon inc-tile-icon-plane mr-3">
<i class="ion ion-md-airplane"></i>
</div>
<div>
<h6 class="font-weight-bold mb-2">{{ __('incentive.section_min_qual') }}</h6>
<p class="text-muted small mb-2">{{ __('incentive.min_qual_intro') }}</p>
<ul class="list-unstyled mb-2">
<li class="mb-1">
<strong style="color: #6b7758;">{{ $incentive->min_direct_partners }}</strong>
{{ __('incentive.min_partners_label') }}
</li>
<li>
<strong style="color: #6b7758;">{{ $incentive->min_customer_abos }}</strong>
{{ __('incentive.min_abos_label') }}
</li>
</ul>
<p class="text-muted small mb-0">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.min_qual_ranking_hint') }}
</p>
</div>
</div>
</div>
</div>
{{-- Punkte-System --}}
<div class="inc-points-section mb-4">
<h6 class="font-weight-bold mb-3">
<i class="ion ion-md-star mr-1" style="color: #d7d700;"></i>
{{ __('incentive.section_points') }}
</h6>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="inc-point-card inc-point-card-partner">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
<i class="ion ion-md-person-add mr-1"></i>
{{ __('incentive.points_partners_title') }}
</h6>
<div class="inc-point-val mb-2">
{{ number_format($incentive->points_partner_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</div>
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_partner_boost') }}</li>
</ul>
</div>
</div>
<div class="col-md-6">
<div class="inc-point-card inc-point-card-abo">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
<i class="ion ion-md-ribbon mr-1"></i>
{{ __('incentive.points_abos_title') }}
</h6>
<div class="inc-point-val mb-2">
{{ number_format($incentive->points_abo_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</div>
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_abo_boost') }}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="inc-participate mb-4">
<h5>
<i class="ion ion-md-log-in mr-1"></i>
{{ __('incentive.participate_title') }}
</h5>
@if ($incentive->isActive())
<p class="small mb-3" style="opacity: .85;">{{ __('incentive.participate_intro') }}</p>
@if (!empty($participateHasTrackableAbos))
<div class="mb-3 p-2 small" style="background: rgba(255,255,255,0.12); border-radius: .5rem;">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.participate_abo_hint') }}
</div>
@endif
<form action="{{ route('user_incentive_participate', [$incentive->slug]) }}" method="POST">
@csrf
<div class="custom-control custom-checkbox mb-3">
<input type="checkbox" class="custom-control-input" id="accept_terms"
name="accept_terms" value="1" required>
<label class="custom-control-label" for="accept_terms">
{{ __('incentive.accept_terms') }}
@if ($incentive->getLang('terms'))
(<a href="#terms" data-toggle="collapse">{{ __('incentive.show_terms') }}</a>)
@endif
</label>
</div>
<button type="submit" class="btn btn-participate btn-block">
<i class="ion ion-md-checkmark mr-1"></i>
{{ __('incentive.participate_now') }}
</button>
</form>
@else
<p class="mb-0" style="opacity: .8;">{{ __('incentive.not_active') }}</p>
@endif
</div>
@if ($incentive->getLang('terms'))
<div class="card inc-terms-toggle mb-4">
<div class="card-header">
<a href="#terms" data-toggle="collapse" class="d-flex align-items-center">
<i class="ion ion-md-document mr-2"></i>
<strong>{{ __('incentive.terms') }}</strong>
<i class="ion ion-md-chevron-down ml-auto"></i>
</a>
</div>
<div id="terms" class="collapse">
<div class="card-body">
<div class="small">{!! $incentive->getLang('terms') !!}</div>
</div>
</div>
</div>
@endif
</div>
</div>
@endif
{{-- ============================================================
LIVE-RANKING immer sichtbar
============================================================ --}}
<div class="inc-ranking-card mt-2 mb-4">
<div class="card-header">
<div class="d-flex align-items-center justify-content-between flex-wrap">
<div class="d-flex align-items-center">
<i class="ion ion-md-list mr-2" style="font-size: 1.2rem; color: #6b7758;"></i>
<span class="ranking-title">{{ __('incentive.section_ranking') }}</span>
<span class="badge-top ml-2">Top {{ $rankingDisplayLimit }}</span>
</div>
<span
class="hint-text">{{ __('incentive.ranking_winners_hint', ['n' => $incentive->max_winners]) }}</span>
</div>
<div class="hint-text mt-2">
{{ __('incentive.ranking_extended_hint', ['n' => $incentive->max_winners]) }}
<br>{{ __('incentive.ranking_anonymous_hint') }}
</div>
</div>
<div class="card-body p-0">
@if ($ranking->isEmpty())
<div class="p-4 text-center text-muted">
<i class="ion ion-md-people mb-2 d-block" style="font-size: 2.5rem; opacity: .4;"></i>
{{ __('incentive.no_participants_with_points') }}
</div>
@else
<div class="table-responsive">
<table class="table inc-ranking-table mb-0">
<thead>
<tr>
<th style="width: 60px;">{{ __('incentive.rank') }}</th>
<th>{{ __('incentive.consultant') }}</th>
<th class="text-right">{{ __('incentive.total_points') }}</th>
<th class="text-center">{{ __('incentive.partners') }}</th>
<th class="text-center">{{ __('incentive.abos') }}</th>
<th class="text-center">{{ __('incentive.status') }}</th>
</tr>
</thead>
<tbody>
@foreach ($ranking as $p)
@php
$isWinner = $p->is_qualified && $p->rank && $p->rank <= $incentive->max_winners;
$isMe = $participant && $p->id === $participant->id;
@endphp
<tr
class="{{ $isWinner ? 'row-winner' : '' }} {{ $p->is_qualified ? 'font-weight-bold' : '' }} {{ $isMe ? 'row-me' : '' }}">
<td class="text-center">
@if ($isWinner && $p->rank && $p->rank <= 3)
<i class="ion ion-md-trophy rank-trophy"></i>
@elseif ($p->rank)
{{ $p->rank }}
@else
&mdash;
@endif
</td>
<td>
@if ($p->accepted_terms_at)
@if ($p->user && $p->user->account)
{{ $p->user->account->first_name }} {{ $p->user->account->last_name }}
@else
{{ $p->user->email ?? 'N/A' }}
@endif
@else
<span class="text-muted">{{ __('incentive.anonymous_consultant') }}</span>
@endif
@if ($isMe)
<span class="badge-me ml-1">{{ __('incentive.you') }}</span>
@endif
</td>
<td class="text-right">
<strong>{{ number_format($p->total_points, 0, ',', '.') }}</strong>
</td>
<td class="text-center">
{{ $p->qualified_partners }}/{{ $incentive->min_direct_partners }}
@if ($p->qualified_partners >= $incentive->min_direct_partners)
<span class="check-ok">&#10003;</span>
@endif
</td>
<td class="text-center">
{{ $p->qualified_abos }}/{{ $incentive->min_customer_abos }}
@if ($p->qualified_abos >= $incentive->min_customer_abos)
<span class="check-ok">&#10003;</span>
@endif
</td>
<td class="text-center">
@if ($isWinner)
<span class="badge-winner">{{ __('incentive.winner') }}</span>
@elseif ($p->is_qualified)
<span class="badge-qualified">{{ __('incentive.qualified') }}</span>
@else
<span class="badge-open-status">{{ __('incentive.open') }}</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
</div>
@endsection

View file

@ -0,0 +1,744 @@
@extends('layouts.layout-2')
@section('styles')
<style>
.incentive-hero {
margin: -1.5rem -1.5rem 0 -1.5rem;
position: relative;
overflow: hidden;
min-height: 520px;
display: flex;
align-items: flex-end;
}
.incentive-hero img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.incentive-hero .hero-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to bottom,
rgba(0, 0, 0, 0.10) 0%,
rgba(0, 0, 0, 0.25) 40%,
rgba(0, 0, 0, 0.75) 100%);
}
.incentive-hero .hero-content {
position: relative;
z-index: 2;
padding: 2.5rem;
width: 100%;
}
.incentive-hero .hero-badge {
display: inline-block;
background: rgba(215, 215, 0, 0.92);
color: #333;
font-weight: 700;
padding: .35rem 1rem;
border-radius: 50px;
font-size: .85rem;
letter-spacing: .5px;
text-transform: uppercase;
margin-bottom: 1rem;
}
.incentive-hero h1 {
font-size: 2.6rem;
font-weight: 800;
color: #fff;
text-shadow: 0 3px 12px rgba(0, 0, 0, 0.5);
margin-bottom: .5rem;
line-height: 1.15;
}
.incentive-hero .hero-subtitle {
font-size: 1.25rem;
color: rgba(255, 255, 255, 0.92);
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}
.incentive-intro {
background: linear-gradient(135deg, #6b7758 0%, #4a5340 100%);
color: #fff;
border-radius: .75rem;
padding: 2rem 2.5rem;
font-size: 1.1rem;
line-height: 1.7;
position: relative;
overflow: hidden;
}
.incentive-intro::before {
content: '\201C';
position: absolute;
top: -10px;
left: 15px;
font-size: 8rem;
opacity: .12;
font-family: Georgia, serif;
line-height: 1;
}
.incentive-intro strong {
color: #d7d700;
}
.incentive-intro .cta-line {
color: #d7d700;
font-weight: 700;
}
.info-tile {
border: none;
border-radius: .75rem;
overflow: hidden;
transition: transform .25s ease, box-shadow .25s ease;
}
.info-tile:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
}
.info-tile .tile-icon {
width: 64px;
height: 64px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1.2rem;
font-size: 1.8rem;
color: #fff;
}
.tile-icon-calendar {
background: linear-gradient(135deg, #6b7758, #4a5340);
}
.tile-icon-plane {
background: linear-gradient(135deg, #6b7758, #8a9a70);
}
.tile-icon-star {
background: linear-gradient(135deg, #b8b800, #d7d700);
}
.info-tile .tile-value {
font-size: 1.6rem;
font-weight: 700;
line-height: 1.2;
}
.info-tile .tile-label {
font-size: .82rem;
color: #888;
margin-top: .25rem;
}
.points-section {
background: #f4f5f0;
border-radius: .75rem;
padding: 2.5rem;
}
.points-section .points-heading {
font-size: 1.35rem;
font-weight: 700;
margin-bottom: 1.8rem;
text-align: center;
}
.point-card {
background: #fff;
border-radius: .75rem;
padding: 1.5rem;
height: 100%;
border-left: 4px solid;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
.point-card-partner {
border-left-color: #6b7758;
}
.point-card-abo {
border-left-color: #d7d700;
}
.point-card .point-value {
font-size: 2rem;
font-weight: 800;
line-height: 1;
margin-bottom: .5rem;
}
.point-card-partner .point-value {
color: #6b7758;
}
.point-card-abo .point-value {
color: #9a9a00;
}
.point-card ul {
padding-left: 1.1rem;
margin: 0;
}
.point-card li {
margin-bottom: .35rem;
color: #555;
font-size: .92rem;
}
.gallery-section {
overflow: hidden;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: .5rem;
}
@media (max-width: 767px) {
.gallery-grid {
grid-template-columns: repeat(2, 1fr);
}
}
.gallery-thumb {
position: relative;
overflow: hidden;
border-radius: .5rem;
cursor: pointer;
aspect-ratio: 4/3;
}
.gallery-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform .35s ease;
}
.gallery-thumb:hover img {
transform: scale(1.08);
}
.gallery-thumb::after {
content: '';
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0);
transition: background .25s ease;
border-radius: .5rem;
}
.gallery-thumb:hover::after {
background: rgba(0, 0, 0, 0.15);
}
.gallery-lightbox {
position: fixed;
inset: 0;
z-index: 9999;
background: rgba(0, 0, 0, 0.92);
display: none;
align-items: center;
justify-content: center;
}
.gallery-lightbox.active {
display: flex;
}
.gallery-lightbox img {
max-width: 90vw;
max-height: 85vh;
border-radius: .5rem;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5);
}
.gallery-lightbox .lb-close {
position: absolute;
top: 1.5rem;
right: 1.5rem;
color: #fff;
font-size: 2rem;
cursor: pointer;
background: rgba(255, 255, 255, 0.15);
border: none;
border-radius: 50%;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
transition: background .2s;
}
.gallery-lightbox .lb-close:hover {
background: rgba(255, 255, 255, 0.3);
}
.gallery-lightbox .lb-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
color: #fff;
font-size: 2.5rem;
cursor: pointer;
background: rgba(255, 255, 255, 0.10);
border: none;
border-radius: 50%;
width: 52px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
transition: background .2s;
}
.gallery-lightbox .lb-nav:hover {
background: rgba(255, 255, 255, 0.25);
}
.gallery-lightbox .lb-prev {
left: 1.5rem;
}
.gallery-lightbox .lb-next {
right: 1.5rem;
}
.cta-banner {
background: linear-gradient(135deg, #6b7758 0%, #4a5340 100%);
border-radius: .75rem;
color: #fff;
padding: 3rem 2rem;
text-align: center;
position: relative;
overflow: hidden;
}
.cta-banner::before {
content: '';
position: absolute;
top: -50%;
right: -30%;
width: 400px;
height: 400px;
background: rgba(255, 255, 255, 0.06);
border-radius: 50%;
}
.cta-banner h3 {
font-size: 1.8rem;
font-weight: 800;
margin-bottom: .5rem;
}
.cta-banner p {
opacity: .9;
font-size: 1.05rem;
}
.cta-banner .btn-light {
font-weight: 700;
padding: .65rem 2.5rem;
font-size: 1.05rem;
border-radius: 50px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
transition: transform .2s ease, box-shadow .2s ease;
}
.cta-banner .btn-light:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
.cta-banner .btn-outline-light {
font-weight: 600;
padding: .65rem 2rem;
font-size: 1.05rem;
border-radius: 50px;
border-width: 2px;
}
.cta-status-icon {
width: 72px;
height: 72px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1.2rem;
font-size: 2rem;
}
.cta-status-icon-success {
background: rgba(255, 255, 255, 0.2);
color: #d7d700;
}
.cta-status-icon-warning {
background: rgba(255, 255, 255, 0.2);
color: #d7d700;
}
.cta-status-icon-muted {
background: rgba(255, 255, 255, 0.15);
color: rgba(255, 255, 255, 0.6);
}
.terms-toggle {
border: none;
border-radius: .75rem;
overflow: hidden;
}
.terms-toggle .card-header {
background: #f4f5f0;
border: none;
padding: 1rem 1.5rem;
}
.terms-toggle .card-header a {
text-decoration: none;
font-weight: 600;
}
@media (max-width: 767px) {
.incentive-hero {
min-height: 360px;
}
.incentive-hero h1 {
font-size: 1.8rem;
}
.incentive-hero .hero-content {
padding: 1.5rem;
}
.incentive-intro {
padding: 1.5rem;
}
.points-section {
padding: 1.5rem;
}
}
</style>
@endsection
@section('content')
{{-- ===== HERO ===== --}}
@if ($incentive->image)
<div class="incentive-hero mb-5">
<img src="{{ asset('img/incentive/' . $incentive->image) }}" alt="{{ $incentive->name }}">
<div class="hero-overlay"></div>
<div class="hero-content">
<span class="hero-badge">
<i class="ion ion-md-trophy mr-1"></i> {{ __('incentive.section_period') }}:
{{ $incentive->qualification_start->format('d.m.') }} &ndash;
{{ $incentive->qualification_end->format('d.m.Y') }}
</span>
<h1>{{ $incentive->name }}</h1>
@if ($incentive->getLang('subtitle'))
<p class="hero-subtitle mb-0">{{ $incentive->getLang('subtitle') }}</p>
@endif
</div>
</div>
@else
<div class="mb-5 pt-3">
<h1 class="font-weight-bold">
<i class="ion ion-md-trophy text-warning mr-2"></i>{{ $incentive->name }}
</h1>
@if ($incentive->getLang('subtitle'))
<p class="lead text-muted">{{ $incentive->getLang('subtitle') }}</p>
@endif
</div>
@endif
{{-- ===== INTRO-TEXT ===== --}}
@if ($incentive->getLang('description'))
<div class="incentive-intro mb-5">
{!! $incentive->getLang('description') !!}
</div>
@else
<div class="incentive-intro mb-5">
<p class="mb-1">
<strong>{{ __('incentive.teaser_intro_bold') }}</strong>
{{ __('incentive.teaser_intro_text') }}
</p>
<p class="mb-0 cta-line">
{{ __('incentive.teaser_intro_cta', ['n' => $incentive->max_winners]) }}
</p>
</div>
@endif
{{-- ===== 3 INFO-KARTEN ===== --}}
<div class="row mb-5">
{{-- Qualifikationszeitraum --}}
<div class="col-md-4 mb-3 mb-md-0">
<div class="card info-tile h-100 shadow-sm">
<div class="card-body text-center py-4">
<div class="tile-icon tile-icon-calendar">
<i class="ion ion-md-calendar"></i>
</div>
<h5 class="font-weight-bold mb-3">{{ __('incentive.section_period') }}</h5>
<div class="mb-2">
<span class="tile-label d-block">{{ __('incentive.qualification_period') }}</span>
<span class="tile-value">
{{ $incentive->qualification_start->format('d.m.Y') }}
&ndash;
{{ $incentive->qualification_end->format('d.m.Y') }}
</span>
</div>
<div>
<span class="tile-label d-block">{{ __('incentive.calculation_period') }}</span>
<strong>{{ __('incentive.teaser_until') }}
{{ $incentive->calculation_end->format('d.m.Y') }}</strong>
</div>
</div>
</div>
</div>
{{-- Mindestqualifikation --}}
<div class="col-md-4 mb-3 mb-md-0">
<div class="card info-tile h-100 shadow-sm">
<div class="card-body text-center py-4">
<div class="tile-icon tile-icon-plane">
<i class="ion ion-md-airplane"></i>
</div>
<h5 class="font-weight-bold mb-3">{{ __('incentive.section_min_qual') }}</h5>
<div class="mb-3">
<div class="tile-value">{{ $incentive->min_direct_partners }}</div>
<span class="tile-label">{{ __('incentive.min_partners_label') }}</span>
</div>
<div>
<div class="tile-value">{{ $incentive->min_customer_abos }}</div>
<span class="tile-label">{{ __('incentive.min_abos_label') }}</span>
</div>
</div>
</div>
</div>
{{-- Punkte-System --}}
<div class="col-md-4">
<div class="card info-tile h-100 shadow-sm">
<div class="card-body text-center py-4">
<div class="tile-icon tile-icon-star">
<i class="ion ion-md-star"></i>
</div>
<h5 class="font-weight-bold mb-3">{{ __('incentive.section_points') }}</h5>
<div class="mb-3">
<span class="tile-value" style="color: #6b7758;">
{{ number_format($incentive->points_partner_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</span>
<span class="tile-label d-block">{{ __('incentive.points_partners_title') }}</span>
</div>
<div>
<span class="tile-value" style="color: #9a9a00;">
{{ number_format($incentive->points_abo_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</span>
<span class="tile-label d-block">{{ __('incentive.points_abos_title') }}</span>
</div>
</div>
</div>
</div>
</div>
{{-- ===== PUNKTE-DETAIL ===== --}}
<div class="points-section mb-5">
<h4 class="points-heading">
<i class="ion ion-md-star text-warning mr-1"></i>
{{ __('incentive.section_points') }}
</h4>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="point-card point-card-partner">
<h6 class="font-weight-bold mb-2">
<i class="ion ion-md-person-add mr-1"></i>
{{ __('incentive.points_partners_title') }}
</h6>
<div class="point-value">
{{ number_format($incentive->points_partner_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</div>
<ul>
<li>{{ __('incentive.teaser_partner_onetime_text') }}</li>
<li>{{ __('incentive.points_starter_package_label') }}</li>
<li>{{ __('incentive.points_partner_boost') }}</li>
</ul>
</div>
</div>
<div class="col-md-6">
<div class="point-card point-card-abo">
<h6 class="font-weight-bold mb-2">
<i class="ion ion-md-ribbon mr-1"></i>
{{ __('incentive.points_abos_title') }}
</h6>
<div class="point-value">
{{ number_format($incentive->points_abo_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</div>
<ul>
<li>{{ __('incentive.teaser_abo_onetime_text') }}</li>
<li>{{ __('incentive.points_abo_direct') }}</li>
<li>{{ __('incentive.points_abo_boost') }}</li>
</ul>
</div>
</div>
</div>
</div>
{{-- ===== BILDERGALERIE ===== --}}
@if (count($galleryImages) > 0)
<div class="gallery-section mb-5">
<h4 class="font-weight-bold mb-3 text-center">
<i class="ion ion-md-images text-primary mr-1"></i>
{{ __('incentive.gallery_title') }}
</h4>
<div class="gallery-grid">
@foreach ($galleryImages as $idx => $img)
<div class="gallery-thumb" data-gallery-index="{{ $idx }}">
<img src="{{ asset($img) }}" alt="Impression {{ $idx + 1 }}" loading="lazy">
</div>
@endforeach
</div>
</div>
{{-- Lightbox --}}
<div class="gallery-lightbox" id="galleryLightbox">
<button class="lb-close" title="Schließen">&times;</button>
<button class="lb-nav lb-prev" title="Zurück"><i class="ion ion-md-arrow-back"></i></button>
<button class="lb-nav lb-next" title="Weiter"><i class="ion ion-md-arrow-forward"></i></button>
<img src="" alt="Gallery" id="lightboxImage">
</div>
@endif
{{-- ===== CTA-BEREICH ===== --}}
<div class="cta-banner mb-5">
@if ($participant && $participant->accepted_terms_at)
<div class="cta-status-icon cta-status-icon-success">
<i class="ion ion-md-checkmark-circle"></i>
</div>
<h3>{{ __('incentive.you_participate') }}</h3>
<p class="mb-4">{{ __('incentive.teaser_cta_already_in') }}</p>
<a href="{{ route('user_incentive_show', [$incentive->slug]) }}" class="btn btn-secondary btn-lg mr-2">
<i class="ion ion-md-list mr-1"></i>{{ __('incentive.teaser_cta_to_ranking') }}
</a>
<a href="{{ route('user_incentive_details', [$incentive->slug]) }}" class="btn btn-outline-secondary btn-lg">
<i class="ion ion-md-stats mr-1"></i>{{ __('incentive.my_details') }}
</a>
@elseif ($participant)
<div class="cta-status-icon cta-status-icon-warning">
<i class="ion ion-md-trophy"></i>
</div>
<h3>{{ __('incentive.teaser_pending_title') }}</h3>
<p class="mb-4">{{ __('incentive.teaser_pending_text') }}</p>
<a href="{{ route('user_incentive_show', [$incentive->slug]) }}" class="btn btn-secondary btn-lg">
<i class="ion ion-md-checkmark mr-1"></i>{{ __('incentive.teaser_cta_confirm') }}
</a>
@elseif($incentive->isActive())
<div class="cta-status-icon cta-status-icon-warning">
<i class="ion ion-md-trophy"></i>
</div>
<h3>{{ __('incentive.teaser_cta_ready') }}</h3>
<p class="mb-4">{{ __('incentive.teaser_cta_text', ['n' => $incentive->max_winners]) }}</p>
<a href="{{ route('user_incentive_show', [$incentive->slug]) }}" class="btn btn-secondary btn-lg">
<i class="ion ion-md-checkmark mr-1"></i>{{ __('incentive.teaser_cta_button') }}
</a>
@else
<div class="cta-status-icon cta-status-icon-muted">
<i class="ion ion-md-time"></i>
</div>
<h3>{{ __('incentive.teaser_cta_coming_soon') }}</h3>
<p class="mb-0">{{ __('incentive.not_active') }}</p>
@endif
</div>
{{-- ===== TEILNAHMEBEDINGUNGEN (Collapse) ===== --}}
@if ($incentive->getLang('terms'))
<div class="card terms-toggle mb-4">
<div class="card-header">
<a href="#terms-teaser" data-toggle="collapse" class="text-body d-flex align-items-center collapsed">
<i class="ion ion-md-document mr-2"></i>
<strong>{{ __('incentive.terms') }}</strong>
<i class="ion ion-md-chevron-down ml-auto"></i>
</a>
</div>
<div id="terms-teaser" class="collapse show">
<div class="card-body">
{!! $incentive->getLang('terms') !!}
</div>
</div>
</div>
@endif
@endsection
@section('scripts')
@if (count($galleryImages) > 0)
<script>
(function() {
var images = @json(collect($galleryImages)->map(fn($i) => asset($i))->values());
var current = 0;
var lightbox = document.getElementById('galleryLightbox');
var lbImg = document.getElementById('lightboxImage');
document.querySelectorAll('.gallery-thumb').forEach(function(el) {
el.addEventListener('click', function() {
current = parseInt(el.dataset.galleryIndex);
lbImg.src = images[current];
lightbox.classList.add('active');
});
});
lightbox.querySelector('.lb-close').addEventListener('click', function() {
lightbox.classList.remove('active');
});
lightbox.querySelector('.lb-prev').addEventListener('click', function(e) {
e.stopPropagation();
current = (current - 1 + images.length) % images.length;
lbImg.src = images[current];
});
lightbox.querySelector('.lb-next').addEventListener('click', function(e) {
e.stopPropagation();
current = (current + 1) % images.length;
lbImg.src = images[current];
});
lightbox.addEventListener('click', function(e) {
if (e.target === lightbox) {
lightbox.classList.remove('active');
}
});
document.addEventListener('keydown', function(e) {
if (!lightbox.classList.contains('active')) return;
if (e.key === 'Escape') lightbox.classList.remove('active');
if (e.key === 'ArrowLeft') lightbox.querySelector('.lb-prev').click();
if (e.key === 'ArrowRight') lightbox.querySelector('.lb-next').click();
});
})();
</script>
@endif
@endsection

View file

@ -65,5 +65,11 @@
@endsection
@section('scripts')
@if ($is_abo)
<script>
window.aboIntervalWarningTemplate = @json(__('abo.warning_next_date_soon_select', ['placeholder_days' => '__DAYS__', 'placeholder_date' => '__DATE__']));
window.aboIntervalInfoTemplate = @json(__('abo.info_next_execution_select', ['placeholder_days' => '__DAYS__', 'placeholder_date' => '__DATE__']));
</script>
@endif
<script src="{{ asset('/js/iq-shopping-cart.js') }}?v=1{{ get_file_last_time('/js/iq-shopping-cart.js') }}"></script>
@endsection

View file

@ -1,369 +1,428 @@
@if (Yard::instance('shopping')->content()->count())
@if (Yard::instance('shopping')->compCount() > 0)
<style>
.yard-items-head {
border-bottom: 1px solid #ddd;
padding-bottom: 8px;
margin-bottom: 8px;
}
.yard-item {
position: relative;
padding-top: 8px;
border-bottom: 1px solid #ddd;
padding-bottom: 8px;
}
@if(Yard::instance('shopping')->content()->count())
@if(Yard::instance('shopping')->compCount() > 0)
<style>
.yard-items-head {
border-bottom: 1px solid #ddd;
padding-bottom: 8px;
margin-bottom: 8px;
}
.yard-item a.shop-item-hl {
color: #9aa983;
font-size: 1.1em;
font-weight: bold;
}
.yard-item {
position: relative;
padding-top: 8px;
border-bottom: 1px solid #ddd;
padding-bottom: 8px;
}
.yard-item .options {
margin-top: 4px;
}
.yard-item a.shop-item-hl {
color: #9aa983;
font-size: 1.1em;
font-weight: bold;
}
.yard-item a.auto-delete-product {
font-size: 0.7em;
font-weight: bold;
text-transform: uppercase;
}
.yard-item .options {
margin-top: 4px;
}
.yard-item .quantity {
position: relative;
}
.yard-item a.auto-delete-product {
font-size: 0.7em;
font-weight: bold;
text-transform: uppercase;
}
.quantity-select {
text-align: right;
}
.yard-item .quantity {
position: relative;
}
.quantity-select select.form-control:not([size]):not([multiple]) {
width: auto;
min-width: 5em;
display: inline-block;
}
.quantity-select {
text-align: right;
}
.yard-item .price-total {
margin-top: 6px;
white-space: nowrap;
}
.quantity-select select.form-control:not([size]):not([multiple]) {
width: auto;
min-width: 5em;
display: inline-block;
}
.yard-item .font-semi-bold {
color: #393939;
font-weight: 500;
}
.yard-item .price-total {
margin-top: 6px;
white-space: nowrap;
}
.yard-item .font-semi-bold .small {
font-size: 1rem;
}
.yard-item .font-semi-bold {
color: #393939;
font-weight: 500;
}
.yard-item .font-bold {
color: #393939;
font-weight: bold;
}
.yard-item .font-semi-bold .small {
font-size: 1rem;
}
.price-single {
white-space: nowrap;
}
.yard-item .font-bold {
color: #393939;
font-weight: bold;
}
.price-total {
font-weight: 600;
font-size: 0.95rem;
white-space: nowrap;
}
.price-single {
white-space: nowrap;
}
.quantity-select {
text-align: right;
}
.price-total {
font-weight: 600;
font-size: 0.95rem;
white-space: nowrap;
}
.quantity-select {
text-align: right;
}
.quantity-select input.form-control {
width: auto;
min-width: 4em;
display: inline-block;
}
.xsmall {
font-size: 85%;
font-weight: 400;
}
</style>
<div id="cartContent">
.quantity-select input.form-control {
width: auto;
min-width: 4em;
display: inline-block;
}
<div class="yard-items-head d-none d-sm-block">
<div class="row">
<div class="col-3 col-sm-2">
<div class="row">&nbsp;</div>
</div>
<div class="col-9 col-sm-10">
<div class="row">
<div class="col-12 col-sm-6 col-md-7">
{{ __('order.article') }}
</div>
<div class="col-6 col-sm-3 col-md-2 text-left">
{{ __('order.unit_price') }}
</div>
<div class="col-6 col-sm-3 col-md-3 text-right">
{{ __('order.quantity') }}
</div>
</div>
</div>
</div>
</div>
@foreach(Yard::instance('shopping')->getContentByOrder() as $row)
@php($product = \App\Models\Product::find($row->id))
<div class="row yard-item">
.xsmall {
font-size: 85%;
font-weight: 400;
}
</style>
<div id="cartContent">
<div class="col-3 col-sm-2">
@if($row->options->has('image'))
<img src="{{ route('product_image', [$row->options->image]) }}" class="d-block ui-w-80 ui-bordered mr-4" alt="">
@else
<img src="{{ asset('/assets/images/1x1.png') }}" class="d-block ui-w-80 ui-bordered mr-4" alt="">
@endif
</div>
<div class="col-9 col-sm-10">
<div class="row">
<div class="col-12 col-sm-6 col-md-7 description">
<div class="media-body">
<div class="d-block text-body" style="font-size: 15px; font-weight: 500;">{{ $row->name }} @if(isset($is_abo) && $is_abo) {!! get_abo_type_badge_by_product($product) !!} @endif</div>
<div class="text-body">
<div>{{ __('order.content') }}: {{ $product->contents }}</div>
<div>{{ __('order.art_no') }}: {{ $product->number }}</div>
<div>{{ __('order.points') }}: @if($row->options->comp) 0 @else {{ $product->getFormattedPoints() }} @endif</div>
</div>
</div>
<div class="options">
@if(!$row->options->comp)
<a href="#" class="auto-delete-product remove_item_form_cart product-tooltip" data-row-id="{{$row->rowId}}" data-product-id="{{ $product->id }}"><i class="fa fa-times"></i> {{ __('order.article_remove') }}</a>
@else
@if(Yard::instance('shopping')->getNumComp() > 1)
{{$row->options->comp}}. {{ __('order.compensation_product') }}
@else
{{ __('order.compensation_product') }}
@endif
@endif
</div>
</div>
<div class="col-6 col-sm-3 col-md-2 text-left font-semi-bold price-single">
<div class="no-line-break">{{ Yard::instance('shopping')->rowPriceNet($row, 3) }} &euro;</div>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="xsmall">~{{ Yard::instance('shopping')->getCurrencyByKey('rowPriceNetCurrency', $row, 3) }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</div>
<div class="col-6 col-sm-3 col-md-3 quantity">
<div class="quantity-select">
@if($row->options->comp)
<span class="text-right product-tooltip" data-toggle="tooltip" title="{{ __('order.compensation_product') }}">1 x</span>
@else
<input type="number" class="form-control text-center cart-input-event-onchange" data-row-id="{{$row->rowId}}" data-product-id="{{ $product->id }}" value="{{ $row->qty }}" name="quantity[{{$row->rowId}}]" maxlength="3" max="999" min="1">
@endif
</div>
<div class="price-total text-right">
<div class="no-line-break">{{ Yard::instance('shopping')->rowSubtotalNet($row) }} &euro;</div>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('rowSubtotalCurrency', $row, 3) }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</div>
</div>
</div>
</div>
</div>
@endforeach
<div class="clearfix"></div>
<div class="yard-items-head d-none d-sm-block">
<div class="row">
<div class="col-3 col-sm-2">
<div class="row">&nbsp;</div>
</div>
<!-- / Shopping cart table -->
<div class="d-flex flex-wrap justify-content-between pb-4">
<div class="mt-2">
<p class="small mb-2"> {!! __('order.you_has_article_in_shopping_cart', ['num'=> Yard::instance('shopping')->compCount() ]) !!}</p>
<p>{{ __('order.points_total') }}: {{ formatNumber(Yard::instance('shopping')->points()) }}</p>
<button type="button" class="btn btn-default btn-sm" id="clear-products-basket"><i class="ion ion-ios-trash"></i> {{ __('order.shopping_cart_delete') }}</button>
</div>
<div class="d-flex">
<div class="text-right mt-2">
<table class="table">
<tbody>
<tr>
<td class="text-left" style="border-top:none;">{{ __('order.subtotal') }}:</td>
<td style="border-top:none;">
<div class="no-line-break">{{ Yard::instance('shopping')->subtotal() }} </div>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('subtotal') }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
<tr>
<td class="text-left">{{ __('Delivery country') }}:</td>
<td>{{ Yard::instance('shopping')->getShippingCountryName() }}</td>
</tr>
<tr>
<td class="text-left">{{ __('order.shipping_costs') }}:</td>
<td>
@php($shippingFree = Yard::instance('shopping')->getShippingFree())
@php($missingValue = Yard::instance('shopping')->getShippingFreeMissingValue())
@php($currentShipping = Yard::instance('shopping')->shippingNet())
@if($shippingFree && intval($currentShipping) == 0)
{{-- Versandkostenfrei erreicht --}}
<div class="badge badge-success font-weight-bold" style="font-size: 0.80rem; padding: 0.3rem 0.4rem;">
<i class="fa fa-check-circle"></i> {{ __('order.free_shipping') }}
</div>
<div class="mt-1">
<small class="text-success font-weight-bold">
<i class="fa fa-gift"></i> {{ __('order.free_shipping_reached', ['amount' => number_format($shippingFree, 2, ',', '.')]) }}
</small>
</div>
@else
{{-- Normale Versandkosten --}}
<div class="no-line-break">{{ $currentShipping }} </div>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('shippingNet') }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
@if($shippingFree && $missingValue > 0)
{{-- Zeige wie viel noch fehlt --}}
<div class="mt-1">
<small class="text-info">
<i class="fa fa-info-circle"></i>
{{ __('order.free_shipping_info', [
'amount' => number_format($shippingFree, 2, ',', '.'),
'missing' => number_format($missingValue, 2, ',', '.')
]) }}
</small>
</div>
@endif
@endif
</td>
</tr>
<tr>
<td class="text-left">{{ __('order.total_without_VAT') }}:</td>
<td>
<div class="no-line-break"> {{ Yard::instance('shopping')->subtotalWithShipping() }} </div>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('subtotalWithShipping') }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
<tr>
<td class="text-left">{{ __('order.plus_VAT') }}:</td>
<td>
<div class="no-line-break">{{ Yard::instance('shopping')->taxWithShipping() }} </div>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('taxWithShipping') }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
@if(Yard::instance('shopping')->getUserTaxFree())
<tr>
<td class="text-left"><strong>{{ __('order.total_net') }}:</strong></td>
<td>
<strong><div class="no-line-break">{{ Yard::instance('shopping')->totalWithShipping() }} </div></strong>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('totalWithShipping') }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
@else
<tr>
<td class="text-left"><strong>{{ __('order.total_gross') }}:</strong></td>
<td>
<strong><div class="no-line-break">{{ Yard::instance('shopping')->totalWithShipping() }} </div></strong>
@if(Yard::instance('shopping')->isPriceCurrency())
<span class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('totalWithShipping') }} {{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
@endif
</tbody>
</table>
<div class="col-9 col-sm-10">
<div class="row">
<div class="col-12 col-sm-6 col-md-7">
{{ __('order.article') }}
</div>
<div class="col-6 col-sm-3 col-md-2 text-left">
{{ __('order.unit_price') }}
</div>
<div class="col-6 col-sm-3 col-md-3 text-right">
{{ __('order.quantity') }}
</div>
</div>
</div>
</div>
<hr>
</div>
@foreach (Yard::instance('shopping')->getContentByOrder() as $row)
@php($product = \App\Models\Product::find($row->id))
<div class="row yard-item">
<div class="col-3 col-sm-2">
@if ($row->options->has('image'))
<img src="{{ route('product_image', [$row->options->image]) }}"
class="d-block ui-w-80 ui-bordered mr-4" alt="">
@else
<img src="{{ asset('/assets/images/1x1.png') }}" class="d-block ui-w-80 ui-bordered mr-4"
alt="">
@endif
</div>
<div class="col-9 col-sm-10">
<div class="row">
<div class="col-12 col-sm-6 col-md-7 description">
<div class="media-body">
<div class="d-block text-body" style="font-size: 15px; font-weight: 500;">
{{ $row->name }} @if (isset($is_abo) && $is_abo)
{!! get_abo_type_badge_by_product($product) !!}
@endif
</div>
<div class="text-body">
<div>{{ __('order.content') }}: {{ $product->contents }}</div>
<div>{{ __('order.art_no') }}: {{ $product->number }}</div>
<div>{{ __('order.points') }}: @if ($row->options->comp)
0
@else
{{ $product->getFormattedPoints() }}
@endif
</div>
</div>
</div>
<div class="options">
@if (!$row->options->comp)
<a href="#"
class="auto-delete-product remove_item_form_cart product-tooltip"
data-row-id="{{ $row->rowId }}" data-product-id="{{ $product->id }}"><i
class="fa fa-times"></i> {{ __('order.article_remove') }}</a>
@else
@if (Yard::instance('shopping')->getNumComp() > 1)
{{ $row->options->comp }}. {{ __('order.compensation_product') }}
@else
{{ __('order.compensation_product') }}
@endif
@endif
</div>
</div>
<div class="col-6 col-sm-3 col-md-2 text-left font-semi-bold price-single">
<div class="no-line-break">{{ Yard::instance('shopping')->rowPriceNet($row, 3) }}
&euro;</div>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="xsmall">~{{ Yard::instance('shopping')->getCurrencyByKey('rowPriceNetCurrency', $row, 3) }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</div>
<div class="col-6 col-sm-3 col-md-3 quantity">
<div class="quantity-select">
@if ($row->options->comp)
<span class="text-right product-tooltip" data-toggle="tooltip"
title="{{ __('order.compensation_product') }}">1 x</span>
@else
<input type="number" class="form-control text-center cart-input-event-onchange"
data-row-id="{{ $row->rowId }}" data-product-id="{{ $product->id }}"
value="{{ $row->qty }}" name="quantity[{{ $row->rowId }}]"
maxlength="3" max="999" min="1">
@endif
</div>
<div class="price-total text-right">
<div class="no-line-break">{{ Yard::instance('shopping')->rowSubtotalNet($row) }}
&euro;</div>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('rowSubtotalCurrency', $row, 3) }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</div>
</div>
</div>
</div>
</div>
@endforeach
<div class="clearfix"></div>
</div>
<!-- / Shopping cart table -->
<div class="d-flex flex-wrap justify-content-between pb-4">
<div class="mt-2">
<p class="small mb-2"> {!! __('order.you_has_article_in_shopping_cart', ['num' => Yard::instance('shopping')->compCount()]) !!}</p>
<p>{{ __('order.points_total') }}: {{ formatNumber(Yard::instance('shopping')->points()) }}</p>
<button type="button" class="btn btn-default btn-sm" id="clear-products-basket"><i
class="ion ion-ios-trash"></i> {{ __('order.shopping_cart_delete') }}</button>
</div>
<div class="d-flex">
<div class="text-right mt-2">
<table class="table">
<tbody>
<tr>
<td class="text-left" style="border-top:none;">{{ __('order.subtotal') }}:</td>
<td style="border-top:none;">
<div class="no-line-break">{{ Yard::instance('shopping')->subtotal() }} </div>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('subtotal') }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
<tr>
<td class="text-left">{{ __('Delivery country') }}:</td>
<td>{{ Yard::instance('shopping')->getShippingCountryName() }}</td>
</tr>
<tr>
<td class="text-left">{{ __('order.shipping_costs') }}:</td>
<td>
@php($shippingFree = Yard::instance('shopping')->getShippingFree())
@php($missingValue = Yard::instance('shopping')->getShippingFreeMissingValue())
@php($currentShipping = Yard::instance('shopping')->shippingNet())
@if ($shippingFree && intval($currentShipping) == 0)
{{-- Versandkostenfrei erreicht --}}
<div class="badge badge-success font-weight-bold"
style="font-size: 0.80rem; padding: 0.3rem 0.4rem;">
<i class="fa fa-check-circle"></i> {{ __('order.free_shipping') }}
</div>
<div class="mt-1">
<small class="text-success font-weight-bold">
<i class="fa fa-gift"></i>
{{ __('order.free_shipping_reached', ['amount' => number_format($shippingFree, 2, ',', '.')]) }}
</small>
</div>
@else
{{-- Normale Versandkosten --}}
<div class="no-line-break">{{ $currentShipping }} </div>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('shippingNet') }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
@if ($shippingFree && $missingValue > 0)
{{-- Zeige wie viel noch fehlt --}}
<div class="mt-1">
<small class="text-info">
<i class="fa fa-info-circle"></i>
{{ __('order.free_shipping_info', [
'amount' => number_format($shippingFree, 2, ',', '.'),
'missing' => number_format($missingValue, 2, ',', '.'),
]) }}
</small>
</div>
@endif
@endif
</td>
</tr>
<tr>
<td class="text-left">{{ __('order.total_without_VAT') }}:</td>
<td>
<div class="no-line-break">
{{ Yard::instance('shopping')->subtotalWithShipping() }} </div>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('subtotalWithShipping') }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
<tr>
<td class="text-left">{{ __('order.plus_VAT') }}:</td>
<td>
<div class="no-line-break">{{ Yard::instance('shopping')->taxWithShipping() }}
</div>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('taxWithShipping') }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
@if (Yard::instance('shopping')->getUserTaxFree())
<tr>
<td class="text-left"><strong>{{ __('order.total_net') }}:</strong></td>
<td>
<strong>
<div class="no-line-break">
{{ Yard::instance('shopping')->totalWithShipping() }} </div>
</strong>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('totalWithShipping') }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
@else
<tr>
<td class="text-left"><strong>{{ __('order.total_gross') }}:</strong></td>
<td>
<strong>
<div class="no-line-break">
{{ Yard::instance('shopping')->totalWithShipping() }} </div>
</strong>
@if (Yard::instance('shopping')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('shopping')->getCurrencyByKey('totalWithShipping') }}
{{ Yard::instance('shopping')->getPriceCurrencyUnit() }} </span>
@endif
</td>
</tr>
@endif
</tbody>
</table>
</div>
</div>
</div>
<hr>
@if (isset($is_abo) && $is_abo)
<div class="text-right">
<div class="alert alert-info">
<h4>{{ __('abo.abo_settings') }}</h4>
@if(isset($is_abo) && $is_abo)
<div class="text-right">
<div class="alert alert-info">
<h4>{{ __('abo.abo_settings') }}</h4>
<div class="form-row">
<div class="col-6 col-sm-8 col-md-9 col-lg-9 mb-1">
</div>
<div class="col-12 col-sm-4 col-md-3 col-lg-3 mb-1 text-right">
<label class="form-label">{{ __('abo.delivery_day') }}*</label>
<select class="custom-select" name="abo_interval">
{!! HTMLHelper::getAboDeliveryOptions() !!}
</select>
</div>
<select class="custom-select" name="abo_interval" id="abo_interval_select">
{!! HTMLHelper::getAboDeliveryOptions() !!}
</select>
<div id="abo_interval_info" class="alert alert-info mt-2 small"></div>
<div id="abo_interval_warning" class="alert alert-warning mt-2 small d-none"></div>
</div>
<div class="col-12 col-sm-12 col-md-4 col-lg-6 mb-1">
<div class="col-12 col-sm-12 col-md-4 col-lg-6 mb-1">
</div>
<div class="col-12 col-sm-12 col-md-8 col-lg-6 mb-1">
<div class="text-right">
<em class="small"> <i> {!! __('abo.abo_order_info_check') !!}</em>
<hr style="margin-top: 10px; margin-bottom: 10px; border-color: #b4b4b4; border-width: 1px;">
<em class="font-weight-bold"> <i> {!! __('abo.abo_order_info_check_2') !!}</em>
<hr style="margin-top: 10px; margin-bottom: 10px; border-color: #b4b4b4; border-width: 1px;">
<em class="small"> <i> {!! __('abo.abo_order_info_check_3', ['abo-min-duration' => \App\Models\Setting::getContentBySlug('abo-min-duration')]) !!}</em>
<hr style="margin-top: 10px; margin-bottom: 10px; border-color: #b4b4b4; border-width: 1px;">
<hr
style="margin-top: 10px; margin-bottom: 10px; border-color: #b4b4b4; border-width: 1px;">
<em class="font-weight-bold"> <i> {!! __('abo.abo_order_info_check_2') !!}</em>
<hr
style="margin-top: 10px; margin-bottom: 10px; border-color: #b4b4b4; border-width: 1px;">
<em class="small"> <i> {!! __('abo.abo_order_info_check_3', [
'abo-min-duration' => \App\Models\Setting::getContentBySlug('abo-min-duration'),
]) !!}</em>
<hr
style="margin-top: 10px; margin-bottom: 10px; border-color: #b4b4b4; border-width: 1px;">
<label class="switcher switcher-success">
<input type="checkbox" class="switcher-input" name="abo_order_info_checkbox"
value="true" required>
<span class="switcher-indicator">
<span class="switcher-yes">
<span class="ion ion-md-checkmark"></span>
</span>
<span class="switcher-no">
<span class="ion ion-md-close"></span>
</span>
</span>
<span
class="switcher-label"><strong>{{ __('abo.abo_order_info_checkbox') }}</strong></span>
</label>
<label class="switcher switcher-success">
<input type="checkbox" class="switcher-input" name="abo_order_info_checkbox" value="true" required>
<span class="switcher-indicator">
<span class="switcher-yes">
<span class="ion ion-md-checkmark"></span>
</span>
<span class="switcher-no">
<span class="ion ion-md-close"></span>
</span>
</span>
<span class="switcher-label"><strong>{{ __('abo.abo_order_info_checkbox') }}</strong></span>
</label>
</div>
</div>
</div>
</div>
<hr>
</div>
</div>
@endif
@php($is_disabled = false)
@if(isset($is_abo) && $is_abo && !\App\Services\AboHelper::aboHasBaseProduct(Yard::instance('shopping')->getContentByOrder()))
@php($is_disabled = true)
<div class="float-right">
<div class="alert alert-danger text-right">
<strong>{!! __('abo.abo_type_info_base', ['base'=>get_abo_type_badge('base')]) !!}</strong>
</div>
</div>
<br clear="all">
@endif
@if((isset($data['for']) && $data['for'] === 'ot-customer') || (isset($for) && $for === 'abo-ot-customer'))
<div class="float-right">
<button type="submit" class="btn btn-secondary" @if($is_disabled) disabled @endif><i class="ion ion-ios-redo"></i> {{ __('order.confirm_and_send_order') }}</button>
</div>
<br><br>
<div class="text-right">
<em class="small"> <i class="fa fa-info-circle"></i> {!! __('order.confirm_send_order_info') !!}</em>
</div>
@else
<div class="float-right">
<button type="submit" class="btn btn-secondary" @if($is_disabled) disabled @endif><i class="ion ion-ios-redo"></i> {{ __('order.confirm_and_proceed_to_checkout') }}</button>
</div>
<br><br>
<div class="text-right">
<em class="small"> <i class="fa fa-lock"></i> {!! __('payment.checkout_ssl_server') !!}</em>
</div>
@endif
@endif
<hr>
</div>
@endif
@php($is_disabled = false)
@if (isset($is_abo) &&
$is_abo &&
!\App\Services\AboHelper::aboHasBaseProduct(Yard::instance('shopping')->getContentByOrder()))
@php($is_disabled = true)
<div class="float-right">
<div class="alert alert-danger text-right">
<strong>{!! __('abo.abo_type_info_base', ['base' => get_abo_type_badge('base')]) !!}</strong>
</div>
</div>
<br clear="all">
@endif
@if ((isset($data['for']) && $data['for'] === 'ot-customer') || (isset($for) && $for === 'abo-ot-customer'))
<div class="float-right">
<button type="submit" class="btn btn-secondary" @if ($is_disabled) disabled @endif><i
class="ion ion-ios-redo"></i> {{ __('order.confirm_and_send_order') }}</button>
</div>
<br><br>
<div class="text-right">
<em class="small"> <i class="fa fa-info-circle"></i> {!! __('order.confirm_send_order_info') !!}</em>
</div>
@else
<div class="float-right">
<button type="submit" class="btn btn-secondary" @if ($is_disabled) disabled @endif><i
class="ion ion-ios-redo"></i> {{ __('order.confirm_and_proceed_to_checkout') }}</button>
</div>
<br><br>
<div class="text-right">
<em class="small"> <i class="fa fa-lock"></i> {!! __('payment.checkout_ssl_server') !!}</em>
</div>
@endif
@endif
@endif

View file

@ -7,6 +7,10 @@
</div>
</h4>
@if(isset($chartData))
@include('user.abo._abo_chart')
@endif
<div class="row">
<div class="col-12">
@ -74,8 +78,8 @@
{{ $user_abo->getCountOrders() }}
</div>
<div class="col-md-3 mb-3">
<div class="text-muted small">{{ __('tables.amount') }}</div>
{{ $user_abo->getFormattedAmount() }}
<div class="text-muted small">{{ __('navigation.points') }}</div>
<strong class="text-primary">{{ $user_abo->getFormattedTotalPoints() }} Pkt.</strong>
</div>
<div class="col-md-3 mb-3">
<div class="text-muted small">{{ __('tables.payment') }}</div>

View file

@ -0,0 +1,132 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2 d-flex justify-content-between align-items-center w-100">
<div>
{{ __('abo.team_customer_abos') }} / {{ __('navigation.overview') }}
</div>
</h4>
@if(isset($chartData))
@include('user.abo._abo_chart')
@endif
<div class="row">
<div class="col-12">
@if($groupedByMember->isEmpty())
<div class="card mb-4">
<div class="card-body">
<p class="text-muted mb-0">{{ __('tables.no_data_available') }}</p>
</div>
</div>
@else
@php $memberIndex = 0; @endphp
@foreach($groupedByMember as $memberId => $memberAbos)
@php
$member = $memberAbos->first()->member;
$memberName = $member && $member->account
? $member->account->first_name . ' ' . $member->account->last_name
: '#' . $memberId;
$totalPoints = $memberAbos->sum(fn($abo) => $abo->getTotalPoints());
$aboCount = $memberAbos->count();
$collapseId = 'member-abos-' . $memberId;
$memberIndex++;
@endphp
<div class="card mb-3">
<div class="card-body pb-2">
<div class="row align-items-center">
<div class="col-sm-6">
<h5 class="mb-0">
<a href="#" class="text-black" data-toggle="modal"
data-target="#modals-load-content"
data-id="{{ $memberId }}"
data-action="business-user-show"
data-back=""
data-modal="modal-md"
data-init_from="member"
data-route="{{ route('modal_load') }}">
<span class="mr-1 ion ion-ios-contact"></span>
{{ $memberName }}
</a>
</h5>
</div>
<div class="col-sm-6 text-right">
<button class="btn btn-sm btn-outline-secondary" type="button"
data-toggle="collapse" data-target="#{{ $collapseId }}"
aria-expanded="{{ $memberIndex === 1 ? 'true' : 'false' }}">
<span class="ion ion-ios-list mr-1"></span>
{{ $aboCount }} {{ __('abo.abo') }}
</button>
</div>
</div>
</div>
<hr class="m-0">
<div class="card-body py-2">
<div class="row">
<div class="col-md-4 mb-1">
<span class="text-muted small">{{ __('abo.abo_count') }}:</span>
<strong class="ml-1">{{ $aboCount }}</strong>
</div>
<div class="col-md-4 mb-1">
<span class="text-muted small">{{ __('navigation.points') }}:</span>
<strong class="ml-1 text-primary">{{ \App\Services\Util::formatNumber($totalPoints) }} Pkt.</strong>
</div>
<div class="col-md-4 mb-1">
<span class="text-muted small">{{ __('tables.active') }}:</span>
<strong class="ml-1">{{ $memberAbos->where('active', true)->count() }} / {{ $aboCount }}</strong>
</div>
</div>
</div>
<div class="collapse {{ $memberIndex === 1 ? 'show' : '' }}" id="{{ $collapseId }}">
<hr class="m-0">
<div class="card-body pt-2 pb-1">
<p class="text-muted small mb-2">
<span class="ion ion-ios-lock mr-1"></span>
{{ __('abo.customer_privacy_info') }}
</p>
<div class="table-responsive">
<table class="table table-sm table-borderless mb-0">
<thead>
<tr class="text-muted small">
<th>{{ __('tables.customer') }}</th>
<th>{{ __('navigation.points') }}</th>
<th>{{ __('tables.status') }}</th>
<th>{{ __('tables.next_date') }}</th>
<th>{{ __('tables.abo_delivery') }}</th>
<th>{{ __('tables.active') }}</th>
</tr>
</thead>
<tbody>
@foreach($memberAbos as $index => $abo)
<tr>
<td>
<span class="badge badge-outline-secondary">
{{ __('tables.customer') }} #{{ $index + 1 }}
</span>
</td>
<td>
<strong class="text-primary">
{{ $abo->getFormattedTotalPoints() }} Pkt.
</strong>
</td>
<td>{!! $abo->getStatusFormated() !!}</td>
<td>{{ $abo->next_date }}</td>
<td>{{ $abo->getCountOrders() }}</td>
<td>{!! get_active_badge($abo->active) !!}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endforeach
@endif
</div>
</div>
@endsection