db-model-router 1.0.6 → 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.
Files changed (137) hide show
  1. package/README.md +150 -11
  2. package/TODO.md +0 -15
  3. package/db-manager/.dbmanager.sqlite +0 -0
  4. package/db-manager/README.md +223 -0
  5. package/db-manager/adapter-proxy.js +361 -0
  6. package/db-manager/demo/cockroachdb.env +6 -0
  7. package/db-manager/demo/demo.sqlite +0 -0
  8. package/db-manager/demo/dynamodb.env +7 -0
  9. package/db-manager/demo/mongodb.env +4 -0
  10. package/db-manager/demo/mssql.env +6 -0
  11. package/db-manager/demo/mysql.env +6 -0
  12. package/db-manager/demo/oracle.env +6 -0
  13. package/db-manager/demo/postgres.env +6 -0
  14. package/db-manager/demo/redis.env +4 -0
  15. package/db-manager/demo/seeds/cockroachdb.sql +32 -0
  16. package/db-manager/demo/seeds/mssql.sql +32 -0
  17. package/db-manager/demo/seeds/mysql.sql +32 -0
  18. package/db-manager/demo/seeds/oracle.sql +43 -0
  19. package/db-manager/demo/seeds/postgres.sql +32 -0
  20. package/db-manager/demo/seeds/sqlite3.sql +32 -0
  21. package/db-manager/demo/sqlite3.env +2 -0
  22. package/db-manager/metadata-db.js +170 -0
  23. package/db-manager/public/.gitkeep +1 -0
  24. package/db-manager/public/css/style.css +1413 -0
  25. package/db-manager/public/js/app.js +1370 -0
  26. package/db-manager/routes/api.js +388 -0
  27. package/db-manager/routes/views.js +61 -0
  28. package/db-manager/server.js +39 -0
  29. package/db-manager/utils/build-filter-config.js +18 -0
  30. package/db-manager/utils/csv-export.js +59 -0
  31. package/db-manager/utils/export-filename.js +39 -0
  32. package/db-manager/utils/filter-tables.js +20 -0
  33. package/db-manager/utils/parse-filters.js +93 -0
  34. package/db-manager/utils/sort-state.js +35 -0
  35. package/db-manager/views/.gitkeep +1 -0
  36. package/db-manager/views/dashboard.ejs +53 -0
  37. package/db-manager/views/history.ejs +52 -0
  38. package/db-manager/views/index.ejs +35 -0
  39. package/db-manager/views/layout.ejs +31 -0
  40. package/db-manager/views/partials/data-panel.ejs +74 -0
  41. package/db-manager/views/partials/header.ejs +36 -0
  42. package/db-manager/views/partials/sidebar.ejs +30 -0
  43. package/db-manager/views/query.ejs +58 -0
  44. package/dbmr.schema.json +22 -44
  45. package/demo/.dockerignore +7 -0
  46. package/demo/.env.example +14 -0
  47. package/demo/Dockerfile +20 -0
  48. package/demo/app.js +39 -0
  49. package/demo/commons/add_migration.js +43 -0
  50. package/demo/commons/db.js +28 -0
  51. package/demo/commons/migrate.js +68 -0
  52. package/demo/commons/modules.js +18 -0
  53. package/demo/commons/password.js +36 -0
  54. package/demo/commons/security.js +30 -0
  55. package/demo/commons/session.js +13 -0
  56. package/demo/commons/webhook.js +81 -0
  57. package/demo/dbmr.schema.json +338 -0
  58. package/demo/middleware/authenticate.js +14 -0
  59. package/demo/middleware/hasPermission.js +30 -0
  60. package/demo/middleware/logger.js +67 -0
  61. package/demo/middleware/tenantIsolation.js +17 -0
  62. package/demo/migrations/20260509170349_create_migrations_table.sql +6 -0
  63. package/demo/migrations/20260509170349_create_saas_tables.sql +69 -0
  64. package/demo/migrations/20260509170349_create_tables.sql +193 -0
  65. package/demo/models/addresses.js +24 -0
  66. package/demo/models/cart_items.js +20 -0
  67. package/demo/models/carts.js +18 -0
  68. package/demo/models/categories.js +22 -0
  69. package/demo/models/coupons.js +25 -0
  70. package/demo/models/index.js +43 -0
  71. package/demo/models/order_items.js +23 -0
  72. package/demo/models/orders.js +27 -0
  73. package/demo/models/payments.js +23 -0
  74. package/demo/models/product_images.js +20 -0
  75. package/demo/models/product_reviews.js +22 -0
  76. package/demo/models/product_variants.js +22 -0
  77. package/demo/models/products.js +32 -0
  78. package/demo/models/role_permissions.js +17 -0
  79. package/demo/models/roles.js +17 -0
  80. package/demo/models/shipments.js +21 -0
  81. package/demo/models/tenants.js +18 -0
  82. package/demo/models/users.js +23 -0
  83. package/demo/models/webhook_logs.js +22 -0
  84. package/demo/models/webhooks.js +19 -0
  85. package/demo/models/wishlists.js +17 -0
  86. package/demo/openapi.json +7000 -0
  87. package/demo/package-lock.json +2810 -0
  88. package/demo/package.json +43 -0
  89. package/demo/routes/addresses/index.js +6 -0
  90. package/demo/routes/auth/index.js +55 -0
  91. package/demo/routes/carts/cart_items/index.js +7 -0
  92. package/demo/routes/carts/index.js +6 -0
  93. package/demo/routes/categories/index.js +6 -0
  94. package/demo/routes/coupons/index.js +6 -0
  95. package/demo/routes/docs.js +18 -0
  96. package/demo/routes/health.js +35 -0
  97. package/demo/routes/index.js +54 -0
  98. package/demo/routes/orders/index.js +6 -0
  99. package/demo/routes/orders/order_items/index.js +7 -0
  100. package/demo/routes/orders/payments/index.js +7 -0
  101. package/demo/routes/orders/shipments/index.js +7 -0
  102. package/demo/routes/products/index.js +6 -0
  103. package/demo/routes/products/product_images/index.js +7 -0
  104. package/demo/routes/products/product_reviews/index.js +7 -0
  105. package/demo/routes/products/product_variants/index.js +7 -0
  106. package/demo/routes/roles/index.js +75 -0
  107. package/demo/routes/roles/permissions/index.js +47 -0
  108. package/demo/routes/tenants/index.js +45 -0
  109. package/demo/routes/users/index.js +45 -0
  110. package/demo/routes/wishlists/index.js +6 -0
  111. package/demo/seeds/saas-seed.js +329 -0
  112. package/docker-compose.yml +61 -0
  113. package/package.json +120 -113
  114. package/scripts/demo-create.js +1 -1
  115. package/skill/SKILL.md +119 -3
  116. package/src/cli/commands/db-manager.js +134 -0
  117. package/src/cli/commands/generate.js +106 -60
  118. package/src/cli/commands/help.js +0 -1
  119. package/src/cli/generate-route.js +60 -21
  120. package/src/cli/generate-saas-structure.js +122 -0
  121. package/src/cli/init/generators.js +6 -0
  122. package/src/cli/init.js +8 -0
  123. package/src/cli/main.js +8 -1
  124. package/src/cli/saas/generate-saas-middleware.js +108 -0
  125. package/src/cli/saas/generate-saas-migrations.js +480 -0
  126. package/src/cli/saas/generate-saas-models.js +211 -0
  127. package/src/cli/saas/generate-saas-openapi.js +419 -0
  128. package/src/cli/saas/generate-saas-routes.js +435 -0
  129. package/src/cli/saas/generate-saas-seeds.js +243 -0
  130. package/src/cli/saas/generate-saas-utils.js +176 -0
  131. package/src/commons/kafka.js +139 -0
  132. package/src/commons/model.js +29 -9
  133. package/src/index.js +2 -0
  134. package/src/mssql/db.js +41 -3
  135. package/src/mysql/db.js +3 -0
  136. package/src/postgres/db.js +6 -0
  137. package/src/cli/generate-db-manager.js +0 -1573
@@ -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>&laquo; Prev</button>
27
+ <span class="page-info"></span>
28
+ <button class="btn btn-next" type="button" disabled>Next &raquo;</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="<">&lt;</option>
58
+ <option value=">">&gt;</option>
59
+ <option value="<=">&lt;=</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>