marketing.blade.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. @extends('admin.table_layouts')
  2. @push('css')
  3. <link href="/assets/global/vendor/summernote/summernote-bs4.min.css" rel="stylesheet">
  4. <link href="/assets/global/vendor/bootstrap-tokenfield/bootstrap-tokenfield.min.css" rel="stylesheet">
  5. <link href="/assets/global/vendor/bootstrap-datepicker/bootstrap-datepicker.min.css" rel="stylesheet">
  6. <link href="/assets/global/vendor/bootstrap-markdown/bootstrap-markdown.min.css" rel="stylesheet">
  7. @endpush
  8. @section('content')
  9. <div class="page-content container-fluid">
  10. <x-admin.table-panel :title="trans('admin.menu.customer_service.marketing')" :theads="[
  11. '#',
  12. ucfirst(trans('validation.attributes.title')),
  13. trans('admin.marketing.send_status'),
  14. trans('admin.marketing.send_time'),
  15. trans('admin.marketing.error_message'),
  16. trans('common.action'),
  17. ]" :count="trans('admin.marketing.counts', ['num' => $marketingMessages->total()])" :pagination="$marketingMessages->links()">
  18. <x-slot:actions>
  19. @can('admin.marketing.email')
  20. <button class="btn btn-primary" data-toggle="modal" data-target="#send_email_modal" type="button">
  21. <i class="fa-solid fa-envelope"></i> {{ trans('admin.marketing.email_send') }}</button>
  22. @endcan
  23. @can('admin.marketing.push')
  24. <button class="btn btn-primary" data-toggle="modal" data-target="#send_push_modal" type="button" disabled>
  25. <i class="fa-solid fa-bell"></i> {{ trans('admin.marketing.push_send') }}</button>
  26. @endcan
  27. </x-slot:actions>
  28. <x-slot:filters>
  29. <x-admin.filter.selectpicker class="col-xxl-1 col-xl-2 col-lg-3 col-md-4 col-sm-6" name="status" :title="trans('common.status.attribute')" :options="[-1 => trans('common.failed'), 0 => trans('common.to_be_send'), 1 => trans('common.success')]" />
  30. </x-slot:filters>
  31. <x-slot:tbody>
  32. @foreach ($marketingMessages as $message)
  33. <tr>
  34. <td> {{ $message->id }} </td>
  35. <td> {{ $message->title }} </td>
  36. <td> {{ $message->status_label }} </td>
  37. <td> {{ $message->created_at }} </td>
  38. <td> {{ $message->error }} </td>
  39. <td>
  40. <a class="btn btn-primary" data-toggle="collapse" href="#marketing_{{ $loop->iteration }}" aria-expanded="false"
  41. aria-controls="marketing_{{ $loop->iteration }}">{{ trans('common.view') }}</a>
  42. @if ($message->type === 1)
  43. <div class="collapse" id="marketing_{{ $loop->iteration }}">{!! $message->content !!}</div>
  44. @else
  45. <div class="collapse" id="marketing_{{ $loop->iteration }}">
  46. <div class="markdown-content" data-markdown="{{ $message->content }}" data-rendered="false"></div>
  47. </div>
  48. @endif
  49. </td>
  50. </tr>
  51. @endforeach
  52. </x-slot:tbody>
  53. </x-admin.table-panel>
  54. </div>
  55. @can('admin.marketing.email')
  56. <div class="modal fade" id="send_email_modal" data-focus-on="input:first" data-backdrop="static" data-keyboard="false" tabindex="-1">
  57. <div class="modal-dialog modal-lg modal-center">
  58. <div class="modal-content">
  59. <div class="modal-header">
  60. <button class="close" data-dismiss="modal" type="button" aria-label="{{ trans('common.close') }}">
  61. <span aria-hidden="true">×</span>
  62. </button>
  63. <h4 class="modal-title">
  64. <i class="icon fa-solid fa-envelopes-bulk"></i>{{ trans('admin.marketing.email_send') }}
  65. </h4>
  66. </div>
  67. <div class="modal-body">
  68. <div class="alert alert-info">
  69. <p class="font-size-18">
  70. <i class="icon fa-solid fa-users-viewfinder"></i> {{ trans('admin.marketing.email.targeted_users_count') }}
  71. <code class="ml-5" id="statistics"></code>
  72. </p>
  73. </div>
  74. <h5>{{ trans('admin.marketing.email.filters') }}</h5>
  75. <form class="form-row" id="filter-form">
  76. <div class="form-group col-lg-3 col-md-4 col-6">
  77. <input class="form-control" name="id" data-plugin="tokenfield" type="text" placeholder="{{ trans('model.user.id') }}" />
  78. </div>
  79. <div class="form-group col-lg-3 col-md-4 col-6">
  80. <input class="form-control" name="username" data-plugin="tokenfield" type="text" placeholder="{{ trans('model.user.username') }}" />
  81. </div>
  82. <div class="form-group col-lg-3 col-md-4 col-6">
  83. <div class="input-group">
  84. <div class="input-group-prepend">
  85. <span class="input-group-text"><i class="fa-solid fa-calendar-minus"></i></span>
  86. </div>
  87. <input class="form-control" name="expire_start" data-plugin="datepicker" type="text"
  88. placeholder="{{ trans('admin.marketing.email.expire_start') }}" autocomplete="off" />
  89. </div>
  90. </div>
  91. <div class="form-group col-lg-3 col-md-4 col-6">
  92. <div class="input-group">
  93. <div class="input-group-prepend">
  94. <span class="input-group-text"><i class="fa-solid fa-calendar-plus"></i></span>
  95. </div>
  96. <input class="form-control" name="expire_end" data-plugin="datepicker" type="text"
  97. placeholder="{{ trans('admin.marketing.email.expire_end') }}" autocomplete="off" />
  98. </div>
  99. </div>
  100. <div class="form-group col-lg-3 col-md-4 col-6">
  101. <div class="input-group">
  102. <input class="form-control" name="traffic" type="number" min="0" max="100"
  103. placeholder="{{ trans('admin.marketing.email.traffic_usage_over') }}" />
  104. <div class="input-group-append">
  105. <span class="input-group-text">%</span>
  106. </div>
  107. </div>
  108. </div>
  109. <div class="form-group col-lg-3 col-md-4 col-6">
  110. <div class="input-group">
  111. <input class="form-control" name="lastAlive" type="number" min="1"
  112. placeholder="{{ trans('admin.marketing.email.recently_active') }}" />
  113. <div class="input-group-append">
  114. <span class="input-group-text">{{ ucfirst(trans('validation.attributes.minute')) }}</span>
  115. </div>
  116. </div>
  117. </div>
  118. <div class="form-group col-auto">
  119. <div class="checkbox-custom checkbox-primary">
  120. <input id="paying" name="paying" type="checkbox" />
  121. <label for="paying">{{ trans('admin.marketing.email.paid_servicing') }}</label>
  122. </div>
  123. </div>
  124. <div class="form-group col-auto">
  125. <div class="checkbox-custom checkbox-primary">
  126. <input id="notPaying" name="notPaying" type="checkbox" />
  127. <label for="notPaying">{{ trans('admin.marketing.email.previously_paid') }}</label>
  128. </div>
  129. </div>
  130. <div class="form-group col-auto">
  131. <div class="checkbox-custom checkbox-primary">
  132. <input id="paid" name="paid" type="checkbox" />
  133. <label for="paid">{{ trans('admin.marketing.email.ever_paid') }}</label>
  134. </div>
  135. </div>
  136. <div class="form-group col-auto">
  137. <div class="checkbox-custom checkbox-primary">
  138. <input id="neverPay" name="neverPay" type="checkbox" />
  139. <label for="neverPay">{{ trans('admin.marketing.email.never_paid') }}</label>
  140. </div>
  141. </div>
  142. <div class="form-group col-auto">
  143. <div class="checkbox-custom checkbox-primary">
  144. <input id="flowAbnormal" name="flowAbnormal" type="checkbox" />
  145. <label for="flowAbnormal">{{ trans('admin.marketing.email.recent_traffic_abnormal') }}</label>
  146. </div>
  147. </div>
  148. @if ($userGroups)
  149. <div class="form-group col-lg-3 col-md-4 col-6">
  150. <select class="form-control show-tick" name="user_group_id[]" data-plugin="selectpicker" data-style="btn-outline btn-primary"
  151. title="{{ trans('model.user_group.attribute') }}" multiple>
  152. @foreach ($userGroups as $key => $group)
  153. <option value="{{ $key }}">{{ $group }}</option>
  154. @endforeach
  155. </select>
  156. </div>
  157. @endif
  158. @if ($levels)
  159. <div class="form-group col-lg-3 col-md-4 col-6">
  160. <select class="form-control show-tick" name="level[]" data-plugin="selectpicker" data-style="btn-outline btn-primary"
  161. title="{{ trans('model.common.level') }}" multiple>
  162. @foreach ($levels as $key => $level)
  163. <option value="{{ $key }}">{{ $level }}</option>
  164. @endforeach
  165. </select>
  166. </div>
  167. @endif
  168. <div class="form-group col-lg-3 col-md-4 col-6">
  169. <select class="form-control show-tick" name="status[]" data-plugin="selectpicker" data-style="btn-outline btn-primary"
  170. title="{{ trans('model.user.account_status') }}" multiple>
  171. <option value="-1">{{ trans('common.status.banned') }}</option>
  172. <option value="0">{{ trans('common.status.inactive') }}</option>
  173. <option value="1">{{ trans('common.status.normal') }}</option>
  174. </select>
  175. </div>
  176. <div class="form-group col-lg-3 col-md-4 col-6">
  177. <select class="form-control" name="enable" data-plugin="selectpicker" data-style="btn-outline btn-primary"
  178. title="{{ trans('model.user.proxy_status') }}">
  179. <option value="1">{{ trans('common.status.enabled') }}</option>
  180. <option value="0">{{ trans('common.status.banned') }}</option>
  181. </select>
  182. </div>
  183. <div class="form-group col-lg-3 col-md-4 col-6">
  184. <button class="btn btn-primary" type="button" onclick="fetchStatistics()">{{ trans('admin.query') }}</button>
  185. <button class="btn btn-danger" type="button" onclick="resetFilterForm()">{{ trans('common.reset') }}</button>
  186. </div>
  187. </form>
  188. <div class="alert" id="msg" style="display: none;"></div>
  189. <form class="form-horizontal" id="send-email-form">
  190. <div class="form-body">
  191. <div class="form-group">
  192. <div class="row">
  193. <label class="col-md-1 control-label" for="title"> {{ ucfirst(trans('validation.attributes.title')) }} </label>
  194. <div class="col-md-8">
  195. <input class="form-control" id="title" name="title" type="text" />
  196. </div>
  197. </div>
  198. </div>
  199. <div class="form-group">
  200. <div class="row">
  201. <label class="col-md-1 control-label" for="content"> {{ ucfirst(trans('validation.attributes.content')) }} </label>
  202. <div class="col-md-11">
  203. <textarea class="form-control" id="content" name="content"></textarea>
  204. </div>
  205. </div>
  206. </div>
  207. </div>
  208. </form>
  209. </div>
  210. <div class="modal-footer">
  211. <button class="btn btn-danger mr-auto" data-dismiss="modal">{{ trans('common.cancel') }}</button>
  212. <button class="btn btn-primary" type="button" onclick="sendEmail()">{{ trans('common.send') }}</button>
  213. </div>
  214. </div>
  215. </div>
  216. </div>
  217. @endcan
  218. @can('admin.marketing.push')
  219. <div class="modal fade" id="send_push_modal" data-focus-on="input:first" data-backdrop="static" data-keyboard="false" tabindex="-1">
  220. <div class="modal-dialog modal-lg modal-center">
  221. <div class="modal-content">
  222. <div class="modal-header">
  223. <button class="close" data-dismiss="modal" type="button" aria-label="{{ trans('common.close') }}">
  224. <span aria-hidden="true">×</span>
  225. </button>
  226. <h4 class="modal-title">{{ trans('admin.marketing.push_send') }}</h4>
  227. </div>
  228. <div class="modal-body">
  229. <div class="alert alert-danger" id="msg" style="display: none;"></div>
  230. <form class="form-horizontal" action="#" method="post">
  231. <div class="form-body">
  232. <div class="form-group">
  233. <div class="row">
  234. <label class="col-md-2 control-label" for="title"> {{ ucfirst(trans('validation.attributes.title')) }} </label>
  235. <div class="col-md-6">
  236. <input class="form-control" id="title" name="title" type="text" />
  237. </div>
  238. </div>
  239. </div>
  240. <div class="form-group">
  241. <div class="row">
  242. <label class="col-md-2 control-label" for="content"> {{ ucfirst(trans('validation.attributes.content')) }} </label>
  243. <div class="col-md-9">
  244. <textarea class="form-control" id="content" name="content" data-provide="markdown" data-iconlibrary="fa" rows="10"></textarea>
  245. </div>
  246. </div>
  247. </div>
  248. </div>
  249. </form>
  250. </div>
  251. <div class="modal-footer">
  252. <button class="btn btn-danger mr-auto" data-dismiss="modal">{{ trans('common.cancel') }}</button>
  253. <button class="btn btn-primary disabled" type="button" onclick="sendPush()">{{ trans('common.send') }}</button>
  254. </div>
  255. </div>
  256. </div>
  257. </div>
  258. @endcan
  259. @endsection
  260. @push('javascript')
  261. <script src="/assets/global/vendor/summernote/summernote-bs4.min.js"></script>
  262. <script src="/assets/global/vendor/bootstrap-datepicker/bootstrap-datepicker.min.js"></script>
  263. @if (app()->getLocale() !== 'en')
  264. <script src="/assets/global/vendor/summernote/lang/summernote-{{ config('common.language')[app()->getLocale()][2] }}.min.js"></script>
  265. <script src="/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.{{ str_replace('_', '-', app()->getLocale()) }}.min.js" charset="UTF-8">
  266. </script>
  267. @endif
  268. <script src="/assets/global/vendor/bootstrap-tokenfield/bootstrap-tokenfield.min.js"></script>
  269. <script src="/assets/global/vendor/bootstrap-markdown/bootstrap-markdown.min.js"></script>
  270. <script src="/assets/global/vendor/marked/marked.min.js"></script>
  271. <script src="/assets/global/js/Plugin/bootstrap-tokenfield.js"></script>
  272. <script src="/assets/global/js/Plugin/bootstrap-datepicker.js"></script>
  273. <script>
  274. function renderMarkdown(element) {
  275. var markdownText = $(element).data("markdown");
  276. var htmlContent = marked(markdownText);
  277. $(element).html(htmlContent);
  278. $(element).data("rendered", true); // Mark as rendered
  279. }
  280. $(document).ready(function() {
  281. // Render Markdown content when collapse is shown
  282. $(".collapse").on("shown.bs.collapse", function() {
  283. if ($(this).find(".markdown-content").length > 0 && $(this).find(".markdown-content").data("rendered") === false) {
  284. renderMarkdown($(this).find(".markdown-content"));
  285. }
  286. });
  287. @can('admin.marketing.email')
  288. fetchStatistics();
  289. $("#content").summernote({
  290. tabsize: 2,
  291. height: 400,
  292. dialogsInBody: true,
  293. lang: '{{ config('common.language')[app()->getLocale()][2] }}' // default: 'en-US'
  294. });
  295. @endcan
  296. });
  297. @can('admin.marketing.email')
  298. function resetFilterForm() {
  299. const form = $("#filter-form");
  300. form[0].reset();
  301. form.find("select").selectpicker("refresh");
  302. form.find("[data-plugin=\"tokenfield\"]").each(function() {
  303. $(this).tokenfield("setTokens", []);
  304. });
  305. fetchStatistics();
  306. }
  307. function fetchStatistics() {
  308. ajaxGet('{{ route('admin.marketing.create', ['type' => 'email']) }}',
  309. collectFormData("#filter-form", {
  310. removeEmpty: true
  311. }), {
  312. beforeSend: function() {
  313. $("#statistics").html('{{ trans('admin.marketing.email.loading_statistics') }}');
  314. },
  315. success: function(data) {
  316. $("#statistics").html(data.count);
  317. },
  318. error: function() {
  319. $("#statistics").html('<p>{{ trans('common.request_failed') }}</p>');
  320. }
  321. }
  322. );
  323. }
  324. function sendEmail() {
  325. const filterData = collectFormData("#filter-form", {
  326. removeEmpty: true
  327. });
  328. const emailData = collectFormData("#send-email-form");
  329. ajaxPost(
  330. '{{ route('admin.marketing.create', ['type' => 'email']) }}?' + $.param(filterData),
  331. emailData, {
  332. beforeSend: function() {
  333. $("#msg")
  334. .show()
  335. .removeClass("alert-danger alert-success")
  336. .addClass("alert-info")
  337. .html('{{ trans('admin.creating') }}');
  338. },
  339. success: function(ret) {
  340. $("#msg")
  341. .show()
  342. .removeClass("alert-info alert-danger alert-success")
  343. .addClass(ret.status === "success" ? "alert-success" : "alert-danger")
  344. .html(ret.message);
  345. },
  346. error: function() {
  347. $("#msg")
  348. .removeClass("alert-info alert-success")
  349. .addClass("alert-danger")
  350. .show()
  351. .html('{{ trans('common.request_failed') }}');
  352. }
  353. }
  354. );
  355. }
  356. @endcan
  357. @can('admin.marketing.push')
  358. function sendPush() {
  359. ajaxPost(
  360. '{{ route('admin.marketing.create', ['type' => 'push']) }}',
  361. collectFormData("#send_push_modal form"), {
  362. beforeSend: function() {
  363. $("#msg").show().html('{{ trans('admin.creating') }}');
  364. },
  365. success: function(ret) {
  366. if (ret.status === "fail") {
  367. $("#msg").show().html(ret.message);
  368. return;
  369. }
  370. $("#send_push_modal").modal("hide");
  371. },
  372. error: function() {
  373. $("#msg").show().html('{{ trans('common.request_failed') }}');
  374. }
  375. }
  376. );
  377. }
  378. @endcan
  379. </script>
  380. @endpush