mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 06:36:00 +00:00
apps skeleton
This commit is contained in:
123
packages/web/src/appobj/AppFileAppObject.svelte
Normal file
123
packages/web/src/appobj/AppFileAppObject.svelte
Normal file
@@ -0,0 +1,123 @@
|
||||
<script lang="ts" context="module">
|
||||
async function openTextFile(fileName, fileType, folderName, tabComponent, icon) {
|
||||
const connProps: any = {};
|
||||
let tooltip = undefined;
|
||||
|
||||
const resp = await apiCall('files/load', {
|
||||
folder: 'app:' + folderName,
|
||||
file: fileName + '.' + fileType,
|
||||
format: 'text',
|
||||
});
|
||||
|
||||
openNewTab(
|
||||
{
|
||||
title: fileName,
|
||||
icon,
|
||||
tabComponent,
|
||||
tooltip,
|
||||
props: {
|
||||
savedFile: fileName + '.' + fileType,
|
||||
savedFolder: 'app:' + folderName,
|
||||
savedFormat: 'text',
|
||||
appFolder: folderName,
|
||||
...connProps,
|
||||
},
|
||||
},
|
||||
{ editor: resp }
|
||||
);
|
||||
}
|
||||
|
||||
export const extractKey = data => data.fileName;
|
||||
export const createMatcher = ({ fileName }) => filter => filterName(filter, fileName);
|
||||
const APP_ICONS = {
|
||||
'command.sql': 'img app-command',
|
||||
'query.sql': 'img app-query',
|
||||
};
|
||||
|
||||
function getAppIcon( data) {
|
||||
return APP_ICONS[data.fileType];
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import { filterName } from 'dbgate-tools';
|
||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
|
||||
import { archiveFilesAsDataSheets, currentArchive, extensions, getCurrentDatabase } from '../stores';
|
||||
|
||||
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
||||
import { exportElectronFile } from '../utility/exportElectronFile';
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import AppObjectCore from './AppObjectCore.svelte';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import {
|
||||
isArchiveFileMarkedAsDataSheet,
|
||||
markArchiveFileAsDataSheet,
|
||||
markArchiveFileAsReadonly,
|
||||
} from '../utility/archiveTools';
|
||||
import { apiCall } from '../utility/api';
|
||||
|
||||
export let data;
|
||||
|
||||
const handleRename = () => {
|
||||
showModal(InputTextModal, {
|
||||
value: data.fileName,
|
||||
label: 'New file name',
|
||||
header: 'Rename file',
|
||||
onConfirm: newFile => {
|
||||
apiCall('apps/rename-file', {
|
||||
file: data.fileName,
|
||||
folder: data.folderName,
|
||||
fileType: data.fileType,
|
||||
newFile,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
showModal(ConfirmModal, {
|
||||
message: `Really delete file ${data.fileName}?`,
|
||||
onConfirm: () => {
|
||||
apiCall('apps/delete-file', {
|
||||
file: data.fileName,
|
||||
folder: data.folderName,
|
||||
fileType: data.fileType,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleClick = () => {
|
||||
if (data.fileType.endsWith('.sql')) {
|
||||
handleOpenSqlFile();
|
||||
}
|
||||
};
|
||||
const handleOpenSqlFile = () => {
|
||||
openTextFile(data.fileName, data.fileType, data.folderName, 'QueryTab', 'img sql-file');
|
||||
};
|
||||
const handleOpenYamlFile = () => {
|
||||
openTextFile(data.fileName, data.fileType, data.folderName, 'YamlEditorTab', 'img yaml');
|
||||
};
|
||||
|
||||
function createMenu() {
|
||||
return [
|
||||
{ text: 'Delete', onClick: handleDelete },
|
||||
{ text: 'Rename', onClick: handleRename },
|
||||
data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile },
|
||||
// data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile },
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<AppObjectCore
|
||||
{...$$restProps}
|
||||
{data}
|
||||
title={data.fileLabel}
|
||||
icon={getAppIcon( data)}
|
||||
menu={createMenu}
|
||||
on:click={handleClick}
|
||||
/>
|
||||
64
packages/web/src/appobj/AppFolderAppObject.svelte
Normal file
64
packages/web/src/appobj/AppFolderAppObject.svelte
Normal file
@@ -0,0 +1,64 @@
|
||||
<script lang="ts" context="module">
|
||||
export const extractKey = data => data.name;
|
||||
export const createMatcher = data => filter => filterName(filter, data.name);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import { filterName } from 'dbgate-tools';
|
||||
|
||||
import { currentApplication } from '../stores';
|
||||
|
||||
import AppObjectCore from './AppObjectCore.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
|
||||
export let data;
|
||||
|
||||
const handleDelete = () => {
|
||||
showModal(ConfirmModal, {
|
||||
message: `Really delete application ${data.name}?`,
|
||||
onConfirm: () => {
|
||||
apiCall('apps/delete-folder', { folder: data.name });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleRename = () => {
|
||||
const { name } = data;
|
||||
|
||||
showModal(InputTextModal, {
|
||||
value: name,
|
||||
label: 'New application name',
|
||||
header: 'Rename application',
|
||||
onConfirm: async newFolder => {
|
||||
await apiCall('apps/rename-folder', {
|
||||
folder: data.name,
|
||||
newFolder: newFolder,
|
||||
});
|
||||
if ($currentApplication == data.name) {
|
||||
$currentApplication = newFolder;
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function createMenu() {
|
||||
return [
|
||||
{ text: 'Delete', onClick: handleDelete },
|
||||
{ text: 'Rename', onClick: handleRename },
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<AppObjectCore
|
||||
{...$$restProps}
|
||||
{data}
|
||||
title={data.name}
|
||||
icon={'img app'}
|
||||
isBold={data.name == $currentApplication}
|
||||
on:click={() => ($currentApplication = data.name)}
|
||||
menu={createMenu}
|
||||
/>
|
||||
@@ -81,7 +81,7 @@
|
||||
markArchiveFileAsDataSheet,
|
||||
markArchiveFileAsReadonly,
|
||||
} from '../utility/archiveTools';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { apiCall } from '../utility/api';
|
||||
|
||||
export let data;
|
||||
|
||||
|
||||
@@ -128,6 +128,23 @@ registerCommand({
|
||||
},
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'new.application',
|
||||
category: 'New',
|
||||
icon: 'img app',
|
||||
name: 'Application',
|
||||
onClick: () => {
|
||||
showModal(InputTextModal, {
|
||||
value: '',
|
||||
label: 'New application name',
|
||||
header: 'Create application',
|
||||
onConfirm: async folder => {
|
||||
apiCall('apps/create-folder', { folder });
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'new.table',
|
||||
category: 'New',
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
'icon version': 'mdi mdi-ticket-confirmation',
|
||||
'icon pin': 'mdi mdi-pin',
|
||||
'icon arrange': 'mdi mdi-arrange-send-to-back',
|
||||
'icon app': 'mdi mdi-layers-triple',
|
||||
|
||||
'icon columns': 'mdi mdi-view-column',
|
||||
'icon columns-outline': 'mdi mdi-view-column-outline',
|
||||
@@ -126,6 +127,9 @@
|
||||
'img diagram': 'mdi mdi-graph color-icon-blue',
|
||||
'img yaml': 'mdi mdi-code-brackets color-icon-red',
|
||||
'img compare': 'mdi mdi-compare color-icon-red',
|
||||
'img app': 'mdi mdi-layers-triple color-icon-magenta',
|
||||
'img app-command': 'mdi mdi-flash color-icon-green',
|
||||
'img app-query': 'mdi mdi-view-comfy color-icon-magenta',
|
||||
|
||||
'img add': 'mdi mdi-plus-circle color-icon-green',
|
||||
'img minus': 'mdi mdi-minus-circle color-icon-red',
|
||||
|
||||
@@ -64,6 +64,7 @@ export const openedModals = writable([]);
|
||||
export const openedSnackbars = writable([]);
|
||||
export const nullStore = readable(null, () => {});
|
||||
export const currentArchive = writableWithStorage('default', 'currentArchive');
|
||||
export const currentApplication = writableWithStorage(null, 'currentApplication');
|
||||
export const isFileDragActive = writable(false);
|
||||
export const selectedCellsCallback = writable(null);
|
||||
export const loadingPluginStore = writable({
|
||||
|
||||
@@ -103,6 +103,18 @@ const archiveFilesLoader = ({ folder }) => ({
|
||||
reloadTrigger: `archive-files-changed-${folder}`,
|
||||
});
|
||||
|
||||
const appFoldersLoader = () => ({
|
||||
url: 'apps/folders',
|
||||
params: {},
|
||||
reloadTrigger: `app-folders-changed`,
|
||||
});
|
||||
|
||||
const appFilesLoader = ({ folder }) => ({
|
||||
url: 'apps/files',
|
||||
params: { folder },
|
||||
reloadTrigger: `app-files-changed-${folder}`,
|
||||
});
|
||||
|
||||
const serverStatusLoader = () => ({
|
||||
url: 'server-connections/server-status',
|
||||
params: {},
|
||||
@@ -401,6 +413,20 @@ export function useArchiveFolders(args = {}) {
|
||||
return useCore(archiveFoldersLoader, args);
|
||||
}
|
||||
|
||||
export function getAppFiles(args) {
|
||||
return getCore(appFilesLoader, args);
|
||||
}
|
||||
export function useAppFiles(args) {
|
||||
return useCore(appFilesLoader, args);
|
||||
}
|
||||
|
||||
export function getAppFolders(args = {}) {
|
||||
return getCore(appFoldersLoader, args);
|
||||
}
|
||||
export function useAppFolders(args = {}) {
|
||||
return useCore(appFoldersLoader, args);
|
||||
}
|
||||
|
||||
export function getInstalledPlugins(args = {}) {
|
||||
return getCore(installedPluginsLoader, args) || [];
|
||||
}
|
||||
|
||||
89
packages/web/src/widgets/AppFilesList.svelte
Normal file
89
packages/web/src/widgets/AppFilesList.svelte
Normal file
@@ -0,0 +1,89 @@
|
||||
<script lang="ts" context="module">
|
||||
const APP_LABELS = {
|
||||
'command.sql': 'SQL commands',
|
||||
'query.sql': 'SQL queries',
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createFreeTableModel } from 'dbgate-datalib';
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import AppObjectList from '../appobj/AppObjectList.svelte';
|
||||
import * as appFileAppObject from '../appobj/AppFileAppObject.svelte';
|
||||
import CloseSearchButton from '../elements/CloseSearchButton.svelte';
|
||||
import DropDownButton from '../elements/DropDownButton.svelte';
|
||||
|
||||
import InlineButton from '../elements/InlineButton.svelte';
|
||||
|
||||
import SearchBoxWrapper from '../elements/SearchBoxWrapper.svelte';
|
||||
import SearchInput from '../elements/SearchInput.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import newQuery from '../query/newQuery';
|
||||
import { currentApplication } from '../stores';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { markArchiveFileAsDataSheet } from '../utility/archiveTools';
|
||||
import { useAppFiles, useArchiveFolders } from '../utility/metadataLoaders';
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
||||
|
||||
let filter = '';
|
||||
|
||||
$: folder = $currentApplication;
|
||||
$: files = useAppFiles({ folder });
|
||||
|
||||
const handleRefreshFiles = () => {
|
||||
apiCall('apps/refresh-files', { folder });
|
||||
};
|
||||
|
||||
function handleNewSqlFile(fileType, header) {
|
||||
showModal(InputTextModal, {
|
||||
value: '',
|
||||
label: 'New file name',
|
||||
header,
|
||||
onConfirm: async file => {
|
||||
newQuery({
|
||||
title: file,
|
||||
// @ts-ignore
|
||||
savedFile: file + '.' + fileType,
|
||||
savedFolder: 'app:' + $currentApplication,
|
||||
savedFormat: 'text',
|
||||
appFolder: $currentApplication,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createAddMenu() {
|
||||
return [
|
||||
{ text: 'New SQL command', onClick: () => handleNewSqlFile('command.sql', 'Create new SQL command') },
|
||||
{ text: 'New query view', onClick: () => handleNewSqlFile('query.sql', 'Create new SQL query') },
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<SearchBoxWrapper>
|
||||
<SearchInput placeholder="Search application files" bind:value={filter} />
|
||||
|
||||
<CloseSearchButton bind:filter />
|
||||
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
||||
<InlineButton on:click={handleRefreshFiles} title="Refresh files of selected application">
|
||||
<FontIcon icon="icon refresh" />
|
||||
</InlineButton>
|
||||
</SearchBoxWrapper>
|
||||
<WidgetsInnerContainer>
|
||||
<AppObjectList
|
||||
list={($files || []).map(file => ({
|
||||
fileName: file.name,
|
||||
folderName: folder,
|
||||
fileType: file.type,
|
||||
fileLabel: file.label,
|
||||
}))}
|
||||
groupFunc={data => APP_LABELS[data.fileType] || 'App config'}
|
||||
module={appFileAppObject}
|
||||
{filter}
|
||||
/>
|
||||
</WidgetsInnerContainer>
|
||||
39
packages/web/src/widgets/AppFolderList.svelte
Normal file
39
packages/web/src/widgets/AppFolderList.svelte
Normal file
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
|
||||
import AppObjectList from '../appobj/AppObjectList.svelte';
|
||||
import * as appFolderAppObject from '../appobj/AppFolderAppObject.svelte';
|
||||
import runCommand from '../commands/runCommand';
|
||||
import CloseSearchButton from '../elements/CloseSearchButton.svelte';
|
||||
|
||||
import InlineButton from '../elements/InlineButton.svelte';
|
||||
|
||||
import SearchBoxWrapper from '../elements/SearchBoxWrapper.svelte';
|
||||
import SearchInput from '../elements/SearchInput.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { useAppFolders } from '../utility/metadataLoaders';
|
||||
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
||||
|
||||
let filter = '';
|
||||
|
||||
$: folders = useAppFolders();
|
||||
|
||||
const handleRefreshFolders = () => {
|
||||
apiCall('apps/refresh-folders');
|
||||
};
|
||||
</script>
|
||||
|
||||
<SearchBoxWrapper>
|
||||
<SearchInput placeholder="Search applications" bind:value={filter} />
|
||||
<CloseSearchButton bind:filter />
|
||||
<InlineButton on:click={() => runCommand('new.application')} title="Create new application">
|
||||
<FontIcon icon="icon plus-thick" />
|
||||
</InlineButton>
|
||||
<InlineButton on:click={handleRefreshFolders} title="Refresh application list">
|
||||
<FontIcon icon="icon refresh" />
|
||||
</InlineButton>
|
||||
</SearchBoxWrapper>
|
||||
<WidgetsInnerContainer>
|
||||
<AppObjectList list={_.sortBy($folders, 'name')} module={appFolderAppObject} {filter} />
|
||||
</WidgetsInnerContainer>
|
||||
19
packages/web/src/widgets/AppWidget.svelte
Normal file
19
packages/web/src/widgets/AppWidget.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import AppFilesList from './AppFilesList.svelte';
|
||||
|
||||
import WidgetColumnBar from './WidgetColumnBar.svelte';
|
||||
import WidgetColumnBarItem from './WidgetColumnBarItem.svelte';
|
||||
|
||||
import { useFavorites } from '../utility/metadataLoaders';
|
||||
import AppFolderList from './AppFolderList.svelte';
|
||||
</script>
|
||||
|
||||
<WidgetColumnBar>
|
||||
<WidgetColumnBarItem title="Applications" name="apps" height="30%" storageName="appsWidget">
|
||||
<AppFolderList />
|
||||
</WidgetColumnBarItem>
|
||||
|
||||
<WidgetColumnBarItem title="Application files" name="files" storageName="appFilesWidget">
|
||||
<AppFilesList />
|
||||
</WidgetColumnBarItem>
|
||||
</WidgetColumnBar>
|
||||
@@ -6,6 +6,7 @@
|
||||
import PluginsWidget from './PluginsWidget.svelte';
|
||||
import CellDataWidget from './CellDataWidget.svelte';
|
||||
import HistoryWidget from './HistoryWidget.svelte';
|
||||
import AppWidget from './AppWidget.svelte';
|
||||
</script>
|
||||
|
||||
<DatabaseWidget hidden={$selectedWidget != 'database'} />
|
||||
@@ -25,3 +26,6 @@
|
||||
{#if $selectedWidget == 'cell-data'}
|
||||
<CellDataWidget />
|
||||
{/if}
|
||||
{#if $selectedWidget == 'app'}
|
||||
<AppWidget />
|
||||
{/if}
|
||||
|
||||
@@ -40,6 +40,11 @@
|
||||
name: 'cell-data',
|
||||
title: 'Selected cell data detail view',
|
||||
},
|
||||
{
|
||||
icon: 'icon app',
|
||||
name: 'app',
|
||||
title: 'Application layers',
|
||||
},
|
||||
// {
|
||||
// icon: 'icon settings',
|
||||
// name: 'settings',
|
||||
|
||||
Reference in New Issue
Block a user