Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/fredy-rizo/ecommerce-delivery-frontend/llms.txt

Use this file to discover all available pages before exploring further.

Ecommerce Delivery uses a layered approach to state: Vuex 4 is wired up as the global store but carries no application modules out of the box — the scaffold module (module-example) exists only as a structural guide. In practice, most feature state lives either in Vue 3 component setup() functions using ref and reactive, or in localStorage for data that must survive page refreshes (the user session and the shopping cart). Understanding which layer owns which piece of data makes it straightforward to know where to read or write when building new features.

Vuex store

The root store is created in src/store/index.js using Quasar’s store wrapper:
// src/store/index.js
import { store } from "quasar/wrappers";
import { createStore } from "vuex";

export default store(function (/* { ssrContext } */) {
  const Store = createStore({
    modules: {},
    strict: process.env.DEBUGGING,
  });

  return Store;
});
The modules object is empty — all Vuex modules must be registered here when added. Strict mode is enabled automatically in development via process.env.DEBUGGING.

module-example

src/store/module-example/ is the canonical template for any new Vuex module. Its index.js wires the four Vuex concerns together and enables namespacing:
// src/store/module-example/index.js
import state from './state'
import * as getters from './getters'
import * as mutations from './mutations'
import * as actions from './actions'

export default {
  namespaced: true,
  getters,
  mutations,
  actions,
  state
}
The state factory returns an empty object that you populate with module-specific data:
// src/store/module-example/state.js
export default function () {
  return {
    //
  }
}
Keep global cross-route state (e.g. notification counts, current user role once loaded) in a Vuex module registered in src/store/index.js. Keep page-scoped state that is not shared across routes in the component’s own setup() with ref() or reactive().

localStorage persistence

Three keys in localStorage serve as the app’s persistence layer across page loads:
KeyValueWritten by
tokenRaw JWT stringLogin handler on successful authentication
userJSON-serialised user objectLogin handler on successful authentication
productCarJSON-serialised array of cart itemsCart page on every quantity change or item removal

Writing session data at login

localStorage.setItem('token', response.token)
localStorage.setItem('user', JSON.stringify(response.user))

Writing cart data

localStorage.setItem('productCar', JSON.stringify(products))

Clearing on logout or session expiry

// On explicit logout (NavBar_users.vue)
const closeSession = () => {
  localStorage.clear();
  dataUser.value = null;
  updateLinks();
};

// On 401/403 API response (src/tools/User.js)
const ValidateSession = (res, router) => {
  if (res.code == "403" || res.code == 403 || res.code == "401" || res.code == 401) {
    router.push({ path: "/login" });
    localStorage.clear();
    return false;
  }
  return true;
};

User state helpers

src/tools/User.js exports three functions that are the single authoritative interface to user session state:

getDataUser()

Reads the user object and token from localStorage, writes the user back to ensure the key always exists, and returns a merged object:
const getDataUser = () => {
  const dataUser = JSON.parse(
    localStorage.getItem("user")
      ? localStorage.getItem("user")
      : '{"id":"1", "name": "Invitado", "address": "Indefinido", "phone_number": "1234567890", "email": "Invitado@email.com", "type":0, "roles":[{"name":"Invitado", "value":"4"}], "membership": {"status": { "code": 3, "status": "Cancelado" }, "expdate": "", "duedate": "", "value": ""}}'
  )
  const token = localStorage.getItem("token")
  localStorage.setItem("user", JSON.stringify(dataUser))
  return { ...dataUser, token }
}
If no user is stored, a guest placeholder object is used so that downstream code never needs to handle null directly.

validateUser({ rol })

Called before any protected action to confirm the current user has the required role. Accepts a single role value or an array of role values:
const validateUser = (data) => {
  const dataUser = JSON.parse(
    localStorage.getItem("user")
      ? localStorage.getItem("user")
      : '{"id":"1","name":"Invitado","roles":[{"name":"usuario","value":"1"}],...}'
  );
  const token = localStorage.getItem("token");

  if (dataUser.roles && dataUser.roles[0].value == 2) {
    return { ...dataUser, token };
  }

  const userRoles = dataUser.roles.map(r => Number(r.value));
  const allowedRoles = Array.isArray(data.rol) ? data.rol.map(Number) : [Number(data.rol)];
  const hasRole = allowedRoles.some(r => userRoles.includes(r));

  return hasRole ? { ...dataUser, token } : false;
};
Returns the merged user+token object when authorised, or false when the user does not have the required role. Components check the return value before making API calls:
const sessionUser = validateUser({ rol: [1, 3] });
if (!sessionUser) return router.push({ path: '/login' });

ValidateSession(res, router)

Called after every API response to reactively invalidate the session on auth errors. See the API Integration page for the full implementation.

Shopping cart state

The cart is entirely localStorage-based. No Vuex module is involved. The lifecycle is:
1

Load on mount

Car.vue reads localStorage.getItem('productCar') inside onMounted() and populates a ref([]) called products.
2

Sync on change

Every quantity increment, decrement, or removal writes the updated array back with localStorage.setItem('productCar', JSON.stringify(products)).
3

Notify other components

After each write, the cart dispatches a custom DOM event so the navbar badge updates without a page reload:
window.dispatchEvent(new Event('cartUpdated'))
4

Clear after order

On a successful order placement, the cart key is removed:
localStorage.removeItem('productCar')
NavBar_users.vue listens for both the native storage event (cross-tab) and the custom cartUpdated event (same tab) to keep the cart badge in sync:
// src/layouts/NavBar_users.vue
const updateCartCount = () => {
  const items = JSON.parse(localStorage.getItem("productCar") || "[]");
  cartCount.value = items.reduce((sum, item) => sum + (item.cant || 1), 0);
};

window.addEventListener("storage", (e) => {
  if (e.key === "productCar") updateCartCount();
});

onMounted(() => {
  updateCartCount();
  window.addEventListener("cartUpdated", updateCartCount);
});

onBeforeUnmount(() => {
  window.removeEventListener("cartUpdated", updateCartCount);
});

Form validation utility

src/tools/ValidateForm.js exports validateRequire, a lightweight client-side validator that checks a set of required fields before submitting a form:
// src/tools/ValidateForm.js
const validateRequire = (data, rules) => {
  let fails = []
  for (let index = 0; index < rules.length; index++) {
    const element = rules[index];
    if (data[element] == null || data[element] == "") {
      fails.push({ message: "Este campo es requerido.", data: element })
    }
  }
  if (fails.length > 0) {
    return { status: false, fails }
  }
  return { status: true, fails }
}

export { validateRequire }
ParameterTypeDescription
dataobjectThe form data object to validate
rulesstring[]Array of field names that must be non-null and non-empty
Returns { status: true, fails: [] } on success, or { status: false, fails: [...] } with a descriptive entry per failing field. A typical call site:
import { validateRequire } from 'src/tools/ValidateForm'

const result = validateRequire(formData, ['name', 'email', 'phone_number'])
if (!result.status) {
  // result.fails is an array of { message, data } objects
  console.error(result.fails)
  return
}
// proceed with API call

Text utility

src/tools/Functions.js exports searchLinksinText, a utility that converts raw URLs embedded in a plain-text string into clickable HTML anchor tags:
// src/tools/Functions.js
const searchLinksinText = (texto) => {
  const regexEnlaces = /https?:\/\/[^\s]+/g;

  return texto.replace(regexEnlaces, (enlace) => {
    return `<a href="${enlace}" target="_blank">${enlace}</a>`;
  });
}

export { searchLinksinText }
This is used when rendering user-generated chat or message content (for example in components/Mensajes/ColumnasChats.vue) to make links inside text bodies interactive without requiring a rich-text editor.

Reactive component state

Feature components use the Vue 3 Composition API exclusively. The typical pattern for a list page is:
// Typical setup() pattern in a list/cart component
import { ref, computed, watch, onMounted } from 'vue'

const products = ref([])
const total = ref(0)

// Recompute total whenever products change
watch(products, (newProducts) => {
  total.value = newProducts.reduce((sum, p) => sum + p.price * p.cant, 0)
}, { deep: true })

onMounted(() => {
  const stored = localStorage.getItem('productCar')
  if (stored) products.value = JSON.parse(stored)
})
computed is used for derived values (totals, filtered lists), watch for side-effects triggered by state changes (persisting to localStorage, recalculating derived values), and onMounted for initial data loading.

Build docs developers (and LLMs) love