Vue.js course Β· Module 1: Vue and Vite

Project: NOVA LAB Control Panel

4 min read
In this lesson5

Congratulations, cadet! We've reached the final project of the first module at NOVA LAB station. Time to combine all the skills you've learned into one functional system.

Today every station module has its own screen, and the engineer on duty runs between them to check that everything works. What's needed is a single panel that gathers the state of the whole station in one place - and that's exactly the panel you'll build.

Project Goal

Build a complete NOVA LAB Control Panel - a Vue.js application that combines all concepts from this module:

  • Vue.js and Vite - modern reactive framework
  • Single File Components - code organization in .vue files
  • Application structure - proper component hierarchy
  • Vue DevTools - diagnostic tools for debugging

Project Requirements

Your Control Panel should include:

1. Station status section

Display the station name, current status (ONLINE/OFFLINE), and last update date. Use Vue interpolation to display data.

2. Station module list

Create an array with at least 4 station modules (e.g., Laboratory, Reactor, Communications, Navigation). Each module should have a name, status, and energy level.

3. Component styling

Use the <style scoped> section to give the application a space panel look - dark background, neon colors, futuristic design.

What the Starter Contains

The starter in the editor already has data and a template skeleton. Simple values, such as the station name, are kept in ref() containers, while the module array is wrapped in a new function, reactive(). It works in a similar way, but it takes an object or an array and makes all of its fields reactive, and you access the items without .value:

1import { ref, reactive } from 'vue'
2
3const stationName = ref('NOVA LAB')
4
5const modules = reactive([
6  { id: 1, name: 'Laboratory', status: 'active', energy: 85 },
7  { id: 2, name: 'Reactor', status: 'active', energy: 92 }
8  // ...and more modules from the starter
9])

When you change modules[0].energy, the bar on the screen moves by itself, and the array remains an ordinary array with the same methods. You'll learn the details of reactive() in the Energy Reactor.

The module cards are created by the v-for directive, which repeats an element for every item of the array. The :key attribute gives each copy a unique identifier, and :style sets the width of the energy bar, just as :class sets a class:

1<div v-for="mod in modules" :key="mod.id" class="module-card">
2  <h3>{{ mod.name }}</h3>
3  <div class="energy-bar">
4    <div class="energy-fill" :style="{ width: mod.energy + '%' }"></div>
5  </div>
6</div>

Vue inserts as many cards as the array has items, so a fifth module added to the data immediately adds a fifth card, with no change to the template. We'll cover rendering lists in detail in the Hologram Workshop.

Extending the Panel

That's where the starter ends. Extend it with the tools you learned in the Launch Zone:

  • a button with @click that toggles the station status between ONLINE and OFFLINE,
  • a computed value with the average energy level of all modules,
  • a class that depends on the module status, set with :class,
  • a v-if and v-else message when the energy of any module drops below 50%,
  • a watch that logs every change of the station status to the console.

You can start the first point with a function like this. It uses the conditional operator: when the station is ONLINE, it switches it to OFFLINE, and the other way round:

1function toggleStation() {
2  stationStatus.value = stationStatus.value === 'ONLINE' ? 'OFFLINE' : 'ONLINE'
3}

Attach it to a button with @click="toggleStation", and the label in the header will change with every click. I recommend building the panel step by step: after every change, glance at the preview and at the Components tab in DevTools instead of writing everything at once.

Tips

  • Start with the <script setup> section with reactive data
  • Use ref() for simple values and reactive() for objects
  • Remember the <template> section with proper Vue syntax
  • Add <style scoped> at the end of the component

The starter puts <script setup> at the very top of the file. That's also a correct order: Vue doesn't enforce one, and the style guide only recommends keeping it consistent across the project, with the style last. You'll send the finished panel to your mentor in the next task, so describe in it where you used ref(), directives, computed and watch.

Experiment with the code below and expand it with your own ideas!

Remember: the control panel is every Launch Zone tool in a single module - data in containers, a template that shows it, and styles that seal the whole thing in its housing.

Code for this lesson: App.vue
1<script setup>
2import { ref, reactive } from 'vue'
3
4// NOVA LAB Control Panel - Module 1 final project
5const stationName = ref('NOVA LAB')
6const stationStatus = ref('ONLINE')
7const lastUpdate = ref(new Date().toLocaleString())
8
9const modules = reactive([
10  { id: 1, name: 'Laboratory', status: 'active', energy: 85 },
11  { id: 2, name: 'Reactor', status: 'active', energy: 92 },
12  { id: 3, name: 'Communications', status: 'warning', energy: 45 },
13  { id: 4, name: 'Navigation', status: 'active', energy: 78 }
14])
15
16// TODO: Add more station modules
17// TODO: Add status toggle functions
18// TODO: Add average energy calculation
19</script>
20
21<template>
22  <div class="control-panel">
23    <header>
24      <h1>{{ stationName }} - Control Panel</h1>
25      <p>Status: {{ stationStatus }} | Last update: {{ lastUpdate }}</p>
26    </header>
27
28    <section class="modules">
29      <h2>Station Modules</h2>
30      <!-- TODO: Display the modules list with v-for -->
31      <!-- TODO: Add dynamic classes based on status -->
32      <div v-for="mod in modules" :key="mod.id" class="module-card">
33        <h3>{{ mod.name }}</h3>
34        <p>Status: {{ mod.status }}</p>
35        <div class="energy-bar">
36          <div class="energy-fill" :style="{ width: mod.energy + '%' }"></div>
37        </div>
38        <span>{{ mod.energy }}%</span>
39      </div>
40    </section>
41  </div>
42</template>
43
44<style scoped>
45.control-panel {
46  background: #0a0a2e;
47  color: #e0e0ff;
48  min-height: 100vh;
49  padding: 2rem;
50  font-family: 'Courier New', monospace;
51}
52header { text-align: center; border-bottom: 1px solid #333; padding-bottom: 1rem; }
53h1 { color: #00d4ff; }
54.modules { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1rem; }
55.module-card { background: #1a1a4e; padding: 1rem; border-radius: 8px; flex: 1; min-width: 200px; border: 1px solid #333; }
56.energy-bar { background: #222; border-radius: 4px; height: 8px; margin: 0.5rem 0; }
57.energy-fill { background: #00d4ff; height: 100%; border-radius: 4px; transition: width 0.3s; }
58</style>

Spotted a mistake in this lesson?

Useful articles