10.April 2026

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

View file

@ -0,0 +1,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