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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +17 -0
- data/app/views/layouts/pg_reports/application.html.erb +7 -5
- data/app/views/pg_reports/dashboard/_fake_source_data.html.erb +43 -0
- data/app/views/pg_reports/dashboard/show.html.erb +52 -6
- data/lib/pg_reports/configuration.rb +12 -0
- data/lib/pg_reports/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ebdc855958933d677530a6bee64fe5534e3b13244e4b851ee3cd06b37e1df10b
|
|
4
|
+
data.tar.gz: 1fae6baba2619b57b10ad86ef37678a34b279c49a7cf0f014810f4b167c2ae80
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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',
|
|
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
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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"
|
|
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}"
|
|
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
|
data/lib/pg_reports/version.rb
CHANGED
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.
|
|
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
|