This commit is contained in:
@@ -14,27 +14,16 @@ TODO CLIENT STUFF
|
|||||||
|
|
||||||
TODO NEXT
|
TODO NEXT
|
||||||
|
|
||||||
|
End to end action
|
||||||
|
- The faster I get to a fully working basic level like being able to do data entry and submit to server new or updated records, the faster I can flesh out all the rest
|
||||||
|
- Code the submit data to server route for update
|
||||||
|
- Code for new record to the server
|
||||||
|
|
||||||
GZ VALIDATE
|
Simulate server broken rules so can integrate into ui and rule system
|
||||||
- Add rules for each field type validation
|
|
||||||
- Is there anything pulled into the validate code directly that is already available off the "v" variable?
|
|
||||||
- localization etc
|
|
||||||
|
|
||||||
Going to need to handle localized numeric entry formats
|
|
||||||
|
|
||||||
|
|
||||||
DATETIME
|
|
||||||
|
|
||||||
- Validation
|
- Validation
|
||||||
- after much fuckery it's becoming clear that between the localization requirements and the complexity of server broken rules coming back from the api that I should just use my own validation code
|
|
||||||
- See here: https://vuetifyjs.com/en/components/forms#form
|
|
||||||
- And here: https://vuetifyjs.com/en/components/text-fields#custom-validation
|
|
||||||
- What is needed is a class that returns an array of functions that can be passed to vuetify "rules" property of components
|
|
||||||
On change each rule is called in turn and if one returns false or a string then it's in an error state
|
|
||||||
- I also need to add that it scans and or keeps a collection of broken rules that come back from the server and each component checks a rule that checks in turn for server broken rules in last api call
|
- I also need to add that it scans and or keeps a collection of broken rules that come back from the server and each component checks a rule that checks in turn for server broken rules in last api call
|
||||||
- And my rules object needs to be localized so I guess it should be aware of the locale object and work with it as required
|
|
||||||
- Should it lazy load error translations? (In as batchy a way as possible?)
|
|
||||||
- Or, perhaps it's more convenient to cache all possible regular form entry errors since there isn't really that many, perhaps less than a dozen when it comes down to it
|
|
||||||
- My rules object needs to be coded so I can easily specify a collection of rules appropriate to a form field
|
- My rules object needs to be coded so I can easily specify a collection of rules appropriate to a form field
|
||||||
- So, for example let's say I have a date input, if I commonly need one to be required and be after or before another field then I should have a single combined rule of
|
- So, for example let's say I have a date input, if I commonly need one to be required and be after or before another field then I should have a single combined rule of
|
||||||
RequiredAndAfterTarget and this method is aggregates both the required and the After rules into a single collection returned
|
RequiredAndAfterTarget and this method is aggregates both the required and the After rules into a single collection returned
|
||||||
@@ -46,9 +35,16 @@ DON'T code the user options with the currency symbol etc until after it's all be
|
|||||||
Locale should fetch those settings the first time it sees they are not present so that they are refreshed upon use and are not stored in localstorage
|
Locale should fetch those settings the first time it sees they are not present so that they are refreshed upon use and are not stored in localstorage
|
||||||
(or should they be? anyway, can work that out later)
|
(or should they be? anyway, can work that out later)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Other fields that are locale dependent
|
Other fields that are locale dependent
|
||||||
DateTime field next
|
DateTime field next
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FIXES REQUIRED
|
||||||
|
|
||||||
- API get code is incorrectly dealing with expired bearer cert, a 401 is returned and it tries to parse the result as if it succeeded when it really should trigger a login process
|
- API get code is incorrectly dealing with expired bearer cert, a 401 is returned and it tries to parse the result as if it succeeded when it really should trigger a login process
|
||||||
- Time zone offset mismatch warning needs expansion, it should only prompt a few times (maybe or find a way to deal with this) and it should offer to change it at the server automatically
|
- Time zone offset mismatch warning needs expansion, it should only prompt a few times (maybe or find a way to deal with this) and it should offer to change it at the server automatically
|
||||||
- Localize time zone mismatch warning
|
- Localize time zone mismatch warning
|
||||||
@@ -160,6 +156,10 @@ Make all fields work according to specs below
|
|||||||
- What to do if they edit? Refresh the list but keep the same page location?
|
- What to do if they edit? Refresh the list but keep the same page location?
|
||||||
|
|
||||||
|
|
||||||
|
TESTING TODO
|
||||||
|
Localized input and parsing and validation
|
||||||
|
- Going to need to handle localized numeric entry formats
|
||||||
|
- Deal with this once form is in good shape and worth testing with different locales
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
TODO AFTER CLIENT block ABOVE:
|
TODO AFTER CLIENT block ABOVE:
|
||||||
|
|||||||
@@ -93,6 +93,14 @@ export default {
|
|||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
fetchPutOptions(data) {
|
||||||
|
return {
|
||||||
|
method: "put",
|
||||||
|
mode: "cors",
|
||||||
|
headers: this.postAuthorizedHeaders(),
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
};
|
||||||
|
},
|
||||||
fetchGetOptions() {
|
fetchGetOptions() {
|
||||||
/* GET WITH AUTH */
|
/* GET WITH AUTH */
|
||||||
return {
|
return {
|
||||||
@@ -134,6 +142,9 @@ export default {
|
|||||||
}
|
}
|
||||||
return store.state.apiUrl + apiPath;
|
return store.state.apiUrl + apiPath;
|
||||||
},
|
},
|
||||||
|
/////////////////////////////
|
||||||
|
// ENCODE QUERY STRING
|
||||||
|
//
|
||||||
buildQuery(obj, sep, eq, name) {
|
buildQuery(obj, sep, eq, name) {
|
||||||
sep = sep || "&";
|
sep = sep || "&";
|
||||||
eq = eq || "=";
|
eq = eq || "=";
|
||||||
@@ -166,6 +177,9 @@ export default {
|
|||||||
encodeURIComponent(stringifyPrimitive(obj))
|
encodeURIComponent(stringifyPrimitive(obj))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
///////////////////////////////////
|
||||||
|
// GET DATA FROM API SERVER
|
||||||
|
//
|
||||||
get(route) {
|
get(route) {
|
||||||
var that = this;
|
var that = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
@@ -195,6 +209,49 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
///////////////////////////////////
|
||||||
|
// POST / PUT DATA TO API SERVER
|
||||||
|
//
|
||||||
|
upsert(route, data) {
|
||||||
|
var that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
//determine if this is a new or existing record
|
||||||
|
var fetchOptions = undefined;
|
||||||
|
if (data.concurrencyToken) {
|
||||||
|
fetchOptions = that.fetchPutOptions(data);
|
||||||
|
} else {
|
||||||
|
fetchOptions = that.fetchPostOptions(data);
|
||||||
|
}
|
||||||
|
fetch(that.APIUrl(route), fetchOptions)
|
||||||
|
.then(that.status)
|
||||||
|
.then(that.json)
|
||||||
|
.then(response => {
|
||||||
|
//Note: response.error indicates there is an error, however this is not an unusual condition
|
||||||
|
//it could be validation errors or other general error so we need to treat it here like it's normal
|
||||||
|
//and let the caller deal with it appropriately
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
//fundamental error, can't proceed with this call
|
||||||
|
|
||||||
|
var errorMessage =
|
||||||
|
"API error: UPSERT route =" + route + ", message =" + error.message;
|
||||||
|
store.commit("logItem", errorMessage);
|
||||||
|
|
||||||
|
if (error.message && error.message.includes("401")) {
|
||||||
|
store.commit(
|
||||||
|
"logItem",
|
||||||
|
"User is not authorized, redirecting to login"
|
||||||
|
);
|
||||||
|
auth.logout();
|
||||||
|
router.push("/login");
|
||||||
|
} else {
|
||||||
|
//alert("Error: " + errorMessage);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//new functions above here
|
//new functions above here
|
||||||
|
|||||||
@@ -75,6 +75,46 @@ function getControlLabel(ctrl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
///////////////////////////////
|
||||||
|
// SERVER ERRORS
|
||||||
|
//
|
||||||
|
Server(v, ref) {
|
||||||
|
|
||||||
|
|
||||||
|
//check if any errors and short circuit if none
|
||||||
|
|
||||||
|
//check if this control is mentioned at all and short circuit return false if not
|
||||||
|
|
||||||
|
var ctrl = getControl(v, ref);
|
||||||
|
if(typeof ctrl == 'undefined'){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//It IS in the list of error controls:
|
||||||
|
|
||||||
|
//check if this control has been changed at all, I guess being here might mean it has?
|
||||||
|
//If this control is dirty / was changed from the value that was in it when the server returned the error then remove the error for this control from the server error list and return false
|
||||||
|
//ELSE if this control is unchanged / not dirty
|
||||||
|
//return the error(s) properly localized
|
||||||
|
|
||||||
|
|
||||||
|
//IF user modifies field in any way then this rule must be cleared
|
||||||
|
|
||||||
|
//Some rules are for the entire object so...???
|
||||||
|
//maybe need a form control that is just about rules or something
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // "ErrorRequiredFieldEmpty": "{0} is a required field. Please enter a value for {0}",
|
||||||
|
// var err = locale.get("ErrorRequiredFieldEmpty");
|
||||||
|
// var fieldName = getControlLabel(ctrl);
|
||||||
|
// err = _.replace(err, "{0}", fieldName);
|
||||||
|
// //lodash replace only replaces first instance so need to do it twice
|
||||||
|
// err = _.replace(err, "{0}", fieldName);
|
||||||
|
// return err;
|
||||||
|
},
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
// REQUIRED
|
// REQUIRED
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-layout v-if="this.formReady">
|
<v-layout v-if="this.formReady">
|
||||||
<v-flex>
|
<v-flex>
|
||||||
<form ref="form">
|
<v-form ref="form">
|
||||||
<v-layout align-center justify-left row wrap>
|
<v-layout align-center justify-left row wrap>
|
||||||
<v-flex xs12 sm6 lg4 xl3 px-2>
|
<v-flex xs12 sm6 lg4 xl3 px-2>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
v-model="obj.count"
|
v-model="obj.count"
|
||||||
:counter="10"
|
:counter="10"
|
||||||
:label="this.$gzlocale.get('WidgetCount')"
|
:label="this.$gzlocale.get('WidgetCount')"
|
||||||
ref="count"
|
ref="count"
|
||||||
:rules="[this.$gzv.Integer(this,'count'),this.$gzv.Required(this,'count')]"
|
:rules="[this.$gzv.Integer(this,'count'),this.$gzv.Required(this,'count')]"
|
||||||
required
|
required
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<gz-date-time-picker
|
<gz-date-time-picker
|
||||||
:label="this.$gzlocale.get('WidgetStartDate')"
|
:label="this.$gzlocale.get('WidgetStartDate')"
|
||||||
v-model="obj.startDate"
|
v-model="obj.startDate"
|
||||||
ref="startDate"
|
ref="startDate"
|
||||||
></gz-date-time-picker>
|
></gz-date-time-picker>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
@@ -63,24 +63,34 @@
|
|||||||
<v-checkbox
|
<v-checkbox
|
||||||
v-model="obj.active"
|
v-model="obj.active"
|
||||||
:label="this.$gzlocale.get('Active')"
|
:label="this.$gzlocale.get('Active')"
|
||||||
ref="active"
|
ref="active"
|
||||||
required
|
required
|
||||||
></v-checkbox>
|
></v-checkbox>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
<v-flex xs12 sm6 lg4 xl3 px-2>
|
||||||
|
<v-text-field
|
||||||
|
v-model="obj.roles"
|
||||||
|
:label="this.$gzlocale.get('WidgetRoles')"
|
||||||
|
ref="roles"
|
||||||
|
:rules="[this.$gzv.Integer(this,'roles'),this.$gzv.Required(this,'roles')]"
|
||||||
|
:error-messages="canHasServerError('roles')"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
|
|
||||||
<v-layout align-center justify-space-around row wrap mt-5>
|
<v-layout align-center justify-space-around row wrap mt-5>
|
||||||
<v-flex xs1>
|
<v-flex xs1>
|
||||||
<v-btn>one</v-btn>
|
<v-btn @click="validate">Force validate</v-btn>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs1>
|
<v-flex xs1>
|
||||||
<v-btn>two</v-btn>
|
<v-btn>test2</v-btn>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs1>
|
<v-flex xs1>
|
||||||
<v-btn @click="submit">submit</v-btn>
|
<v-btn @click="submit">save</v-btn>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</form>
|
</v-form>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</template>
|
</template>
|
||||||
@@ -133,10 +143,27 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
obj: {},
|
obj: {},
|
||||||
|
serverErrors: {},
|
||||||
formReady: false
|
formReady: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getErrorCount() {
|
||||||
|
return 3;
|
||||||
|
},
|
||||||
|
canHasServerError(fieldName) {
|
||||||
|
if (fieldName == "roles") {
|
||||||
|
return ["This is an error"];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validate() {
|
||||||
|
// this.$refs.form.resetValidation();
|
||||||
|
// this.$refs.form.validate();
|
||||||
|
//test to manually insert an error into the field like a server validation error would need to do
|
||||||
|
//UPDATE: this cannot work because you need to bind to error-messages, not set it directly like here
|
||||||
|
// this.$refs.roles["error-messages"] = ["This is an error!"];
|
||||||
|
// this.$refs.roles.error = true;
|
||||||
|
},
|
||||||
getDataFromApi() {
|
getDataFromApi() {
|
||||||
var url = "Widget/" + this.$route.params.id;
|
var url = "Widget/" + this.$route.params.id;
|
||||||
this.$gzapi.get(url).then(res => {
|
this.$gzapi.get(url).then(res => {
|
||||||
@@ -145,9 +172,61 @@ export default {
|
|||||||
},
|
},
|
||||||
submit() {
|
submit() {
|
||||||
// debugger;
|
// debugger;
|
||||||
//this.$validator.validateAll();
|
//Submit the form data to the api
|
||||||
this.$refs.form.validate();
|
|
||||||
}
|
//Check for broken rules, do not submit with broken rules
|
||||||
|
//No broken rules then:
|
||||||
|
//Gather up the form data into a submitable object (can I just submit the existing object??)
|
||||||
|
//Post it
|
||||||
|
//Update the local object with the returned result
|
||||||
|
//Check the return object for broken rules
|
||||||
|
var that = this;
|
||||||
|
var url = "Widget/" + this.$route.params.id;
|
||||||
|
this.$gzapi
|
||||||
|
.upsert(url, this.obj)
|
||||||
|
.then(res => {
|
||||||
|
if (res.error) {
|
||||||
|
debugger;
|
||||||
|
that.serverErrors = res.error;
|
||||||
|
that.$refs.form.resetValidation();
|
||||||
|
that.$refs.form.validate();
|
||||||
|
//example error when submit when there are no roles set at all (blank)
|
||||||
|
//{"error":{"code":"2200","details":[{"code":"2200","message":"","target":"roles","error":"VALIDATION_FAILED"}],"message":"Object did not pass validation"}}
|
||||||
|
//todo: integrate validation error into form so user can see it
|
||||||
|
//User can only submit if no existing errors so should be starting with a clean error free form and then will see validation errors under appropriate fields
|
||||||
|
//if a user makes a change to a control then that control being changed should remove the rule from the server until the next submit happens
|
||||||
|
} else {
|
||||||
|
//Logic for detecing if a post or put: if id then it was a post, if no id then it was a put
|
||||||
|
if (res.id) {
|
||||||
|
//Handle "post" of new record
|
||||||
|
that.obj = res.data;
|
||||||
|
} else {
|
||||||
|
//Handle "put" of an existing record
|
||||||
|
that.obj.concurrencyToken = res.data.concurrencyToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
//we should be here if it was a gross error of some kind not a mild one like validation but more like if the server doesn't exist or something I guess
|
||||||
|
|
||||||
|
console.log(error);
|
||||||
|
alert("Houston, we have a problem");
|
||||||
|
});
|
||||||
|
|
||||||
|
//example from login form
|
||||||
|
// if (this.input.username != "" && this.input.password != "") {
|
||||||
|
// auth
|
||||||
|
// .authenticate(this.input.username, this.input.password)
|
||||||
|
// .then(() => {
|
||||||
|
// this.$router.replace({ name: "home" });
|
||||||
|
// })
|
||||||
|
// .catch(function(error) {
|
||||||
|
// /* xeslint-disable-next-line */
|
||||||
|
// //console.log(error);
|
||||||
|
// alert("login failed: " + error);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
} //end of submit()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user