Reba calculator

Simple demo to show an element with with Alpine.js

by lmeurs

HTML

<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

<h1 class="header">
  REBA calculator
  <img src="https://justiceandpeace.nl/wp-content/uploads/2022/06/Partner-AMIF-300x300-1.jpg" />
</h1>

<form
  x-cloak
  x-data="alpineToDemoBridge"
  @submit.prevent="demo.nextStep()"
  @reset.prevent="demo.reset()"
>

  <fieldset
    id="step-amv"
    @step-activation="if (input.isAmv === undefined) submitButtonDisabled = true"
  >
    <legend>Wat is jouw leeftijd?</legend>

    <label>
      <input type="radio" value="false" x-model="input.isAmv">
      Ik ben 18 jaar of ouder
    </label>

    <label>
      <input type="radio" value="true" x-model="input.isAmv">
      Ik ben 17 jaar of jonger (amv)
    </label>
  </fieldset>
    
  <fieldset 
    id="step-composition"
    @step-activation="if (input.isAmv == 'true') setStep('++')"
    x-show="stepEqualsOrHigher('composition') && input.isAmv === 'false' "
    x-transition:enter.opacity.duration.500ms
  >
    <legend>Hoe is jouw gezin samengesteld?</legend>

    <label>
      <select x-model="input.amountOfAdults">
        <template x-for="option in [1,2,3,4,5]" :key="option">
          <option :value="option" :selected="option == input.amountOfAdults" x-text="option"></option>
        </template>
      </select>

      <span x-show="input.amountOfAdults == 1">volwassene (18 jaar of ouder)</span>
      <span x-show="input.amountOfAdults != 1">volwassenen (18 jaar of ouder)</span>
    </label> 
    
    <label>
      <select x-model="input.amountOfChildren">
        <template x-for="option in [0,1,2,3,4,5]" :key="option">
          <option :value="option" :selected="option == input.amountOfChildren" x-text="option"></option>
        </template>
      </select>

      <span x-show="input.amountOfChildren == 1">kind (17 jaar of jonger)</span>
      <span x-show="input.amountOfChildren != 1">kinderen (17 jaar of jonger)</span>
    </label>

    <label x-show="input.amountOfChildren>0"...

SCSS

[x-cloak] { display: none !important; }

body {
  font: 12px/1.2 sans-serif;
}

fieldset {
  margin: 2em 0;
  padding: 1em;
  border: 1px solid #ccc;
}

label {
  display: block;
  line-height: 2;
}

input {
  &.error {
    border-color: red;
    outline-color: red;
  }

  &[type=checkbox] {
    vertical-align: middle;
  }
  
  &.indent {
    www-margin-left: 1.75em;
  }
}

.result {
  font-weight: bold;
  font-size: 18px;
}

button {
  border-radius: 0;
  border: 0;
  color: white;
  font-size: 15px;
  padding: .5em 1em;
  background: green;
  cursor: pointer;

  &.reset { background: orange; }
  &:disabled { opacity: .25; }
}

.header {
  img {
    display: block;
    width: 100px;
    margin: 1em 0;
  }
}

.version-info {
  color: #888;
  font-style: italic; 
}

JavaScript

// Reba: main object
let reba = {};

// Reba data object
// NB: amounts are per week and automatically converted to per month (multiplied by 4.33).
reba.data = {
	// "Zakgeld"
  pocketMoney: 14.47,
  // "Eetgeld"
  foodMoney: [],
  // "Wooncomponent"
  livingComponent: [],
  // "Logeerregeling"
  lodgingAllowance: [],
};

// Define food money per week, keyed by amount of persons.
reba.data.foodMoney[1] = { perAdult: 58.24, perChild: 48.51 };
reba.data.foodMoney[2] = { perAdult: 58.24, perChild: 48.51 };
reba.data.foodMoney[3] = { perAdult: 49.49, perChild: 41.23 };
reba.data.foodMoney[4] = { perAdult: 43.68, perChild: 36.40 };

// Define living component per week, keyed by amount of persons.
reba.data.livingComponent[1] = 50;
reba.data.livingComponent[2] = 25;
reba.data.livingComponent[3] = 12.5;
reba.data.livingComponent[4] = 12.5;

// Define lodging allowance per week, keyed by amount of persons.
reba.data.lodgingAllowance[1] = 75;
reba.data.lodgingAllowance[2] = 25;
reba.data.lodgingAllowance[3] = 12.5;
reba.data.lodgingAllowance[4] = 12.5;

/**
 * Calculate: main function
 */
reba.calculate = function(values) {
  // Parse weekly amounts to monthly.
  this.parseWeeklyToMonthly();

  // Calculate amount of persons.
	this.calculateAmountOfPersons(values);

  // Calculate week money.
	this.calculateWeekMoney(values);

  // Calculate exemption ("Vrijstelling"): 25% of the income with a maximum of 264.
  values.exemption = Math.min(264, values.monthlyIncome * .25);

  // Calculate maximum own contribution ("maximale eigen bijdrage").
	this.calculateMaxOwnContribution(values);

  // Calculate own contribution ("eigen bijdrage"): income minus exemption, or maximum.
  values.ownContribution = Math.min(values.monthlyIncome - values.exemption, values.maxOwnContribution); 
};

/**
 * Parse data: from weekly to monthly amounts
 */
reba.parseWeeklyToMonthly = function(dataObject) {
  // Only execute once (exit if no data object provided and set flag).
  if (!dataObject &&...