@rebasepro/studio 0.1.2 → 0.2.1
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/LICENSE +17 -109
- package/dist/{ApiExplorer-DHVmWYfK.js → ApiExplorer-BmcdhAX0.js} +3 -4
- package/dist/ApiExplorer-BmcdhAX0.js.map +1 -0
- package/dist/{AuthSimulationSelector-CM488Eei.js → AuthSimulationSelector-DGoXkWSg.js} +2 -3
- package/dist/AuthSimulationSelector-DGoXkWSg.js.map +1 -0
- package/dist/{BranchesView-DcHZtvXo.js → BranchesView-BiTEwIhd.js} +2 -3
- package/dist/BranchesView-BiTEwIhd.js.map +1 -0
- package/dist/{CronJobsView-CijCToeK.js → CronJobsView-CNfz0etw.js} +2 -3
- package/dist/CronJobsView-CNfz0etw.js.map +1 -0
- package/dist/{JSEditor-CSHA0t_O.js → JSEditor-Ch8z8lJ4.js} +4 -5
- package/dist/JSEditor-Ch8z8lJ4.js.map +1 -0
- package/dist/{RLSEditor-BzDjqo6w.js → RLSEditor-CHEExeSB.js} +2 -3
- package/dist/RLSEditor-CHEExeSB.js.map +1 -0
- package/dist/{SQLEditor-Cr9Kg_Qg.js → SQLEditor-BELYJQRP.js} +75 -58
- package/dist/SQLEditor-BELYJQRP.js.map +1 -0
- package/dist/{StorageView-BYoslzBR.js → StorageView-B7AsN2qX.js} +2 -3
- package/dist/StorageView-B7AsN2qX.js.map +1 -0
- package/dist/common/src/util/entities.d.ts +2 -2
- package/dist/common/src/util/relations.d.ts +1 -1
- package/dist/core/src/components/LoginView/LoginView.d.ts +1 -6
- package/dist/core/src/contexts/SnackbarProvider.d.ts +1 -1
- package/dist/core/src/hooks/data/save.d.ts +2 -2
- package/dist/core/src/hooks/data/useEntityFetch.d.ts +5 -0
- package/dist/core/src/hooks/useResolvedComponent.d.ts +1 -1
- package/dist/index.es.js +8 -9
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +168 -150
- package/dist/index.umd.js.map +1 -1
- package/dist/types/src/controllers/auth.d.ts +9 -8
- package/dist/types/src/controllers/client.d.ts +3 -0
- package/dist/types/src/types/auth_adapter.d.ts +356 -0
- package/dist/types/src/types/collections.d.ts +67 -2
- package/dist/types/src/types/database_adapter.d.ts +94 -0
- package/dist/types/src/types/entity_actions.d.ts +7 -1
- package/dist/types/src/types/entity_callbacks.d.ts +1 -1
- package/dist/types/src/types/entity_views.d.ts +36 -1
- package/dist/types/src/types/index.d.ts +2 -0
- package/dist/types/src/types/plugins.d.ts +1 -1
- package/dist/types/src/types/properties.d.ts +24 -5
- package/dist/types/src/types/property_config.d.ts +6 -2
- package/dist/types/src/types/relations.d.ts +1 -1
- package/dist/types/src/types/translations.d.ts +8 -0
- package/dist/types/src/users/user.d.ts +5 -0
- package/dist/ui/src/components/FilterChip.d.ts +42 -0
- package/dist/ui/src/components/index.d.ts +5 -0
- package/dist/ui/src/icons/index.d.ts +2 -0
- package/package.json +21 -18
- package/src/components/ApiExplorer/ApiExplorer.tsx +1 -1
- package/src/components/ApiExplorer/EndpointDetail.tsx +10 -2
- package/src/components/ApiExplorer/TryItPanel.tsx +17 -6
- package/src/components/AuthSimulationSelector.tsx +1 -2
- package/src/components/Branches/BranchesView.tsx +24 -2
- package/src/components/CronJobs/CronJobsView.tsx +19 -2
- package/src/components/JSEditor/JSEditor.tsx +37 -6
- package/src/components/JSEditor/JSEditorSidebar.tsx +12 -2
- package/src/components/JSEditor/JSMonacoEditor.tsx +16 -3
- package/src/components/RLSEditor/PolicyEditor.tsx +19 -2
- package/src/components/RLSEditor/RLSEditor.tsx +22 -2
- package/src/components/SQLEditor/SQLEditor.tsx +124 -68
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +1 -2
- package/src/components/SQLEditor/SchemaBrowser.tsx +14 -2
- package/src/components/StorageView/StorageView.tsx +39 -2
- package/src/components/StudioHomePage.tsx +1 -2
- package/src/utils/sql_utils.ts +1 -1
- package/dist/ApiExplorer-DHVmWYfK.js.map +0 -1
- package/dist/AuthSimulationSelector-CM488Eei.js.map +0 -1
- package/dist/BranchesView-DcHZtvXo.js.map +0 -1
- package/dist/CronJobsView-CijCToeK.js.map +0 -1
- package/dist/JSEditor-CSHA0t_O.js.map +0 -1
- package/dist/RLSEditor-BzDjqo6w.js.map +0 -1
- package/dist/SQLEditor-Cr9Kg_Qg.js.map +0 -1
- package/dist/StorageView-BYoslzBR.js.map +0 -1
|
@@ -3,16 +3,45 @@ import { IconForView } from "@rebasepro/core";
|
|
|
3
3
|
import { useStudioCollectionRegistry, useStudioSideEntityController } from "@rebasepro/core";
|
|
4
4
|
import React, { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
import {
|
|
7
|
+
Alert,
|
|
8
|
+
Button,
|
|
9
|
+
Checkbox,
|
|
10
|
+
Chip,
|
|
11
|
+
CircularProgress,
|
|
12
|
+
cls,
|
|
13
|
+
DatabaseIcon,
|
|
14
|
+
defaultBorderMixin,
|
|
15
|
+
Dialog,
|
|
16
|
+
DialogActions,
|
|
17
|
+
DialogContent,
|
|
18
|
+
DialogTitle,
|
|
19
|
+
IconButton,
|
|
20
|
+
iconSize,
|
|
21
|
+
InputLabel,
|
|
22
|
+
Menu,
|
|
23
|
+
MenuIcon,
|
|
24
|
+
MenuItem,
|
|
25
|
+
MoreVerticalIcon,
|
|
26
|
+
Paper,
|
|
27
|
+
PencilIcon,
|
|
28
|
+
PlayIcon,
|
|
29
|
+
PlusIcon,
|
|
30
|
+
ResizablePanels,
|
|
31
|
+
Select,
|
|
32
|
+
SelectItem,
|
|
33
|
+
Tab,
|
|
34
|
+
Tabs,
|
|
35
|
+
TerminalIcon,
|
|
36
|
+
TextareaAutosize,
|
|
37
|
+
TextField,
|
|
38
|
+
Tooltip,
|
|
39
|
+
Typography,
|
|
40
|
+
VirtualTable,
|
|
41
|
+
VirtualTableColumn,
|
|
42
|
+
XIcon
|
|
43
|
+
} from "@rebasepro/ui";
|
|
44
|
+
|
|
16
45
|
import { useRebaseContext, useSnackbarController, ConfirmationDialog, ErrorView, useTranslation } from "@rebasepro/core";
|
|
17
46
|
import { MonacoEditor } from "./MonacoEditor";
|
|
18
47
|
import { SQLEditorSidebar, Snippet } from "./SQLEditorSidebar";
|
|
@@ -152,14 +181,21 @@ const FixedEditorOverlay = ({
|
|
|
152
181
|
);
|
|
153
182
|
};
|
|
154
183
|
|
|
184
|
+
const getStoragePrefix = (baseUrl?: string) => {
|
|
185
|
+
if (!baseUrl) return "default";
|
|
186
|
+
return baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
187
|
+
};
|
|
188
|
+
|
|
155
189
|
export const SQLEditor = () => {
|
|
156
|
-
const { databaseAdmin } = useRebaseContext();
|
|
190
|
+
const { databaseAdmin, client } = useRebaseContext();
|
|
157
191
|
const sideEntityController = useStudioSideEntityController();
|
|
158
192
|
const snackbarController = useSnackbarController();
|
|
159
193
|
const collectionRegistry = useStudioCollectionRegistry();
|
|
160
194
|
|
|
161
195
|
const { t } = useTranslation();
|
|
162
196
|
|
|
197
|
+
const projectPrefix = useMemo(() => getStoragePrefix(client?.baseUrl), [client?.baseUrl]);
|
|
198
|
+
|
|
163
199
|
// Schema state
|
|
164
200
|
const [schemas, setSchemas] = useState<Record<string, TableInfo[]>>({});
|
|
165
201
|
const [isSchemaLoading, setIsSchemaLoading] = useState(true);
|
|
@@ -168,10 +204,12 @@ export const SQLEditor = () => {
|
|
|
168
204
|
|
|
169
205
|
// Connection state
|
|
170
206
|
const [selectedDatabase, setSelectedDatabase] = useState<string | undefined>(() => {
|
|
171
|
-
|
|
207
|
+
const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_") : "default";
|
|
208
|
+
return localStorage.getItem(`rebase_sql_selected_db_${projectPrefixSync}`) || undefined;
|
|
172
209
|
});
|
|
173
210
|
const [selectedRole, setSelectedRole] = useState<string | undefined>(() => {
|
|
174
|
-
|
|
211
|
+
const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_") : "default";
|
|
212
|
+
return localStorage.getItem(`rebase_sql_selected_role_${projectPrefixSync}`) || undefined;
|
|
175
213
|
});
|
|
176
214
|
|
|
177
215
|
const [availableDatabases, setAvailableDatabases] = useState<string[]>([]);
|
|
@@ -192,7 +230,8 @@ export const SQLEditor = () => {
|
|
|
192
230
|
execTime: number | null,
|
|
193
231
|
lastExecutedSql: string | null
|
|
194
232
|
}>>(() => {
|
|
195
|
-
const
|
|
233
|
+
const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_") : "default";
|
|
234
|
+
const saved = localStorage.getItem(`rebase_sql_tabs_${projectPrefixSync}`);
|
|
196
235
|
if (saved) {
|
|
197
236
|
const parsed = JSON.parse(saved);
|
|
198
237
|
return parsed.map((t: Record<string, unknown>) => ({
|
|
@@ -208,8 +247,8 @@ export const SQLEditor = () => {
|
|
|
208
247
|
id: "1",
|
|
209
248
|
name: "Query 1",
|
|
210
249
|
sql: "SELECT * FROM ",
|
|
211
|
-
database: localStorage.getItem(
|
|
212
|
-
role: localStorage.getItem(
|
|
250
|
+
database: localStorage.getItem(`rebase_sql_selected_db_${projectPrefixSync}`) || undefined,
|
|
251
|
+
role: localStorage.getItem(`rebase_sql_selected_role_${projectPrefixSync}`) || undefined,
|
|
213
252
|
results: null,
|
|
214
253
|
loading: false,
|
|
215
254
|
error: null,
|
|
@@ -218,7 +257,8 @@ export const SQLEditor = () => {
|
|
|
218
257
|
}];
|
|
219
258
|
});
|
|
220
259
|
const [activeTabId, setActiveTabId] = useState<string>(() => {
|
|
221
|
-
|
|
260
|
+
const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_") : "default";
|
|
261
|
+
return localStorage.getItem(`rebase_sql_active_tab_${projectPrefixSync}`) || "1";
|
|
222
262
|
});
|
|
223
263
|
|
|
224
264
|
const activeTab = tabs.find(t => t.id === activeTabId) || tabs[0];
|
|
@@ -243,30 +283,31 @@ export const SQLEditor = () => {
|
|
|
243
283
|
useEffect(() => {
|
|
244
284
|
let mounted = true;
|
|
245
285
|
const fetchConnectionConfig = async () => {
|
|
246
|
-
if (!databaseAdmin?.fetchAvailableDatabases || !databaseAdmin?.fetchAvailableRoles) {
|
|
286
|
+
if (!databaseAdmin?.fetchAvailableDatabases || !databaseAdmin?.fetchAvailableRoles || !databaseAdmin?.executeSql) {
|
|
247
287
|
setConnectionConfigError(t("studio_sql_sql_not_supported"));
|
|
248
288
|
setIsLoadingConfig(false);
|
|
249
289
|
return;
|
|
250
290
|
}
|
|
251
291
|
|
|
252
292
|
try {
|
|
253
|
-
const [dbs, roles, currentDbFromApi] = await Promise.all([
|
|
293
|
+
const [dbs, roles, currentDbFromApi, currentUserResult] = await Promise.all([
|
|
254
294
|
databaseAdmin.fetchAvailableDatabases(),
|
|
255
295
|
databaseAdmin.fetchAvailableRoles(),
|
|
256
|
-
typeof databaseAdmin?.fetchCurrentDatabase === "function" ? databaseAdmin.fetchCurrentDatabase() : Promise.resolve(undefined)
|
|
296
|
+
typeof databaseAdmin?.fetchCurrentDatabase === "function" ? databaseAdmin.fetchCurrentDatabase() : Promise.resolve(undefined),
|
|
297
|
+
databaseAdmin.executeSql("SELECT current_user AS role").catch(() => [])
|
|
257
298
|
]);
|
|
258
299
|
|
|
259
300
|
if (mounted) {
|
|
260
301
|
setAvailableDatabases(dbs);
|
|
261
302
|
setAvailableRoles(roles);
|
|
262
303
|
|
|
263
|
-
const loadedDb = localStorage.getItem(
|
|
264
|
-
const loadedRole = localStorage.getItem(
|
|
304
|
+
const loadedDb = localStorage.getItem(`rebase_sql_selected_db_${projectPrefix}`) || undefined;
|
|
305
|
+
const loadedRole = localStorage.getItem(`rebase_sql_selected_role_${projectPrefix}`) || undefined;
|
|
265
306
|
|
|
266
|
-
const initialActiveTabId = localStorage.getItem(
|
|
267
|
-
let initialTabs:
|
|
307
|
+
const initialActiveTabId = localStorage.getItem(`rebase_sql_active_tab_${projectPrefix}`) || "1";
|
|
308
|
+
let initialTabs: Array<{ id?: string; database?: string; role?: string }> = [];
|
|
268
309
|
try {
|
|
269
|
-
const savedTabs = localStorage.getItem(
|
|
310
|
+
const savedTabs = localStorage.getItem(`rebase_sql_tabs_${projectPrefix}`);
|
|
270
311
|
if (savedTabs) initialTabs = JSON.parse(savedTabs);
|
|
271
312
|
} catch (e) { /* ignore */ }
|
|
272
313
|
const currentActiveTab = initialTabs.find(t => t.id === initialActiveTabId);
|
|
@@ -279,22 +320,26 @@ export const SQLEditor = () => {
|
|
|
279
320
|
|
|
280
321
|
if (actualDb) {
|
|
281
322
|
setSelectedDatabase(actualDb);
|
|
282
|
-
localStorage.setItem(
|
|
283
|
-
setTabs(prev => prev.map(t => t.id === initialActiveTabId && !t.database ? { ...t,
|
|
284
|
-
database: actualDb } : t));
|
|
323
|
+
localStorage.setItem(`rebase_sql_selected_db_${projectPrefix}`, actualDb);
|
|
324
|
+
setTabs(prev => prev.map(t => t.id === initialActiveTabId && (!t.database || !dbs.includes(t.database)) ? { ...t, database: actualDb } : t));
|
|
285
325
|
}
|
|
286
326
|
|
|
327
|
+
const currentUser = (currentUserResult?.[0] as Record<string, unknown> | undefined)?.role as string | undefined;
|
|
287
328
|
let actualRole = currentActiveTab?.role || loadedRole;
|
|
329
|
+
|
|
288
330
|
if (actualRole && !roles.includes(actualRole)) actualRole = undefined;
|
|
289
331
|
if (!actualRole && roles.length > 0) {
|
|
290
|
-
|
|
332
|
+
if (currentUser && roles.includes(currentUser)) {
|
|
333
|
+
actualRole = currentUser;
|
|
334
|
+
} else {
|
|
335
|
+
actualRole = roles.includes("postgres") ? "postgres" : roles[0];
|
|
336
|
+
}
|
|
291
337
|
}
|
|
292
338
|
|
|
293
339
|
if (actualRole) {
|
|
294
340
|
setSelectedRole(actualRole);
|
|
295
|
-
localStorage.setItem(
|
|
296
|
-
setTabs(prev => prev.map(t => t.id === initialActiveTabId && !t.role ? { ...t,
|
|
297
|
-
role: actualRole } : t));
|
|
341
|
+
localStorage.setItem(`rebase_sql_selected_role_${projectPrefix}`, actualRole);
|
|
342
|
+
setTabs(prev => prev.map(t => t.id === initialActiveTabId && (!t.role || !roles.includes(t.role)) ? { ...t, role: actualRole } : t));
|
|
298
343
|
}
|
|
299
344
|
}
|
|
300
345
|
} catch (err: unknown) {
|
|
@@ -313,22 +358,20 @@ role: actualRole } : t));
|
|
|
313
358
|
fetchConnectionConfig();
|
|
314
359
|
|
|
315
360
|
return () => { mounted = false; };
|
|
316
|
-
}, [databaseAdmin]);
|
|
361
|
+
}, [databaseAdmin, projectPrefix]);
|
|
317
362
|
|
|
318
363
|
const handleDatabaseChange = (db: string, tabId?: string) => {
|
|
319
364
|
setSelectedDatabase(db);
|
|
320
|
-
localStorage.setItem(
|
|
321
|
-
setTabs(prev => prev.map(t => t.id === (tabId || activeTabId) ? { ...t,
|
|
322
|
-
database: db } : t));
|
|
365
|
+
localStorage.setItem(`rebase_sql_selected_db_${projectPrefix}`, db);
|
|
366
|
+
setTabs(prev => prev.map(t => t.id === (tabId || activeTabId) ? { ...t, database: db } : t));
|
|
323
367
|
// Reset so the schema will be re-fetched for the new database
|
|
324
368
|
schemaFetchedRef.current = false;
|
|
325
369
|
};
|
|
326
370
|
|
|
327
371
|
const handleRoleChange = (role: string, tabId?: string) => {
|
|
328
372
|
setSelectedRole(role);
|
|
329
|
-
localStorage.setItem(
|
|
330
|
-
setTabs(prev => prev.map(t => t.id === (tabId || activeTabId) ? { ...t,
|
|
331
|
-
role } : t));
|
|
373
|
+
localStorage.setItem(`rebase_sql_selected_role_${projectPrefix}`, role);
|
|
374
|
+
setTabs(prev => prev.map(t => t.id === (tabId || activeTabId) ? { ...t, role } : t));
|
|
332
375
|
};
|
|
333
376
|
|
|
334
377
|
const handleTabChange = useCallback((newTabId: string) => {
|
|
@@ -337,22 +380,20 @@ role } : t));
|
|
|
337
380
|
if (newTab) {
|
|
338
381
|
if (newTab.database && newTab.database !== selectedDatabase) {
|
|
339
382
|
setSelectedDatabase(newTab.database);
|
|
340
|
-
localStorage.setItem(
|
|
383
|
+
localStorage.setItem(`rebase_sql_selected_db_${projectPrefix}`, newTab.database);
|
|
341
384
|
schemaFetchedRef.current = false;
|
|
342
385
|
} else if (!newTab.database && selectedDatabase) {
|
|
343
|
-
setTabs(prev => prev.map(t => t.id === newTabId ? { ...t,
|
|
344
|
-
database: selectedDatabase } : t));
|
|
386
|
+
setTabs(prev => prev.map(t => t.id === newTabId ? { ...t, database: selectedDatabase } : t));
|
|
345
387
|
}
|
|
346
388
|
|
|
347
389
|
if (newTab.role && newTab.role !== selectedRole) {
|
|
348
390
|
setSelectedRole(newTab.role);
|
|
349
|
-
localStorage.setItem(
|
|
391
|
+
localStorage.setItem(`rebase_sql_selected_role_${projectPrefix}`, newTab.role);
|
|
350
392
|
} else if (!newTab.role && selectedRole) {
|
|
351
|
-
setTabs(prev => prev.map(t => t.id === newTabId ? { ...t,
|
|
352
|
-
role: selectedRole } : t));
|
|
393
|
+
setTabs(prev => prev.map(t => t.id === newTabId ? { ...t, role: selectedRole } : t));
|
|
353
394
|
}
|
|
354
395
|
}
|
|
355
|
-
}, [tabs, selectedDatabase, selectedRole]);
|
|
396
|
+
}, [tabs, selectedDatabase, selectedRole, projectPrefix]);
|
|
356
397
|
|
|
357
398
|
const fetchSchema = useCallback(async () => {
|
|
358
399
|
if (!databaseAdmin?.executeSql) {
|
|
@@ -566,7 +607,8 @@ role: selectedRole });
|
|
|
566
607
|
}, [editingCell, schemas, activeTab.lastExecutedSql, activeTab.results, databaseAdmin, updateActiveTab, snackbarController, selectedDatabase, selectedRole]);
|
|
567
608
|
|
|
568
609
|
const [columnWidths, setColumnWidths] = useState<Record<string, Record<string, number>>>(() => {
|
|
569
|
-
const
|
|
610
|
+
const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_") : "default";
|
|
611
|
+
const saved = localStorage.getItem(`rebase_sql_column_widths_${projectPrefixSync}`);
|
|
570
612
|
return saved ? JSON.parse(saved) : {};
|
|
571
613
|
});
|
|
572
614
|
const [snippets, setSnippets] = useState<Snippet[]>([]);
|
|
@@ -576,12 +618,20 @@ role: selectedRole });
|
|
|
576
618
|
|
|
577
619
|
// Load from local storage
|
|
578
620
|
useEffect(() => {
|
|
579
|
-
const savedSnippets = localStorage.getItem(
|
|
580
|
-
if (savedSnippets)
|
|
621
|
+
const savedSnippets = localStorage.getItem(`rebase_sql_snippets_${projectPrefix}`);
|
|
622
|
+
if (savedSnippets) {
|
|
623
|
+
setSnippets(JSON.parse(savedSnippets));
|
|
624
|
+
} else {
|
|
625
|
+
setSnippets([]);
|
|
626
|
+
}
|
|
581
627
|
|
|
582
|
-
const savedHistory = localStorage.getItem(
|
|
583
|
-
if (savedHistory)
|
|
584
|
-
|
|
628
|
+
const savedHistory = localStorage.getItem(`rebase_sql_history_${projectPrefix}`);
|
|
629
|
+
if (savedHistory) {
|
|
630
|
+
setHistory(JSON.parse(savedHistory));
|
|
631
|
+
} else {
|
|
632
|
+
setHistory([]);
|
|
633
|
+
}
|
|
634
|
+
}, [projectPrefix]);
|
|
585
635
|
|
|
586
636
|
// Save tabs and active tab to local storage
|
|
587
637
|
useEffect(() => {
|
|
@@ -592,21 +642,21 @@ role: selectedRole });
|
|
|
592
642
|
database: t.database,
|
|
593
643
|
role: t.role
|
|
594
644
|
}));
|
|
595
|
-
localStorage.setItem(
|
|
596
|
-
}, [tabs]);
|
|
645
|
+
localStorage.setItem(`rebase_sql_tabs_${projectPrefix}`, JSON.stringify(sanitizedTabs));
|
|
646
|
+
}, [tabs, projectPrefix]);
|
|
597
647
|
|
|
598
648
|
useEffect(() => {
|
|
599
|
-
localStorage.setItem(
|
|
600
|
-
}, [activeTabId]);
|
|
649
|
+
localStorage.setItem(`rebase_sql_active_tab_${projectPrefix}`, activeTabId);
|
|
650
|
+
}, [activeTabId, projectPrefix]);
|
|
601
651
|
|
|
602
652
|
const saveSnippets = (newSnippets: Snippet[]) => {
|
|
603
653
|
setSnippets(newSnippets);
|
|
604
|
-
localStorage.setItem(
|
|
654
|
+
localStorage.setItem(`rebase_sql_snippets_${projectPrefix}`, JSON.stringify(newSnippets));
|
|
605
655
|
};
|
|
606
656
|
|
|
607
657
|
const saveHistory = (newHistory: string[]) => {
|
|
608
658
|
setHistory(newHistory);
|
|
609
|
-
localStorage.setItem(
|
|
659
|
+
localStorage.setItem(`rebase_sql_history_${projectPrefix}`, JSON.stringify(newHistory.slice(-50)));
|
|
610
660
|
};
|
|
611
661
|
|
|
612
662
|
const handleDeleteSnippet = (id: string) => {
|
|
@@ -667,10 +717,10 @@ role: selectedRole });
|
|
|
667
717
|
[key]: width
|
|
668
718
|
}
|
|
669
719
|
};
|
|
670
|
-
localStorage.setItem(
|
|
720
|
+
localStorage.setItem(`rebase_sql_column_widths_${projectPrefix}`, JSON.stringify(newWidths));
|
|
671
721
|
return newWidths;
|
|
672
722
|
});
|
|
673
|
-
}, [activeTab.sql]);
|
|
723
|
+
}, [activeTab.sql, projectPrefix]);
|
|
674
724
|
|
|
675
725
|
const handlePrettify = () => {
|
|
676
726
|
// Simple formatting for now
|
|
@@ -743,7 +793,7 @@ role: selectedRole });
|
|
|
743
793
|
} finally {
|
|
744
794
|
updateActiveTab({ loading: false });
|
|
745
795
|
}
|
|
746
|
-
}, [activeTab.sql, autoLimit, databaseAdmin, history, updateActiveTab]);
|
|
796
|
+
}, [activeTab.sql, autoLimit, databaseAdmin, history, updateActiveTab, selectedDatabase, selectedRole]);
|
|
747
797
|
|
|
748
798
|
const handleRun = useCallback(async (selectedText?: string) => {
|
|
749
799
|
const sqlTarget = selectedText || activeTab.sql;
|
|
@@ -1142,7 +1192,8 @@ id: String(ra.entityId) })}
|
|
|
1142
1192
|
|
|
1143
1193
|
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
1144
1194
|
try {
|
|
1145
|
-
const
|
|
1195
|
+
const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_") : "default";
|
|
1196
|
+
const saved = localStorage.getItem(`rebase_sql_editor_sidebar_size_${projectPrefixSync}`);
|
|
1146
1197
|
return saved !== null ? parseFloat(saved) : 20;
|
|
1147
1198
|
} catch (e) {
|
|
1148
1199
|
return 20;
|
|
@@ -1150,7 +1201,8 @@ id: String(ra.entityId) })}
|
|
|
1150
1201
|
});
|
|
1151
1202
|
const [editorHeight, setEditorHeight] = useState(() => {
|
|
1152
1203
|
try {
|
|
1153
|
-
const
|
|
1204
|
+
const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_") : "default";
|
|
1205
|
+
const saved = localStorage.getItem(`rebase_sql_editor_height_${projectPrefixSync}`);
|
|
1154
1206
|
return saved !== null ? parseFloat(saved) : 50;
|
|
1155
1207
|
} catch (e) {
|
|
1156
1208
|
return 50;
|
|
@@ -1159,15 +1211,15 @@ id: String(ra.entityId) })}
|
|
|
1159
1211
|
|
|
1160
1212
|
useEffect(() => {
|
|
1161
1213
|
try {
|
|
1162
|
-
localStorage.setItem(
|
|
1214
|
+
localStorage.setItem(`rebase_sql_editor_sidebar_size_${projectPrefix}`, sidebarSize.toString());
|
|
1163
1215
|
} catch (e) { /* ignore */ }
|
|
1164
|
-
}, [sidebarSize]);
|
|
1216
|
+
}, [sidebarSize, projectPrefix]);
|
|
1165
1217
|
|
|
1166
1218
|
useEffect(() => {
|
|
1167
1219
|
try {
|
|
1168
|
-
localStorage.setItem(
|
|
1220
|
+
localStorage.setItem(`rebase_sql_editor_height_${projectPrefix}`, editorHeight.toString());
|
|
1169
1221
|
} catch (e) { /* ignore */ }
|
|
1170
|
-
}, [editorHeight]);
|
|
1222
|
+
}, [editorHeight, projectPrefix]);
|
|
1171
1223
|
|
|
1172
1224
|
const activeSnippet = snippets.find(s => s.sql === activeTab.sql);
|
|
1173
1225
|
const isFavorite = activeSnippet?.isFavorite || false;
|
|
@@ -1296,7 +1348,11 @@ isFavorite: !s.isFavorite } : s));
|
|
|
1296
1348
|
className="text-text-secondary dark:text-text-secondary-dark font-medium mr-2"
|
|
1297
1349
|
>
|
|
1298
1350
|
<DatabaseIcon size={iconSize.small} className="mr-1.5 text-text-disabled dark:text-text-disabled-dark"/>
|
|
1299
|
-
<span className="max-w-[
|
|
1351
|
+
<span className="max-w-[160px] truncate">
|
|
1352
|
+
{isLoadingConfig
|
|
1353
|
+
? "..."
|
|
1354
|
+
: `${selectedDatabase || t("studio_sql_select_db")}${selectedRole ? ` (${selectedRole})` : ""}`}
|
|
1355
|
+
</span>
|
|
1300
1356
|
</Button>
|
|
1301
1357
|
}
|
|
1302
1358
|
>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import { Trash2Icon } from "lucide-react";
|
|
3
|
+
import { cls, defaultBorderMixin, IconButton, iconSize, Tab, Tabs, Trash2Icon, Typography } from "@rebasepro/ui";
|
|
5
4
|
import { useTranslation } from "@rebasepro/core";
|
|
6
5
|
import { SchemaBrowser } from "./SchemaBrowser";
|
|
7
6
|
import { TableInfo } from "./SQLEditor";
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
CircularProgress,
|
|
6
|
+
cls,
|
|
7
|
+
CopyIcon,
|
|
8
|
+
defaultBorderMixin,
|
|
9
|
+
IconButton,
|
|
10
|
+
iconSize,
|
|
11
|
+
Menu,
|
|
12
|
+
MenuItem,
|
|
13
|
+
MoreVerticalIcon,
|
|
14
|
+
RefreshCwIcon,
|
|
15
|
+
Typography
|
|
16
|
+
} from "@rebasepro/ui";
|
|
5
17
|
import { TableInfo } from "./SQLEditor";
|
|
6
18
|
import { ErrorView, useTranslation } from "@rebasepro/core";
|
|
7
19
|
|
|
@@ -1,7 +1,44 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
ArrowLeftIcon,
|
|
5
|
+
Button,
|
|
6
|
+
Checkbox,
|
|
7
|
+
CheckIcon,
|
|
8
|
+
Chip,
|
|
9
|
+
CircularProgress,
|
|
10
|
+
cls,
|
|
11
|
+
CopyIcon,
|
|
12
|
+
defaultBorderMixin,
|
|
13
|
+
Dialog,
|
|
14
|
+
DialogActions,
|
|
15
|
+
DialogContent,
|
|
16
|
+
DialogTitle,
|
|
17
|
+
DownloadIcon,
|
|
18
|
+
FileIcon,
|
|
19
|
+
FileTextIcon,
|
|
20
|
+
FileUpload,
|
|
21
|
+
FolderIcon,
|
|
22
|
+
FolderPlusIcon,
|
|
23
|
+
HomeIcon,
|
|
24
|
+
IconButton,
|
|
25
|
+
iconSize,
|
|
26
|
+
ImageIcon,
|
|
27
|
+
LayoutGridIcon,
|
|
28
|
+
ListIcon,
|
|
29
|
+
LoadingButton,
|
|
30
|
+
Music2Icon,
|
|
31
|
+
PlusIcon,
|
|
32
|
+
RefreshCwIcon,
|
|
33
|
+
ResizablePanels,
|
|
34
|
+
TextField,
|
|
35
|
+
Tooltip,
|
|
36
|
+
Trash2Icon,
|
|
37
|
+
Typography,
|
|
38
|
+
UploadCloudIcon,
|
|
39
|
+
VideoIcon,
|
|
40
|
+
XIcon
|
|
41
|
+
} from "@rebasepro/ui";
|
|
5
42
|
import { useStorageSource, useSnackbarController, ErrorView, useApiConfig } from "@rebasepro/core";
|
|
6
43
|
import type { StorageListResult } from "@rebasepro/types";
|
|
7
44
|
import { useSearchParams } from "react-router-dom";
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { HomePageSection, PluginGenericProps } from "@rebasepro/types";
|
|
2
2
|
import React, { useEffect } from "react";
|
|
3
3
|
import { Card, cls, Container, Typography } from "@rebasepro/ui";
|
|
4
|
-
import { ArrowRightIcon } from "
|
|
5
|
-
import { iconSize } from "@rebasepro/ui";
|
|
4
|
+
import { ArrowRightIcon, iconSize } from "@rebasepro/ui";
|
|
6
5
|
import { IconForView, useRebaseContext, useRestoreScroll, useSlot } from "@rebasepro/core";
|
|
7
6
|
import { useNavigate } from "react-router-dom";
|
|
8
7
|
import { useStudioBreadcrumbs, BootstrapAdminBanner } from "@rebasepro/core";
|
package/src/utils/sql_utils.ts
CHANGED
|
@@ -105,7 +105,7 @@ export function resolveQueryCollections(
|
|
|
105
105
|
for (const table of tables) {
|
|
106
106
|
// Match table name against collection table or slug->snake_case
|
|
107
107
|
const matched = collections.find(c => {
|
|
108
|
-
const tableName = (c as
|
|
108
|
+
const tableName = (c as EntityCollection & { table?: string }).table || toSnakeCase(c.slug);
|
|
109
109
|
return tableName === table.name;
|
|
110
110
|
});
|
|
111
111
|
|