Graph update and little noe ui tweaks
This commit is contained in:
		@@ -17,13 +17,14 @@
 | 
				
			|||||||
body {
 | 
					body {
 | 
				
			||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  overflow-x: hidden;
 | 
					/*  overflow-x: hidden;*/
 | 
				
			||||||
  min-width: 320px;
 | 
					  min-width: 320px;
 | 
				
			||||||
  background: #FFFFFF;
 | 
					  background: green;
 | 
				
			||||||
  font-family: 'Roboto', system-ui, -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
 | 
					  font-family: 'Roboto', system-ui, -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  line-height: 1.4285em;
 | 
					  line-height: 1.4285em;
 | 
				
			||||||
  color: rgba(0, 0, 0, 0.87);
 | 
					  color: rgba(0, 0, 0, 0.87);
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:root {
 | 
					:root {
 | 
				
			||||||
@@ -95,7 +96,7 @@ body {
 | 
				
			|||||||
	font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif;
 | 
						font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#app {
 | 
					#app {
 | 
				
			||||||
	background: var(--body_bg_color);
 | 
					/*	background: var(--body_bg_color);*/
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.ui.segment {
 | 
					.ui.segment {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,6 +87,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			margin: 0;
 | 
								margin: 0;
 | 
				
			||||||
			padding: 0;
 | 
								padding: 0;
 | 
				
			||||||
 | 
								overflow: hidden;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		.place-holder {
 | 
							.place-holder {
 | 
				
			||||||
			width: 100%;
 | 
								width: 100%;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										431
									
								
								client/src/components/Metrictracking/MetricGraphsComponent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										431
									
								
								client/src/components/Metrictracking/MetricGraphsComponent.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,431 @@
 | 
				
			|||||||
 | 
					<style type="text/css" scoped>
 | 
				
			||||||
 | 
						.an-graph {
 | 
				
			||||||
 | 
							background: #fefefe;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.inactive.segment {
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.active.segment {
 | 
				
			||||||
 | 
							outline: 4px solid cyan;
 | 
				
			||||||
 | 
							outline-offset: -5px;
 | 
				
			||||||
 | 
							outline-style: dashed;
 | 
				
			||||||
 | 
							max-height: 2000px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.not-padded {
 | 
				
			||||||
 | 
							margin-left: -5px;
 | 
				
			||||||
 | 
							margin-right: -5px;
 | 
				
			||||||
 | 
							margin-bottom: -10px;
 | 
				
			||||||
 | 
							padding-right: 5px;
 | 
				
			||||||
 | 
							padding-left: 5px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.sticky-boy {
 | 
				
			||||||
 | 
							position: fixed;
 | 
				
			||||||
 | 
							top: -1px;
 | 
				
			||||||
 | 
							right: 10px;
 | 
				
			||||||
 | 
							z-index: 100;
 | 
				
			||||||
 | 
							width: 70%;
 | 
				
			||||||
 | 
							background: orange;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.animate-height {
 | 
				
			||||||
 | 
							transition: max-height 0.8s linear;
 | 
				
			||||||
 | 
							max-height: 450px;
 | 
				
			||||||
 | 
							overflow: hidden;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<div class="ui very compact grid" :class="{'sticky-boy':editGraphs}">
 | 
				
			||||||
 | 
								<div class="sixteen wide column" v-if="!editGraphs">
 | 
				
			||||||
 | 
									<div class="ui basic padded segment">
 | 
				
			||||||
 | 
										<!-- Just a space to keep things clickable	 -->
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="sixteen wide column">
 | 
				
			||||||
 | 
									<dix class="ui basic segment" v-if="!editGraphs">
 | 
				
			||||||
 | 
										<div class="ui button" v-on:click="toggleEditGraphs">
 | 
				
			||||||
 | 
											<i class="edit icon"></i>
 | 
				
			||||||
 | 
											<span>Add/Edit Graphs</span>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</dix>
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<div v-if="editGraphs">
 | 
				
			||||||
 | 
										<div class="ui green button" v-on:click="addGraph()">
 | 
				
			||||||
 | 
											<i class="plus icon"></i>
 | 
				
			||||||
 | 
											New Graph
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="ui basic button" v-on:click="toggleEditGraphs">
 | 
				
			||||||
 | 
											<i class="check circle icon"></i>
 | 
				
			||||||
 | 
											Done Editing Graphs
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							<div v-for="(graph, index) in graphs" :class="`ui not-padded ${editGraphs?'active ':'inactive '}segment animate-height`">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<!-- Edit options -->
 | 
				
			||||||
 | 
								<div class="ui small header" v-if="editGraphs">
 | 
				
			||||||
 | 
									<div class="ui grid">
 | 
				
			||||||
 | 
										<div class="eight wide column">
 | 
				
			||||||
 | 
											<b>Graph #{{ index+1 }}</b>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="eight wide right aligned column">
 | 
				
			||||||
 | 
											<span class="ui tiny compact inverted red button" v-on:click="removeGraph(index)">
 | 
				
			||||||
 | 
												Remove Graph
 | 
				
			||||||
 | 
												<i class="close icon"></i>
 | 
				
			||||||
 | 
											</span>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<h3 class="ui center aligned dividing header">
 | 
				
			||||||
 | 
									{{ getGraphTitle(graph) }}
 | 
				
			||||||
 | 
								</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<div v-if="graph?.type == PILL_CALENDAR">
 | 
				
			||||||
 | 
									<PillCalendarGraph
 | 
				
			||||||
 | 
										:graph="graph"
 | 
				
			||||||
 | 
										:tempChartDays="tempChartDays"
 | 
				
			||||||
 | 
										:userFields="userFields"
 | 
				
			||||||
 | 
										:cycleData="cycleData"
 | 
				
			||||||
 | 
										:edit-graphs="editGraphs"
 | 
				
			||||||
 | 
										:showZeroValues="graph?.options?.showZeroValues"
 | 
				
			||||||
 | 
										:showTextValues="graph?.options?.showTextValues"
 | 
				
			||||||
 | 
										:connectDays="graph?.options?.connectDays"
 | 
				
			||||||
 | 
										:hideValues="graph?.options?.hideValues"
 | 
				
			||||||
 | 
										:hideIcons="graph?.options?.hideIcons"
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<div v-if="editGraphs" class="ui segment">
 | 
				
			||||||
 | 
										<p>Calendar Graph Toggles</p>
 | 
				
			||||||
 | 
										<div v-on:click="toggelValue(index, 'hideIcons')"class="ui button">
 | 
				
			||||||
 | 
											<span v-if="graph?.options?.hideIcons">Show</span><span v-else>Hide</span> Icons
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div v-on:click="toggelValue(index, 'hideValues')"class="ui button">
 | 
				
			||||||
 | 
											<span v-if="graph?.options?.hideValues">Show</span><span v-else>Hide</span> Values
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div v-on:click="toggelValue(index, 'showZeroValues')"class="ui button">
 | 
				
			||||||
 | 
											<span v-if="!graph?.options?.showZeroValues">Show</span><span v-else>Hide</span> Lowest Value
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div v-on:click="toggelValue(index, 'showTextValues')"class="ui button">
 | 
				
			||||||
 | 
											<span v-if="!graph?.options?.showTextValues">Show</span><span v-else>Hide</span> Text Value
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div v-on:click="toggelValue(index, 'connectDays')"class="ui button">
 | 
				
			||||||
 | 
											<span v-if="!graph?.options?.connectDays">Connect</span><span v-else>Disconnect</span> Days
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div v-if="graph?.type == LAST_DONE">
 | 
				
			||||||
 | 
									Last done not implemented
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div v-if="!graph.fieldIds || graph.fieldIds && graph.fieldIds.length == 0">
 | 
				
			||||||
 | 
									<h5>Blank Graph</h5>
 | 
				
			||||||
 | 
									<span v-if="!editGraphs">Click "Edit Graphs" then,</span>
 | 
				
			||||||
 | 
									Select Graph type and Metrics to display
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div v-if="graph?.type == undefined && graph.fieldIds && graph.fieldIds.length > 0">
 | 
				
			||||||
 | 
									<div :id="`graphdiv${index}`" style="width: 100%; min-height: 320px;"></div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<div class="ui segment" v-if="editGraphs">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<!-- change graph type -->
 | 
				
			||||||
 | 
									<div v-for="(graphType, graphId) in graphTypesDef" class="ui buttons">
 | 
				
			||||||
 | 
										<div class="ui tiny button" v-on:click="changeGraphType(index, graphId)" :class="{'green':(String(graphId) == String(graph?.type))}">
 | 
				
			||||||
 | 
											{{ graphType }}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<div v-for="fieldId in fields">
 | 
				
			||||||
 | 
										<span v-if="graph.fieldIds && graph.fieldIds.includes(fieldId)" v-on:click="toggleGraphField(fieldId, index)">
 | 
				
			||||||
 | 
											<i class="green check square icon"></i>
 | 
				
			||||||
 | 
										</span>
 | 
				
			||||||
 | 
										<span v-else v-on:click="toggleGraphField(fieldId, index)">
 | 
				
			||||||
 | 
											<i class="square outline icon"></i>
 | 
				
			||||||
 | 
										</span>
 | 
				
			||||||
 | 
										<i :class="`${$parent.getFieldColor(fieldId)} ${$parent.getFieldIcon(fieldId)} icon`"></i>
 | 
				
			||||||
 | 
										<b>{{ userFields[fieldId]?.label }}</b>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<div class="ui very compact grid" :class="{'sticky-boy':editGraphs}">
 | 
				
			||||||
 | 
								<div class="sixteen wide column" v-if="!editGraphs">
 | 
				
			||||||
 | 
									<div class="ui basic padded segment">
 | 
				
			||||||
 | 
										<!-- Just a space to keep things clickable	 -->
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="sixteen wide column">
 | 
				
			||||||
 | 
									<dix class="ui basic segment" v-if="!editGraphs">
 | 
				
			||||||
 | 
										<div class="ui button" v-on:click="toggleEditGraphs">
 | 
				
			||||||
 | 
											<i class="edit icon"></i>
 | 
				
			||||||
 | 
											<span>Add/Edit Graphs</span>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</dix>
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<div v-if="editGraphs">
 | 
				
			||||||
 | 
										<div class="ui green button" v-on:click="addGraph()">
 | 
				
			||||||
 | 
											<i class="plus icon"></i>
 | 
				
			||||||
 | 
											New Graph
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="ui basic button" v-on:click="toggleEditGraphs">
 | 
				
			||||||
 | 
											<i class="check circle icon"></i>
 | 
				
			||||||
 | 
											Done Editing Graphs
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<!-- Anchor for scrolling to the bottom of graphs -->
 | 
				
			||||||
 | 
							<div ref="anchor"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const PILL_CALENDAR = 'pillCalendar'
 | 
				
			||||||
 | 
						const LAST_DONE = 'lastDone'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export default {
 | 
				
			||||||
 | 
							name: 'MetricTrackingGraphs',
 | 
				
			||||||
 | 
							props: [
 | 
				
			||||||
 | 
								'tempChartDays', 	// Number of days to display
 | 
				
			||||||
 | 
								'fields', 			// field IDs for display/order
 | 
				
			||||||
 | 
								'userFields', 		// field values defined by user
 | 
				
			||||||
 | 
								'graphs', 			// Graph data defined by user
 | 
				
			||||||
 | 
								'cycleData', 		// ALL user data
 | 
				
			||||||
 | 
								'calendar', 		// Date data for currently open day
 | 
				
			||||||
 | 
								'editGraphs'		// boolean for edit or not edit graphs
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							components: {
 | 
				
			||||||
 | 
								'PillCalendarGraph':require('@/components/Metrictracking/PillCalendarGraph.vue').default,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							data: function(){
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									graphTypesDef:{
 | 
				
			||||||
 | 
										// [LAST_DONE]: 'Last Done',
 | 
				
			||||||
 | 
										'undefined':'Line Graph (Default)',
 | 
				
			||||||
 | 
										[PILL_CALENDAR]:'Calendar Graph',
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									localGraphData:[],
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							beforeCreate() {
 | 
				
			||||||
 | 
								// Constants
 | 
				
			||||||
 | 
								this.PILL_CALENDAR = PILL_CALENDAR
 | 
				
			||||||
 | 
								this.LAST_DONE = LAST_DONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Include JS libraries
 | 
				
			||||||
 | 
								let graphsScript = document.createElement('script')
 | 
				
			||||||
 | 
								graphsScript.setAttribute('src', '//cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.min.js')
 | 
				
			||||||
 | 
					      		document.head.appendChild(graphsScript)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							mounted(){
 | 
				
			||||||
 | 
								this.localGraphData = this.graphs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.graphCurrentData()
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							updated(){
 | 
				
			||||||
 | 
								// update graphs here? Or watch graphs prop
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							watch: {
 | 
				
			||||||
 | 
								// whenever question changes, this function will run
 | 
				
			||||||
 | 
								userFields(newFields, oldFields) {
 | 
				
			||||||
 | 
									// console.log([newFields, oldFields])
 | 
				
			||||||
 | 
									if( JSON.stringify(oldFields) == "{}" ){
 | 
				
			||||||
 | 
										this.graphCurrentData()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								tempChartDays(newDays, oldDays){
 | 
				
			||||||
 | 
									if( newDays != oldDays ){
 | 
				
			||||||
 | 
										this.graphCurrentData()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							methods: {
 | 
				
			||||||
 | 
								saveGraphs(){
 | 
				
			||||||
 | 
									this.$emit('saveGraphs', this.localGraphData)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								toggleEditGraphs(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									setTimeout(() => {
 | 
				
			||||||
 | 
										// scroll last graph into view
 | 
				
			||||||
 | 
										this.$refs.anchor.scrollIntoView({
 | 
				
			||||||
 | 
											behavior: 'smooth',
 | 
				
			||||||
 | 
											block: 'center',
 | 
				
			||||||
 | 
											inline: 'center'
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									}, 800)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.$emit('toggleEditGraphs')
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								changeGraphType(index, newType){
 | 
				
			||||||
 | 
									console.log(index + ' change to ' + newType)
 | 
				
			||||||
 | 
									this.localGraphData[index]['type'] = newType
 | 
				
			||||||
 | 
									this.saveGraphs()
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								addGraph(){
 | 
				
			||||||
 | 
									this.localGraphData.push({})
 | 
				
			||||||
 | 
									this.saveGraphs()
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								removeGraph(index){
 | 
				
			||||||
 | 
									this.localGraphData.splice(index, 1)
 | 
				
			||||||
 | 
									this.saveGraphs()
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								toggelValue(graphIndex, optionName){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(!this.localGraphData[graphIndex].options){
 | 
				
			||||||
 | 
										this.localGraphData[graphIndex].options = {}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(this.localGraphData[graphIndex].options[optionName]){
 | 
				
			||||||
 | 
										this.localGraphData[graphIndex].options[optionName] = false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										this.localGraphData[graphIndex].options[optionName] = true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									console.log(this.localGraphData[graphIndex].options[optionName])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.saveGraphs()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								toggleGraphField(fieldId, graphIndex){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(!Array.isArray(this.localGraphData[graphIndex].fieldIds)){
 | 
				
			||||||
 | 
										this.localGraphData[graphIndex].fieldIds = []
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const inSetCheck = this.localGraphData[graphIndex]?.fieldIds.indexOf(fieldId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(inSetCheck == -1){
 | 
				
			||||||
 | 
										this.localGraphData[graphIndex]?.fieldIds.push(fieldId)					
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if(inSetCheck > -1){
 | 
				
			||||||
 | 
										this.localGraphData[graphIndex]?.fieldIds.splice(inSetCheck,1)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.saveGraphs()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								getGraphTitle(graph){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const graphFields = graph?.fieldIds || []
 | 
				
			||||||
 | 
									let fieldTitles = []
 | 
				
			||||||
 | 
									graphFields.forEach(fieldId => {
 | 
				
			||||||
 | 
										fieldTitles.push(this.userFields[fieldId]?.label)
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// console.log(fieldTitles)
 | 
				
			||||||
 | 
									const title = fieldTitles.join(', ')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return title
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								graphCurrentData(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// try again if dygraphs isn't loaded
 | 
				
			||||||
 | 
									if( typeof(window.Dygraph) != 'function' ){
 | 
				
			||||||
 | 
										setTimeout(() => {
 | 
				
			||||||
 | 
											this.graphCurrentData()
 | 
				
			||||||
 | 
										}, 100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const graphOptions = {
 | 
				
			||||||
 | 
										interactionModel: {},
 | 
				
			||||||
 | 
										// pointClickCallback: function(e, pt){
 | 
				
			||||||
 | 
										// 	console.log(e)
 | 
				
			||||||
 | 
										// 	console.log(pt)
 | 
				
			||||||
 | 
										// 	console.log(this.getValue(pt.idx, 0))
 | 
				
			||||||
 | 
										// }
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Excel date format YYYYMMDD
 | 
				
			||||||
 | 
									const convertToExcelDate = (dateCode) => {
 | 
				
			||||||
 | 
										return dateCode
 | 
				
			||||||
 | 
										.split('.')
 | 
				
			||||||
 | 
										.reverse()
 | 
				
			||||||
 | 
										.map(item => String(item).padStart(2,0))
 | 
				
			||||||
 | 
										.join('')
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Generate set of keys for graph length
 | 
				
			||||||
 | 
									let dataKeys = Object.keys(this.cycleData)
 | 
				
			||||||
 | 
									dataKeys = dataKeys.splice(0, this.tempChartDays)
 | 
				
			||||||
 | 
									console.log(dataKeys)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// build CSV data for each graph
 | 
				
			||||||
 | 
									this.graphs.forEach((graph,index) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// only chart line graphs with dygraphs
 | 
				
			||||||
 | 
										if( graph.type != undefined ){
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if( !graph.fieldIds ){
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// CSV or path to a CSV file.
 | 
				
			||||||
 | 
										let dataString = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Lookup graph field titles
 | 
				
			||||||
 | 
										let graphLabels = ['Date']
 | 
				
			||||||
 | 
										graph.fieldIds.forEach(fieldId => {
 | 
				
			||||||
 | 
											const graphLabel = this.userFields[fieldId]?.label
 | 
				
			||||||
 | 
											const escapedLabel = graphLabel.replaceAll(',','')
 | 
				
			||||||
 | 
											graphLabels.push(escapedLabel)
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
										dataString += graphLabels.join(',') + '\n'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
										// build each row, for each day
 | 
				
			||||||
 | 
										for (var i = 0; i < dataKeys.length; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											let nextFragment = []
 | 
				
			||||||
 | 
											// push date code to first column
 | 
				
			||||||
 | 
											nextFragment.push(convertToExcelDate(dataKeys[i]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											graph.fieldIds.forEach(fieldId => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												const currentEntry = this.cycleData[dataKeys[i]]
 | 
				
			||||||
 | 
												let currentValue = currentEntry[fieldId]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// setup correct float graphing
 | 
				
			||||||
 | 
												if(fieldId == 'BT'){
 | 
				
			||||||
 | 
													// parse temp to fixed length float 00.00
 | 
				
			||||||
 | 
													currentValue = parseFloat(currentValue).toFixed(2)
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												if( currentValue == undefined ){
 | 
				
			||||||
 | 
													currentValue = -1
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												nextFragment.push(currentValue)
 | 
				
			||||||
 | 
													
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											dataString += nextFragment.join(',') + "\n"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
										let graphDiv = document.getElementById("graphdiv"+index)
 | 
				
			||||||
 | 
										const g = new Dygraph(graphDiv, dataString ,graphOptions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@@ -1,26 +1,547 @@
 | 
				
			|||||||
<style type="text/css" scoped></style>
 | 
					<style type="text/css" scoped>
 | 
				
			||||||
 | 
						div.calendar {
 | 
				
			||||||
 | 
						    width: calc(100% - 4px);
 | 
				
			||||||
 | 
						    min-height: 350px;
 | 
				
			||||||
 | 
						    display: flex;
 | 
				
			||||||
 | 
						    margin: 5px 8px 15px;
 | 
				
			||||||
 | 
						    flex-wrap: wrap;
 | 
				
			||||||
 | 
						    flex-direction: row;
 | 
				
			||||||
 | 
						    justify-content: flex-start;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day {
 | 
				
			||||||
 | 
							flex: 0 0 calc(14.28% - 2px);
 | 
				
			||||||
 | 
							min-height: 50px;
 | 
				
			||||||
 | 
							border: 1px solid var(--border_color);
 | 
				
			||||||
 | 
							font-size: 1.2em;
 | 
				
			||||||
 | 
							overflow: hidden;
 | 
				
			||||||
 | 
							box-sizing: border-box;
 | 
				
			||||||
 | 
							position: relative;
 | 
				
			||||||
 | 
							line-height: 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
					    	align-items: flex-end;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.today {
 | 
				
			||||||
 | 
							font-weight: bold;
 | 
				
			||||||
 | 
							text-decoration: underline;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.active-entry {
 | 
				
			||||||
 | 
							outline: #07f4f4;
 | 
				
			||||||
 | 
							outline-style: none;
 | 
				
			||||||
 | 
							outline-width: medium;
 | 
				
			||||||
 | 
							outline-style: none;
 | 
				
			||||||
 | 
							outline-offset: -1px;
 | 
				
			||||||
 | 
							outline-style: solid;
 | 
				
			||||||
 | 
							outline-width: 3px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day ~ .has-data {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day ~ .no-data {
 | 
				
			||||||
 | 
							background: #c7c7c787;
 | 
				
			||||||
 | 
							opacity: 0.6;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day > .number {
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							top: 0;
 | 
				
			||||||
 | 
							right: 5px;
 | 
				
			||||||
 | 
							z-index: 10;
 | 
				
			||||||
 | 
							opacity: 0.4;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day > .sex {
 | 
				
			||||||
 | 
							font-size: 0.7em;
 | 
				
			||||||
 | 
							border-radius: 5px;
 | 
				
			||||||
 | 
							background: rgba(249, 0, 0, 0.15);
 | 
				
			||||||
 | 
							color: white;
 | 
				
			||||||
 | 
							padding: 0 0 0 4px;
 | 
				
			||||||
 | 
							z-index: 10;
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							left: 0;
 | 
				
			||||||
 | 
							height: 26px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day > .period {
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							bottom: 1px;
 | 
				
			||||||
 | 
							left: 1px;
 | 
				
			||||||
 | 
							right: 1px;
 | 
				
			||||||
 | 
							height: 5px;
 | 
				
			||||||
 | 
							background: red;
 | 
				
			||||||
 | 
							z-index: 10;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day > .mucus {
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							bottom: 0;
 | 
				
			||||||
 | 
							left: 0;
 | 
				
			||||||
 | 
							right: 0;
 | 
				
			||||||
 | 
							min-height: 10px;
 | 
				
			||||||
 | 
							background: #abecff7d;
 | 
				
			||||||
 | 
							z-index: 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day > .notes {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.pill-container {
 | 
				
			||||||
 | 
							width: 100%;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.pill {
 | 
				
			||||||
 | 
							width: calc(100% - 8px);
 | 
				
			||||||
 | 
							min-height: 2px;
 | 
				
			||||||
 | 
							margin: 0 4px;
 | 
				
			||||||
 | 
							box-sizing: border-box;
 | 
				
			||||||
 | 
							display: inline-block;
 | 
				
			||||||
 | 
							background: rgb(50 218 255 / 44%);
 | 
				
			||||||
 | 
							border-radius: 40px;
 | 
				
			||||||
 | 
							text-align: center;
 | 
				
			||||||
 | 
							line-height: 1em;
 | 
				
			||||||
 | 
							position: relative;
 | 
				
			||||||
 | 
							color: white;
 | 
				
			||||||
 | 
							font-size: 0.7em;
 | 
				
			||||||
 | 
						    padding: 2px;
 | 
				
			||||||
 | 
						    overflow: hidden;
 | 
				
			||||||
 | 
						    white-space: nowrap;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.pill.did-last {
 | 
				
			||||||
 | 
							margin-left: 0;
 | 
				
			||||||
 | 
							border-top-left-radius: 0;
 | 
				
			||||||
 | 
							border-bottom-left-radius: 0;
 | 
				
			||||||
 | 
							width: calc(100% - 5px);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.pill.did-next {
 | 
				
			||||||
 | 
							margin-right: 0;
 | 
				
			||||||
 | 
							border-top-right-radius: 0;
 | 
				
			||||||
 | 
							border-bottom-right-radius: 0;
 | 
				
			||||||
 | 
							width: calc(100% - 5px);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.pill.did-next.did-last {
 | 
				
			||||||
 | 
							width: 100%;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					/*	.last-high:after {
 | 
				
			||||||
 | 
							content: '';
 | 
				
			||||||
 | 
							width: 0;
 | 
				
			||||||
 | 
							height: 0;
 | 
				
			||||||
 | 
							border-top: 15px solid transparent;
 | 
				
			||||||
 | 
							border-bottom: 3px solid transparent;
 | 
				
			||||||
 | 
							border-left: 10px solid rgb(50 218 255 / 44%);
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							left: 0;
 | 
				
			||||||
 | 
							top: -13px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.next-high:before {
 | 
				
			||||||
 | 
							content: '';
 | 
				
			||||||
 | 
							width: 0;
 | 
				
			||||||
 | 
							height: 0;
 | 
				
			||||||
 | 
							border-top: 15px solid transparent;
 | 
				
			||||||
 | 
							border-bottom: 3px solid transparent;
 | 
				
			||||||
 | 
							border-right: 10px solid rgb(50 218 255 / 44%);
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							right: 0;
 | 
				
			||||||
 | 
							top: -13px;
 | 
				
			||||||
 | 
						}*/
 | 
				
			||||||
 | 
						.big-day {
 | 
				
			||||||
 | 
							display: inline-block;
 | 
				
			||||||
 | 
							width: 100%;
 | 
				
			||||||
 | 
							min-height: 2px;
 | 
				
			||||||
 | 
							margin: 0 auto;
 | 
				
			||||||
 | 
							text-align: center;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.zero-day {
 | 
				
			||||||
 | 
							opacity: 0.5;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.icon-spacer {
 | 
				
			||||||
 | 
							display: inline-block;
 | 
				
			||||||
 | 
							background-color: greenyellow;
 | 
				
			||||||
 | 
							width: 20px;
 | 
				
			||||||
 | 
							height: 2px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.past-entries {
 | 
				
			||||||
 | 
							width: 100%;
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							justify-content: space-around;
 | 
				
			||||||
 | 
					/*		padding: 0 10px;*/
 | 
				
			||||||
 | 
							overflow-x: scroll;
 | 
				
			||||||
 | 
							overflow-y: hidden;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.past-entry {
 | 
				
			||||||
 | 
							position: relative;
 | 
				
			||||||
 | 
							text-align: center;
 | 
				
			||||||
 | 
							border: 1px solid;
 | 
				
			||||||
 | 
							border-color: var(--dark_border_color);
 | 
				
			||||||
 | 
							color: var(--text_color);
 | 
				
			||||||
 | 
							flex-grow: 1;
 | 
				
			||||||
 | 
							cursor: pointer;
 | 
				
			||||||
 | 
							font-weight: bold;
 | 
				
			||||||
 | 
							min-width: 40px;
 | 
				
			||||||
 | 
							min-height: 40px;
 | 
				
			||||||
 | 
							margin: 5px 0 10px;
 | 
				
			||||||
 | 
							line-height: 2.3em;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.day-list {
 | 
				
			||||||
 | 
							width: 100%;
 | 
				
			||||||
 | 
							height: 80px;
 | 
				
			||||||
 | 
							background-color: green;
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							justify-content: space-around;
 | 
				
			||||||
 | 
							overflow-x: scroll;
 | 
				
			||||||
 | 
							overflow-y: hidden;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.day-list-item {
 | 
				
			||||||
 | 
							flex-grow: 1;
 | 
				
			||||||
 | 
							border: 1px solid black;
 | 
				
			||||||
 | 
							width: 25px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.pill.red { background-color: #db2828 }
 | 
				
			||||||
 | 
					.pill.orange { background-color: #f2711c }
 | 
				
			||||||
 | 
					.pill.yellow { background-color: #fbbd08 }
 | 
				
			||||||
 | 
					.pill.olive { background-color: #b5cc18 }
 | 
				
			||||||
 | 
					.pill.green { background-color: #21ba45 }
 | 
				
			||||||
 | 
					.pill.teal { background-color: #00b5ad }
 | 
				
			||||||
 | 
					.pill.blue { background-color: #2185d0 }
 | 
				
			||||||
 | 
					.pill.violet { background-color: #6435c9 }
 | 
				
			||||||
 | 
					.pill.purple { background-color: #a333c8 }
 | 
				
			||||||
 | 
					.pill.pink { background-color: #e03997 }
 | 
				
			||||||
 | 
					.pill.brown { background-color: #a5673f }
 | 
				
			||||||
 | 
					.pill.grey { background-color: #767676 }
 | 
				
			||||||
 | 
					.pill.black { background-color: #1b1c1d }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
	<div>
 | 
						<div>
 | 
				
			||||||
		I'm a calednar yo
 | 
							<div class="calendar">
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
								<div v-for="day in calendar.weekdays" class="day">
 | 
				
			||||||
 | 
									{{ day }}
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div v-for="day in calendar.days" class="day" 
 | 
				
			||||||
 | 
									:class="{
 | 
				
			||||||
 | 
										'today':day == calendar.today,
 | 
				
			||||||
 | 
										'active-entry':calendar.dateCode == `${day}.${calendar.month}.${calendar.year}`,
 | 
				
			||||||
 | 
										'has-data':cycleData[`${day}.${calendar.month}.${calendar.year}`],
 | 
				
			||||||
 | 
										'no-data':showDayDataColor(day),
 | 
				
			||||||
 | 
									}">
 | 
				
			||||||
 | 
									<!-- v-on:click="openDayData(`${day}.${calendar.month}.${calendar.year}`)" -->
 | 
				
			||||||
 | 
									<span class="number">{{ day }}</span>
 | 
				
			||||||
 | 
									<!-- {{ `${day}.${calendar.month}.${calendar.year}` }} -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
									<span class="pill-container" v-for="(entry, dateCode) in getChartData" v-if="dateCode == `${day}.${calendar.month}.${calendar.year}`">
 | 
				
			||||||
 | 
										<span 
 | 
				
			||||||
 | 
											v-for="(dayData, fieldId) in entry" 
 | 
				
			||||||
 | 
											v-if="showZeroValuesCheck(dayData.value, fieldId)"
 | 
				
			||||||
 | 
											class="pill" 
 | 
				
			||||||
 | 
											:class="[$parent.$parent.getFieldColor(fieldId), { 
 | 
				
			||||||
 | 
												'did-next':dayData.didNext, 
 | 
				
			||||||
 | 
												'did-last':dayData.didLast,
 | 
				
			||||||
 | 
												'last-high':dayData.lastHigh,
 | 
				
			||||||
 | 
												'next-high':dayData.nextHigh,
 | 
				
			||||||
 | 
												}]">
 | 
				
			||||||
 | 
											<!-- 'zero-day':dayData.value == lowestGraphValue, -->
 | 
				
			||||||
 | 
											<!-- <i v-if="dayData.value != 0" :class="`tiny ${$parent.$parent.getFieldColor(fieldId)} ${$parent.$parent.getFieldIcon(fieldId)} icon`"></i> -->
 | 
				
			||||||
 | 
											<!-- <span v-else class="icon-spacer"></span> 
 | 
				
			||||||
 | 
												:style="{height:(Math.round(dayData.value*5)+'px')}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											-->
 | 
				
			||||||
 | 
											<span v-if="dayData.value > lowestGraphValue-1" class="big-day">
 | 
				
			||||||
 | 
												<i v-if="!hideIcons" :class="`tiny white ${$parent.$parent.getFieldIcon(fieldId)} icon`"></i>
 | 
				
			||||||
 | 
												<span v-if="!hideValues">
 | 
				
			||||||
 | 
													{{ getDayValue(fieldId, dayData.value) }}
 | 
				
			||||||
 | 
												</span>
 | 
				
			||||||
 | 
											</span>
 | 
				
			||||||
 | 
										</span>
 | 
				
			||||||
 | 
									</span>
 | 
				
			||||||
 | 
									<!-- <span v-for="fieldId in graph.fieldIds"></span> -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// let chartData = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export default {
 | 
						export default {
 | 
				
			||||||
		props: [
 | 
							props: [
 | 
				
			||||||
			'graphOptions', // options associated with this graph
 | 
								'graph', // options associated with this graph
 | 
				
			||||||
 | 
								'userFields', // all field attributes
 | 
				
			||||||
			'tempChartDays', // number of days to display
 | 
								'tempChartDays', // number of days to display
 | 
				
			||||||
			'cycleData', // all users metric data
 | 
								'cycleData', // all users metric data
 | 
				
			||||||
 | 
								'editGraphs', // display additional edit options
 | 
				
			||||||
 | 
								// Graph options
 | 
				
			||||||
 | 
								'showZeroValues', // Hide graph data with value of zero
 | 
				
			||||||
 | 
								'showTextValues', // Show button text or button value 
 | 
				
			||||||
 | 
								'connectDays', // Calculates next and previous day connections.
 | 
				
			||||||
 | 
								'hideValues', // Hide all values on the graph
 | 
				
			||||||
 | 
								'hideIcons', // option to hide icons
 | 
				
			||||||
		],
 | 
							],
 | 
				
			||||||
		data: function(){
 | 
							data: function(){
 | 
				
			||||||
			return {
 | 
								return {
 | 
				
			||||||
				openModel:true,
 | 
									openModel:true,
 | 
				
			||||||
 | 
									calendar: {
 | 
				
			||||||
 | 
										dateObject: null,
 | 
				
			||||||
 | 
										dateCode: null,
 | 
				
			||||||
 | 
										monthName: '',
 | 
				
			||||||
 | 
										dayName:'',
 | 
				
			||||||
 | 
										daysAgo:0,
 | 
				
			||||||
 | 
										month: '',
 | 
				
			||||||
 | 
										year: '',
 | 
				
			||||||
 | 
										days: [],
 | 
				
			||||||
 | 
										weekdays: ['S','M','T','W','T','F','S'],
 | 
				
			||||||
 | 
										today: 0,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									chartDateCodes: [], // array of date codes in chart
 | 
				
			||||||
 | 
									listDateCodes: [],
 | 
				
			||||||
 | 
									dayList: true,
 | 
				
			||||||
 | 
									lowestGraphValue: 0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							mounted(){
 | 
				
			||||||
 | 
								this.setupCalendar(new Date())
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							computed: {
 | 
				
			||||||
 | 
								getChartData(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									let chartData = {}
 | 
				
			||||||
 | 
									let chartValues = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// iterate every day in month by day code
 | 
				
			||||||
 | 
									this.chartDateCodes.forEach((chartDayCode, codeIndex) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// lookup data for that day
 | 
				
			||||||
 | 
										const cycleDayData = this.cycleData[chartDayCode]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// if chart data is set for this day
 | 
				
			||||||
 | 
										if( cycleDayData && Object.keys(cycleDayData).length > 0){
 | 
				
			||||||
 | 
											chartData[chartDayCode] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// go over each field to be displayed on graph
 | 
				
			||||||
 | 
											this.graph.fieldIds.forEach((graphFieldId) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												if( cycleDayData[graphFieldId] == undefined ){
 | 
				
			||||||
 | 
													return
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// track all chart values
 | 
				
			||||||
 | 
												chartValues.push(cycleDayData[graphFieldId])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												chartData[chartDayCode][graphFieldId] = {
 | 
				
			||||||
 | 
													didLast: false,
 | 
				
			||||||
 | 
													lastHigh: false,
 | 
				
			||||||
 | 
													didNext: false,
 | 
				
			||||||
 | 
													nextHigh: false,
 | 
				
			||||||
 | 
													value: cycleDayData[graphFieldId]
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.lowestGraphValue = Math.min(...chartValues)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// determine next and previous states for display
 | 
				
			||||||
 | 
									this.chartDateCodes.forEach((chartDayCode, codeIndex) => {
 | 
				
			||||||
 | 
										if(chartData[chartDayCode]  && this.connectDays){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											const previousDateCode = this.chartDateCodes[codeIndex-1]
 | 
				
			||||||
 | 
											const nextDateCode = this.chartDateCodes[codeIndex+1]
 | 
				
			||||||
 | 
											
 | 
				
			||||||
 | 
											Object.keys(chartData[chartDayCode]).forEach((graphFieldId) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												const currentValue = chartData[chartDayCode][graphFieldId].value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// check for previous entry
 | 
				
			||||||
 | 
												if( chartData[previousDateCode] && chartData[previousDateCode][graphFieldId] ){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													chartData[chartDayCode][graphFieldId].didLast = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													// set low value flag
 | 
				
			||||||
 | 
													const lastHigh = chartData[previousDateCode][graphFieldId].value > 0
 | 
				
			||||||
 | 
													chartData[chartDayCode][graphFieldId].lastHigh = lastHigh && currentValue == 0
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// check for next entry
 | 
				
			||||||
 | 
												if( chartData[nextDateCode] && chartData[nextDateCode][graphFieldId] ){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													chartData[chartDayCode][graphFieldId].didNext = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													// set low value flag
 | 
				
			||||||
 | 
													const nextHigh = chartData[nextDateCode][graphFieldId].value > 0
 | 
				
			||||||
 | 
													chartData[chartDayCode][graphFieldId].nextHigh = nextHigh && currentValue == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// console.log(chartData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return chartData
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		methods: {
 | 
							methods: {
 | 
				
			||||||
			closeModel(){
 | 
								showZeroValuesCheck(dayValue, fieldId){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// if graph type is boolean or there are two options
 | 
				
			||||||
 | 
									let isBooleanField = this.userFields[fieldId].type == 'boolean'
 | 
				
			||||||
 | 
									if(this.userFields[fieldId].customOptions){
 | 
				
			||||||
 | 
										let options = this.userFields[fieldId].customOptions
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
										isBooleanField = options.split(',').length == 2
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(isBooleanField && !this.showZeroValues){
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
										const parsedValue = this.getDayValue(fieldId, dayValue)
 | 
				
			||||||
 | 
										if(parsedValue == 'Yes'){
 | 
				
			||||||
 | 
											return true
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											return false
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return this.showZeroValues || dayValue > this.lowestGraphValue
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								getDayValue(fieldId, value){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if( !this.showTextValues ){
 | 
				
			||||||
 | 
										return value
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									let options = 'error, Yes, No'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(this.userFields[fieldId].customOptions){
 | 
				
			||||||
 | 
										options = this.userFields[fieldId].customOptions
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const values = options.split(',')
 | 
				
			||||||
 | 
									const selection = String(values[value]).trim()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return selection
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								displayDayFromCode(dateCode){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const parts = dateCode.split('.')
 | 
				
			||||||
 | 
									return `${parts[0]}`
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								showDayDataColor(day){
 | 
				
			||||||
 | 
									// Determine if day has any data set
 | 
				
			||||||
 | 
									if(day == ''){
 | 
				
			||||||
 | 
										return false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return !(this.cycleData[`${day}.${this.calendar.month}.${this.calendar.year}`])
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								generateDateCode(date){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const dateSetup = [
 | 
				
			||||||
 | 
										date.getDate(), // 1-31 (Day)
 | 
				
			||||||
 | 
										date.getMonth()+1, // 0-11 (Month)
 | 
				
			||||||
 | 
										date.getFullYear(), // 1888-2022 (Year)
 | 
				
			||||||
 | 
									]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return dateSetup.join('.')
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								setupCalendar(date){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// visualize each day change
 | 
				
			||||||
 | 
									this.working = true
 | 
				
			||||||
 | 
									setTimeout(() => {
 | 
				
			||||||
 | 
										this.working = false
 | 
				
			||||||
 | 
									}, 500)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(!date && this.dateObject){
 | 
				
			||||||
 | 
										date = this.dateObject
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if(!date){
 | 
				
			||||||
 | 
										date = new Date()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.calendar.dateObject = date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.calendar.dateCode = this.generateDateCode(date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// calculate days ago since current date
 | 
				
			||||||
 | 
									const now = new Date()
 | 
				
			||||||
 | 
									const diffSeconds = Math.floor((now - date) / 1000) // subtract unix timestamps, convert MS to S
 | 
				
			||||||
 | 
									const dayInterval = diffSeconds / 86400 // seconds in a day
 | 
				
			||||||
 | 
									this.calendar.daysAgo = Math.floor(dayInterval)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// ------------
 | 
				
			||||||
 | 
									// setup calendar display
 | 
				
			||||||
 | 
									var y = date.getFullYear()
 | 
				
			||||||
 | 
									var m = date.getMonth()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									var firstDay = new Date(y, m, 1);
 | 
				
			||||||
 | 
									var lastDay = new Date(y, m + 1, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									function getDaysInMonth(year, month) {
 | 
				
			||||||
 | 
										return new Date(year, month, 0).getDate();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const currentYear = date.getFullYear();
 | 
				
			||||||
 | 
									const currentMonth = date.getMonth() + 1;
 | 
				
			||||||
 | 
									this.calendar.monthName = date.toLocaleString("en-US", { month: "long" });
 | 
				
			||||||
 | 
									this.calendar.dayName = date.toLocaleString("en-US", { weekday: "long" });
 | 
				
			||||||
 | 
									this.calendar.year = currentYear
 | 
				
			||||||
 | 
									const daysInCurrentMonth = getDaysInMonth(currentYear, currentMonth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const monthStartDay = firstDay.getDay()
 | 
				
			||||||
 | 
									let days = Array(monthStartDay).fill(""); // Pad days to start on correct weekday
 | 
				
			||||||
 | 
									for (let i = 0; i < daysInCurrentMonth; i++) {
 | 
				
			||||||
 | 
										days.push(i+1)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									this.calendar.days = days
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// set today
 | 
				
			||||||
 | 
									this.calendar.today = date.getDate()
 | 
				
			||||||
 | 
									this.calendar.month = date.getMonth()+1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// setup date codes for key matching on calendar
 | 
				
			||||||
 | 
									this.calendar.days.forEach((day) => {
 | 
				
			||||||
 | 
										if( day !== "" ){
 | 
				
			||||||
 | 
											let dateDay = new Date(y, m, day);
 | 
				
			||||||
 | 
											let dayCode = this.generateDateCode(dateDay)
 | 
				
			||||||
 | 
											this.chartDateCodes.push(dayCode)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// generate past date codes for list
 | 
				
			||||||
 | 
									for (let i = 0; i < this.tempChartDays; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										const now = new Date()
 | 
				
			||||||
 | 
										const pastDate = now.setDate(now.getDate() - i)
 | 
				
			||||||
 | 
										const pastDateObj = new Date(pastDate)
 | 
				
			||||||
 | 
										const newCode = this.generateDateCode(pastDateObj)
 | 
				
			||||||
 | 
										this.listDateCodes.push(newCode)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
									// return codes.reverse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/*
 | 
				
			||||||
 | 
										October 2022
 | 
				
			||||||
 | 
									S M T W T F S
 | 
				
			||||||
 | 
									  1 2 3 4 5 6
 | 
				
			||||||
 | 
									7 8 9 
 | 
				
			||||||
 | 
									*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// -------
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,7 @@
 | 
				
			|||||||
	@media only screen and (max-width: 740px) {
 | 
						@media only screen and (max-width: 740px) {
 | 
				
			||||||
		.modal-content {
 | 
							.modal-content {
 | 
				
			||||||
			width: 100%;
 | 
								width: 100%;
 | 
				
			||||||
			padding-bottom: 55px;
 | 
					/*			padding-bottom: 55px;*/
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1369,6 +1369,7 @@
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		.edit-button {
 | 
							.edit-button {
 | 
				
			||||||
			padding: 6px 0px 0;
 | 
								padding: 6px 0px 0;
 | 
				
			||||||
 | 
								flex-grow: 1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		.edit-button > span:not(.ui) {
 | 
							.edit-button > span:not(.ui) {
 | 
				
			||||||
			display: none;
 | 
								display: none;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -285,23 +285,28 @@
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
			justClosed(){
 | 
								justClosed(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Dont do anything when not is closed.
 | 
				
			||||||
 | 
									// Its already saved, this will make interface feel snappy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Scroll note into view
 | 
									// Scroll note into view
 | 
				
			||||||
				this.$el.scrollIntoView({
 | 
									// this.$el.scrollIntoView({
 | 
				
			||||||
					behavior: 'smooth',
 | 
									// 	behavior: 'smooth',
 | 
				
			||||||
					block: 'center',
 | 
									// 	block: 'center',
 | 
				
			||||||
					inline: 'center'
 | 
									// 	inline: 'center'
 | 
				
			||||||
				})
 | 
									// })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				//After scroll, trigger green outline animation
 | 
									// this.$bus.$emit('notification','Note Saved')
 | 
				
			||||||
				setTimeout(() => {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					this.triggerClosedAnimation = true
 | 
									// //After scroll, trigger green outline animation
 | 
				
			||||||
					setTimeout(()=>{
 | 
									// setTimeout(() => {
 | 
				
			||||||
						//After 3 seconds, hide it
 | 
					 | 
				
			||||||
						this.triggerClosedAnimation = false
 | 
					 | 
				
			||||||
					}, 1500)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				}, 500)
 | 
									// 	this.triggerClosedAnimation = true
 | 
				
			||||||
 | 
									// 	setTimeout(()=>{
 | 
				
			||||||
 | 
									// 		//After 3 seconds, hide it
 | 
				
			||||||
 | 
									// 		this.triggerClosedAnimation = false
 | 
				
			||||||
 | 
									// 	}, 1500)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// }, 500)
 | 
				
			||||||
				
 | 
									
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -172,15 +172,16 @@ const SquireButtonFunctions = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			//Fetch the container
 | 
								//Fetch the container
 | 
				
			||||||
			let container = document.getElementById('squire-id')
 | 
								let container = document.getElementById('squire-id')
 | 
				
			||||||
 | 
								this.$router.go(-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Array.from( container.getElementsByClassName('active') ).forEach(item => {
 | 
								setTimeout(()=>{
 | 
				
			||||||
				item.classList.remove('active');
 | 
					
 | 
				
			||||||
			})
 | 
									Array.from( container.getElementsByClassName('active') ).forEach(item => {
 | 
				
			||||||
 | 
										item.classList.remove('active');
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
								},600)
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			//Close menu if user is on mobile, then sort list
 | 
					 | 
				
			||||||
			if(this.$store.getters.getIsUserOnMobile){
 | 
					 | 
				
			||||||
				this.$router.go(-1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		deleteCompletedListItems(){
 | 
							deleteCompletedListItems(){
 | 
				
			||||||
			//
 | 
								//
 | 
				
			||||||
@@ -190,53 +191,57 @@ const SquireButtonFunctions = {
 | 
				
			|||||||
			//Fetch the container
 | 
								//Fetch the container
 | 
				
			||||||
			let container = document.getElementById('squire-id')
 | 
								let container = document.getElementById('squire-id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			//Go through each item, on first level, look for Unordered Lists
 | 
								//Close menu if user is on mobile, then sort list	
 | 
				
			||||||
			container.childNodes.forEach( (node) => {
 | 
								this.$router.go(-1)
 | 
				
			||||||
				if(node.nodeName == 'UL'){
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					//Create two categories, done and not done list items
 | 
								setTimeout(()=>{
 | 
				
			||||||
					let undoneElements = document.createDocumentFragment()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					//Go through each item in each list we found
 | 
									//Go through each item, on first level, look for Unordered Lists
 | 
				
			||||||
					node.childNodes.forEach( (checkListItem, index) => {
 | 
									container.childNodes.forEach( (node) => {
 | 
				
			||||||
 | 
										if(node.nodeName == 'UL'){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
 | 
											//Create two categories, done and not done list items
 | 
				
			||||||
						if(checkListItem.nodeName == 'UL'){
 | 
											let undoneElements = document.createDocumentFragment()
 | 
				
			||||||
							return
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
						//Check if list item has active class
 | 
											//Go through each item in each list we found
 | 
				
			||||||
						const checkedItem = checkListItem.classList.contains('active')
 | 
											node.childNodes.forEach( (checkListItem, index) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						//Check if the next item is a list, Keep lists with intented items together
 | 
												//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
 | 
				
			||||||
						let sublist = null
 | 
												if(checkListItem.nodeName == 'UL'){
 | 
				
			||||||
						if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
 | 
													return
 | 
				
			||||||
							sublist = node.childNodes[index+1]
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						//Push checked items and their sub lists to the done set
 | 
					 | 
				
			||||||
						if(!checkedItem){
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							undoneElements.appendChild( checkListItem.cloneNode(true) )
 | 
					 | 
				
			||||||
							if(sublist){
 | 
					 | 
				
			||||||
								undoneElements.appendChild( sublist.cloneNode(true) )
 | 
					 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						}
 | 
												//Check if list item has active class
 | 
				
			||||||
 | 
												const checkedItem = checkListItem.classList.contains('active')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					})
 | 
												//Check if the next item is a list, Keep lists with intented items together
 | 
				
			||||||
 | 
												let sublist = null
 | 
				
			||||||
 | 
												if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
 | 
				
			||||||
 | 
													sublist = node.childNodes[index+1]
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					//Remove all HTML from node, push unfinished items, then finished below them
 | 
												//Push checked items and their sub lists to the done set
 | 
				
			||||||
					node.innerHTML = null
 | 
												if(!checkedItem){
 | 
				
			||||||
					node.appendChild(undoneElements)
 | 
					
 | 
				
			||||||
 | 
													undoneElements.appendChild( checkListItem.cloneNode(true) )
 | 
				
			||||||
 | 
													if(sublist){
 | 
				
			||||||
 | 
														undoneElements.appendChild( sublist.cloneNode(true) )
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											//Remove all HTML from node, push unfinished items, then finished below them
 | 
				
			||||||
 | 
											node.innerHTML = null
 | 
				
			||||||
 | 
											node.appendChild(undoneElements)
 | 
				
			||||||
 | 
											
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								}, 600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			//Close menu if user is on mobile, then sort list
 | 
					 | 
				
			||||||
			if(this.$store.getters.getIsUserOnMobile){
 | 
					 | 
				
			||||||
				this.$router.go(-1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		sortList(){
 | 
							sortList(){
 | 
				
			||||||
			//
 | 
								//
 | 
				
			||||||
@@ -246,61 +251,65 @@ const SquireButtonFunctions = {
 | 
				
			|||||||
			//Fetch the container
 | 
								//Fetch the container
 | 
				
			||||||
			let container = document.getElementById('squire-id')
 | 
								let container = document.getElementById('squire-id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			//Go through each item, on first level, look for Unordered Lists
 | 
					 | 
				
			||||||
			container.childNodes.forEach( (node) => {
 | 
					 | 
				
			||||||
				if(node.nodeName == 'UL'){
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					//Create two categories, done and not done list items
 | 
					 | 
				
			||||||
					let doneElements = document.createDocumentFragment()
 | 
					 | 
				
			||||||
					let undoneElements = document.createDocumentFragment()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					//Go through each item in each list we found
 | 
					 | 
				
			||||||
					node.childNodes.forEach( (checkListItem, index) => {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
 | 
					 | 
				
			||||||
						if(checkListItem.nodeName == 'UL'){
 | 
					 | 
				
			||||||
							return
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						//Check if list item has active class
 | 
					 | 
				
			||||||
						const checkedItem = checkListItem.classList.contains('active')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						//Check if the next item is a list, Keep lists with intented items together
 | 
					 | 
				
			||||||
						let sublist = null
 | 
					 | 
				
			||||||
						if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
 | 
					 | 
				
			||||||
							sublist = node.childNodes[index+1]
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						//Push checked items and their sub lists to the done set
 | 
					 | 
				
			||||||
						if(checkedItem){
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							doneElements.appendChild( checkListItem.cloneNode(true) )
 | 
					 | 
				
			||||||
							if(sublist){
 | 
					 | 
				
			||||||
								doneElements.appendChild( sublist.cloneNode(true) )
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						} else {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							undoneElements.appendChild( checkListItem.cloneNode(true) )
 | 
					 | 
				
			||||||
							if(sublist){
 | 
					 | 
				
			||||||
								undoneElements.appendChild( sublist.cloneNode(true) )
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					//Remove all HTML from node, push unfinished items, then finished below them
 | 
					 | 
				
			||||||
					node.innerHTML = null
 | 
					 | 
				
			||||||
					node.appendChild(undoneElements)
 | 
					 | 
				
			||||||
					node.appendChild(doneElements)
 | 
					 | 
				
			||||||
					
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			//Close menu if user is on mobile	
 | 
								//Close menu if user is on mobile	
 | 
				
			||||||
			if(this.$store.getters.getIsUserOnMobile){
 | 
								this.$router.go(-1)
 | 
				
			||||||
				this.$router.go(-1)
 | 
					
 | 
				
			||||||
			}
 | 
								setTimeout(()=>{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									//Go through each item, on first level, look for Unordered Lists
 | 
				
			||||||
 | 
									container.childNodes.forEach( (node) => {
 | 
				
			||||||
 | 
										if(node.nodeName == 'UL'){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											//Create two categories, done and not done list items
 | 
				
			||||||
 | 
											let doneElements = document.createDocumentFragment()
 | 
				
			||||||
 | 
											let undoneElements = document.createDocumentFragment()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											//Go through each item in each list we found
 | 
				
			||||||
 | 
											node.childNodes.forEach( (checkListItem, index) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
 | 
				
			||||||
 | 
												if(checkListItem.nodeName == 'UL'){
 | 
				
			||||||
 | 
													return
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												//Check if list item has active class
 | 
				
			||||||
 | 
												const checkedItem = checkListItem.classList.contains('active')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												//Check if the next item is a list, Keep lists with intented items together
 | 
				
			||||||
 | 
												let sublist = null
 | 
				
			||||||
 | 
												if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
 | 
				
			||||||
 | 
													sublist = node.childNodes[index+1]
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												//Push checked items and their sub lists to the done set
 | 
				
			||||||
 | 
												if(checkedItem){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													doneElements.appendChild( checkListItem.cloneNode(true) )
 | 
				
			||||||
 | 
													if(sublist){
 | 
				
			||||||
 | 
														doneElements.appendChild( sublist.cloneNode(true) )
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													undoneElements.appendChild( checkListItem.cloneNode(true) )
 | 
				
			||||||
 | 
													if(sublist){
 | 
				
			||||||
 | 
														undoneElements.appendChild( sublist.cloneNode(true) )
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											//Remove all HTML from node, push unfinished items, then finished below them
 | 
				
			||||||
 | 
											node.innerHTML = null
 | 
				
			||||||
 | 
											node.appendChild(undoneElements)
 | 
				
			||||||
 | 
											node.appendChild(doneElements)
 | 
				
			||||||
 | 
											
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								},600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		calculateMath(){
 | 
							calculateMath(){
 | 
				
			||||||
			//
 | 
								//
 | 
				
			||||||
@@ -310,6 +319,9 @@ const SquireButtonFunctions = {
 | 
				
			|||||||
			//Fetch the container
 | 
								//Fetch the container
 | 
				
			||||||
			let container = document.getElementById('squire-id')
 | 
								let container = document.getElementById('squire-id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								//Close menu if user is on mobile, then sort list	
 | 
				
			||||||
 | 
								this.$router.go(-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// simple function that trys to evaluate javascript
 | 
								// simple function that trys to evaluate javascript
 | 
				
			||||||
			const shittyMath = (string) => {
 | 
								const shittyMath = (string) => {
 | 
				
			||||||
				//Remove all chars but math chars
 | 
									//Remove all chars but math chars
 | 
				
			||||||
@@ -322,38 +334,39 @@ const SquireButtonFunctions = {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			//Go through each item, on first level, look for Unordered Lists
 | 
								setTimeout(()=>{
 | 
				
			||||||
			container.childNodes.forEach( (node) => {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				const line = node.innerText.trim()
 | 
									//Go through each item, on first level, look for Unordered Lists
 | 
				
			||||||
 | 
									container.childNodes.forEach( (node) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// = sign exists and its the last character in the string
 | 
										const line = node.innerText.trim()
 | 
				
			||||||
				if(line.indexOf('=') != -1 && (line.length-1) == line.indexOf('=')){
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					//Pull out everything before the formula and try to evaluate it
 | 
										// = sign exists and its the last character in the string
 | 
				
			||||||
					const formula = line.split('=').shift()
 | 
										if(line.indexOf('=') != -1 && (line.length-1) == line.indexOf('=')){
 | 
				
			||||||
					const output = shittyMath(formula)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					//If its a number and didn't throw an error, update the line
 | 
											//Pull out everything before the formula and try to evaluate it
 | 
				
			||||||
					if(!isNaN(output) && output != null){
 | 
											const formula = line.split('=').shift()
 | 
				
			||||||
 | 
											const output = shittyMath(formula)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						//Since there is HTML in the line, splice in the number after the = sign
 | 
											//If its a number and didn't throw an error, update the line
 | 
				
			||||||
						let equalLocation = node.innerHTML.indexOf('=')
 | 
											if(!isNaN(output) && output != null){
 | 
				
			||||||
						let newLine = node.innerHTML.slice(0, equalLocation+1).trim()
 | 
					 | 
				
			||||||
						newLine += ` ${output}`
 | 
					 | 
				
			||||||
						newLine += node.innerHTML.slice(equalLocation+1).trim()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
						//Slam in that new HTML with the output
 | 
												//Since there is HTML in the line, splice in the number after the = sign
 | 
				
			||||||
						node.innerHTML = newLine
 | 
												let equalLocation = node.innerHTML.indexOf('=')
 | 
				
			||||||
 | 
												let newLine = node.innerHTML.slice(0, equalLocation+1).trim()
 | 
				
			||||||
 | 
												newLine += ` ${output}`
 | 
				
			||||||
 | 
												newLine += node.innerHTML.slice(equalLocation+1).trim()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												//Slam in that new HTML with the output
 | 
				
			||||||
 | 
												node.innerHTML = newLine
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
					
 | 
										
 | 
				
			||||||
			})
 | 
									})
 | 
				
			||||||
 | 
								},600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			//Close menu if user is on mobile, then sort list
 | 
					 | 
				
			||||||
			if(this.$store.getters.getIsUserOnMobile){
 | 
					 | 
				
			||||||
				this.$router.go(-1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		setText(inText){
 | 
							setText(inText){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<style>
 | 
					<style scoped>
 | 
				
			||||||
	div.no-padding {
 | 
						div.no-padding {
 | 
				
			||||||
		padding: 10px 0 40px !important;
 | 
							padding: 10px 0 40px !important;
 | 
				
			||||||
		box-sizing: border-box;
 | 
							box-sizing: border-box;
 | 
				
			||||||
@@ -233,16 +233,26 @@
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	.an-graph {
 | 
						.days-ago-display {
 | 
				
			||||||
		background: #fefefe82;
 | 
							font-size: 0.7em;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.input-grid {
 | 
				
			||||||
 | 
						    display: flex;
 | 
				
			||||||
 | 
							flex-direction: column;
 | 
				
			||||||
 | 
							flex-wrap: nowrap;
 | 
				
			||||||
 | 
							justify-content: flex-start;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.bottom.aligned.row {
 | 
				
			||||||
 | 
							flex: 1 1 auto;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
	<div class="squire-box no-padding" ref="scrollcontainer">
 | 
						<div class="squire-box no-padding" ref="scrollcontainer">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<!-- intro modal  -->
 | 
				
			||||||
		<modal v-if="!noteId" :click-out-close="false">
 | 
							<modal v-if="!noteId" :click-out-close="false">
 | 
				
			||||||
			<div class="ui segment">
 | 
								<div class="ui segment">
 | 
				
			||||||
				<div class="ui center aligned middle aligned grid">
 | 
									<div class="ui center aligned middle aligned grid">
 | 
				
			||||||
@@ -273,10 +283,10 @@
 | 
				
			|||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<!-- section 1 entry -->
 | 
							<!-- section 1 entry -->
 | 
				
			||||||
		<section class="swipe-section">
 | 
							<section class="swipe-section" v-if="true">
 | 
				
			||||||
			<!-- <delete-button v-if="noteId > 0" class="ui small button" :note-id="noteId" /> -->
 | 
								<!-- <delete-button v-if="noteId > 0" class="ui small button" :note-id="noteId" /> -->
 | 
				
			||||||
			<div class="ui small centered dividing header">
 | 
								<div class="ui small centered dividing header">
 | 
				
			||||||
				Metric Tracking Bet
 | 
									Metric Tracking
 | 
				
			||||||
				<span class="sub header"><i class="small lock icon"></i>All data Encrypted. Only accessible by you.</span>
 | 
									<span class="sub header"><i class="small lock icon"></i>All data Encrypted. Only accessible by you.</span>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -314,10 +324,18 @@
 | 
				
			|||||||
			<!-- data input -->
 | 
								<!-- data input -->
 | 
				
			||||||
			<div class="ui basic segment">
 | 
								<div class="ui basic segment">
 | 
				
			||||||
				<div class="ui very compact grid">
 | 
									<div class="ui very compact grid">
 | 
				
			||||||
					<div class="ui twelve wide middle aligned column">
 | 
										<div class="four wide column"></div>
 | 
				
			||||||
 | 
										<div class="ui eight wide middle aligned center aligned column">
 | 
				
			||||||
						<div class="ui header">
 | 
											<div class="ui header">
 | 
				
			||||||
							Entry for {{ calendar.monthName }},
 | 
												<span :class="{'loading-day-title':loadingDay}">
 | 
				
			||||||
							<span :class="{'loading-day-title':loadingDay}">{{ calendar.today }}</span>
 | 
													{{ calendar.monthName }}
 | 
				
			||||||
 | 
													{{ calendar.today }}, {{ calendar.dayName }}
 | 
				
			||||||
 | 
													<span class="days-ago-display">
 | 
				
			||||||
 | 
														<!-- <span v-if="calendar.daysAgo == 0">Today</span> -->
 | 
				
			||||||
 | 
														<span v-if="calendar.daysAgo == 1"><br>{{ calendar.daysAgo }} day ago</span>
 | 
				
			||||||
 | 
														<span v-if="calendar.daysAgo > 1"><br>{{ calendar.daysAgo }} days ago</span>
 | 
				
			||||||
 | 
													</span>
 | 
				
			||||||
 | 
												</span>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div class="ui four wide right aligned middle aligned column">
 | 
										<div class="ui four wide right aligned middle aligned column">
 | 
				
			||||||
@@ -339,77 +357,89 @@
 | 
				
			|||||||
				<div class="ui form" :class="{'loading-day':loadingDay}">
 | 
									<div class="ui form" :class="{'loading-day':loadingDay}">
 | 
				
			||||||
					<draggable v-model="fields" class="ui compact grid" ghost-class="ghost" @end="onDragEnd" handle=".draggable">
 | 
										<draggable v-model="fields" class="ui compact grid" ghost-class="ghost" @end="onDragEnd" handle=".draggable">
 | 
				
			||||||
						<div v-for="field in fields" :key="field" 
 | 
											<div v-for="field in fields" :key="field" 
 | 
				
			||||||
							:class="userFields[field]?.width ? userFields[field]?.width+' wide column':'eight wide column'">
 | 
												:class="userFields[field]?.width ? userFields[field]?.width+' wide stretched column':'eight wide stretched column'">
 | 
				
			||||||
							<!-- field label display -->
 | 
					 | 
				
			||||||
							<div class="ui very compact grid">
 | 
					 | 
				
			||||||
								<div class="ui sixteen wide center aligned column">
 | 
					 | 
				
			||||||
									<i :class="`${getFieldColor(field)} ${getFieldIcon(field)} icon`"></i>
 | 
					 | 
				
			||||||
									<b>{{ userFields[field]?.label }}</b>
 | 
					 | 
				
			||||||
									<span>{{  field }}</span>
 | 
					 | 
				
			||||||
								</div>
 | 
					 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
							<!-- float -->
 | 
												<div class="ui very compact grid input-grid"> 
 | 
				
			||||||
							<div v-if="userFields[field]?.type == 'float'" class="ui fluid input">
 | 
													<!-- field label display -->
 | 
				
			||||||
								<input type="text" :placeholder="userFields[field]?.label" v-on:keyup="e => saveField(field, e.target.value)" :value="openDay[field]">
 | 
													<div class="row">
 | 
				
			||||||
							</div>
 | 
														<div class="ui sixteen wide column">
 | 
				
			||||||
 | 
															<b><i :class="`${getFieldColor(field)} ${getFieldIcon(field)} icon`"></i>
 | 
				
			||||||
							<!-- range -->
 | 
															{{ userFields[field]?.label }}</b>
 | 
				
			||||||
							<div v-if="userFields[field]?.type == 'shortRange'">
 | 
															<!-- <span>{{  field }}</span> -->
 | 
				
			||||||
								<div :class="{green:(openDay[field] == 1)}" v-on:click="saveField(field, 1)" class="ui button">1</div>
 | 
					 | 
				
			||||||
								<div :class="{green:(openDay[field] == 2)}" v-on:click="saveField(field, 2)" class="ui button">2</div>
 | 
					 | 
				
			||||||
								<div :class="{green:(openDay[field] == 3)}" v-on:click="saveField(field, 3)" class="ui button">3</div>
 | 
					 | 
				
			||||||
								<div :class="{green:(openDay[field] == 4)}" v-on:click="saveField(field, 4)" class="ui button">4</div>
 | 
					 | 
				
			||||||
								<div :class="{green:(openDay[field] == 5)}" v-on:click="saveField(field, 5)" class="ui button">5</div>
 | 
					 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							<!-- text area -->
 | 
					 | 
				
			||||||
							<div v-if="userFields[field]?.type == 'text'">
 | 
					 | 
				
			||||||
								<textarea rows="3" v-on:keyup="e => saveField(field, e.target.value, 2000)" :value="openDay[field]">
 | 
					 | 
				
			||||||
								</textarea>
 | 
					 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							<!-- boolean -->
 | 
					 | 
				
			||||||
							<div v-if="userFields[field]?.type == 'boolean'">
 | 
					 | 
				
			||||||
								<div :class="{green:(openDay[field] == 1)}" v-on:click="saveField(field, 1)" class="ui button">Yes</div>
 | 
					 | 
				
			||||||
								<div :class="{green:(openDay[field] == 2)}" v-on:click="saveField(field, 2)" class="ui button">No</div>
 | 
					 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							<div v-if="['sex','period','mucus','pms'].includes(userFields[field]?.type)">
 | 
					 | 
				
			||||||
								<div class="option-buttons">
 | 
					 | 
				
			||||||
									<div :class="{green:(openDay[field] == key)}" v-on:click="saveField(field, key)" class="ui compact button" v-for="(item,key) in fieldTypes[userFields[field]?.type].split(',')">{{ item }}</div>
 | 
					 | 
				
			||||||
								</div>
 | 
					 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							<div v-if="userFields[field]?.type == 'custom'">
 | 
					 | 
				
			||||||
								<div class="option-buttons">
 | 
					 | 
				
			||||||
									<div 
 | 
					 | 
				
			||||||
										v-for="(item, value) in userFields[field]?.customOptions.split(',')"
 | 
					 | 
				
			||||||
										v-on:click="saveField(field, value)" 
 | 
					 | 
				
			||||||
										:class="{green:(openDay[field] == value) && openDay[field] !== ''}"
 | 
					 | 
				
			||||||
										class="ui compact button">
 | 
					 | 
				
			||||||
											{{ item.trim() }}
 | 
					 | 
				
			||||||
									</div>
 | 
														</div>
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													<!-- input display -->
 | 
				
			||||||
 | 
													<div class="row">
 | 
				
			||||||
 | 
														<div class="sixteen wide column">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							<div class="ui very compact grid">
 | 
															<!-- float -->
 | 
				
			||||||
								<div class="ui six wide column">
 | 
															<div v-if="userFields[field]?.type == 'float'" class="ui fluid input">
 | 
				
			||||||
									<span v-on:click="editField(field)">
 | 
																<input type="text" :placeholder="userFields[field]?.label" v-on:keyup="e => saveField(field, e.target.value)" :value="openDay[field]">
 | 
				
			||||||
										<i class="clickable grey edit outline icon"></i>
 | 
															</div>
 | 
				
			||||||
									</span>
 | 
					
 | 
				
			||||||
 | 
															<!-- range -->
 | 
				
			||||||
 | 
															<div v-if="userFields[field]?.type == 'shortRange'">
 | 
				
			||||||
 | 
																<div :class="{green:(openDay[field] == 1)}" v-on:click="saveField(field, 1)" class="ui button">1</div>
 | 
				
			||||||
 | 
																<div :class="{green:(openDay[field] == 2)}" v-on:click="saveField(field, 2)" class="ui button">2</div>
 | 
				
			||||||
 | 
																<div :class="{green:(openDay[field] == 3)}" v-on:click="saveField(field, 3)" class="ui button">3</div>
 | 
				
			||||||
 | 
																<div :class="{green:(openDay[field] == 4)}" v-on:click="saveField(field, 4)" class="ui button">4</div>
 | 
				
			||||||
 | 
																<div :class="{green:(openDay[field] == 5)}" v-on:click="saveField(field, 5)" class="ui button">5</div>
 | 
				
			||||||
 | 
															</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
															<!-- text area -->
 | 
				
			||||||
 | 
															<div v-if="userFields[field]?.type == 'text'">
 | 
				
			||||||
 | 
																<textarea rows="3" v-on:keyup="e => saveField(field, e.target.value, 2000)" :value="openDay[field]">
 | 
				
			||||||
 | 
																</textarea>
 | 
				
			||||||
 | 
															</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
															<!-- boolean -->
 | 
				
			||||||
 | 
															<div v-if="userFields[field]?.type == 'boolean'">
 | 
				
			||||||
 | 
																<div class="option-buttons">
 | 
				
			||||||
 | 
																	<div :class="{green:(openDay[field] == 1)}" v-on:click="saveField(field, 1)" class="ui compact button">Yes</div>
 | 
				
			||||||
 | 
																	<div :class="{green:(openDay[field] == 2)}" v-on:click="saveField(field, 2)" class="ui compact button">No</div>
 | 
				
			||||||
 | 
																</div>
 | 
				
			||||||
 | 
															</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
															<div v-if="['sex','period','mucus','pms'].includes(userFields[field]?.type)">
 | 
				
			||||||
 | 
																<div class="option-buttons">
 | 
				
			||||||
 | 
																	<div :class="{green:(openDay[field] == key)}" v-on:click="saveField(field, key)" class="ui compact button" v-for="(item,key) in fieldTypesDef[userFields[field]?.type].split(',')">{{ item }}</div>
 | 
				
			||||||
 | 
																</div>
 | 
				
			||||||
 | 
															</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
															<div v-if="userFields[field]?.type == 'custom'">
 | 
				
			||||||
 | 
																<div class="option-buttons">
 | 
				
			||||||
 | 
																	<div 
 | 
				
			||||||
 | 
																		v-for="(item, value) in userFields[field]?.customOptions.split(',')"
 | 
				
			||||||
 | 
																		v-on:click="saveField(field, value)" 
 | 
				
			||||||
 | 
																		:class="{green:(openDay[field] == value) && openDay[field] !== ''}"
 | 
				
			||||||
 | 
																		class="ui compact button">
 | 
				
			||||||
 | 
																			{{ item.trim() }}
 | 
				
			||||||
 | 
																	</div>
 | 
				
			||||||
 | 
																</div>
 | 
				
			||||||
 | 
															</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														</div>
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
								<div class="four wide center aligned column draggable">
 | 
					
 | 
				
			||||||
									<span>
 | 
													<div class="bottom aligned row">
 | 
				
			||||||
										<i class="grey grip lines icon"></i>
 | 
														<div class="ui six wide column">
 | 
				
			||||||
									</span>
 | 
															<span v-on:click="editField(field)">
 | 
				
			||||||
								</div>
 | 
																<i class="clickable grey edit outline icon"></i>
 | 
				
			||||||
								<div class="ui six wide right aligned column">
 | 
															</span>
 | 
				
			||||||
									<span v-if="openDay[field] !== ''" v-on:click="saveField(field, '')">
 | 
														</div>
 | 
				
			||||||
										<i class="grey clickable reply icon"></i>
 | 
														<div class="four wide center aligned column draggable">
 | 
				
			||||||
									</span>
 | 
															<span>
 | 
				
			||||||
 | 
																<i class="grey grip lines icon"></i>
 | 
				
			||||||
 | 
															</span>
 | 
				
			||||||
 | 
														</div>
 | 
				
			||||||
 | 
														<div class="ui six wide right aligned column">
 | 
				
			||||||
 | 
															<span v-if="openDay[field] !== ''" v-on:click="saveField(field, '')">
 | 
				
			||||||
 | 
																<i class="grey clickable reply icon"></i>
 | 
				
			||||||
 | 
															</span>
 | 
				
			||||||
 | 
														</div>
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
 | 
													
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
@@ -420,7 +450,7 @@
 | 
				
			|||||||
		</section>
 | 
							</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<!-- section 2 analysis -->
 | 
							<!-- section 2 analysis -->
 | 
				
			||||||
		<section class="swipe-section">
 | 
							<section class="swipe-section" v-if="!loading">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<div class="ui small centered dividing header">
 | 
								<div class="ui small centered dividing header">
 | 
				
			||||||
				Review Data
 | 
									Review Data
 | 
				
			||||||
@@ -445,7 +475,7 @@
 | 
				
			|||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<!-- calendar -->
 | 
								<!-- calendar -->
 | 
				
			||||||
			<div class="calendar">
 | 
								<div class="calendar" v-if="false">
 | 
				
			||||||
				
 | 
									
 | 
				
			||||||
				<div v-for="day in calendar.weekdays" class="day">
 | 
									<div v-for="day in calendar.weekdays" class="day">
 | 
				
			||||||
					{{ day }}
 | 
										{{ day }}
 | 
				
			||||||
@@ -476,7 +506,7 @@
 | 
				
			|||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<div class="ui segment">
 | 
								<div class="ui segment" v-if="false">
 | 
				
			||||||
				<a class="ui clickable" v-on:click="toggleFolded('key')">
 | 
									<a class="ui clickable" v-on:click="toggleFolded('key')">
 | 
				
			||||||
					<i class="tiny circular blue clickable plus icon"></i>
 | 
										<i class="tiny circular blue clickable plus icon"></i>
 | 
				
			||||||
					Calendar Explanation
 | 
										Calendar Explanation
 | 
				
			||||||
@@ -558,31 +588,40 @@
 | 
				
			|||||||
			<!-- Temp graph -->
 | 
								<!-- Temp graph -->
 | 
				
			||||||
			<div class="ui basic segment" >
 | 
								<div class="ui basic segment" >
 | 
				
			||||||
				<div class="ui dividing header">
 | 
									<div class="ui dividing header">
 | 
				
			||||||
					Chart data for the last {{ tempChartDays }} days
 | 
										Chart data for the last {{ tempChartDays }} entries
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<div class="ui tiny compact fluid buttons">
 | 
									<div class="ui tiny compact fluid buttons">
 | 
				
			||||||
					<div :class="{'green':(tempChartDays == 1000000)}" v-on:click="tempChartDays = 1000000; graphCurrentData()" class="ui button">ALL</div>
 | 
										<div :class="{'green':(tempChartDays == 1000000)}" v-on:click="tempChartDays = 1000000; " class="ui button">ALL</div>
 | 
				
			||||||
					<div :class="{'green':(tempChartDays == 90)}" v-on:click="tempChartDays = 90; graphCurrentData()" class="ui button">90</div>
 | 
										<div :class="{'green':(tempChartDays == 90)}" v-on:click="tempChartDays = 90; " class="ui button">90</div>
 | 
				
			||||||
					<div :class="{'green':(tempChartDays == 60)}" v-on:click="tempChartDays = 60; graphCurrentData()" class="ui button">60</div>
 | 
										<div :class="{'green':(tempChartDays == 60)}" v-on:click="tempChartDays = 60; " class="ui button">60</div>
 | 
				
			||||||
					<div :class="{'green':(tempChartDays == 30)}" v-on:click="tempChartDays = 30; graphCurrentData()" class="ui button">30</div>
 | 
										<div :class="{'green':(tempChartDays == 30)}" v-on:click="tempChartDays = 30; " class="ui button">30</div>
 | 
				
			||||||
					<div :class="{'green':(tempChartDays == 7)}" v-on:click="tempChartDays = 7; graphCurrentData()" class="ui button">7</div>
 | 
										<div :class="{'green':(tempChartDays == 15)}" v-on:click="tempChartDays = 15; " class="ui button">15</div>
 | 
				
			||||||
 | 
										<div :class="{'green':(tempChartDays == 7)}" v-on:click="tempChartDays = 7; " class="ui button">7</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				
 | 
									
 | 
				
			||||||
				<!-- <div id="graphdiv" style="width: 100%; min-height: 320px;"></div> -->
 | 
									<!-- <div id="graphdiv" style="width: 100%; min-height: 320px;"></div> -->
 | 
				
			||||||
			<div class="ui divider"></div>
 | 
									<div class="ui divider"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<div v-for="(graph, index) in graphs" class="an-graph">
 | 
									<MetricGraphs 
 | 
				
			||||||
					<div class="ui small dividing header">
 | 
										:key="'graph-updates-'+graphUpdates"
 | 
				
			||||||
						{{ graph?.title }}
 | 
										:tempChartDays="tempChartDays"
 | 
				
			||||||
					</div>
 | 
										:fields="fields"
 | 
				
			||||||
					<div :id="`graphdiv${index}`" style="width: 100%; min-height: 320px;"></div>
 | 
										:userFields="userFields"
 | 
				
			||||||
					<br>
 | 
										:graphs="graphs"
 | 
				
			||||||
 | 
										:cycleData="cycleData"
 | 
				
			||||||
 | 
										:calendar="calendar"
 | 
				
			||||||
 | 
										@saveGraphs="saveGraphs"
 | 
				
			||||||
 | 
										@toggleEditGraphs="toggleEditGraphs"
 | 
				
			||||||
 | 
										:editGraphs="editGraphs"
 | 
				
			||||||
 | 
									/>			
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<div class="ui very padded basic segment">
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			<!-- notes  -->
 | 
								<!-- notes  -->
 | 
				
			||||||
			<div class="ui basic segment">
 | 
								<div class="ui basic segment" v-if="false">
 | 
				
			||||||
				<div class="ui clickable"  v-on:click="toggleFolded('notes')">
 | 
									<div class="ui clickable"  v-on:click="toggleFolded('notes')">
 | 
				
			||||||
					<i class="tiny circular blue clickable plus icon"></i>
 | 
										<i class="tiny circular blue clickable plus icon"></i>
 | 
				
			||||||
					Additional Notes
 | 
										Additional Notes
 | 
				
			||||||
@@ -780,21 +819,21 @@
 | 
				
			|||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div v-for="(entry, key) in fieldDefinition" class="row" v-if="!['id'].includes(key)">
 | 
										<div v-for="(entry, key) in fieldDefinition" class="row" v-if="!['id'].includes(key)">
 | 
				
			||||||
						
 | 
											
 | 
				
			||||||
						<div v-if="fieldDefinitionOptions[key]" class="sixteen wide column">
 | 
											<div v-if="fieldDefinitionOptionsDef[key]" class="sixteen wide column">
 | 
				
			||||||
							{{ mapFormTerm(key) }}
 | 
												{{ mapFormTerm(key) }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							<div v-if="fieldDefinitionOptions[key].type == 'text'" class="ui fluid input">
 | 
												<div v-if="fieldDefinitionOptionsDef[key].type == 'text'" class="ui fluid input">
 | 
				
			||||||
								<input type="text" :value="editFieldObject[key]" v-on:keyup="e => setNewFieldOption(e, key)">
 | 
													<input type="text" :value="editFieldObject[key]" v-on:keyup="e => setNewFieldOption(e, key)">
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							<div v-if="fieldDefinitionOptions[key].type == 'commatextoptions' && editFieldObject?.type == 'custom'" class="ui fluid input">
 | 
												<div v-if="fieldDefinitionOptionsDef[key].type == 'commatextoptions' && editFieldObject?.type == 'custom'" class="ui fluid input">
 | 
				
			||||||
								<input type="text" :value="editFieldObject[key]" v-on:keyup="e => setNewFieldOption(e, key)">
 | 
													<input type="text" :value="editFieldObject[key]" v-on:keyup="e => setNewFieldOption(e, key)">
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							<div v-if="fieldDefinitionOptions[key].type == 'option'">
 | 
												<div v-if="fieldDefinitionOptionsDef[key].type == 'option'">
 | 
				
			||||||
								<div v-for="option in fieldDefinitionOptions[key].options" 
 | 
													<div v-for="option in fieldDefinitionOptionsDef[key].options" 
 | 
				
			||||||
									v-on:click="setNewFieldOption(null, key, option)"
 | 
														v-on:click="setNewFieldOption(null, key, option)"
 | 
				
			||||||
									:class="{'green':editFieldObject[key] == option}"
 | 
														:class="{'green':editFieldObject[key] == option}"
 | 
				
			||||||
									class="ui button">
 | 
														class="ui button">
 | 
				
			||||||
@@ -802,8 +841,8 @@
 | 
				
			|||||||
								</div>
 | 
													</div>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							<div v-if="fieldDefinitionOptions[key].type == 'icons'">
 | 
												<div v-if="fieldDefinitionOptionsDef[key].type == 'icons'">
 | 
				
			||||||
								<div v-for="option in fieldDefinitionOptions[key].options" 
 | 
													<div v-for="option in fieldDefinitionOptionsDef[key].options" 
 | 
				
			||||||
									v-on:click="setNewFieldOption(null, key, option)"
 | 
														v-on:click="setNewFieldOption(null, key, option)"
 | 
				
			||||||
									:class="{'green':editFieldObject[key] == option}"
 | 
														:class="{'green':editFieldObject[key] == option}"
 | 
				
			||||||
									class="ui icon button">
 | 
														class="ui icon button">
 | 
				
			||||||
@@ -812,8 +851,8 @@
 | 
				
			|||||||
							</div>
 | 
												</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							<!-- :class="{'green':}" -->
 | 
												<!-- :class="{'green':}" -->
 | 
				
			||||||
							<div v-if="fieldDefinitionOptions[key].type == 'color'">
 | 
												<div v-if="fieldDefinitionOptionsDef[key].type == 'color'">
 | 
				
			||||||
								<div v-for="option in fieldDefinitionOptions[key].options" 
 | 
													<div v-for="option in fieldDefinitionOptionsDef[key].options" 
 | 
				
			||||||
									v-on:click="setNewFieldOption(null, key, option)"
 | 
														v-on:click="setNewFieldOption(null, key, option)"
 | 
				
			||||||
									:class="`ui ${option} icon button`">
 | 
														:class="`ui ${option} icon button`">
 | 
				
			||||||
										<i v-if="editFieldObject[key] == option" class="white check icon"></i>
 | 
															<i v-if="editFieldObject[key] == option" class="white check icon"></i>
 | 
				
			||||||
@@ -865,10 +904,6 @@
 | 
				
			|||||||
	import axios from 'axios'
 | 
						import axios from 'axios'
 | 
				
			||||||
	import draggable from 'vuedraggable'
 | 
						import draggable from 'vuedraggable'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	import { Chart } from 'chart.js/auto'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var BASAL_TEMP = 'BT'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	export default {
 | 
						export default {
 | 
				
			||||||
		name: 'MetricTracking',
 | 
							name: 'MetricTracking',
 | 
				
			||||||
		components: {
 | 
							components: {
 | 
				
			||||||
@@ -876,6 +911,7 @@
 | 
				
			|||||||
			'delete-button': require('@/components/NoteDeleteButtonComponent.vue').default,
 | 
								'delete-button': require('@/components/NoteDeleteButtonComponent.vue').default,
 | 
				
			||||||
			'modal': require('@/components/ModalComponent.vue').default,
 | 
								'modal': require('@/components/ModalComponent.vue').default,
 | 
				
			||||||
			draggable,
 | 
								draggable,
 | 
				
			||||||
 | 
								'MetricGraphs':require('@/components/Metrictracking/MetricGraphsComponent.vue').default,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		data () {
 | 
							data () {
 | 
				
			||||||
			return {
 | 
								return {
 | 
				
			||||||
@@ -886,15 +922,8 @@
 | 
				
			|||||||
				showNotes: false,
 | 
									showNotes: false,
 | 
				
			||||||
				fields:[], // Array of field IDs
 | 
									fields:[], // Array of field IDs
 | 
				
			||||||
				userFields:{}, // Objects of field definitions
 | 
									userFields:{}, // Objects of field definitions
 | 
				
			||||||
				graphs:[
 | 
									graphs:[],
 | 
				
			||||||
					// types [null, 'lastDone', 'pillCalendar']
 | 
									fieldTypesDef:{
 | 
				
			||||||
					{type:'pillCalendar', title:'Pill Cal', fieldIds:['JX3QD','K3KII']},
 | 
					 | 
				
			||||||
					{type:'lastDone', title:'Last Done', fieldIds:['JX3QD','K3KII']},
 | 
					 | 
				
			||||||
					{title:'Basal Temp', fieldIds:['BT']},
 | 
					 | 
				
			||||||
					{title:'Basal Temp - Cervical Fluid', fieldIds:['BT','CM']},
 | 
					 | 
				
			||||||
					{title:'Test Graph', fieldIds:['40DA6','MI9B9']},
 | 
					 | 
				
			||||||
				],
 | 
					 | 
				
			||||||
				fieldTypes:{
 | 
					 | 
				
			||||||
					'float':'Precise Number',
 | 
										'float':'Precise Number',
 | 
				
			||||||
					'shortRange':'Options 1-5',
 | 
										'shortRange':'Options 1-5',
 | 
				
			||||||
					'longRange':'Options 1-10',
 | 
										'longRange':'Options 1-10',
 | 
				
			||||||
@@ -922,7 +951,7 @@
 | 
				
			|||||||
				fieldDefinition:{
 | 
									fieldDefinition:{
 | 
				
			||||||
					'id':'','type':'','label':'','icon':'','color':'','width':'','customOptions':''
 | 
										'id':'','type':'','label':'','icon':'','color':'','width':'','customOptions':''
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				fieldDefinitionOptions:{
 | 
									fieldDefinitionOptionsDef:{
 | 
				
			||||||
					label:{'type':'text'},
 | 
										label:{'type':'text'},
 | 
				
			||||||
					customOptions:{'type':'commatextoptions'},
 | 
										customOptions:{'type':'commatextoptions'},
 | 
				
			||||||
					icon:{'type':'icons', 'options':['heart','smile','cat','crow','dog','dove','dragon','feather','feather alternate','fish','frog','hippo','horse','horse head','kiwi bird','otter','paw','spider','video','headphones','motorcycle','truck','monster truck','campground','cloud sun','drumstick bite','football ball','fruit-apple','hiking','mountain','tractor','tree','wind','wine bottle','coffee','flask','glass cheers','glass martini','beer','toilet paper','gift','globe','hand holding heart','comment','graduation cap','hat cowboy','hat wizard','mitten','user tie','laptop code','microchip','shield alternate','mouse','plug','power off','satellite','hammer','wrench','bell','eye','marker','paperclip','atom','award','theater masks','music','grin alternate','grin tongue squint outline','laugh wink','fire','fire alternate','poop','sun','money bill alternate','piggy bank','heart outline','heartbeat','running','walking','bacon','bone','bread slice','candy cane','carrot','cheese','cloud meatball','cookie','egg','hamburger','hotdog','ice cream','lemon','lemon outline','pepper hot','pizza slice','seedling','stroopwafel','leaf','book dead','broom','cloud moon','ghost','mask','skull crossbones','certificate','check','check circle','joint','cannabis','bong','gem','futbol','brain','dna','hand spock','hand spock outline','meteor','moon','moon outline','robot','rocket','satellite dish','space shuttle','user astronaut','fingerprint','thumbs up','thumbs down']},
 | 
										icon:{'type':'icons', 'options':['heart','smile','cat','crow','dog','dove','dragon','feather','feather alternate','fish','frog','hippo','horse','horse head','kiwi bird','otter','paw','spider','video','headphones','motorcycle','truck','monster truck','campground','cloud sun','drumstick bite','football ball','fruit-apple','hiking','mountain','tractor','tree','wind','wine bottle','coffee','flask','glass cheers','glass martini','beer','toilet paper','gift','globe','hand holding heart','comment','graduation cap','hat cowboy','hat wizard','mitten','user tie','laptop code','microchip','shield alternate','mouse','plug','power off','satellite','hammer','wrench','bell','eye','marker','paperclip','atom','award','theater masks','music','grin alternate','grin tongue squint outline','laugh wink','fire','fire alternate','poop','sun','money bill alternate','piggy bank','heart outline','heartbeat','running','walking','bacon','bone','bread slice','candy cane','carrot','cheese','cloud meatball','cookie','egg','hamburger','hotdog','ice cream','lemon','lemon outline','pepper hot','pizza slice','seedling','stroopwafel','leaf','book dead','broom','cloud moon','ghost','mask','skull crossbones','certificate','check','check circle','joint','cannabis','bong','gem','futbol','brain','dna','hand spock','hand spock outline','meteor','moon','moon outline','robot','rocket','satellite dish','space shuttle','user astronaut','fingerprint','thumbs up','thumbs down']},
 | 
				
			||||||
@@ -931,15 +960,18 @@
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
				cycleData: {},
 | 
									cycleData: {},
 | 
				
			||||||
				totalEntries: 0,
 | 
									totalEntries: 0,
 | 
				
			||||||
				openDay: {},
 | 
									openDay: {}, // current day values, updates into cycleData when saved
 | 
				
			||||||
				loadingDayTimeout: null,
 | 
									loadingDayTimeout: null,
 | 
				
			||||||
				loadingDay: false,
 | 
									loadingDay: false,
 | 
				
			||||||
				saveDataDebounce:null,
 | 
									saveDataDebounce:null,
 | 
				
			||||||
 | 
									loading: true,
 | 
				
			||||||
				saving: 0, // 0 blank, 1 modified, 2 saving, 3 saved
 | 
									saving: 0, // 0 blank, 1 modified, 2 saving, 3 saved
 | 
				
			||||||
				calendar: {
 | 
									calendar: {
 | 
				
			||||||
					dateObject: null,
 | 
										dateObject: null,
 | 
				
			||||||
					dateCode: null,
 | 
										dateCode: null,
 | 
				
			||||||
					monthName: '',
 | 
										monthName: '',
 | 
				
			||||||
 | 
										dayName:'',
 | 
				
			||||||
 | 
										daysAgo:0,
 | 
				
			||||||
					month: '',
 | 
										month: '',
 | 
				
			||||||
					year: '',
 | 
										year: '',
 | 
				
			||||||
					days: [],
 | 
										days: [],
 | 
				
			||||||
@@ -952,6 +984,8 @@
 | 
				
			|||||||
				editFieldId:'',
 | 
									editFieldId:'',
 | 
				
			||||||
				editFieldObject:{},
 | 
									editFieldObject:{},
 | 
				
			||||||
				appDataImport:'',
 | 
									appDataImport:'',
 | 
				
			||||||
 | 
									graphUpdates:0,
 | 
				
			||||||
 | 
									editGraphs: false,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		beforeCreate() {
 | 
							beforeCreate() {
 | 
				
			||||||
@@ -972,11 +1006,6 @@
 | 
				
			|||||||
				location.reload();
 | 
									location.reload();
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Include JS libraries
 | 
					 | 
				
			||||||
			let graphsScript = document.createElement('script')
 | 
					 | 
				
			||||||
			graphsScript.setAttribute('src', '//cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.min.js')
 | 
					 | 
				
			||||||
      		document.head.appendChild(graphsScript)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// setup date to today
 | 
								// setup date to today
 | 
				
			||||||
			this.setupCalendar()
 | 
								this.setupCalendar()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1035,6 +1064,19 @@
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		methods: {
 | 
							methods: {
 | 
				
			||||||
 | 
								saveGraphs(newGraphData){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.graphs = newGraphData
 | 
				
			||||||
 | 
									this.saveCycleData()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// re-render graph data on update
 | 
				
			||||||
 | 
									this.$nextTick(() => {
 | 
				
			||||||
 | 
										this.graphUpdates++
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								toggleEditGraphs(){
 | 
				
			||||||
 | 
									this.editGraphs = !this.editGraphs
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
			getFieldColor(field){
 | 
								getFieldColor(field){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				let color = null
 | 
									let color = null
 | 
				
			||||||
@@ -1134,14 +1176,14 @@
 | 
				
			|||||||
			validateCustomFieldsForm(){
 | 
								validateCustomFieldsForm(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				const checks = [] //check
 | 
									const checks = [] //check
 | 
				
			||||||
				const checkFields = Object.keys(this.fieldDefinitionOptions)
 | 
									const checkFields = Object.keys(this.fieldDefinitionOptionsDef)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				checkFields.forEach(row => {
 | 
									checkFields.forEach(row => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// console.log(this.editFieldObject[row])
 | 
										// console.log(this.editFieldObject[row])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// don't worry about optional fields
 | 
										// don't worry about optional fields
 | 
				
			||||||
					if(this.fieldDefinitionOptions[row].optional){
 | 
										if(this.fieldDefinitionOptionsDef[row].optional){
 | 
				
			||||||
						checks.push(true)
 | 
											checks.push(true)
 | 
				
			||||||
						return
 | 
											return
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
@@ -1323,98 +1365,6 @@
 | 
				
			|||||||
				this.fields.push(fieldId)
 | 
									this.fields.push(fieldId)
 | 
				
			||||||
				this.saveCycleData()
 | 
									this.saveCycleData()
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			graphCurrentData(){
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				const graphOptions = {
 | 
					 | 
				
			||||||
					interactionModel: {},
 | 
					 | 
				
			||||||
					// pointClickCallback: function(e, pt){
 | 
					 | 
				
			||||||
					// 	console.log(e)
 | 
					 | 
				
			||||||
					// 	console.log(pt)
 | 
					 | 
				
			||||||
					// 	console.log(this.getValue(pt.idx, 0))
 | 
					 | 
				
			||||||
					// }
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Excel date format YYYYMMDD
 | 
					 | 
				
			||||||
				const convertToExcelDate = (dateCode) => {
 | 
					 | 
				
			||||||
					return dateCode
 | 
					 | 
				
			||||||
					.split('.')
 | 
					 | 
				
			||||||
					.reverse()
 | 
					 | 
				
			||||||
					.map(item => String(item).padStart(2,0))
 | 
					 | 
				
			||||||
					.join('')
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				const sortableDate = (dateCode) => {
 | 
					 | 
				
			||||||
					return parseInt(
 | 
					 | 
				
			||||||
						dateCode
 | 
					 | 
				
			||||||
							.split('.')
 | 
					 | 
				
			||||||
							.map(i => String(i).padStart(2, '0'))
 | 
					 | 
				
			||||||
							.reverse()
 | 
					 | 
				
			||||||
							.join('')
 | 
					 | 
				
			||||||
					)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Generate set of keys for graph length
 | 
					 | 
				
			||||||
				let dataKeys = Object.keys(this.cycleData)
 | 
					 | 
				
			||||||
				dataKeys.sort((a,b) => {
 | 
					 | 
				
			||||||
						a = sortableDate(a)
 | 
					 | 
				
			||||||
						b = sortableDate(b)
 | 
					 | 
				
			||||||
						return b - a
 | 
					 | 
				
			||||||
					})
 | 
					 | 
				
			||||||
				dataKeys = dataKeys.splice(0, this.tempChartDays)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// build CSV data for each graph
 | 
					 | 
				
			||||||
				this.graphs.forEach((graph,index) => {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// CSV or path to a CSV file.
 | 
					 | 
				
			||||||
					let dataString = ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// Lookup graph field titles
 | 
					 | 
				
			||||||
					let graphLabels = ['Date']
 | 
					 | 
				
			||||||
					graph.fieldIds.forEach(fieldId => {
 | 
					 | 
				
			||||||
						const graphLabel = this.userFields[fieldId]?.label
 | 
					 | 
				
			||||||
						graphLabels.push(graphLabel)
 | 
					 | 
				
			||||||
					})
 | 
					 | 
				
			||||||
					dataString += graphLabels.join(',') + '\n'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					
 | 
					 | 
				
			||||||
					// build each row, for each day
 | 
					 | 
				
			||||||
					for (var i = 0; i < dataKeys.length; i++) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						let nextFragment = []
 | 
					 | 
				
			||||||
						// push date code to first column
 | 
					 | 
				
			||||||
						nextFragment.push(convertToExcelDate(dataKeys[i]))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						graph.fieldIds.forEach(fieldId => {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							const currentEntry = this.cycleData[dataKeys[i]]
 | 
					 | 
				
			||||||
							let currentValue = currentEntry[fieldId]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							if(fieldId == 'BT'){
 | 
					 | 
				
			||||||
								// parse temp to fixed length float 00.00
 | 
					 | 
				
			||||||
								currentValue = parseFloat(currentValue).toFixed(2)
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
							if(fieldId == 'CM'){
 | 
					 | 
				
			||||||
								currentValue = parseFloat('97.'+currentValue)
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							nextFragment.push(currentValue)
 | 
					 | 
				
			||||||
								
 | 
					 | 
				
			||||||
						})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						dataString += nextFragment.join(',') + "\n"
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					
 | 
					 | 
				
			||||||
					let graphDiv = document.getElementById("graphdiv"+index)
 | 
					 | 
				
			||||||
					const g = new Dygraph(graphDiv, dataString ,graphOptions)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
				
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			saveField(fieldId, value, optionalTimeout){
 | 
								saveField(fieldId, value, optionalTimeout){
 | 
				
			||||||
				
 | 
									
 | 
				
			||||||
				// Dont save value if it hasn't changed
 | 
									// Dont save value if it hasn't changed
 | 
				
			||||||
@@ -1472,8 +1422,6 @@
 | 
				
			|||||||
					this.cycleData[this.calendar.dateCode] = cleanDayData
 | 
										this.cycleData[this.calendar.dateCode] = cleanDayData
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				this.graphCurrentData()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				this.saveCycleData()
 | 
									this.saveCycleData()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
@@ -1499,6 +1447,7 @@
 | 
				
			|||||||
				axios.post('/api/metric-tracking/get')
 | 
									axios.post('/api/metric-tracking/get')
 | 
				
			||||||
				.then(({ data }) => {
 | 
									.then(({ data }) => {
 | 
				
			||||||
					
 | 
										
 | 
				
			||||||
 | 
										this.loading = false
 | 
				
			||||||
					this.setApplicationStateJson(data)
 | 
										this.setApplicationStateJson(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
@@ -1532,27 +1481,49 @@
 | 
				
			|||||||
				this.cycleData = json?.cycleData || this.cycleData
 | 
									this.cycleData = json?.cycleData || this.cycleData
 | 
				
			||||||
				this.fields = [...new Set(json?.fields)] || this.fields
 | 
									this.fields = [...new Set(json?.fields)] || this.fields
 | 
				
			||||||
				this.userFields = json?.userFields || this.userFields
 | 
									this.userFields = json?.userFields || this.userFields
 | 
				
			||||||
				// this.graphs = json?.graphs || this.graphs
 | 
									this.graphs = json?.graphs || this.graphs
 | 
				
			||||||
 | 
					 | 
				
			||||||
				// console.log(this.fields)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				this.$nextTick(() => {
 | 
									this.$nextTick(() => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										this.getApplicationStateJson()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					this.totalEntries = Object.keys(this.cycleData).length
 | 
										this.totalEntries = Object.keys(this.cycleData).length
 | 
				
			||||||
					this.setupFields()
 | 
										this.setupFields()
 | 
				
			||||||
					this.openDayData(this.calendar.dateCode)
 | 
										this.openDayData(this.calendar.dateCode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					this.graphCurrentData()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					this.generateTonsOfRandomData()
 | 
										this.generateTonsOfRandomData()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			getApplicationStateJson(){
 | 
								getApplicationStateJson(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// convert date code into sortable int
 | 
				
			||||||
 | 
									const sortableDate = (code) => {
 | 
				
			||||||
 | 
										code = code
 | 
				
			||||||
 | 
											.split('.')
 | 
				
			||||||
 | 
											.reverse()
 | 
				
			||||||
 | 
											.map(i => String(i).padStart(2, '0'))
 | 
				
			||||||
 | 
											.join('')
 | 
				
			||||||
 | 
										return parseInt(code)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Sort cycle data, newest first
 | 
				
			||||||
 | 
									let sortedData = Object.keys(this.cycleData)
 | 
				
			||||||
 | 
										.sort((a,b) => {
 | 
				
			||||||
 | 
											return sortableDate(b) - sortableDate(a)
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// setup new object with sorted data
 | 
				
			||||||
 | 
									let sortedCycleData = sortedData.reduce((result, key) => {
 | 
				
			||||||
 | 
										result[key] = this.cycleData[key]
 | 
				
			||||||
 | 
										return result
 | 
				
			||||||
 | 
									},{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				return JSON.stringify({
 | 
									return JSON.stringify({
 | 
				
			||||||
					fields: this.fields,
 | 
										fields: this.fields,
 | 
				
			||||||
					cycleData: this.cycleData,
 | 
										cycleData: sortedCycleData,
 | 
				
			||||||
					userFields: this.userFields,
 | 
										userFields: this.userFields,
 | 
				
			||||||
					// graphs: this.graphs,
 | 
										graphs: this.graphs,
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			saveCycleData(){
 | 
								saveCycleData(){
 | 
				
			||||||
@@ -1624,6 +1595,7 @@
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
			setupCalendar(date){
 | 
								setupCalendar(date){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// visualize each day change
 | 
				
			||||||
				this.working = true
 | 
									this.working = true
 | 
				
			||||||
				setTimeout(() => {
 | 
									setTimeout(() => {
 | 
				
			||||||
					this.working = false
 | 
										this.working = false
 | 
				
			||||||
@@ -1640,6 +1612,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				this.calendar.dateCode = this.generateDateCode(date)
 | 
									this.calendar.dateCode = this.generateDateCode(date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// calculate days ago since current date
 | 
				
			||||||
 | 
									const now = new Date()
 | 
				
			||||||
 | 
									const diffSeconds = Math.floor((now - date) / 1000) // subtract unix timestamps, convert MS to S
 | 
				
			||||||
 | 
									const dayInterval = diffSeconds / 86400 // seconds in a day
 | 
				
			||||||
 | 
									this.calendar.daysAgo = Math.floor(dayInterval)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// ------------
 | 
									// ------------
 | 
				
			||||||
				// setup calendar display
 | 
									// setup calendar display
 | 
				
			||||||
@@ -1656,6 +1635,7 @@
 | 
				
			|||||||
				const currentYear = date.getFullYear();
 | 
									const currentYear = date.getFullYear();
 | 
				
			||||||
				const currentMonth = date.getMonth() + 1;
 | 
									const currentMonth = date.getMonth() + 1;
 | 
				
			||||||
				this.calendar.monthName = date.toLocaleString("en-US", { month: "long" });
 | 
									this.calendar.monthName = date.toLocaleString("en-US", { month: "long" });
 | 
				
			||||||
 | 
									this.calendar.dayName = date.toLocaleString("en-US", { weekday: "long" });
 | 
				
			||||||
				this.calendar.year = currentYear
 | 
									this.calendar.year = currentYear
 | 
				
			||||||
				const daysInCurrentMonth = getDaysInMonth(currentYear, currentMonth);
 | 
									const daysInCurrentMonth = getDaysInMonth(currentYear, currentMonth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1704,7 +1684,6 @@
 | 
				
			|||||||
					workingDate.setDate(workingDate.getDate()-1)
 | 
										workingDate.setDate(workingDate.getDate()-1)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				this.graphCurrentData(5000)
 | 
					 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,7 @@ export default new Vuex.Store({
 | 
				
			|||||||
					'menu-text': '#5e6268',
 | 
										'menu-text': '#5e6268',
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				'black':{
 | 
									'black':{
 | 
				
			||||||
					'body_bg_color': 'linear-gradient(135deg, rgba(0,0,0,1) 0%, rgba(23,12,46,1) 100%)',
 | 
										'body_bg_color': 'rgb(12 4 30)',
 | 
				
			||||||
					//'#0f0f0f',//'#000',
 | 
										//'#0f0f0f',//'#000',
 | 
				
			||||||
					'small_element_bg_color': '#000',
 | 
										'small_element_bg_color': '#000',
 | 
				
			||||||
					'text_color': '#FFF',
 | 
										'text_color': '#FFF',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -539,3 +539,11 @@ Attachment.processUrl = (userId, noteId, url) => {
 | 
				
			|||||||
		}, scrapeTime )
 | 
							}, scrapeTime )
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Attachment.generatePushKey = (userId) => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Attachment.deletePushKey = (userId) => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Attachment.getPushkey = (userId) => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Attachment.pushUrl = (userId) => {}
 | 
				
			||||||
@@ -65,5 +65,30 @@ router.post('/upload', upload.single('file'), function (req, res, next) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Push URL to attachments
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// get push key
 | 
				
			||||||
 | 
					router.get('/getpushkey', function (req, res) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Attachment.delete(userId, req.body.attachmentId)
 | 
				
			||||||
 | 
						.then( data => res.send(data) )
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// generate new push key
 | 
				
			||||||
 | 
					router.post('/generatepushkey', function (req, res) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// delete push key
 | 
				
			||||||
 | 
					router.post('/deletepushkey', function (req, res) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// push url to attchments
 | 
				
			||||||
 | 
					router.get('/pushurl', function (req, res) {
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = router
 | 
					module.exports = router
 | 
				
			||||||
		Reference in New Issue
	
	Block a user