feat(ui): constrain add-account to a root-type dropdown + sub-path
Free-typing the full hierarchical name let admins fat-finger the parent (wrong/invalid root). Replace the single name field with a required Account Type select (the 5 valid roots, mirroring _VALID_ACCOUNT_PREFIXES) plus a sub-account input, a live 'Will create: ...' preview, and per-segment validation (each part must be a capitalized Beancount account component). The root prefix is now structurally guaranteed valid. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
caef3cf5e8
commit
051c9f0c22
2 changed files with 44 additions and 11 deletions
|
|
@ -71,7 +71,8 @@ window.app = Vue.createApp({
|
|||
},
|
||||
addAccountDialog: {
|
||||
show: false,
|
||||
name: '',
|
||||
rootType: 'Expenses',
|
||||
subPath: '',
|
||||
description: '',
|
||||
loading: false
|
||||
},
|
||||
|
|
@ -292,6 +293,16 @@ window.app = Vue.createApp({
|
|||
})
|
||||
return options
|
||||
},
|
||||
accountRootTypes() {
|
||||
// The five Beancount root account types — the only valid parents.
|
||||
// Mirrors the server's _VALID_ACCOUNT_PREFIXES.
|
||||
return ['Assets', 'Liabilities', 'Equity', 'Income', 'Expenses']
|
||||
},
|
||||
addAccountFullName() {
|
||||
const sub = (this.addAccountDialog.subPath || '').trim().replace(/^:+|:+$/g, '')
|
||||
if (!this.addAccountDialog.rootType || !sub) return ''
|
||||
return `${this.addAccountDialog.rootType}:${sub}`
|
||||
},
|
||||
userOptions() {
|
||||
const options = []
|
||||
this.users.forEach(user => {
|
||||
|
|
@ -573,17 +584,26 @@ window.app = Vue.createApp({
|
|||
}
|
||||
},
|
||||
showAddAccountDialog() {
|
||||
this.addAccountDialog.name = ''
|
||||
this.addAccountDialog.rootType = 'Expenses'
|
||||
this.addAccountDialog.subPath = ''
|
||||
this.addAccountDialog.description = ''
|
||||
this.addAccountDialog.show = true
|
||||
},
|
||||
async submitAddAccount() {
|
||||
const name = (this.addAccountDialog.name || '').trim()
|
||||
const validPrefixes = ['Assets:', 'Liabilities:', 'Equity:', 'Income:', 'Expenses:']
|
||||
if (!validPrefixes.some(p => name.startsWith(p))) {
|
||||
const name = this.addAccountFullName
|
||||
if (!name) {
|
||||
this.$q.notify({type: 'warning', message: 'Enter a sub-account name'})
|
||||
return
|
||||
}
|
||||
// Each segment under the root must be a valid Beancount account
|
||||
// component: start with an uppercase letter, then letters/digits/hyphens.
|
||||
const badSegment = name.split(':').slice(1).find(
|
||||
seg => !/^[A-Z][A-Za-z0-9-]*$/.test(seg)
|
||||
)
|
||||
if (badSegment !== undefined) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: `Account name must start with one of: ${validPrefixes.join(', ')}`
|
||||
message: `Invalid segment "${badSegment}" — each part must start with a capital letter (letters, digits, hyphens only)`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue