apps skeleton

This commit is contained in:
Jan Prochazka
2022-01-27 14:31:46 +01:00
parent 8e3d614fb8
commit 595c9424df
16 changed files with 506 additions and 2 deletions

View 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}
/>

View 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}
/>

View File

@@ -81,7 +81,7 @@
markArchiveFileAsDataSheet,
markArchiveFileAsReadonly,
} from '../utility/archiveTools';
import { apiCall } from '../utility/api';
import { apiCall } from '../utility/api';
export let data;

View File

@@ -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',

View File

@@ -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',

View File

@@ -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({

View File

@@ -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) || [];
}

View 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>

View 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>

View 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>

View File

@@ -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}

View File

@@ -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',