fix(web): cablear ResetPasswordModal en UserEditPage [UDT-008]

El row click de UsersListPage navega directo a /usuarios/:id/editar,
por lo que el modal montado solo en UserDetailPage no era alcanzable
desde el flujo real. Ahora tambien esta en el header del EditPage,
al lado del boton Volver, oculto cuando el target es el user logueado.
This commit is contained in:
2026-04-15 21:00:08 -03:00
parent 9e93c70d8b
commit 06908263f6
2 changed files with 33 additions and 3 deletions

View File

@@ -18,6 +18,8 @@ import {
} from '@/components/ui/form' } from '@/components/ui/form'
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 { useAuthStore } from '@/stores/authStore'
const editSchema = z.object({ const editSchema = z.object({
nombre: z.string().min(1, 'El nombre es requerido'), nombre: z.string().min(1, 'El nombre es requerido'),
@@ -45,6 +47,7 @@ export function UserEditPage() {
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const userId = Number(id) const userId = Number(id)
const navigate = useNavigate() const navigate = useNavigate()
const loggedUserId = useAuthStore((s) => s.user?.id)
const { data: user, isLoading } = useUser(userId) const { data: user, isLoading } = useUser(userId)
const { mutate, isPending, error } = useUpdateUser(userId) const { mutate, isPending, error } = useUpdateUser(userId)
@@ -112,10 +115,13 @@ export function UserEditPage() {
<div className="max-w-xl space-y-6"> <div className="max-w-xl space-y-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h1 className="text-xl font-semibold">Editar Usuario</h1> <h1 className="text-xl font-semibold">Editar Usuario</h1>
<div className="flex items-center gap-2">
{loggedUserId !== userId && <ResetPasswordModal userId={userId} />}
<Button variant="ghost" size="sm" onClick={() => navigate('/usuarios')}> <Button variant="ghost" size="sm" onClick={() => navigate('/usuarios')}>
Volver Volver
</Button> </Button>
</div> </div>
</div>
{/* Username — display only, not editable */} {/* Username — display only, not editable */}
<div className="space-y-1"> <div className="space-y-1">

View File

@@ -138,4 +138,28 @@ describe('UserEditPage', () => {
// Should NOT navigate // Should NOT navigate
expect(mockNavigate).not.toHaveBeenCalled() expect(mockNavigate).not.toHaveBeenCalled()
}) })
it('shows "Resetear contraseña" button when editing another user', async () => {
server.use(
http.get(`${API_URL}/api/v1/users/5`, () => HttpResponse.json(mockUserDetail)),
)
renderEditPage(5)
await waitFor(() => expect(screen.getByDisplayValue('Juan')).toBeInTheDocument())
expect(screen.getByRole('button', { name: /resetear contraseña/i })).toBeInTheDocument()
})
it('hides "Resetear contraseña" button when editing own profile', async () => {
server.use(
http.get(`${API_URL}/api/v1/users/1`, () =>
HttpResponse.json({ ...mockUserDetail, id: 1, username: 'admin' }),
),
)
renderEditPage(1)
await waitFor(() => expect(screen.getByDisplayValue('Juan')).toBeInTheDocument())
expect(screen.queryByRole('button', { name: /resetear contraseña/i })).not.toBeInTheDocument()
})
}) })