sage-rails 0.0.3

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 (83) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +202 -0
  3. data/app/assets/images/chevron-down-zinc-500.svg +1 -0
  4. data/app/assets/images/chevron-right.svg +1 -0
  5. data/app/assets/images/loading.svg +4 -0
  6. data/app/assets/images/sage/chevron-down-zinc-500.svg +1 -0
  7. data/app/assets/images/sage/chevron-right.svg +1 -0
  8. data/app/assets/images/sage/loading.svg +4 -0
  9. data/app/assets/javascripts/sage/application.js +18 -0
  10. data/app/assets/stylesheets/sage/application.css +308 -0
  11. data/app/controllers/sage/actions_controller.rb +5 -0
  12. data/app/controllers/sage/application_controller.rb +4 -0
  13. data/app/controllers/sage/base_controller.rb +10 -0
  14. data/app/controllers/sage/checks_controller.rb +65 -0
  15. data/app/controllers/sage/dashboards_controller.rb +130 -0
  16. data/app/controllers/sage/queries/messages_controller.rb +62 -0
  17. data/app/controllers/sage/queries_controller.rb +596 -0
  18. data/app/helpers/sage/application_helper.rb +30 -0
  19. data/app/helpers/sage/queries_helper.rb +23 -0
  20. data/app/javascript/controllers/element_removal_controller.js +7 -0
  21. data/app/javascript/sage/controllers/clipboard_controller.js +26 -0
  22. data/app/javascript/sage/controllers/dashboard_controller.js +132 -0
  23. data/app/javascript/sage/controllers/reverse_infinite_scroll_controller.js +146 -0
  24. data/app/javascript/sage/controllers/search_controller.js +47 -0
  25. data/app/javascript/sage/controllers/select_controller.js +215 -0
  26. data/app/javascript/sage.js +19 -0
  27. data/app/jobs/sage/application_job.rb +4 -0
  28. data/app/jobs/sage/process_report_job.rb +80 -0
  29. data/app/mailers/sage/application_mailer.rb +6 -0
  30. data/app/models/sage/application_record.rb +5 -0
  31. data/app/models/sage/message.rb +8 -0
  32. data/app/schemas/sage/report_response_schema.rb +8 -0
  33. data/app/views/layouts/application.html.erb +34 -0
  34. data/app/views/layouts/sage/application.html.erb +94 -0
  35. data/app/views/sage/checks/_form.html.erb +81 -0
  36. data/app/views/sage/checks/_search.html.erb +8 -0
  37. data/app/views/sage/checks/edit.html.erb +10 -0
  38. data/app/views/sage/checks/index.html.erb +58 -0
  39. data/app/views/sage/checks/new.html.erb +8 -0
  40. data/app/views/sage/dashboards/_form.html.erb +50 -0
  41. data/app/views/sage/dashboards/_search.html.erb +8 -0
  42. data/app/views/sage/dashboards/index.html.erb +58 -0
  43. data/app/views/sage/dashboards/new.html.erb +8 -0
  44. data/app/views/sage/dashboards/show.html.erb +58 -0
  45. data/app/views/sage/messages/_form.html.erb +14 -0
  46. data/app/views/sage/queries/_caching.html.erb +17 -0
  47. data/app/views/sage/queries/_form.html.erb +72 -0
  48. data/app/views/sage/queries/_input.html.erb +17 -0
  49. data/app/views/sage/queries/_message.html.erb +25 -0
  50. data/app/views/sage/queries/_message.turbo_stream.erb +10 -0
  51. data/app/views/sage/queries/_new_form.html.erb +43 -0
  52. data/app/views/sage/queries/_run.html.erb +232 -0
  53. data/app/views/sage/queries/_search.html.erb +8 -0
  54. data/app/views/sage/queries/_statement_box.html.erb +241 -0
  55. data/app/views/sage/queries/_streaming_message.html.erb +14 -0
  56. data/app/views/sage/queries/create.turbo_stream.erb +114 -0
  57. data/app/views/sage/queries/edit.html.erb +48 -0
  58. data/app/views/sage/queries/index.html.erb +59 -0
  59. data/app/views/sage/queries/messages/create.turbo_stream.erb +22 -0
  60. data/app/views/sage/queries/messages/index.html.erb +44 -0
  61. data/app/views/sage/queries/messages/index.turbo_stream.erb +15 -0
  62. data/app/views/sage/queries/new.html.erb +195 -0
  63. data/app/views/sage/queries/run.html.erb +1 -0
  64. data/app/views/sage/queries/run.turbo_stream.erb +3 -0
  65. data/app/views/sage/queries/show.html.erb +49 -0
  66. data/app/views/sage/queries/table_schema.html.erb +77 -0
  67. data/app/views/sage/shared/_navigation.html.erb +26 -0
  68. data/app/views/sage/shared/_overlay.html.erb +11 -0
  69. data/config/importmap.rb +11 -0
  70. data/config/initializers/pagy.rb +2 -0
  71. data/config/initializers/ransack.rb +152 -0
  72. data/config/routes.rb +31 -0
  73. data/lib/generators/sage/USAGE +13 -0
  74. data/lib/generators/sage/install/install_generator.rb +128 -0
  75. data/lib/generators/sage/install/templates/sage.rb +22 -0
  76. data/lib/sage/database_schema_context.rb +56 -0
  77. data/lib/sage/engine.rb +260 -0
  78. data/lib/sage/model_scopes_context.rb +185 -0
  79. data/lib/sage/report_processor.rb +263 -0
  80. data/lib/sage/version.rb +3 -0
  81. data/lib/sage.rb +25 -0
  82. data/lib/tasks/sage_tasks.rake +4 -0
  83. metadata +245 -0
@@ -0,0 +1,48 @@
1
+ <% blazer_title "Edit Query" %>
2
+ <%#= render partial: "form" %>
3
+
4
+ <div class="row padding left-align">
5
+ <h5 class="">Edit Query</h5>
6
+ <%= link_to 'Back', query_path(@query) %>
7
+ </div>
8
+
9
+ <div class='grid'>
10
+ <div class='s12 m6 l3 border rounded' style='height: 88vh; display: flex; flex-direction: column;'>
11
+ <!-- Chat Messages Area -->
12
+ <%= turbo_stream_from "messages" %>
13
+ <div id=<%= dom_id(@query, 'messages') %> style='flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column;'
14
+ data-controller="sage--reverse-infinite-scroll"
15
+ data-sage--reverse-infinite-scroll-url-value="<%= query_messages_path(@query) %>"
16
+ data-sage--reverse-infinite-scroll-page-value="1">
17
+
18
+ <!-- Messages container -->
19
+ <div data-sage--reverse-infinite-scroll-target="entries" id="entries">
20
+ <%= turbo_frame_tag "messages-content", src: query_messages_path(@query), loading: :lazy do %>
21
+ <div class="center-align padding">
22
+ <small class="secondary-text">Loading messages...</small>
23
+ </div>
24
+ <% end %>
25
+ </div>
26
+ </div>
27
+
28
+ <!-- Chat Input Form -->
29
+ <%= render partial: 'input', locals: { query: @query } %>
30
+ </div>
31
+
32
+ <div class='m9' style='position: relative;'>
33
+ <div class='grid'>
34
+ <div class='s6'>
35
+ <%= render partial: "statement_box", locals: { query: @query } %>
36
+ </div>
37
+ <div class='s6'>
38
+ <%= render 'new_form' %>
39
+ </div>
40
+ </div>
41
+
42
+ <%= turbo_frame_tag dom_id(@query, 'results'), src: run_query_path(@query.id) do %>
43
+ Loading...
44
+ <% end %>
45
+
46
+ </div>
47
+ </div>
48
+
@@ -0,0 +1,59 @@
1
+ <div id="">
2
+ <div class="padding left-align <%= "#{current_page?(root_path) ? 'active' : ''}" %>">
3
+ <h5>Queries</h5>
4
+ </div>
5
+
6
+ <%# SUB NAVIGATION %>
7
+ <div class="right-align" style="">
8
+ <% if blazer_user %>
9
+ <%= link_to "All", root_path, class: !params[:filter] ? "active" : nil, style: "margin-right: 40px;" %>
10
+ <% if Blazer.audit %>
11
+ <%= link_to "Viewed", root_path(filter: "viewed"), class: params[:filter] == "viewed" ? "active" : nil, style: "margin-right: 40px;" %>
12
+ <% end %>
13
+
14
+ <%= link_to "Mine", root_path(filter: "mine"), class: params[:filter] == "mine" ? "active" : nil, style: "margin-right: 40px;" %>
15
+ <% end %>
16
+ <%= link_to new_query_path do %>
17
+ <button>
18
+ New Query
19
+ </button>
20
+ <% end %>
21
+ </div>
22
+ </div>
23
+
24
+ <%= render partial: 'search' %>
25
+
26
+ <%= turbo_frame_tag "queries" do %>
27
+ <% if @queries.any? %>
28
+ <table class="border">
29
+ <thead>
30
+ <tr>
31
+ <th>Name</th>
32
+ <% if Blazer.user_class %>
33
+ <th style="width: 20%;">Mastermind</th>
34
+ <% end%>
35
+ </tr>
36
+ </thead>
37
+ <tbody>
38
+ <% @queries.each do |query| %>
39
+ <tr>
40
+ <td>
41
+ <%= link_to query.name, query_path(query), class: "text-decoration-none", data: { turbo_frame: '_top' } %>
42
+ </td>
43
+ <% if Blazer.user_class %>
44
+ <td><%= query.creator&.name %></td>
45
+ <% end %>
46
+ </tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
50
+
51
+ <div style="display: flex; justify-content: center; padding: 1rem;">
52
+ <%== pagy_nav(@pagy) if @pagy.pages > 1 %>
53
+ </div>
54
+ <% else %>
55
+ <div class="mt-3">
56
+ <p>No queries found.</p>
57
+ </div>
58
+ <% end %>
59
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <%= turbo_stream.append dom_id(@query, 'messages') do %>
2
+ <% if @need_day_separator %>
3
+ <div class="day-separator center-align small-margin">
4
+ <small class="secondary-text bold"><%= format_message_date(@message.created_at.to_date) %></small>
5
+ </div>
6
+ <% end %>
7
+ <%= render partial: 'sage/queries/message', locals: { message: @message } %>
8
+ <script>
9
+ setTimeout(() => {
10
+ const messagesDiv = document.getElementById('<%= dom_id(@query, 'messages') %>');
11
+ if (messagesDiv) {
12
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
13
+ }
14
+ }, 10);
15
+ </script>
16
+ <% end %>
17
+
18
+ <%= turbo_stream.replace(dom_id(@query, 'input')) do %>
19
+ <%= render partial: 'sage/queries/input', locals: { query: @query } %>
20
+ <% end %>
21
+
22
+ <%= turbo_stream.update 'chat-textarea', "" %>
@@ -0,0 +1,44 @@
1
+ <%= turbo_frame_tag "messages-content" do %>
2
+ <% if @messages.any? %>
3
+ <% if @pagy.next %>
4
+ <!-- Pagination trigger for loading older messages -->
5
+ <div data-sage--reverse-infinite-scroll-target="pagination" style="height: 1px;"></div>
6
+ <% end %>
7
+
8
+ <% messages_grouped_by_day(@messages).each do |date, messages| %>
9
+ <div class="day-separator center-align small-margin">
10
+ <small class="secondary-text bold"><%= format_message_date(date) %></small>
11
+ </div>
12
+ <%= render partial: 'sage/queries/message', collection: messages %>
13
+ <% end %>
14
+ <% else %>
15
+ <!-- Empty state -->
16
+ <div class="center-align padding" style="margin-top: 40px;">
17
+ <i class="large" style="font-size: 3rem; color: var(--surface-variant); margin-bottom: 16px;">💬</i>
18
+ <div class="medium-text secondary-text" style="margin-bottom: 8px;">No messages yet</div>
19
+ <small class="secondary-text">Start a conversation by asking a question below</small>
20
+ </div>
21
+ <% end %>
22
+
23
+ <script>
24
+ // Scroll to bottom on initial load - only for page 1
25
+ <% if params[:page].blank? || params[:page].to_i == 1 %>
26
+ // Use multiple timeouts to ensure content is loaded
27
+ setTimeout(() => {
28
+ const messagesDiv = document.getElementById('<%= dom_id(@query, 'messages') %>');
29
+ if (messagesDiv) {
30
+ console.log("Scrolling to bottom, scrollHeight:", messagesDiv.scrollHeight);
31
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
32
+ }
33
+ }, 100);
34
+
35
+ setTimeout(() => {
36
+ const messagesDiv = document.getElementById('<%= dom_id(@query, 'messages') %>');
37
+ if (messagesDiv) {
38
+ console.log("Final scroll to bottom, scrollHeight:", messagesDiv.scrollHeight);
39
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
40
+ }
41
+ }, 500);
42
+ <% end %>
43
+ </script>
44
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <%= turbo_stream.append "entries" do %>
2
+ <% if @pagy.next %>
3
+ <!-- Add pagination trigger for next page -->
4
+ <div data-sage--reverse-infinite-scroll-target="pagination" style="height: 1px;"></div>
5
+ <% end %>
6
+
7
+ <div data-page="<%= @pagy.page %>">
8
+ <% messages_grouped_by_day(@messages).each do |date, messages| %>
9
+ <div class="day-separator center-align small-margin">
10
+ <small class="secondary-text bold"><%= format_message_date(date) %></small>
11
+ </div>
12
+ <%= render partial: 'sage/queries/message', collection: messages %>
13
+ <% end %>
14
+ </div>
15
+ <% end %>
@@ -0,0 +1,195 @@
1
+ <%= blazer_title "New Query" %>
2
+ <%# <%= render partial: "form" %>
3
+
4
+ <div class="padding left-align">
5
+ <h5 class="">New Query</h5>
6
+ </div>
7
+
8
+ <div class="sage-container">
9
+ <div class="sage-header">
10
+ <h5>What are you curious about?</h5>
11
+ </div>
12
+
13
+ <%= form_with url: queries_path, method: :post, local: true, scope: :query do |f| %>
14
+ <div class="form-group">
15
+ <%= label_tag "query[question]", "Your Question", class: "form-label" %>
16
+ <%= text_area_tag "query[question]", nil,
17
+ class: "form-control",
18
+ rows: 8,
19
+ placeholder: "e.g., Show me the top 10 customers by revenue in the last 30 days",
20
+ required: true,
21
+ data: { sage_target: "question" } %>
22
+ <small class="form-text text-muted">
23
+ Be specific about tables, columns, and conditions when possible
24
+ </small>
25
+ </div>
26
+
27
+ <div class="form-actions">
28
+ <button type='submit' data-disable-with='Generating...'>Generate Report</button>
29
+ </div>
30
+ <% end %>
31
+
32
+ <turbo-frame id="query_result">
33
+ </turbo-frame>
34
+
35
+ <% if @schema.present? %>
36
+ <div class="padding">
37
+ <h6>Schema Guide</h6>
38
+ <p class="small-text">Available tables and their columns</p>
39
+
40
+ <div class="grid">
41
+ <% @schema.each_with_index do |table_info, index| %>
42
+ <div class="s12 m6 l4">
43
+ <article class="schema-card">
44
+ <h5 class="table-title">
45
+ <i class="small">table</i>
46
+ <%= table_info[:table].to_s.gsub('_', ' ').titleize.truncate(20) %>
47
+ </h5>
48
+ <p class="small-text grey-text table-name"><%= table_info[:table].truncate(25) %></p>
49
+
50
+ <p class="column-count">
51
+ <span class="large-text"><%= table_info[:columns].size %></span>
52
+ <span class="small-text">columns</span>
53
+ </p>
54
+
55
+ <%= link_to "View Columns",
56
+ table_schema_queries_path(table_name: table_info[:table]),
57
+ class: "button small view-columns-btn",
58
+ data: { turbo_frame: "overlay" } %>
59
+ </article>
60
+ </div>
61
+ <% end %>
62
+ </div>
63
+ </div>
64
+ <% else %>
65
+ <div class="center-align padding">
66
+ <p class="grey-text">Schema information not available</p>
67
+ </div>
68
+ <% end %>
69
+ </div>
70
+
71
+ <style>
72
+ .sage-container {
73
+ max-width: 1400px;
74
+ margin: 0 auto;
75
+ padding: 20px;
76
+ }
77
+
78
+ .sage-header {
79
+ margin-bottom: 30px;
80
+ text-align: center;
81
+ }
82
+
83
+ .sage-header h1 {
84
+ margin-bottom: 10px;
85
+ }
86
+
87
+ .form-group {
88
+ margin-bottom: 20px;
89
+ }
90
+
91
+ .form-label {
92
+ font-weight: bold;
93
+ margin-bottom: 5px;
94
+ display: block;
95
+ }
96
+
97
+ .form-control {
98
+ width: 100%;
99
+ padding: 8px 12px;
100
+ font-size: 14px;
101
+ border: 1px solid #ddd;
102
+ border-radius: 4px;
103
+ background-color: #fff;
104
+ color: #333;
105
+ }
106
+
107
+ .form-control:focus {
108
+ outline: none;
109
+ border-color: #4CAF50;
110
+ box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
111
+ }
112
+
113
+ /* Dark mode styles for form controls */
114
+ body.dark .form-control {
115
+ background-color: #2a2a2a;
116
+ color: #e6e6e6;
117
+ border-color: #444;
118
+ }
119
+
120
+ body.dark .form-control::placeholder {
121
+ color: #888;
122
+ }
123
+
124
+ body.dark .form-control:focus {
125
+ border-color: #4CAF50;
126
+ box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3);
127
+ }
128
+
129
+ .form-actions {
130
+ text-align: center;
131
+ }
132
+
133
+ .btn {
134
+ display: inline-block;
135
+ padding: 10px 20px;
136
+ font-size: 16px;
137
+ font-weight: 500;
138
+ text-align: center;
139
+ text-decoration: none;
140
+ border: none;
141
+ border-radius: 4px;
142
+ cursor: pointer;
143
+ transition: all 0.2s;
144
+ }
145
+
146
+ .btn-primary {
147
+ background-color: #4CAF50;
148
+ color: white;
149
+ }
150
+
151
+ .btn-primary:hover {
152
+ background-color: #45a049;
153
+ }
154
+
155
+ .btn-primary:disabled {
156
+ opacity: 0.6;
157
+ cursor: not-allowed;
158
+ }
159
+
160
+ .text-muted {
161
+ color: #666;
162
+ font-size: 14px;
163
+ }
164
+
165
+ /* Dark mode styles for labels and helper text */
166
+ body.dark .form-label {
167
+ color: #e6e6e6;
168
+ }
169
+
170
+ body.dark .text-muted {
171
+ color: #999;
172
+ }
173
+
174
+ .schema-card {
175
+ height: 100%;
176
+ display: flex;
177
+ flex-direction: column;
178
+ }
179
+
180
+ .table-title {
181
+ white-space: nowrap;
182
+ overflow: hidden;
183
+ text-overflow: ellipsis;
184
+ margin-bottom: 5px;
185
+ }
186
+
187
+ .table-name {
188
+ white-space: nowrap;
189
+ overflow: hidden;
190
+ text-overflow: ellipsis;
191
+ margin-bottom: 10px;
192
+ }
193
+
194
+ </style>
195
+
@@ -0,0 +1 @@
1
+ <%= render 'run' %>
@@ -0,0 +1,3 @@
1
+ <%= turbo_stream.replace dom_id(@query, 'results') do %>
2
+ <%= render "run" %>
3
+ <% end %>
@@ -0,0 +1,49 @@
1
+ <% blazer_title @query.name %>
2
+
3
+ <div class="padding left-align">
4
+ <h5 style="">
5
+ <%= @query.name %>
6
+ </h5>
7
+ </div>
8
+
9
+ <div class="row right-align">
10
+ <%= link_to "Edit", edit_query_path(@query, params: variable_params(@query)), class: "btn btn-default", data: { turbo: false }, disabled: !@query.editable?(blazer_user) %>
11
+ <%= link_to "Fork", new_query_path(params: {variables: variable_params(@query), fork_query_id: @query.id, data_source: @query.data_source, name: @query.name}), class: "btn btn-info" %>
12
+ <% if !@error && @success %>
13
+ <%= button_to "Download", "/queries/run.csv", params: @run_data, class: "btn btn-primary" %>
14
+ <% end %>
15
+ </div>
16
+
17
+ <% if @sql_errors.any? %>
18
+ <div class="alert alert-danger">
19
+ <ul>
20
+ <% @sql_errors.each do |message| %>
21
+ <li><%= message %></li>
22
+ <% end %>
23
+ </ul>
24
+ </div>
25
+ <% end %>
26
+
27
+ <% if @query.description.present? %>
28
+ <p style="white-space: pre-line;"><%= @query.description %></p>
29
+ <% end %>
30
+
31
+ <%= render partial: "blazer/variables", locals: { action: query_path(@query) } %>
32
+
33
+ <pre id="code"><code><%= @statement.display_statement %></code></pre>
34
+
35
+ <% if @success %>
36
+ <%= turbo_frame_tag dom_id(@query, 'results'), src: run_query_path(@query.id, from_show: true) do %>
37
+ loading...
38
+ <% end %>
39
+ <% end %>
40
+
41
+ <%= javascript_tag nonce: true do %>
42
+ // do not highlight really long queries
43
+ // this can lead to performance issues
44
+ var code = $("#code code")
45
+ if (code.text().length < 10000) {
46
+ hljs.highlightElement(code.get(0))
47
+ }
48
+ <% end %>
49
+
@@ -0,0 +1,77 @@
1
+ <%= turbo_frame_tag "overlay" do %>
2
+ <%= render layout: 'sage/shared/overlay', locals: { title: @table_display_name } do %>
3
+ <% if @table_info.present? %>
4
+ <p class="small-text grey-text"><%= @table_info[:table] %></p>
5
+
6
+ <div class="space"></div>
7
+
8
+ <h6>Columns (<%= @table_info[:columns].size %>)</h6>
9
+ <div class="columns-grid">
10
+ <% @table_info[:columns].each do |column| %>
11
+ <div class="row padding">
12
+ <div class="max">
13
+ <span class="column-name"><%= column[:name] %></span>
14
+ </div>
15
+ <div>
16
+ <span class="chip small"><%= column[:data_type].upcase %></span>
17
+ </div>
18
+ </div>
19
+ <% end %>
20
+ </div>
21
+ <% else %>
22
+ <h5>Table Not Found</h5>
23
+ <p>The requested table schema could not be found.</p>
24
+ <% end %>
25
+ <% end %>
26
+ <% end %>
27
+
28
+ <style>
29
+ .overlay-header {
30
+ padding: 20px;
31
+ border-bottom: 1px solid #eee;
32
+ }
33
+
34
+ .overlay-header h4 {
35
+ margin: 0 0 5px 0;
36
+ }
37
+
38
+ .overlay-content {
39
+ padding: 20px;
40
+ }
41
+
42
+ .columns-grid {
43
+ max-height: 400px;
44
+ overflow-y: auto;
45
+ }
46
+
47
+ .column-row {
48
+ padding: 10px 0;
49
+ border-bottom: 1px solid #f0f0f0;
50
+ }
51
+
52
+ .column-row:last-child {
53
+ border-bottom: none;
54
+ }
55
+
56
+ .column-info {
57
+ display: flex;
58
+ justify-content: space-between;
59
+ align-items: center;
60
+ }
61
+
62
+ .column-name {
63
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
64
+ font-weight: 500;
65
+ color: #0969da;
66
+ }
67
+
68
+ .column-type {
69
+ font-size: 11px;
70
+ color: #6c757d;
71
+ background: #e9ecef;
72
+ padding: 2px 8px;
73
+ border-radius: 12px;
74
+ text-transform: uppercase;
75
+ font-weight: 500;
76
+ }
77
+ </style>
@@ -0,0 +1,26 @@
1
+ <nav class="m l left">
2
+ <header>
3
+ <button class="extra circle transparent">
4
+ Sage
5
+ </button>
6
+ </header>
7
+
8
+ <button id="theme-toggle" class='transparent circle' onclick="changeTheme()">
9
+ <i></i>
10
+ </button>
11
+ <%= link_to root_path, class: "#{current_page?(root_path) ? 'active' : ''}" do %>
12
+ <i>widgets</i>
13
+ <span>Queries</span>
14
+ <% end %>
15
+ <%= link_to checks_path, class: "#{current_page?(checks_path) ? 'active' : ''}" do %>
16
+ <i>style</i>
17
+ <span>Checks</span>
18
+ <% end %>
19
+ <%= link_to dashboards_path, class: "#{current_page?(dashboards_path) ? 'active' : ''}" do %>
20
+ <i>code</i>
21
+ <span>Dashboards</span>
22
+ <% end %>
23
+ </nav>
24
+
25
+
26
+
@@ -0,0 +1,11 @@
1
+ <dialog id='dialog' class="right" style='width: 400px;'>
2
+ <h5><%= local_assigns[:title] %></h5>
3
+ <div>
4
+ <%= yield %>
5
+ </div>
6
+
7
+ <div class="space"></div>
8
+ <nav class="right-align">
9
+ <button class="button" onclick="ui('#dialog'); document.getElementById('overlay').innerHTML = '';">Close</button>
10
+ </nav>
11
+ </dialog>
@@ -0,0 +1,11 @@
1
+ pin "sage/application", to: "sage/application.js", preload: true
2
+ pin "sage", to: "sage.js"
3
+ pin "sage/controllers/search_controller", to: "sage/controllers/search_controller.js"
4
+ pin "sage/controllers/clipboard_controller", to: "sage/controllers/clipboard_controller.js"
5
+ pin "sage/controllers/select_controller", to: "sage/controllers/select_controller.js"
6
+ pin "sage/controllers/dashboard_controller", to: "sage/controllers/dashboard_controller.js"
7
+ pin "sage/controllers/reverse_infinite_scroll_controller", to: "sage/controllers/reverse_infinite_scroll_controller.js"
8
+
9
+ # Don't pin common libraries - let the host app handle them
10
+ # pin "@hotwired/stimulus", to: "stimulus.min.js"
11
+ # pin "@hotwired/turbo-rails", to: "turbo.min.js"
@@ -0,0 +1,2 @@
1
+ Pagy::DEFAULT[:items] = 10
2
+ Pagy::DEFAULT[:overflow] = :last_page