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

@ -1,22 +1,30 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
<a href="{{route('admin_abos')}}" class="btn btn-sm btn-default float-right">{{ __('back') }}</a>
{{ __('navigation.abo') }} <span class="text-muted">{{ '#'.$user_abo->payone_userid }}</span>
<a href="{{ route('admin_abos') }}" class="btn btn-sm btn-default float-right">{{ __('back') }}</a>
{{ __('navigation.abo') }} <span class="text-muted">{{ '#' . $user_abo->payone_userid }}</span>
</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>
@ -27,20 +35,24 @@
@include('admin.customer._customer_detail', ['shopping_user' => $customer_detail])
</div>
{!! Form::open(['action' => route('user_abos_update', [$view, $user_abo->id]), 'class' => 'form-horizontal', 'id'=>'cart-order-form']) !!}
<input type="hidden" name="is_for" value="{{ $user_abo->is_for }}">
<div class="card mt-3">
@include('admin.abo._order_abo')
{!! Form::open([
'action' => route('user_abos_update', [$view, $user_abo->id]),
'class' => 'form-horizontal',
'id' => 'cart-order-form',
]) !!}
<input type="hidden" name="is_for" value="{{ $user_abo->is_for }}">
<div class="card mt-3">
@include('admin.abo._order_abo')
</div>
@if ($comp_products && Yard::instance('shopping')->getNumComp() > 0)
<div id="holder_html_view_comp_product">
@include('user.order.comp_product')
</div>
@endif
@if($comp_products && Yard::instance('shopping')->getNumComp() > 0)
<div id="holder_html_view_comp_product">
@include('user.order.comp_product')
</div>
@endif
{{ Form::close() }}
{{ Form::close() }}
<div class="card mt-3">
@include('admin.abo._initial_composition')
</div>
@ -54,48 +66,49 @@
</div>
<a href="{{route('admin_abos')}}" class="btn btn-sm btn-default mt-2 float-right">{{ __('back') }}</a>
<a href="{{ route('admin_abos') }}" class="btn btn-sm btn-default mt-2 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')
@ -107,4 +120,4 @@
</script>
@endsection
@endsection

View file

@ -9,33 +9,48 @@
<div class="card mb-4">
<h5 class="card-header">Struktur</h5>
<div class="card-body">
<button type="button" id="" data-save="navigation-structure" class="btn btn-primary nestable_list_menu_save">{{ __('save') }}</button>
<button type="button" id="" data-save="navigation-structure"
class="btn btn-primary nestable_list_menu_save">{{ __('save') }}</button>
<hr>
<div class="row">
<div class="col-sm-6">
<div class="white-box">
<div class="custom-dd dd" id="nestable_list_1" data-href-save="{{ route('admin_downloadcenter_item_store', ['structure'])}}">
<div class="custom-dd dd" id="nestable_list_1"
data-href-save="{{ route('admin_downloadcenter_item_store', ['structure']) }}">
<ol class="dd-list">
@if(count($category_active))
@foreach($category_active as $category)
<li class="dd-item" data-id="{{ $category->id }}" data-name="{{ $category->slug }}">
<span class="pull-right">
<a href="#" class="btn btn-sm mt-1 nestable_update_btn" data-action="update-category-active" data-target="self" data-id="{{ $category->id }}" data-url="{{ route('admin_downloadcenter_item_store', ['update_ajax']) }}">
@if($category->active) <i class="fa fa-eye text-success"></i> @else <i class="fa fa-eye-slash text-danger"></i> @endif
</a>
<a href="{{ route('admin_downloadcenter_item_delete', ['obj' => 'category', 'id'=> $category->id])}}" class="btn btn-sm mt-1 nestable_list_delete"><i class="fa fa-trash text-danger"></i></a>
</span>
@if (count($category_active))
@foreach ($category_active as $category)
<li class="dd-item" data-id="{{ $category->id }}"
data-name="{{ $category->slug }}">
<div style="display: inline-block;">
<div class="ml-4">
<a href="#"
class="btn btn-sm mt-1 mt-0 nestable_update_btn"
data-action="update-category-active" data-target="self"
data-id="{{ $category->id }}"
data-url="{{ route('admin_downloadcenter_item_store', ['update_ajax']) }}">
@if ($category->active)
<i class="fa fa-eye text-success"></i>
@else
<i class="fa fa-eye-slash text-danger"></i>
@endif
</a>
<a href="{{ route('admin_downloadcenter_item_delete', ['obj' => 'category', 'id' => $category->id]) }}"
class="btn btn-sm mt-1 mt-0 nestable_list_delete"><i
class="fa fa-trash text-danger"></i></a>
</div>
</div>
<div class="dd-handle">
<strong> {{ $category->name }} </strong>
<strong> {{ $category->name }} </strong>
</div>
<ol class="dd-list">
{!! \App\Services\DcHelper::makeNestableList($category->id) !!}
{!! \App\Services\DcHelper::makeNestableList($category->id) !!}
<li class="dd-item" data-id="0">
</li>
</ol>
</li>
@endforeach
@endif
@endforeach
@endif
</ol>
</div>
</div>
@ -47,23 +62,27 @@
<ol class="dd-list">
<li class="dd-item" data-id="0">
<div class="dd-handle dd-nodrag">
<strong><em> Neue Tags </em></strong>
<strong><em> Neue Tags </em></strong>
</div>
<ol class="dd-list">
@if(count($tags_inactive))
@foreach($tags_inactive as $tag)
<li class="dd-item" data-id="{{ $tag->id }}">
<span class="pull-right">
<a href="{{ route('admin_downloadcenter_item_delete', ['obj' => 'tag', 'id'=> $tag->id])}}" class="btn btn-sm nestable_list_delete"><i class="fa fa-trash text-danger"></i></a>
</span>
<div class="dd-handle">
{{ $tag->name }}
</div>
</li>
@endforeach
@endif
@if (count($tags_inactive))
@foreach ($tags_inactive as $tag)
<li class="dd-item" data-id="{{ $tag->id }}">
<div style="display: inline-block; width: 100%;">
<div class="float-right">
<a href="{{ route('admin_downloadcenter_item_delete', ['obj' => 'tag', 'id' => $tag->id]) }}"
class="btn btn-sm nestable_list_delete"><i
class="fa fa-trash text-danger"></i></a>
</div>
</div>
<div class="dd-handle">
{{ $tag->name }}
</div>
</li>
@endforeach
@endif
<li class="dd-item " data-id="0">
</li>
</ol>
</li>
@ -73,16 +92,20 @@
</div>
</div>
<hr>
<button type="button" id="" data-save="navigation-structure" class="btn btn-primary nestable_list_menu_save">{{ __('save') }}</button>
<button type="button" id="" data-save="navigation-structure"
class="btn btn-primary nestable_list_menu_save">{{ __('save') }}</button>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
{!! Form::open(['action' => route('admin_downloadcenter_item_store', ['category']), 'class' => 'form-horizontal']) !!}
{!! Form::open([
'action' => route('admin_downloadcenter_item_store', ['category']),
'class' => 'form-horizontal',
]) !!}
<label class="form-label" for="dc_category_name">Neue Katagorie anlegen</label>
{{ Form::text('dc_category_name', '', array('placeholder'=>'Bezeichung Katagorie', 'class'=>'form-control', 'id'=>'dc_category_name')) }}
{{ Form::text('dc_category_name', '', ['placeholder' => 'Bezeichung Katagorie', 'class' => 'form-control', 'id' => 'dc_category_name']) }}
<button type="submit" class="btn btn-submit mt-4">{{ __('save') }}</button>&nbsp;
{!! Form::close() !!}
<br>
@ -90,7 +113,7 @@
<br>
{!! Form::open(['action' => route('admin_downloadcenter_item_store', ['tag']), 'class' => 'form-horizontal']) !!}
<label class="form-label" for="dc_tag_name">Neuen Tag anlegen</label>
{{ Form::text('dc_tag_name', '', array('placeholder'=>'Bezeichung Tag', 'class'=>'form-control', 'id'=>'dc_tag_name')) }}
{{ Form::text('dc_tag_name', '', ['placeholder' => 'Bezeichung Tag', 'class' => 'form-control', 'id' => 'dc_tag_name']) }}
<button type="submit" class="btn btn-submit mt-4">{{ __('save') }}</button>&nbsp;
{!! Form::close() !!}
<br>
@ -101,19 +124,18 @@
@endsection
@section('styles')
<link rel="stylesheet" href="/vendor/libs/nestable/nestable.css">
<style>
.dd-list {
min-width: auto;
}
.dd-nodrag{
pointer-events: none;
}
</style>
<link rel="stylesheet" href="/vendor/libs/nestable/nestable.css">
<style>
.dd-list {
min-width: auto;
}
.dd-nodrag {
pointer-events: none;
}
</style>
@endsection
@section('scripts')
<script src="/vendor/libs/nestable/jquery-nestable-full.js?v=1"></script>
<script src="/js/nestable-init.js?v=1"></script>
@endsection
<script src="/vendor/libs/nestable/jquery-nestable-full.js?v=1"></script>
<script src="/js/nestable-init.js?v=1"></script>
@endsection

View file

@ -0,0 +1,214 @@
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
{{-- ===== KONFIGURATION ===== --}}
<div class="card mb-4">
<div class="card-header">
<i class="ion ion-md-settings mr-1"></i>
<strong>{{ __('incentive.configuration') }}</strong>
</div>
<div class="card-body">
<div class="form-row">
<div class="form-group col-md-8">
<label for="name">{{ __('incentive.name') }} *</label>
<input type="text" class="form-control" id="name" name="name"
value="{{ old('name', $incentive->name ?? '') }}" required>
<small class="form-text text-muted">{{ __('incentive.name_help') }}</small>
</div>
<div class="form-group col-md-4">
<label for="status">{{ __('incentive.status') }} *</label>
<select class="custom-select" id="status" name="status" required>
@foreach (\App\Models\Incentive::$statusTypes as $key => $label)
<option value="{{ $key }}" @if (old('status', $incentive->status ?? 0) == $key) selected @endif>
{{ __('incentive.status_' . $label) }}
</option>
@endforeach
</select>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="qualification_start">{{ __('incentive.qualification_start') }} *</label>
{!! Form::text(
'qualification_start',
old(
'qualification_start',
isset($incentive->qualification_start) ? $incentive->qualification_start->format('d.m.Y') : '',
),
[
'class' => 'form-control datepicker-base',
'required' => true,
],
) !!}
</div>
<div class="form-group col-md-4">
<label for="qualification_end">{{ __('incentive.qualification_end') }} *</label>
{!! Form::text(
'qualification_end',
old(
'qualification_end',
isset($incentive->qualification_end) ? $incentive->qualification_end->format('d.m.Y') : '',
),
[
'class' => 'form-control datepicker-base',
'required' => true,
],
) !!}
</div>
<div class="form-group col-md-4">
<label for="calculation_end">{{ __('incentive.calculation_end') }} *</label>
{!! Form::text(
'calculation_end',
old('calculation_end', isset($incentive->calculation_end) ? $incentive->calculation_end->format('d.m.Y') : ''),
[
'class' => 'form-control datepicker-base',
'required' => true,
],
) !!}
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="points_partner_onetime">{{ __('incentive.points_partner_onetime') }} *</label>
<input type="number" class="form-control" id="points_partner_onetime" name="points_partner_onetime"
value="{{ old('points_partner_onetime', $incentive->points_partner_onetime ?? 600) }}"
min="0" required>
</div>
<div class="form-group col-md-4">
<label for="points_abo_onetime">{{ __('incentive.points_abo_onetime') }} *</label>
<input type="number" class="form-control" id="points_abo_onetime" name="points_abo_onetime"
value="{{ old('points_abo_onetime', $incentive->points_abo_onetime ?? 400) }}" min="0"
required>
</div>
<div class="form-group col-md-4">
<label for="max_winners">{{ __('incentive.max_winners') }} *</label>
<input type="number" class="form-control" id="max_winners" name="max_winners"
value="{{ old('max_winners', $incentive->max_winners ?? 30) }}" min="1" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="min_direct_partners">{{ __('incentive.min_direct_partners') }} *</label>
<input type="number" class="form-control" id="min_direct_partners" name="min_direct_partners"
value="{{ old('min_direct_partners', $incentive->min_direct_partners ?? 4) }}" min="0"
required>
</div>
<div class="form-group col-md-6">
<label for="min_customer_abos">{{ __('incentive.min_customer_abos') }} *</label>
<input type="number" class="form-control" id="min_customer_abos" name="min_customer_abos"
value="{{ old('min_customer_abos', $incentive->min_customer_abos ?? 6) }}" min="0" required>
</div>
</div>
<div class="form-group mb-0">
<label for="image">{{ __('incentive.image') }}</label>
<input type="text" class="form-control" id="image" name="image"
value="{{ old('image', $incentive->image ?? '') }}">
<small class="form-text text-muted">{{ __('incentive.image_help') }}</small>
</div>
</div>
</div>
{{-- ===== INHALTE: DEUTSCH (Standard) ===== --}}
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<i class="ion ion-md-flag mr-1"></i>
<strong>{{ __('incentive.content_lang_de') }}</strong>
<span class="badge badge-light ml-1">{{ __('incentive.default_language') }}</span>
</div>
<div class="card-body">
<div class="form-group">
<label for="subtitle">{{ __('incentive.subtitle') }}</label>
<input type="text" class="form-control" id="subtitle" name="subtitle"
value="{{ old('subtitle', $incentive->subtitle ?? '') }}"
placeholder="{{ __('incentive.subtitle_placeholder') }}">
<small class="form-text text-muted">{{ __('incentive.subtitle_help') }}</small>
</div>
<div class="form-group">
<label for="description">
<i class="ion ion-md-text mr-1"></i>
{{ __('incentive.description') }}
</label>
<textarea class="form-control summernote-small" id="description" name="description" rows="6">{{ old('description', $incentive->description ?? '') }}</textarea>
<small class="form-text text-muted">{{ __('incentive.description_help') }}</small>
</div>
<div class="form-group mb-0">
<label for="terms">
<i class="ion ion-md-document mr-1"></i>
{{ __('incentive.terms') }}
</label>
<textarea class="form-control summernote-small" id="terms" name="terms" rows="8">{{ old('terms', $incentive->terms ?? '') }}</textarea>
<small class="form-text text-muted">{{ __('incentive.terms_help') }}</small>
</div>
</div>
</div>
{{-- ===== INHALTE: WEITERE SPRACHEN ===== --}}
@foreach ($languages as $locale => $localeData)
@if ($locale !== 'de')
<div class="card mb-4">
<div class="card-header">
<i class="ion ion-md-flag mr-1"></i>
<strong>{{ $localeData['native'] }}</strong>
<span class="badge badge-secondary ml-1">{{ strtoupper($locale) }}</span>
<small class="text-muted ml-2">{{ __('incentive.lang_fallback_hint') }}</small>
</div>
<div class="card-body">
@php $existingIncentive = $incentive ?? null; @endphp
<div class="form-group">
<label for="trans_name_{{ $locale }}">{{ __('incentive.name') }}</label>
<input type="text" class="form-control" id="trans_name_{{ $locale }}"
name="trans_name_{{ $locale }}"
value="{{ old('trans_name_' . $locale, $existingIncentive ? $existingIncentive->getTrans('name', $locale) : '') }}"
placeholder="{{ $existingIncentive->name ?? '' }}">
</div>
<div class="form-group">
<label for="trans_subtitle_{{ $locale }}">{{ __('incentive.subtitle') }}</label>
<input type="text" class="form-control" id="trans_subtitle_{{ $locale }}"
name="trans_subtitle_{{ $locale }}"
value="{{ old('trans_subtitle_' . $locale, $existingIncentive ? $existingIncentive->getTrans('subtitle', $locale) : '') }}"
placeholder="{{ $existingIncentive->subtitle ?? __('incentive.subtitle_placeholder') }}">
</div>
<div class="form-group">
<label for="trans_description_{{ $locale }}">
<i class="ion ion-md-text mr-1"></i>
{{ __('incentive.description') }}
</label>
<textarea class="form-control summernote-small" id="trans_description_{{ $locale }}"
name="trans_description_{{ $locale }}" rows="6">{{ old('trans_description_' . $locale, $existingIncentive ? $existingIncentive->getTrans('description', $locale) : '') }}</textarea>
</div>
<div class="form-group mb-0">
<label for="trans_terms_{{ $locale }}">
<i class="ion ion-md-document mr-1"></i>
{{ __('incentive.terms') }}
</label>
<textarea class="form-control summernote-small" id="trans_terms_{{ $locale }}" name="trans_terms_{{ $locale }}"
rows="8">{{ old('trans_terms_' . $locale, $existingIncentive ? $existingIncentive->getTrans('terms', $locale) : '') }}</textarea>
</div>
</div>
</div>
@endif
@endforeach
<hr class="mb-3">

View file

@ -0,0 +1,61 @@
{{-- Zusammenfassung --}}
<div class="row mb-3">
<div class="col-md-3">
<div class="card text-center bg-light">
<div class="card-body py-2">
<small class="text-muted">{{ __('incentive.total_points') }}</small>
<h4 class="mb-0 font-weight-bold">{{ number_format($participant->total_points, 0, ',', '.') }}</h4>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center bg-light">
<div class="card-body py-2">
<small class="text-muted">{{ __('incentive.rank') }}</small>
<h4 class="mb-0 font-weight-bold">{{ $participant->rank ?? '-' }}</h4>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center bg-light">
<div class="card-body py-2">
<small class="text-muted">{{ __('incentive.partners') }}</small>
<h4 class="mb-0 font-weight-bold {{ $participant->qualified_partners >= $incentive->min_direct_partners ? 'text-success' : 'text-danger' }}">
{{ $participant->qualified_partners }}/{{ $incentive->min_direct_partners }}
</h4>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center bg-light">
<div class="card-body py-2">
<small class="text-muted">{{ __('incentive.abos') }}</small>
<h4 class="mb-0 font-weight-bold {{ $participant->qualified_abos >= $incentive->min_customer_abos ? 'text-success' : 'text-danger' }}">
{{ $participant->qualified_abos }}/{{ $incentive->min_customer_abos }}
</h4>
</div>
</div>
</div>
</div>
{{-- Sektion A: Neupartner-Punkte --}}
<h6 class="font-weight-bold">{{ __('incentive.section_partners') }}</h6>
<div class="mb-3">
@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>
{{-- Sektion B: Kundenabo-Punkte --}}
<h6 class="font-weight-bold">{{ __('incentive.section_abos') }}</h6>
@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'),
])

View file

@ -0,0 +1,18 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
{{ __('incentive.create') }}
</h4>
<div class="card">
<div class="card-body">
<form action="{{ route('admin_incentive_store') }}" method="POST">
@csrf
@include('admin.incentive._form')
<button type="submit" class="btn btn-success">{{ __('incentive.save') }}</button>
<a href="{{ route('admin_incentives') }}" class="btn btn-default">{{ __('incentive.cancel') }}</a>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,18 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
{{ __('incentive.edit') }}: {{ $incentive->name }}
</h4>
<div class="card">
<div class="card-body">
<form action="{{ route('admin_incentive_update', [$incentive->id]) }}" method="POST">
@csrf
@include('admin.incentive._form', ['incentive' => $incentive])
<button type="submit" class="btn btn-success">{{ __('incentive.save') }}</button>
<a href="{{ route('admin_incentive_show', [$incentive->id]) }}" class="btn btn-default">{{ __('incentive.cancel') }}</a>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,51 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
{{ __('incentive.incentives') }}
<a href="{{ route('admin_incentive_create') }}" class="btn btn-sm btn-success float-right">
<span class="fa fa-plus"></span> {{ __('incentive.create') }}
</a>
</h4>
<div class="card">
<div class="card-datatable table-responsive">
<table class="table table-striped table-bordered" id="datatable-incentives">
<thead>
<tr>
<th>#</th>
<th>{{ __('incentive.name') }}</th>
<th>{{ __('incentive.status') }}</th>
<th>{{ __('incentive.period') }}</th>
<th>{{ __('incentive.participants') }}</th>
<th>{{ __('incentive.actions') }}</th>
</tr>
</thead>
</table>
</div>
</div>
<script>
$(document).ready(function() {
$('#datatable-incentives').DataTable({
"processing": true,
"serverSide": true,
ajax: {
url: '{!! route('admin_incentives_datatable') !!}'
},
"order": [[0, "desc"]],
"columns": [
{ data: 'id', name: 'id' },
{ data: 'name', name: 'name' },
{ data: 'status_label', name: 'status_label', searchable: false },
{ data: 'period', name: 'period', searchable: false, orderable: false },
{ data: 'participants_count', name: 'participants_count', searchable: false, orderable: false },
{ data: 'action', name: 'action', searchable: false, orderable: false }
],
"bLengthChange": false,
"iDisplayLength": 50,
"language": {
"url": "/js/datatables-{{ \App::getLocale() }}.json"
}
});
});
</script>
@endsection

View file

@ -0,0 +1,191 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
{{ $incentive->name }}
<span class="badge badge-{{ $incentive->getStatusColor() }}">{{ $incentive->getStatusType() }}</span>
<div class="float-right">
<a href="{{ route('admin_incentive_edit', [$incentive->id]) }}" class="btn btn-sm btn-warning">
<span class="fa fa-edit"></span> {{ __('incentive.edit') }}
</a>
{{-- <form action="{{ route('admin_incentive_recalculate', [$incentive->id]) }}" method="POST" class="d-inline">
@csrf
<button type="submit" class="btn btn-sm btn-info" onclick="return confirm('{{ __('incentive.recalculate_confirm') }}')">
<span class="fa fa-sync"></span> {{ __('incentive.recalculate') }}
</button>
</form>
<form action="{{ route('admin_incentive_recalculate', [$incentive->id]) }}" method="POST" class="d-inline">
@csrf
<input type="hidden" name="force" value="1">
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('{{ __('incentive.force_recalculate_confirm') }}')">
<span class="fa fa-redo"></span> {{ __('incentive.force_recalculate') }}
</button>
</form> --}}
</div>
</h4>
{{-- Konfiguration --}}
<div class="card mb-4">
<div class="card-header">{{ __('incentive.configuration') }}</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<strong>{{ __('incentive.qualification_start') }}:</strong>
{{ $incentive->qualification_start->format('d.m.Y') }}<br>
<strong>{{ __('incentive.qualification_end') }}:</strong>
{{ $incentive->qualification_end->format('d.m.Y') }}<br>
<strong>{{ __('incentive.calculation_end') }}:</strong>
{{ $incentive->calculation_end->format('d.m.Y') }}
</div>
<div class="col-md-4">
<strong>{{ __('incentive.points_partner_onetime') }}:</strong>
{{ $incentive->points_partner_onetime }}<br>
<strong>{{ __('incentive.points_abo_onetime') }}:</strong> {{ $incentive->points_abo_onetime }}<br>
<strong>{{ __('incentive.max_winners') }}:</strong> {{ $incentive->max_winners }}
</div>
<div class="col-md-4">
<strong>{{ __('incentive.min_direct_partners') }}:</strong> {{ $incentive->min_direct_partners }}<br>
<strong>{{ __('incentive.min_customer_abos') }}:</strong> {{ $incentive->min_customer_abos }}
</div>
</div>
</div>
</div>
{{-- Ranking --}}
<div class="card">
<div class="card-header">
{{ __('incentive.ranking') }} ({{ $participants->count() }} {{ __('incentive.participants') }})
</div>
<div class="card-body p-0">
<table class="table table-striped table-bordered mb-0">
<thead>
<tr>
<th>{{ __('incentive.rank') }}</th>
<th>{{ __('incentive.consultant') }}</th>
<th>E-Mail</th>
<th>{{ __('incentive.admin_terms_accepted') }}</th>
<th>{{ __('incentive.total_points') }}</th>
<th>{{ __('incentive.partners') }}</th>
<th>{{ __('incentive.abos') }}</th>
<th>{{ __('incentive.qualified') }}</th>
<th>{{ __('incentive.status') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@forelse($participants as $p)
@php
$isWinner = $p->is_qualified && $p->rank && $p->rank <= $incentive->max_winners;
@endphp
<tr
class="{{ $isWinner ? 'table-success font-weight-bold' : ($p->is_qualified ? 'font-weight-bold' : '') }}">
<td>{{ $p->rank ?? '—' }}</td>
<td>
@if ($p->user && $p->user->account)
{{ $p->user->account->first_name }} {{ $p->user->account->last_name }}
@else
{{ $p->user->email ?? 'N/A' }}
@endif
</td>
<td>{{ $p->user->email ?? '' }}</td>
<td>
@if ($p->accepted_terms_at)
<span class="badge badge-success">{{ __('incentive.yes') }}</span>
<span class="text-muted small d-block"
title="{{ __('incentive.admin_terms_accepted_at_tooltip') }}">{{ $p->accepted_terms_at->format('d.m.Y H:i') }}</span>
@else
<span class="badge badge-secondary">{{ __('incentive.admin_terms_pending') }}</span>
@endif
</td>
<td>{{ number_format($p->total_points, 0, ',', '.') }}</td>
<td>
{{ $p->qualified_partners }}/{{ $incentive->min_direct_partners }}
@if ($p->qualified_partners >= $incentive->min_direct_partners)
<span class="text-success">&#10003;</span>
@endif
</td>
<td>
{{ $p->qualified_abos }}/{{ $incentive->min_customer_abos }}
@if ($p->qualified_abos >= $incentive->min_customer_abos)
<span class="text-success">&#10003;</span>
@endif
</td>
<td>
@if ($p->is_qualified)
<span class="badge badge-success">{{ __('incentive.yes') }}</span>
@else
<span class="badge badge-secondary">{{ __('incentive.no') }}</span>
@endif
</td>
<td>
@if ($isWinner)
<span class="badge badge-warning">{{ __('incentive.winner') }}</span>
@elseif($p->is_qualified)
<span class="badge badge-info">{{ __('incentive.qualified') }}</span>
@else
<span class="badge badge-secondary">{{ __('incentive.open') }}</span>
@endif
</td>
<td>
<button type="button" class="btn btn-sm btn-outline-primary btn-participant-details"
data-participant-id="{{ $p->id }}"
data-participant-name="{{ $p->user && $p->user->account ? $p->user->account->first_name . ' ' . $p->user->account->last_name : ($p->user->email ?? 'N/A') }}">
<span class="fa fa-eye"></span>
</button>
</td>
</tr>
@empty
<tr>
<td colspan="10" class="text-center text-muted">{{ __('incentive.no_participants') }}</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
{{-- Modal: Teilnehmer-Details --}}
<div class="modal fade" id="participantDetailsModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ __('incentive.calculation_details') }}: <span
id="modalParticipantName"></span></h5>
<button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
</div>
<div class="modal-body" id="modalParticipantBody">
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary"
data-dismiss="modal">{{ __('incentive.close') }}</button>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
$(document).on('click', '.btn-participant-details', function() {
var participantId = $(this).data('participant-id');
var participantName = $(this).data('participant-name');
$('#modalParticipantName').text(participantName);
$('#modalParticipantBody').html(
'<div class="text-center py-4"><div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div></div>'
);
$('#participantDetailsModal').modal('show');
$.get('{{ url('/admin/incentives/participant') }}/' + participantId + '/details', function(html) {
$('#modalParticipantBody').html(html);
}).fail(function() {
$('#modalParticipantBody').html(
'<div class="alert alert-danger">Fehler beim Laden der Details.</div>');
});
});
</script>
@endsection

View file

@ -1,50 +1,47 @@
@extends('layouts.layout-2')
@section('content')
<style>
.td-entry-table-margin {
padding-bottom: 6px;
border-bottom: 1px solid rgb(221, 221, 221);
margin-bottom: 6px;
}
</style>
<style>
.td-entry-table-margin {
padding-bottom: 6px;
border-bottom: 1px solid rgb(221, 221, 221);
margin-bottom: 6px;
}
</style>
<div class="card">
<h5 class="card-header">
Guthaben offen
<div class="float-right">
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#modals-load-content"
data-id="new"
data-action="add-user-credit"
data-back="{{url()->current()}}"
data-id="new" data-action="add-user-credit" data-back="{{ url()->current() }}"
data-route="{{ route('modal_load') }}"><span class="fa fa-plus-circle"></span> Guthaben hinzufügen
</button>
</div>
</h5>
@if(isset($add_credit_error) && $add_credit_error)
<div class="col-sm-12">
<div class="alert alert-danger p-2 mt-2">
<ul>
<li>{{ $add_credit_error }}</li>
</ul>
@if (isset($add_credit_error) && $add_credit_error)
<div class="col-sm-12">
<div class="alert alert-danger p-2 mt-2">
<ul>
<li>{{ $add_credit_error }}</li>
</ul>
</div>
</div>
</div>
@endif
<div class="card-datatable table-responsive pt-0">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th>{{__('Account ID')}}</th>
<th>{{__('First name')}}</th>
<th>{{__('Last name') }}</th>
<th>{{__('E-Mail') }}</th>
<th>{{__('Betrag') }}</th>
<th>{{__('Guthaben')}}</th>
<th>{{__('#')}}</th>
</tr>
<tr>
<th>{{ __('Account ID') }}</th>
<th>{{ __('First name') }}</th>
<th>{{ __('Last name') }}</th>
<th>{{ __('E-Mail') }}</th>
<th>{{ __('Betrag') }}</th>
<th>{{ __('Guthaben') }}</th>
<th>{{ __('#') }}</th>
</tr>
</thead>
<tbody>
@foreach ($user_credit_items as $user_credit_item)
@ -57,28 +54,29 @@
<td>
@foreach ($user_credit_item['entries'] as $key => $credit)
<div class="td-entry-table-margin">
<i class="fa fa-plus-circle text-secondary"></i>
{!! formatNumber($credit->credit) !!} &euro; |
<i class="fa fa-plus-circle text-secondary"></i>
{!! formatNumber($credit->credit) !!} &euro; |
{{ formatTextWithLineBreaks($credit->message, true) }}
/ {{ $credit->created_at->format("d.m.Y") }}
@if($deleteTime = $credit->deleteTime())
/ <span class="no-line-break">
<a class="btn btn-danger btn-xs" href="{{ route('admin_payments_credit_delete', [$credit->id, 'user_credit_item']) }}" onclick="return confirm('Wirklich löschen?');">
<i class="ion ion-ios-trash"></i>
</a> noch {{ $deleteTime }} min.
</span>
@endif
/ {{ $credit->created_at->format('d.m.Y') }}
@if ($deleteTime = $credit->deleteTime())
/ <span class="no-line-break">
<a class="btn btn-danger btn-xs"
href="{{ route('admin_payments_credit_delete', [$credit->id, 'user_credit_item']) }}"
onclick="return confirm('Wirklich löschen?');">
<i class="ion ion-ios-trash"></i>
</a> noch {{ $deleteTime }} min.
</span>
@endif
</div>
@endforeach
</td>
<td>
<button type="button" class="btn btn-sm btn-secondary" data-toggle="modal" data-target="#modals-credit"
data-userid="{{ $user_credit_item['user_id'] }}"
data-email="{{ $user_credit_item['email'] }}"
data-back="{{url()->current()}}"
<button type="button" class="btn btn-sm btn-secondary" data-toggle="modal"
data-target="#modals-credit" data-userid="{{ $user_credit_item['user_id'] }}"
data-email="{{ $user_credit_item['email'] }}" data-back="{{ url()->current() }}"
data-action="create_credit">
<span class="fa fa-dollar-sign"></span> <strong>Guthaben auszahlen</strong>
</button>
<span class="fa fa-dollar-sign"></span> <strong>Guthaben auszahlen</strong>
</button>
</td>
</tr>
@endforeach
@ -88,147 +86,242 @@
</div>
<div class="card mt-3">
<div class="card mt-3 mb-3" id="credit-stats-card">
<div class="card-body py-3">
<div class="row align-items-center">
<div class="col-auto text-muted small">
<i class="fa fa-chart-bar mr-1"></i> Statistik:
<span class="font-weight-bold text-dark" id="stats-credit-period">
{{ $filter_months[session('credit_filter_month')] ?? '' }} {{ session('credit_filter_year') }}
</span>
</div>
<div class="col text-right d-flex justify-content-end">
<div class="mr-4 text-center">
<div class="text-muted" style="font-size:0.75rem;">Anzahl Gutschriften</div>
<div class="h5 mb-0 font-weight-bold text-primary" id="stats-credit-count">
<i class="fa fa-spinner fa-spin fa-sm"></i>
</div>
</div>
<div class="text-center">
<div class="text-muted" style="font-size:0.75rem;">Gesamtsumme</div>
<div class="h5 mb-0 font-weight-bold text-success" id="stats-credit-total">
<i class="fa fa-spinner fa-spin fa-sm"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card mt-0">
<h5 class="card-header">
Gutschriften / Auszahlungen
Gutschriften / Auszahlungen
</h5>
<div class="card-body p-0">
{!! Form::open(['action' => route('admin_payments_credit'), 'class' => 'form-horizontal', 'id'=>'form_filter_payment_credits']) !!}
{!! Form::open([
'action' => route('admin_payments_credit'),
'class' => 'form-horizontal',
'id' => 'form_filter_payment_credits',
]) !!}
<div class="form-row align-items-center px-4 pb-2 pt-3">
<div class="col-12 col-sm-4 col-md-4 col-lg-4 mb-1">
<input class="form-control on_keyup_credits" name="credit_filter_name" type="text" value="{{session('credit_filter_name')}}" placeholder="Name">
<input class="form-control on_keyup_credits" name="credit_filter_name" type="text"
value="{{ session('credit_filter_name') }}" placeholder="Name">
</div>
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_credits" name="credit_filter_month">
@foreach($filter_months as $key=>$value)
<option value="{{$key}}" @if(session('credit_filter_month') == $key) selected @endif>{{$value}}</option>
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_credits" name="credit_filter_month">
@foreach ($filter_months as $key => $value)
<option value="{{ $key }}" @if (session('credit_filter_month') == $key) selected @endif>
{{ $value }}</option>
@endforeach
</select>
</select>
</div>
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_credits" name="credit_filter_year">
@foreach($filter_years as $key=>$value)
<option value="{{$value}}" @if(session('credit_filter_year') == $value) selected @endif>{{$value}}</option>
@foreach ($filter_years as $key => $value)
<option value="{{ $value }}" @if (session('credit_filter_year') == $value) selected @endif>
{{ $value }}</option>
@endforeach
</select>
</div>
</div>
{!! Form::close() !!}
<div class="card-datatable table-responsive pt-0">
<table class="datatables-style table table-striped table-bordered" id="datatable-credit">
<thead>
<tr>
<th>ID</th>
<th>{{__('G.Nr.')}}</th>
<th>{{__('Gutschrift')}}</th>
<th>{{__('First name')}}</th>
<th>{{__('Last name') }}</th>
<th>{{__('E-Mail') }}</th>
<th>{{__('Betrag') }}</th>
<th>{{__('Datum') }}</th>
<th>{{__('Zahlung')}}</th>
<th>{{__('aus Guthaben')}}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="card-datatable table-responsive pt-0">
<table class="datatables-style table table-striped table-bordered" id="datatable-credit">
<thead>
<tr>
<th>ID</th>
<th>{{ __('G.Nr.') }}</th>
<th>{{ __('Gutschrift') }}</th>
<th>{{ __('First name') }}</th>
<th>{{ __('Last name') }}</th>
<th>{{ __('E-Mail') }}</th>
<th>{{ __('Betrag') }}</th>
<th>{{ __('Datum') }}</th>
<th>{{ __('Zahlung') }}</th>
<th>{{ __('aus Guthaben') }}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modals-credit">
<div class="modal-dialog">
<form class="modal-content form-prevent-multiple-submits" action="{{ route('admin_payments_credit_create') }}" method="post">
<form class="modal-content form-prevent-multiple-submits" action="{{ route('admin_payments_credit_create') }}"
method="post">
@csrf
<input type="hidden" name="userid" value="">
<input type="hidden" name="action" value="create_credit">
<input type="hidden" name="back" value="{{url()->current()}}">
<input type="hidden" name="back" value="{{ url()->current() }}">
<div class="modal-header">
<h5 class="modal-title">{{__('Gutschrift')}}</h5>
<h5 class="modal-title">{{ __('Gutschrift') }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
</div>
<div class="modal-body">
<div class="form-group col-sm-12">
{{ Form::select('credit_option', ['create'=>'Gutschrift erstellen'], false, array('data-live-search'=>'false', 'class'=>'selectpicker')) }}
{{ Form::select('credit_option', ['create' => 'Gutschrift erstellen'], false, ['data-live-search' => 'false', 'class' => 'selectpicker']) }}
</div>
<div class="form-group col-sm-12">
<label class="form-label" for="credit_date">{{ __('Gutschriftsdatum') }}</label>
{!! Form::text('credit_date', \Carbon::now()->format("d.m.Y"), ['class'=>'form-control datepicker-base']) !!}
{!! Form::text('credit_date', \Carbon::now()->format('d.m.Y'), ['class' => 'form-control datepicker-base']) !!}
</div>
<div class="form-group col-sm-12">
<label class="form-label" for="credit_number">{{ __('Gutschriftsnummer') }}</label>
{!! Form::text('credit_number', App\Services\Credit::getCreditNumber(), ['class'=>'form-control', 'disabled']) !!}
<em> nächste Gutschriftsnummer <a href="{{ route('admin_settings') }}"><i class="fa fa-edit"></i></a></em>
{!! Form::text('credit_number', App\Services\Credit::getCreditNumber(), ['class' => 'form-control', 'disabled']) !!}
<em> nächste Gutschriftsnummer <a href="{{ route('admin_settings') }}"><i
class="fa fa-edit"></i></a></em>
</div>
<div class="form-group col-sm-12">
<label class="custom-control custom-checkbox">
{!! Form::checkbox('credit_send_mail', 1, true, ['class'=>'custom-control-input']) !!}
<span class="custom-control-label">Gutschrift an <span id="set_credit_send_mail">mail</span></span>
{!! Form::checkbox('credit_send_mail', 1, true, ['class' => 'custom-control-input']) !!}
<span class="custom-control-label">Gutschrift an <span
id="set_credit_send_mail">mail</span></span>
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{__('close')}}</button>
<button type="submit" class="btn btn-primary button-prevent-multiple-submits"><i class="spinner fa fa-spinner fa-spin"></i> {{ __('save')}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ __('close') }}</button>
<button type="submit" class="btn btn-primary button-prevent-multiple-submits"><i
class="spinner fa fa-spinner fa-spin"></i> {{ __('save') }}</button>
</div>
</form>
</div>
</div>
<script>
$( document ).ready(function() {
function loadCreditStats() {
var month = $('select[name=credit_filter_month]').val();
var year = $('select[name=credit_filter_year]').val();
var name = $('input[name=credit_filter_name]').val();
$.getJSON('{!! route('admin_payments_credit_stats') !!}', {
credit_filter_month: month,
credit_filter_year: year,
credit_filter_name: name
}, function(data) {
$('#stats-credit-count').text(data.count);
$('#stats-credit-total').text(data.total + ' €');
var monthName = $('select[name=credit_filter_month] option:selected').text();
$('#stats-credit-period').text(monthName + ' ' + year);
});
}
$(document).ready(function() {
loadCreditStats();
var oTable = $('#datatable-credit').DataTable({
"processing": true,
"serverSide": true,
ajax: {
url: '{!! route( 'admin_payments_credit_datatable') !!}',
data: function(d) {
d.credit_filter_name = $('input[name=credit_filter_name]').val();
d.credit_filter_month = $('select[name=credit_filter_month]').val();
d.credit_filter_year = $('select[name=credit_filter_year]').val();
}
"processing": true,
"serverSide": true,
ajax: {
url: '{!! route('admin_payments_credit_datatable') !!}',
data: function(d) {
d.credit_filter_name = $('input[name=credit_filter_name]').val();
d.credit_filter_month = $('select[name=credit_filter_month]').val();
d.credit_filter_year = $('select[name=credit_filter_year]').val();
}
},
"order": [
[0, "desc"]
],
"columns": [{
data: 'id',
searchable: false
},
"order": [[0, "desc" ]],
"columns": [
{ data: 'id', searchable: false },
{ data: 'full_number', name: 'full_number' },
{ data: 'view', name: 'view' },
{ data: 'user.account.first_name', name: 'user.account.first_name', orderable: false },
{ data: 'user.account.last_name', name: 'user.account.last_name', orderable: false },
{ data: 'user.email', name: 'user.email', orderable: false },
{ data: 'total', name: 'total' },
{ data: 'date', name: 'date' },
{ data: 'status', name: 'status', searchable: false },
{ data: 'credits', name: 'credits', orderable: false },
],
"bLengthChange": false,
"iDisplayLength": 100,
"language": {
"url": "/js/datatables-{{ \App::getLocale() }}.json"
}
});
$('select.on_change_credits').on('change', function(){
oTable.draw();
});
$('input.on_keyup_credits').on('keyup', function(){
oTable.draw();
});
$( document ).ready(function() {
$('#modals-credit').on('show.bs.modal', function (event) {
{
data: 'full_number',
name: 'full_number'
},
{
data: 'view',
name: 'view'
},
{
data: 'user.account.first_name',
name: 'user.account.first_name',
orderable: false
},
{
data: 'user.account.last_name',
name: 'user.account.last_name',
orderable: false
},
{
data: 'user.email',
name: 'user.email',
orderable: false
},
{
data: 'total',
name: 'total'
},
{
data: 'date',
name: 'date'
},
{
data: 'status',
name: 'status',
searchable: false
},
{
data: 'credits',
name: 'credits',
orderable: false
},
],
"bLengthChange": false,
"iDisplayLength": 100,
"language": {
"url": "/js/datatables-{{ \App::getLocale() }}.json"
}
});
$('select.on_change_credits').on('change', function() {
oTable.draw();
loadCreditStats();
});
$('input.on_keyup_credits').on('keyup', function() {
oTable.draw();
loadCreditStats();
});
$(document).ready(function() {
$('#modals-credit').on('show.bs.modal', function(event) {
var button = $(event.relatedTarget);
if(event.relatedTarget){
$(this).find(".modal-content input[name='userid']").val(button.data('userid'));
$(this).find(".modal-body #set_credit_send_mail").html(button.data('email'));
if (event.relatedTarget) {
$(this).find(".modal-content input[name='userid']").val(button.data(
'userid'));
$(this).find(".modal-body #set_credit_send_mail").html(button.data(
'email'));
}
});
});
});
</script>
@endsection

View file

@ -1,50 +1,84 @@
@extends('layouts.layout-2')
@section('content')
<div class="card mb-3" id="invoice-stats-card">
<div class="card-body py-3">
<div class="row align-items-center">
<div class="col-auto text-muted small">
<i class="fa fa-chart-bar mr-1"></i> Statistik:
<span class="font-weight-bold text-dark" id="stats-invoice-period">
{{ $filter_months[session('invoice_filter_month')] ?? '' }} {{ session('invoice_filter_year') }}
</span>
</div>
<div class="col text-right d-flex justify-content-end">
<div class="mr-4 text-center">
<div class="text-muted" style="font-size:0.75rem;">Anzahl Rechnungen</div>
<div class="h5 mb-0 font-weight-bold text-primary" id="stats-invoice-count">
<i class="fa fa-spinner fa-spin fa-sm"></i>
</div>
</div>
<div class="text-center">
<div class="text-muted" style="font-size:0.75rem;">Gesamtsumme</div>
<div class="h5 mb-0 font-weight-bold text-success" id="stats-invoice-total">
<i class="fa fa-spinner fa-spin fa-sm"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<h5 class="card-header">
Finanzen / Rechnungen
</h5>
<div class="card-body p-0">
{!! Form::open(['action' => route('admin_payments_invoice'), 'class' => 'form-horizontal', 'id'=>'form_filter_payment_invoices']) !!}
{!! Form::open([
'action' => route('admin_payments_invoice'),
'class' => 'form-horizontal',
'id' => 'form_filter_payment_invoices',
]) !!}
<div class="form-row align-items-center px-4 pb-2 pt-3">
<div class="col-12 col-sm-4 col-md-4 col-lg-4 mb-1">
<input class="form-control on_keyup_invoice" name="invoice_filter_name" type="text" value="{{session('invoice_filter_name')}}" placeholder="Name">
</div>
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_invoice" name="invoice_filter_month">
@foreach($filter_months as $key=>$value)
<option value="{{$key}}" @if(session('invoice_filter_month') == $key) selected @endif>{{$value}}</option>
@endforeach
</select>
</div>
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_invoice" name="invoice_filter_year">
@foreach($filter_years as $key=>$value)
<option value="{{$value}}" @if(session('invoice_filter_year') == $value) selected @endif>{{$value}}</option>
@endforeach
</select>
</div>
<div class="form-row align-items-center px-4 pb-2 pt-3">
<div class="col-12 col-sm-4 col-md-4 col-lg-4 mb-1">
<input class="form-control on_keyup_invoice" name="invoice_filter_name" type="text"
value="{{ session('invoice_filter_name') }}" placeholder="Name">
</div>
{!! Form::close() !!}
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_invoice" name="invoice_filter_month">
@foreach ($filter_months as $key => $value)
<option value="{{ $key }}" @if (session('invoice_filter_month') == $key) selected @endif>
{{ $value }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_invoice" name="invoice_filter_year">
@foreach ($filter_years as $key => $value)
<option value="{{ $value }}" @if (session('invoice_filter_year') == $value) selected @endif>
{{ $value }}</option>
@endforeach
</select>
</div>
</div>
{!! Form::close() !!}
<div class="card-datatable table-responsive pt-0">
<table class="datatables-style table table-striped table-bordered" id="datatable-invoice">
<thead>
<tr>
<th>#</th>
<th>{{__('tables.in_no')}}</th>
<th>{{__('tables.invoice')}}</th>
<th>{{__('tables.amount') }}</th>
<th>{{__('tables.status')}}</th>
<th>{{__('First name')}}</th>
<th>{{__('Last name') }}</th>
<th>{{__('E-Mail') }}</th>
<th>{{__('tables.date') }}</th>
<th>{{__('tables.art')}}</th>
</tr>
<tr>
<th>#</th>
<th>{{ __('tables.in_no') }}</th>
<th>{{ __('tables.invoice') }}</th>
<th>{{ __('tables.amount') }}</th>
<th>{{ __('tables.status') }}</th>
<th>{{ __('First name') }}</th>
<th>{{ __('Last name') }}</th>
<th>{{ __('E-Mail') }}</th>
<th>{{ __('tables.date') }}</th>
<th>{{ __('tables.art') }}</th>
</tr>
</thead>
<tbody>
</tbody>
@ -54,45 +88,106 @@
</div>
<script>
$( document ).ready(function() {
function loadInvoiceStats() {
var month = $('select[name=invoice_filter_month]').val();
var year = $('select[name=invoice_filter_year]').val();
var name = $('input[name=invoice_filter_name]').val();
$.getJSON('{!! route('admin_payments_invoice_stats') !!}', {
invoice_filter_month: month,
invoice_filter_year: year,
invoice_filter_name: name
}, function(data) {
$('#stats-invoice-count').text(data.count);
$('#stats-invoice-total').text(data.total + ' €');
var monthName = $('select[name=invoice_filter_month] option:selected').text();
$('#stats-invoice-period').text(monthName + ' ' + year);
});
}
$(document).ready(function() {
loadInvoiceStats();
var oTable = $('#datatable-invoice').DataTable({
"processing": true,
"serverSide": true,
"stateSave": true,
"searching": false,
ajax: {
url: '{!! route('admin_payments_invoice_datatable') !!}',
data: function(d) {
d.invoice_filter_name = $('input[name=invoice_filter_name]').val();
d.invoice_filter_month = $('select[name=invoice_filter_month]').val();
d.invoice_filter_year = $('select[name=invoice_filter_year]').val();
}
},
"order": [[0, "desc" ]],
"columns": [
{ data: 'id', searchable: false },
{ data: 'full_number', name: 'full_number' },
{ data: 'invoice', name: 'invoice', orderable: false, searchable: false },
{ data: 'total_shipping', name: 'total_shipping', orderable: false, searchable: false },
{ data: 'txaction', name: 'txaction', orderable: false, searchable: false },
{ data: 'shopping_order.shopping_user.billing_firstname', name: 'shopping_order.shopping_user.billing_firstname', orderable: false },
{ data: 'shopping_order.shopping_user.billing_lastname', name: 'shopping_order.shopping_user.billing_lastname', orderable: false },
{ data: 'shopping_order.shopping_user.billing_email', name: 'shopping_order.shopping_user.billing_email', orderable: false },
{ data: 'created_at', name: 'created_at' },
{ data: 'status', name: 'status', searchable: false },
],
"bLengthChange": false,
"iDisplayLength": 100,
"language": {
"url": "/js/datatables-{{ \App::getLocale() }}.json"
"processing": true,
"serverSide": true,
"stateSave": true,
"searching": false,
ajax: {
url: '{!! route('admin_payments_invoice_datatable') !!}',
data: function(d) {
d.invoice_filter_name = $('input[name=invoice_filter_name]').val();
d.invoice_filter_month = $('select[name=invoice_filter_month]').val();
d.invoice_filter_year = $('select[name=invoice_filter_year]').val();
}
});
$('select.on_change_invoice').on('change', function(){
},
"order": [
[0, "desc"]
],
"columns": [{
data: 'id',
searchable: false
},
{
data: 'full_number',
name: 'full_number'
},
{
data: 'invoice',
name: 'invoice',
orderable: false,
searchable: false
},
{
data: 'total_shipping',
name: 'total_shipping',
orderable: false,
searchable: false
},
{
data: 'txaction',
name: 'txaction',
orderable: false,
searchable: false
},
{
data: 'shopping_order.shopping_user.billing_firstname',
name: 'shopping_order.shopping_user.billing_firstname',
orderable: false
},
{
data: 'shopping_order.shopping_user.billing_lastname',
name: 'shopping_order.shopping_user.billing_lastname',
orderable: false
},
{
data: 'shopping_order.shopping_user.billing_email',
name: 'shopping_order.shopping_user.billing_email',
orderable: false
},
{
data: 'created_at',
name: 'created_at'
},
{
data: 'status',
name: 'status',
searchable: false
},
],
"bLengthChange": false,
"iDisplayLength": 100,
"language": {
"url": "/js/datatables-{{ \App::getLocale() }}.json"
}
});
$('select.on_change_invoice').on('change', function() {
oTable.draw();
loadInvoiceStats();
});
$('input.on_keyup_invoice').on('keyup', function(){
$('input.on_keyup_invoice').on('keyup', function() {
oTable.draw();
loadInvoiceStats();
});
});
/*$('#filter_sales_year').on('change', function(){

View file

@ -0,0 +1,327 @@
@if (isset($activeIncentive) && $activeIncentive)
<div class="d-flex col-xl-12 align-items-stretch">
<div class="w-100 mb-4 inc-dash-widget">
{{-- Hero-Bild mit Overlay --}}
@if ($activeIncentive->image)
<div class="inc-dash-hero">
<img src="{{ asset('img/incentive/' . $activeIncentive->image) }}" alt="{{ $activeIncentive->name }}">
<div class="inc-dash-hero-overlay"></div>
<div class="inc-dash-hero-content">
<span class="inc-dash-badge">
<i class="ion ion-md-trophy mr-1"></i> Incentive VIP
</span>
<h4 class="inc-dash-title">
{{ $activeIncentive->getLang('name') ?: $activeIncentive->name }}
</h4>
@if ($activeIncentive->getLang('subtitle'))
<p class="inc-dash-subtitle">{{ $activeIncentive->getLang('subtitle') }}</p>
@endif
</div>
</div>
@else
<div class="inc-dash-header-plain">
<span class="inc-dash-badge">
<i class="ion ion-md-trophy mr-1"></i> Incentive VIP
</span>
<h4 class="font-weight-bold mb-1">
{{ $activeIncentive->getLang('name') ?: $activeIncentive->name }}
</h4>
@if ($activeIncentive->getLang('subtitle'))
<p class="text-muted mb-0">{{ $activeIncentive->getLang('subtitle') }}</p>
@endif
</div>
@endif
{{-- Inhaltsbereich --}}
<div class="inc-dash-body">
{{-- Status-Leiste (nur fuer Teilnehmer) --}}
@if ($incentiveParticipant)
<div class="inc-dash-stats">
@if ($incentiveParticipant->rank)
<div class="inc-dash-stat">
<span class="inc-dash-stat-value">{{ $incentiveParticipant->rank }}</span>
<span class="inc-dash-stat-label">{{ __('incentive.your_rank') }}</span>
</div>
@endif
<div class="inc-dash-stat">
<span
class="inc-dash-stat-value">{{ number_format($incentiveParticipant->total_points, 0, ',', '.') }}</span>
<span class="inc-dash-stat-label">{{ __('incentive.points_short') }}</span>
</div>
<div class="inc-dash-stat">
@if ($incentiveParticipant->is_qualified)
<span class="inc-dash-qual-yes">{{ __('incentive.qualified') }}</span>
@else
<span class="inc-dash-qual-no">{{ __('incentive.open') }}</span>
@endif
</div>
</div>
@endif
{{-- Bilder-Leiste --}}
@php
$dashGallery = [];
$galleryDir = public_path('img/incentive');
if (is_dir($galleryDir)) {
$galleryFiles = glob($galleryDir . '/*.{jpg,jpeg,png,webp}', GLOB_BRACE) ?: [];
$heroImg = $activeIncentive->image;
foreach ($galleryFiles as $gf) {
$bn = basename($gf);
if ($bn !== $heroImg) {
$dashGallery[] = 'img/incentive/' . $bn;
}
}
sort($dashGallery);
$dashGallery = array_slice($dashGallery, 0, 4);
}
@endphp
@if (count($dashGallery) > 0)
<div class="inc-dash-gallery">
@foreach ($dashGallery as $gi)
<div class="inc-dash-gallery-thumb">
<img src="{{ asset($gi) }}" alt="Impression" loading="lazy">
</div>
@endforeach
</div>
@endif
{{-- Buttons --}}
<div class="inc-dash-actions">
<a href="{{ route('user_incentive_teaser', [$activeIncentive->slug]) }}"
class="btn inc-dash-btn-primary">
<i class="ion ion-md-trophy mr-1"></i>
{{ __('incentive.dashboard_btn_teaser') }}
</a>
<a href="{{ route('user_incentive_show', [$activeIncentive->slug]) }}"
class="btn inc-dash-btn-secondary">
<i class="ion ion-md-list mr-1"></i>
{{ __('incentive.dashboard_btn_ranking') }}
</a>
</div>
</div>
</div>
</div>
<style>
.inc-dash-widget {
border-radius: .75rem;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.10);
background: #fff;
}
.inc-dash-hero {
position: relative;
height: 180px;
overflow: hidden;
}
.inc-dash-hero img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.inc-dash-hero-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to bottom,
rgba(0, 0, 0, 0.05) 0%,
rgba(0, 0, 0, 0.20) 40%,
rgba(0, 0, 0, 0.70) 100%);
}
.inc-dash-hero-content {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 1.5rem 2rem;
z-index: 2;
}
.inc-dash-badge {
display: inline-block;
background: rgba(215, 215, 0, 0.92);
color: #333;
font-weight: 700;
padding: .25rem .8rem;
border-radius: 50px;
font-size: .75rem;
letter-spacing: .04em;
text-transform: uppercase;
margin-bottom: .6rem;
}
.inc-dash-title {
color: #fff;
font-weight: 800;
font-size: 1.6rem;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
margin: 0 0 .25rem 0;
line-height: 1.2;
}
.inc-dash-subtitle {
color: rgba(255, 255, 255, 0.88);
font-size: .95rem;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
margin: 0;
}
.inc-dash-header-plain {
background: linear-gradient(135deg, #6b7758 0%, #4a5340 100%);
color: #fff;
padding: 1.5rem 2rem;
}
.inc-dash-header-plain h4 {
color: #fff;
}
.inc-dash-body {
padding: 1.2rem 1.5rem 1.5rem;
}
.inc-dash-stats {
display: flex;
align-items: center;
gap: 1.5rem;
padding: .8rem 1rem;
margin-bottom: 1rem;
background: #f4f5f0;
border-radius: .5rem;
}
.inc-dash-stat {
text-align: center;
}
.inc-dash-stat-value {
display: block;
font-size: 1.4rem;
font-weight: 800;
color: #6b7758;
line-height: 1.1;
}
.inc-dash-stat-label {
font-size: .7rem;
text-transform: uppercase;
letter-spacing: .04em;
color: #888;
}
.inc-dash-qual-yes {
display: inline-block;
background: linear-gradient(135deg, #b8b800, #d7d700);
color: #333;
font-weight: 700;
padding: .3rem .8rem;
border-radius: 50px;
font-size: .78rem;
}
.inc-dash-qual-no {
display: inline-block;
background: #e8e8e8;
color: #777;
font-weight: 600;
padding: .3rem .8rem;
border-radius: 50px;
font-size: .78rem;
}
.inc-dash-gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: .4rem;
margin-bottom: 1rem;
border-radius: .5rem;
overflow: hidden;
}
.inc-dash-gallery-thumb {
aspect-ratio: 16/10;
overflow: hidden;
}
.inc-dash-gallery-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform .3s ease;
}
.inc-dash-gallery-thumb:hover img {
transform: scale(1.06);
}
.inc-dash-actions {
display: flex;
gap: .6rem;
flex-wrap: wrap;
}
.inc-dash-btn-primary {
background: linear-gradient(135deg, #6b7758, #4a5340);
color: #fff !important;
border: none;
border-radius: 50px;
padding: .55rem 1.8rem;
font-weight: 700;
font-size: .9rem;
transition: transform .2s, box-shadow .2s;
}
.inc-dash-btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 14px rgba(107, 119, 88, 0.35);
color: #fff !important;
}
.inc-dash-btn-secondary {
background: transparent;
color: #6b7758 !important;
border: 2px solid #6b7758;
border-radius: 50px;
padding: .55rem 1.8rem;
font-weight: 600;
font-size: .9rem;
transition: background .2s, color .2s;
}
.inc-dash-btn-secondary:hover {
background: #6b7758;
color: #fff !important;
}
@media (max-width: 575px) {
.inc-dash-hero {
height: 200px;
}
.inc-dash-hero-content {
padding: 1rem 1.2rem;
}
.inc-dash-title {
font-size: 1.2rem;
}
.inc-dash-gallery {
grid-template-columns: repeat(2, 1fr);
}
.inc-dash-stats {
gap: .8rem;
}
}
</style>
@endif

View file

@ -1,4 +1,4 @@
@if($user->active == 1)
@if($user->active == 1 || ($user->payment_account && ! $user->isActiveAccount()))
@if($user->payment_account && $user->daysActiveAccount() <= config('mivita.remind_first_days'))
<div class="d-flex col-xl-12 align-items-stretch">
<div class="card w-100 mb-4">

View file

@ -1,27 +1,27 @@
@php
$selectedMonth = request()->get('stats_month', date('n'));
$selectedYear = request()->get('stats_year', date('Y'));
// Start- und Enddatum für den Monat
$startDate = \Carbon\Carbon::createFromDate($selectedYear, $selectedMonth, 1)->startOfMonth();
$endDate = \Carbon\Carbon::createFromDate($selectedYear, $selectedMonth, 1)->endOfMonth();
// UserBusiness für den Monat laden (enthält Payline-Punkte)
$userBusiness = \App\Models\UserBusiness::where('user_id', $user->id)
->where('month', $selectedMonth)
->where('year', $selectedYear)
->first();
// UserSalesVolume für KP-Punkte
$userSalesVolume = $user->getUserSalesVolume($selectedMonth, $selectedYear, 'first');
// Kunden-Umsatz Punkte (KP - Eigene Punkte + Shop)
$customerPoints = $userSalesVolume ? $userSalesVolume->getPointsKPSum() : 0;
// Team-Umsatz Punkte (Payline) - Live-Berechnung wenn UserBusiness nicht verfügbar
$teamPaylinePoints = 0;
$isLiveCalculation = false;
if ($userBusiness) {
// Gespeicherte Daten aus UserBusiness verwenden
$teamPaylinePoints = $userBusiness->payline_points ?? 0;
@ -29,9 +29,13 @@
// Live-Berechnung über TreeCalcBot (für aktuellen Monat)
$isLiveCalculation = true;
try {
$treeCalcBot = new \App\Services\BusinessPlan\TreeCalcBot((int)$selectedMonth, (int)$selectedYear, 'member');
$treeCalcBot = new \App\Services\BusinessPlan\TreeCalcBot(
(int) $selectedMonth,
(int) $selectedYear,
'member',
);
$treeCalcBot->initBusinesslUserDetail($user);
if ($treeCalcBot->business_user) {
$teamPaylinePoints = $treeCalcBot->business_user->payline_points ?? 0;
}
@ -40,48 +44,50 @@
$teamPaylinePoints = 0;
}
}
// Direkte Neupartner (Firstlines) in diesem Monat
$directNewPartners = \App\User::where('m_sponsor', $user->id)
->whereColumn('id', '!=', 'm_sponsor')
->where('active_date', '>=', $startDate)
->where('active_date', '<=', $endDate)
->where('m_level', '!=', null)
->where('payment_account', '!=', null)
->count();
// ===== REKURSIVE TEAM-STRUKTUR =====
// Hilfsfunktion um alle Team-Mitglieder rekursiv zu sammeln (inkl. Sponsor-Kette)
$getAllTeamMemberIds = function($sponsorId, $maxDepth = 20) use (&$getAllTeamMemberIds) {
$getAllTeamMemberIds = function ($sponsorId, $maxDepth = 20) use (&$getAllTeamMemberIds) {
static $cache = [];
if (isset($cache[$sponsorId])) {
return $cache[$sponsorId];
}
$teamIds = [];
$currentLevel = [$sponsorId];
$depth = 0;
while (!empty($currentLevel) && $depth < $maxDepth) {
$children = \App\User::whereIn('m_sponsor', $currentLevel)
->whereColumn('id', '!=', 'm_sponsor')
->where('m_level', '!=', null)
->where('payment_account', '!=', null)
->whereNull('deleted_at')
->pluck('id')
->toArray();
$teamIds = array_merge($teamIds, $children);
$currentLevel = $children;
$depth++;
}
$cache[$sponsorId] = $teamIds;
return $teamIds;
};
// Alle Team-Mitglieder rekursiv laden
$allTeamMemberIds = $getAllTeamMemberIds($user->id);
// Neupartner im gesamten Team (rekursiv im Marketingplan)
$teamNewPartners = 0;
if (!empty($allTeamMemberIds)) {
@ -90,21 +96,21 @@
->where('active_date', '<=', $endDate)
->count();
}
// Kundenabos (is_for = 'customer' oder shop-bezogen)
$customerAbos = \App\Models\UserAbo::where('member_id', $user->id)
->where('is_for', 'customer')
->where('is_for', 'ot')
->whereIn('status', [1, 2]) // aktive Abos
->where('active', true)
->count();
// Eigene Abos
$ownAbos = \App\Models\UserAbo::where('user_id', $user->id)
->where('is_for', 'me')
->whereIn('status', [1, 2])
->where('active', true)
->count();
// Team-Abos (rekursiv über gesamtes Team im Marketingplan)
$teamAbos = 0;
if (!empty($allTeamMemberIds)) {
@ -116,131 +122,152 @@
}
@endphp
@if($user->isActiveAccount())
<div class="d-flex col-xl-12 align-items-stretch">
<div class="card w-100 mb-4">
<h5 class="card-header with-elements d-flex justify-content-between align-items-center flex-wrap">
<div class="card-header-title">
<i class="ion ion-md-stats mr-2"></i>{{__('home.monthly_statistics') }} - {{ HTMLHelper::getMonth($selectedMonth) }} {{ $selectedYear }}
</div>
<div class="d-flex align-items-center mt-2 mt-md-0">
<select id="stats-month-filter" class="form-control custom-select form-control-sm mr-2" style="width: auto;">
@foreach(HTMLHelper::getTransMonths() as $monthNum => $monthName)
<option value="{{ $monthNum }}" {{ $selectedMonth == $monthNum ? 'selected' : '' }}>{{ $monthName }}</option>
@endforeach
</select>
<select id="stats-year-filter" class="form-control custom-select form-control-sm" style="width: auto;">
@foreach(HTMLHelper::getYearRange() as $year)
<option value="{{ $year }}" {{ $selectedYear == $year ? 'selected' : '' }}>{{ $year }}</option>
@endforeach
</select>
</div>
</h5>
<div class="card-body">
<div class="row">
{{-- Kunden-Umsatz Punkte --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-cart" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.customer_turnover_points') }}</div>
<div class="font-weight-bold text-large">{{ number_format($customerPoints, 0, ',', '.') }}</div>
</div>
</div>
@if ($user->isActiveAccount())
<div class="d-flex col-xl-12 align-items-stretch">
<div class="card w-100 mb-4">
<h5 class="card-header with-elements d-flex justify-content-between align-items-center flex-wrap">
<div class="card-header-title">
<i class="ion ion-md-stats mr-2"></i>{{ __('home.monthly_statistics') }} -
{{ HTMLHelper::getMonth($selectedMonth) }} {{ $selectedYear }}
</div>
{{-- Team-Umsatz Punkte (Payline) --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-success text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-people" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">
{{ __('home.team_turnover_points') }} (Payline)
@if($isLiveCalculation)
<span class="badge badge-pill badge-info ml-1" title="{{ __('home.live_calculation_hint') }}">Live</span>
@endif
<div class="d-flex align-items-center mt-2 mt-md-0">
<select id="stats-month-filter" class="form-control custom-select form-control-sm mr-2"
style="width: auto;">
@foreach (HTMLHelper::getTransMonths() as $monthNum => $monthName)
<option value="{{ $monthNum }}" {{ $selectedMonth == $monthNum ? 'selected' : '' }}>
{{ $monthName }}</option>
@endforeach
</select>
<select id="stats-year-filter" class="form-control custom-select form-control-sm"
style="width: auto;">
@foreach (HTMLHelper::getYearRange() as $year)
<option value="{{ $year }}" {{ $selectedYear == $year ? 'selected' : '' }}>
{{ $year }}</option>
@endforeach
</select>
</div>
</h5>
<div class="card-body">
<div class="row">
{{-- Kunden-Umsatz Punkte --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center"
style="width: 50px; height: 50px;">
<i class="ion ion-md-cart" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.customer_turnover_points') }}</div>
<div class="font-weight-bold text-large">
{{ number_format($customerPoints, 0, ',', '.') }}</div>
</div>
<div class="font-weight-bold text-large">{{ number_format($teamPaylinePoints, 0, ',', '.') }}</div>
</div>
</div>
</div>
{{-- Direkte Neupartner --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-info text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-person-add" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.direct_new_partners') }}</div>
<div class="font-weight-bold text-large">{{ $directNewPartners }}</div>
{{-- Team-Umsatz Punkte (Payline) --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-success text-white rounded-circle d-flex align-items-center justify-content-center"
style="width: 50px; height: 50px;">
<i class="ion ion-md-people" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">
{{ __('home.team_turnover_points') }} (Payline)
@if ($isLiveCalculation)
<span class="badge badge-pill badge-info ml-1"
title="{{ __('home.live_calculation_hint') }}">Live</span>
@endif
</div>
<div class="font-weight-bold text-large">
{{ number_format($teamPaylinePoints, 0, ',', '.') }}
</div>
</div>
</div>
</div>
</div>
{{-- Neupartner im Team --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-warning text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-contacts" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.team_new_partners') }}</div>
<div class="font-weight-bold text-large">{{ $teamNewPartners }}</div>
{{-- Direkte Neupartner --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-info text-white rounded-circle d-flex align-items-center justify-content-center"
style="width: 50px; height: 50px;">
<i class="ion ion-md-person-add" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.direct_new_partners') }}
</div>
<div class="font-weight-bold text-large">{{ $directNewPartners }}</div>
</div>
</div>
</div>
</div>
{{-- Kundenabos --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-secondary text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-repeat" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.customer_subscriptions') }}</div>
<div class="font-weight-bold text-large">{{ $customerAbos }} + {{ $ownAbos }} {{ __('home.own') }}</div>
{{-- Neupartner im Team --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-warning text-white rounded-circle d-flex align-items-center justify-content-center"
style="width: 50px; height: 50px;">
<i class="ion ion-md-contacts" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.team_new_partners') }}</div>
<div class="font-weight-bold text-large">{{ $teamNewPartners }}</div>
</div>
</div>
</div>
</div>
{{-- Team-Abos --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-dark text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-sync" style="font-size: 1.5rem;"></i>
{{-- Kundenabos --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-secondary text-white rounded-circle d-flex align-items-center justify-content-center"
style="width: 50px; height: 50px;">
<i class="ion ion-md-repeat" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.customer_subscriptions') }}
</div>
<div class="font-weight-bold text-large">{{ $customerAbos }} +
{{ $ownAbos }}
{{ __('home.own') }}</div>
</div>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.team_subscriptions') }}</div>
<div class="font-weight-bold text-large">{{ $teamAbos }}</div>
</div>
{{-- Team-Abos --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-dark text-white rounded-circle d-flex align-items-center justify-content-center"
style="width: 50px; height: 50px;">
<i class="ion ion-md-sync" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.team_subscriptions') }}</div>
<div class="font-weight-bold text-large">{{ $teamAbos }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
// Filter für Monat/Jahr - Statistiken
function updateStatsFilter() {
var month = $('#stats-month-filter').val();
var year = $('#stats-year-filter').val();
var url = new URL(window.location.href);
url.searchParams.set('stats_month', month);
url.searchParams.set('stats_year', year);
window.location.href = url.toString();
}
<script>
$(document).ready(function() {
// Filter für Monat/Jahr - Statistiken
function updateStatsFilter() {
var month = $('#stats-month-filter').val();
var year = $('#stats-year-filter').val();
var url = new URL(window.location.href);
url.searchParams.set('stats_month', month);
url.searchParams.set('stats_year', year);
window.location.href = url.toString();
}
$('#stats-month-filter, #stats-year-filter').on('change', function() {
updateStatsFilter();
$('#stats-month-filter, #stats-year-filter').on('change', function() {
updateStatsFilter();
});
});
});
</script>
</script>
@endif

View file

@ -15,35 +15,42 @@
</div>
</div>
@endif
<h4 class="media align-items-center font-weight-bold py-3 mb-2">
{{-- <img src="assets/img/avatars/1.png" alt="" class="ui-w-50 rounded-circle"> --}}
<div class="media-body ml-3">
{{ __('home.welcome_back') }}, {{$user->account->first_name}}!
<div class="text-muted text-tiny mt-1"><small class="font-weight-normal">{{ __('home.today_is') }} {{__('cal.weekdays.'.$now->format('l'))}}, {{$now->format('j')}}. {{__('cal.months.'.$now->format('F'))}} {{$now->format('Y')}}</small></div>
</div>
</h4>
<h4 class="media align-items-center font-weight-bold py-3 mb-2">
{{-- <img src="assets/img/avatars/1.png" alt="" class="ui-w-50 rounded-circle"> --}}
<div class="media-body ml-3">
{{ __('home.welcome_back') }}, {{ $user->account->first_name }}!
<div class="text-muted text-tiny mt-1"><small class="font-weight-normal">{{ __('home.today_is') }}
{{ __('cal.weekdays.' . $now->format('l')) }}, {{ $now->format('j') }}.
{{ __('cal.months.' . $now->format('F')) }} {{ $now->format('Y') }}</small></div>
</div>
</h4>
<hr class="container-m-nx mt-0 mb-4">
<hr class="container-m-nx mt-0 mb-4">
<div class="row">
<div class="row">
@if (Auth::user()->isActiveAccount())
@include('dashboard._news')
@include('dashboard._reminder')
@include('dashboard._incentive')
@endif
@include('dashboard._membership')
@include('dashboard._shop')
@include('dashboard._reminder')
@include('dashboard._membership')
@include('dashboard._shop')
@if (Auth::user()->isActiveAccount())
@include('dashboard._statistics')
@include('dashboard._points')
@include('dashboard._activities')
@endif
@include('dashboard._settings')
@include('dashboard._settings')
</div>
</div>
<hr class="container-m-nx mt-0 mb-4">
<hr class="container-m-nx mt-0 mb-4">
@endsection

View file

@ -200,12 +200,20 @@
</a>
</li>
<li class="sidenav-item{{ Request::is('user/abos/team/*') ? ' active' : '' }}">
<li class="sidenav-item{{ Request::is('user/abos/team/show') ? ' active' : '' }}">
<a href="{{ route('user_abos_team_show') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-refresh-circle"></i>
<div>{{ __('navigation.teamabos') }}</div>
</a>
</li>
<li
class="sidenav-item{{ Request::is('user/abos/team/customers', 'user/abos/team/customers/*') ? ' active' : '' }}">
<a href="{{ route('user_abos_team_customers') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-refresh-circle"></i>
<div>{{ __('navigation.team_customers') }}</div>
</a>
</li>
<li
class="sidenav-item{{ Request::is('user/abos/me', 'user/abos/detail/me/*') ? ' active' : '' }}">
<a href="{{ route('user_abos', ['me']) }}" class="sidenav-link"><i
@ -259,6 +267,15 @@
<div>{{ __('navigation.downloadcenter') }}</div>
</a>
</li>
@php $activeIncentive = \App\Models\Incentive::active()->first(); @endphp
@if ($activeIncentive)
<li class="sidenav-item{{ Request::is('incentive/*') ? ' active' : '' }}">
<a href="{{ route('user_incentive_teaser', [$activeIncentive->slug]) }}"
class="sidenav-link"><i class="text-secondary sidenav-icon ion ion-md-trophy"></i>
<div>{{ __('navigation.incentive') }}</div>
</a>
</li>
@endif
@endif
@endif
@ -397,6 +414,27 @@
</ul>
</li>
<li class="sidenav-item @if (Request::is('admin/incentives*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-trophy"></i>
<div>{{ __('navigation.incentives') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('admin/incentives') ? ' active' : '' }}">
<a href="{{ route('admin_incentives') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-list"></i>
<div>{{ __('navigation.overview') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/incentives/create') ? ' active' : '' }}">
<a href="{{ route('admin_incentive_create') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-add-circle"></i>
<div>{{ __('navigation.create') }}</div>
</a>
</li>
</ul>
</li>
<li class="sidenav-item @if (Request::is('admin/product/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-cube"></i>
@ -573,6 +611,12 @@
<div>{{ __('navigation.user_cleanup') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('superadmin/tools', 'superadmin/tool/*') ? ' active' : '' }}">
<a href="{{ route('superadmin_tools') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-cog"></i>
<div>{{ __('navigation.tools') }}</div>
</a>
</li>
@endif
@if (Auth::user()->isSySAdmin())
<li class="sidenav-divider mb-1"></li>

View file

@ -0,0 +1,89 @@
@php $total = 0; $colspan = 3 + count($calculation_months); @endphp
<div class="table-responsive">
<table class="table table-sm table-striped table-bordered mb-0">
<thead>
<tr>
<th>{{ $label_header }}</th>
<th>{{ $date_header }}</th>
<th class="text-right">{{ __('incentive.onetime') }}</th>
@foreach($calculation_months as $period)
<th class="text-right">{{ str_pad($period['month'], 2, '0', STR_PAD_LEFT) }}/{{ $period['year'] }}</th>
@endforeach
<th class="text-right font-weight-bold">{{ __('incentive.sum') }}</th>
</tr>
</thead>
<tbody>
@forelse($sources as $idx => $source)
<tr class="{{ count($source['transactions']) > 0 ? 'cursor-pointer' : '' }}"
@if(count($source['transactions']) > 0)
data-toggle="collapse" data-target="#transactions-{{ $type }}-{{ $source['id'] }}" aria-expanded="false"
@endif
>
<td>
@if(count($source['transactions']) > 0)
<i class="fa fa-chevron-right fa-xs text-muted mr-1 toggle-icon" id="icon-{{ $type }}-{{ $source['id'] }}"></i>
@endif
{{ $source['label'] }}
</td>
<td>{{ str_pad($source['month'], 2, '0', STR_PAD_LEFT) }}/{{ $source['year'] }}</td>
<td class="text-right">{{ number_format($source['onetime'], 0, ',', '.') }}</td>
@foreach($source['monthly'] as $mp)
<td class="text-right">{{ $mp > 0 ? number_format($mp, 0, ',', '.') : '-' }}</td>
@endforeach
<td class="text-right font-weight-bold">{{ number_format($source['total'], 0, ',', '.') }}</td>
</tr>
@if(count($source['transactions']) > 0)
<tr class="collapse" id="transactions-{{ $type }}-{{ $source['id'] }}">
<td colspan="{{ $colspan + 1 }}" class="p-0 border-0">
<table class="table table-sm mb-0 bg-light">
<thead>
<tr class="text-muted small">
<th class="pl-4">{{ __('incentive.transaction_date') }}</th>
<th>{{ __('incentive.transaction_description') }}</th>
<th>{{ __('incentive.transaction_period') }}</th>
<th class="text-right">{{ __('incentive.transaction_type') }}</th>
<th class="text-right pr-4">{{ __('incentive.transaction_points') }}</th>
</tr>
</thead>
<tbody>
@foreach($source['transactions'] as $tx)
<tr class="small">
<td class="pl-4">{{ $tx['date'] }}</td>
<td>{{ $tx['label'] }}</td>
<td>{{ str_pad($tx['month'], 2, '0', STR_PAD_LEFT) }}/{{ $tx['year'] }}</td>
<td class="text-right">
@if($tx['type'] === 'onetime')
<span class="badge badge-primary">{{ __('incentive.onetime') }}</span>
@else
<span class="badge badge-info">{{ __('incentive.accumulated') }}</span>
@endif
</td>
<td class="text-right pr-4 font-weight-bold">{{ number_format($tx['points'], 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
</table>
</td>
</tr>
@endif
@php $total += $source['total']; @endphp
@empty
<tr>
<td colspan="{{ $colspan + 1 }}" class="text-center text-muted">{{ $empty_message }}</td>
</tr>
@endforelse
@if(count($sources) > 0)
<tr class="font-weight-bold table-secondary">
<td colspan="{{ $colspan }}">{{ __('incentive.subtotal') }}</td>
<td class="text-right">{{ number_format($total, 0, ',', '.') }}</td>
</tr>
@endif
</tbody>
</table>
</div>
<style>
.cursor-pointer { cursor: pointer; }
.cursor-pointer:hover { background-color: rgba(0,0,0,.05) !important; }
.toggle-icon { transition: transform .2s; }
tr[aria-expanded="true"] .toggle-icon { transform: rotate(90deg); }
</style>

View file

@ -1,196 +1,278 @@
{!! Form::open(['action' => route('portal.my_subscriptions.create', 5), 'method' => 'POST', 'class' => '']) !!}
<div class="card-body">
@if(Yard::instance('subscription')->content()->count())
<style>
.cart-item {
position: relative;
padding: 15px 0;
border-bottom: 1px solid #ddd;
}
.cart-item:last-child {
border-bottom: none;
}
.cart-item .product-image {
max-width: 80px;
}
.cart-item .product-title {
font-size: 1.1em;
font-weight: 500;
color: #393939;
}
.cart-item .product-details {
font-size: 0.9em;
color: #666;
}
.cart-item .quantity-input {
width: 80px;
text-align: center;
}
.cart-summary {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
}
.cart-summary .table {
margin-bottom: 0;
}
.cart-summary .table td {
border-top: none;
padding: 8px 0;
}
.cart-summary .total-row {
font-weight: bold;
border-top: 1px solid #ddd;
}
</style>
@if (Yard::instance('subscription')->content()->count())
<style>
.cart-item {
position: relative;
padding: 15px 0;
border-bottom: 1px solid #ddd;
}
.cart-item:last-child {
border-bottom: none;
}
.cart-item .product-image {
max-width: 80px;
}
.cart-item .product-title {
font-size: 1.1em;
font-weight: 500;
color: #393939;
}
.cart-item .product-details {
font-size: 0.9em;
color: #666;
}
.cart-item .quantity-input {
width: 80px;
text-align: center;
}
.cart-summary {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
}
.cart-summary .table {
margin-bottom: 0;
}
.cart-summary .table td {
border-top: none;
padding: 8px 0;
}
.cart-summary .total-row {
font-weight: bold;
border-top: 1px solid #ddd;
}
</style>
<div class="cart-overview">
<h4 class="mb-4">{{ __('abo.abo_order_hl') }}</h4>
@if (isset($error))
<div class="alert alert-danger">
<strong>{{ $error }}</strong>
</div>
@endif
@foreach (Yard::instance('subscription')->getContentByOrder() as $row)
@php($product = \App\Models\Product::find($row->id))
<div class="cart-item">
<div class="row align-items-center">
<div class="col-3 col-sm-2">
@if ($row->options->has('image'))
<img src="{{ route('product_image', [$row->options->image]) }}" class="product-image"
alt="{{ $row->name }}">
@endif
</div>
<div class="col-9 col-sm-10">
<div class="row">
<div class="col-12 col-sm-6 col-md-7">
<div class="product-title">
{{ $row->name }}
<div class="cart-overview">
<h4 class="mb-4">{{ __('abo.abo_order_hl') }}</h4>
@if(isset($error))
<div class="alert alert-danger">
<strong>{{ $error }}</strong>
</div>
@endif
@foreach(Yard::instance('subscription')->getContentByOrder() as $row)
@php($product = \App\Models\Product::find($row->id))
<div class="cart-item">
<div class="row align-items-center">
<div class="col-3 col-sm-2">
@if($row->options->has('image'))
<img src="{{ route('product_image', [$row->options->image]) }}" class="product-image" alt="{{ $row->name }}">
@endif
</div>
<div class="col-9 col-sm-10">
<div class="row">
<div class="col-12 col-sm-6 col-md-7">
<div class="product-title">
{{ $row->name }}
{!! get_abo_type_badge_by_product($product) !!}
</div>
<div class="product-details">
<div>{{ __('order.content') }}: {{ $product->contents }}</div>
<div>{{ __('order.art_no') }}: {{ $product->number }}</div>
</div>
</div>
<div class="product-details">
<div>{{ __('order.content') }}: {{ $product->contents }}</div>
<div>{{ __('order.art_no') }}: {{ $product->number }}</div>
<div class="col-6 col-sm-3 col-md-2">
<div class="price-single">
<div style="">{{ $row->price() }}
{{ Yard::instance('subscription')->getUserTaxFree() }}</div>
@if (Yard::instance('subscription')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('price', $row, 2) }}
{{ Yard::instance('subscription')->getPriceCurrencyUnit() }} </span>
@endif
</div>
</div>
</div>
<div class="col-6 col-sm-3 col-md-2">
<div class="price-single">
<div style="">{{ $row->price() }} {{ Yard::instance('subscription')->getUserTaxFree() }}</div>
@if(Yard::instance('subscription')->isPriceCurrency())
<span class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('price', $row, 2) }} {{ Yard::instance('subscription')->getPriceCurrencyUnit() }} </span>
@endif
</div>
</div>
<div class="col-6 col-sm-3 col-md-3">
<div class="quantity-select text-right">
@if($row->options->comp)
<span class="text-right">1 x</span>
@else
<span class="text-right">{{ $row->qty }} x</span>
@endif
</div>
<div class="price-total text-right mt-2">
<span class="font-bold text-price-total"><strong>{{ $row->subtotal() }} &euro; </strong></span>
@if(Yard::instance('subscription')->isPriceCurrency())
<br>
<span class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('subtotal', $row, 2) }} {{ Yard::instance('subscription')->getPriceCurrencyUnit() }} </span> @endif
<div class="col-6 col-sm-3 col-md-3">
<div class="quantity-select text-right">
@if ($row->options->comp)
<span class="text-right">1 x</span>
@else
<span class="text-right">{{ $row->qty }} x</span>
@endif
</div>
<div class="price-total text-right mt-2">
<span class="font-bold text-price-total"><strong>{{ $row->subtotal() }} &euro;
</strong></span>
@if (Yard::instance('subscription')->isPriceCurrency())
<br>
<span
class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('subtotal', $row, 2) }}
{{ Yard::instance('subscription')->getPriceCurrencyUnit() }} </span>
@endif
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endforeach
@endforeach
<div class="cart-summary mt-4">
<table class="table">
<tbody>
<tr>
<td>{{ __('order.subtotal') }}:</td>
<td class="text-right">
{{ Yard::instance('subscription')->total() }}
@if(Yard::instance('subscription')->isPriceCurrency())
<span class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('subtotal') }} {{ Yard::instance('subscription')->getPriceCurrencyUnit() }}</span>
@endif
</td>
</tr>
<tr>
<td>{{ __('Delivery country') }}:</td>
<td class="text-right">
{{ Yard::instance('subscription')->getShippingCountryName() }}
</td>
</tr>
<tr>
<td>{{ __('order.shipping_costs') }}:</td>
<td class="text-right">
{{ Yard::instance('subscription')->shipping() }}
</td>
</tr>
@if(Yard::instance('subscription')->getUserTaxFree())
<tr>
<td class="">{{ __('order.sum_net') }}:</td>
<td class="text-right">{{ Yard::instance('subscription')->subtotalWithShipping() }} </td>
</tr>
@else
<tr>
<td class="">{{ __('order.total_without_VAT') }}:</td>
<td class="text-right">{{ Yard::instance('subscription')->subtotalWithShipping() }} </td>
</tr>
<tr>
<td class="">{{ __('order.plus_VAT') }}:</td>
<td class="text-right">{{ Yard::instance('subscription')->taxWithShipping() }} </td>
</tr>
@endif
<tr class="total-row">
<td>
@if(Yard::instance('subscription')->getUserTaxFree())
{{ __('order.total_net') }}:
@else
{{ __('order.total_gross') }}:
@endif
</td>
<td class="text-right">
{{ Yard::instance('subscription')->totalWithShipping() }}
@if(Yard::instance('subscription')->isPriceCurrency())
<span class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('totalWithShipping') }} {{ Yard::instance('subscription')->getPriceCurrencyUnit() }}</span>
@endif
</td>
</tr>
</tbody>
</table>
<div class="cart-summary mt-4">
<table class="table">
<tbody>
<tr>
<td>{{ __('order.subtotal') }}:</td>
<td class="text-right">
{{ Yard::instance('subscription')->total() }}
@if (Yard::instance('subscription')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('subtotal') }}
{{ Yard::instance('subscription')->getPriceCurrencyUnit() }}</span>
@endif
</td>
</tr>
<tr>
<td>{{ __('Delivery country') }}:</td>
<td class="text-right">
{{ Yard::instance('subscription')->getShippingCountryName() }}
</td>
</tr>
<tr>
<td>{{ __('order.shipping_costs') }}:</td>
<td class="text-right">
{{ Yard::instance('subscription')->shipping() }}
</td>
</tr>
@if (Yard::instance('subscription')->getUserTaxFree())
<tr>
<td class="">{{ __('order.sum_net') }}:</td>
<td class="text-right">{{ Yard::instance('subscription')->subtotalWithShipping() }}
</td>
</tr>
@else
<tr>
<td class="">{{ __('order.total_without_VAT') }}:</td>
<td class="text-right">{{ Yard::instance('subscription')->subtotalWithShipping() }}
</td>
</tr>
<tr>
<td class="">{{ __('order.plus_VAT') }}:</td>
<td class="text-right">{{ Yard::instance('subscription')->taxWithShipping() }} </td>
</tr>
@endif
<tr class="total-row">
<td>
@if (Yard::instance('subscription')->getUserTaxFree())
{{ __('order.total_net') }}:
@else
{{ __('order.total_gross') }}:
@endif
</td>
<td class="text-right">
{{ Yard::instance('subscription')->totalWithShipping() }}
@if (Yard::instance('subscription')->isPriceCurrency())
<span
class="small">~{{ Yard::instance('subscription')->getCurrencyByKey('totalWithShipping') }}
{{ Yard::instance('subscription')->getPriceCurrencyUnit() }}</span>
@endif
</td>
</tr>
</tbody>
</table>
</div>
<div class="abo-settings mt-4 text-right">
<div class="alert alert-info text-left">
<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" id="abo_interval_select">
{!! HTMLHelper::getAboDeliveryOptions() !!}
</select>
<div id="abo_interval_info" class="alert alert-info mt-2 small d-none"></div>
<div id="abo_interval_warning" class="alert alert-warning mt-2 small d-none"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
var select = document.getElementById('abo_interval_select');
var info = document.getElementById('abo_interval_info');
var warning = document.getElementById('abo_interval_warning');
if (!select || !info || !warning) return;
var infoTpl = @json(__('abo.info_next_execution_select', ['placeholder_days' => '__DAYS__', 'placeholder_date' => '__DATE__']));
var warnTpl = @json(__('abo.warning_next_date_soon_select', ['placeholder_days' => '__DAYS__', 'placeholder_date' => '__DATE__']));
function checkDays() {
var option = select.options[select.selectedIndex];
var days = parseInt(option.getAttribute('data-days'), 10);
var date = option.getAttribute('data-date') || '';
if (isNaN(days)) {
return;
}
var showWarning = days < 20;
if (showWarning) {
info.innerHTML = '';
info.classList.add('d-none');
warning.innerHTML =
'<i class="fa fa-exclamation-triangle"></i> ' +
warnTpl.replace('__DAYS__', days).replace('__DATE__', date);
warning.classList.remove('d-none');
} else {
warning.classList.add('d-none');
info.innerHTML =
'<i class="fa fa-info-circle"></i> ' +
infoTpl.replace('__DAYS__', days).replace('__DATE__', date);
info.classList.remove('d-none');
}
}
select.addEventListener('change', checkDays);
checkDays();
});
</script>
</div>
<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') !!}</i></em>
<hr class="my-2" style="border-color: #b4b4b4;">
<em class="font-weight-bold"><i>{!! __('abo.abo_order_info_check_2') !!}</i></em>
<hr class="my-2" style="border-color: #b4b4b4;">
<em class="small"><i>{!! __('abo.abo_order_info_check_3', [
'abo-min-duration' => \App\Models\Setting::getContentBySlug('abo-min-duration'),
]) !!}</i></em>
<hr class="my-2" style="border-color: #b4b4b4;">
<label class="switcher switcher-success d-inline-flex align-items-center">
<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>
</div>
</div>
<div class="abo-settings mt-4">
<h5>{{ __('abo.abo_settings') }}</h5>
<div class="form-group">
<label>{{ __('abo.delivery_day') }}*</label>
<select class="form-control custom-select" name="abo_interval">
{!! HTMLHelper::getAboDeliveryOptions() !!}
</select>
</div>
<div class="alert alert-info">
<i class="fa fa-info-circle"></i> {!! __('abo.abo_order_info_check') !!}
<hr>
<i class="fa fa-info-circle"></i> {!! __('abo.abo_order_info_check_2') !!}
<hr>
<i class="fa fa-info-circle"></i> {!! __('abo.abo_order_info_check_3', ['abo-min-duration' => \App\Models\Setting::getContentBySlug('abo-min-duration')]) !!}
</div>
</div>
</div>
@endif
@endif
</div>
<div class="card-footer">
<button type="submit" class="btn btn-default" name="action" value="back">{{ __('abo.back') }}</button>
<div class="float-right">
<button type="submit" class="btn btn-secondary" name="action" value="checkout"><i class="ion ion-ios-redo"></i> {{ __('order.confirm_and_proceed_to_checkout') }}</button>
<button type="submit" class="btn btn-secondary" name="action" value="checkout"><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-lock"></i> {!! __('payment.checkout_ssl_server') !!}</em>
<em class="small"><i class="fa fa-info-circle"></i> {!! __('order.confirm_send_order_info') !!}</em>
</div>
</div>
{!! Form::close() !!}

View file

@ -7,7 +7,6 @@
<ul class="sidenav-inner{{ empty($layout_sidenav_horizontal) ? ' py-1' : '' }}">
@if (Auth::guard('customers')->check())
<li class="sidenav-item{{ Request::is('portal/dashboard') ? ' active' : '' }}">
<a href="{{ route('portal.dashboard') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-home"></i>
@ -26,14 +25,12 @@
<div>{{ __('navigation.my_orders') }}</div>
</a>
</li>
@if (Auth::user()->is_admin || Util::isTestSystem(true))
<li class="sidenav-item{{ Request::is('portal/subscriptions') ? ' active' : '' }}">
<a href="{{ route('portal.my_subscriptions') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-refresh-circle"></i>
<div>{{ __('navigation.myabo') }} <span class="badge badge-warning">DEV</span></div>
</a>
</li>
@endif
<li class="sidenav-item{{ Request::is('portal/subscriptions') ? ' active' : '' }}">
<a href="{{ route('portal.my_subscriptions') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-refresh-circle"></i>
<div>{{ __('navigation.myabo') }} </div>
</a>
</li>
{{--
<li class="sidenav-item{{ Request::is('portal/settings') ? ' active' : '' }}">
<a href="{{ route('portal.settings') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-settings"></i><div>{{ __('navigation.settings') }}</div></a>

View file

@ -271,6 +271,7 @@
@endif
</div>
<hr class="m-0">
@if (! $shopping_order->is_abo)
<div class="card-body">
<h6 class="font-weight-semibold">
{{ __('order.reorder') }}
@ -288,6 +289,7 @@
</div>
<hr class="m-0">
@endif
<div class="card-body">
<h6 class="font-weight-semibold">

View file

@ -1,29 +1,61 @@
@extends('layouts.layout-2')
@section('content')
<div class="card mt-5">
<h5 class="card-header py-4 px-5">Sys Admin Tools</h5>
<h5 class="card-header py-4 px-5">
@if (Auth::user()->isSuperAdmin())
Super
@else
Sys
@endif
Admin Tools
</h5>
<div class="row no-gutters row-bordered">
<div class="col-md-12 p-5">
<a href="{{ route('sysadmin_tool', ['buyings_products']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Add Buyings Products from Order</a>
<a href="{{ route('sysadmin_tool', ['business_structur']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Business Strukrur speichern</a>
<a href="{{ route('sysadmin_tool', ['sales_members']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Buchnungen Pakete Berater nach Jahren</a>
<a href="{{ route('sysadmin_tool', ['customers']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Kundenhoheit prüfen</a>
<a href="{{ route('sysadmin_tool', ['cronjobs']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Cron Jobs</a>
<a href="{{ route('sysadmin_tool', ['domainssl']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Subdomains prüfen SSL/Aktiv</a>
<a href="{{ route('sysadmin_tool', ['shopping_orders']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Shopping Orders Käufe</a>
<a href="{{ route('sysadmin_tool', ['import']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Import</a>
<a href="{{ route('sysadmin_tool', ['corrections']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Corrections Points / Payment / Price / Tax / Tax Spit / Points / Discount </a>
<a href="{{ route('sysadmin_tool', ['change_user_businesses']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Change user level qual_pp in UserBusiness </a>
<a href="{{ route('sysadmin_tool', ['repair_sales_volume_invoice']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Repair Sales Volume save InvoiceID </a>
<a href="{{ route('sysadmin_tool', ['user_credit_items_change_message']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; User Credit items Change Message </a>
<a href="{{ route('sysadmin_tool', ['clean_html_product_description']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; clean_html_product_description </a>
<a href="{{ route('sysadmin_tool', ['user_credit_items_add_from']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; user_credit_items add from month year </a>
<a href="{{ route('sysadmin_tool', ['import_dbip_country_lite']) }}" class="d-block mb-3"><i class="ion ion-ios-arrow-forward"></i>&nbsp; Import dbip-country-lite </a>
<div class="col-md-12 p-5">
@if (Auth::user()->isSysAdmin())
<a href="{{ route('sysadmin_tool', ['buyings_products']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Add Buyings Products from Order</a>
<a href="{{ route('sysadmin_tool', ['business_structur']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Business Strukrur speichern</a>
<a href="{{ route('sysadmin_tool', ['sales_members']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Buchnungen Pakete Berater nach Jahren</a>
<a href="{{ route('sysadmin_tool', ['customers']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Kundenhoheit prüfen</a>
<a href="{{ route('sysadmin_tool', ['cronjobs']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Cron Jobs</a>
<a href="{{ route('sysadmin_tool', ['domainssl']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Subdomains prüfen SSL/Aktiv</a>
<a href="{{ route('sysadmin_tool', ['shopping_orders']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Shopping Orders Käufe</a>
<a href="{{ route('sysadmin_tool', ['import']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Import</a>
<a href="{{ route('sysadmin_tool', ['corrections']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Corrections Points / Payment / Price / Tax / Tax
Spit /
Points / Discount </a>
<a href="{{ route('sysadmin_tool', ['change_user_businesses']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Change user level qual_pp in UserBusiness </a>
<a href="{{ route('sysadmin_tool', ['repair_sales_volume_invoice']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Repair Sales Volume save InvoiceID </a>
<a href="{{ route('sysadmin_tool', ['user_credit_items_change_message']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; User Credit items Change Message </a>
<a href="{{ route('sysadmin_tool', ['clean_html_product_description']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; clean_html_product_description </a>
<a href="{{ route('sysadmin_tool', ['user_credit_items_add_from']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; user_credit_items add from month year </a>
<a href="{{ route('sysadmin_tool', ['import_dbip_country_lite']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; Import dbip-country-lite </a>
<a href="{{ route('sysadmin_tool', ['payone_callback_testbench']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; <strong>Payone-Callback Testbench</strong>
({{ parse_url(route('api.payment_status', [], true), PHP_URL_HOST) }}/payment/status) </a>
@endif
@if (Auth::user()->isSuperAdmin())
<a href="{{ route('sysadmin_tool', ['abo_orders_overview']) }}" class="d-block mb-3"><i
class="ion ion-ios-arrow-forward"></i>&nbsp; <strong class="text-danger">Abo-Bestellungen
Zahlungsdifferenzen</strong> </a>
@endif
</div>
</div>
</div>
</div>
@endsection
</div>
@endsection

View file

@ -0,0 +1,135 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold mb-4">
Abo-Bestellungen Soll-Brutto vs. tatsächlich eingezogen (ShoppingPayment)
</h4>
<div class="card mb-4">
<div class="card-body">
<div class="btn-group mb-3" role="group">
<a href="{{ route('sysadmin_tool', ['abo_orders_overview']) }}"
class="btn btn-{{ $filter === 'all' ? 'primary' : 'outline-primary' }}">Alle</a>
<a href="{{ route('sysadmin_tool', ['abo_orders_overview']) }}?filter=berater"
class="btn btn-{{ $filter === 'berater' ? 'warning' : 'outline-warning' }}">Berater</a>
<a href="{{ route('sysadmin_tool', ['abo_orders_overview']) }}?filter=kunde"
class="btn btn-{{ $filter === 'kunde' ? 'info' : 'outline-info' }}">Kunden</a>
</div>
<div class="row">
<div class="col-md-3">
<div class="alert alert-info mb-0">
<strong>Gesamt Bestellungen:</strong> {{ $summary['total_orders'] }}
</div>
</div>
<div class="col-md-3">
<div class="alert alert-{{ $summary['affected_orders'] > 0 ? 'danger' : 'success' }} mb-0">
<strong>Betroffene Bestellungen:</strong> {{ $summary['affected_orders'] }}
</div>
</div>
<div class="col-md-3">
<div class="alert alert-{{ $summary['total_diff'] != 0 ? 'danger' : 'success' }} mb-0">
<strong>Fehlbetrag gesamt:</strong> {{ number_format($summary['total_diff'], 2, ',', '.') }} &euro;
</div>
</div>
<div class="col-md-3">
<div class="alert alert-warning mb-0">
<strong>Hinweis:</strong> Eingezogen = Summe <code>shopping_payments.amount</code> bei TX
paid / extern_paid / invoice_paid. Soll = <code>total_shipping</code> (Brutto).
</div>
</div>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-bordered table-sm">
<thead class="thead-dark">
<tr>
<th>Abo-Order ID</th>
<th>Abo ID</th>
<th>Order ID</th>
<th>User</th>
<th>Typ</th>
<th class="text-right">Netto+Versand (subtotal_ws)</th>
<th class="text-right">MwSt</th>
<th class="text-right">Soll Brutto (total_shipping)</th>
<th class="text-right">Eingezogen (Zahlung)</th>
<th>Zahlungs-TX</th>
<th class="text-right text-danger">Differenz (Soll Ist)</th>
<th>Status</th>
<th>Bezahlt</th>
<th>TX-Action</th>
<th>Datum</th>
</tr>
</thead>
<tbody>
@foreach ($rows as $row)
<tr class="{{ isset($row['diff']) && $row['diff'] != 0 ? 'table-danger' : '' }}">
<td>{{ $row['abo_order_id'] }}</td>
<td>{{ $row['abo_id'] }}</td>
<td>{!! $row['order_id'] !!}</td>
<td>
<small>
@if ($row['user_id'])
<strong>#{{ $row['user_id'] }}</strong>
@endif
{{ $row['user_name'] }}<br>
{{ $row['user_email'] }}
</small>
</td>
<td>
@if ($row['is_for'] === 'me')
<span class="badge badge-outline-warning-dark">Berater</span>
@else
<span class="badge badge-outline-info">Kunde</span>
@endif
</td>
<td class="text-right">{{ number_format($row['subtotal_ws'], 2, ',', '.') }} &euro;</td>
<td class="text-right">{{ number_format($row['tax'], 2, ',', '.') }} &euro;</td>
<td class="text-right">{{ number_format($row['total_shipping'], 2, ',', '.') }} &euro;</td>
<td class="text-right">
@if ($row['actual_charged_eur'] !== null)
{{ number_format($row['actual_charged_eur'], 2, ',', '.') }} &euro;
@else
<span class="text-muted"
title="Keine Zahlung mit Status paid/extern_paid/invoice_paid"></span>
@endif
</td>
<td>
<small>{{ $row['payment_txactions'] ?: '' }}</small>
@if (($row['payment_count'] ?? 0) > 1)
<span class="badge badge-secondary"
title="Anzahl Zahlungszeilen">{{ $row['payment_count'] }}</span>
@endif
</td>
<td class="text-right">
@if ($row['diff'] === null)
<span class="text-muted"></span>
@elseif($row['diff'] != 0)
<strong class="text-danger">{{ number_format($row['diff'], 2, ',', '.') }}
&euro;</strong>
@else
<span class="text-success">0,00 &euro;</span>
@endif
</td>
<td>{!! $row['status_badge'] ?? '<span class="badge badge-pill badge-secondary">' . $row['status'] . '</span>' !!}</td>
<td>
@if ($row['paid'])
<span class="badge badge-success">Ja</span>
@else
<span class="badge badge-danger">Nein</span>
@endif
</td>
<td>{{ $row['txaction'] }}</td>
<td><small>{{ $row['created_at'] ? $row['created_at']->format('d.m.Y H:i') : '-' }}</small>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,268 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold mb-3">
Payone-Callback Testbench
</h4>
<p class="text-muted mb-4">
<strong>Erstbestellung Abo (realistisch):</strong> Zuerst legt der Checkout das Abo an
(<code>CheckoutController</code> <code>AboHelper::createNewAbo</code>), danach meldet Payone per
<code>POST {{ route('api.payment_status', [], true) }}</code> den Zahlungsstatus dann laufen u. a.
<code>Payment::paymentStatusPaidAction</code>, Abo-Freischaltung (<code>setAboActive</code>) und
<code>IncentiveTracker::trackAboActivated</code>. Die Testbench-Schritte sind in dieser Reihenfolge angeordnet.
Nur außerhalb Production.
</p>
@if (session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif
<div class="card mb-4">
<div class="card-header font-weight-bold">1. Test-Bestellung anlegen</div>
<div class="card-body">
<form method="post" action="{{ route('sysadmin_tool_store', ['payone_callback_testbench']) }}">
@csrf
<input type="hidden" name="action" value="create_fixture">
<div class="form-group row">
<label class="col-md-3 col-form-label">Betrag (Brutto EUR)</label>
<div class="col-md-4">
<input type="number" name="amount_eur" class="form-control" step="0.01" min="0.01"
value="{{ old('amount_eur', '119.00') }}" required>
</div>
</div>
<div class="form-group row">
<label class="col-md-3 col-form-label">Berater (users.id)</label>
<div class="col-md-4">
<input type="number" name="consultant_user_id" class="form-control" min="1"
value="{{ old('consultant_user_id', '454') }}" required>
</div>
<div class="col-md-5">
<small class="form-text text-muted">
Bei <code>is_for = me</code> wird <code>auth_user_id</code> gesetzt; bei <code>ot</code> bleibt
<code>auth_user_id</code> leer und <code>member_id</code> wird auf diese Berater-ID gesetzt.
</small>
</div>
</div>
<div class="form-group row">
<div class="col-md-6 offset-md-3">
<div class="form-check">
<input type="checkbox" name="is_abo" id="is_abo" class="form-check-input" value="1"
{{ old('is_abo') ? 'checked' : '' }}>
<label class="form-check-label" for="is_abo">Als Abo-Bestellung (<code>is_abo</code>)</label>
</div>
<div class="form-check mt-2">
<input type="checkbox" name="is_for_ot" id="is_for_ot" class="form-check-input" value="1"
{{ old('is_for_ot', true) ? 'checked' : '' }}>
<label class="form-check-label" for="is_for_ot">Kundenkontext <code>is_for = ot</code> (für
Incentive <code>trackAboActivated</code>)</label>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-md-6 offset-md-3">
<button type="submit" class="btn btn-primary">Bestellung + Zahlung anlegen</button>
</div>
</div>
</form>
</div>
</div>
@if (!empty($fixture))
<div class="card mb-4 border-success">
<div class="card-header font-weight-bold text-success">Angelegte Testdaten</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">shopping_order_id</dt>
<dd class="col-sm-9"><code>{{ $fixture['shopping_order_id'] }}</code></dd>
<dt class="col-sm-3">shopping_payment_id</dt>
<dd class="col-sm-9"><code>{{ $fixture['shopping_payment_id'] }}</code></dd>
<dt class="col-sm-3">reference (16)</dt>
<dd class="col-sm-9"><code>{{ $fixture['reference'] }}</code></dd>
<dt class="col-sm-3">amount (Cent)</dt>
<dd class="col-sm-9"><code>{{ $fixture['amount_cents'] }}</code> (=
{{ number_format($fixture['amount_eur'], 2, ',', '.') }} )</dd>
<dt class="col-sm-3">Berater-ID</dt>
<dd class="col-sm-9"><code>{{ $fixture['consultant_user_id'] ?? '' }}</code></dd>
<dt class="col-sm-3">Zuordnung</dt>
<dd class="col-sm-9"><small>{{ $fixture['assignment_note'] ?? '' }}</small></dd>
<dt class="col-sm-3">API-URL</dt>
<dd class="col-sm-9"><small class="text-break">{{ $fixture['api_url'] }}</small></dd>
</dl>
<hr>
<p class="mb-2 font-weight-bold">2. Checkout-Erfolg (nur Abo)</p>
<p class="small text-muted mb-2">
Wie <code>handleSuccessfulTransaction</code> / <code>transactionApproved</code>:
<code>AboHelper::createNewAbo($ShoppingPayment)</code> legt UserAbo + UserAboOrder an.
Ohne vorherige <code>payment_transactions</code> wird eine Minimal-Transaktion angelegt.
</p>
@if (!empty($fixture['is_abo']))
<form method="post" action="{{ route('sysadmin_tool_store', ['payone_callback_testbench']) }}"
class="d-inline">
@csrf
<input type="hidden" name="action" value="simulate_checkout_success">
<input type="hidden" name="shopping_order_id" value="{{ $fixture['shopping_order_id'] }}">
<button type="submit" class="btn btn-success">Abo anlegen (<code>createNewAbo</code>)</button>
</form>
@else
<p class="small text-muted mb-0">Nicht-Abo Schritt entfällt.</p>
@endif
<hr>
<p class="mb-2 font-weight-bold">3. Payone-API: Zahlung bestätigt (<code>paid</code>)</p>
<p class="small text-muted mb-2">
<code>Api\PayoneController::paymentStatus</code> <code>Payment::paymentStatusPaidAction</code>
(Abo freischalten, Incentive, ). Bei Abo-Erstbestellung nur nach Schritt 2 (sonst Fehlermeldung).
</p>
<form method="post" action="{{ route('sysadmin_tool_store', ['payone_callback_testbench']) }}"
class="d-inline mr-2">
@csrf
<input type="hidden" name="action" value="simulate_paid">
<input type="hidden" name="shopping_order_id" value="{{ $fixture['shopping_order_id'] }}">
<button type="submit" class="btn btn-warning">Simulate <code>txaction=paid</code> (Erstbestellung)</button>
</form>
<form method="post" action="{{ route('sysadmin_tool_store', ['payone_callback_testbench']) }}"
class="d-inline">
@csrf
<input type="hidden" name="action" value="clear_fixture">
<button type="submit" class="btn btn-outline-secondary btn-sm">Session-Daten löschen</button>
</form>
@php
$benchUserAboId = $userAboId ?? ($checkoutSuccess['user_abo_id'] ?? null);
@endphp
@if (!empty($fixture['is_abo']) && $benchUserAboId)
<hr>
<p class="mb-2 font-weight-bold">4. Abo-Verlängerung (wie <code>user:make_abo_order</code> / Cron)</p>
<p class="small text-muted mb-2">
Setzt <code>next_date</code> auf heute, entfernt ggf. heutige <code>user_abo_orders</code> (Test-Wiederholung),
führt dann <code>UserMakeAboOrder::makeOrder</code> aus (neue Bestellung + Payone). Anschließend Incentive-Zähler
(z.&nbsp;B. qualifizierte Abos) und Umsatzpunkte prüfen für Rechnung/SV wie in Produktion Schritt 5.
</p>
<form method="post" action="{{ route('sysadmin_tool_store', ['payone_callback_testbench']) }}"
class="mb-3">
@csrf
<input type="hidden" name="action" value="simulate_cron_renewal">
<div class="form-group row align-items-center">
<label class="col-md-3 col-form-label"><code>user_abos.id</code></label>
<div class="col-md-4">
<input type="number" name="user_abo_id" class="form-control" min="1"
value="{{ old('user_abo_id', $benchUserAboId) }}">
</div>
<div class="col-md-5">
<small class="form-text text-muted mb-0">Standard aus Session nach Schritt 2/3; anpassbar.</small>
</div>
</div>
<button type="submit" class="btn btn-outline-primary">Cron-Verlängerung ausführen</button>
</form>
@endif
@if (!empty($cronRenewalOrderId))
<hr>
<p class="mb-2 font-weight-bold">5. Payone-API: Verlängerung bestätigt (<code>paid</code>)</p>
<p class="small text-muted mb-2">
Wie Server-zu-Server-Callback nach Cron-Zahlung: <code>Payment::paymentStatusPaidAction</code> (Rechnung,
<code>createAndSalesVolume</code> <code>IncentiveTracker::trackSalesVolume</code>, ).
</p>
<form method="post" action="{{ route('sysadmin_tool_store', ['payone_callback_testbench']) }}"
class="d-inline">
@csrf
<input type="hidden" name="action" value="simulate_paid">
<input type="hidden" name="shopping_order_id" value="{{ $cronRenewalOrderId }}">
<button type="submit" class="btn btn-warning">Simulate <code>txaction=paid</code> (Verlängerung)</button>
</form>
@endif
<hr>
<p class="mb-2 font-weight-bold">Manuell (z. B. Postman / curl)</p>
<pre class="bg-light p-3 small text-break mb-0" style="white-space: pre-wrap;">{{ $fixture['curl'] }}</pre>
</div>
</div>
@endif
@if (!empty($simulateResult))
<div class="card mb-4 border-info">
<div class="card-header font-weight-bold">Ergebnis Schritt 3 (Payone-API)</div>
<div class="card-body">
@if (!empty($simulateResult['hint']))
<p class="small text-muted mb-3">{{ $simulateResult['hint'] }}</p>
@endif
<dl class="row">
<dt class="col-sm-3">HTTP (Kernel)</dt>
<dd class="col-sm-9"><code>{{ $simulateResult['http_status'] }}</code></dd>
<dt class="col-sm-3">Body</dt>
<dd class="col-sm-9"><code>{{ $simulateResult['body'] }}</code> <span class="text-muted">(Payone
erwartet TSOK)</span></dd>
<dt class="col-sm-3">Order paid</dt>
<dd class="col-sm-9">{{ $simulateResult['order_paid'] ? 'true' : 'false' }}</dd>
<dt class="col-sm-3">Order txaction</dt>
<dd class="col-sm-9"><code>{{ $simulateResult['order_txaction'] ?? 'null' }}</code></dd>
</dl>
<p class="mb-1 font-weight-bold">Gesendeter Payload</p>
<pre class="bg-light p-3 small mb-0">{{ json_encode($simulateResult['payload'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}</pre>
</div>
</div>
@endif
@if (!empty($checkoutSuccess))
<div class="card mb-4 border-success">
<div class="card-header font-weight-bold text-success">Ergebnis Schritt 2 (Checkout / createNewAbo)</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">user_abo_id</dt>
<dd class="col-sm-9"><code>{{ $checkoutSuccess['user_abo_id'] ?? '' }}</code></dd>
<dt class="col-sm-3">user_abo_orders.id</dt>
<dd class="col-sm-9"><code>{{ $checkoutSuccess['user_abo_order_id'] ?? '' }}</code></dd>
<dt class="col-sm-3">shopping_order_id</dt>
<dd class="col-sm-9"><code>{{ $checkoutSuccess['shopping_order_id'] ?? '' }}</code></dd>
<dt class="col-sm-3">Order paid (nachher)</dt>
<dd class="col-sm-9">{{ !empty($checkoutSuccess['order_paid_after']) ? 'true' : 'false' }}</dd>
</dl>
@if (!empty($checkoutSuccess['hint']))
<p class="small text-muted mb-0 mt-2">{{ $checkoutSuccess['hint'] }}</p>
@endif
</div>
</div>
@endif
@if (!empty($cronRenewal))
<div class="card mb-4 border-primary">
<div class="card-header font-weight-bold text-primary">Ergebnis Schritt 4 (Cron-Verlängerung)</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">Erfolg</dt>
<dd class="col-sm-9">{{ !empty($cronRenewal['success']) ? 'true' : 'false' }}</dd>
@if (!empty($cronRenewal['shopping_order_id']))
<dt class="col-sm-3">Neue shopping_order_id</dt>
<dd class="col-sm-9"><code>{{ $cronRenewal['shopping_order_id'] }}</code></dd>
@endif
@if (!empty($cronRenewal['user_abo_id']))
<dt class="col-sm-3">user_abo_id</dt>
<dd class="col-sm-9"><code>{{ $cronRenewal['user_abo_id'] }}</code></dd>
@endif
@if (!empty($cronRenewal['message']))
<dt class="col-sm-3">Hinweis</dt>
<dd class="col-sm-9">{{ $cronRenewal['message'] }}</dd>
@endif
@if (!empty($cronRenewal['diagnosis']))
<dt class="col-sm-3">Diagnose</dt>
<dd class="col-sm-9">
<pre class="bg-light p-2 small mb-0 text-break"
style="white-space: pre-wrap;">{{ json_encode($cronRenewal['diagnosis'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}</pre>
</dd>
@endif
</dl>
@if (!empty($cronRenewal['hint']))
<p class="small text-muted mb-0 mt-2">{{ $cronRenewal['hint'] }}</p>
@endif
</div>
</div>
@endif
@endsection

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