Home.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import Page from "./Page.js";
  2. import Elem from "../Elem.js";
  3. import Format from "../Format.js";
  4. import Notifier from "../Notifier.js";
  5. import Transaction from "../data/Transaction.js";
  6. export default class Home extends Page{
  7. constructor(){
  8. super("Home", ["addMenu", "viewMenu", "logout"]);
  9. this.incomeTotal = user.account.incomeTotal();
  10. const date = new Date();
  11. const month = date.toLocaleString("en-US", {month: "long"});
  12. if(!user.account.isPopulated){
  13. let from = new Date();
  14. from.setDate(1);
  15. from = Format.transactionDate(from);
  16. let to = new Date();
  17. to = Format.transactionDate(to);
  18. Transaction.fetch(user.account.id, from, to)
  19. .then((transactions)=>{
  20. if(transactions.length > 0) user.account.addManyTransactions(transactions);
  21. user.account.isPopulated = true;
  22. this.renderData();
  23. })
  24. .catch((err)=>{
  25. new Notifier("error", "Unable to retrieve transactions");
  26. });
  27. }
  28. this.render(month);
  29. }
  30. submitOnEnter(event){
  31. if(event.key === "Enter"){
  32. user.account.balance = Format.dollarsToCents(this.container.querySelector(".balance input").value);
  33. user.account.save();
  34. new Elem(this.container.querySelector(".balance dd"))
  35. .clear()
  36. .text(Format.currency(user.account.balance));
  37. }
  38. }
  39. editBalance(){
  40. this.balanceContainer.removeOnclick();
  41. new Elem(this.container.querySelector(".balance dd"))
  42. .text("")
  43. .append(new Elem("input")
  44. .type("number")
  45. .step("0.01")
  46. .value(user.account.balance / 100)
  47. .onkeydown(this.submitOnEnter.bind(this))
  48. .focus()
  49. );
  50. }
  51. generateColor(spent, amount){
  52. if(amount <= 0) return "hsl(0, 100%, 50%)";
  53. const percent = Math.min(spent / amount, 1);
  54. let hue;
  55. if(percent < 0.5){
  56. hue = percent / 0.5 * 45;
  57. }else{
  58. hue = 45 + (percent - 0.5) / 0.5 * (120 -45);
  59. }
  60. const saturation = 65 - percent * 15;
  61. const lightness = 28 + (1 - Math.abs(percent - 0.5) * 2) * 10;
  62. return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
  63. }
  64. renderData(){
  65. this.incomeTotal = user.account.incomeTotal();
  66. this.container.querySelector(".discretionary").replaceWith(this.createDiscretionary().get());
  67. this.container.querySelector(".incomeDisplay").textContent = Format.currency(this.incomeTotal);
  68. this.container.querySelector(".billsDisplay").textContent = Format.currency(user.account.billsTotal())
  69. this.renderAllowances();
  70. }
  71. createDiscretionary(){
  72. const discretionary = user.account.getDiscretionary();
  73. const remaining = discretionary - user.account.getDiscretionarySpent();
  74. return new Elem("dl")
  75. .addClass("discretionary")
  76. .append(new Elem("dt")
  77. .text("Remaining Discretionary:")
  78. )
  79. .append(new Elem("dd")
  80. .append(new Elem("span")
  81. .text(Format.currency(remaining))
  82. .addStyle("color", this.generateColor(remaining, discretionary))
  83. )
  84. .append(new Elem("span")
  85. .addClass("amountFull")
  86. .text(` / ${Format.currency(discretionary)}`)
  87. )
  88. );
  89. }
  90. renderAllowances(){
  91. const allowances = new Elem("div")
  92. .addClass("allowances");
  93. for(let i = 0; i < user.account.allowances.length; i++){
  94. const a = user.account.allowances[i];
  95. const spent = user.account.categorySpent(a);
  96. const total = a.currencyAmount(this.incomeTotal);
  97. new Elem("dl")
  98. .addClass(a.id)
  99. .append(new Elem("dt")
  100. .text(`${a.name}: `)
  101. )
  102. .append(new Elem("dd")
  103. .append(new Elem("span")
  104. .text(Format.currency(spent))
  105. .addStyle("color", this.generateColor(spent, total))
  106. )
  107. .append(new Elem("span")
  108. .text(` / ${Format.currency(total)}`)
  109. .addClass("amountFull")
  110. )
  111. )
  112. .appendTo(allowances);
  113. const existingContainer = this.container.querySelector(".allowances");
  114. if(existingContainer){
  115. existingContainer.replaceWith(allowances.get());
  116. }else{
  117. this.container.appendChild(allowances.get());
  118. }
  119. }
  120. }
  121. render(month){
  122. new Elem("h1")
  123. .text(month)
  124. .appendTo(this.container);
  125. this.balanceContainer = new Elem("dl")
  126. .addClass("balance")
  127. .append(new Elem("dt")
  128. .text("Balance: ")
  129. )
  130. .append(new Elem("dd")
  131. .text(Format.currency(user.account.balance))
  132. )
  133. .onclick(this.editBalance.bind(this))
  134. .appendTo(this.container);
  135. new Elem("dl")
  136. .append(new Elem("dt")
  137. .text("Income: ")
  138. )
  139. .append(new Elem("dd")
  140. .addClass("incomeDisplay")
  141. .text(Format.currency(this.incomeTotal))
  142. )
  143. .appendTo(this.container);
  144. new Elem("dl")
  145. .append(new Elem("dt")
  146. .text("Bills: ")
  147. )
  148. .append(new Elem("dd")
  149. .addClass("billsDisplay")
  150. .text(Format.currency(user.account.billsTotal()))
  151. )
  152. .appendTo(this.container);
  153. this.createDiscretionary().appendTo(this.container);
  154. new Elem("h3").text("Allowances:").appendTo(this.container);
  155. this.renderAllowances();
  156. }
  157. }