analytics.js 9.7 KB

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