github.com/shoshinnikita/budget-manager@v0.7.1-0.20220131195411-8c46ff1c6778/templates/months.html (about) 1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>{{ .YearInterval }} Overview | Budget Manager</title> 8 9 <!-- Theme Switcher --> 10 <script src="{{ asStaticURL `/static/js/theme-switcher.js` }}"></script> 11 12 <link rel="stylesheet" href="{{ asStaticURL `/static/css/common.css` }}"> 13 14 <style> 15 /* | App */ 16 17 #content { 18 display: grid; 19 grid-template-rows: min-content auto; 20 row-gap: 50px; 21 } 22 23 /* || Months */ 24 25 #year { 26 display: grid; 27 grid-template-columns: min-content auto min-content; 28 } 29 30 .switch-year-btn { 31 transform: translateX(-10px); 32 width: 45px; 33 } 34 35 #next-year-btn { 36 transform: translateX(10px) rotate(180deg); 37 } 38 39 .switch-year-btn .feather-icon { 40 height: 100%; 41 padding: 0; 42 } 43 44 .switch-year-btn .feather-icon>svg { 45 height: 100%; 46 width: 100%; 47 } 48 49 #months-list { 50 display: grid; 51 grid-template-columns: repeat(6, 1fr); 52 column-gap: 20px; 53 row-gap: 20px; 54 } 55 56 .month { 57 padding: 10px 10px 20px; 58 } 59 60 .month.disabled { 61 cursor: default; 62 opacity: 0.5; 63 } 64 65 .month.disabled .month__results { 66 opacity: 0; 67 } 68 69 .month__header { 70 font-size: 18px; 71 } 72 73 .month__results { 74 display: grid; 75 grid-template-columns: min-content max-content; 76 column-gap: 10px; 77 margin-top: 7px; 78 } 79 80 .month__result { 81 text-align: right; 82 } 83 84 /* || Year Overview */ 85 86 #year-overview { 87 column-gap: 20px; 88 display: grid; 89 grid-template-columns: min-content auto; 90 height: 100%; 91 margin-left: 45px; 92 max-height: 400px; 93 } 94 95 #year-overview__results { 96 height: min-content; 97 } 98 99 #year-overview__results .card__body { 100 column-gap: 10px; 101 display: grid; 102 grid-template-columns: max-content max-content; 103 font-size: 18px; 104 height: min-content; 105 padding-left: 10px; 106 padding-right: 30px; 107 } 108 109 .year-overview__result { 110 text-align: right; 111 } 112 113 #year-overview__chart__wrapper { 114 position: relative; 115 /* 116 There's a bug when width and height are 100%. Div isn't decreasing width and height if we resize screen. 117 So, use 99% instead. 118 */ 119 height: 99%; 120 width: 99%; 121 } 122 123 /* | Layouts */ 124 125 @media (max-width: 1350px) { 126 127 .switch-year-btn { 128 transform: translateX(-5px); 129 width: 35px; 130 } 131 132 #next-year-btn { 133 transform: translateX(5px) rotate(180deg); 134 } 135 136 #months-list { 137 grid-template-columns: repeat(4, 1fr); 138 } 139 140 .month { 141 padding: 10px; 142 } 143 144 #year-overview { 145 margin-left: 35px; 146 } 147 148 #year-overview__results .card__title { 149 font-size: 18px; 150 } 151 152 #year-overview__results .card__body { 153 font-size: 16px; 154 } 155 } 156 157 @media (max-width: 1100px) { 158 159 .month__header { 160 font-size: 16px; 161 } 162 163 .month__results { 164 font-size: 14px; 165 } 166 } 167 </style> 168 </head> 169 170 <body> 171 <div id="app"> 172 <div id="header"> 173 <div> 174 <span class="header__path__element">{{ .YearInterval }} Overview</span> 175 </div> 176 </div> 177 178 <div id="content"> 179 <div id="year"> 180 <div id="prev-year-btn" class="switch-year-btn"> 181 <a href="/months?offset={{ call $.Add .Offset 1 }}" class="feather-icon"> 182 {{ template "components/icon" "chevron-left" }} 183 </a> 184 </div> 185 186 <div id="months-list"> 187 {{ range .Months }} 188 189 {{ $attributes := `class="month card disabled noselect" title="No data for the month"` }} 190 {{ if ne .ID 0 }} 191 {{ $attributes = (printf `href="/months/month?year=%d&month=%d" class="month card card--hover" title="Go to the month"` .Year .Month)}} 192 {{ end }} 193 194 <a {{ toHTMLAttr $attributes }}> 195 <div class="month__header">{{ .Month }} {{ .Year }}</div> 196 <div class="month__results"> 197 <div>Income:</div> 198 <div class="month__result money--gain">{{ .TotalIncome }}</div> 199 <div>Spend:</div> 200 <div class="month__result money--lose">{{ .TotalSpend }}</div> 201 <div>Result:</div> 202 <div class="month__result"> 203 {{ if ge .Result 0 }} 204 <span class="money--gain">{{ .Result }}</span> 205 {{ else }} 206 <span class="money--lose">{{ .Result }}</span> 207 {{ end }} 208 </div> 209 </div> 210 </a> 211 {{ end }} 212 </div> 213 214 <div id="next-year-btn" class="switch-year-btn"> 215 {{ $offset := call $.Add .Offset -1 }} 216 {{ if ge $offset 0 }} 217 <a href="/months?offset={{ call $.Add .Offset -1 }}" class="feather-icon"> 218 {{ template "components/icon" "chevron-left" }} 219 </a> 220 {{ else }} 221 <a class="feather-icon disabled" title="No more months"> 222 {{ template "components/icon" "chevron-left" }} 223 </a> 224 {{ end }} 225 </div> 226 </div> 227 228 <div id="year-overview"> 229 <div id="year-overview__results" class="card"> 230 <div class="card__title noselect">{{ .YearInterval }} Results</div> 231 232 <div class="card__body"> 233 <div>Total Income:</div> 234 <div class="year-overview__result money--gain">{{ .TotalIncome }}</div> 235 236 <div>Total Spend:</div> 237 <div class="year-overview__result money--lose">{{ .TotalSpend }}</div> 238 239 <div>Result:</div> 240 <div class="year-overview__result"> 241 {{ if ge .Result 0 }} 242 <span class="money--gain">{{ .Result }}</span> 243 {{ else }} 244 <span class="money--lose">{{ .Result }}</span> 245 {{ end }} 246 </div> 247 </div> 248 </div> 249 250 <div id="year-overview__chart__wrapper"> 251 <canvas id="year-overview__chart"></canvas> 252 </div> 253 </div> 254 </div> 255 256 {{ template "components/footer.html" .Footer }} 257 </div> 258 259 <script src="{{ asStaticURL `/static/vendor/chart.js/chart.min.js` }}"></script> 260 <script src="{{ asStaticURL `/static/js/chart.js` }}"></script> 261 262 <script> 263 const monthResults = JSON.parse(`{{ .Months }}`); 264 const monthNames = ["January", "February", "March", "April", "May", "June", 265 "July", "August", "September", "October", "November", "December"]; 266 267 // Create the chart 268 const yearOverviewCtx = document.getElementById("year-overview__chart").getContext("2d"); 269 const yearOverviewChart = new Chart(yearOverviewCtx, { 270 type: "line", 271 data: { 272 labels: monthResults.map(v => monthNames[v["month"] - 1]), 273 datasets: [ 274 // Incomes 275 { 276 data: monthResults.map(v => v["total_income"]), 277 borderColor: "green", 278 backgroundColor: "#00ff0018", // green with alpha 279 fill: true, 280 }, 281 // Spends 282 { 283 data: monthResults.map(v => -v["total_spend"]), 284 borderColor: "crimson", 285 backgroundColor: "#dc143c18", // crimson with alpha 286 fill: true, 287 } 288 ], 289 }, 290 options: { 291 // Change bezier curves 292 elements: { 293 line: { tension: 0.2 }, 294 point: { hitRadius: 5 } 295 }, 296 scales: { 297 y: { ticks: { callback: Chart.Ticks.formatters.money } } 298 } 299 } 300 }); 301 302 window.addEventListener(themeChangeEventName, () => { 303 const gridLinesColor = getGridLinesColor(); 304 305 // Update color of grid lines 306 const scales = yearOverviewChart.options.scales; 307 for (const axis of [scales.x, scales.y]) { 308 axis.grid.color = gridLinesColor 309 } 310 311 yearOverviewChart.update(); 312 }) 313 </script> 314 </body> 315 316 </html>