From 2e0eb654e9c8aa69fc0937c7374e8f213581a9fb Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Wed, 22 Feb 2023 14:15:59 +0530 Subject: [PATCH 01/33] incr: scaffold template builder --- models/baseModels/PrintTemplate.ts | 8 ++++++++ models/index.ts | 2 ++ schemas/app/PrintTemplate.json | 32 ++++++++++++++++++++++++++++++ schemas/schemas.ts | 10 ++++++---- src/pages/TemplateBuilder.vue | 11 ++++++++++ src/router.ts | 6 ++++++ src/utils/sidebarConfig.ts | 5 +++++ 7 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 models/baseModels/PrintTemplate.ts create mode 100644 schemas/app/PrintTemplate.json create mode 100644 src/pages/TemplateBuilder.vue diff --git a/models/baseModels/PrintTemplate.ts b/models/baseModels/PrintTemplate.ts new file mode 100644 index 000000000..54bbec34b --- /dev/null +++ b/models/baseModels/PrintTemplate.ts @@ -0,0 +1,8 @@ +import { Doc } from 'fyo/model/doc'; + +export class PrintTemplate extends Doc { + name?: string; + type?: string; + template?: string; + isCustom?: boolean; +} diff --git a/models/index.ts b/models/index.ts index 75899c29e..14a7ea4d0 100644 --- a/models/index.ts +++ b/models/index.ts @@ -29,6 +29,7 @@ import { StockLedgerEntry } from './inventory/StockLedgerEntry'; import { StockMovement } from './inventory/StockMovement'; import { StockMovementItem } from './inventory/StockMovementItem'; +import { PrintTemplate } from './baseModels/PrintTemplate'; export const models = { Account, AccountingLedgerEntry, @@ -48,6 +49,7 @@ export const models = { SalesInvoice, SalesInvoiceItem, SetupWizard, + PrintTemplate, Tax, TaxSummary, // Inventory Models diff --git a/schemas/app/PrintTemplate.json b/schemas/app/PrintTemplate.json new file mode 100644 index 000000000..68ad64f99 --- /dev/null +++ b/schemas/app/PrintTemplate.json @@ -0,0 +1,32 @@ +{ + "name": "PrintTemplate", + "label": "Print Template", + "naming": "manual", + "isSingle": false, + "fields": [ + { + "fieldname": "name", + "label": "Template Name", + "fieldtype": "Data", + "required": true + }, + { + "fieldname": "type", + "label": "Template Type", + "fieldtype": "Select", + "required": true + }, + { + "fieldname": "template", + "label": "Template", + "fieldtype": "Data", + "required": true + }, + { + "fieldname": "isCustom", + "label": "Is Custom", + "fieldtype": "Check", + "default": false + } + ] +} diff --git a/schemas/schemas.ts b/schemas/schemas.ts index 8d4fdf0f5..e560ab851 100644 --- a/schemas/schemas.ts +++ b/schemas/schemas.ts @@ -2,11 +2,13 @@ import Account from './app/Account.json'; import AccountingLedgerEntry from './app/AccountingLedgerEntry.json'; import AccountingSettings from './app/AccountingSettings.json'; import Address from './app/Address.json'; +import Batch from './app/Batch.json'; import Color from './app/Color.json'; import CompanySettings from './app/CompanySettings.json'; import Currency from './app/Currency.json'; import Defaults from './app/Defaults.json'; import GetStarted from './app/GetStarted.json'; +import InventorySettings from './app/inventory/InventorySettings.json'; import Location from './app/inventory/Location.json'; import PurchaseReceipt from './app/inventory/PurchaseReceipt.json'; import PurchaseReceiptItem from './app/inventory/PurchaseReceiptItem.json'; @@ -17,6 +19,7 @@ import StockMovement from './app/inventory/StockMovement.json'; import StockMovementItem from './app/inventory/StockMovementItem.json'; import StockTransfer from './app/inventory/StockTransfer.json'; import StockTransferItem from './app/inventory/StockTransferItem.json'; +import UOMConversionItem from './app/inventory/UOMConversionItem.json'; import Invoice from './app/Invoice.json'; import InvoiceItem from './app/InvoiceItem.json'; import Item from './app/Item.json'; @@ -28,6 +31,7 @@ import Party from './app/Party.json'; import Payment from './app/Payment.json'; import PaymentFor from './app/PaymentFor.json'; import PrintSettings from './app/PrintSettings.json'; +import PrintTemplate from './app/PrintTemplate.json'; import PurchaseInvoice from './app/PurchaseInvoice.json'; import PurchaseInvoiceItem from './app/PurchaseInvoiceItem.json'; import SalesInvoice from './app/SalesInvoice.json'; @@ -37,7 +41,6 @@ import Tax from './app/Tax.json'; import TaxDetail from './app/TaxDetail.json'; import TaxSummary from './app/TaxSummary.json'; import UOM from './app/UOM.json'; -import UOMConversionItem from './app/inventory/UOMConversionItem.json'; import PatchRun from './core/PatchRun.json'; import SingleValue from './core/SingleValue.json'; import SystemSettings from './core/SystemSettings.json'; @@ -46,8 +49,6 @@ import child from './meta/child.json'; import submittable from './meta/submittable.json'; import tree from './meta/tree.json'; import { Schema, SchemaStub } from './types'; -import InventorySettings from './app/inventory/InventorySettings.json'; -import Batch from './app/Batch.json' export const coreSchemas: Schema[] = [ PatchRun as Schema, @@ -66,6 +67,7 @@ export const appSchemas: Schema[] | SchemaStub[] = [ Misc as Schema, SetupWizard as Schema, GetStarted as Schema, + PrintTemplate as Schema, Color as Schema, Currency as Schema, @@ -116,5 +118,5 @@ export const appSchemas: Schema[] | SchemaStub[] = [ PurchaseReceipt as Schema, PurchaseReceiptItem as Schema, - Batch as Schema + Batch as Schema, ]; diff --git a/src/pages/TemplateBuilder.vue b/src/pages/TemplateBuilder.vue new file mode 100644 index 000000000..31aa504d6 --- /dev/null +++ b/src/pages/TemplateBuilder.vue @@ -0,0 +1,11 @@ + + diff --git a/src/router.ts b/src/router.ts index f1baf30f9..6f0b2694e 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,5 @@ import { ModelNameEnum } from 'models/types'; +import TemplateBuilder from 'src/pages/TemplateBuilder.vue'; import ChartOfAccounts from 'src/pages/ChartOfAccounts.vue'; import CommonForm from 'src/pages/CommonForm/CommonForm.vue'; import Dashboard from 'src/pages/Dashboard/Dashboard.vue'; @@ -127,6 +128,11 @@ const routes: RouteRecordRaw[] = [ name: 'Import Wizard', component: ImportWizard, }, + { + path: '/template-builder', + name: 'Template Builder', + component: TemplateBuilder, + }, { path: '/settings', name: 'Settings', diff --git a/src/utils/sidebarConfig.ts b/src/utils/sidebarConfig.ts index a60e603cf..7ce17925b 100644 --- a/src/utils/sidebarConfig.ts +++ b/src/utils/sidebarConfig.ts @@ -272,6 +272,11 @@ async function getCompleteSidebar(): Promise { name: 'import-wizard', route: '/import-wizard', }, + { + label: t`Template Builder`, + name: 'template-builder', + route: '/template-builder', + }, { label: t`Settings`, name: 'settings', From 28aa0f135e8d2096e9f0c979ba3a47ff63383811 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Wed, 22 Feb 2023 15:51:20 +0530 Subject: [PATCH 02/33] incr: update routing - update PrintTemplate UI code --- models/baseModels/PrintTemplate.ts | 46 +++++++ models/types.ts | 1 + schemas/app/PrintTemplate.json | 8 +- src/pages/CommonForm/CommonForm.vue | 17 +-- src/pages/TemplateBuilder.vue | 189 +++++++++++++++++++++++++++- src/router.ts | 3 +- src/utils/sidebarConfig.ts | 6 +- src/utils/ui.ts | 15 +++ 8 files changed, 263 insertions(+), 22 deletions(-) diff --git a/models/baseModels/PrintTemplate.ts b/models/baseModels/PrintTemplate.ts index 54bbec34b..28e87a64e 100644 --- a/models/baseModels/PrintTemplate.ts +++ b/models/baseModels/PrintTemplate.ts @@ -1,8 +1,54 @@ import { Doc } from 'fyo/model/doc'; +import { SchemaMap } from 'schemas/types'; +import { ListsMap, ListViewSettings, ReadOnlyMap } from 'fyo/model/types'; +import { ModelNameEnum } from 'models/types'; export class PrintTemplate extends Doc { name?: string; type?: string; template?: string; isCustom?: boolean; + + static getListViewSettings(): ListViewSettings { + return { + formRoute: ({ name }) => `/template-builder/${name}`, + columns: ['name', 'type', 'isCustom'], + }; + } + + readOnly: ReadOnlyMap = { + name: () => !!this.isCustom, + type: () => !!this.isCustom, + template: () => !!this.isCustom, + }; + + static lists: ListsMap = { + type(doc?: Doc) { + let enableInventory: boolean = false; + let schemaMap: SchemaMap = {}; + if (doc) { + enableInventory = !!doc.fyo.singles.AccountingSettings?.enableInventory; + schemaMap = doc.fyo.schemaMap; + } + + const models = [ + ModelNameEnum.SalesInvoice, + ModelNameEnum.PurchaseInvoice, + ModelNameEnum.Payment, + ]; + + if (enableInventory) { + models.push( + ModelNameEnum.Shipment, + ModelNameEnum.PurchaseReceipt, + ModelNameEnum.StockMovement + ); + } + + return models.map((value) => ({ + value, + label: schemaMap[value]?.label ?? value, + })); + }, + }; } diff --git a/models/types.ts b/models/types.ts index d6c4d3c2d..800348fc0 100644 --- a/models/types.ts +++ b/models/types.ts @@ -21,6 +21,7 @@ export enum ModelNameEnum { Payment = 'Payment', PaymentFor = 'PaymentFor', PrintSettings = 'PrintSettings', + PrintTemplate = 'PrintTemplate', PurchaseInvoice = 'PurchaseInvoice', PurchaseInvoiceItem = 'PurchaseInvoiceItem', SalesInvoice = 'SalesInvoice', diff --git a/schemas/app/PrintTemplate.json b/schemas/app/PrintTemplate.json index 68ad64f99..e933d4b08 100644 --- a/schemas/app/PrintTemplate.json +++ b/schemas/app/PrintTemplate.json @@ -13,20 +13,22 @@ { "fieldname": "type", "label": "Template Type", - "fieldtype": "Select", + "fieldtype": "AutoComplete", + "default": "SalesInvoice", "required": true }, { "fieldname": "template", "label": "Template", - "fieldtype": "Data", + "fieldtype": "Text", "required": true }, { "fieldname": "isCustom", "label": "Is Custom", "fieldtype": "Check", - "default": false + "default": false, + "readOnly": true } ] } diff --git a/src/pages/CommonForm/CommonForm.vue b/src/pages/CommonForm/CommonForm.vue index 124ea1302..fdef4edd6 100644 --- a/src/pages/CommonForm/CommonForm.vue +++ b/src/pages/CommonForm/CommonForm.vue @@ -116,6 +116,7 @@ import { docsPathMap } from 'src/utils/misc'; import { docsPathRef, focusedDocsRef } from 'src/utils/refs'; import { ActionGroup, UIGroupedFields } from 'src/utils/types'; import { + getDocFromNameIfExistsElseNew, getFieldsGroupedByTabAndSection, getGroupedActionsForDoc, } from 'src/utils/ui'; @@ -277,18 +278,10 @@ export default defineComponent({ return; } - if (this.name) { - await this.setDocFromName(this.name); - } else { - this.docOrNull = this.fyo.doc.getNewDoc(this.schemaName); - } - }, - async setDocFromName(name: string) { - try { - this.docOrNull = await this.fyo.doc.getDoc(this.schemaName, name); - } catch (err) { - this.docOrNull = this.fyo.doc.getNewDoc(this.schemaName); - } + this.docOrNull = await getDocFromNameIfExistsElseNew( + this.schemaName, + this.name + ); }, async toggleQuickEditDoc(doc: Doc | null) { if (this.quickEditDoc && doc) { diff --git a/src/pages/TemplateBuilder.vue b/src/pages/TemplateBuilder.vue index 31aa504d6..ec64b3c31 100644 --- a/src/pages/TemplateBuilder.vue +++ b/src/pages/TemplateBuilder.vue @@ -1,11 +1,194 @@ - From 11c714a9571f83e19678ee9d0d4d4d2471976ea6 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 9 Mar 2023 11:47:25 +0530 Subject: [PATCH 20/33] feat: add autocomplete for linklabels - update editor styling --- package.json | 1 + src/pages/TemplateBuilder/TemplateBuilder.vue | 17 ++- .../TemplateBuilder/TemplateBuilderHint.vue | 6 +- src/pages/TemplateBuilder/TemplateEditor.vue | 143 +++++++++++++++++- yarn.lock | 2 +- 5 files changed, 150 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 420698589..0c05a90ef 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test": "scripts/test.sh" }, "dependencies": { + "@codemirror/autocomplete": "^6.4.2", "@codemirror/lang-vue": "^0.1.1", "@popperjs/core": "^2.10.2", "better-sqlite3": "^7.5.3", diff --git a/src/pages/TemplateBuilder/TemplateBuilder.vue b/src/pages/TemplateBuilder/TemplateBuilder.vue index 62e4e9e2c..407f3663d 100644 --- a/src/pages/TemplateBuilder/TemplateBuilder.vue +++ b/src/pages/TemplateBuilder/TemplateBuilder.vue @@ -178,7 +178,7 @@ >
-
+

{{ t`Value Keys` }}

@@ -186,7 +186,7 @@ class="overflow-auto custom-scroll p-4" style="max-height: 80vh; width: 25vw" > - +
@@ -196,11 +196,12 @@ {{ t`Template` }}
@@ -253,14 +254,14 @@ export default defineComponent({ return { doc: null, showEditor: false, - hint: null, + hints: undefined, values: null, templateCollapsed: false, helpersCollapsed: true, displayDoc: null, scale: 0.65, } as { - hint: null | Record; + hints?: Record; values: null | PrintValues; doc: PrintTemplate | null; showEditor: boolean; @@ -344,7 +345,7 @@ export default defineComponent({ }, async setDisplayDoc(value: string) { if (!value) { - this.hint = null; + delete this.hints; this.values = null; this.displayDoc = null; return; @@ -356,7 +357,7 @@ export default defineComponent({ } const displayDoc = await getDocFromNameIfExistsElseNew(schemaName, value); - this.hint = getPrintTemplatePropHints(displayDoc); + this.hints = getPrintTemplatePropHints(displayDoc); this.values = await getPrintTemplatePropValues(displayDoc); this.displayDoc = displayDoc; }, diff --git a/src/pages/TemplateBuilder/TemplateBuilderHint.vue b/src/pages/TemplateBuilder/TemplateBuilderHint.vue index 4f2cf7bd3..a2dab9c5d 100644 --- a/src/pages/TemplateBuilder/TemplateBuilderHint.vue +++ b/src/pages/TemplateBuilder/TemplateBuilderHint.vue @@ -55,7 +55,7 @@
@@ -74,7 +74,7 @@ export default defineComponent({ name: 'TemplateBuilderHint', props: { prefix: { type: String, default: '' }, - hint: { type: Object, required: true }, + hints: { type: Object, required: true }, level: { type: Number, default: 0 }, }, data() { @@ -83,7 +83,7 @@ export default defineComponent({ }; }, mounted() { - this.rows = Object.entries(this.hint) + this.rows = Object.entries(this.hints) .map(([key, value]) => ({ key, value, diff --git a/src/pages/TemplateBuilder/TemplateEditor.vue b/src/pages/TemplateBuilder/TemplateEditor.vue index 2b84ca77a..fd989e2a6 100644 --- a/src/pages/TemplateBuilder/TemplateEditor.vue +++ b/src/pages/TemplateBuilder/TemplateEditor.vue @@ -2,8 +2,13 @@
diff --git a/yarn.lock b/yarn.lock index d9b6cec53..a24f11433 100644 --- a/yarn.lock +++ b/yarn.lock @@ -962,7 +962,7 @@ "@babel/helper-validator-identifier" "^7.15.7" to-fast-properties "^2.0.0" -"@codemirror/autocomplete@^6.0.0": +"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.4.2": version "6.4.2" resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.4.2.tgz#938b25223bd21f97b2a6d85474643355f98b505b" integrity sha512-8WE2xp+D0MpWEv5lZ6zPW1/tf4AGb358T5GWYiKEuCP8MvFfT3tH2mIF9Y2yr2e3KbHuSvsVhosiEyqCpiJhZQ== From 6ec819f4bad33e7908368790fd5a9cddc6495f23 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 9 Mar 2023 14:26:21 +0530 Subject: [PATCH 21/33] feat: add HorizontalResizer --- src/components/HorizontalResizer.vue | 138 +++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/components/HorizontalResizer.vue diff --git a/src/components/HorizontalResizer.vue b/src/components/HorizontalResizer.vue new file mode 100644 index 000000000..b46a9aac9 --- /dev/null +++ b/src/components/HorizontalResizer.vue @@ -0,0 +1,138 @@ + + From fcec404e63938c50b201bc86a7008addeed70269 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 9 Mar 2023 15:49:02 +0530 Subject: [PATCH 22/33] fix(ui): start restyling editor - add left pane for template - add baseTemplate when starting with a new template --- src/components/Controls/Base.vue | 2 + src/pages/TemplateBuilder/TemplateBuilder.vue | 271 +++++++----------- src/utils/printTemplates.ts | 14 + 3 files changed, 118 insertions(+), 169 deletions(-) diff --git a/src/components/Controls/Base.vue b/src/components/Controls/Base.vue index df3553d1d..0fc29198c 100644 --- a/src/components/Controls/Base.vue +++ b/src/components/Controls/Base.vue @@ -16,6 +16,7 @@ :step="step" :max="df.maxvalue" :min="df.minvalue" + :style="inputStyles" @blur="(e) => !isReadOnly && triggerChange(e.target.value)" @focus="(e) => !isReadOnly && $emit('focus', e)" @input="(e) => !isReadOnly && $emit('input', e)" @@ -41,6 +42,7 @@ export default { size: String, showLabel: Boolean, autofocus: Boolean, + inputStyles: { type: Object, default: () => ({}) }, textRight: { type: [null, Boolean], default: null }, readOnly: { type: [null, Boolean], default: null }, required: { type: [null, Boolean], default: null }, diff --git a/src/pages/TemplateBuilder/TemplateBuilder.vue b/src/pages/TemplateBuilder/TemplateBuilder.vue index 407f3663d..11a86e882 100644 --- a/src/pages/TemplateBuilder/TemplateBuilder.vue +++ b/src/pages/TemplateBuilder/TemplateBuilder.vue @@ -4,19 +4,25 @@ + +
@@ -35,177 +41,74 @@ />
- + + + +
- -
- + + + +
+
- -
+ +

- {{ t`Controls` }} + {{ t`Value Keys` }}

- -
- - - - - -
-
- - -
-

- {{ t`Template` }} -

- -
- - -
-