2
0
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:
18alantom 2022-03-01 14:16:12 +05:30 committed by Alan
parent e1389184ed
commit 3d175f1730
3 changed files with 111 additions and 7 deletions

View 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>

View File

@ -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;
} }

View File

@ -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`,