2
0
mirror of https://github.com/frappe/books.git synced 2025-02-02 12:08:27 +00:00
books/src/pages/CommonForm/CommonForm.vue

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

334 lines
8.5 KiB
Vue
Raw Normal View History

2023-02-17 21:05:06 +05:30
<template>
<FormContainer>
<template #header-left v-if="hasDoc">
<StatusBadge :status="status" class="h-8" />
</template>
2023-02-20 10:22:19 +05:30
<template #header v-if="hasDoc">
<Button
v-if="!doc.isCancelled && !doc.dirty && isPrintable"
:icon="true"
@click="routeTo(`/print/${doc.schemaName}/${doc.name}`)"
>
{{ t`Print` }}
</Button>
2023-02-20 10:22:19 +05:30
<DropdownWithActions
v-for="group of groupedActions"
:key="group.label"
:type="group.type"
:actions="group.actions"
>
<p v-if="group.group">
{{ group.group }}
</p>
<feather-icon v-else name="more-horizontal" class="w-4 h-4" />
</DropdownWithActions>
<Button v-if="doc?.canSave" type="primary" @click="sync">
2023-02-20 10:22:19 +05:30
{{ t`Save` }}
</Button>
<Button v-else-if="doc?.canSubmit" type="primary" @click="submit">{{
t`Submit`
}}</Button>
2023-02-20 10:22:19 +05:30
</template>
2023-02-17 21:05:06 +05:30
<template #body>
<FormHeader
:form-title="title"
:form-sub-title="schema.label"
class="sticky top-0 bg-white border-b"
2023-02-20 10:22:19 +05:30
>
</FormHeader>
2023-02-17 21:05:06 +05:30
<!-- Section Container -->
<div v-if="hasDoc" class="overflow-auto custom-scroll">
<CommonFormSection
v-for="([name, fields], idx) in activeGroup.entries()"
@editrow="(doc: Doc) => toggleQuickEditDoc(doc)"
:key="name + idx"
2023-02-17 21:05:06 +05:30
ref="section"
class="p-4"
:class="idx !== 0 && activeGroup.size > 1 ? 'border-t' : ''"
:show-title="activeGroup.size > 1 && name !== t`Default`"
2023-02-17 21:05:06 +05:30
:title="name"
:fields="fields"
:doc="doc"
:errors="errors"
@value-change="onValueChange"
2023-02-17 21:05:06 +05:30
/>
</div>
<!-- Tab Bar -->
<div
class="
mt-auto
px-4
pb-4
flex
gap-8
border-t
flex-shrink-0
sticky
bottom-0
bg-white
"
v-if="groupedFields && groupedFields.size > 1"
2023-02-17 21:05:06 +05:30
>
<div
v-for="key of groupedFields.keys()"
2023-02-17 21:05:06 +05:30
:key="key"
@click="activeTab = key"
class="text-sm cursor-pointer"
:class="
key === activeTab
? 'text-blue-500 font-semibold border-t-2 border-blue-500'
: ''
"
:style="{
paddingTop: key === activeTab ? 'calc(1rem - 2px)' : '1rem',
}"
>
{{ key }}
</div>
</div>
</template>
<template #quickedit>
<Transition name="quickedit">
<QuickEditForm
v-if="hasQeDoc"
:name="qeDoc.name"
:show-name="false"
:show-save="false"
:source-doc="qeDoc"
:schema-name="qeDoc.schemaName"
:white="true"
:route-back="false"
:load-on-close="false"
@close="() => toggleQuickEditDoc(null)"
/>
</Transition>
</template>
2023-02-17 21:05:06 +05:30
</FormContainer>
</template>
<script lang="ts">
import { DocValue } from 'fyo/core/types';
2023-02-17 21:05:06 +05:30
import { Doc } from 'fyo/model/doc';
import { ValidationError } from 'fyo/utils/errors';
2023-02-20 10:22:19 +05:30
import { getDocStatus } from 'models/helpers';
2023-02-17 21:05:06 +05:30
import { ModelNameEnum } from 'models/types';
import { Field, Schema } from 'schemas/types';
2023-02-20 10:22:19 +05:30
import Button from 'src/components/Button.vue';
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
2023-02-17 21:05:06 +05:30
import FormContainer from 'src/components/FormContainer.vue';
import FormHeader from 'src/components/FormHeader.vue';
2023-02-20 10:22:19 +05:30
import StatusBadge from 'src/components/StatusBadge.vue';
import { handleErrorWithDialog } from 'src/errorHandling';
import { getErrorMessage } from 'src/utils';
import { docsPathMap } from 'src/utils/misc';
import { docsPathRef, focusedDocsRef } from 'src/utils/refs';
2023-02-20 10:22:19 +05:30
import { ActionGroup, UIGroupedFields } from 'src/utils/types';
import {
getDocFromNameIfExistsElseNew,
2023-02-20 10:22:19 +05:30
getFieldsGroupedByTabAndSection,
getGroupedActionsForDoc,
isPrintable,
routeTo,
2023-02-20 10:22:19 +05:30
} from 'src/utils/ui';
import { computed, defineComponent, nextTick } from 'vue';
import QuickEditForm from '../QuickEditForm.vue';
2023-02-17 21:05:06 +05:30
import CommonFormSection from './CommonFormSection.vue';
export default defineComponent({
props: {
name: { type: String, default: '' },
schemaName: { type: String, default: ModelNameEnum.SalesInvoice },
2023-02-17 21:05:06 +05:30
},
2023-02-20 10:22:19 +05:30
provide() {
return {
schemaName: computed(() => this.docOrNull?.schemaName),
name: computed(() => this.docOrNull?.name),
doc: computed(() => this.docOrNull),
};
},
2023-02-17 21:05:06 +05:30
data() {
return {
errors: {},
2023-02-17 21:05:06 +05:30
docOrNull: null,
activeTab: this.t`Default`,
groupedFields: null,
quickEditDoc: null,
isPrintable: false,
} as {
errors: Record<string, string>;
docOrNull: null | Doc;
activeTab: string;
groupedFields: null | UIGroupedFields;
quickEditDoc: null | Doc;
isPrintable: boolean;
};
2023-02-17 21:05:06 +05:30
},
async mounted() {
if (this.fyo.store.isDevelopment) {
// @ts-ignore
window.cf = this;
}
2023-02-20 10:22:19 +05:30
await this.setDoc();
focusedDocsRef.add(this.docOrNull);
this.updateGroupedFields();
if (this.groupedFields) {
this.activeTab = [...this.groupedFields.keys()][0];
}
this.isPrintable = await isPrintable(this.schemaName);
2023-02-17 21:05:06 +05:30
},
activated(): void {
docsPathRef.value = docsPathMap[this.schemaName] ?? '';
focusedDocsRef.add(this.docOrNull);
},
deactivated(): void {
docsPathRef.value = '';
if (this.docOrNull) {
focusedDocsRef.delete(this.doc);
}
},
2023-02-17 21:05:06 +05:30
computed: {
hasDoc(): boolean {
return !!this.docOrNull;
},
hasQeDoc(): boolean {
return !!this.quickEditDoc;
},
2023-02-20 10:22:19 +05:30
status(): string {
if (!this.hasDoc) {
return '';
}
return getDocStatus(this.doc);
},
2023-02-17 21:05:06 +05:30
doc(): Doc {
const doc = this.docOrNull as Doc | null;
if (!doc) {
throw new ValidationError(
this.t`Doc ${this.schema.label} ${this.name} not set`
);
}
return doc;
},
qeDoc(): Doc {
const doc = this.quickEditDoc as Doc | null;
if (!doc) {
throw new ValidationError(
this.t`Doc ${this.schema.label} ${this.name} not set`
);
}
return doc;
},
2023-02-17 21:05:06 +05:30
title(): string {
2023-02-20 10:22:19 +05:30
if (this.schema.isSubmittable && this.docOrNull?.notInserted) {
2023-02-17 21:05:06 +05:30
return this.t`New Entry`;
}
2023-02-20 10:22:19 +05:30
return this.docOrNull?.name! ?? this.t`New Entry`;
2023-02-17 21:05:06 +05:30
},
schema(): Schema {
const schema = this.fyo.schemaMap[this.schemaName];
if (!schema) {
throw new ValidationError(`no schema found with ${this.schemaName}`);
}
return schema;
},
activeGroup(): Map<string, Field[]> {
if (!this.groupedFields) {
return new Map();
}
const group = this.groupedFields.get(this.activeTab);
2023-02-17 21:05:06 +05:30
if (!group) {
throw new ValidationError(
`Tab group ${this.activeTab} has no value set`
);
}
return group;
},
2023-02-20 10:22:19 +05:30
groupedActions(): ActionGroup[] {
if (!this.hasDoc) {
return [];
}
2023-02-17 21:05:06 +05:30
2023-02-20 10:22:19 +05:30
return getGroupedActionsForDoc(this.doc);
},
},
methods: {
routeTo,
updateGroupedFields(): void {
if (!this.hasDoc) {
return;
}
this.groupedFields = getFieldsGroupedByTabAndSection(
this.schema,
this.doc
);
},
async sync() {
try {
await this.doc.sync();
this.updateGroupedFields();
} catch (err) {
await handleErrorWithDialog(err, this.doc);
}
},
async submit() {
try {
await this.doc.submit();
this.updateGroupedFields();
} catch (err) {
await handleErrorWithDialog(err, this.doc);
}
},
2023-02-20 10:22:19 +05:30
async setDoc() {
if (this.hasDoc) {
return;
}
2023-02-17 21:05:06 +05:30
this.docOrNull = await getDocFromNameIfExistsElseNew(
this.schemaName,
this.name
);
},
async toggleQuickEditDoc(doc: Doc | null) {
if (this.quickEditDoc && doc) {
this.quickEditDoc = null;
await nextTick();
}
this.quickEditDoc = doc;
},
async onValueChange(field: Field, value: DocValue) {
const { fieldname } = field;
delete this.errors[fieldname];
try {
await this.doc.set(fieldname, value);
} catch (err) {
if (!(err instanceof Error)) {
return;
}
this.errors[fieldname] = getErrorMessage(err, this.doc);
}
this.updateGroupedFields();
},
2023-02-20 10:22:19 +05:30
},
components: {
FormContainer,
FormHeader,
CommonFormSection,
StatusBadge,
Button,
DropdownWithActions,
QuickEditForm,
2023-02-20 10:22:19 +05:30
},
});
2023-02-17 21:05:06 +05:30
</script>