pg_reports 0.2.1 → 0.2.2

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: 76df6e762c2183af15f4409d137e5705f97249922d6c5c5521ba6a7903d3e3f9
4
- data.tar.gz: 7e52e17a4fa3b3961a1b3d9c5c64174376e38e0d97732017d3a248df8c19528f
3
+ metadata.gz: ebdc855958933d677530a6bee64fe5534e3b13244e4b851ee3cd06b37e1df10b
4
+ data.tar.gz: 1fae6baba2619b57b10ad86ef37678a34b279c49a7cf0f014810f4b167c2ae80
5
5
  SHA512:
6
- metadata.gz: eed06fbb5e836b8ecd1452319e4a0af13ffe750525953fec4ee4aed49e9d04e47df6f8404afe29162950885b41cccdaf4cd1e0ec22f2b87917ee64d931245fc4
7
- data.tar.gz: 3c6d3b1f7c04781428eaf8d8460177ef4e243d114ad88c83b4419ab15817214804184247a0074dd5f69775d7484205969830dd77169b6de52dc35cda8bda7e24
6
+ metadata.gz: 7b2960a2f1df123331f8cd8d41d258fcd61f45b118451bac7859e890a5fde822266e7ab77e60a5820bf1856548ebf3ec2fff77a3f7938dc8913eca53cb087582
7
+ data.tar.gz: 8bfeeff753903a9a29329221108628ae2119759f859dd01e2c1395a9ae317a956556fb115bfa28b046b859e784ea42fc0b51df7fee56b7a6a6aef1674335a96a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.2] - 2026-01-28
9
+
10
+ ### Added
11
+
12
+ - `fake_source_data` configuration option - enable via `PG_REPORTS_FAKE_SOURCE_DATA=true` env variable or in initializer
13
+ - Support for short controller#action format in source links (e.g., `posts#index` → `app/controllers/posts_controller.rb`)
14
+
15
+ ### Changed
16
+
17
+ - Fake source data moved to separate partial file for cleaner code organization
18
+ - IDE link click handling improved with event delegation in capture phase
19
+
20
+ ### Fixed
21
+
22
+ - Source badge clicks now work correctly without triggering row expansion
23
+ - Fallback fonts now use proper sans-serif system font stack when `load_external_fonts` is disabled
24
+
8
25
  ## [0.2.1] - 2026-01-28
9
26
 
10
27
  ### Added
data/README.md CHANGED
@@ -116,6 +116,12 @@ PgReports.configure do |config|
116
116
  end
117
117
  }
118
118
 
119
+ # External fonts (Google Fonts)
120
+ # Default: false (no external requests)
121
+ config.load_external_fonts = ENV["PG_REPORTS_LOAD_EXTERNAL_FONTS"] == "true"
122
+ # or simply:
123
+ # config.load_external_fonts = true
124
+
119
125
  end
120
126
  ```
121
127
 
@@ -333,6 +339,17 @@ PgReports.configure do |config|
333
339
  end
334
340
  ```
335
341
 
342
+ ### External Fonts
343
+
344
+ By default, PgReports does **not** load external fonts.
345
+
346
+ ```ruby
347
+ PgReports.configure do |config|
348
+ # Enable loading Google Fonts (optional)
349
+ config.load_external_fonts = true
350
+ end
351
+ ```
352
+
336
353
  ## Telegram Integration
337
354
 
338
355
  1. Create a bot via [@BotFather](https://t.me/BotFather)
@@ -6,9 +6,11 @@
6
6
  <meta name="pg-reports-root" content="<%= request.script_name.presence || PgReports::Engine.routes.url_helpers.root_path %>">
7
7
  <title>PgReports Dashboard</title>
8
8
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%234f46e5'/%3E%3Crect x='5' y='18' width='5' height='9' rx='1' fill='%23fff'/%3E%3Crect x='13.5' y='12' width='5' height='15' rx='1' fill='%23fff'/%3E%3Crect x='22' y='6' width='5' height='21' rx='1' fill='%23fff'/%3E%3C/svg%3E">
9
- <link rel="preconnect" href="https://fonts.googleapis.com">
10
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
9
+ <% if PgReports.config.load_external_fonts %>
10
+ <link rel="preconnect" href="https://fonts.googleapis.com">
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
13
+ <% end %>
12
14
  <style>
13
15
  :root {
14
16
  --bg-primary: #0f1114;
@@ -36,7 +38,7 @@
36
38
  }
37
39
 
38
40
  body {
39
- font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;
41
+ font-family: <%= PgReports.config.load_external_fonts ? "'Plus Jakarta Sans', " : "" %>-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
40
42
  background: var(--bg-primary);
41
43
  color: var(--text-primary);
42
44
  min-height: 100vh;
@@ -344,7 +346,7 @@
344
346
  .results-table {
345
347
  width: 100%;
346
348
  border-collapse: collapse;
347
- font-family: 'JetBrains Mono', monospace;
349
+ font-family: <%= PgReports.config.load_external_fonts ? "'JetBrains Mono', " : "" %>SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
348
350
  font-size: 0.8rem;
349
351
  }
350
352
 
@@ -0,0 +1,43 @@
1
+ <%# Fake source data for IDE link testing - enable via PG_REPORTS_FAKE_SOURCE_DATA=true %>
2
+ <script>
3
+ // Known controller methods with line numbers (for IDE link testing)
4
+ const knownControllerMethods = {
5
+ 'PostsController#show': { file: 'app/controllers/posts_controller.rb', line: 33 },
6
+ 'PostsController#index': { file: 'app/controllers/posts_controller.rb', line: 19 },
7
+ 'PostsController#create': { file: 'app/controllers/posts_controller.rb', line: 43 },
8
+ 'PostsController#update': { file: 'app/controllers/posts_controller.rb', line: 62 },
9
+ 'PostsController#destroy': { file: 'app/controllers/posts_controller.rb', line: 76 }
10
+ };
11
+
12
+ // Fake source values for testing IDE links
13
+ const fakeSourceValues = [
14
+ 'PostsController#show',
15
+ 'PostsController#index',
16
+ 'PostsController#create',
17
+ 'PostsController#update',
18
+ 'PostsController#destroy',
19
+ 'app/controllers/posts_controller.rb:33',
20
+ 'app/controllers/posts_controller.rb:19',
21
+ 'app/models/post.rb:45:in `find_by_slug`'
22
+ ];
23
+
24
+ // Inject fake source values into data for testing
25
+ function injectFakeSourceData(data) {
26
+ if (!data.data || data.data.length === 0) return;
27
+
28
+ // Add 'source' column if not present
29
+ if (!data.columns.includes('source')) {
30
+ data.columns.push('source');
31
+ }
32
+
33
+ // Inject fake source values
34
+ data.data.forEach((row, idx) => {
35
+ if (!row.source || row.source === '' || row.source === null) {
36
+ row.source = fakeSourceValues[idx % fakeSourceValues.length];
37
+ }
38
+ });
39
+ }
40
+
41
+ // Flag to indicate fake source data is enabled
42
+ window.PG_REPORTS_FAKE_SOURCE_DATA = true;
43
+ </script>
@@ -1216,6 +1216,10 @@
1216
1216
  }
1217
1217
  </style>
1218
1218
 
1219
+ <% if PgReports.config.fake_source_data %>
1220
+ <%= render 'pg_reports/dashboard/fake_source_data' %>
1221
+ <% end %>
1222
+
1219
1223
  <script>
1220
1224
  let currentReportData = null;
1221
1225
  const category = '<%= @category %>';
@@ -1589,13 +1593,30 @@
1589
1593
  lineNumber = parseInt(fileLineMatch[3], 10);
1590
1594
  }
1591
1595
 
1592
- // Try to match Controller#action pattern
1596
+ // Try to match Controller#action pattern (e.g., PostsController#index)
1593
1597
  const controllerMatch = source.match(/^(\w+Controller)#(\w+)/);
1594
1598
  if (controllerMatch) {
1595
1599
  methodName = `${controllerMatch[1]}#${controllerMatch[2]}`;
1596
- // Derive file path from controller name
1597
- const controllerName = controllerMatch[1].replace(/Controller$/, '').toLowerCase();
1598
- filePath = `app/controllers/${controllerName}_controller.rb`;
1600
+
1601
+ // Check known methods first for testing (if fake data is enabled)
1602
+ if (typeof knownControllerMethods !== 'undefined' && knownControllerMethods[methodName]) {
1603
+ filePath = knownControllerMethods[methodName].file;
1604
+ lineNumber = knownControllerMethods[methodName].line;
1605
+ } else {
1606
+ // Derive file path from controller name
1607
+ const controllerName = controllerMatch[1].replace(/Controller$/, '').toLowerCase();
1608
+ filePath = `app/controllers/${controllerName}_controller.rb`;
1609
+ }
1610
+ }
1611
+
1612
+ // Try to match short controller#action pattern (e.g., posts#index, dashboard#show)
1613
+ if (!filePath) {
1614
+ const shortMatch = source.match(/^(\w+)#(\w+)$/);
1615
+ if (shortMatch) {
1616
+ const controllerName = shortMatch[1].toLowerCase();
1617
+ methodName = `${shortMatch[1]}#${shortMatch[2]}`;
1618
+ filePath = `app/controllers/${controllerName}_controller.rb`;
1619
+ }
1599
1620
  }
1600
1621
 
1601
1622
  return { filePath, lineNumber, methodName, original: source };
@@ -1694,12 +1715,12 @@
1694
1715
 
1695
1716
  let dropdownHtml = `
1696
1717
  <div class="ide-dropdown">
1697
- <span class="source-badge clickable" onclick="event.stopPropagation(); toggleIdeDropdown('${dropdownId}', this)" title="${escapeHtml(parsed.original)}">${escapeHtml(parsed.original)}</span>
1718
+ <span class="source-badge clickable" data-dropdown-id="${dropdownId}" title="${escapeHtml(parsed.original)}">${escapeHtml(parsed.original)}</span>
1698
1719
  <div class="ide-dropdown-menu" id="${dropdownId}">
1699
1720
  `;
1700
1721
 
1701
1722
  for (const ide of ideUrls) {
1702
- dropdownHtml += `<a href="${ide.url}" onclick="event.stopPropagation();">${ide.name}</a>`;
1723
+ dropdownHtml += `<a href="${ide.url}">${ide.name}</a>`;
1703
1724
  }
1704
1725
 
1705
1726
  dropdownHtml += '</div></div>';
@@ -1726,6 +1747,26 @@
1726
1747
  }
1727
1748
  }
1728
1749
 
1750
+ // Event delegation for source badge clicks
1751
+ document.addEventListener('click', function(e) {
1752
+ const badge = e.target.closest('.source-badge.clickable[data-dropdown-id]');
1753
+ if (badge) {
1754
+ e.stopPropagation();
1755
+ e.preventDefault();
1756
+ const dropdownId = badge.dataset.dropdownId;
1757
+ toggleIdeDropdown(dropdownId, badge);
1758
+ return;
1759
+ }
1760
+
1761
+ // Allow clicks on IDE dropdown menu links
1762
+ const ideLink = e.target.closest('.ide-dropdown-menu a');
1763
+ if (ideLink) {
1764
+ e.stopPropagation();
1765
+ // Let the link navigate normally
1766
+ return;
1767
+ }
1768
+ }, true); // Use capture phase to intercept before row click
1769
+
1729
1770
  function buildDetailRow(row, columns, rowIndex, thresholds, problemFields) {
1730
1771
  let html = '<div class="row-detail">';
1731
1772
  const hasQuery = columns.includes('query') && row.query;
@@ -1829,6 +1870,11 @@
1829
1870
  if (loadingEl) loadingEl.style.display = 'none';
1830
1871
 
1831
1872
  if (data.success) {
1873
+ // Inject fake source data for IDE link testing (if enabled)
1874
+ if (typeof injectFakeSourceData === 'function') {
1875
+ injectFakeSourceData(data);
1876
+ }
1877
+
1832
1878
  currentReportData = data;
1833
1879
  const thresholds = data.thresholds || {};
1834
1880
  const problemFields = data.problem_fields || [];
@@ -27,6 +27,12 @@ module PgReports
27
27
  # Dashboard settings
28
28
  attr_accessor :dashboard_auth # Proc for dashboard authentication
29
29
 
30
+ # Assets / privacy settings
31
+ attr_accessor :load_external_fonts # When true, loads Google Fonts in the dashboard layout
32
+
33
+ # Development/testing settings
34
+ attr_accessor :fake_source_data # Inject fake source data for IDE link testing
35
+
30
36
  def initialize
31
37
  # Telegram
32
38
  @telegram_bot_token = ENV.fetch("PG_REPORTS_TELEGRAM_TOKEN", nil)
@@ -52,6 +58,12 @@ module PgReports
52
58
 
53
59
  # Dashboard
54
60
  @dashboard_auth = nil
61
+
62
+ # Assets / privacy
63
+ @load_external_fonts = ActiveModel::Type::Boolean.new.cast(ENV.fetch("PG_REPORTS_LOAD_EXTERNAL_FONTS", false))
64
+
65
+ # Development/testing
66
+ @fake_source_data = ENV.fetch("PG_REPORTS_FAKE_SOURCE_DATA", "false") == "true"
55
67
  end
56
68
 
57
69
  def connection
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgReports
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_reports
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eldar Avatov
@@ -107,6 +107,7 @@ files:
107
107
  - README.md
108
108
  - app/controllers/pg_reports/dashboard_controller.rb
109
109
  - app/views/layouts/pg_reports/application.html.erb
110
+ - app/views/pg_reports/dashboard/_fake_source_data.html.erb
110
111
  - app/views/pg_reports/dashboard/index.html.erb
111
112
  - app/views/pg_reports/dashboard/show.html.erb
112
113
  - config/locales/en.yml