diff --git a/amplify/data/resource.ts b/amplify/data/resource.ts index 1ce7cdfd..693f2ed4 100644 --- a/amplify/data/resource.ts +++ b/amplify/data/resource.ts @@ -114,7 +114,7 @@ const schema = a onboardingCompleted: a.boolean().required(), onboardingData: a.json(), }) - .secondaryIndexes(index => [index('storeId')]) + .secondaryIndexes(index => [index('userId')]) .authorization(allow => [allow.authenticated().to(['read', 'update', 'delete', 'create'])]), Product: a @@ -142,6 +142,7 @@ const schema = a collection: a.belongsTo('Collection', 'collectionId'), owner: a.string().required(), }) + .secondaryIndexes(index => [index('storeId'), index('collectionId')]) .authorization(allow => [ allow.ownerDefinedIn('owner').to(['update', 'delete', 'read', 'create']), allow.guest().to(['read']), diff --git a/app/(setup-layout)/my-store/hooks/useUserStores.ts b/app/(setup-layout)/my-store/hooks/useUserStores.ts index 267ac900..27ad8ae2 100644 --- a/app/(setup-layout)/my-store/hooks/useUserStores.ts +++ b/app/(setup-layout)/my-store/hooks/useUserStores.ts @@ -2,7 +2,9 @@ import { use } from 'react' import { generateClient } from 'aws-amplify/data' import type { Schema } from '@/amplify/data/resource' -const client = generateClient() +const client = generateClient({ + authMode: 'userPool', +}) const STORE_LIMITS = { Imperial: 5, @@ -47,13 +49,14 @@ export function getUserStores(userId: string | null, userPlan?: string) { async function fetchUserStores(userId: string, userPlan?: string) { try { // Obtener todas las tiendas del usuario (para verificar límites) - const { data: allUserStores } = await client.models.UserStore.list({ - authMode: 'userPool', - filter: { - userId: { eq: userId }, + const { data: allUserStores } = await client.models.UserStore.listUserStoreByUserId( + { + userId: userId, }, - selectionSet: ['storeId', 'storeName', 'storeType', 'onboardingCompleted'], - }) + { + selectionSet: ['storeId', 'storeName', 'storeType', 'onboardingCompleted'], + } + ) const completedStores = allUserStores || [] diff --git a/app/store/components/product-management/InventoryPage.tsx b/app/store/components/product-management/InventoryPage.tsx index 60c05fbc..bd260772 100644 --- a/app/store/components/product-management/InventoryPage.tsx +++ b/app/store/components/product-management/InventoryPage.tsx @@ -2,8 +2,14 @@ import Link from 'next/link' import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' import { Icons } from '@/app/store/icons/index' +import { getStoreId } from '@/utils/store-utils' +import { useParams, usePathname } from 'next/navigation' +import { routes } from '@/utils/routes' export function InventoryPage() { + const pathname = usePathname() + const params = useParams() + const storeId = getStoreId(params, pathname) return (

Inventario

@@ -24,7 +30,7 @@ export function InventoryPage() {

{/* Botón */} - + diff --git a/app/store/components/product-management/ProductList.tsx b/app/store/components/product-management/ProductList.tsx index cd5359c9..6489fa79 100644 --- a/app/store/components/product-management/ProductList.tsx +++ b/app/store/components/product-management/ProductList.tsx @@ -125,8 +125,7 @@ export function ProductList({
{loading && (
- -

Cargando productos...

+
)} diff --git a/app/store/components/product-management/product-table/product-pagination.tsx b/app/store/components/product-management/product-table/product-pagination.tsx index 752b5124..1c6919d6 100644 --- a/app/store/components/product-management/product-table/product-pagination.tsx +++ b/app/store/components/product-management/product-table/product-pagination.tsx @@ -47,6 +47,8 @@ export function ProductPagination({ 5 10 20 + 50 + 100
diff --git a/app/store/components/product-management/utils/collection-form-utils.ts b/app/store/components/product-management/utils/collection-form-utils.ts index 9585dfb5..1d3154da 100644 --- a/app/store/components/product-management/utils/collection-form-utils.ts +++ b/app/store/components/product-management/utils/collection-form-utils.ts @@ -264,7 +264,7 @@ export const useCollectionForm = ({ ) // Redirigir a la lista de colecciones - router.push(routes.store.collections(storeId)) + router.push(routes.store.products.collections(storeId)) // No desactivamos isSubmitting para mantener el botón deshabilitado hasta la redirección } catch (error) { console.error('Error al guardar la colección:', error) diff --git a/app/store/hooks/useCollections.ts b/app/store/hooks/useCollections.ts index 09534269..520ba163 100644 --- a/app/store/hooks/useCollections.ts +++ b/app/store/hooks/useCollections.ts @@ -3,7 +3,9 @@ import { generateClient } from 'aws-amplify/data' import type { Schema } from '@/amplify/data/resource' import { useQuery, useMutation, useQueryClient, UseQueryResult } from '@tanstack/react-query' -const client = generateClient() +const client = generateClient({ + authMode: 'userPool', +}) // Clave base para las consultas de colecciones const COLLECTIONS_KEY = 'collections' @@ -65,17 +67,14 @@ export const useCollections = () => { queryKey: [COLLECTIONS_KEY, id], queryFn: async () => { // Obtener la colección - const collection = await performOperation(() => - client.models.Collection.get({ id }, { authMode: 'userPool' }) - ) + const collection = await performOperation(() => client.models.Collection.get({ id })) // Si la colección existe, obtener sus productos if (collection) { // Obtener productos de la colección const productsData = await performOperation(() => - client.models.Product.list({ - filter: { collectionId: { eq: id } }, - authMode: 'userPool', + client.models.Product.listProductByCollectionId({ + collectionId: id, }) ) @@ -102,20 +101,23 @@ export const useCollections = () => { return useQuery({ queryKey: [COLLECTIONS_KEY, 'list', storeId], queryFn: () => { - const filter = storeId ? { storeId: { eq: storeId } } : undefined + if (!storeId) { + throw new Error('Store ID is required to list collections by store.') + } + return performOperation(() => - client.models.Collection.list({ - filter, - authMode: 'userPool', + client.models.Collection.listCollectionByStoreId({ + storeId: storeId, }) ) }, staleTime: 5 * 60 * 1000, // 5 minutos en caché + enabled: !!storeId, }) } /** - * Obtiene los productos de una colección específica + * Obtiene los productos de una colección específica usando el GSI en collectionId * @param collectionId - ID de la colección * @returns Resultado de la consulta con los productos de la colección */ @@ -124,9 +126,8 @@ export const useCollections = () => { queryKey: [COLLECTIONS_KEY, collectionId, 'products'], queryFn: () => { return performOperation(() => - client.models.Product.list({ - filter: { collectionId: { eq: collectionId } }, - authMode: 'userPool', + client.models.Product.listProductByCollectionId({ + collectionId: collectionId, }) ) }, @@ -141,11 +142,7 @@ export const useCollections = () => { const useCreateCollection = () => { return useMutation({ mutationFn: (collectionInput: CollectionInput) => - performOperation(() => - client.models.Collection.create(collectionInput, { - authMode: 'userPool', - }) - ), + performOperation(() => client.models.Collection.create(collectionInput)), onSuccess: () => { // Invalidar consultas para actualizar la lista queryClient.invalidateQueries({ queryKey: [COLLECTIONS_KEY, 'list'] }) @@ -160,15 +157,10 @@ export const useCollections = () => { return useMutation({ mutationFn: ({ id, data }: { id: string; data: Partial }) => performOperation(() => - client.models.Collection.update( - { - id, - ...data, - }, - { - authMode: 'userPool', - } - ) + client.models.Collection.update({ + id, + ...data, + }) ), onSuccess: data => { // Actualizar la colección en caché @@ -183,8 +175,7 @@ export const useCollections = () => { */ const useDeleteCollection = () => { return useMutation({ - mutationFn: (id: string) => - performOperation(() => client.models.Collection.delete({ id }, { authMode: 'userPool' })), + mutationFn: (id: string) => performOperation(() => client.models.Collection.delete({ id })), onSuccess: (_, id) => { // Eliminar la colección de la caché queryClient.removeQueries({ queryKey: [COLLECTIONS_KEY, id] }) @@ -202,13 +193,10 @@ export const useCollections = () => { const addProductToCollection = async (collectionId: string, productId: string) => { // Actualizar el producto para asignarle la colección return performOperation(() => - client.models.Product.update( - { - id: productId, - collectionId: collectionId, - }, - { authMode: 'userPool' } - ) + client.models.Product.update({ + id: productId, + collectionId: collectionId, + }) ) } @@ -220,13 +208,10 @@ export const useCollections = () => { const removeProductFromCollection = async (productId: string) => { // Actualizar el producto para eliminar la referencia a la colección return performOperation(() => - client.models.Product.update( - { - id: productId, - collectionId: null, // Eliminar la referencia a la colección - }, - { authMode: 'userPool' } - ) + client.models.Product.update({ + id: productId, + collectionId: null, // Eliminar la referencia a la colección + }) ) } diff --git a/app/store/hooks/useProducts.ts b/app/store/hooks/useProducts.ts index f4d992d6..257691c1 100644 --- a/app/store/hooks/useProducts.ts +++ b/app/store/hooks/useProducts.ts @@ -3,7 +3,9 @@ import { generateClient } from 'aws-amplify/api' import { getCurrentUser } from 'aws-amplify/auth' import type { Schema } from '@/amplify/data/resource' -const client = generateClient() +const client = generateClient({ + authMode: 'userPool', +}) /** * Interfaz para representar un producto @@ -126,7 +128,7 @@ export function useProducts( const queryClient = useQueryClient() // Valores por defecto para paginación - const limit = options?.limit || 60 + const limit = options?.limit || 10 const sortDirection = options?.sortDirection || 'DESC' const sortField = options?.sortField || 'creationDate' const enabled = options?.enabled !== false && !!storeId @@ -135,12 +137,15 @@ export function useProducts( const fetchProductsPage = async ({ pageParam = null }: { pageParam: string | null }) => { if (!storeId) throw new Error('Store ID is required') - const { data, nextToken } = await client.models.Product.list({ - filter: { storeId: { eq: storeId } }, - authMode: 'userPool', - limit, - nextToken: pageParam, - }) + const { data, nextToken } = await client.models.Product.listProductByStoreId( + { + storeId: storeId, + }, + { + limit, + nextToken: pageParam, + } + ) // Ordenamos manualmente los resultados const sortedData = [...(data || [])].sort((a, b) => { @@ -187,16 +192,13 @@ export function useProducts( mutationFn: async (productData: ProductCreateInput) => { const { username } = await getCurrentUser() - const { data } = await client.models.Product.create( - { - ...productData, - storeId: storeId || '', - owner: username, - status: productData.status || 'DRAFT', - quantity: productData.quantity || 0, - }, - { authMode: 'userPool' } - ) + const { data } = await client.models.Product.create({ + ...productData, + storeId: storeId || '', + owner: username, + status: productData.status || 'DRAFT', + quantity: productData.quantity || 0, + }) return data as IProduct }, @@ -229,7 +231,7 @@ export function useProducts( // Mutación para actualizar un producto const updateProductMutation = useMutation({ mutationFn: async (productData: ProductUpdateInput) => { - const { data } = await client.models.Product.update(productData, { authMode: 'userPool' }) + const { data } = await client.models.Product.update(productData) return data as IProduct }, onSuccess: updatedProduct => { @@ -259,7 +261,7 @@ export function useProducts( // Mutación para eliminar un producto const deleteProductMutation = useMutation({ mutationFn: async (id: string) => { - await client.models.Product.delete({ id }, { authMode: 'userPool' }) + await client.models.Product.delete({ id }) return id }, onSuccess: deletedId => { @@ -287,9 +289,7 @@ export function useProducts( // Mutación para eliminar múltiples productos const deleteMultipleProductsMutation = useMutation({ mutationFn: async (ids: string[]) => { - await Promise.all( - ids.map(id => client.models.Product.delete({ id }, { authMode: 'userPool' })) - ) + await Promise.all(ids.map(id => client.models.Product.delete({ id }))) return ids }, onSuccess: deletedIds => { @@ -352,7 +352,6 @@ export function useProducts( // Primero obtenemos todos los productos de la tienda actual const { data: storeProducts } = await client.models.Product.list({ filter: { storeId: { eq: storeId } }, - authMode: 'userPool', }) // Buscamos el producto en los productos de la tienda diff --git a/middlewares/ownership/collectionOwnership.ts b/middlewares/ownership/collectionOwnership.ts index 62c92720..16914575 100644 --- a/middlewares/ownership/collectionOwnership.ts +++ b/middlewares/ownership/collectionOwnership.ts @@ -50,28 +50,22 @@ export async function handleCollectionOwnershipMiddleware(request: NextRequest) try { // Verificar que el usuario tenga acceso a la tienda - const storeResult = await cookiesClient.models.UserStore.get( - { - id: currentStoreId, - }, - { - authMode: 'userPool', - } - ) + const storeResult = await cookiesClient.models.UserStore.get({ + id: currentStoreId, + }) // Si la tienda no existe o no pertenece al usuario, verificar si es colaborador if (!storeResult.data || storeResult.data.userId !== userId) { - const userStoreResult = await cookiesClient.models.UserStore.list({ - filter: { - storeId: { - eq: currentStoreId, - }, - userId: { - eq: userId, - }, + const userStoreResult = await cookiesClient.models.UserStore.listUserStoreByUserId( + { + userId: userId, }, - authMode: 'userPool', - }) + { + filter: { + storeId: { eq: currentStoreId }, + }, + } + ) if (!userStoreResult.data || userStoreResult.data.length === 0) { const redirectUrl = new URL('/my-store', request.url) @@ -96,14 +90,9 @@ export async function handleCollectionOwnershipMiddleware(request: NextRequest) } // Para colecciones existentes, verificar que pertenezcan a la tienda actual - const { data: collection } = await cookiesClient.models.Collection.get( - { - id: collectionId, - }, - { - authMode: 'userPool', - } - ) + const { data: collection } = await cookiesClient.models.Collection.get({ + id: collectionId, + }) if (!collection) { const redirectUrl = new URL(`/store/${currentStoreId}/products/collections`, request.url) diff --git a/middlewares/ownership/productOwnership.ts b/middlewares/ownership/productOwnership.ts index afb23385..7e050a0b 100644 --- a/middlewares/ownership/productOwnership.ts +++ b/middlewares/ownership/productOwnership.ts @@ -50,28 +50,22 @@ export async function handleProductOwnershipMiddleware(request: NextRequest) { try { // Verificar que el usuario tenga acceso a la tienda - const storeResult = await cookiesClient.models.UserStore.get( - { - id: currentStoreId, - }, - { - authMode: 'userPool', - } - ) + const storeResult = await cookiesClient.models.UserStore.get({ + id: currentStoreId, + }) // Si la tienda no existe o no pertenece al usuario, verificar si es colaborador if (!storeResult.data || storeResult.data.userId !== userId) { - const userStoreResult = await cookiesClient.models.UserStore.list({ - filter: { - storeId: { - eq: currentStoreId, - }, - userId: { - eq: userId, - }, + const userStoreResult = await cookiesClient.models.UserStore.listUserStoreByUserId( + { + userId: userId, }, - authMode: 'userPool', - }) + { + filter: { + storeId: { eq: currentStoreId }, + }, + } + ) if (!userStoreResult.data || userStoreResult.data.length === 0) { const redirectUrl = new URL('/my-store', request.url) @@ -99,14 +93,9 @@ export async function handleProductOwnershipMiddleware(request: NextRequest) { } // Para productos existentes, verificar que pertenezcan a la tienda actual - const { data: product } = await cookiesClient.models.Product.get( - { - id: productId, - }, - { - authMode: 'userPool', - } - ) + const { data: product } = await cookiesClient.models.Product.get({ + id: productId, + }) if (!product) { const redirectUrl = new URL(`/store/${currentStoreId}/products`, request.url) diff --git a/middlewares/store-access/store.ts b/middlewares/store-access/store.ts index db31524e..eaa21570 100644 --- a/middlewares/store-access/store.ts +++ b/middlewares/store-access/store.ts @@ -16,9 +16,8 @@ async function hasValidPlan(session: any) { async function checkStoreLimit(userId: string, plan: string) { try { - const { data: stores } = await cookiesClient.models.UserStore.list({ - authMode: 'userPool', - filter: { userId: { eq: userId } }, + const { data: stores } = await cookiesClient.models.UserStore.listUserStoreByUserId({ + userId: userId, }) const storeCount = stores?.length || 0 diff --git a/middlewares/store-access/storeAccess.ts b/middlewares/store-access/storeAccess.ts index 9b3c7f53..0663f348 100644 --- a/middlewares/store-access/storeAccess.ts +++ b/middlewares/store-access/storeAccess.ts @@ -33,14 +33,17 @@ export async function handleStoreAccessMiddleware(request: NextRequest) { try { // Verificar si la tienda pertenece al usuario - const { data: stores } = await cookiesClient.models.UserStore.list({ - filter: { - userId: { eq: userId as string }, - storeId: { eq: requestedStoreId }, + const { data: stores } = await cookiesClient.models.UserStore.listUserStoreByUserId( + { + userId: userId as string, }, - selectionSet: ['storeId'], - authMode: 'userPool', - }) + { + filter: { + storeId: { eq: requestedStoreId }, + }, + selectionSet: ['storeId'], + } + ) // Si la tienda no pertenece al usuario, redirigir a my-store if (!stores || stores.length === 0) { diff --git a/utils/AmplifyUtils.ts b/utils/AmplifyUtils.ts index 8c196b94..4a393a7d 100644 --- a/utils/AmplifyUtils.ts +++ b/utils/AmplifyUtils.ts @@ -12,6 +12,7 @@ export const { runWithAmplifyServerContext } = createServerRunner({ export const cookiesClient = generateServerClientUsingCookies({ config: outputs, cookies, + authMode: 'userPool', }) export async function AuthGetCurrentUserServer() {