testimonials 0.7.8 → 0.7.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f77889bc1fe3b99ca3b75dad0936498a35afa29249bceb5c9000ef090b275cfc
4
- data.tar.gz: 0bd9fdedb0d9b11be9c4b8ebdea7277d4f45c249723b5c43dc93bb7b2e74cb7b
3
+ metadata.gz: 746c0b7bc1421a8aadfbbbd7e9d41cb4aabc81ff0c6afa60669e4d9c06e963b0
4
+ data.tar.gz: 3914ba53cef25fcb6f541e6b4176e1d6135205cb37cdcc05b289125d456f1bbb
5
5
  SHA512:
6
- metadata.gz: 2b60920030a7e8099116b1d8abd8e31f98fbf9b984f4c3faaebf7fd3449ac232574016c0b0d5c62ea0ecd6647500e2646baf8264c657ed913df98ab7fa71c207
7
- data.tar.gz: 3c5b5e7209197b41f28cb90240b3c232123ad91833e14be80031af275becf143f9376316d36857c9de7db844eb8630aa570de8bc09d0a14173a534da4f309980
6
+ metadata.gz: 55f8694212e3f1395481121efd0f67c8474e9422173ba63e9b05cf66f40320b340301a1be39537cfbeb8ce17292b289d4f73247b2a05742e52f79b1bad6d47ae
7
+ data.tar.gz: 1a57a1aa934adc3d3b92199d922effc5ec36d66dc9138f819b1b153f754431c5e5bd7bea4f21e65fe51bb725d732e648acf7f389f8168ac277f174e220130b19
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.9
4
+
5
+ - NPS is now optional at install time. `bin/rails generate testimonials:install
6
+ --skip-nps` leaves out the `testimonials_nps_responses` table and writes
7
+ `config.nps = false`, so an app that only wants testimonials carries neither
8
+ the table nor the NPS tab. `bin/rails generate testimonials:nps` adds the
9
+ table later, mirroring the `testimonials:tenant` upgrade path.
10
+ - The guard is `config.nps`, not table introspection — nothing checks the
11
+ schema at runtime and nothing touches the database at boot. Consequently
12
+ `/testimonials/nps_responses` now 404s when `config.nps` is false instead of
13
+ still serving history; flip the flag back to read old responses. The
14
+ dashboard nav already hid the tab, so only a typed URL reached it.
15
+ - `testimonials:seed_demo` skips NPS rows when the flow is off, and the
16
+ `testimonials:tenant` migration skips the NPS table when it isn't there.
17
+
3
18
  ## 0.7.8
4
19
 
5
20
  - The NPS dashboard now says what the score means. The card carries the band
data/README.md CHANGED
@@ -33,6 +33,15 @@ bin/rails db:migrate
33
33
  The generator writes the initializer, the migration, and mounts the engine at
34
34
  `/testimonials`. Nothing renders until the widget is opened.
35
35
 
36
+ Don't want NPS? Install without it — no table, no dashboard tab, no prompt:
37
+
38
+ ```bash
39
+ bin/rails generate testimonials:install --skip-nps
40
+ ```
41
+
42
+ Changed your mind later: `bin/rails generate testimonials:nps`, migrate, and
43
+ set `config.nps = true`.
44
+
36
45
  Optional demo data:
37
46
 
38
47
  ```bash
@@ -242,16 +251,29 @@ throttle would allow it. Detractors (0–6) trigger `on_detractor`, which pairs
242
251
  well with [ideasbugs](https://github.com/yshmarov/ideasbugs). Your score and
243
252
  every response live at `/testimonials/nps_responses`.
244
253
 
254
+ | 1. One question, eleven taps | 2. The reason, optional |
255
+ | --- | --- |
256
+ | ![The NPS prompt: how likely are you to recommend us, scored 0 to 10](https://raw.githubusercontent.com/yshmarov/testimonials/main/docs/screenshots/nps-01-score.jpg) | ![The follow-up asking for the main reason behind the score](https://raw.githubusercontent.com/yshmarov/testimonials/main/docs/screenshots/nps-02-comment.png) |
257
+ | Ten seconds of work, and "Not now" is always there. | The comment is where the actionable half lives. Promoters go straight on to the testimonial form. |
258
+
245
259
  The score is `% promoters − % detractors`, from −100 to +100, and the dashboard
246
260
  reads it against the usual benchmarks so nobody has to go and google them:
247
261
  below 0 needs work, 0–29 is good, 30–69 is great, 70 and up is world-class. A
248
262
  scale under the number shows where you sit, and under 30 responses the card
249
263
  says the sample is too thin to read much into.
250
264
 
265
+ <img src="https://raw.githubusercontent.com/yshmarov/testimonials/main/docs/screenshots/nps-dashboard.jpg" alt="The NPS dashboard: a score of 41 badged Great, a scale marking where it sits between −100 and +100, promoter/passive/detractor counts, and the collapsed panel explaining the formula and the benchmarks" width="820">
266
+
251
267
  `/testimonials/nps/new` is the same question on its own page — the link for an
252
268
  email campaign, where there is no app session to prompt inside of. A promoter
253
269
  who scores 9–10 gets the testimonial form right there on the page.
254
270
 
271
+ NPS is optional. `config.nps = false` — what `--skip-nps` writes at install —
272
+ turns off the prompt, the public page, the `/api/stats` score and the dashboard
273
+ tab, and `/testimonials/nps_responses` then 404s. Turning it off on an install
274
+ that has the table hides existing responses along with everything else; flip it
275
+ back to read them.
276
+
255
277
  ## The public pages
256
278
 
257
279
  Two standalone, self-styled pages for people outside the app — drop either link
@@ -8,6 +8,9 @@ module Testimonials
8
8
  layout :testimonials_admin_layout
9
9
 
10
10
  before_action :require_admin
11
+ # An install run with --skip-nps has no table behind these pages, and the
12
+ # nav already hides the tab whenever config.nps is off.
13
+ before_action :require_nps
11
14
  before_action :set_response, only: :show
12
15
 
13
16
  def index
@@ -31,6 +34,10 @@ module Testimonials
31
34
 
32
35
  private
33
36
 
37
+ def require_nps
38
+ head :not_found unless Testimonials.config.nps
39
+ end
40
+
34
41
  def set_response
35
42
  @response = NpsResponse.for_tenant(current_tenant).find(params[:id])
36
43
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  <div class="page-head">
4
4
  <h1><%= t('testimonials.dashboard.nps', default: 'NPS') %></h1>
5
- <%# The shareable NPS page, same idea as the testimonial one. This dashboard
6
- still reads history when config.nps is off, but that page 404s — so the
7
- link needs both switches. %>
8
- <% if Testimonials.config.public_collection && Testimonials.config.nps %>
5
+ <%# The shareable NPS page, same idea as the testimonial one. config.nps is
6
+ already true here the controller 404s otherwise — so only the public
7
+ switch is left to check. %>
8
+ <% if Testimonials.config.public_collection %>
9
9
  <a class="button share-link" href="<%= nps_collection_url %>" target="_blank" rel="noopener"
10
10
  title="<%= nps_collection_url %>">
11
11
  🔗 <%= t('testimonials.dashboard.nps_page', default: 'Ask for NPS scores') %>
@@ -12,8 +12,15 @@ module Testimonials
12
12
 
13
13
  desc 'Installs testimonials: config initializer, migration, and engine mount.'
14
14
 
15
+ # NPS is the one part of the gem an app can genuinely not want. Skipping
16
+ # it leaves out the table and writes `config.nps = false`, so nothing
17
+ # ever reaches a table that isn't there — no runtime introspection, no
18
+ # boot-time database call. `testimonials:nps` adds it later.
19
+ class_option :skip_nps, type: :boolean, default: false,
20
+ desc: 'Leave out the NPS table and turn the NPS flow off'
21
+
15
22
  def create_initializer
16
- copy_file 'initializer.rb', 'config/initializers/testimonials.rb'
23
+ template 'initializer.rb.tt', 'config/initializers/testimonials.rb'
17
24
  end
18
25
 
19
26
  def create_migration_file
@@ -29,8 +36,12 @@ module Testimonials
29
36
  say "\ntestimonials installed. Run `rails db:migrate`, then add", :green
30
37
  say '`<%= testimonials_tag %>` before </body> in your layout.'
31
38
  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.'
39
+ samples = options[:skip_nps] ? 'testimonials' : 'testimonials and NPS'
40
+ say "Optional: run `bin/rails testimonials:seed_demo` for sample #{samples}."
33
41
  say "Collect from outside the app via /testimonials/new.\n"
42
+ return unless options[:skip_nps]
43
+
44
+ say 'Installed without NPS. Add it later with `bin/rails generate testimonials:nps`.', :yellow
34
45
  end
35
46
 
36
47
  private
@@ -27,6 +27,7 @@ class CreateTestimonialsTables < ActiveRecord::Migration<%= migration_version %>
27
27
  add_index :testimonials_testimonials, %i[tenant status]
28
28
  add_index :testimonials_testimonials, %i[tenant status consent_given]
29
29
 
30
+ <% unless options[:skip_nps] -%>
30
31
  create_table :testimonials_nps_responses do |t|
31
32
  t.integer :score, null: false
32
33
  t.text :comment
@@ -42,6 +43,7 @@ class CreateTestimonialsTables < ActiveRecord::Migration<%= migration_version %>
42
43
  end
43
44
  add_index :testimonials_nps_responses, %i[tenant score]
44
45
 
46
+ <% end -%>
45
47
  create_table :testimonials_prompt_events do |t|
46
48
  t.string :kind, null: false
47
49
  t.string :action, null: false
@@ -84,7 +84,15 @@ Testimonials.configure do |config|
84
84
  # config.public_api = false
85
85
 
86
86
  # NPS surveys. Promoters (9–10) are offered the testimonial form right away.
87
+ <% if options[:skip_nps] -%>
88
+ # Installed with --skip-nps, so there is no testimonials_nps_responses table
89
+ # and the NPS flow, dashboard tab and public page stay off. To add it later:
90
+ # `bin/rails generate testimonials:nps && bin/rails db:migrate`, then flip
91
+ # this to true.
92
+ config.nps = false
93
+ <% else -%>
87
94
  # config.nps = true
95
+ <% end -%>
88
96
  # config.nps_reprompt_after = 90.days
89
97
 
90
98
  # Called with each saved Testimonial or NpsResponse — notify Slack, email…
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+
6
+ module Testimonials
7
+ module Generators
8
+ # For apps that installed with --skip-nps and later want the 0–10 survey:
9
+ # creates the testimonials_nps_responses table. A full install already has
10
+ # it. Additive and safe — nothing reads the table until config.nps is true.
11
+ #
12
+ # bin/rails generate testimonials:nps && bin/rails db:migrate
13
+ class NpsGenerator < Rails::Generators::Base
14
+ include ActiveRecord::Generators::Migration
15
+
16
+ source_root File.expand_path('templates', __dir__)
17
+
18
+ desc 'Adds the NPS responses table for an install that skipped it.'
19
+
20
+ def create_migration_file
21
+ migration_template 'create_testimonials_nps_responses.rb.tt',
22
+ 'db/migrate/create_testimonials_nps_responses.rb'
23
+ end
24
+
25
+ def post_install
26
+ say "\nNPS table queued. Run `rails db:migrate`, then set", :green
27
+ say 'config.nps = true in config/initializers/testimonials.rb to turn the flow on.'
28
+ end
29
+
30
+ private
31
+
32
+ def migration_version
33
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateTestimonialsNpsResponses < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ create_table :testimonials_nps_responses do |t|
6
+ t.integer :score, null: false
7
+ t.text :comment
8
+ t.string :author_id
9
+ t.string :name
10
+ t.string :email
11
+ t.string :page_url
12
+ t.string :user_agent
13
+ t.string :locale
14
+ t.string :tenant # opaque per-tenant key; nil = single global collection
15
+
16
+ t.timestamps
17
+ end
18
+ add_index :testimonials_nps_responses, %i[tenant score]
19
+ end
20
+ end
@@ -3,13 +3,17 @@
3
3
  class AddTenantToTestimonials < ActiveRecord::Migration<%= migration_version %>
4
4
  def change
5
5
  add_column :testimonials_testimonials, :tenant, :string
6
- add_column :testimonials_nps_responses, :tenant, :string
7
6
  add_column :testimonials_prompt_events, :tenant, :string
8
7
 
9
8
  add_index :testimonials_testimonials, %i[tenant status]
10
9
  add_index :testimonials_testimonials, %i[tenant status consent_given]
11
- add_index :testimonials_nps_responses, %i[tenant score]
12
10
  add_index :testimonials_prompt_events, %i[tenant author_id kind]
13
11
  add_index :testimonials_prompt_events, %i[tenant visitor_token kind]
12
+
13
+ # An install run with --skip-nps has no NPS table to add the column to.
14
+ return unless table_exists?(:testimonials_nps_responses)
15
+
16
+ add_column :testimonials_nps_responses, :tenant, :string
17
+ add_index :testimonials_nps_responses, %i[tenant score]
14
18
  end
15
19
  end
@@ -90,7 +90,8 @@ module Testimonials
90
90
  def self.load!(tenant: nil)
91
91
  {
92
92
  testimonials: load_testimonials!(tenant: tenant),
93
- nps_responses: load_nps_responses!(tenant: tenant),
93
+ # An install run with --skip-nps has no table to seed into.
94
+ nps_responses: (Testimonials.config.nps ? load_nps_responses!(tenant: tenant) : []),
94
95
  prompt_events: load_prompt_events!(tenant: tenant)
95
96
  }
96
97
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Testimonials
4
- VERSION = '0.7.8'
4
+ VERSION = '0.7.9'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: testimonials
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.8
4
+ version: 0.7.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -97,7 +97,9 @@ files:
97
97
  - config/routes.rb
98
98
  - lib/generators/testimonials/install/install_generator.rb
99
99
  - lib/generators/testimonials/install/templates/create_testimonials_tables.rb.tt
100
- - lib/generators/testimonials/install/templates/initializer.rb
100
+ - lib/generators/testimonials/install/templates/initializer.rb.tt
101
+ - lib/generators/testimonials/nps/nps_generator.rb
102
+ - lib/generators/testimonials/nps/templates/create_testimonials_nps_responses.rb.tt
101
103
  - lib/generators/testimonials/tenant/templates/add_tenant_to_testimonials.rb.tt
102
104
  - lib/generators/testimonials/tenant/tenant_generator.rb
103
105
  - lib/tasks/testimonials_tasks.rake