Graph update and little noe ui tweaks
This commit is contained in:
parent
d3acd62688
commit
c61f0c0198
@ -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)
|
||||||
|
|
||||||
|
setTimeout(()=>{
|
||||||
|
|
||||||
Array.from( container.getElementsByClassName('active') ).forEach(item => {
|
Array.from( container.getElementsByClassName('active') ).forEach(item => {
|
||||||
item.classList.remove('active');
|
item.classList.remove('active');
|
||||||
})
|
})
|
||||||
|
|
||||||
//Close menu if user is on mobile, then sort list
|
},600)
|
||||||
if(this.$store.getters.getIsUserOnMobile){
|
|
||||||
this.$router.go(-1)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
deleteCompletedListItems(){
|
deleteCompletedListItems(){
|
||||||
//
|
//
|
||||||
@ -190,6 +191,11 @@ 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)
|
||||||
|
|
||||||
|
setTimeout(()=>{
|
||||||
|
|
||||||
//Go through each item, on first level, look for Unordered Lists
|
//Go through each item, on first level, look for Unordered Lists
|
||||||
container.childNodes.forEach( (node) => {
|
container.childNodes.forEach( (node) => {
|
||||||
if(node.nodeName == 'UL'){
|
if(node.nodeName == 'UL'){
|
||||||
@ -233,10 +239,9 @@ const SquireButtonFunctions = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
//Close menu if user is on mobile, then sort list
|
}, 600)
|
||||||
if(this.$store.getters.getIsUserOnMobile){
|
|
||||||
this.$router.go(-1)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
sortList(){
|
sortList(){
|
||||||
//
|
//
|
||||||
@ -246,6 +251,11 @@ 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
|
||||||
|
this.$router.go(-1)
|
||||||
|
|
||||||
|
setTimeout(()=>{
|
||||||
|
|
||||||
//Go through each item, on first level, look for Unordered Lists
|
//Go through each item, on first level, look for Unordered Lists
|
||||||
container.childNodes.forEach( (node) => {
|
container.childNodes.forEach( (node) => {
|
||||||
if(node.nodeName == 'UL'){
|
if(node.nodeName == 'UL'){
|
||||||
@ -297,10 +307,9 @@ const SquireButtonFunctions = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
//Close menu if user is on mobile
|
},600)
|
||||||
if(this.$store.getters.getIsUserOnMobile){
|
|
||||||
this.$router.go(-1)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
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,6 +334,8 @@ const SquireButtonFunctions = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeout(()=>{
|
||||||
|
|
||||||
//Go through each item, on first level, look for Unordered Lists
|
//Go through each item, on first level, look for Unordered Lists
|
||||||
container.childNodes.forEach( (node) => {
|
container.childNodes.forEach( (node) => {
|
||||||
|
|
||||||
@ -349,11 +363,10 @@ const SquireButtonFunctions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
},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,16 +357,22 @@
|
|||||||
<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'">
|
||||||
|
|
||||||
|
<div class="ui very compact grid input-grid">
|
||||||
<!-- field label display -->
|
<!-- field label display -->
|
||||||
<div class="ui very compact grid">
|
<div class="row">
|
||||||
<div class="ui sixteen wide center aligned column">
|
<div class="ui sixteen wide column">
|
||||||
<i :class="`${getFieldColor(field)} ${getFieldIcon(field)} icon`"></i>
|
<b><i :class="`${getFieldColor(field)} ${getFieldIcon(field)} icon`"></i>
|
||||||
<b>{{ userFields[field]?.label }}</b>
|
{{ userFields[field]?.label }}</b>
|
||||||
<span>{{ field }}</span>
|
<!-- <span>{{ field }}</span> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- input display -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="sixteen wide column">
|
||||||
|
|
||||||
<!-- float -->
|
<!-- float -->
|
||||||
<div v-if="userFields[field]?.type == 'float'" class="ui fluid input">
|
<div v-if="userFields[field]?.type == 'float'" class="ui fluid input">
|
||||||
<input type="text" :placeholder="userFields[field]?.label" v-on:keyup="e => saveField(field, e.target.value)" :value="openDay[field]">
|
<input type="text" :placeholder="userFields[field]?.label" v-on:keyup="e => saveField(field, e.target.value)" :value="openDay[field]">
|
||||||
@ -371,13 +395,15 @@
|
|||||||
|
|
||||||
<!-- boolean -->
|
<!-- boolean -->
|
||||||
<div v-if="userFields[field]?.type == '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="option-buttons">
|
||||||
<div :class="{green:(openDay[field] == 2)}" v-on:click="saveField(field, 2)" class="ui button">No</div>
|
<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>
|
||||||
|
|
||||||
<div v-if="['sex','period','mucus','pms'].includes(userFields[field]?.type)">
|
<div v-if="['sex','period','mucus','pms'].includes(userFields[field]?.type)">
|
||||||
<div class="option-buttons">
|
<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 :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>
|
</div>
|
||||||
|
|
||||||
@ -393,8 +419,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ui very compact grid">
|
<div class="bottom aligned row">
|
||||||
<div class="ui six wide column">
|
<div class="ui six wide column">
|
||||||
<span v-on:click="editField(field)">
|
<span v-on:click="editField(field)">
|
||||||
<i class="clickable grey edit outline icon"></i>
|
<i class="clickable grey edit outline icon"></i>
|
||||||
@ -413,6 +441,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</draggable>
|
</draggable>
|
||||||
</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
|
Loading…
Reference in New Issue
Block a user