Panel de control multiinquilino.
Un prototipo de panel de análisis para entornos multiinquilino, desarrollado con Next.js, React, TypeScript y Tailwind CSS.

Una tienda se registra utilizando solo su nombre, sin contraseña, y obtiene análisis de ventas y precios para sus propios productos y variantes, extraídos de una base de datos SQLite real en lugar de datos simulados. Se desarrolló como un proyecto práctico para MokshaAI: utiliza Next.js (enrutador de páginas), Redux Toolkit y Recharts, basado en un esquema relacional pequeño que incluye tiendas, productos, variantes y pedidos.
Autenticación solo para compras, basada en cookies.
No hay un campo de contraseña. La ruta /api/login valida el nombre de la tienda enviado contra la tabla de tiendas (isShopNameValid) y, en caso de éxito, establece una cookie de sesión firmada con atributo httpOnly y una duración de una hora; no es un token que el cliente almacena y vuelve a adjuntar. El módulo authSlice de Redux rastrea al usuario autenticado mediante acciones asíncronas que acceden a las rutas /api/login y /api/logout, y solo lee la información del usuario persistida desde localStorage en una acción explícita llamada rehydrateUser, ejecutada después de la carga inicial; esto evita leer localStorage durante el renderizado en el servidor, lo cual podría causar un problema de sincronización.
Cada métrica viene con su propia tendencia.
La función getSalesSummary no solo calcula el total de unidades e ingresos para el rango de fechas seleccionado, sino que también retrocede ese mismo período en la misma cantidad, ejecuta la misma consulta agregada nuevamente y devuelve ambos totales uno al lado del otro, con los cambios absolutos y porcentuales ya calculados (y protegidos contra períodos anteriores con cero ventas, donde un cambio porcentual no está definido para evitar una división por cero). Cada número que se muestra en el panel puede incluir información como "↑12% comparado con el período anterior" sin necesidad de realizar consultas manuales adicionales en la interfaz de usuario.
SELECT day,
SUM(total_units) AS total_units,
SUM(total_revenue) AS total_revenue
FROM sales_summary
WHERE day BETWEEN ? AND ? -- + optional product_id / variant_id filters
GROUP BY day
ORDER BY day ASCRedux como un límite, no como el destino final.
Los datos fluyen en una sola dirección: punto final REST -> slice de Redux (autenticación, panel de control, precio, ventas) -> selector -> Recharts. Los componentes de análisis nunca consultan directamente la base de datos, lo cual es la razón principal por la que un componente puede ser probado o reutilizado sin necesidad de un archivo SQLite activo. Las búsquedas de productos y variantes, necesarias constantemente para los menús desplegables de filtro, pasan por una pequeña caché en memoria en dashboard.js con un tiempo de vida útil (TTL) de 5 minutos, lo que significa que al cambiar entre productos, no se vuelve a consultar la base de datos SQLite en cada selección.

