1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

..

24 Commits

Author SHA1 Message Date
19d9f56f08 chore(release): 0.7.19 2023-11-01 09:43:50 +01:00
4c89578188 Merge branch 'develop' of https://github.com/antares-sql/antares into beta 2023-11-01 09:42:59 +01:00
f6fb266771 fix: table field changes not saved on text fields if pressing enter on textarea modal 2023-10-31 17:57:58 +01:00
3e739bcaa2 chore(release): 0.7.19-beta.2 2023-10-29 11:47:08 +01:00
debc1da289 fix: ssh tunnel keep-alive not working properly 2023-10-28 19:09:19 +02:00
3c2e2be40f chore: regenerate package-lock.json 2023-10-28 18:47:06 +02:00
581ec6a25d Merge branch 'develop' of https://github.com/antares-sql/antares into develop 2023-10-28 18:25:03 +02:00
d30a978cd6 refactor: replace ssh2-promise with @fabio286/ssh2-promise 2023-10-28 18:25:01 +02:00
0015f2e860 Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-10-28 11:58:06 +02:00
389e6624d8 chore: update stylelint dependencies 2023-10-28 11:55:18 +02:00
275344eb8b chore(release): 0.7.19-beta.1 2023-10-26 01:07:15 +02:00
cf24adf99e Merge branch 'develop' of https://github.com/antares-sql/antares into beta 2023-10-26 01:06:54 +02:00
2eae580e18 chore: update electron 2023-10-22 19:04:31 +02:00
e4eb27d503 feat(MySQL): RLIKE and NOT RLIKE regular expression filters, closes #688 2023-10-18 18:08:01 +02:00
580973fd04 chore(release): 0.7.19-beta.0 2023-10-14 15:04:00 +02:00
215ab783ab chore: email change 2023-10-14 15:03:09 +02:00
72148e991c Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-10-13 09:23:41 +02:00
6e4c16741a chore: Update CODE_OF_CONDUCT.md 2023-10-13 09:22:04 +02:00
9f9c63bfcc feat: "now" and "random" options added in datetime related data in insert rows tool, closes #402 2023-10-10 18:36:35 +02:00
c0dcf30e73 fix: IN and NOT IN filters not working properly, fixes #687 2023-10-09 18:13:30 +02:00
ddd290c903 fix: timeout issue on long time sql import 2023-10-03 18:19:16 +02:00
267c017921 chore(release): 0.7.18 2023-10-03 09:31:58 +02:00
b3b698b3a2 fix: hotfix for Microsoft Store unauthorized process 2023-10-03 09:31:26 +02:00
71ac3a5164 chore(release): 0.7.17 2023-09-30 16:46:49 +02:00
28 changed files with 14969 additions and 507 deletions

BIN
.nvmrc Normal file

Binary file not shown.

View File

@@ -2,6 +2,49 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.7.19](https://github.com/antares-sql/antares/compare/v0.7.19-beta.2...v0.7.19) (2023-11-01)
### Bug Fixes
* table field changes not saved on text fields if pressing enter on textarea modal ([f6fb266](https://github.com/antares-sql/antares/commit/f6fb266771f2d798c8ae42b997c1e33520cf21c3))
### [0.7.19-beta.2](https://github.com/antares-sql/antares/compare/v0.7.19-beta.1...v0.7.19-beta.2) (2023-10-29)
### Bug Fixes
* ssh tunnel keep-alive not working properly ([debc1da](https://github.com/antares-sql/antares/commit/debc1da289d5e35d59adf69d094b329cf93af536))
### [0.7.19-beta.1](https://github.com/antares-sql/antares/compare/v0.7.19-beta.0...v0.7.19-beta.1) (2023-10-25)
### Features
* **MySQL:** RLIKE and NOT RLIKE regular expression filters, closes [#688](https://github.com/antares-sql/antares/issues/688) ([e4eb27d](https://github.com/antares-sql/antares/commit/e4eb27d503e8f912178359c01c62a9b523d17848))
### [0.7.19-beta.0](https://github.com/antares-sql/antares/compare/v0.7.18...v0.7.19-beta.0) (2023-10-14)
### Features
* "now" and "random" options added in datetime related data in insert rows tool, closes [#402](https://github.com/antares-sql/antares/issues/402) ([9f9c63b](https://github.com/antares-sql/antares/commit/9f9c63bfcc3423bfeef143cd835f48c62900a799))
### Bug Fixes
* IN and NOT IN filters not working properly, fixes [#687](https://github.com/antares-sql/antares/issues/687) ([c0dcf30](https://github.com/antares-sql/antares/commit/c0dcf30e73a69b25b01ba31d21b27c1983ed2db6))
* timeout issue on long time sql import ([ddd290c](https://github.com/antares-sql/antares/commit/ddd290c90344241eaa70cb528552e942fd7edec0))
### [0.7.18](https://github.com/antares-sql/antares/compare/v0.7.17...v0.7.18) (2023-10-03)
### Bug Fixes
* hotfix for Microsoft Store unauthorized process ([b3b698b](https://github.com/antares-sql/antares/commit/b3b698b3a23a3c848921ab40fc0fec5d8178ef0e))
### [0.7.17](https://github.com/antares-sql/antares/compare/v0.7.17-beta.2...v0.7.17) (2023-09-30)
### [0.7.17-beta.2](https://github.com/antares-sql/antares/compare/v0.7.17-beta.1...v0.7.17-beta.2) (2023-09-28)

View File

@@ -61,7 +61,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
fabio286@gmail.com.
info@fabiodistasio.it.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the

14959
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "antares",
"productName": "Antares",
"version": "0.7.17-beta.2",
"version": "0.7.19",
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
"license": "MIT",
"repository": "https://github.com/antares-sql/antares.git",
@@ -27,7 +27,7 @@
"contributors:add": "all-contributors add",
"contributors:generate": "all-contributors generate"
},
"author": "Fabio Di Stasio <fabio286@gmail.com>",
"author": "Fabio Di Stasio <info@fabiodistasio.it>",
"main": "./dist/main.js",
"antares": {
"devtoolsId": "nhdogjmejiglipccpnnnanhbledajbpd"
@@ -146,7 +146,7 @@
"source-map-support": "~0.5.20",
"spectre.css": "~0.5.9",
"sql-formatter": "~13.0.0",
"ssh2-promise": "~1.0.2",
"@fabio286/ssh2-promise": "~1.0.4-b",
"v-mask": "~2.3.0",
"vue": "~3.3.4",
"vue-i18n": "~9.2.2",
@@ -171,7 +171,7 @@
"chalk": "~4.1.2",
"cross-env": "~7.0.2",
"css-loader": "~6.5.0",
"electron": "~22.3.23",
"electron": "~22.3.27",
"electron-builder": "~22.10.3",
"eslint": "~7.32.0",
"eslint-config-standard": "~16.0.3",
@@ -193,10 +193,10 @@
"sass-loader": "~12.3.0",
"standard-version": "~9.3.1",
"style-loader": "~3.3.1",
"stylelint": "~14.9.1",
"stylelint-config-recommended-vue": "~1.4.0",
"stylelint-config-standard": "~26.0.0",
"stylelint-scss": "~4.3.0",
"stylelint": "^15.11.0",
"stylelint-config-recommended-vue": "~1.5.0",
"stylelint-config-standard": "~34.0.0",
"stylelint-scss": "~5.3.0",
"tree-kill": "~1.2.2",
"ts-loader": "~9.2.8",
"ts-node": "~10.9.1",
@@ -208,10 +208,5 @@
"webpack-cli": "~4.9.1",
"webpack-dev-server": "~4.11.1",
"xvfb-maybe": "~0.2.1"
},
"overrides": {
"ssh2-promise": {
"ssh2": "github:Fabio286/ssh2"
}
}
}

View File

@@ -51,6 +51,7 @@ export default class {
{ name: 'collation', group: 'database', types: ['string'] },
{ name: 'engine', group: 'database', types: ['string'] },
{ name: 'now', group: 'date', types: ['string', 'datetime'] },
{ name: 'past', group: 'date', types: ['string', 'datetime'] },
{ name: 'future', group: 'date', types: ['string', 'datetime'] },
// { name: 'between', group: 'date', types: ['string'] },
@@ -161,7 +162,9 @@ export default class {
{ name: 'filePath', group: 'system', types: ['string'] },
{ name: 'semver', group: 'system', types: ['string'] },
{ name: 'now', group: 'time', types: ['string', 'time'] },
{ name: 'recent', group: 'time', types: ['string', 'time'] },
{ name: 'random', group: 'time', types: ['string', 'time'] },
{ name: 'vehicle', group: 'vehicle', types: ['string'] },
{ name: 'manufacturer', group: 'vehicle', types: ['string'] },

View File

@@ -9,6 +9,7 @@ export const defaults: Customizations = {
dataTypes: [],
indexTypes: [],
foreignActions: [],
operators: ['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'],
// Core
database: false,
collations: false,

View File

@@ -9,6 +9,7 @@ export const customizations: Customizations = {
defaultUser: 'root',
defaultDatabase: null,
dataTypes: mysqlTypes,
operators: ['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', 'RLIKE', 'NOT RLIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'],
indexTypes: [
'PRIMARY',
'INDEX',

View File

@@ -1,3 +1,4 @@
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import * as mysql from 'mysql2/promise';
import * as pg from 'pg';
import { FirebirdSQLClient } from 'src/main/libs/clients/FirebirdSQLClient';
@@ -5,7 +6,6 @@ import MysqlExporter from 'src/main/libs/exporters/sql/MysqlExporter';
import PostgreSQLExporter from 'src/main/libs/exporters/sql/PostgreSQLExporter';
import MySQLImporter from 'src/main/libs/importers/sql/MySQLlImporter';
import PostgreSQLImporter from 'src/main/libs/importers/sql/PostgreSQLImporter';
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import { MySQLClient } from '../../main/libs/clients/MySQLClient';
import { PostgreSQLClient } from '../../main/libs/clients/PostgreSQLClient';
@@ -363,8 +363,7 @@ export interface QueryBuilderObject {
offset: number;
join: string[];
update: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
insert: {[key: string]: any}[];
insert: {[key: string]: string | boolean | number }[];
delete: boolean;
}

View File

@@ -1,4 +1,5 @@
import { TypesGroup } from './antares';
import { TableFilterOperator } from './tableApis';
export interface Customizations {
// Defaults
@@ -8,6 +9,7 @@ export interface Customizations {
dataTypes?: TypesGroup[];
indexTypes?: string[];
foreignActions?: string[];
operators?: TableFilterOperator[];
// Core
database?: boolean;
collations?: boolean;

View File

@@ -21,7 +21,7 @@ export interface TableDeleteParams {
rows: {[key: string]: any};
}
export type TableFilterOperator = '=' | '!=' | '>' | '<' | '>=' | '<=' | 'IN' | 'NOT IN' | 'LIKE' | 'NOT LIKE' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL'
export type TableFilterOperator = '=' | '!=' | '>' | '<' | '>=' | '<=' | 'IN' | 'NOT IN' | 'LIKE' | 'NOT LIKE' | 'RLIKE' | 'NOT RLIKE' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL'
export interface TableFilterClausole {
active: boolean;

View File

@@ -0,0 +1,17 @@
import { faker } from '@faker-js/faker';
import * as moment from 'moment';
export const fakerCustom = {
seed: faker.seed,
setLocale: faker.setLocale,
...faker,
date: {
now: () => moment().format('YYYY-MM-DD HH:mm:ss'),
...faker.date
},
time: {
now: () => moment().format('HH:mm:ss'),
random: () => moment(faker.date.recent()).format('HH:mm:ss'),
...faker.time
}
};

View File

@@ -55,7 +55,7 @@ export default (connections: {[key: string]: antares.Client}) => {
port: conn.sshPort ? conn.sshPort : 22,
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : null,
passphrase: conn.sshPassphrase,
keepaliveInterval: conn.sshKeepAliveInterval ?? conn.sshKeepAliveInterval*1000
keepaliveInterval: conn.sshKeepAliveInterval ? conn.sshKeepAliveInterval*1000 : null
};
}
@@ -137,7 +137,7 @@ export default (connections: {[key: string]: antares.Client}) => {
port: conn.sshPort ? conn.sshPort : 22,
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : null,
passphrase: conn.sshPassphrase,
keepaliveInterval: conn.sshKeepAliveInterval ?? conn.sshKeepAliveInterval*1000
keepaliveInterval: conn.sshKeepAliveInterval ? conn.sshKeepAliveInterval*1000 : null
};
}

View File

@@ -1,8 +1,8 @@
import { faker } from '@faker-js/faker';
import customizations from 'common/customizations';
import { ARRAY, BIT, BLOB, BOOLEAN, DATE, DATETIME, FLOAT, LONG_TEXT, NUMBER, TEXT, TEXT_SEARCH } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares';
import { InsertRowsParams } from 'common/interfaces/tableApis';
import { fakerCustom } from 'common/libs/fakerCustom';
import { sqlEscaper } from 'common/libs/sqlUtils';
import { ipcMain } from 'electron';
import * as fs from 'fs';
@@ -371,19 +371,19 @@ export default (connections: {[key: string]: antares.Client}) => {
let fakeValue;
if (params.locale)
faker.locale = params.locale;
fakerCustom.locale = params.locale;
if (Object.keys(params.row[key].params).length) {
Object.keys(params.row[key].params).forEach(param => {
if (!isNaN(params.row[key].params[param]))
parsedParams[param] = +params.row[key].params[param];
if (!isNaN(params.row[key].params[param]))// Converts string numerics params to number
parsedParams[param] = Number(params.row[key].params[param]);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fakeValue = (faker as any)[params.row[key].group][params.row[key].method](parsedParams);
fakeValue = (fakerCustom as any)[params.row[key].group][params.row[key].method](parsedParams);
}
else
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fakeValue = (faker as any)[params.row[key].group][params.row[key].method]();
fakeValue = (fakerCustom as any)[params.row[key].group][params.row[key].method]();
if (typeof fakeValue === 'string') {
if (params.row[key].length)

View File

@@ -1,7 +1,7 @@
import * as antares from 'common/interfaces/antares';
import mysql from 'mysql2/promise';
import * as pg from 'pg';
import SSH2Promise from 'ssh2-promise';
import SSH2Promise = require('@fabio286/ssh2-promise');
const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
// Remove comments, newlines and multiple spaces
@@ -16,7 +16,7 @@ const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
/**
* As Simple As Possible Query Builder Core
*/
export abstract class AntaresCore {
export abstract class BaseClient {
_client: antares.ClientCode;
protected _cUid: string
protected _params: mysql.ConnectionOptions | pg.ClientConfig | { databasePath: string; readonly: boolean};

View File

@@ -4,9 +4,9 @@ import * as antares from 'common/interfaces/antares';
import * as firebird from 'node-firebird';
import * as path from 'path';
import { AntaresCore } from '../AntaresCore';
import { BaseClient } from './BaseClient';
export class FirebirdSQLClient extends AntaresCore {
export class FirebirdSQLClient extends BaseClient {
private _schema?: string;
private _runningConnections: Map<string, number>;
private _connectionsToCommit: Map<string, firebird.Transaction>;

View File

@@ -1,12 +1,12 @@
import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/mysql';
import * as antares from 'common/interfaces/antares';
import * as mysql from 'mysql2/promise';
import { AntaresCore } from '../AntaresCore';
import SSH2Promise = require('ssh2-promise');
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import { BaseClient } from './BaseClient';
export class MySQLClient extends AntaresCore {
export class MySQLClient extends BaseClient {
private _schema?: string;
private _runningConnections: Map<string, number>;
private _connectionsToCommit: Map<string, mysql.Connection | mysql.PoolConnection>;
@@ -168,7 +168,10 @@ export class MySQLClient extends AntaresCore {
dbConfig.port = tunnel.localPort;
}
catch (err) {
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
throw err;
}
}
@@ -187,7 +190,10 @@ export class MySQLClient extends AntaresCore {
this._connection.end();
clearInterval(this._keepaliveTimer);
this._keepaliveTimer = undefined;
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
}
async getConnection () {
@@ -256,9 +262,12 @@ export class MySQLClient extends AntaresCore {
}
private async keepAlive () {
const connection = await (this._connection as mysql.Pool).getConnection();
await connection.ping();
connection.release();
try {
const connection = await (this._connection as mysql.Pool).getConnection();
await connection.ping();
connection.release();
}
catch (_) {}
}
use (schema: string) {

View File

@@ -1,13 +1,13 @@
import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/postgresql';
import * as antares from 'common/interfaces/antares';
import * as pg from 'pg';
import * as pgAst from 'pgsql-ast-parser';
import { AntaresCore } from '../AntaresCore';
import SSH2Promise = require('ssh2-promise');
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import { ConnectionOptions } from 'tls';
import { BaseClient } from './BaseClient';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function pgToString (value: any) {
return value.toString();
@@ -81,7 +81,7 @@ type builtinsTypes =
'JSONB' |
'REGNAMESPACE' |
'REGROLE';
export class PostgreSQLClient extends AntaresCore {
export class PostgreSQLClient extends BaseClient {
private _schema?: string;
private _runningConnections: Map<string, number>;
private _connectionsToCommit: Map<string, pg.Client | pg.PoolClient>;
@@ -180,7 +180,10 @@ export class PostgreSQLClient extends AntaresCore {
dbConfig.port = tunnel.localPort;
}
catch (err) {
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
throw err;
}
}
@@ -236,13 +239,19 @@ export class PostgreSQLClient extends AntaresCore {
this._connection.end();
clearInterval(this._keepaliveTimer);
this._keepaliveTimer = undefined;
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
}
private async keepAlive () {
const connection = await this._connection.connect() as pg.PoolClient;
await connection.query('SELECT 1+1');
connection.release();
try {
const connection = await this._connection.connect() as pg.PoolClient;
await connection.query('SELECT 1+1');
connection.release();
}
catch (_) {}
}
use (schema: string, connection?: pg.Client | pg.PoolClient) {

View File

@@ -3,9 +3,9 @@ import dataTypes from 'common/data-types/sqlite';
import { DATETIME, FLOAT, NUMBER, TIME } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares';
import { AntaresCore } from '../AntaresCore';
import { BaseClient } from './BaseClient';
export class SQLiteClient extends AntaresCore {
export class SQLiteClient extends BaseClient {
private _schema?: string;
private _connectionsToCommit: Map<string, sqlite.Database>;
protected _connection?: sqlite.Database;

View File

@@ -6,6 +6,7 @@ const isWindows = process.platform === 'win32';
const indexPath = path.resolve(__dirname, 'index.html').split(path.sep).join('/');
export function validateSender (frame: WebFrameMain) {
if (process.windowsStore) return true; // TEMP HOTFIX
const frameUrl = new URL(frame.url);
const prefix = isWindows ? 'file:///' : 'file://';
const framePath = frameUrl.href.replace(prefix, '');

View File

@@ -1,8 +1,8 @@
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import * as antares from 'common/interfaces/antares';
import { ImportOptions } from 'common/interfaces/importer';
import * as mysql from 'mysql2';
import * as pg from 'pg';
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import { MySQLClient } from '../libs/clients/MySQLClient';
import { PostgreSQLClient } from '../libs/clients/PostgreSQLClient';

View File

@@ -27,11 +27,7 @@
:title="t('general.cancel')"
@click="killTabQuery()"
>
<BaseIcon
class="mr-1"
icon-name="mdiWindowCLose"
:size="24"
/>
<BaseIcon icon-name="mdiWindowClose" :size="24" />
<span class="d-invisible pr-1">{{ t('general.run') }}</span>
</button>
<button

View File

@@ -3,7 +3,7 @@
ref="tableWrapper"
class="vscroll no-outline"
tabindex="0"
:style="{'height': resultsSize+'px'}"
:style="{ 'height': resultsSize + 'px' }"
@blur="deselectRows"
@focus="hasFocus = true"
@keyup.delete="showDeleteConfirmModal"
@@ -28,7 +28,7 @@
v-for="(result, index) in resultsWithRows"
:key="index"
class="tab-item"
:class="{'active': resultsetIndex === index}"
:class="{ 'active': resultsetIndex === index }"
@click="selectResultset(index)"
>
<a>{{ result.fields ? result.fields[0]?.table : '' }} ({{ result.rows.length }})</a>
@@ -57,7 +57,7 @@
<span>{{ field.alias || field.name }}</span>
<BaseIcon
v-if="isSortable && currentSort === field.name || currentSort === `${field.table}.${field.name}`"
:icon-name="currentSortDir === 'asc' ? 'mdiSortAscending':'mdiSortDescending'"
:icon-name="currentSortDir === 'asc' ? 'mdiSortAscending' : 'mdiSortDescending'"
:size="18"
class="sort-icon ml-1"
/>
@@ -90,7 +90,7 @@
:fields="fieldsObj"
:key-usage="keyUsage"
:element-type="elementType"
:class="{'selected': selectedRows.includes(row._antares_id)}"
:class="{ 'selected': selectedRows.includes(row._antares_id) }"
:selected="selectedRows.includes(row._antares_id)"
:selected-cell="selectedRows.length === 1 && selectedRows.includes(row._antares_id) ? selectedField : null"
@start-editing="isEditingRow = true"
@@ -163,7 +163,7 @@
<BaseSelect
v-model="sqlExportOptions.sqlInsertDivider"
class="form-select"
:options="[{value: 'bytes', label: 'KiB'}, {value: 'rows', label: t('database.row', 2)}]"
:options="[{ value: 'bytes', label: 'KiB' }, { value: 'rows', label: t('database.row', 2) }]"
/>
</div>
</div>
@@ -208,9 +208,9 @@
v-model="csvExportOptions.stringDelimiter"
class="form-select"
:options="[
{value: '', label: t('general.none')},
{value: 'single', label: t('general.singleQuote')},
{value: 'double', label: t('general.doubleQuote')}
{ value: '', label: t('general.none') },
{ value: 'single', label: t('general.singleQuote') },
{ value: 'double', label: t('general.doubleQuote') }
]"
/>
</div>
@@ -235,7 +235,10 @@
</label>
</div>
<div class="column col-7">
<label class="form-switch d-inline-block" @click.prevent="csvExportOptions.header = !csvExportOptions.header">
<label
class="form-switch d-inline-block"
@click.prevent="csvExportOptions.header = !csvExportOptions.header"
>
<input type="checkbox" :checked="csvExportOptions.header">
<i class="form-icon" />
</label>
@@ -249,10 +252,10 @@
<script setup lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import faker from '@faker-js/faker';
import { BLOB, DATE, DATETIME, LONG_TEXT, TEXT, TIME } from 'common/fieldTypes';
import { QueryResult, TableField } from 'common/interfaces/antares';
import { TableUpdateParams } from 'common/interfaces/tableApis';
import { fakerCustom } from 'common/libs/fakerCustom';
import { jsonToSqlInsert } from 'common/libs/sqlUtils';
import { uidGen } from 'common/libs/uidGen';
import * as json2php from 'json2php';
@@ -299,7 +302,7 @@ const emit = defineEmits([
'duplicate-row'
]);
const resultTable: Ref<Component & {updateWindow: () => void}> = ref(null);
const resultTable: Ref<Component & { updateWindow: () => void }> = ref(null);
const tableWrapper: Ref<HTMLDivElement> = ref(null);
const table: Ref<HTMLDivElement> = ref(null);
const resultsSize = ref(0);
@@ -377,7 +380,7 @@ const keyUsage = computed(() => resultsWithRows.value.length ? resultsWithRows.v
const fieldsObj = computed(() => {
if (sortedResults.value.length) {
const fieldsObj: {[key: string]: TableField} = {};
const fieldsObj: { [key: string]: TableField } = {};
for (const key in sortedResults.value[0]) {
if (key === '_antares_id') continue;
@@ -483,7 +486,7 @@ const resizeResults = () => {
const refreshScroller = () => resizeResults();
const updateField = (payload: { field: string; type: string; content: any }, row: {[key: string]: any}) => {
const updateField = (payload: { field: string; type: string; content: any }, row: { [key: string]: any }) => {
const orgRow: any = localResults.value.find((lr: any) => lr._antares_id === row._antares_id);
Object.keys(orgRow).forEach(key => { // remap the row
@@ -598,7 +601,7 @@ const copyRow = (format: string) => {
json: contentToCopy,
client: workspaceClient.value,
fields: fieldsObj.value as {
[key: string]: {type: string; datePrecision: number};
[key: string]: { type: string; datePrecision: number };
},
table: getTable(resultsetIndex.value)
});
@@ -670,28 +673,18 @@ const fillCell = (event: { name: string; group: string; type: string }) => {
datePrecision += i === 0 ? '.S' : 'S';
}
if (event.group === 'custom') {
if (event.type === 'time' && event.name === 'now')
fakeValue = moment().format(`HH:mm:ss${datePrecision}`);
else if (event.type === 'time' && event.name === 'random')
fakeValue = moment(faker.date.recent()).format(`HH:mm:ss${datePrecision}`);
else if (event.type === 'datetime' && event.name === 'now')
fakeValue = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
}
else {
fakeValue = (faker as any)[event.group][event.name]();
if (['string', 'number'].includes(typeof fakeValue)) {
if (typeof fakeValue === 'number')
fakeValue = String(fakeValue);
fakeValue = (fakerCustom as any)[event.group][event.name]();
if (['string', 'number'].includes(typeof fakeValue)) {
if (typeof fakeValue === 'number')
fakeValue = String(fakeValue);
if (selectedCell.value.length)
fakeValue = fakeValue.substring(0, selectedCell.value.length < 1024 ? Number(selectedCell.value.length) : 1024);
}
else if ([...DATE, ...DATETIME].includes(selectedCell.value.type))
fakeValue = moment(fakeValue).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
else if (TIME.includes(selectedCell.value.type))
fakeValue = moment(fakeValue).format(`HH:mm:ss${datePrecision}`);
if (selectedCell.value.length)
fakeValue = fakeValue.substring(0, selectedCell.value.length < 1024 ? Number(selectedCell.value.length) : 1024);
}
else if ([...DATE, ...DATETIME].includes(selectedCell.value.type))
fakeValue = moment(fakeValue).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
else if (TIME.includes(selectedCell.value.type))
fakeValue = moment(fakeValue).format(`HH:mm:ss${datePrecision}`);
const params = {
primary: primaryField.value?.name,
@@ -859,12 +852,12 @@ const downloadTable = (format: 'csv' | 'json' | 'sql' | 'php', table: string, po
type: format,
content: rows,
fields: JSON.parse(JSON.stringify(fieldsObj.value)) as {
[key: string]: {type: string; datePrecision: number};
[key: string]: { type: string; datePrecision: number };
},
client: workspaceClient.value,
table,
sqlOptions: popup ? { ...sqlExportOptions.value }: null,
csvOptions: popup ? { ...csvExportOptions.value }: null
sqlOptions: popup ? { ...sqlExportOptions.value } : null,
csvOptions: popup ? { ...csvExportOptions.value } : null
});
};
@@ -897,7 +890,7 @@ const onKey = async (e: KeyboardEvent) => {
if (!(e.ctrlKey || e.metaKey) && (e.code.includes('Arrow') || e.code === 'Tab') && sortedResults.value.length > 0 && !e.altKey) {
e.preventDefault();
const aviableFields= Object.keys(sortedResults.value[0]).slice(0, -1); // removes _antares_id
const aviableFields = Object.keys(sortedResults.value[0]).slice(0, -1); // removes _antares_id
if (!selectedField.value)
selectedField.value = aviableFields[0];
@@ -914,8 +907,8 @@ const onKey = async (e: KeyboardEvent) => {
nextIndex = selectedIndex + 1;
nextFieldIndex = selectedFieldIndex;
if (nextIndex > sortedResults.value.length -1)
nextIndex = sortedResults.value.length -1;
if (nextIndex > sortedResults.value.length - 1)
nextIndex = sortedResults.value.length - 1;
break;
case 'ArrowUp':
@@ -931,7 +924,7 @@ const onKey = async (e: KeyboardEvent) => {
nextIndex = selectedIndex;
nextFieldIndex = selectedFieldIndex + 1;
if (nextFieldIndex > aviableFields.length -1)
if (nextFieldIndex > aviableFields.length - 1)
nextFieldIndex = 0;
break;
@@ -941,7 +934,7 @@ const onKey = async (e: KeyboardEvent) => {
nextFieldIndex = selectedFieldIndex - 1;
if (nextFieldIndex < 0)
nextFieldIndex = aviableFields.length -1;
nextFieldIndex = aviableFields.length - 1;
break;
@@ -950,11 +943,11 @@ const onKey = async (e: KeyboardEvent) => {
if (e.shiftKey) {
nextFieldIndex = selectedFieldIndex - 1;
if (nextFieldIndex < 0)
nextFieldIndex = aviableFields.length -1;
nextFieldIndex = aviableFields.length - 1;
}
else {
nextFieldIndex = selectedFieldIndex + 1;
if (nextFieldIndex > aviableFields.length -1)
if (nextFieldIndex > aviableFields.length - 1)
nextFieldIndex = 0;
}
}
@@ -1049,32 +1042,33 @@ onUnmounted(() => {
<style lang="scss" scoped>
.vscroll {
height: 1000px;
overflow: auto;
overflow-anchor: none;
height: 1000px;
overflow: auto;
overflow-anchor: none;
}
.column-resizable {
&:hover,
&:active {
resize: horizontal;
overflow: hidden;
}
&:hover,
&:active {
resize: horizontal;
overflow: hidden;
}
}
.table-column-title {
display: flex;
align-items: center;
display: flex;
align-items: center;
}
.sort-icon {
font-size: 0.7rem;
line-height: 1;
margin-left: 0.2rem;
font-size: 0.7rem;
line-height: 1;
margin-left: 0.2rem;
}
.result-tabs {
background: transparent !important;
margin: 0;
background: transparent !important;
margin: 0;
}
</style>

View File

@@ -197,13 +197,13 @@ const fakerMethods = {
{ name: 'amount', group: 'finance' }
],
datetime: [
{ name: 'now', group: 'custom' },
{ name: 'now', group: 'date' },
{ name: 'past', group: 'date' },
{ name: 'future', group: 'date' }
],
time: [
{ name: 'now', group: 'custom' },
{ name: 'random', group: 'custom' }
{ name: 'now', group: 'time' },
{ name: 'random', group: 'time' }
],
uuid: [
{ name: 'uuid', group: 'random' }

View File

@@ -580,7 +580,7 @@ const onKey = (e: KeyboardEvent) => {
if (!editingField.value && e.key === 'Enter')
return editON(props.selectedCell);
if (editingField.value && e.key === 'Enter' && !isBaseSelectField.value)
if (editingField.value && e.key === 'Enter' && !isBaseSelectField.value && !isTextareaEditor.value)
return editOFF();
if (editingField.value && e.key === 'Escape') {

View File

@@ -183,6 +183,7 @@
<WorkspaceTabTableFilters
v-if="isSearch"
:fields="fields"
:is-quering="isQuering"
:conn-client="connection.client"
@filter="updateFilters"
@filter-change="onFilterChange"

View File

@@ -9,6 +9,7 @@
<input
v-model="row.active"
type="checkbox"
:disabled="isQuering"
@change="doFilter"
><i class="form-icon" />
</label>
@@ -18,11 +19,13 @@
:options="fields"
option-track-by="name"
option-label="name"
:disabled="isQuering"
/>
<BaseSelect
v-model="row.op"
class="form-select ml-2 col-auto select-sm"
:options="operators"
:disabled="isQuering"
/>
<div class="workspace-table-filters-row-value ml-2">
<input
@@ -30,12 +33,14 @@
v-model="row.value"
type="text"
class="form-input input-sm"
:disabled="isQuering"
>
<input
v-if="row.op === 'BETWEEN'"
v-model="row.value2"
type="text"
class="form-input ml-2 input-sm"
:disabled="isQuering"
>
</div>
<button
@@ -87,15 +92,14 @@ const { t } = useI18n();
const props = defineProps({
fields: Array as Prop<TableField[]>,
connClient: String as Prop<ClientCode>
connClient: String as Prop<ClientCode>,
isQuering: Boolean
});
const emit = defineEmits(['filter-change', 'filter']);
const rows = ref([]);
const operators = ref<TableFilterOperator[]>([
'=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'
]);
const operators: TableFilterOperator[] = customizations[props.connClient].operators;
const clientCustomizations = computed(() => customizations[props.connClient]);
@@ -122,7 +126,7 @@ const createClausole = (filter: TableFilterClausole) => {
const { elementsWrapper: ew, stringsWrapper: sw } = clientCustomizations.value;
let value;
if (isNumeric) {
if (isNumeric && !['IN', 'NOT IN', 'RLIKE', 'NOT RLIKE'].includes(filter.op)) {
if (isNaN(Number(filter.value)))
filter.value = '';
if (isNaN(Number(filter.value2)))
@@ -145,7 +149,7 @@ const createClausole = (filter: TableFilterClausole) => {
val = val.trim();
return isNumeric ? val : `${sw}${val}${sw}`;
}).join(',');
value = `(${filter.value})`;
value = `(${value})`;
break;
case 'IS NULL':
case 'IS NOT NULL':

View File

@@ -4,109 +4,109 @@ declare module '@/App.vue';
declare module 'v-mask';
declare module 'json2php';
declare module 'vuedraggable' {// <- to export as default
const draggableComponent: import('vue').DefineComponent<{
list: {
type: ArrayConstructor;
required: boolean;
default: any;
};
modelValue: {
type: ArrayConstructor;
required: boolean;
default: any;
};
itemKey: {
type: (FunctionConstructor | StringConstructor)[];
required: boolean;
};
clone: {
type: FunctionConstructor;
default: (original: any) => any;
};
tag: {
type: StringConstructor;
default: string;
};
move: {
type: FunctionConstructor;
default: any;
};
componentData: {
type: ObjectConstructor;
required: boolean;
default: any;
};
const draggableComponent: import('vue').DefineComponent<{
list: {
type: ArrayConstructor;
required: boolean;
default: any;
};
modelValue: {
type: ArrayConstructor;
required: boolean;
default: any;
};
itemKey: {
type: (FunctionConstructor | StringConstructor)[];
required: boolean;
};
clone: {
type: FunctionConstructor;
default: (original: any) => any;
};
tag: {
type: StringConstructor;
default: string;
};
move: {
type: FunctionConstructor;
default: any;
};
componentData: {
type: ObjectConstructor;
required: boolean;
default: any;
};
}, unknown, {
error: boolean;
error: boolean;
}, {
realList(): any;
getKey(): any;
realList(): any;
getKey(): any;
}, {
getUnderlyingVm(domElement: any): any;
getUnderlyingPotencialDraggableComponent(htmElement: any): any;
emitChanges(evt: any): void;
alterList(onList: any): void;
spliceList(): void;
updatePosition(oldIndex: any, newIndex: any): void;
getRelatedContextFromMoveEvent({ to, related }: {
to: any;
related: any;
}): any;
getVmIndexFromDomIndex(domIndex: any): any;
onDragStart(evt: any): void;
onDragAdd(evt: any): void;
onDragRemove(evt: any): void;
onDragUpdate(evt: any): void;
computeFutureIndex(relatedContext: any, evt: any): any;
onDragMove(evt: any, originalEvent: any): any;
onDragEnd(): void;
getUnderlyingVm(domElement: any): any;
getUnderlyingPotencialDraggableComponent(htmElement: any): any;
emitChanges(evt: any): void;
alterList(onList: any): void;
spliceList(): void;
updatePosition(oldIndex: any, newIndex: any): void;
getRelatedContextFromMoveEvent({ to, related }: {
to: any;
related: any;
}): any;
getVmIndexFromDomIndex(domIndex: any): any;
onDragStart(evt: any): void;
onDragAdd(evt: any): void;
onDragRemove(evt: any): void;
onDragUpdate(evt: any): void;
computeFutureIndex(relatedContext: any, evt: any): any;
onDragMove(evt: any, originalEvent: any): any;
onDragEnd(): void;
}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, any[], any, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<{
move: Function;
tag: string;
clone: Function;
list: unknown[];
modelValue: unknown[];
componentData: Record<string, any>;
move: Function;
tag: string;
clone: Function;
list: unknown[];
modelValue: unknown[];
componentData: Record<string, any>;
} & {
itemKey?: string | Function;
itemKey?: string | Function;
}>, {
move: Function;
tag: string;
clone: Function;
list: unknown[];
modelValue: unknown[];
componentData: Record<string, any>;
move: Function;
tag: string;
clone: Function;
list: unknown[];
modelValue: unknown[];
componentData: Record<string, any>;
}>;
export = draggableComponent;
export = draggableComponent;
}
declare const SvgIcon: import('vue').DefineComponent<{
type: {
type: StringConstructor;
default: string;
};
path: {
type: StringConstructor;
default: string;
};
size: {
type: NumberConstructor;
optional: boolean;
};
viewbox: {
type: StringConstructor;
optional: boolean;
};
flip: {
type: StringConstructor;
optional: boolean;
};
rotate: {
type: NumberConstructor;
optional: boolean;
};
}>;
type: {
type: StringConstructor;
default: string;
};
path: {
type: StringConstructor;
default: string;
};
size: {
type: NumberConstructor;
optional: boolean;
};
viewbox: {
type: StringConstructor;
optional: boolean;
};
flip: {
type: StringConstructor;
optional: boolean;
};
rotate: {
type: NumberConstructor;
optional: boolean;
};
}>;
declare module '@jamescoyle/vue-icon' {
export default SvgIcon;
}
declare module '@jamescoyle/vue-icon' {
export default SvgIcon;
}