mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
fix(ux): replace status badge with status pill
This commit is contained in:
parent
21ca586604
commit
32ca9e1c32
@ -11,8 +11,9 @@
|
||||
flex-shrink-0
|
||||
"
|
||||
>
|
||||
<h1>{{ formTitle }}</h1>
|
||||
<p class="text-gray-600">
|
||||
<h1 v-if="formTitle">{{ formTitle }}</h1>
|
||||
<slot />
|
||||
<p v-if="formSubTitle" class="text-gray-600">
|
||||
{{ formSubTitle }}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<Badge
|
||||
class="flex-center"
|
||||
:color="color"
|
||||
v-if="status"
|
||||
:class="defaultSize ? 'text-sm px-3' : ''"
|
||||
>{{ statusLabel }}</Badge
|
||||
>
|
||||
</template>
|
||||
<script>
|
||||
import { getStatusText, statusColor } from 'models/helpers';
|
||||
import Badge from './Badge.vue';
|
||||
|
||||
export default {
|
||||
name: 'StatusBadge',
|
||||
props: { status: String, defaultSize: { type: Boolean, default: true } },
|
||||
computed: {
|
||||
color() {
|
||||
return statusColor[this.status];
|
||||
},
|
||||
statusLabel() {
|
||||
return getStatusText(this.status) || this.status;
|
||||
},
|
||||
},
|
||||
components: { Badge },
|
||||
};
|
||||
</script>
|
109
src/components/StatusPill.vue
Normal file
109
src/components/StatusPill.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<p class="pill font-medium" :class="styleClass">{{ text }}</p>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { isPesa } from 'fyo/utils';
|
||||
import { Invoice } from 'models/baseModels/Invoice/Invoice';
|
||||
import { Party } from 'models/baseModels/Party/Party';
|
||||
import { getBgTextColorClass } from 'src/utils/colors';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
type Status = ReturnType<typeof getStatus>;
|
||||
type UIColors = 'gray' | 'orange' | 'red' | 'green' | 'blue';
|
||||
|
||||
export default defineComponent({
|
||||
props: { doc: { type: Doc, required: true } },
|
||||
computed: {
|
||||
styleClass(): string {
|
||||
return getBgTextColorClass(this.color);
|
||||
},
|
||||
status(): Status {
|
||||
return getStatus(this.doc);
|
||||
},
|
||||
text() {
|
||||
const hasOutstanding = isPesa(this.doc.outstandingAmount);
|
||||
if (hasOutstanding && this.status === 'Outstanding') {
|
||||
const amt = this.fyo.format(this.doc.outstandingAmount, 'Currency');
|
||||
return this.t`Unpaid ${amt}`;
|
||||
}
|
||||
|
||||
if (this.doc instanceof Invoice && this.status === 'NotTransferred') {
|
||||
const amt = this.fyo.format(this.doc.stockNotTransferred, 'Float');
|
||||
return this.t`Pending Qty. ${amt}`;
|
||||
}
|
||||
|
||||
return {
|
||||
Draft: this.t`Draft`,
|
||||
Cancelled: this.t`Cancelled`,
|
||||
Outstanding: this.t`Outstanding`,
|
||||
NotTransferred: this.t`Not Transferred`,
|
||||
NotSaved: this.t`Not Saved`,
|
||||
Paid: this.t`Paid`,
|
||||
Saved: this.t`Saved`,
|
||||
Submitted: this.t`Submitted`,
|
||||
}[this.status];
|
||||
},
|
||||
color(): UIColors {
|
||||
return statusColorMap[this.status];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const statusColorMap: Record<Status, UIColors> = {
|
||||
Draft: 'gray',
|
||||
Cancelled: 'red',
|
||||
Outstanding: 'orange',
|
||||
NotTransferred: 'orange',
|
||||
NotSaved: 'orange',
|
||||
Paid: 'green',
|
||||
Saved: 'blue',
|
||||
Submitted: 'blue',
|
||||
};
|
||||
|
||||
function getStatus(doc: Doc) {
|
||||
if (doc.notInserted) {
|
||||
return 'Draft';
|
||||
}
|
||||
|
||||
if (doc.dirty) {
|
||||
return 'NotSaved';
|
||||
}
|
||||
|
||||
if (doc instanceof Party && doc.outstandingAmount?.isZero() !== true) {
|
||||
return 'Outstanding';
|
||||
}
|
||||
|
||||
if (doc.schema.isSubmittable) {
|
||||
return getSubmittableStatus(doc);
|
||||
}
|
||||
|
||||
return 'Saved';
|
||||
}
|
||||
|
||||
function getSubmittableStatus(doc: Doc) {
|
||||
if (doc.isCancelled) {
|
||||
return 'Cancelled';
|
||||
}
|
||||
|
||||
const isInvoice = doc instanceof Invoice;
|
||||
if (isInvoice && doc.outstandingAmount?.isZero() !== true) {
|
||||
return 'Outstanding';
|
||||
}
|
||||
|
||||
if (isInvoice && (doc.stockNotTransferred ?? 0) > 0) {
|
||||
return 'NotTransferred';
|
||||
}
|
||||
|
||||
if (isInvoice && doc.outstandingAmount?.isZero() === true) {
|
||||
return 'Paid';
|
||||
}
|
||||
|
||||
if (doc.isSubmitted) {
|
||||
return 'Submitted';
|
||||
}
|
||||
|
||||
// no-op
|
||||
return 'NotSaved';
|
||||
}
|
||||
</script>
|
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<FormContainer>
|
||||
<template #header-left v-if="hasDoc">
|
||||
<StatusBadge :status="status" class="h-8" />
|
||||
<Barcode
|
||||
class="h-8"
|
||||
v-if="canShowBarcode"
|
||||
@ -11,7 +10,7 @@
|
||||
}"
|
||||
/>
|
||||
<ExchangeRate
|
||||
v-if="hasDoc && doc.isMultiCurrency"
|
||||
v-if="canShowExchangeRate"
|
||||
:disabled="doc?.isSubmitted || doc?.isCancelled"
|
||||
:from-currency="fromCurrency"
|
||||
:to-currency="toCurrency"
|
||||
@ -21,6 +20,12 @@
|
||||
await doc.set('exchangeRate', exchangeRate)
|
||||
"
|
||||
/>
|
||||
<p
|
||||
v-if="schema.label && !(canShowBarcode || canShowExchangeRate)"
|
||||
class="text-xl font-semibold items-center text-gray-600"
|
||||
>
|
||||
{{ schema.label }}
|
||||
</p>
|
||||
</template>
|
||||
<template #header v-if="hasDoc">
|
||||
<Button
|
||||
@ -59,11 +64,8 @@
|
||||
}}</Button>
|
||||
</template>
|
||||
<template #body>
|
||||
<FormHeader
|
||||
:form-title="title"
|
||||
:form-sub-title="schema.label"
|
||||
class="sticky top-0 bg-white border-b"
|
||||
>
|
||||
<FormHeader :form-title="title" class="sticky top-0 bg-white border-b">
|
||||
<StatusPill v-if="hasDoc" :doc="doc" />
|
||||
</FormHeader>
|
||||
|
||||
<!-- Section Container -->
|
||||
@ -155,7 +157,7 @@ import ExchangeRate from 'src/components/Controls/ExchangeRate.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
import FormContainer from 'src/components/FormContainer.vue';
|
||||
import FormHeader from 'src/components/FormHeader.vue';
|
||||
import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import StatusPill from 'src/components/StatusPill.vue';
|
||||
import { getErrorMessage } from 'src/utils';
|
||||
import { shortcutsKey } from 'src/utils/injectionKeys';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
@ -270,6 +272,9 @@ export default defineComponent({
|
||||
// @ts-ignore
|
||||
return typeof this.doc?.addItem === 'function';
|
||||
},
|
||||
canShowExchangeRate(): boolean {
|
||||
return this.hasDoc && !!this.doc.isMultiCurrency;
|
||||
},
|
||||
exchangeRate(): number {
|
||||
if (!this.hasDoc || typeof this.doc.exchangeRate !== 'number') {
|
||||
return 1;
|
||||
@ -433,13 +438,13 @@ export default defineComponent({
|
||||
FormContainer,
|
||||
FormHeader,
|
||||
CommonFormSection,
|
||||
StatusBadge,
|
||||
Button,
|
||||
DropdownWithActions,
|
||||
Barcode,
|
||||
ExchangeRate,
|
||||
LinkedEntries,
|
||||
RowEditForm,
|
||||
StatusPill,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -312,11 +312,6 @@ const linkEntryDisplayFields: Record<string, string[]> = {
|
||||
@apply border-0;
|
||||
}
|
||||
|
||||
.pill {
|
||||
@apply py-0.5 px-1.5 rounded-md text-xs;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.pill-container:empty {
|
||||
display: none;
|
||||
}
|
||||
|
@ -200,3 +200,9 @@ input[type='number']::-webkit-inner-spin-button {
|
||||
[dir='rtl'] .custom-scroll::-webkit-scrollbar-track:vertical {
|
||||
border-right: solid 1px theme('colors.gray.200');
|
||||
}
|
||||
|
||||
.pill {
|
||||
@apply py-0.5 px-1.5 rounded-md text-xs;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import { fyo } from 'src/initFyo';
|
||||
import router from 'src/router';
|
||||
import { SelectFileOptions } from 'utils/types';
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
import { stringifyCircular } from './';
|
||||
import { evaluateHidden } from './doc';
|
||||
import { showDialog, showToast } from './interactive';
|
||||
import { selectFile } from './ipcCalls';
|
||||
|
Loading…
Reference in New Issue
Block a user