mirror of
https://github.com/frappe/books.git
synced 2024-12-22 10:58:59 +00:00
chore: enable typescript eslint rules
- refactor code to satisfy rules (batch 1) - use ensuredir in build to prevent ENOENT
This commit is contained in:
parent
8a1392e533
commit
4415c04a88
31
.eslintrc.js
31
.eslintrc.js
@ -1,23 +1,32 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
|
browser: true,
|
||||||
|
es2020: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-console': 'warn',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-debugger': 'warn',
|
||||||
'arrow-body-style': 'off',
|
'arrow-body-style': 'off',
|
||||||
'prefer-arrow-callback': 'off',
|
'prefer-arrow-callback': 'warn',
|
||||||
'vue/no-mutating-props': 'off',
|
'vue/no-mutating-props': 'off',
|
||||||
'vue/multi-word-component-names': 'off',
|
'vue/multi-word-component-names': 'off',
|
||||||
'vue/no-useless-template-attributes': 'off',
|
'vue/no-useless-template-attributes': 'off',
|
||||||
|
'vue/one-component-per-file': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/no-floating-promises': 'off',
|
||||||
|
'@typescript-eslint/no-misused-promises': 'off',
|
||||||
},
|
},
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: { project: true, tsconfigRootDir: __dirname },
|
||||||
parser: '@typescript-eslint/parser',
|
plugins: ['@typescript-eslint'],
|
||||||
},
|
extends: [
|
||||||
|
'plugin:vue/vue3-strongly-recommended',
|
||||||
extends: ['plugin:vue/vue3-essential', '@vue/typescript'],
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['.eslintrc.js'],
|
||||||
};
|
};
|
||||||
|
@ -24,9 +24,9 @@ await packageApp();
|
|||||||
|
|
||||||
function updatePaths() {
|
function updatePaths() {
|
||||||
fs.removeSync(buildDirPath);
|
fs.removeSync(buildDirPath);
|
||||||
fs.mkdirSync(buildDirPath);
|
fs.ensureDirSync(buildDirPath);
|
||||||
fs.removeSync(packageDirPath);
|
fs.removeSync(packageDirPath);
|
||||||
fs.mkdirSync(packageDirPath);
|
fs.ensureDirSync(packageDirPath);
|
||||||
fs.ensureDirSync(path.join(buildDirPath, 'node_modules'));
|
fs.ensureDirSync(path.join(buildDirPath, 'node_modules'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,10 @@ export class AuthHandler {
|
|||||||
return { ...this.#config };
|
return { ...this.#config };
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {}
|
init() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async login(email: string, password: string) {
|
async login(email: string, password: string) {
|
||||||
if (email === 'Administrator') {
|
if (email === 'Administrator') {
|
||||||
this.#session.user = 'Administrator';
|
this.#session.user = 'Administrator';
|
||||||
@ -107,8 +110,6 @@ export class AuthHandler {
|
|||||||
// TODO: Implement this with auth flow
|
// TODO: Implement this with auth flow
|
||||||
}
|
}
|
||||||
|
|
||||||
async purgeCache() {}
|
|
||||||
|
|
||||||
#getServerURL() {
|
#getServerURL() {
|
||||||
return this.#config.serverURL || '';
|
return this.#config.serverURL || '';
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export class DocHandler {
|
|||||||
this.observer = new Observable();
|
this.observer = new Observable();
|
||||||
}
|
}
|
||||||
|
|
||||||
async purgeCache() {
|
purgeCache() {
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,10 +82,10 @@ export class DocHandler {
|
|||||||
getNewDoc(
|
getNewDoc(
|
||||||
schemaName: string,
|
schemaName: string,
|
||||||
data: DocValueMap | RawValueMap = {},
|
data: DocValueMap | RawValueMap = {},
|
||||||
cacheDoc: boolean = true,
|
cacheDoc = true,
|
||||||
schema?: Schema,
|
schema?: Schema,
|
||||||
Model?: typeof Doc,
|
Model?: typeof Doc,
|
||||||
isRawValueMap: boolean = true
|
isRawValueMap = true
|
||||||
): Doc {
|
): Doc {
|
||||||
if (!this.models[schemaName] && Model) {
|
if (!this.models[schemaName] && Model) {
|
||||||
this.models[schemaName] = Model;
|
this.models[schemaName] = Model;
|
||||||
@ -153,7 +153,7 @@ export class DocHandler {
|
|||||||
|
|
||||||
// propagate change to `docs`
|
// propagate change to `docs`
|
||||||
doc.on('change', (params: unknown) => {
|
doc.on('change', (params: unknown) => {
|
||||||
this.docs!.trigger('change', params);
|
this.docs.trigger('change', params);
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.on('afterSync', () => {
|
doc.on('afterSync', () => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Doc } from 'fyo/model/doc';
|
import type { Doc } from 'fyo/model/doc';
|
||||||
import { Money } from 'pesa';
|
import type { Money } from 'pesa';
|
||||||
import { RawValue } from 'schemas/types';
|
import type { RawValue } from 'schemas/types';
|
||||||
import { AuthDemuxBase } from 'utils/auth/types';
|
import type { AuthDemuxBase } from 'utils/auth/types';
|
||||||
import { DatabaseDemuxBase } from 'utils/db/types';
|
import type { DatabaseDemuxBase } from 'utils/db/types';
|
||||||
|
|
||||||
export type Attachment = { name: string; type: string; data: string };
|
export type Attachment = { name: string; type: string; data: string };
|
||||||
export type DocValue =
|
export type DocValue =
|
||||||
@ -31,12 +31,12 @@ export type DatabaseDemuxConstructor = new (
|
|||||||
|
|
||||||
export type AuthDemuxConstructor = new (isElectron?: boolean) => AuthDemuxBase;
|
export type AuthDemuxConstructor = new (isElectron?: boolean) => AuthDemuxBase;
|
||||||
|
|
||||||
export enum ConfigKeys {
|
export type ConfigMap = {
|
||||||
Files = 'files',
|
files: ConfigFile[];
|
||||||
LastSelectedFilePath = 'lastSelectedFilePath',
|
lastSelectedFilePath: null | string;
|
||||||
Language = 'language',
|
language: string
|
||||||
DeviceId = 'deviceId',
|
deviceId: string
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface ConfigFile {
|
export interface ConfigFile {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import type Store from 'electron-store';
|
import type Store from 'electron-store';
|
||||||
|
import { ConfigMap } from 'fyo/core/types';
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
config: Map<string, unknown> | Store;
|
config: Map<string, unknown> | Store;
|
||||||
constructor(isElectron: boolean) {
|
constructor(isElectron: boolean) {
|
||||||
this.config = new Map();
|
this.config = new Map();
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
const Config: typeof Store = require('electron-store');
|
const Config = require('electron-store') as typeof Store;
|
||||||
this.config = new Config();
|
this.config = new Config();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,15 +24,19 @@ export class Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get(key: string, defaultValue?: unknown): unknown {
|
get<K extends keyof ConfigMap>(
|
||||||
return this.config.get(key) ?? defaultValue;
|
key: K,
|
||||||
|
defaultValue?: ConfigMap[K]
|
||||||
|
): ConfigMap[K] | undefined {
|
||||||
|
const value = this.config.get(key) as ConfigMap[K] | undefined;
|
||||||
|
return value ?? defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
set(key: string, value: unknown) {
|
set<K extends keyof ConfigMap>(key: K, value: ConfigMap[K]) {
|
||||||
this.config.set(key, value);
|
this.config.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(key: string) {
|
delete(key: keyof ConfigMap) {
|
||||||
this.config.delete(key);
|
this.config.delete(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
fyo/index.ts
17
fyo/index.ts
@ -35,7 +35,7 @@ export class Fyo {
|
|||||||
doc: DocHandler;
|
doc: DocHandler;
|
||||||
db: DatabaseHandler;
|
db: DatabaseHandler;
|
||||||
|
|
||||||
_initialized: boolean = false;
|
_initialized = false;
|
||||||
|
|
||||||
errorLog: ErrorLog[] = [];
|
errorLog: ErrorLog[] = [];
|
||||||
temp?: Record<string, unknown>;
|
temp?: Record<string, unknown>;
|
||||||
@ -94,7 +94,7 @@ export class Fyo {
|
|||||||
return format(value, field, doc ?? null, this);
|
return format(value, field, doc ?? null, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setIsElectron() {
|
setIsElectron() {
|
||||||
try {
|
try {
|
||||||
this.isElectron = Boolean(require('electron'));
|
this.isElectron = Boolean(require('electron'));
|
||||||
} catch {
|
} catch {
|
||||||
@ -105,7 +105,7 @@ export class Fyo {
|
|||||||
async initializeAndRegister(
|
async initializeAndRegister(
|
||||||
models: ModelMap = {},
|
models: ModelMap = {},
|
||||||
regionalModels: ModelMap = {},
|
regionalModels: ModelMap = {},
|
||||||
force: boolean = false
|
force = false
|
||||||
) {
|
) {
|
||||||
if (this._initialized && !force) return;
|
if (this._initialized && !force) return;
|
||||||
|
|
||||||
@ -121,8 +121,8 @@ export class Fyo {
|
|||||||
// temp params while calling routes
|
// temp params while calling routes
|
||||||
this.temp = {};
|
this.temp = {};
|
||||||
|
|
||||||
await this.doc.init();
|
this.doc.init();
|
||||||
await this.auth.init();
|
this.auth.init();
|
||||||
await this.db.init();
|
await this.db.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,14 +189,14 @@ export class Fyo {
|
|||||||
let value: DocValue | Doc[];
|
let value: DocValue | Doc[];
|
||||||
try {
|
try {
|
||||||
doc = await this.doc.getDoc(schemaName, name);
|
doc = await this.doc.getDoc(schemaName, name);
|
||||||
value = doc.get(fieldname!);
|
value = doc.get(fieldname);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
value = undefined;
|
value = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === undefined && schemaName === name) {
|
if (value === undefined && schemaName === name) {
|
||||||
const sv = await this.db.getSingleValues({
|
const sv = await this.db.getSingleValues({
|
||||||
fieldname: fieldname!,
|
fieldname: fieldname,
|
||||||
parent: schemaName,
|
parent: schemaName,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -221,8 +221,7 @@ export class Fyo {
|
|||||||
this.errorLog = [];
|
this.errorLog = [];
|
||||||
this.temp = {};
|
this.temp = {};
|
||||||
await this.db.purgeCache();
|
await this.db.purgeCache();
|
||||||
await this.auth.purgeCache();
|
this.doc.purgeCache();
|
||||||
await this.doc.purgeCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
store = {
|
store = {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Fyo } from 'fyo';
|
import { Fyo } from 'fyo';
|
||||||
import { ConfigKeys } from 'fyo/core/types';
|
|
||||||
import { Noun, Telemetry, Verb } from './types';
|
import { Noun, Telemetry, Verb } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,8 +27,8 @@ import { Noun, Telemetry, Verb } from './types';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export class TelemetryManager {
|
export class TelemetryManager {
|
||||||
#url: string = '';
|
#url = '';
|
||||||
#token: string = '';
|
#token = '';
|
||||||
#started = false;
|
#started = false;
|
||||||
fyo: Fyo;
|
fyo: Fyo;
|
||||||
|
|
||||||
@ -108,16 +107,12 @@ export class TelemetryManager {
|
|||||||
noun: Noun,
|
noun: Noun,
|
||||||
more?: Record<string, unknown>
|
more?: Record<string, unknown>
|
||||||
): Telemetry {
|
): Telemetry {
|
||||||
const countryCode = this.fyo.singles.SystemSettings?.countryCode as
|
const countryCode = this.fyo.singles.SystemSettings?.countryCode;
|
||||||
| string
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
country: countryCode ?? '',
|
country: countryCode ?? '',
|
||||||
language: this.fyo.store.language,
|
language: this.fyo.store.language,
|
||||||
deviceId:
|
deviceId:
|
||||||
this.fyo.store.deviceId ||
|
this.fyo.store.deviceId || (this.fyo.config.get('deviceId') ?? '-'),
|
||||||
(this.fyo.config.get(ConfigKeys.DeviceId) as string),
|
|
||||||
instanceId: this.fyo.store.instanceId,
|
instanceId: this.fyo.store.instanceId,
|
||||||
version: this.fyo.store.appVersion,
|
version: this.fyo.store.appVersion,
|
||||||
openCount: this.fyo.store.openCount,
|
openCount: this.fyo.store.openCount,
|
||||||
|
9
main.ts
9
main.ts
@ -1,3 +1,4 @@
|
|||||||
|
// eslint-disable-next-line
|
||||||
require('source-map-support').install({
|
require('source-map-support').install({
|
||||||
handleUncaughtException: false,
|
handleUncaughtException: false,
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
@ -22,10 +23,10 @@ import registerIpcMainMessageListeners from './main/registerIpcMainMessageListen
|
|||||||
import registerProcessListeners from './main/registerProcessListeners';
|
import registerProcessListeners from './main/registerProcessListeners';
|
||||||
|
|
||||||
export class Main {
|
export class Main {
|
||||||
title: string = 'Frappe Books';
|
title = 'Frappe Books';
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
||||||
winURL: string = '';
|
winURL = '';
|
||||||
checkedForUpdate = false;
|
checkedForUpdate = false;
|
||||||
mainWindow: BrowserWindow | null = null;
|
mainWindow: BrowserWindow | null = null;
|
||||||
|
|
||||||
@ -130,9 +131,9 @@ export class Main {
|
|||||||
this.registerAppProtocol();
|
this.registerAppProtocol();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mainWindow!.loadURL(this.winURL);
|
this.mainWindow.loadURL(this.winURL);
|
||||||
if (this.isDevelopment && !this.isTest) {
|
if (this.isDevelopment && !this.isTest) {
|
||||||
this.mainWindow!.webContents.openDevTools();
|
this.mainWindow.webContents.openDevTools();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setMainWindowListeners();
|
this.setMainWindowListeners();
|
||||||
|
@ -4,6 +4,7 @@ import fetch from 'node-fetch';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { Creds } from 'utils/types';
|
import { Creds } from 'utils/types';
|
||||||
import { rendererLog } from './helpers';
|
import { rendererLog } from './helpers';
|
||||||
|
import type { Main } from 'main';
|
||||||
|
|
||||||
export function getUrlAndTokenString(): Creds {
|
export function getUrlAndTokenString(): Creds {
|
||||||
const inProduction = app.isPackaged;
|
const inProduction = app.isPackaged;
|
||||||
@ -42,7 +43,7 @@ export function getUrlAndTokenString(): Creds {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendError(body: string) {
|
export async function sendError(body: string, main: Main) {
|
||||||
const { errorLogUrl, tokenString } = getUrlAndTokenString();
|
const { errorLogUrl, tokenString } = getUrlAndTokenString();
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: tokenString,
|
Authorization: tokenString,
|
||||||
@ -51,6 +52,6 @@ export async function sendError(body: string) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await fetch(errorLogUrl, { method: 'POST', headers, body }).catch((err) => {
|
await fetch(errorLogUrl, { method: 'POST', headers, body }).catch((err) => {
|
||||||
rendererLog(err);
|
rendererLog(main, err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,7 @@ import fs from 'fs/promises';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { parseCSV } from 'utils/csvParser';
|
import { parseCSV } from 'utils/csvParser';
|
||||||
import { LanguageMap } from 'utils/types';
|
import { LanguageMap } from 'utils/types';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
const fetch = require('node-fetch').default;
|
|
||||||
|
|
||||||
const VALENTINES_DAY = 1644796800000;
|
const VALENTINES_DAY = 1644796800000;
|
||||||
|
|
||||||
@ -100,7 +99,7 @@ async function fetchContentsFromApi(code: string) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resJson = await res.json();
|
const resJson = (await res.json()) as { content: string };
|
||||||
return Buffer.from(resJson.content, 'base64').toString();
|
return Buffer.from(resJson.content, 'base64').toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +137,9 @@ async function getLastUpdated(code: string): Promise<Date> {
|
|||||||
return new Date(VALENTINES_DAY);
|
return new Date(VALENTINES_DAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resJson = await res.json();
|
const resJson = (await res.json()) as {
|
||||||
|
commit: { author: { date: string } };
|
||||||
|
}[];
|
||||||
try {
|
try {
|
||||||
return new Date(resJson[0].commit.author.date);
|
return new Date(resJson[0].commit.author.date);
|
||||||
} catch {
|
} catch {
|
||||||
@ -187,7 +188,7 @@ async function storeFile(code: string, contents: string) {
|
|||||||
|
|
||||||
async function errorHandledFetch(url: string) {
|
async function errorHandledFetch(url: string) {
|
||||||
try {
|
try {
|
||||||
return (await fetch(url)) as Response;
|
return await fetch(url);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { constants } from 'fs';
|
import { constants } from 'fs';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import { ConfigFile, ConfigKeys } from 'fyo/core/types';
|
import { ConfigFile } from 'fyo/core/types';
|
||||||
import { Main } from 'main';
|
import { Main } from 'main';
|
||||||
import config from 'utils/config';
|
import config from 'utils/config';
|
||||||
import { BackendResponse } from 'utils/ipc/types';
|
import { BackendResponse } from 'utils/ipc/types';
|
||||||
@ -8,7 +8,7 @@ import { IPC_CHANNELS } from 'utils/messages';
|
|||||||
import type { ConfigFilesWithModified } from 'utils/types';
|
import type { ConfigFilesWithModified } from 'utils/types';
|
||||||
|
|
||||||
export async function setAndGetCleanedConfigFiles() {
|
export async function setAndGetCleanedConfigFiles() {
|
||||||
const files = config.get(ConfigKeys.Files, []) as ConfigFile[];
|
const files = config.get('files', []);
|
||||||
|
|
||||||
const cleanedFileMap: Map<string, ConfigFile> = new Map();
|
const cleanedFileMap: Map<string, ConfigFile> = new Map();
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@ -30,7 +30,7 @@ export async function setAndGetCleanedConfigFiles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cleanedFiles = Array.from(cleanedFileMap.values());
|
const cleanedFiles = Array.from(cleanedFileMap.values());
|
||||||
config.set(ConfigKeys.Files, cleanedFiles);
|
config.set('files', cleanedFiles);
|
||||||
return cleanedFiles;
|
return cleanedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,9 @@ export async function getConfigFilesWithModified(files: ConfigFile[]) {
|
|||||||
return filesWithModified;
|
return filesWithModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getErrorHandledReponse(func: () => Promise<unknown>) {
|
export async function getErrorHandledReponse(
|
||||||
|
func: () => Promise<unknown> | unknown
|
||||||
|
) {
|
||||||
const response: BackendResponse = {};
|
const response: BackendResponse = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { app, dialog, ipcMain } from 'electron';
|
import {
|
||||||
|
MessageBoxOptions,
|
||||||
|
OpenDialogOptions,
|
||||||
|
SaveDialogOptions,
|
||||||
|
app,
|
||||||
|
dialog,
|
||||||
|
ipcMain,
|
||||||
|
} from 'electron';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -20,39 +27,60 @@ import {
|
|||||||
import { saveHtmlAsPdf } from './saveHtmlAsPdf';
|
import { saveHtmlAsPdf } from './saveHtmlAsPdf';
|
||||||
|
|
||||||
export default function registerIpcMainActionListeners(main: Main) {
|
export default function registerIpcMainActionListeners(main: Main) {
|
||||||
ipcMain.handle(IPC_ACTIONS.GET_OPEN_FILEPATH, async (event, options) => {
|
ipcMain.handle(
|
||||||
return await dialog.showOpenDialog(main.mainWindow!, options);
|
IPC_ACTIONS.GET_OPEN_FILEPATH,
|
||||||
});
|
async (_, options: OpenDialogOptions) => {
|
||||||
|
return await dialog.showOpenDialog(main.mainWindow!, options);
|
||||||
ipcMain.handle(IPC_ACTIONS.GET_SAVE_FILEPATH, async (event, options) => {
|
|
||||||
return await dialog.showSaveDialog(main.mainWindow!, options);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.GET_DIALOG_RESPONSE, async (event, options) => {
|
|
||||||
if (main.isDevelopment || main.isLinux) {
|
|
||||||
Object.assign(options, { icon: main.icon });
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return await dialog.showMessageBox(main.mainWindow!, options);
|
ipcMain.handle(
|
||||||
});
|
IPC_ACTIONS.GET_SAVE_FILEPATH,
|
||||||
|
async (_, options: SaveDialogOptions) => {
|
||||||
|
return await dialog.showSaveDialog(main.mainWindow!, options);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.SHOW_ERROR, async (event, { title, content }) => {
|
ipcMain.handle(
|
||||||
return await dialog.showErrorBox(title, content);
|
IPC_ACTIONS.GET_DIALOG_RESPONSE,
|
||||||
});
|
async (_, options: MessageBoxOptions) => {
|
||||||
|
if (main.isDevelopment || main.isLinux) {
|
||||||
|
Object.assign(options, { icon: main.icon });
|
||||||
|
}
|
||||||
|
|
||||||
|
return await dialog.showMessageBox(main.mainWindow!, options);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
IPC_ACTIONS.SHOW_ERROR,
|
||||||
|
(_, { title, content }: { title: string; content: string }) => {
|
||||||
|
return dialog.showErrorBox(title, content);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
IPC_ACTIONS.SAVE_HTML_AS_PDF,
|
IPC_ACTIONS.SAVE_HTML_AS_PDF,
|
||||||
async (event, html, savePath, width: number, height: number) => {
|
async (
|
||||||
|
_,
|
||||||
|
html: string,
|
||||||
|
savePath: string,
|
||||||
|
width: number,
|
||||||
|
height: number
|
||||||
|
) => {
|
||||||
return await saveHtmlAsPdf(html, savePath, app, width, height);
|
return await saveHtmlAsPdf(html, savePath, app, width, height);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.SAVE_DATA, async (event, data, savePath) => {
|
ipcMain.handle(
|
||||||
return await fs.writeFile(savePath, data, { encoding: 'utf-8' });
|
IPC_ACTIONS.SAVE_DATA,
|
||||||
});
|
async (_, data: string, savePath: string) => {
|
||||||
|
return await fs.writeFile(savePath, data, { encoding: 'utf-8' });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.SEND_ERROR, (event, bodyJson) => {
|
ipcMain.handle(IPC_ACTIONS.SEND_ERROR, (_, bodyJson: string) => {
|
||||||
sendError(bodyJson);
|
sendError(bodyJson, main);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.CHECK_FOR_UPDATES, async () => {
|
ipcMain.handle(IPC_ACTIONS.CHECK_FOR_UPDATES, async () => {
|
||||||
@ -72,7 +100,7 @@ export default function registerIpcMainActionListeners(main: Main) {
|
|||||||
main.checkedForUpdate = true;
|
main.checkedForUpdate = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.GET_LANGUAGE_MAP, async (event, code) => {
|
ipcMain.handle(IPC_ACTIONS.GET_LANGUAGE_MAP, async (_, code: string) => {
|
||||||
const obj = { languageMap: {}, success: true, message: '' };
|
const obj = { languageMap: {}, success: true, message: '' };
|
||||||
try {
|
try {
|
||||||
obj.languageMap = await getLanguageMap(code);
|
obj.languageMap = await getLanguageMap(code);
|
||||||
@ -117,20 +145,20 @@ export default function registerIpcMainActionListeners(main: Main) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.GET_CREDS, async (event) => {
|
ipcMain.handle(IPC_ACTIONS.GET_CREDS, () => {
|
||||||
return getUrlAndTokenString();
|
return getUrlAndTokenString();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.DELETE_FILE, async (_, filePath) => {
|
ipcMain.handle(IPC_ACTIONS.DELETE_FILE, async (_, filePath: string) => {
|
||||||
return getErrorHandledReponse(async () => await fs.unlink(filePath));
|
return getErrorHandledReponse(async () => await fs.unlink(filePath));
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.GET_DB_LIST, async (_) => {
|
ipcMain.handle(IPC_ACTIONS.GET_DB_LIST, async () => {
|
||||||
const files = await setAndGetCleanedConfigFiles();
|
const files = await setAndGetCleanedConfigFiles();
|
||||||
return await getConfigFilesWithModified(files);
|
return await getConfigFilesWithModified(files);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.GET_ENV, async (_) => {
|
ipcMain.handle(IPC_ACTIONS.GET_ENV, () => {
|
||||||
return {
|
return {
|
||||||
isDevelopment: main.isDevelopment,
|
isDevelopment: main.isDevelopment,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
@ -149,7 +177,7 @@ export default function registerIpcMainActionListeners(main: Main) {
|
|||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
IPC_ACTIONS.DB_CREATE,
|
IPC_ACTIONS.DB_CREATE,
|
||||||
async (_, dbPath: string, countryCode: string) => {
|
async (_, dbPath: string, countryCode: string) => {
|
||||||
return await getErrorHandledReponse(async function dbFunc() {
|
return await getErrorHandledReponse(async () => {
|
||||||
return await databaseManager.createNewDatabase(dbPath, countryCode);
|
return await databaseManager.createNewDatabase(dbPath, countryCode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -158,7 +186,7 @@ export default function registerIpcMainActionListeners(main: Main) {
|
|||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
IPC_ACTIONS.DB_CONNECT,
|
IPC_ACTIONS.DB_CONNECT,
|
||||||
async (_, dbPath: string, countryCode?: string) => {
|
async (_, dbPath: string, countryCode?: string) => {
|
||||||
return await getErrorHandledReponse(async function dbFunc() {
|
return await getErrorHandledReponse(async () => {
|
||||||
return await databaseManager.connectToDatabase(dbPath, countryCode);
|
return await databaseManager.connectToDatabase(dbPath, countryCode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -167,7 +195,7 @@ export default function registerIpcMainActionListeners(main: Main) {
|
|||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
IPC_ACTIONS.DB_CALL,
|
IPC_ACTIONS.DB_CALL,
|
||||||
async (_, method: DatabaseMethod, ...args: unknown[]) => {
|
async (_, method: DatabaseMethod, ...args: unknown[]) => {
|
||||||
return await getErrorHandledReponse(async function dbFunc() {
|
return await getErrorHandledReponse(async () => {
|
||||||
return await databaseManager.call(method, ...args);
|
return await databaseManager.call(method, ...args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -176,15 +204,15 @@ export default function registerIpcMainActionListeners(main: Main) {
|
|||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
IPC_ACTIONS.DB_BESPOKE,
|
IPC_ACTIONS.DB_BESPOKE,
|
||||||
async (_, method: string, ...args: unknown[]) => {
|
async (_, method: string, ...args: unknown[]) => {
|
||||||
return await getErrorHandledReponse(async function dbFunc() {
|
return await getErrorHandledReponse(async () => {
|
||||||
return await databaseManager.callBespoke(method, ...args);
|
return await databaseManager.callBespoke(method, ...args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle(IPC_ACTIONS.DB_SCHEMA, async (_) => {
|
ipcMain.handle(IPC_ACTIONS.DB_SCHEMA, async () => {
|
||||||
return await getErrorHandledReponse(async function dbFunc() {
|
return await getErrorHandledReponse(() => {
|
||||||
return await databaseManager.getSchemaMap();
|
return databaseManager.getSchemaMap();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,11 @@ export default function registerIpcMainMessageListeners(main: Main) {
|
|||||||
main.mainWindow!.reload();
|
main.mainWindow!.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on(IPC_MESSAGES.OPEN_EXTERNAL, (_, link) => {
|
ipcMain.on(IPC_MESSAGES.OPEN_EXTERNAL, (_, link: string) => {
|
||||||
shell.openExternal(link);
|
shell.openExternal(link);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath) => {
|
ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath: string) => {
|
||||||
return shell.showItemInFolder(filePath);
|
return shell.showItemInFolder(filePath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
10
src/App.vue
10
src/App.vue
@ -36,7 +36,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ConfigKeys } from 'fyo/core/types';
|
|
||||||
import { RTL_LANGUAGES } from 'fyo/utils/consts';
|
import { RTL_LANGUAGES } from 'fyo/utils/consts';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { systemLanguageRef } from 'src/utils/refs';
|
import { systemLanguageRef } from 'src/utils/refs';
|
||||||
@ -126,10 +125,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async setInitialScreen(): Promise<void> {
|
async setInitialScreen(): Promise<void> {
|
||||||
const lastSelectedFilePath = fyo.config.get(
|
const lastSelectedFilePath = fyo.config.get('lastSelectedFilePath', null);
|
||||||
ConfigKeys.LastSelectedFilePath,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof lastSelectedFilePath !== 'string' ||
|
typeof lastSelectedFilePath !== 'string' ||
|
||||||
@ -159,7 +155,7 @@ export default defineComponent({
|
|||||||
updateConfigFiles(fyo);
|
updateConfigFiles(fyo);
|
||||||
},
|
},
|
||||||
async fileSelected(filePath: string, isNew?: boolean): Promise<void> {
|
async fileSelected(filePath: string, isNew?: boolean): Promise<void> {
|
||||||
fyo.config.set(ConfigKeys.LastSelectedFilePath, filePath);
|
fyo.config.set('lastSelectedFilePath', filePath);
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
this.activeScreen = Screen.SetupWizard;
|
this.activeScreen = Screen.SetupWizard;
|
||||||
return;
|
return;
|
||||||
@ -173,7 +169,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async setupComplete(setupWizardOptions: SetupWizardOptions): Promise<void> {
|
async setupComplete(setupWizardOptions: SetupWizardOptions): Promise<void> {
|
||||||
const filePath = fyo.config.get(ConfigKeys.LastSelectedFilePath);
|
const filePath = fyo.config.get('lastSelectedFilePath');
|
||||||
if (typeof filePath !== 'string') {
|
if (typeof filePath !== 'string') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
import { ConfigKeys } from 'fyo/core/types';
|
|
||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
import { BaseError } from 'fyo/utils/errors';
|
import { BaseError } from 'fyo/utils/errors';
|
||||||
import { ErrorLog } from 'fyo/utils/types';
|
import { ErrorLog } from 'fyo/utils/types';
|
||||||
@ -10,6 +9,7 @@ import { fyo } from './initFyo';
|
|||||||
import router from './router';
|
import router from './router';
|
||||||
import { getErrorMessage, stringifyCircular } from './utils';
|
import { getErrorMessage, stringifyCircular } from './utils';
|
||||||
import { DialogOptions, ToastOptions } from './utils/types';
|
import { DialogOptions, ToastOptions } from './utils/types';
|
||||||
|
import { UnknownFunction } from 'utils/types';
|
||||||
const { ipcRenderer } = require('electron');
|
const { ipcRenderer } = require('electron');
|
||||||
|
|
||||||
function shouldNotStore(error: Error) {
|
function shouldNotStore(error: Error) {
|
||||||
@ -23,7 +23,7 @@ export async function sendError(errorLogObj: ErrorLog) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorLogObj.more ??= {};
|
errorLogObj.more ??= {};
|
||||||
errorLogObj.more!.path ??= router.currentRoute.value.fullPath;
|
errorLogObj.more.path ??= router.currentRoute.value.fullPath;
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
error_name: errorLogObj.name,
|
error_name: errorLogObj.name,
|
||||||
@ -77,7 +77,7 @@ export async function handleError(
|
|||||||
logToConsole: boolean,
|
logToConsole: boolean,
|
||||||
error: Error,
|
error: Error,
|
||||||
more: Record<string, unknown> = {},
|
more: Record<string, unknown> = {},
|
||||||
notifyUser: boolean = true
|
notifyUser = true
|
||||||
) {
|
) {
|
||||||
if (logToConsole) {
|
if (logToConsole) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -127,7 +127,13 @@ export async function handleErrorWithDialog(
|
|||||||
},
|
},
|
||||||
isPrimary: true,
|
isPrimary: true,
|
||||||
},
|
},
|
||||||
{ label: t`Cancel`, action() {}, isEscape: true },
|
{
|
||||||
|
label: t`Cancel`,
|
||||||
|
action() {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
isEscape: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +158,7 @@ export async function showErrorDialog(title?: string, content?: string) {
|
|||||||
|
|
||||||
// Wrapper Functions
|
// Wrapper Functions
|
||||||
|
|
||||||
export function getErrorHandled(func: Function) {
|
export function getErrorHandled(func: UnknownFunction) {
|
||||||
return async function errorHandled(...args: unknown[]) {
|
return async function errorHandled(...args: unknown[]) {
|
||||||
try {
|
try {
|
||||||
return await func(...args);
|
return await func(...args);
|
||||||
@ -167,7 +173,7 @@ export function getErrorHandled(func: Function) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getErrorHandledSync(func: Function) {
|
export function getErrorHandledSync(func: UnknownFunction) {
|
||||||
return function errorHandledSync(...args: unknown[]) {
|
return function errorHandledSync(...args: unknown[]) {
|
||||||
try {
|
try {
|
||||||
return func(...args);
|
return func(...args);
|
||||||
@ -208,7 +214,7 @@ function getIssueUrlQuery(errorLogObj?: ErrorLog): string {
|
|||||||
body.push(`**Platform**: \`${fyo.store.platform}\``);
|
body.push(`**Platform**: \`${fyo.store.platform}\``);
|
||||||
body.push(`**Path**: \`${router.currentRoute.value.fullPath}\``);
|
body.push(`**Path**: \`${router.currentRoute.value.fullPath}\``);
|
||||||
|
|
||||||
body.push(`**Language**: \`${fyo.config.get(ConfigKeys.Language)}\``);
|
body.push(`**Language**: \`${fyo.config.get('language') ?? '-'}\``);
|
||||||
if (fyo.singles.SystemSettings?.countryCode) {
|
if (fyo.singles.SystemSettings?.countryCode) {
|
||||||
body.push(`**Country**: \`${fyo.singles.SystemSettings.countryCode}\``);
|
body.push(`**Country**: \`${fyo.singles.SystemSettings.countryCode}\``);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ export class Importer {
|
|||||||
this.templateFieldsMap = new Map();
|
this.templateFieldsMap = new Map();
|
||||||
this.templateFieldsPicked = new Map();
|
this.templateFieldsPicked = new Map();
|
||||||
|
|
||||||
templateFields.forEach((f, i) => {
|
templateFields.forEach((f) => {
|
||||||
this.templateFieldsMap.set(f.fieldKey, f);
|
this.templateFieldsMap.set(f.fieldKey, f);
|
||||||
this.templateFieldsPicked.set(f.fieldKey, true);
|
this.templateFieldsPicked.set(f.fieldKey, true);
|
||||||
});
|
});
|
||||||
|
@ -214,7 +214,6 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { setupDummyInstance } from 'dummy';
|
import { setupDummyInstance } from 'dummy';
|
||||||
const { ipcRenderer } = require('electron');
|
|
||||||
import { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import Button from 'src/components/Button.vue';
|
import Button from 'src/components/Button.vue';
|
||||||
@ -224,11 +223,12 @@ import Loading from 'src/components/Loading.vue';
|
|||||||
import Modal from 'src/components/Modal.vue';
|
import Modal from 'src/components/Modal.vue';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
import { showDialog } from 'src/utils/interactive';
|
import { showDialog } from 'src/utils/interactive';
|
||||||
import { deleteDb, getSavePath } from 'src/utils/ipcCalls';
|
import { deleteDb, getSavePath, getSelectedFilePath } from 'src/utils/ipcCalls';
|
||||||
import { updateConfigFiles } from 'src/utils/misc';
|
import { updateConfigFiles } from 'src/utils/misc';
|
||||||
import { IPC_ACTIONS } from 'utils/messages';
|
import { IPC_ACTIONS } from 'utils/messages';
|
||||||
import type { ConfigFilesWithModified } from 'utils/types';
|
import type { ConfigFilesWithModified } from 'utils/types';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'DatabaseSelector',
|
name: 'DatabaseSelector',
|
||||||
@ -353,13 +353,7 @@ export default defineComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = (
|
const filePath = (await getSelectedFilePath())?.filePaths?.[0];
|
||||||
await ipcRenderer.invoke(IPC_ACTIONS.GET_OPEN_FILEPATH, {
|
|
||||||
title: this.t`Select file`,
|
|
||||||
properties: ['openFile'],
|
|
||||||
filters: [{ name: 'SQLite DB File', extensions: ['db'] }],
|
|
||||||
})
|
|
||||||
)?.filePaths?.[0];
|
|
||||||
this.emitFileSelected(filePath);
|
this.emitFileSelected(filePath);
|
||||||
},
|
},
|
||||||
async selectFile(file: ConfigFilesWithModified) {
|
async selectFile(file: ConfigFilesWithModified) {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { ipcRenderer } = require('electron');
|
const { ipcRenderer } = require('electron');
|
||||||
import { ConfigKeys } from 'fyo/core/types';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { CUSTOM_EVENTS, IPC_ACTIONS } from 'utils/messages';
|
import { CUSTOM_EVENTS, IPC_ACTIONS } from 'utils/messages';
|
||||||
import { UnexpectedLogObject } from 'utils/types';
|
import { UnexpectedLogObject } from 'utils/types';
|
||||||
@ -16,7 +15,7 @@ import { stringifyCircular } from './utils';
|
|||||||
import { setLanguageMap } from './utils/language';
|
import { setLanguageMap } from './utils/language';
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const language = fyo.config.get(ConfigKeys.Language) as string;
|
const language = fyo.config.get('language') as string;
|
||||||
if (language) {
|
if (language) {
|
||||||
await setLanguageMap(language);
|
await setLanguageMap(language);
|
||||||
}
|
}
|
||||||
@ -74,7 +73,13 @@ function setErrorHandlers(app: VueApp) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.onunhandledrejection = (event: PromiseRejectionEvent) => {
|
window.onunhandledrejection = (event: PromiseRejectionEvent) => {
|
||||||
const error = event.reason;
|
let error: Error;
|
||||||
|
if (event.reason instanceof Error) {
|
||||||
|
error = event.reason;
|
||||||
|
} else {
|
||||||
|
error = new Error(String(event.reason));
|
||||||
|
}
|
||||||
|
|
||||||
handleError(true, error).catch((err) => console.error(err));
|
handleError(true, error).catch((err) => console.error(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ export default function registerIpcRendererListeners() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', function () {
|
document.addEventListener('visibilitychange', () => {
|
||||||
const { visibilityState } = document;
|
const { visibilityState } = document;
|
||||||
if (visibilityState === 'visible' && !fyo.telemetry.started) {
|
if (visibilityState === 'visible' && !fyo.telemetry.started) {
|
||||||
fyo.telemetry.start();
|
fyo.telemetry.start();
|
||||||
|
@ -12,6 +12,7 @@ import Settings from 'src/pages/Settings/Settings.vue';
|
|||||||
import TemplateBuilder from 'src/pages/TemplateBuilder/TemplateBuilder.vue';
|
import TemplateBuilder from 'src/pages/TemplateBuilder/TemplateBuilder.vue';
|
||||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
||||||
import { historyState } from './utils/refs';
|
import { historyState } from './utils/refs';
|
||||||
|
import type { HistoryState } from 'vue-router';
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
@ -122,8 +123,9 @@ const routes: RouteRecordRaw[] = [
|
|||||||
const router = createRouter({ routes, history: createWebHistory() });
|
const router = createRouter({ routes, history: createWebHistory() });
|
||||||
|
|
||||||
router.afterEach(() => {
|
router.afterEach(() => {
|
||||||
historyState.forward = !!history.state?.forward;
|
const state = history.state as HistoryState;
|
||||||
historyState.back = !!history.state?.back;
|
historyState.forward = !!state.forward;
|
||||||
|
historyState.back = !!state.back;
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -262,7 +262,7 @@ async function checkAndCreateDoc(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const doc = await fyo.doc.getNewDoc(schemaName, docObject);
|
const doc = fyo.doc.getNewDoc(schemaName, docObject);
|
||||||
return doc.sync();
|
return doc.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
src/shims-tsx.d.ts
vendored
4
src/shims-tsx.d.ts
vendored
@ -3,9 +3,9 @@ import Vue, { VNode } from 'vue'
|
|||||||
declare global {
|
declare global {
|
||||||
namespace JSX {
|
namespace JSX {
|
||||||
// tslint:disable no-empty-interface
|
// tslint:disable no-empty-interface
|
||||||
interface Element extends VNode {}
|
type Element = VNode
|
||||||
// tslint:disable no-empty-interface
|
// tslint:disable no-empty-interface
|
||||||
interface ElementClass extends Vue {}
|
type ElementClass = Vue
|
||||||
interface IntrinsicElements {
|
interface IntrinsicElements {
|
||||||
[elem: string]: any
|
[elem: string]: any
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ function evaluateFieldMeta(
|
|||||||
field: Field,
|
field: Field,
|
||||||
doc?: Doc,
|
doc?: Doc,
|
||||||
meta?: 'required' | 'hidden' | 'readOnly',
|
meta?: 'required' | 'hidden' | 'readOnly',
|
||||||
defaultValue: boolean = false
|
defaultValue = false
|
||||||
) {
|
) {
|
||||||
if (meta === undefined) {
|
if (meta === undefined) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
@ -118,7 +118,7 @@ export async function getCsvExportData(
|
|||||||
|
|
||||||
const tableFieldRowMap = parentNameMap[parentName];
|
const tableFieldRowMap = parentNameMap[parentName];
|
||||||
if (!tableFieldRowMap || !Object.keys(tableFieldRowMap ?? {}).length) {
|
if (!tableFieldRowMap || !Object.keys(tableFieldRowMap ?? {}).length) {
|
||||||
rows.push([baseRowData, headers.child.map((_) => '')].flat());
|
rows.push([baseRowData, headers.child.map(() => '')].flat());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ import { fyo } from 'src/initFyo';
|
|||||||
|
|
||||||
export function stringifyCircular(
|
export function stringifyCircular(
|
||||||
obj: unknown,
|
obj: unknown,
|
||||||
ignoreCircular: boolean = false,
|
ignoreCircular = false,
|
||||||
convertDocument: boolean = false
|
convertDocument = false
|
||||||
) {
|
) {
|
||||||
const cacheKey: string[] = [];
|
const cacheKey: string[] = [];
|
||||||
const cacheValue: unknown[] = [];
|
const cacheValue: unknown[] = [];
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Fyo } from 'fyo';
|
import { Fyo } from 'fyo';
|
||||||
import { ConfigFile, ConfigKeys } from 'fyo/core/types';
|
import { ConfigFile } from 'fyo/core/types';
|
||||||
import { getRegionalModels, models } from 'models/index';
|
import { getRegionalModels, models } from 'models/index';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { TargetField } from 'schemas/types';
|
import { TargetField } from 'schemas/types';
|
||||||
@ -68,7 +68,7 @@ async function checkSingleLinks(fyo: Fyo) {
|
|||||||
.flat()
|
.flat()
|
||||||
.filter((field) => field.fieldtype === 'Link' && field.target)
|
.filter((field) => field.fieldtype === 'Link' && field.target)
|
||||||
.map((field) => ({
|
.map((field) => ({
|
||||||
fieldKey: `${field.schemaName}.${field.fieldname}`,
|
fieldKey: `${field.schemaName!}.${field.fieldname}`,
|
||||||
target: (field as TargetField).target,
|
target: (field as TargetField).target,
|
||||||
}));
|
}));
|
||||||
const linkFieldsMap = getMapFromList(linkFields, 'fieldKey');
|
const linkFieldsMap = getMapFromList(linkFields, 'fieldKey');
|
||||||
@ -126,10 +126,10 @@ async function setVersion(fyo: Fyo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setDeviceId(fyo: Fyo) {
|
function setDeviceId(fyo: Fyo) {
|
||||||
let deviceId = fyo.config.get(ConfigKeys.DeviceId) as string | undefined;
|
let deviceId = fyo.config.get('deviceId');
|
||||||
if (deviceId === undefined) {
|
if (deviceId === undefined) {
|
||||||
deviceId = getRandomString();
|
deviceId = getRandomString();
|
||||||
fyo.config.set(ConfigKeys.DeviceId, deviceId);
|
fyo.config.set('deviceId', deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
fyo.store.deviceId = deviceId;
|
fyo.store.deviceId = deviceId;
|
||||||
@ -176,7 +176,7 @@ async function setOpenCount(fyo: Fyo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getOpenCountFromFiles(fyo: Fyo) {
|
function getOpenCountFromFiles(fyo: Fyo) {
|
||||||
const configFile = fyo.config.get(ConfigKeys.Files, []) as ConfigFile[];
|
const configFile = fyo.config.get('files', []) as ConfigFile[];
|
||||||
for (const file of configFile) {
|
for (const file of configFile) {
|
||||||
if (file.id === fyo.singles.SystemSettings?.instanceId) {
|
if (file.id === fyo.singles.SystemSettings?.instanceId) {
|
||||||
return file.openCount ?? 0;
|
return file.openCount ?? 0;
|
||||||
|
@ -13,7 +13,7 @@ type DialogReturn<DO extends DialogOptions> = DO['buttons'] extends {
|
|||||||
|
|
||||||
export async function showDialog<DO extends DialogOptions>(options: DO) {
|
export async function showDialog<DO extends DialogOptions>(options: DO) {
|
||||||
const preWrappedButtons: DialogButton[] = options.buttons ?? [
|
const preWrappedButtons: DialogButton[] = options.buttons ?? [
|
||||||
{ label: t`Okay`, action: () => {}, isEscape: true },
|
{ label: t`Okay`, action: () => null, isEscape: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
|
@ -9,11 +9,20 @@ import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
|||||||
import { SelectFileOptions, SelectFileReturn, TemplateFile } from 'utils/types';
|
import { SelectFileOptions, SelectFileReturn, TemplateFile } from 'utils/types';
|
||||||
import { showDialog, showToast } from './interactive';
|
import { showDialog, showToast } from './interactive';
|
||||||
import { setLanguageMap } from './language';
|
import { setLanguageMap } from './language';
|
||||||
|
import type { OpenDialogReturnValue } from 'electron';
|
||||||
|
|
||||||
export function reloadWindow() {
|
export function reloadWindow() {
|
||||||
return ipcRenderer.send(IPC_MESSAGES.RELOAD_MAIN_WINDOW);
|
return ipcRenderer.send(IPC_MESSAGES.RELOAD_MAIN_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getSelectedFilePath(): Promise<OpenDialogReturnValue> {
|
||||||
|
return await ipcRenderer.invoke(IPC_ACTIONS.GET_OPEN_FILEPATH, {
|
||||||
|
title: this.t`Select file`,
|
||||||
|
properties: ['openFile'],
|
||||||
|
filters: [{ name: 'SQLite DB File', extensions: ['db'] }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function getTemplates(): Promise<TemplateFile[]> {
|
export async function getTemplates(): Promise<TemplateFile[]> {
|
||||||
return await ipcRenderer.invoke(IPC_ACTIONS.GET_TEMPLATES);
|
return await ipcRenderer.invoke(IPC_ACTIONS.GET_TEMPLATES);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export const languageCodeMap: Record<string, string> = {
|
|||||||
|
|
||||||
export async function setLanguageMap(
|
export async function setLanguageMap(
|
||||||
initLanguage?: string,
|
initLanguage?: string,
|
||||||
dontReload: boolean = false
|
dontReload = false
|
||||||
) {
|
) {
|
||||||
const oldLanguage = fyo.config.get('language') as string;
|
const oldLanguage = fyo.config.get('language') as string;
|
||||||
initLanguage ??= oldLanguage;
|
initLanguage ??= oldLanguage;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Fyo } from 'fyo';
|
import { Fyo } from 'fyo';
|
||||||
import { ConfigFile, ConfigKeys } from 'fyo/core/types';
|
import { ConfigFile } from 'fyo/core/types';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { SetupWizard } from 'models/baseModels/SetupWizard/SetupWizard';
|
import { SetupWizard } from 'models/baseModels/SetupWizard/SetupWizard';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
@ -64,7 +64,7 @@ export function getSetupWizardDoc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function updateConfigFiles(fyo: Fyo): ConfigFile {
|
export function updateConfigFiles(fyo: Fyo): ConfigFile {
|
||||||
const configFiles = fyo.config.get(ConfigKeys.Files, []) as ConfigFile[];
|
const configFiles = fyo.config.get('files', []) as ConfigFile[];
|
||||||
|
|
||||||
const companyName = fyo.singles.AccountingSettings!.companyName as string;
|
const companyName = fyo.singles.AccountingSettings!.companyName as string;
|
||||||
const id = fyo.singles.SystemSettings!.instanceId as string;
|
const id = fyo.singles.SystemSettings!.instanceId as string;
|
||||||
@ -83,7 +83,7 @@ export function updateConfigFiles(fyo: Fyo): ConfigFile {
|
|||||||
newFile = configFiles[fileIndex];
|
newFile = configFiles[fileIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
fyo.config.set(ConfigKeys.Files, configFiles);
|
fyo.config.set('files', configFiles);
|
||||||
return newFile;
|
return newFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,11 +93,12 @@ export function getPrintTemplatePropHints(schemaName: string, fyo: Fyo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showHSN(doc: Doc): boolean {
|
function showHSN(doc: Doc): boolean {
|
||||||
if (!Array.isArray(doc.items)) {
|
const items = doc.items;
|
||||||
|
if (!Array.isArray(items)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return doc.items.map((i) => i.hsnCode).every(Boolean);
|
return items.map((i: Doc) => i.hsnCode).every(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formattedTotalDiscount(doc: Doc): string {
|
function formattedTotalDiscount(doc: Doc): string {
|
||||||
@ -246,15 +247,14 @@ function constructPrintDocument(innerHTML: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getAllCSSAsStyleElem() {
|
function getAllCSSAsStyleElem() {
|
||||||
const cssTexts = [];
|
const cssTexts: string[] = [];
|
||||||
for (const sheet of document.styleSheets) {
|
for (const sheet of document.styleSheets) {
|
||||||
for (const rule of sheet.cssRules) {
|
for (const rule of sheet.cssRules) {
|
||||||
cssTexts.push(rule.cssText);
|
cssTexts.push(rule.cssText);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
if (sheet.ownerRule) {
|
||||||
for (const rule of sheet.ownerRule ?? []) {
|
cssTexts.push(sheet.ownerRule.cssText);
|
||||||
cssTexts.push(rule.cssText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
|
import type { HistoryState } from 'vue-router';
|
||||||
|
|
||||||
export const showSidebar = ref(true);
|
export const showSidebar = ref(true);
|
||||||
export const docsPathRef = ref<string>('');
|
export const docsPathRef = ref<string>('');
|
||||||
export const systemLanguageRef = ref<string>('');
|
export const systemLanguageRef = ref<string>('');
|
||||||
export const historyState = reactive({
|
export const historyState = reactive({
|
||||||
forward: !!history.state?.forward,
|
forward: !!(history.state as HistoryState)?.forward,
|
||||||
back: !!history.state?.back,
|
back: !!(history.state as HistoryState)?.back,
|
||||||
});
|
});
|
||||||
|
@ -346,8 +346,8 @@ export class Search {
|
|||||||
* - Marked indices are rebuilt when the modal is opened.
|
* - Marked indices are rebuilt when the modal is opened.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
_obsSet: boolean = false;
|
_obsSet = false;
|
||||||
numSearches: number = 0;
|
numSearches = 0;
|
||||||
searchables: Record<string, Searchable>;
|
searchables: Record<string, Searchable>;
|
||||||
keywords: Record<string, Keyword[]>;
|
keywords: Record<string, Keyword[]>;
|
||||||
priorityMap: Record<string, number> = {
|
priorityMap: Record<string, number> = {
|
||||||
@ -707,7 +707,7 @@ export class Search {
|
|||||||
|
|
||||||
_getDocSearchItemFromKeyword(keyword: Keyword): DocSearchItem {
|
_getDocSearchItemFromKeyword(keyword: Keyword): DocSearchItem {
|
||||||
const schemaName = keyword.meta.schemaName as string;
|
const schemaName = keyword.meta.schemaName as string;
|
||||||
const schemaLabel = this.fyo.schemaMap[schemaName]?.label!;
|
const schemaLabel = this.fyo.schemaMap[schemaName]?.label ?? schemaName;
|
||||||
const route = this._getRouteFromKeyword(keyword);
|
const route = this._getRouteFromKeyword(keyword);
|
||||||
return {
|
return {
|
||||||
label: keyword.values[0],
|
label: keyword.values[0],
|
||||||
|
@ -12,7 +12,7 @@ interface ModMap {
|
|||||||
|
|
||||||
type Mod = keyof ModMap;
|
type Mod = keyof ModMap;
|
||||||
type Context = unknown;
|
type Context = unknown;
|
||||||
type ShortcutFunction = () => void;
|
type ShortcutFunction = () => unknown;
|
||||||
type ShortcutConfig = {
|
type ShortcutConfig = {
|
||||||
callback: ShortcutFunction;
|
callback: ShortcutFunction;
|
||||||
propagate: boolean;
|
propagate: boolean;
|
||||||
@ -117,8 +117,8 @@ export class Shortcuts {
|
|||||||
context: Context,
|
context: Context,
|
||||||
shortcut: string[],
|
shortcut: string[],
|
||||||
callback: ShortcutFunction,
|
callback: ShortcutFunction,
|
||||||
propagate: boolean = false,
|
propagate = false,
|
||||||
removeIfSet: boolean = true
|
removeIfSet = true
|
||||||
): void {
|
): void {
|
||||||
const key = this.getKey(shortcut);
|
const key = this.getKey(shortcut);
|
||||||
if (!key) {
|
if (!key) {
|
||||||
|
@ -3,8 +3,8 @@ import { routeFilters } from 'src/utils/filters';
|
|||||||
import { fyo } from '../initFyo';
|
import { fyo } from '../initFyo';
|
||||||
import { SidebarConfig, SidebarItem, SidebarRoot } from './types';
|
import { SidebarConfig, SidebarItem, SidebarRoot } from './types';
|
||||||
|
|
||||||
export async function getSidebarConfig(): Promise<SidebarConfig> {
|
export function getSidebarConfig(): SidebarConfig {
|
||||||
const sideBar = await getCompleteSidebar();
|
const sideBar = getCompleteSidebar();
|
||||||
return getFilteredSidebar(sideBar);
|
return getFilteredSidebar(sideBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ function getRegionalSidebar(): SidebarRoot[] {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getInventorySidebar(): Promise<SidebarRoot[]> {
|
function getInventorySidebar(): SidebarRoot[] {
|
||||||
const hasInventory = !!fyo.singles.AccountingSettings?.enableInventory;
|
const hasInventory = !!fyo.singles.AccountingSettings?.enableInventory;
|
||||||
if (!hasInventory) {
|
if (!hasInventory) {
|
||||||
return [];
|
return [];
|
||||||
@ -101,7 +101,7 @@ async function getInventorySidebar(): Promise<SidebarRoot[]> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getReportSidebar() {
|
function getReportSidebar() {
|
||||||
return {
|
return {
|
||||||
label: t`Reports`,
|
label: t`Reports`,
|
||||||
name: 'reports',
|
name: 'reports',
|
||||||
@ -132,7 +132,7 @@ async function getReportSidebar() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCompleteSidebar(): Promise<SidebarConfig> {
|
function getCompleteSidebar(): SidebarConfig {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: t`Get Started`,
|
label: t`Get Started`,
|
||||||
@ -141,7 +141,7 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
|
|||||||
icon: 'general',
|
icon: 'general',
|
||||||
iconSize: '24',
|
iconSize: '24',
|
||||||
iconHeight: 5,
|
iconHeight: 5,
|
||||||
hidden: () => fyo.singles.SystemSettings!.hideGetStarted as boolean,
|
hidden: () => !!fyo.singles.SystemSettings?.hideGetStarted,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`Dashboard`,
|
label: t`Dashboard`,
|
||||||
@ -254,9 +254,9 @@ async function getCompleteSidebar(): Promise<SidebarConfig> {
|
|||||||
},
|
},
|
||||||
] as SidebarItem[],
|
] as SidebarItem[],
|
||||||
},
|
},
|
||||||
await getReportSidebar(),
|
getReportSidebar(),
|
||||||
await getInventorySidebar(),
|
getInventorySidebar(),
|
||||||
await getRegionalSidebar(),
|
getRegionalSidebar(),
|
||||||
{
|
{
|
||||||
label: t`Setup`,
|
label: t`Setup`,
|
||||||
name: 'setup',
|
name: 'setup',
|
||||||
|
@ -90,7 +90,7 @@ export type ActionGroup = {
|
|||||||
export type DropdownItem = {
|
export type DropdownItem = {
|
||||||
label: string;
|
label: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
action?: Function;
|
action?: () => unknown;
|
||||||
group?: string;
|
group?: string;
|
||||||
component?: { template: string };
|
component?: { template: string };
|
||||||
isGroup?: boolean;
|
isGroup?: boolean;
|
||||||
|
@ -386,7 +386,7 @@ export function toggleSidebar(value?: boolean) {
|
|||||||
export function focusOrSelectFormControl(
|
export function focusOrSelectFormControl(
|
||||||
doc: Doc,
|
doc: Doc,
|
||||||
ref: any,
|
ref: any,
|
||||||
clear: boolean = true
|
clear= true
|
||||||
) {
|
) {
|
||||||
if (!doc?.fyo) {
|
if (!doc?.fyo) {
|
||||||
return;
|
return;
|
||||||
@ -507,7 +507,7 @@ export async function commonDocCancel(doc: Doc): Promise<boolean> {
|
|||||||
|
|
||||||
export async function commonDocSync(
|
export async function commonDocSync(
|
||||||
doc: Doc,
|
doc: Doc,
|
||||||
useDialog: boolean = false
|
useDialog = false
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
let success: boolean;
|
let success: boolean;
|
||||||
if (useDialog) {
|
if (useDialog) {
|
||||||
|
@ -100,7 +100,7 @@ export function useDocShortcuts(
|
|||||||
shortcuts: Shortcuts,
|
shortcuts: Shortcuts,
|
||||||
docRef: DocRef,
|
docRef: DocRef,
|
||||||
name: string,
|
name: string,
|
||||||
isMultiple: boolean = true
|
isMultiple = true
|
||||||
) {
|
) {
|
||||||
let context = name;
|
let context = name;
|
||||||
if (isMultiple) {
|
if (isMultiple) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
|
import type { ConfigMap } from 'fyo/core/types';
|
||||||
|
|
||||||
const config = new Store();
|
const config = new Store<ConfigMap>();
|
||||||
export default config;
|
export default config;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import type { ConfigFile } from "fyo/core/types";
|
import type { ConfigFile } from 'fyo/core/types';
|
||||||
|
|
||||||
export type UnknownMap = Record<string, unknown>;
|
export type UnknownMap = Record<string, unknown>;
|
||||||
export type Translation = { translation: string; context?: string };
|
export type Translation = { translation: string; context?: string };
|
||||||
export type LanguageMap = Record<string, Translation>;
|
export type LanguageMap = Record<string, Translation>;
|
||||||
|
export type UnknownFunction = (...args: unknown[]) => unknown;
|
||||||
|
|
||||||
export type CountryInfoMap = Record<string, CountryInfo | undefined>;
|
export type CountryInfoMap = Record<string, CountryInfo | undefined>;
|
||||||
export interface CountryInfo {
|
export interface CountryInfo {
|
||||||
@ -71,4 +72,4 @@ interface ModMap {
|
|||||||
|
|
||||||
export interface ConfigFilesWithModified extends ConfigFile {
|
export interface ConfigFilesWithModified extends ConfigFile {
|
||||||
modified: string;
|
modified: string;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user