Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 12, 2022 11:59 pm GMT

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

An income tracker will allow users to monitor and keep track of their expenses. The income tracker app makes it easy for anyone to add, edit, update, and delete specific data from the client-side, and it updates accordingly in the database.

This article will teach us how to build an income tracker app with Vue.js and using Appwrite for storing the data.

First, let's get an introduction to some of the technologies used to build the income tracker app.

Vue.js: It is an open-source progressive and versatile frontend framework for building web user interfaces and single page applications with the bedrock technologies of HTML, CSS, and JavaScript.

Appwrite: It is a secure self hosted open-source backend-as-a-service that provides developers all the core APIs to build applications ranging from web to mobile.

Getting Started with Vue

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

vue create income-tracker

With the project set up, let's start a development server that is accessible on http://localhost:8080

cd income-tracker yarn serve

In the terminal, let's install Appwrite client-side SDK with the command. The installation of this dependency will enable configure Appwrite and use it across our application.

yarn add appwrite

Appwrite Setup

To get the full functionalities of Appwrite backend features, we will manually set it up using Docker.

Now, let's get the Appwrite server running. Before, we can get this to work, install the Docker CLI. In our project folder, install the Docker installer tool in the terminal which will give us access to our Appwrite console dashboard. The installation supports different operating system (OS) with this getting started guide.

Note: Use http://localhost/console to access the Appwrite console.

Creating a New Appwrite Project

Once we have created an account, click on the Create Project. We will name the project income-tracker.

Add new project

With the income tracker project created, let's create a collection and add a lists of attributes.

Navigate to the Database tab and click the Add Collection and create a new collection called tracker

collection name - tracker

Within the collection, click the Attributes tab and create the following attributes for our document.

attributes

The most exciting part of this configuration is that Appwrite will accept the data from the client-side and store them in the documents.

Initialising the Web SDK

In the project with our Vue code, create a new file utils.js in the src directory to define the new Appwrite instance and other helpful variables.

Copy and paste the following code.

import { Appwrite } from 'appwrite';// Init your Web SDKconst appwrite = new Appwrite();appwrite  .setEndpoint('http://EndpointURL.example') // Replace this with your endpoint  .setProject('ProjectID'); // Replace this with your ProjectIDappwrite.account.createAnonymousSession().then(  (response) => {    console.log(response);  },  (error) => {    console.log(error);  });export const db = appwrite.database;export const COLLECTION_ID = 'COLLECTION ID'; // Replace with your Collection ID

To bypass some security requirements, we created an anonymous session on Appwrite.

The PROJECT_ID in the above code, the value is found in the Settings under the Home tab.

project id

For the COLLECTION_ID, access it in the Collection Settings in the Database tab.

collection id

At the Collection Level within the settings tab, set the Read and Write access to have the values of role:all.

Creating the Income Tracker

Let's create the navigation menu that will display the current expenses.

In the Header.vue file in the components folder, paste in the following code.

<template>  <header>    <h1>Income Tracker</h1>    <div class="total-income">      $500    </div>  </header></template><style scoped>header {  display: flex;  align-items: center;  justify-content: space-between;}h1, .total-income {  color: var(--grey);  font-weight: 700;  font-family: 'Inter', sans-serif;}h1 {  font-size: 2rem;}.total-income {  font-size: 1.75rem;  background: var(--bg-total-income);  padding: .3125rem 1.5625rem;  border-radius: 0.5rem;}</style>

Creating the Income Form

Here, we will create the income form with input that accept text and date attributes.

Create another file in the components folder called IncomeForm.vue and paste the following code.

<template>  <section>    <form class="income-form">      <div class="form-inner">        <input          v-model="income"          placeholder="Income Description"          type="text"          required        />        <input          v-model="price"          min="0"          placeholder="Price..."          type="number"          required        />        <input          v-model="date"          placeholder="Income date..."          type="date"          required        />        <input type="submit" value="Add Income" />      </div>    </form>  </section></template><script>export default {  data() {    return {      income: '',      price: '',      date: null,    };  },};</script>

The code above has the data properties for the income, price, and date variables set to an empty string and null respectively. For the reference of this data properties, we bound them to the <input> element using the v-model directive.

Another important component that we need for this application is a list that will hold all the accepted data.

Create the IncomeList.vue component and paste the following code.

<template>  <section>    <div class="income-item">      <div class="space desc">Web Design</div>      <div class="space price">$500</div>      <div class="space date">2022-05-24</div>      <div class="btn">        <div class="btn-edit">update</div>        <div class="btn-del">delete</div>      </div>    </div>  </section></template><style scoped>section {  padding: unset;}.income-item {  background: #ffffff;  padding: 0.625em 0.94em;  border-radius: 5px;  box-shadow: 0px 4px 3px rgba(0, 0, 0, 0.1);  position: relative;  margin: 2em 0;}.space + .space {  margin-top: 1em;}.desc {  font-size: 1.5rem;}.btn {  position: absolute;  bottom: 0;  right: 0;  display: flex;  align-items: center;  padding: 0.75em;  text-transform: capitalize;}.btn-edit {  color: var(--grey);}.btn-del {  margin-left: 10px;  color: var(--alert);}.btn-edit,.btn-del {  cursor: pointer;}@media screen and (min-width: 768px) {  .desc {    font-size: 2rem;  }  .price {    font-size: 1.5rem;  }  .date {    font-size: 1.5rem;  }  .btn-edit,  .btn-del {    font-size: 1.5rem;  }}@media screen and (min-width: 1200px) {  .income-item,  .modal__wrapper {    width: 80%;    margin-inline: auto;  }}</style>

With this in place, let's import the IncomeForm.vue, IncomeList.vue, andHeader.vue` component into the application entry point App.vue with the following code.

`








import Header from "./components/Header"
import IncomeForm from './components/IncomeForm'
import IncomeList from "./components/IncomeList";

export default {
name: 'App',
components: {
Header,
IncomeForm,
IncomeList
},
}

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

*,
*::before,
*::after {
box-sizing: border-box;
}

/* Reset margins */
body,
h1,
h2,
h3,
h4,
h5,
p,
figure,
picture {
margin: 0;
}

body {
font-family: 'Montserrat', sans-serif;
background: var(--light)
}

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

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

/* make form elements easier to work with */
input,
button,
textarea,
select {
font: inherit;
}

button {
cursor: pointer;
}

section {
padding: 3em 0;
}

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

/income form and income list styling/
input {
width: 100%;
border: 1px solid gray;
}

.income-form {
display: block;
}

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

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

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

.form-inner input[type="submit"]:hover {
background-position: 100%;
color: #FFF;
}

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

input + input {
margin: 0;
}

input {
border: unset;
}

}

`

Our app should look like this with the recent changes.

income tracker app

Fetch All Income List

We create a function to fetch all the listed income from the Appwrite database when the page loads. Update the <script> section in the App.vue file with the following code.

// imported component

import { COLLECTION_ID, db } from '@/utils';

export default {
name: 'App',
components: {
// all components
},
created() {
this.fetchLists();
},
data() {
return {
lists: [],
};
},
methods: {
fetchLists() {
let promise = db.listDocuments(COLLECTION_ID);

  promise.then(    (res) =&gt; {      this.lists = res.documents;    },    (err) =&gt; {      console.log(err);    }  );},

},
};

We created a lists array property in the data function to store the lists and retrieve them using the listDocuments API.

In the created() lifecycle method, run the fetchLists() function when the App component is created.

Finally update the <template> section in the App.vue component with the following code.


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

To reuse the function to fetch all lists after creating a new income list, we bind the :fetchLists prop to the fetchLists method we defined earlier.

Creating a new Income List

In the IncomeForm.vue file is where we handle the income addition to the database.

Paste the following code to update the file.

`




v-model="income"
placeholder="Income Description"
type="text"
required
/>
v-model="price"
min="0"
placeholder="Price..."
type="number"
required
/>
v-model="date"
placeholder="Income date..."
type="date"
required
/>




import { COLLECTION_ID, db } from '@/utils';

export default {
props: ['fetchLists'],
// data
methods: {
addIncome() {
if (this.income === '' && this.price === '' && this.date === '') {
return;
}

  let promise = db.createDocument(COLLECTION_ID, 'unique()', {    income: this.income.charAt(0).toUpperCase() + this.income.slice(1),    price: this.price,    date: this.date,  });  promise.then(    () =&gt; {      this.fetchLists();      this.income = '';      this.price = '';      this.date = '';    },    (err) =&gt; {      console.log(err);    }  );},

},
};

`

In the addIncome method, we use Appwrites createDocument API to write a new list to the database. An error message is logged if the write operation fails. We fetch an updated list of all income after adding a new list.

The Appwrite web console will display one document representing a list in the image below:

list in the document

Updating the Income list Component

In the App.vue component, we update the income list components props to include the looped data and the fetchLists method.

`








// import component
import IncomeList from './components/IncomeList';

export default {
components: {
// other components
IncomeList,
},
};

`

fetchLists runs once the refreshData event is fired.

Let's update the IncomeList.vue component to handle list updates and deletion. We will also include a component to edit an income list. First, we add the update list function in the script portion with:

`

import { db } from '@/utils';

export default {
props: ['data'],
data() {
return {
open: false,
income: '',
price: '',
date: '',
};
},
methods: {
updateIncome() {
this.open = !this.open;
},

updateIncomeMethod() {  if (this.income === '' &amp;&amp; this.price === '' &amp;&amp; this.date === '') {    return;  }  let promise = db.updateDocument(this.data.$collection, this.data.$id, {    income: this.income.charAt(0).toUpperCase() + this.income.slice(1),    price: this.price,    date: this.date,  });  this.open = false;  promise.then(    () =&gt; {      this.$emit('refreshData');    },    (err) =&gt; {      console.log(err);    }  );},deleteIncome() {  let promise = db.deleteDocument(this.data.$collection, this.data.$id);  promise.then(    () =&gt; {      this.$emit('refreshData');    },    (err) =&gt; {      console.log('error occured', err);    }  );},

},
};

`

We added a state variable to manage the visibility of a lists action buttons. Appwrites updateDocument API uses the collection ID and document ID passed as props to update the comment. Once the list is updated, we emit the refreshData event to fetch all income list.

We update the template portion to utilize the methods and variables created.

`



{{ data.income }}
${{ data.price.toLocaleString('en-US') }}
{{ data.date }}

update
delete

<div v-if="this.open" class="modal__wrapper">  <form class="income-form" @submit.prevent="updateIncomeMethod">    <div class="form-inner">      <input        v-model="income"        :placeholder="data.income"        type="text"        required      />      <input        v-model="price"        :placeholder="data.price"        min="0"        type="number"        required      />      <input v-model="date" :placeholder="data.date" type="date" required />      <input type="submit" value="Update" />    </div>  </form></div>



`

The image below represents the working app.

working demo


Original Link: https://dev.to/terieyenike/how-to-build-an-income-tracker-using-vuejs-and-appwrite-1989

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To