mirror of
https://github.com/frappe/books.git
synced 2025-02-08 23:18:31 +00:00
refactor: deprecate use of remote; remove bg transparency on macOS
This commit is contained in:
parent
864d07fd1b
commit
f0424d0ee4
@ -39,7 +39,7 @@
|
||||
"@vue/cli-plugin-router": "^4.5.0",
|
||||
"@vue/cli-service": "^4.5.0",
|
||||
"autoprefixer": "^9",
|
||||
"electron": "^8.0.0",
|
||||
"electron": "^8.5.5",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-notarize": "^1.1.1",
|
||||
"electron-updater": "^4.3.9",
|
||||
@ -53,8 +53,7 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat",
|
||||
"vue-cli-plugin-electron-builder": "^2.0.0",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^5.61.0"
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"gitHooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
|
28
src/App.vue
28
src/App.vue
@ -30,43 +30,43 @@ import SetupWizard from './pages/SetupWizard/SetupWizard';
|
||||
import DatabaseSelector from './pages/DatabaseSelector';
|
||||
import Settings from '@/pages/Settings/Settings.vue';
|
||||
import WindowsTitleBar from '@/components/WindowsTitleBar';
|
||||
import { remote } from 'electron';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import config from '@/config';
|
||||
import { connectToLocalDatabase } from '@/utils';
|
||||
import { getMainWindowSize } from '@/screenSize';
|
||||
import { IPC_MESSAGES, IPC_ACTIONS } from '@/messages';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
activeScreen: null
|
||||
activeScreen: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
activeScreen(value) {
|
||||
async activeScreen(value) {
|
||||
if (!value) return;
|
||||
let { width, height } = getMainWindowSize();
|
||||
const { width, height } = ipcRenderer.invoke(
|
||||
IPC_ACTIONS.GET_PRIMARY_DISPLAY_SIZE
|
||||
);
|
||||
let size = {
|
||||
Desk: [width, height],
|
||||
DatabaseSelector: [600, 600],
|
||||
SetupWizard: [600, 600],
|
||||
Settings: [460, 577]
|
||||
Settings: [460, 577],
|
||||
}[value];
|
||||
let resizable = value === 'Desk';
|
||||
|
||||
let win = remote.getCurrentWindow();
|
||||
if (size.length) {
|
||||
win.setSize(...size);
|
||||
win.setResizable(resizable);
|
||||
if (size.length && value != 'Settings') {
|
||||
ipcRenderer.send(IPC_MESSAGES.RESIZE_MAIN_WINDOW, size, resizable);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Desk,
|
||||
SetupWizard,
|
||||
DatabaseSelector,
|
||||
Settings,
|
||||
WindowsTitleBar
|
||||
WindowsTitleBar,
|
||||
},
|
||||
async mounted() {
|
||||
let lastSelectedFilePath = config.get('lastSelectedFilePath', null);
|
||||
@ -99,7 +99,7 @@ export default {
|
||||
},
|
||||
checkForUpdates() {
|
||||
frappe.events.trigger('check-for-updates');
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,32 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
import { app, protocol, BrowserWindow, ipcMain } from 'electron';
|
||||
import electron, {
|
||||
app,
|
||||
dialog,
|
||||
protocol,
|
||||
BrowserWindow,
|
||||
ipcMain,
|
||||
Menu,
|
||||
} from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
|
||||
import {
|
||||
createProtocol
|
||||
// installVueDevtools
|
||||
} from 'vue-cli-plugin-electron-builder/lib';
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
|
||||
|
||||
import saveHtmlAsPdf from './saveHtmlAsPdf';
|
||||
import { IPC_MESSAGES, IPC_ACTIONS } from './messages';
|
||||
import theme from '@/theme';
|
||||
import { getMainWindowSize } from './screenSize';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
// Global ref to prevent garbage collection.
|
||||
let mainWindow;
|
||||
let winURL;
|
||||
let checkedForUpdate = false;
|
||||
|
||||
// Scheme must be registered before the app is ready
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'app', privileges: { secure: true, standard: true } }
|
||||
{ scheme: 'app', privileges: { secure: true, standard: true } },
|
||||
]);
|
||||
|
||||
/* -----------------------------
|
||||
* Main process helper functions
|
||||
* -----------------------------*/
|
||||
|
||||
function getMainWindowSize() {
|
||||
let height;
|
||||
if (app.isReady()) {
|
||||
// const screen = require('electron').screen;
|
||||
const screen = electron.screen;
|
||||
height = screen.getPrimaryDisplay().workAreaSize.height;
|
||||
height = height > 907 ? 907 : height;
|
||||
} else {
|
||||
height = 907;
|
||||
}
|
||||
const width = Math.ceil(1.323 * height);
|
||||
return { height, width };
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
let { width, height } = getMainWindowSize();
|
||||
mainWindow = new BrowserWindow({
|
||||
vibrancy: 'sidebar',
|
||||
@ -35,10 +57,10 @@ function createWindow() {
|
||||
width,
|
||||
height,
|
||||
webPreferences: {
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
|
||||
},
|
||||
frame: isLinux,
|
||||
resizable: true
|
||||
resizable: true,
|
||||
});
|
||||
|
||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||
@ -58,6 +80,12 @@ function createWindow() {
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('did-finish-load', () => {
|
||||
mainWindow.webContents.send('store-on-window', {
|
||||
appVersion: app.getVersion(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createSettingsWindow(tab = 'General') {
|
||||
@ -68,49 +96,111 @@ function createSettingsWindow(tab = 'General') {
|
||||
height: 577,
|
||||
backgroundColor: theme.backgroundColor.gray['200'],
|
||||
webPreferences: {
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
|
||||
},
|
||||
resizable: false
|
||||
resizable: false,
|
||||
});
|
||||
|
||||
settingsWindow.loadURL(`${winURL}#/settings/${tab}`);
|
||||
settingsWindow.on('close', () => {
|
||||
mainWindow.reload();
|
||||
});
|
||||
}
|
||||
|
||||
ipcMain.on('check-for-updates', () => {
|
||||
/* ---------------------------------
|
||||
* Register ipcMain message handlers
|
||||
* ---------------------------------*/
|
||||
|
||||
ipcMain.on(IPC_MESSAGES.CHECK_FOR_UPDATES, () => {
|
||||
if (!isDevelopment && !checkedForUpdate) {
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
checkedForUpdate = true;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('open-settings-window', (event, tab) => {
|
||||
ipcMain.on(IPC_MESSAGES.OPEN_SETTINGS, (event, tab) => {
|
||||
createSettingsWindow(tab);
|
||||
});
|
||||
|
||||
ipcMain.on('reload-main-window', () => {
|
||||
ipcMain.on(IPC_MESSAGES.OPEN_SETTINGS, (event) => {
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
const menu = Menu.getApplicationMenu();
|
||||
menu.popup({ window });
|
||||
});
|
||||
|
||||
ipcMain.on(IPC_MESSAGES.RELOAD_MAIN_WINDOW, () => {
|
||||
mainWindow.reload();
|
||||
});
|
||||
|
||||
// Quit when all windows are closed.
|
||||
ipcMain.on(IPC_MESSAGES.RESIZE_MAIN_WINDOW, (event, size, resizable) => {
|
||||
const [width, height] = size;
|
||||
if (!width || !height) return;
|
||||
mainWindow.setSize(width, height);
|
||||
mainWindow.setResizable(resizable);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC_MESSAGES.CLOSE_CURRENT_WINDOW, (event) => {
|
||||
event.sender.getOwnerBrowserWindow().close();
|
||||
});
|
||||
|
||||
ipcMain.on(IPC_MESSAGES.MINIMIZE_CURRENT_WINDOW, (event) => {
|
||||
event.sender.getOwnerBrowserWindow().minimize();
|
||||
});
|
||||
|
||||
/* ----------------------------------
|
||||
* Register ipcMain function handlers
|
||||
* ----------------------------------*/
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.TOGGLE_MAXIMIZE_CURRENT_WINDOW, (event) => {
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
const maximizing = !window.isMaximized();
|
||||
if (maximizing) {
|
||||
window.maximize();
|
||||
} else {
|
||||
window.unmaximize();
|
||||
}
|
||||
return maximizing;
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.GET_OPEN_FILEPATH, async (event, options) => {
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
return await dialog.showOpenDialog(window, options);
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.GET_SAVE_FILEPATH, async (event, options) => {
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
return await dialog.showSaveDialog(window, options);
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.GET_PRIMARY_DISPLAY_SIZE, (event) => {
|
||||
return getMainWindowSize();
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.GET_DIALOG_RESPONSE, async (event, options) => {
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
return await dialog.showMessageBox(window, options);
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.SAVE_HTML_AS_PDF, async (event, html, savePath) => {
|
||||
return await saveHtmlAsPdf(html, savePath);
|
||||
});
|
||||
|
||||
/* ------------------------------
|
||||
* Register app lifecycle methods
|
||||
* ------------------------------*/
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', async () => {
|
||||
if (isDevelopment && !process.env.IS_TEST) {
|
||||
// Install Vue Devtools
|
||||
@ -128,10 +218,13 @@ app.on('ready', async () => {
|
||||
createWindow();
|
||||
});
|
||||
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
/* ------------------------------
|
||||
* Register node#process messages
|
||||
* ------------------------------*/
|
||||
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', data => {
|
||||
process.on('message', (data) => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit();
|
||||
}
|
||||
|
@ -41,8 +41,9 @@
|
||||
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
import { remote } from 'electron';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import Base from './Base';
|
||||
import { IPC_ACTIONS } from '@/messages'
|
||||
|
||||
export default {
|
||||
name: 'AttachImage',
|
||||
@ -61,7 +62,7 @@ export default {
|
||||
filters: [{ name: 'Image', extensions: ['png', 'jpg', 'jpeg', 'webp'] }]
|
||||
};
|
||||
|
||||
const { filePaths } = await remote.dialog.showOpenDialog(options);
|
||||
const { filePaths } = await ipcRenderer.invoke(IPC_ACTIONS.GET_OPEN_FILEPATH, options);
|
||||
if (filePaths && filePaths[0]) {
|
||||
let dataURL = await this.getDataURL(filePaths[0]);
|
||||
this.triggerChange(dataURL);
|
||||
|
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
class="pt-6 pb-2 px-2 h-full block window-drag flex justify-between flex-col bg-gray-100"
|
||||
:style="sidebarBackground"
|
||||
>
|
||||
<div class="pt-6 pb-2 px-2 h-full block window-drag flex justify-between flex-col bg-gray-100" >
|
||||
<div class="window-no-drag">
|
||||
<WindowControls v-if="platform === 'Mac'" class="px-3 mb-6" />
|
||||
<div class="px-3">
|
||||
@ -47,7 +44,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { remote } from 'electron';
|
||||
import sidebarConfig from '../sidebarConfig';
|
||||
import WindowControls from './WindowControls';
|
||||
|
||||
@ -60,15 +56,8 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
sidebarBackground() {
|
||||
return this.platform === 'Mac'
|
||||
? {
|
||||
'background-color': 'rgba(255, 255, 255, 0.6)'
|
||||
}
|
||||
: null;
|
||||
},
|
||||
appVersion() {
|
||||
return remote.app.getVersion();
|
||||
return frappe.store.appVersion;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
@ -19,32 +19,29 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import electron from 'electron';
|
||||
import { runWindowAction } from '@/utils';
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
export default {
|
||||
name: 'WindowControls',
|
||||
props: {
|
||||
buttons: {
|
||||
type: Array,
|
||||
default: () => ['close', 'minimize', 'maximize']
|
||||
}
|
||||
default: () => ['close', 'minimize', 'maximize'],
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
action(name) {
|
||||
async action(name) {
|
||||
if (this.buttons.includes(name)) {
|
||||
let window = electron.remote.getCurrentWindow();
|
||||
if (name === 'maximize' && window.isMaximized()) {
|
||||
name = 'unmaximize';
|
||||
}
|
||||
this.$emit(name);
|
||||
window[name]();
|
||||
const actionRan = await runWindowAction(name);
|
||||
this.$emit(actionRan);
|
||||
}
|
||||
},
|
||||
getColorClasses(name) {
|
||||
let classes = {
|
||||
close: 'bg-red-500 hover:bg-red-700',
|
||||
minimize: 'bg-yellow-500 hover:bg-yellow-700',
|
||||
maximize: 'bg-green-500 hover:bg-green-700'
|
||||
maximize: 'bg-green-500 hover:bg-green-700',
|
||||
}[name];
|
||||
|
||||
if (this.buttons.includes(name)) {
|
||||
@ -52,7 +49,7 @@ export default {
|
||||
}
|
||||
|
||||
return 'bg-gray-500';
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -30,23 +30,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import electron from 'electron';
|
||||
import { openMenu } from '@/menu';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { runWindowAction } from '@/utils';
|
||||
import { IPC_MESSAGES } from '@/messages';
|
||||
|
||||
export default {
|
||||
name: 'WindowsTitleBar',
|
||||
methods: {
|
||||
action(name) {
|
||||
let window = electron.remote.getCurrentWindow();
|
||||
if (name === 'maximize' && window.isMaximized()) {
|
||||
name = 'unmaximize';
|
||||
}
|
||||
this.$emit(name);
|
||||
window[name]();
|
||||
async action(name) {
|
||||
const actionRan = await runWindowAction(name);
|
||||
this.$emit(actionRan);
|
||||
},
|
||||
openMenu() {
|
||||
openMenu();
|
||||
}
|
||||
}
|
||||
ipcRenderer.send(IPC_MESSAGES.OPEN_MENU);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
24
src/main.js
24
src/main.js
@ -14,6 +14,7 @@ import router from './router';
|
||||
|
||||
// other imports
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { IPC_MESSAGES } from './messages';
|
||||
import Store from 'electron-store';
|
||||
|
||||
(async () => {
|
||||
@ -26,17 +27,22 @@ import Store from 'electron-store';
|
||||
frappe.fetch = window.fetch.bind();
|
||||
|
||||
frappe.events.on('reload-main-window', () => {
|
||||
ipcRenderer.send('reload-main-window');
|
||||
ipcRenderer.send(IPC_MESSAGES.RELOAD_MAIN_WINDOW);
|
||||
});
|
||||
|
||||
frappe.events.on('check-for-updates', () => {
|
||||
let { autoUpdate } = frappe.AccountingSettings;
|
||||
if (autoUpdate == null || autoUpdate === 1) {
|
||||
ipcRenderer.send('check-for-updates');
|
||||
ipcRenderer.send(IPC_MESSAGES.CHECK_FOR_UPDATES);
|
||||
}
|
||||
});
|
||||
|
||||
window.frappe = frappe;
|
||||
window.frappe.store = {};
|
||||
|
||||
ipcRenderer.on('store-on-window', (event, message) => {
|
||||
Object.assign(window.frappe.store, message);
|
||||
});
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.component('feather-icon', FeatherIcon);
|
||||
@ -51,22 +57,22 @@ import Store from 'electron-store';
|
||||
return {
|
||||
win32: 'Windows',
|
||||
darwin: 'Mac',
|
||||
linux: 'Linux'
|
||||
linux: 'Linux',
|
||||
}[process.platform];
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
_(...args) {
|
||||
return frappe._(...args);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Vue.config.errorHandler = (err, vm, info) => {
|
||||
console.error(err, vm, info);
|
||||
};
|
||||
|
||||
process.on('unhandledRejection', error => {
|
||||
process.on('unhandledRejection', (error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
@ -75,8 +81,8 @@ import Store from 'electron-store';
|
||||
el: '#app',
|
||||
router,
|
||||
components: {
|
||||
App
|
||||
App,
|
||||
},
|
||||
template: '<App/>'
|
||||
template: '<App/>',
|
||||
});
|
||||
})();
|
||||
|
11
src/menu.js
11
src/menu.js
@ -1,11 +0,0 @@
|
||||
const { remote } = require('electron');
|
||||
const { Menu } = remote;
|
||||
|
||||
function openMenu() {
|
||||
const menu = Menu.getApplicationMenu();
|
||||
menu.popup({ window: remote.getCurrentWindow() });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
openMenu
|
||||
};
|
18
src/messages.js
Normal file
18
src/messages.js
Normal file
@ -0,0 +1,18 @@
|
||||
export const IPC_MESSAGES = {
|
||||
OPEN_MENU: 'open-menu',
|
||||
OPEN_SETTINGS: 'open-settings',
|
||||
CHECK_FOR_UPDATES: 'check-for-updates',
|
||||
RELOAD_MAIN_WINDOW: 'reload-main-window',
|
||||
RESIZE_MAIN_WINDOW: 'resize-main-window',
|
||||
CLOSE_CURRENT_WINDOW: 'close-current-window',
|
||||
MINIMIZE_CURRENT_WINDOW: 'minimize-current-window',
|
||||
};
|
||||
|
||||
export const IPC_ACTIONS = {
|
||||
TOGGLE_MAXIMIZE_CURRENT_WINDOW: 'toggle-maximize-current-window',
|
||||
GET_OPEN_FILEPATH: 'open-dialog',
|
||||
GET_SAVE_FILEPATH: 'save-dialog',
|
||||
GET_DIALOG_RESPONSE: 'show-message-box',
|
||||
GET_PRIMARY_DISPLAY_SIZE: 'get-primary-display-size',
|
||||
SAVE_HTML_AS_PDF: 'save-html-as-pdf'
|
||||
};
|
@ -4,24 +4,28 @@
|
||||
<h4 class="pb-2">{{ _('Data Import') }}</h4>
|
||||
<frappe-control
|
||||
:docfield="{
|
||||
fieldtype: 'Select',
|
||||
fieldname: 'referenceDoctype',
|
||||
options: ['Select...', 'Item', 'Party', 'Account']
|
||||
}"
|
||||
@change="doctype => showTable(doctype)"
|
||||
fieldtype: 'Select',
|
||||
fieldname: 'referenceDoctype',
|
||||
options: ['Select...', 'Item', 'Party', 'Account'],
|
||||
}"
|
||||
@change="(doctype) => showTable(doctype)"
|
||||
/>
|
||||
<f-button secondary v-if="doctype" primary @click="uploadCSV">Upload CSV</f-button>
|
||||
<f-button secondary v-if="doctype" primary @click="downloadCSV">Download CSV Template</f-button>
|
||||
<f-button secondary v-if="doctype" primary @click="uploadCSV"
|
||||
>Upload CSV</f-button
|
||||
>
|
||||
<f-button secondary v-if="doctype" primary @click="downloadCSV"
|
||||
>Download CSV Template</f-button
|
||||
>
|
||||
<f-button primary @click="importData">Submit</f-button>
|
||||
|
||||
<frappe-control
|
||||
v-if="doctype"
|
||||
ref="fileInput"
|
||||
style="position: absolute; display: none;"
|
||||
style="position: absolute; display: none"
|
||||
:docfield="{
|
||||
fieldtype: 'File',
|
||||
fieldname: 'CSV File',
|
||||
}"
|
||||
fieldtype: 'File',
|
||||
fieldname: 'CSV File',
|
||||
}"
|
||||
@change="uploadCSV"
|
||||
/>
|
||||
<div class="pt-2" ref="datatable" v-once></div>
|
||||
@ -35,13 +39,12 @@ import { convertFieldsToDatatableColumns } from 'frappejs/client/ui/utils';
|
||||
import { writeFile } from 'frappejs/server/utils';
|
||||
import path from 'path';
|
||||
import csv2json from 'csvjson-csv2json';
|
||||
const { remote } = require('electron');
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
doctype: undefined,
|
||||
fileUploaded: false
|
||||
fileUploaded: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -59,7 +62,7 @@ export default {
|
||||
this.datatable = new DataTable(this.$refs.datatable, {
|
||||
columns,
|
||||
data: [[]],
|
||||
pasteFromClipboard: true
|
||||
pasteFromClipboard: true,
|
||||
});
|
||||
},
|
||||
async downloadCSV() {
|
||||
@ -72,19 +75,26 @@ export default {
|
||||
const documentsPath =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? path.resolve('.')
|
||||
: remote.getGlobal('documentsPath');
|
||||
: frappe.store.documentsPath;
|
||||
|
||||
await writeFile(
|
||||
path.resolve(documentsPath + `/frappe-accounting/${this.doctype}.csv`),
|
||||
csvString
|
||||
);
|
||||
let title = frappe._('Message');
|
||||
let message = frappe._('Template saved successfully.');
|
||||
|
||||
if (documentsPath === undefined) {
|
||||
title = frappe._('Error');
|
||||
message = frappe._('Template could not be saved.');
|
||||
} else {
|
||||
await writeFile(
|
||||
path.resolve(
|
||||
documentsPath + `/frappe-accounting/${this.doctype}.csv`
|
||||
),
|
||||
csvString
|
||||
);
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: 'show-dialog',
|
||||
args: {
|
||||
title: 'Message',
|
||||
message: `Template Saved Successfully`
|
||||
}
|
||||
args: { title, message },
|
||||
});
|
||||
},
|
||||
uploadCSV(file) {
|
||||
@ -93,9 +103,9 @@ export default {
|
||||
reader.onload = () => {
|
||||
const meta = frappe.getMeta(this.doctype);
|
||||
let header = reader.result.split('\n')[0];
|
||||
header = header.split(',').map(label => {
|
||||
header = header.split(',').map((label) => {
|
||||
let fieldname;
|
||||
meta.fields.some(field => {
|
||||
meta.fields.some((field) => {
|
||||
if (field.label === label.trim()) {
|
||||
fieldname = field.fieldname;
|
||||
return true;
|
||||
@ -120,19 +130,19 @@ export default {
|
||||
importData() {
|
||||
const rows = this.datatable.datamanager.getRows();
|
||||
|
||||
const data = rows.map(row => {
|
||||
const data = rows.map((row) => {
|
||||
return row.slice(1).reduce((prev, curr) => {
|
||||
prev[curr.column.field.fieldname] = curr.content;
|
||||
return prev;
|
||||
}, {});
|
||||
});
|
||||
|
||||
data.forEach(async d => {
|
||||
data.forEach(async (d) => {
|
||||
try {
|
||||
await frappe
|
||||
.newDoc(
|
||||
Object.assign(d, {
|
||||
doctype: this.doctype
|
||||
doctype: this.doctype,
|
||||
})
|
||||
)
|
||||
.insert();
|
||||
@ -144,10 +154,10 @@ export default {
|
||||
method: 'show-dialog',
|
||||
args: {
|
||||
title: 'Message',
|
||||
message: `Data Imported Successfully`
|
||||
}
|
||||
message: `Data Imported Successfully`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -21,7 +21,12 @@
|
||||
>
|
||||
<div
|
||||
class="h-full shadow-lg mb-12 absolute"
|
||||
style="width: 21cm; min-height: 29.7cm; height: max-content; transform: scale(0.755);"
|
||||
style="
|
||||
width: 21cm;
|
||||
min-height: 29.7cm;
|
||||
height: max-content;
|
||||
transform: scale(0.755);
|
||||
"
|
||||
ref="printContainer"
|
||||
>
|
||||
<component
|
||||
@ -52,7 +57,8 @@ import Button from '@/components/Button';
|
||||
import BackLink from '@/components/BackLink';
|
||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
||||
import { makePDF } from '@/utils';
|
||||
import { remote } from 'electron';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { IPC_ACTIONS } from '@/messages';
|
||||
|
||||
export default {
|
||||
name: 'PrintView',
|
||||
@ -63,13 +69,13 @@ export default {
|
||||
DropdownWithAction,
|
||||
Button,
|
||||
BackLink,
|
||||
TwoColumnForm
|
||||
TwoColumnForm,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
doc: null,
|
||||
showCustomiser: false,
|
||||
printSettings: null
|
||||
printSettings: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
@ -82,21 +88,27 @@ export default {
|
||||
},
|
||||
printTemplate() {
|
||||
return this.meta.printTemplate;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async makePDF() {
|
||||
let destination = await this.getSavePath();
|
||||
let html = this.$refs.printContainer.innerHTML;
|
||||
makePDF(html, destination);
|
||||
const savePath = await this.getSavePath();
|
||||
if (!savePath) return;
|
||||
|
||||
const html = this.$refs.printContainer.innerHTML;
|
||||
makePDF(html, savePath);
|
||||
},
|
||||
async getSavePath() {
|
||||
const options = {
|
||||
title: this._('Select folder'),
|
||||
defaultPath: `${this.name}.pdf`
|
||||
defaultPath: `${this.name}.pdf`,
|
||||
};
|
||||
|
||||
let { filePath } = await remote.dialog.showSaveDialog(options);
|
||||
let { filePath } = await ipcRenderer.invoke(
|
||||
IPC_ACTIONS.GET_SAVE_FILEPATH,
|
||||
options
|
||||
);
|
||||
|
||||
if (filePath) {
|
||||
if (!filePath.endsWith('.pdf')) {
|
||||
filePath = filePath + '.pdf';
|
||||
@ -104,7 +116,7 @@ export default {
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -27,8 +27,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
import { remote } from 'electron';
|
||||
import { _ } from 'frappejs/utils';
|
||||
import WindowControls from '@/components/WindowControls';
|
||||
import TabGeneral from './TabGeneral.vue';
|
||||
@ -80,11 +78,6 @@ export default {
|
||||
if (index !== -1) {
|
||||
this.activeTab = index;
|
||||
}
|
||||
|
||||
let currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.on('close', () => {
|
||||
frappe.events.trigger('reload-main-window');
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getIconComponent(tab) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
:df="meta.getField('logo')"
|
||||
:value="doc.logo"
|
||||
@change="
|
||||
value => {
|
||||
(value) => {
|
||||
doc.set('logo', value);
|
||||
doc.update();
|
||||
}
|
||||
@ -24,7 +24,7 @@
|
||||
:value="doc.displayLogo"
|
||||
:show-label="true"
|
||||
@change="
|
||||
value => {
|
||||
(value) => {
|
||||
doc.set('displayLogo', value);
|
||||
doc.update();
|
||||
}
|
||||
@ -38,27 +38,28 @@
|
||||
</template>
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
import { remote } from 'electron';
|
||||
import { dialog, ipcRenderer } from 'electron';
|
||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
||||
import FormControl from '@/components/Controls/FormControl';
|
||||
import { IPC_ACTIONS } from '@/messages';
|
||||
|
||||
export default {
|
||||
name: 'TabInvoice',
|
||||
components: {
|
||||
TwoColumnForm,
|
||||
FormControl
|
||||
FormControl,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
doctype: 'PrintSettings',
|
||||
name: 'PrintSettings'
|
||||
name: 'PrintSettings',
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
companyName: null,
|
||||
doc: null,
|
||||
showEdit: false
|
||||
showEdit: false,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
@ -79,27 +80,23 @@ export default {
|
||||
'email',
|
||||
'phone',
|
||||
'address',
|
||||
'gstin'
|
||||
].map(field => this.meta.getField(field));
|
||||
}
|
||||
'gstin',
|
||||
].map((field) => this.meta.getField(field));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openFileSelector() {
|
||||
remote.dialog.showOpenDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: frappe._('Select Logo'),
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'Invoice Logo', extensions: ['png', 'jpg', 'svg'] }]
|
||||
},
|
||||
files => {
|
||||
if (files && files[0]) {
|
||||
this.doc.set('logo', `file://${files[0]}`);
|
||||
this.doc.update();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
async openFileSelector() {
|
||||
const options = {
|
||||
title: frappe._('Select Logo'),
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'Invoice Logo', extensions: ['png', 'jpg', 'svg'] }],
|
||||
};
|
||||
const { filePaths } = await ipcRenderer.invoke(IPC_ACTIONS.GET_OPEN_FILEPATH, options);
|
||||
if (filePaths[0] !== undefined) {
|
||||
this.doc.set('logo', `file://${files[0]}`);
|
||||
this.doc.update;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -51,7 +51,8 @@ import TwoColumnForm from '@/components/TwoColumnForm';
|
||||
import FormControl from '@/components/Controls/FormControl';
|
||||
import Button from '@/components/Button';
|
||||
import config from '@/config';
|
||||
import { remote } from 'electron';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { IPC_MESSAGES } from '@/messages';
|
||||
|
||||
export default {
|
||||
name: 'TabSystem',
|
||||
@ -74,7 +75,7 @@ export default {
|
||||
changeFile() {
|
||||
config.set('lastSelectedFilePath', null);
|
||||
frappe.events.trigger('reload-main-window');
|
||||
remote.getCurrentWindow().close();
|
||||
ipcRenderer.send(IPC_MESSAGES.CLOSE_CURRENT_WINDOW);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
54
src/saveHtmlAsPdf.js
Normal file
54
src/saveHtmlAsPdf.js
Normal file
@ -0,0 +1,54 @@
|
||||
import fs from 'fs';
|
||||
import { shell, BrowserWindow } from 'electron';
|
||||
|
||||
const PRINT_OPTIONS = {
|
||||
marginsType: 1, // no margin
|
||||
pageSize: 'A4',
|
||||
printBackground: true,
|
||||
printBackgrounds: true,
|
||||
printSelectionOnly: false,
|
||||
};
|
||||
|
||||
export default async function makePDF(html, savePath) {
|
||||
const printWindow = getInitializedPrintWindow();
|
||||
|
||||
printWindow.webContents.executeJavaScript(`
|
||||
document.body.innerHTML = \`${html}\`;
|
||||
`);
|
||||
|
||||
const sleep = (m) => new Promise((r) => setTimeout(r, m));
|
||||
// TODO: Check if event 'paint' works after bumping electron.
|
||||
printWindow.webContents.on('did-finish-load', async () => {
|
||||
await sleep(1000); // Required else pdf'll be blank.
|
||||
printWindow.webContents.printToPDF(PRINT_OPTIONS).then((data) => {
|
||||
printWindow.destroy();
|
||||
fs.writeFile(savePath, data, (error) => {
|
||||
if (error) throw error;
|
||||
return shell.openItem(savePath);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getInitializedPrintWindow() {
|
||||
const printWindow = new BrowserWindow({
|
||||
width: 595,
|
||||
height: 842,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
|
||||
},
|
||||
});
|
||||
printWindow.loadURL(getPrintWindowUrl());
|
||||
return printWindow;
|
||||
}
|
||||
|
||||
function getPrintWindowUrl() {
|
||||
let url = global.WEBPACK_DEV_SERVER_URL;
|
||||
if (url) {
|
||||
url = url + 'print';
|
||||
} else {
|
||||
url = 'app://./print.html';
|
||||
}
|
||||
return url;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import electron from 'electron';
|
||||
|
||||
export function getMainWindowSize() {
|
||||
let screen = electron.screen || electron.remote.screen;
|
||||
let { height } = screen.getPrimaryDisplay().workAreaSize;
|
||||
let width;
|
||||
|
||||
if (height > 907) {
|
||||
height = 907;
|
||||
}
|
||||
width = Math.ceil(1.323 * height);
|
||||
|
||||
return { width, height };
|
||||
}
|
175
src/utils.js
175
src/utils.js
@ -2,7 +2,8 @@ import frappe from 'frappejs';
|
||||
import fs from 'fs';
|
||||
import { _ } from 'frappejs/utils';
|
||||
import migrate from './migrate';
|
||||
import { remote, shell, ipcRenderer } from 'electron';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { IPC_MESSAGES, IPC_ACTIONS } from './messages';
|
||||
import SQLite from 'frappejs/backends/sqlite';
|
||||
import postStart from '../server/postStart';
|
||||
import router from '@/router';
|
||||
@ -12,10 +13,13 @@ import config from '@/config';
|
||||
export async function createNewDatabase() {
|
||||
const options = {
|
||||
title: _('Select folder'),
|
||||
defaultPath: 'frappe-books.db'
|
||||
defaultPath: 'frappe-books.db',
|
||||
};
|
||||
|
||||
let { filePath } = await remote.dialog.showSaveDialog(options);
|
||||
let { filePath } = await ipcRenderer.invoke(
|
||||
IPC_ACTIONS.GET_SAVE_FILEPATH,
|
||||
options
|
||||
);
|
||||
if (filePath) {
|
||||
if (!filePath.endsWith('.db')) {
|
||||
filePath = filePath + '.db';
|
||||
@ -30,10 +34,10 @@ export async function createNewDatabase() {
|
||||
action() {
|
||||
fs.unlinkSync(filePath);
|
||||
return filePath;
|
||||
}
|
||||
},
|
||||
},
|
||||
{ label: _('Cancel'), action() {} }
|
||||
]
|
||||
{ label: _('Cancel'), action() {} },
|
||||
],
|
||||
});
|
||||
} else {
|
||||
return filePath;
|
||||
@ -45,10 +49,13 @@ export async function loadExistingDatabase() {
|
||||
const options = {
|
||||
title: _('Select file'),
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'SQLite DB File', extensions: ['db'] }]
|
||||
filters: [{ name: 'SQLite DB File', extensions: ['db'] }],
|
||||
};
|
||||
|
||||
let { filePaths } = await remote.dialog.showOpenDialog(options);
|
||||
const { filePaths } = await ipcRenderer.invoke(
|
||||
IPC_ACTIONS.GET_OPEN_FILEPATH,
|
||||
options
|
||||
);
|
||||
|
||||
if (filePaths && filePaths[0]) {
|
||||
return filePaths[0];
|
||||
@ -58,7 +65,7 @@ export async function loadExistingDatabase() {
|
||||
export async function connectToLocalDatabase(filepath) {
|
||||
frappe.login('Administrator');
|
||||
frappe.db = new SQLite({
|
||||
dbPath: filepath
|
||||
dbPath: filepath,
|
||||
});
|
||||
await frappe.db.connect();
|
||||
await migrate();
|
||||
@ -66,13 +73,13 @@ export async function connectToLocalDatabase(filepath) {
|
||||
|
||||
// set file info in config
|
||||
let files = config.get('files') || [];
|
||||
if (!files.find(file => file.filePath === filepath)) {
|
||||
if (!files.find((file) => file.filePath === filepath)) {
|
||||
files = [
|
||||
{
|
||||
companyName: frappe.AccountingSettings.companyName,
|
||||
filePath: filepath
|
||||
filePath: filepath,
|
||||
},
|
||||
...files
|
||||
...files,
|
||||
];
|
||||
config.set('files', files);
|
||||
}
|
||||
@ -84,16 +91,17 @@ export async function connectToLocalDatabase(filepath) {
|
||||
export async function showMessageDialog({
|
||||
message,
|
||||
description,
|
||||
buttons = []
|
||||
buttons = [],
|
||||
}) {
|
||||
let buttonLabels = buttons.map(a => a.label);
|
||||
const { response } = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
message,
|
||||
detail: description,
|
||||
buttons: buttonLabels
|
||||
}
|
||||
const options = {
|
||||
message,
|
||||
detail: description,
|
||||
buttons: buttons.map((a) => a.label),
|
||||
};
|
||||
|
||||
const { response } = await ipcRenderer.invoke(
|
||||
IPC_ACTIONS.GET_DIALOG_RESPONSE,
|
||||
options
|
||||
);
|
||||
|
||||
let button = buttons[response];
|
||||
@ -103,11 +111,11 @@ export async function showMessageDialog({
|
||||
}
|
||||
|
||||
export function deleteDocWithPrompt(doc) {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
showMessageDialog({
|
||||
message: _('Are you sure you want to delete {0} "{1}"?', [
|
||||
doc.doctype,
|
||||
doc.name
|
||||
doc.name,
|
||||
]),
|
||||
description: _('This action is permanent'),
|
||||
buttons: [
|
||||
@ -117,18 +125,18 @@ export function deleteDocWithPrompt(doc) {
|
||||
doc
|
||||
.delete()
|
||||
.then(() => resolve(true))
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
handleErrorWithDialog(e, doc);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: _('Cancel'),
|
||||
action() {
|
||||
resolve(false);
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -138,11 +146,11 @@ export function partyWithAvatar(party) {
|
||||
data() {
|
||||
return {
|
||||
imageURL: null,
|
||||
label: null
|
||||
label: null,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
Avatar
|
||||
Avatar,
|
||||
},
|
||||
async mounted() {
|
||||
this.imageURL = await frappe.db.getValue('Party', party, 'image');
|
||||
@ -153,7 +161,7 @@ export function partyWithAvatar(party) {
|
||||
<Avatar class="flex-shrink-0" :imageURL="imageURL" :label="label" size="sm" />
|
||||
<span class="ml-2 truncate">{{ label }}</span>
|
||||
</div>
|
||||
`
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
@ -173,8 +181,8 @@ export function openQuickEdit({ doctype, name, hideFields, defaults = {} }) {
|
||||
name,
|
||||
hideFields,
|
||||
values: defaults,
|
||||
lastRoute: currentRoute
|
||||
}
|
||||
lastRoute: currentRoute,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -183,7 +191,7 @@ export function getErrorMessage(e, doc) {
|
||||
if (e.type === frappe.errors.LinkValidationError) {
|
||||
errorMessage = _('{0} {1} is linked with existing records.', [
|
||||
doc.doctype,
|
||||
doc.name
|
||||
doc.name,
|
||||
]);
|
||||
} else if (e.type === frappe.errors.DuplicateEntryError) {
|
||||
errorMessage = _('{0} {1} already exists.', [doc.doctype, doc.name]);
|
||||
@ -197,69 +205,8 @@ export function handleErrorWithDialog(e, doc) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
// NOTE: a hack to find all the css from the current document and inject it to the print version
|
||||
// remove this if you are able to fix and get the default css loading on the page
|
||||
function injectCSS(contents) {
|
||||
const styles = document.getElementsByTagName('style');
|
||||
|
||||
for (let style of styles) {
|
||||
contents.insertCSS(style.innerHTML);
|
||||
}
|
||||
}
|
||||
|
||||
export async function makePDF(html, destination) {
|
||||
const { BrowserWindow } = remote;
|
||||
|
||||
let printWindow = new BrowserWindow({
|
||||
width: 595,
|
||||
height: 842,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
});
|
||||
|
||||
let webpackDevServerURL = remote.getGlobal('WEBPACK_DEV_SERVER_URL');
|
||||
if (webpackDevServerURL) {
|
||||
// Load the url of the dev server if in development mode
|
||||
printWindow.loadURL(webpackDevServerURL + 'print');
|
||||
} else {
|
||||
// Load the index.html when not in development
|
||||
printWindow.loadURL(`app://./print.html`);
|
||||
}
|
||||
|
||||
printWindow.on('closed', () => {
|
||||
printWindow = null;
|
||||
});
|
||||
|
||||
const code = `
|
||||
document.body.innerHTML = \`${html}\`;
|
||||
`;
|
||||
|
||||
printWindow.webContents.executeJavaScript(code);
|
||||
|
||||
const printOptions = {
|
||||
marginsType: 1, // no margin
|
||||
pageSize: 'A4',
|
||||
printBackground: true,
|
||||
printBackgrounds: true,
|
||||
printSelectionOnly: false
|
||||
};
|
||||
|
||||
const sleep = m => new Promise(r => setTimeout(r, m));
|
||||
|
||||
printWindow.webContents.on('did-finish-load', async () => {
|
||||
injectCSS(printWindow.webContents);
|
||||
await sleep(1000);
|
||||
printWindow.webContents.printToPDF(printOptions).then(data => {
|
||||
printWindow.close();
|
||||
fs.writeFile(destination, data, error => {
|
||||
if (error) throw error;
|
||||
return (shell.openItem(destination));
|
||||
});
|
||||
});
|
||||
});
|
||||
export async function makePDF(html, savePath) {
|
||||
ipcRenderer.invoke(IPC_ACTIONS.SAVE_HTML_AS_PDF, html, savePath);
|
||||
}
|
||||
|
||||
export function getActionsForDocument(doc) {
|
||||
@ -267,24 +214,24 @@ export function getActionsForDocument(doc) {
|
||||
|
||||
let deleteAction = {
|
||||
component: {
|
||||
template: `<span class="text-red-700">{{ _('Delete') }}</span>`
|
||||
template: `<span class="text-red-700">{{ _('Delete') }}</span>`,
|
||||
},
|
||||
condition: doc => !doc.isNew() && !doc.submitted && !doc.meta.isSingle,
|
||||
condition: (doc) => !doc.isNew() && !doc.submitted && !doc.meta.isSingle,
|
||||
action: () =>
|
||||
deleteDocWithPrompt(doc).then(res => {
|
||||
deleteDocWithPrompt(doc).then((res) => {
|
||||
if (res) {
|
||||
router.push(`/list/${doc.doctype}`);
|
||||
}
|
||||
})
|
||||
}),
|
||||
};
|
||||
|
||||
let actions = [...(doc.meta.actions || []), deleteAction]
|
||||
.filter(d => (d.condition ? d.condition(doc) : true))
|
||||
.map(d => {
|
||||
.filter((d) => (d.condition ? d.condition(doc) : true))
|
||||
.map((d) => {
|
||||
return {
|
||||
label: d.label,
|
||||
component: d.component,
|
||||
action: d.action.bind(this, doc, router)
|
||||
action: d.action.bind(this, doc, router),
|
||||
};
|
||||
});
|
||||
|
||||
@ -292,5 +239,23 @@ export function getActionsForDocument(doc) {
|
||||
}
|
||||
|
||||
export function openSettings(tab = 'General') {
|
||||
ipcRenderer.send('open-settings-window', tab);
|
||||
ipcRenderer.send(IPC_MESSAGES.OPEN_SETTINGS, tab);
|
||||
}
|
||||
|
||||
export async function runWindowAction(name) {
|
||||
switch (name) {
|
||||
case 'close':
|
||||
ipcRenderer.send(IPC_MESSAGES.CLOSE_CURRENT_WINDOW);
|
||||
break;
|
||||
case 'minimize':
|
||||
ipcRenderer.send(IPC_MESSAGES.MINIMIZE_CURRENT_WINDOW);
|
||||
break;
|
||||
case 'maximize':
|
||||
const maximizing = await ipcRenderer.invoke(
|
||||
IPC_ACTIONS.TOGGLE_MAXIMIZE_CURRENT_WINDOW
|
||||
);
|
||||
name = maximizing ? name : 'unmaximize';
|
||||
break;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
@ -4,18 +4,21 @@ const webpack = require('webpack');
|
||||
module.exports = {
|
||||
pluginOptions: {
|
||||
electronBuilder: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
nodeIntegration: true,
|
||||
chainWebpackRendererProcess: (config) => {
|
||||
config.target('electron-renderer');
|
||||
},
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
index: {
|
||||
entry: 'src/main.js',
|
||||
filename: 'index.html'
|
||||
filename: 'index.html',
|
||||
},
|
||||
print: {
|
||||
entry: 'src/print.js',
|
||||
filename: 'print.html'
|
||||
}
|
||||
filename: 'print.html',
|
||||
},
|
||||
},
|
||||
runtimeCompiler: true,
|
||||
lintOnSave: process.env.NODE_ENV !== 'production',
|
||||
@ -23,7 +26,7 @@ module.exports = {
|
||||
Object.assign(config.resolve.alias, {
|
||||
deepmerge$: 'deepmerge/dist/umd.js',
|
||||
'frappe-charts$': 'frappe-charts/dist/frappe-charts.esm.js',
|
||||
'~': path.resolve('.')
|
||||
'~': path.resolve('.'),
|
||||
});
|
||||
|
||||
config.plugins.push(
|
||||
@ -36,10 +39,10 @@ module.exports = {
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.txt$/i,
|
||||
use: 'raw-loader'
|
||||
use: 'raw-loader',
|
||||
});
|
||||
|
||||
config.devtool = 'source-map';
|
||||
},
|
||||
transpileDependencies: ['frappejs']
|
||||
transpileDependencies: ['frappejs'],
|
||||
};
|
||||
|
@ -5024,7 +5024,7 @@ electron@5.0.0:
|
||||
electron-download "^4.1.0"
|
||||
extract-zip "^1.0.3"
|
||||
|
||||
electron@^8.0.0:
|
||||
electron@^8.5.5:
|
||||
version "8.5.5"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-8.5.5.tgz#17b12bd70139c0099f750fc5de0d480bf03acb96"
|
||||
integrity sha512-e355H+tRDial0m+X2v+l+0SnaATAPw4sNjv9qmdk/6MJz/glteVJwVJEnxTjPfEELIJSChrBWDBVpjdDvoBF4Q==
|
||||
@ -12759,7 +12759,7 @@ webpack@^4.0.0, webpack@^4.16.1, webpack@^4.18.0:
|
||||
watchpack "^1.7.4"
|
||||
webpack-sources "^1.4.1"
|
||||
|
||||
webpack@^5.22.0, webpack@^5.61.0:
|
||||
webpack@^5.22.0:
|
||||
version "5.61.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.61.0.tgz#fa827f0ee9bdfd141dd73c3e891e955ebd52fe7f"
|
||||
integrity sha512-fPdTuaYZ/GMGFm4WrPi2KRCqS1vDp773kj9S0iI5Uc//5cszsFEDgHNaX4Rj1vobUiU1dFIV3mA9k1eHeluFpw==
|
||||
|
Loading…
x
Reference in New Issue
Block a user