mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
commit
794ababe0f
@ -6,6 +6,7 @@ const {
|
|||||||
DEFAULT_INTERNAL_PRECISION,
|
DEFAULT_INTERNAL_PRECISION,
|
||||||
DEFAULT_DISPLAY_PRECISION,
|
DEFAULT_DISPLAY_PRECISION,
|
||||||
} = require('./utils/consts');
|
} = require('./utils/consts');
|
||||||
|
const { markRaw } = require('vue');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
initializeAndRegister(customModels = {}, force = false) {
|
initializeAndRegister(customModels = {}, force = false) {
|
||||||
@ -55,7 +56,12 @@ module.exports = {
|
|||||||
display = parseInt(display);
|
display = parseInt(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pesa = getMoneyMaker({ currency, precision, display });
|
this.pesa = getMoneyMaker({
|
||||||
|
currency,
|
||||||
|
precision,
|
||||||
|
display,
|
||||||
|
wrapper: markRaw,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
init(force) {
|
init(force) {
|
||||||
|
@ -2,18 +2,28 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
<div class="col-8 mx-auto text-right mt-4">
|
<div class="col-8 mx-auto text-right mt-4">
|
||||||
<f-button primary @click="$emit('makePDF', $refs.printComponent.innerHTML)">{{ t('PDF') }}</f-button>
|
<f-button
|
||||||
|
primary
|
||||||
|
@click="$emit('makePDF', $refs.printComponent.innerHTML)"
|
||||||
|
>{{ t('PDF') }}</f-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="printComponent" class="col-8 bg-white mt-4 mx-auto border shadow">
|
<div ref="printComponent" class="col-8 bg-white mt-4 mx-auto border shadow">
|
||||||
<div class="print-format" style="padding: 3.5rem; font-size: 8pt !important;">
|
<div
|
||||||
|
class="print-format"
|
||||||
|
style="padding: 3.5rem; font-size: 8pt !important"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-center">GSTR3B-Form</h3>
|
<h3 class="text-center">GSTR3B-Form</h3>
|
||||||
<h5>GSTIN: {{ jsonData.gstin }}</h5>
|
<h5>GSTIN: {{ jsonData.gstin }}</h5>
|
||||||
<h5>Period: {{ jsonData.ret_period }}</h5>
|
<h5>Period: {{ jsonData.ret_period }}</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5>3.1 Details of Outward Supplies and inward supplies liable to reverse charge</h5>
|
<h5>
|
||||||
|
3.1 Details of Outward Supplies and inward supplies liable
|
||||||
|
to reverse charge
|
||||||
|
</h5>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -27,7 +37,10 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>(a) Outward taxable supplies(other than zero rated, nil rated and exempted</td>
|
<td>
|
||||||
|
(a) Outward taxable supplies(other than zero rated, nil rated
|
||||||
|
and exempted
|
||||||
|
</td>
|
||||||
<td class="right">{{ jsonData.sup_details.osup_det.txval }}</td>
|
<td class="right">{{ jsonData.sup_details.osup_det.txval }}</td>
|
||||||
<td class="right">{{ jsonData.sup_details.osup_det.iamt }}</td>
|
<td class="right">{{ jsonData.sup_details.osup_det.iamt }}</td>
|
||||||
<td class="right">{{ jsonData.sup_details.osup_det.camt }}</td>
|
<td class="right">{{ jsonData.sup_details.osup_det.camt }}</td>
|
||||||
@ -44,7 +57,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>(b) Other outward supplies(Nil rated,Exempted)</td>
|
<td>(b) Other outward supplies(Nil rated,Exempted)</td>
|
||||||
<td class="right">{{ jsonData.sup_details.osup_nil_exmp.txval }}</td>
|
<td class="right">
|
||||||
|
{{ jsonData.sup_details.osup_nil_exmp.txval }}
|
||||||
|
</td>
|
||||||
<td class="disabled"></td>
|
<td class="disabled"></td>
|
||||||
<td class="disabled"></td>
|
<td class="disabled"></td>
|
||||||
<td class="disabled"></td>
|
<td class="disabled"></td>
|
||||||
@ -60,7 +75,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>(e) Non-GST outward supplies</td>
|
<td>(e) Non-GST outward supplies</td>
|
||||||
<td class="right">{{ jsonData.sup_details.osup_nongst.txval }}</td>
|
<td class="right">
|
||||||
|
{{ jsonData.sup_details.osup_nongst.txval }}
|
||||||
|
</td>
|
||||||
<td class="disabled"></td>
|
<td class="disabled"></td>
|
||||||
<td class="disabled"></td>
|
<td class="disabled"></td>
|
||||||
<td class="disabled"></td>
|
<td class="disabled"></td>
|
||||||
@ -69,7 +86,11 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h5>3.2 Of the supplies shown in 3.1 (a) above, details of inter-State supplies made to unregisterd persons, composition taxable persons and UIN holders</h5>
|
<h5>
|
||||||
|
3.2 Of the supplies shown in 3.1 (a) above, details of
|
||||||
|
inter-State supplies made to unregisterd persons, composition taxable
|
||||||
|
persons and UIN holders
|
||||||
|
</h5>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -83,17 +104,26 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Supplies made to Unregistered Persons</td>
|
<td>Supplies made to Unregistered Persons</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.unreg_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.unreg_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.pos }}</p>
|
<p>{{ row.pos }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.unreg_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.unreg_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.txval }}</p>
|
<p>{{ row.txval }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.unreg_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.unreg_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.iamt }}</p>
|
<p>{{ row.iamt }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -101,17 +131,26 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Suppliies made to Composition Taxable Persons</td>
|
<td>Suppliies made to Composition Taxable Persons</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.comp_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.comp_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.pos }}</p>
|
<p>{{ row.pos }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.comp_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.comp_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.txval }}</p>
|
<p>{{ row.txval }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.comp_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.comp_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.iamt }}</p>
|
<p>{{ row.iamt }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -119,17 +158,26 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Supplies made to UIN holders</td>
|
<td>Supplies made to UIN holders</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.uin_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.uin_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.pos }}</p>
|
<p>{{ row.pos }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.uin_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.uin_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.txval }}</p>
|
<p>{{ row.txval }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="right">
|
<td class="right">
|
||||||
<div v-for="(row, i) in jsonData.inter_sup.uin_details" :key="i">
|
<div
|
||||||
|
v-for="(row, i) in jsonData.inter_sup.uin_details"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
<p>{{ row.iamt }}</p>
|
<p>{{ row.iamt }}</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -173,7 +221,10 @@
|
|||||||
<td class="right">{{ jsonData.itc_elg.itc_avl[1].csamt }}</td>
|
<td class="right">{{ jsonData.itc_elg.itc_avl[1].csamt }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> (3) Inward supplies liable to reverse charge (other than 1 & 2 above)</td>
|
<td>
|
||||||
|
(3) Inward supplies liable to reverse charge (other than
|
||||||
|
1 & 2 above)
|
||||||
|
</td>
|
||||||
<td class="right">{{ jsonData.itc_elg.itc_avl[2].iamt }}</td>
|
<td class="right">{{ jsonData.itc_elg.itc_avl[2].iamt }}</td>
|
||||||
<td class="right">{{ jsonData.itc_elg.itc_avl[2].camt }}</td>
|
<td class="right">{{ jsonData.itc_elg.itc_avl[2].camt }}</td>
|
||||||
<td class="right">{{ jsonData.itc_elg.itc_avl[2].samt }}</td>
|
<td class="right">{{ jsonData.itc_elg.itc_avl[2].samt }}</td>
|
||||||
@ -251,7 +302,10 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h5>5. Values of exempt, nil rated and non-GST inward supplies</h5>
|
<h5>
|
||||||
|
5. Values of exempt, nil rated and non-GST inward
|
||||||
|
supplies
|
||||||
|
</h5>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -262,14 +316,24 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>From a supplier under composition scheme, Exempt and Nil rated</td>
|
<td>
|
||||||
<td class="right">{{ jsonData.inward_sup.isup_details[0].inter }}</td>
|
From a supplier under composition scheme, Exempt and Nil rated
|
||||||
<td class="right">{{ jsonData.inward_sup.isup_details[0].intra }}</td>
|
</td>
|
||||||
|
<td class="right">
|
||||||
|
{{ jsonData.inward_sup.isup_details[0].inter }}
|
||||||
|
</td>
|
||||||
|
<td class="right">
|
||||||
|
{{ jsonData.inward_sup.isup_details[0].intra }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Non GST Inward Supplies</td>
|
<td>Non GST Inward Supplies</td>
|
||||||
<td class="right">{{ jsonData.inward_sup.isup_details[1].inter }}</td>
|
<td class="right">
|
||||||
<td class="right">{{ jsonData.inward_sup.isup_details[1].intra }}</td>
|
{{ jsonData.inward_sup.isup_details[1].inter }}
|
||||||
|
</td>
|
||||||
|
<td class="right">
|
||||||
|
{{ jsonData.inward_sup.isup_details[1].intra }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -282,11 +346,12 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'GSTR3BPrintView',
|
name: 'GSTR3BPrintView',
|
||||||
props: ['doc'],
|
props: ['doc'],
|
||||||
|
emits: ['makePDF'],
|
||||||
computed: {
|
computed: {
|
||||||
jsonData() {
|
jsonData() {
|
||||||
return JSON.parse(this.doc.jsonData);
|
return JSON.parse(this.doc.jsonData);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -300,4 +365,3 @@ export default {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -3,9 +3,19 @@
|
|||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto"></div>
|
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto"></div>
|
||||||
<div class="col-8 mx-auto text-right mt-4">
|
<div class="col-8 mx-auto text-right mt-4">
|
||||||
<f-button primary @click="$emit('send', $refs.printComponent.innerHTML)">{{ t('Send') }}</f-button>
|
<f-button
|
||||||
<f-button secondary @click="toggleCustomizer">{{ t('Customize') }}</f-button>
|
primary
|
||||||
<f-button secondary @click="$emit('makePDF', $refs.printComponent.innerHTML)">{{ t('PDF') }}</f-button>
|
@click="$emit('send', $refs.printComponent.innerHTML)"
|
||||||
|
>{{ t('Send') }}</f-button
|
||||||
|
>
|
||||||
|
<f-button secondary @click="toggleCustomizer">{{
|
||||||
|
t('Customize')
|
||||||
|
}}</f-button>
|
||||||
|
<f-button
|
||||||
|
secondary
|
||||||
|
@click="$emit('makePDF', $refs.printComponent.innerHTML)"
|
||||||
|
>{{ t('PDF') }}</f-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
@ -20,7 +30,10 @@
|
|||||||
@updateTemplateView="updateTemplateView"
|
@updateTemplateView="updateTemplateView"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 bg-white mt-4 mx-auto border shadow" ref="printComponent">
|
<div
|
||||||
|
class="col-8 bg-white mt-4 mx-auto border shadow"
|
||||||
|
ref="printComponent"
|
||||||
|
>
|
||||||
<component
|
<component
|
||||||
:themeColor="themeColor"
|
:themeColor="themeColor"
|
||||||
:font="font"
|
:font="font"
|
||||||
@ -42,14 +55,15 @@ import InvoiceCustomizer from '@/components/InvoiceCustomizer';
|
|||||||
const invoiceTemplates = {
|
const invoiceTemplates = {
|
||||||
'Basic I': InvoiceTemplate1,
|
'Basic I': InvoiceTemplate1,
|
||||||
'Basic II': InvoiceTemplate2,
|
'Basic II': InvoiceTemplate2,
|
||||||
Modern: InvoiceTemplate3
|
Modern: InvoiceTemplate3,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'InvoicePrint',
|
name: 'InvoicePrint',
|
||||||
props: ['doc'],
|
props: ['doc'],
|
||||||
|
emits: ['send', 'makePDF'],
|
||||||
components: {
|
components: {
|
||||||
InvoiceCustomizer
|
InvoiceCustomizer,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -57,7 +71,7 @@ export default {
|
|||||||
themeColor: undefined,
|
themeColor: undefined,
|
||||||
template: undefined,
|
template: undefined,
|
||||||
font: undefined,
|
font: undefined,
|
||||||
usedForReRender: 0
|
usedForReRender: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
@ -96,7 +110,7 @@ export default {
|
|||||||
},
|
},
|
||||||
updateTemplateView() {
|
updateTemplateView() {
|
||||||
this.usedForReRender += 1;
|
this.usedForReRender += 1;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
@ -27,8 +27,8 @@
|
|||||||
"luxon": "^2.0.2",
|
"luxon": "^2.0.2",
|
||||||
"pesa": "^1.1.3",
|
"pesa": "^1.1.3",
|
||||||
"sqlite3": "npm:@vscode/sqlite3@^5.0.7",
|
"sqlite3": "npm:@vscode/sqlite3@^5.0.7",
|
||||||
"vue": "^2.6.14",
|
"vue": "^3.2.30",
|
||||||
"vue-router": "^3.5.3"
|
"vue-router": "^4.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
"@babel/core": "^7.16.0",
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but Frappe Books doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong>We're sorry but Frappe Books doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app"></div>
|
<!-- built files will replace body.innerHTML -->
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -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 #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>
|
||||||
|
@ -101,6 +101,7 @@ export default {
|
|||||||
textOffsetX: { default: 0, type: Number },
|
textOffsetX: { default: 0, type: Number },
|
||||||
textOffsetY: { default: 0, type: Number },
|
textOffsetY: { default: 0, type: Number },
|
||||||
},
|
},
|
||||||
|
emits: ['change'],
|
||||||
computed: {
|
computed: {
|
||||||
cx() {
|
cx() {
|
||||||
return 50 + this.offsetX;
|
return 50 + this.offsetX;
|
||||||
|
@ -38,6 +38,7 @@ import { fuzzyMatch } from '@/utils';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AutoComplete',
|
name: 'AutoComplete',
|
||||||
|
emits: ['focus'],
|
||||||
extends: Base,
|
extends: Base,
|
||||||
components: {
|
components: {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { showMessageDialog } from '../../utils';
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Base',
|
name: 'Base',
|
||||||
props: [
|
props: [
|
||||||
@ -33,6 +32,7 @@ export default {
|
|||||||
'readOnly',
|
'readOnly',
|
||||||
'autofocus',
|
'autofocus',
|
||||||
],
|
],
|
||||||
|
emits: ['focus', 'input', 'change'],
|
||||||
inject: {
|
inject: {
|
||||||
doctype: {
|
doctype: {
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -70,11 +70,12 @@ import Base from './Base';
|
|||||||
export default {
|
export default {
|
||||||
name: 'Check',
|
name: 'Check',
|
||||||
extends: Base,
|
extends: Base,
|
||||||
|
emits: ['focus'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
offBorderColor: 'rgba(17, 43, 66, 0.201322)',
|
offBorderColor: 'rgba(17, 43, 66, 0.201322)',
|
||||||
offColor: '#FFFFFF',
|
offColor: '#FFFFFF',
|
||||||
color: '#A1ABB4'
|
color: '#A1ABB4',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{{ df.label }}
|
{{ df.label }}
|
||||||
</div>
|
</div>
|
||||||
<Popover placement="bottom-end">
|
<Popover placement="bottom-end">
|
||||||
<template v-slot:target="{ togglePopover }">
|
<template #target="{ togglePopover }">
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
:class="inputClasses"
|
:class="inputClasses"
|
||||||
@ -25,7 +25,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="text-sm py-3 px-2 text-center" slot="content">
|
<template #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>
|
||||||
|
@ -34,6 +34,7 @@ import Float from './Float';
|
|||||||
export default {
|
export default {
|
||||||
name: 'Currency',
|
name: 'Currency',
|
||||||
extends: Float,
|
extends: Float,
|
||||||
|
emits: ['input', 'focus'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showInput: false,
|
showInput: false,
|
||||||
|
@ -12,7 +12,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
this.targetWatcher();
|
this.targetWatcher();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -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;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,10 +3,12 @@ import frappe from 'frappe';
|
|||||||
import AutoComplete from './AutoComplete';
|
import AutoComplete from './AutoComplete';
|
||||||
import Badge from '@/components/Badge';
|
import Badge from '@/components/Badge';
|
||||||
import { openQuickEdit } from '@/utils';
|
import { openQuickEdit } from '@/utils';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Link',
|
name: 'Link',
|
||||||
extends: AutoComplete,
|
extends: AutoComplete,
|
||||||
|
emits: ['new-doc'],
|
||||||
methods: {
|
methods: {
|
||||||
async getSuggestions(keyword = '') {
|
async getSuggestions(keyword = '') {
|
||||||
let doctype = this.getTarget();
|
let doctype = this.getTarget();
|
||||||
@ -64,7 +66,7 @@ export default {
|
|||||||
label: 'Create',
|
label: 'Create',
|
||||||
value: 'Create',
|
value: 'Create',
|
||||||
action: () => this.openNewDoc(),
|
action: () => this.openNewDoc(),
|
||||||
component: {
|
component: markRaw({
|
||||||
template: `
|
template: `
|
||||||
<div class="flex items-center font-semibold">{{ t('Create') }}
|
<div class="flex items-center font-semibold">{{ t('Create') }}
|
||||||
<Badge color="blue" class="ml-2" v-if="isNewValue">{{ linkValue }}</Badge>
|
<Badge color="blue" class="ml-2" v-if="isNewValue">{{ linkValue }}</Badge>
|
||||||
@ -78,7 +80,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: { Badge },
|
components: { Badge },
|
||||||
},
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async getFilters(keyword) {
|
async getFilters(keyword) {
|
||||||
|
@ -60,6 +60,7 @@ import Base from './Base';
|
|||||||
export default {
|
export default {
|
||||||
name: 'Select',
|
name: 'Select',
|
||||||
extends: Base,
|
extends: Base,
|
||||||
|
emits: ['focus'],
|
||||||
methods: {
|
methods: {
|
||||||
map(v) {
|
map(v) {
|
||||||
if (this.df.map) {
|
if (this.df.map) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
:class="{
|
:class="{
|
||||||
'px-2 py-3': size === 'small',
|
'px-2 py-3': size === 'small',
|
||||||
'px-3 py-4': size !== 'small',
|
'px-3 py-4': size !== 'small',
|
||||||
'text-right': isNumeric(df)
|
'text-right': isNumeric(df),
|
||||||
}"
|
}"
|
||||||
v-for="df in tableFields"
|
v-for="df in tableFields"
|
||||||
:key="df.fieldname"
|
:key="df.fieldname"
|
||||||
@ -31,7 +31,7 @@
|
|||||||
:ratio="ratio"
|
:ratio="ratio"
|
||||||
class="text-gray-500 cursor-pointer border-transparent px-2 w-full"
|
class="text-gray-500 cursor-pointer border-transparent px-2 w-full"
|
||||||
v-if="!isReadOnly"
|
v-if="!isReadOnly"
|
||||||
@click.native="addRow"
|
@click="addRow"
|
||||||
>
|
>
|
||||||
<div class="flex items-center pl-1">
|
<div class="flex items-center pl-1">
|
||||||
<feather-icon name="plus" class="w-4 h-4 text-gray-500" />
|
<feather-icon name="plus" class="w-4 h-4 text-gray-500" />
|
||||||
@ -40,7 +40,7 @@
|
|||||||
class="flex justify-between"
|
class="flex justify-between"
|
||||||
:class="{
|
:class="{
|
||||||
'px-2 py-3': size === 'small',
|
'px-2 py-3': size === 'small',
|
||||||
'px-3 py-4': size !== 'small'
|
'px-3 py-4': size !== 'small',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ t('Add Row') }}
|
{{ t('Add Row') }}
|
||||||
@ -50,7 +50,7 @@
|
|||||||
class="text-right"
|
class="text-right"
|
||||||
:class="{
|
:class="{
|
||||||
'px-2 py-3': size === 'small',
|
'px-2 py-3': size === 'small',
|
||||||
'px-3 py-4': size !== 'small'
|
'px-3 py-4': size !== 'small',
|
||||||
}"
|
}"
|
||||||
v-if="maxRowsBeforeOverflow && value.length > maxRowsBeforeOverflow"
|
v-if="maxRowsBeforeOverflow && value.length > maxRowsBeforeOverflow"
|
||||||
>
|
>
|
||||||
@ -71,15 +71,15 @@ export default {
|
|||||||
extends: Base,
|
extends: Base,
|
||||||
props: {
|
props: {
|
||||||
showHeader: {
|
showHeader: {
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
maxRowsBeforeOverflow: {
|
maxRowsBeforeOverflow: {
|
||||||
default: 0
|
default: 0,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Row,
|
Row,
|
||||||
TableRow
|
TableRow,
|
||||||
},
|
},
|
||||||
data: () => ({ rowContainerHeight: null }),
|
data: () => ({ rowContainerHeight: null }),
|
||||||
watch: {
|
watch: {
|
||||||
@ -95,8 +95,8 @@ export default {
|
|||||||
this.rowContainerHeight = `${containerHeight}px`;
|
this.rowContainerHeight = `${containerHeight}px`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
focus() {},
|
focus() {},
|
||||||
@ -109,13 +109,13 @@ export default {
|
|||||||
},
|
},
|
||||||
removeRow(row) {
|
removeRow(row) {
|
||||||
let rows = this.value || [];
|
let rows = this.value || [];
|
||||||
rows = rows.filter(_row => _row !== row);
|
rows = rows.filter((_row) => _row !== row);
|
||||||
this.triggerChange(rows);
|
this.triggerChange(rows);
|
||||||
},
|
},
|
||||||
scrollToRow(index) {
|
scrollToRow(index) {
|
||||||
let row = this.$refs['table-row'][index];
|
let row = this.$refs['table-row'][index];
|
||||||
row && row.$el.scrollIntoView({ block: 'nearest' });
|
row && row.$el.scrollIntoView({ block: 'nearest' });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
ratio() {
|
ratio() {
|
||||||
@ -123,8 +123,8 @@ export default {
|
|||||||
},
|
},
|
||||||
tableFields() {
|
tableFields() {
|
||||||
let meta = frappe.getMeta(this.df.childtype);
|
let meta = frappe.getMeta(this.df.childtype);
|
||||||
return meta.tableFields.map(fieldname => meta.getField(fieldname));
|
return meta.tableFields.map((fieldname) => meta.getField(fieldname));
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -39,6 +39,7 @@ import { getErrorMessage } from '../../errorHandling';
|
|||||||
export default {
|
export default {
|
||||||
name: 'TableRow',
|
name: 'TableRow',
|
||||||
props: ['row', 'tableFields', 'size', 'ratio', 'isNumeric'],
|
props: ['row', 'tableFields', 'size', 'ratio', 'isNumeric'],
|
||||||
|
emits: ['remove'],
|
||||||
components: {
|
components: {
|
||||||
Row,
|
Row,
|
||||||
},
|
},
|
||||||
@ -59,14 +60,14 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$set(this.errors, df.fieldname, null);
|
this.errors[df.fieldname] = null;
|
||||||
const oldValue = this.row.get(df.fieldname);
|
const oldValue = this.row.get(df.fieldname);
|
||||||
if (oldValue === value) {
|
if (oldValue === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.row.set(df.fieldname, value).catch((e) => {
|
this.row.set(df.fieldname, value).catch((e) => {
|
||||||
this.$set(this.errors, df.fieldname, getErrorMessage(e, this.row));
|
this.errors[df.fieldname] = getErrorMessage(e, this.row);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getErrorString() {
|
getErrorString() {
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
:class="['resize-none', inputClasses]"
|
:class="['resize-none', inputClasses]"
|
||||||
:value="value"
|
:value="value"
|
||||||
:placeholder="inputPlaceholder"
|
:placeholder="inputPlaceholder"
|
||||||
@blur="e => triggerChange(e.target.value)"
|
@blur="(e) => triggerChange(e.target.value)"
|
||||||
@focus="e => $emit('focus', e)"
|
@focus="(e) => $emit('focus', e)"
|
||||||
@input="e => $emit('input', e)"
|
@input="(e) => $emit('input', e)"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -21,6 +21,7 @@ import Base from './Base';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Text',
|
name: 'Text',
|
||||||
extends: Base
|
extends: Base,
|
||||||
|
emits: ['focus', 'input'],
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Popover @open="selectCurrentMonthYear">
|
<Popover @open="selectCurrentMonthYear">
|
||||||
<template v-slot:target="{ togglePopover, handleBlur }">
|
<template #target="{ togglePopover, handleBlur }">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
:class="inputClass"
|
:class="inputClass"
|
||||||
@ -11,7 +11,7 @@
|
|||||||
@blur="handleBlur"
|
@blur="handleBlur"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content="{ togglePopover }">
|
<template #content="{ togglePopover }">
|
||||||
<div class="text-left p-3 select-none">
|
<div class="text-left p-3 select-none">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="font-medium text-blue-500 text-base">
|
<span class="font-medium text-blue-500 text-base">
|
||||||
@ -123,6 +123,7 @@ import Popover from '../Popover';
|
|||||||
export default {
|
export default {
|
||||||
name: 'DatePicker',
|
name: 'DatePicker',
|
||||||
props: ['value', 'placeholder', 'readonly', 'formatValue', 'inputClass'],
|
props: ['value', 'placeholder', 'readonly', 'formatValue', 'inputClass'],
|
||||||
|
emits: ['change'],
|
||||||
components: {
|
components: {
|
||||||
Popover,
|
Popover,
|
||||||
},
|
},
|
||||||
|
@ -4,11 +4,8 @@
|
|||||||
:hide-arrow="true"
|
:hide-arrow="true"
|
||||||
:placement="right ? 'bottom-end' : 'bottom-start'"
|
:placement="right ? 'bottom-end' : 'bottom-start'"
|
||||||
>
|
>
|
||||||
<div
|
<template #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 #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,22 +0,0 @@
|
|||||||
<template>
|
|
||||||
<Dropdown v-bind="$attrs">
|
|
||||||
<template v-slot="{ toggleDropdown }">
|
|
||||||
<div @click="toggleDropdown()">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Dropdown from './Dropdown';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'DropdownWithAction',
|
|
||||||
components: {
|
|
||||||
Dropdown
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -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
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Popover @close="emitFilterChange" placement="bottom-end">
|
<Popover @close="emitFilterChange" placement="bottom-end">
|
||||||
<template v-slot:target="{ togglePopover }">
|
<template #target="{ togglePopover }">
|
||||||
<Button :icon="true" @click="togglePopover()">
|
<Button :icon="true" @click="togglePopover()">
|
||||||
<span class="flex items-center">
|
<span class="flex items-center">
|
||||||
<Icon name="filter" size="12" class="stroke-current text-gray-800" />
|
<Icon name="filter" size="12" class="stroke-current text-gray-800" />
|
||||||
@ -15,7 +15,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
<div slot="content">
|
<template #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,13 @@ export default {
|
|||||||
Popover,
|
Popover,
|
||||||
Button,
|
Button,
|
||||||
Icon,
|
Icon,
|
||||||
FormControl
|
FormControl,
|
||||||
},
|
},
|
||||||
props: ['fields'],
|
props: ['fields'],
|
||||||
|
emits: ['change'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
filters: []
|
filters: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -138,11 +159,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 +187,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"
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
<h4>Customize</h4>
|
<h4>Customize</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 text-right">
|
<div class="col-6 text-right">
|
||||||
<f-button secondary @click="saveAndClose">{{ t('Save & Close') }}</f-button>
|
<f-button secondary @click="saveAndClose">{{
|
||||||
|
t('Save & Close')
|
||||||
|
}}</f-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -14,7 +16,9 @@
|
|||||||
<form-layout :doc="doc" :fields="fields" @updateDoc="saveDoc" />
|
<form-layout :doc="doc" :fields="fields" @updateDoc="saveDoc" />
|
||||||
<sketch-picker v-model="color" class="shadow-none" />
|
<sketch-picker v-model="color" class="shadow-none" />
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<f-button secondary @click="openCompanySettings">{{ t('Company Settings') }}</f-button>
|
<f-button secondary @click="openCompanySettings">{{
|
||||||
|
t('Company Settings')
|
||||||
|
}}</f-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -27,15 +31,22 @@ import { Sketch } from 'vue-color';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'InvoiceCustomizer',
|
name: 'InvoiceCustomizer',
|
||||||
|
emits: [
|
||||||
|
'changeTemplate',
|
||||||
|
'changeFont',
|
||||||
|
'closeInvoiceCustomizer',
|
||||||
|
'updateTemplateView',
|
||||||
|
'changeColor',
|
||||||
|
],
|
||||||
components: {
|
components: {
|
||||||
FormLayout,
|
FormLayout,
|
||||||
'sketch-picker': Sketch
|
'sketch-picker': Sketch,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
doc: null,
|
doc: null,
|
||||||
fields: [],
|
fields: [],
|
||||||
color: {}
|
color: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
@ -43,7 +54,7 @@ export default {
|
|||||||
this.color.hex = this.doc.themeColor;
|
this.color.hex = this.doc.themeColor;
|
||||||
const meta = frappe.getMeta('SalesInvoiceSettings');
|
const meta = frappe.getMeta('SalesInvoiceSettings');
|
||||||
this.fields = meta.fields.filter(
|
this.fields = meta.fields.filter(
|
||||||
field => field.fieldname !== 'numberSeries'
|
(field) => field.fieldname !== 'numberSeries'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -67,16 +78,16 @@ export default {
|
|||||||
this.$emit('updateTemplateView');
|
this.$emit('updateTemplateView');
|
||||||
});
|
});
|
||||||
this.$formModal.open(settings);
|
this.$formModal.open(settings);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
color: async function() {
|
color: async function () {
|
||||||
if (this.doc) {
|
if (this.doc) {
|
||||||
if (this.doc.themeColor != this.color.hex) {
|
if (this.doc.themeColor != this.color.hex) {
|
||||||
this.$emit('changeColor', this.color.hex);
|
this.$emit('changeColor', this.color.hex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -24,6 +24,7 @@ import { createPopper } from '@popperjs/core';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Popover',
|
name: 'Popover',
|
||||||
|
emits: ['open', 'close'],
|
||||||
props: {
|
props: {
|
||||||
hideArrow: {
|
hideArrow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -55,7 +56,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 +66,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() {
|
beforeUnmount() {
|
||||||
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>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
toggleDropdown,
|
toggleDropdown,
|
||||||
highlightItemUp,
|
highlightItemUp,
|
||||||
highlightItemDown,
|
highlightItemDown,
|
||||||
selectHighlightedItem
|
selectHighlightedItem,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -22,9 +22,13 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
@input="search"
|
@input="
|
||||||
|
() => {
|
||||||
|
search();
|
||||||
|
toggleDropdown(true);
|
||||||
|
}
|
||||||
|
"
|
||||||
ref="input"
|
ref="input"
|
||||||
@focus="$toggleDropdown = toggleDropdown"
|
|
||||||
@keydown.up="highlightItemUp"
|
@keydown.up="highlightItemUp"
|
||||||
@keydown.down="highlightItemDown"
|
@keydown.down="highlightItemDown"
|
||||||
@keydown.enter="selectHighlightedItem"
|
@keydown.enter="selectHighlightedItem"
|
||||||
@ -40,7 +44,7 @@
|
|||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import reports from '../../reports/view';
|
import reports from '../../reports/view';
|
||||||
import Dropdown from '@/components/Dropdown';
|
import Dropdown from '@/components/Dropdown';
|
||||||
import { routeTo } from '@/utils'
|
import { routeTo } from '@/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -48,20 +52,18 @@ export default {
|
|||||||
inputValue: '',
|
inputValue: '',
|
||||||
searchList: [],
|
searchList: [],
|
||||||
suggestions: [],
|
suggestions: [],
|
||||||
$toggleDropdown: null
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Dropdown
|
Dropdown,
|
||||||
},
|
},
|
||||||
|
emits: ['change'],
|
||||||
mounted() {
|
mounted() {
|
||||||
this.makeSearchList();
|
this.makeSearchList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async search() {
|
async search() {
|
||||||
this.$toggleDropdown && this.$toggleDropdown(true);
|
this.suggestions = this.searchList.filter((d) => {
|
||||||
|
|
||||||
this.suggestions = this.searchList.filter(d => {
|
|
||||||
let key = this.inputValue.toLowerCase();
|
let key = this.inputValue.toLowerCase();
|
||||||
return d.label.toLowerCase().includes(key);
|
return d.label.toLowerCase().includes(key);
|
||||||
});
|
});
|
||||||
@ -80,7 +82,7 @@ export default {
|
|||||||
const views = this.getViews();
|
const views = this.getViews();
|
||||||
|
|
||||||
let searchList = [...doctypes, ...reports, ...views];
|
let searchList = [...doctypes, ...reports, ...views];
|
||||||
this.searchList = searchList.map(d => {
|
this.searchList = searchList.map((d) => {
|
||||||
if (d.route) {
|
if (d.route) {
|
||||||
d.action = () => routeTo(d.route);
|
d.action = () => routeTo(d.route);
|
||||||
this.inputValue = '';
|
this.inputValue = '';
|
||||||
@ -90,37 +92,44 @@ export default {
|
|||||||
},
|
},
|
||||||
getDoctypes() {
|
getDoctypes() {
|
||||||
let doctypes = Object.keys(frappe.models).sort();
|
let doctypes = Object.keys(frappe.models).sort();
|
||||||
let doctypeMetas = doctypes.map(doctype => frappe.getMeta(doctype));
|
let doctypeMetas = doctypes.map((doctype) => frappe.getMeta(doctype));
|
||||||
let searchableDoctypes = doctypeMetas.filter(meta => {
|
let searchableDoctypes = doctypeMetas.filter((meta) => {
|
||||||
return !meta.isSingle && !meta.isChild;
|
return !meta.isSingle && !meta.isChild;
|
||||||
});
|
});
|
||||||
return searchableDoctypes.map(meta => {
|
return searchableDoctypes.map((meta) => {
|
||||||
return {
|
return {
|
||||||
label: meta.label || meta.name,
|
label: meta.label || meta.name,
|
||||||
route: `/list/${meta.name}`,
|
route: `/list/${meta.name}`,
|
||||||
group: 'List'
|
group: 'List',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getReports() {
|
getReports() {
|
||||||
return Object.values(reports).map(report => {
|
return Object.values(reports).map((report) => {
|
||||||
return {
|
return {
|
||||||
label: report.title,
|
label: report.title,
|
||||||
route: `/report/${report.method}`,
|
route: `/report/${report.method}`,
|
||||||
group: 'Reports'
|
group: 'Reports',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
getViews() {
|
getViews() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: 'Chart of Accounts',
|
label: 'Chart of Accounts',
|
||||||
route: '/chartOfAccounts',
|
route: '/chartOfAccounts',
|
||||||
group: 'List'
|
group: 'List',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
input[type='search']::-webkit-search-decoration,
|
||||||
|
input[type='search']::-webkit-search-cancel-button,
|
||||||
|
input[type='search']::-webkit-search-results-button,
|
||||||
|
input[type='search']::-webkit-search-results-decoration {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -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,9 +103,11 @@ 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],
|
||||||
|
emits: ['change-db-file'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
companyName: '',
|
companyName: '',
|
||||||
@ -123,6 +126,7 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
WindowControls,
|
WindowControls,
|
||||||
|
Icon,
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.companyName = await sidebarConfig.getTitle();
|
this.companyName = await sidebarConfig.getTitle();
|
||||||
@ -168,7 +172,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 !== '/') {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
:style="{ opacity }"
|
:style="{ opacity }"
|
||||||
v-if="show"
|
v-if="show"
|
||||||
>
|
>
|
||||||
<feather-icon :name="iconName" class="w-6 h-6 mr-3" :class="iconColor" />
|
<FeatherIcon :name="iconName" class="w-6 h-6 mr-3" :class="iconColor" />
|
||||||
<div @click="actionClicked" :class="actionText ? 'cursor-pointer' : ''">
|
<div @click="actionClicked" :class="actionText ? 'cursor-pointer' : ''">
|
||||||
<p class="text-base">{{ message }}</p>
|
<p class="text-base">{{ message }}</p>
|
||||||
<button
|
<button
|
||||||
@ -36,8 +36,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { getColorClass } from '../colors';
|
import { getColorClass } from '../colors';
|
||||||
|
import FeatherIcon from './FeatherIcon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
FeatherIcon,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
@ -77,10 +81,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const mountTarget = document.createElement('div');
|
|
||||||
mountTarget.id = 'toast-target';
|
|
||||||
this.$el.parentElement.append(mountTarget);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.opacity = 1;
|
this.opacity = 1;
|
||||||
}, 50);
|
}, 50);
|
||||||
|
@ -12,8 +12,7 @@
|
|||||||
@change="(value) => onChange(df, value)"
|
@change="(value) => onChange(df, value)"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<template v-if="inlineEditField === df && inlineEditDoc">
|
<div class="border-b" :key="df.fieldname" v-if="renderInline(df)">
|
||||||
<div class="border-b" :key="df.fieldname">
|
|
||||||
<TwoColumnForm
|
<TwoColumnForm
|
||||||
ref="inlineEditForm"
|
ref="inlineEditForm"
|
||||||
:doc="inlineEditDoc"
|
:doc="inlineEditDoc"
|
||||||
@ -25,10 +24,7 @@
|
|||||||
@error="(msg) => $emit('error', msg)"
|
@error="(msg) => $emit('error', msg)"
|
||||||
/>
|
/>
|
||||||
<div class="flex px-4 pb-2">
|
<div class="flex px-4 pb-2">
|
||||||
<Button
|
<Button class="w-1/2 text-gray-900" @click="inlineEditField = null">
|
||||||
class="w-1/2 text-gray-900"
|
|
||||||
@click="inlineEditField = null"
|
|
||||||
>
|
|
||||||
{{ t('Cancel') }}
|
{{ t('Cancel') }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -40,9 +36,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
<div
|
<div
|
||||||
:key="df.fieldname"
|
:key="df.fieldname + '-else'"
|
||||||
v-else
|
v-else
|
||||||
class="grid"
|
class="grid"
|
||||||
:class="{ 'border-b': !noBorder }"
|
:class="{ 'border-b': !noBorder }"
|
||||||
@ -98,6 +93,7 @@ import { handleErrorWithDialog, getErrorMessage } from '../errorHandling';
|
|||||||
|
|
||||||
let TwoColumnForm = {
|
let TwoColumnForm = {
|
||||||
name: 'TwoColumnForm',
|
name: 'TwoColumnForm',
|
||||||
|
emits: ['error', 'change'],
|
||||||
props: {
|
props: {
|
||||||
doc: Object,
|
doc: Object,
|
||||||
fields: Array,
|
fields: Array,
|
||||||
@ -140,6 +136,11 @@ let TwoColumnForm = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
renderInline(df) {
|
||||||
|
return (
|
||||||
|
this.inlineEditField?.fieldname === df?.fieldname && this.inlineEditDoc
|
||||||
|
);
|
||||||
|
},
|
||||||
evaluateBoolean(fieldProp, defaultValue) {
|
evaluateBoolean(fieldProp, defaultValue) {
|
||||||
const type = typeof fieldProp;
|
const type = typeof fieldProp;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -167,7 +168,7 @@ let TwoColumnForm = {
|
|||||||
|
|
||||||
let oldValue = this.doc.get(df.fieldname);
|
let oldValue = this.doc.get(df.fieldname);
|
||||||
|
|
||||||
this.$set(this.errors, df.fieldname, null);
|
this.errors[df.fieldname] = null;
|
||||||
if (oldValue === value) {
|
if (oldValue === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -186,7 +187,7 @@ let TwoColumnForm = {
|
|||||||
onChangeCommon(df, value) {
|
onChangeCommon(df, value) {
|
||||||
this.doc.set(df.fieldname, value).catch((e) => {
|
this.doc.set(df.fieldname, value).catch((e) => {
|
||||||
// set error message for this field
|
// set error message for this field
|
||||||
this.$set(this.errors, df.fieldname, getErrorMessage(e, this.doc));
|
this.errors[df.fieldname] = getErrorMessage(e, this.doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.autosave && this.doc._dirty && !this.doc.isNew()) {
|
if (this.autosave && this.doc._dirty && !this.doc.isNew()) {
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { runWindowAction } from '@/utils';
|
import { runWindowAction } from '@/utils';
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WindowControls',
|
name: 'WindowControls',
|
||||||
|
emits: ['close', 'minimize', 'maximize', 'unmaximize'],
|
||||||
props: {
|
props: {
|
||||||
buttons: {
|
buttons: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -36,6 +36,7 @@ import { IPC_MESSAGES } from '@/messages';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WindowsTitleBar',
|
name: 'WindowsTitleBar',
|
||||||
|
emits: ['close', 'minimize', 'maximize', 'unmaximize'],
|
||||||
methods: {
|
methods: {
|
||||||
async action(name) {
|
async action(name) {
|
||||||
const actionRan = await runWindowAction(name);
|
const actionRan = await runWindowAction(name);
|
||||||
|
@ -1,18 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div class="scroll-container">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'WithScroll',
|
name: 'WithScroll',
|
||||||
|
emits: ['scroll'],
|
||||||
mounted() {
|
mounted() {
|
||||||
let handler = () => {
|
this.listener = () => {
|
||||||
let { scrollLeft, scrollTop } = this.$el;
|
let { scrollLeft, scrollTop } = this.$el;
|
||||||
this.$emit('scroll', { scrollLeft, scrollTop });
|
this.$emit('scroll', { scrollLeft, scrollTop });
|
||||||
};
|
};
|
||||||
this.$el.addEventListener('scroll', handler);
|
this.$el.addEventListener('scroll', this.listener);
|
||||||
this.$once('hook:beforeDestroy', () => {
|
|
||||||
this.$el.removeEventListener('scroll', handler);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
render() {
|
beforeUnmount() {
|
||||||
return this.$slots.default[0];
|
if (this.listener) {
|
||||||
|
this.$el.removeEventListener('scroll', this.listener);
|
||||||
|
delete this.listener;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.scroll-container {
|
||||||
|
height: calc(100vh - 12rem);
|
||||||
|
}
|
||||||
|
.scroll-container::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
.scroll-container::-webkit-scrollbar-thumb {
|
||||||
|
background-color: theme('colors.gray.200');
|
||||||
|
}
|
||||||
|
.scroll-container::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: theme('colors.gray.300');
|
||||||
|
}
|
||||||
|
.scroll-container::-webkit-scrollbar-track {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -12,6 +12,5 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
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('body');
|
||||||
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,16 +1,18 @@
|
|||||||
<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 #title>
|
||||||
|
<h1 class="text-2xl font-bold">
|
||||||
{{ t('Chart of Accounts') }}
|
{{ t('Chart of Accounts') }}
|
||||||
</h1>
|
</h1>
|
||||||
<template slot="actions">
|
</template>
|
||||||
|
<template #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 v-for="account in allAccounts" :key="account.name">
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
mt-2
|
mt-2
|
||||||
@ -25,7 +27,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}`">
|
||||||
@ -47,7 +48,7 @@
|
|||||||
hover:text-gray-900
|
hover:text-gray-900
|
||||||
focus:outline-none
|
focus:outline-none
|
||||||
"
|
"
|
||||||
@click.stop="addAccount(account, 'addingAccount')"
|
@click="addAccount(account, 'addingAccount')"
|
||||||
>
|
>
|
||||||
{{ t('Add Account') }}
|
{{ t('Add Account') }}
|
||||||
</button>
|
</button>
|
||||||
@ -58,7 +59,7 @@
|
|||||||
hover:text-gray-900
|
hover:text-gray-900
|
||||||
focus:outline-none
|
focus:outline-none
|
||||||
"
|
"
|
||||||
@click.stop="addAccount(account, 'addingGroupAccount')"
|
@click="addAccount(account, 'addingGroupAccount')"
|
||||||
>
|
>
|
||||||
{{ t('Add Group') }}
|
{{ t('Add Group') }}
|
||||||
</button>
|
</button>
|
||||||
@ -122,7 +123,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</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 #title>
|
||||||
<template slot="actions">
|
<h1 class="text-2xl font-bold">{{ t('Dashboard') }}</h1>
|
||||||
|
</template>
|
||||||
|
<template #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 #title>{{ t('Top Expenses') }}</template>
|
||||||
<PeriodSelector
|
<template #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>
|
||||||
|
@ -46,7 +46,7 @@ export default {
|
|||||||
default: () => ['This Year', 'This Quarter', 'This Month'],
|
default: () => ['This Year', 'This Quarter', 'This Month'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits: ['change'],
|
||||||
components: {
|
components: {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
},
|
},
|
||||||
|
@ -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 #title>{{ t('Profit and Loss') }}</template>
|
||||||
|
<template #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 #title>{{ invoice.title }}</template>
|
||||||
|
<template #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">
|
||||||
|
@ -163,6 +163,7 @@ import { showErrorDialog } from '../errorHandling';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DatabaseSelector',
|
name: 'DatabaseSelector',
|
||||||
|
emits: ['database-connect'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loadingDatabase: false,
|
loadingDatabase: false,
|
||||||
|
@ -1,26 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex overflow-hidden">
|
<div class="flex overflow-hidden">
|
||||||
<Sidebar
|
<Sidebar
|
||||||
class="w-44 flex-shrink-0"
|
class="w-48 flex-shrink-0"
|
||||||
@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 v-slot="{ Component }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<router-view class="flex-1" :key="$route.path" />
|
<component :is="Component" class="flex-1" :key="$route.path" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
|
|
||||||
<div class="flex" v-if="showQuickEdit">
|
<div class="flex" v-if="showQuickEdit">
|
||||||
|
<router-view name="edit" v-slot="{ Component }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<router-view
|
<component
|
||||||
name="edit"
|
:is="Component"
|
||||||
class="w-80 flex-1"
|
class="w-80 flex-1"
|
||||||
:key="$route.query.doctype + $route.query.name"
|
:key="$route.query.doctype + $route.query.name"
|
||||||
/>
|
/>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="toast-container"
|
id="toast-container"
|
||||||
class="absolute bottom-0 flex flex-col items-center mb-3"
|
class="absolute bottom-0 flex flex-col items-center mb-3"
|
||||||
style="width: calc(100% - 11rem)"
|
style="width: calc(100% - 12rem)"
|
||||||
>
|
>
|
||||||
<div id="toast-target" />
|
<div id="toast-target" />
|
||||||
</div>
|
</div>
|
||||||
@ -31,6 +36,7 @@
|
|||||||
import Sidebar from '../components/Sidebar';
|
import Sidebar from '../components/Sidebar';
|
||||||
export default {
|
export default {
|
||||||
name: 'Desk',
|
name: 'Desk',
|
||||||
|
emits: ['change-db-file'],
|
||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
},
|
},
|
||||||
|
@ -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 #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 #title>
|
||||||
<template slot="actions">
|
<BackLink />
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
<StatusBadge :status="status" />
|
<StatusBadge :status="status" />
|
||||||
<Button
|
<Button
|
||||||
v-if="doc.submitted"
|
v-if="doc.submitted"
|
||||||
@ -269,7 +271,7 @@ export default {
|
|||||||
|
|
||||||
let query = this.$route.query;
|
let query = this.$route.query;
|
||||||
if (query.values && query.doctype === this.doctype) {
|
if (query.values && query.doctype === this.doctype) {
|
||||||
this.doc.set(this.$router.currentRoute.query.values);
|
this.doc.set(this.$router.currentRoute.value.query.values);
|
||||||
}
|
}
|
||||||
this.status = getInvoiceStatus(this.doc);
|
this.status = getInvoiceStatus(this.doc);
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<BackLink slot="title" />
|
<template #title>
|
||||||
<template slot="actions" v-if="doc">
|
<BackLink />
|
||||||
|
</template>
|
||||||
|
<template #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 {
|
||||||
|
@ -31,14 +31,14 @@
|
|||||||
<Row
|
<Row
|
||||||
gap="1rem"
|
gap="1rem"
|
||||||
class="cursor-pointer text-gray-900 flex-1"
|
class="cursor-pointer text-gray-900 flex-1"
|
||||||
@click.native="openForm(doc)"
|
@click="openForm(doc)"
|
||||||
:columnCount="columns.length"
|
:columnCount="columns.length"
|
||||||
>
|
>
|
||||||
<ListCell
|
<ListCell
|
||||||
v-for="column in columns"
|
v-for="column in columns"
|
||||||
:key="column.label"
|
:key="column.label"
|
||||||
:class="{
|
:class="{
|
||||||
'text-right': ['Float', 'Currency'].includes(column.fieldtype)
|
'text-right': ['Float', 'Currency'].includes(column.fieldtype),
|
||||||
}"
|
}"
|
||||||
:doc="doc"
|
:doc="doc"
|
||||||
:column="column"
|
:column="column"
|
||||||
@ -66,22 +66,23 @@ import Button from '@/components/Button';
|
|||||||
export default {
|
export default {
|
||||||
name: 'List',
|
name: 'List',
|
||||||
props: ['listConfig', 'filters'],
|
props: ['listConfig', 'filters'],
|
||||||
|
emits: ['makeNewDoc'],
|
||||||
components: {
|
components: {
|
||||||
Row,
|
Row,
|
||||||
ListCell,
|
ListCell,
|
||||||
Avatar,
|
Avatar,
|
||||||
Button
|
Button,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
listConfig(oldValue, newValue) {
|
listConfig(oldValue, newValue) {
|
||||||
if (oldValue.doctype !== newValue.doctype) {
|
if (oldValue.doctype !== newValue.doctype) {
|
||||||
this.setupColumnsAndData();
|
this.setupColumnsAndData();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
data: []
|
data: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -93,7 +94,7 @@ export default {
|
|||||||
},
|
},
|
||||||
hasImage() {
|
hasImage() {
|
||||||
return this.meta.hasField('image');
|
return this.meta.hasField('image');
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await this.setupColumnsAndData();
|
await this.setupColumnsAndData();
|
||||||
@ -113,7 +114,7 @@ export default {
|
|||||||
}
|
}
|
||||||
openQuickEdit({
|
openQuickEdit({
|
||||||
doctype: this.doctype,
|
doctype: this.doctype,
|
||||||
name: doc.name
|
name: doc.name,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async updateData(filters) {
|
async updateData(filters) {
|
||||||
@ -122,7 +123,7 @@ export default {
|
|||||||
doctype: this.doctype,
|
doctype: this.doctype,
|
||||||
fields: ['*'],
|
fields: ['*'],
|
||||||
filters,
|
filters,
|
||||||
orderBy: 'creation'
|
orderBy: 'creation',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getFilters() {
|
getFilters() {
|
||||||
@ -133,7 +134,7 @@ export default {
|
|||||||
},
|
},
|
||||||
prepareColumns() {
|
prepareColumns() {
|
||||||
return this.listConfig.columns
|
return this.listConfig.columns
|
||||||
.map(col => {
|
.map((col) => {
|
||||||
if (typeof col === 'string') {
|
if (typeof col === 'string') {
|
||||||
const field = this.meta.getField(col);
|
const field = this.meta.getField(col);
|
||||||
if (!field) return null;
|
if (!field) return null;
|
||||||
@ -142,7 +143,7 @@ export default {
|
|||||||
return col;
|
return col;
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -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 #title>
|
||||||
<template slot="actions">
|
<h1 class="text-2xl font-bold" v-if="title">
|
||||||
|
{{ title }}
|
||||||
|
</h1>
|
||||||
|
</template>
|
||||||
|
<template #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 #title>
|
||||||
<template slot="actions">
|
<BackLink />
|
||||||
|
</template>
|
||||||
|
<template #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"
|
||||||
@ -52,7 +54,6 @@
|
|||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import PageHeader from '@/components/PageHeader';
|
import PageHeader from '@/components/PageHeader';
|
||||||
import SearchBar from '@/components/SearchBar';
|
import SearchBar from '@/components/SearchBar';
|
||||||
import DropdownWithAction from '@/components/DropdownWithAction';
|
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import BackLink from '@/components/BackLink';
|
import BackLink from '@/components/BackLink';
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
import TwoColumnForm from '@/components/TwoColumnForm';
|
||||||
@ -66,7 +67,6 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
SearchBar,
|
SearchBar,
|
||||||
DropdownWithAction,
|
|
||||||
Button,
|
Button,
|
||||||
BackLink,
|
BackLink,
|
||||||
TwoColumnForm,
|
TwoColumnForm,
|
||||||
|
@ -88,7 +88,7 @@ import { openQuickEdit, getActionsForDocument } from '@/utils';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'QuickEditForm',
|
name: 'QuickEditForm',
|
||||||
props: ['doctype', 'name', 'values', 'hideFields', 'showFields'],
|
props: ['doctype', 'name', 'valueJSON', 'hideFields', 'showFields'],
|
||||||
components: {
|
components: {
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -106,9 +106,17 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
if (!this.valueJSON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.values = JSON.parse(this.valueJSON);
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
doc: null,
|
doc: null,
|
||||||
|
values: null,
|
||||||
titleField: null,
|
titleField: null,
|
||||||
imageField: null,
|
imageField: null,
|
||||||
statusText: null,
|
statusText: null,
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<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 #title>
|
||||||
<template slot="actions">
|
<h1 class="text-2xl font-bold">{{ report.title }}</h1>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
<DropdownWithActions
|
<DropdownWithActions
|
||||||
v-for="group of actionGroups"
|
v-for="group of actionGroups"
|
||||||
@click="group.action(reportData, filters)"
|
|
||||||
:key="group.label"
|
:key="group.label"
|
||||||
:type="group.type"
|
:type="group.type"
|
||||||
:actions="group.actions"
|
:actions="group.actions"
|
||||||
@ -58,8 +59,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
<WithScroll @scroll="onBodyScroll">
|
<WithScroll
|
||||||
<div class="flex-1 overflow-auto report-scroll-container">
|
@scroll="onBodyScroll"
|
||||||
|
class="flex-1 overflow-auto"
|
||||||
|
style="height: calc(100vh - 12rem)"
|
||||||
|
>
|
||||||
<Row
|
<Row
|
||||||
v-show="row.isShown"
|
v-show="row.isShown"
|
||||||
v-for="(row, i) in rows"
|
v-for="(row, i) in rows"
|
||||||
@ -88,7 +92,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
|
||||||
</WithScroll>
|
</WithScroll>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -104,6 +107,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, markRaw } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Report',
|
name: 'Report',
|
||||||
@ -161,13 +165,16 @@ export default {
|
|||||||
rows = data;
|
rows = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reportData.columns = this.report.getColumns({ filters: this.filters, data });
|
this.reportData.columns = markRaw(this.report.getColumns({
|
||||||
|
filters: this.filters,
|
||||||
|
data,
|
||||||
|
}));
|
||||||
|
|
||||||
if (!rows) {
|
if (!rows) {
|
||||||
rows = [];
|
rows = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reportData.rows = this.addTreeMeta(rows);
|
this.reportData.rows = markRaw(this.addTreeMeta(rows));
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -250,7 +257,7 @@ export default {
|
|||||||
? frappe.format(cellValue, column)
|
? frappe.format(cellValue, column)
|
||||||
: '';
|
: '';
|
||||||
return {
|
return {
|
||||||
render(h) {
|
render() {
|
||||||
return h('span', formattedValue);
|
return h('span', formattedValue);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -366,22 +373,3 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.report-scroll-container {
|
|
||||||
height: calc(100vh - 12rem);
|
|
||||||
}
|
|
||||||
.report-scroll-container::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
.report-scroll-container::-webkit-scrollbar-thumb {
|
|
||||||
background-color: theme('colors.gray.200');
|
|
||||||
}
|
|
||||||
.report-scroll-container::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: theme('colors.gray.300');
|
|
||||||
}
|
|
||||||
.report-scroll-container::-webkit-scrollbar-track {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -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 #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, markRaw } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
@ -79,17 +82,17 @@ export default {
|
|||||||
{
|
{
|
||||||
label: t('Invoice'),
|
label: t('Invoice'),
|
||||||
icon: 'invoice',
|
icon: 'invoice',
|
||||||
component: TabInvoice,
|
component: markRaw(TabInvoice),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('General'),
|
label: t('General'),
|
||||||
icon: 'general',
|
icon: 'general',
|
||||||
component: TabGeneral,
|
component: markRaw(TabGeneral),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('System'),
|
label: t('System'),
|
||||||
icon: 'system',
|
icon: 'system',
|
||||||
component: TabSystem,
|
component: markRaw(TabSystem),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -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',
|
||||||
|
@ -17,6 +17,7 @@ import TwoColumnForm from '@/components/TwoColumnForm';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TabGeneral',
|
name: 'TabGeneral',
|
||||||
|
emits: ['change'],
|
||||||
components: {
|
components: {
|
||||||
TwoColumnForm,
|
TwoColumnForm,
|
||||||
},
|
},
|
||||||
|
@ -58,6 +58,7 @@ export default {
|
|||||||
TwoColumnForm,
|
TwoColumnForm,
|
||||||
FormControl,
|
FormControl,
|
||||||
},
|
},
|
||||||
|
emits: ['change'],
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
doctype: 'PrintSettings',
|
doctype: 'PrintSettings',
|
||||||
|
@ -29,6 +29,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
TwoColumnForm,
|
TwoColumnForm,
|
||||||
},
|
},
|
||||||
|
emits: ['change'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
doc: null,
|
doc: null,
|
||||||
|
@ -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 #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 #content>
|
||||||
<div class="p-2 text-sm">
|
<div class="p-2 text-sm">
|
||||||
{{ emailError }}
|
{{ emailError }}
|
||||||
</div>
|
</div>
|
||||||
@ -86,6 +86,7 @@ import {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SetupWizard',
|
name: 'SetupWizard',
|
||||||
|
emits: ['setup-complete', 'setup-canceled'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
doc: null,
|
doc: null,
|
||||||
|
@ -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;
|
||||||
|
@ -8,7 +8,7 @@ function onDocumentClick(e, el, fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const outsideClickDirective = {
|
export const outsideClickDirective = {
|
||||||
bind(el, binding) {
|
beforeMount(el, binding) {
|
||||||
el.dataset.outsideClickIndex = instances.length;
|
el.dataset.outsideClickIndex = instances.length;
|
||||||
|
|
||||||
const fn = binding.value;
|
const fn = binding.value;
|
||||||
@ -19,7 +19,7 @@ export const outsideClickDirective = {
|
|||||||
document.addEventListener('click', click);
|
document.addEventListener('click', click);
|
||||||
instances.push(click);
|
instances.push(click);
|
||||||
},
|
},
|
||||||
unbind(el) {
|
unmounted(el) {
|
||||||
const index = el.dataset.outsideClickIndex;
|
const index = el.dataset.outsideClickIndex;
|
||||||
const handler = instances[index];
|
const handler = instances[index];
|
||||||
document.addEventListener('click', handler);
|
document.addEventListener('click', handler);
|
||||||
|
32
src/utils.js
32
src/utils.js
@ -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, h } from 'vue';
|
||||||
import { handleErrorWithDialog } from './errorHandling';
|
import { handleErrorWithDialog } from './errorHandling';
|
||||||
import { IPC_ACTIONS, IPC_MESSAGES } from './messages';
|
import { IPC_ACTIONS, IPC_MESSAGES } from './messages';
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ export function openQuickEdit({
|
|||||||
showFields,
|
showFields,
|
||||||
defaults = {},
|
defaults = {},
|
||||||
}) {
|
}) {
|
||||||
let currentRoute = router.currentRoute;
|
let currentRoute = router.currentRoute.value;
|
||||||
let query = currentRoute.query;
|
let query = currentRoute.query;
|
||||||
let method = 'push';
|
let method = 'push';
|
||||||
if (query.edit && query.doctype === doctype) {
|
if (query.edit && query.doctype === doctype) {
|
||||||
@ -163,7 +163,7 @@ export function openQuickEdit({
|
|||||||
name,
|
name,
|
||||||
showFields: showFields ?? getShowFields(doctype),
|
showFields: showFields ?? getShowFields(doctype),
|
||||||
hideFields,
|
hideFields,
|
||||||
values: defaults,
|
valueJSON: stringifyCircular(defaults),
|
||||||
lastRoute: currentRoute,
|
lastRoute: currentRoute,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -271,7 +271,10 @@ export function getInvoiceStatus(doc) {
|
|||||||
|
|
||||||
export function routeTo(route) {
|
export function routeTo(route) {
|
||||||
let routeOptions = route;
|
let routeOptions = route;
|
||||||
if (typeof route === 'string' && route === router.currentRoute.fullPath) {
|
if (
|
||||||
|
typeof route === 'string' &&
|
||||||
|
route === router.currentRoute.value.fullPath
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,13 +335,24 @@ export async function getSavePath(name, extention) {
|
|||||||
return { canceled, filePath };
|
return { canceled, filePath };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function replaceAndAppendMount(app, replaceId) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
const target = document.getElementById(replaceId);
|
||||||
|
const parent = target.parentElement;
|
||||||
|
const clone = target.cloneNode();
|
||||||
|
|
||||||
|
app.mount(fragment);
|
||||||
|
target.replaceWith(fragment);
|
||||||
|
parent.append(clone);
|
||||||
|
}
|
||||||
|
|
||||||
export function showToast(props) {
|
export function showToast(props) {
|
||||||
new Vue({
|
const toast = createApp({
|
||||||
el: '#toast-target',
|
render() {
|
||||||
render(createElement) {
|
return h(Toast, { ...props });
|
||||||
return createElement(Toast, { props });
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
replaceAndAppendMount(toast, 'toast-target');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function titleCase(phrase) {
|
export function titleCase(phrase) {
|
||||||
@ -441,6 +455,8 @@ export function stringifyCircular(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.showToast = showToast;
|
||||||
|
|
||||||
export function checkForUpdates(force = false) {
|
export function checkForUpdates(force = false) {
|
||||||
ipcRenderer.invoke(IPC_ACTIONS.CHECK_FOR_UPDATES, force);
|
ipcRenderer.invoke(IPC_ACTIONS.CHECK_FOR_UPDATES, force);
|
||||||
}
|
}
|
||||||
|
171
yarn.lock
171
yarn.lock
@ -293,6 +293,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.2.tgz#3723cd5c8d8773eef96ce57ea1d9b7faaccd12ac"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.2.tgz#3723cd5c8d8773eef96ce57ea1d9b7faaccd12ac"
|
||||||
integrity sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==
|
integrity sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==
|
||||||
|
|
||||||
|
"@babel/parser@^7.16.4":
|
||||||
|
version "7.17.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.0.tgz#f0ac33eddbe214e4105363bb17c3341c5ffcc43c"
|
||||||
|
integrity sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==
|
||||||
|
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.0":
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.0":
|
||||||
version "7.16.2"
|
version "7.16.2"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz#2977fca9b212db153c195674e57cfab807733183"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz#2977fca9b212db153c195674e57cfab807733183"
|
||||||
@ -1789,6 +1794,48 @@
|
|||||||
semver "^7.3.4"
|
semver "^7.3.4"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
|
"@vue/compiler-core@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.30.tgz#6c5b362490930e72de8d033270a145e3830ae5c4"
|
||||||
|
integrity sha512-64fq1KfcR+k3Vlw+IsBM2VhV5B+2IP3YxvKU8LWCDLrkmlXtbf2eMK6+0IwX5KP41D0f1gzryIiXR7P8cB9O5Q==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.16.4"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
"@vue/compiler-dom@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.30.tgz#ed15e6243227baeaa445d04df804aee6e4926eab"
|
||||||
|
integrity sha512-t7arHz2SXLCXlF2fdGDFVbhENbGMez254Z5edUqb//6WXJU1lC7GvSkUE7i5x8WSjgfqt60i0V8zdmk16rvLdw==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-core" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
|
||||||
|
"@vue/compiler-sfc@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.30.tgz#9d2e56adb859059551fc1204bc37503f168c4d0c"
|
||||||
|
integrity sha512-P/5YpILtcQY92z72gxhkyOUPHVskEzhSrvYi91Xcr+csOxaDaYU5OqOxCzZKcf3Og70Tat404vO1OHrwprN90A==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.16.4"
|
||||||
|
"@vue/compiler-core" "3.2.30"
|
||||||
|
"@vue/compiler-dom" "3.2.30"
|
||||||
|
"@vue/compiler-ssr" "3.2.30"
|
||||||
|
"@vue/reactivity-transform" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
magic-string "^0.25.7"
|
||||||
|
postcss "^8.1.10"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
"@vue/compiler-ssr@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.30.tgz#fc2bc13a9cdfd70fcffab3f0bc7de141cd9c3411"
|
||||||
|
integrity sha512-OUh3MwAu/PsD7VN3UOdBbTkltkrUCNouSht47+CMRzpUR5+ta7+xyMAVHeq8wg4YZenWaJimbR5TL35Ka4Vk6g==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
|
||||||
"@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.1.2":
|
"@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.1.2":
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz#f9f5fb53464b0c37b2c8d2f3fbfe44df60f61dc9"
|
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz#f9f5fb53464b0c37b2c8d2f3fbfe44df60f61dc9"
|
||||||
@ -1805,6 +1852,11 @@
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prettier "^1.18.2 || ^2.0.0"
|
prettier "^1.18.2 || ^2.0.0"
|
||||||
|
|
||||||
|
"@vue/devtools-api@^6.0.0-beta.18":
|
||||||
|
version "6.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.0.2.tgz#92267911dd2772c5a7641d2889051c6193a7c6aa"
|
||||||
|
integrity sha512-1W8ylfudTFepNgpgY1Dp29iVPlseIy+0+4xXoMkxKmdnxWx8vm8dNgz4F83zML46hHb9aP382JURFfqLuJcWYQ==
|
||||||
|
|
||||||
"@vue/eslint-config-typescript@^7.0.0":
|
"@vue/eslint-config-typescript@^7.0.0":
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-7.0.0.tgz#220c70c2edf7a253e739298525f4d401b8ef0038"
|
resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-7.0.0.tgz#220c70c2edf7a253e739298525f4d401b8ef0038"
|
||||||
@ -1817,6 +1869,54 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz#ceb924b4ecb3b9c43871c7a429a02f8423e621ab"
|
resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz#ceb924b4ecb3b9c43871c7a429a02f8423e621ab"
|
||||||
integrity sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==
|
integrity sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==
|
||||||
|
|
||||||
|
"@vue/reactivity-transform@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.30.tgz#2006e9f4645777a481b78ae77fc486159afa8480"
|
||||||
|
integrity sha512-Le5XzCJyK3qTjoTnvQG/Ehu8fYjayauMNFyMaEnwFlm/avDofpuibpS9u+/6AgzsGnVWN+i0Jgf25bJd9DIwMw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.16.4"
|
||||||
|
"@vue/compiler-core" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
magic-string "^0.25.7"
|
||||||
|
|
||||||
|
"@vue/reactivity@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.30.tgz#fdae2bb66d075c34593ea7e15c6831300a1ad39e"
|
||||||
|
integrity sha512-qlNKbkRn2JiGxVUEdoXbLAy+vcuHUCcq+YH2uXWz0BNMvXY2plmz+oqsw+694llwmYLkke5lbdYF4DIupisIkg==
|
||||||
|
dependencies:
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
|
||||||
|
"@vue/runtime-core@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.30.tgz#1acc119ff8a49c06af6b03611bc4e03f464ca8a2"
|
||||||
|
integrity sha512-RTi7xH0Ht/6wfbo2WFBMJTEiyWFTqGhrksJm8lz6E+auO6lXZ6Eq3gPNfLt47GDWCm4xyrv+rs5R4UbarPEQ1Q==
|
||||||
|
dependencies:
|
||||||
|
"@vue/reactivity" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
|
||||||
|
"@vue/runtime-dom@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.30.tgz#16a85b359ea1fff9b1dd61e9d00e93f4652aba5e"
|
||||||
|
integrity sha512-a3+jrncDvEFQmB+v9k0VyT4/Y3XO6OAueCroXXY4yLyr6PJeyxljweV5TzvW0rvVzH9sZO0QAvG76Lo+6C92Qw==
|
||||||
|
dependencies:
|
||||||
|
"@vue/runtime-core" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
csstype "^2.6.8"
|
||||||
|
|
||||||
|
"@vue/server-renderer@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.30.tgz#4acccad3933475d07b94560c6cb205363975b969"
|
||||||
|
integrity sha512-pzb8J/w+JdZVOtuKFlirGqrs4GP60FXGDJySw3WV2pCetuFstaacDrnymEeSo3ohAD+Qjv7zAG+Y7OvkdxQxmQ==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-ssr" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
|
||||||
|
"@vue/shared@3.2.30":
|
||||||
|
version "3.2.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.30.tgz#e2ba8f6692399c27c81c668ecd3f1a4e13ee2f5e"
|
||||||
|
integrity sha512-B3HouBtUxcfu2w2d+VhdLcVBXKYYhXiFMAfQ+hoe8NUhKkPRkWDIqhpuehCZxVQ3S2dN1P1WfKGlxGC+pfmxGg==
|
||||||
|
|
||||||
"@vue/web-component-wrapper@^1.2.0":
|
"@vue/web-component-wrapper@^1.2.0":
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz#b6b40a7625429d2bd7c2281ddba601ed05dc7f1a"
|
resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz#b6b40a7625429d2bd7c2281ddba601ed05dc7f1a"
|
||||||
@ -4065,6 +4165,11 @@ csso@^4.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
css-tree "^1.1.2"
|
css-tree "^1.1.2"
|
||||||
|
|
||||||
|
csstype@^2.6.8:
|
||||||
|
version "2.6.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.19.tgz#feeb5aae89020bb389e1f63669a5ed490e391caa"
|
||||||
|
integrity sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ==
|
||||||
|
|
||||||
csvjson-csv2json@^5.0.6:
|
csvjson-csv2json@^5.0.6:
|
||||||
version "5.0.6"
|
version "5.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/csvjson-csv2json/-/csvjson-csv2json-5.0.6.tgz#b056f77dd3c13a2f550ada1c31f05072aad6ff3a"
|
resolved "https://registry.yarnpkg.com/csvjson-csv2json/-/csvjson-csv2json-5.0.6.tgz#b056f77dd3c13a2f550ada1c31f05072aad6ff3a"
|
||||||
@ -4981,6 +5086,11 @@ estraverse@^5.1.0, estraverse@^5.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
|
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
|
||||||
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
|
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
|
||||||
|
|
||||||
|
estree-walker@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||||
|
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||||
|
|
||||||
esutils@^2.0.2:
|
esutils@^2.0.2:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||||
@ -7402,6 +7512,13 @@ luxon@^2.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.0.2.tgz#11f2cd4a11655fdf92e076b5782d7ede5bcdd133"
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.0.2.tgz#11f2cd4a11655fdf92e076b5782d7ede5bcdd133"
|
||||||
integrity sha512-ZRioYLCgRHrtTORaZX1mx+jtxKtKuI5ZDvHNAmqpUzGqSrR+tL4FVLn/CUGMA3h0+AKD1MAxGI5GnCqR5txNqg==
|
integrity sha512-ZRioYLCgRHrtTORaZX1mx+jtxKtKuI5ZDvHNAmqpUzGqSrR+tL4FVLn/CUGMA3h0+AKD1MAxGI5GnCqR5txNqg==
|
||||||
|
|
||||||
|
magic-string@^0.25.7:
|
||||||
|
version "0.25.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||||
|
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
|
||||||
|
dependencies:
|
||||||
|
sourcemap-codec "^1.4.4"
|
||||||
|
|
||||||
make-dir@^2.0.0:
|
make-dir@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||||
@ -7782,6 +7899,11 @@ nanoid@^3.1.22, nanoid@^3.1.30:
|
|||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.32.tgz#8f96069e6239cc0a9ae8c0d3b41a3b4933a88c0a"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.32.tgz#8f96069e6239cc0a9ae8c0d3b41a3b4933a88c0a"
|
||||||
integrity sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw==
|
integrity sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw==
|
||||||
|
|
||||||
|
nanoid@^3.2.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
|
||||||
|
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
|
||||||
|
|
||||||
nanomatch@^1.2.9:
|
nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
version "1.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||||
@ -8442,9 +8564,9 @@ performance-now@^2.1.0:
|
|||||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||||
|
|
||||||
pesa@^1.1.3:
|
pesa@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/pesa/-/pesa-1.1.3.tgz#cddd43b02a1db55cd6fb7b220257d33a268588a4"
|
resolved "https://registry.yarnpkg.com/pesa/-/pesa-1.1.6.tgz#a123dc7a31c977bbfb6dbbee14a9913c8385cd0f"
|
||||||
integrity sha512-WcgR2zb5h8h+k9JQb+xkLsYkdMuoxqKgqWm5uTcbi3EGNg3r0tfzcvIBpRYLtZ6TtICbyCRZxWi0WXCh5jSw0A==
|
integrity sha512-EDs4Tj8QX7hZITkKhfHeVT/9oQRqRPyF3pVet1FqGX94/zkcqreBYb94ojUVrzZmir26+IHaKyCpTB6eqC04uw==
|
||||||
|
|
||||||
pg-connection-string@2.5.0:
|
pg-connection-string@2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
@ -8947,6 +9069,15 @@ postcss@^8:
|
|||||||
nanoid "^3.1.22"
|
nanoid "^3.1.22"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
postcss@^8.1.10:
|
||||||
|
version "8.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.6.tgz#c5ff3c3c457a23864f32cb45ac9b741498a09ae1"
|
||||||
|
integrity sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.2.0"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
postcss@^8.3.5:
|
postcss@^8.3.5:
|
||||||
version "8.4.5"
|
version "8.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95"
|
||||||
@ -10069,6 +10200,11 @@ source-map-js@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
|
||||||
integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
|
integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
|
||||||
|
|
||||||
|
source-map-js@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
|
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||||
|
|
||||||
source-map-resolve@^0.5.0:
|
source-map-resolve@^0.5.0:
|
||||||
version "0.5.3"
|
version "0.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
|
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
|
||||||
@ -10116,6 +10252,11 @@ source-map@^0.7.3, source-map@~0.7.2:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||||
|
|
||||||
|
sourcemap-codec@^1.4.4:
|
||||||
|
version "1.4.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||||
|
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||||
|
|
||||||
spdx-correct@^3.0.0:
|
spdx-correct@^3.0.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||||
@ -11338,10 +11479,12 @@ vue-loader@^15.9.2:
|
|||||||
vue-hot-reload-api "^2.3.0"
|
vue-hot-reload-api "^2.3.0"
|
||||||
vue-style-loader "^4.1.0"
|
vue-style-loader "^4.1.0"
|
||||||
|
|
||||||
vue-router@^3.5.3:
|
vue-router@^4.0.12:
|
||||||
version "3.5.3"
|
version "4.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.3.tgz#041048053e336829d05dafacf6a8fb669a2e7999"
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.12.tgz#8dc792cddf5bb1abcc3908f9064136de7e13c460"
|
||||||
integrity sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg==
|
integrity sha512-CPXvfqe+mZLB1kBWssssTiWg4EQERyqJZes7USiqfW9B5N2x+nHlnsM1D3b5CaJ6qgCvMmYJnz+G0iWjNCvXrg==
|
||||||
|
dependencies:
|
||||||
|
"@vue/devtools-api" "^6.0.0-beta.18"
|
||||||
|
|
||||||
vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
|
vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
|
||||||
version "4.1.3"
|
version "4.1.3"
|
||||||
@ -11364,10 +11507,16 @@ vue-template-es2015-compiler@^1.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||||
|
|
||||||
vue@^2.6.14:
|
vue@^3.2.30:
|
||||||
version "2.6.14"
|
version "3.2.30"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.30.tgz#47de3039631ac22cab2fd26b427575260199b8bb"
|
||||||
integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==
|
integrity sha512-ZmTFWVJUX2XADkuOB8GcLTuxnBLogjJBTNVrM7WsTnjqRQ+VR8bLNrvNsbn8vj/LaP5+0WFAPrpngOYE2x+e+Q==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.2.30"
|
||||||
|
"@vue/compiler-sfc" "3.2.30"
|
||||||
|
"@vue/runtime-dom" "3.2.30"
|
||||||
|
"@vue/server-renderer" "3.2.30"
|
||||||
|
"@vue/shared" "3.2.30"
|
||||||
|
|
||||||
watchpack-chokidar2@^2.0.1:
|
watchpack-chokidar2@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user