testimonials 0.7.10 → 0.8.0
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 +4 -4
- data/AGENTS.md +4 -1
- data/CHANGELOG.md +45 -0
- data/README.md +2 -0
- data/app/controllers/concerns/testimonials/request_context.rb +75 -0
- data/app/controllers/testimonials/application_controller.rb +10 -56
- data/app/controllers/testimonials/dashboard_controller.rb +40 -0
- data/app/controllers/testimonials/nps_responses_controller.rb +1 -4
- data/app/controllers/testimonials/submissions_controller.rb +169 -0
- data/app/controllers/testimonials/testimonials_controller.rb +6 -166
- data/app/views/layouts/testimonials/application.html.erb +5 -8
- data/app/views/testimonials/nps_responses/index.html.erb +154 -152
- data/app/views/testimonials/nps_responses/show.html.erb +7 -5
- data/app/views/testimonials/shared/_dashboard.html.erb +18 -0
- data/app/views/testimonials/testimonials/index.html.erb +110 -108
- data/app/views/testimonials/testimonials/show.html.erb +7 -5
- data/config/routes.rb +18 -5
- data/lib/generators/testimonials/install/install_generator.rb +2 -6
- data/lib/generators/testimonials/install/templates/create_testimonials_tables.rb.tt +3 -3
- data/lib/generators/testimonials/install/templates/initializer.rb.tt +10 -2
- data/lib/generators/testimonials/migration_helpers.rb +35 -0
- data/lib/generators/testimonials/nps/nps_generator.rb +2 -6
- data/lib/generators/testimonials/nps/templates/create_testimonials_nps_responses.rb.tt +1 -1
- data/lib/generators/testimonials/prompt_events/prompt_events_generator.rb +2 -6
- data/lib/generators/testimonials/prompt_events/templates/create_testimonials_prompt_events.rb.tt +1 -1
- data/lib/generators/testimonials/tenant/tenant_generator.rb +2 -6
- data/lib/testimonials/configuration.rb +24 -1
- data/lib/testimonials/dashboard.css +278 -221
- data/lib/testimonials/version.rb +1 -1
- data/lib/testimonials.rb +8 -0
- metadata +6 -1
|
@@ -1,124 +1,126 @@
|
|
|
1
1
|
<% content_for :body_class, 'tm-index' %>
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
<%= render layout: 'testimonials/shared/dashboard' do %>
|
|
4
|
+
<div class="page-head">
|
|
5
|
+
<h1><%= t('testimonials.dashboard.title', default: 'Testimonials') %></h1>
|
|
6
|
+
<%# The shareable public form — drop this link into an email or DM. It lives
|
|
7
|
+
on the page, not in the nav, so it survives a host's admin_layout. %>
|
|
8
|
+
<% if Testimonials.config.public_collection %>
|
|
9
|
+
<a class="button share-link" href="<%= collection_url %>" target="_blank" rel="noopener"
|
|
10
|
+
title="<%= collection_url %>">
|
|
11
|
+
🔗 <%= t('testimonials.dashboard.collection_page', default: 'Ask for testimonials') %>
|
|
12
|
+
</a>
|
|
13
|
+
<% end %>
|
|
14
|
+
</div>
|
|
14
15
|
|
|
15
|
-
<div class="dashboard-shell <%= 'has-selected' if @selected_testimonial %>">
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
<div class="dashboard-shell <%= 'has-selected' if @selected_testimonial %>">
|
|
17
|
+
<aside class="dashboard-sidebar" aria-label="<%= t('testimonials.dashboard.testimonials', default: 'Testimonials') %>">
|
|
18
|
+
<div class="tabs">
|
|
19
|
+
<% Testimonials::Testimonial::STATUSES.each do |status| %>
|
|
20
|
+
<%= link_to testimonials_path(status: status, kind: @kind, q: @query),
|
|
21
|
+
class: ('active' if status == @status) do %>
|
|
22
|
+
<%= t("testimonials.statuses.#{status}", default: status.humanize) %>
|
|
23
|
+
<span class="count"><%= @counts.fetch(status, 0) %></span>
|
|
24
|
+
<% end %>
|
|
23
25
|
<% end %>
|
|
24
|
-
|
|
25
|
-
</div>
|
|
26
|
+
</div>
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
<form class="filters" method="get">
|
|
29
|
+
<input type="hidden" name="status" value="<%= @status %>">
|
|
30
|
+
<select name="kind" data-autosubmit="true">
|
|
31
|
+
<option value=""><%= t('testimonials.dashboard.all_kinds', default: 'All kinds') %></option>
|
|
32
|
+
<% Testimonials::Testimonial::KINDS.each do |kind| %>
|
|
33
|
+
<option value="<%= kind %>" <%= 'selected' if kind == @kind %>>
|
|
34
|
+
<%= t("testimonials.kinds.#{kind}", default: kind.humanize) %>
|
|
35
|
+
</option>
|
|
36
|
+
<% end %>
|
|
37
|
+
</select>
|
|
38
|
+
<input type="search" name="q" value="<%= @query %>"
|
|
39
|
+
placeholder="<%= t('testimonials.dashboard.search_placeholder', default: 'Search text, name, email…') %>"
|
|
40
|
+
aria-label="<%= t('testimonials.dashboard.search', default: 'Search') %>">
|
|
41
|
+
<button><%= t('testimonials.dashboard.search', default: 'Search') %></button>
|
|
42
|
+
</form>
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
<div class="record-list">
|
|
45
|
+
<% if @testimonials.any? %>
|
|
46
|
+
<% @testimonials.each do |testimonial| %>
|
|
47
|
+
<%= link_to testimonials_path(status: @status, kind: @kind, q: @query, page: @page,
|
|
48
|
+
testimonial_id: testimonial.id),
|
|
49
|
+
class: ['record-row testimonial-row',
|
|
50
|
+
('active' if @selected_testimonial&.id == testimonial.id)].compact do %>
|
|
51
|
+
<span class="record-main">
|
|
52
|
+
<span class="record-topline">
|
|
53
|
+
<% if testimonial.rating %>
|
|
54
|
+
<span class="stars"><%= '★' * testimonial.rating %><span class="off"><%= '★' * (5 - testimonial.rating) %></span></span>
|
|
55
|
+
<% else %>
|
|
56
|
+
<span class="badge locale"><%= t("testimonials.kinds.#{testimonial.kind}", default: testimonial.kind.humanize) %></span>
|
|
57
|
+
<% end %>
|
|
58
|
+
<span class="badge status-<%= testimonial.status %>">
|
|
59
|
+
<%= t("testimonials.statuses.#{testimonial.status}", default: testimonial.status.humanize) %>
|
|
60
|
+
</span>
|
|
61
|
+
<span class="muted record-time" title="<%= testimonial.created_at %>">
|
|
62
|
+
<%= time_ago_in_words(testimonial.created_at) %>
|
|
63
|
+
</span>
|
|
59
64
|
</span>
|
|
60
|
-
<span class="
|
|
61
|
-
|
|
65
|
+
<span class="record-copy">
|
|
66
|
+
<% if testimonial.body.present? %>
|
|
67
|
+
<%= truncate(testimonial.body, length: 120) %>
|
|
68
|
+
<% else %>
|
|
69
|
+
<em><%= t('testimonials.kinds.video', default: 'Video') %></em>
|
|
70
|
+
<% end %>
|
|
71
|
+
</span>
|
|
72
|
+
<span class="muted record-meta">
|
|
73
|
+
<span>#<%= testimonial.id %></span>
|
|
74
|
+
<% if testimonial.display_name.present? %>
|
|
75
|
+
<span><%= testimonial.display_name %></span>
|
|
76
|
+
<% end %>
|
|
77
|
+
<% if testimonial.title_company.present? %>
|
|
78
|
+
<span><%= testimonial.title_company %></span>
|
|
79
|
+
<% end %>
|
|
62
80
|
</span>
|
|
63
81
|
</span>
|
|
64
|
-
<span class="record-
|
|
65
|
-
<% if testimonial.
|
|
66
|
-
|
|
67
|
-
<% else %>
|
|
68
|
-
<em><%= t('testimonials.kinds.video', default: 'Video') %></em>
|
|
69
|
-
<% end %>
|
|
70
|
-
</span>
|
|
71
|
-
<span class="muted record-meta">
|
|
72
|
-
<span>#<%= testimonial.id %></span>
|
|
73
|
-
<% if testimonial.display_name.present? %>
|
|
74
|
-
<span><%= testimonial.display_name %></span>
|
|
82
|
+
<span class="record-side">
|
|
83
|
+
<% if testimonial.video_attached? %>
|
|
84
|
+
<span class="muted">🎥</span>
|
|
75
85
|
<% end %>
|
|
76
|
-
<% if testimonial.
|
|
77
|
-
<span
|
|
86
|
+
<% if testimonial.featured? %>
|
|
87
|
+
<span class="muted">⭐</span>
|
|
78
88
|
<% end %>
|
|
89
|
+
<span class="muted"><%= testimonial.consent_given? ? '✓' : '—' %></span>
|
|
79
90
|
</span>
|
|
80
|
-
|
|
81
|
-
<span class="record-side">
|
|
82
|
-
<% if testimonial.video_attached? %>
|
|
83
|
-
<span class="muted">🎥</span>
|
|
84
|
-
<% end %>
|
|
85
|
-
<% if testimonial.featured? %>
|
|
86
|
-
<span class="muted">⭐</span>
|
|
87
|
-
<% end %>
|
|
88
|
-
<span class="muted"><%= testimonial.consent_given? ? '✓' : '—' %></span>
|
|
89
|
-
</span>
|
|
91
|
+
<% end %>
|
|
90
92
|
<% end %>
|
|
93
|
+
<% else %>
|
|
94
|
+
<div class="empty"><%= t('testimonials.dashboard.empty', default: 'Nothing here yet.') %></div>
|
|
91
95
|
<% end %>
|
|
92
|
-
|
|
93
|
-
<div class="empty"><%= t('testimonials.dashboard.empty', default: 'Nothing here yet.') %></div>
|
|
94
|
-
<% end %>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<div class="pager">
|
|
98
|
-
<% if @page > 1 %>
|
|
99
|
-
<%= link_to t('testimonials.dashboard.newer', default: '← Newer'),
|
|
100
|
-
testimonials_path(status: @status, kind: @kind, q: @query, page: @page - 1) %>
|
|
101
|
-
<% end %>
|
|
102
|
-
<% if @more %>
|
|
103
|
-
<%= link_to t('testimonials.dashboard.older', default: 'Older →'),
|
|
104
|
-
testimonials_path(status: @status, kind: @kind, q: @query, page: @page + 1) %>
|
|
105
|
-
<% end %>
|
|
106
|
-
</div>
|
|
107
|
-
</aside>
|
|
96
|
+
</div>
|
|
108
97
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
<h2><%= t('testimonials.dashboard.select_testimonial', default: 'Select a testimonial') %></h2>
|
|
119
|
-
<p class="muted"><%= t('testimonials.dashboard.select_testimonial_hint',
|
|
120
|
-
default: 'Open a review from the list to approve, feature, or tune the best line.') %></p>
|
|
98
|
+
<div class="pager">
|
|
99
|
+
<% if @page > 1 %>
|
|
100
|
+
<%= link_to t('testimonials.dashboard.newer', default: '← Newer'),
|
|
101
|
+
testimonials_path(status: @status, kind: @kind, q: @query, page: @page - 1) %>
|
|
102
|
+
<% end %>
|
|
103
|
+
<% if @more %>
|
|
104
|
+
<%= link_to t('testimonials.dashboard.older', default: 'Older →'),
|
|
105
|
+
testimonials_path(status: @status, kind: @kind, q: @query, page: @page + 1) %>
|
|
106
|
+
<% end %>
|
|
121
107
|
</div>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
108
|
+
</aside>
|
|
109
|
+
|
|
110
|
+
<main class="dashboard-detail" aria-label="<%= t('testimonials.dashboard.current_testimonial', default: 'Current testimonial') %>">
|
|
111
|
+
<% if @selected_testimonial %>
|
|
112
|
+
<p class="mobile-back">
|
|
113
|
+
<%= link_to t('testimonials.dashboard.back', default: '← All testimonials'),
|
|
114
|
+
testimonials_path(status: @status, kind: @kind, q: @query, page: @page) %>
|
|
115
|
+
</p>
|
|
116
|
+
<%= render 'testimonial_panel', testimonial: @selected_testimonial %>
|
|
117
|
+
<% else %>
|
|
118
|
+
<div class="empty-state">
|
|
119
|
+
<h2><%= t('testimonials.dashboard.select_testimonial', default: 'Select a testimonial') %></h2>
|
|
120
|
+
<p class="muted"><%= t('testimonials.dashboard.select_testimonial_hint',
|
|
121
|
+
default: 'Open a review from the list to approve, feature, or tune the best line.') %></p>
|
|
122
|
+
</div>
|
|
123
|
+
<% end %>
|
|
124
|
+
</main>
|
|
125
|
+
</div>
|
|
126
|
+
<% end %>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<% content_for :body_class, 'tm-show' %>
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
<%= render layout: 'testimonials/shared/dashboard' do %>
|
|
4
|
+
<div class="breadcrumb">
|
|
5
|
+
<%= link_to t('testimonials.dashboard.title', default: 'Testimonials'), root_path %>
|
|
6
|
+
/ #<%= @testimonial.id %>
|
|
7
|
+
</div>
|
|
7
8
|
|
|
8
|
-
<%= render 'testimonial_panel', testimonial: @testimonial %>
|
|
9
|
+
<%= render 'testimonial_panel', testimonial: @testimonial %>
|
|
10
|
+
<% end %>
|
data/config/routes.rb
CHANGED
|
@@ -22,14 +22,27 @@ Testimonials::Engine.routes.draw do
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# Media by testimonial id, gated (admin, author, or public_api + publishable).
|
|
25
|
-
get ':id/video', to: 'media#video', as: :testimonial_video
|
|
26
|
-
get ':id/poster', to: 'media#poster', as: :testimonial_poster
|
|
27
|
-
get ':id/avatar', to: 'media#avatar', as: :testimonial_avatar
|
|
25
|
+
get ':id/video', to: 'media#video', as: :testimonial_video
|
|
26
|
+
get ':id/poster', to: 'media#poster', as: :testimonial_poster
|
|
27
|
+
get ':id/avatar', to: 'media#avatar', as: :testimonial_avatar
|
|
28
28
|
|
|
29
29
|
# Flat, human URLs: the mount path IS the resource. /testimonials is the
|
|
30
30
|
# dashboard, POST /testimonials the widget endpoint, /testimonials/2 a testimonial.
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
#
|
|
32
|
+
# No `id: /\d+/` constraint: it made these routes bigint-only, which forced
|
|
33
|
+
# the tables to be bigint too, which meant a uuid-keyed host could never
|
|
34
|
+
# attach a video or avatar (its active_storage_attachments.record_id is a
|
|
35
|
+
# uuid column). The constraint was never load-bearing — every fixed-name
|
|
36
|
+
# route above is declared first, so ordering already does the disambiguating,
|
|
37
|
+
# and an id that matches no record still 404s, just via RecordNotFound rather
|
|
38
|
+
# than a routing error.
|
|
39
|
+
#
|
|
40
|
+
# POST goes to SubmissionsController, not to this resource: the widget's write
|
|
41
|
+
# endpoint is public and the rest is staff-only, and only the staff half
|
|
42
|
+
# inherits config.base_controller_class. Sharing one controller would put a
|
|
43
|
+
# host's admin authentication in front of a member leaving a review.
|
|
44
|
+
post '', to: 'submissions#create', as: :submissions
|
|
45
|
+
resources :testimonials, path: '', only: %i[index show update destroy]
|
|
33
46
|
|
|
34
47
|
root to: 'testimonials#index'
|
|
35
48
|
end
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require 'rails/generators'
|
|
4
4
|
require 'rails/generators/active_record'
|
|
5
|
+
require_relative '../migration_helpers'
|
|
5
6
|
|
|
6
7
|
module Testimonials
|
|
7
8
|
module Generators
|
|
8
9
|
class InstallGenerator < Rails::Generators::Base
|
|
9
10
|
include ActiveRecord::Generators::Migration
|
|
11
|
+
include MigrationHelpers
|
|
10
12
|
|
|
11
13
|
source_root File.expand_path('templates', __dir__)
|
|
12
14
|
|
|
@@ -59,12 +61,6 @@ module Testimonials
|
|
|
59
61
|
'widget — open it from your own button. Add it later with ' \
|
|
60
62
|
'`bin/rails generate testimonials:prompt_events`.', :yellow
|
|
61
63
|
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
|
|
65
|
-
def migration_version
|
|
66
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
67
|
-
end
|
|
68
64
|
end
|
|
69
65
|
end
|
|
70
66
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateTestimonialsTables < ActiveRecord::Migration<%= migration_version %>
|
|
4
4
|
def change
|
|
5
|
-
create_table :testimonials_testimonials do |t|
|
|
5
|
+
create_table :testimonials_testimonials<%= primary_key_type_option %> do |t|
|
|
6
6
|
t.string :kind, null: false, default: 'text'
|
|
7
7
|
t.text :body
|
|
8
8
|
t.integer :rating
|
|
@@ -28,7 +28,7 @@ class CreateTestimonialsTables < ActiveRecord::Migration<%= migration_version %>
|
|
|
28
28
|
add_index :testimonials_testimonials, %i[tenant status consent_given]
|
|
29
29
|
<% unless options[:skip_nps] -%>
|
|
30
30
|
|
|
31
|
-
create_table :testimonials_nps_responses do |t|
|
|
31
|
+
create_table :testimonials_nps_responses<%= primary_key_type_option %> do |t|
|
|
32
32
|
t.integer :score, null: false
|
|
33
33
|
t.text :comment
|
|
34
34
|
t.string :author_id
|
|
@@ -45,7 +45,7 @@ class CreateTestimonialsTables < ActiveRecord::Migration<%= migration_version %>
|
|
|
45
45
|
<% end -%>
|
|
46
46
|
<% unless options[:skip_prompt_events] -%>
|
|
47
47
|
|
|
48
|
-
create_table :testimonials_prompt_events do |t|
|
|
48
|
+
create_table :testimonials_prompt_events<%= primary_key_type_option %> do |t|
|
|
49
49
|
t.string :kind, null: false
|
|
50
50
|
t.string :action, null: false
|
|
51
51
|
t.string :author_id
|
|
@@ -13,9 +13,17 @@ Testimonials.configure do |config|
|
|
|
13
13
|
# only — override before deploying.
|
|
14
14
|
# config.authorize_admin = ->(request) { request.env["warden"]&.user&.admin? }
|
|
15
15
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
16
|
+
# Two ways to put the dashboard inside an admin you already have.
|
|
17
|
+
#
|
|
18
|
+
# The layout only — the gem's controllers, your shell:
|
|
18
19
|
# config.admin_layout = "admin/application"
|
|
20
|
+
#
|
|
21
|
+
# Or the whole stack. Name the controller your own admin inherits from and
|
|
22
|
+
# the dashboard picks up its layout, helpers, authentication and any request
|
|
23
|
+
# context its before_actions set up. Only the dashboard inherits it; the
|
|
24
|
+
# widget's endpoints stay public, so this cannot ask a member leaving a
|
|
25
|
+
# review for a staff session.
|
|
26
|
+
# config.base_controller_class = "Admin::BaseController"
|
|
19
27
|
|
|
20
28
|
# Attribute submissions to a user (optional). Return an object responding
|
|
21
29
|
# to #id, or nil. Receives the request.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Testimonials
|
|
4
|
+
module Generators
|
|
5
|
+
# Shared bits every migration-writing generator needs.
|
|
6
|
+
module MigrationHelpers
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def migration_version
|
|
10
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Follow the host's own key type instead of forcing bigint. An app that
|
|
14
|
+
# keys its models with uuids sets this, and its
|
|
15
|
+
# `active_storage_attachments.record_id` is then a uuid column — bigint
|
|
16
|
+
# tables here could never take a video or avatar attachment, because a
|
|
17
|
+
# uuid foreign key has nowhere to point.
|
|
18
|
+
#
|
|
19
|
+
# Same lookup Rails' own Active Storage, Action Text and Action Mailbox
|
|
20
|
+
# migrations do, so a host that set it once gets consistent tables from
|
|
21
|
+
# all of them.
|
|
22
|
+
#
|
|
23
|
+
# Rendered as a `create_table` option rather than a bare value, because a
|
|
24
|
+
# template is expanded at generate time: emitting the method name would
|
|
25
|
+
# put `id: primary_key_type` in the migration, where nothing defines it.
|
|
26
|
+
# A host with no setting gets no option at all, so the migration reads
|
|
27
|
+
# exactly as it always did.
|
|
28
|
+
def primary_key_type_option
|
|
29
|
+
config = Rails.configuration.generators
|
|
30
|
+
type = config.options[config.orm][:primary_key_type]
|
|
31
|
+
type ? ", id: :#{type}" : ''
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'rails/generators'
|
|
4
4
|
require 'rails/generators/active_record'
|
|
5
|
+
require_relative '../migration_helpers'
|
|
5
6
|
|
|
6
7
|
module Testimonials
|
|
7
8
|
module Generators
|
|
@@ -12,6 +13,7 @@ module Testimonials
|
|
|
12
13
|
# bin/rails generate testimonials:nps && bin/rails db:migrate
|
|
13
14
|
class NpsGenerator < Rails::Generators::Base
|
|
14
15
|
include ActiveRecord::Generators::Migration
|
|
16
|
+
include MigrationHelpers
|
|
15
17
|
|
|
16
18
|
source_root File.expand_path('templates', __dir__)
|
|
17
19
|
|
|
@@ -26,12 +28,6 @@ module Testimonials
|
|
|
26
28
|
say "\nNPS table queued. Run `rails db:migrate`, then set", :green
|
|
27
29
|
say 'config.nps = true in config/initializers/testimonials.rb to turn the flow on.'
|
|
28
30
|
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def migration_version
|
|
33
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
34
|
-
end
|
|
35
31
|
end
|
|
36
32
|
end
|
|
37
33
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateTestimonialsNpsResponses < ActiveRecord::Migration<%= migration_version %>
|
|
4
4
|
def change
|
|
5
|
-
create_table :testimonials_nps_responses do |t|
|
|
5
|
+
create_table :testimonials_nps_responses<%= primary_key_type_option %> do |t|
|
|
6
6
|
t.integer :score, null: false
|
|
7
7
|
t.text :comment
|
|
8
8
|
t.string :author_id
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'rails/generators'
|
|
4
4
|
require 'rails/generators/active_record'
|
|
5
|
+
require_relative '../migration_helpers'
|
|
5
6
|
|
|
6
7
|
module Testimonials
|
|
7
8
|
module Generators
|
|
@@ -13,6 +14,7 @@ module Testimonials
|
|
|
13
14
|
# bin/rails generate testimonials:prompt_events && bin/rails db:migrate
|
|
14
15
|
class PromptEventsGenerator < Rails::Generators::Base
|
|
15
16
|
include ActiveRecord::Generators::Migration
|
|
17
|
+
include MigrationHelpers
|
|
16
18
|
|
|
17
19
|
source_root File.expand_path('templates', __dir__)
|
|
18
20
|
|
|
@@ -28,12 +30,6 @@ module Testimonials
|
|
|
28
30
|
say 'config.prompt_events = true in config/initializers/testimonials.rb to let'
|
|
29
31
|
say 'testimonial_prompt! auto-open the widget again.'
|
|
30
32
|
end
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
def migration_version
|
|
35
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
36
|
-
end
|
|
37
33
|
end
|
|
38
34
|
end
|
|
39
35
|
end
|
data/lib/generators/testimonials/prompt_events/templates/create_testimonials_prompt_events.rb.tt
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateTestimonialsPromptEvents < ActiveRecord::Migration<%= migration_version %>
|
|
4
4
|
def change
|
|
5
|
-
create_table :testimonials_prompt_events do |t|
|
|
5
|
+
create_table :testimonials_prompt_events<%= primary_key_type_option %> do |t|
|
|
6
6
|
t.string :kind, null: false
|
|
7
7
|
t.string :action, null: false
|
|
8
8
|
t.string :author_id
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'rails/generators'
|
|
4
4
|
require 'rails/generators/active_record'
|
|
5
|
+
require_relative '../migration_helpers'
|
|
5
6
|
|
|
6
7
|
module Testimonials
|
|
7
8
|
module Generators
|
|
@@ -15,6 +16,7 @@ module Testimonials
|
|
|
15
16
|
# bin/rails generate testimonials:tenant && bin/rails db:migrate
|
|
16
17
|
class TenantGenerator < Rails::Generators::Base
|
|
17
18
|
include ActiveRecord::Generators::Migration
|
|
19
|
+
include MigrationHelpers
|
|
18
20
|
|
|
19
21
|
source_root File.expand_path('templates', __dir__)
|
|
20
22
|
|
|
@@ -29,12 +31,6 @@ module Testimonials
|
|
|
29
31
|
say "\ntenant column queued. Run `rails db:migrate`, then set", :green
|
|
30
32
|
say 'config.tenant in config/initializers/testimonials.rb to scope per tenant.'
|
|
31
33
|
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def migration_version
|
|
36
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
37
|
-
end
|
|
38
34
|
end
|
|
39
35
|
end
|
|
40
36
|
end
|
|
@@ -5,6 +5,10 @@ module Testimonials
|
|
|
5
5
|
# works with zero configuration; the hooks below let an app decide who gets
|
|
6
6
|
# prompted, who can triage, and how submissions are attributed.
|
|
7
7
|
class Configuration
|
|
8
|
+
# The gem's own dashboard layout. Compared against, so DashboardController
|
|
9
|
+
# can tell "the host left this alone" from "the host chose this".
|
|
10
|
+
DEFAULT_ADMIN_LAYOUT = 'testimonials/application'
|
|
11
|
+
|
|
8
12
|
# Shown in the widget ("Enjoying %{app}?") and interpolated into the
|
|
9
13
|
# default questions. nil resolves to the Rails application name.
|
|
10
14
|
attr_accessor :app_name
|
|
@@ -19,8 +23,26 @@ module Testimonials
|
|
|
19
23
|
|
|
20
24
|
# Layout used by the built-in dashboard. Override this to render
|
|
21
25
|
# Testimonials inside your app's admin shell, e.g. "admin/application".
|
|
26
|
+
# The lighter of the two ways to adopt a host's chrome; see
|
|
27
|
+
# base_controller_class for the other.
|
|
22
28
|
attr_accessor :admin_layout
|
|
23
29
|
|
|
30
|
+
# The controller the DASHBOARD inherits from, as a String so it resolves
|
|
31
|
+
# lazily rather than at config time. Default: a plain
|
|
32
|
+
# 'ActionController::Base', where `authorize_admin` is the only gate.
|
|
33
|
+
#
|
|
34
|
+
# Name the controller your own admin already inherits from and the
|
|
35
|
+
# dashboard adopts that whole stack — layout, helpers, authentication, and
|
|
36
|
+
# any request context your before_actions set up (a `Current` attribute the
|
|
37
|
+
# layout reads, say). `admin_layout` covers only the layout, which leaves a
|
|
38
|
+
# host layout calling its own helpers to raise NameError under the engine's
|
|
39
|
+
# isolated namespace.
|
|
40
|
+
#
|
|
41
|
+
# Only the dashboard uses it. The widget's endpoints stay on the engine's
|
|
42
|
+
# own public controller, so an admin base controller here can never demand
|
|
43
|
+
# a staff session from a member leaving a review.
|
|
44
|
+
attr_accessor :base_controller_class
|
|
45
|
+
|
|
24
46
|
# Resolve the current user for attribution (optional). Return an object
|
|
25
47
|
# responding to #id, or nil. Receives the request.
|
|
26
48
|
attr_accessor :current_user
|
|
@@ -117,7 +139,8 @@ module Testimonials
|
|
|
117
139
|
@app_name = nil
|
|
118
140
|
@enabled = ->(_request) { true }
|
|
119
141
|
@authorize_admin = ->(_request) { Rails.env.development? }
|
|
120
|
-
@admin_layout =
|
|
142
|
+
@admin_layout = DEFAULT_ADMIN_LAYOUT
|
|
143
|
+
@base_controller_class = 'ActionController::Base'
|
|
121
144
|
@current_user = ->(_request) {}
|
|
122
145
|
@tenant = ->(_request) {}
|
|
123
146
|
@user_display = lambda { |user|
|