mirror of
				https://github.com/KevinMidboe/brewPi.git
				synced 2025-10-29 16:50:12 +00:00 
			
		
		
		
	Feat: Colored brew pages (#4)
* Removed graph animation * Smoother graph by removing data point bubbles * Added relatime button which keeps reloading every 2 sec * Clear timeout when destroying/navigating away from page * Added primary & secondary colors to match label per brew * Only render graph if dataset is list of length * Also color mobile image container primary color of brew * Add white background on graphs because of low contrast secondary background color
This commit is contained in:
		
							
								
								
									
										191
									
								
								src/brews.json
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								src/brews.json
									
									
									
									
									
								
							| @@ -1,91 +1,110 @@ | |||||||
| [{ | [ | ||||||
|   "beer": { |   { | ||||||
|     "name": "Kveldsbris", |     "beer": { | ||||||
|     "brewery": "Kinn Bryggeri", |       "name": "Kveldsbris", | ||||||
|     "category": "Pilsner/Lys Lager", |       "brewery": "Kinn Bryggeri", | ||||||
|     "description": "" |       "category": "Belgian Pale Ale", | ||||||
|  |       "description": "" | ||||||
|  |     }, | ||||||
|  |     "date": "1682272800", | ||||||
|  |     "by": ["Alf", "Kevin"], | ||||||
|  |     "abv": "5.6", | ||||||
|  |     "description": "Kveldsbris - Evening Breeze - is offered as a clean, refreshing and not-too-alcoholic Belgian ale. It has a fine balance of Belgian yeastiness with the premier hop varieties from three wildly different districts: East Kent Goldings on England's south coast, Saaz from Zatec in the Czech Republic, and Amarillo from Yakima Valley in the United States of America. Kveldsbris is a most versatile table ale that can cope with more spiciness and richness than a regular wheat beer.", | ||||||
|  |     "image": "kinn_kveldsbris.png", | ||||||
|  |     "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", | ||||||
|  |     "order_page": "https://oslo.bryggselv.no/finest/104923/finest-originals-utepils-allgrain-ølsett-25-liter", | ||||||
|  |     "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024", | ||||||
|  |     "color_primary": "#FCF28F", | ||||||
|  |     "color_secondary": "rgba(252, 242, 143, 0.2)" | ||||||
|   }, |   }, | ||||||
|   "date": "1682272800", |   { | ||||||
|   "by": ["Alf", "Kevin"], |     "beer": { | ||||||
|   "abv": "5.6", |       "name": "FUCK YEAH IPA", | ||||||
|   "description": "", |       "brewery": "Finest", | ||||||
|   "image": "kinn_kveldsbris.png", |       "category": "American IPA", | ||||||
|   "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", |       "description": "" | ||||||
|   "order_page": "https://oslo.bryggselv.no/finest/104923/finest-originals-utepils-allgrain-ølsett-25-liter", |     }, | ||||||
|   "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024" |     "date": "1648922400", | ||||||
| }, { |     "by": ["Alf", "Kevin"], | ||||||
|   "beer": { |     "abv": "7", | ||||||
|     "name": "FUCK YEAH IPA", |     "description": "", | ||||||
|     "brewery": "Finest", |     "image": "finest_fuck-yeah-IPA.jpg", | ||||||
|     "category": "American IPA", |     "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", | ||||||
|     "description": "" |     "order_page": "https://web.archive.org/web/20210225043236/https://www.bryggselv.no/finest/105943/fuck-yeah-ipa-ultra-american-west-coast-ipa-25-liter", | ||||||
|  |     "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024", | ||||||
|  |     "color_primary": "#0169A6", | ||||||
|  |     "color_secondary": "rgba(1, 105, 166, 0.2)" | ||||||
|   }, |   }, | ||||||
|   "date": "1648922400", |   { | ||||||
|   "by": ["Alf", "Kevin"], |     "beer": { | ||||||
|   "abv": "7", |       "name": "Love in a canoe", | ||||||
|   "description": "", |       "brewery": "Finest", | ||||||
|   "image": "finest_fuck-yeah-IPA.jpg", |       "category": "Mexican Lager", | ||||||
|   "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", |       "description": "" | ||||||
|   "order_page": "https://web.archive.org/web/20210225043236/https://www.bryggselv.no/finest/105943/fuck-yeah-ipa-ultra-american-west-coast-ipa-25-liter", |     }, | ||||||
|   "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024" |     "date": "1646420400", | ||||||
| }, { |     "by": ["Alf", "Kevin"], | ||||||
|   "beer": { |     "abv": "4.7", | ||||||
|     "name": "Love in a canoe", |     "description": "", | ||||||
|     "brewery": "Finest", |     "image": "finest_love-in-a-canoe.jpeg", | ||||||
|     "category": "Mexican Lager", |     "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", | ||||||
|     "description": "" |     "order_page": "https://oslo.bryggselv.no/finest/104092/love-in-a-canoe-allgrain-ølsett-25-liter", | ||||||
|  |     "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024", | ||||||
|  |     "color_primary": "#F7F0DF", | ||||||
|  |     "color_secondary": "rgba(247, 240, 223, 0.2)" | ||||||
|   }, |   }, | ||||||
|   "date": "1646420400", |   { | ||||||
|   "by": ["Alf", "Kevin"], |     "beer": { | ||||||
|   "abv": "4.7", |       "name": "Utepils", | ||||||
|   "description": "", |       "brewery": "Finest", | ||||||
|   "image": "finest_love-in-a-canoe.jpeg", |       "category": "", | ||||||
|   "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", |       "description": "" | ||||||
|   "order_page": "https://oslo.bryggselv.no/finest/104092/love-in-a-canoe-allgrain-ølsett-25-liter", |     }, | ||||||
|   "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024" |     "date": "1637694000", | ||||||
| }, { |     "by": ["Alf", "Kevin"], | ||||||
|   "beer": { |     "abv": "5.0", | ||||||
|     "name": "Utepils", |     "description": "", | ||||||
|     "brewery": "Finest", |     "image": "finest_utepils.jpeg", | ||||||
|     "category": "", |     "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", | ||||||
|     "description": "" |     "order_page": "https://www.bryggselv.no/finest/105932/kinn-kveldsbris-allgrain-ølsett-25-liter", | ||||||
|  |     "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024", | ||||||
|  |     "color_primary": "#F4E9D3", | ||||||
|  |     "color_secondary": "rgba(244, 233, 211, 0.2)" | ||||||
|   }, |   }, | ||||||
|   "date": "1637694000", |   { | ||||||
|   "by": ["Alf", "Kevin"], |     "beer": { | ||||||
|   "abv": "5.0", |       "name": "HELLES Tysk Lager", | ||||||
|   "description": "", |       "brewery": "Münchener Helles", | ||||||
|   "image": "finest_utepils.jpeg", |       "category": "Tysk Lager", | ||||||
|   "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", |       "description": "" | ||||||
|   "order_page": "https://www.bryggselv.no/finest/105932/kinn-kveldsbris-allgrain-ølsett-25-liter", |     }, | ||||||
|   "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024" |     "date": "1629396000", | ||||||
| }, { |     "by": ["Adrian", "Kevin", "Mats"], | ||||||
|   "beer": { |     "abv": "5.3", | ||||||
|     "name": "HELLES Tysk Lager", |     "description": "", | ||||||
|     "brewery": "Münchener Helles", |     "image": "helles_tysk-lager.jpeg", | ||||||
|     "category": "Tysk Lager", |     "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", | ||||||
|     "description": "" |     "order_page": "https://oslo.bryggselv.no/finest/106231/finest-helles-allgrain-ølsett-25-liter", | ||||||
|  |     "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024", | ||||||
|  |     "color_primary": "#C5893D", | ||||||
|  |     "color_secondary": "rgba(197, 137, 61, 0.2)" | ||||||
|   }, |   }, | ||||||
|   "date": "1629396000", |   { | ||||||
|   "by": ["Adrian", "Kevin", "Mats"], |     "beer": { | ||||||
|   "abv": "5.3", |       "name": "Lazy Days Weiss", | ||||||
|   "description": "", |       "brewery": "Finest", | ||||||
|   "image": "helles_tysk-lager.jpeg", |       "category": "Weissbier", | ||||||
|   "recipe": "https://docs.google.com/document/d/1FL7ibXxW1r_zFNLK338pyjfMiCCaTOi2fzuMoInA3dQ", |       "description": "" | ||||||
|   "order_page": "https://oslo.bryggselv.no/finest/106231/finest-helles-allgrain-ølsett-25-liter", |     }, | ||||||
|   "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024" |     "date": "1621706400", | ||||||
| }, { |     "by": ["Alf", "Kevin", "Kristian"], | ||||||
|   "beer": { |     "abv": "5.3", | ||||||
|     "name": "Lazy Days Weiss", |     "description": "", | ||||||
|     "brewery": "Finest", |     "image": "finest_lazy-days.jpeg", | ||||||
|     "category": "Weissbier", |     "recipe": "https://docs.google.com/document/u/0/d/1I6qX4l4jDzK51GxBt3IdEv-HyNQHAx8ijc5dMlG1Xkk", | ||||||
|     "description": "" |     "order_page": "https://oslo.bryggselv.no/finest/106231/finest-helles-allgrain-ølsett-25-liter", | ||||||
|   }, |     "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024", | ||||||
|   "date": "1621706400", |     "color_primary": "#6C8D9E", | ||||||
|   "by": ["Alf", "Kevin", "Kristian"], |     "color_secondary": "rgba(108, 141, 158, 0.2)" | ||||||
|   "abv": "5.3", |   } | ||||||
|   "description": "", | ] | ||||||
|   "image": "finest_lazy-days.jpeg", |  | ||||||
|   "recipe": "https://docs.google.com/document/u/0/d/1I6qX4l4jDzK51GxBt3IdEv-HyNQHAx8ijc5dMlG1Xkk", |  | ||||||
|   "order_page": "https://oslo.bryggselv.no/finest/106231/finest-helles-allgrain-ølsett-25-liter", |  | ||||||
|   "untapped": "https://untappd.com/b/kinn-bryggeri-kveldsbris/695024" |  | ||||||
| }] |  | ||||||
|   | |||||||
| @@ -94,17 +94,16 @@ | |||||||
|  |  | ||||||
|   function renderChart() { |   function renderChart() { | ||||||
|     const context: CanvasRenderingContext2D | null = chartCanvas.getContext('2d'); |     const context: CanvasRenderingContext2D | null = chartCanvas.getContext('2d'); | ||||||
|     if (!context) return |     if (!context) return; | ||||||
|  |  | ||||||
|     // create labels and singular dataset (data) |     // create labels and singular dataset (data) | ||||||
|     const labels: string[] = dateLabelsFormatedBasedOnResolution(dataFrames); |     const labels: string[] = dateLabelsFormatedBasedOnResolution(dataFrames); | ||||||
|     const data: ChartDataset = { |     const data: ChartDataset = { | ||||||
|       data: dataFrames.map((frame) => frame.value), |       data: dataFrames.map((frame) => frame.value), | ||||||
|       borderWidth: 3, |       borderWidth: 3 | ||||||
|     }; |     }; | ||||||
|     // based on name, add label and color options to dataset |     // based on name, add label and color options to dataset | ||||||
|     setDataColorAndName(data) |     setDataColorAndName(data); | ||||||
|  |  | ||||||
|  |  | ||||||
|     // create chart instance, most here is chart options |     // create chart instance, most here is chart options | ||||||
|     chart = new Chart(context, { |     chart = new Chart(context, { | ||||||
| @@ -114,9 +113,12 @@ | |||||||
|         datasets: [data] |         datasets: [data] | ||||||
|       }, |       }, | ||||||
|       options: { |       options: { | ||||||
|  |         animation: { | ||||||
|  |           duration: 0 | ||||||
|  |         }, | ||||||
|         elements: { |         elements: { | ||||||
|           point: { |           point: { | ||||||
|             radius: 2 |             radius: 0 | ||||||
|           }, |           }, | ||||||
|           line: { |           line: { | ||||||
|             tension: 0.5 |             tension: 0.5 | ||||||
|   | |||||||
| @@ -5,8 +5,8 @@ | |||||||
|  |  | ||||||
|   export let data; |   export let data; | ||||||
|   let brew = data.brew; |   let brew = data.brew; | ||||||
|   let temperatureData: IChartFrame[] = data.graphData.temperature; |   let temperatureData: IChartFrame[] = data?.graphData?.temperature; | ||||||
|   let humidityData: IChartFrame[] = data.graphData.humidity; |   let humidityData: IChartFrame[] = data?.graphData?.humidity; | ||||||
|  |  | ||||||
|   const dateFormat: Intl.DateTimeFormatOptions = { |   const dateFormat: Intl.DateTimeFormatOptions = { | ||||||
|     weekday: 'long', |     weekday: 'long', | ||||||
| @@ -20,11 +20,11 @@ | |||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <section class="card"> | <section class="card"> | ||||||
|   <div class="desktop-only image-container" style="height: {height}px"> |   <div class="desktop-only image-container" style="height: {height}px; background-color: {brew.color_primary || '#93a4a0'}"> | ||||||
|     <img src="/images/{brew.image}" alt="Tuborg Sommerøl" aria-label="Tuborg Sommerøl" /> |     <img src="/images/{brew.image}" alt="Tuborg Sommerøl" aria-label="Tuborg Sommerøl" /> | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
|   <div class="beer-container" bind:clientHeight="{height}"> |   <div class="beer-container" bind:clientHeight="{height}" style="background-color: {brew.color_secondary || '#DFE6E5'}"> | ||||||
|     <h1>{brew.beer.name}</h1> |     <h1>{brew.beer.name}</h1> | ||||||
|  |  | ||||||
|     <div class="links"> |     <div class="links"> | ||||||
| @@ -57,25 +57,22 @@ | |||||||
|       </tbody> |       </tbody> | ||||||
|     </table> |     </table> | ||||||
|  |  | ||||||
|     <div class="mobile-only image-container"> |     <div class="mobile-only image-container" style="background-color: {brew.color_primary || '#93a4a0'}"> | ||||||
|       <img src="/images/{brew.image}" alt="Tuborg Sommerøl" aria-label="Tuborg Sommerøl" /> |       <img src="/images/{brew.image}" alt="Tuborg Sommerøl" aria-label="Tuborg Sommerøl" /> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <h3>Historie</h3> |     <h3>Beskrivelse</h3> | ||||||
|     <p> |     <p>{brew.description}</p> | ||||||
|       I 1873 ble Tuborg Bryggeri grunnlagt av Carl Frederik Tietgen på Hellerud i Danmark. I 1970 |  | ||||||
|       ble Tuborg Bryggeri en del av Carlsberg. |  | ||||||
|     </p> |  | ||||||
|  |  | ||||||
|     <div class="graph-container"> |     <div class="graph-container"> | ||||||
|       {#if temperatureData} |       {#if temperatureData && temperatureData?.length} | ||||||
|         <div class="graph"> |         <div class="graph"> | ||||||
|           <h3>Temperature during fermentation</h3> |           <h3>Temperature during fermentation</h3> | ||||||
|           <Graph dataFrames="{temperatureData}" name="Temperature" hideTitle="{true}" /> |           <Graph dataFrames="{temperatureData}" name="Temperature" hideTitle="{true}" /> | ||||||
|         </div> |         </div> | ||||||
|       {/if} |       {/if} | ||||||
|  |  | ||||||
|       {#if humidityData} |       {#if humidityData && temperatureData?.length} | ||||||
|         <div class="graph"> |         <div class="graph"> | ||||||
|           <h3>Humidity during carbonation</h3> |           <h3>Humidity during carbonation</h3> | ||||||
|           <Graph dataFrames="{humidityData}" name="Humidity" hideTitle="{true}" /> |           <Graph dataFrames="{humidityData}" name="Humidity" hideTitle="{true}" /> | ||||||
| @@ -134,7 +131,6 @@ | |||||||
|     display: flex; |     display: flex; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     min-height: 1px; |     min-height: 1px; | ||||||
|     background-color: #93a4a0; |  | ||||||
|     padding: 3rem 1rem; |     padding: 3rem 1rem; | ||||||
|  |  | ||||||
|     @include tablet { |     @include tablet { | ||||||
| @@ -144,7 +140,8 @@ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @include mobile { |     @include mobile { | ||||||
|       margin: 2rem 0; |       width: calc(100% + 2rem); | ||||||
|  |       margin: 2rem 0 2rem -1rem; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     img { |     img { | ||||||
| @@ -234,4 +231,10 @@ | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   :global(canvas) { | ||||||
|  |     background-color: white; | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     padding: 0 0.2rem; | ||||||
|  |   } | ||||||
| </style> | </style> | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import { onMount } from 'svelte'; |   import { onDestroy, onMount } from 'svelte'; | ||||||
|   import Graph from '../../lib/components/Graph.svelte'; |   import Graph from '../../lib/components/Graph.svelte'; | ||||||
|   import type IChartFrame from '../../lib/interfaces/IChartFrame'; |   import type IChartFrame from '../../lib/interfaces/IChartFrame'; | ||||||
|   import type { PageData } from './$types'; |   import type { PageData } from './$types'; | ||||||
| @@ -21,7 +21,14 @@ | |||||||
|     return fetch(`/api/graph/${unit}`, options).then((resp) => resp.json()); |     return fetch(`/api/graph/${unit}`, options).then((resp) => resp.json()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const buttonMinutes = [ |   interface IButtonMinute { | ||||||
|  |     value: number; | ||||||
|  |     name: string; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   let timeout: ReturnType<typeof setTimeout>; | ||||||
|  |   const buttonMinutes: Array<IButtonMinute> = [ | ||||||
|  |     { value: 2.4, name: 'Realtime' }, | ||||||
|     { value: 15, name: 'Last 15 minutes' }, |     { value: 15, name: 'Last 15 minutes' }, | ||||||
|     { value: 60, name: 'Last hour' }, |     { value: 60, name: 'Last hour' }, | ||||||
|     { value: 360, name: 'Last 6 hours' }, |     { value: 360, name: 'Last 6 hours' }, | ||||||
| @@ -33,8 +40,9 @@ | |||||||
|     { value: 518400, name: 'Last year' } |     { value: 518400, name: 'Last year' } | ||||||
|   ]; |   ]; | ||||||
|  |  | ||||||
|   function reload(mins: number) { |   function reload(button: IButtonMinute) { | ||||||
|     minutes = mins; |     minutes = button.value; | ||||||
|  |     clearTimeout(timeout); | ||||||
|     const to: Date = new Date(); |     const to: Date = new Date(); | ||||||
|     const from = new Date(to.getTime() - minutes * 60 * 1000); |     const from = new Date(to.getTime() - minutes * 60 * 1000); | ||||||
|     const size = 40; |     const size = 40; | ||||||
| @@ -42,6 +50,10 @@ | |||||||
|     fetchData('temperature', from, to, size).then((resp) => (temperatureData = resp?.data)); |     fetchData('temperature', from, to, size).then((resp) => (temperatureData = resp?.data)); | ||||||
|     fetchData('humidity', from, to, size).then((resp) => (humidityData = resp?.data)); |     fetchData('humidity', from, to, size).then((resp) => (humidityData = resp?.data)); | ||||||
|     fetchData('pressure', from, to, size).then((resp) => (pressureData = resp?.data)); |     fetchData('pressure', from, to, size).then((resp) => (pressureData = resp?.data)); | ||||||
|  |  | ||||||
|  |     if (button.name === 'Realtime') { | ||||||
|  |       timeout = setTimeout(() => reload(button), 2000); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   function scrollSelectedButtonIntoView() { |   function scrollSelectedButtonIntoView() { | ||||||
| @@ -59,13 +71,13 @@ | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   onMount(scrollSelectedButtonIntoView); |   onMount(scrollSelectedButtonIntoView); | ||||||
|  |   onDestroy(() => clearTimeout(timeout)) | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <div class="button-wrapper"> | <div class="button-wrapper"> | ||||||
|   {#each buttonMinutes as button} |   {#each buttonMinutes as button} | ||||||
|     <button |     <button on:click="{() => reload(button)}" class="{button.value === minutes ? 'selected' : ''}" | ||||||
|       on:click="{() => reload(button.value)}" |       >{button.name}</button | ||||||
|       class="{button.value === minutes ? 'selected' : ''}">{button.name}</button |  | ||||||
|     > |     > | ||||||
|   {/each} |   {/each} | ||||||
| </div> | </div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user