This commit is contained in:
2019-06-05 00:02:53 +00:00
parent 9427fd1a8a
commit f4d9220d62
9 changed files with 195 additions and 88 deletions

View File

@@ -7,33 +7,29 @@ Success is the ability to go from failure to failure without loss of enthusiasm
NEXT TODOS:
TODO: LOGIN navigate back to prior request when it triggers a login?
- Or would this cause a loop at times and fuck shit up?
TODO: Trial mode client should offer alternative logins right on login page and fill in for user to try out
- So a list of user names and a short text of what that user is used for when testing
- User can select and it will prefill in the form
- Obvs when not trial mode then the client doesn't offer it
TODO: Form dirty indicator on the form and easy to see, maybe the background tinges or something
TODO: RIGHTS FOR LIST
- TODO LIST OBJECT RESEARCH / DECISION
- TODO: ?? DECISION server widget lists and other lists
- Either the list should show items with alternate icons to EDIT if they are read only or...
- Use a generic OPEN icon and link instead and user doesn't see status until they open it.
- ReadFULL record but no change should show record read only
- To test use accounts: ReadFullRecord = AuthorizationRoles.BizAdminLimited | AuthorizationRoles.InventoryLimited
- WidgetList should check if even possible to read any part of record, if not then no link to edit
- WidgetList should check if Own record possible and check the list object for owner ID (maybe all lists will need to provide owner ID's?)
TODO: Persist view on return
TODO: VIEW PERSISTANCE / STATE
- Persist view on return
- useful info here: https://vuejsdevelopers.com/2017/04/16/vue-js-browser-button-ux/
- there's another item like this below somewhere.
- Widget list, refresh page causes items per page to reset back to 5 from custom setting, it should cache that shit at least for a session anyway
- Although people probably would want this to be saved to survive sessions
- maybe it should save it per device locally only so that the customizations are local to the device so they can customize differently for different ui's?
- Or, maybe they always want to see 50 widgets no matter where but 10 clients??
- SCROLL POSITION !! - Very important, must return UI to exact scroll position on navigation backwards, not doing so causes a hellish UI to use.
- Seems to be a thing in teh vue router already:
- https://router.vuejs.org/guide/advanced/scroll-behavior.html
- Same position in window
- Same settings in any grids being shown and scrolled to same point in grids
- CODING:
- Need two types of page state, one that survives a logout login session and the other that is cleared
- For example, a user may want to always show 30 items in one list but 5 in another so this should survive login
- On the other hand the scroll position should not survive a session and should be cleared
- We don't need to worry about this for the pages in theory as there is a built in method used in router to enable it which seems flakey but it's the way to do it
- Focus only on the type that survives a session for now, the rest as required
TODO: NEW WIDGET
TODO: ADD NEW WIDGET
- Code for new record to the server
- Need a path to making a new record
- ID 0 maybe
@@ -59,7 +55,10 @@ TODO: Grid / LIST VIEW = I know customers will want to control what shows in the
- I had a customer today request the Description field from unit show in the workorder service grid, it doesn't do that
- Customers probably want the option of picking what fields show and what don't
- Need to think this over, do I have defined columns or is the list just for display to select the record in which case can it just be one column with user selected values showing??
- Security and business rules may affect this as well, for some users they have rights to some of the record but not all of it, i.e. subcontractor so we do need granular field level control over what goes out from the server and at the client expectations
- Maybe list objects also return a list of fields the current user will expect to see so the list can be pre-set up with the columns at the client *then* the data fetched to populate the list
- Server should not send fields that are restricted or have them blocked out or something.
TODO: Grid list view should be easier to open, the open button should not be at the far right but more of the whole entire element or a larger portion of it maybe?
TODO: Outstanding case with vuetify bug in clear button when readonly, check if fixed and if it isn't might need a workaround
TODO: code the user options with the currency symbol etc on the server and then update client to fetch them. Use static values instad in locale.
@@ -188,11 +187,7 @@ Make all fields work according to specs below
- something I just forgot as I went to write it and got stuck reading older shit here
- SCROLL POSITION !! - Very important, must return UI to exact scroll position on navigation backwards, not doing so causes a hellish UI to use.
- Seems to be a thing in teh vue router already:
- https://router.vuejs.org/guide/advanced/scroll-behavior.html
- Same position in window
- Same settings in any grids being shown and scrolled to same point in grids
- AUTOMATED UI TESTING - I need to institute it now and make tests so I have a template to work off for all future tests
@@ -264,3 +259,16 @@ AUTOCOMPLETE
- Customer demo!!
*********************************************************************************************************************************************************
## TODO...MAYBE??
This is stuff I thought of but is dubiously necessary for the initial release at least or could be polish added later:
TODO: Form dirty indicator on the form and easy to see, maybe the background tinges or something
- Save button kind of is, do we need this?
TODO: Trial mode client should offer alternative logins right on login page and fill in for user to try out
- So a list of user names and a short text of what that user is used for when testing
- User can select and it will prefill in the form
- Obvs when not trial mode then the client doesn't offer it

View File

@@ -90,7 +90,9 @@
<v-content>
<v-container fluid fill-height>
<v-layout justify-center>
<router-view></router-view>
<transition name="fade" mode="out-in" @after-leave="afterLeave">
<router-view class="view"></router-view>
</transition>
</v-layout>
</v-container>
</v-content>
@@ -166,6 +168,11 @@ export default {
},
props: {
source: String
},
methods: {
afterLeave() {
this.$root.$emit("triggerScroll");
}
}
};
</script>

View File

@@ -1,9 +1,9 @@
/* eslint-disable */
/* xeslint-disable */
import _ from "../libs/lodash.min.js";
import store from "../store";
import rights from "./bizroles";
export default {
export default {
ROLE_RIGHTS: rights,
AUTHORIZATION_ROLES: {
///<summary>No role set</summary>
@@ -48,8 +48,7 @@ export default {
/////////////////////////////////
//
//
getRights(vm, oType) {
getRights(vm, oType) {
//from bizroles.cs:
//HOW THIS WORKS / WHATS EXPECTED
//Change = CREATE, RETRIEVE, UPDATE, DELETE - Full rights
@@ -72,24 +71,23 @@ export default {
//Get the AyaNova stock role rights for that object
var objectRoleRights = this.ROLE_RIGHTS[typeName];
//get the logged in user's role
var userRole = vm.$store.state.roles;
//calculate the effective rights
//calculate the effective rights
//a non zero result of the bitwise calculation means true and zero means false so using !! to force it into a boolean value (contrary to some style guides that say !! is obscure but I say it saves a lot of typing)
var canChange = !!(userRole & objectRoleRights.Change);
var canChange = !!(userRole & objectRoleRights.Change);
var canReadFullRecord = !!(userRole & objectRoleRights.ReadFullRecord);
ret.change=canChange;
ret.delete=ret.change;//FOR NOW
ret.read=canReadFullRecord;
ret.change = canChange;
ret.delete = ret.change; //FOR NOW
ret.read = canReadFullRecord;
return ret;
}
};
/*
/*
USING BITWISE OPERATORS CHEAT SHEET
//https://codeburst.io/using-javascript-bitwise-operators-in-real-life-f551a731ff5
// Test whether your bit number has a single attribute. '&' ensures
@@ -127,4 +125,4 @@ if (myBitNumber == (myBitNumber | (HAS_FOO2 | HAS_FOO4))) {
if (myBitNumber == (myBitNumber | (HAS_FOO2 | HAS_FOO3 | HAS_FOO4))) {
// False
}
*/
*/

View File

@@ -1,4 +1,4 @@
/* Xeslint-disable */
/* xeslint-disable */
///////////////////////////////
// gzform
//
@@ -11,6 +11,7 @@
// Add any new keys used to the block in locale.js=>commonKeysEditForm
import Vue from "vue";
import errorHandler from "./errorhandler";
import store from "../store";
var triggeringChange = false;
@@ -436,8 +437,8 @@ export default {
// This is required so that server errors can be cleared when input is changed
//
onChange(vm, ref) {
//eslint-disable-next-line
console.log("GZFORM::onChange triggered!");
//xeslint-disable-next-line
//console.log("GZFORM::onChange triggered!");
if (triggeringChange || vm.formState.loading) {
return;
}
@@ -505,5 +506,23 @@ export default {
newState.vm.formState.readOnly = newState.readOnly;
}
});
},
////////////////////////////////////
// Get form settings
// for form specified or empty object if there is none
// EAch form is responsible for what it stores and how it initializes, this just provides that
// the form does the actual work of what settings it requires
//
getFormSettings(formKey) {
var theFormSettings = store.state.formSettings[formKey];
return theFormSettings;
},
////////////////////////////////////
// Set form settings
// for form key specified
//
//
setFormSettings(formKey, formSettings) {
store.commit("setFormSettings", formKey, formSettings);
}
};

View File

@@ -62,7 +62,7 @@ export default {
if (!result) {
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
const copyHotkey = isMac ? "⌘C" : "CTRL+C";
result = prompt(`Press ${copyHotkey}`, string); // eslint-disable-line no-alert
result = prompt(`Press ${copyHotkey}`, string);
if (!result) {
return false;
}

View File

@@ -41,7 +41,7 @@
:headers="headers"
:items="Items"
item-key="id"
:pagination.sync="pagination"
:pagination.sync="localFormSettings.pagination"
:total-items="totalItems"
:loading="loading"
:rows-per-page-items="rowsPerPageItems"
@@ -71,9 +71,11 @@
</template>
<script>
/* xeslint-disable */
/* Xeslint-disable */
const FORM_KEY = "inventorywidgetlist";
export default {
created() {
beforeCreate() {
var that = this;
this.$gzlocale
.fetch([
@@ -94,10 +96,22 @@ export default {
header.text = that.$gzlocale.get(header.text);
});
})
.then(() => (this.formState.ready = true))
.then(() => {
//eslint-disable-next-line
debugger;
var storedFormSettings = that.$gzform.getFormSettings(FORM_KEY);
//set default values for form settings if they are not present yet
if (!storedFormSettings) {
storedFormSettings = {
pagination: {}
};
}
that.localFormSettings = storedFormSettings;
that.formState.ready = true;
})
.catch(err => {
this.formState.ready = true; //show the form anyway so we know what's what
this.$gzHandleFormError(err);
that.formState.ready = true; //show the form anyway so we know what's what
that.$gzHandleFormError(err);
});
},
data() {
@@ -116,7 +130,9 @@ export default {
totalItems: 0,
Items: [],
loading: true,
pagination: {},
localFormSettings: {
pagination: {}
},
selected: [],
rowsPerPageItems: [5, 10, 25, 99],
rowsPerPageText: "blah per blah",
@@ -138,7 +154,7 @@ export default {
};
},
watch: {
pagination: {
"localFormSettings.pagination": {
handler() {
this.getDataFromApi();
},
@@ -151,25 +167,43 @@ export default {
this.dialogdata.showeditdialog = true;
},
getDataFromApi() {
var that = this;
var listOptions = {
offset: 0,
limit: 5,
sort: "name",
asc: true
};
if (this.pagination.rowsPerPage && this.pagination.rowsPerPage > 0) {
if (
this.localFormSettings.pagination.rowsPerPage &&
this.localFormSettings.pagination.rowsPerPage > 0
) {
listOptions.offset =
(this.pagination.page - 1) * this.pagination.rowsPerPage;
listOptions.limit = this.pagination.rowsPerPage;
(this.localFormSettings.pagination.page - 1) *
this.localFormSettings.pagination.rowsPerPage;
listOptions.limit = this.localFormSettings.pagination.rowsPerPage;
}
listOptions.sort = this.pagination.sortBy;
listOptions.asc = !this.pagination.descending;
listOptions.sort = this.localFormSettings.pagination.sortBy;
listOptions.asc = !this.localFormSettings.pagination.descending;
//set the list settings in the store since we were successful at retrieval
if (
that.localFormSettings &&
that.localFormSettings.pagination &&
that.localFormSettings.pagination.rowsPerPage
) {
//eslint-disable-next-line
debugger;
that.$gzform.setFormSettings(FORM_KEY, that.localFormSettings);
}
this.loading = true;
var listUrl = "Widget/ListWidgets?" + this.$gzapi.buildQuery(listOptions);
this.$gzapi.get(listUrl).then(res => {
this.loading = false;
this.Items = res.data;
this.totalItems = res.paging.count;
that.loading = false;
that.Items = res.data;
that.totalItems = res.paging.count;
});
},
editItem(item) {
@@ -181,30 +215,3 @@ export default {
}
};
</script>
//Example api response // { // "data": [ // { // "id": 1, // "concurrencyToken":
2262471, // "ownerId": 1, // "name": "Handcrafted Wooden Bacon 23", // "serial":
1, // "dollarAmount": 25.42, // "active": true, // "roles": 8212, //
"startDate": "2018-11-19T12:20:42.920058", // "endDate":
"2018-11-19T15:37:47.053849", // "notes": "Voluptas assumenda laudantium nemo
cupiditate. Quia voluptatem reiciendis et. Sit non error est. Tenetur provident
nostrum. Voluptatem voluptatem et." // }, // { // "id": 2, //
"concurrencyToken": 2262494, // "ownerId": 1, // "name": "Ergonomic Soft Gloves
24", // "serial": 2, // "dollarAmount": 530.39, // "active": true, // "roles":
8212, // "startDate": "2018-11-19T12:17:32.488013", // "endDate":
"2018-11-19T17:01:18.425666", // "notes": "Sed rerum minima blanditiis est.
Praesentium consequatur numquam nostrum voluptatem libero dolores voluptatem et.
Aut et nobis consectetur voluptatem minus. Ipsa nemo non in iste adipisci
voluptatem. Minus consequatur in accusantium." // }, // { // "id": 3, //
"concurrencyToken": 2262518, // "ownerId": 1, // "name": "Fantastic Metal
Computer 25", // "serial": 3, // "dollarAmount": 494.3, // "active": true, //
"roles": 8212, // "startDate": "2018-11-19T13:06:47.437006", // "endDate":
"2018-11-19T14:41:44.665721", // "notes": "Facere et ex. Ipsa aspernatur itaque
maiores sint nulla esse incidunt. Architecto labore voluptatem dolore iusto ut."
// } // ], // "paging": { // "count": 100, // "offset": 0, // "limit": 3, //
"first":
"http://localhost:7575/api/v8.0/Widget/ListWidgets?pageNo=1&pageSize=3", //
"previous": null, // "next":
"http://localhost:7575/api/v8.0/Widget/ListWidgets?pageNo=1&pageSize=3", //
"last": "http://localhost:7575/api/v8.0/Widget/ListWidgets?pageNo=34&pageSize=3"
// } // }

View File

@@ -9,14 +9,67 @@ import operations from "./views/operations.vue";
import notfound from "./views/notfound.vue";
Vue.use(Router);
/* Xeslint-disable */
// scrollBehavior:
// - only available in html5 history mode
// - defaults to no scroll behavior
// - return false to prevent scroll
const scrollBehavior = function(to, from, savedPosition) {
if (savedPosition) {
// savedPosition is only available for popstate navigations.
return savedPosition;
} else {
const position = {};
// scroll to anchor by returning the selector
if (to.hash) {
position.selector = to.hash;
// specify offset of the element
if (to.hash === "#anchor2") {
position.offset = { y: 100 };
}
if (document.querySelector(to.hash)) {
return position;
}
// if the returned position is falsy or an empty object,
// will retain current scroll position.
return false;
}
return new Promise(resolve => {
// check if any matched route config has meta that requires scrolling to top
if (to.matched.some(m => m.meta.scrollToTop)) {
// coords will be used if no selector is provided,
// or if the selector didn't match any element.
position.x = 0;
position.y = 0;
}
// wait for the out transition to complete (if necessary)
this.app.$root.$once("triggerScroll", () => {
// if the resolved position is falsy or an empty object,
// will retain current scroll position.
resolve(position);
});
});
}
};
export default new Router({
mode: "history",
base: process.env.BASE_URL,
scrollBehavior,
routes: [
{
path: "/login",
name: "login",
meta: { scrollToTop: true },
component: () =>
import(/* webpackChunkName: "login" */ "./views/login.vue")
},
@@ -24,7 +77,13 @@ export default new Router({
{
path: "/",
name: "home",
meta: { scrollToTop: true },
component: Home
// ,
// beforeEnter(to, from, next) {
// store.state.tempsessionsettings = false;//here is a way to reset the temp session settings, but I'll likely do it through logout proces instead, keeping this for example purposes
// next();
// }
},
{
path: "/about",
@@ -59,6 +118,7 @@ export default new Router({
{
path: "/accounting",
name: "accounting",
//meta: { scrollToTop: true },
component: accounting
},
{

View File

@@ -27,7 +27,8 @@ export default new Vuex.Store({
timeZoneOffset: -7 //timeZoneOffset is in decimal hours
},
navItems: [],
logArray: []
logArray: [],
formSettings: {} //this is the settings on forms that survive a refresh like grid number of items to show etc
},
mutations: {
login(state, data) {
@@ -84,6 +85,15 @@ export default new Vuex.Store({
state.logArray.length - MaxLogLength
);
}
},
clearAllFormSettings(state) {
state.formSettings = {};
},
setFormSettings(state, formKey, data) {
state.formSettings[formKey] = data;
},
clearFormSettings(state, formKey) {
delete state.formSettings[formKey];
}
},
actions: {}

View File

@@ -280,8 +280,6 @@ export default {
mounted() {
this.clientInfo = {};
this.clientInfo = aboutInfo;
//eslint-disable-next-line
//console.log("MOUNTED");
},
data() {
return {