Keith

Vue JS Workout Application Plus SupaBase Backend

by Keith Rowles • 01/01/2024Vue

Webpage design concept illustration

Summary

A full CRUD Vue JS workout application using Supabase for the authentication and data storage.

Using Vue 3 composition API instead of the options API.

The application is an interactive website where you can register and login.

You can create, read, update and delete workouts.

Note: You can view all workouts… but only edit and delete workouts that you have created.

SupaBase is used for the backend for user authentication and a database to store workout information.

The project has been deployed to Netlify.

Tech and Tools

  • HTML
  • CSS
  • JavaScript
  • Vue
  • Vue Router
  • SupaBase
  • Vite
  • Tailwind CSS
  • Post CSS
  • SASS
  • Netlify
  • Uid
  • Environment Variables

Sample Code

Please view my GitHub repository for full code. Link provided at bottom of the page.

App.vue

<template>
  <div class="min-h-full font-Poppins box-border" v-if="appReady">
    <Navigation />
    <router-view />
  </div>
</template>

<script>
import Navigation from './components/Navigation.vue';
import { onMounted, ref } from 'vue';
import { supabase } from './supabase/init';
import store from './store/index';

export default {
  components: { Navigation },
  setup() {
    // Create data / vars
    const appReady = ref(null);

    // Check to see if user is already logged in
    onMounted(async () => {
      try {
        const {
          data: { user },
        } = await supabase.auth.getUser();

        // If user does not exist, need to make app ready
        if (!user) {
          appReady.value = true;
        }

        // Runs when there is a auth state change
        // if user is logged in, this will fire
        supabase.auth.onAuthStateChange((_, session) => {
          console.log('hello', session);
          store.methods.setUser(session);
          appReady.value = true;
        });
      } catch (error) {
        console.error(error);
      }
    });

    return { appReady };
  },
};
</script>

<style lang="scss">
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700&display=swap');
</style>

Login.vue

<template>
  <div class="max-w-screen-sm mx-auto px-4 py-10">
    <!-- error handling -->
    <div
      v-if="errorMessage"
      class="mb-10 p-4 rounded-md bg-light-grey shadow-lg"
    >
      <p class="text-red-500">{{ errorMessage }}</p>
    </div>
    <!-- login form -->
    <form
      class="p-8 flex flex-col bg-light-grey rounded-md shadow-lg"
      @submit.prevent="login"
    >
      <h1 class="text-3xl text-at-light-green mb-4">Login</h1>
      <div class="flex flex-col mb-2">
        <label for="email" class="mb-1 text-sm text-at-light-green"
          >Email</label
        >
        <input
          type="text"
          required
          class="p-2 text-gray-500 focus:outline-none"
          id="email"
          v-model="email"
        />
      </div>

      <div class="flex flex-col mb-2">
        <label for="password" class="mb-1 text-sm text-at-light-green"
          >Password</label
        >
        <input
          type="password"
          required
          class="p-2 text-gray-500 focus:outline-none"
          id="password"
          v-model="password"
        />
      </div>

      <button
        type="submit"
        class="mt-6 py-2 px-6 rounded-sm self-start text-sm text-white bg-at-light-green duration-200 border-solid border-2 border-transparent hover:border-at-light-green hover:bg-white hover:text-at-light-green"
      >
        Login
      </button>

      <router-link class="text-sm mt-6 text-center" :to="{ name: 'Register' }"
        >Don't have an account?
        <span class="text-at-light-green">Register</span></router-link
      >
    </form>
  </div>
</template>

<script>
import { ref } from 'vue';
import { supabase } from '../supabase/init';
import { useRouter } from 'vue-router';

export default {
  name: 'login',
  setup() {
    // Create data / vars
    const router = useRouter();
    const email = ref(null);
    const password = ref(null);
    const errorMessage = ref(null);
    // Login function
    const login = async () => {
      try {
        const { error } = await supabase.auth.signInWithPassword({
          email: email.value,
          password: password.value,
        });
        if (error) throw error;

        router.push({ name: 'Home' });
      } catch (error) {
        errorMessage.value = `Error: ${error.message}`;
        setTimeout(() => {
          errorMessage.value = null;
        }, 5000);
      }
    };

    return { email, password, errorMessage, login };
  },
};
</script>

Demo

Open demo on Netlify.

Link to Demo

Repo

Open repository on GitHub.

Link to Demo