You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
6.4 KiB
199 lines
6.4 KiB
import { Component } from '../../lib/vues6/vues6.js'
|
|
import { utility } from '../service/Utility.service.js'
|
|
import { location_service } from '../service/Location.service.js'
|
|
import { resource_service } from '../service/Resource.service.js'
|
|
import { action_service } from '../service/Action.service.js'
|
|
|
|
const template = `
|
|
<div class="card col-12 col-sm-10 offset-sm-1 col-md-8 offset-md-2 col-xl-6 offset-xl-3">
|
|
<h3 class="card-title mb-4 mt-4">
|
|
<button class="btn-lg btn" @click="back"><i class="fa fa-arrow-left"></i></button> {{ title }}
|
|
</h3>
|
|
<form v-on:submit.prevent="do_nothing" v-if="is_ready">
|
|
<div class="form-group" v-for="field of definition.fields">
|
|
<span v-if="field.type.startsWith('select')">
|
|
<label :for="uuid+field.field">{{ field.name }}</label>
|
|
<select
|
|
:id="uuid+field.field"
|
|
class="form-control"
|
|
v-model="data[field.field]"
|
|
:required="field.required"
|
|
:readonly="mode === 'view'"
|
|
:multiple="!!field.type.endsWith('.multiple')"
|
|
>
|
|
<option v-for="option of field.options" :value="option.value">{{ typeof option.display === 'function' ? option.display(option) : option.display }}</option>
|
|
</select>
|
|
<small class="form-text" style="color: darkred;" v-if="field.error">{{ field.error }}</small>
|
|
</span>
|
|
<span v-if="field.type === 'text'">
|
|
<label :for="uuid+field.field">{{ field.name }}</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
:id="uuid+field.field"
|
|
v-model="data[field.field]"
|
|
:required="field.required"
|
|
:placeholder="field.placeholder"
|
|
:readonly="mode === 'view'"
|
|
ref="input"
|
|
>
|
|
<small class="form-text" style="color: darkred;" v-if="field.error">{{ field.error }}</small>
|
|
</span>
|
|
</div>
|
|
</form>
|
|
<div class="col-12 text-right mb-4 mr-0 mt-2">
|
|
<span style="color: darkred;" class="font-italic mr-3" v-if="error_message">{{ error_message }}</span>
|
|
<span class="font-italic mr-3 text-muted" v-if="other_message">{{ other_message }}</span>
|
|
<button
|
|
class="btn btn-primary"
|
|
type="button"
|
|
v-if="mode !== 'view'"
|
|
@click="save_click"
|
|
>Save</button>
|
|
</div>
|
|
</div>
|
|
`
|
|
|
|
const title_map = {
|
|
insert: 'Create',
|
|
update: 'Edit',
|
|
view: 'View',
|
|
}
|
|
|
|
export default class FormComponent extends Component {
|
|
static get selector() {
|
|
return 'cobalt-form'
|
|
}
|
|
|
|
static get template() {
|
|
return template
|
|
}
|
|
|
|
static get props() {
|
|
return ['resource', 'form_id', 'initial_mode']
|
|
}
|
|
|
|
definition = {}
|
|
data = {}
|
|
uuid = ''
|
|
title = ''
|
|
error_message = ''
|
|
other_message = ''
|
|
|
|
is_ready = false
|
|
mode = ''
|
|
id = ''
|
|
|
|
reset() {
|
|
this.definition = {}
|
|
this.data = {}
|
|
this.uuid = ''
|
|
this.title = ''
|
|
this.error_message = ''
|
|
this.other_message = ''
|
|
this.is_ready = false
|
|
}
|
|
|
|
async vue_on_create(internal = false) {
|
|
if ( !internal ) {
|
|
this.mode = this.initial_mode
|
|
this.id = this.form_id
|
|
this.resource_class = await resource_service.get(this.resource)
|
|
} else {
|
|
this.reset()
|
|
}
|
|
|
|
this.uuid = utility.uuid()
|
|
await this.load()
|
|
await this.init()
|
|
}
|
|
|
|
async init() {
|
|
for ( const field of this.definition.fields ) {
|
|
if ( field.type.startsWith('select.dynamic') ) {
|
|
field._options = field._options || field.options
|
|
const rsc = await resource_service.get(field._options.resource)
|
|
|
|
field.options = (await rsc.list()).map(item => {
|
|
return {
|
|
display: typeof field._options.display === 'function' ? field._options.display(item) : item[field._options.display || 'display'],
|
|
value: typeof field._options.value === 'function' ? field._options.value(item) : item[field._options.value || 'display'],
|
|
}
|
|
})
|
|
}
|
|
|
|
if ( field.type.endsWith('.multiple') ) {
|
|
this.data[field.field] = []
|
|
}
|
|
}
|
|
|
|
this.is_ready = true
|
|
this.$nextTick(() => {
|
|
if ( this.mode !== 'view' ) this.$refs.input[0].focus()
|
|
})
|
|
}
|
|
|
|
async load() {
|
|
this.definition = this.resource_class.form_definition
|
|
if (this.mode !== 'insert') {
|
|
this.data = await this.resource_class.get(this.id)
|
|
}
|
|
|
|
this.title = title_map[this.mode] + ' ' + this.resource_class.item
|
|
}
|
|
|
|
async on_create() {
|
|
this.id = this.data.id
|
|
this.mode = 'update'
|
|
await this.vue_on_create(true)
|
|
location_service.set_query(`mode=update&id=${this.id}`)
|
|
}
|
|
|
|
async save_click() {
|
|
if ( !this.validate() ) return
|
|
try {
|
|
if (this.mode === 'insert') {
|
|
this.data = await this.resource_class.create(this.data)
|
|
await this.on_create()
|
|
} else if (this.mode === 'update') {
|
|
await this.resource_class.update(this.id, this.data)
|
|
}
|
|
|
|
this.error_message = ''
|
|
this.other_message = `The ${this.resource_class.item} was saved.`
|
|
} catch (e) {
|
|
if ( e.response && e.response.data && e.response.data.message )
|
|
this.error_message = e.response.data.message
|
|
else this.error_message = 'An unknown error occurred while saving the form.'
|
|
}
|
|
}
|
|
|
|
validate() {
|
|
let valid = true
|
|
for ( const field of this.definition.fields ) {
|
|
if ( field.required && (!(field.field in this.data) || !this.data[field.field]) ) {
|
|
field.error = 'This field is required.'
|
|
valid = false
|
|
} else {
|
|
field.error = ''
|
|
}
|
|
}
|
|
|
|
this.$forceUpdate()
|
|
|
|
return valid
|
|
}
|
|
|
|
back() {
|
|
return action_service.perform({
|
|
text: '',
|
|
type: 'resource',
|
|
resource: this.resource,
|
|
action: 'list',
|
|
})
|
|
}
|
|
|
|
do_nothing() {}
|
|
}
|
|
|