nquery 0.1.0 → 0.1.1
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/CHANGELOG.md +26 -0
- data/README.md +263 -24
- data/app/assets/builds/nquery/application.css +61 -4
- data/app/assets/builds/nquery/application.js +107 -12
- data/app/controllers/concerns/nquery/breadcrumbs.rb +5 -3
- data/app/controllers/concerns/nquery/chart_actions.rb +1 -0
- data/app/controllers/concerns/nquery/chart_results.rb +17 -3
- data/app/controllers/nquery/admin/data_sources_controller.rb +49 -2
- data/app/controllers/nquery/admin/groups_controller.rb +8 -2
- data/app/controllers/nquery/admin/users_controller.rb +6 -1
- data/app/controllers/nquery/application_controller.rb +45 -28
- data/app/controllers/nquery/dashboards_controller.rb +30 -0
- data/app/controllers/nquery/embed/charts_controller.rb +1 -0
- data/app/controllers/nquery/embed/dashboards_controller.rb +1 -0
- data/app/controllers/nquery/errors_controller.rb +11 -0
- data/app/controllers/nquery/home_controller.rb +1 -0
- data/app/controllers/nquery/onboarding/admins_controller.rb +42 -0
- data/app/controllers/nquery/onboarding/base_controller.rb +27 -0
- data/app/controllers/nquery/onboarding/companies_controller.rb +30 -0
- data/app/controllers/nquery/onboarding/confirmations_controller.rb +48 -0
- data/app/controllers/nquery/onboarding/congrats_controller.rb +15 -0
- data/app/controllers/nquery/queries_controller.rb +0 -1
- data/app/controllers/nquery/sessions_controller.rb +14 -17
- data/app/helpers/nquery/data_sources_helper.rb +13 -0
- data/app/helpers/nquery/groups_helper.rb +19 -0
- data/app/javascript/nquery/controllers/data_source_form_controller.js +25 -0
- data/app/mailers/nquery/application_mailer.rb +7 -0
- data/app/mailers/nquery/devise_mailer.rb +9 -0
- data/app/models/nquery/audit.rb +2 -2
- data/app/models/nquery/collection.rb +1 -1
- data/app/models/nquery/dashboard.rb +1 -1
- data/app/models/nquery/data_source.rb +139 -4
- data/app/models/nquery/organization.rb +38 -0
- data/app/models/nquery/query.rb +8 -0
- data/app/models/nquery/user.rb +45 -10
- data/app/views/nquery/admin/data_sources/_form.html.erb +121 -0
- data/app/views/nquery/admin/data_sources/edit.html.erb +6 -8
- data/app/views/nquery/admin/data_sources/new.html.erb +6 -7
- data/app/views/nquery/admin/groups/_form.html.erb +47 -0
- data/app/views/nquery/admin/groups/edit.html.erb +15 -6
- data/app/views/nquery/admin/groups/index.html.erb +25 -20
- data/app/views/nquery/admin/groups/new.html.erb +6 -6
- data/app/views/nquery/admin/groups/show.html.erb +98 -19
- data/app/views/nquery/admin/logs/index.html.erb +26 -30
- data/app/views/nquery/charts/_builder_form.html.erb +6 -4
- data/app/views/nquery/charts/edit.html.erb +2 -1
- data/app/views/nquery/dashboards/index.html.erb +6 -0
- data/app/views/nquery/dashboards/new.html.erb +34 -0
- data/app/views/nquery/devise_mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/nquery/devise_mailer/confirmation_instructions.text.erb +5 -0
- data/app/views/nquery/errors/internal_server_error.html.erb +10 -0
- data/app/views/nquery/errors/not_found.html.erb +10 -0
- data/app/views/nquery/home/index.html.erb +7 -15
- data/app/views/nquery/onboarding/admins/new.html.erb +21 -0
- data/app/views/nquery/onboarding/companies/new.html.erb +25 -0
- data/app/views/nquery/onboarding/confirmations/show.html.erb +17 -0
- data/app/views/nquery/onboarding/congrats/show.html.erb +11 -0
- data/app/views/nquery/sessions/new.html.erb +8 -9
- data/app/views/nquery/shared/_auth_shell.html.erb +1 -1
- data/app/views/nquery/shared/_topbar.html.erb +0 -4
- data/config/routes.rb +26 -7
- data/db/migrate/20260721000001_create_nquery_tables.rb +1 -1
- data/db/migrate/20260722000001_add_key_to_nquery_data_sources.rb +8 -0
- data/db/migrate/20260722000002_create_nquery_organizations.rb +11 -0
- data/db/migrate/20260722000003_add_devise_to_nquery_users.rb +35 -0
- data/db/migrate/20260722000004_add_onboarding_completed_at_to_nquery_organizations.rb +7 -0
- data/lib/generators/nquery/install_generator.rb +9 -17
- data/lib/nquery/configuration.rb +7 -23
- data/lib/nquery/csv_importer.rb +3 -3
- data/lib/nquery/data_sources/adapter.rb +4 -0
- data/lib/nquery/data_sources/postgresql_adapter.rb +15 -1
- data/lib/nquery/data_sources/rails_adapter.rb +5 -0
- data/lib/nquery/data_sources/syncer.rb +50 -0
- data/lib/nquery/encryption.rb +50 -0
- data/lib/nquery/engine.rb +42 -3
- data/lib/nquery/onboarding/admin_provisioner.rb +40 -0
- data/lib/nquery/onboarding/password_confirmation.rb +46 -0
- data/lib/nquery/onboarding.rb +40 -0
- data/lib/nquery/permissions/resolver.rb +14 -4
- data/lib/nquery/query_runner.rb +5 -9
- data/lib/nquery/readonly_sql.rb +25 -0
- data/lib/nquery/seeder.rb +25 -34
- data/lib/nquery/setup.rb +75 -0
- data/lib/nquery/version.rb +1 -1
- data/lib/nquery.rb +9 -0
- data/lib/tasks/nquery.rake +6 -0
- data/nquery.gemspec +9 -4
- metadata +131 -23
- data/app/controllers/nquery/registrations_controller.rb +0 -31
- data/app/views/nquery/registrations/new.html.erb +0 -34
|
@@ -1,24 +1,103 @@
|
|
|
1
|
-
<div class="nq-page-header"
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
<div class="nq-page-header">
|
|
2
|
+
<h1><%= @group.name %></h1>
|
|
3
|
+
<p class="nq-muted nq-resource-meta">
|
|
4
|
+
<span class="nq-badge <%= group_type_badge_class(@group) %>"><%= group_type_label(@group) %></span>
|
|
5
|
+
· <%= pluralize(@members.size, "member") %>
|
|
6
|
+
</p>
|
|
7
|
+
<div class="nq-actions">
|
|
8
|
+
<%= link_to edit_admin_group_path(@group), class: "nq-btn" do %>
|
|
9
|
+
<%= nq_icon(:edit) %>
|
|
10
|
+
<span>Edit</span>
|
|
11
|
+
<% end %>
|
|
12
|
+
<%= link_to by_group_admin_permissions_path(group_id: @group.id), class: "nq-btn" do %>
|
|
13
|
+
<span>Permissions</span>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<% if @group.description.present? %>
|
|
19
|
+
<div class="nq-card nq-group-description nq-mb">
|
|
20
|
+
<p><%= @group.description %></p>
|
|
21
|
+
</div>
|
|
22
|
+
<% end %>
|
|
23
|
+
|
|
24
|
+
<div class="nq-grid nq-grid-2 nq-group-layout">
|
|
25
|
+
<section class="nq-section">
|
|
26
|
+
<h2 class="nq-section-title">Members</h2>
|
|
27
|
+
|
|
28
|
+
<% if @members.any? %>
|
|
29
|
+
<div class="nq-card nq-card-table">
|
|
30
|
+
<table class="nq-table">
|
|
31
|
+
<thead>
|
|
32
|
+
<tr>
|
|
33
|
+
<th>Name</th>
|
|
34
|
+
<th>Email</th>
|
|
35
|
+
<th></th>
|
|
36
|
+
</tr>
|
|
37
|
+
</thead>
|
|
38
|
+
<tbody>
|
|
39
|
+
<% @members.each do |user| %>
|
|
40
|
+
<tr>
|
|
41
|
+
<td><%= link_to user.name, admin_user_path(user) %></td>
|
|
42
|
+
<td class="nq-muted"><%= user.email %></td>
|
|
43
|
+
<td>
|
|
44
|
+
<% unless @group.system_group == "all_users" %>
|
|
45
|
+
<div class="nq-actions">
|
|
46
|
+
<%= button_to "Remove",
|
|
47
|
+
remove_member_admin_group_path(@group),
|
|
48
|
+
method: :delete,
|
|
49
|
+
params: { user_id: user.id },
|
|
50
|
+
class: "nq-btn nq-btn-ghost",
|
|
51
|
+
form: { onsubmit: "return confirm('#{j("Remove #{user.name} from #{@group.name}?")}');" } %>
|
|
52
|
+
</div>
|
|
53
|
+
<% end %>
|
|
54
|
+
</td>
|
|
55
|
+
</tr>
|
|
56
|
+
<% end %>
|
|
57
|
+
</tbody>
|
|
58
|
+
</table>
|
|
59
|
+
</div>
|
|
60
|
+
<% else %>
|
|
61
|
+
<div class="nq-card nq-empty-state">
|
|
62
|
+
<h2>No members yet</h2>
|
|
63
|
+
<p class="nq-muted">Add users to this group to grant them shared permissions.</p>
|
|
64
|
+
</div>
|
|
65
|
+
<% end %>
|
|
15
66
|
</section>
|
|
67
|
+
|
|
16
68
|
<% if @available_users.any? %>
|
|
17
|
-
<section class="nq-
|
|
18
|
-
<h2>Add member</h2>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
69
|
+
<section class="nq-section">
|
|
70
|
+
<h2 class="nq-section-title">Add member</h2>
|
|
71
|
+
|
|
72
|
+
<div class="nq-card">
|
|
73
|
+
<%= form_with url: add_member_admin_group_path(@group), method: :post, class: "nq-form nq-add-member-form" do %>
|
|
74
|
+
<div class="nq-form-row">
|
|
75
|
+
<%= label_tag :user_id, "User" %>
|
|
76
|
+
<%= select_tag :user_id,
|
|
77
|
+
options_from_collection_for_select(@available_users, :id, :name_with_email),
|
|
78
|
+
include_blank: "Select a user…",
|
|
79
|
+
class: "nq-input",
|
|
80
|
+
required: true %>
|
|
81
|
+
<p class="nq-field-hint">Only active users who are not already in this group are listed.</p>
|
|
82
|
+
</div>
|
|
83
|
+
<%= submit_tag "Add to group", class: "nq-btn nq-btn-primary" %>
|
|
84
|
+
<% end %>
|
|
85
|
+
</div>
|
|
86
|
+
</section>
|
|
87
|
+
<% elsif @group.system_group == "all_users" %>
|
|
88
|
+
<section class="nq-section">
|
|
89
|
+
<h2 class="nq-section-title">Add member</h2>
|
|
90
|
+
<div class="nq-card">
|
|
91
|
+
<%= nq_flash_card "Every active user is automatically added to this group.", type: :info, toast: false %>
|
|
92
|
+
</div>
|
|
93
|
+
</section>
|
|
94
|
+
<% else %>
|
|
95
|
+
<section class="nq-section">
|
|
96
|
+
<h2 class="nq-section-title">Add member</h2>
|
|
97
|
+
<div class="nq-card nq-empty-state">
|
|
98
|
+
<h2>Everyone is already here</h2>
|
|
99
|
+
<p class="nq-muted">All active users are already members of this group.</p>
|
|
100
|
+
</div>
|
|
22
101
|
</section>
|
|
23
102
|
<% end %>
|
|
24
103
|
</div>
|
|
@@ -40,35 +40,31 @@
|
|
|
40
40
|
<% end %>
|
|
41
41
|
</section>
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
<%= nq_card_table columns: ["User", "Query", "Collection", "Dashboard", "Status", "When"], class: "nq-mt nq-logs-table" do %>
|
|
44
|
+
<% if @audits.any? %>
|
|
45
|
+
<% @audits.each do |audit| %>
|
|
46
|
+
<%
|
|
47
|
+
statement = audit.statement.to_s
|
|
48
|
+
status_class = audit.status == "success" ? "nq-badge-success" : "nq-badge-error"
|
|
49
|
+
%>
|
|
46
50
|
<tr>
|
|
47
|
-
<
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
<
|
|
51
|
+
<td data-label="User"><%= audit.user&.name || "—" %></td>
|
|
52
|
+
<td data-label="Query">
|
|
53
|
+
<code class="nq-logs-query" title="<%= statement %>"><%= truncate(statement, length: 80) %></code>
|
|
54
|
+
</td>
|
|
55
|
+
<td data-label="Collection"><%= audit.query&.collection&.name || "—" %></td>
|
|
56
|
+
<td data-label="Dashboard"><%= audit.query&.chart&.dashboard_cards&.first&.dashboard&.name || "—" %></td>
|
|
57
|
+
<td data-label="Status"><span class="nq-badge <%= status_class %>"><%= audit.status %></span></td>
|
|
58
|
+
<td data-label="When">
|
|
59
|
+
<time datetime="<%= audit.created_at.iso8601 %>" title="<%= l(audit.created_at, format: :long) %>">
|
|
60
|
+
<%= time_ago_in_words(audit.created_at) %> ago
|
|
61
|
+
</time>
|
|
62
|
+
</td>
|
|
53
63
|
</tr>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<td><%= audit.query&.collection&.name || "—" %></td>
|
|
62
|
-
<td><%= audit.query&.chart&.dashboard_cards&.first&.dashboard&.name || "—" %></td>
|
|
63
|
-
<td><span class="nq-badge"><%= audit.status %></span></td>
|
|
64
|
-
<td><%= time_ago_in_words(audit.created_at) %> ago</td>
|
|
65
|
-
</tr>
|
|
66
|
-
<% end %>
|
|
67
|
-
<% else %>
|
|
68
|
-
<tr>
|
|
69
|
-
<td colspan="6" class="nq-muted">No query logs found.</td>
|
|
70
|
-
</tr>
|
|
71
|
-
<% end %>
|
|
72
|
-
</tbody>
|
|
73
|
-
</table>
|
|
74
|
-
</section>
|
|
64
|
+
<% end %>
|
|
65
|
+
<% else %>
|
|
66
|
+
<tr class="nq-logs-empty">
|
|
67
|
+
<td colspan="6" class="nq-muted">No query logs found.</td>
|
|
68
|
+
</tr>
|
|
69
|
+
<% end %>
|
|
70
|
+
<% end %>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<% viz = chart.visualization.is_a?(Hash) ? chart.visualization : {} %>
|
|
2
2
|
<% chart_type = viz["type"].presence || "table" %>
|
|
3
3
|
<% active_chart_type = chart_builder_chart_types.include?(chart_type) ? chart_type : "bar" %>
|
|
4
|
+
<% builder_data = {
|
|
5
|
+
controller: "chart-builder",
|
|
6
|
+
query_save_url: chart.query&.persisted? ? query_path(chart.query) : nil
|
|
7
|
+
} %>
|
|
8
|
+
<% builder_data[:initial_result] = result if local_assigns[:result].present? %>
|
|
4
9
|
<%= form_with model: chart, url: form_url, class: "nq-form nq-chart-builder-form", html: {
|
|
5
|
-
data:
|
|
6
|
-
controller: "chart-builder",
|
|
7
|
-
query_save_url: chart.query&.persisted? ? query_path(chart.query) : nil
|
|
8
|
-
}
|
|
10
|
+
data: builder_data
|
|
9
11
|
} do |f| %>
|
|
10
12
|
<%= render "nquery/charts/new_header",
|
|
11
13
|
f: f,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
<div class="nq-page-header">
|
|
2
2
|
<h1>Dashboards</h1>
|
|
3
|
+
<% if @collections.any? %>
|
|
4
|
+
<%= link_to new_dashboard_path, class: "nq-btn nq-btn-primary" do %>
|
|
5
|
+
<%= nq_icon(:plus) %>
|
|
6
|
+
<span>New dashboard</span>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% end %>
|
|
3
9
|
</div>
|
|
4
10
|
|
|
5
11
|
<div class="nq-card nq-card-table">
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<div class="nq-page-header">
|
|
2
|
+
<h1>New dashboard</h1>
|
|
3
|
+
<p class="nq-muted nq-resource-meta">Choose a collection, then add charts after you create it.</p>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<%= form_with model: @dashboard, url: dashboards_path, class: "nq-form nq-card" do |f| %>
|
|
7
|
+
<% if @dashboard.errors.any? %>
|
|
8
|
+
<div class="nq-form-errors" role="alert">
|
|
9
|
+
<p><strong>Please fix the following:</strong></p>
|
|
10
|
+
<ul>
|
|
11
|
+
<% @dashboard.errors.full_messages.each do |message| %>
|
|
12
|
+
<li><%= message %></li>
|
|
13
|
+
<% end %>
|
|
14
|
+
</ul>
|
|
15
|
+
</div>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<div class="nq-form-row">
|
|
19
|
+
<%= f.label :name %>
|
|
20
|
+
<%= f.text_field :name, class: "nq-input", autofocus: true %>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="nq-form-row">
|
|
23
|
+
<%= f.label :description %>
|
|
24
|
+
<%= f.text_area :description, class: "nq-textarea", rows: 2 %>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="nq-form-row">
|
|
27
|
+
<%= f.label :collection_id, "Collection" %>
|
|
28
|
+
<%= f.collection_select :collection_id, @collections, :id, :name, { prompt: "Select a collection" }, class: "nq-input" %>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="nq-form-actions">
|
|
31
|
+
<%= link_to "Cancel", dashboards_path, class: "nq-btn" %>
|
|
32
|
+
<%= f.submit "Create", class: "nq-btn nq-btn-primary" %>
|
|
33
|
+
</div>
|
|
34
|
+
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= render layout: "nquery/shared/auth_shell",
|
|
2
|
+
locals: {
|
|
3
|
+
title: "Something went wrong",
|
|
4
|
+
subtitle: "We hit an unexpected error. Please try again in a moment.",
|
|
5
|
+
footer_text: "Head back to",
|
|
6
|
+
footer_label: "home",
|
|
7
|
+
footer_link: root_path
|
|
8
|
+
} do %>
|
|
9
|
+
<p class="nq-muted">Error 500</p>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= render layout: "nquery/shared/auth_shell",
|
|
2
|
+
locals: {
|
|
3
|
+
title: "Page not found",
|
|
4
|
+
subtitle: "The page you were looking for doesn’t exist or is no longer available.",
|
|
5
|
+
footer_text: "Head back to",
|
|
6
|
+
footer_label: "home",
|
|
7
|
+
footer_link: root_path
|
|
8
|
+
} do %>
|
|
9
|
+
<p class="nq-muted">Error 404</p>
|
|
10
|
+
<% end %>
|
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
<%= nq_icon(:chart) %>
|
|
12
12
|
<span>New question</span>
|
|
13
13
|
<% end %>
|
|
14
|
-
|
|
15
|
-
<%=
|
|
16
|
-
|
|
14
|
+
<% if @collections.any? %>
|
|
15
|
+
<%= link_to new_dashboard_path, class: "nq-btn nq-btn-primary nq-home-hero-cta" do %>
|
|
16
|
+
<%= nq_icon(:plus) %>
|
|
17
|
+
<span>New dashboard</span>
|
|
18
|
+
<% end %>
|
|
17
19
|
<% end %>
|
|
18
20
|
</div>
|
|
19
21
|
<% end %>
|
|
@@ -35,16 +37,6 @@
|
|
|
35
37
|
<span class="nq-home-action-label">Dashboards</span>
|
|
36
38
|
<span class="nq-muted nq-home-action-desc">View all your dashboards</span>
|
|
37
39
|
<% end %>
|
|
38
|
-
|
|
39
|
-
<% if @root_collection %>
|
|
40
|
-
<%= link_to collection_path(@root_collection), class: "nq-home-action-card" do %>
|
|
41
|
-
<span class="nq-home-action-icon nq-home-action-icon-chart">
|
|
42
|
-
<%= nq_icon(:chart) %>
|
|
43
|
-
</span>
|
|
44
|
-
<span class="nq-home-action-label">Analytics</span>
|
|
45
|
-
<span class="nq-muted nq-home-action-desc">Open <%= @root_collection.name %></span>
|
|
46
|
-
<% end %>
|
|
47
|
-
<% end %>
|
|
48
40
|
</section>
|
|
49
41
|
|
|
50
42
|
<div class="nq-home-recents">
|
|
@@ -86,8 +78,8 @@
|
|
|
86
78
|
<div class="nq-home-empty">
|
|
87
79
|
<%= nq_icon(:dashboard, class: "nq-icon-lg") %>
|
|
88
80
|
<p class="nq-muted">No dashboards yet</p>
|
|
89
|
-
<% if @
|
|
90
|
-
<%= link_to
|
|
81
|
+
<% if @collections.any? %>
|
|
82
|
+
<%= link_to new_dashboard_path, class: "nq-btn nq-btn-primary" do %>
|
|
91
83
|
<%= nq_icon(:plus) %>
|
|
92
84
|
<span>Create dashboard</span>
|
|
93
85
|
<% end %>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= render layout: "nquery/shared/auth_shell",
|
|
2
|
+
locals: {
|
|
3
|
+
title: "Create your admin account",
|
|
4
|
+
subtitle: "We'll email you a link to set your password."
|
|
5
|
+
} do %>
|
|
6
|
+
<%= form_with model: @user, url: onboarding_admin_path, class: "nq-form nq-auth-form" do |form| %>
|
|
7
|
+
<div class="nq-form-row">
|
|
8
|
+
<%= form.label :first_name, "First name" %>
|
|
9
|
+
<%= form.text_field :first_name, class: "nq-input", required: true, autofocus: true %>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="nq-form-row">
|
|
12
|
+
<%= form.label :last_name, "Last name" %>
|
|
13
|
+
<%= form.text_field :last_name, class: "nq-input", required: true %>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="nq-form-row">
|
|
16
|
+
<%= form.label :email, "Email address" %>
|
|
17
|
+
<%= form.email_field :email, class: "nq-input", required: true, autocomplete: "email" %>
|
|
18
|
+
</div>
|
|
19
|
+
<button type="submit" class="nq-btn nq-btn-primary nq-btn-block nq-auth-submit">Send confirmation email</button>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%= render layout: "nquery/shared/auth_shell",
|
|
2
|
+
locals: {
|
|
3
|
+
title: "Set up your company",
|
|
4
|
+
subtitle: "Tell us about your organization to get started."
|
|
5
|
+
} do %>
|
|
6
|
+
<%= form_with model: @organization, url: onboarding_company_path, class: "nq-form nq-auth-form" do |form| %>
|
|
7
|
+
<div class="nq-form-row">
|
|
8
|
+
<%= form.label :name, "Company name" %>
|
|
9
|
+
<%= form.text_field :name, class: "nq-input", required: true, autofocus: true %>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="nq-form-row">
|
|
12
|
+
<%= form.label :website, "Website" %>
|
|
13
|
+
<%= form.url_field :website, class: "nq-input", placeholder: "https://example.com" %>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="nq-form-row">
|
|
16
|
+
<%= form.label :logo, "Logo" %>
|
|
17
|
+
<%= form.file_field :logo, class: "nq-input" %>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="nq-form-row">
|
|
20
|
+
<%= form.label :cover_image, "Cover image" %>
|
|
21
|
+
<%= form.file_field :cover_image, class: "nq-input" %>
|
|
22
|
+
</div>
|
|
23
|
+
<button type="submit" class="nq-btn nq-btn-primary nq-btn-block nq-auth-submit">Continue</button>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% end %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%= render layout: "nquery/shared/auth_shell",
|
|
2
|
+
locals: {
|
|
3
|
+
title: "Set your password",
|
|
4
|
+
subtitle: "Choose a password for your admin account."
|
|
5
|
+
} do %>
|
|
6
|
+
<%= form_with model: @user, url: onboarding_confirm_path(confirmation_token: params[:confirmation_token]), method: :patch, class: "nq-form nq-auth-form" do |form| %>
|
|
7
|
+
<div class="nq-form-row">
|
|
8
|
+
<%= form.label :password, "Password" %>
|
|
9
|
+
<%= form.password_field :password, class: "nq-input", required: true, autofocus: true, autocomplete: "new-password" %>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="nq-form-row">
|
|
12
|
+
<%= form.label :password_confirmation, "Confirm password" %>
|
|
13
|
+
<%= form.password_field :password_confirmation, class: "nq-input", required: true, autocomplete: "new-password" %>
|
|
14
|
+
</div>
|
|
15
|
+
<button type="submit" class="nq-btn nq-btn-primary nq-btn-block nq-auth-submit">Confirm and sign in</button>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%= render layout: "nquery/shared/auth_shell",
|
|
2
|
+
locals: {
|
|
3
|
+
title: "Check your email",
|
|
4
|
+
subtitle: if @email.present?
|
|
5
|
+
"We sent a confirmation link to #{@email}. Follow it to set your password and sign in."
|
|
6
|
+
else
|
|
7
|
+
"We sent a confirmation link to your administrator email. Follow it to set your password and sign in."
|
|
8
|
+
end
|
|
9
|
+
} do %>
|
|
10
|
+
<p class="nq-muted">Didn't receive it? Check spam or contact your administrator.</p>
|
|
11
|
+
<% end %>
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
<%= render layout: "nquery/shared/auth_shell",
|
|
2
2
|
locals: {
|
|
3
3
|
title: "Welcome back",
|
|
4
|
-
subtitle: "Sign in to explore your charts and dashboards."
|
|
5
|
-
footer_text: "Don't have an account?",
|
|
6
|
-
footer_link: signup_path,
|
|
7
|
-
footer_label: "Create an account"
|
|
4
|
+
subtitle: "Sign in to explore your charts and dashboards."
|
|
8
5
|
} do %>
|
|
9
|
-
<%= form_with url:
|
|
6
|
+
<%= form_with url: nquery_user_session_path,
|
|
7
|
+
scope: :nquery_user,
|
|
8
|
+
html: { class: "nq-form nq-auth-form", id: "new_nquery_user" } do |form| %>
|
|
10
9
|
<div class="nq-form-row">
|
|
11
|
-
<%=
|
|
12
|
-
<%=
|
|
10
|
+
<%= form.label :email, "Email address" %>
|
|
11
|
+
<%= form.email_field :email,
|
|
13
12
|
class: "nq-input",
|
|
14
13
|
required: true,
|
|
15
14
|
autofocus: true,
|
|
@@ -17,8 +16,8 @@
|
|
|
17
16
|
placeholder: "you@company.com" %>
|
|
18
17
|
</div>
|
|
19
18
|
<div class="nq-form-row">
|
|
20
|
-
<%=
|
|
21
|
-
<%=
|
|
19
|
+
<%= form.label :password, "Password" %>
|
|
20
|
+
<%= form.password_field :password,
|
|
22
21
|
class: "nq-input",
|
|
23
22
|
required: true,
|
|
24
23
|
autocomplete: "current-password",
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
<header class="nq-topbar">
|
|
2
|
-
<div class="nq-search">
|
|
3
|
-
<input type="search" placeholder="Search charts, dashboards..." class="nq-input" />
|
|
4
|
-
</div>
|
|
5
2
|
<div class="nq-topbar-actions">
|
|
6
|
-
<span class="nq-badge"><%= Nquery::DataSource.active.first&.name || "Main DB" %></span>
|
|
7
3
|
<% if current_nquery_user %>
|
|
8
4
|
<span class="nq-user"><%= current_nquery_user.name %></span>
|
|
9
5
|
<%= button_to "Sign out", logout_path, method: :delete, class: "nq-btn nq-btn-ghost" %>
|
data/config/routes.rb
CHANGED
|
@@ -5,6 +5,14 @@ Nquery::Engine.routes.draw do
|
|
|
5
5
|
|
|
6
6
|
get "browse", to: redirect("/collections")
|
|
7
7
|
|
|
8
|
+
namespace :onboarding do
|
|
9
|
+
resource :company, only: %i[new create], controller: "companies"
|
|
10
|
+
resource :admin, only: %i[new create], controller: "admins"
|
|
11
|
+
get "congrats", to: "congrats#show"
|
|
12
|
+
get "confirm", to: "confirmations#show", as: :confirm
|
|
13
|
+
patch "confirm", to: "confirmations#update"
|
|
14
|
+
end
|
|
15
|
+
|
|
8
16
|
resources :collections do
|
|
9
17
|
member do
|
|
10
18
|
patch :archive
|
|
@@ -27,7 +35,7 @@ Nquery::Engine.routes.draw do
|
|
|
27
35
|
end
|
|
28
36
|
end
|
|
29
37
|
|
|
30
|
-
resources :dashboards
|
|
38
|
+
resources :dashboards do
|
|
31
39
|
resources :charts, module: :dashboard do
|
|
32
40
|
member do
|
|
33
41
|
get :embed
|
|
@@ -54,7 +62,9 @@ Nquery::Engine.routes.draw do
|
|
|
54
62
|
post :add_member, on: :member
|
|
55
63
|
delete :remove_member, on: :member
|
|
56
64
|
end
|
|
57
|
-
resources :data_sources, only: %i[index new create edit update]
|
|
65
|
+
resources :data_sources, only: %i[index new create edit update] do
|
|
66
|
+
post :test_connection, on: :collection
|
|
67
|
+
end
|
|
58
68
|
resources :logs, only: %i[index]
|
|
59
69
|
resources :permissions, only: %i[index] do
|
|
60
70
|
collection do
|
|
@@ -65,14 +75,23 @@ Nquery::Engine.routes.draw do
|
|
|
65
75
|
end
|
|
66
76
|
end
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
devise_for :nquery_users,
|
|
79
|
+
class_name: "Nquery::User",
|
|
80
|
+
path: "",
|
|
81
|
+
path_names: { sign_in: "login", sign_out: "logout" },
|
|
82
|
+
controllers: { sessions: "nquery/sessions" },
|
|
83
|
+
router_name: :nquery,
|
|
84
|
+
sign_out_via: :delete,
|
|
85
|
+
skip: %i[registrations passwords confirmations unlocks]
|
|
86
|
+
|
|
87
|
+
devise_scope :nquery_user do
|
|
88
|
+
get "users/confirmation", to: "onboarding/confirmations#show", as: :nquery_user_confirmation
|
|
89
|
+
end
|
|
73
90
|
|
|
74
91
|
namespace :embed do
|
|
75
92
|
get "charts/show", to: "charts#show", as: :public_chart
|
|
76
93
|
get "dashboards/show", to: "dashboards#show", as: :public_dashboard
|
|
77
94
|
end
|
|
95
|
+
|
|
96
|
+
match "*unmatched", to: "errors#not_found", via: :all
|
|
78
97
|
end
|
|
@@ -95,7 +95,7 @@ class CreateNqueryTables < ActiveRecord::Migration[8.0]
|
|
|
95
95
|
create_table :nquery_dashboards do |t|
|
|
96
96
|
t.string :name, null: false
|
|
97
97
|
t.text :description
|
|
98
|
-
t.references :collection, foreign_key: { to_table: :nquery_collections }
|
|
98
|
+
t.references :collection, null: false, foreign_key: { to_table: :nquery_collections }
|
|
99
99
|
t.references :creator, foreign_key: { to_table: :nquery_users }
|
|
100
100
|
t.json :settings, null: false, default: {}
|
|
101
101
|
t.timestamps
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddDeviseToNqueryUsers < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
change_table :nquery_users, bulk: true do |t|
|
|
6
|
+
t.string :encrypted_password, limit: 128
|
|
7
|
+
t.string :reset_password_token
|
|
8
|
+
t.datetime :reset_password_sent_at
|
|
9
|
+
t.datetime :remember_created_at
|
|
10
|
+
t.string :confirmation_token
|
|
11
|
+
t.datetime :confirmed_at
|
|
12
|
+
t.datetime :confirmation_sent_at
|
|
13
|
+
t.string :unconfirmed_email
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
add_index :nquery_users, :reset_password_token, unique: true
|
|
17
|
+
add_index :nquery_users, :confirmation_token, unique: true
|
|
18
|
+
|
|
19
|
+
reversible do |dir|
|
|
20
|
+
dir.up do
|
|
21
|
+
execute <<~SQL.squish
|
|
22
|
+
UPDATE nquery_users
|
|
23
|
+
SET encrypted_password = password_digest
|
|
24
|
+
WHERE encrypted_password IS NULL AND password_digest IS NOT NULL
|
|
25
|
+
SQL
|
|
26
|
+
|
|
27
|
+
execute <<~SQL.squish
|
|
28
|
+
UPDATE nquery_users
|
|
29
|
+
SET confirmed_at = CURRENT_TIMESTAMP
|
|
30
|
+
WHERE confirmed_at IS NULL AND encrypted_password IS NOT NULL
|
|
31
|
+
SQL
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|