<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Ovulation Predictor Calc in Vue</title>
</head>
<body>

  <section class="hero is-primary has-text-centered">
  <div class="hero-body">
    <div class="container">
      <h1 class="title is-1">Ovulation Predictor Calculator</h1>
    </div>
  </div>
  </section>
  
  <section id="app" class="section content has-text-centered">
    
<transition name="fade" mode="out-in">
    <div class="content columns level is-centered
" v-if="!calcReturned" key="calendar">

      <div class="column is-one-third level has-text-centered ">
        <p class="subtitle">Please select the first day of your last menstrual period:</p>
        <vuejs-datepicker :inline="true" v-model="date">{{ date }}</vuejs-datepicker>
      </div>

      <div class="column is-one-third has-text-centered level">
        <p>Usual number of days in your cycle:</p>
        <div class="select is-primary">
          <select name="days" v-model="cycleSelected">
            <option v-for="n in 45" v-if="n >= 20">{{ n }}</option>
          </select>
        </div>

        <div class="level-item">
          <button class="button is-large is-primary is-rounded " id="calculate-btn" @click="startCalc(); calculateFertileBegin(); calculateFertileEnds(); calculateDueDate();">Calculate</button>
        </div>
      </div>
    </div>

    <div class="content has-text-centered" v-else key="result">

      <p class="subtitle">Here are the results based on the information you provided:</p>
      <p>Your next most fertile period is <strong>{{ fertileFrom }}</strong> to <strong>{{ fertileUntil }}</strong>.</p>
      <p>If you conceive within this timeframe, your estimated due date will be <strong>{{ dueDate }}</strong>.</p>
      <div class="level-item">
        <button class="button is-large is-primary is-rounded" id="resetCalc" @click="resetCalc">Calculate Again</button>
      </div>

    </div>
</transition>
    
    <div class="notification is-full">*Average length will vary by woman. **A woman's best days to conceive can start at least one day prior and last at least one day after fertile date.
    </div>

  </section>
</body>

</html>
/* Override Vue Datepicker CSS */

.vdp-datepicker__calendar {
    width: 100% !important;
}
.vdp-datepicker__calendar .cell.selected {
    background: #00d2b2 !important;
    color: #fff;
    font-weight: bold;
}
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day:hover, .vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month:hover, .vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year:hover {
    border: 1px solid #00d2b2 !important;
}
.vdp-datepicker__calendar .cell.selected:hover {
    background: #00d2b2 !important;
}
.select {
  margin-bottom: 1em;
}
.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  transition: opacity 1.5s;
}
.fade-leave {
  opacity: 0;
}
.fade-leave-active {
}
// Ovulation Predictor Calculator using Vue.js + Vue Datepicker + MomentJS + Bulma CSS

new Vue({
  el: '#app',
  components: {
  	vuejsDatepicker
  },
  data() {
    return {
      calcReturned: false,
      date: new Date(),
      cycleSelected: 28,
      fertileFrom: "",
      fertileUntil: "",
      dueDate: "",
      fullDate: "",
    };
  },
  methods: {
    startCalc: function() {
      this.calcReturned = true;
    },
    calcDate: function() {
      var day = parseInt(document.querySelector("span.day.selected").innerHTML);
      var month = document.querySelector("span.month.selected").innerHTML;
      var year = parseInt(document.querySelector("span.year.selected").innerHTML);

      var months = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
      ];
      
      months.forEach(function (value, i) {
      if (value === month && i <= 8 ) {
        i ++;
        month = "0" + i.toString();
      } else if (value === month && i >= 9 ) {
          i ++;
        month = i.toString();
      }
      });
      
      this.fullDate = year + "" + month + "" + day;
    },
    calculateFertileBegin: function() {
      this.calcDate();
      if ( this.cycleSelected === 28) {
        this.fertileFrom = moment(this.fullDate, 'YYYYMMDD').add(12, 'days').format('MMMM Do YYYY');
      } else {
          this.fertileFrom = moment(this.fullDate, 'YYYYMMDD').add(this.cycleSelected - 28 + 12, 'days').format('MMMM Do YYYY');  
      }
    },    
    calculateFertileEnds: function() {
      //this.calcDate(); is called from calculateFertileBegin
      if ( this.cycleSelected === 28) {
        this.fertileUntil = moment(this.fullDate, 'YYYYMMDD').add(16, 'days').format('MMMM Do YYYY');
      } else {
          this.fertileUntil = moment(this.fullDate, 'YYYYMMDD').add(this.cycleSelected - 28 + 16, 'days').format('MMMM Do YYYY');  
      }
    },
    calculateDueDate: function() {
      //this.calcDate(); is called from calculateFertileBegin
      if ( this.cycleSelected === 28) {
        this.dueDate = moment(this.fullDate, 'YYYYMMDD').add(280, 'days').format('MMMM Do YYYY');
      } else {
          this.dueDate = moment(this.fullDate, 'YYYYMMDD').add(this.cycleSelected - 28 + 280, 'days').format('MMMM Do YYYY');  
      }
    },
    resetCalc: function() {
      this.calcReturned = false;
      this.date = new Date();
      this.cycleSelected = 28;
      this.fertileFrom = "";
      this.fertileUntil = "";
      this.dueDate = "";
      this.fullDate = "";
    },
  },
});

View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js
  2. https://unpkg.com/vuejs-datepicker
  3. https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js