case 4302
This commit is contained in:
@@ -151,12 +151,12 @@ Use of the `ayPrepareData` function is optional and most reports will not requir
|
|||||||
This is the default ayPrepareData method:
|
This is the default ayPrepareData method:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
async function ayPrepareData(ayData) {
|
async function ayPrepareData(reportData) {
|
||||||
return ayData;
|
return reportData;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`ayData` parameter is an object containing three keys with data required to render the report and it's format is detailed below in the [Report Data Objects](#report-data-structure) section.
|
`reportData` parameter is an object containing three keys with data required to render the report and it's format is detailed below in the [Report Data Objects](#report-data-structure) section.
|
||||||
|
|
||||||
Note that the SAMPLE DATA section of the user interface in the report designer shows the raw data _before_ it is passed to this function, not how it would look _after_ this function is run. If you need to see how the data is shaped after a ayPrepareData you can make use of the `ayJSON` helper documented below to view the raw data on the rendered report itself.
|
Note that the SAMPLE DATA section of the user interface in the report designer shows the raw data _before_ it is passed to this function, not how it would look _after_ this function is run. If you need to see how the data is shaped after a ayPrepareData you can make use of the `ayJSON` helper documented below to view the raw data on the rendered report itself.
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ Importing of report templates is done from the administration report templates [
|
|||||||
|
|
||||||
When a report template is processed, business object data is provided to the template along with other data. Some of this data is used internally by the provided helpers and you have full access to these data values in your templates, Handlebar helpers and PrepareData scripts.
|
When a report template is processed, business object data is provided to the template along with other data. Some of this data is used internally by the provided helpers and you have full access to these data values in your templates, Handlebar helpers and PrepareData scripts.
|
||||||
|
|
||||||
In the `ayPrepareData` function on the Prepare Data tab of the report designer the `ayData` parameter contains three properties detailed below you can use in preparing data:
|
In the `ayPrepareData` function on the Prepare Data tab of the report designer the `reportData` parameter contains three properties detailed below you can use in preparing data:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -473,9 +473,9 @@ Note that you need to tell AyaNova which translation keys to pre-fetch before re
|
|||||||
For example, let's say you wanted to display the translation for Customer and WorkOrder, you would insert a call to get the translations as follows:
|
For example, let's say you wanted to display the translation for Customer and WorkOrder, you would insert a call to get the translations as follows:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
async function ayPrepareData(ayData) {
|
async function ayPrepareData(reportData) {
|
||||||
await ayGetTranslations(["Customer", "WorkOrder"]);
|
await ayGetTranslations(["Customer", "WorkOrder"]);
|
||||||
return ayData;
|
return reportData;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -507,12 +507,15 @@ The `ayGroupByKey` function is provided to group the report data by a key name i
|
|||||||
The following shows using the ayGroupBy function in the ayPrepareData to reformat a Customer Unit List into groups by Customer name which is provided in the CustomerViz property:
|
The following shows using the ayGroupBy function in the ayPrepareData to reformat a Customer Unit List into groups by Customer name which is provided in the CustomerViz property:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
async function ayPrepareData(ayData) {
|
async function ayPrepareData(reportData) {
|
||||||
//send the raw report data to the groupByKey function which will return a new array grouped by the key name provided
|
//send the raw report data to the groupByKey function which will return a new array grouped by the key name provided
|
||||||
ayData.ayReportData = ayGroupByKey(ayData.ayReportData, "CustomerViz");
|
reportData.ayReportData = ayGroupByKey(
|
||||||
|
reportData.ayReportData,
|
||||||
|
"CustomerViz"
|
||||||
|
);
|
||||||
|
|
||||||
//return the data into the pipeline to send to the report template
|
//return the data into the pipeline to send to the report template
|
||||||
return ayData;
|
return reportData;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -532,7 +535,7 @@ While it is possible to login via a script using alternate credentials and acces
|
|||||||
|
|
||||||
### API convenience functions
|
### API convenience functions
|
||||||
|
|
||||||
Two functions are provided with the report to assist with API usage from your ayPrepareData custom function:
|
The following functions are provided to assist with API usage from your ayPrepareData custom function:
|
||||||
|
|
||||||
_GET_
|
_GET_
|
||||||
|
|
||||||
@@ -549,27 +552,40 @@ The ayPostToAPI function works with POST routes in the API:
|
|||||||
The `token` parameter is optional and if not provided will be set by default to the current User's access token.
|
The `token` parameter is optional and if not provided will be set by default to the current User's access token.
|
||||||
The `route` parameter will be automatically prepended with the server local api url if you do not provide the starting "http" in the URL. Do not include the initial slash.
|
The `route` parameter will be automatically prepended with the server local api url if you do not provide the starting "http" in the URL. Do not include the initial slash.
|
||||||
|
|
||||||
|
_PUT_
|
||||||
|
|
||||||
|
The ayPutToAPI function works with PUT routes in the API:
|
||||||
|
|
||||||
|
`async function ayPutToAPI(route, data, token) {...`
|
||||||
|
|
||||||
|
The `token` parameter is optional and if not provided will be set by default to the current User's access token.
|
||||||
|
The `route` parameter will be automatically prepended with the server local api url if you do not provide the starting "http" in the URL. Do not include the initial slash.
|
||||||
|
|
||||||
_Parameters_
|
_Parameters_
|
||||||
|
|
||||||
The `route` parameter is required and will automatically include the current API server URL prepended if not provided (it looks for "http" at the start). For example you can specifiy the `route` parameter as simply the end portion of the route without the slash: "server-info" or the full route to the server from the server URL meta property. For example the full route to fetch data from the `server-info` API route can be constructed as follows:
|
The `route` parameter is required and will automatically include the current API server URL prepended if not provided (it looks for "http" at the start).
|
||||||
`` let route=`${ayData.ayServerMetaData.ayApiUrl}server-info`; ``
|
|
||||||
|
|
||||||
The `token` parameter is optional and if not provided will be set by default to the current User's access token or you can specify it by using the authorization token provided in the Client data as follows:
|
For example you can specifiy the `route` parameter as simply the end portion of the route without the slash: "server-info" or the full route to the server from the server URL meta property.
|
||||||
`let token=ayData.ayClientMetaData.Authorization;`
|
|
||||||
|
For example the full route to fetch data from the `server-info` API route can be constructed as follows:
|
||||||
|
`` let route=`${reportData.ayServerMetaData.ayApiUrl}server-info`; ``
|
||||||
|
|
||||||
|
The `token` parameter is **optional** and if not provided will be set by default to the current User's access token or you can specify it by using the authorization token provided in the Client data as follows:
|
||||||
|
`let token=reportData.ayClientMetaData.Authorization;`
|
||||||
|
|
||||||
The `data` parameter of a POST route differs for each route and is documented in the developer's API documentation.
|
The `data` parameter of a POST route differs for each route and is documented in the developer's API documentation.
|
||||||
|
|
||||||
The following is an example of accessing the API with both a GET and POST action:
|
The following is an example of accessing the API with both a GET and POST action:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
async function ayPrepareData(ayData) {
|
async function ayPrepareData(reportData) {
|
||||||
//Example API GET method
|
//Example API GET method
|
||||||
//to fetch data from API server
|
//to fetch data from API server
|
||||||
//using the "server-info" route
|
//using the "server-info" route
|
||||||
|
|
||||||
//Add the data to the main report data object
|
//Add the data to the main report data object
|
||||||
//so it's available to the template
|
//so it's available to the template
|
||||||
ayData.myData = {
|
reportData.myData = {
|
||||||
ServerInfo: await ayGetFromAPI("server-info")
|
ServerInfo: await ayGetFromAPI("server-info")
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -579,8 +595,11 @@ async function ayPrepareData(ayData) {
|
|||||||
//construct the POST object
|
//construct the POST object
|
||||||
let searchPostData = { phrase: "fish" };
|
let searchPostData = { phrase: "fish" };
|
||||||
|
|
||||||
ayData.myData.SearchResults = await ayPostToAPI("search", searchPostData);
|
reportData.myData.SearchResults = await ayPostToAPI(
|
||||||
return ayData;
|
"search",
|
||||||
|
searchPostData
|
||||||
|
);
|
||||||
|
return reportData;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ let PreParedReportDataObject = null;
|
|||||||
// Pre render function
|
// Pre render function
|
||||||
//
|
//
|
||||||
async function ayPreRender(ayData) {
|
async function ayPreRender(ayData) {
|
||||||
if (typeof ayPrepareData === "function") {
|
if (typeof ayPrepareData === "function") {
|
||||||
PreParedReportDataObject = await ayPrepareData(ayData);
|
PreParedReportDataObject = await ayPrepareData(ayData);
|
||||||
} else {
|
} else {
|
||||||
PreParedReportDataObject = ayData;
|
PreParedReportDataObject = ayData;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//##################################### HANDLEBARS HELPERS ###################################################
|
//##################################### HANDLEBARS HELPERS ###################################################
|
||||||
@@ -18,128 +18,128 @@ async function ayPreRender(ayData) {
|
|||||||
// Set our stock handlebars helpers
|
// Set our stock handlebars helpers
|
||||||
//
|
//
|
||||||
function ayRegisterHelpers() {
|
function ayRegisterHelpers() {
|
||||||
Handlebars.registerHelper("ayCaps", function (ayValue) {
|
Handlebars.registerHelper("ayCaps", function (ayValue) {
|
||||||
return ayValue.toUpperCase();
|
return ayValue.toUpperCase();
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayDateTime", function (ayValue) {
|
|
||||||
return utcDateToShortDateAndTimeLocalized(ayValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayDate", function (ayValue) {
|
|
||||||
return utcDateToShortDateLocalized(ayValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayTime", function (ayValue) {
|
|
||||||
return utcDateToShortTimeLocalized(ayValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayDecimal", function (ayValue) {
|
|
||||||
return decimalLocalized(ayValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayCurrency", function (ayValue) {
|
|
||||||
return currencyLocalized(ayValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayWiki", function (ayValue) {
|
|
||||||
if (ayValue == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
//replace attachment urls with tokenized local urls
|
|
||||||
let src = ayValue.replace(/\[ATTACH:(.*)\]/g, function (match, p1) {
|
|
||||||
return attachmentDownloadUrl(p1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Handlebars.SafeString(
|
|
||||||
DOMPurify.sanitize(marked.parse(src, { breaks: true }))
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayJSON", function (obj) {
|
Handlebars.registerHelper("ayDateTime", function (ayValue) {
|
||||||
return JSON.stringify(obj, null, 3);
|
return utcDateToShortDateAndTimeLocalized(ayValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper("ayLink", function (text, url) {
|
Handlebars.registerHelper("ayDate", function (ayValue) {
|
||||||
var url = Handlebars.escapeExpression(url),
|
return utcDateToShortDateLocalized(ayValue);
|
||||||
text = Handlebars.escapeExpression(text);
|
});
|
||||||
|
|
||||||
return new Handlebars.SafeString("<a href='" + url + "'>" + text + "</a>");
|
Handlebars.registerHelper("ayTime", function (ayValue) {
|
||||||
});
|
return utcDateToShortTimeLocalized(ayValue);
|
||||||
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper("ayLogo", function (size) {
|
Handlebars.registerHelper("ayDecimal", function (ayValue) {
|
||||||
|
return decimalLocalized(ayValue);
|
||||||
if (AYMETA.ayServerMetaData) {
|
});
|
||||||
switch (size) {
|
|
||||||
case "small":
|
Handlebars.registerHelper("ayCurrency", function (ayValue) {
|
||||||
if (!AYMETA.ayServerMetaData.HasSmallLogo) {
|
return currencyLocalized(ayValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper("ayWiki", function (ayValue) {
|
||||||
|
if (ayValue == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case "medium":
|
|
||||||
if (!AYMETA.ayServerMetaData.HasMediumLogo) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "large":
|
|
||||||
if (!AYMETA.ayServerMetaData.HasLargeLogo) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var url = `${Handlebars.escapeExpression(
|
|
||||||
AYMETA.ayServerMetaData.ayApiUrl
|
|
||||||
)}logo/${size}`;
|
|
||||||
return new Handlebars.SafeString("<img src='" + url + "'/>");
|
|
||||||
});
|
|
||||||
|
|
||||||
Handlebars.registerHelper("ayT", function (translationKey) {
|
//replace attachment urls with tokenized local urls
|
||||||
if (ayTranslationKeyCache[translationKey] == undefined) {
|
let src = ayValue.replace(/\[ATTACH:(.*)\]/g, function (match, p1) {
|
||||||
throw `ayT reporting helper error: the key "${translationKey}" is not present in the translation cache, did you forget to include it in your call to "await ayGetTranslations(['ExampleTranslationKey1','ExampleTranslationKey2','etc']);" in ayPrepareData()\nTranslationKeyCache contains: ${JSON.stringify(
|
return attachmentDownloadUrl(p1);
|
||||||
ayTranslationKeyCache,
|
});
|
||||||
null,
|
|
||||||
3
|
|
||||||
)}?`;
|
|
||||||
// return translationKey;
|
|
||||||
}
|
|
||||||
return ayTranslationKeyCache[translationKey];
|
|
||||||
});
|
|
||||||
|
|
||||||
///////////////////////////////////////////
|
return new Handlebars.SafeString(
|
||||||
// BarCode helper using
|
DOMPurify.sanitize(marked.parse(src, { breaks: true }))
|
||||||
// https://github.com/metafloor/bwip-js#browser-usage
|
);
|
||||||
//
|
});
|
||||||
Handlebars.registerHelper("ayBC", function (text, options) {
|
|
||||||
let canvas = document.getElementById("aybarcode");
|
|
||||||
if (canvas == null) {
|
|
||||||
canvas = document.createElement("canvas");
|
|
||||||
canvas.id = "aybarcode";
|
|
||||||
}
|
|
||||||
let opt = JSON.parse(options);
|
|
||||||
if (text == null) {
|
|
||||||
text = "";
|
|
||||||
} else {
|
|
||||||
text = text.toString();
|
|
||||||
}
|
|
||||||
opt.text = text;
|
|
||||||
opt.textxalign = opt.textxalign || "center";
|
|
||||||
|
|
||||||
bwipjs.toCanvas(canvas, opt);
|
Handlebars.registerHelper("ayJSON", function (obj) {
|
||||||
var url = canvas.toDataURL("image/png");
|
return JSON.stringify(obj, null, 3);
|
||||||
return new Handlebars.SafeString("<img src='" + url + "'/>");
|
});
|
||||||
});
|
|
||||||
|
Handlebars.registerHelper("ayLink", function (text, url) {
|
||||||
|
var url = Handlebars.escapeExpression(url),
|
||||||
|
text = Handlebars.escapeExpression(text);
|
||||||
|
|
||||||
|
return new Handlebars.SafeString(
|
||||||
|
"<a href='" + url + "'>" + text + "</a>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper("ayLogo", function (size) {
|
||||||
|
if (AYMETA.ayServerMetaData) {
|
||||||
|
switch (size) {
|
||||||
|
case "small":
|
||||||
|
if (!AYMETA.ayServerMetaData.HasSmallLogo) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "medium":
|
||||||
|
if (!AYMETA.ayServerMetaData.HasMediumLogo) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "large":
|
||||||
|
if (!AYMETA.ayServerMetaData.HasLargeLogo) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var url = `${Handlebars.escapeExpression(
|
||||||
|
AYMETA.ayServerMetaData.ayApiUrl
|
||||||
|
)}logo/${size}`;
|
||||||
|
return new Handlebars.SafeString("<img src='" + url + "'/>");
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper("ayT", function (translationKey) {
|
||||||
|
if (ayTranslationKeyCache[translationKey] == undefined) {
|
||||||
|
throw `ayT reporting helper error: the key "${translationKey}" is not present in the translation cache, did you forget to include it in your call to "await ayGetTranslations(['ExampleTranslationKey1','ExampleTranslationKey2','etc']);" in ayPrepareData()\nTranslationKeyCache contains: ${JSON.stringify(
|
||||||
|
ayTranslationKeyCache,
|
||||||
|
null,
|
||||||
|
3
|
||||||
|
)}?`;
|
||||||
|
// return translationKey;
|
||||||
|
}
|
||||||
|
return ayTranslationKeyCache[translationKey];
|
||||||
|
});
|
||||||
|
|
||||||
|
///////////////////////////////////////////
|
||||||
|
// BarCode helper using
|
||||||
|
// https://github.com/metafloor/bwip-js#browser-usage
|
||||||
|
//
|
||||||
|
Handlebars.registerHelper("ayBC", function (text, options) {
|
||||||
|
let canvas = document.getElementById("aybarcode");
|
||||||
|
if (canvas == null) {
|
||||||
|
canvas = document.createElement("canvas");
|
||||||
|
canvas.id = "aybarcode";
|
||||||
|
}
|
||||||
|
let opt = JSON.parse(options);
|
||||||
|
if (text == null) {
|
||||||
|
text = "";
|
||||||
|
} else {
|
||||||
|
text = text.toString();
|
||||||
|
}
|
||||||
|
opt.text = text;
|
||||||
|
opt.textxalign = opt.textxalign || "center";
|
||||||
|
|
||||||
|
bwipjs.toCanvas(canvas, opt);
|
||||||
|
var url = canvas.toDataURL("image/png");
|
||||||
|
return new Handlebars.SafeString("<img src='" + url + "'/>");
|
||||||
|
});
|
||||||
} //eof
|
} //eof
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
// Concat helper using
|
// Concat helper using
|
||||||
// https://stackoverflow.com/a/52571635/8939
|
// https://stackoverflow.com/a/52571635/8939
|
||||||
//
|
//
|
||||||
Handlebars.registerHelper('ayConcat', function() {
|
Handlebars.registerHelper("ayConcat", function () {
|
||||||
arguments = [...arguments].slice(0, -1);
|
arguments = [...arguments].slice(0, -1);
|
||||||
return arguments.join('');
|
return arguments.join("");
|
||||||
});
|
});
|
||||||
|
|
||||||
//##################################### LOCALIZATION & TRANSLATION ###################################################
|
//##################################### LOCALIZATION & TRANSLATION ###################################################
|
||||||
@@ -151,78 +151,81 @@ Handlebars.registerHelper('ayConcat', function() {
|
|||||||
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
||||||
//
|
//
|
||||||
function utcDateToShortDateAndTimeLocalized(ayValue) {
|
function utcDateToShortDateAndTimeLocalized(ayValue) {
|
||||||
if (!ayValue) {
|
if (!ayValue) {
|
||||||
return "";
|
return "";
|
||||||
}
|
|
||||||
|
|
||||||
//parse the date which is identified as utc ("2020-02-06T18:18:49.148011Z")
|
|
||||||
let parsedDate = new Date(ayValue);
|
|
||||||
|
|
||||||
//is it a valid date?
|
|
||||||
if (!(parsedDate instanceof Date && !isNaN(parsedDate))) {
|
|
||||||
return "not valid";
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedDate.toLocaleString(
|
|
||||||
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
|
||||||
{
|
|
||||||
timeZone: AYMETA.ayClientMetaData.TimeZoneName || "America/Winnipeg",
|
|
||||||
dateStyle: "short",
|
|
||||||
timeStyle: "short",
|
|
||||||
hour12: AYMETA.ayClientMetaData.Hour12
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
//parse the date which is identified as utc ("2020-02-06T18:18:49.148011Z")
|
||||||
|
let parsedDate = new Date(ayValue);
|
||||||
|
|
||||||
|
//is it a valid date?
|
||||||
|
if (!(parsedDate instanceof Date && !isNaN(parsedDate))) {
|
||||||
|
return "not valid";
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedDate.toLocaleString(
|
||||||
|
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
||||||
|
{
|
||||||
|
timeZone:
|
||||||
|
AYMETA.ayClientMetaData.TimeZoneName || "America/Winnipeg",
|
||||||
|
dateStyle: "short",
|
||||||
|
timeStyle: "short",
|
||||||
|
hour12: AYMETA.ayClientMetaData.Hour12
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
// Turn a utc date into a displayable
|
// Turn a utc date into a displayable
|
||||||
// short date
|
// short date
|
||||||
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
||||||
function utcDateToShortDateLocalized(ayValue) {
|
function utcDateToShortDateLocalized(ayValue) {
|
||||||
if (!ayValue) {
|
if (!ayValue) {
|
||||||
return "";
|
return "";
|
||||||
}
|
|
||||||
|
|
||||||
//parse the date which is identified as utc ("2020-02-06T18:18:49.148011Z")
|
|
||||||
let parsedDate = new Date(ayValue);
|
|
||||||
|
|
||||||
//is it a valid date?
|
|
||||||
if (!(parsedDate instanceof Date && !isNaN(parsedDate))) {
|
|
||||||
return "not valid";
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedDate.toLocaleDateString(
|
|
||||||
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
|
||||||
{
|
|
||||||
timeZone: AYMETA.ayClientMetaData.TimeZoneName || "America/Winnipeg",
|
|
||||||
dateStyle: "short"
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
//parse the date which is identified as utc ("2020-02-06T18:18:49.148011Z")
|
||||||
|
let parsedDate = new Date(ayValue);
|
||||||
|
|
||||||
|
//is it a valid date?
|
||||||
|
if (!(parsedDate instanceof Date && !isNaN(parsedDate))) {
|
||||||
|
return "not valid";
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedDate.toLocaleDateString(
|
||||||
|
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
||||||
|
{
|
||||||
|
timeZone:
|
||||||
|
AYMETA.ayClientMetaData.TimeZoneName || "America/Winnipeg",
|
||||||
|
dateStyle: "short"
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
// Turn a utc date into a displayable
|
// Turn a utc date into a displayable
|
||||||
// short time
|
// short time
|
||||||
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
||||||
function utcDateToShortTimeLocalized(ayValue) {
|
function utcDateToShortTimeLocalized(ayValue) {
|
||||||
if (!ayValue) {
|
if (!ayValue) {
|
||||||
return "";
|
return "";
|
||||||
}
|
|
||||||
|
|
||||||
//parse the date which is identified as utc ("2020-02-06T18:18:49.148011Z")
|
|
||||||
let parsedDate = new Date(ayValue);
|
|
||||||
|
|
||||||
//is it a valid date?
|
|
||||||
if (!(parsedDate instanceof Date && !isNaN(parsedDate))) {
|
|
||||||
return "not valid";
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedDate.toLocaleTimeString(
|
|
||||||
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
|
||||||
{
|
|
||||||
timeZone: AYMETA.ayClientMetaData.TimeZoneName || "America/Winnipeg",
|
|
||||||
timeStyle: "short",
|
|
||||||
hour12: AYMETA.ayClientMetaData.Hour12
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
//parse the date which is identified as utc ("2020-02-06T18:18:49.148011Z")
|
||||||
|
let parsedDate = new Date(ayValue);
|
||||||
|
|
||||||
|
//is it a valid date?
|
||||||
|
if (!(parsedDate instanceof Date && !isNaN(parsedDate))) {
|
||||||
|
return "not valid";
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedDate.toLocaleTimeString(
|
||||||
|
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
||||||
|
{
|
||||||
|
timeZone:
|
||||||
|
AYMETA.ayClientMetaData.TimeZoneName || "America/Winnipeg",
|
||||||
|
timeStyle: "short",
|
||||||
|
hour12: AYMETA.ayClientMetaData.Hour12
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
@@ -230,16 +233,16 @@ function utcDateToShortTimeLocalized(ayValue) {
|
|||||||
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
||||||
//
|
//
|
||||||
function currencyLocalized(ayValue) {
|
function currencyLocalized(ayValue) {
|
||||||
if (!ayValue) {
|
if (!ayValue) {
|
||||||
return "";
|
return "";
|
||||||
}
|
|
||||||
return new Intl.NumberFormat(
|
|
||||||
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
|
||||||
{
|
|
||||||
style: "currency",
|
|
||||||
currency: AYMETA.ayClientMetaData.CurrencyName || "USD"
|
|
||||||
}
|
}
|
||||||
).format(ayValue);
|
return new Intl.NumberFormat(
|
||||||
|
AYMETA.ayClientMetaData.LanguageName || "en-US",
|
||||||
|
{
|
||||||
|
style: "currency",
|
||||||
|
currency: AYMETA.ayClientMetaData.CurrencyName || "USD"
|
||||||
|
}
|
||||||
|
).format(ayValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
@@ -247,12 +250,12 @@ function currencyLocalized(ayValue) {
|
|||||||
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
//(PRIVATE NOT DOCUMENTED, FOR HELPER USE)
|
||||||
//
|
//
|
||||||
function decimalLocalized(ayValue) {
|
function decimalLocalized(ayValue) {
|
||||||
if (!ayValue) {
|
if (!ayValue) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return new Intl.NumberFormat(
|
return new Intl.NumberFormat(
|
||||||
AYMETA.ayClientMetaData.LanguageName || "en-US"
|
AYMETA.ayClientMetaData.LanguageName || "en-US"
|
||||||
).format(ayValue);
|
).format(ayValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
@@ -264,99 +267,133 @@ var ayTranslationKeyCache = {};
|
|||||||
// GET TRANSLATIONS FROM API SERVER
|
// GET TRANSLATIONS FROM API SERVER
|
||||||
//
|
//
|
||||||
async function ayGetTranslations(keys) {
|
async function ayGetTranslations(keys) {
|
||||||
if (!keys || keys.length == 0) {
|
if (!keys || keys.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let transData = await ayPostToAPI("translation/subset", keys);
|
let transData = await ayPostToAPI("translation/subset", keys);
|
||||||
transData.data.forEach(function storeFetchedTranslationItemsInCache(item) {
|
transData.data.forEach(function storeFetchedTranslationItemsInCache(
|
||||||
ayTranslationKeyCache[item.key] = item.value;
|
item
|
||||||
});
|
) {
|
||||||
} catch (error) {
|
ayTranslationKeyCache[item.key] = item.value;
|
||||||
//fundamental error, can't proceed with this call
|
});
|
||||||
// handleError("GET", error, route);
|
} catch (error) {
|
||||||
//todo: deal with this properly
|
//fundamental error, can't proceed with this call
|
||||||
throw error;
|
// handleError("GET", error, route);
|
||||||
}
|
//todo: deal with this properly
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//##################################### API UTILITIES ###################################################
|
//##################################### API UTILITIES ###################################################
|
||||||
|
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
// GET DATA FROM API SERVER
|
// GET DATA FROM API SERVER
|
||||||
//
|
//
|
||||||
async function ayGetFromAPI(route, token) {
|
async function ayGetFromAPI(route, token) {
|
||||||
token = token || AYMETA.ayClientMetaData.Authorization;
|
token = token || AYMETA.ayClientMetaData.Authorization;
|
||||||
if (route && !route.startsWith("http")) {
|
if (route && !route.startsWith("http")) {
|
||||||
route = AYMETA.ayServerMetaData.ayApiUrl + route;
|
route = AYMETA.ayServerMetaData.ayApiUrl + route;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let r = await fetch(route, {
|
let r = await fetch(route, {
|
||||||
method: "get",
|
method: "get",
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: token
|
Authorization: token
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return await r.json();
|
return await r.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//fundamental error, can't proceed with this call
|
//fundamental error, can't proceed with this call
|
||||||
// handleError("GET", error, route);
|
// handleError("GET", error, route);
|
||||||
//todo: deal with this properly
|
//todo: deal with this properly
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
// POST DATA TO API SERVER
|
// POST DATA TO API SERVER
|
||||||
//
|
//
|
||||||
async function ayPostToAPI(route, data, token) {
|
async function ayPostToAPI(route, data, token) {
|
||||||
token = token || AYMETA.ayClientMetaData.Authorization;
|
token = token || AYMETA.ayClientMetaData.Authorization;
|
||||||
if (route && !route.startsWith("http")) {
|
if (route && !route.startsWith("http")) {
|
||||||
route = AYMETA.ayServerMetaData.ayApiUrl + route;
|
route = AYMETA.ayServerMetaData.ayApiUrl + route;
|
||||||
}
|
}
|
||||||
try {
|
//api expects custom fields to be a string not an object
|
||||||
fetchOptions = {
|
if (data && data.CustomFields && data.CustomFields.constructor === Object) {
|
||||||
method: "post",
|
data.CustomFields = JSON.stringify(data.CustomFields);
|
||||||
mode: "cors",
|
}
|
||||||
headers: {
|
try {
|
||||||
Accept: "application/json",
|
fetchOptions = {
|
||||||
"Content-Type": "application/json",
|
method: "post",
|
||||||
Authorization: token
|
mode: "cors",
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify(data)
|
Accept: "application/json",
|
||||||
};
|
"Content-Type": "application/json",
|
||||||
let r = await fetch(route, fetchOptions);
|
Authorization: token
|
||||||
return await r.json();
|
},
|
||||||
} catch (error) {
|
body: JSON.stringify(data)
|
||||||
//todo: better handle this
|
};
|
||||||
throw error;
|
let r = await fetch(route, fetchOptions);
|
||||||
}
|
return await r.json();
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
// PUT DATA TO API SERVER
|
||||||
|
//
|
||||||
|
async function ayPutToAPI(route, data, token) {
|
||||||
|
token = token || AYMETA.ayClientMetaData.Authorization;
|
||||||
|
if (route && !route.startsWith("http")) {
|
||||||
|
route = AYMETA.ayServerMetaData.ayApiUrl + route;
|
||||||
|
}
|
||||||
|
|
||||||
|
//api expects custom fields to be a string not an object
|
||||||
|
if (data && data.CustomFields && data.CustomFields.constructor === Object) {
|
||||||
|
data.CustomFields = JSON.stringify(data.CustomFields);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
fetchOptions = {
|
||||||
|
method: "put",
|
||||||
|
mode: "cors",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: token
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
};
|
||||||
|
let r = await fetch(route, fetchOptions);
|
||||||
|
return await r.json();
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// attachment download URL
|
// attachment download URL
|
||||||
// (PRIVATE NOT DOCUMENTED FOR HELPER USE)
|
// (PRIVATE NOT DOCUMENTED FOR HELPER USE)
|
||||||
function attachmentDownloadUrl(fileId, ctype) {
|
function attachmentDownloadUrl(fileId, ctype) {
|
||||||
//http://localhost:7575/api/v8/attachment/download/100?t=sssss
|
//http://localhost:7575/api/v8/attachment/download/100?t=sssss
|
||||||
//Ctype is optional and is the MIME content type, used to detect image urls at client for drag and drop ops
|
//Ctype is optional and is the MIME content type, used to detect image urls at client for drag and drop ops
|
||||||
//in wiki but ignored by server
|
//in wiki but ignored by server
|
||||||
|
|
||||||
let url =
|
let url =
|
||||||
"attachment/download/" +
|
"attachment/download/" +
|
||||||
fileId +
|
fileId +
|
||||||
"?t=" +
|
"?t=" +
|
||||||
AYMETA.ayClientMetaData.DownloadToken;
|
AYMETA.ayClientMetaData.DownloadToken;
|
||||||
|
|
||||||
if (ctype && ctype.includes("image")) {
|
if (ctype && ctype.includes("image")) {
|
||||||
url += "&i=1";
|
url += "&i=1";
|
||||||
}
|
}
|
||||||
|
|
||||||
return AYMETA.ayServerMetaData.ayApiUrl + url;
|
return AYMETA.ayServerMetaData.ayApiUrl + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
//##################################### CODE UTILITIES ###################################################
|
//##################################### CODE UTILITIES ###################################################
|
||||||
@@ -370,23 +407,29 @@ function attachmentDownloadUrl(fileId, ctype) {
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
function ayGroupByKey(reportDataArray, groupByKeyName) {
|
function ayGroupByKey(reportDataArray, groupByKeyName) {
|
||||||
//array to hold grouped data
|
//array to hold grouped data
|
||||||
const ret = [];
|
const ret = [];
|
||||||
//iterate through the raw reprot data
|
//iterate through the raw reprot data
|
||||||
for (let i = 0; i < reportDataArray.length; i++) {
|
for (let i = 0; i < reportDataArray.length; i++) {
|
||||||
//search the ret array for a group with this name and if found return a reference to that group object
|
//search the ret array for a group with this name and if found return a reference to that group object
|
||||||
let groupObject = ret.find(z => z.group == reportDataArray[i][groupByKeyName]);
|
let groupObject = ret.find(
|
||||||
if (groupObject != undefined) {
|
(z) => z.group == reportDataArray[i][groupByKeyName]
|
||||||
//there is already a matching group in the return array so just push this raw report data record into it
|
);
|
||||||
groupObject.items.push(reportDataArray[i]);
|
if (groupObject != undefined) {
|
||||||
//update the count for this group's items
|
//there is already a matching group in the return array so just push this raw report data record into it
|
||||||
groupObject.count++;
|
groupObject.items.push(reportDataArray[i]);
|
||||||
} else {
|
//update the count for this group's items
|
||||||
//No group yet, so start a new one in the ret array and push this raw report data record
|
groupObject.count++;
|
||||||
ret.push({ group: reportDataArray[i][groupByKeyName], items: [reportDataArray[i]], count: 1 });
|
} else {
|
||||||
}
|
//No group yet, so start a new one in the ret array and push this raw report data record
|
||||||
}
|
ret.push({
|
||||||
return ret;
|
group: reportDataArray[i][groupByKeyName],
|
||||||
|
items: [reportDataArray[i]],
|
||||||
|
count: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// //Utils
|
// //Utils
|
||||||
@@ -395,4 +438,3 @@ function ayGroupByKey(reportDataArray, groupByKeyName) {
|
|||||||
// n = n + "";
|
// n = n + "";
|
||||||
// return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
|
// return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user