mirror of
https://github.com/frappe/books.git
synced 2025-02-08 23:18:31 +00:00
refactor: vue 2 → 3 migration
- render functions - slots - creation
This commit is contained in:
parent
ddf7cd2c62
commit
2f6a2f4cc2
@ -1,6 +1,6 @@
|
|||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import frappe from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
import { t } from 'frappe';
|
import { h } from 'vue';
|
||||||
import PartyWidget from './PartyWidget.vue';
|
import PartyWidget from './PartyWidget.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -44,9 +44,9 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
quickEditWidget: (doc) => ({
|
quickEditWidget: (doc) => ({
|
||||||
render(h) {
|
render() {
|
||||||
return h(PartyWidget, {
|
return h(PartyWidget, {
|
||||||
props: { doc },
|
doc,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import frappe from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
import { t } from 'frappe';
|
import { h } from 'vue';
|
||||||
import PartyWidget from './PartyWidget.vue';
|
import PartyWidget from './PartyWidget.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -44,9 +44,9 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
quickEditWidget: (doc) => ({
|
quickEditWidget: (doc) => ({
|
||||||
render(h) {
|
render() {
|
||||||
return h(PartyWidget, {
|
return h(PartyWidget, {
|
||||||
props: { doc },
|
doc,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -9,7 +9,7 @@ import electron, {
|
|||||||
protocol,
|
protocol,
|
||||||
shell,
|
shell,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
|
import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer';
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
@ -279,14 +279,8 @@ app.on('activate', () => {
|
|||||||
|
|
||||||
app.on('ready', async () => {
|
app.on('ready', async () => {
|
||||||
if (isDevelopment && !process.env.IS_TEST) {
|
if (isDevelopment && !process.env.IS_TEST) {
|
||||||
// Install Vue Devtools
|
|
||||||
// Devtools extensions are broken in Electron 6.0.0 and greater
|
|
||||||
// See https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/378 for more info
|
|
||||||
// Electron will not launch with Devtools extensions installed on Windows 10 with dark mode
|
|
||||||
// If you are not using Windows 10 dark mode, you may uncomment these lines
|
|
||||||
// In addition, if the linked issue is closed, you can upgrade electron and uncomment these lines
|
|
||||||
try {
|
try {
|
||||||
await installExtension(VUEJS_DEVTOOLS);
|
await installExtension(VUEJS3_DEVTOOLS);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Vue Devtools failed to install:', e.toString());
|
console.error('Vue Devtools failed to install:', e.toString());
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template v-slot:title>
|
||||||
<a
|
<a
|
||||||
class="cursor-pointer font-semibold flex items-center"
|
class="cursor-pointer font-semibold flex items-center"
|
||||||
slot="title"
|
|
||||||
@click="$router.back()"
|
@click="$router.back()"
|
||||||
>
|
>
|
||||||
<feather-icon name="chevron-left" class="w-5 h-5" />
|
<feather-icon name="chevron-left" class="w-5 h-5" />
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
:style="style"
|
:style="style"
|
||||||
:class="_class"
|
:class="_class"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners"
|
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</button>
|
</button>
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { showMessageDialog } from '../../utils';
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Base',
|
name: 'Base',
|
||||||
props: [
|
props: [
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="text-sm py-3 px-2 text-center" slot="content">
|
<template v-slot:content>
|
||||||
|
<div class="text-sm py-3 px-2 text-center">
|
||||||
<div>
|
<div>
|
||||||
<Row class="border-none" :column-count="5" gap="0.5rem">
|
<Row class="border-none" :column-count="5" gap="0.5rem">
|
||||||
<div
|
<div
|
||||||
@ -43,11 +44,12 @@
|
|||||||
:placeholder="t('Custom Hex')"
|
:placeholder="t('Custom Hex')"
|
||||||
:class="inputClasses"
|
:class="inputClasses"
|
||||||
:value="value"
|
:value="value"
|
||||||
@change="e => setColorValue(e.target.value)"
|
@change="(e) => setColorValue(e.target.value)"
|
||||||
class="bg-gray-100"
|
class="bg-gray-100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -62,7 +64,7 @@ export default {
|
|||||||
extends: Base,
|
extends: Base,
|
||||||
components: {
|
components: {
|
||||||
Popover,
|
Popover,
|
||||||
Row
|
Row,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setColorValue(value) {
|
setColorValue(value) {
|
||||||
@ -72,16 +74,16 @@ export default {
|
|||||||
if (/^#[0-9A-F]{6}$/i.test(value)) {
|
if (/^#[0-9A-F]{6}$/i.test(value)) {
|
||||||
this.triggerChange(value);
|
this.triggerChange(value);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
colors() {
|
colors() {
|
||||||
return this.df.colors;
|
return this.df.colors;
|
||||||
},
|
},
|
||||||
selectedColorLabel() {
|
selectedColorLabel() {
|
||||||
let color = this.colors.find(c => this.value === c.value);
|
let color = this.colors.find((c) => this.value === c.value);
|
||||||
return color ? color.label : this.value;
|
return color ? color.label : this.value;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
import Float from './Float';
|
import Float from './Float';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -12,10 +12,11 @@ import Float from './Float';
|
|||||||
import Currency from './Currency';
|
import Currency from './Currency';
|
||||||
import Text from './Text';
|
import Text from './Text';
|
||||||
import Color from './Color';
|
import Color from './Color';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormControl',
|
name: 'FormControl',
|
||||||
render(h) {
|
render() {
|
||||||
let controls = {
|
let controls = {
|
||||||
Data,
|
Data,
|
||||||
Select,
|
Select,
|
||||||
@ -30,13 +31,12 @@ export default {
|
|||||||
Float,
|
Float,
|
||||||
Currency,
|
Currency,
|
||||||
Text,
|
Text,
|
||||||
Color
|
Color,
|
||||||
};
|
};
|
||||||
let { df } = this.$attrs;
|
let { df } = this.$attrs;
|
||||||
return h(controls[df.fieldtype] || Data, {
|
return h(controls[df.fieldtype] || Data, {
|
||||||
props: this.$attrs,
|
...this.$attrs,
|
||||||
on: this.$listeners,
|
ref: 'control',
|
||||||
ref: 'control'
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -45,6 +45,6 @@ export default {
|
|||||||
},
|
},
|
||||||
getInput() {
|
getInput() {
|
||||||
return this.$refs.control.$refs.input;
|
return this.$refs.control.$refs.input;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
@ -4,11 +4,8 @@
|
|||||||
:hide-arrow="true"
|
:hide-arrow="true"
|
||||||
:placement="right ? 'bottom-end' : 'bottom-start'"
|
:placement="right ? 'bottom-end' : 'bottom-start'"
|
||||||
>
|
>
|
||||||
<div
|
<template v-slot:target>
|
||||||
slot="target"
|
<div class="h-full" v-on-outside-click="() => (isShown = false)">
|
||||||
class="h-full"
|
|
||||||
v-on-outside-click="() => (isShown = false)"
|
|
||||||
>
|
|
||||||
<slot
|
<slot
|
||||||
:toggleDropdown="toggleDropdown"
|
:toggleDropdown="toggleDropdown"
|
||||||
:highlightItemUp="highlightItemUp"
|
:highlightItemUp="highlightItemUp"
|
||||||
@ -16,7 +13,9 @@
|
|||||||
:selectHighlightedItem="selectHighlightedItem"
|
:selectHighlightedItem="selectHighlightedItem"
|
||||||
></slot>
|
></slot>
|
||||||
</div>
|
</div>
|
||||||
<div slot="content" class="bg-white rounded w-full min-w-40">
|
</template>
|
||||||
|
<template v-slot:content>
|
||||||
|
<div class="bg-white rounded w-full min-w-40">
|
||||||
<div class="p-1 max-h-64 overflow-auto text-sm">
|
<div class="p-1 max-h-64 overflow-auto text-sm">
|
||||||
<div v-if="isLoading" class="p-2 text-gray-600 italic">
|
<div v-if="isLoading" class="p-2 text-gray-600 italic">
|
||||||
{{ t('Loading...') }}
|
{{ t('Loading...') }}
|
||||||
@ -69,6 +68,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import feather from 'feather-icons';
|
import feather from 'feather-icons';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
const validIcons = Object.keys(feather.icons);
|
const validIcons = Object.keys(feather.icons);
|
||||||
|
|
||||||
@ -7,38 +8,29 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
|
// default: 'middle-finger',
|
||||||
required: true,
|
required: true,
|
||||||
validator(value) {
|
validator: (value) => validIcons.includes(value),
|
||||||
const valid = validIcons.includes(value);
|
|
||||||
if (!valid) {
|
|
||||||
console.warn(
|
|
||||||
`name property for feather-icon must be one of `,
|
|
||||||
validIcons
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
render(h) {
|
},
|
||||||
let icon = feather.icons[this.name];
|
render() {
|
||||||
return h('svg', {
|
const icon = feather.icons[this.name];
|
||||||
attrs: Object.assign({}, icon.attrs, {
|
const svg = h('svg', {
|
||||||
|
...Object.assign({}, icon.attrs, {
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
'stroke-linecap': 'round',
|
'stroke-linecap': 'round',
|
||||||
'stroke-linejoin': 'round',
|
'stroke-linejoin': 'round',
|
||||||
'stroke-width': 1.5,
|
'stroke-width': 1.5,
|
||||||
width: null,
|
width: null,
|
||||||
height: null
|
height: null,
|
||||||
}),
|
}),
|
||||||
on: this.$listeners,
|
|
||||||
class: [icon.attrs.class],
|
class: [icon.attrs.class],
|
||||||
domProps: {
|
innerHTML: icon.contents,
|
||||||
innerHTML: icon.contents
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
return svg;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://github.com/frappe/frappejs/commits/master/ui/components/FeatherIcon.vue
|
// https://github.com/frappe/frappejs/commits/master/ui/components/FeatherIcon.vue
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
<div slot="content">
|
<template v-slot:content>
|
||||||
|
<div>
|
||||||
<div class="p-3">
|
<div class="p-3">
|
||||||
<template v-if="filters.length">
|
<template v-if="filters.length">
|
||||||
<div
|
<div
|
||||||
@ -33,10 +34,10 @@
|
|||||||
placeholder: 'Field',
|
placeholder: 'Field',
|
||||||
fieldname: 'fieldname',
|
fieldname: 'fieldname',
|
||||||
fieldtype: 'Select',
|
fieldtype: 'Select',
|
||||||
options: fieldOptions
|
options: fieldOptions,
|
||||||
}"
|
}"
|
||||||
:value="filter.fieldname"
|
:value="filter.fieldname"
|
||||||
@change="value => (filter.fieldname = value)"
|
@change="(value) => (filter.fieldname = value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-2 w-24">
|
<div class="ml-2 w-24">
|
||||||
@ -47,10 +48,10 @@
|
|||||||
placeholder: 'Condition',
|
placeholder: 'Condition',
|
||||||
fieldname: 'condition',
|
fieldname: 'condition',
|
||||||
fieldtype: 'Select',
|
fieldtype: 'Select',
|
||||||
options: conditions
|
options: conditions,
|
||||||
}"
|
}"
|
||||||
:value="filter.condition"
|
:value="filter.condition"
|
||||||
@change="value => (filter.condition = value)"
|
@change="(value) => (filter.condition = value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-2 w-24">
|
<div class="ml-2 w-24">
|
||||||
@ -60,15 +61,23 @@
|
|||||||
:df="{
|
:df="{
|
||||||
placeholder: 'Value',
|
placeholder: 'Value',
|
||||||
fieldname: 'value',
|
fieldname: 'value',
|
||||||
fieldtype: 'Data'
|
fieldtype: 'Data',
|
||||||
}"
|
}"
|
||||||
:value="filter.value"
|
:value="filter.value"
|
||||||
@change="value => (filter.value = value)"
|
@change="(value) => (filter.value = value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="ml-2 cursor-pointer w-5 h-5 flex-center hover:bg-gray-100 rounded-md"
|
class="
|
||||||
|
ml-2
|
||||||
|
cursor-pointer
|
||||||
|
w-5
|
||||||
|
h-5
|
||||||
|
flex-center
|
||||||
|
hover:bg-gray-100
|
||||||
|
rounded-md
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<feather-icon
|
<feather-icon
|
||||||
name="x"
|
name="x"
|
||||||
@ -85,13 +94,24 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-base border-t px-3 py-2 flex items-center text-gray-600 cursor-pointer hover:bg-gray-100"
|
class="
|
||||||
|
text-base
|
||||||
|
border-t
|
||||||
|
px-3
|
||||||
|
py-2
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
text-gray-600
|
||||||
|
cursor-pointer
|
||||||
|
hover:bg-gray-100
|
||||||
|
"
|
||||||
@click="addNewFilter"
|
@click="addNewFilter"
|
||||||
>
|
>
|
||||||
<feather-icon name="plus" class="w-4 h-4" />
|
<feather-icon name="plus" class="w-4 h-4" />
|
||||||
<span class="ml-2">{{ t('Add a filter') }}</span>
|
<span class="ml-2">{{ t('Add a filter') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -109,7 +129,7 @@ let conditions = [
|
|||||||
{ label: 'Greater Than', value: '>' },
|
{ label: 'Greater Than', value: '>' },
|
||||||
{ label: 'Less Than', value: '<' },
|
{ label: 'Less Than', value: '<' },
|
||||||
{ label: 'Is Empty', value: 'is null' },
|
{ label: 'Is Empty', value: 'is null' },
|
||||||
{ label: 'Is Not Empty', value: 'is not null' }
|
{ label: 'Is Not Empty', value: 'is not null' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -118,12 +138,12 @@ export default {
|
|||||||
Popover,
|
Popover,
|
||||||
Button,
|
Button,
|
||||||
Icon,
|
Icon,
|
||||||
FormControl
|
FormControl,
|
||||||
},
|
},
|
||||||
props: ['fields'],
|
props: ['fields'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
filters: []
|
filters: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -138,11 +158,11 @@ export default {
|
|||||||
this.filters.push({ fieldname, condition, value });
|
this.filters.push({ fieldname, condition, value });
|
||||||
},
|
},
|
||||||
removeFilter(filter) {
|
removeFilter(filter) {
|
||||||
this.filters = this.filters.filter(f => f !== filter);
|
this.filters = this.filters.filter((f) => f !== filter);
|
||||||
},
|
},
|
||||||
setFilter(filters) {
|
setFilter(filters) {
|
||||||
this.filters = [];
|
this.filters = [];
|
||||||
Object.keys(filters).map(fieldname => {
|
Object.keys(filters).map((fieldname) => {
|
||||||
let parts = filters[fieldname];
|
let parts = filters[fieldname];
|
||||||
let condition, value;
|
let condition, value;
|
||||||
if (Array.isArray(parts)) {
|
if (Array.isArray(parts)) {
|
||||||
@ -166,24 +186,27 @@ export default {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
this.$emit('change', filters);
|
this.$emit('change', filters);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fieldOptions() {
|
fieldOptions() {
|
||||||
return this.fields.map(df => ({ label: df.label, value: df.fieldname }));
|
return this.fields.map((df) => ({
|
||||||
|
label: df.label,
|
||||||
|
value: df.fieldname,
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
conditions() {
|
conditions() {
|
||||||
return conditions;
|
return conditions;
|
||||||
},
|
},
|
||||||
activeFilterCount() {
|
activeFilterCount() {
|
||||||
return this.filters.filter(filter => filter.value).length;
|
return this.filters.filter((filter) => filter.value).length;
|
||||||
},
|
},
|
||||||
filterAppliedMessage() {
|
filterAppliedMessage() {
|
||||||
if (this.activeFilterCount === 1) {
|
if (this.activeFilterCount === 1) {
|
||||||
return this.t('1 filter applied');
|
return this.t('1 filter applied');
|
||||||
}
|
}
|
||||||
return this.t('{0} filters applied', [this.activeFilterCount]);
|
return this.t('{0} filters applied', [this.activeFilterCount]);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<component
|
<component
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners"
|
|
||||||
:is="iconComponent"
|
:is="iconComponent"
|
||||||
:class="iconClasses"
|
:class="iconClasses"
|
||||||
:active="active"
|
:active="active"
|
||||||
|
@ -55,7 +55,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
let listener = (e) => {
|
this.listener = (e) => {
|
||||||
let $els = [this.$refs.reference, this.$refs.popover];
|
let $els = [this.$refs.reference, this.$refs.popover];
|
||||||
let insideClick = $els.some(
|
let insideClick = $els.some(
|
||||||
($el) => $el && (e.target === $el || $el.contains(e.target))
|
($el) => $el && (e.target === $el || $el.contains(e.target))
|
||||||
@ -65,15 +65,17 @@ export default {
|
|||||||
}
|
}
|
||||||
this.close();
|
this.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.show == null) {
|
if (this.show == null) {
|
||||||
document.addEventListener('click', listener);
|
document.addEventListener('click', this.listener);
|
||||||
this.$once('hook:beforeDestroy', () => {
|
|
||||||
document.removeEventListener('click', listener);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.popper && this.popper.destroy();
|
this.popper && this.popper.destroy();
|
||||||
|
if (this.listener) {
|
||||||
|
document.removeEventListener('click', this.listener);
|
||||||
|
delete this.listener;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setupPopper() {
|
setupPopper() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="inline-grid border-b" :style="style" v-on="$listeners">
|
<div class="inline-grid border-b" :style="style" v-bind="$attrs">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -9,21 +9,21 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
columnWidth: {
|
columnWidth: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '1fr'
|
default: '1fr',
|
||||||
},
|
},
|
||||||
columnCount: {
|
columnCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
ratio: {
|
ratio: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
},
|
},
|
||||||
gridTemplateColumns: {
|
gridTemplateColumns: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
gap: String
|
gap: String,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
@ -33,7 +33,9 @@ export default {
|
|||||||
obj['grid-template-columns'] = `repeat(${this.columnCount}, ${this.columnWidth})`;
|
obj['grid-template-columns'] = `repeat(${this.columnCount}, ${this.columnWidth})`;
|
||||||
}
|
}
|
||||||
if (this.ratio.length) {
|
if (this.ratio.length) {
|
||||||
obj['grid-template-columns'] = this.ratio.map(r => `${r}fr`).join(' ');
|
obj['grid-template-columns'] = this.ratio
|
||||||
|
.map((r) => `${r}fr`)
|
||||||
|
.join(' ');
|
||||||
}
|
}
|
||||||
if (this.gridTemplateColumns) {
|
if (this.gridTemplateColumns) {
|
||||||
obj['grid-template-columns'] = this.gridTemplateColumns;
|
obj['grid-template-columns'] = this.gridTemplateColumns;
|
||||||
@ -43,7 +45,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -48,9 +48,10 @@
|
|||||||
:class="isActiveGroup(group) && !group.items ? 'bg-white' : ''"
|
:class="isActiveGroup(group) && !group.items ? 'bg-white' : ''"
|
||||||
@click="onGroupClick(group)"
|
@click="onGroupClick(group)"
|
||||||
>
|
>
|
||||||
<component
|
<Icon
|
||||||
:is="group.icon"
|
:name="group.icon"
|
||||||
class="w-5 h-5"
|
:size="group.iconSize || '18'"
|
||||||
|
:height="group.iconHeight"
|
||||||
:active="isActiveGroup(group)"
|
:active="isActiveGroup(group)"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
@ -102,6 +103,7 @@ import WindowControls from './WindowControls';
|
|||||||
import { routeTo } from '@/utils';
|
import { routeTo } from '@/utils';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import router from '../router';
|
import router from '../router';
|
||||||
|
import Icon from './Icon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: [Button],
|
components: [Button],
|
||||||
@ -123,6 +125,7 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
WindowControls,
|
WindowControls,
|
||||||
|
Icon,
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.companyName = await sidebarConfig.getTitle();
|
this.companyName = await sidebarConfig.getTitle();
|
||||||
@ -168,7 +171,7 @@ export default {
|
|||||||
routeTo,
|
routeTo,
|
||||||
reportIssue,
|
reportIssue,
|
||||||
setActiveGroup() {
|
setActiveGroup() {
|
||||||
const { fullPath } = this.$router.currentRoute;
|
const { fullPath } = this.$router.currentRoute.value;
|
||||||
const fallBackGroup = this.activeGroup;
|
const fallBackGroup = this.activeGroup;
|
||||||
this.activeGroup = this.groups.find((g) => {
|
this.activeGroup = this.groups.find((g) => {
|
||||||
if (fullPath.startsWith(g.route) && g.route !== '/') {
|
if (fullPath.startsWith(g.route) && g.route !== '/') {
|
||||||
|
52
src/main.js
52
src/main.js
@ -1,6 +1,6 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import Vue from 'vue';
|
import { createApp } from 'vue';
|
||||||
import models from '../models';
|
import models from '../models';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import FeatherIcon from './components/FeatherIcon';
|
import FeatherIcon from './components/FeatherIcon';
|
||||||
@ -26,12 +26,29 @@ import { showToast, stringifyCircular } from './utils';
|
|||||||
window.frappe = frappe;
|
window.frappe = frappe;
|
||||||
window.frappe.store = {};
|
window.frappe.store = {};
|
||||||
|
|
||||||
|
window.onerror = (message, source, lineno, colno, error) => {
|
||||||
|
error = error ?? new Error('triggered in window.onerror');
|
||||||
|
handleError(true, error, { message, source, lineno, colno });
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on('unhandledRejection', (error) => {
|
||||||
|
handleError(true, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('uncaughtException', (error) => {
|
||||||
|
handleError(true, error, () => process.exit(1));
|
||||||
|
});
|
||||||
|
|
||||||
registerIpcRendererListeners();
|
registerIpcRendererListeners();
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
const app = createApp({
|
||||||
Vue.component('feather-icon', FeatherIcon);
|
template: '<App/>',
|
||||||
Vue.directive('on-outside-click', outsideClickDirective);
|
});
|
||||||
Vue.mixin({
|
app.use(router);
|
||||||
|
app.component('App', App);
|
||||||
|
app.component('feather-icon', FeatherIcon);
|
||||||
|
app.directive('on-outside-click', outsideClickDirective);
|
||||||
|
app.mixin({
|
||||||
computed: {
|
computed: {
|
||||||
frappe() {
|
frappe() {
|
||||||
return frappe;
|
return frappe;
|
||||||
@ -50,7 +67,7 @@ import { showToast, stringifyCircular } from './utils';
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.config.errorHandler = (err, vm, info) => {
|
app.config.errorHandler = (err, vm, info) => {
|
||||||
const more = {
|
const more = {
|
||||||
info,
|
info,
|
||||||
};
|
};
|
||||||
@ -67,28 +84,7 @@ import { showToast, stringifyCircular } from './utils';
|
|||||||
console.error(err, vm, info);
|
console.error(err, vm, info);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.onerror = (message, source, lineno, colno, error) => {
|
app.mount('#app');
|
||||||
error = error ?? new Error('triggered in window.onerror');
|
|
||||||
handleError(true, error, { message, source, lineno, colno });
|
|
||||||
};
|
|
||||||
|
|
||||||
process.on('unhandledRejection', (error) => {
|
|
||||||
handleError(true, error);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('uncaughtException', (error) => {
|
|
||||||
handleError(true, error, () => process.exit(1));
|
|
||||||
});
|
|
||||||
|
|
||||||
/* eslint-disable no-new */
|
|
||||||
new Vue({
|
|
||||||
el: '#app',
|
|
||||||
router,
|
|
||||||
components: {
|
|
||||||
App,
|
|
||||||
},
|
|
||||||
template: '<App/>',
|
|
||||||
});
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function registerIpcRendererListeners() {
|
function registerIpcRendererListeners() {
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col overflow-y-hidden">
|
<div class="flex flex-col overflow-y-hidden">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<h1 slot="title" class="text-2xl font-bold">
|
<template v-slot:title>
|
||||||
|
<h1 class="text-2xl font-bold">
|
||||||
{{ t('Chart of Accounts') }}
|
{{ t('Chart of Accounts') }}
|
||||||
</h1>
|
</h1>
|
||||||
<template slot="actions">
|
</template>
|
||||||
|
<template v-slot:actions>
|
||||||
<SearchBar class="ml-2" />
|
<SearchBar class="ml-2" />
|
||||||
</template>
|
</template>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div class="flex-1 flex px-8 overflow-y-auto">
|
<div class="flex-1 flex px-8 overflow-y-auto">
|
||||||
<div class="flex-1" v-if="root">
|
<div class="flex-1" v-if="root">
|
||||||
<template v-for="account in allAccounts">
|
|
||||||
<div
|
<div
|
||||||
|
v-for="account in allAccounts"
|
||||||
|
:key="account.name"
|
||||||
class="
|
class="
|
||||||
mt-2
|
mt-2
|
||||||
px-4
|
px-4
|
||||||
@ -25,7 +28,6 @@
|
|||||||
account.level !== 0 ? 'text-base' : 'text-lg',
|
account.level !== 0 ? 'text-base' : 'text-lg',
|
||||||
isQuickEditOpen(account) ? 'bg-gray-200' : '',
|
isQuickEditOpen(account) ? 'bg-gray-200' : '',
|
||||||
]"
|
]"
|
||||||
:key="account.name"
|
|
||||||
@click="onClick(account)"
|
@click="onClick(account)"
|
||||||
>
|
>
|
||||||
<div class="flex items-center" :class="`pl-${account.level * 8}`">
|
<div class="flex items-center" :class="`pl-${account.level * 8}`">
|
||||||
@ -37,10 +39,7 @@
|
|||||||
>
|
>
|
||||||
{{ account.name }}
|
{{ account.name }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-if="account.isGroup" class="ml-6 hidden group-hover:block">
|
||||||
v-if="account.isGroup"
|
|
||||||
class="ml-6 hidden group-hover:block"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
class="
|
class="
|
||||||
text-xs text-gray-800
|
text-xs text-gray-800
|
||||||
@ -122,7 +121,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<h1 slot="title" class="text-2xl font-bold">{{ t('Dashboard') }}</h1>
|
<template v-slot:title>
|
||||||
<template slot="actions">
|
<h1 class="text-2xl font-bold">{{ t('Dashboard') }}</h1>
|
||||||
|
</template>
|
||||||
|
<template v-slot:actions>
|
||||||
<SearchBar class="ml-2" />
|
<SearchBar class="ml-2" />
|
||||||
</template>
|
</template>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<SectionHeader>
|
<SectionHeader>
|
||||||
<template slot="title">{{ t('Top Expenses') }}</template>
|
<template v-slot:title>{{ t('Top Expenses') }}</template>
|
||||||
<PeriodSelector
|
<template v-slot:action>
|
||||||
slot="action"
|
<PeriodSelector :value="period" @change="(value) => (period = value)" />
|
||||||
:value="period"
|
</template>
|
||||||
@change="(value) => (period = value)"
|
|
||||||
/>
|
|
||||||
</SectionHeader>
|
</SectionHeader>
|
||||||
<div class="flex relative" v-show="hasData">
|
<div class="flex relative" v-show="hasData">
|
||||||
<div class="w-1/2">
|
<div class="w-1/2">
|
||||||
@ -38,7 +36,10 @@
|
|||||||
@change="(value) => (active = value)"
|
@change="(value) => (active = value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expenses.length === 0" class="flex-1 w-full h-full flex-center my-20">
|
<div
|
||||||
|
v-if="expenses.length === 0"
|
||||||
|
class="flex-1 w-full h-full flex-center my-20"
|
||||||
|
>
|
||||||
<span class="text-base text-gray-600">
|
<span class="text-base text-gray-600">
|
||||||
{{ t('No expenses in this period') }}
|
{{ t('No expenses in this period') }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<SectionHeader>
|
<SectionHeader>
|
||||||
<template slot="title">{{ t('Profit and Loss') }}</template>
|
<template v-slot:title>{{ t('Profit and Loss') }}</template>
|
||||||
|
<template v-slot:action>
|
||||||
<PeriodSelector
|
<PeriodSelector
|
||||||
slot="action"
|
|
||||||
:value="period"
|
:value="period"
|
||||||
:options="['This Year', 'This Quarter']"
|
:options="['This Year', 'This Quarter']"
|
||||||
@change="(value) => (period = value)"
|
@change="(value) => (period = value)"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
</SectionHeader>
|
</SectionHeader>
|
||||||
<BarChart
|
<BarChart
|
||||||
v-if="hasData"
|
v-if="hasData"
|
||||||
|
@ -6,22 +6,22 @@
|
|||||||
:key="invoice.title"
|
:key="invoice.title"
|
||||||
>
|
>
|
||||||
<SectionHeader>
|
<SectionHeader>
|
||||||
<template slot="title">{{ invoice.title }}</template>
|
<template v-slot:title>{{ invoice.title }}</template>
|
||||||
|
<template v-slot:action>
|
||||||
<PeriodSelector
|
<PeriodSelector
|
||||||
v-if="invoice.hasData"
|
v-if="invoice.hasData"
|
||||||
slot="action"
|
|
||||||
:value="$data[invoice.periodKey]"
|
:value="$data[invoice.periodKey]"
|
||||||
@change="(value) => ($data[invoice.periodKey] = value)"
|
@change="(value) => ($data[invoice.periodKey] = value)"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-else
|
v-else
|
||||||
slot="action"
|
|
||||||
:icon="true"
|
:icon="true"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="newInvoice(invoice)"
|
@click="newInvoice(invoice)"
|
||||||
>
|
>
|
||||||
<feather-icon name="plus" class="w-4 h-4 text-white" />
|
<feather-icon name="plus" class="w-4 h-4 text-white" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</template>
|
||||||
</SectionHeader>
|
</SectionHeader>
|
||||||
<div>
|
<div>
|
||||||
<div class="mt-6 flex justify-between">
|
<div class="mt-6 flex justify-between">
|
||||||
|
@ -5,17 +5,23 @@
|
|||||||
@change-db-file="$emit('change-db-file')"
|
@change-db-file="$emit('change-db-file')"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-1 overflow-y-hidden bg-white">
|
<div class="flex flex-1 overflow-y-hidden bg-white">
|
||||||
|
<router-view class="flex-1" :key="$route.path" v-slot="{ Component }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<router-view class="flex-1" :key="$route.path" />
|
<component :is="Component" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
|
|
||||||
<div class="flex" v-if="showQuickEdit">
|
<div class="flex" v-if="showQuickEdit">
|
||||||
<keep-alive>
|
|
||||||
<router-view
|
<router-view
|
||||||
name="edit"
|
name="edit"
|
||||||
class="w-80 flex-1"
|
class="w-80 flex-1"
|
||||||
:key="$route.query.doctype + $route.query.name"
|
:key="$route.query.doctype + $route.query.name"
|
||||||
/>
|
v-slot="{ Component }"
|
||||||
|
>
|
||||||
|
<keep-alive>
|
||||||
|
<component :is="Component" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="toast-container"
|
id="toast-container"
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col overflow-y-hidden">
|
<div class="flex flex-col overflow-y-hidden">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<h1 slot="title" class="text-2xl font-bold">
|
<template v-slot:title>
|
||||||
|
<h1 class="text-2xl font-bold">
|
||||||
{{ t('Setup your workspace') }}
|
{{ t('Setup your workspace') }}
|
||||||
</h1>
|
</h1>
|
||||||
|
</template>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div class="px-8">
|
<div class="px-8">
|
||||||
<div class="border-t"></div>
|
<div class="border-t"></div>
|
||||||
@ -91,6 +93,7 @@ import { openSettings } from '@/utils';
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { IPC_MESSAGES } from '@/messages';
|
import { IPC_MESSAGES } from '@/messages';
|
||||||
import { routeTo } from '@/utils';
|
import { routeTo } from '@/utils';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'GetStarted',
|
name: 'GetStarted',
|
||||||
@ -391,9 +394,9 @@ export default {
|
|||||||
let size = completed ? '24' : '18';
|
let size = completed ? '24' : '18';
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
render(h) {
|
render() {
|
||||||
return h(Icon, {
|
return h(Icon, {
|
||||||
props: Object.assign(
|
...Object.assign(
|
||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
size,
|
size,
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col" v-if="doc">
|
<div class="flex flex-col" v-if="doc">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<BackLink slot="title" />
|
<template v-slot:title>
|
||||||
<template slot="actions">
|
<BackLink />
|
||||||
|
</template>
|
||||||
|
<template v-slot:actions>
|
||||||
<StatusBadge :status="status" />
|
<StatusBadge :status="status" />
|
||||||
<Button
|
<Button
|
||||||
v-if="doc.submitted"
|
v-if="doc.submitted"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<BackLink slot="title" />
|
<template v-slot:title>
|
||||||
<template slot="actions" v-if="doc">
|
<BackLink />
|
||||||
|
</template>
|
||||||
|
<template v-slot:actions v-if="doc">
|
||||||
<StatusBadge :status="status" />
|
<StatusBadge :status="status" />
|
||||||
<DropdownWithActions class="ml-2" :actions="actions" />
|
<DropdownWithActions class="ml-2" :actions="actions" />
|
||||||
<Button
|
<Button
|
||||||
@ -123,11 +125,7 @@ import DropdownWithActions from '@/components/DropdownWithActions';
|
|||||||
import FormControl from '@/components/Controls/FormControl';
|
import FormControl from '@/components/Controls/FormControl';
|
||||||
import BackLink from '@/components/BackLink';
|
import BackLink from '@/components/BackLink';
|
||||||
import StatusBadge from '@/components/StatusBadge';
|
import StatusBadge from '@/components/StatusBadge';
|
||||||
import {
|
import { showMessageDialog, getActionsForDocument, routeTo } from '@/utils';
|
||||||
showMessageDialog,
|
|
||||||
getActionsForDocument,
|
|
||||||
routeTo,
|
|
||||||
} from '@/utils';
|
|
||||||
import { handleErrorWithDialog } from '../errorHandling';
|
import { handleErrorWithDialog } from '../errorHandling';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<h1 slot="title" class="text-2xl font-bold" v-if="title">{{ title }}</h1>
|
<template v-slot:title>
|
||||||
<template slot="actions">
|
<h1 class="text-2xl font-bold" v-if="title">
|
||||||
|
{{ title }}
|
||||||
|
</h1>
|
||||||
|
</template>
|
||||||
|
<template v-slot:actions>
|
||||||
<FilterDropdown
|
<FilterDropdown
|
||||||
ref="filterDropdown"
|
ref="filterDropdown"
|
||||||
@change="applyFilter"
|
@change="applyFilter"
|
||||||
@ -35,7 +39,7 @@ import List from './List';
|
|||||||
import listConfigs from './listConfig';
|
import listConfigs from './listConfig';
|
||||||
// import Icon from '@/components/Icon';
|
// import Icon from '@/components/Icon';
|
||||||
import FilterDropdown from '@/components/FilterDropdown';
|
import FilterDropdown from '@/components/FilterDropdown';
|
||||||
import { routeTo } from '@/utils'
|
import { routeTo } from '@/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListView',
|
name: 'ListView',
|
||||||
@ -46,7 +50,7 @@ export default {
|
|||||||
Button,
|
Button,
|
||||||
SearchBar,
|
SearchBar,
|
||||||
// Icon,
|
// Icon,
|
||||||
FilterDropdown
|
FilterDropdown,
|
||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
if (typeof this.filters === 'object') {
|
if (typeof this.filters === 'object') {
|
||||||
@ -83,10 +87,10 @@ export default {
|
|||||||
query: {
|
query: {
|
||||||
edit: 1,
|
edit: 1,
|
||||||
doctype: this.doctype,
|
doctype: this.doctype,
|
||||||
name
|
name,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
meta() {
|
meta() {
|
||||||
@ -99,13 +103,13 @@ export default {
|
|||||||
return {
|
return {
|
||||||
title: this.doctype,
|
title: this.doctype,
|
||||||
doctype: this.doctype,
|
doctype: this.doctype,
|
||||||
columns: this.meta.getKeywordFields()
|
columns: this.meta.getKeywordFields(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title() {
|
title() {
|
||||||
return this.listConfig.title || this.doctype;
|
return this.listConfig.title || this.doctype;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex flex-col flex-1">
|
<div class="flex flex-col flex-1">
|
||||||
<PageHeader class="bg-white z-10">
|
<PageHeader class="bg-white z-10">
|
||||||
<BackLink slot="title" />
|
<template v-slot:title>
|
||||||
<template slot="actions">
|
<BackLink />
|
||||||
|
</template>
|
||||||
|
<template v-slot:actions>
|
||||||
<Button
|
<Button
|
||||||
class="text-gray-900 text-xs ml-2"
|
class="text-gray-900 text-xs ml-2"
|
||||||
@click="showCustomiser = !showCustomiser"
|
@click="showCustomiser = !showCustomiser"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col max-w-full">
|
<div class="flex flex-col max-w-full">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<h1 slot="title" class="text-2xl font-bold">{{ report.title }}</h1>
|
<template v-slot:title>
|
||||||
<template slot="actions">
|
<h1 class="text-2xl font-bold">{{ report.title }}</h1>
|
||||||
|
</template>
|
||||||
|
<template v-slot:actions>
|
||||||
<DropdownWithActions
|
<DropdownWithActions
|
||||||
v-for="group of actionGroups"
|
v-for="group of actionGroups"
|
||||||
@click="group.action(reportData, filters)"
|
@click="group.action(reportData, filters)"
|
||||||
@ -104,6 +106,7 @@ import WithScroll from '@/components/WithScroll';
|
|||||||
import FormControl from '@/components/Controls/FormControl';
|
import FormControl from '@/components/Controls/FormControl';
|
||||||
import DropdownWithActions from '../components/DropdownWithActions.vue';
|
import DropdownWithActions from '../components/DropdownWithActions.vue';
|
||||||
import reportViewConfig from '@/../reports/view';
|
import reportViewConfig from '@/../reports/view';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Report',
|
name: 'Report',
|
||||||
@ -161,7 +164,10 @@ export default {
|
|||||||
rows = data;
|
rows = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reportData.columns = this.report.getColumns({ filters: this.filters, data });
|
this.reportData.columns = this.report.getColumns({
|
||||||
|
filters: this.filters,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
if (!rows) {
|
if (!rows) {
|
||||||
rows = [];
|
rows = [];
|
||||||
@ -250,7 +256,7 @@ export default {
|
|||||||
? frappe.format(cellValue, column)
|
? frappe.format(cellValue, column)
|
||||||
: '';
|
: '';
|
||||||
return {
|
return {
|
||||||
render(h) {
|
render() {
|
||||||
return h('span', formattedValue);
|
return h('span', formattedValue);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col overflow-hidden">
|
<div class="flex flex-col overflow-hidden">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<h1 slot="title" class="text-2xl font-bold">
|
<template v-slot:title>
|
||||||
|
<h1 class="text-2xl font-bold">
|
||||||
{{ t('Settings') }}
|
{{ t('Settings') }}
|
||||||
</h1>
|
</h1>
|
||||||
|
</template>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div class="flex justify-center flex-1 mb-8 mt-2">
|
<div class="flex justify-center flex-1 mb-8 mt-2">
|
||||||
<div
|
<div
|
||||||
@ -60,6 +62,7 @@ import PageHeader from '@/components/PageHeader';
|
|||||||
import StatusBadge from '@/components/StatusBadge';
|
import StatusBadge from '@/components/StatusBadge';
|
||||||
import { callInitializeMoneyMaker } from '../../utils';
|
import { callInitializeMoneyMaker } from '../../utils';
|
||||||
import { showToast } from '../../utils';
|
import { showToast } from '../../utils';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
@ -138,10 +141,10 @@ export default {
|
|||||||
},
|
},
|
||||||
getIconComponent(tab) {
|
getIconComponent(tab) {
|
||||||
return {
|
return {
|
||||||
render(h) {
|
render() {
|
||||||
return h(Icon, {
|
return h(Icon, {
|
||||||
class: 'w-6 h-6',
|
class: 'w-6 h-6',
|
||||||
props: Object.assign(
|
...Object.assign(
|
||||||
{
|
{
|
||||||
name: tab.icon,
|
name: tab.icon,
|
||||||
size: '24',
|
size: '24',
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
:autofocus="true"
|
:autofocus="true"
|
||||||
/>
|
/>
|
||||||
<Popover placement="auto" :show-popup="Boolean(emailError)">
|
<Popover placement="auto" :show-popup="Boolean(emailError)">
|
||||||
<template slot="target">
|
<template v-slot:target>
|
||||||
<FormControl
|
<FormControl
|
||||||
:df="meta.getField('email')"
|
:df="meta.getField('email')"
|
||||||
:value="doc.email"
|
:value="doc.email"
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template slot="content">
|
<template v-slot:content>
|
||||||
<div class="p-2 text-sm">
|
<div class="p-2 text-sm">
|
||||||
{{ emailError }}
|
{{ emailError }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,12 +10,7 @@ import PrintView from '@/pages/PrintView/PrintView';
|
|||||||
import QuickEditForm from '@/pages/QuickEditForm';
|
import QuickEditForm from '@/pages/QuickEditForm';
|
||||||
import Report from '@/pages/Report';
|
import Report from '@/pages/Report';
|
||||||
import Settings from '@/pages/Settings/Settings';
|
import Settings from '@/pages/Settings/Settings';
|
||||||
import Vue from 'vue';
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
import Router from 'vue-router';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Vue.use(Router);
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -107,7 +102,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let router = new Router({ routes });
|
let router = createRouter({ routes, history: createWebHistory() });
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
window.router = router;
|
window.router = router;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
import Icon from './components/Icon';
|
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
getTitle: async () => {
|
getTitle: async () => {
|
||||||
@ -10,16 +9,18 @@ const config = {
|
|||||||
{
|
{
|
||||||
title: t('Get Started'),
|
title: t('Get Started'),
|
||||||
route: '/get-started',
|
route: '/get-started',
|
||||||
icon: getIcon('general', '24', '5'),
|
icon: 'general',
|
||||||
|
iconSize: '24',
|
||||||
|
iconHeight: '5',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Dashboard'),
|
title: t('Dashboard'),
|
||||||
route: '/',
|
route: '/',
|
||||||
icon: getIcon('dashboard'),
|
icon: 'dashboard',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Sales'),
|
title: t('Sales'),
|
||||||
icon: getIcon('sales'),
|
icon: 'sales',
|
||||||
route: '/list/SalesInvoice',
|
route: '/list/SalesInvoice',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Purchases'),
|
title: t('Purchases'),
|
||||||
icon: getIcon('purchase'),
|
icon: 'purchase',
|
||||||
route: '/list/PurchaseInvoice',
|
route: '/list/PurchaseInvoice',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -53,7 +54,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Common'),
|
title: t('Common'),
|
||||||
icon: getIcon('common-entries'),
|
icon: 'common-entries',
|
||||||
route: '/list/Item',
|
route: '/list/Item',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -75,7 +76,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Reports'),
|
title: t('Reports'),
|
||||||
icon: getIcon('reports'),
|
icon: 'reports',
|
||||||
route: '/report/general-ledger',
|
route: '/report/general-ledger',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -108,7 +109,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Setup'),
|
title: t('Setup'),
|
||||||
icon: getIcon('settings'),
|
icon: 'settings',
|
||||||
route: '/chart-of-accounts',
|
route: '/chart-of-accounts',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -129,22 +130,4 @@ const config = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
function getIcon(name, size = '18', height = null) {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
render(h) {
|
|
||||||
return h(Icon, {
|
|
||||||
props: Object.assign(
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
size,
|
|
||||||
height,
|
|
||||||
},
|
|
||||||
this.$attrs
|
|
||||||
),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
@ -5,7 +5,7 @@ import { ipcRenderer } from 'electron';
|
|||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
import { isPesa } from 'frappe/utils';
|
import { isPesa } from 'frappe/utils';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import Vue from 'vue';
|
import { createApp } from 'vue';
|
||||||
import { handleErrorWithDialog } from './errorHandling';
|
import { handleErrorWithDialog } from './errorHandling';
|
||||||
import { IPC_ACTIONS, IPC_MESSAGES } from './messages';
|
import { IPC_ACTIONS, IPC_MESSAGES } from './messages';
|
||||||
|
|
||||||
@ -333,7 +333,7 @@ export async function getSavePath(name, extention) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function showToast(props) {
|
export function showToast(props) {
|
||||||
new Vue({
|
createApp({
|
||||||
el: '#toast-target',
|
el: '#toast-target',
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
return createElement(Toast, { props });
|
return createElement(Toast, { props });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user