mirror of
https://github.com/frappe/books.git
synced 2025-01-05 16:12:21 +00:00
feat(ui): add a loading element
This commit is contained in:
parent
e1389184ed
commit
3d175f1730
63
src/components/Loading.vue
Normal file
63
src/components/Loading.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 flex justify-end pb-6 pr-6"
|
||||||
|
style="width: calc(100% - 12rem)"
|
||||||
|
v-if="open && !close"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="
|
||||||
|
text-gray-900
|
||||||
|
shadow-lg
|
||||||
|
px-3
|
||||||
|
py-3
|
||||||
|
items-center
|
||||||
|
w-96
|
||||||
|
z-10
|
||||||
|
bg-white
|
||||||
|
rounded-lg
|
||||||
|
"
|
||||||
|
v-if="true"
|
||||||
|
>
|
||||||
|
<p class="text-base text-gray-600 pb-2" v-if="message.length">{{ message }}</p>
|
||||||
|
<div class="w-full flex flex-row items-center">
|
||||||
|
<div class="w-full bg-gray-200 h-3 mr-2 rounded">
|
||||||
|
<div
|
||||||
|
class="h-3 rounded bg-blue-400"
|
||||||
|
:style="{ width: `${percent * 100}%` }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<feather-icon
|
||||||
|
name="x"
|
||||||
|
class="
|
||||||
|
w-4
|
||||||
|
h-4
|
||||||
|
ml-auto
|
||||||
|
text-gray-600
|
||||||
|
cursor-pointer
|
||||||
|
hover:text-gray-800
|
||||||
|
"
|
||||||
|
@click="closeToast"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
open: { type: Boolean, default: false },
|
||||||
|
percent: { type: Number, default: 0.5 },
|
||||||
|
message: { type: String, default: '' },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
close: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeToast() {
|
||||||
|
this.close = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -35,6 +35,12 @@ type LabelTemplateFieldMap = {
|
|||||||
[key: string]: TemplateField;
|
[key: string]: TemplateField;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type LoadingStatusCallback = (
|
||||||
|
isMakingEntries: boolean,
|
||||||
|
entriesMade: number,
|
||||||
|
totalEntries: number
|
||||||
|
) => void;
|
||||||
|
|
||||||
interface TemplateField {
|
interface TemplateField {
|
||||||
label: string;
|
label: string;
|
||||||
fieldname: string;
|
fieldname: string;
|
||||||
@ -354,11 +360,15 @@ export class Importer {
|
|||||||
return Object.keys(docMap).map((k) => docMap[k]);
|
return Object.keys(docMap).map((k) => docMap[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async importData(): Promise<Status> {
|
async importData(setLoadingStatus: LoadingStatusCallback): Promise<Status> {
|
||||||
const status: Status = { success: false, names: [], message: '' };
|
const status: Status = { success: false, names: [], message: '' };
|
||||||
const shouldDeleteName = await isNameAutoSet(this.doctype);
|
const shouldDeleteName = await isNameAutoSet(this.doctype);
|
||||||
|
const docObjs = this.getDocs();
|
||||||
|
|
||||||
for (const docObj of this.getDocs()) {
|
let entriesMade = 0;
|
||||||
|
setLoadingStatus(true, 0, docObjs.length);
|
||||||
|
|
||||||
|
for (const docObj of docObjs) {
|
||||||
if (shouldDeleteName) {
|
if (shouldDeleteName) {
|
||||||
delete docObj.name;
|
delete docObj.name;
|
||||||
}
|
}
|
||||||
@ -378,7 +388,10 @@ export class Importer {
|
|||||||
if (this.shouldSubmit) {
|
if (this.shouldSubmit) {
|
||||||
await doc.submit();
|
await doc.submit();
|
||||||
}
|
}
|
||||||
|
entriesMade += 1;
|
||||||
|
setLoadingStatus(true, entriesMade, docObjs.length);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
setLoadingStatus(false, entriesMade, docObjs.length);
|
||||||
const messages = [
|
const messages = [
|
||||||
frappe.t`Could not import ${this.doctype} ${doc.name}.`,
|
frappe.t`Could not import ${this.doctype} ${doc.name}.`,
|
||||||
];
|
];
|
||||||
@ -405,6 +418,7 @@ export class Importer {
|
|||||||
status.names.push(doc.name);
|
status.names.push(doc.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoadingStatus(false, entriesMade, docObjs.length);
|
||||||
status.success = true;
|
status.success = true;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col overflow-hidden">
|
<div class="flex flex-col overflow-hidden w-full">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<template #title>
|
<template #title>
|
||||||
<h1 class="text-2xl font-bold">
|
<h1 class="text-2xl font-bold">
|
||||||
@ -212,7 +212,14 @@
|
|||||||
:key="'matrix-row-' + i"
|
:key="'matrix-row-' + i"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="w-4 h-4 text-gray-600 hover:text-gray-900 cursor-pointer outline-none"
|
class="
|
||||||
|
w-4
|
||||||
|
h-4
|
||||||
|
text-gray-600
|
||||||
|
hover:text-gray-900
|
||||||
|
cursor-pointer
|
||||||
|
outline-none
|
||||||
|
"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
importer.dropRow(i);
|
importer.dropRow(i);
|
||||||
@ -306,12 +313,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!importType"
|
v-if="!importType"
|
||||||
class="flex justify-center h-full items-center mb-16"
|
class="flex justify-center h-full w-full items-center mb-16"
|
||||||
>
|
>
|
||||||
<HowTo link="https://youtu.be/ukHAgcnVxTQ">
|
<HowTo link="https://youtu.be/ukHAgcnVxTQ">
|
||||||
{{ t`How to Use Data Import?` }}
|
{{ t`How to Use Data Import?` }}
|
||||||
</HowTo>
|
</HowTo>
|
||||||
</div>
|
</div>
|
||||||
|
<Loading
|
||||||
|
v-if="openLoading"
|
||||||
|
:open="openLoading"
|
||||||
|
:percent="percentLoading"
|
||||||
|
:message="messageLoading"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@ -326,6 +339,7 @@ import { IPC_ACTIONS } from '@/messages';
|
|||||||
import { getSavePath, saveData, showMessageDialog } from '@/utils';
|
import { getSavePath, saveData, showMessageDialog } from '@/utils';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import Loading from '../components/Loading.vue';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
@ -334,6 +348,7 @@ export default {
|
|||||||
DropdownWithActions,
|
DropdownWithActions,
|
||||||
FeatherIcon,
|
FeatherIcon,
|
||||||
HowTo,
|
HowTo,
|
||||||
|
Loading,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -343,6 +358,9 @@ export default {
|
|||||||
file: null,
|
file: null,
|
||||||
importer: null,
|
importer: null,
|
||||||
importType: '',
|
importType: '',
|
||||||
|
openLoading: false,
|
||||||
|
percentLoading: 0,
|
||||||
|
messageLoading: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -533,7 +551,9 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { success, names, message } = await this.importer.importData();
|
const { success, names, message } = await this.importer.importData(
|
||||||
|
this.setLoadingStatus
|
||||||
|
);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: this.t`Import Failed`,
|
message: this.t`Import Failed`,
|
||||||
@ -552,6 +572,13 @@ export default {
|
|||||||
this.importType = importType;
|
this.importType = importType;
|
||||||
this.importer = new Importer(this.labelDoctypeMap[this.importType]);
|
this.importer = new Importer(this.labelDoctypeMap[this.importType]);
|
||||||
},
|
},
|
||||||
|
setLoadingStatus(isMakingEntries, entriesMade, totalEntries) {
|
||||||
|
this.openLoading = isMakingEntries;
|
||||||
|
this.percentLoading = entriesMade / totalEntries;
|
||||||
|
this.messageLoading = isMakingEntries
|
||||||
|
? `${entriesMade} entries made out of ${totalEntries}...`
|
||||||
|
: '';
|
||||||
|
},
|
||||||
async selectFile() {
|
async selectFile() {
|
||||||
const options = {
|
const options = {
|
||||||
title: this.t`Select File`,
|
title: this.t`Select File`,
|
||||||
|
Loading…
Reference in New Issue
Block a user