Newsletter-Listen: Empfaengerlisten filtern, speichern und exportieren
Admin-Bereich "Newsletter-Listen" (Plan dev/_tickets/entwicklungsplan- newsletter-listen.md): Berater und Kunden nach Filtern zusammenstellen, als Liste speichern und wieder laden, Export als XLSX/CSV. Jeder Export wird protokolliert (newsletter_list_exports). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c568248046
commit
c858bd6ed0
26 changed files with 3664 additions and 0 deletions
|
|
@ -94,4 +94,8 @@ return [
|
|||
'team_customers' => 'Team Kunden',
|
||||
'payment_monitor' => 'Payment Monitor',
|
||||
'payment_monitor_management' => 'Payment Monitor GF',
|
||||
'newsletter_lists' => 'Newsletter-Listen',
|
||||
'newsletter_create' => 'Liste erstellen',
|
||||
'newsletter_saved' => 'Gespeicherte Listen',
|
||||
'newsletter_exports' => 'Export-Protokoll',
|
||||
];
|
||||
|
|
|
|||
158
resources/lang/de/newsletter.php
Normal file
158
resources/lang/de/newsletter.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Newsletter-Listen',
|
||||
'intro' => 'Empfängerliste aus Kunden und Beratern zusammenstellen, prüfen und für rapidmail exportieren. Gelöschte, anonymisierte, Fake- und Test-Datensätze werden immer ausgeschlossen, doppelte E-Mail-Adressen automatisch entfernt.',
|
||||
'filter' => [
|
||||
'source' => 'Quelle',
|
||||
'language' => 'Sprache',
|
||||
'country' => 'Land',
|
||||
'optin' => 'Newsletter-Opt-in',
|
||||
'purchase' => 'Kaufstatus',
|
||||
'member' => 'Zugeordneter Berater / Sponsor',
|
||||
'created_from' => 'Angelegt von',
|
||||
'created_to' => 'Angelegt bis',
|
||||
'company' => 'Firma',
|
||||
'consultant_status' => 'Berater-Status',
|
||||
'level' => 'Karriere-Level',
|
||||
'include_admins' => 'Admin-Accounts einbeziehen',
|
||||
'all' => 'alle',
|
||||
'none_member' => 'ohne Zuordnung',
|
||||
'consultant_section' => 'Berater-Filter',
|
||||
'refresh' => 'Vorschau aktualisieren',
|
||||
'reset' => 'Filter zurücksetzen',
|
||||
],
|
||||
'source_option' => [
|
||||
'customers' => 'Kunden',
|
||||
'consultants' => 'Berater',
|
||||
'both' => 'Kunden und Berater',
|
||||
],
|
||||
'source' => [
|
||||
'customer' => 'Kunde',
|
||||
'consultant' => 'Berater',
|
||||
],
|
||||
'optin_option' => [
|
||||
'all' => 'alle (Opt-in wird als Spalte ausgegeben)',
|
||||
'subscribed' => 'nur mit Opt-in',
|
||||
'unsubscribed' => 'nur ohne Opt-in',
|
||||
],
|
||||
'optin_value' => [
|
||||
'yes' => 'ja',
|
||||
'no' => 'nein',
|
||||
'contract' => 'Vertrag',
|
||||
],
|
||||
'purchase_option' => [
|
||||
'all' => 'alle',
|
||||
'has_buyed' => 'hat gekauft',
|
||||
'none' => 'noch nie gekauft',
|
||||
'months_3' => 'gekauft in den letzten 3 Monaten',
|
||||
'months_6' => 'gekauft in den letzten 6 Monaten',
|
||||
'months_12' => 'gekauft in den letzten 12 Monaten',
|
||||
'active_abo' => 'hat aktives Abo',
|
||||
],
|
||||
'company_option' => [
|
||||
'all' => 'alle',
|
||||
'with' => 'nur mit Firma',
|
||||
'without' => 'nur ohne Firma',
|
||||
],
|
||||
'consultant_status_option' => [
|
||||
'active' => 'nur aktive',
|
||||
'inactive' => 'nur inaktive',
|
||||
'all' => 'alle',
|
||||
],
|
||||
'status_value' => [
|
||||
'active' => 'aktiv',
|
||||
'inactive' => 'inaktiv',
|
||||
],
|
||||
'stats' => [
|
||||
'total' => 'eindeutige Adressen',
|
||||
'customers' => 'Kunden',
|
||||
'consultants' => 'Berater',
|
||||
'duplicates' => 'Duplikate entfernt',
|
||||
'invalid' => 'ungültige Adressen',
|
||||
'loading' => 'Liste wird berechnet …',
|
||||
],
|
||||
'hint' => [
|
||||
'optin_all' => 'Die Liste enthält auch Kunden ohne Newsletter-Opt-in. Die Spalte „Newsletter" kennzeichnet den Status je Adresse.',
|
||||
'consultants_contract' => 'Berater haben kein eigenes Opt-in; Grundlage ist der Beratervertrag (Spalte „Newsletter" = Vertrag).',
|
||||
],
|
||||
'export' => [
|
||||
'email' => 'Emailadresse',
|
||||
'firstname' => 'Vorname',
|
||||
'lastname' => 'Nachname',
|
||||
'salutation_label' => 'Anrede',
|
||||
'street' => 'Straße',
|
||||
'zipcode' => 'PLZ',
|
||||
'city' => 'Wohnort',
|
||||
'company' => 'Firma',
|
||||
'consultant' => 'Berater',
|
||||
'country' => 'Land',
|
||||
'language' => 'Sprache',
|
||||
'source_label' => 'Quelle',
|
||||
'number' => 'Nummer',
|
||||
'consultant_number' => 'Berater-Nr.',
|
||||
'optin_label' => 'Newsletter',
|
||||
'status_label' => 'Status',
|
||||
'created_at' => 'Angelegt',
|
||||
'button_xlsx' => 'Export XLSX',
|
||||
'button_csv' => 'Export CSV',
|
||||
'template_only' => 'Nur die 9 Spalten der Vorlage exportieren',
|
||||
],
|
||||
'table' => [
|
||||
'email' => 'E-Mail',
|
||||
'name' => 'Name',
|
||||
'salutation' => 'Anrede',
|
||||
'zipcode' => 'PLZ',
|
||||
'city' => 'Ort',
|
||||
'country' => 'Land',
|
||||
'language' => 'Sprache',
|
||||
'consultant' => 'Berater / Sponsor',
|
||||
'source' => 'Quelle',
|
||||
'optin' => 'Newsletter',
|
||||
'status' => 'Status',
|
||||
'created' => 'Angelegt',
|
||||
],
|
||||
'saved' => [
|
||||
'title' => 'Gespeicherte Listen',
|
||||
'current' => 'Geladene Liste',
|
||||
'name' => 'Name',
|
||||
'name_placeholder' => 'z. B. Newsletter DE Kunden',
|
||||
'save_new' => 'Als neue Liste speichern',
|
||||
'save_changes' => 'Änderungen in dieser Liste speichern',
|
||||
'summary' => 'Filter',
|
||||
'source' => 'Quelle',
|
||||
'last_export' => 'Letzter Export',
|
||||
'last_count' => 'Adressen',
|
||||
'created_by' => 'Angelegt von',
|
||||
'created_at' => 'Angelegt',
|
||||
'actions' => 'Aktionen',
|
||||
'load' => 'Filter laden',
|
||||
'delete' => 'Löschen',
|
||||
'confirm_delete' => 'Liste wirklich löschen? Das Export-Protokoll bleibt erhalten.',
|
||||
'stored' => 'Liste gespeichert.',
|
||||
'updated' => 'Liste aktualisiert.',
|
||||
'deleted' => 'Liste gelöscht.',
|
||||
'intro' => 'Gespeicherte Filterkombinationen. Über „Filter laden" wird die Liste in die Vorschau übernommen, der Export nutzt die hinterlegten Filter.',
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Export-Protokoll',
|
||||
'intro' => 'Jeder Download wird mit Benutzer, Zeitpunkt, Filter und Anzahl der Adressen festgehalten.',
|
||||
'date' => 'Datum',
|
||||
'user' => 'Benutzer',
|
||||
'list' => 'Liste',
|
||||
'adhoc' => 'ohne gespeicherte Liste',
|
||||
'summary' => 'Filter',
|
||||
'format' => 'Format',
|
||||
'rows' => 'Adressen',
|
||||
'filename' => 'Datei',
|
||||
],
|
||||
'validation' => [
|
||||
'source' => 'Ungültige Quelle.',
|
||||
'option' => 'Ungültiger Filterwert.',
|
||||
'member' => 'Ungültige Berater-Auswahl.',
|
||||
'date' => 'Ungültiges Datum.',
|
||||
'name' => 'Bitte einen Namen angeben (max. 120 Zeichen).',
|
||||
'list' => 'Liste nicht gefunden.',
|
||||
'date_range' => 'Das Enddatum darf nicht vor dem Startdatum liegen.',
|
||||
],
|
||||
];
|
||||
|
|
@ -94,4 +94,8 @@ return [
|
|||
'team_customers' => 'Team Customers',
|
||||
'payment_monitor' => 'Payment Monitor',
|
||||
'payment_monitor_management' => 'Payment Monitor (Mgmt)',
|
||||
'newsletter_lists' => 'Newsletter lists',
|
||||
'newsletter_create' => 'Create list',
|
||||
'newsletter_saved' => 'Saved lists',
|
||||
'newsletter_exports' => 'Export log',
|
||||
];
|
||||
|
|
|
|||
158
resources/lang/en/newsletter.php
Normal file
158
resources/lang/en/newsletter.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Newsletter lists',
|
||||
'intro' => 'Build a recipient list from customers and consultants, review it and export it for rapidmail. Deleted, anonymised, fake and test records are always excluded; duplicate e-mail addresses are removed automatically.',
|
||||
'filter' => [
|
||||
'source' => 'Source',
|
||||
'language' => 'Language',
|
||||
'country' => 'Country',
|
||||
'optin' => 'Newsletter opt-in',
|
||||
'purchase' => 'Purchase status',
|
||||
'member' => 'Assigned consultant / sponsor',
|
||||
'created_from' => 'Created from',
|
||||
'created_to' => 'Created until',
|
||||
'company' => 'Company',
|
||||
'consultant_status' => 'Consultant status',
|
||||
'level' => 'Career level',
|
||||
'include_admins' => 'Include admin accounts',
|
||||
'all' => 'all',
|
||||
'none_member' => 'without assignment',
|
||||
'consultant_section' => 'Consultant filters',
|
||||
'refresh' => 'Refresh preview',
|
||||
'reset' => 'Reset filters',
|
||||
],
|
||||
'source_option' => [
|
||||
'customers' => 'Customers',
|
||||
'consultants' => 'Consultants',
|
||||
'both' => 'Customers and consultants',
|
||||
],
|
||||
'source' => [
|
||||
'customer' => 'Customer',
|
||||
'consultant' => 'Consultant',
|
||||
],
|
||||
'optin_option' => [
|
||||
'all' => 'all (opt-in is exported as a column)',
|
||||
'subscribed' => 'only with opt-in',
|
||||
'unsubscribed' => 'only without opt-in',
|
||||
],
|
||||
'optin_value' => [
|
||||
'yes' => 'yes',
|
||||
'no' => 'no',
|
||||
'contract' => 'contract',
|
||||
],
|
||||
'purchase_option' => [
|
||||
'all' => 'all',
|
||||
'has_buyed' => 'has purchased',
|
||||
'none' => 'never purchased',
|
||||
'months_3' => 'purchased in the last 3 months',
|
||||
'months_6' => 'purchased in the last 6 months',
|
||||
'months_12' => 'purchased in the last 12 months',
|
||||
'active_abo' => 'has an active subscription',
|
||||
],
|
||||
'company_option' => [
|
||||
'all' => 'all',
|
||||
'with' => 'only with company',
|
||||
'without' => 'only without company',
|
||||
],
|
||||
'consultant_status_option' => [
|
||||
'active' => 'active only',
|
||||
'inactive' => 'inactive only',
|
||||
'all' => 'all',
|
||||
],
|
||||
'status_value' => [
|
||||
'active' => 'active',
|
||||
'inactive' => 'inactive',
|
||||
],
|
||||
'stats' => [
|
||||
'total' => 'unique addresses',
|
||||
'customers' => 'customers',
|
||||
'consultants' => 'consultants',
|
||||
'duplicates' => 'duplicates removed',
|
||||
'invalid' => 'invalid addresses',
|
||||
'loading' => 'Calculating list …',
|
||||
],
|
||||
'hint' => [
|
||||
'optin_all' => 'The list also contains customers without newsletter opt-in. The "Newsletter" column marks the status per address.',
|
||||
'consultants_contract' => 'Consultants have no separate opt-in; the legal basis is the consultant contract ("Newsletter" column = contract).',
|
||||
],
|
||||
'export' => [
|
||||
'email' => 'Email address',
|
||||
'firstname' => 'First name',
|
||||
'lastname' => 'Last name',
|
||||
'salutation_label' => 'Salutation',
|
||||
'street' => 'Street',
|
||||
'zipcode' => 'Postcode',
|
||||
'city' => 'City',
|
||||
'company' => 'Company',
|
||||
'consultant' => 'Consultant',
|
||||
'country' => 'Country',
|
||||
'language' => 'Language',
|
||||
'source_label' => 'Source',
|
||||
'number' => 'Number',
|
||||
'consultant_number' => 'Consultant no.',
|
||||
'optin_label' => 'Newsletter',
|
||||
'status_label' => 'Status',
|
||||
'created_at' => 'Created',
|
||||
'button_xlsx' => 'Export XLSX',
|
||||
'button_csv' => 'Export CSV',
|
||||
'template_only' => 'Export only the 9 template columns',
|
||||
],
|
||||
'table' => [
|
||||
'email' => 'E-mail',
|
||||
'name' => 'Name',
|
||||
'salutation' => 'Salutation',
|
||||
'zipcode' => 'Postcode',
|
||||
'city' => 'City',
|
||||
'country' => 'Country',
|
||||
'language' => 'Language',
|
||||
'consultant' => 'Consultant / sponsor',
|
||||
'source' => 'Source',
|
||||
'optin' => 'Newsletter',
|
||||
'status' => 'Status',
|
||||
'created' => 'Created',
|
||||
],
|
||||
'saved' => [
|
||||
'title' => 'Saved lists',
|
||||
'current' => 'Loaded list',
|
||||
'name' => 'Name',
|
||||
'name_placeholder' => 'e.g. Newsletter DE customers',
|
||||
'save_new' => 'Save as new list',
|
||||
'save_changes' => 'Save changes to this list',
|
||||
'summary' => 'Filters',
|
||||
'source' => 'Source',
|
||||
'last_export' => 'Last export',
|
||||
'last_count' => 'Addresses',
|
||||
'created_by' => 'Created by',
|
||||
'created_at' => 'Created',
|
||||
'actions' => 'Actions',
|
||||
'load' => 'Load filters',
|
||||
'delete' => 'Delete',
|
||||
'confirm_delete' => 'Really delete this list? The export log is kept.',
|
||||
'stored' => 'List saved.',
|
||||
'updated' => 'List updated.',
|
||||
'deleted' => 'List deleted.',
|
||||
'intro' => 'Saved filter combinations. "Load filters" puts the list into the preview; the export uses the stored filters.',
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Export log',
|
||||
'intro' => 'Every download is recorded with user, time, filters and number of addresses.',
|
||||
'date' => 'Date',
|
||||
'user' => 'User',
|
||||
'list' => 'List',
|
||||
'adhoc' => 'without saved list',
|
||||
'summary' => 'Filters',
|
||||
'format' => 'Format',
|
||||
'rows' => 'Addresses',
|
||||
'filename' => 'File',
|
||||
],
|
||||
'validation' => [
|
||||
'source' => 'Invalid source.',
|
||||
'option' => 'Invalid filter value.',
|
||||
'member' => 'Invalid consultant selection.',
|
||||
'date' => 'Invalid date.',
|
||||
'name' => 'Please enter a name (max. 120 characters).',
|
||||
'list' => 'List not found.',
|
||||
'date_range' => 'The end date must not be before the start date.',
|
||||
],
|
||||
];
|
||||
|
|
@ -95,4 +95,8 @@ return [
|
|||
'my_statistics' => 'Mi estadística',
|
||||
'team_statistics' => 'Estadística del equipo',
|
||||
'withdrawals' => 'Desistimientos',
|
||||
'newsletter_lists' => 'Listas de newsletter',
|
||||
'newsletter_create' => 'Crear lista',
|
||||
'newsletter_saved' => 'Listas guardadas',
|
||||
'newsletter_exports' => 'Registro de exportaciones',
|
||||
];
|
||||
|
|
|
|||
158
resources/lang/es/newsletter.php
Normal file
158
resources/lang/es/newsletter.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Listas de newsletter',
|
||||
'intro' => 'Crea una lista de destinatarios a partir de clientes y tutores, revísala y expórtala para rapidmail. Los registros eliminados, anonimizados, falsos y de prueba se excluyen siempre; las direcciones duplicadas se eliminan automáticamente.',
|
||||
'filter' => [
|
||||
'source' => 'Fuente',
|
||||
'language' => 'Idioma',
|
||||
'country' => 'País',
|
||||
'optin' => 'Opt-in newsletter',
|
||||
'purchase' => 'Estado de compra',
|
||||
'member' => 'Tutor / patrocinador asignado',
|
||||
'created_from' => 'Creado desde',
|
||||
'created_to' => 'Creado hasta',
|
||||
'company' => 'Empresa',
|
||||
'consultant_status' => 'Estado del tutor',
|
||||
'level' => 'Nivel de carrera',
|
||||
'include_admins' => 'Incluir cuentas de administrador',
|
||||
'all' => 'todos',
|
||||
'none_member' => 'sin asignación',
|
||||
'consultant_section' => 'Filtros de tutores',
|
||||
'refresh' => 'Actualizar vista previa',
|
||||
'reset' => 'Restablecer filtros',
|
||||
],
|
||||
'source_option' => [
|
||||
'customers' => 'Clientes',
|
||||
'consultants' => 'Tutores',
|
||||
'both' => 'Clientes y tutores',
|
||||
],
|
||||
'source' => [
|
||||
'customer' => 'Cliente',
|
||||
'consultant' => 'Tutor',
|
||||
],
|
||||
'optin_option' => [
|
||||
'all' => 'todos (el opt-in se exporta como columna)',
|
||||
'subscribed' => 'solo con opt-in',
|
||||
'unsubscribed' => 'solo sin opt-in',
|
||||
],
|
||||
'optin_value' => [
|
||||
'yes' => 'sí',
|
||||
'no' => 'no',
|
||||
'contract' => 'contrato',
|
||||
],
|
||||
'purchase_option' => [
|
||||
'all' => 'todos',
|
||||
'has_buyed' => 'ha comprado',
|
||||
'none' => 'nunca ha comprado',
|
||||
'months_3' => 'compró en los últimos 3 meses',
|
||||
'months_6' => 'compró en los últimos 6 meses',
|
||||
'months_12' => 'compró en los últimos 12 meses',
|
||||
'active_abo' => 'tiene una suscripción activa',
|
||||
],
|
||||
'company_option' => [
|
||||
'all' => 'todos',
|
||||
'with' => 'solo con empresa',
|
||||
'without' => 'solo sin empresa',
|
||||
],
|
||||
'consultant_status_option' => [
|
||||
'active' => 'solo activos',
|
||||
'inactive' => 'solo inactivos',
|
||||
'all' => 'todos',
|
||||
],
|
||||
'status_value' => [
|
||||
'active' => 'activo',
|
||||
'inactive' => 'inactivo',
|
||||
],
|
||||
'stats' => [
|
||||
'total' => 'direcciones únicas',
|
||||
'customers' => 'clientes',
|
||||
'consultants' => 'tutores',
|
||||
'duplicates' => 'duplicados eliminados',
|
||||
'invalid' => 'direcciones no válidas',
|
||||
'loading' => 'Calculando la lista …',
|
||||
],
|
||||
'hint' => [
|
||||
'optin_all' => 'La lista también contiene clientes sin opt-in de newsletter. La columna «Newsletter» indica el estado de cada dirección.',
|
||||
'consultants_contract' => 'Los tutores no tienen opt-in propio; la base es el contrato de tutor (columna «Newsletter» = contrato).',
|
||||
],
|
||||
'export' => [
|
||||
'email' => 'Correo electrónico',
|
||||
'firstname' => 'Nombre',
|
||||
'lastname' => 'Apellidos',
|
||||
'salutation_label' => 'Tratamiento',
|
||||
'street' => 'Calle',
|
||||
'zipcode' => 'Código postal',
|
||||
'city' => 'Ciudad',
|
||||
'company' => 'Empresa',
|
||||
'consultant' => 'Tutor',
|
||||
'country' => 'País',
|
||||
'language' => 'Idioma',
|
||||
'source_label' => 'Fuente',
|
||||
'number' => 'Número',
|
||||
'consultant_number' => 'N.º de tutor',
|
||||
'optin_label' => 'Newsletter',
|
||||
'status_label' => 'Estado',
|
||||
'created_at' => 'Creado',
|
||||
'button_xlsx' => 'Exportar XLSX',
|
||||
'button_csv' => 'Exportar CSV',
|
||||
'template_only' => 'Exportar solo las 9 columnas de la plantilla',
|
||||
],
|
||||
'table' => [
|
||||
'email' => 'Correo',
|
||||
'name' => 'Nombre',
|
||||
'salutation' => 'Tratamiento',
|
||||
'zipcode' => 'CP',
|
||||
'city' => 'Ciudad',
|
||||
'country' => 'País',
|
||||
'language' => 'Idioma',
|
||||
'consultant' => 'Tutor / patrocinador',
|
||||
'source' => 'Fuente',
|
||||
'optin' => 'Newsletter',
|
||||
'status' => 'Estado',
|
||||
'created' => 'Creado',
|
||||
],
|
||||
'saved' => [
|
||||
'title' => 'Listas guardadas',
|
||||
'current' => 'Lista cargada',
|
||||
'name' => 'Nombre',
|
||||
'name_placeholder' => 'p. ej. Newsletter DE clientes',
|
||||
'save_new' => 'Guardar como lista nueva',
|
||||
'save_changes' => 'Guardar cambios en esta lista',
|
||||
'summary' => 'Filtros',
|
||||
'source' => 'Fuente',
|
||||
'last_export' => 'Última exportación',
|
||||
'last_count' => 'Direcciones',
|
||||
'created_by' => 'Creado por',
|
||||
'created_at' => 'Creado',
|
||||
'actions' => 'Acciones',
|
||||
'load' => 'Cargar filtros',
|
||||
'delete' => 'Eliminar',
|
||||
'confirm_delete' => '¿Eliminar esta lista? El registro de exportaciones se conserva.',
|
||||
'stored' => 'Lista guardada.',
|
||||
'updated' => 'Lista actualizada.',
|
||||
'deleted' => 'Lista eliminada.',
|
||||
'intro' => 'Combinaciones de filtros guardadas. «Cargar filtros» lleva la lista a la vista previa; la exportación usa los filtros guardados.',
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Registro de exportaciones',
|
||||
'intro' => 'Cada descarga se registra con usuario, fecha, filtros y número de direcciones.',
|
||||
'date' => 'Fecha',
|
||||
'user' => 'Usuario',
|
||||
'list' => 'Lista',
|
||||
'adhoc' => 'sin lista guardada',
|
||||
'summary' => 'Filtros',
|
||||
'format' => 'Formato',
|
||||
'rows' => 'Direcciones',
|
||||
'filename' => 'Archivo',
|
||||
],
|
||||
'validation' => [
|
||||
'source' => 'Fuente no válida.',
|
||||
'option' => 'Valor de filtro no válido.',
|
||||
'member' => 'Selección de tutor no válida.',
|
||||
'date' => 'Fecha no válida.',
|
||||
'name' => 'Indique un nombre (máx. 120 caracteres).',
|
||||
'list' => 'Lista no encontrada.',
|
||||
'date_range' => 'La fecha final no puede ser anterior a la inicial.',
|
||||
],
|
||||
];
|
||||
|
|
@ -95,4 +95,8 @@ return [
|
|||
'my_statistics' => 'Ma statistique',
|
||||
'team_statistics' => 'Statistiques de l\'équipe',
|
||||
'withdrawals' => 'Rétractations',
|
||||
'newsletter_lists' => 'Listes newsletter',
|
||||
'newsletter_create' => 'Créer une liste',
|
||||
'newsletter_saved' => 'Listes enregistrées',
|
||||
'newsletter_exports' => 'Journal des exports',
|
||||
];
|
||||
|
|
|
|||
158
resources/lang/fr/newsletter.php
Normal file
158
resources/lang/fr/newsletter.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Listes newsletter',
|
||||
'intro' => 'Composez une liste de destinataires à partir des clients et des conseillers, vérifiez-la et exportez-la pour rapidmail. Les enregistrements supprimés, anonymisés, fictifs et de test sont toujours exclus ; les adresses en double sont retirées automatiquement.',
|
||||
'filter' => [
|
||||
'source' => 'Source',
|
||||
'language' => 'Langue',
|
||||
'country' => 'Pays',
|
||||
'optin' => 'Opt-in newsletter',
|
||||
'purchase' => 'Statut d’achat',
|
||||
'member' => 'Conseiller / parrain attribué',
|
||||
'created_from' => 'Créé à partir du',
|
||||
'created_to' => 'Créé jusqu’au',
|
||||
'company' => 'Société',
|
||||
'consultant_status' => 'Statut du conseiller',
|
||||
'level' => 'Niveau de carrière',
|
||||
'include_admins' => 'Inclure les comptes administrateurs',
|
||||
'all' => 'tous',
|
||||
'none_member' => 'sans attribution',
|
||||
'consultant_section' => 'Filtres conseillers',
|
||||
'refresh' => 'Actualiser l’aperçu',
|
||||
'reset' => 'Réinitialiser les filtres',
|
||||
],
|
||||
'source_option' => [
|
||||
'customers' => 'Clients',
|
||||
'consultants' => 'Conseillers',
|
||||
'both' => 'Clients et conseillers',
|
||||
],
|
||||
'source' => [
|
||||
'customer' => 'Client',
|
||||
'consultant' => 'Conseiller',
|
||||
],
|
||||
'optin_option' => [
|
||||
'all' => 'tous (l’opt-in est exporté comme colonne)',
|
||||
'subscribed' => 'uniquement avec opt-in',
|
||||
'unsubscribed' => 'uniquement sans opt-in',
|
||||
],
|
||||
'optin_value' => [
|
||||
'yes' => 'oui',
|
||||
'no' => 'non',
|
||||
'contract' => 'contrat',
|
||||
],
|
||||
'purchase_option' => [
|
||||
'all' => 'tous',
|
||||
'has_buyed' => 'a acheté',
|
||||
'none' => 'n’a jamais acheté',
|
||||
'months_3' => 'a acheté au cours des 3 derniers mois',
|
||||
'months_6' => 'a acheté au cours des 6 derniers mois',
|
||||
'months_12' => 'a acheté au cours des 12 derniers mois',
|
||||
'active_abo' => 'a un abonnement actif',
|
||||
],
|
||||
'company_option' => [
|
||||
'all' => 'tous',
|
||||
'with' => 'uniquement avec société',
|
||||
'without' => 'uniquement sans société',
|
||||
],
|
||||
'consultant_status_option' => [
|
||||
'active' => 'actifs uniquement',
|
||||
'inactive' => 'inactifs uniquement',
|
||||
'all' => 'tous',
|
||||
],
|
||||
'status_value' => [
|
||||
'active' => 'actif',
|
||||
'inactive' => 'inactif',
|
||||
],
|
||||
'stats' => [
|
||||
'total' => 'adresses uniques',
|
||||
'customers' => 'clients',
|
||||
'consultants' => 'conseillers',
|
||||
'duplicates' => 'doublons retirés',
|
||||
'invalid' => 'adresses invalides',
|
||||
'loading' => 'Calcul de la liste …',
|
||||
],
|
||||
'hint' => [
|
||||
'optin_all' => 'La liste contient aussi des clients sans opt-in newsletter. La colonne « Newsletter » indique le statut de chaque adresse.',
|
||||
'consultants_contract' => 'Les conseillers n’ont pas d’opt-in propre ; la base est le contrat de conseiller (colonne « Newsletter » = contrat).',
|
||||
],
|
||||
'export' => [
|
||||
'email' => 'Adresse e-mail',
|
||||
'firstname' => 'Prénom',
|
||||
'lastname' => 'Nom',
|
||||
'salutation_label' => 'Civilité',
|
||||
'street' => 'Rue',
|
||||
'zipcode' => 'Code postal',
|
||||
'city' => 'Ville',
|
||||
'company' => 'Société',
|
||||
'consultant' => 'Conseiller',
|
||||
'country' => 'Pays',
|
||||
'language' => 'Langue',
|
||||
'source_label' => 'Source',
|
||||
'number' => 'Numéro',
|
||||
'consultant_number' => 'N° conseiller',
|
||||
'optin_label' => 'Newsletter',
|
||||
'status_label' => 'Statut',
|
||||
'created_at' => 'Créé le',
|
||||
'button_xlsx' => 'Exporter XLSX',
|
||||
'button_csv' => 'Exporter CSV',
|
||||
'template_only' => 'Exporter uniquement les 9 colonnes du modèle',
|
||||
],
|
||||
'table' => [
|
||||
'email' => 'E-mail',
|
||||
'name' => 'Nom',
|
||||
'salutation' => 'Civilité',
|
||||
'zipcode' => 'CP',
|
||||
'city' => 'Ville',
|
||||
'country' => 'Pays',
|
||||
'language' => 'Langue',
|
||||
'consultant' => 'Conseiller / parrain',
|
||||
'source' => 'Source',
|
||||
'optin' => 'Newsletter',
|
||||
'status' => 'Statut',
|
||||
'created' => 'Créé le',
|
||||
],
|
||||
'saved' => [
|
||||
'title' => 'Listes enregistrées',
|
||||
'current' => 'Liste chargée',
|
||||
'name' => 'Nom',
|
||||
'name_placeholder' => 'p. ex. Newsletter DE clients',
|
||||
'save_new' => 'Enregistrer comme nouvelle liste',
|
||||
'save_changes' => 'Enregistrer les modifications dans cette liste',
|
||||
'summary' => 'Filtres',
|
||||
'source' => 'Source',
|
||||
'last_export' => 'Dernier export',
|
||||
'last_count' => 'Adresses',
|
||||
'created_by' => 'Créé par',
|
||||
'created_at' => 'Créé le',
|
||||
'actions' => 'Actions',
|
||||
'load' => 'Charger les filtres',
|
||||
'delete' => 'Supprimer',
|
||||
'confirm_delete' => 'Supprimer vraiment cette liste ? Le journal des exports est conservé.',
|
||||
'stored' => 'Liste enregistrée.',
|
||||
'updated' => 'Liste mise à jour.',
|
||||
'deleted' => 'Liste supprimée.',
|
||||
'intro' => 'Combinaisons de filtres enregistrées. « Charger les filtres » reprend la liste dans l’aperçu ; l’export utilise les filtres enregistrés.',
|
||||
],
|
||||
'exports' => [
|
||||
'title' => 'Journal des exports',
|
||||
'intro' => 'Chaque téléchargement est consigné avec l’utilisateur, l’heure, les filtres et le nombre d’adresses.',
|
||||
'date' => 'Date',
|
||||
'user' => 'Utilisateur',
|
||||
'list' => 'Liste',
|
||||
'adhoc' => 'sans liste enregistrée',
|
||||
'summary' => 'Filtres',
|
||||
'format' => 'Format',
|
||||
'rows' => 'Adresses',
|
||||
'filename' => 'Fichier',
|
||||
],
|
||||
'validation' => [
|
||||
'source' => 'Source invalide.',
|
||||
'option' => 'Valeur de filtre invalide.',
|
||||
'member' => 'Sélection de conseiller invalide.',
|
||||
'date' => 'Date invalide.',
|
||||
'name' => 'Veuillez saisir un nom (120 caractères max.).',
|
||||
'list' => 'Liste introuvable.',
|
||||
'date_range' => 'La date de fin ne peut pas précéder la date de début.',
|
||||
],
|
||||
];
|
||||
51
resources/views/admin/newsletter/exports.blade.php
Normal file
51
resources/views/admin/newsletter/exports.blade.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
@extends('layouts.layout-2')
|
||||
|
||||
@section('content')
|
||||
|
||||
<h4 class="font-weight-bold py-2 mb-2">
|
||||
{{ __('newsletter.exports.title') }}
|
||||
</h4>
|
||||
<p class="text-muted">{{ __('newsletter.exports.intro') }}</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-datatable table-responsive">
|
||||
<table class="table table-striped table-bordered" id="datatables-newsletter-exports">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('newsletter.exports.date') }}</th>
|
||||
<th>{{ __('newsletter.exports.user') }}</th>
|
||||
<th>{{ __('newsletter.exports.list') }}</th>
|
||||
<th>{{ __('newsletter.exports.summary') }}</th>
|
||||
<th>{{ __('newsletter.exports.format') }}</th>
|
||||
<th>{{ __('newsletter.exports.rows') }}</th>
|
||||
<th>{{ __('newsletter.exports.filename') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#datatables-newsletter-exports').DataTable({
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
ajax: { url: '{!! route('admin_newsletter_lists_exports_datatable') !!}' },
|
||||
order: [[0, 'desc']],
|
||||
columns: [
|
||||
{ data: 'created_at', name: 'created_at', searchable: false },
|
||||
{ data: 'user', name: 'user', orderable: false, searchable: false },
|
||||
{ data: 'list', name: 'list', orderable: false, searchable: false },
|
||||
{ data: 'summary', name: 'summary', orderable: false, searchable: false },
|
||||
{ data: 'format', name: 'format' },
|
||||
{ data: 'row_count', name: 'row_count', searchable: false },
|
||||
{ data: 'filename', name: 'filename' }
|
||||
],
|
||||
bLengthChange: false,
|
||||
iDisplayLength: 50,
|
||||
language: { url: '/js/datatables-{{ \App::getLocale() }}.json' }
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
304
resources/views/admin/newsletter/index.blade.php
Normal file
304
resources/views/admin/newsletter/index.blade.php
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
@extends('layouts.layout-2')
|
||||
|
||||
@section('content')
|
||||
|
||||
<h4 class="font-weight-bold py-2 mb-2">
|
||||
{{ __('newsletter.title') }}
|
||||
</h4>
|
||||
<p class="text-muted">{{ __('newsletter.intro') }}</p>
|
||||
|
||||
@if($list)
|
||||
<div class="alert alert-secondary py-2 d-flex align-items-center justify-content-between flex-wrap">
|
||||
<div>
|
||||
<strong>{{ __('newsletter.saved.current') }}:</strong> {{ $list->name }}
|
||||
<span class="text-muted small ml-2">
|
||||
@if($list->last_exported_at)
|
||||
{{ __('newsletter.saved.last_export') }}: {{ $list->last_exported_at->format('d.m.Y H:i') }} ({{ $list->last_row_count }} {{ __('newsletter.saved.last_count') }})
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
<a href="{{ route('admin_newsletter_lists_saved') }}" class="btn btn-sm btn-outline-secondary">{{ __('newsletter.saved.title') }}</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<form id="newsletter-filter" method="get" action="{{ route('admin_newsletter_lists') }}" onsubmit="return false;">
|
||||
<input type="hidden" name="refresh" id="filter_refresh" value="0">
|
||||
<input type="hidden" name="list_id" value="{{ $list?->id }}">
|
||||
<div class="form-row">
|
||||
<div class="col-md-12 mb-3">
|
||||
<label class="form-label d-block">{{ __('newsletter.filter.source') }}</label>
|
||||
@foreach($sources as $source)
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" class="custom-control-input" id="source_{{ $source }}" name="source" value="{{ $source }}" @if($filter['source'] === $source) checked @endif>
|
||||
<label class="custom-control-label" for="source_{{ $source }}">{{ __('newsletter.source_option.'.$source) }}</label>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-md-2 mb-3">
|
||||
<label class="form-label" for="filter_language">{{ __('newsletter.filter.language') }}</label>
|
||||
<select class="custom-select" name="language" id="filter_language">
|
||||
<option value="" @if($filter['language'] === '') selected @endif>{{ __('newsletter.filter.all') }}</option>
|
||||
@foreach($languages as $language)
|
||||
<option value="{{ $language }}" @if($filter['language'] === $language) selected @endif>{{ strtoupper($language) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label" for="filter_country_ids">{{ __('newsletter.filter.country') }}</label>
|
||||
<select class="selectpicker form-control" data-style="btn-default" data-live-search="true" data-actions-box="true" multiple name="country_ids[]" id="filter_country_ids" title="{{ __('newsletter.filter.all') }}">
|
||||
@foreach($countries as $country)
|
||||
<option value="{{ $country->id }}" @if(in_array($country->id, $filter['country_ids'], true)) selected @endif>{{ $country->code }} – {{ $country->getLocated() }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label" for="filter_optin">{{ __('newsletter.filter.optin') }}</label>
|
||||
<select class="custom-select" name="optin" id="filter_optin">
|
||||
@foreach($optin_options as $option)
|
||||
<option value="{{ $option }}" @if($filter['optin'] === $option) selected @endif>{{ __('newsletter.optin_option.'.$option) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label" for="filter_purchase">{{ __('newsletter.filter.purchase') }}</label>
|
||||
<select class="custom-select" name="purchase" id="filter_purchase">
|
||||
@foreach($purchase_options as $option)
|
||||
<option value="{{ $option }}" @if($filter['purchase'] === $option) selected @endif>{{ __('newsletter.purchase_option.'.$option) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label" for="filter_member_id">{{ __('newsletter.filter.member') }}</label>
|
||||
<select class="selectpicker form-control" data-style="btn-default" data-live-search="true" name="member_id" id="filter_member_id">
|
||||
<option value="" @if($filter['member_id'] === '') selected @endif>{{ __('newsletter.filter.all') }}</option>
|
||||
<option value="none" @if($filter['member_id'] === 'none') selected @endif>{{ __('newsletter.filter.none_member') }}</option>
|
||||
@foreach($members as $member)
|
||||
<option value="{{ $member->id }}" @if((string) $filter['member_id'] === (string) $member->id) selected @endif>{{ $member->getFullName() }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<label class="form-label" for="filter_created_from">{{ __('newsletter.filter.created_from') }}</label>
|
||||
<input type="date" class="form-control" name="created_from" id="filter_created_from" value="{{ $filter['created_from'] }}">
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<label class="form-label" for="filter_created_to">{{ __('newsletter.filter.created_to') }}</label>
|
||||
<input type="date" class="form-control" name="created_to" id="filter_created_to" value="{{ $filter['created_to'] }}">
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label" for="filter_company">{{ __('newsletter.filter.company') }}</label>
|
||||
<select class="custom-select" name="company" id="filter_company">
|
||||
@foreach($company_options as $option)
|
||||
<option value="{{ $option }}" @if($filter['company'] === $option) selected @endif>{{ __('newsletter.company_option.'.$option) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="consultant-filters" class="border-top pt-3 mt-1" @if($filter['source'] === 'customers') style="display:none" @endif>
|
||||
<div class="small font-weight-semibold text-muted mb-2">{{ __('newsletter.filter.consultant_section') }}</div>
|
||||
<div class="form-row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label" for="filter_consultant_status">{{ __('newsletter.filter.consultant_status') }}</label>
|
||||
<select class="custom-select" name="consultant_status" id="filter_consultant_status">
|
||||
@foreach($consultant_status_options as $option)
|
||||
<option value="{{ $option }}" @if($filter['consultant_status'] === $option) selected @endif>{{ __('newsletter.consultant_status_option.'.$option) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label" for="filter_level_ids">{{ __('newsletter.filter.level') }}</label>
|
||||
<select class="selectpicker form-control" data-style="btn-default" data-actions-box="true" multiple name="level_ids[]" id="filter_level_ids" title="{{ __('newsletter.filter.all') }}">
|
||||
@foreach($levels as $level)
|
||||
<option value="{{ $level->id }}" @if(in_array($level->id, $filter['level_ids'], true)) selected @endif>{{ $level->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3 pt-4">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" name="include_admins" id="filter_include_admins" value="1" @if($filter['include_admins']) checked @endif>
|
||||
<label class="custom-control-label" for="filter_include_admins">{{ __('newsletter.filter.include_admins') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row align-items-center border-top pt-3">
|
||||
<div class="col-md-8 mb-2">
|
||||
<button type="button" class="btn btn-primary" id="filter_apply"><span class="fa fa-sync"></span> {{ __('newsletter.filter.refresh') }}</button>
|
||||
<a href="{{ route('admin_newsletter_lists') }}?reset=filter" class="btn btn-outline-dark">{{ __('newsletter.filter.reset') }}</a>
|
||||
</div>
|
||||
<div class="col-md-4 mb-2 text-right">
|
||||
<div class="custom-control custom-checkbox d-inline-block mr-3">
|
||||
<input type="checkbox" class="custom-control-input" name="template_only" id="filter_template_only" value="1" @if($filter['template_only']) checked @endif>
|
||||
<label class="custom-control-label" for="filter_template_only">{{ __('newsletter.export.template_only') }}</label>
|
||||
</div>
|
||||
<a href="#" class="btn btn-success export-link" data-format="xlsx"><span class="fa fa-file-excel"></span> {{ __('newsletter.export.button_xlsx') }}</a>
|
||||
<a href="#" class="btn btn-outline-success export-link" data-format="csv"><span class="fa fa-file-csv"></span> {{ __('newsletter.export.button_csv') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="newsletter-save-form" method="post" action="{{ route('admin_newsletter_lists_store') }}" class="form-row align-items-center border-top pt-3 mt-2">
|
||||
@csrf
|
||||
<div id="newsletter-save-fields"></div>
|
||||
<div class="col-md-5 mb-2">
|
||||
<input type="text" class="form-control" name="name" maxlength="120" required placeholder="{{ __('newsletter.saved.name_placeholder') }}" value="{{ old('name', $list?->name) }}">
|
||||
@error('name')<div class="text-danger small">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-7 mb-2">
|
||||
@if($list)
|
||||
<button type="submit" class="btn btn-outline-primary" data-action="{{ route('admin_newsletter_lists_update', [$list->id]) }}"><span class="fa fa-save"></span> {{ __('newsletter.saved.save_changes') }}</button>
|
||||
@endif
|
||||
<button type="submit" class="btn btn-outline-primary" data-action="{{ route('admin_newsletter_lists_store') }}"><span class="fa fa-plus"></span> {{ __('newsletter.saved.save_new') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card-body py-2">
|
||||
<div class="alert alert-warning py-2 mb-2" id="hint_optin_all" @if($filter['optin'] !== 'all') style="display:none" @endif>{{ __('newsletter.hint.optin_all') }}</div>
|
||||
<div class="alert alert-info py-2 mb-2" id="hint_consultants" @if($filter['source'] === 'customers') style="display:none" @endif>{{ __('newsletter.hint.consultants_contract') }}</div>
|
||||
<div id="newsletter-stats" class="text-muted small">{{ __('newsletter.stats.loading') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-datatable table-responsive">
|
||||
<table class="table table-striped table-bordered" id="datatables-newsletter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('newsletter.table.email') }}</th>
|
||||
<th>{{ __('newsletter.table.name') }}</th>
|
||||
<th>{{ __('newsletter.table.salutation') }}</th>
|
||||
<th>{{ __('newsletter.table.zipcode') }}</th>
|
||||
<th>{{ __('newsletter.table.city') }}</th>
|
||||
<th>{{ __('newsletter.table.country') }}</th>
|
||||
<th>{{ __('newsletter.table.language') }}</th>
|
||||
<th>{{ __('newsletter.table.consultant') }}</th>
|
||||
<th>{{ __('newsletter.table.source') }}</th>
|
||||
<th>{{ __('newsletter.table.optin') }}</th>
|
||||
<th>{{ __('newsletter.table.status') }}</th>
|
||||
<th>{{ __('newsletter.table.created') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var form = $('#newsletter-filter');
|
||||
var statsLabels = {
|
||||
total: @json(__('newsletter.stats.total')),
|
||||
customers: @json(__('newsletter.stats.customers')),
|
||||
consultants: @json(__('newsletter.stats.consultants')),
|
||||
duplicates: @json(__('newsletter.stats.duplicates')),
|
||||
invalid: @json(__('newsletter.stats.invalid'))
|
||||
};
|
||||
|
||||
function filterParams(d) {
|
||||
d = d || {};
|
||||
form.serializeArray().forEach(function (field) {
|
||||
if (field.name.slice(-2) === '[]') {
|
||||
var key = field.name.slice(0, -2);
|
||||
(d[key] = d[key] || []).push(field.value);
|
||||
} else {
|
||||
d[field.name] = field.value;
|
||||
}
|
||||
});
|
||||
return d;
|
||||
}
|
||||
|
||||
var oTable = $('#datatables-newsletter').DataTable({
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
ajax: {
|
||||
url: '{!! route('admin_newsletter_lists_datatable') !!}',
|
||||
data: function (d) {
|
||||
filterParams(d);
|
||||
}
|
||||
},
|
||||
order: [[0, 'asc']],
|
||||
columns: [
|
||||
{ data: 'email', name: 'email' },
|
||||
{ data: 'name', name: 'name', orderable: false },
|
||||
{ data: 'salutation_label', name: 'salutation_label' },
|
||||
{ data: 'zipcode', name: 'zipcode' },
|
||||
{ data: 'city', name: 'city' },
|
||||
{ data: 'country', name: 'country' },
|
||||
{ data: 'language', name: 'language' },
|
||||
{ data: 'consultant', name: 'consultant' },
|
||||
{ data: 'source_label', name: 'source_label' },
|
||||
{ data: 'optin_label', name: 'optin_label' },
|
||||
{ data: 'status_label', name: 'status_label' },
|
||||
{ data: 'created_at', name: 'created_at' }
|
||||
],
|
||||
bLengthChange: false,
|
||||
iDisplayLength: 50,
|
||||
language: {
|
||||
url: '/js/datatables-{{ \App::getLocale() }}.json'
|
||||
}
|
||||
});
|
||||
|
||||
$('#datatables-newsletter').on('xhr.dt', function (e, settings, json) {
|
||||
$('#filter_refresh').val('0');
|
||||
if (!json || !json.stats) {
|
||||
return;
|
||||
}
|
||||
var s = json.stats;
|
||||
$('#newsletter-stats').html(
|
||||
'<strong>' + s.total + '</strong> ' + statsLabels.total +
|
||||
' · ' + statsLabels.customers + ': ' + s.customers +
|
||||
' · ' + statsLabels.consultants + ': ' + s.consultants +
|
||||
' · ' + statsLabels.duplicates + ': ' + s.duplicates +
|
||||
' · ' + statsLabels.invalid + ': ' + s.invalid
|
||||
);
|
||||
});
|
||||
|
||||
function syncHints() {
|
||||
var source = form.find('input[name=source]:checked').val();
|
||||
$('#consultant-filters').toggle(source !== 'customers');
|
||||
$('#hint_consultants').toggle(source !== 'customers');
|
||||
$('#hint_optin_all').toggle($('#filter_optin').val() === 'all' && source !== 'consultants');
|
||||
}
|
||||
|
||||
$('#filter_apply').on('click', function () {
|
||||
$('#filter_refresh').val('1');
|
||||
syncHints();
|
||||
oTable.draw();
|
||||
});
|
||||
|
||||
form.find('select, input[type=date], input[type=checkbox], input[type=radio]').on('change', function () {
|
||||
syncHints();
|
||||
oTable.draw();
|
||||
});
|
||||
|
||||
$('#newsletter-save-form button[type=submit]').on('click', function () {
|
||||
$('#newsletter-save-form').attr('action', $(this).data('action'));
|
||||
});
|
||||
|
||||
$('#newsletter-save-form').on('submit', function () {
|
||||
var fields = $('#newsletter-save-fields').empty();
|
||||
form.serializeArray().forEach(function (field) {
|
||||
if (field.name === 'refresh' || field.name === 'list_id') {
|
||||
return;
|
||||
}
|
||||
fields.append($('<input>', { type: 'hidden', name: field.name, value: field.value }));
|
||||
});
|
||||
});
|
||||
|
||||
$('.export-link').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
var base = '{!! route('admin_newsletter_lists_export', ['format' => '__FORMAT__']) !!}'.replace('__FORMAT__', $(this).data('format'));
|
||||
window.location.href = base + '?' + $.param(filterParams({}));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
56
resources/views/admin/newsletter/saved.blade.php
Normal file
56
resources/views/admin/newsletter/saved.blade.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
@extends('layouts.layout-2')
|
||||
|
||||
@section('content')
|
||||
|
||||
<h4 class="font-weight-bold py-2 mb-2">
|
||||
{{ __('newsletter.saved.title') }}
|
||||
</h4>
|
||||
<p class="text-muted">{{ __('newsletter.saved.intro') }}</p>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<a href="{{ route('admin_newsletter_lists') }}" class="btn btn-sm btn-primary"><span class="fa fa-plus"></span> {{ __('navigation.newsletter_create') }}</a>
|
||||
</div>
|
||||
<div class="card-datatable table-responsive">
|
||||
<table class="table table-striped table-bordered" id="datatables-newsletter-saved">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('newsletter.saved.name') }}</th>
|
||||
<th>{{ __('newsletter.saved.source') }}</th>
|
||||
<th>{{ __('newsletter.saved.summary') }}</th>
|
||||
<th>{{ __('newsletter.saved.last_export') }}</th>
|
||||
<th>{{ __('newsletter.saved.last_count') }}</th>
|
||||
<th>{{ __('newsletter.saved.created_by') }}</th>
|
||||
<th>{{ __('newsletter.saved.created_at') }}</th>
|
||||
<th>{{ __('newsletter.saved.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#datatables-newsletter-saved').DataTable({
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
ajax: { url: '{!! route('admin_newsletter_lists_saved_datatable') !!}' },
|
||||
order: [[0, 'asc']],
|
||||
columns: [
|
||||
{ data: 'name', name: 'name' },
|
||||
{ data: 'source', name: 'source' },
|
||||
{ data: 'summary', name: 'summary', orderable: false, searchable: false },
|
||||
{ data: 'last_exported_at', name: 'last_exported_at', searchable: false },
|
||||
{ data: 'last_row_count', name: 'last_row_count', searchable: false },
|
||||
{ data: 'creator', name: 'creator', orderable: false, searchable: false },
|
||||
{ data: 'created_at', name: 'created_at', searchable: false },
|
||||
{ data: 'actions', name: 'actions', orderable: false, searchable: false }
|
||||
],
|
||||
bLengthChange: false,
|
||||
iDisplayLength: 50,
|
||||
language: { url: '/js/datatables-{{ \App::getLocale() }}.json' }
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
|
@ -326,6 +326,32 @@
|
|||
<div>{{ __('navigation.clients') }}</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidenav-item @if (Request::is('admin/newsletter-lists*')) open @endif">
|
||||
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
|
||||
<i class="sidenav-icon ion ion-md-mail"></i>
|
||||
<div>{{ __('navigation.newsletter_lists') }}</div>
|
||||
</a>
|
||||
<ul class="sidenav-menu">
|
||||
<li class="sidenav-item{{ Request::is('admin/newsletter-lists', 'admin/newsletter-lists/load/*') ? ' active' : '' }}">
|
||||
<a href="{{ route('admin_newsletter_lists') }}" class="sidenav-link"><i
|
||||
class="sidenav-icon ion ion-md-funnel"></i>
|
||||
<div>{{ __('navigation.newsletter_create') }}</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidenav-item{{ Request::is('admin/newsletter-lists/saved*') ? ' active' : '' }}">
|
||||
<a href="{{ route('admin_newsletter_lists_saved') }}" class="sidenav-link"><i
|
||||
class="sidenav-icon ion ion-md-bookmarks"></i>
|
||||
<div>{{ __('navigation.newsletter_saved') }}</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidenav-item{{ Request::is('admin/newsletter-lists/exports*') ? ' active' : '' }}">
|
||||
<a href="{{ route('admin_newsletter_lists_exports') }}" class="sidenav-link"><i
|
||||
class="sidenav-icon ion ion-md-list-box"></i>
|
||||
<div>{{ __('navigation.newsletter_exports') }}</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="sidenav-item @if (Request::is('admin/sales/*', 'admin/abos', 'admin/abos/*', 'admin/withdrawals', 'admin/withdrawals/*')) open @endif">
|
||||
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
|
||||
<i class="sidenav-icon ion ion-ios-cart"></i>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue