How to Build an Income Tracker Using Vue.js and Appwrite

An revenue tracker will enable customers to observe and preserve monitor of their bills. The revenue tracker app makes it straightforward for anybody so as to add, edit, replace, and delete particular information from the client-side, and it updates accordingly within the database.

This text will train us the way to construct an revenue tracker app with Vue.js and utilizing Appwrite for storing the information.

First, let’s get an introduction to among the applied sciences used to construct the revenue tracker app.

Vue.js: It’s an open-source progressive and versatile frontend framework for constructing net consumer interfaces and single web page purposes with the bedrock applied sciences of HTML, CSS, and JavaScript.

Appwrite: It’s a safe self hosted open-source backend-as-a-service that gives builders all of the core APIs to construct purposes starting from net to cell.



Getting Began with Vue

In our terminal run the next command. This can create a boilerplate app and scaffold the Vue.js code for developmemt.

vue create income-tracker
Enter fullscreen mode

Exit fullscreen mode

With the venture arrange, let’s begin a improvement server that’s accessible on http://localhost:8080

cd income-tracker 

yarn serve
Enter fullscreen mode

Exit fullscreen mode

Within the terminal, let’s set up Appwrite client-side SDK with the command. The set up of this dependency will allow configure Appwrite and use it throughout our utility.

yarn add appwrite
Enter fullscreen mode

Exit fullscreen mode



Appwrite Setup

To get the complete functionalities of Appwrite backend options, we are going to manually set it up utilizing Docker.

Now, let’s get the Appwrite server operating. Earlier than, we are able to get this to work, set up the Docker CLI. In our venture folder, set up the Docker installer software within the terminal which can give us entry to our Appwrite console dashboard. The set up helps totally different working system (OS) with this getting started guide.

Observe: Use http://localhost/console to entry the Appwrite console.



Making a New Appwrite Mission

As soon as we’ve got created an account, click on on the Create Mission. We are going to identify the venture income-tracker.

With the revenue tracker venture created, let’s create a group and add a lists of attributes.

Navigate to the Database tab and click on the Add Assortment and create a brand new assortment referred to as tracker

collection name - tracker

Throughout the assortment, click on the Attributes tab and create the next attributes for our doc.

attributes

Probably the most thrilling a part of this configuration is that Appwrite will settle for the information from the client-side and retailer them within the paperwork.



Initialising the Net SDK

Within the venture with our Vue code, create a brand new file utils.js within the src listing to outline the brand new Appwrite occasion and different useful variables.

Copy and paste the next code.

import { Appwrite } from 'appwrite';
// Init your Net SDK
const appwrite = new Appwrite();
appwrite
  .setEndpoint('http://EndpointURL.instance') // Substitute this together with your endpoint
  .setProject('ProjectID'); // Substitute this together with your ProjectID

appwrite.account.createAnonymousSession().then(
  (response) => {
    console.log(response);
  },
  (error) => {
    console.log(error);
  }
);

export const db = appwrite.database;
export const COLLECTION_ID = 'COLLECTION ID'; // Substitute together with your Assortment ID
Enter fullscreen mode

Exit fullscreen mode

To bypass some safety necessities, we created an nameless session on Appwrite.

The PROJECT_ID within the above code, the worth is discovered within the Settings beneath the Residence tab.

project id

For the COLLECTION_ID, entry it within the Assortment Settings within the Database tab.

collection id

On the Assortment Degree throughout the settings tab, set the Learn and Write entry to have the values of position:all.



Creating the Revenue Tracker

Let’s create the navigation menu that can show the present bills.

Within the Header.vue file within the elements folder, paste within the following code.

<template>
  <header>
    <h1>Revenue Tracker</h1>
    <div class="total-income">
      $500
    </div>
  </header>
</template>

<type scoped>
header {
  show: flex;
  align-gadgets: heart;
  justify-content material: area-between;
}

h1, .whole-revenue {
  shade: var(--gray);
  font-weight: 700;
  font-household: 'Inter', sans-serif;
}

h1 {
  font-measurement: 2rem;
}

.whole-revenue {
  font-measurement: 1.75rem;
  background: var(--bg-whole-revenue);
  padding: .3125rem 1.5625rem;
  border-radius: 0.5rem;
}
</type>
Enter fullscreen mode

Exit fullscreen mode



Creating the Revenue Type

Right here, we are going to create the revenue type with enter that settle for textual content and date attributes.

Create one other file within the elements folder referred to as IncomeForm.vue and paste the next code.

<template>
  <part>
    <type class="income-form">
      <div class="form-inner">
        <enter
          v-mannequin="revenue"
          placeholder="Revenue Description"
          kind="textual content"
          required
        />
        <enter
          v-mannequin="value"
          min="0"
          placeholder="Value..."
          kind="quantity"
          required
        />
        <enter
          v-mannequin="date"
          placeholder="Revenue date..."
          kind="date"
          required
        />
        <enter kind="submit" worth="Add Revenue" />
      </div>
    </type>
  </part>
</template>

<script>
export default {
  information() {
    return {
      revenue: '',
      value: '',
      date: null,
    };
  },
};
</script>
Enter fullscreen mode

Exit fullscreen mode

The code above has the information properties for the revenue, value, and date variables set to an empty string and null respectively. For the reference of this information properties, we sure them to the <enter> aspect utilizing the v-model directive.

One other essential element that we want for this utility is an inventory that can maintain all of the accepted information.

Create the IncomeList.vue element and paste the next code.

<template>
  <part>
    <div class="income-item">
      <div class="area desc">Net Design</div>
      <div class="area value">$500</div>
      <div class="area date">2022-05-24</div>
      <div class="btn">
        <div class="btn-edit">replace</div>
        <div class="btn-del">delete</div>
      </div>
    </div>
  </part>
</template>

<type scoped>
part {
  padding: unset;
}

.revenue-merchandise {
  background: #ffffff;
  padding: 0.625em 0.94em;
  border-radius: 5px;
  field-shadow: 0px 4px 3px rgba(0, 0, 0, 0.1);
  place: relative;
  margin: 2em 0;
}

.area + .area {
  margin-high: 1em;
}

.desc {
  font-measurement: 1.5rem;
}

.btn {
  place: absolute;
  backside: 0;
  proper: 0;
  show: flex;
  align-gadgets: heart;
  padding: 0.75em;
  textual content-remodel: capitalize;
}

.btn-edit {
  shade: var(--gray);
}

.btn-del {
  margin-left: 10px;
  shade: var(--alert);
}

.btn-edit,
.btn-del {
  cursor: pointer;
}

@media display and (min-width: 768px) {
  .desc {
    font-measurement: 2rem;
  }

  .value {
    font-measurement: 1.5rem;
  }

  .date {
    font-measurement: 1.5rem;
  }

  .btn-edit,
  .btn-del {
    font-measurement: 1.5rem;
  }
}

@media display and (min-width: 1200px) {
  .revenue-merchandise,
  .modal__wrapper {
    width: 80%;
    margin-inline: auto;
  }
}
</type>
Enter fullscreen mode

Exit fullscreen mode

With this in place, let’s import the IncomeForm.vue, IncomeList.vue, andHeader.vue` element into the appliance entry level App.vue with the next code.

`

import Header from “./elements/Header”
import IncomeForm from ‘./elements/IncomeForm’
import IncomeList from “./elements/IncomeList”;

export default {
identify: ‘App’,
elements: {
Header,
IncomeForm,
IncomeList
},
}

:root {
–light: #F8F8F8;
–dark: #313131;
–grey: #888;
–primary: #FFCE00;
–secondary: #FE4880;
–alert: #FF1E2D;
–bg-total-income: #DFDFDF;
}

*,
*::earlier than,
*::after {
box-sizing: border-box;
}

/* Reset margins */
physique,
h1,
h2,
h3,
h4,
h5,
p,
determine,
image {
margin: 0;
}

physique {
font-family: ‘Montserrat’, sans-serif;
background: var(–light)
}

h1,
h2,
h3,
h4,
h5,
h6,
p {
font-weight: 400;
}

img,
picutre {
max-width: 100%;
show: block;
}

/* make type components simpler to work with */
enter,
button,
textarea,
choose {
font: inherit;
}

button {
cursor: pointer;
}

part {
padding: 3em 0;
}

.container {
max-width: 75rem;
width: 85%;
margin-inline: auto;
}

/revenue type and revenue record styling/
enter {
width: 100%;
border: 1px strong grey;
}

.income-form {
show: block;
}

.form-inner enter {
font-size: 1.125rem;
padding: 0.625em 0.94em;
background: #fff;
border-radius: 5px;
}

enter + enter {
margin-top: 2em;
}

.form-inner enter[type=submit] {
cursor: pointer;
background-image: linear-gradient(to proper, var(–primary) 50%, var(–primary) 50%, var(–secondary));
background-size: 200%;
background-position: 0%;
shade: var(–dark);
text-transform: uppercase;
transition: 0.4s;
border: unset;
}

.form-inner enter[type=”submit”]:hover {
background-position: 100%;
shade: #FFF;
}

@media display and (min-width: 1200px) {
.form-inner {
show: flex;
justify-content: heart;
}

enter + enter {
margin: 0;
}

enter {
border: unset;
}

}

`

Our app ought to appear to be this with the current adjustments.

income tracker app



Fetch All Revenue Checklist

We create a operate to fetch all of the listed revenue from the Appwrite database when the web page hundreds. Replace the <script> part within the App.vue file with the next code.

// imported element

import { COLLECTION_ID, db } from ‘@/utils’;

export default {
identify: ‘App’,
elements: {
// all elements
},
created() {
this.fetchLists();
},
information() {
return {
lists: [],
};
},
strategies: {
fetchLists() {
let promise = db.listDocuments(COLLECTION_ID);

  promise.then(
    (res) =&gt; {
      this.lists = res.paperwork;
    },
    (err) =&gt; {
      console.log(err);
    }
  );
},
Enter fullscreen mode

Exit fullscreen mode

},
};

We created a lists array property within the information operate to retailer the lists and retrieve them utilizing the listDocuments API.

Within the created() lifecycle methodology, run the fetchLists() operate when the App element is created.

Lastly replace the <template> part within the App.vue element with the next code.


<template>
<part class="container">
<Header :totalIncome="lists" />
<IncomeForm :fetchLists="fetchLists" />
<div v-for="information in lists" :key="information.revenue">
<IncomeList :information="information" v-on:refreshData="fetchLists" />
</div>
</part>
</template>

To reuse the operate to fetch all lists after creating a brand new revenue record, we bind the :fetchLists prop to the fetchLists methodology we outlined earlier.



Creating a brand new Revenue Checklist

Within the IncomeForm.vue file is the place we deal with the revenue addition to the database.

Paste the next code to replace the file.

`

v-model=”revenue”
placeholder=”Revenue Description”
kind=”textual content”
required
/>
v-model=”value”
min=”0″
placeholder=”Value…”
kind=”quantity”
required
/>
v-model=”date”
placeholder=”Revenue date…”
kind=”date”
required
/>

import { COLLECTION_ID, db } from ‘@/utils’;

export default {
props: [‘fetchLists’],
// information
strategies: {
addIncome() {
if (this.revenue === ” && this.value === ” && this.date === ”) {
return;
}

  let promise = db.createDocument(COLLECTION_ID, 'distinctive()', {
    revenue: this.revenue.charAt(0).toUpperCase() + this.revenue.slice(1),
    value: this.value,
    date: this.date,
  });

  promise.then(
    () =&gt; {
      this.fetchLists();
      this.revenue="";
      this.value="";
      this.date="";
    },
    (err) =&gt; {
      console.log(err);
    }
  );
},
Enter fullscreen mode

Exit fullscreen mode

},
};

`

Within the addIncome methodology, we use Appwrite’s createDocument API to write down a brand new record to the database. An error message is logged if the write operation fails. We fetch an up to date record of all revenue after including a brand new record.

The Appwrite net console will show one doc representing an inventory within the picture under:

list in the document



Updating the Revenue record Part

Within the App.vue element, we replace the revenue record element’s props to incorporate the looped information and the fetchLists methodology.

`

// import element
import IncomeList from ‘./elements/IncomeList’;

export default {
elements: {
// different elements
IncomeList,
},
};

`

fetchLists runs as soon as the refreshData occasion is fired.

Let’s replace the IncomeList.vue element to deal with record updates and deletion. We will even embody a element to edit an revenue record. First, we add the replace record operate within the script portion with:

`

import { db } from ‘@/utils’;

export default {
props: [‘data’],
information() {
return {
open: false,
revenue: ”,
value: ”,
date: ”,
};
},
strategies: {
updateIncome() {
this.open = !this.open;
},

updateIncomeMethod() {
  if (this.revenue === '' &amp;&amp; this.value === '' &amp;&amp; this.date === '') {
    return;
  }

  let promise = db.updateDocument(this.information.$assortment, this.information.$id, {
    revenue: this.revenue.charAt(0).toUpperCase() + this.revenue.slice(1),
    value: this.value,
    date: this.date,
  });
  this.open = false;
  promise.then(
    () =&gt; {
      this.$emit('refreshData');
    },
    (err) =&gt; {
      console.log(err);
    }
  );
},

deleteIncome() {
  let promise = db.deleteDocument(this.information.$assortment, this.information.$id);
  promise.then(
    () =&gt; {
      this.$emit('refreshData');
    },
    (err) =&gt; {
      console.log('error occured', err);
    }
  );
},
Enter fullscreen mode

Exit fullscreen mode

},
};

`

We added a state variable to handle the visibility of an inventory’s motion buttons. Appwrite’s updateDocument API makes use of the gathering ID and doc ID handed as props to replace the remark. As soon as the record is up to date, we emit the refreshData occasion to fetch all revenue record.

We replace the template portion to make the most of the strategies and variables created.

`

{{ information.revenue }}
${{ information.value.toLocaleString(‘en-US’) }}
{{ information.date }}

replace
delete

<div v-if="this.open" class="modal__wrapper">
  <type class="income-form" @submit.stop="updateIncomeMethod">
    <div class="form-inner">
      <enter
        v-model="revenue"
        :placeholder="information.revenue"
        kind="textual content"
        required
      />
      <enter
        v-model="value"
        :placeholder="information.value"
        min="0"
        kind="quantity"
        required
      />
      <enter v-model="date" :placeholder="information.date" kind="date" required />

      <enter kind="submit" worth="Replace" />
    </div>
  </type>
</div>
Enter fullscreen mode

Exit fullscreen mode

`

The picture under represents the working app.

working demo

Add a Comment

Your email address will not be published. Required fields are marked *