dbviewer 0.4.8 → 0.5.0
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/README.md +4 -20
- data/app/controllers/concerns/dbviewer/database_operations.rb +1 -30
- data/app/controllers/dbviewer/api/base_controller.rb +19 -0
- data/app/controllers/dbviewer/api/database_controller.rb +14 -0
- data/app/controllers/dbviewer/api/queries_controller.rb +18 -0
- data/app/controllers/dbviewer/api/tables_controller.rb +39 -0
- data/app/controllers/dbviewer/home_controller.rb +0 -92
- data/app/views/dbviewer/home/index.html.erb +5 -5
- data/app/views/dbviewer/tables/show.html.erb +135 -2
- data/app/views/layouts/dbviewer/application.html.erb +184 -84
- data/app/views/layouts/dbviewer/shared/_sidebar.html.erb +0 -1
- data/config/routes.rb +18 -7
- data/lib/dbviewer/version.rb +1 -1
- data/lib/generators/dbviewer/structured_api_generator.rb +11 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 628884438be23468b7910e2a13c14b2aa00cc6361f01cb09b53ecdcf1d8a0b78
|
4
|
+
data.tar.gz: e9952d0c11ae3669b2604ac208f1a4ae224c37eb4d5f2b1074c4f284bb0bc24a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 369f106d728156712416e6d1f65c51e156ec1357e717c5cc4f95e9fe16dbe696e3a06b8c70a2ef2fd6ae559dd0e7ad7b73fc6a5b2e0ccc396a756633d733c2dd
|
7
|
+
data.tar.gz: 273df74ea6e69b327b01faecb4b3572983fbd461ee042c41ba2402729482ead2b9ebaed117c95158ff01def2e1a1373aaf0fed75d8268d8546b0f3e38d2ff2ae
|
data/README.md
CHANGED
@@ -5,7 +5,8 @@
|
|
5
5
|
DBViewer is a powerful Rails engine that provides a comprehensive interface to view and explore database tables, records, and schema.
|
6
6
|
It's designed for development, debugging, and database analysis, offering a clean and intuitive way to interact with your application's database.
|
7
7
|
|
8
|
-
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/
|
8
|
+
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/0d2719ad-f5b4-4818-891d-5bff7be6c5c3" />
|
9
|
+
|
9
10
|
|
10
11
|
## ✨ Features
|
11
12
|
|
@@ -45,27 +46,10 @@ It's designed for development, debugging, and database analysis, offering a clea
|
|
45
46
|
|
46
47
|
<details>
|
47
48
|
<summary>Click to see more screenshots</summary>
|
48
|
-
|
49
|
-
#### Dashboard Overview
|
50
|
-
|
51
|
-
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/4e803d51-9a5b-4c80-bb4c-a761dba15a40" />
|
52
|
-
|
53
|
-
#### Table Details
|
54
|
-
|
55
|
-
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/fe425ab4-5b22-4839-87bc-050b80ad4cf0" />
|
56
|
-
|
57
|
-
#### Query Editor
|
58
|
-
|
59
|
-
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/392c73c7-0724-4a39-8ffa-8ff5115c5d5f" />
|
60
|
-
|
61
|
-
#### Query Logs
|
62
|
-
|
63
|
-
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/7fcf3355-be3c-4d6a-9ab0-811333be5bbc" />
|
64
49
|
|
65
|
-
|
50
|
+
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/7d708c14-5f78-42c4-b769-2167546b3aad" />
|
51
|
+
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/f6d9a39a-a571-4328-908a-d96b3148f707" />
|
66
52
|
|
67
|
-
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/0a2f838f-4ca6-4592-b939-7c7f8ac40f48" />
|
68
|
-
|
69
53
|
</details>
|
70
54
|
|
71
55
|
## 📥 Installation
|
@@ -50,7 +50,6 @@ module Dbviewer
|
|
50
50
|
database_manager.tables.map do |table_name|
|
51
51
|
table_stats = {
|
52
52
|
name: table_name
|
53
|
-
# columns_count: database_manager.column_count(table_name)
|
54
53
|
}
|
55
54
|
|
56
55
|
# Only fetch record counts if explicitly requested
|
@@ -72,36 +71,8 @@ module Dbviewer
|
|
72
71
|
largest_tables: tables.sort_by { |t| -t[:record_count] }.first(10),
|
73
72
|
empty_tables: tables.select { |t| t[:record_count] == 0 }
|
74
73
|
}
|
75
|
-
|
76
|
-
# Calculate total foreign key relationships
|
77
|
-
begin
|
78
|
-
total_relationships = 0
|
79
|
-
tables.each do |table|
|
80
|
-
metadata = fetch_table_metadata(table[:name])
|
81
|
-
total_relationships += metadata[:foreign_keys].size if metadata && metadata[:foreign_keys]
|
82
|
-
end
|
83
|
-
analytics[:total_relationships] = total_relationships
|
84
|
-
rescue => e
|
85
|
-
Rails.logger.error("Error calculating relationship count: #{e.message}")
|
86
|
-
analytics[:total_relationships] = 0
|
87
|
-
end
|
88
|
-
|
89
74
|
# Calculate schema size if possible
|
90
|
-
|
91
|
-
analytics[:schema_size] = calculate_schema_size
|
92
|
-
rescue => e
|
93
|
-
Rails.logger.error("Error calculating schema size: #{e.message}")
|
94
|
-
analytics[:schema_size] = nil
|
95
|
-
end
|
96
|
-
|
97
|
-
# Calculate average rows per table
|
98
|
-
if tables.any?
|
99
|
-
analytics[:avg_records_per_table] = (analytics[:total_records].to_f / tables.size).round(1)
|
100
|
-
analytics[:avg_columns_per_table] = (analytics[:total_columns].to_f / tables.size).round(1)
|
101
|
-
else
|
102
|
-
analytics[:avg_records_per_table] = 0
|
103
|
-
analytics[:avg_columns_per_table] = 0
|
104
|
-
end
|
75
|
+
analytics[:schema_size] = calculate_schema_size
|
105
76
|
|
106
77
|
analytics
|
107
78
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Dbviewer
|
2
|
+
module Api
|
3
|
+
class BaseController < ApplicationController
|
4
|
+
# Skip setting the tables instance variable for API endpoints since we don't need it
|
5
|
+
skip_before_action :set_tables
|
6
|
+
|
7
|
+
# Common API response handling for errors
|
8
|
+
def render_error(error_message, status = :internal_server_error)
|
9
|
+
Rails.logger.error(error_message)
|
10
|
+
render json: { error: error_message }, status: status
|
11
|
+
end
|
12
|
+
|
13
|
+
# Common API response handling for success
|
14
|
+
def render_success(data)
|
15
|
+
render json: data
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Dbviewer
|
2
|
+
module Api
|
3
|
+
class DatabaseController < BaseController
|
4
|
+
def size
|
5
|
+
begin
|
6
|
+
size = calculate_schema_size
|
7
|
+
render_success(schema_size: size)
|
8
|
+
rescue => e
|
9
|
+
render_error("Error calculating schema size: #{e.message}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Dbviewer
|
2
|
+
module Api
|
3
|
+
class QueriesController < BaseController
|
4
|
+
def recent
|
5
|
+
@recent_queries = if Dbviewer.configuration.enable_query_logging
|
6
|
+
Dbviewer::Logger.instance.recent_queries(limit: 10)
|
7
|
+
else
|
8
|
+
[]
|
9
|
+
end
|
10
|
+
|
11
|
+
render_success({
|
12
|
+
enabled: Dbviewer.configuration.enable_query_logging,
|
13
|
+
queries: @recent_queries
|
14
|
+
})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Dbviewer
|
2
|
+
module Api
|
3
|
+
class TablesController < BaseController
|
4
|
+
def index
|
5
|
+
tables = fetch_tables_with_stats(include_record_counts: false)
|
6
|
+
render_success(total_tables: tables.size)
|
7
|
+
end
|
8
|
+
|
9
|
+
def records
|
10
|
+
tables = fetch_tables_with_stats(include_record_counts: true)
|
11
|
+
|
12
|
+
records_data = {
|
13
|
+
total_records: tables.sum { |t| t[:record_count] },
|
14
|
+
largest_tables: tables.sort_by { |t| -t[:record_count] }.first(10),
|
15
|
+
empty_tables: tables.select { |t| t[:record_count] == 0 },
|
16
|
+
avg_records_per_table: tables.any? ? (tables.sum { |t| t[:record_count] }.to_f / tables.size).round(1) : 0
|
17
|
+
}
|
18
|
+
|
19
|
+
render_success(records_data)
|
20
|
+
end
|
21
|
+
|
22
|
+
def relationships_count
|
23
|
+
begin
|
24
|
+
tables = fetch_tables_with_stats(include_record_counts: false)
|
25
|
+
total_relationships = 0
|
26
|
+
|
27
|
+
tables.each do |table|
|
28
|
+
metadata = fetch_table_metadata(table[:name])
|
29
|
+
total_relationships += metadata[:foreign_keys].size if metadata && metadata[:foreign_keys]
|
30
|
+
end
|
31
|
+
|
32
|
+
render_success(total_relationships: total_relationships)
|
33
|
+
rescue => e
|
34
|
+
render_error("Error calculating relationship count: #{e.message}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,98 +1,6 @@
|
|
1
1
|
module Dbviewer
|
2
2
|
class HomeController < ApplicationController
|
3
3
|
def index
|
4
|
-
# Load page immediately without heavy data
|
5
|
-
# Data will be loaded asynchronously via AJAX
|
6
|
-
end
|
7
|
-
|
8
|
-
def analytics
|
9
|
-
# This method is deprecated but kept for backward compatibility
|
10
|
-
analytics_data = fetch_database_analytics
|
11
|
-
# Remove record data which will be served by the records endpoint
|
12
|
-
analytics_data.delete(:total_records)
|
13
|
-
analytics_data.delete(:largest_tables)
|
14
|
-
analytics_data.delete(:empty_tables)
|
15
|
-
analytics_data.delete(:avg_records_per_table)
|
16
|
-
|
17
|
-
respond_to do |format|
|
18
|
-
format.json { render json: analytics_data }
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def tables_count
|
23
|
-
tables = fetch_tables_with_stats(include_record_counts: false)
|
24
|
-
|
25
|
-
respond_to do |format|
|
26
|
-
format.json { render json: { total_tables: tables.size } }
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def relationships_count
|
31
|
-
begin
|
32
|
-
tables = fetch_tables_with_stats(include_record_counts: false)
|
33
|
-
total_relationships = 0
|
34
|
-
|
35
|
-
tables.each do |table|
|
36
|
-
metadata = fetch_table_metadata(table[:name])
|
37
|
-
total_relationships += metadata[:foreign_keys].size if metadata && metadata[:foreign_keys]
|
38
|
-
end
|
39
|
-
|
40
|
-
respond_to do |format|
|
41
|
-
format.json { render json: { total_relationships: total_relationships } }
|
42
|
-
end
|
43
|
-
rescue => e
|
44
|
-
Rails.logger.error("Error calculating relationship count: #{e.message}")
|
45
|
-
respond_to do |format|
|
46
|
-
format.json { render json: { total_relationships: 0, error: e.message }, status: :internal_server_error }
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def database_size
|
52
|
-
begin
|
53
|
-
size = calculate_schema_size
|
54
|
-
|
55
|
-
respond_to do |format|
|
56
|
-
format.json { render json: { schema_size: size } }
|
57
|
-
end
|
58
|
-
rescue => e
|
59
|
-
Rails.logger.error("Error calculating schema size: #{e.message}")
|
60
|
-
respond_to do |format|
|
61
|
-
format.json { render json: { schema_size: nil, error: e.message }, status: :internal_server_error }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def records
|
67
|
-
tables = fetch_tables_with_stats(include_record_counts: true)
|
68
|
-
|
69
|
-
records_data = {
|
70
|
-
total_records: tables.sum { |t| t[:record_count] },
|
71
|
-
largest_tables: tables.sort_by { |t| -t[:record_count] }.first(10),
|
72
|
-
empty_tables: tables.select { |t| t[:record_count] == 0 },
|
73
|
-
avg_records_per_table: tables.any? ? (tables.sum { |t| t[:record_count] }.to_f / tables.size).round(1) : 0
|
74
|
-
}
|
75
|
-
|
76
|
-
respond_to do |format|
|
77
|
-
format.json { render json: records_data }
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def recent_queries
|
82
|
-
@recent_queries = if Dbviewer.configuration.enable_query_logging
|
83
|
-
Dbviewer::Logger.instance.recent_queries(limit: 10)
|
84
|
-
else
|
85
|
-
[]
|
86
|
-
end
|
87
|
-
|
88
|
-
respond_to do |format|
|
89
|
-
format.json do
|
90
|
-
render json: {
|
91
|
-
enabled: Dbviewer.configuration.enable_query_logging,
|
92
|
-
queries: @recent_queries
|
93
|
-
}
|
94
|
-
end
|
95
|
-
end
|
96
4
|
end
|
97
5
|
|
98
6
|
private
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</div>
|
9
9
|
</div>
|
10
10
|
|
11
|
-
<div class="row g-3 mb-4
|
11
|
+
<div class="row g-3 mb-4 dashboard-analytics-cards">
|
12
12
|
<div class="col-md-4">
|
13
13
|
<div class="card h-100 border-0 shadow-sm <%= stat_card_bg_class %>">
|
14
14
|
<div class="card-body d-flex align-items-center">
|
@@ -299,7 +299,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
299
299
|
}
|
300
300
|
|
301
301
|
// Load tables count data
|
302
|
-
fetch('<%= api_tables_path %>', {
|
302
|
+
fetch('<%= dbviewer.api_tables_path %>', {
|
303
303
|
headers: {
|
304
304
|
'Accept': 'application/json',
|
305
305
|
'X-Requested-With': 'XMLHttpRequest'
|
@@ -324,7 +324,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
324
324
|
});
|
325
325
|
|
326
326
|
// Load database size data
|
327
|
-
fetch('<%=
|
327
|
+
fetch('<%= dbviewer.size_api_database_path %>', {
|
328
328
|
headers: {
|
329
329
|
'Accept': 'application/json',
|
330
330
|
'X-Requested-With': 'XMLHttpRequest'
|
@@ -349,7 +349,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
349
349
|
});
|
350
350
|
|
351
351
|
// Load records data separately
|
352
|
-
fetch('<%=
|
352
|
+
fetch('<%= dbviewer.records_api_tables_path %>', {
|
353
353
|
headers: {
|
354
354
|
'Accept': 'application/json',
|
355
355
|
'X-Requested-With': 'XMLHttpRequest'
|
@@ -377,7 +377,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
377
377
|
});
|
378
378
|
|
379
379
|
// Load recent queries data
|
380
|
-
fetch('<%=
|
380
|
+
fetch('<%= dbviewer.recent_api_queries_path %>', {
|
381
381
|
headers: {
|
382
382
|
'Accept': 'application/json',
|
383
383
|
'X-Requested-With': 'XMLHttpRequest'
|
@@ -314,7 +314,7 @@
|
|
314
314
|
<% content_for :sidebar_active do %>active<% end %>
|
315
315
|
|
316
316
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
317
|
-
<div>
|
317
|
+
<div class="d-flex justify-content-between align-items-center">
|
318
318
|
<h1>Table: <%= @table_name %></h1>
|
319
319
|
</div>
|
320
320
|
<div class="d-flex gap-2">
|
@@ -393,7 +393,7 @@
|
|
393
393
|
</div>
|
394
394
|
|
395
395
|
<!-- Records Section -->
|
396
|
-
<div class="dbviewer-card card mb-4">
|
396
|
+
<div class="dbviewer-card card mb-4" id="table-section">
|
397
397
|
<div class="card-header d-flex justify-content-between align-items-center">
|
398
398
|
<h5 class="mb-0">
|
399
399
|
<select id="per-page-select" class="form-select form-select-sm" onchange="window.location.href='<%= table_path(@table_name) %>?<%= per_page_url_params(@table_name) %>'">
|
@@ -414,6 +414,9 @@
|
|
414
414
|
<% if active_filters > 0 %>
|
415
415
|
<span class="badge bg-info ms-2" title="Active filters"><i class="bi bi-funnel-fill me-1"></i><%= active_filters %></span>
|
416
416
|
<% end %>
|
417
|
+
<button type="button" class="btn btn-outline-secondary btn-sm ms-2" id="fullscreen-toggle" title="Toggle fullscreen">
|
418
|
+
<i class="bi bi-fullscreen" id="fullscreen-icon"></i>
|
419
|
+
</button>
|
417
420
|
</div>
|
418
421
|
</div>
|
419
422
|
<div class="card-body p-0">
|
@@ -1311,6 +1314,71 @@
|
|
1311
1314
|
[data-bs-theme="dark"] .record-detail-table .code-block {
|
1312
1315
|
background-color: var(--bs-dark);
|
1313
1316
|
}
|
1317
|
+
|
1318
|
+
/* Fullscreen table styles */
|
1319
|
+
.table-fullscreen {
|
1320
|
+
position: fixed !important;
|
1321
|
+
top: 0 !important;
|
1322
|
+
left: 0 !important;
|
1323
|
+
width: 100vw !important;
|
1324
|
+
height: 100vh !important;
|
1325
|
+
z-index: 9999 !important;
|
1326
|
+
background: var(--bs-body-bg) !important;
|
1327
|
+
margin: 0 !important;
|
1328
|
+
border-radius: 0 !important;
|
1329
|
+
overflow: hidden !important;
|
1330
|
+
display: flex !important;
|
1331
|
+
flex-direction: column !important;
|
1332
|
+
}
|
1333
|
+
|
1334
|
+
.table-fullscreen .card-body {
|
1335
|
+
flex: 1 !important;
|
1336
|
+
overflow: hidden !important;
|
1337
|
+
display: flex !important;
|
1338
|
+
flex-direction: column !important;
|
1339
|
+
}
|
1340
|
+
|
1341
|
+
.table-fullscreen .table-responsive {
|
1342
|
+
flex: 1 !important;
|
1343
|
+
overflow: auto !important;
|
1344
|
+
}
|
1345
|
+
|
1346
|
+
.table-fullscreen .card-header {
|
1347
|
+
flex-shrink: 0 !important;
|
1348
|
+
position: sticky !important;
|
1349
|
+
top: 0 !important;
|
1350
|
+
z-index: 10000 !important;
|
1351
|
+
background: var(--bs-body-bg) !important;
|
1352
|
+
border-bottom: 1px solid var(--bs-border-color) !important;
|
1353
|
+
}
|
1354
|
+
|
1355
|
+
/* Hide pagination in fullscreen mode */
|
1356
|
+
.table-fullscreen .pagination-container {
|
1357
|
+
display: none !important;
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
/* Adjust table header in fullscreen */
|
1361
|
+
.table-fullscreen .dbviewer-table-header {
|
1362
|
+
position: sticky !important;
|
1363
|
+
top: 0 !important;
|
1364
|
+
z-index: 100 !important;
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
/* Ensure body doesn't scroll when table is fullscreen */
|
1368
|
+
body.table-fullscreen-active {
|
1369
|
+
overflow: hidden !important;
|
1370
|
+
}
|
1371
|
+
|
1372
|
+
/* Fullscreen button hover effect */
|
1373
|
+
#fullscreen-toggle:hover {
|
1374
|
+
background-color: var(--bs-secondary-bg) !important;
|
1375
|
+
border-color: var(--bs-secondary-border-subtle) !important;
|
1376
|
+
}
|
1377
|
+
|
1378
|
+
/* Smooth transitions */
|
1379
|
+
#table-section {
|
1380
|
+
transition: all 0.3s ease-in-out;
|
1381
|
+
}
|
1314
1382
|
</style>
|
1315
1383
|
|
1316
1384
|
<% if @timestamp_data.present? %>
|
@@ -1516,5 +1584,70 @@
|
|
1516
1584
|
|
1517
1585
|
return section;
|
1518
1586
|
}
|
1587
|
+
|
1588
|
+
// Table fullscreen functionality
|
1589
|
+
document.addEventListener('DOMContentLoaded', function() {
|
1590
|
+
const fullscreenToggle = document.getElementById('fullscreen-toggle');
|
1591
|
+
const fullscreenIcon = document.getElementById('fullscreen-icon');
|
1592
|
+
const tableSection = document.getElementById('table-section');
|
1593
|
+
|
1594
|
+
if (fullscreenToggle && tableSection) {
|
1595
|
+
// Key for storing fullscreen state in localStorage
|
1596
|
+
const fullscreenStateKey = 'dbviewer-table-fullscreen-<%= @table_name %>';
|
1597
|
+
|
1598
|
+
// Function to apply fullscreen state
|
1599
|
+
function applyFullscreenState(isFullscreen) {
|
1600
|
+
if (isFullscreen) {
|
1601
|
+
// Enter fullscreen
|
1602
|
+
tableSection.classList.add('table-fullscreen');
|
1603
|
+
document.body.classList.add('table-fullscreen-active');
|
1604
|
+
fullscreenIcon.classList.remove('bi-fullscreen');
|
1605
|
+
fullscreenIcon.classList.add('bi-fullscreen-exit');
|
1606
|
+
fullscreenToggle.setAttribute('title', 'Exit fullscreen');
|
1607
|
+
} else {
|
1608
|
+
// Exit fullscreen
|
1609
|
+
tableSection.classList.remove('table-fullscreen');
|
1610
|
+
document.body.classList.remove('table-fullscreen-active');
|
1611
|
+
fullscreenIcon.classList.remove('bi-fullscreen-exit');
|
1612
|
+
fullscreenIcon.classList.add('bi-fullscreen');
|
1613
|
+
fullscreenToggle.setAttribute('title', 'Toggle fullscreen');
|
1614
|
+
}
|
1615
|
+
}
|
1616
|
+
|
1617
|
+
// Restore fullscreen state from localStorage on page load
|
1618
|
+
try {
|
1619
|
+
const savedState = localStorage.getItem(fullscreenStateKey);
|
1620
|
+
if (savedState === 'true') {
|
1621
|
+
applyFullscreenState(true);
|
1622
|
+
}
|
1623
|
+
} catch (e) {
|
1624
|
+
// Handle localStorage not available (private browsing, etc.)
|
1625
|
+
console.warn('Could not restore fullscreen state:', e);
|
1626
|
+
}
|
1627
|
+
|
1628
|
+
fullscreenToggle.addEventListener('click', function() {
|
1629
|
+
const isFullscreen = tableSection.classList.contains('table-fullscreen');
|
1630
|
+
const newState = !isFullscreen;
|
1631
|
+
|
1632
|
+
// Apply the new state
|
1633
|
+
applyFullscreenState(newState);
|
1634
|
+
|
1635
|
+
// Save state to localStorage
|
1636
|
+
try {
|
1637
|
+
localStorage.setItem(fullscreenStateKey, newState.toString());
|
1638
|
+
} catch (e) {
|
1639
|
+
// Handle localStorage not available (private browsing, etc.)
|
1640
|
+
console.warn('Could not save fullscreen state:', e);
|
1641
|
+
}
|
1642
|
+
});
|
1643
|
+
|
1644
|
+
// Exit fullscreen with Escape key
|
1645
|
+
document.addEventListener('keydown', function(e) {
|
1646
|
+
if (e.key === 'Escape' && tableSection.classList.contains('table-fullscreen')) {
|
1647
|
+
fullscreenToggle.click();
|
1648
|
+
}
|
1649
|
+
});
|
1650
|
+
}
|
1651
|
+
});
|
1519
1652
|
</script>
|
1520
1653
|
<% end %>
|
@@ -32,9 +32,11 @@
|
|
32
32
|
/* Base styles and typography */
|
33
33
|
body {
|
34
34
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
35
|
-
font-size: 0.
|
35
|
+
font-size: 0.875rem;
|
36
36
|
line-height: 1.5;
|
37
37
|
letter-spacing: -0.01em;
|
38
|
+
color: #464c54; /* More subdued text color for light theme */
|
39
|
+
background-color: #f5f6f9; /* Subtle gray background */
|
38
40
|
}
|
39
41
|
|
40
42
|
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
@@ -43,11 +45,11 @@
|
|
43
45
|
line-height: 1.25;
|
44
46
|
}
|
45
47
|
|
46
|
-
/* Core layout styles */
|
48
|
+
/* Core layout styles - Grafana-like compact layout */
|
47
49
|
.dbviewer-wrapper { display: flex; flex-direction: column; min-height: 100vh; }
|
48
|
-
.dbviewer-navbar { height:
|
49
|
-
.dbviewer-navbar-spacer { height:
|
50
|
-
.dbviewer-content { display: flex; flex: 1; min-height: calc(100vh -
|
50
|
+
.dbviewer-navbar { height: 48px; } /* Reduced height for more compact header */
|
51
|
+
.dbviewer-navbar-spacer { height: 48px; } /* Creates space for the fixed navbar */
|
52
|
+
.dbviewer-content { display: flex; flex: 1; min-height: calc(100vh - 48px); padding-top: 0; }
|
51
53
|
|
52
54
|
/* Smooth theme transitions */
|
53
55
|
html {
|
@@ -67,19 +69,19 @@
|
|
67
69
|
transition: color 0.2s ease, background-color 0.2s ease;
|
68
70
|
}
|
69
71
|
|
70
|
-
/* Sidebar styles - enhanced for elegance */
|
72
|
+
/* Sidebar styles - enhanced for elegance with Grafana-like compact design */
|
71
73
|
.dbviewer-sidebar {
|
72
|
-
width:
|
73
|
-
height: calc(100vh -
|
74
|
+
width: 240px; /* More compact sidebar width */
|
75
|
+
height: calc(100vh - 48px);
|
74
76
|
position: fixed;
|
75
|
-
top:
|
77
|
+
top: 48px; /* Positioned right below the fixed navbar */
|
76
78
|
left: 0;
|
77
79
|
z-index: 1000;
|
78
80
|
display: flex;
|
79
81
|
flex-direction: column;
|
80
82
|
transition: transform 0.3s ease-in-out, background-color 0.2s ease, border-color 0.2s ease;
|
81
|
-
overflow: hidden;
|
82
|
-
box-shadow:
|
83
|
+
overflow: hidden;
|
84
|
+
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.04); /* Subtler shadow */
|
83
85
|
}
|
84
86
|
|
85
87
|
/* Dark mode overrides */
|
@@ -94,7 +96,7 @@
|
|
94
96
|
}
|
95
97
|
|
96
98
|
.dbviewer-sidebar-header {
|
97
|
-
padding:
|
99
|
+
padding: 0.8rem 1rem; /* Reduced padding for more compact header */
|
98
100
|
font-weight: 600;
|
99
101
|
align-items: center;
|
100
102
|
justify-content: space-between;
|
@@ -157,7 +159,7 @@
|
|
157
159
|
.dbviewer-sidebar-content {
|
158
160
|
flex: 1;
|
159
161
|
overflow-y: auto;
|
160
|
-
padding: 0.
|
162
|
+
padding: 0.25rem 0; /* Reduced padding */
|
161
163
|
height: 100%;
|
162
164
|
/* Improved scrollbar */
|
163
165
|
scrollbar-width: thin;
|
@@ -183,9 +185,9 @@
|
|
183
185
|
|
184
186
|
.dbviewer-main {
|
185
187
|
flex: 1;
|
186
|
-
margin-left:
|
187
|
-
padding: 2rem
|
188
|
-
padding-top:
|
188
|
+
margin-left: 240px; /* Reduced sidebar width */
|
189
|
+
padding: 1.2rem 1.5rem; /* Reduced padding for more compact look */
|
190
|
+
padding-top: 0.8rem; /* Adjusted for fixed header */
|
189
191
|
min-width: 0;
|
190
192
|
animation: fadeIn 0.5s ease-in-out;
|
191
193
|
transition: all 0.3s ease;
|
@@ -281,19 +283,20 @@
|
|
281
283
|
}
|
282
284
|
}
|
283
285
|
|
284
|
-
/* Extra small screens */
|
286
|
+
/* Extra small screens - more compact for small devices */
|
285
287
|
@media (max-width: 575.98px) {
|
286
|
-
.dbviewer-main { padding: 0.
|
288
|
+
.dbviewer-main { padding: 0.6rem; }
|
287
289
|
h1, .h1 { font-size: 1.6rem; }
|
288
290
|
h2, .h2 { font-size: 1.4rem; }
|
289
291
|
h3, .h3 { font-size: 1.2rem; }
|
290
292
|
|
291
293
|
.btn-sm {
|
292
|
-
font-size: 0.
|
294
|
+
font-size: 0.75rem;
|
295
|
+
padding: 0.2rem 0.5rem;
|
293
296
|
}
|
294
297
|
|
295
298
|
.card-body {
|
296
|
-
padding:
|
299
|
+
padding: 0.75rem;
|
297
300
|
}
|
298
301
|
}
|
299
302
|
|
@@ -343,30 +346,31 @@
|
|
343
346
|
border-color: rgba(134, 183, 254, 0.7);
|
344
347
|
}
|
345
348
|
|
346
|
-
/* Table structure and visualization components */
|
349
|
+
/* Table structure and visualization components - Grafana-inspired */
|
347
350
|
.dbviewer-card {
|
348
|
-
border-radius:
|
351
|
+
border-radius: 3px; /* Smaller border radius for Grafana look */
|
349
352
|
transition: all 0.2s ease;
|
350
353
|
overflow: hidden;
|
351
|
-
box-shadow: 0
|
354
|
+
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05); /* Subtler shadow */
|
352
355
|
height: 100%;
|
353
356
|
}
|
354
357
|
|
355
358
|
.dbviewer-card:hover {
|
356
|
-
box-shadow: 0
|
357
|
-
transform: translateY(-
|
359
|
+
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.07), 0 1px 3px rgba(0, 0, 0, 0.06);
|
360
|
+
transform: translateY(-1px); /* Subtler hover effect */
|
358
361
|
}
|
359
362
|
|
360
|
-
/* Improved card headers */
|
363
|
+
/* Improved card headers - more compact */
|
361
364
|
.dbviewer-card .card-header {
|
362
365
|
font-weight: 600;
|
363
|
-
padding: 1rem
|
366
|
+
padding: 0.7rem 1rem; /* Reduced padding */
|
364
367
|
border-bottom-width: 1px;
|
368
|
+
font-size: 0.95rem; /* Slightly smaller font */
|
365
369
|
}
|
366
370
|
|
367
|
-
/* Card body padding */
|
371
|
+
/* Card body padding - more compact */
|
368
372
|
.dbviewer-card .card-body {
|
369
|
-
padding:
|
373
|
+
padding: 0.9rem 1rem; /* Reduced padding */
|
370
374
|
}
|
371
375
|
|
372
376
|
[data-bs-theme="light"] .dbviewer-card {
|
@@ -483,7 +487,7 @@
|
|
483
487
|
/* List group styling for dark mode */
|
484
488
|
[data-bs-theme="dark"] .list-group-item {
|
485
489
|
background-color: var(--bs-dark);
|
486
|
-
border-color: rgba(255, 255, 255, 0.
|
490
|
+
border-color: rgba(255, 255, 255, 0.08); /* Subtler border */
|
487
491
|
color: var(--bs-light);
|
488
492
|
}
|
489
493
|
|
@@ -494,10 +498,10 @@
|
|
494
498
|
z-index: 1;
|
495
499
|
}
|
496
500
|
|
497
|
-
/* Equal height for timeline and structure cards */
|
501
|
+
/* Equal height for timeline and structure cards - Grafana-inspired compact layout */
|
498
502
|
.two-column-layout .card { height: 100%; display: flex; flex-direction: column; }
|
499
|
-
.two-column-layout .card-body { flex: 1; display: flex; flex-direction: column; }
|
500
|
-
.two-column-layout .chart-container { flex: 1; min-height:
|
503
|
+
.two-column-layout .card-body { flex: 1; display: flex; flex-direction: column; padding: 0.75rem; } /* Reduced padding */
|
504
|
+
.two-column-layout .chart-container { flex: 1; min-height: 220px; } /* Reduced min-height */
|
501
505
|
.two-column-layout .structure-container { padding: 0; }
|
502
506
|
.two-column-layout .tab-content { flex: 1; overflow: hidden; display: flex; flex-direction: column; }
|
503
507
|
.two-column-layout .tab-pane { flex: 1; overflow: hidden; }
|
@@ -560,24 +564,28 @@
|
|
560
564
|
font-size: 1.2rem;
|
561
565
|
}
|
562
566
|
|
563
|
-
/* Header styles for a more professional look */
|
567
|
+
/* Header styles for a more professional Grafana-like look */
|
564
568
|
.navbar {
|
565
569
|
transition: all 0.3s ease;
|
566
|
-
padding: 0.
|
567
|
-
box-shadow: 0
|
570
|
+
padding: 0 0.75rem; /* Reduced horizontal padding */
|
571
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); /* Subtler shadow */
|
572
|
+
min-height: 48px; /* Fixed height */
|
568
573
|
}
|
569
574
|
|
570
575
|
.navbar-brand {
|
571
576
|
font-weight: 600;
|
572
577
|
letter-spacing: -0.01em;
|
578
|
+
font-size: 1.1rem; /* Smaller brand */
|
579
|
+
padding: 0.1rem 0; /* Reduced padding */
|
573
580
|
}
|
574
581
|
|
575
582
|
.nav-link {
|
576
583
|
font-weight: 500;
|
577
|
-
padding: 0.
|
578
|
-
border-radius:
|
584
|
+
padding: 0.35rem 0.65rem !important; /* Reduced padding */
|
585
|
+
border-radius: 2px; /* Smaller radius for Grafana look */
|
579
586
|
transition: all 0.2s ease;
|
580
|
-
margin: 0
|
587
|
+
margin: 0 1px; /* Reduced margin */
|
588
|
+
font-size: 0.9rem; /* Smaller font */
|
581
589
|
}
|
582
590
|
|
583
591
|
.nav-link.active {
|
@@ -602,11 +610,11 @@
|
|
602
610
|
border-radius: 0;
|
603
611
|
border-left: 0;
|
604
612
|
border-right: 0;
|
605
|
-
padding: 0.
|
613
|
+
padding: 0.5rem 0.85rem; /* Reduced padding for compact look */
|
606
614
|
transition: all 0.15s ease-in-out;
|
607
615
|
position: relative;
|
608
616
|
font-weight: 500;
|
609
|
-
font-size: 0.
|
617
|
+
font-size: 0.85rem; /* Smaller font for Grafana-like appearance */
|
610
618
|
}
|
611
619
|
|
612
620
|
.list-group-item:first-child {
|
@@ -679,23 +687,23 @@
|
|
679
687
|
color: #adb5bd;
|
680
688
|
}
|
681
689
|
|
682
|
-
/* Enhanced button styling */
|
690
|
+
/* Enhanced button styling - Grafana-inspired */
|
683
691
|
.btn {
|
684
692
|
font-weight: 500;
|
685
|
-
padding: 0.
|
686
|
-
border-radius:
|
693
|
+
padding: 0.4rem 0.85rem; /* Reduced padding */
|
694
|
+
border-radius: 2px; /* Smaller radius for Grafana look */
|
687
695
|
transition: all 0.2s ease;
|
688
|
-
box-shadow: 0 1px
|
696
|
+
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
|
689
697
|
}
|
690
698
|
|
691
699
|
.btn-sm {
|
692
|
-
padding: 0.
|
693
|
-
font-size: 0.
|
700
|
+
padding: 0.25rem 0.6rem; /* Reduced padding */
|
701
|
+
font-size: 0.8rem; /* Smaller font size */
|
694
702
|
}
|
695
703
|
|
696
704
|
.btn-lg {
|
697
|
-
padding: 0.
|
698
|
-
font-size:
|
705
|
+
padding: 0.5rem 1rem; /* Reduced padding */
|
706
|
+
font-size: 0.95rem; /* Smaller font size */
|
699
707
|
}
|
700
708
|
|
701
709
|
.btn:active {
|
@@ -760,23 +768,23 @@
|
|
760
768
|
text-decoration: underline;
|
761
769
|
}
|
762
770
|
|
763
|
-
/* Enhanced code blocks and SQL query styling */
|
771
|
+
/* Enhanced code blocks and SQL query styling - Grafana-inspired */
|
764
772
|
pre, code {
|
765
773
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
766
|
-
border-radius:
|
774
|
+
border-radius: 2px; /* Smaller radius */
|
767
775
|
transition: all 0.2s ease;
|
768
776
|
}
|
769
777
|
|
770
778
|
pre {
|
771
|
-
padding:
|
772
|
-
margin-bottom:
|
773
|
-
border-radius:
|
774
|
-
box-shadow: 0
|
779
|
+
padding: 0.75rem; /* Reduced padding */
|
780
|
+
margin-bottom: 0.75rem; /* Reduced margin */
|
781
|
+
border-radius: 2px; /* Smaller radius */
|
782
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
775
783
|
}
|
776
784
|
|
777
785
|
code {
|
778
|
-
font-size: 0.
|
779
|
-
padding: 0.
|
786
|
+
font-size: 0.825em; /* Smaller font */
|
787
|
+
padding: 0.15em 0.3em; /* Reduced padding */
|
780
788
|
}
|
781
789
|
|
782
790
|
/* SQL query code in tables */
|
@@ -868,7 +876,7 @@
|
|
868
876
|
color: #ffffff;
|
869
877
|
}
|
870
878
|
|
871
|
-
/* Enhanced table styling */
|
879
|
+
/* Enhanced table styling - Grafana-like compact design */
|
872
880
|
.table {
|
873
881
|
margin-bottom: 0;
|
874
882
|
width: 100%;
|
@@ -881,12 +889,12 @@
|
|
881
889
|
}
|
882
890
|
|
883
891
|
.table thead th {
|
884
|
-
padding:
|
892
|
+
padding: 0.65rem 0.75rem; /* Reduced padding */
|
885
893
|
font-weight: 600;
|
886
894
|
border-top: 0;
|
887
895
|
vertical-align: middle;
|
888
896
|
letter-spacing: 0.01em;
|
889
|
-
font-size: 0.
|
897
|
+
font-size: 0.78rem; /* Smaller font */
|
890
898
|
text-transform: uppercase;
|
891
899
|
position: sticky;
|
892
900
|
top: 0;
|
@@ -895,10 +903,10 @@
|
|
895
903
|
}
|
896
904
|
|
897
905
|
.table tbody td {
|
898
|
-
padding: 0.
|
906
|
+
padding: 0.5rem 0.75rem; /* Reduced padding */
|
899
907
|
vertical-align: middle;
|
900
908
|
border-color: var(--bs-border-color);
|
901
|
-
font-size: 0.
|
909
|
+
font-size: 0.85rem; /* Smaller font */
|
902
910
|
}
|
903
911
|
|
904
912
|
.table tbody tr:hover {
|
@@ -960,14 +968,21 @@
|
|
960
968
|
color: #000 !important;
|
961
969
|
}
|
962
970
|
|
963
|
-
/*
|
971
|
+
/* Badge styling - More Grafana-like */
|
972
|
+
.badge {
|
973
|
+
padding: 0.3em 0.5em;
|
974
|
+
font-size: 0.75em;
|
975
|
+
border-radius: 2px;
|
976
|
+
}
|
977
|
+
|
978
|
+
/* Enhanced alert styling - Grafana-inspired */
|
964
979
|
.alert {
|
965
|
-
border-radius:
|
980
|
+
border-radius: 2px; /* Smaller radius for Grafana look */
|
966
981
|
border-width: 0;
|
967
|
-
padding: 1rem
|
968
|
-
box-shadow: 0
|
982
|
+
padding: 0.7rem 1rem; /* Reduced padding */
|
983
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
969
984
|
position: relative;
|
970
|
-
border-left:
|
985
|
+
border-left: 3px solid transparent; /* Thinner border */
|
971
986
|
}
|
972
987
|
|
973
988
|
.alert-info {
|
@@ -1091,40 +1106,40 @@
|
|
1091
1106
|
background-color: var(--bs-light);
|
1092
1107
|
}
|
1093
1108
|
|
1094
|
-
/* Enhanced stat cards and metric icons */
|
1109
|
+
/* Enhanced stat cards and metric icons - Grafana-inspired */
|
1095
1110
|
.stat-card-bg {
|
1096
1111
|
background: #ffffff;
|
1097
|
-
border-radius:
|
1098
|
-
box-shadow: 0
|
1112
|
+
border-radius: 3px; /* Smaller radius for Grafana look */
|
1113
|
+
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.03);
|
1099
1114
|
transition: all 0.3s ease;
|
1100
1115
|
border: none;
|
1101
1116
|
}
|
1102
1117
|
|
1103
1118
|
.stat-card-bg:hover {
|
1104
|
-
transform: translateY(-
|
1105
|
-
box-shadow: 0
|
1119
|
+
transform: translateY(-1px); /* Subtler hover */
|
1120
|
+
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.06);
|
1106
1121
|
}
|
1107
1122
|
|
1108
1123
|
[data-bs-theme="dark"] .stat-card-bg {
|
1109
1124
|
background-color: #212529;
|
1110
|
-
box-shadow: 0
|
1125
|
+
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.08);
|
1111
1126
|
}
|
1112
1127
|
|
1113
|
-
/* Enhanced metric icon styling */
|
1128
|
+
/* Enhanced metric icon styling - Grafana-inspired */
|
1114
1129
|
.metric-icon {
|
1115
1130
|
display: flex;
|
1116
1131
|
align-items: center;
|
1117
1132
|
justify-content: center;
|
1118
|
-
border-radius:
|
1119
|
-
width:
|
1120
|
-
height:
|
1121
|
-
min-width:
|
1133
|
+
border-radius: 3px; /* Smaller radius */
|
1134
|
+
width: 50px; /* Smaller icon */
|
1135
|
+
height: 50px; /* Smaller icon */
|
1136
|
+
min-width: 50px; /* Smaller icon */
|
1122
1137
|
text-align: center;
|
1123
|
-
background: linear-gradient(135deg, rgba(var(--bs-primary-rgb), 0.
|
1138
|
+
background: linear-gradient(135deg, rgba(var(--bs-primary-rgb), 0.15) 0%, rgba(var(--bs-primary-rgb), 0.08) 100%);
|
1124
1139
|
color: var(--bs-primary);
|
1125
|
-
font-size: 1.
|
1140
|
+
font-size: 1.4rem; /* Smaller icon */
|
1126
1141
|
transition: all 0.3s ease;
|
1127
|
-
box-shadow: 0
|
1142
|
+
box-shadow: 0 1px 5px rgba(var(--bs-primary-rgb), 0.08);
|
1128
1143
|
}
|
1129
1144
|
|
1130
1145
|
.stat-card-bg:hover .metric-icon {
|
@@ -1181,17 +1196,28 @@
|
|
1181
1196
|
transition: all 0.2s ease;
|
1182
1197
|
}
|
1183
1198
|
|
1184
|
-
/* Improved table hover effect */
|
1199
|
+
/* Improved table hover effect - Grafana-like */
|
1185
1200
|
.table-hover > tbody > tr:hover {
|
1186
1201
|
transition: all 0.15s ease;
|
1187
1202
|
}
|
1188
1203
|
|
1189
1204
|
[data-bs-theme="light"] .table-hover > tbody > tr:hover {
|
1190
|
-
background-color: rgba(13, 110, 253, 0.
|
1205
|
+
background-color: rgba(13, 110, 253, 0.03); /* Subtler hover */
|
1191
1206
|
}
|
1192
1207
|
|
1193
1208
|
[data-bs-theme="dark"] .table-hover > tbody > tr:hover {
|
1194
|
-
background-color: rgba(13, 110, 253, 0.
|
1209
|
+
background-color: rgba(13, 110, 253, 0.05); /* Subtler hover */
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
/* Grafana-inspired grid layout adjustments */
|
1213
|
+
.row {
|
1214
|
+
margin-right: -0.5rem;
|
1215
|
+
margin-left: -0.5rem;
|
1216
|
+
}
|
1217
|
+
|
1218
|
+
.row > [class^="col-"] {
|
1219
|
+
padding-right: 0.5rem;
|
1220
|
+
padding-left: 0.5rem;
|
1195
1221
|
}
|
1196
1222
|
|
1197
1223
|
/* Button press effect */
|
@@ -1228,6 +1254,80 @@
|
|
1228
1254
|
[data-bs-theme="dark"] html::-webkit-scrollbar-thumb {
|
1229
1255
|
background-color: rgba(255, 255, 255, 0.2);
|
1230
1256
|
}
|
1257
|
+
|
1258
|
+
/* Grafana-like panel enhancements */
|
1259
|
+
body {
|
1260
|
+
color: #464c54; /* More subdued text color for light theme */
|
1261
|
+
background-color: #f5f6f9; /* Subtle gray background */
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
[data-bs-theme="dark"] body {
|
1265
|
+
background-color: #161719; /* Darker background for dark theme */
|
1266
|
+
color: #d8d9da; /* Softer text color for dark theme */
|
1267
|
+
}
|
1268
|
+
|
1269
|
+
/* Make font sizes consistently smaller for more compact look */
|
1270
|
+
body {
|
1271
|
+
font-size: 0.875rem;
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
h1, .h1 { font-size: 1.6rem; }
|
1275
|
+
h2, .h2 { font-size: 1.4rem; }
|
1276
|
+
h3, .h3 { font-size: 1.2rem; }
|
1277
|
+
h4, .h4 { font-size: 1.1rem; }
|
1278
|
+
h5, .h5 { font-size: 1rem; }
|
1279
|
+
|
1280
|
+
/* Unify panel appearance */
|
1281
|
+
.card, .list-group {
|
1282
|
+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.06);
|
1283
|
+
border: none;
|
1284
|
+
border-radius: 2px;
|
1285
|
+
}
|
1286
|
+
|
1287
|
+
[data-bs-theme="dark"] .card,
|
1288
|
+
[data-bs-theme="dark"] .list-group {
|
1289
|
+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
|
1290
|
+
background-color: #212124;
|
1291
|
+
}
|
1292
|
+
|
1293
|
+
/* Improved tab styling */
|
1294
|
+
.nav-tabs {
|
1295
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
.nav-tabs .nav-link {
|
1299
|
+
border: none;
|
1300
|
+
border-bottom: 2px solid transparent;
|
1301
|
+
background-color: transparent;
|
1302
|
+
padding: 0.5rem 0.75rem;
|
1303
|
+
margin-bottom: -1px;
|
1304
|
+
font-size: 0.9rem;
|
1305
|
+
}
|
1306
|
+
|
1307
|
+
.nav-tabs .nav-link.active {
|
1308
|
+
border-bottom-color: var(--bs-primary);
|
1309
|
+
color: var(--bs-primary);
|
1310
|
+
}
|
1311
|
+
|
1312
|
+
[data-bs-theme="dark"] .nav-tabs {
|
1313
|
+
border-color: rgba(255, 255, 255, 0.1);
|
1314
|
+
}
|
1315
|
+
|
1316
|
+
/* Remove excessive margins between elements */
|
1317
|
+
.row + .row,
|
1318
|
+
.card + .card {
|
1319
|
+
margin-top: 0.75rem;
|
1320
|
+
}
|
1321
|
+
|
1322
|
+
/* Elegant form controls */
|
1323
|
+
.form-control, .form-select {
|
1324
|
+
padding: 0.4rem 0.6rem;
|
1325
|
+
font-size: 0.875rem;
|
1326
|
+
height: auto;
|
1327
|
+
border-radius: 2px;
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
/* Add this right above the style closing tag */
|
1231
1331
|
</style>
|
1232
1332
|
</head>
|
1233
1333
|
<body>
|
@@ -1,6 +1,5 @@
|
|
1
1
|
<div class="dbviewer-sidebar-top">
|
2
2
|
<div class="dbviewer-table-filter-container p-1 mb-0">
|
3
|
-
<i class="bi bi-search dbviewer-table-filter-icon"></i>
|
4
3
|
<input type="text" class="form-control form-control-sm dbviewer-table-filter mb-0"
|
5
4
|
id="tableSearch" placeholder="Filter tables..." aria-label="Filter tables">
|
6
5
|
</div>
|
data/config/routes.rb
CHANGED
@@ -19,13 +19,24 @@ Dbviewer::Engine.routes.draw do
|
|
19
19
|
# Homepage and API endpoints
|
20
20
|
get "dashboard", to: "home#index", as: :dashboard
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
namespace :api do
|
23
|
+
resources :tables, only: [ :index ] do
|
24
|
+
collection do
|
25
|
+
get "records"
|
26
|
+
get "relationships_count"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
resource :database, only: [], controller: "database" do
|
31
|
+
get "size"
|
32
|
+
end
|
33
|
+
|
34
|
+
resources :queries, only: [] do
|
35
|
+
collection do
|
36
|
+
get "recent"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
29
40
|
|
30
41
|
root to: "home#index"
|
31
42
|
end
|
data/lib/dbviewer/version.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Dbviewer
|
2
|
+
module Generators
|
3
|
+
class StructuredApiGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path("../templates", __FILE__)
|
5
|
+
|
6
|
+
def create_initializer
|
7
|
+
template "structured_api_initializer.rb", "config/initializers/dbviewer_structured_api.rb"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbviewer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wailan Tirajoh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -55,6 +55,10 @@ files:
|
|
55
55
|
- app/controllers/concerns/dbviewer/database_operations.rb
|
56
56
|
- app/controllers/concerns/dbviewer/error_handling.rb
|
57
57
|
- app/controllers/concerns/dbviewer/pagination_concern.rb
|
58
|
+
- app/controllers/dbviewer/api/base_controller.rb
|
59
|
+
- app/controllers/dbviewer/api/database_controller.rb
|
60
|
+
- app/controllers/dbviewer/api/queries_controller.rb
|
61
|
+
- app/controllers/dbviewer/api/tables_controller.rb
|
58
62
|
- app/controllers/dbviewer/application_controller.rb
|
59
63
|
- app/controllers/dbviewer/entity_relationship_diagrams_controller.rb
|
60
64
|
- app/controllers/dbviewer/home_controller.rb
|
@@ -96,6 +100,7 @@ files:
|
|
96
100
|
- lib/dbviewer/table_query_params.rb
|
97
101
|
- lib/dbviewer/version.rb
|
98
102
|
- lib/generators/dbviewer/initializer_generator.rb
|
103
|
+
- lib/generators/dbviewer/structured_api_generator.rb
|
99
104
|
- lib/generators/dbviewer/templates/initializer.rb
|
100
105
|
- lib/tasks/dbviewer_tasks.rake
|
101
106
|
homepage: https://github.com/wailantirajoh/dbviewer
|