sage-rails 0.1.3 → 0.1.4

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: c226da9e35872f807813789601f70a61e0642fe895491226748730abf0ca5ae6
4
- data.tar.gz: 383fa6d133de1e90321769974377b71333d3c90bd67858796835fc70be0694ca
3
+ metadata.gz: cdf45a19f9379935c65e7ee18b70a4cc2ce0df00b71fe13586c89ffe15180524
4
+ data.tar.gz: 86c1c3fda7b28206020da469995a5bcc6215c61b9249e60e0b121307b31ab66d
5
5
  SHA512:
6
- metadata.gz: edff5d0c62223b69d985ba897aa619c9e924eff365a6b3c94f8f617bb06b3b28f5929fbea0ce555db046ef78f57986d78c71c8d20965062093374075b214cdf2
7
- data.tar.gz: cddfd4cbeb87ba36fbd196fa1c992493a2fca258bc817be25faff43d3f1023c2e1ccd1fa08c915cfd769ee84845df97431dd6a947ed9dea9ec12ecf700fc8682
6
+ metadata.gz: 717576a4f348569e8610e9675132456b8d213156e666b930c1600e55c513583a05086e4d1082d4efc90ba7a7b3bccd76fbbde81b4e883c318fc94b3fe14a248c
7
+ data.tar.gz: c3c47a9212d72c6360a3cadcf2e4f20db07d16ad5fe9864faad84a95011ca164d251b0ad2df36e0c0e097e57d6549e9025f25061d000e3dcfa1974b659b9c6f6
data/README.md CHANGED
@@ -34,13 +34,6 @@ Run bundle install:
34
34
  $ bundle install
35
35
  ```
36
36
 
37
- ## Sprockets
38
-
39
- If using sprockets, add this line to your app/javascript/application.js file:
40
- ```js
41
- import "sage/applicaiton";
42
- ```
43
-
44
37
  ## Getting Started
45
38
 
46
39
  Run the install generator to set up Sage in your Rails application:
@@ -60,6 +53,14 @@ After installation, run the migrations:
60
53
  $ rails db:migrate
61
54
  ```
62
55
 
56
+ ### Sprockets
57
+
58
+ If using sprockets, add this line to your app/javascript/application.js file:
59
+ ```js
60
+ import "sage/application";
61
+ ```
62
+
63
+
63
64
  ## LLM Configuration
64
65
 
65
66
  Sage supports both Anthropic Claude and OpenAI models for SQL generation. Configure your preferred AI service in `config/initializers/sage.rb`:
@@ -2,21 +2,65 @@ import { Controller } from "@hotwired/stimulus";
2
2
 
3
3
  export default class extends Controller {
4
4
  static targets = ["content", "icon"]
5
+ static values = {
6
+ storageKey: { type: String, default: "sage_query_sql_visibility" }
7
+ }
8
+
9
+ connect() {
10
+ // Load saved visibility state from localStorage
11
+ this.loadVisibilityState()
12
+ }
5
13
 
6
14
  toggle() {
7
15
  try {
8
16
  const content = this.contentTarget
9
17
  const icon = this.iconTarget
10
-
11
- if (content.style.display === "none") {
12
- content.style.display = "block"
13
- icon.textContent = "visibility_off"
18
+
19
+ const isHidden = window.getComputedStyle(content).display === "none"
20
+
21
+ if (isHidden) {
22
+ this.showContent()
14
23
  } else {
15
- content.style.display = "none"
16
- icon.textContent = "visibility"
24
+ this.hideContent()
17
25
  }
18
26
  } catch (error) {
19
27
  console.error("Error in toggle:", error)
20
28
  }
21
29
  }
30
+
31
+ showContent() {
32
+ this.contentTarget.style.display = "block"
33
+ this.iconTarget.textContent = "visibility_off"
34
+ this.saveVisibilityState(true)
35
+ }
36
+
37
+ hideContent() {
38
+ this.contentTarget.style.display = "none"
39
+ this.iconTarget.textContent = "visibility"
40
+ this.saveVisibilityState(false)
41
+ }
42
+
43
+ saveVisibilityState(isVisible) {
44
+ try {
45
+ localStorage.setItem(this.storageKeyValue, JSON.stringify(isVisible))
46
+ } catch (error) {
47
+ console.error("Error saving visibility state:", error)
48
+ }
49
+ }
50
+
51
+ loadVisibilityState() {
52
+ try {
53
+ const savedState = localStorage.getItem(this.storageKeyValue)
54
+ if (savedState !== null) {
55
+ const isVisible = JSON.parse(savedState)
56
+ if (isVisible) {
57
+ this.showContent()
58
+ } else {
59
+ this.hideContent()
60
+ }
61
+ }
62
+ } catch (error) {
63
+ console.error("Error loading visibility state:", error)
64
+ }
65
+ }
22
66
  }
@@ -140,8 +140,9 @@ module Sage
140
140
  @query ||= Blazer::Query.find_by(id: params[:query_id]) if params[:query_id]
141
141
 
142
142
  # use query data source when present
143
- data_source = @query.data_source if @query && @query.data_source
143
+ data_source = @query.data_source if @query && @query.data_source.present?
144
144
  data_source ||= params[:data_source]
145
+ data_source ||= "main" # Fallback to main data source
145
146
  @data_source = Blazer.data_sources[data_source]
146
147
 
147
148
  # Prefer params statement over query's saved statement (for live editing)
@@ -2,23 +2,65 @@ import { Controller } from "@hotwired/stimulus";
2
2
 
3
3
  export default class extends Controller {
4
4
  static targets = ["content", "icon"]
5
+ static values = {
6
+ storageKey: { type: String, default: "sage_query_sql_visibility" }
7
+ }
8
+
9
+ connect() {
10
+ // Load saved visibility state from localStorage
11
+ this.loadVisibilityState()
12
+ }
5
13
 
6
14
  toggle() {
7
15
  try {
8
16
  const content = this.contentTarget
9
17
  const icon = this.iconTarget
10
-
18
+
11
19
  const isHidden = window.getComputedStyle(content).display === "none"
12
-
20
+
13
21
  if (isHidden) {
14
- content.style.display = "block"
15
- icon.textContent = "visibility_off"
22
+ this.showContent()
16
23
  } else {
17
- content.style.display = "none"
18
- icon.textContent = "visibility"
24
+ this.hideContent()
19
25
  }
20
26
  } catch (error) {
21
27
  console.error("Error in toggle:", error)
22
28
  }
23
29
  }
30
+
31
+ showContent() {
32
+ this.contentTarget.style.display = "block"
33
+ this.iconTarget.textContent = "visibility_off"
34
+ this.saveVisibilityState(true)
35
+ }
36
+
37
+ hideContent() {
38
+ this.contentTarget.style.display = "none"
39
+ this.iconTarget.textContent = "visibility"
40
+ this.saveVisibilityState(false)
41
+ }
42
+
43
+ saveVisibilityState(isVisible) {
44
+ try {
45
+ localStorage.setItem(this.storageKeyValue, JSON.stringify(isVisible))
46
+ } catch (error) {
47
+ console.error("Error saving visibility state:", error)
48
+ }
49
+ }
50
+
51
+ loadVisibilityState() {
52
+ try {
53
+ const savedState = localStorage.getItem(this.storageKeyValue)
54
+ if (savedState !== null) {
55
+ const isVisible = JSON.parse(savedState)
56
+ if (isVisible) {
57
+ this.showContent()
58
+ } else {
59
+ this.hideContent()
60
+ }
61
+ }
62
+ } catch (error) {
63
+ console.error("Error loading visibility state:", error)
64
+ }
65
+ }
24
66
  }
@@ -10,36 +10,38 @@
10
10
 
11
11
  <% @variable_params = @query.persisted? ? variable_params(@query) : nested_variable_params(@query) %>
12
12
 
13
- <%= form_for @query, url: (@query.persisted? ? query_path(@query, params: @variable_params) : queries_path(params: @variable_params)), html: {autocomplete: "off", class: ""} do |f| %>
13
+ <!-- Hidden Run button form outside the main form -->
14
+ <%= form_with url: (defined?(sage) && sage.respond_to?(:run_queries_path) ? sage.run_queries_path : run_queries_path), method: :post, local: false, html: {id: 'run-query-form', style: "display: none;"} do |f| %>
15
+ <%= f.hidden_field :statement, id: 'run_query_statement' %>
16
+ <%= f.hidden_field :query_id, value: @query.id %>
17
+ <%= f.hidden_field :data_source, value: @query.data_source || Blazer.data_sources.keys.first %>
18
+ <% end %>
19
+
20
+ <%= form_for @query, url: (@query.persisted? ? query_path(@query, params: @variable_params) : queries_path(params: @variable_params)), html: {autocomplete: "off", class: "", id: 'query-form'} do |f| %>
14
21
  <div class="grid" style='margin-top: 0px'>
15
22
  <div class="s10">
16
23
  <div class="field label border">
17
24
  <%= f.text_field :name %>
18
25
  <%= f.label :name, class: (@query.name.present? ? "active" : "") %>
19
26
  </div>
20
-
27
+
21
28
  <div class="field textarea label border">
22
29
  <%= f.text_area :description, style: "height: 145px; resize: vertical;" %>
23
30
  <%= f.label :description, "Description (Optional)", class: (@query.description.present? ? "active" : "") %>
24
31
  </div>
25
-
32
+
26
33
  <%= f.hidden_field :statement, id: 'query_statement_form' %>
27
34
  </div>
28
-
35
+
29
36
  <div class="s2">
30
37
  <nav style="display: flex; flex-direction: column; gap: 12px; padding-left: 8px;">
31
38
  <button type='button' onclick="formatSQL()" class="button secondary" title="Format SQL">
32
39
  <i>auto_fix_high</i>
33
40
  </button>
34
-
35
- <%= form_with url: (defined?(sage) && sage.respond_to?(:run_queries_path) ? sage.run_queries_path : run_queries_path), method: :post, local: false, style: "margin: 0;" do |f| %>
36
- <%= f.hidden_field :statement, id: 'run_query_statement' %>
37
- <%= f.hidden_field :query_id, value: @query.id %>
38
- <%= f.hidden_field :data_source, value: @query.data_source || Blazer.data_sources.keys.first %>
39
- <button type='submit' class="button primary" title="Run Query">
40
- <i>play_arrow</i>
41
- </button>
42
- <% end %>
41
+
42
+ <button type='button' onclick="document.getElementById('run-query-form').requestSubmit()" class="button primary" title="Run Query">
43
+ <i>play_arrow</i>
44
+ </button>
43
45
 
44
46
  <button type="submit" name="commit" value="<%= @query.persisted? ? "Update" : "Create" %>" class="button primary" title="<%= @query.persisted? ? "Update" : "Create" %>">
45
47
  <i><%= @query.persisted? ? "save" : "add" %></i>
@@ -33,7 +33,7 @@
33
33
  <% else %>
34
34
  <% unless @only_chart %>
35
35
  <%= render partial: "caching" %>
36
- <p class="text-muted" style="margin-bottom: 10px;">
36
+ <p class="text-muted" style="margin-top: <%= @rows.any? ? '0' : '40px' %>; margin-bottom: 10px;">
37
37
  <% if @row_limit && @rows.size > @row_limit %>
38
38
  First
39
39
  <% @rows = @rows.first(@row_limit) %>
@@ -225,8 +225,8 @@
225
225
  <% end %>
226
226
  </div>
227
227
  <% end %>
228
- <% elsif @only_chart %>
229
- <p class="text-muted">No rows</p>
228
+ <% elsif @only_chart %>
229
+ <p class="text-muted">No rows</p>
230
+ <% end %>
230
231
  <% end %>
231
232
  <% end %>
232
- <% end %>
@@ -35,14 +35,25 @@
35
35
  <span>Statement</span>
36
36
  <i class="material-icons" data-sage--query-toggle-target="icon" data-action="click->sage--query-toggle#toggle" style="cursor: pointer; font-size: 18px; opacity: 0.6;">visibility_off</i>
37
37
  </div>
38
-
39
- <pre id="code" data-sage--query-toggle-target="content"><code><%= @statement.try(:display_statement) || @query.statement %></code></pre>
38
+
39
+ <% statement_display = begin
40
+ @statement.try(:display_statement) if @statement&.statement.present?
41
+ rescue => e
42
+ Rails.logger.error("Error displaying statement: #{e.message}")
43
+ nil
44
+ end %>
45
+
46
+ <pre id="code" data-sage--query-toggle-target="content"><code><%= statement_display || @query.statement || "-- No SQL statement defined --" %></code></pre>
40
47
  </div>
41
48
 
42
- <% if @success %>
49
+ <% if @success && @query.statement.present? && @query.data_source.present? %>
43
50
  <%= turbo_frame_tag dom_id(@query, 'results'), src: run_query_path(@query.id, from_show: true, variables: variable_params(@query)) do %>
44
51
  loading...
45
52
  <% end %>
53
+ <% elsif @query.statement.blank? %>
54
+ <div class="padding center-align">
55
+ <p class="secondary-text">No SQL statement to execute. <%= link_to "Edit this query", edit_query_path(@query) %> to add a statement.</p>
56
+ </div>
46
57
  <% end %>
47
58
 
48
59
  <%= javascript_tag nonce: true do %>
data/lib/sage/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sage
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sage-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Jones