ADM-001: Medios y Secciones (fundacional) #15
@@ -15,6 +15,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
||||||
import type { MedioDetail } from '../types'
|
import type { MedioDetail } from '../types'
|
||||||
|
|
||||||
@@ -136,22 +143,24 @@ export function MedioForm({ initialData, isPending, error, onSubmit }: MedioForm
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Tipo</FormLabel>
|
<FormLabel>Tipo</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value ? String(field.value) : ''}
|
||||||
{...field}
|
onValueChange={(v) => field.onChange(v === '' ? '' : Number(v))}
|
||||||
value={field.value ?? ''}
|
disabled={isPending}
|
||||||
disabled={isPending}
|
>
|
||||||
aria-label="Tipo"
|
<FormControl>
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
<SelectTrigger className="w-full" aria-label="Tipo">
|
||||||
>
|
<SelectValue placeholder="Seleccioná un tipo" />
|
||||||
<option value="">Seleccioná un tipo</option>
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
{TIPO_MEDIO_OPTIONS.map((o) => (
|
{TIPO_MEDIO_OPTIONS.map((o) => (
|
||||||
<option key={o.value} value={o.value}>
|
<SelectItem key={o.value} value={String(o.value)}>
|
||||||
{o.label}
|
{o.label}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
</FormControl>
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import { Input } from '@/components/ui/input'
|
|||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { CanPerform } from '@/components/auth/CanPerform'
|
import { CanPerform } from '@/components/auth/CanPerform'
|
||||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { MediosTable } from '../components/MediosTable'
|
import { MediosTable } from '../components/MediosTable'
|
||||||
import { useMediosList } from '../hooks/useMediosList'
|
import { useMediosList } from '../hooks/useMediosList'
|
||||||
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
import { TIPO_MEDIO_OPTIONS } from '../tipoMedio'
|
||||||
@@ -70,28 +77,36 @@ export function MediosListPage() {
|
|||||||
aria-label="Buscar medios"
|
aria-label="Buscar medios"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Tipo"
|
value={tipo !== undefined ? String(tipo) : '__all__'}
|
||||||
onChange={(e) => handleTipoChange(e.target.value)}
|
onValueChange={(v) => handleTipoChange(v === '__all__' ? '' : v)}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos los tipos</option>
|
<SelectTrigger className="h-9 w-40" aria-label="Tipo">
|
||||||
{TIPO_MEDIO_OPTIONS.map((o) => (
|
<SelectValue placeholder="Todos los tipos" />
|
||||||
<option key={o.value} value={o.value}>
|
</SelectTrigger>
|
||||||
{o.label}
|
<SelectContent>
|
||||||
</option>
|
<SelectItem value="__all__">Todos los tipos</SelectItem>
|
||||||
))}
|
{TIPO_MEDIO_OPTIONS.map((o) => (
|
||||||
</select>
|
<SelectItem key={o.value} value={String(o.value)}>
|
||||||
|
{o.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Estado"
|
value={activo !== undefined ? String(activo) : '__all__'}
|
||||||
onChange={(e) => handleActivoChange(e.target.value)}
|
onValueChange={(v) => handleActivoChange(v === '__all__' ? '' : v)}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos</option>
|
<SelectTrigger className="h-9 w-32" aria-label="Estado">
|
||||||
<option value="true">Activos</option>
|
<SelectValue placeholder="Todos" />
|
||||||
<option value="false">Inactivos</option>
|
</SelectTrigger>
|
||||||
</select>
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos</SelectItem>
|
||||||
|
<SelectItem value="true">Activos</SelectItem>
|
||||||
|
<SelectItem value="false">Inactivos</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/components/ui/card'
|
} from '@/components/ui/card'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useRoles } from '../../roles/hooks/useRoles'
|
import { useRoles } from '../../roles/hooks/useRoles'
|
||||||
import { RolPermisosEditor } from '../components/RolPermisosEditor'
|
import { RolPermisosEditor } from '../components/RolPermisosEditor'
|
||||||
|
|
||||||
@@ -28,28 +35,28 @@ export function RolPermisosPage() {
|
|||||||
<CardContent className="space-y-6">
|
<CardContent className="space-y-6">
|
||||||
{/* Selector de rol */}
|
{/* Selector de rol */}
|
||||||
<div className="flex flex-col gap-1 max-w-xs">
|
<div className="flex flex-col gap-1 max-w-xs">
|
||||||
<label
|
<label className="text-sm font-medium text-foreground">
|
||||||
htmlFor="rol-selector"
|
|
||||||
className="text-sm font-medium text-foreground"
|
|
||||||
>
|
|
||||||
Rol
|
Rol
|
||||||
</label>
|
</label>
|
||||||
{loadingRoles ? (
|
{loadingRoles ? (
|
||||||
<p className="text-sm text-muted-foreground">Cargando roles...</p>
|
<p className="text-sm text-muted-foreground">Cargando roles...</p>
|
||||||
) : (
|
) : (
|
||||||
<select
|
<Select
|
||||||
id="rol-selector"
|
value={selectedRol ?? '__none__'}
|
||||||
value={selectedRol ?? ''}
|
onValueChange={(v) => setSelectedRol(v === '__none__' ? null : v)}
|
||||||
onChange={(e) => setSelectedRol(e.target.value || null)}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">— Seleccioná un rol —</option>
|
<SelectTrigger className="w-full" aria-label="Rol">
|
||||||
{rolesActivos.map((r) => (
|
<SelectValue placeholder="— Seleccioná un rol —" />
|
||||||
<option key={r.codigo} value={r.codigo}>
|
</SelectTrigger>
|
||||||
{r.nombre} ({r.codigo})
|
<SelectContent>
|
||||||
</option>
|
<SelectItem value="__none__">— Seleccioná un rol —</SelectItem>
|
||||||
))}
|
{rolesActivos.map((r) => (
|
||||||
</select>
|
<SelectItem key={r.codigo} value={r.codigo}>
|
||||||
|
{r.nombre} ({r.codigo})
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
||||||
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
||||||
import type { SeccionDetail, TipoSeccion } from '../types'
|
import type { SeccionDetail, TipoSeccion } from '../types'
|
||||||
@@ -100,22 +107,24 @@ export function SeccionForm({ initialData, isPending, error, onSubmit }: Seccion
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Medio</FormLabel>
|
<FormLabel>Medio</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value ? String(field.value) : ''}
|
||||||
{...field}
|
onValueChange={(v) => field.onChange(Number(v))}
|
||||||
value={field.value ?? ''}
|
disabled={isPending || isEdit}
|
||||||
disabled={isPending || isEdit}
|
>
|
||||||
aria-label="Medio"
|
<FormControl>
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
<SelectTrigger className="w-full" aria-label="Medio">
|
||||||
>
|
<SelectValue placeholder="Seleccioná un medio" />
|
||||||
<option value="">Seleccioná un medio</option>
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
{medios.map((m) => (
|
{medios.map((m) => (
|
||||||
<option key={m.id} value={m.id}>
|
<SelectItem key={m.id} value={String(m.id)}>
|
||||||
{m.nombre}
|
{m.nombre}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
</FormControl>
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -165,22 +174,24 @@ export function SeccionForm({ initialData, isPending, error, onSubmit }: Seccion
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Tipo</FormLabel>
|
<FormLabel>Tipo</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value ?? ''}
|
||||||
{...field}
|
onValueChange={(v) => field.onChange(v as TipoSeccion)}
|
||||||
value={field.value ?? ''}
|
disabled={isPending}
|
||||||
disabled={isPending}
|
>
|
||||||
aria-label="Tipo de sección"
|
<FormControl>
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
<SelectTrigger className="w-full" aria-label="Tipo de sección">
|
||||||
>
|
<SelectValue placeholder="Seleccioná un tipo" />
|
||||||
<option value="">Seleccioná un tipo</option>
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
{TIPO_SECCION_OPTIONS.map((o) => (
|
{TIPO_SECCION_OPTIONS.map((o) => (
|
||||||
<option key={o.value} value={o.value}>
|
<SelectItem key={o.value} value={o.value}>
|
||||||
{o.label}
|
{o.label}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
</FormControl>
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
import { useMediosList } from '@/features/medios/hooks/useMediosList'
|
||||||
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
import { TIPO_SECCION_OPTIONS } from '../tipoSeccion'
|
||||||
import type { TipoSeccion } from '../types'
|
import type { TipoSeccion } from '../types'
|
||||||
@@ -40,51 +47,56 @@ export function SeccionesFilters({
|
|||||||
aria-label="Buscar secciones"
|
aria-label="Buscar secciones"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Medio"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => onMedioIdChange(v === '__all__' ? undefined : Number(v))}
|
||||||
const v = e.target.value
|
|
||||||
onMedioIdChange(v === '' ? undefined : Number(v))
|
|
||||||
}}
|
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos los medios</option>
|
<SelectTrigger className="h-9 w-44" aria-label="Medio">
|
||||||
{medios.map((m) => (
|
<SelectValue placeholder="Todos los medios" />
|
||||||
<option key={m.id} value={m.id}>
|
</SelectTrigger>
|
||||||
{m.nombre}
|
<SelectContent>
|
||||||
</option>
|
<SelectItem value="__all__">Todos los medios</SelectItem>
|
||||||
))}
|
{medios.map((m) => (
|
||||||
</select>
|
<SelectItem key={m.id} value={String(m.id)}>
|
||||||
|
{m.nombre}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Tipo de sección"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => onTipoChange(v === '__all__' ? undefined : (v as TipoSeccion))}
|
||||||
const v = e.target.value
|
|
||||||
onTipoChange(v === '' ? undefined : (v as TipoSeccion))
|
|
||||||
}}
|
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos los tipos</option>
|
<SelectTrigger className="h-9 w-44" aria-label="Tipo de sección">
|
||||||
{TIPO_SECCION_OPTIONS.map((o) => (
|
<SelectValue placeholder="Todos los tipos" />
|
||||||
<option key={o.value} value={o.value}>
|
</SelectTrigger>
|
||||||
{o.label}
|
<SelectContent>
|
||||||
</option>
|
<SelectItem value="__all__">Todos los tipos</SelectItem>
|
||||||
))}
|
{TIPO_SECCION_OPTIONS.map((o) => (
|
||||||
</select>
|
<SelectItem key={o.value} value={o.value}>
|
||||||
|
{o.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<select
|
<Select
|
||||||
aria-label="Estado"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => {
|
||||||
const v = e.target.value
|
if (v === '__all__') onActivoChange(undefined)
|
||||||
if (v === '') onActivoChange(undefined)
|
|
||||||
else onActivoChange(v === 'true')
|
else onActivoChange(v === 'true')
|
||||||
}}
|
}}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos</option>
|
<SelectTrigger className="h-9 w-32" aria-label="Estado">
|
||||||
<option value="true">Activos</option>
|
<SelectValue placeholder="Todos" />
|
||||||
<option value="false">Inactivos</option>
|
</SelectTrigger>
|
||||||
</select>
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos</SelectItem>
|
||||||
|
<SelectItem value="true">Activos</SelectItem>
|
||||||
|
<SelectItem value="false">Inactivos</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useCreateUser } from '../hooks/useCreateUser'
|
import { useCreateUser } from '../hooks/useCreateUser'
|
||||||
import { useRolesForSelect } from '../hooks/useRolesForSelect'
|
import { useRolesForSelect } from '../hooks/useRolesForSelect'
|
||||||
import type { CreatedUserDto } from '../api/createUser'
|
import type { CreatedUserDto } from '../api/createUser'
|
||||||
@@ -202,23 +209,26 @@ export function UserForm({ onSuccess }: UserFormProps) {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Rol</FormLabel>
|
<FormLabel>Rol</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value}
|
||||||
{...field}
|
onValueChange={field.onChange}
|
||||||
disabled={disabled || rolesError}
|
disabled={disabled || !!rolesError}
|
||||||
aria-label="Rol"
|
>
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
<FormControl>
|
||||||
>
|
<SelectTrigger className="w-full" aria-label="Rol">
|
||||||
<option value="">
|
<SelectValue
|
||||||
{rolesLoading ? 'Cargando roles...' : 'Seleccioná un rol'}
|
placeholder={rolesLoading ? 'Cargando roles...' : 'Seleccioná un rol'}
|
||||||
</option>
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
{rolOptions.map((r) => (
|
{rolOptions.map((r) => (
|
||||||
<option key={r.codigo} value={r.codigo}>
|
<SelectItem key={r.codigo} value={r.codigo}>
|
||||||
{r.nombre}
|
{r.nombre}
|
||||||
</option>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
</FormControl>
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
import { useDebouncedValue } from '@/hooks/useDebouncedValue'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
|
|
||||||
interface UsersFiltersProps {
|
interface UsersFiltersProps {
|
||||||
onRolChange: (rol: string) => void
|
onRolChange: (rol: string) => void
|
||||||
@@ -38,32 +45,39 @@ export function UsersFilters({ onRolChange, onActivoChange, onSearchChange }: Us
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Rol select */}
|
{/* Rol select */}
|
||||||
<select
|
<Select
|
||||||
aria-label="Rol"
|
defaultValue="__all__"
|
||||||
onChange={(e) => onRolChange(e.target.value)}
|
onValueChange={(v) => onRolChange(v === '__all__' ? '' : v)}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
{ROL_OPTIONS.map((r) => (
|
<SelectTrigger className="h-9 w-40" aria-label="Rol">
|
||||||
<option key={r.value} value={r.value}>
|
<SelectValue placeholder="Todos los roles" />
|
||||||
{r.label}
|
</SelectTrigger>
|
||||||
</option>
|
<SelectContent>
|
||||||
))}
|
{ROL_OPTIONS.map((r) => (
|
||||||
</select>
|
<SelectItem key={r.value || '__all__'} value={r.value || '__all__'}>
|
||||||
|
{r.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
{/* Activo filter */}
|
{/* Activo filter */}
|
||||||
<select
|
<Select
|
||||||
aria-label="Estado"
|
defaultValue="__all__"
|
||||||
onChange={(e) => {
|
onValueChange={(v) => {
|
||||||
const v = e.target.value
|
if (v === '__all__') onActivoChange(undefined)
|
||||||
if (v === '') onActivoChange(undefined)
|
|
||||||
else onActivoChange(v === 'true')
|
else onActivoChange(v === 'true')
|
||||||
}}
|
}}
|
||||||
className="flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
||||||
>
|
>
|
||||||
<option value="">Todos</option>
|
<SelectTrigger className="h-9 w-32" aria-label="Estado">
|
||||||
<option value="true">Activos</option>
|
<SelectValue placeholder="Todos" />
|
||||||
<option value="false">Inactivos</option>
|
</SelectTrigger>
|
||||||
</select>
|
<SelectContent>
|
||||||
|
<SelectItem value="__all__">Todos</SelectItem>
|
||||||
|
<SelectItem value="true">Activos</SelectItem>
|
||||||
|
<SelectItem value="false">Inactivos</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form'
|
} from '@/components/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { useUser } from '../hooks/useUser'
|
import { useUser } from '../hooks/useUser'
|
||||||
import { useUpdateUser } from '../hooks/useUpdateUser'
|
import { useUpdateUser } from '../hooks/useUpdateUser'
|
||||||
import { ResetPasswordModal } from '../components/ResetPasswordModal'
|
import { ResetPasswordModal } from '../components/ResetPasswordModal'
|
||||||
@@ -199,18 +206,22 @@ export function UserEditPage() {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Rol</FormLabel>
|
<FormLabel>Rol</FormLabel>
|
||||||
<FormControl>
|
<Select
|
||||||
<select
|
value={field.value}
|
||||||
{...field}
|
onValueChange={field.onChange}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
aria-label="Rol"
|
>
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
<FormControl>
|
||||||
>
|
<SelectTrigger className="w-full" aria-label="Rol">
|
||||||
<option value="admin">Admin</option>
|
<SelectValue placeholder="Seleccioná un rol" />
|
||||||
<option value="cajero">Cajero</option>
|
</SelectTrigger>
|
||||||
<option value="reportes">Reportes</option>
|
</FormControl>
|
||||||
</select>
|
<SelectContent>
|
||||||
</FormControl>
|
<SelectItem value="admin">Admin</SelectItem>
|
||||||
|
<SelectItem value="cajero">Cajero</SelectItem>
|
||||||
|
<SelectItem value="reportes">Reportes</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -61,7 +61,11 @@ describe('CreateMedioPage', () => {
|
|||||||
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'RAD01')
|
await userEvent.type(screen.getByLabelText(/código/i), 'RAD01')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Radio AM')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Radio AM')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo/i), '2')
|
|
||||||
|
// Open Radix Select trigger and pick "Radio" (value=2)
|
||||||
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^radio$/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
||||||
|
|
||||||
await waitFor(() => expect(mockNavigate).toHaveBeenCalledWith('/admin/medios'))
|
await waitFor(() => expect(mockNavigate).toHaveBeenCalledWith('/admin/medios'))
|
||||||
@@ -81,7 +85,11 @@ describe('CreateMedioPage', () => {
|
|||||||
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'DUP01')
|
await userEvent.type(screen.getByLabelText(/código/i), 'DUP01')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Duplicado')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Duplicado')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo/i), '1')
|
|
||||||
|
// Open Radix Select trigger and pick "Diario" (value=1)
|
||||||
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^diario$/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
|
|||||||
@@ -79,7 +79,11 @@ describe('MedioForm — create mode', () => {
|
|||||||
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'DIA99')
|
await userEvent.type(screen.getByLabelText(/código/i), 'DIA99')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Diario')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Diario')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo/i), '1')
|
|
||||||
|
// Open the Radix Select trigger and pick option
|
||||||
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /diario/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear medio/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
|||||||
@@ -144,8 +144,9 @@ describe('MediosListPage', () => {
|
|||||||
|
|
||||||
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
||||||
|
|
||||||
const tipoSelect = screen.getByRole('combobox', { name: /tipo/i })
|
// Open the Radix Select trigger and pick "Diario" (value=1)
|
||||||
await userEvent.selectOptions(tipoSelect, '1')
|
await userEvent.click(screen.getByRole('combobox', { name: /tipo/i }))
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^diario$/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const filtered = requests.find((u) => u.includes('tipo=1'))
|
const filtered = requests.find((u) => u.includes('tipo=1'))
|
||||||
|
|||||||
@@ -88,15 +88,25 @@ describe('SeccionForm — create mode', () => {
|
|||||||
const onSubmit = vi.fn()
|
const onSubmit = vi.fn()
|
||||||
renderForm({ onSubmit })
|
renderForm({ onSubmit })
|
||||||
|
|
||||||
// Wait for medios to load
|
// Open Medio trigger, wait for medios to load, then pick one
|
||||||
|
const medioTrigger = screen.getByRole('combobox', { name: /medio/i })
|
||||||
|
await userEvent.click(medioTrigger)
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: 'Diario El Día' }))
|
||||||
|
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/medio/i), '1')
|
|
||||||
await userEvent.type(screen.getByLabelText(/código/i), 'CLAS99')
|
await userEvent.type(screen.getByLabelText(/código/i), 'CLAS99')
|
||||||
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Sección')
|
await userEvent.type(screen.getByLabelText(/nombre/i), 'Mi Sección')
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo de sección/i), 'clasificados')
|
|
||||||
|
// Open Tipo trigger and pick Clasificados
|
||||||
|
const tipoTrigger = screen.getByRole('combobox', { name: /tipo de sección/i })
|
||||||
|
await userEvent.click(tipoTrigger)
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
||||||
|
)
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: 'Clasificados' }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /crear sección/i }))
|
await userEvent.click(screen.getByRole('button', { name: /crear sección/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -117,8 +127,9 @@ describe('SeccionForm — edit mode', () => {
|
|||||||
renderForm({ initialData: sampleSeccion })
|
renderForm({ initialData: sampleSeccion })
|
||||||
const codigoInput = screen.getByLabelText(/código/i) as HTMLInputElement
|
const codigoInput = screen.getByLabelText(/código/i) as HTMLInputElement
|
||||||
expect(codigoInput.disabled).toBe(true)
|
expect(codigoInput.disabled).toBe(true)
|
||||||
const medioSelect = screen.getByLabelText(/medio/i) as HTMLSelectElement
|
// Radix Select trigger is a <button> — check it's disabled
|
||||||
expect(medioSelect.disabled).toBe(true)
|
const medioTrigger = screen.getByRole('combobox', { name: /medio/i })
|
||||||
|
expect(medioTrigger).toBeDisabled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('pre-fills form with initialData values', async () => {
|
it('pre-fills form with initialData values', async () => {
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ describe('SeccionesFilters', () => {
|
|||||||
it('loads medios options from API', async () => {
|
it('loads medios options from API', async () => {
|
||||||
renderFilters()
|
renderFilters()
|
||||||
|
|
||||||
|
// Open the Medio Radix Select to see options in the portal
|
||||||
|
const medioTrigger = await screen.findByRole('combobox', { name: /medio/i })
|
||||||
|
await userEvent.click(medioTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
@@ -66,34 +70,55 @@ describe('SeccionesFilters', () => {
|
|||||||
it('calls onMedioIdChange when medio is selected', async () => {
|
it('calls onMedioIdChange when medio is selected', async () => {
|
||||||
const handlers = renderFilters()
|
const handlers = renderFilters()
|
||||||
|
|
||||||
|
// Open Medio trigger, wait for options to load, then pick one
|
||||||
|
const medioTrigger = await screen.findByRole('combobox', { name: /medio/i })
|
||||||
|
await userEvent.click(medioTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Diario El Día' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/medio/i), '1')
|
await userEvent.click(screen.getByRole('option', { name: 'Diario El Día' }))
|
||||||
expect(handlers.onMedioIdChange).toHaveBeenCalledWith(1)
|
expect(handlers.onMedioIdChange).toHaveBeenCalledWith(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls onTipoChange when tipo is selected', async () => {
|
it('calls onTipoChange when tipo is selected', async () => {
|
||||||
const handlers = renderFilters()
|
const handlers = renderFilters()
|
||||||
|
|
||||||
|
// Open Tipo de sección trigger and pick an option
|
||||||
|
const tipoTrigger = screen.getByRole('combobox', { name: /tipo de sección/i })
|
||||||
|
await userEvent.click(tipoTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/tipo de sección/i), 'clasificados')
|
await userEvent.click(screen.getByRole('option', { name: 'Clasificados' }))
|
||||||
expect(handlers.onTipoChange).toHaveBeenCalledWith('clasificados')
|
expect(handlers.onTipoChange).toHaveBeenCalledWith('clasificados')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls onActivoChange when estado is selected', async () => {
|
it('calls onActivoChange when estado is selected', async () => {
|
||||||
const handlers = renderFilters()
|
const handlers = renderFilters()
|
||||||
await userEvent.selectOptions(screen.getByLabelText(/estado/i), 'true')
|
|
||||||
|
// Open Estado trigger and pick Activos
|
||||||
|
const estadoTrigger = screen.getByRole('combobox', { name: /estado/i })
|
||||||
|
await userEvent.click(estadoTrigger)
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByRole('option', { name: 'Activos' })).toBeInTheDocument(),
|
||||||
|
)
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: 'Activos' }))
|
||||||
expect(handlers.onActivoChange).toHaveBeenCalledWith(true)
|
expect(handlers.onActivoChange).toHaveBeenCalledWith(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders all tipo options', async () => {
|
it('renders all tipo options', async () => {
|
||||||
renderFilters()
|
renderFilters()
|
||||||
|
|
||||||
|
// Open Tipo de sección trigger to see options in portal
|
||||||
|
const tipoTrigger = screen.getByRole('combobox', { name: /tipo de sección/i })
|
||||||
|
await userEvent.click(tipoTrigger)
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
expect(screen.getByRole('option', { name: 'Clasificados' })).toBeInTheDocument(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -107,6 +107,13 @@ describe('UserEditPage', () => {
|
|||||||
await userEvent.clear(nombreInput)
|
await userEvent.clear(nombreInput)
|
||||||
await userEvent.type(nombreInput, 'Pedro')
|
await userEvent.type(nombreInput, 'Pedro')
|
||||||
|
|
||||||
|
// Confirm the rol Select has a value by opening and re-selecting the prefilled value
|
||||||
|
// (Radix Select in jsdom needs interaction to register the item text in context)
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
await waitFor(() => expect(screen.getByRole('option', { name: /cajero/i })).toBeInTheDocument())
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /cajero/i }))
|
||||||
|
|
||||||
// Submit
|
// Submit
|
||||||
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
||||||
|
|
||||||
@@ -129,6 +136,12 @@ describe('UserEditPage', () => {
|
|||||||
|
|
||||||
await waitFor(() => expect(screen.getByDisplayValue('Juan')).toBeInTheDocument())
|
await waitFor(() => expect(screen.getByDisplayValue('Juan')).toBeInTheDocument())
|
||||||
|
|
||||||
|
// Confirm rol by opening and re-selecting the prefilled value
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
await waitFor(() => expect(screen.getByRole('option', { name: /cajero/i })).toBeInTheDocument())
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /cajero/i }))
|
||||||
|
|
||||||
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
await userEvent.click(screen.getByRole('button', { name: /guardar|actualizar|save/i }))
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
|
|||||||
@@ -109,6 +109,10 @@ describe('UserForm — roles dropdown integration', () => {
|
|||||||
)
|
)
|
||||||
renderForm()
|
renderForm()
|
||||||
|
|
||||||
|
// Open the Radix Select trigger to see options in the portal
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
|
||||||
// Wait for roles to load — active options should appear.
|
// Wait for roles to load — active options should appear.
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(
|
expect(
|
||||||
@@ -132,15 +136,21 @@ describe('UserForm — roles dropdown integration', () => {
|
|||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
renderForm(onSuccess)
|
renderForm(onSuccess)
|
||||||
|
|
||||||
|
// Open trigger and wait for Cajero option to load
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await user.click(rolTrigger)
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Pick Cajero option
|
||||||
|
await user.click(screen.getByRole('option', { name: 'Cajero' }))
|
||||||
|
|
||||||
await user.type(screen.getByLabelText(/usuario/i), 'jdoe123')
|
await user.type(screen.getByLabelText(/usuario/i), 'jdoe123')
|
||||||
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
||||||
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
||||||
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
||||||
await user.selectOptions(screen.getByLabelText(/rol/i), 'cajero')
|
|
||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
||||||
|
|
||||||
@@ -176,15 +186,20 @@ describe('UserForm — backend error display', () => {
|
|||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
renderForm()
|
renderForm()
|
||||||
|
|
||||||
|
// Open trigger, wait for options, pick Cajero
|
||||||
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await user.click(rolTrigger)
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
expect(screen.getByRole('option', { name: 'Cajero' })).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await user.click(screen.getByRole('option', { name: 'Cajero' }))
|
||||||
|
|
||||||
await user.type(screen.getByLabelText(/usuario/i), 'existing')
|
await user.type(screen.getByLabelText(/usuario/i), 'existing')
|
||||||
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
await user.type(screen.getByLabelText(/^contraseña$/i), 'Secret12')
|
||||||
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
await user.type(screen.getByLabelText(/nombre/i), 'Juan')
|
||||||
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
await user.type(screen.getByLabelText(/apellido/i), 'Doe')
|
||||||
await user.selectOptions(screen.getByLabelText(/rol/i), 'cajero')
|
|
||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
await user.click(screen.getByRole('button', { name: /crear usuario/i }))
|
||||||
|
|
||||||
|
|||||||
@@ -171,8 +171,10 @@ describe('UsersListPage', () => {
|
|||||||
|
|
||||||
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
await waitFor(() => expect(requests.length).toBeGreaterThan(0))
|
||||||
|
|
||||||
const rolSelect = screen.getByRole('combobox', { name: /rol/i })
|
// Open the Radix Select trigger and pick "Admin"
|
||||||
await userEvent.selectOptions(rolSelect, 'admin')
|
const rolTrigger = screen.getByRole('combobox', { name: /rol/i })
|
||||||
|
await userEvent.click(rolTrigger)
|
||||||
|
await userEvent.click(screen.getByRole('option', { name: /^admin$/i }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const filtered = requests.find((u) => u.includes('rol=admin'))
|
const filtered = requests.find((u) => u.includes('rol=admin'))
|
||||||
|
|||||||
@@ -1 +1,10 @@
|
|||||||
import '@testing-library/jest-dom'
|
import '@testing-library/jest-dom'
|
||||||
|
|
||||||
|
// Radix UI primitives use pointer capture APIs not available in jsdom.
|
||||||
|
// Provide no-op stubs so Radix Select/etc. work in unit tests.
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.HTMLElement.prototype.hasPointerCapture = () => false
|
||||||
|
window.HTMLElement.prototype.setPointerCapture = () => {}
|
||||||
|
window.HTMLElement.prototype.releasePointerCapture = () => {}
|
||||||
|
window.HTMLElement.prototype.scrollIntoView = () => {}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user