Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSON as an export option #173

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Effortlessly move data in and out of your database:

- ✅ Export data to CSV.
- ✅ Export data to Microsoft Excel.
- Export data to JSON (in progress).
- Export data to JSON.
- ❌ Export data to SQL (in progress).
- ❌ Export data to XML (in progress).
- ❌ Export to Clipboard (in progress).
Expand Down
20 changes: 20 additions & 0 deletions src/libs/StructuredTextFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import fs from 'fs';

export type SupportedStructuredFileType = 'json'

function jsonSerializer(data: any) {

Check warning on line 5 in src/libs/StructuredTextFile.ts

View workflow job for this annotation

GitHub Actions / Lint, Type Check and Test

Unexpected any. Specify a different type
return JSON.stringify(data, undefined, 2);
}

export default function saveStructuredTextFile(
fileName: string,
type: SupportedStructuredFileType,
records: object[]
) {

const serializers = {
json: jsonSerializer,
}

fs.writeFileSync(fileName, serializers[type](records))
}
Comment on lines +9 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is overkill. We only need a single function that able to write text file.

4 changes: 4 additions & 0 deletions src/main/ipc/ipc_file_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import fs from 'fs';
import saveCsvFile from '../../libs/SaveCSVFile';
import saveExcelFile from '../../libs/SaveExcelFile';
import CommunicateHandler from './../CommunicateHandler';
import saveStructuredTextFile, { SupportedStructuredFileType } from '../../libs/StructuredTextFile';

CommunicateHandler.handle(
'show-message-box',
Expand All @@ -35,6 +36,9 @@ CommunicateHandler.handle(
}
},
)
.handle('save-structured-text-file', ([fileName, type, records]: [string, string, object[]]) => {
saveStructuredTextFile(fileName, type as SupportedStructuredFileType, records);
})
.handle('save-csv-file', ([fileName, records]: [string, object[]]) => {
saveCsvFile(fileName, records);
})
Expand Down
4 changes: 4 additions & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
UpdateDownloadedEvent,
UpdateInfo,
} from 'electron-updater';
import { SupportedStructuredFileType } from 'libs/StructuredTextFile.js';

export type Channels = 'ipc-example' | 'create-connection';

Expand Down Expand Up @@ -74,6 +75,9 @@ const electronHandler = {
showFileInFolder: (fileName: string) =>
ipcRenderer.invoke('show-item-in-folder', [fileName]),

saveStructuredTextFile: (fileName: string, type: SupportedStructuredFileType, records: object[]) =>
ipcRenderer.invoke('save-structured-text-file', [fileName, type, records]),

saveCsvFile: (fileName: string, records: object[]) =>
ipcRenderer.invoke('save-csv-file', [fileName, records]),

Expand Down
53 changes: 37 additions & 16 deletions src/renderer/screens/DatabaseScreen/ExportModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
faFileExcel,
faFileCsv,
faFileText,
faCircleCheck,
faTimesCircle,
faSpinner,
faEllipsis,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { SupportedStructuredFileType } from 'libs/StructuredTextFile';
import { getDisplayableFromDatabaseRows } from 'libs/TransformResult';
import { useCallback, useState } from 'react';
import Button from 'renderer/components/Button';
Expand All @@ -16,18 +18,35 @@ import Stack from 'renderer/components/Stack';
import TextField from 'renderer/components/TextField';
import { QueryResult } from 'types/SqlResult';

function combineExportOptionText(name: string, extensions: string[]): string {
const ext = extensions.map(x => `*.${x}`).join(", ");
return `${name} (${ext})`
}

const EXPORT_OPTIONS = [
{
text: 'Excel (*.xlsx)',
name: 'Excel',
icon: <FontAwesomeIcon color="#27ae60" icon={faFileExcel} />,
value: 'excel',
extensions: ['xlsx'],
},
{
text: 'Comma Separated Value (*.csv)',
name: 'Comma Separated Value',
icon: <FontAwesomeIcon color="#e67e22" icon={faFileCsv} />,
value: 'csv',
extensions: ['csv'],
},
];
{
name: 'JSON',
icon: <FontAwesomeIcon color="#2f2f2c" icon={faFileText} />,
value: 'json',
extensions: ['json'],
}
].map((item) => ({...item, text: combineExportOptionText(item.name, item.extensions) }));

const EXPORT_OPTIONS_FILE_FILTERS = Object.fromEntries(
EXPORT_OPTIONS.map(({ value, extensions, name }) => [value, { extensions, name }])
);

interface ExportModalProps {
data: QueryResult;
Expand All @@ -43,20 +62,10 @@ function ExportModalConfig({
const [fileName, setFileName] = useState('');

const onBrowseFileClicked = useCallback(() => {
const filter = EXPORT_OPTIONS_FILE_FILTERS[format];
if (!filter) return;
window.electron
.showSaveDialog({
filters: [
format === 'excel'
? {
name: 'Excel',
extensions: ['xlsx'],
}
: {
name: 'CSV',
extensions: ['csv'],
},
],
})
.showSaveDialog({ filters: [filter] })
.then((value) => setFileName(value ?? ''));
}, [format]);

Expand Down Expand Up @@ -213,6 +222,18 @@ export default function ExportModal({ data, onClose }: ExportModalProps) {
console.error(e);
setStage('ERROR');
});
} else if (format === "json") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can follow this for JSON

https://github.com/querymx/querym/blob/main/src/renderer/screens/DatabaseScreen/QueryResultViewer/useDataTableContextMenu.tsx#L81

We just need to do

jsonString = // can follow the above code;
window.electron.saveTextFile(fileName, jsonString);

window.electron
.saveStructuredTextFile(
fileName,
format as SupportedStructuredFileType,
getDisplayableFromDatabaseRows(data.rows, data.headers)
)
.then(() => setStage('SUCCESS'))
.catch((e) => {
console.error(e);
setStage('ERROR');
});
} else {
setStage('ERROR');
}
Expand Down