analytics.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. let analytics = {
  2. isPopulated: false,
  3. ingredient: undefined,
  4. recipe: undefined,
  5. transactionsByDate: [],
  6. display: function(Transaction){
  7. if(!this.isPopulated){
  8. document.getElementById("analRecipeContent").style.display = "none";
  9. let to = new Date()
  10. let from = new Date(to.getFullYear(), to.getMonth() - 1, to.getDate());
  11. document.getElementById("analStartDate").valueAsDate = from;
  12. document.getElementById("analEndDate").valueAsDate = to;
  13. let analSlider = document.getElementById("analSlider");
  14. analSlider.onclick = ()=>{this.switchDisplay()};
  15. analSlider.checked = false;
  16. document.getElementById("analDateBtn").onclick = ()=>{this.newDates(Transaction)};
  17. this.populateButtons();
  18. if(merchant.ingredients.length > 0){
  19. this.ingredient = merchant.ingredients[0].ingredient;
  20. }
  21. if(merchant.recipes.length > 0){
  22. this.recipe = merchant.recipes[0];
  23. }
  24. this.newDates(Transaction);
  25. this.isPopulated = true;
  26. }
  27. },
  28. populateButtons: function(){
  29. let ingredientButtons = document.getElementById("analIngredientList");
  30. let recipeButtons = document.getElementById("analRecipeList");
  31. while(ingredientButtons.children.length > 0){
  32. ingredientButtons.removeChild(ingredientButtons.firstChild);
  33. }
  34. for(let i = 0; i < merchant.ingredients.length; i++){
  35. let button = document.createElement("button");
  36. button.innerText = merchant.ingredients[i].ingredient.name;
  37. button.classList.add("choosable");
  38. button.onclick = ()=>{
  39. this.ingredient = merchant.ingredients[i].ingredient;
  40. this.displayIngredient();
  41. };
  42. ingredientButtons.appendChild(button);
  43. }
  44. while(recipeButtons.children.length > 0){
  45. recipeButtons.removeChild(recipeButtons.firstChild);
  46. }
  47. for(let i = 0; i < merchant.recipes.length; i++){
  48. let button = document.createElement("button");
  49. button.innerText = merchant.recipes[i].name;
  50. button.classList.add("choosable");
  51. button.onclick = ()=>{
  52. this.recipe = merchant.recipes[i];
  53. this.displayRecipe();
  54. };
  55. recipeButtons.appendChild(button);
  56. }
  57. },
  58. getData: function(from, to, Transaction){
  59. console.log(from);
  60. console.log(to);
  61. let data = {
  62. from: from,
  63. to: to,
  64. recipes: []
  65. }
  66. let loader = document.getElementById("loaderContainer");
  67. loader.style.display = "flex";
  68. return fetch("/transaction", {
  69. method: "post",
  70. headers: {
  71. "Content-Type": "application/json;charset=utf-8"
  72. },
  73. body: JSON.stringify(data)
  74. })
  75. .then(response => response.json())
  76. .then((response)=>{
  77. if(typeof(response) === "string"){
  78. controller.createBanner(response, "error");
  79. }else{
  80. console.log(response);
  81. this.transactionsByDate = [];
  82. let startOfDay = new Date(from.getTime());
  83. startOfDay.setHours(0, 0, 0, 0);
  84. let endOfDay = new Date(from.getTime());
  85. endOfDay.setDate(endOfDay.getDate() + 1);
  86. endOfDay.setHours(0, 0, 0, 0);
  87. let transactionIndex = 0;
  88. while(startOfDay <= to){
  89. let currentTransactions = [];
  90. while(transactionIndex < response.length && new Date(response[transactionIndex].date) < endOfDay){
  91. currentTransactions.push(new Transaction(
  92. response[transactionIndex]._id,
  93. response[transactionIndex].date,
  94. response[transactionIndex].recipes,
  95. merchant
  96. ));
  97. transactionIndex++;
  98. }
  99. let thing = {
  100. date: new Date(startOfDay.getTime()),
  101. transactions: currentTransactions
  102. };
  103. this.transactionsByDate.push(thing);
  104. startOfDay.setDate(startOfDay.getDate() + 1);
  105. endOfDay.setDate(endOfDay.getDate() + 1);
  106. }
  107. console.log(this.transactionsByDate);
  108. }
  109. })
  110. .catch((err)=>{
  111. console.log(err);
  112. controller.createBanner("UNABLE TO UPDATE THE PAGE", "error");
  113. })
  114. .finally(()=>{
  115. loader.style.display = "none";
  116. });
  117. },
  118. displayIngredient: function(){
  119. if(this.ingredient === undefined || this.transactionsByDate.length === 0){
  120. return;
  121. }
  122. //break down data into dates and quantities
  123. let dates = [];
  124. let quantities = [];
  125. for(let i = 0; i < this.transactionsByDate.length; i++){
  126. dates.push(this.transactionsByDate[i].date);
  127. let sum = 0;
  128. for(let j = 0; j < this.transactionsByDate[i].transactions.length; j++){
  129. let transaction = this.transactionsByDate[i].transactions[j];
  130. sum += transaction.getIngredientQuantity(this.ingredient);
  131. }
  132. quantities.push(sum);
  133. }
  134. //create and display the graph
  135. let trace = {
  136. x: dates,
  137. y: quantities,
  138. mode: "lines+markers",
  139. line: {
  140. color: "rgb(255, 99, 107)"
  141. }
  142. }
  143. let yaxis = `QUANTITY (${this.ingredient.unit.toUpperCase()})`;
  144. const layout = {
  145. title: this.ingredient.name.toUpperCase(),
  146. xaxis: {title: "DATE"},
  147. yaxis: {title: yaxis}
  148. }
  149. Plotly.newPlot("itemUseGraph", [trace], layout);
  150. //Create min/max/avg
  151. //Current ingredient is stored on the "analMinUse" element
  152. let min = quantities[0];
  153. let max = quantities[0];
  154. let sum = 0;
  155. for(let i = 0; i < quantities.length; i++){
  156. if(quantities[i] < min){
  157. min = quantities[i];
  158. }
  159. if(quantities[i] > max){
  160. max = quantities[i];
  161. }
  162. sum += quantities[i];
  163. }
  164. document.getElementById("analMinUse").innerText = `${min.toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  165. document.getElementById("analAvgUse").innerText = `${(sum / quantities.length).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  166. document.getElementById("analMaxUse").innerText = `${max.toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  167. //Create weekday averages
  168. let dayUse = [0, 0, 0, 0, 0, 0, 0];
  169. let dayCount = [0, 0, 0, 0, 0, 0, 0];
  170. for(let i = 0; i < quantities.length; i++){
  171. dayUse[dates[i].getDay()] += quantities[i];
  172. dayCount[dates[i].getDay()]++;
  173. }
  174. document.getElementById("analDayOne").innerText = `${(dayUse[0] / dayCount[0]).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  175. document.getElementById("analDayTwo").innerText = `${(dayUse[1] / dayCount[1]).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  176. document.getElementById("analDayThree").innerText = `${(dayUse[2] / dayCount[2]).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  177. document.getElementById("analDayFour").innerText = `${(dayUse[3] / dayCount[3]).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  178. document.getElementById("analDayFive").innerText = `${(dayUse[4] / dayCount[4]).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  179. document.getElementById("analDaySix").innerText = `${(dayUse[5] / dayCount[5]).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  180. document.getElementById("analDaySeven").innerText = `${(dayUse[6] / dayCount[6]).toFixed(2)} ${this.ingredient.unit.toUpperCase()}`;
  181. },
  182. displayRecipe: function(){
  183. if(this.recipe === undefined || this.transactionsByDate.length === 0){
  184. return;
  185. }
  186. //break down data into dates and quantities
  187. let dates = [];
  188. let quantities = [];
  189. for(let i = 0; i < this.transactionsByDate.length; i++){
  190. dates.push(this.transactionsByDate[i].date);
  191. let sum = 0;
  192. for(let j = 0; j < this.transactionsByDate[i].transactions.length; j++){
  193. const transaction = this.transactionsByDate[i].transactions[j];
  194. for(let k = 0; k < transaction.recipes.length; k++){
  195. if(transaction.recipes[k].recipe === this.recipe){
  196. sum += transaction.recipes[k].quantity;
  197. }
  198. }
  199. }
  200. quantities.push(sum);
  201. }
  202. //create and display the graph
  203. const trace = {
  204. x: dates,
  205. y: quantities,
  206. mode: "lines+markers",
  207. line: {
  208. color: "rgb(255, 99, 107)"
  209. }
  210. }
  211. const layout = {
  212. title: this.recipe.name.toUpperCase(),
  213. xaxis: {title: "DATE"},
  214. yaxis: {title: "QUANTITY"}
  215. }
  216. Plotly.newPlot("recipeSalesGraph", [trace], layout);
  217. //Display the boxes at the bottom
  218. //Current recipe is stored on the "recipeAvgUse" element
  219. let avg = 0;
  220. for(let i = 0; i < quantities.length; i++){
  221. avg += quantities[i];
  222. }
  223. avg = avg / quantities.length;
  224. document.getElementById("recipeAvgUse").innerText = avg.toFixed(2);
  225. document.getElementById("recipeAvgRevenue").innerText = `$${(avg * this.recipe.price).toFixed(2)}`;
  226. },
  227. switchDisplay: function(){
  228. const checkbox = document.getElementById("analSlider");
  229. let ingredient = document.getElementById("analIngredientContent");
  230. let recipe = document.getElementById("analRecipeContent");
  231. if(checkbox.checked === true){
  232. ingredient.style.display = "none";
  233. recipe.style.display = "flex";
  234. this.displayRecipe();
  235. }else{
  236. ingredient.style.display = "flex";
  237. recipe.style.display = "none";
  238. this.displayIngredient();
  239. }
  240. },
  241. newDates: async function(Transaction){
  242. const from = document.getElementById("analStartDate").valueAsDate;
  243. const to = document.getElementById("analEndDate").valueAsDate;
  244. from.setHours(0, 0, 0, 0);
  245. to.setDate(to.getDate() + 1);
  246. to.setHours(0, 0, 0, 0);
  247. await this.getData(from, to, Transaction);
  248. if(document.getElementById("analSlider").checked === true){
  249. this.displayRecipe();
  250. }else{
  251. this.displayIngredient();
  252. }
  253. }
  254. }
  255. module.exports = analytics;