db-model-router 1.0.5 → 1.0.7
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.
- package/README.md +150 -11
- package/TODO.md +0 -14
- package/db-manager/.dbmanager.sqlite +0 -0
- package/db-manager/README.md +223 -0
- package/db-manager/adapter-proxy.js +361 -0
- package/db-manager/demo/cockroachdb.env +6 -0
- package/db-manager/demo/demo.sqlite +0 -0
- package/db-manager/demo/dynamodb.env +7 -0
- package/db-manager/demo/mongodb.env +4 -0
- package/db-manager/demo/mssql.env +6 -0
- package/db-manager/demo/mysql.env +6 -0
- package/db-manager/demo/oracle.env +6 -0
- package/db-manager/demo/postgres.env +6 -0
- package/db-manager/demo/redis.env +4 -0
- package/db-manager/demo/seeds/cockroachdb.sql +32 -0
- package/db-manager/demo/seeds/mssql.sql +32 -0
- package/db-manager/demo/seeds/mysql.sql +32 -0
- package/db-manager/demo/seeds/oracle.sql +43 -0
- package/db-manager/demo/seeds/postgres.sql +32 -0
- package/db-manager/demo/seeds/sqlite3.sql +32 -0
- package/db-manager/demo/sqlite3.env +2 -0
- package/db-manager/metadata-db.js +170 -0
- package/db-manager/public/.gitkeep +1 -0
- package/db-manager/public/css/style.css +1413 -0
- package/db-manager/public/js/app.js +1370 -0
- package/db-manager/routes/api.js +388 -0
- package/db-manager/routes/views.js +61 -0
- package/db-manager/server.js +39 -0
- package/db-manager/utils/build-filter-config.js +18 -0
- package/db-manager/utils/csv-export.js +59 -0
- package/db-manager/utils/export-filename.js +39 -0
- package/db-manager/utils/filter-tables.js +20 -0
- package/db-manager/utils/parse-filters.js +93 -0
- package/db-manager/utils/sort-state.js +35 -0
- package/db-manager/views/.gitkeep +1 -0
- package/db-manager/views/dashboard.ejs +53 -0
- package/db-manager/views/history.ejs +52 -0
- package/db-manager/views/index.ejs +35 -0
- package/db-manager/views/layout.ejs +31 -0
- package/db-manager/views/partials/data-panel.ejs +74 -0
- package/db-manager/views/partials/header.ejs +36 -0
- package/db-manager/views/partials/sidebar.ejs +30 -0
- package/db-manager/views/query.ejs +58 -0
- package/dbmr.schema.json +23 -45
- package/demo/.env.example +1 -0
- package/demo/app.js +3 -1
- package/demo/commons/db.js +11 -0
- package/demo/commons/migrate.js +3 -0
- package/demo/commons/modules.js +18 -0
- package/demo/commons/password.js +36 -0
- package/demo/commons/webhook.js +81 -0
- package/demo/dbmr.schema.json +22 -46
- package/demo/middleware/authenticate.js +14 -0
- package/demo/middleware/hasPermission.js +30 -0
- package/demo/middleware/tenantIsolation.js +17 -0
- package/demo/migrations/20260509170349_create_saas_tables.sql +69 -0
- package/demo/migrations/{20260430155809_create_tables.sql → 20260509170349_create_tables.sql} +11 -25
- package/demo/models/addresses.js +5 -3
- package/demo/models/cart_items.js +5 -3
- package/demo/models/carts.js +5 -3
- package/demo/models/categories.js +5 -3
- package/demo/models/coupons.js +5 -3
- package/demo/models/index.js +43 -0
- package/demo/models/order_items.js +4 -2
- package/demo/models/orders.js +5 -3
- package/demo/models/payments.js +5 -3
- package/demo/models/product_images.js +4 -2
- package/demo/models/product_reviews.js +5 -3
- package/demo/models/product_variants.js +5 -3
- package/demo/models/products.js +5 -3
- package/demo/models/role_permissions.js +17 -0
- package/demo/models/roles.js +17 -0
- package/demo/models/shipments.js +5 -3
- package/demo/models/tenants.js +18 -0
- package/demo/models/users.js +12 -8
- package/demo/models/webhook_logs.js +22 -0
- package/demo/models/webhooks.js +19 -0
- package/demo/models/wishlists.js +4 -2
- package/demo/openapi.json +1744 -616
- package/demo/package-lock.json +24 -24
- package/demo/package.json +9 -0
- package/demo/routes/{addresses.js → addresses/index.js} +1 -1
- package/demo/routes/auth/index.js +55 -0
- package/demo/routes/carts/{cart_items.js → cart_items/index.js} +1 -1
- package/demo/routes/{carts.js → carts/index.js} +1 -1
- package/demo/routes/{categories.js → categories/index.js} +1 -1
- package/demo/routes/{coupons.js → coupons/index.js} +1 -1
- package/demo/routes/index.js +39 -24
- package/demo/routes/{orders.js → orders/index.js} +1 -1
- package/demo/routes/orders/{order_items.js → order_items/index.js} +1 -1
- package/demo/routes/orders/{payments.js → payments/index.js} +1 -1
- package/demo/routes/orders/{shipments.js → shipments/index.js} +1 -1
- package/demo/routes/{products.js → products/index.js} +1 -1
- package/demo/routes/products/{product_images.js → product_images/index.js} +1 -1
- package/demo/routes/products/{product_reviews.js → product_reviews/index.js} +1 -1
- package/demo/routes/products/{product_variants.js → product_variants/index.js} +1 -1
- package/demo/routes/roles/index.js +75 -0
- package/demo/routes/roles/permissions/index.js +47 -0
- package/demo/routes/tenants/index.js +45 -0
- package/demo/routes/users/index.js +45 -0
- package/demo/routes/{wishlists.js → wishlists/index.js} +1 -1
- package/demo/seeds/saas-seed.js +329 -0
- package/docker-compose.yml +61 -0
- package/package.json +120 -113
- package/scripts/demo-create.js +1 -1
- package/skill/SKILL.md +119 -3
- package/src/cli/commands/db-manager.js +134 -0
- package/src/cli/commands/generate.js +112 -43
- package/src/cli/commands/help.js +0 -1
- package/src/cli/diff-engine.js +2 -1
- package/src/cli/generate-model.js +9 -4
- package/src/cli/generate-openapi.js +40 -13
- package/src/cli/generate-route.js +61 -22
- package/src/cli/generate-saas-structure.js +122 -0
- package/src/cli/init/generators.js +42 -30
- package/src/cli/init.js +8 -0
- package/src/cli/main.js +8 -1
- package/src/cli/saas/generate-saas-middleware.js +108 -0
- package/src/cli/saas/generate-saas-migrations.js +480 -0
- package/src/cli/saas/generate-saas-models.js +211 -0
- package/src/cli/saas/generate-saas-openapi.js +419 -0
- package/src/cli/saas/generate-saas-routes.js +435 -0
- package/src/cli/saas/generate-saas-seeds.js +243 -0
- package/src/cli/saas/generate-saas-utils.js +176 -0
- package/src/commons/kafka.js +139 -0
- package/src/commons/model.js +29 -9
- package/src/index.js +2 -0
- package/src/mssql/db.js +41 -3
- package/src/mysql/db.js +3 -0
- package/src/postgres/db.js +6 -0
- package/src/sqlite3/db.js +11 -0
- package/demo/docs/llm.md +0 -197
- package/demo/llms.txt +0 -70
- package/demo/routes/users.js +0 -6
- package/src/cli/commands/generate-llm-docs.js +0 -418
- /package/demo/migrations/{20260430155808_create_migrations_table.sql → 20260509170349_create_migrations_table.sql} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parses filter query parameters into adapter-compatible filter arrays.
|
|
5
|
+
*
|
|
6
|
+
* Supports two formats:
|
|
7
|
+
* 1. New format: filter[0][col]=name&filter[0][op]=like&filter[0][val]=ali
|
|
8
|
+
* → [['name', 'like', 'ali']]
|
|
9
|
+
* 2. Legacy format: filter[column_name]=value (defaults to 'like' operator)
|
|
10
|
+
* → [['column_name', 'like', 'value']]
|
|
11
|
+
*
|
|
12
|
+
* @param {object} queryParams - The query parameters object (e.g., req.query)
|
|
13
|
+
* @returns {Array<[string, string, string]>} Array of [column, operator, value] tuples
|
|
14
|
+
*/
|
|
15
|
+
function parseFilters(queryParams) {
|
|
16
|
+
var filters = [];
|
|
17
|
+
|
|
18
|
+
if (!queryParams || typeof queryParams !== "object") {
|
|
19
|
+
return filters;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var filterParam = queryParams.filter;
|
|
23
|
+
|
|
24
|
+
if (!filterParam || typeof filterParam !== "object") {
|
|
25
|
+
// Try flat key format: { 'filter[name]': 'ali' }
|
|
26
|
+
var keys = Object.keys(queryParams);
|
|
27
|
+
for (var i = 0; i < keys.length; i++) {
|
|
28
|
+
var key = keys[i];
|
|
29
|
+
var match = key.match(/^filter\[(.+)\]$/);
|
|
30
|
+
if (match) {
|
|
31
|
+
var column = match[1];
|
|
32
|
+
var value = queryParams[key];
|
|
33
|
+
if (typeof value === "string" && value.length > 0) {
|
|
34
|
+
filters.push([column, "like", value]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return filters;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if it's the new indexed format: filter[0][col], filter[0][op], filter[0][val]
|
|
42
|
+
// Express parses this as: { filter: { '0': { col: 'name', op: 'like', val: 'ali' }, ... } }
|
|
43
|
+
// or as: { filter: [ { col: 'name', op: 'like', val: 'ali' } ] }
|
|
44
|
+
if (Array.isArray(filterParam)) {
|
|
45
|
+
// Array format
|
|
46
|
+
for (var j = 0; j < filterParam.length; j++) {
|
|
47
|
+
var item = filterParam[j];
|
|
48
|
+
if (
|
|
49
|
+
item &&
|
|
50
|
+
item.col &&
|
|
51
|
+
item.op &&
|
|
52
|
+
item.val !== undefined &&
|
|
53
|
+
item.val !== ""
|
|
54
|
+
) {
|
|
55
|
+
filters.push([item.col, item.op, item.val]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return filters;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Object format — could be indexed { '0': { col, op, val } } or legacy { name: 'ali' }
|
|
62
|
+
var filterKeys = Object.keys(filterParam);
|
|
63
|
+
|
|
64
|
+
// Check if first key is numeric (indexed format)
|
|
65
|
+
if (filterKeys.length > 0 && /^\d+$/.test(filterKeys[0])) {
|
|
66
|
+
for (var k = 0; k < filterKeys.length; k++) {
|
|
67
|
+
var entry = filterParam[filterKeys[k]];
|
|
68
|
+
if (
|
|
69
|
+
entry &&
|
|
70
|
+
entry.col &&
|
|
71
|
+
entry.op &&
|
|
72
|
+
entry.val !== undefined &&
|
|
73
|
+
entry.val !== ""
|
|
74
|
+
) {
|
|
75
|
+
filters.push([entry.col, entry.op, entry.val]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return filters;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Legacy nested object format: { filter: { name: 'ali', email: 'gmail' } }
|
|
82
|
+
for (var m = 0; m < filterKeys.length; m++) {
|
|
83
|
+
var col = filterKeys[m];
|
|
84
|
+
var val = filterParam[col];
|
|
85
|
+
if (typeof val === "string" && val.length > 0) {
|
|
86
|
+
filters.push([col, "like", val]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return filters;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { parseFilters };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Computes the next sort state given the current state and the clicked column.
|
|
5
|
+
*
|
|
6
|
+
* Sort state cycles: null → 'asc' → 'desc' → null
|
|
7
|
+
* Clicking a different column resets to ascending on the new column.
|
|
8
|
+
*
|
|
9
|
+
* @param {{ column: string|null, dir: string|null }} currentState - Current sort state
|
|
10
|
+
* @param {string} clickedColumn - The column header that was clicked
|
|
11
|
+
* @returns {{ column: string|null, dir: string|null }} The new sort state
|
|
12
|
+
*/
|
|
13
|
+
function nextSortState(currentState, clickedColumn) {
|
|
14
|
+
var currentColumn = currentState && currentState.column;
|
|
15
|
+
var currentDir = currentState && currentState.dir;
|
|
16
|
+
|
|
17
|
+
// Clicking a different column → start ascending on new column
|
|
18
|
+
if (clickedColumn !== currentColumn) {
|
|
19
|
+
return { column: clickedColumn, dir: "asc" };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Same column clicked — cycle through states
|
|
23
|
+
if (currentDir === null || currentDir === undefined) {
|
|
24
|
+
return { column: clickedColumn, dir: "asc" };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (currentDir === "asc") {
|
|
28
|
+
return { column: clickedColumn, dir: "desc" };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// currentDir === 'desc' → clear sort
|
|
32
|
+
return { column: null, dir: null };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { nextSortState };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="system">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>DB Manager - Dashboard</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Fira+Sans:wght@300;400;500;600;700&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
<link
|
|
14
|
+
href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
|
15
|
+
rel="stylesheet"
|
|
16
|
+
/>
|
|
17
|
+
<link rel="stylesheet" href="/css/style.css" />
|
|
18
|
+
<script>
|
|
19
|
+
(function () {
|
|
20
|
+
var saved = localStorage.getItem("dbm-theme") || "system";
|
|
21
|
+
document.documentElement.setAttribute("data-theme", saved);
|
|
22
|
+
})();
|
|
23
|
+
</script>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<%- include('partials/header') %>
|
|
27
|
+
<main class="app-container">
|
|
28
|
+
<aside class="sidebar"><%- include('partials/sidebar') %></aside>
|
|
29
|
+
<section class="data-panel">
|
|
30
|
+
<div class="dashboard-content">
|
|
31
|
+
<h2 class="dashboard-title">Database Overview</h2>
|
|
32
|
+
<div class="dashboard-table-wrapper">
|
|
33
|
+
<table class="dashboard-table">
|
|
34
|
+
<thead>
|
|
35
|
+
<tr>
|
|
36
|
+
<th>Table Name</th>
|
|
37
|
+
<th>Columns</th>
|
|
38
|
+
<th>Indexes</th>
|
|
39
|
+
<th>Rows</th>
|
|
40
|
+
<th>Size (MB)</th>
|
|
41
|
+
</tr>
|
|
42
|
+
</thead>
|
|
43
|
+
<tbody class="dashboard-table-body">
|
|
44
|
+
<!-- Populated by client-side JS -->
|
|
45
|
+
</tbody>
|
|
46
|
+
</table>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</section>
|
|
50
|
+
</main>
|
|
51
|
+
<script src="/js/app.js"></script>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="system">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>DB Manager - History</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Fira+Sans:wght@300;400;500;600;700&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
<link
|
|
14
|
+
href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
|
15
|
+
rel="stylesheet"
|
|
16
|
+
/>
|
|
17
|
+
<link rel="stylesheet" href="/css/style.css" />
|
|
18
|
+
<script>
|
|
19
|
+
(function () {
|
|
20
|
+
var saved = localStorage.getItem("dbm-theme") || "system";
|
|
21
|
+
document.documentElement.setAttribute("data-theme", saved);
|
|
22
|
+
})();
|
|
23
|
+
</script>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<%- include('partials/header') %>
|
|
27
|
+
<main class="app-container">
|
|
28
|
+
<aside class="sidebar"><%- include('partials/sidebar') %></aside>
|
|
29
|
+
<section class="data-panel">
|
|
30
|
+
<div class="history-content">
|
|
31
|
+
<h2 class="history-title">Query History</h2>
|
|
32
|
+
<div class="history-table-wrapper">
|
|
33
|
+
<table class="history-table">
|
|
34
|
+
<thead>
|
|
35
|
+
<tr>
|
|
36
|
+
<th>#</th>
|
|
37
|
+
<th>Query</th>
|
|
38
|
+
<th>Rows</th>
|
|
39
|
+
<th>Executed At</th>
|
|
40
|
+
</tr>
|
|
41
|
+
</thead>
|
|
42
|
+
<tbody class="history-table-body">
|
|
43
|
+
<!-- Populated by client-side JS -->
|
|
44
|
+
</tbody>
|
|
45
|
+
</table>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</section>
|
|
49
|
+
</main>
|
|
50
|
+
<script src="/js/app.js"></script>
|
|
51
|
+
</body>
|
|
52
|
+
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="system">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>DB Manager</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Fira+Sans:wght@300;400;500;600;700&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
<link
|
|
14
|
+
href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
|
15
|
+
rel="stylesheet"
|
|
16
|
+
/>
|
|
17
|
+
<link rel="stylesheet" href="/css/style.css" />
|
|
18
|
+
<script>
|
|
19
|
+
(function () {
|
|
20
|
+
var saved = localStorage.getItem("dbm-theme") || "system";
|
|
21
|
+
document.documentElement.setAttribute("data-theme", saved);
|
|
22
|
+
})();
|
|
23
|
+
</script>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<%- include('partials/header') %>
|
|
27
|
+
<main class="app-container">
|
|
28
|
+
<aside class="sidebar"><%- include('partials/sidebar') %></aside>
|
|
29
|
+
<section class="data-panel">
|
|
30
|
+
<%- include('partials/data-panel') %>
|
|
31
|
+
</section>
|
|
32
|
+
</main>
|
|
33
|
+
<script src="/js/app.js"></script>
|
|
34
|
+
</body>
|
|
35
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="system">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>DB Manager</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Fira+Sans:wght@300;400;500;600;700&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
<link
|
|
14
|
+
href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
|
15
|
+
rel="stylesheet"
|
|
16
|
+
/>
|
|
17
|
+
<link rel="stylesheet" href="/css/style.css" />
|
|
18
|
+
<script>
|
|
19
|
+
// Apply saved theme immediately to prevent flash
|
|
20
|
+
(function () {
|
|
21
|
+
var saved = localStorage.getItem("dbm-theme") || "system";
|
|
22
|
+
document.documentElement.setAttribute("data-theme", saved);
|
|
23
|
+
})();
|
|
24
|
+
</script>
|
|
25
|
+
</head>
|
|
26
|
+
<body>
|
|
27
|
+
<%- include('partials/header') %>
|
|
28
|
+
<main class="app-container"><%- body %></main>
|
|
29
|
+
<script src="/js/app.js"></script>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<div class="data-panel-content">
|
|
2
|
+
<div class="data-toolbar">
|
|
3
|
+
<button class="btn btn-filter" type="button">
|
|
4
|
+
<span class="material-icons">filter_list</span> Filter
|
|
5
|
+
</button>
|
|
6
|
+
<button class="btn btn-add" type="button">
|
|
7
|
+
<span class="material-icons">add</span> Add
|
|
8
|
+
</button>
|
|
9
|
+
<button class="btn btn-delete" type="button" disabled>
|
|
10
|
+
<span class="material-icons">delete</span> Delete
|
|
11
|
+
</button>
|
|
12
|
+
<button class="btn btn-export" type="button" disabled>
|
|
13
|
+
<span class="material-icons">file_download</span> Export
|
|
14
|
+
</button>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="filter-tags"></div>
|
|
17
|
+
<div class="data-table-wrapper">
|
|
18
|
+
<table class="data-table">
|
|
19
|
+
<thead>
|
|
20
|
+
<tr class="column-headers"></tr>
|
|
21
|
+
</thead>
|
|
22
|
+
<tbody class="data-rows"></tbody>
|
|
23
|
+
</table>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="pagination-controls">
|
|
26
|
+
<button class="btn btn-prev" type="button" disabled>« Prev</button>
|
|
27
|
+
<span class="page-info"></span>
|
|
28
|
+
<button class="btn btn-next" type="button" disabled>Next »</button>
|
|
29
|
+
<select class="page-size" aria-label="Page size">
|
|
30
|
+
<option value="30">30</option>
|
|
31
|
+
<option value="50">50</option>
|
|
32
|
+
<option value="100">100</option>
|
|
33
|
+
<option value="0">No limit</option>
|
|
34
|
+
</select>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- Filter Popup Modal -->
|
|
39
|
+
<div class="filter-modal-overlay" style="display: none">
|
|
40
|
+
<div class="filter-modal">
|
|
41
|
+
<div class="filter-modal-header">
|
|
42
|
+
<h3>Add Filter</h3>
|
|
43
|
+
<button class="filter-modal-close" type="button">
|
|
44
|
+
<span class="material-icons">close</span>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="filter-modal-body">
|
|
48
|
+
<div class="filter-modal-row">
|
|
49
|
+
<select class="filter-col-select" aria-label="Column">
|
|
50
|
+
<option value="">Column...</option>
|
|
51
|
+
</select>
|
|
52
|
+
<select class="filter-op-select" aria-label="Operator">
|
|
53
|
+
<option value="=">=</option>
|
|
54
|
+
<option value="!=">!=</option>
|
|
55
|
+
<option value="like">like</option>
|
|
56
|
+
<option value="not like">not like</option>
|
|
57
|
+
<option value="<"><</option>
|
|
58
|
+
<option value=">">></option>
|
|
59
|
+
<option value="<="><=</option>
|
|
60
|
+
<option value=">=">>=</option>
|
|
61
|
+
</select>
|
|
62
|
+
<input
|
|
63
|
+
type="text"
|
|
64
|
+
class="filter-val-input"
|
|
65
|
+
placeholder="Value..."
|
|
66
|
+
aria-label="Filter value"
|
|
67
|
+
/>
|
|
68
|
+
<button class="btn-filter-add" type="button">
|
|
69
|
+
<span class="material-icons">add</span>
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<header class="app-header">
|
|
2
|
+
<h1>DB Manager</h1>
|
|
3
|
+
<div class="header-right">
|
|
4
|
+
<div class="connection-info">
|
|
5
|
+
<span class="db-type"><%= dbType %></span>
|
|
6
|
+
<span class="db-name"><%= dbName %></span>
|
|
7
|
+
<span class="db-host"><%= dbHost %></span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="theme-switcher" role="radiogroup" aria-label="Theme selection">
|
|
10
|
+
<button
|
|
11
|
+
class="theme-btn"
|
|
12
|
+
data-theme-value="light"
|
|
13
|
+
aria-label="Light mode"
|
|
14
|
+
title="Light mode"
|
|
15
|
+
>
|
|
16
|
+
<span class="material-icons">light_mode</span>
|
|
17
|
+
</button>
|
|
18
|
+
<button
|
|
19
|
+
class="theme-btn"
|
|
20
|
+
data-theme-value="dark"
|
|
21
|
+
aria-label="Dark mode"
|
|
22
|
+
title="Dark mode"
|
|
23
|
+
>
|
|
24
|
+
<span class="material-icons">dark_mode</span>
|
|
25
|
+
</button>
|
|
26
|
+
<button
|
|
27
|
+
class="theme-btn"
|
|
28
|
+
data-theme-value="system"
|
|
29
|
+
aria-label="System mode"
|
|
30
|
+
title="System mode"
|
|
31
|
+
>
|
|
32
|
+
<span class="material-icons">desktop_windows</span>
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</header>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<div class="nav-links">
|
|
2
|
+
<a href="/dashboard" class="nav-link" data-page="dashboard">
|
|
3
|
+
<span class="material-icons">space_dashboard</span>
|
|
4
|
+
Dashboard
|
|
5
|
+
</a>
|
|
6
|
+
<a href="/query" class="nav-link" data-page="query">
|
|
7
|
+
<span class="material-icons">terminal</span>
|
|
8
|
+
Query
|
|
9
|
+
</a>
|
|
10
|
+
<a href="/history" class="nav-link" data-page="history">
|
|
11
|
+
<span class="material-icons">schedule</span>
|
|
12
|
+
History
|
|
13
|
+
</a>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="sidebar-tables">
|
|
16
|
+
<button class="sidebar-tables-toggle" aria-expanded="true">
|
|
17
|
+
<span class="material-icons">storage</span>
|
|
18
|
+
Tables
|
|
19
|
+
<span class="material-icons toggle-arrow">expand_more</span>
|
|
20
|
+
</button>
|
|
21
|
+
<div class="sidebar-tables-content">
|
|
22
|
+
<input
|
|
23
|
+
type="text"
|
|
24
|
+
class="table-search"
|
|
25
|
+
placeholder="Search tables..."
|
|
26
|
+
aria-label="Search tables"
|
|
27
|
+
/>
|
|
28
|
+
<ul class="table-list"></ul>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="system">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>DB Manager - Query</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Fira+Sans:wght@300;400;500;600;700&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
<link
|
|
14
|
+
href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
|
15
|
+
rel="stylesheet"
|
|
16
|
+
/>
|
|
17
|
+
<link rel="stylesheet" href="/css/style.css" />
|
|
18
|
+
<script>
|
|
19
|
+
(function () {
|
|
20
|
+
var saved = localStorage.getItem("dbm-theme") || "system";
|
|
21
|
+
document.documentElement.setAttribute("data-theme", saved);
|
|
22
|
+
})();
|
|
23
|
+
</script>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<%- include('partials/header') %>
|
|
27
|
+
<main class="app-container">
|
|
28
|
+
<aside class="sidebar"><%- include('partials/sidebar') %></aside>
|
|
29
|
+
<section class="data-panel">
|
|
30
|
+
<div class="query-content">
|
|
31
|
+
<textarea
|
|
32
|
+
class="query-editor"
|
|
33
|
+
placeholder="Enter SQL query..."
|
|
34
|
+
aria-label="SQL query editor"
|
|
35
|
+
></textarea>
|
|
36
|
+
<div class="query-actions">
|
|
37
|
+
<button class="btn btn-run" type="button">
|
|
38
|
+
<span class="material-icons">play_arrow</span> Run
|
|
39
|
+
</button>
|
|
40
|
+
<button class="btn btn-export-query" type="button" disabled>
|
|
41
|
+
<span class="material-icons">file_download</span> Export
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="query-error" style="display: none"></div>
|
|
45
|
+
<div class="query-results-wrapper">
|
|
46
|
+
<table class="query-results-table">
|
|
47
|
+
<thead>
|
|
48
|
+
<tr class="query-column-headers"></tr>
|
|
49
|
+
</thead>
|
|
50
|
+
<tbody class="query-data-rows"></tbody>
|
|
51
|
+
</table>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</section>
|
|
55
|
+
</main>
|
|
56
|
+
<script src="/js/app.js"></script>
|
|
57
|
+
</body>
|
|
58
|
+
</html>
|