2
0
mirror of https://github.com/frappe/books.git synced 2025-01-10 10:16:22 +00:00
books/src/pages/QuickEditForm.vue
18alantom 440419b367 fix(ux): documentation path
- remove unnecessary injects
2023-03-01 14:05:59 +05:30

334 lines
8.3 KiB
Vue

<template>
<div
class="border-s h-full overflow-auto w-quick-edit"
:class="white ? 'bg-white' : 'bg-gray-25'"
>
<!-- Quick edit Tool bar -->
<div
class="flex items-center justify-between px-4 h-row-largest"
:class="{ 'border-b': showName }"
>
<!-- Close Button and Status Text -->
<div class="flex items-center">
<Button :icon="true" @click="routeToPrevious">
<feather-icon name="x" class="w-4 h-4" />
</Button>
<span v-if="statusText" class="ms-2 text-base text-gray-600">{{
statusText
}}</span>
</div>
<!-- Actions, Badge and Status Change Buttons -->
<div class="flex items-stretch gap-2" v-if="showSave">
<StatusBadge :status="status" />
<DropdownWithActions :actions="actions" />
<Button
:icon="true"
@click="sync"
type="primary"
v-if="doc?.canSave"
class="text-white text-xs"
>
{{ t`Save` }}
</Button>
<Button
:icon="true"
@click="submit"
type="primary"
v-else-if="doc?.canSubmit"
class="text-white text-xs"
>
{{ t`Submit` }}
</Button>
</div>
</div>
<!-- Name and image -->
<div
class="items-center"
:class="imageField ? 'grid' : 'flex justify-center'"
:style="{
height: `calc(var(--h-row-mid) * ${!!imageField ? '2 + 1px' : '1'})`,
gridTemplateColumns: `minmax(0, 1.1fr) minmax(0, 2fr)`,
}"
v-if="doc && showName && (titleField || imageField)"
>
<FormControl
v-if="imageField"
class="ms-4"
:df="imageField"
:value="doc[imageField.fieldname]"
@change="(value) => valueChange(imageField, value)"
:letter-placeholder="doc[titleField.fieldname]?.[0] ?? ''"
/>
<FormControl
v-if="titleField"
:class="!!imageField ? 'me-4' : 'w-full mx-4'"
:input-class="[
'font-semibold text-xl',
!!imageField ? '' : 'text-center',
]"
ref="titleControl"
size="small"
:df="titleField"
:value="doc[titleField.fieldname]"
:read-only="doc.inserted || doc.schema.naming !== 'manual'"
@change="(value) => valueChange(titleField, value)"
/>
</div>
<!-- Rest of the form -->
<TwoColumnForm
class="w-full"
ref="form"
v-if="doc"
:doc="doc"
:fields="fields"
:autosave="false"
:column-ratio="[1.1, 2]"
/>
<!-- QuickEdit Widgets -->
<component v-if="quickEditWidget" :is="quickEditWidget" />
</div>
</template>
<script>
import { computed } from '@vue/reactivity';
import { t } from 'fyo';
import { Doc } from 'fyo/model/doc';
import { getDocStatus } from 'models/helpers';
import Button from 'src/components/Button.vue';
import FormControl from 'src/components/Controls/FormControl.vue';
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
import StatusBadge from 'src/components/StatusBadge.vue';
import TwoColumnForm from 'src/components/TwoColumnForm.vue';
import { fyo } from 'src/initFyo';
import { getQuickEditWidget } from 'src/utils/quickEditWidgets';
import { focusedDocsRef } from 'src/utils/refs';
import { getActionsForDoc, openQuickEdit } from 'src/utils/ui';
export default {
name: 'QuickEditForm',
props: {
name: String,
schemaName: String,
defaults: String,
white: { type: Boolean, default: false },
routeBack: { type: Boolean, default: true },
showName: { type: Boolean, default: true },
showSave: { type: Boolean, default: true },
sourceDoc: { type: Doc, default: null },
loadOnClose: { type: Boolean, default: true },
sourceFields: { type: Array, default: () => [] },
hideFields: { type: Array, default: () => [] },
showFields: { type: Array, default: () => [] },
},
components: {
Button,
FormControl,
StatusBadge,
TwoColumnForm,
DropdownWithActions,
},
emits: ['close'],
provide() {
return {
schemaName: this.schemaName,
name: this.name,
doc: computed(() => this.doc),
};
},
data() {
return {
doc: null,
values: null,
titleField: null,
imageField: null,
statusText: null,
};
},
async mounted() {
if (this.defaults) {
this.values = JSON.parse(this.defaults);
}
await this.fetchFieldsAndDoc();
focusedDocsRef.add(this.doc);
if (fyo.store.isDevelopment) {
window.qef = this;
}
},
activated() {
focusedDocsRef.add(this.doc);
},
deactivated() {
focusedDocsRef.delete(this.doc);
},
unmounted() {
focusedDocsRef.delete(this.doc);
},
computed: {
isChild() {
return !!this?.doc?.schema?.isChild;
},
schema() {
return fyo.schemaMap[this.schemaName] ?? null;
},
status() {
return getDocStatus(this.doc);
},
fields() {
if (this.sourceFields?.length) {
return this.sourceFields;
}
if (!this.schema) {
return [];
}
const fieldnames = (this.schema.quickEditFields ?? ['name']).filter(
(f) => !this.hideFields.includes(f)
);
if (this.showFields?.length) {
fieldnames.push(
...this.schema.fields
.map((f) => f.fieldname)
.filter((f) => this.showFields.includes(f))
);
}
return fieldnames.map((f) => fyo.getField(this.schemaName, f));
},
actions() {
return getActionsForDoc(this.doc);
},
quickEditWidget() {
if (this.doc?.notInserted ?? true) {
return null;
}
const widget = getQuickEditWidget(this.schemaName);
if (widget === null) {
return null;
}
return widget(this.doc);
},
},
methods: {
async fetchFieldsAndDoc() {
if (!this.schema) {
return;
}
const titleField = this.schema.titleField;
this.titleField = fyo.getField(this.schemaName, titleField);
this.imageField = fyo.getField(this.schemaName, 'image');
await this.fetchDoc();
// setup the title field
this.setTitleField();
// set default values
if (this.values) {
this.doc?.set(this.values);
}
},
setTitleField() {
const { fieldname, readOnly } = this.titleField;
if (!this.doc?.notInserted || !this?.doc[fieldname]) {
return;
}
const isManual = this.schema.naming === 'manual';
const isNumberSeries = fyo.getField(this.schemaName, 'numberSeries');
if (readOnly && (!this?.doc[fieldname] || isNumberSeries)) {
this.doc.set(fieldname, t`New ${this.schema.label}`);
}
if (this?.doc[fieldname] && !isManual) {
return;
}
this.doc.set(fieldname, '');
setTimeout(() => {
this.$refs.titleControl?.focus();
}, 300);
},
async fetchDoc() {
if (this.sourceDoc) {
return (this.doc = this.sourceDoc);
}
if (!this.schemaName) {
this.$router.back();
}
if (this.name) {
try {
this.doc = await fyo.doc.getDoc(this.schemaName, this.name);
} catch (e) {
this.$router.back();
}
} else {
this.doc = fyo.doc.getNewDoc(this.schemaName);
}
if (this.doc === null) {
return;
}
this.doc.once('afterRename', () => {
openQuickEdit({
schemaName: this.schemaName,
name: this.doc.name,
});
});
},
valueChange(df, value) {
this.$refs.form.onChange(df, value);
},
async sync() {
this.statusText = t`Saving`;
try {
await this.$refs.form.sync();
setTimeout(() => {
this.statusText = null;
}, 300);
} catch (err) {
this.statusText = null;
console.error(err);
}
},
async submit() {
this.statusText = t`Submitting`;
try {
await this.$refs.form.submit();
setTimeout(() => {
this.statusText = null;
}, 300);
} catch (err) {
this.statusText = null;
console.error(err);
}
},
routeToPrevious() {
if (this.loadOnClose && this.doc.dirty && !this.doc.notInserted) {
this.doc.load();
}
if (this.routeBack) {
this.$router.back();
} else {
this.$emit('close');
}
},
},
};
</script>