SYNC: Merge pull request #3 from dbgate/feature/zip

This commit is contained in:
Jan Prochazka
2025-04-23 13:17:54 +02:00
committed by Diflow
parent 54c53f0b56
commit 8f4118a6b8
82 changed files with 3981 additions and 2814 deletions

View File

@@ -14,6 +14,8 @@
import { apiCall } from '../utility/api';
import { useArchiveFolders } from '../utility/metadataLoaders';
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
import InlineUploadButton from '../buttons/InlineUploadButton.svelte';
import { isProApp } from '../utility/proTools';
let filter = '';
@@ -22,11 +24,47 @@
const handleRefreshFolders = () => {
apiCall('archive/refresh-folders');
};
async function handleUploadedFile(filePath, fileName) {
await apiCall('archive/save-uploaded-zip', { filePath, fileName });
}
</script>
<SearchBoxWrapper>
<SearchInput placeholder="Search archive folders" bind:value={filter} />
<CloseSearchButton bind:filter />
{#if isProApp()}
<InlineUploadButton
icon="icon upload"
filters={[
{
name: `All supported files`,
extensions: ['zip'],
},
{ name: `ZIP files`, extensions: ['zip'] },
]}
onProcessFile={handleUploadedFile}
/>
{/if}
<!-- {#if electron}
<InlineButton on:click={handleOpenElectronFile} title="Add file" data-testid="ArchiveFolderList_uploadZipFile">
<FontIcon icon="icon plus-thick" />
</InlineButton>
{:else}
<InlineButtonLabel
on:click={() => {}}
title="Add file"
data-testid="ArchiveFolderList_uploadZipFile"
htmlFor="uploadZipFileButton"
>
<FontIcon icon="icon plus-thick" />
</InlineButtonLabel>
{/if}
<input type="file" id="uploadZipFileButton" hidden on:change={handleUploadedFile} /> -->
<InlineButton on:click={() => runCommand('new.archiveFolder')} title="Add new archive folder">
<FontIcon icon="icon plus-thick" />
</InlineButton>

View File

@@ -0,0 +1,75 @@
<script lang="ts">
import { evalFilterBehaviour } from 'dbgate-tools';
import DataFilterControl from '../datagrid/DataFilterControl.svelte';
import InlineButton from '../buttons/InlineButton.svelte';
import SelectField from '../forms/SelectField.svelte';
import _ from 'lodash';
import FontIcon from '../icons/FontIcon.svelte';
export let compoudFilter: { [key: string]: string };
export let onSetCompoudFilter;
export let columnNames: string[];
export let filterBehaviour = evalFilterBehaviour;
$: columnsReal = Object.keys(compoudFilter || {});
$: columnsUsed = columnsReal.length > 0 ? columnsReal : [columnNames[0]];
</script>
{#each columnsUsed as column, index}
<div class="flex">
<SelectField
isNative
value={column}
on:change={e => {
const keys = Object.keys(compoudFilter || {});
const values = Object.values(compoudFilter || {});
keys[index] = e.detail;
const newFilter = _.zipObject(keys, values);
onSetCompoudFilter(newFilter);
}}
options={columnNames.map(col => ({
label: col,
value: col,
})) || []}
/>
<DataFilterControl
{filterBehaviour}
filter={compoudFilter?.[column] ?? ''}
setFilter={value => {
onSetCompoudFilter({
...compoudFilter,
[column]: value,
});
}}
placeholder="Filter"
/>
{#if index == 0}
<InlineButton
on:click={() => {
const newColumn = columnNames.find(x => !columnsUsed.includes(x));
if (!newColumn) return;
onSetCompoudFilter({
...compoudFilter,
[newColumn]: '',
});
}}
title="Add filter column"
square
>
<FontIcon icon="icon plus-thick" />
</InlineButton>
{:else}
<InlineButton
on:click={() => {
onSetCompoudFilter(_.omit(compoudFilter, column));
}}
title="Remove filter column"
square
>
<FontIcon icon="icon minus-thick" />
</InlineButton>
{/if}
</div>
{/each}

View File

@@ -10,9 +10,8 @@
import { apiCall } from '../utility/api';
import { useFiles } from '../utility/metadataLoaders';
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
import getElectron from '../utility/getElectron';
import InlineButtonLabel from '../buttons/InlineButtonLabel.svelte';
import resolveApi, { resolveApiHeaders } from '../utility/resolveApi';
import { isProApp } from '../utility/proTools';
import InlineUploadButton from '../buttons/InlineUploadButton.svelte';
let filter = '';
@@ -23,12 +22,12 @@
const queryFiles = useFiles({ folder: 'query' });
const sqliteFiles = useFiles({ folder: 'sqlite' });
const diagramFiles = useFiles({ folder: 'diagrams' });
const jobFiles = useFiles({ folder: 'jobs' });
const importExportJobFiles = useFiles({ folder: 'impexp' });
const dataDeployJobFiles = useFiles({ folder: 'datadeploy' });
const dbCompareJobFiles = useFiles({ folder: 'dbcompare' });
const perspectiveFiles = useFiles({ folder: 'perspectives' });
const modelTransformFiles = useFiles({ folder: 'modtrans' });
const electron = getElectron();
$: files = [
...($sqlFiles || []),
...($shellFiles || []),
@@ -38,8 +37,10 @@
...($sqliteFiles || []),
...($diagramFiles || []),
...($perspectiveFiles || []),
...($jobFiles || []),
...($importExportJobFiles || []),
...($modelTransformFiles || []),
...((isProApp() && $dataDeployJobFiles) || []),
...((isProApp() && $dbCompareJobFiles) || []),
];
function handleRefreshFiles() {
@@ -53,50 +54,23 @@
'sqlite',
'diagrams',
'perspectives',
'jobs',
'impexp',
'modtrans',
'datadeploy',
'dbcompare',
],
});
}
function dataFolderTitle(folder) {
if (folder == 'modtrans') return 'Model transforms';
if (folder == 'datadeploy') return 'Data deploy jobs';
if (folder == 'dbcompare') return 'Database compare jobs';
return _.startCase(folder);
}
async function handleUploadedFile(e) {
const files = [...e.target.files];
for (const file of files) {
const formData = new FormData();
formData.append('name', file.name);
formData.append('data', file);
const fetchOptions = {
method: 'POST',
body: formData,
headers: resolveApiHeaders(),
};
const apiBase = resolveApi();
const resp = await fetch(`${apiBase}/uploads/upload-data-file`, fetchOptions);
const fileData = await resp.json();
}
}
async function handleOpenElectronFile() {
const filePaths = await electron.showOpenDialog({
filters: [
{
name: `All supported files`,
extensions: ['sql'],
},
{ name: `SQL files`, extensions: ['sql'] },
],
properties: ['showHiddenFiles', 'openFile'],
});
const filePath = filePaths && filePaths[0];
await apiCall('uploads/save-data-file', { filePath });
async function handleUploadedFile(filePath, fileName) {
await apiCall('files/save-uploaded-file', { filePath, fileName });
}
</script>
@@ -104,26 +78,20 @@
<SearchBoxWrapper>
<SearchInput placeholder="Search saved files" bind:value={filter} />
<CloseSearchButton bind:filter />
{#if electron}
<InlineButton on:click={handleOpenElectronFile} title="Add file" data-testid="SavedFileList_buttonAddFile">
<FontIcon icon="icon plus-thick" />
</InlineButton>
{:else}
<InlineButtonLabel
on:click={() => {}}
title="Add file"
data-testid="SavedFileList_buttonAddFile"
htmlFor="uploadSavedFileButton"
>
<FontIcon icon="icon plus-thick" />
</InlineButtonLabel>
{/if}
<InlineUploadButton
filters={[
{
name: `All supported files`,
extensions: ['sql'],
},
{ name: `SQL files`, extensions: ['sql'] },
]}
onProcessFile={handleUploadedFile}
/>
<InlineButton on:click={handleRefreshFiles} title="Refresh files" data-testid="SavedFileList_buttonRefresh">
<FontIcon icon="icon refresh" />
</InlineButton>
</SearchBoxWrapper>
<input type="file" id="uploadSavedFileButton" hidden on:change={handleUploadedFile} />
<AppObjectList list={files} module={savedFileAppObject} groupFunc={data => dataFolderTitle(data.folder)} {filter} />
</WidgetsInnerContainer>