dbviewer 0.5.7 → 0.5.8
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/app/controllers/dbviewer/api/tables_controller.rb +55 -0
- data/app/views/dbviewer/tables/show.html.erb +73 -8
- data/config/routes.rb +3 -0
- data/lib/dbviewer/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d46526c59b9c355c9e717e76ebe81299bbdb051be58fb1b789fbe2fa1c50c0e4
|
4
|
+
data.tar.gz: 713277fda9b00b0fe4a48b2e8324632f2c7920b25cbaad369afe2d159c2bcf29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77444f6cf1b7d847d114bdef9f965d9c90e4e216666db1fccb7de5652f92950cae7793bab07da982e473517f4c350d0dc186e094291138258c9a589de983bf5c
|
7
|
+
data.tar.gz: fc6801c693d632bf8a4bfe26d0aa4b0614ba8570867cf14575f279813c2b8599e5f3288f1bc0175d2db108d62ec2362d484c47a2e0059d5eb28c1a4d4a091d52
|
@@ -16,6 +16,61 @@ module Dbviewer
|
|
16
16
|
render_success(total_relationships: total_relationships)
|
17
17
|
end
|
18
18
|
|
19
|
+
def relationship_counts
|
20
|
+
table_name = params[:id]
|
21
|
+
record_id = params[:record_id]
|
22
|
+
|
23
|
+
unless table_name.present? && record_id.present?
|
24
|
+
render_error("Table name and record ID are required", 400)
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
# Get table metadata to find relationships
|
30
|
+
metadata = fetch_table_metadata(table_name)
|
31
|
+
|
32
|
+
unless metadata
|
33
|
+
render_error("Table not found", 404)
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get reverse foreign keys (has_many relationships)
|
38
|
+
reverse_foreign_keys = metadata.dig(:reverse_foreign_keys) || []
|
39
|
+
|
40
|
+
relationship_counts = reverse_foreign_keys.map do |rel|
|
41
|
+
begin
|
42
|
+
# Count records in the related table that reference this record
|
43
|
+
count_query = "SELECT COUNT(*) as count FROM #{rel[:from_table]} WHERE #{rel[:column]} = ?"
|
44
|
+
result = database_manager.connection.exec_query(count_query, "Count Query", [ record_id ])
|
45
|
+
count = result.rows.first&.first || 0
|
46
|
+
|
47
|
+
{
|
48
|
+
table: rel[:from_table],
|
49
|
+
foreign_key: rel[:column],
|
50
|
+
count: count.to_i
|
51
|
+
}
|
52
|
+
rescue => e
|
53
|
+
Rails.logger.error "Error counting relationships for #{rel[:from_table]}: #{e.message}"
|
54
|
+
{
|
55
|
+
table: rel[:from_table],
|
56
|
+
foreign_key: rel[:column],
|
57
|
+
count: 0,
|
58
|
+
error: e.message
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
render_success({
|
64
|
+
table_name: table_name,
|
65
|
+
record_id: record_id,
|
66
|
+
relationships: relationship_counts
|
67
|
+
})
|
68
|
+
rescue => e
|
69
|
+
Rails.logger.error "Error fetching relationship counts: #{e.message}"
|
70
|
+
render_error("Error fetching relationship counts: #{e.message}", 500)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
19
74
|
private
|
20
75
|
|
21
76
|
def fetch_tables_count
|
@@ -777,7 +777,11 @@
|
|
777
777
|
const primaryKeyValue = recordData[Object.keys(recordData).find(key => key === 'id') || Object.keys(recordData)[0]];
|
778
778
|
|
779
779
|
if (primaryKeyValue !== null && primaryKeyValue !== undefined && primaryKeyValue !== '') {
|
780
|
-
|
780
|
+
const hasManySection = createRelationshipSection('Has Many', reverseForeignKeys, recordData, 'has_many', primaryKeyValue);
|
781
|
+
relationshipsContent.appendChild(hasManySection);
|
782
|
+
|
783
|
+
// Fetch relationship counts asynchronously
|
784
|
+
fetchRelationshipCounts('<%= @table_name %>', primaryKeyValue, reverseForeignKeys, hasManySection);
|
781
785
|
}
|
782
786
|
}
|
783
787
|
|
@@ -1433,6 +1437,55 @@
|
|
1433
1437
|
});
|
1434
1438
|
|
1435
1439
|
// Helper function to create relationship sections
|
1440
|
+
// Function to fetch relationship counts from API
|
1441
|
+
async function fetchRelationshipCounts(tableName, recordId, relationships, hasManySection) {
|
1442
|
+
try {
|
1443
|
+
const response = await fetch(`/dbviewer/api/tables/${tableName}/relationship_counts?record_id=${recordId}`);
|
1444
|
+
if (!response.ok) {
|
1445
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
1446
|
+
}
|
1447
|
+
|
1448
|
+
const data = await response.json();
|
1449
|
+
|
1450
|
+
// Update each count in the UI
|
1451
|
+
const countSpans = hasManySection.querySelectorAll('.relationship-count');
|
1452
|
+
|
1453
|
+
relationships.forEach((relationship, index) => {
|
1454
|
+
const countSpan = countSpans[index];
|
1455
|
+
if (countSpan) {
|
1456
|
+
const relationshipData = data.relationships.find(r =>
|
1457
|
+
r.table === relationship.from_table && r.foreign_key === relationship.column
|
1458
|
+
);
|
1459
|
+
|
1460
|
+
if (relationshipData) {
|
1461
|
+
const count = relationshipData.count;
|
1462
|
+
let badgeClass = 'bg-secondary';
|
1463
|
+
let badgeText = `${count} record${count !== 1 ? 's' : ''}`;
|
1464
|
+
|
1465
|
+
// Use different colors based on count
|
1466
|
+
if (count > 0) {
|
1467
|
+
badgeClass = count > 10 ? 'bg-warning' : 'bg-success';
|
1468
|
+
}
|
1469
|
+
|
1470
|
+
countSpan.innerHTML = `<span class="badge ${badgeClass}">${badgeText}</span>`;
|
1471
|
+
} else {
|
1472
|
+
// Fallback if no data found
|
1473
|
+
countSpan.innerHTML = '<span class="badge bg-danger">Error</span>';
|
1474
|
+
}
|
1475
|
+
}
|
1476
|
+
});
|
1477
|
+
|
1478
|
+
} catch (error) {
|
1479
|
+
console.error('Error fetching relationship counts:', error);
|
1480
|
+
|
1481
|
+
// Show error state in UI
|
1482
|
+
const countSpans = hasManySection.querySelectorAll('.relationship-count');
|
1483
|
+
countSpans.forEach(span => {
|
1484
|
+
span.innerHTML = '<span class="badge bg-danger">Error</span>';
|
1485
|
+
});
|
1486
|
+
}
|
1487
|
+
}
|
1488
|
+
|
1436
1489
|
function createRelationshipSection(title, relationships, recordData, type, primaryKeyValue = null) {
|
1437
1490
|
const section = document.createElement('div');
|
1438
1491
|
section.className = 'relationship-section mb-4';
|
@@ -1503,7 +1556,12 @@
|
|
1503
1556
|
<span class="text-muted">${fk.from_table}.</span><strong>${fk.column}</strong>
|
1504
1557
|
</td>
|
1505
1558
|
<td>
|
1506
|
-
<span class="
|
1559
|
+
<span class="relationship-count">
|
1560
|
+
<span class="badge bg-secondary">
|
1561
|
+
<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
|
1562
|
+
Loading...
|
1563
|
+
</span>
|
1564
|
+
</span>
|
1507
1565
|
</td>
|
1508
1566
|
<td>
|
1509
1567
|
<a href="/dbviewer/tables/${fk.from_table}?column_filters[${fk.column}]=${encodeURIComponent(primaryKeyValue)}"
|
@@ -2108,12 +2166,6 @@
|
|
2108
2166
|
background: transparent !important;
|
2109
2167
|
}
|
2110
2168
|
|
2111
|
-
[data-bs-theme="dark"] .flatpickr-day.disabled:hover {
|
2112
|
-
background: transparent !important;
|
2113
|
-
color: #6c757d !important;
|
2114
|
-
cursor: not-allowed;
|
2115
|
-
}
|
2116
|
-
|
2117
2169
|
/* Dark mode other day states */
|
2118
2170
|
[data-bs-theme="dark"] .flatpickr-day.nextMonthDay,
|
2119
2171
|
[data-bs-theme="dark"] .flatpickr-day.prevMonthDay {
|
@@ -2263,6 +2315,19 @@
|
|
2263
2315
|
.flatpickr-next-month:hover {
|
2264
2316
|
background: rgba(var(--bs-primary-rgb), 0.1);
|
2265
2317
|
}
|
2318
|
+
|
2319
|
+
/* Relationship count styling */
|
2320
|
+
.relationship-count .badge {
|
2321
|
+
min-width: 80px;
|
2322
|
+
display: inline-flex;
|
2323
|
+
align-items: center;
|
2324
|
+
justify-content: center;
|
2325
|
+
}
|
2326
|
+
|
2327
|
+
.relationship-count .spinner-border-sm {
|
2328
|
+
width: 0.875rem;
|
2329
|
+
height: 0.875rem;
|
2330
|
+
}
|
2266
2331
|
</style>
|
2267
2332
|
|
2268
2333
|
<script>
|
data/config/routes.rb
CHANGED
data/lib/dbviewer/version.rb
CHANGED