Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mauroperez055/infoJobs/llms.txt

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

InfoJobs DevBoard uses Zustand 5 for all global client state. Zustand was chosen because it requires no Provider wrapper around the component tree — each store is simply a custom hook that any component can import and call directly. Stores are created with Zustand’s create factory, which accepts a callback that receives set and get helpers for updating and reading state.
Neither store persists state to localStorage or any other storage mechanism. All state is held in memory and is lost when the page is refreshed.

authStore

The authStore tracks whether the current user has authenticated. It is the single source of truth consulted by ProtectedRoute, Header, JobCard, and the Login / Register pages.

Source

import { create } from 'zustand';

export const useAuthStore = create((set) => ({
  // State
  isLoggedIn: false,

  // Actions
  login:  () => set({ isLoggedIn: true }),
  logout: () => set({ isLoggedIn: false }),
}));

State and actions

NameTypeDescription
isLoggedInbooleantrue when the user has logged in during this session
login()actionSets isLoggedIn to true
logout()actionSets isLoggedIn to false

Usage in a component

import { useAuthStore } from '../store/authStore';

function MyComponent() {
  const { isLoggedIn, login, logout } = useAuthStore();

  return (
    <div>
      {isLoggedIn ? (
        <button onClick={logout}>Cerrar sesión</button>
      ) : (
        <button onClick={login}>Iniciar sesión</button>
      )}
    </div>
  );
}
The current implementation is frontend-only. Both login() and logout() simply flip a boolean flag — no credentials are sent to the backend, no JWT is issued or validated, and no session cookie is set.

favoriteStore

The favoriteStore maintains the list of job IDs that the user has saved as favourites. The favourite count is displayed in the Header nav link, and individual JobCard and DetailPageHeader components use it to show a filled or empty heart icon.

Source

import { create } from 'zustand';

export const useFavoriteStore = create((set, get) => ({
  // State
  favorites: [],

  // Actions
  addFavorite: (id) => {
    set((state) => ({
      favorites: state.favorites.includes(id)
        ? state.favorites
        : [...state.favorites, id],
    }));
  },

  removeFavorite: (id) => {
    set((state) => ({
      favorites: state.favorites.filter((favId) => favId !== id),
    }));
  },

  isFavorite: (id) => {
    return get().favorites.includes(id);
  },

  toggleFavorite: (id) => {
    const { isFavorite, addFavorite, removeFavorite } = get();
    isFavorite(id) ? removeFavorite(id) : addFavorite(id);
  },

  // Derived state
  countFavorites: () => get().favorites.length,
}));

State and actions

NameTypeDescription
favoritesstring[]Array of saved job IDs
addFavorite(id)actionAdds id to favorites if not already present (idempotent)
removeFavorite(id)actionRemoves id from favorites
isFavorite(id)selectorReturns true if id is in favorites
toggleFavorite(id)actionCalls addFavorite or removeFavorite based on current state
countFavorites()derived stateReturns the total number of saved job IDs

Usage in a component

import { useFavoriteStore } from '../store/favoriteStore';

function FavoriteButton({ id }) {
  const { isFavorite, toggleFavorite } = useFavoriteStore();

  return (
    <button
      onClick={() => toggleFavorite(id)}
      aria-label={isFavorite(id) ? 'Remove from favorites' : 'Add to favorites'}
    >
      {isFavorite(id) ? '❤️' : '🤍'}
    </button>
  );
}

Displaying the favourite count

The Header component reads countFavorites() to show a live badge next to the profile link:
import { useFavoriteStore } from '../store/favoriteStore';

const { countFavorites } = useFavoriteStore();
const numberOfFavorites = countFavorites();

// Inside JSX:
// Perfil ❤️ ({numberOfFavorites})

Build docs developers (and LLMs) love