testimonials 0.7.5 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/README.md +27 -6
  4. data/app/controllers/testimonials/collection_controller.rb +20 -6
  5. data/app/views/layouts/testimonials/application.html.erb +2 -7
  6. data/app/views/layouts/testimonials/collection.html.erb +2 -1
  7. data/app/views/testimonials/collection/nps.html.erb +14 -0
  8. data/app/views/testimonials/nps_responses/index.html.erb +12 -1
  9. data/app/views/testimonials/testimonials/index.html.erb +11 -1
  10. data/config/locales/testimonials.ar.yml +5 -2
  11. data/config/locales/testimonials.bg.yml +5 -2
  12. data/config/locales/testimonials.bn.yml +5 -2
  13. data/config/locales/testimonials.de.yml +5 -2
  14. data/config/locales/testimonials.el.yml +5 -2
  15. data/config/locales/testimonials.en.yml +5 -2
  16. data/config/locales/testimonials.es.yml +5 -2
  17. data/config/locales/testimonials.fr.yml +5 -2
  18. data/config/locales/testimonials.hi.yml +5 -2
  19. data/config/locales/testimonials.hr.yml +5 -2
  20. data/config/locales/testimonials.id.yml +5 -2
  21. data/config/locales/testimonials.it.yml +5 -2
  22. data/config/locales/testimonials.ja.yml +5 -2
  23. data/config/locales/testimonials.ko.yml +5 -2
  24. data/config/locales/testimonials.lb.yml +5 -2
  25. data/config/locales/testimonials.nl.yml +5 -2
  26. data/config/locales/testimonials.pl.yml +5 -2
  27. data/config/locales/testimonials.pt.yml +5 -2
  28. data/config/locales/testimonials.ro.yml +5 -2
  29. data/config/locales/testimonials.ru.yml +5 -2
  30. data/config/locales/testimonials.th.yml +5 -2
  31. data/config/locales/testimonials.tr.yml +5 -2
  32. data/config/locales/testimonials.uk.yml +5 -2
  33. data/config/locales/testimonials.ur.yml +5 -2
  34. data/config/locales/testimonials.vi.yml +5 -2
  35. data/config/locales/testimonials.zh-CN.yml +5 -2
  36. data/config/routes.rb +1 -0
  37. data/lib/generators/testimonials/install/install_generator.rb +1 -0
  38. data/lib/tasks/testimonials_tasks.rake +11 -0
  39. data/lib/testimonials/dashboard.css +6 -2
  40. data/lib/testimonials/demo_video.rb +82 -0
  41. data/lib/testimonials/engine.rb +4 -0
  42. data/lib/testimonials/seeds.rb +162 -0
  43. data/lib/testimonials/version.rb +1 -1
  44. data/lib/testimonials/widget.js +28 -9
  45. data/lib/testimonials.rb +1 -0
  46. metadata +5 -1
@@ -50,6 +50,8 @@ nl:
50
50
  page:
51
51
  title: "Deel je ervaring met %{app}"
52
52
  subtitle: "Jouw woorden helpen anderen beslissen — bedankt!"
53
+ nps_title: "Help ons %{app} te verbeteren"
54
+ nps_subtitle: "Eén korte vraag — het kost tien seconden."
53
55
  kinds:
54
56
  text: "Tekst"
55
57
  video: "Video"
@@ -57,8 +59,6 @@ nl:
57
59
  widget: "In-app widget"
58
60
  page: "Verzamelpagina"
59
61
  nps: "NPS-opvolging"
60
- collection_page: "Vraag om testimonials"
61
- download_video: "Video downloaden"
62
62
  statuses:
63
63
  pending: "In afwachting"
64
64
  approved: "Goedgekeurd"
@@ -66,6 +66,9 @@ nl:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "Om NPS-scores vragen"
70
+ collection_page: "Vraag om testimonials"
71
+ download_video: "Video downloaden"
69
72
  all_kinds: "Alle soorten"
70
73
  search: "Zoeken"
71
74
  search_placeholder: "Zoek tekst, naam, e-mail…"
@@ -50,6 +50,8 @@ pl:
50
50
  page:
51
51
  title: "Podziel się swoim doświadczeniem z %{app}"
52
52
  subtitle: "Twoje słowa pomagają innym podjąć decyzję — dziękujemy!"
53
+ nps_title: "Pomóż nam ulepszyć %{app}"
54
+ nps_subtitle: "Jedno krótkie pytanie — zajmie dziesięć sekund."
53
55
  kinds:
54
56
  text: "Tekst"
55
57
  video: "Wideo"
@@ -57,8 +59,6 @@ pl:
57
59
  widget: "Widżet w aplikacji"
58
60
  page: "Strona zbierania"
59
61
  nps: "Kontynuacja NPS"
60
- collection_page: "Poproś o opinie"
61
- download_video: "Pobierz wideo"
62
62
  statuses:
63
63
  pending: "Oczekujące"
64
64
  approved: "Zatwierdzone"
@@ -66,6 +66,9 @@ pl:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "Poproś o oceny NPS"
70
+ collection_page: "Poproś o opinie"
71
+ download_video: "Pobierz wideo"
69
72
  all_kinds: "Wszystkie rodzaje"
70
73
  search: "Szukaj"
71
74
  search_placeholder: "Szukaj tekstu, imienia, e-maila…"
@@ -50,6 +50,8 @@ pt:
50
50
  page:
51
51
  title: "Compartilhe sua experiência com %{app}"
52
52
  subtitle: "Suas palavras ajudam outras pessoas a decidir — obrigado!"
53
+ nps_title: "Ajude-nos a melhorar o %{app}"
54
+ nps_subtitle: "Uma pergunta rápida — leva dez segundos."
53
55
  kinds:
54
56
  text: "Texto"
55
57
  video: "Vídeo"
@@ -57,8 +59,6 @@ pt:
57
59
  widget: "Widget no app"
58
60
  page: "Página de coleta"
59
61
  nps: "Acompanhamento NPS"
60
- collection_page: "Pedir depoimentos"
61
- download_video: "Baixar o vídeo"
62
62
  statuses:
63
63
  pending: "Pendente"
64
64
  approved: "Aprovado"
@@ -66,6 +66,9 @@ pt:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "Pedir pontuações NPS"
70
+ collection_page: "Pedir depoimentos"
71
+ download_video: "Baixar o vídeo"
69
72
  all_kinds: "Todos os tipos"
70
73
  search: "Buscar"
71
74
  search_placeholder: "Buscar texto, nome, e-mail…"
@@ -50,6 +50,8 @@ ro:
50
50
  page:
51
51
  title: "Împărtășiți-vă experiența cu %{app}"
52
52
  subtitle: "Cuvintele dvs. îi ajută pe alții să decidă — mulțumim!"
53
+ nps_title: "Ajutați-ne să îmbunătățim %{app}"
54
+ nps_subtitle: "O întrebare rapidă — durează zece secunde."
53
55
  kinds:
54
56
  text: "Text"
55
57
  video: "Video"
@@ -57,8 +59,6 @@ ro:
57
59
  widget: "Widget în aplicație"
58
60
  page: "Pagină de colectare"
59
61
  nps: "Continuare NPS"
60
- collection_page: "Cere testimoniale"
61
- download_video: "Descarcă videoclipul"
62
62
  statuses:
63
63
  pending: "În așteptare"
64
64
  approved: "Aprobat"
@@ -66,6 +66,9 @@ ro:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "Cere scoruri NPS"
70
+ collection_page: "Cere testimoniale"
71
+ download_video: "Descarcă videoclipul"
69
72
  all_kinds: "Toate tipurile"
70
73
  search: "Căutare"
71
74
  search_placeholder: "Căutați text, nume, email…"
@@ -50,6 +50,8 @@ ru:
50
50
  page:
51
51
  title: "Поделитесь своим опытом с %{app}"
52
52
  subtitle: "Ваши слова помогают другим решиться — спасибо!"
53
+ nps_title: "Помогите нам улучшить %{app}"
54
+ nps_subtitle: "Один короткий вопрос — займёт десять секунд."
53
55
  kinds:
54
56
  text: "Текст"
55
57
  video: "Видео"
@@ -57,8 +59,6 @@ ru:
57
59
  widget: "Виджет в приложении"
58
60
  page: "Страница сбора"
59
61
  nps: "После NPS"
60
- collection_page: "Попросить отзывы"
61
- download_video: "Скачать видео"
62
62
  statuses:
63
63
  pending: "Ожидает"
64
64
  approved: "Одобрено"
@@ -66,6 +66,9 @@ ru:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "Запросить оценки NPS"
70
+ collection_page: "Попросить отзывы"
71
+ download_video: "Скачать видео"
69
72
  all_kinds: "Все типы"
70
73
  search: "Поиск"
71
74
  search_placeholder: "Искать текст, имя, почту…"
@@ -50,6 +50,8 @@ th:
50
50
  page:
51
51
  title: "แบ่งปันประสบการณ์ของคุณกับ %{app}"
52
52
  subtitle: "คำพูดของคุณช่วยให้คนอื่นตัดสินใจได้ — ขอบคุณ!"
53
+ nps_title: "ช่วยเราปรับปรุง %{app}"
54
+ nps_subtitle: "คำถามเดียวสั้น ๆ — ใช้เวลาสิบวินาที"
53
55
  kinds:
54
56
  text: "ข้อความ"
55
57
  video: "วิดีโอ"
@@ -57,8 +59,6 @@ th:
57
59
  widget: "วิดเจ็ตในแอป"
58
60
  page: "หน้ารวบรวม"
59
61
  nps: "ติดตามผล NPS"
60
- collection_page: "ขอคำรับรอง"
61
- download_video: "ดาวน์โหลดวิดีโอ"
62
62
  statuses:
63
63
  pending: "รอดำเนินการ"
64
64
  approved: "อนุมัติแล้ว"
@@ -66,6 +66,9 @@ th:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "ขอคะแนน NPS"
70
+ collection_page: "ขอคำรับรอง"
71
+ download_video: "ดาวน์โหลดวิดีโอ"
69
72
  all_kinds: "ทุกประเภท"
70
73
  search: "ค้นหา"
71
74
  search_placeholder: "ค้นหาข้อความ ชื่อ อีเมล…"
@@ -50,6 +50,8 @@ tr:
50
50
  page:
51
51
  title: "%{app} deneyiminizi paylaşın"
52
52
  subtitle: "Sözleriniz başkalarının karar vermesine yardımcı olur — teşekkürler!"
53
+ nps_title: "%{app} deneyimini iyileştirmemize yardım edin"
54
+ nps_subtitle: "Tek bir kısa soru — on saniye sürer."
53
55
  kinds:
54
56
  text: "Metin"
55
57
  video: "Video"
@@ -57,8 +59,6 @@ tr:
57
59
  widget: "Uygulama içi bileşen"
58
60
  page: "Toplama sayfası"
59
61
  nps: "NPS takibi"
60
- collection_page: "Görüş isteyin"
61
- download_video: "Videoyu indir"
62
62
  statuses:
63
63
  pending: "Beklemede"
64
64
  approved: "Onaylandı"
@@ -66,6 +66,9 @@ tr:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "NPS puanı isteyin"
70
+ collection_page: "Görüş isteyin"
71
+ download_video: "Videoyu indir"
69
72
  all_kinds: "Tüm türler"
70
73
  search: "Ara"
71
74
  search_placeholder: "Metin, ad, e-posta ara…"
@@ -50,6 +50,8 @@ uk:
50
50
  page:
51
51
  title: "Поділіться своїм досвідом із %{app}"
52
52
  subtitle: "Ваші слова допомагають іншим зважитися — дякуємо!"
53
+ nps_title: "Допоможіть нам покращити %{app}"
54
+ nps_subtitle: "Одне коротке питання — займе десять секунд."
53
55
  kinds:
54
56
  text: "Текст"
55
57
  video: "Відео"
@@ -57,8 +59,6 @@ uk:
57
59
  widget: "Віджет у застосунку"
58
60
  page: "Сторінка збору"
59
61
  nps: "Після NPS"
60
- collection_page: "Попросити відгуки"
61
- download_video: "Завантажити відео"
62
62
  statuses:
63
63
  pending: "Очікує"
64
64
  approved: "Схвалено"
@@ -66,6 +66,9 @@ uk:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "Запитати оцінки NPS"
70
+ collection_page: "Попросити відгуки"
71
+ download_video: "Завантажити відео"
69
72
  all_kinds: "Усі типи"
70
73
  search: "Пошук"
71
74
  search_placeholder: "Шукати текст, ім'я, пошту…"
@@ -50,6 +50,8 @@ ur:
50
50
  page:
51
51
  title: "%{app} کے ساتھ اپنا تجربہ شیئر کریں"
52
52
  subtitle: "آپ کے الفاظ دوسروں کو فیصلہ کرنے میں مدد دیتے ہیں — شکریہ!"
53
+ nps_title: "%{app} کو بہتر بنانے میں ہماری مدد کریں"
54
+ nps_subtitle: "ایک مختصر سوال — دس سیکنڈ لگیں گے۔"
53
55
  kinds:
54
56
  text: "متن"
55
57
  video: "ویڈیو"
@@ -57,8 +59,6 @@ ur:
57
59
  widget: "ایپ کے اندر ویجیٹ"
58
60
  page: "جمع کرنے کا صفحہ"
59
61
  nps: "NPS فالو اپ"
60
- collection_page: "تعریفی کلمات طلب کریں"
61
- download_video: "ویڈیو ڈاؤن لوڈ کریں"
62
62
  statuses:
63
63
  pending: "زیر التوا"
64
64
  approved: "منظور شدہ"
@@ -66,6 +66,9 @@ ur:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "NPS اسکور کی درخواست کریں"
70
+ collection_page: "تعریفی کلمات طلب کریں"
71
+ download_video: "ویڈیو ڈاؤن لوڈ کریں"
69
72
  all_kinds: "تمام اقسام"
70
73
  search: "تلاش"
71
74
  search_placeholder: "متن، نام، ای میل تلاش کریں…"
@@ -50,6 +50,8 @@ vi:
50
50
  page:
51
51
  title: "Chia sẻ trải nghiệm của bạn với %{app}"
52
52
  subtitle: "Lời của bạn giúp người khác quyết định — cảm ơn bạn!"
53
+ nps_title: "Giúp chúng tôi cải thiện %{app}"
54
+ nps_subtitle: "Một câu hỏi nhanh — chỉ mất mười giây."
53
55
  kinds:
54
56
  text: "Văn bản"
55
57
  video: "Video"
@@ -57,8 +59,6 @@ vi:
57
59
  widget: "Widget trong ứng dụng"
58
60
  page: "Trang thu thập"
59
61
  nps: "Tiếp nối NPS"
60
- collection_page: "Xin cảm nhận"
61
- download_video: "Tải video xuống"
62
62
  statuses:
63
63
  pending: "Đang chờ"
64
64
  approved: "Đã duyệt"
@@ -66,6 +66,9 @@ vi:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "Yêu cầu điểm NPS"
70
+ collection_page: "Xin cảm nhận"
71
+ download_video: "Tải video xuống"
69
72
  all_kinds: "Tất cả các loại"
70
73
  search: "Tìm kiếm"
71
74
  search_placeholder: "Tìm văn bản, tên, email…"
@@ -50,6 +50,8 @@ zh-CN:
50
50
  page:
51
51
  title: "分享您使用 %{app} 的体验"
52
52
  subtitle: "您的评价能帮助他人做出选择——谢谢!"
53
+ nps_title: "帮助我们改进 %{app}"
54
+ nps_subtitle: "只有一个问题 — 十秒即可完成。"
53
55
  kinds:
54
56
  text: "文字"
55
57
  video: "视频"
@@ -57,8 +59,6 @@ zh-CN:
57
59
  widget: "应用内组件"
58
60
  page: "收集页面"
59
61
  nps: "NPS 跟进"
60
- collection_page: "征集用户评价"
61
- download_video: "下载视频"
62
62
  statuses:
63
63
  pending: "待处理"
64
64
  approved: "已批准"
@@ -66,6 +66,9 @@ zh-CN:
66
66
  dashboard:
67
67
  title: "Testimonials"
68
68
  nps: "NPS"
69
+ nps_page: "邀请填写 NPS"
70
+ collection_page: "征集用户评价"
71
+ download_video: "下载视频"
69
72
  all_kinds: "所有类型"
70
73
  search: "搜索"
71
74
  search_placeholder: "搜索文字、姓名、邮箱…"
data/config/routes.rb CHANGED
@@ -7,6 +7,7 @@ Testimonials::Engine.routes.draw do
7
7
  get 'dashboard.js', to: 'widgets#dashboard', as: :dashboard_script
8
8
  get 'dashboard.css', to: 'widgets#dashboard_stylesheet', as: :dashboard_stylesheet
9
9
  get 'new', to: 'collection#show', as: :collection
10
+ get 'nps/new', to: 'collection#nps', as: :nps_collection
10
11
  resources :events, only: :create
11
12
  resource :nps, only: :create, controller: 'nps'
12
13
  resources :nps_responses, only: %i[index show]
@@ -29,6 +29,7 @@ module Testimonials
29
29
  say "\ntestimonials installed. Run `rails db:migrate`, then add", :green
30
30
  say '`<%= testimonials_tag %>` before </body> in your layout.'
31
31
  say 'Triage testimonials at /testimonials (development only until you set config.authorize_admin).'
32
+ say 'Optional: run `bin/rails testimonials:seed_demo` for sample testimonials and NPS.'
32
33
  say "Collect from outside the app via /testimonials/new.\n"
33
34
  end
34
35
 
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :testimonials do
4
+ desc 'Create or refresh testimonials demo data'
5
+ task seed_demo: :environment do
6
+ result = Testimonials::Seeds.load!
7
+ puts "Seeded #{result[:testimonials].size} testimonials, " \
8
+ "#{result[:nps_responses].size} NPS responses, and " \
9
+ "#{result[:prompt_events].size} prompt events."
10
+ end
11
+ end
@@ -20,6 +20,10 @@ a:hover { text-decoration: underline; }
20
20
  .container { max-width: 1020px; margin: 0 auto; padding: 24px 16px 64px; }
21
21
  h1 { font-size: 22px; margin: 0 0 16px; }
22
22
  h1.title-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
23
+ /* Index heading row: title on the left, the shareable public link on the right. */
24
+ .page-head { display: flex; align-items: center; justify-content: space-between;
25
+ gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
26
+ .page-head h1 { margin: 0; }
23
27
  .nav { display: flex; gap: 8px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
24
28
  .nav a { padding: 6px 16px; border: 1px solid var(--border); border-radius: 999px;
25
29
  background: var(--surface); color: var(--muted); font-weight: 600; }
@@ -99,9 +103,9 @@ img.avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; }
99
103
  .tm-index, .tm-nps-index { overflow: hidden; }
100
104
  .tm-index .container, .tm-nps-index .container { max-width: 1280px; display: flex; flex-direction: column; padding-bottom: 16px; }
101
105
  .tm-index .nav, .tm-nps-index .nav,
102
- .tm-index .container > h1, .tm-nps-index .container > h1,
106
+ .tm-index .container > .page-head, .tm-nps-index .container > .page-head,
103
107
  .tm-nps-index .stat-row { flex: 0 0 auto; }
104
- .tm-index .container > h1, .tm-nps-index .container > h1 { margin-bottom: 14px; }
108
+ .tm-index .container > .page-head, .tm-nps-index .container > .page-head { margin-bottom: 14px; }
105
109
  .tm-nps-index .stat-row { margin-bottom: 14px; }
106
110
  .dashboard-shell { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: minmax(330px, 430px) 1fr;
107
111
  gap: 16px; }
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Testimonials
4
+ # The demo video for `bin/rails testimonials:seed_demo`, written to Active
5
+ # Storage at seed time instead of shipped as a media file: three seconds of
6
+ # 320x180 H.264, embedded below as Base64 (~4 KB).
7
+ #
8
+ # It lives in a .rb rather than a .mp4 because the gemspec ships lib/**/*,
9
+ # so a bundled clip downloads with every install whether or not the app ever
10
+ # seeds. The 1.1 MB file this replaces was the whole difference between a
11
+ # 91 KB gem and a 1.0 MB one.
12
+ #
13
+ # Encoded once with ffmpeg (`gradients` filter, CRF 30). Seeding decodes it
14
+ # and needs no ffmpeg and no network.
15
+ module DemoVideo
16
+ FILENAME = 'testimonials-demo.mp4'
17
+ CONTENT_TYPE = 'video/mp4'
18
+
19
+ # unpack1('m') rather than Base64.decode64: base64 is a bundled gem from
20
+ # Ruby 3.4 on, and this needs no dependency.
21
+ DATA = <<~MP4
22
+ AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAATsbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAC7gAAQAA
23
+ AQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
24
+ AAAAAgAABBZ0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAC7gAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAA
25
+ AAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAUAAAAC0AAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAu4AAAIAAABAAAAAAOO
26
+ bWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAwAAAAkABVxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRl
27
+ b0hhbmRsZXIAAAADOW1pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAA
28
+ AQAAAvlzdGJsAAAAwXN0c2QAAAAAAAAAAQAAALFhdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAUAAtABIAAAASAAAAAAA
29
+ AAABFUxhdmM2Mi4yOC4xMDEgbGlieDI2NAAAAAAAAAAAAAAAGP//AAAAN2F2Y0MBZAAM/+EAGmdkAAys2UFBn58BEAAAAwAQ
30
+ AAADAYDxQplgAQAGaOvhEsiw/fj4AAAAABBwYXNwAAAAAQAAAAEAAAAUYnRydAAAAAAAABpKAAAAAAAAABhzdHRzAAAAAAAA
31
+ AAEAAAAkAAAEAAAAABRzdHNzAAAAAAAAAAEAAAABAAABMGN0dHMAAAAAAAAAJAAAAAEAAAgAAAAAAQAAFAAAAAABAAAIAAAA
32
+ AAEAAAAAAAAAAQAABAAAAAABAAAUAAAAAAEAAAgAAAAAAQAAAAAAAAABAAAEAAAAAAEAABQAAAAAAQAACAAAAAABAAAAAAAA
33
+ AAEAAAQAAAAAAQAAFAAAAAABAAAIAAAAAAEAAAAAAAAAAQAABAAAAAABAAAUAAAAAAEAAAgAAAAAAQAAAAAAAAABAAAEAAAA
34
+ AAEAABQAAAAAAQAACAAAAAABAAAAAAAAAAEAAAQAAAAAAQAADAAAAAABAAAEAAAAAAEAABQAAAAAAQAACAAAAAABAAAAAAAA
35
+ AAEAAAQAAAAAAQAAFAAAAAABAAAIAAAAAAEAAAAAAAAAAQAABAAAAAABAAAIAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAJAAA
36
+ AAEAAACkc3RzegAAAAAAAAAAAAAAJAAAA7kAAAB5AAAAFAAAAA0AAAASAAAAlwAAAA8AAAANAAAADwAAAH4AAAAVAAAADwAA
37
+ AA0AAAB1AAAAEQAAAA0AAAAPAAAAdgAAABMAAAAZAAAAEwAAAH8AAAAVAAAADQAAAA8AAABkAAAADQAAAHoAAAAWAAAADQAA
38
+ ABgAAABsAAAAHAAAABMAAAARAAAALQAAABRzdGNvAAAAAAAAAAEAAAUcAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAA
39
+ AAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY2Mi4xMi4xMDEAAAAI
40
+ ZnJlZQAACeRtZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE2NSByMzIyMiBiMzU2MDVhIC0gSC4y
41
+ NjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQu
42
+ aHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3Vi
43
+ bWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9
44
+ MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRz
45
+ PTYgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1
46
+ cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9
47
+ MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEyIHNjZW5l
48
+ Y3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0zMC4wIHFjb21wPTAu
49
+ NjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAABA2WIhAAR//7Y5/MsK6Oq
50
+ 4ykwwKqpcOrL3cKlAye4T82LJaFDykH8t1sy3r0hSL9GJNa5NcmViBufos9UN9gzGYbJ+IZ2zZxsnkenejy2v3fx5TJjlNiI
51
+ lUKJMls0KLDFbhz82ajhsUGCezq0DCHCjALIv+g8Mldnn5k8ZpN3EDjYJa/Xr4debHnbe8MjXRg4AQX9CzvPxKZ0FOpU9kMq
52
+ wZCGtEL61eQfUrbRJEckKT/uj9Mn36kbkwySpgwWy+X4zhIAkU/8maLRVjnj3td16WQD8v41QkTqIAbj5xutvfzCthCT3bAU
53
+ ImCyAj/3VMTOf7e7/2wID8LxWW1qhuqm/ESy1IEAAAB1QZokbEEf/jgvWdARAG+Q716M5y2kxC9zQxestdinyTkZHEkobNZe
54
+ Io8iY7hdsf0DXmMiCDUQw95YR69/upji0ZszBzFfuWMkcLkAUJgrX+nwInbF+cqfxkLSvqekuduanhGoUda6qvCLmYfPCvzx
55
+ NxfhXOsDAAAAEEGeQniG/wBYjIHfgJhI7pUAAAAJAZ5hdEM/AAz4AAAADgGeY2pDPwBHVe9UT+NVAAAAk0GaaEmoQWiZTAgj
56
+ //5OF6TRRZQ2sCZT82gD55snmBc2VCQrOHPcOMYrUu92MLNi02iv2WXT9FMZgape5MLJHXHldLvlu7k/CFKLw2TsP4goIIv2
57
+ A0GVHytWGZhTXcrvynPcIiCNPTdlUiUZF1C+6li8nBIR5DvBIbxaifgPirI49DZjWGI4LD9Fk4D9Mk6u/Hz4hQAAAAtBnoZF
58
+ ESw3/wAKSQAAAAkBnqV0Qz8ADPkAAAALAZ6nakM/AA3O5UAAAAB6QZqsSahBbJlMCCH//hTYBQCtXsAgZYOKSFqkWnaC/Wij
59
+ xJ5s6a/Y7hDhIUZlx2/xywHIkSnDM2r4Klv5XBlBN6qnzJTqqk+yvA9xuk/8kLWoRSVtss1qi9t1jOAtWByPaDxjREr0X/j3
60
+ qecy72Sxv95hmOVrB5AoHXUAAAARQZ7KRRUsN/8BwpyYHnxbssEAAAALAZ7pdEM/AjpJ0kgAAAAJAZ7rakM/AAz4AAAAcUGa
61
+ 8EmoQWyZTAgh//4UOL0ADDajsAFfKeG2gD56qAAAiqpCX0yVYhu9bOme3s4DGSBB2yReDbbxgDkkHFzuvnjpDGDem5tLKCGy
62
+ N0ZquwIPOSO6KZ3q3LXuCpdZ/SxDWgozBxavNevHX48zkuER3WlxAAAADUGfDkUVLDf/AAsRpuEAAAAJAZ8tdEM/AAz5AAAA
63
+ CwGfL2pDPwAYfXuIAAAAckGbNEmoQWyZTAgh//4Vi5hOfTkSf5AAl/tSTYIiqHQf+JoqzIiQNAosx3e6pjOqhCv+sRoSwWIs
64
+ XT/hAb3MjgAk/NEr6gq7TliYYNhbZPzRJT9BC4ddsf6xPRGAKZstBUhKoATJ4XZ0bVBq9IESu87RGAAAAA9Bn1JFFSw3/wBa
65
+ zFR31lkAAAAVAZ9xdEM/DKx44q58rCKABc+jNttAAAAADwGfc2pDPwpKlr9xTLXjQAAAAHtBm3hJqEFsmUwIIf/+HDmA5LyP
66
+ cQCfNs0myk1SonheXPdPZkqxRFjapoAYqbdtP2kcthV/0DXUJnlQj8P7ZAygspQBG0ruDBQs0hVOxV4vBQdqX6Yx3q1OWOFW
67
+ OMMdxy/Gp6C4S1Wx764qh0io/rcvEW5qcTKrLnd4y4EAAAARQZ+WRRUsN/8YmcPDKq0qbPAAAAAJAZ+1dEM/AAz5AAAACwGf
68
+ t2pDPwAOHwCxAAAAYEGbukmoQWyZTBRMEP/+BHWxMz6bge/VZzDUA72k3ya7C3oZVf/PTOrs91w3SCrENLFOFHO+ze0Utt9Y
69
+ VyswKtHXivU26w8GJkIlypVFzku5e1+SyBoRKYs7X1bBNA22hQAAAAkBn9lqQz8ADPkAAAB2QZveSeEKUmUwIf/996JsAXS2
70
+ K1esjM8XQMnoefMADEibOu9HOW2puLo6+gGCB5kZiHnSwiS1MiAnnuUG3p9OiA0lrA54+VaPVsDM5CelCc1RkjkeienoiT/1
71
+ ZzWoaPYTFKluzq54JieXFpBaiDUKpMKDVQiyaQAAABJBn/xFNEw3/wGzpbq1BRJROyEAAAAJAZ4bdEM/AAz5AAAAFAGeHWpD
72
+ PwIr6Qgz9t+ZbZmeTl6AAAAAaEGaAkmoQWiZTAhv//qi+YgClQu6CxhavI0kgzACj8NYTyIQ5tL9ytv8jlExcuAxd1atD1gY
73
+ xjChoFnui4qCOOvfNQ0AbGwaZgUNQNGjnW4KM/ROBx1R75pGN/ucMT8HuI+u9GR3nQk4AAAAGEGeIEURLDf/CIoKnEu1YRZB
74
+ H07eo3g+9wAAAA8Bnl90Qz8CPD+Jjbs8moAAAAANAZ5BakM/AWLO+qKI0QAAAClBmkNJqEFsmUwIZ//6WAPnM/YVcDReliMF
75
+ 1zpDvCZ+hXeJsQLqi7OoGA==
76
+ MP4
77
+
78
+ def self.bytes
79
+ @bytes ||= DATA.unpack1('m')
80
+ end
81
+ end
82
+ end
@@ -4,6 +4,10 @@ module Testimonials
4
4
  class Engine < ::Rails::Engine
5
5
  isolate_namespace Testimonials
6
6
 
7
+ rake_tasks do
8
+ load File.expand_path('../tasks/testimonials_tasks.rake', __dir__)
9
+ end
10
+
7
11
  initializer 'testimonials.helpers' do
8
12
  ActiveSupport.on_load(:action_view) do
9
13
  include Testimonials::WidgetHelper
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ require_relative 'demo_video'
5
+
6
+ module Testimonials
7
+ module Seeds
8
+ TESTIMONIALS = [
9
+ {
10
+ seed_id: 'approved-founder',
11
+ kind: 'text',
12
+ body: 'We added the widget after onboarding and collected better customer quotes in the first week.',
13
+ best_line: 'Collected better customer quotes in the first week.',
14
+ rating: 5,
15
+ status: 'approved',
16
+ featured: true,
17
+ consent_given: true,
18
+ consent_text: 'You can use my testimonial publicly in your marketing and sales.',
19
+ name: 'Maya Chen',
20
+ email: 'maya@example.com',
21
+ title_company: 'Founder, DemoCRM',
22
+ source: 'widget',
23
+ page_url: '/onboarding/complete',
24
+ locale: 'en'
25
+ },
26
+ {
27
+ seed_id: 'pending-operator',
28
+ kind: 'text',
29
+ body: 'The NPS prompt felt lightweight and did not interrupt the workflow.',
30
+ rating: 4,
31
+ status: 'pending',
32
+ featured: false,
33
+ consent_given: true,
34
+ consent_text: 'You can use my testimonial publicly in your marketing and sales.',
35
+ name: 'Jon Bell',
36
+ email: 'jon@example.com',
37
+ title_company: 'Operations Lead, ExampleCo',
38
+ source: 'nps',
39
+ page_url: '/reports',
40
+ locale: 'en'
41
+ },
42
+ {
43
+ seed_id: 'private-archive',
44
+ kind: 'text',
45
+ body: 'Useful for our internal rollout notes, but please do not publish my name.',
46
+ rating: 3,
47
+ status: 'archived',
48
+ featured: false,
49
+ consent_given: false,
50
+ consent_text: 'You can only use my testimonial privately in your marketing and sales.',
51
+ name: 'Priya Rao',
52
+ email: 'priya@example.com',
53
+ title_company: 'Customer Success, SampleOps',
54
+ source: 'page',
55
+ page_url: '/testimonials/new',
56
+ locale: 'en'
57
+ },
58
+ {
59
+ seed_id: 'approved-video',
60
+ kind: 'video',
61
+ body: 'Short demo video testimonial with a real MP4 attachment.',
62
+ best_line: 'A real video testimonial is ready to review.',
63
+ rating: 5,
64
+ status: 'approved',
65
+ featured: true,
66
+ consent_given: true,
67
+ consent_text: 'You can use my testimonial publicly in your marketing and sales.',
68
+ name: 'Alex Rivera',
69
+ email: 'alex@example.com',
70
+ title_company: 'Head of Product, VideoDemo',
71
+ source: 'page',
72
+ page_url: '/testimonials/new',
73
+ locale: 'en'
74
+ }
75
+ ].freeze
76
+
77
+ NPS_RESPONSES = [
78
+ { seed_id: 'promoter', score: 10,
79
+ comment: 'Fast to install and easy to trust because the data stays in our app.' },
80
+ { seed_id: 'passive', score: 8, comment: 'The flow is useful, but I would like more display examples.' },
81
+ { seed_id: 'detractor', score: 4, comment: 'I was not sure when the prompt would appear again.' }
82
+ ].freeze
83
+
84
+ PROMPT_EVENTS = [
85
+ { kind: 'testimonial', action: 'shown', visitor_token: 'testimonials-demo-visitor' },
86
+ { kind: 'testimonial', action: 'dismissed', visitor_token: 'testimonials-demo-visitor' },
87
+ { kind: 'nps', action: 'submitted', author_id: 'testimonials-demo:nps-promoter' }
88
+ ].freeze
89
+
90
+ def self.load!(tenant: nil)
91
+ {
92
+ testimonials: load_testimonials!(tenant: tenant),
93
+ nps_responses: load_nps_responses!(tenant: tenant),
94
+ prompt_events: load_prompt_events!(tenant: tenant)
95
+ }
96
+ end
97
+
98
+ def self.load_testimonials!(tenant:)
99
+ TESTIMONIALS.map do |attributes|
100
+ seed_id = attributes.fetch(:seed_id)
101
+ testimonial = Testimonials::Testimonial.find_or_initialize_by(
102
+ author_id: "testimonials-demo:#{seed_id}",
103
+ tenant: tenant.presence
104
+ )
105
+ testimonial.assign_attributes(
106
+ attributes.except(:seed_id).merge(tenant: tenant.presence, user_agent: 'testimonials demo seed')
107
+ )
108
+ testimonial.save!
109
+ attach_demo_video!(testimonial) if seed_id == 'approved-video'
110
+ testimonial
111
+ end
112
+ end
113
+ private_class_method :load_testimonials!
114
+
115
+ def self.load_nps_responses!(tenant:)
116
+ NPS_RESPONSES.map do |attributes|
117
+ seed_id = attributes.fetch(:seed_id)
118
+ response = Testimonials::NpsResponse.find_or_initialize_by(
119
+ author_id: "testimonials-demo:nps-#{seed_id}",
120
+ tenant: tenant.presence
121
+ )
122
+ response.assign_attributes(
123
+ attributes.except(:seed_id).merge(
124
+ name: 'Demo Respondent',
125
+ email: "#{seed_id}@example.com",
126
+ page_url: '/dashboard',
127
+ user_agent: 'testimonials demo seed',
128
+ locale: 'en',
129
+ tenant: tenant.presence
130
+ )
131
+ )
132
+ response.save!
133
+ response
134
+ end
135
+ end
136
+ private_class_method :load_nps_responses!
137
+
138
+ def self.load_prompt_events!(tenant:)
139
+ PROMPT_EVENTS.map do |attributes|
140
+ event = Testimonials::PromptEvent.find_or_initialize_by(
141
+ attributes.slice(:kind, :action, :author_id, :visitor_token).merge(tenant: tenant.presence)
142
+ )
143
+ event.save!
144
+ event
145
+ end
146
+ end
147
+ private_class_method :load_prompt_events!
148
+
149
+ def self.attach_demo_video!(testimonial)
150
+ return unless Testimonials.config.video_enabled?
151
+ return unless testimonial.respond_to?(:video_file)
152
+ return if testimonial.video_file.attached?
153
+
154
+ testimonial.video_file.attach(
155
+ io: StringIO.new(DemoVideo.bytes),
156
+ filename: DemoVideo::FILENAME,
157
+ content_type: DemoVideo::CONTENT_TYPE
158
+ )
159
+ end
160
+ private_class_method :attach_demo_video!
161
+ end
162
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Testimonials
4
- VERSION = '0.7.5'
4
+ VERSION = '0.7.7'
5
5
  end