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

@ -16,6 +16,7 @@ return [
'abo_order_info_check_2' => 'Die erste Lieferung und Abrechnung erfolgt am Tag der Abo-Einrichtung. Danach erfolgt der Versand automatisch am gewählten Liefertag des Folgemonats.',
'abo_order_info_check_3' => 'Als Zahlungsmethoden stehen PayPal und Kreditkarte zur Verfügung. <strong>Das Abo hat eine Mindestlaufzeit von :abo-min-duration Monaten.</strong> Danach kann es jederzeit pausiert, geändert oder gekündigt werden.',
'abo_order_info_checkbox' => 'Ja, ich habe die Abo-Bedingungen verstanden!',
'abo_order_info_checkbox_required' => 'Bitte bestätige die Abo-Bedingungen, um fortzufahren.',
'abo_infos' => 'Abo Infos',
'abo_delivery_infos' => 'Abo Lieferinfos',
'abo_start_date' => 'Beginn des Abos',
@ -33,6 +34,14 @@ return [
'abo_copy_abo_interval' => 'Die Anpassung des Abonnement-Liefertags wirkt sich auf den kommenden Ausführungstermin aus, wenn das Abonnement aktiv ist.',
'error_abo_interval' => 'Das Abo Interval nicht korrekt',
'error_abo_interval_in_the_past' => 'Das Abo wurde diesen Monat noch nicht ausgeführt. Eine Änderung auf einen vergangenen Tag würde den aktuellen Monat überspringen.',
'warning_next_date_soon' => 'Hinweis: Die nächste Abo-Ausführung ist bereits in :days Tagen (:date).',
'warning_next_date_soon_select' => 'Hinweis: Die nächste Abo-Ausführung ist bereits in :placeholder_days Tagen (:placeholder_date).',
'warning_next_date_info' => 'Die nächste Abo-Ausführung ist in :days Tagen am :date.',
'info_next_execution_select' => 'Nächste Ausführung: in :placeholder_days Tagen am :placeholder_date.',
'error_change_locked' => 'Änderungen sind nicht mehr möglich. Die nächste Ausführung ist in :days Tagen. Änderungen müssen mindestens 10 Tage vorher erfolgen.',
'error_abo_interval_too_soon' => 'Der gewählte Liefertag liegt nur :days Tage entfernt. Bitte wähle einen Liefertag, der mindestens 10 Tage in der Zukunft liegt.',
'error_cancel_locked' => 'Eine Kündigung ist nicht mehr möglich. Die nächste Ausführung ist in :days Tagen. Kündigungen müssen mindestens 3 Tage vorher erfolgen.',
'error_pause_locked' => 'Das Abo kann nicht mehr pausiert werden. Die nächste Ausführung ist in :days Tagen. Pausieren muss mindestens 3 Tage vorher erfolgen.',
'error_next_date' => 'Das Datum für die nächste Ausführung nicht korrekt',
'checkout_mail_abo_hl' => 'Dein Abo / regelmäßige Lieferung.',
'checkout_mail_abo_start' => 'Dein Abo wurde erfolgreich mit folgenden Einstellungen angelegt:',
@ -102,6 +111,12 @@ return [
'cancel_abo' => 'Abo kündigen',
'confirm_cancel' => 'Möchten Sie das Abo wirklich kündigen?',
'team_subscriptions' => 'Team Abos',
'team_customer_abos' => 'Team Kunden-Abos',
'chart_monthly_abos' => 'Abos pro Monat',
'chart_active_abos' => 'Aktive Abos',
'chart_abos_label' => 'Abos',
'abo_count' => 'Anzahl Abos',
'customer_privacy_info' => 'Aus Datenschutzgründen werden keine persönlichen Kundendaten angezeigt.',
'every_month_on' => 'monatlich am :day.',
'back' => 'zurück',
];

View file

@ -1,61 +1,61 @@
<?php
return array(
'' => '',
'MIVITA_Consultancy_agreement' => 'MIVITA_Beratervertrag',
'active_role' => 'Aktive Rolle',
'activities' => 'Aktivitäten',
'adjust_data' => 'Daten anpassen',
'adviser_membership_active' => 'Berater-Mitgliedschaft aktiv',
'adviser_onlineshop_active' => 'Berater-Online-Shop aktiv',
'adviser_onlineshop_inactive' => 'Berater-Shop inaktiv',
'advisor_account_inactive' => 'Berater-Account inaktiv',
'at' => 'am',
'change_your_email_address' => 'Ändere Deine E-Mail Adresse.',
'change_your_personal_data' => 'Ändere Deine persönlichen Daten.',
'change_your_personal_password' => 'Ändere Dein persönliches Passwort.',
'create_your_personal_password' =>
array(
'' => 'Erstelle Dein persönlichen Passwort.',
),
'current_points_for' => 'Aktuelle Punkte für',
'data' => 'Daten',
'data_complete_unlocked' => 'Daten vollständig, freigeschaltet',
'declaration_of_consent' => 'Einverständniserklärung',
'email_verified' => 'E-Mail verifizier',
'expired_on' => 'abgelaufen am',
'log_out_and_see_you_soon' => 'Abmelden und bis bald.',
'login' => 'Anmeldung',
'manage_membership' => 'Mitgliedschaft verwalten',
'manage_membership_now_here' => 'Mitgliedschaft jetzt hier verwalten',
'membership' => 'Mitgliedschaft',
'news_updates' => 'Neuigkeiten & Updates',
'news_archive' => 'News-Archiv',
'news_archive_title' => 'Alle Neuigkeiten & Updates',
'news_archive_current' => 'Aktuelle News',
'news_archive_older' => 'Ältere Meldungen',
'news_archive_empty' => 'Keine älteren Meldungen vorhanden.',
'news_archive_link' => 'Alle News ansehen',
'news_back_to_dashboard' => 'Zurück zum Dashboard',
'open_since' => 'Eröffnet seit',
'open_your_shop' => 'Eröffne Deinen eigenen mivita-Shop',
'read_less' => 'Weniger anzeigen',
'read_more' => 'Mehr lesen',
'privacy_policy_approved' => 'Datenschutzerklärung zugestimmt',
'security' => 'Sicherheit',
'settings_your_shop' => 'Deine Shop-Einstellungen',
'shop_not_booked' => 'Shop nicht gebucht',
'today_is' => 'Heute ist',
'until' => 'bis zum',
'welcome_back' => 'Willkommen zurück',
'your_shop' => 'Dein Shop',
'monthly_statistics' => 'Monatsstatistik',
'customer_turnover_points' => 'Kunden-Umsatz Punkte',
'team_turnover_points' => 'Team-Umsatz Punkte',
'direct_new_partners' => 'Direkte Neupartner',
'team_new_partners' => 'Neupartner im Team',
'customer_subscriptions' => 'Kundenabos',
'team_subscriptions' => 'Teamabos',
'own' => 'Eigene',
'live_calculation_hint' => 'Live-Berechnung (noch nicht abgeschlossen)',
);
return [
'' => '',
'MIVITA_Consultancy_agreement' => 'MIVITA_Beratervertrag',
'active_role' => 'Aktive Rolle',
'activities' => 'Aktivitäten',
'adjust_data' => 'Daten anpassen',
'adviser_membership_active' => 'Berater-Mitgliedschaft aktiv',
'adviser_onlineshop_active' => 'Berater-Online-Shop aktiv',
'adviser_onlineshop_inactive' => 'Berater-Shop inaktiv',
'advisor_account_inactive' => 'Berater-Account inaktiv',
'at' => 'am',
'change_your_email_address' => 'Ändere Deine E-Mail Adresse.',
'change_your_personal_data' => 'Ändere Deine persönlichen Daten.',
'change_your_personal_password' => 'Ändere Dein persönliches Passwort.',
'create_your_personal_password' => [
'' => 'Erstelle Dein persönlichen Passwort.',
],
'current_points_for' => 'Aktuelle Punkte für',
'data' => 'Daten',
'data_complete_unlocked' => 'Daten vollständig, freigeschaltet',
'declaration_of_consent' => 'Einverständniserklärung',
'email_verified' => 'E-Mail verifizier',
'expired_on' => 'abgelaufen am',
'log_out_and_see_you_soon' => 'Abmelden und bis bald.',
'login' => 'Anmeldung',
'manage_membership' => 'Mitgliedschaft verwalten',
'manage_membership_now_here' => 'Mitgliedschaft jetzt hier verwalten',
'membership' => 'Mitgliedschaft',
'news_updates' => 'Neuigkeiten & Updates',
'news_archive' => 'News-Archiv',
'news_archive_title' => 'Alle Neuigkeiten & Updates',
'news_archive_current' => 'Aktuelle News',
'news_archive_older' => 'Ältere Meldungen',
'news_archive_empty' => 'Keine älteren Meldungen vorhanden.',
'news_archive_link' => 'Alle News ansehen',
'news_back_to_dashboard' => 'Zurück zum Dashboard',
'open_since' => 'Eröffnet seit',
'open_your_shop' => 'Eröffne Deinen eigenen mivita-Shop',
'read_less' => 'Weniger anzeigen',
'read_more' => 'Mehr lesen',
'privacy_policy_approved' => 'Datenschutzerklärung zugestimmt',
'security' => 'Sicherheit',
'settings_your_shop' => 'Deine Shop-Einstellungen',
'shop_not_booked' => 'Shop nicht gebucht',
'today_is' => 'Heute ist',
'until' => 'bis zum',
'welcome_back' => 'Willkommen zurück',
'your_shop' => 'Dein Shop',
'monthly_statistics' => 'Monatsstatistik',
'customer_turnover_points' => 'Kunden-Umsatz Punkte',
'team_turnover_points' => 'Team-Umsatz Punkte',
'direct_new_partners' => 'Direkte Neupartner',
'team_new_partners' => 'Neupartner im Team',
'customer_subscriptions' => 'Kundenabos',
'team_subscriptions' => 'Teamabos',
'own' => 'Eigene',
'live_calculation_hint' => 'Live-Berechnung (noch nicht abgeschlossen)',
'live_calculation_hint_text' => 'Wird erst zum Monatsende berechnet.',
];

View file

@ -0,0 +1,172 @@
<?php
return [
// Allgemein
'incentives' => 'Incentives',
'incentive' => 'Incentive',
'name' => 'Name',
'status' => 'Status',
'period' => 'Zeitraum',
'actions' => 'Aktionen',
'participants' => 'Teilnehmer',
'save' => 'Speichern',
'cancel' => 'Abbrechen',
'yes' => 'Ja',
'no' => 'Nein',
'you' => 'Du',
// Status
'status_draft' => 'Entwurf',
'status_active' => 'Aktiv',
'status_closed' => 'Beendet',
// CRUD
'create' => 'Neues Incentive anlegen',
'edit' => 'Bearbeiten',
'created' => 'Incentive wurde erfolgreich angelegt.',
'updated' => 'Incentive wurde erfolgreich aktualisiert.',
// Konfiguration
'configuration' => 'Konfiguration',
'qualification_start' => 'Qualifikationsbeginn',
'qualification_end' => 'Qualifikationsende',
'calculation_end' => 'Berechnungsende',
'points_partner_onetime' => 'Einmalpunkte pro Partner',
'points_abo_onetime' => 'Einmalpunkte pro Abo',
'min_direct_partners' => 'Mind. direkte Partner',
'min_customer_abos' => 'Mind. Kundenabos',
'max_winners' => 'Max. Gewinner',
'image' => 'Bild',
'image_help' => 'Dateiname des Bildes im Ordner public/img/incentive/ (z.B. montenegro-2026.jpg)',
'description' => 'Beschreibung / Werbetext',
'description_help' => 'Motivierender Einleitungstext der auf der Teaser-Seite angezeigt wird.',
'terms' => 'Teilnahmebedingungen',
'terms_help' => 'Vollständiger Text der Teilnahmebedingungen. Wird als aufklappbarer Bereich auf der Seite angezeigt.',
'name_help' => 'Interner Name des Incentives (wird auch als Seitenüberschrift angezeigt).',
'subtitle' => 'Untertitel',
'subtitle_placeholder' => 'z.B. Deine exklusive Auszeit an der Adria!',
'subtitle_help' => 'Kurzer Werbespruch, der im Hero-Bereich unter dem Titel angezeigt wird.',
'content_lang_de' => 'Deutsch',
'default_language' => 'Standard',
'lang_fallback_hint' => 'Leer lassen = Deutsch wird als Fallback verwendet.',
// Ranking
'ranking' => 'Rangliste',
'rank' => 'Rang',
'consultant' => 'Berater',
'total_points' => 'Gesamtpunkte',
'partners' => 'Partner',
'abos' => 'Abos',
'qualified' => 'Qualifiziert',
'open' => 'Offen',
'winner' => 'Gewinner',
'no_participants' => 'Noch keine Teilnehmer.',
'no_participants_with_points' => 'Noch keine Teilnehmer mit Punkten.',
'anonymous_consultant' => 'Anonymer Berater',
'ranking_anonymous_hint' => 'Namen erscheinen erst, wenn die Teilnahme am Incentive bestätigt wurde.',
'ranking_extended_hint' => 'Die Liste zeigt die Plätze 130. Die besten :n qualifizierten Berater (hervorgehoben) gewinnen; die Plätze danach zeigen, wer noch nachlegen kann.',
'calculation_details' => 'Berechnungsdetails',
'close' => 'Schliessen',
// Neuberechnung
'recalculate' => 'Neuberechnung',
'recalculate_confirm' => 'Soll die Neuberechnung gestartet werden?',
'force_recalculate' => 'Komplett neu berechnen',
'force_recalculate_confirm' => 'ACHTUNG: Alle bestehenden Logs werden geloescht und komplett neu berechnet. Fortfahren?',
'recalculated' => 'Neuberechnung abgeschlossen. :participants Teilnehmer verarbeitet, :errors Fehler.',
// Admin-Rangliste
'admin_terms_accepted' => 'Teilnahme (Bedingungen)',
'admin_terms_pending' => 'Ausstehend',
'admin_terms_accepted_at_tooltip' => 'Zeitpunkt der Bestätigung',
// Teilnahme (User)
'participate_title' => 'Jetzt teilnehmen!',
'accept_terms' => 'Ich akzeptiere die Teilnahmebedingungen',
'show_terms' => 'Bedingungen anzeigen',
'participate_now' => 'Jetzt teilnehmen',
'not_active' => 'Dieses Incentive ist derzeit nicht aktiv.',
'terms_required' => 'Bitte akzeptiere die Teilnahmebedingungen.',
'already_participating' => 'Du nimmst bereits teil.',
'participation_confirmed' => 'Deine Teilnahme wurde bestaetigt!',
// Teaser-Seite
'teaser_hero_subtitle' => 'Deine exklusive Auszeit an der Adria wartet auf dich!',
'teaser_intro_bold' => 'Pack deine Koffer, denn mivita belohnt deine Bestleistungen!',
'teaser_intro_text' => 'Erlebe unvergessliche Tage an der malerischen Küste, tausche dich mit den Top-Leadern aus und feiere deinen Erfolg mit uns!',
'teaser_intro_cta' => 'Gehörst du zu den besten :n Partnern? Dann bist du dabei!',
'teaser_until' => 'bis',
'teaser_partner_onetime_text' => 'einmalig pro direkt gesponsertem Neupartner im Qualifikationszeitraum.',
'teaser_abo_onetime_text' => 'einmalig pro neu abgeschlossenem Kundenabo im Qualifikationszeitraum.',
'teaser_cta_ready' => 'Bist du bereit für die Challenge?',
'teaser_cta_text' => 'Melde dich jetzt an, um im offiziellen Ranking gelistet zu werden. Nur die besten :n qualifizierten Berater gewinnen!',
'teaser_cta_button' => 'Jetzt zum Ranking & Teilnehmen',
'teaser_cta_to_ranking' => 'Zur Live-Rangliste',
'teaser_cta_already_in' => 'Du bist bereits angemeldet. Verfolge deinen aktuellen Rang in der Live-Rangliste.',
'teaser_pending_title' => 'Deine Punkte laufen bereits',
'teaser_pending_text' => 'Bestätige die Teilnahme, damit dein Name in der Rangliste erscheint und du die Detailansicht nutzen kannst.',
'teaser_cta_confirm' => 'Teilnahme bestätigen',
'teaser_cta_coming_soon' => 'Bald geht es los!',
// Show-Seite Sektionen
'section_period' => 'Der Qualifikationszeitraum',
'qualification_period' => 'Qualifikationszeitraum',
'calculation_period' => 'Endspurt (Berechnungsende)',
'calculation_period_hint' => 'Akkumulierte Punkte werden bis einschließlich :date gerechnet.',
'section_min_qual' => 'Dein Ticket: Die Mindestqualifikation',
'min_qual_intro' => 'Um im offiziellen Ranking gelistet zu werden und für den Gewinn in Frage zu kommen, müssen im Qualifikationszeitraum folgende Basis-Ziele erreicht werden:',
'min_partners_label' => 'direkte neue Teampartner (jeweils nur mit einem Starterpaket)',
'min_abos_label' => 'neu abgeschlossene Kundenabos',
'min_qual_ranking_hint' => 'Im Live-Ranking wird dein Name erst dann fettgedruckt hervorgehoben, wenn du diese Mindestqualifikation erfolgreich erreicht hast.',
'section_points' => 'So sammelst du deine Incentive-Punkte',
'points_partners_title' => 'Punkte für neue Teampartner',
'points_abos_title' => 'Punkte für Kundenabos',
'points_short' => 'Pkt.',
'points_onetime_label' => 'einmalig pro Neupartner/Abo',
'points_starter_package_label' => 'jeweils mit einem dirket bestellten Starterpaket, neue Partner mit nur einer Mitgliedschaft zählen leider nicht mit.',
'points_partner_boost' => 'Zusatz-Boost: Du erhältst alle Kunden- und Eigenumsatzpunkte deines Neupartners ab seinem Starttag, innerhalb des Qualifikationszeitraums.',
'points_abo_direct' => 'Dein eigenes Abo zählt auch mit - auch bestehende Abos.',
'points_abo_boost' => 'Zusatz-Boost: Du erhältst die monatlichen Abopunkte ab dem Abschlussmonat, innerhalb des Qualifikationszeitraums.',
'section_ranking' => 'Das Live-Ranking',
'ranking_winners_hint' => 'Nur die besten :n qualifizierten Berater gewinnen.',
'dashboard_btn_teaser' => 'Zum Incentive',
'dashboard_btn_ranking' => 'Zur Live-Rangliste',
'read_more' => 'Mehr lesen',
'read_less' => 'Weniger lesen',
'you_participate' => 'Du nimmst teil!',
'your_rank' => 'Dein aktueller Rang',
'participate_intro' => 'Bist du bereit für die Challenge? Melde dich einmalig an, um im offiziellen Ranking gelistet zu werden.',
'pending_confirmation_banner' => 'Deine Punkte werden bereits im Qualifikationszeitraum mitgerechnet. Bitte bestätige die Teilnahme, damit dein Name in der Rangliste sichtbar wird und du alle Funktionen nutzen kannst.',
'details_requires_confirmation' => 'Die Detailansicht ist erst nach Bestätigung der Teilnahme verfügbar.',
'participate_abo_hint' => 'Es liegt mindestens ein für die Wertung relevantes Abo vor (aktives Berater-Abo oder Kundenabo im Qualifikationszeitraum). Mit dem Teilnehmen werden die Punkte dafür direkt nach den aktuellen Regeln übernommen.',
// Berechnungsdetails (User)
'my_details' => 'Meine Berechnung',
'my_calculation' => 'Meine Berechnungsuebersicht',
'back_to_ranking' => 'Zurueck zur Rangliste',
'section_partners' => 'A. Neupartner-Punkte',
'section_abos' => 'B. Kundenabo-Punkte',
'new_partner' => 'Neupartner',
'entry_date' => 'Einstieg',
'customer_abo' => 'Kundenabo',
'abo_date' => 'Abschluss',
'onetime' => 'Einmalig',
'sum' => 'Gesamt',
'subtotal' => 'Zwischensumme',
'no_partners_yet' => 'Noch keine Neupartner erfasst.',
'no_abos_yet' => 'Noch keine Kundenabos erfasst.',
'not_yet_qualified' => 'Noch nicht qualifiziert',
// Transaktionsdetails
'transaction_date' => 'Datum',
'transaction_description' => 'Beschreibung',
'transaction_period' => 'Zeitraum',
'transaction_type' => 'Art',
'transaction_points' => 'Punkte',
'onetime_registration' => 'Einmalig: Registrierung',
'onetime_abo_activation' => 'Einmalig: Abo-Aktivierung',
'accumulated' => 'Umsatz',
// Galerie
'gallery_title' => 'Impressionen',
];

View file

@ -36,10 +36,10 @@ return [
'shopping_instance_not_found' => 'Fehler: Es wurde keine ShoppingInstance gefunden',
'shopping_user_not_found' => 'Fehler: Es wurde kein ShoppingUser gefunden',
'account_released' => 'Account freigeschaltet',
'cart_product_not_allowed_for_order_type' => 'Der Warenkorb enthält Artikel, die für diese Bestellart nicht vorgesehen sind. Bitte leere den Warenkorb und wähle nur Produkte, die zu dieser Bestellung passen.',
];
/*
{{ __('msg.') }}
{{ __('msg.') }}
__('msg.')
msg.name
*/

View file

@ -75,8 +75,15 @@ return [
'level_reports' => 'Level Reports',
'dashboard_news' => 'Dashboard News',
'teamabos' => 'Team Abos',
'team_customer_abos' => 'Team Kunden-Abos',
'customer_orders' => 'Kundenbestellungen',
'external_orders' => 'Externe Bestellungen',
'tools' => 'Tools',
'news_archive' => 'News Archiv',
'incentive' => 'Incentive',
'incentives' => 'Incentives',
'create' => 'Neu anlegen',
'my_abo' => 'Mein Abo',
'my_subscriptions' => 'Meine Abos',
'team_customers' => 'Team Kunden',
];

View file

@ -118,6 +118,7 @@ return [
'reorder' => 'Nachbestellen',
'reorder_info' => 'Möchtest Du diesen Artikel noch einmal bestellen?<br>Mit einem Klick auf den Button werden die Artikel erneut in den Warenkorb gelegt und du wirst auf die Warenkorb-Seite weitergeleitet.',
'reorder_info_2' => 'Dein Lieferland ist: :country<br>Möchtest du deine Bestellung in ein anders Land liefern lassen, ändere bitte Deine Rechnungs- oder Lieferadresse unter <a class="text-primary" href=":link">Meine Daten</a>',
'reorder_abo_not_allowed' => 'Abo-Bestellungen können nicht über „Nachbestellen“ wiederholt werden. Bitte nutze deine Abo-Verwaltung oder den Shop für Einzelbestellungen.',
'free_shipping' => 'Versandkostenfrei',
'free_shipping_reached' => 'Ab :amount € versandkostenfrei',
'free_shipping_info' => 'Noch :missing € bis zum versandkostenfreien Versand (ab :amount €)',

View file

@ -16,6 +16,7 @@ return [
'abo_order_info_check_2' => 'The first delivery and billing takes place on the day the subscription is set up. After that, shipping is automatically carried out on the selected delivery day of the following month.',
'abo_order_info_check_3' => 'PayPal and credit card are available as payment methods. <strong>The subscription has a minimum duration of :abo-min-duration months.</strong> After that, it can be paused, changed or canceled at any time.',
'abo_order_info_checkbox' => 'Yes, I have understood the subscription terms!',
'abo_order_info_checkbox_required' => 'Please confirm the subscription terms to continue.',
'abo_infos' => 'Subscription info',
'abo_delivery_infos' => 'Subscription delivery information',
'abo_start_date' => 'Start date of the subscription',
@ -99,9 +100,23 @@ return [
'change_my_data_empty' => 'You have not yet stored a billing and delivery address, without this you cannot create a subscription, please create it.',
'abo_error_basis_product' => 'Error: Please select at least one base product.',
'error_abo_interval_in_the_past' => 'The subscription has not been executed this month yet. Changing to a past day would skip the current month.',
'warning_next_date_soon' => 'Note: The next subscription execution is in :days days (:date).',
'warning_next_date_soon_select' => 'Note: The next subscription execution is in :placeholder_days days (:placeholder_date).',
'warning_next_date_info' => 'The next subscription execution is in :days days on :date.',
'info_next_execution_select' => 'Next execution: in :placeholder_days days on :placeholder_date.',
'error_change_locked' => 'Changes are no longer possible. The next execution is in :days days. Changes must be made at least 10 days in advance.',
'error_abo_interval_too_soon' => 'The selected delivery day is only :days days away. Please choose a delivery day at least 10 days in the future.',
'error_cancel_locked' => 'Cancellation is no longer possible. The next execution is in :days days. Cancellations must be made at least 3 days in advance.',
'error_pause_locked' => 'The subscription can no longer be paused. The next execution is in :days days. Pausing must be done at least 3 days in advance.',
'cancel_abo' => 'Cancel subscription',
'confirm_cancel' => 'Do you really want to cancel the subscription?',
'back' => 'back',
'team_subscriptions' => 'Team subscriptions',
'team_customer_abos' => 'Team Customer Subscriptions',
'chart_monthly_abos' => 'Subscriptions per month',
'chart_active_abos' => 'Active subscriptions',
'chart_abos_label' => 'subscriptions',
'abo_count' => 'Number of subscriptions',
'customer_privacy_info' => 'For privacy reasons, no personal customer data is displayed.',
'every_month_on' => 'monthly on :day.',
];

View file

@ -1,60 +1,60 @@
<?php
return array(
'MIVITA_Consultancy_agreement' => 'MIVITA_Consultant contract',
'active_role' => 'active role',
'activities' => 'activities',
'adjust_data' => 'adjust data',
'adviser_membership_active' => 'consultant membership active',
'adviser_onlineshop_active' => 'consultant online shop active',
'adviser_onlineshop_inactive' => 'consultant shop inactive',
'advisor_account_inactive' => 'consultant account inactive',
'at' => 'at the',
'change_your_email_address' => 'Change your email address.',
'change_your_personal_data' => 'Change your personal information.',
'change_your_personal_password' => 'Change your personal password.',
'create_your_personal_password' =>
array(
'' => 'Create your personal password.',
),
'current_points_for' => 'current points for',
'data' => 'data',
'data_complete_unlocked' => 'data complete, unlocked',
'declaration_of_consent' => 'consent form',
'email_verified' => 'verify email',
'expired_on' => 'expired on',
'log_out_and_see_you_soon' => 'Sign out and see you soon.',
'login' => 'registration',
'manage_membership' => 'manage membership',
'manage_membership_now_here' => 'manage your membership here now',
'membership' => 'membership',
'news_updates' => 'News & Updates',
'news_archive' => 'News Archive',
'news_archive_title' => 'All News & Updates',
'news_archive_current' => 'Current News',
'news_archive_older' => 'Older Posts',
'news_archive_empty' => 'No older posts available.',
'news_archive_link' => 'View all news',
'news_back_to_dashboard' => 'Back to Dashboard',
'open_since' => 'opened since',
'open_your_shop' => 'open your own mivita shop',
'read_less' => 'Show less',
'read_more' => 'Read more',
'privacy_policy_approved' => 'privacy policy agreed',
'security' => 'security',
'settings_your_shop' => 'your shop settings',
'shop_not_booked' => 'shop not booked',
'today_is' => 'today is',
'until' => 'until',
'welcome_back' => 'welcome back',
'your_shop' => 'your shop',
'monthly_statistics' => 'Monthly Statistics',
'customer_turnover_points' => 'Customer Turnover Points',
'team_turnover_points' => 'Team Turnover Points',
'direct_new_partners' => 'Direct New Partners',
'team_new_partners' => 'New Partners in Team',
'customer_subscriptions' => 'Customer Subscriptions',
'team_subscriptions' => 'Team Subscriptions',
'own' => 'Own',
'live_calculation_hint' => 'Live calculation (not yet finalized)',
);
return [
'MIVITA_Consultancy_agreement' => 'MIVITA_Consultant contract',
'active_role' => 'active role',
'activities' => 'activities',
'adjust_data' => 'adjust data',
'adviser_membership_active' => 'consultant membership active',
'adviser_onlineshop_active' => 'consultant online shop active',
'adviser_onlineshop_inactive' => 'consultant shop inactive',
'advisor_account_inactive' => 'consultant account inactive',
'at' => 'at the',
'change_your_email_address' => 'Change your email address.',
'change_your_personal_data' => 'Change your personal information.',
'change_your_personal_password' => 'Change your personal password.',
'create_your_personal_password' => [
'' => 'Create your personal password.',
],
'current_points_for' => 'current points for',
'data' => 'data',
'data_complete_unlocked' => 'data complete, unlocked',
'declaration_of_consent' => 'consent form',
'email_verified' => 'verify email',
'expired_on' => 'expired on',
'log_out_and_see_you_soon' => 'Sign out and see you soon.',
'login' => 'registration',
'manage_membership' => 'manage membership',
'manage_membership_now_here' => 'manage your membership here now',
'membership' => 'membership',
'news_updates' => 'News & Updates',
'news_archive' => 'News Archive',
'news_archive_title' => 'All News & Updates',
'news_archive_current' => 'Current News',
'news_archive_older' => 'Older Posts',
'news_archive_empty' => 'No older posts available.',
'news_archive_link' => 'View all news',
'news_back_to_dashboard' => 'Back to Dashboard',
'open_since' => 'opened since',
'open_your_shop' => 'open your own mivita shop',
'read_less' => 'Show less',
'read_more' => 'Read more',
'privacy_policy_approved' => 'privacy policy agreed',
'security' => 'security',
'settings_your_shop' => 'your shop settings',
'shop_not_booked' => 'shop not booked',
'today_is' => 'today is',
'until' => 'until',
'welcome_back' => 'welcome back',
'your_shop' => 'your shop',
'monthly_statistics' => 'Monthly Statistics',
'customer_turnover_points' => 'Customer Turnover Points',
'team_turnover_points' => 'Team Turnover Points',
'direct_new_partners' => 'Direct New Partners',
'team_new_partners' => 'New Partners in Team',
'customer_subscriptions' => 'Customer Subscriptions',
'team_subscriptions' => 'Team Subscriptions',
'own' => 'Own',
'live_calculation_hint' => 'Live calculation (not yet finalized)',
'live_calculation_hint_text' => 'Will be calculated at the end of the month.',
];

View file

@ -0,0 +1,172 @@
<?php
return [
// General
'incentives' => 'Incentives',
'incentive' => 'Incentive',
'name' => 'Name',
'status' => 'Status',
'period' => 'Period',
'actions' => 'Actions',
'participants' => 'Participants',
'save' => 'Save',
'cancel' => 'Cancel',
'yes' => 'Yes',
'no' => 'No',
'you' => 'You',
// Status
'status_draft' => 'Draft',
'status_active' => 'Active',
'status_closed' => 'Closed',
// CRUD
'create' => 'Create new incentive',
'edit' => 'Edit',
'created' => 'Incentive has been created successfully.',
'updated' => 'Incentive has been updated successfully.',
// Configuration
'configuration' => 'Configuration',
'qualification_start' => 'Qualification start',
'qualification_end' => 'Qualification end',
'calculation_end' => 'Calculation end',
'points_partner_onetime' => 'One-time points per partner',
'points_abo_onetime' => 'One-time points per subscription',
'min_direct_partners' => 'Min. direct partners',
'min_customer_abos' => 'Min. customer subscriptions',
'max_winners' => 'Max. winners',
'image' => 'Image',
'image_help' => 'Filename of the image in public/img/incentive/ folder (e.g. montenegro-2026.jpg)',
'description' => 'Description / Promo text',
'description_help' => 'Motivating intro text shown on the teaser page.',
'terms' => 'Terms and conditions',
'terms_help' => 'Full text of the terms and conditions. Displayed as a collapsible section on the page.',
'name_help' => 'Internal name of the incentive (also shown as the page headline).',
'subtitle' => 'Subtitle',
'subtitle_placeholder' => 'e.g. Your exclusive getaway on the Adriatic coast!',
'subtitle_help' => 'Short tagline displayed in the hero area below the title.',
'content_lang_de' => 'German',
'default_language' => 'Default',
'lang_fallback_hint' => 'Leave empty = German will be used as fallback.',
// Ranking
'ranking' => 'Ranking',
'rank' => 'Rank',
'consultant' => 'Consultant',
'total_points' => 'Total points',
'partners' => 'Partners',
'abos' => 'Subscriptions',
'qualified' => 'Qualified',
'open' => 'Open',
'winner' => 'Winner',
'no_participants' => 'No participants yet.',
'no_participants_with_points' => 'No participants with points yet.',
'anonymous_consultant' => 'Anonymous consultant',
'ranking_anonymous_hint' => 'Names appear only after participation in the incentive has been confirmed.',
'ranking_extended_hint' => 'The list shows ranks 130. The best :n qualified consultants (highlighted) win; the ranks below show who can still push ahead.',
'calculation_details' => 'Calculation Details',
'close' => 'Close',
// Recalculation
'recalculate' => 'Recalculate',
'recalculate_confirm' => 'Do you want to start the recalculation?',
'force_recalculate' => 'Full recalculation',
'force_recalculate_confirm' => 'WARNING: All existing logs will be deleted and recalculated from scratch. Continue?',
'recalculated' => 'Recalculation completed. :participants participants processed, :errors errors.',
// Admin ranking
'admin_terms_accepted' => 'Participation (terms)',
'admin_terms_pending' => 'Pending',
'admin_terms_accepted_at_tooltip' => 'Confirmation time',
// Participation (User)
'participate_title' => 'Join now!',
'accept_terms' => 'I accept the terms and conditions',
'show_terms' => 'Show terms',
'participate_now' => 'Join now',
'not_active' => 'This incentive is currently not active.',
'terms_required' => 'Please accept the terms and conditions.',
'already_participating' => 'You are already participating.',
'participation_confirmed' => 'Your participation has been confirmed!',
// Teaser page
'teaser_hero_subtitle' => 'Your exclusive getaway on the Adriatic coast awaits!',
'teaser_intro_bold' => 'Pack your bags, because mivita rewards your top performance!',
'teaser_intro_text' => 'Experience unforgettable days on the picturesque coastline, connect with top leaders and celebrate your success with us!',
'teaser_intro_cta' => 'Are you among the best :n partners? Then you\'re in!',
'teaser_until' => 'until',
'teaser_partner_onetime_text' => 'one-time for each directly sponsored new partner during the qualification period.',
'teaser_abo_onetime_text' => 'one-time for each newly concluded customer subscription during the qualification period.',
'teaser_cta_ready' => 'Ready for the challenge?',
'teaser_cta_text' => 'Sign up now to be listed in the official ranking. Only the best :n qualified consultants win!',
'teaser_cta_button' => 'Go to ranking & join',
'teaser_cta_to_ranking' => 'To the live ranking',
'teaser_cta_already_in' => 'You are already registered. Track your current rank in the live ranking.',
'teaser_pending_title' => 'Your points are already counting',
'teaser_pending_text' => 'Confirm participation so your name appears in the ranking and you can open the detail view.',
'teaser_cta_confirm' => 'Confirm participation',
'teaser_cta_coming_soon' => 'Coming soon!',
// Show page sections
'section_period' => 'The Qualification Period',
'qualification_period' => 'Qualification period',
'calculation_period' => 'Final sprint (calculation end)',
'calculation_period_hint' => 'Accumulated points are counted up to and including :date.',
'section_min_qual' => 'Your Ticket: Minimum Qualification',
'min_qual_intro' => 'To be listed in the official ranking and be eligible to win, the following base goals must be achieved during the qualification period:',
'min_partners_label' => 'direct new team partners (each only with one starter package)',
'min_abos_label' => 'newly concluded customer subscriptions',
'min_qual_ranking_hint' => 'In the live ranking, your name will only be highlighted in bold once you have successfully reached this minimum qualification.',
'section_points' => 'How to Collect Your Incentive Points',
'points_partners_title' => 'Points for new team partners',
'points_abos_title' => 'Points for customer subscriptions',
'points_short' => 'pts.',
'points_onetime_label' => 'one-time per new partner/subscription',
'points_starter_package_label' => 'each with a directly ordered starter package, new partners with only one membership do not count.',
'points_partner_boost' => 'Bonus boost: You also receive all customer and own sales points of your new partner from their start date, within the qualification period.',
'points_abo_direct' => 'Your own subscription also counts - existing subscriptions are also included.',
'points_abo_boost' => 'Bonus boost: You also receive the monthly subscription points from the subscription start month, within the qualification period.',
'section_ranking' => 'The Live Ranking',
'ranking_winners_hint' => 'Only the best :n qualified consultants win.',
'dashboard_btn_teaser' => 'To the incentive',
'dashboard_btn_ranking' => 'To the live ranking',
'read_more' => 'Read more',
'read_less' => 'Read less',
'you_participate' => 'You are participating!',
'your_rank' => 'Your current rank',
'participate_intro' => 'Ready for the challenge? Register once to be listed in the official ranking.',
'pending_confirmation_banner' => 'Your points are already counted for the qualification period. Please confirm participation so your name appears in the ranking and you can use all features.',
'details_requires_confirmation' => 'The detail view is available only after you confirm participation.',
'participate_abo_hint' => 'You already have at least one subscription that counts (active consultant subscription or a customer subscription started in the qualification period). When you join, points for it are applied immediately according to the current rules.',
// Calculation details (User)
'my_details' => 'My calculation',
'my_calculation' => 'My calculation overview',
'back_to_ranking' => 'Back to ranking',
'section_partners' => 'A. New partner points',
'section_abos' => 'B. Customer subscription points',
'new_partner' => 'New partner',
'entry_date' => 'Entry date',
'customer_abo' => 'Customer subscription',
'abo_date' => 'Start date',
'onetime' => 'One-time',
'sum' => 'Total',
'subtotal' => 'Subtotal',
'no_partners_yet' => 'No new partners recorded yet.',
'no_abos_yet' => 'No customer subscriptions recorded yet.',
'not_yet_qualified' => 'Not yet qualified',
// Transaction details
'transaction_date' => 'Date',
'transaction_description' => 'Description',
'transaction_period' => 'Period',
'transaction_type' => 'Type',
'transaction_points' => 'Points',
'onetime_registration' => 'One-time: Registration',
'onetime_abo_activation' => 'One-time: Subscription activation',
'accumulated' => 'Sales',
// Gallery
'gallery_title' => 'Impressions',
];

View file

@ -1,42 +1,42 @@
<?php
return array (
'VATID_could_not_be_validated' => 'The VAT ID could not be validated, please check your entry',
'VATID_successfully_entered' => 'VAT ID entered successfully',
'abo_deaktivert' => 'subscription option disabled',
'account_released' => 'Account activated',
'booked_package_has_been_changed' => 'booked package has been changed.',
'cancel_membership_is_requested' => 'Termination of membership has been requested',
'compensation_products_cannot_be_0' => 'Error: The compensation products cannot be 0.',
'contact_delete' => 'contact deleted',
'country_account_has_been_changed__cost_has_been_reset' => 'The billing country has been changed and the goods checklist has been reset',
'error_checkbox_not_confirm' => 'Error: Checkbox not confirmed',
'error_occurred_with_order' => 'An error occurred while ordering',
'file_deleted' => 'file deleted',
'file_empty' => '"file empty"',
'file_not_found' => 'file not found',
'file_uploaded' => 'file uploaded',
'homeparty_delete' => 'time out party deleted',
'homeparty_guest_delete' => 'time out party guest deleted',
'link_for_homeparty_not_found' => 'Link for the time-out party was not found or is no longer active.',
'no_change_made' => 'no change made',
'no_id_card_deposited_please_upload_first' => 'No ID provided, please upload it first',
'no_trade_licence_deposited_please_upload_first' => 'No business license stored, please upload it first',
'please_enter_reason_why_you_not_need_trade_licence' => 'Please provide a reason why you do not need a business license',
'please_select_compensation_product' => 'Please select a compensation product',
'please_select_count_compensation_products' => 'Please select :count compensation products',
'reverse_charge_procedure_and_VATID_deleted' => 'reverse charge procedure and VAT ID deleted',
'shipping_cost_cannot_be_0' => 'Error: Shipping cost cannot be 0',
'shipping_costs_were_not_calculated_correctly' => 'Error: Shipping costs were not calculated correctly',
'shipping_country_was_not_correctly' => 'Error: The shipping country was not processed correctly in the shopping cart',
'shipping_country_was_not_found' => 'Error: Shipping country not found',
'shopping_cart_was_not_user_shop' => 'Error: The consultant has no store, the order cannot be continued',
'shopping_cart_was_shipping_free' => 'Error: The shopping cart was specified as free shipping',
'shopping_instance_not_found' => 'Error: No ShoppingInstance was found',
'shopping_user_not_found' => 'Error: No ShoppingUser was found',
'user_not_found' => 'The consultant was not found.<br>The account has been deactivated or deleted.',
'your_shopping_cart_is_empty_please_add_products_first' =>
array (
'' => 'Your shopping cart is empty, please add products first',
),
);
return [
'VATID_could_not_be_validated' => 'The VAT ID could not be validated, please check your entry',
'VATID_successfully_entered' => 'VAT ID entered successfully',
'abo_deaktivert' => 'subscription option disabled',
'account_released' => 'Account activated',
'booked_package_has_been_changed' => 'booked package has been changed.',
'cancel_membership_is_requested' => 'Termination of membership has been requested',
'compensation_products_cannot_be_0' => 'Error: The compensation products cannot be 0.',
'contact_delete' => 'contact deleted',
'country_account_has_been_changed__cost_has_been_reset' => 'The billing country has been changed and the goods checklist has been reset',
'error_checkbox_not_confirm' => 'Error: Checkbox not confirmed',
'error_occurred_with_order' => 'An error occurred while ordering',
'file_deleted' => 'file deleted',
'file_empty' => '"file empty"',
'file_not_found' => 'file not found',
'file_uploaded' => 'file uploaded',
'homeparty_delete' => 'time out party deleted',
'homeparty_guest_delete' => 'time out party guest deleted',
'link_for_homeparty_not_found' => 'Link for the time-out party was not found or is no longer active.',
'no_change_made' => 'no change made',
'no_id_card_deposited_please_upload_first' => 'No ID provided, please upload it first',
'no_trade_licence_deposited_please_upload_first' => 'No business license stored, please upload it first',
'please_enter_reason_why_you_not_need_trade_licence' => 'Please provide a reason why you do not need a business license',
'please_select_compensation_product' => 'Please select a compensation product',
'please_select_count_compensation_products' => 'Please select :count compensation products',
'reverse_charge_procedure_and_VATID_deleted' => 'reverse charge procedure and VAT ID deleted',
'shipping_cost_cannot_be_0' => 'Error: Shipping cost cannot be 0',
'shipping_costs_were_not_calculated_correctly' => 'Error: Shipping costs were not calculated correctly',
'shipping_country_was_not_correctly' => 'Error: The shipping country was not processed correctly in the shopping cart',
'shipping_country_was_not_found' => 'Error: Shipping country not found',
'shopping_cart_was_not_user_shop' => 'Error: The consultant has no store, the order cannot be continued',
'shopping_cart_was_shipping_free' => 'Error: The shopping cart was specified as free shipping',
'shopping_instance_not_found' => 'Error: No ShoppingInstance was found',
'shopping_user_not_found' => 'Error: No ShoppingUser was found',
'user_not_found' => 'The consultant was not found.<br>The account has been deactivated or deleted.',
'your_shopping_cart_is_empty_please_add_products_first' => [
'' => 'Your shopping cart is empty, please add products first',
],
'cart_product_not_allowed_for_order_type' => 'Your cart contains items that are not allowed for this order type. Please clear the cart and only add products that match this order.',
];

View file

@ -75,8 +75,15 @@ return [
'shop' => 'Shop',
'to_shop' => 'To Shop',
'teamabos' => 'Team Abos',
'team_customer_abos' => 'Team Customer Subscriptions',
'customer_orders' => 'Customer Orders',
'external_orders' => 'External Orders',
'tools' => 'Tools',
'news_archive' => 'News Archive',
'incentive' => 'Incentive',
'incentives' => 'Incentives',
'create' => 'Create new',
'my_abo' => 'My Abo',
'my_subscriptions' => 'My Subscriptions',
'team_customers' => 'Team Customers',
];

View file

@ -118,4 +118,8 @@ return [
'free_shipping' => 'Free Shipping',
'free_shipping_reached' => 'Free shipping from :amount €',
'free_shipping_info' => 'Only :missing € more for free shipping (from :amount €)',
'reorder' => 'Reorder',
'reorder_info' => 'Would you like to order these items again?<br>Click the button to add them to your cart and go to the cart page.',
'reorder_info_2' => 'Your delivery country is: :country<br>If you want a different country, update your billing or delivery address under <a class="text-primary" href=":link">My details</a>',
'reorder_abo_not_allowed' => 'Subscription orders cannot be repeated via “Reorder”. Please use your subscription area or the shop for one-off orders.',
];

View file

@ -16,6 +16,7 @@ return [
'abo_order_info_check_2' => 'La primera entrega y facturación se realiza el día en que se establece la suscripción. Después, el envío se realiza automáticamente el día de entrega seleccionado del mes siguiente.',
'abo_order_info_check_3' => 'PayPal y tarjeta de crédito están disponibles como métodos de pago. <strong>La suscripción tiene una duración mínima de :abo-min-duration meses.</strong> Después, puede pausarse, cambiarse o cancelarse en cualquier momento.',
'abo_order_info_checkbox' => '¡Sí, he entendido los términos de la suscripción!',
'abo_order_info_checkbox_required' => 'Por favor, confirma los términos de la suscripción para continuar.',
'abo_infos' => 'Información de suscripción',
'abo_delivery_infos' => 'Información de entrega de la suscripción',
'abo_start_date' => 'Fecha de inicio de la suscripción',
@ -99,9 +100,23 @@ return [
'change_my_data_empty' => 'Aún no ha almacenado una dirección de facturación y entrega, sin esta no puede crear una suscripción, por favor créela.',
'abo_error_basis_product' => 'Error: Por favor seleccione al menos un producto base.',
'error_abo_interval_in_the_past' => 'La suscripción no se ha ejecutado este mes aún. Cambiar a un día pasado saltaría el mes actual.',
'warning_next_date_soon' => 'Nota: La próxima ejecución de la suscripción es en :days días (:date).',
'warning_next_date_soon_select' => 'Nota: La próxima ejecución de la suscripción es en :placeholder_days días (:placeholder_date).',
'warning_next_date_info' => 'La próxima ejecución de la suscripción es en :days días el :date.',
'info_next_execution_select' => 'Próxima ejecución: en :placeholder_days días el :placeholder_date.',
'error_change_locked' => 'Los cambios ya no son posibles. La próxima ejecución es en :days días. Los cambios deben realizarse al menos 10 días antes.',
'error_abo_interval_too_soon' => 'El día de entrega seleccionado está a solo :days días. Elija un día de entrega con al menos 10 días de anticipación.',
'error_cancel_locked' => 'La cancelación ya no es posible. La próxima ejecución es en :days días. Las cancelaciones deben realizarse al menos 3 días antes.',
'error_pause_locked' => 'La suscripción ya no puede pausarse. La próxima ejecución es en :days días. La pausa debe realizarse al menos 3 días antes.',
'cancel_abo' => 'Cancelar suscripción',
'confirm_cancel' => '¿Realmente desea cancelar la suscripción?',
'back' => 'atrás',
'team_subscriptions' => 'Suscripciones de equipo',
'team_customer_abos' => 'Suscripciones de clientes del equipo',
'chart_monthly_abos' => 'Suscripciones por mes',
'chart_active_abos' => 'Suscripciones activas',
'chart_abos_label' => 'suscripciones',
'abo_count' => 'Número de suscripciones',
'customer_privacy_info' => 'Por razones de privacidad, no se muestran datos personales del cliente.',
'every_month_on' => 'mensualmente el :day.',
];

View file

@ -1,51 +1,60 @@
<?php
return array(
'MIVITA_Consultancy_agreement' => 'MIVITA_Contrato de consultor',
'active_role' => 'rol activo',
'activities' => 'actividades',
'adjust_data' => 'ajustar datos',
'adviser_membership_active' => 'membresía consultora activa',
'adviser_onlineshop_active' => 'tienda online del consultor activa',
'adviser_onlineshop_inactive' => 'tienda de consultores inactiva',
'advisor_account_inactive' => 'cuenta de consultor inactiva',
'at' => 'en el',
'change_your_email_address' => 'cambia tu direccion de correo electronico.',
'change_your_personal_data' => 'cambie su información personal.',
'change_your_personal_password' => 'cambie su contraseña personal.',
'create_your_personal_password' =>
array(
'' => 'crea tu contraseña personal.',
),
'current_points_for' => 'puntos actuales para',
'data' => 'datos',
'data_complete_unlocked' => 'datos completos, desbloqueados',
'declaration_of_consent' => 'formulario de consentimiento',
'email_verified' => 'verificar correo electrónico',
'expired_on' => 'expirado el',
'log_out_and_see_you_soon' => 'Cierra sesión y nos vemos pronto.',
'login' => 'registro',
'manage_membership' => 'gestionar la afiliación',
'manage_membership_now_here' => 'gestiona tu membresía aquí ahora',
'membership' => 'afiliación',
'news_updates' => 'Noticias y Actualizaciones',
'news_archive' => 'Archivo de Noticias',
'news_archive_title' => 'Todas las Noticias y Actualizaciones',
'news_archive_current' => 'Noticia actual',
'news_archive_older' => 'Publicaciones anteriores',
'news_archive_empty' => 'No hay publicaciones anteriores disponibles.',
'news_archive_link' => 'Ver todas las noticias',
'news_back_to_dashboard' => 'Volver al panel',
'open_since' => 'abierto desde',
'open_your_shop' => 'abre tu propia tienda mivita',
'read_less' => 'Mostrar menos',
'read_more' => 'Leer más',
'privacy_policy_approved' => 'política de privacidad acordada',
'security' => 'seguridad',
'settings_your_shop' => 'configuración de tu tienda',
'shop_not_booked' => 'tienda no reservada',
'today_is' => 'hoy es',
'until' => 'hasta',
'welcome_back' => 'bienvenido de nuevo',
'your_shop' => 'tu tienda',
);
return [
'MIVITA_Consultancy_agreement' => 'MIVITA_Contrato de consultor',
'active_role' => 'rol activo',
'activities' => 'actividades',
'adjust_data' => 'ajustar datos',
'adviser_membership_active' => 'membresía consultora activa',
'adviser_onlineshop_active' => 'tienda online del consultor activa',
'adviser_onlineshop_inactive' => 'tienda de consultores inactiva',
'advisor_account_inactive' => 'cuenta de consultor inactiva',
'at' => 'en el',
'change_your_email_address' => 'cambia tu direccion de correo electronico.',
'change_your_personal_data' => 'cambie su información personal.',
'change_your_personal_password' => 'cambie su contraseña personal.',
'create_your_personal_password' => [
'' => 'crea tu contraseña personal.',
],
'current_points_for' => 'puntos actuales para',
'data' => 'datos',
'data_complete_unlocked' => 'datos completos, desbloqueados',
'declaration_of_consent' => 'formulario de consentimiento',
'email_verified' => 'verificar correo electrónico',
'expired_on' => 'expirado el',
'log_out_and_see_you_soon' => 'Cierra sesión y nos vemos pronto.',
'login' => 'registro',
'manage_membership' => 'gestionar la afiliación',
'manage_membership_now_here' => 'gestiona tu membresía aquí ahora',
'membership' => 'afiliación',
'news_updates' => 'Noticias y Actualizaciones',
'news_archive' => 'Archivo de Noticias',
'news_archive_title' => 'Todas las Noticias y Actualizaciones',
'news_archive_current' => 'Noticia actual',
'news_archive_older' => 'Publicaciones anteriores',
'news_archive_empty' => 'No hay publicaciones anteriores disponibles.',
'news_archive_link' => 'Ver todas las noticias',
'news_back_to_dashboard' => 'Volver al panel',
'open_since' => 'abierto desde',
'open_your_shop' => 'abre tu propia tienda mivita',
'read_less' => 'Mostrar menos',
'read_more' => 'Leer más',
'privacy_policy_approved' => 'política de privacidad acordada',
'security' => 'seguridad',
'settings_your_shop' => 'configuración de tu tienda',
'shop_not_booked' => 'tienda no reservada',
'today_is' => 'hoy es',
'until' => 'hasta',
'welcome_back' => 'bienvenido de nuevo',
'your_shop' => 'tu tienda',
'monthly_statistics' => 'Estadísticas mensuales',
'customer_turnover_points' => 'Puntos de venta de clientes',
'team_turnover_points' => 'Puntos de venta del equipo',
'direct_new_partners' => 'Nuevos socios directos',
'team_new_partners' => 'Nuevos socios en el equipo',
'customer_subscriptions' => 'Suscripciones de clientes',
'team_subscriptions' => 'Suscripciones del equipo',
'own' => 'Propio',
'live_calculation_hint' => 'Cálculo en vivo (no finalizado)',
'live_calculation_hint_text' => 'Se calculará al final del mes.',
];

View file

@ -0,0 +1,172 @@
<?php
return [
// General
'incentives' => 'Incentivos',
'incentive' => 'Incentivo',
'name' => 'Nombre',
'status' => 'Estado',
'period' => 'Periodo',
'actions' => 'Acciones',
'participants' => 'Participantes',
'save' => 'Guardar',
'cancel' => 'Cancelar',
'yes' => 'Si',
'no' => 'No',
'you' => 'Tu',
// Status
'status_draft' => 'Borrador',
'status_active' => 'Activo',
'status_closed' => 'Cerrado',
// CRUD
'create' => 'Crear nuevo incentivo',
'edit' => 'Editar',
'created' => 'El incentivo se ha creado correctamente.',
'updated' => 'El incentivo se ha actualizado correctamente.',
// Configuration
'configuration' => 'Configuracion',
'qualification_start' => 'Inicio de calificacion',
'qualification_end' => 'Fin de calificacion',
'calculation_end' => 'Fin de calculo',
'points_partner_onetime' => 'Puntos unicos por socio',
'points_abo_onetime' => 'Puntos unicos por suscripcion',
'min_direct_partners' => 'Min. socios directos',
'min_customer_abos' => 'Min. suscripciones de clientes',
'max_winners' => 'Max. ganadores',
'image' => 'Imagen',
'image_help' => 'Nombre del archivo de imagen en la carpeta public/img/incentive/ (p.ej. montenegro-2026.jpg)',
'description' => 'Descripcion / Texto promocional',
'description_help' => 'Texto introductorio motivador que se muestra en la pagina teaser.',
'terms' => 'Terminos y condiciones',
'terms_help' => 'Texto completo de los terminos y condiciones. Se muestra como seccion desplegable en la pagina.',
'name_help' => 'Nombre interno del incentivo (tambien se muestra como titulo de la pagina).',
'subtitle' => 'Subtitulo',
'subtitle_placeholder' => 'p.ej. Tu escapada exclusiva en la costa adriatica!',
'subtitle_help' => 'Eslogan corto que se muestra en el area hero debajo del titulo.',
'content_lang_de' => 'Aleman',
'default_language' => 'Predeterminado',
'lang_fallback_hint' => 'Dejar vacio = se usara el aleman como alternativa.',
// Ranking
'ranking' => 'Clasificacion',
'rank' => 'Puesto',
'consultant' => 'Consultor',
'total_points' => 'Puntos totales',
'partners' => 'Socios',
'abos' => 'Suscripciones',
'qualified' => 'Calificado',
'open' => 'Abierto',
'winner' => 'Ganador',
'no_participants' => 'Aun no hay participantes.',
'no_participants_with_points' => 'Aun no hay participantes con puntos.',
'anonymous_consultant' => 'Consultor anonimo',
'ranking_anonymous_hint' => 'Los nombres solo se muestran despues de confirmar la participacion en el incentivo.',
'ranking_extended_hint' => 'La lista muestra los puestos 130. Los mejores :n consultores calificados (marcados) ganan; los puestos siguientes muestran quien aun puede reforzar.',
'calculation_details' => 'Detalles del calculo',
'close' => 'Cerrar',
// Recalculation
'recalculate' => 'Recalcular',
'recalculate_confirm' => 'Desea iniciar el recalculo?',
'force_recalculate' => 'Recalculo completo',
'force_recalculate_confirm' => 'ATENCION: Se eliminaran todos los registros existentes y se recalculara desde cero. Continuar?',
'recalculated' => 'Recalculo completado. :participants participantes procesados, :errors errores.',
// Admin ranking
'admin_terms_accepted' => 'Participacion (terminos)',
'admin_terms_pending' => 'Pendiente',
'admin_terms_accepted_at_tooltip' => 'Fecha de confirmacion',
// Participation (User)
'participate_title' => 'Participa ahora!',
'accept_terms' => 'Acepto los terminos y condiciones',
'show_terms' => 'Mostrar terminos',
'participate_now' => 'Participar ahora',
'not_active' => 'Este incentivo no esta activo actualmente.',
'terms_required' => 'Por favor acepta los terminos y condiciones.',
'already_participating' => 'Ya estas participando.',
'participation_confirmed' => 'Tu participacion ha sido confirmada!',
// Teaser page
'teaser_hero_subtitle' => 'Tu escapada exclusiva en la costa adriatica te espera!',
'teaser_intro_bold' => 'Haz las maletas, porque mivita premia tus mejores resultados!',
'teaser_intro_text' => 'Vive dias inolvidables en la pintoresca costa, conecta con los mejores lideres y celebra tu exito con nosotros!',
'teaser_intro_cta' => 'Eres de los mejores :n socios? Entonces estas dentro!',
'teaser_until' => 'hasta',
'teaser_partner_onetime_text' => 'unico por cada nuevo socio patrocinado directamente durante el periodo de calificacion.',
'teaser_abo_onetime_text' => 'unico por cada nueva suscripcion de cliente concluida durante el periodo de calificacion.',
'teaser_cta_ready' => 'Listo para el desafio?',
'teaser_cta_text' => 'Registrate ahora para aparecer en el ranking oficial. Solo los mejores :n consultores calificados ganan!',
'teaser_cta_button' => 'Ir al ranking y participar',
'teaser_cta_to_ranking' => 'Al ranking en vivo',
'teaser_cta_already_in' => 'Ya estas registrado. Sigue tu puesto actual en el ranking en vivo.',
'teaser_pending_title' => 'Tus puntos ya cuentan',
'teaser_pending_text' => 'Confirma la participacion para que tu nombre aparezca en el ranking y puedas abrir la vista detallada.',
'teaser_cta_confirm' => 'Confirmar participacion',
'teaser_cta_coming_soon' => 'Proximamente!',
// Show page sections
'section_period' => 'El Periodo de Calificacion',
'qualification_period' => 'Periodo de calificacion',
'calculation_period' => 'Recta final (fin de calculo)',
'calculation_period_hint' => 'Los puntos acumulados se cuentan hasta el :date inclusive.',
'section_min_qual' => 'Tu Billete: Calificacion Minima',
'min_qual_intro' => 'Para aparecer en el ranking oficial y poder ganar, deben alcanzarse los siguientes objetivos base durante el periodo de calificacion:',
'min_partners_label' => 'nuevos socios directos del equipo (cada uno solo con un paquete de inicio)',
'min_abos_label' => 'nuevas suscripciones de clientes concluidas',
'min_qual_ranking_hint' => 'En el ranking en vivo, tu nombre solo se resaltara en negrita cuando hayas alcanzado exitosamente esta calificacion minima.',
'section_points' => 'Como Acumular tus Puntos de Incentivo',
'points_partners_title' => 'Puntos por nuevos socios del equipo',
'points_abos_title' => 'Puntos por suscripciones de clientes',
'points_short' => 'pts.',
'points_onetime_label' => 'unico por nuevo socio/suscripcion',
'points_starter_package_label' => 'cada uno con un paquete de inicio directo ordenado, los nuevos socios con solo una membresia no cuentan.',
'points_partner_boost' => 'Bonus extra: Tambien recibes todos los puntos de ventas de clientes y propios de tu nuevo socio desde su fecha de inicio, dentro del periodo de calificacion.',
'points_abo_direct' => 'Tu propia suscripcion tambien cuenta - incluso suscripciones existentes.',
'points_abo_boost' => 'Bonus extra: Tambien recibes los puntos mensuales de suscripcion desde el mes de inicio, dentro del periodo de calificacion.',
'section_ranking' => 'El Ranking en Vivo',
'ranking_winners_hint' => 'Solo los mejores :n consultores calificados ganan.',
'dashboard_btn_teaser' => 'Al incentivo',
'dashboard_btn_ranking' => 'Al ranking en vivo',
'read_more' => 'Leer más',
'read_less' => 'Leer menos',
'you_participate' => 'Estas participando!',
'your_rank' => 'Tu puesto actual',
'participate_intro' => 'Listo para el desafio? Registrate una vez para aparecer en el ranking oficial.',
'pending_confirmation_banner' => 'Tus puntos ya cuentan en el periodo de calificacion. Confirma la participacion para que tu nombre sea visible en el ranking y puedas usar todas las funciones.',
'details_requires_confirmation' => 'La vista detallada solo esta disponible despues de confirmar la participacion.',
'participate_abo_hint' => 'Ya tienes al menos una suscripcion relevante (suscripcion de consultor activa o suscripcion de cliente en el periodo de calificacion). Al participar, los puntos se aplican de inmediato segun las reglas vigentes.',
// Calculation details (User)
'my_details' => 'Mi calculo',
'my_calculation' => 'Mi resumen de calculo',
'back_to_ranking' => 'Volver a la clasificacion',
'section_partners' => 'A. Puntos de nuevos socios',
'section_abos' => 'B. Puntos de suscripciones de clientes',
'new_partner' => 'Nuevo socio',
'entry_date' => 'Fecha de entrada',
'customer_abo' => 'Suscripcion de cliente',
'abo_date' => 'Fecha de inicio',
'onetime' => 'Unico',
'sum' => 'Total',
'subtotal' => 'Subtotal',
'no_partners_yet' => 'Aun no se han registrado nuevos socios.',
'no_abos_yet' => 'Aun no se han registrado suscripciones de clientes.',
'not_yet_qualified' => 'Aun no calificado',
// Detalles de transacciones
'transaction_date' => 'Fecha',
'transaction_description' => 'Descripcion',
'transaction_period' => 'Periodo',
'transaction_type' => 'Tipo',
'transaction_points' => 'Puntos',
'onetime_registration' => 'Unico: Registro',
'onetime_abo_activation' => 'Unico: Activacion de suscripcion',
'accumulated' => 'Ventas',
// Galería
'gallery_title' => 'Impresiones',
];

View file

@ -75,8 +75,15 @@ return [
'shop' => 'Tienda',
'to_shop' => 'A la Tienda',
'teamabos' => 'Suscripciones del Equipo',
'team_customer_abos' => 'Suscripciones de Clientes del Equipo',
'customer_orders' => 'Pedidos de clientes',
'external_orders' => 'Pedidos externos',
'tools' => 'Herramientas',
'news_archive' => 'Archivo de Noticias',
'incentive' => 'Incentivo',
'incentives' => 'Incentivos',
'create' => 'Crear nuevo',
'my_abo' => 'Mi Suscripción',
'my_subscriptions' => 'Mis Suscripciones',
'team_customers' => 'Clientes del Equipo',
];

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