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() { async openNewDoc() {
const schemaName = this.df.target; const schemaName = this.df.target;
const linkDoc = fyo.doc.getNewDoc(schemaName); const name = this.linkValue;
const filters = await this.getCreateFilters(); const filters = await this.getCreateFilters();
const { openQuickEdit } = await import('src/utils/ui'); const { openQuickEdit } = await import('src/utils/ui');
openQuickEdit({ const doc = fyo.doc.getNewDoc(schemaName, { name, ...filters });
schemaName, openQuickEdit({ doc });
name: linkDoc.name,
defaults: Object.assign({}, filters, {
name: this.linkValue,
}),
});
linkDoc.once('afterSync', () => { doc.once('afterSync', () => {
this.$router.back(); this.$router.back();
this.results = []; this.results = [];
this.triggerChange(linkDoc.name); this.triggerChange(doc.name);
}); });
}, },
async getCreateFilters() { async getCreateFilters() {

View File

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

View File

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

View File

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

View File

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

View File

@ -38,13 +38,10 @@ export type SettingsTab =
| ModelNameEnum.SystemSettings; | ModelNameEnum.SystemSettings;
export interface QuickEditOptions { export interface QuickEditOptions {
doc?: Doc; doc: Doc;
schemaName?: string;
name?: string;
hideFields?: string[]; hideFields?: string[];
showFields?: string[]; showFields?: string[];
defaults?: Record<string, unknown>; defaults?: Record<string, unknown>;
listFilters?: QueryFilter;
} }
export type SidebarConfig = SidebarRoot[]; 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({ export async function openQuickEdit({
doc, doc,
schemaName,
name,
hideFields = [], hideFields = [],
showFields = [], showFields = [],
defaults = {},
listFilters = {},
}: QuickEditOptions) { }: QuickEditOptions) {
if (doc) { const { schemaName, name } = doc;
schemaName = doc.schemaName; if (!name) {
name = doc.name; throw new ValueError(t`Quick edit error: ${schemaName} entry has no name.`);
} }
if (!doc && (!schemaName || !name)) { if (router.currentRoute.value.query.name === 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) {
return; return;
} }
const forWhat = (defaults?.for ?? []) as string[]; const query = {
if (forWhat[0] === 'not in') { edit: 1,
const purpose = forWhat[1]?.[0]; name,
schemaName,
defaults = Object.assign({ showFields,
for: hideFields,
purpose === 'Sales' };
? 'Purchases' router.push({ query });
: purpose === 'Purchases'
? 'Sales'
: 'Both',
});
}
if (forWhat[0] === 'not in' && forWhat[1] === 'Sales') {
defaults = Object.assign({ for: 'Purchases' });
}
router[method]({
query: {
edit: 1,
schemaName,
name,
showFields,
hideFields,
defaults: stringifyCircular(defaults),
filters: JSON.stringify(listFilters),
},
});
} }
export async function openSettings(tab: SettingsTab) { export async function openSettings(tab: SettingsTab) {