/** * Memoized Redux Selectors * * Centralized selectors using createSelector for performance optimization. * These selectors prevent unnecessary re-renders by memoizing derived state. */ import { createSelector } from '@reduxjs/toolkit'; import type { RootState } from './store'; // ============================================================================ // Auth Selectors // ============================================================================ /** Select auth slice */ const selectAuthState = (state: RootState) => state.auth; /** Select current user with memoization */ export const selectCurrentUser = createSelector( [selectAuthState], (auth) => auth.currentUser, ); /** Select if user is authenticated */ export const selectIsAuthenticated = createSelector( [selectCurrentUser], (user) => !!user, ); /** Select user's role */ export const selectUserRole = createSelector( [selectCurrentUser], (user) => user?.app_role, ); /** Select user's permissions */ export const selectUserPermissions = createSelector( [selectUserRole], (role) => { if (!role) return []; return role.permissions || []; }, ); // ============================================================================ // Entity Selectors Factory // ============================================================================ /** * Create memoized selectors for an entity slice * * @example * const { selectData, selectCount, selectLoading } = createEntitySelectors('assets'); * const assets = useAppSelector(selectData); */ export function createEntitySelectors(sliceName: keyof RootState) { type EntityState = { data: T[]; loading: boolean; count: number; refetch: boolean; }; const selectSlice = (state: RootState) => state[sliceName] as unknown as EntityState; const selectData = createSelector([selectSlice], (slice) => slice.data); const selectLoading = createSelector([selectSlice], (slice) => slice.loading); const selectCount = createSelector([selectSlice], (slice) => slice.count); const selectRefetch = createSelector([selectSlice], (slice) => slice.refetch); const selectFirstItem = createSelector([selectData], (data) => data[0]); const selectById = (id: string) => createSelector([selectData], (data) => data.find((item: T & { id: string }) => item.id === id), ); return { selectSlice, selectData, selectLoading, selectCount, selectRefetch, selectFirstItem, selectById, }; } // ============================================================================ // Pre-built Entity Selectors // ============================================================================ import type { Asset, TourPage, Project, Role, User } from '../types/entities'; /** Asset selectors */ export const assetSelectors = createEntitySelectors('assets'); /** Tour page selectors */ export const tourPageSelectors = createEntitySelectors('tour_pages'); /** Project selectors */ export const projectSelectors = createEntitySelectors('projects'); /** Role selectors */ export const roleSelectors = createEntitySelectors('roles'); /** User selectors */ export const userSelectors = createEntitySelectors('users'); // ============================================================================ // Style Selectors // ============================================================================ /** Select style slice */ const selectStyleState = (state: RootState) => state.style; /** Select dark mode with memoization */ export const selectDarkMode = createSelector( [selectStyleState], (style) => style.darkMode, ); /** Select background layout color */ export const selectBgLayoutColor = createSelector( [selectStyleState], (style) => style.bgLayoutColor, ); /** Select focus ring color */ export const selectFocusRingColor = createSelector( [selectStyleState], (style) => style.focusRingColor, ); /** Select corners style */ export const selectCorners = createSelector( [selectStyleState], (style) => style.corners, ); /** Select cards color */ export const selectCardsColor = createSelector( [selectStyleState], (style) => style.cardsColor, ); // ============================================================================ // Constructor Selectors // ============================================================================ /** Select constructor slice */ const selectConstructorState = (state: RootState) => state.constructorUI; /** Select editor tab */ export const selectEditorTab = createSelector( [selectConstructorState], (constructor) => constructor.editorTab, ); /** Select if menu is open */ export const selectIsMenuOpen = createSelector( [selectConstructorState], (constructor) => constructor.isMenuOpen, ); /** Select if editor is collapsed */ export const selectIsEditorCollapsed = createSelector( [selectConstructorState], (constructor) => constructor.isEditorCollapsed, ); /** Select last active page ID for a project */ export const selectLastActivePageId = (projectId: string) => createSelector( [selectConstructorState], (constructor) => constructor.lastActivePageId[projectId], );