2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

fix: simplify QuickEditForm and usage

- type QuickEditForm, TwoColumnForm
- dont use QuickEditForm for tables
- dont keep-alice QuickEditForm
This commit is contained in:
18alantom 2023-04-17 10:56:00 +05:30
parent 5a3b87333e
commit c754fd35ce
7 changed files with 130 additions and 186 deletions

View File

@ -131,24 +131,17 @@ export default {
},
async openNewDoc() {
const schemaName = this.df.target;
const linkDoc = fyo.doc.getNewDoc(schemaName);
const name = this.linkValue;
const filters = await this.getCreateFilters();
const { openQuickEdit } = await import('src/utils/ui');
openQuickEdit({
schemaName,
name: linkDoc.name,
defaults: Object.assign({}, filters, {
name: this.linkValue,
}),
});
const doc = fyo.doc.getNewDoc(schemaName, { name, ...filters });
openQuickEdit({ doc });
linkDoc.once('afterSync', () => {
doc.once('afterSync', () => {
this.$router.back();
this.results = [];
this.triggerChange(linkDoc.name);
this.triggerChange(doc.name);
});
},
async getCreateFilters() {

View File

@ -8,7 +8,7 @@
ref="controls"
size="small"
:df="df"
:value="doc[df.fieldname]"
:value="(doc[df.fieldname] ?? []) as unknown[]"
@change="async (value) => await onChange(df, value)"
/>
@ -52,21 +52,25 @@
</template>
</div>
</template>
<script>
<script lang="ts">
import { Doc } from 'fyo/model/doc';
import FormControl from 'src/components/Controls/FormControl.vue';
import { fyo } from 'src/initFyo';
import { getErrorMessage } from 'src/utils';
import { evaluateHidden } from 'src/utils/doc';
import Table from './Controls/Table.vue';
import { defineComponent } from 'vue';
import { Field } from 'schemas/types';
import { PropType } from 'vue';
import { DocValue } from 'fyo/core/types';
export default {
export default defineComponent({
name: 'TwoColumnForm',
props: {
doc: Doc,
fields: { type: Array, default: () => [] },
doc: { type: Doc, required: true },
fields: { type: Array as PropType<Field[]>, default: () => [] },
columnRatio: {
type: Array,
type: Array as PropType<number[]>,
default: () => [1, 1],
},
},
@ -79,7 +83,7 @@ export default {
return {
formFields: [],
errors: {},
};
} as { formFields: Field[]; errors: Record<string, string> };
},
components: {
FormControl,
@ -88,22 +92,23 @@ export default {
mounted() {
this.setFormFields();
if (fyo.store.isDevelopment) {
// @ts-ignore
window.tcf = this;
}
},
methods: {
getFieldHeight(df) {
if (['AttachImage', 'Text'].includes(df.fieldtype)) {
getFieldHeight(field: Field) {
if (['AttachImage', 'Text'].includes(field.fieldtype)) {
return 'calc((var(--h-row-mid) + 1px) * 2)';
}
if (this.errors[df.fieldname]) {
if (this.errors[field.fieldname]) {
return 'calc((var(--h-row-mid) + 1px) * 2)';
}
return 'calc(var(--h-row-mid) + 1px)';
},
async onChange(field, value) {
async onChange(field: Field, value: DocValue) {
const { fieldname } = field;
delete this.errors[fieldname];
@ -148,5 +153,5 @@ export default {
};
},
},
};
});
</script>

View File

@ -120,20 +120,7 @@
</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>
<Transition name="quickedit"> </Transition>
<Transition name="quickedit">
<LinkedEntries
v-if="showLinks && !hasQeDoc"
@ -452,7 +439,6 @@ export default defineComponent({
StatusBadge,
Button,
DropdownWithActions,
QuickEditForm,
Barcode,
ExchangeRate,
LinkedEntries,

View File

@ -22,12 +22,10 @@ import { toggleSidebar } from 'src/utils/ui';
<router-view name="edit" v-slot="{ Component, route }">
<Transition name="quickedit">
<div v-if="route?.query?.edit">
<keep-alive>
<component
:is="Component"
:key="route.query.schemaName + route.query.name"
/>
</keep-alive>
</div>
</Transition>
</router-view>

View File

@ -1,8 +1,5 @@
<template>
<div
class="border-s h-full overflow-auto w-quick-edit"
:class="white ? 'bg-white' : 'bg-gray-25'"
>
<div class="border-s h-full overflow-auto w-quick-edit bg-white">
<!-- Quick edit Tool bar -->
<div
class="
@ -13,10 +10,10 @@
h-row-largest
sticky
top-0
border-b
bg-white
"
style="z-index: 1"
:class="{ 'border-b': showName }"
>
<!-- Close Button and Status Text -->
<div class="flex items-center">
@ -29,7 +26,7 @@
</div>
<!-- Actions, Badge and Status Change Buttons -->
<div class="flex items-stretch gap-2" v-if="showSave">
<div class="flex items-stretch gap-2">
<StatusBadge :status="status" />
<DropdownWithActions :actions="actions" />
<Button
@ -61,15 +58,15 @@
height: `calc(var(--h-row-mid) * ${!!imageField ? '2 + 1px' : '1'})`,
gridTemplateColumns: `minmax(0, 1.1fr) minmax(0, 2fr)`,
}"
v-if="doc && showName && (titleField || imageField)"
v-if="doc && (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] ?? ''"
@change="(value) => valueChange(imageField as Field, value)"
:letter-placeholder="letterPlaceHolder"
/>
<FormControl
v-if="titleField"
@ -83,7 +80,7 @@
:df="titleField"
:value="doc[titleField.fieldname]"
:read-only="doc.inserted || doc.schema.naming !== 'manual'"
@change="(value) => valueChange(titleField, value)"
@change="(value) => valueChange(titleField as Field, value)"
/>
</div>
@ -98,11 +95,14 @@
/>
</div>
</template>
<script>
<script lang="ts">
import { computed } from '@vue/reactivity';
import { t } from 'fyo';
import { Doc } from 'fyo/model/doc';
import { DocValue } from 'fyo/core/types';
import { DocStatus } from 'fyo/model/types';
import { getDocStatus } from 'models/helpers';
import { InvoiceStatus } from 'models/types';
import { Field, Schema } from 'schemas/types';
import Button from 'src/components/Button.vue';
import FormControl from 'src/components/Controls/FormControl.vue';
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
@ -110,27 +110,21 @@ import StatusBadge from 'src/components/StatusBadge.vue';
import TwoColumnForm from 'src/components/TwoColumnForm.vue';
import { fyo } from 'src/initFyo';
import { shortcutsKey } from 'src/utils/injectionKeys';
import { DocRef } from 'src/utils/types';
import {
commonDocSubmit,
commonDocSync,
focusOrSelectFormControl,
getActionsForDoc,
openQuickEdit,
} from 'src/utils/ui';
import { useDocShortcuts } from 'src/utils/vueUtils';
import { inject, ref } from 'vue';
import { defineComponent, inject, ref } from 'vue';
export default {
export default defineComponent({
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 },
name: { type: String, required: true },
schemaName: { type: String, required: true },
hideFields: { type: Array, default: () => [] },
showFields: { type: Array, default: () => [] },
},
@ -143,7 +137,7 @@ export default {
},
emits: ['close'],
setup() {
const doc = ref(null);
const doc = ref(null) as DocRef;
const shortcuts = inject(shortcutsKey);
let context = 'QuickEditForm';
@ -152,6 +146,7 @@ export default {
}
return {
form: ref<InstanceType<typeof TwoColumnForm> | null>(null),
doc,
context,
shortcuts,
@ -164,37 +159,52 @@ export default {
},
data() {
return {
values: null,
titleField: null,
imageField: null,
statusText: null,
statusText: '',
} as {
titleField: null | Field;
imageField: null | Field;
statusText: string;
};
},
activated() {
console.log('act');
this.setShortcuts();
},
async mounted() {
if (this.defaults) {
this.values = JSON.parse(this.defaults);
}
await this.fetchFieldsAndDoc();
focusOrSelectFormControl(this.doc, this.$refs.titleControl, false);
console.log('mou');
await this.initialize();
if (fyo.store.isDevelopment) {
// @ts-ignore
window.qef = this;
}
this.setShortcuts();
},
computed: {
isChild() {
return !!this?.doc?.schema?.isChild;
letterPlaceHolder() {
if (!this.doc) {
return '';
}
const fn = this.titleField?.fieldname ?? 'name';
const value = this.doc.get(fn);
if (typeof value === 'string') {
return value[0];
}
return '';
},
schema() {
return fyo.schemaMap[this.schemaName] ?? null;
schema(): Schema {
return fyo.schemaMap[this.schemaName]!;
},
status() {
status(): DocStatus | InvoiceStatus {
if (!this.doc) {
return 'Draft';
}
return getDocStatus(this.doc);
},
fields() {
@ -217,92 +227,84 @@ export default {
return fieldnames.map((f) => fyo.getField(this.schemaName, f));
},
actions() {
if (!this.doc) {
return [];
}
return getActionsForDoc(this.doc);
},
},
methods: {
setShortcuts() {
if (this.shortcuts.has(this.context, ['Escape'])) {
if (this.shortcuts?.has(this.context, ['Escape'])) {
return;
}
this.shortcuts.set(this.context, ['Escape'], () => {
this.shortcuts?.set(this.context, ['Escape'], () => {
this.routeToPrevious();
});
},
async fetchFieldsAndDoc() {
async initialize() {
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();
// set default values
if (this.values) {
this.doc?.set(this.values);
}
},
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) {
this.setFields();
await this.setDoc();
if (!this.doc) {
return;
}
this.doc.once('afterRename', () => {
openQuickEdit({
schemaName: this.schemaName,
name: this.doc.name,
});
});
focusOrSelectFormControl(this.doc, this.$refs.titleControl, false);
},
valueChange(df, value) {
this.$refs.form.onChange(df, value);
setFields() {
const titleFieldName = this.schema.titleField ?? 'name';
this.titleField = fyo.getField(this.schemaName, titleFieldName) ?? null;
this.imageField = fyo.getField(this.schemaName, 'image') ?? null;
},
async setDoc() {
try {
this.doc = await fyo.doc.getDoc(this.schemaName, this.name);
} catch (e) {
return this.$router.back();
}
},
valueChange(field: Field, value: DocValue) {
this.form?.onChange(field, value);
},
async sync() {
if (!this.doc) {
return;
}
this.statusText = t`Saving`;
await commonDocSync(this.doc);
setTimeout(() => {
this.statusText = null;
this.statusText = '';
}, 300);
},
async submit() {
if (!this.doc) {
return;
}
this.statusText = t`Submitting`;
await commonDocSubmit(this.doc);
setTimeout(() => {
this.statusText = null;
this.statusText = '';
}, 300);
},
routeToPrevious() {
if (this.doc.dirty && !this.doc.notInserted) {
this.doc.load();
async routeToPrevious() {
if (this.doc?.dirty && this.doc?.inserted) {
await this.doc.load();
}
if (this.routeBack) {
this.$router.back();
} else {
this.$emit('close');
if (this.doc && this.doc.notInserted) {
await this.doc.delete();
}
this.$router.back();
},
},
};
});
</script>

View File

@ -38,13 +38,10 @@ export type SettingsTab =
| ModelNameEnum.SystemSettings;
export interface QuickEditOptions {
doc?: Doc;
schemaName?: string;
name?: string;
doc: Doc;
hideFields?: string[];
showFields?: string[];
defaults?: Record<string, unknown>;
listFilters?: QueryFilter;
}
export type SidebarConfig = SidebarRoot[];

View File

@ -35,63 +35,26 @@ export const toastDurationMap = { short: 2_500, long: 5_000 } as const;
export async function openQuickEdit({
doc,
schemaName,
name,
hideFields = [],
showFields = [],
defaults = {},
listFilters = {},
}: QuickEditOptions) {
if (doc) {
schemaName = doc.schemaName;
name = doc.name;
const { schemaName, name } = doc;
if (!name) {
throw new ValueError(t`Quick edit error: ${schemaName} entry has no name.`);
}
if (!doc && (!schemaName || !name)) {
throw new ValueError(t`Schema Name or Name not passed to Open Quick Edit`);
}
const currentRoute = router.currentRoute.value;
const query = currentRoute.query;
let method: 'push' | 'replace' = 'push';
if (query.edit && query.schemaName === schemaName) {
method = 'replace';
}
if (query.name === name) {
if (router.currentRoute.value.query.name === name) {
return;
}
const forWhat = (defaults?.for ?? []) as string[];
if (forWhat[0] === 'not in') {
const purpose = forWhat[1]?.[0];
defaults = Object.assign({
for:
purpose === 'Sales'
? 'Purchases'
: purpose === 'Purchases'
? 'Sales'
: 'Both',
});
}
if (forWhat[0] === 'not in' && forWhat[1] === 'Sales') {
defaults = Object.assign({ for: 'Purchases' });
}
router[method]({
query: {
const query = {
edit: 1,
schemaName,
name,
schemaName,
showFields,
hideFields,
defaults: stringifyCircular(defaults),
filters: JSON.stringify(listFilters),
},
});
};
router.push({ query });
}
export async function openSettings(tab: SettingsTab) {