Pinia Store Error Fix

User
pinia.js?v=5601174a:941 Uncaught (in promise) TypeError: Cannot destructure property 'state' of 'options' as it is undefined.
at setup (FullLayout.vue:8:20)

<script>
import { RouterView } from 'vue-router';
import VerticalSidebarVue from './vertical-sidebar/VerticalSidebar.vue';
import VerticalHeaderVue from './vertical-header/VerticalHeader.vue';
import Customizer from './customizer/CustomizerPanel.vue';
import FooterPanel from './footer/FooterPanel.vue';
import { useCustomizerStore } from '@/stores/customizer';
const customizer = useCustomizerStore();
</script>
Assistant
The error:

pinia.js?v=5601174a:941 Uncaught (in promise) TypeError: Cannot destructure property 'state' of 'options' as it is undefined.

suggests that Pinia is unable to find the expected `state` property in the store's setup, possibly due to incorrect store initialization or a missing Pinia plugin.

### **Possible Causes and Fixes:**

#### 1. **Ensure Pinia is Properly Installed and Registered**
Make sure Pinia is installed in your project:

yarn add pinia

or

npm install pinia

Then, ensure you have registered Pinia in your `main.ts` (or `main.js`):

import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';

const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount('#app');

#### 2. **Check Your Customizer Store**
Ensure `useCustomizerStore` is properly defined in `@/stores/customizer.ts`. A proper Pinia store should be structured as follows:

import { defineStore } from 'pinia';

export const useCustomizerStore = defineStore({
  id: 'customizer',
  state: () => ({
    fontTheme: 'default-font',
    mini_sidebar: false,
    inputBg: false,
    Customizer_drawer: false,
  }),
  actions: {
    SET_CUSTOMIZER_DRAWER(value: boolean) {
      this.Customizer_drawer = value;
    },
  },
});

If you are using **the setup store syntax**, define it like this:

import { defineStore } from 'pinia';
import { ref } from 'vue';

export const useCustomizerStore = defineStore('customizer', () => {
  const fontTheme = ref('default-font');
  const mini_sidebar = ref(false);
  const inputBg = ref(false);
  const Customizer_drawer = ref(false);

  function SET_CUSTOMIZER_DRAWER(value: boolean) {
    Customizer_drawer.value = value;
  }

  return { fontTheme, mini_sidebar, inputBg, Customizer_drawer, SET_CUSTOMIZER_DRAWER };
});

#### 3. **Ensure Store Import and Usage is Correct**
In `FullLayout.vue`, verify that you're calling the store function correctly:

import { useCustomizerStore } from '@/stores/customizer';

const customizer = useCustomizerStore();

If using **Composition API**, ensure the store is properly called inside `setup`:

import { useCustomizerStore } from '@/stores/customizer';
import { storeToRefs } from 'pinia';

const customizer = useCustomizerStore();
const { fontTheme, mini_sidebar, inputBg, Customizer_drawer } = storeToRefs(customizer);

#### 4. **Restart Dev Server**
After making changes, restart your development server:

yarn dev

or

npm run dev

This will clear any potential stale caches and reload Pinia correctly.

### **Summary of Fixes**
1. Ensure Pinia is installed and registered in `main.ts`.
2. Check the structure of your `useCustomizerStore` store.
3. Ensure you are correctly calling and using `useCustomizerStore` inside `setup()`.
4. Restart your development server after making these changes.

Let me know if the issue persists! πŸš€

User
customizer.ts
import { defineStore } from 'pinia';
import config from '@/config';

export const useCustomizerStore = defineStore({
id: 'customizer',
state: () => ({
Sidebar_drawer: config.Sidebar_drawer,
Customizer_drawer: config.Customizer_drawer,
mini_sidebar: config.mini_sidebar,
fontTheme: config.fontTheme,
inputBg: config.inputBg
}),

getters: {},
actions: {
SET_SIDEBAR_DRAWER() {
this.Sidebar_drawer = !this.Sidebar_drawer;
},
SET_MINI_SIDEBAR(payload: boolean) {
this.mini_sidebar = payload;
},
SET_CUSTOMIZER_DRAWER(payload: boolean) {
this.Customizer_drawer = payload;
},
SET_FONT(payload: string) {
this.fontTheme = payload;
}
}
});

Assistant
Your `customizer.ts` store appears to be structured correctly, but based on the error:

TypeError: Cannot destructure property 'state' of 'options' as it is undefined.

it seems that the store might not be properly initialized. Below are potential fixes and debugging steps:

## πŸ” **Possible Issues & Fixes**
### 1️⃣ **Ensure Pinia is Properly Installed and Registered**
Since you're using Pinia, make sure it's installed and added to your project:

yarn add pinia

or

npm install pinia

Then, ensure it is **registered** in your `main.ts`:

import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';

const app = createApp(App);
const pinia = createPinia();

app.use(pinia);
app.use(router);
app.mount('#app');

### 2️⃣ **Refactor the Store to Use the Object Syntax Properly**
The `defineStore` function supports two types of syntax:
– **Object Syntax** (What you're using)
– **Setup Syntax** (Alternative)

#### βœ… **Fix: Use `state` and `actions` Correctly**
Modify your store like this:

import { defineStore } from 'pinia';
import config from '@/config';

export const useCustomizerStore = defineStore('customizer', {
  state: () => ({
    Sidebar_drawer: config.Sidebar_drawer ?? false,
    Customizer_drawer: config.Customizer_drawer ?? false,
    mini_sidebar: config.mini_sidebar ?? false,
    fontTheme: config.fontTheme ?? 'default-font',
    inputBg: config.inputBg ?? false
  }),
  actions: {
    SET_SIDEBAR_DRAWER() {
      this.Sidebar_drawer = !this.Sidebar_drawer;
    },
    SET_MINI_SIDEBAR(payload: boolean) {
      this.mini_sidebar = payload;
    },
    SET_CUSTOMIZER_DRAWER(payload: boolean) {
      this.Customizer_drawer = payload;
    },
    SET_FONT(payload: string) {
      this.fontTheme = payload;
    }
  }
});


### 3️⃣ **Check the Import & Usage in `FullLayout.vue`**
Verify that the store is properly imported and used inside the `setup` function in `FullLayout.vue`:

#### βœ… **Fix: Ensure Proper Store Import**

import { useCustomizerStore } from '@/stores/customizer';
import { storeToRefs } from 'pinia';

const customizer = useCustomizerStore();
const { fontTheme, mini_sidebar, inputBg, Customizer_drawer } = storeToRefs(customizer);


### 4️⃣ **Restart Your Dev Server**
Sometimes, Pinia caches old states or fails to pick up changes. Restart your server:

yarn dev

or

npm run dev

## 🎯 **Final Summary**
βœ” Ensure `pinia` is **installed and registered** in `main.ts`
βœ” **Fix state initialization** in `customizer.ts` using `??` defaults
βœ” Use `storeToRefs()` when **accessing state in Vue components**
βœ” **Restart** the development server

Try these fixes, and let me know if the error persists! πŸš€

User
<script>
import { ref } from 'vue';
import Google from '@/assets/images/auth/social-google.svg';
import { useAuthStore } from '@/stores/auth';
import { Form } from 'vee-validate';

const checkbox = ref(false);
const valid = ref(false);
const show1 = ref(false);
//const logform = ref();
const password = ref('admin123');
const username = ref('info@codedthemes.com');
const passwordRules = ref([
  (v: string) => !!v || 'Password is required',
  (v: string) => (v && v.length <= 10) || 'Password must be less than 10 characters'
]);
const emailRules = ref([(v: string) => !!v || 'E-mail is required', (v: string) => /.+@.+..+/.test(v) || 'E-mail must be valid']);

/* eslint-disable @typescript-eslint/no-explicit-any */
function validate(values: any, { setErrors }: any) {
  const authStore = useAuthStore();
  return authStore.login(username.value, password.value).catch((error) => setErrors({ apiError: error }));
}
</script>

Sign in with Google

OR

Sign in with Email address

Forgot password?

Sign In

{{ errors.apiError }}

Don't Have an account?

<style>
.custom-devider {
  border-color: rgba(0, 0, 0, 0.08) !important;
}
.googleBtn {
  border-color: rgba(0, 0, 0, 0.08);
  margin: 30px 0 20px 0;
}
.outlinedInput .v-field {
  border: 1px solid rgba(0, 0, 0, 0.08);
  box-shadow: none;
}
.orbtn {
  padding: 2px 40px;
  border-color: rgba(0, 0, 0, 0.08);
  margin: 20px 15px;
}
.pwdInput {
  position: relative;
  .v-input__append {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
  }
}
.loginForm {
  .v-text-field .v-field--active input {
    font-weight: 500;
  }
}
</style>

adjust below form like above

<script>
import { ref,inject,onMounted } from "vue";
import { useForm, useField } from 'vee-validate'
import { useAuthStore } from '@/stores/auth';
import { useRouter } from 'vue-router';
import publicApi from '@/publicApi';
import SingleColumn from '@/layouts/SingleColumn.vue';
import {signInWithGoogle, signInWithGithub} from '@/firebase';
import { useLoadingStore } from '@/stores/loading';
import { nextTick } from "vue";
const loadingStore = useLoadingStore();
const authStore = useAuthStore();
const router = useRouter();
const apiUrl = inject('apiUrl');
const showPassword = ref(false);
const user = ref(null);
const alertMessage=ref("");
const alertColor=ref("");
console.log(apiUrl);
const { handleSubmit, handleReset, values } = useForm({
    validationSchema: {
     
      email (value:string) {
        if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/i.test(value)) return true

        return 'Must be a valid e-mail.'
      },
      password (value:string) {
        if (value?.length >= 6) return true

        return 'Password needs to be at least 6 characters.'
      }

    },
  })


  const email = useField('email')
  const password = useField('password')
  const submit = handleSubmit(async (values) => {
    loadingStore.setLoading(true);
    alertMessage.value = "";
    alertColor.value = "";
    try {
    const response = await publicApi.post(`${apiUrl}/login`, {
      email: values.email,
      password: values.password
    });
    
   console.log(response);
    if (response.data.success) {
      alertMessage.value = "Login successful!";
      alertColor.value = 'success';
      authStore.setUser(response.data.user, response.data.token);
      router.push('/dashboard');
    } else {
      console.log(response.data);
      alertMessage.value = response.data.message;
      alertColor.value = 'error';
      console.error("Login failed:", response.data.message);
    }
  } catch (error: any) {
   console.log(error);
  
    alertMessage.value =error.response?.data.message;
    alertColor.value = 'error';
    console.error("Error during login:", error.response?.data.message || error.message);
  } finally {
    loadingStore.setLoading(false);
  }
  })

onMounted(() => {
  if (authStore.isAuthenticated) {
    router.push("/dashboard"); // Redirect if already logged in
  }
});

const getFirebaseToken = async (provider:string) => {
  try {
    // Sign in with Google or GitHub
    const result = provider === "google" ? await signInWithGoogle() : await signInWithGithub();
    //console.log(result);
    // Get Firebase ID token
    const idToken = await result.user.getIdToken();
    console.log(result.user.displayName, result.user.email, result.user.photoURL, result.user.uid);
    const userData = {
      name: result.user.displayName,
      email: result.user.email,
      avatar: result.user.photoURL,
      firebase_uid: result.user.uid,
      id_token: idToken,
    };
    loadingStore.setLoading(true);
    const response = await publicApi.post(`${apiUrl}/firebase-login`, {
      name: result.user.displayName,
      email: result.user.email,
      avatar: result.user.photoURL,
      firebase_uid: result.user.uid,
      id_token: idToken,
    });
    console.log(result.user.displayName);
    console.log(response);
    if (response.data.success) {
      alertMessage.value = "Login successful!";
      alertColor.value = 'success';
      authStore.setUser(response.data.user, response.data.token);
      router.push('/dashboard');
    } else {
      console.log(response.data);
      alertMessage.value = response.data.message;
      alertColor.value = 'error';
      console.error("Login failed:", response.data.message);
    }
  } catch (error: any) {
    console.log("Error getting Firebase Token:", error);
    alertMessage.value = error.response?.data.message;
    alertColor.value = 'error';
    console.error("Error during login:", error.response?.data.message || error.message);
  } finally{
    loadingStore.setLoading(false);
  }
};
</script>

Login

login

clear

Forgot Password?
Login with Google

Assistant
Here’s your adjusted form, keeping the structure and styles from your original `vee-validate` implementation while integrating it into the new approach using `useForm`, `useField`, and better TypeScript practices.

### **Key Adjustments**
1. **Use `vee-validate` Properly:**
– `useForm` is now handling the form submission.
– `useField` is used for validation.
2. **Proper Pinia Store Usage:**
– `useAuthStore()` is correctly initialized.
3. **Loading and Error Handling:**
– Properly manages UI feedback for login attempts.
4. **Google Authentication Support:**
– Integrated `getFirebaseToken` for social login.

### **Updated Code**

<script setup lang="ts">
import { ref, inject, onMounted } from "vue";
import { useForm, useField } from "vee-validate";
import { useAuthStore } from "@/stores/auth";
import { useRouter } from "vue-router";
import publicApi from "@/publicApi";
import SingleColumn from "@/layouts/SingleColumn.vue";
import { signInWithGoogle, signInWithGithub } from "@/firebase";
import { useLoadingStore } from "@/stores/loading";

const loadingStore = useLoadingStore();
const authStore = useAuthStore();
const router = useRouter();
const apiUrl = inject("apiUrl");

const showPassword = ref(false);
const alertMessage = ref("");
const alertColor = ref("");

const { handleSubmit, handleReset } = useForm({
  validationSchema: {
    email(value: string) {
      if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/i.test(value))
        return true;
      return "Must be a valid e-mail.";
    },
    password(value: string) {
      if (value?.length >= 6) return true;
      return "Password needs to be at least 6 characters.";
    },
  },
});

const email = useField("email");
const password = useField("password");

const submit = handleSubmit(async (values) => {
  loadingStore.setLoading(true);
  alertMessage.value = "";
  alertColor.value = "";

  try {
    const response = await publicApi.post(`${apiUrl}/login`, {
      email: values.email,
      password: values.password,
    });

    if (response.data.success) {
      alertMessage.value = "Login successful!";
      alertColor.value = "success";
      authStore.setUser(response.data.user, response.data.token);
      router.push("/dashboard");
    } else {
      alertMessage.value = response.data.message;
      alertColor.value = "error";
    }
  } catch (error: any) {
    alertMessage.value = error.response?.data.message || "Login failed.";
    alertColor.value = "error";
  } finally {
    loadingStore.setLoading(false);
  }
});

onMounted(() => {
  if (authStore.isAuthenticated) {
    router.push("/dashboard"); // Redirect if already logged in
  }
});

const getFirebaseToken = async (provider: string) => {
  try {
    // Sign in with Google or GitHub
    const result =
      provider === "google" ? await signInWithGoogle() : await signInWithGithub();
    const idToken = await result.user.getIdToken();

    const userData = {
      name: result.user.displayName,
      email: result.user.email,
      avatar: result.user.photoURL,
      firebase_uid: result.user.uid,
      id_token: idToken,
    };

    loadingStore.setLoading(true);
    const response = await publicApi.post(`${apiUrl}/firebase-login`, userData);

    if (response.data.success) {
      alertMessage.value = "Login successful!";
      alertColor.value = "success";
      authStore.setUser(response.data.user, response.data.token);
      router.push("/dashboard");
    } else {
      alertMessage.value = response.data.message;
      alertColor.value = "error";
    }
  } catch (error: any) {
    alertMessage.value = error.response?.data.message || "Authentication failed.";
    alertColor.value = "error";
  } finally {
    loadingStore.setLoading(false);
  }
};
</script>

<template>
  <SingleColumn :md="12" :mt="4">
    <h3 class="text-h4 text-center mb-4">Login</h3>

    <v-card class="py-8 px-6 text-center mx-auto ma-4" elevation="12" max-width="600">
      <v-sheet class="mx-auto mt-4 mb-4" max-width="600">
        <form @submit.prevent="submit">
          <v-text-field
            v-model="email.value.value"
            :error-messages="email.errorMessage.value"
            label="E-mail"
            variant="outlined"
          ></v-text-field>

          <v-text-field
            v-model="password.value.value"
            :type="showPassword ? 'text' : 'password'"
            :error-messages="password.errorMessage.value"
            prepend-inner-icon="mdi-lock"
            :append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
            @click:append-inner="showPassword = !showPassword"
            label="Password"
            variant="outlined"
          ></v-text-field>

          <v-btn class="me-4" type="submit" :loading="loadingStore.isLoading">
            Login
          </v-btn>
          <v-btn @click="handleReset"> Clear </v-btn>
        </form>

        <v-btn :to="`/forget-password`" color="primary" class="mt-2">Forgot Password?</v-btn>

        <v-btn prepend-icon="mdi-google" color="primary" @click="getFirebaseToken('google')">
          Login with Google
        </v-btn>
      </v-sheet>
    </v-card>

    <TheAlert v-if="alertMessage" :title="alertMessage" :color="alertColor" :icon="alertColor" />
  </SingleColumn>
</template>

<style lang="scss">
.v-card {
  text-align: center;
}

.v-text-field {
  margin-bottom: 16px;
}

.v-btn {
  margin-top: 16px;
}

</style>

### **πŸ”Ή Key Enhancements in This Code:**
βœ… **Uses `vee-validate` correctly** with `useForm` and `useField`
βœ… **Handles form submission and validation smoothly**
βœ… **Manages loading state** using `useLoadingStore`
βœ… **Improves UI feedback** with alerts and error messages
βœ… **Firebase authentication support** for Google login

Now, it should work as expected! Let me know if you need any modifications. πŸš€

Leave a Comment

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