mirror of
https://github.com/frappe/books.git
synced 2024-11-08 23:00:56 +00:00
incr: status badge and action buttons
This commit is contained in:
parent
7cfd0726d6
commit
01e197f439
@ -56,7 +56,7 @@
|
||||
<script lang="ts">
|
||||
import { Money } from 'pesa';
|
||||
import { getCreateRoute } from 'src/router';
|
||||
import { getStatus, routeTo } from 'src/utils/ui';
|
||||
import { routeTo } from 'src/utils/ui';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Button from '../Button.vue';
|
||||
import StatusBadge from '../StatusBadge.vue';
|
||||
@ -78,7 +78,17 @@ export default defineComponent({
|
||||
linked: { type: Object as PropType<Linked>, required: true },
|
||||
},
|
||||
methods: {
|
||||
getStatus,
|
||||
getStatus(entry: { cancelled?: boolean; submitted?: boolean }) {
|
||||
if (entry.cancelled) {
|
||||
return 'Cancelled';
|
||||
}
|
||||
|
||||
if (entry.submitted) {
|
||||
return 'Submitted';
|
||||
}
|
||||
|
||||
return 'Saved';
|
||||
},
|
||||
async openEntry(name: string) {
|
||||
const route = getCreateRoute(this.linked.schemaName, name);
|
||||
await routeTo(route);
|
||||
|
@ -1,11 +1,35 @@
|
||||
<template>
|
||||
<FormContainer>
|
||||
<template #header v-if="hasDoc">
|
||||
<StatusBadge :status="status" />
|
||||
<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="() => doc.sync()">
|
||||
{{ t`Save` }}
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="doc?.canSubmit"
|
||||
type="primary"
|
||||
@click="() => doc.submit()"
|
||||
>{{ t`Submit` }}</Button
|
||||
>
|
||||
</template>
|
||||
<template #body>
|
||||
<FormHeader
|
||||
:form-title="title"
|
||||
:form-sub-title="schema.label"
|
||||
class="sticky top-0 bg-white border-b"
|
||||
/>
|
||||
>
|
||||
</FormHeader>
|
||||
|
||||
<!-- Section Container -->
|
||||
<div v-if="hasDoc" class="overflow-auto custom-scroll">
|
||||
@ -61,20 +85,34 @@
|
||||
<script lang="ts">
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import { getDocStatus } from 'models/helpers';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { Field, Schema } from 'schemas/types';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
import FormContainer from 'src/components/FormContainer.vue';
|
||||
import FormHeader from 'src/components/FormHeader.vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import { ActionGroup, UIGroupedFields } from 'src/utils/types';
|
||||
import {
|
||||
getFieldsGroupedByTabAndSection,
|
||||
getGroupedActionsForDoc,
|
||||
} from 'src/utils/ui';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import CommonFormSection from './CommonFormSection.vue';
|
||||
|
||||
type UIGroupedFields = Map<string, Map<string, Field[]>>;
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
name: { type: String, default: 'PAY-1008' },
|
||||
schemaName: { type: String, default: ModelNameEnum.Payment },
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
schemaName: computed(() => this.docOrNull?.schemaName),
|
||||
name: computed(() => this.docOrNull?.name),
|
||||
doc: computed(() => this.docOrNull),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
docOrNull: null,
|
||||
@ -82,19 +120,24 @@ export default defineComponent({
|
||||
} as { docOrNull: null | Doc; activeTab: string };
|
||||
},
|
||||
async mounted() {
|
||||
if (this.name && !this.docOrNull) {
|
||||
this.docOrNull = await this.fyo.doc.getDoc(this.schemaName, this.name);
|
||||
}
|
||||
|
||||
if (this.fyo.store.isDevelopment) {
|
||||
// @ts-ignore
|
||||
window.cf = this;
|
||||
}
|
||||
|
||||
await this.setDoc();
|
||||
},
|
||||
computed: {
|
||||
hasDoc(): boolean {
|
||||
return !!this.docOrNull;
|
||||
},
|
||||
status(): string {
|
||||
if (!this.hasDoc) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return getDocStatus(this.doc);
|
||||
},
|
||||
doc(): Doc {
|
||||
const doc = this.docOrNull as Doc | null;
|
||||
if (!doc) {
|
||||
@ -105,9 +148,10 @@ export default defineComponent({
|
||||
return doc;
|
||||
},
|
||||
title(): string {
|
||||
if (this.schema.isSubmittable && !this.docOrNull?.notInserted) {
|
||||
if (this.schema.isSubmittable && this.docOrNull?.notInserted) {
|
||||
return this.t`New Entry`;
|
||||
}
|
||||
|
||||
return this.docOrNull?.name!;
|
||||
},
|
||||
schema(): Schema {
|
||||
@ -131,27 +175,34 @@ export default defineComponent({
|
||||
allGroups(): UIGroupedFields {
|
||||
return getFieldsGroupedByTabAndSection(this.schema);
|
||||
},
|
||||
groupedActions(): ActionGroup[] {
|
||||
if (!this.hasDoc) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return getGroupedActionsForDoc(this.doc);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async setDoc() {
|
||||
if (this.hasDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.name) {
|
||||
this.docOrNull = await this.fyo.doc.getDoc(this.schemaName, this.name);
|
||||
} else {
|
||||
this.docOrNull = this.fyo.doc.getNewDoc(this.schemaName);
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
FormContainer,
|
||||
FormHeader,
|
||||
CommonFormSection,
|
||||
StatusBadge,
|
||||
Button,
|
||||
DropdownWithActions,
|
||||
},
|
||||
components: { FormContainer, FormHeader, CommonFormSection },
|
||||
});
|
||||
|
||||
function getFieldsGroupedByTabAndSection(schema: Schema): UIGroupedFields {
|
||||
const grouped: UIGroupedFields = new Map();
|
||||
for (const field of schema?.fields ?? []) {
|
||||
const tab = field.tab ?? 'Default';
|
||||
const section = field.section ?? 'Default';
|
||||
if (!grouped.has(tab)) {
|
||||
grouped.set(tab, new Map());
|
||||
}
|
||||
|
||||
const tabbed = grouped.get(tab)!;
|
||||
if (!tabbed.has(section)) {
|
||||
tabbed.set(section, []);
|
||||
}
|
||||
|
||||
tabbed.get(section)!.push(field);
|
||||
}
|
||||
|
||||
return grouped;
|
||||
}
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="filteredFields.length > 0">
|
||||
<div
|
||||
v-if="showTitle && title"
|
||||
class="flex justify-between items-center cursor-pointer select-none"
|
||||
@ -48,7 +48,9 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
filteredFields(): Field[] {
|
||||
return (this.fields ?? []).filter((f) => !evaluateHidden(f, this.doc));
|
||||
return (this.fields ?? []).filter(
|
||||
(f) => !evaluateHidden(f, this.doc) && !f.meta
|
||||
);
|
||||
},
|
||||
},
|
||||
components: { FormControl },
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Doc } from "fyo/model/doc";
|
||||
import { FieldType } from "schemas/types";
|
||||
import { QueryFilter } from "utils/db/types";
|
||||
import type { Doc } from 'fyo/model/doc';
|
||||
import type { Action } from 'fyo/model/types';
|
||||
import type { Field, FieldType } from 'schemas/types';
|
||||
import type { QueryFilter } from 'utils/db/types';
|
||||
|
||||
export interface MessageDialogButton {
|
||||
label: string;
|
||||
@ -31,7 +32,7 @@ export interface QuickEditOptions {
|
||||
hideFields?: string[];
|
||||
showFields?: string[];
|
||||
defaults?: Record<string, unknown>;
|
||||
listFilters?: QueryFilter
|
||||
listFilters?: QueryFilter;
|
||||
}
|
||||
|
||||
export type SidebarConfig = SidebarRoot[];
|
||||
@ -44,7 +45,7 @@ export interface SidebarRoot {
|
||||
iconHeight?: string;
|
||||
hidden?: () => boolean;
|
||||
items?: SidebarItem[];
|
||||
filters?: QueryFilter
|
||||
filters?: QueryFilter;
|
||||
}
|
||||
|
||||
export interface SidebarItem {
|
||||
@ -55,7 +56,6 @@ export interface SidebarItem {
|
||||
hidden?: () => boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface ExportField {
|
||||
fieldname: string;
|
||||
fieldtype: FieldType;
|
||||
@ -70,5 +70,13 @@ export interface ExportTableField {
|
||||
fields: ExportField[];
|
||||
}
|
||||
|
||||
export type ActionGroup = {
|
||||
group: string;
|
||||
label: string;
|
||||
type: string;
|
||||
actions: Action[];
|
||||
};
|
||||
|
||||
export type UIGroupedFields = Map<string, Map<string, Field[]>>;
|
||||
export type ExportFormat = 'csv' | 'json';
|
||||
export type PeriodKey = 'This Year' | 'This Quarter' | 'This Month'
|
||||
export type PeriodKey = 'This Year' | 'This Quarter' | 'This Month';
|
||||
|
@ -9,6 +9,7 @@ import { Action } from 'fyo/model/types';
|
||||
import { getActions } from 'fyo/utils';
|
||||
import { getDbError, LinkValidationError, ValueError } from 'fyo/utils/errors';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { Schema } from 'schemas/types';
|
||||
import { handleErrorWithDialog } from 'src/errorHandling';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import router from 'src/router';
|
||||
@ -17,10 +18,12 @@ import { App, createApp, h, ref } from 'vue';
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
import { stringifyCircular } from './';
|
||||
import {
|
||||
ActionGroup,
|
||||
MessageDialogOptions,
|
||||
QuickEditOptions,
|
||||
SettingsTab,
|
||||
ToastOptions,
|
||||
UIGroupedFields,
|
||||
} from './types';
|
||||
|
||||
export async function openQuickEdit({
|
||||
@ -274,14 +277,7 @@ export function getActionsForDoc(doc?: Doc): Action[] {
|
||||
});
|
||||
}
|
||||
|
||||
export function getGroupedActionsForDoc(doc?: Doc) {
|
||||
type Group = {
|
||||
group: string;
|
||||
label: string;
|
||||
type: string;
|
||||
actions: Action[];
|
||||
};
|
||||
|
||||
export function getGroupedActionsForDoc(doc?: Doc): ActionGroup[] {
|
||||
const actions = getActionsForDoc(doc);
|
||||
const actionsMap = actions.reduce((acc, ac) => {
|
||||
if (!ac.group) {
|
||||
@ -297,7 +293,7 @@ export function getGroupedActionsForDoc(doc?: Doc) {
|
||||
|
||||
acc[ac.group].actions.push(ac);
|
||||
return acc;
|
||||
}, {} as Record<string, Group>);
|
||||
}, {} as Record<string, ActionGroup>);
|
||||
|
||||
const grouped = Object.keys(actionsMap)
|
||||
.filter(Boolean)
|
||||
@ -393,14 +389,24 @@ function getDuplicateAction(doc: Doc): Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function getStatus(entry: { cancelled?: boolean; submitted?: boolean }) {
|
||||
if (entry.cancelled) {
|
||||
return 'Cancelled';
|
||||
export function getFieldsGroupedByTabAndSection(
|
||||
schema: Schema
|
||||
): UIGroupedFields {
|
||||
const grouped: UIGroupedFields = new Map();
|
||||
for (const field of schema?.fields ?? []) {
|
||||
const tab = field.tab ?? 'Default';
|
||||
const section = field.section ?? 'Default';
|
||||
if (!grouped.has(tab)) {
|
||||
grouped.set(tab, new Map());
|
||||
}
|
||||
|
||||
if (entry.submitted) {
|
||||
return 'Submitted';
|
||||
const tabbed = grouped.get(tab)!;
|
||||
if (!tabbed.has(section)) {
|
||||
tabbed.set(section, []);
|
||||
}
|
||||
|
||||
return 'Saved';
|
||||
tabbed.get(section)!.push(field);
|
||||
}
|
||||
|
||||
return grouped;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user