Skip to content
Merged

Dev #25

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ name: "CodeQL Advanced"

on:
push:
branches: ["*"]
branches: ["main", "dev"]
pull_request:
branches: ["*"]
branches: ["main", "dev"]
schedule:
- cron: '27 8 * * 3'

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/unit_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: Ejecutar tests con Jest
on:
push:
branches:
- '*' # En cualquier push a cualquier rama
- main
- dev
pull_request:
branches:
- main
Expand Down
106 changes: 53 additions & 53 deletions amplify/data/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ const schema = a
UserSubscription: a
.model({
id: a.id().required(),
userId: a.string().required(), // Llave primaria (external_reference)
subscriptionId: a.string().required(), // Id de la suscripción
planName: a.string().required(), // Nombre del plan (reason)
nextPaymentDate: a.datetime(), // Próxima fecha de pago (opcional)
pendingPlan: a.string(), // Nuevo plan pendiente (opcional)
pendingStartDate: a.datetime(), // Fecha del plan pendiente a activar
planPrice: a.float(), // Precio del plan
lastFourDigits: a.integer(), // Últimos 4 dígitos de la tarjeta
userId: a.string().required(),
subscriptionId: a.string().required(),
planName: a.string().required(),
nextPaymentDate: a.datetime(),
pendingPlan: a.string(),
pendingStartDate: a.datetime(),
planPrice: a.float(),
lastFourDigits: a.integer(),
})
.identifier(['id'])
.authorization(allow => [
Expand All @@ -89,19 +89,19 @@ const schema = a

UserStore: a
.model({
userId: a.string().required(), // Relaciona la tienda con el usuario
storeId: a.string().required(), // Identificador único de la tienda
storeName: a.string().required(), // Nombre de la tienda
storeDescription: a.string(), // Descripción opcional de la tienda
storeLogo: a.string(), // URL de la imagen del logo de la tienda
storeFavicon: a.string(), // URL de la imagen del favicon de la tienda
storeBanner: a.string(), // URL de la imagen del banner de la tienda
storeTheme: a.string(), // Tema de la tienda (opcional)
storeCurrency: a.string(), // Moneda de la tienda
storeType: a.string(), // Tipo de tienda (física, virtual, etc.)
storeStatus: a.string(), // Estado de la tienda (activa, inactiva, etc.)
storePolicy: a.string(), // Política de la tienda (opcional)
storeAdress: a.string(), // Dirección de la tienda
userId: a.string().required(),
storeId: a.string().required(),
storeName: a.string().required(),
storeDescription: a.string(),
storeLogo: a.string(),
storeFavicon: a.string(),
storeBanner: a.string(),
storeTheme: a.string(),
storeCurrency: a.string(),
storeType: a.string(),
storeStatus: a.string(),
storePolicy: a.string(),
storeAdress: a.string(),
contactEmail: a.string(),
contactPhone: a.float(),
contactName: a.string(),
Expand All @@ -120,49 +120,49 @@ const schema = a
Product: a
.model({
id: a.id().required(),
storeId: a.string().required(), // Relaciona el producto con la tienda
name: a.string().required(), // Nombre del producto
description: a.string(), // Descripción del producto
price: a.float(), // Precio del producto
compareAtPrice: a.float(), // Precio de comparación (opcional)
costPerItem: a.float(), // Costo por artículo (opcional)
sku: a.string(), // SKU del producto (opcional)
barcode: a.string(), // Código de barras (opcional)
quantity: a.integer(), // Cantidad en inventario
category: a.string(), // Categoría del producto
images: a.json(), // Array de imágenes [{url: string, alt: string}]
attributes: a.json(), // Array de atributos [{name: string, values: string[]}]
status: a.string(), // Estado: ACTIVE, INACTIVE, PENDING, DRAFT
slug: a.string(), // URL amigable del producto
featured: a.boolean(), // Producto destacado
tags: a.json(), // Array de etiquetas
variants: a.json(), // Variantes del producto
collectionId: a.string(), // ID de la colección
supplier: a.string(), // Proveedor del producto
collection: a.belongsTo('Collection', 'collectionId'), // Relación con la colección
owner: a.string().required(), // Usuario que creo el producto
storeId: a.string().required(),
name: a.string().required(),
description: a.string(),
price: a.float(),
compareAtPrice: a.float(),
costPerItem: a.float(),
sku: a.string(),
barcode: a.string(),
quantity: a.integer(),
category: a.string(),
images: a.json(),
attributes: a.json(),
status: a.string(),
slug: a.string(),
featured: a.boolean(),
tags: a.json(),
variants: a.json(),
collectionId: a.string(),
supplier: a.string(),
collection: a.belongsTo('Collection', 'collectionId'),
owner: a.string().required(),
})
.authorization(allow => [
allow.ownerDefinedIn('owner').to(['update', 'delete', 'read', 'create']), // Solo el creador puede editar y eliminar
allow.ownerDefinedIn('owner').to(['update', 'delete', 'read', 'create']),
allow.guest().to(['read']),
]),

Collection: a
.model({
storeId: a.string().required(), // Relaciona la colección con la tienda
title: a.string().required(), // Nombre de la colección
description: a.string(), // Descripción de la colección
image: a.string(), // URL de la imagen de la colección
slug: a.string(), // URL amigable de la colección
storeId: a.string().required(),
title: a.string().required(),
description: a.string(),
image: a.string(),
slug: a.string(),
isActive: a.boolean().required(),
sortOrder: a.integer(), // Orden de la colección
owner: a.string().required(), // Usuario que creo la colección
products: a.hasMany('Product', 'collectionId'), // Relación con productos
sortOrder: a.integer(),
owner: a.string().required(),
products: a.hasMany('Product', 'collectionId'),
})
.secondaryIndexes(index => [index('storeId')])
.authorization(allow => [
allow.ownerDefinedIn('owner').to(['update', 'delete', 'read', 'create']), // Solo el creador puede editar y eliminar
allow.guest().to(['read']), // Visitantes pueden ver las colecciones
allow.ownerDefinedIn('owner').to(['update', 'delete', 'read', 'create']),
allow.guest().to(['read']),
]),
})
.authorization(allow => [
Expand Down
10 changes: 7 additions & 3 deletions app/(main-layout)/landing/components/FirstView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
'use client'

import { Button } from '@/components/ui/button'
import { useRouter } from 'next/navigation'
import { ArrowRight } from 'lucide-react'
import { motion } from 'framer-motion'
import Link from 'next/link'
Expand Down Expand Up @@ -32,9 +35,10 @@ const slides = [
export function FirstView() {
const swiperRef = useRef<SwiperType>()
const [activeIndex, setActiveIndex] = useState(0)
const router = useRouter()

const rediret = () => {
window.location.href = '/first-steps'
const redirect = () => {
router.push('/first-steps')
}

return (
Expand Down Expand Up @@ -93,7 +97,7 @@ export function FirstView() {
<Button
size="lg"
className="bg-primary hover:bg-primary/90 text-white px-8 rounded-full"
onClick={rediret}
onClick={redirect}
>
Crear mi tienda
<ArrowRight className="ml-2 h-4 w-4" />
Expand Down
18 changes: 16 additions & 2 deletions app/(setup-layout)/my-store/components/StoreSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import Image from 'next/image'
import Link from 'next/link'
import { Suspense } from 'react'
Expand Down Expand Up @@ -107,7 +109,7 @@ function StoreData({ userId, userPlan }: { userId: string | null; userPlan?: str
stores = [],
canCreateStore = false,
error,
} = result as { stores: any[]; canCreateStore: boolean; error?: string }
} = result as { stores: unknown[]; canCreateStore: boolean; error?: string }

if (error) {
return (
Expand All @@ -120,10 +122,22 @@ function StoreData({ userId, userPlan }: { userId: string | null; userPlan?: str

// Componente principal
export function StoreSelector() {
const { userData } = useAuthUser()
const { userData, isLoading } = useAuthUser()
const cognitoUsername = userData?.['cognito:username']
const userPlan = userData?.['custom:plan']

if (isLoading) {
return (
<Loader
size="large"
color="black"
centered
text="Cargando tus tiendas..."
className="bg-gray-50 p-6 rounded-xl shadow-sm"
/>
)
}

return (
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
Expand Down
80 changes: 64 additions & 16 deletions hooks/auth/useAuthUser.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,72 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, useCallback } from 'react'
import { fetchAuthSession } from 'aws-amplify/auth'
import { Hub } from 'aws-amplify/utils'

export const useAuthUser = () => {
// Estado para guardar el payload completo
const [userData, setUserData] = useState<any>(null)
interface UserPayload {
sub: string
email: string
nickName?: string
phone?: string
cognitoUsername: string
userId: string
plan?: string
picture?: string
identities?: any[]
[key: string]: any
}

useEffect(() => {
// Función para obtener la sesión y los datos del usuario
const fetchUserData = async () => {
try {
const session = await fetchAuthSession()
const payload = session.tokens?.idToken?.payload
setUserData(payload)
} catch (error) {
console.error('Error fetching user data:', error)
}
interface AuthUserResult {
userData: UserPayload | null
isLoading: boolean
error: Error | null
refreshUserData: () => Promise<void>
}

export const useAuthUser = (): AuthUserResult => {
const [userData, setUserData] = useState<UserPayload | null>(null)
const [isLoading, setIsLoading] = useState<boolean>(true)
const [error, setError] = useState<Error | null>(null)

const fetchUserData = useCallback(async () => {
setIsLoading(true)
setError(null)

try {
const session = await fetchAuthSession()
const payload = session.tokens?.idToken?.payload as UserPayload
setUserData(payload)
} catch (err) {
console.error('Error fetching user data:', err)
setError(err instanceof Error ? err : new Error('Error desconocido'))
setUserData(null)
} finally {
setIsLoading(false)
}
}, [])

// Escucha eventos de Auth para refrescar los datos
useEffect(() => {
fetchUserData()
}, [])

return { userData }
const unsubscribe = Hub.listen('auth', ({ payload }) => {
if (
['signIn', 'signOut', 'tokenRefresh', 'signIn_failure', 'signOut_failure'].includes(
payload.event
)
) {
fetchUserData()
}
})

return () => {
unsubscribe() // limpia el listener al desmontar
}
}, [fetchUserData])

return {
userData,
isLoading,
error,
refreshUserData: fetchUserData,
}
}