#vue
#javascript
#frontend
Day 14: Final Project - Task Master
Welcome to Day 14! You made it. You are now a Vue developer.
To graduate, we are going to build Task Master.
- Features: Add task, Delete task, Toggle Done, Filter (All/Favs).
- Tech Stack: Pinia (State), Router (Pages), Composables (Logic).
1. The Store (stores/TaskStore.ts)
We need a store to manage our tasks.
import { defineStore } from 'pinia'
export const useTaskStore = defineStore('taskStore', {
state: () => ({
tasks: [
{ id: 1, title: "Learn Vue 3", isFav: true },
{ id: 2, title: "Build an App", isFav: false }
],
name: "My Tasks"
}),
getters: {
favs() {
return this.tasks.filter(t => t.isFav)
},
favCount() {
return this.tasks.reduce((p, c) => c.isFav ? p + 1 : p, 0)
},
totalCount: (state) => {
return state.tasks.length
}
},
actions: {
addTask(task) {
this.tasks.push(task)
},
deleteTask(id) {
this.tasks = this.tasks.filter(t => t.id !== id)
},
toggleFav(id) {
const task = this.tasks.find(t => t.id === id)
if (task) task.isFav = !task.isFav
}
}
})
2. The Components
TaskDetails.vue
<script setup>
import { useTaskStore } from '../stores/TaskStore'
const props = defineProps(['task'])
const taskStore = useTaskStore()
</script>
<template>
<div class="task">
<h3>{{ task.title }}</h3>
<div class="icons">
<i
@click="taskStore.deleteTask(task.id)"
class="material-icons"
>delete</i>
<i
@click="taskStore.toggleFav(task.id)"
class="material-icons"
:class="{active: task.isFav}"
>favorite</i>
</div>
</div>
</template>
TaskForm.vue
<script setup>
import { ref } from 'vue'
import { useTaskStore } from '../stores/TaskStore'
const taskStore = useTaskStore()
const newTask = ref('')
const handleSubmit = () => {
if (newTask.value.length > 0) {
taskStore.addTask({
title: newTask.value,
isFav: false,
id: Math.floor(Math.random() * 10000)
})
newTask.value = ''
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input
type="text"
placeholder="I need to..."
v-model="newTask"
>
<button>Add</button>
</form>
</template>
3. The View (App.vue or HomeView.vue)
<script setup>
import { useTaskStore } from './stores/TaskStore'
import TaskDetails from './components/TaskDetails.vue'
import TaskForm from './components/TaskForm.vue'
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
const taskStore = useTaskStore()
const { tasks, loading, favs, totalCount, favCount } = storeToRefs(taskStore)
const filter = ref('all')
</script>
<template>
<main>
<header>
<h1>{{ taskStore.name }}</h1>
</header>
<div class="new-task-form">
<TaskForm />
</div>
<nav class="filter">
<button @click="filter = 'all'">All tasks</button>
<button @click="filter = 'favs'">Fav tasks</button>
</nav>
<div class="task-list" v-if="filter === 'all'">
<p>You have {{ totalCount }} tasks left to do</p>
<div v-for="task in tasks">
<TaskDetails :task="task" />
</div>
</div>
<div class="task-list" v-if="filter === 'favs'">
<p>You have {{ favCount }} favs task left to do</p>
<div v-for="task in favs">
<TaskDetails :task="task" />
</div>
</div>
</main>
</template>
Conclusion
You have just built a state-managed, component-based application.
Where to go from here?
- Nuxt.js: The Next.js equivalent for Vue. Server Side Rendering (SSR) and auto-imports.
- VueUse: A massive collection of ready-made composables.
- UI Libraries: PrimeVue, Vuetify, or Tailwind.
Thank you for following this series. Go build something amazing!