Auth: idUsuario login via sige_sam_v3.usuario

- Login uses idUsuario + password (plain text or SHA256)
- API types: HelpDeskUser with idUsuario, nombres, apPaterno
- Sidebar/layout: fullName from nombres + apPaterno
- pnpm-workspace.yaml: approve unrs-resolver builds
This commit is contained in:
Nurfog committed 2026-09-10 11:36:46 -03:00
1 parent 2a303c73ae
commit 72775500da
7 files changed
+45 -31

No files matched your search

+10
View File
@@ -138,6 +138,9 @@ packages:
'@emnapi/runtime@1.10.0': '@emnapi/runtime@1.10.0':
resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
'@emnapi/runtime@1.11.3':
resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
'@emnapi/wasi-threads@1.2.1': '@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
@@ -2275,6 +2278,11 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
optional: true optional: true
'@emnapi/runtime@1.11.3':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/wasi-threads@1.2.1': '@emnapi/wasi-threads@1.2.1':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -2436,6 +2444,8 @@ snapshots:
optional: true optional: true
'@img/sharp-wasm32@0.35.4': '@img/sharp-wasm32@0.35.4':
dependencies:
'@emnapi/runtime': 1.11.3
optional: true optional: true
'@img/sharp-webcontainers-wasm32@0.35.4': '@img/sharp-webcontainers-wasm32@0.35.4':
+2
View File
@@ -0,0 +1,2 @@
allowBuilds:
unrs-resolver: true
+3 -2
View File
@@ -79,7 +79,8 @@ function AppShell({ children }: { children: React.ReactNode }) {
function TopNavbarUser() { function TopNavbarUser() {
const { user } = useAuth(); const { user } = useAuth();
const initials = user?.nombre const fullName = user ? `${user.nombres} ${user.apPaterno}` : "Usuario";
const initials = user?.nombres
?.split(" ") ?.split(" ")
.map((n: string) => n[0]) .map((n: string) => n[0])
.join("") .join("")
@@ -90,7 +91,7 @@ function TopNavbarUser() {
<div className="d-flex align-items-center gap-2"> <div className="d-flex align-items-center gap-2">
<div className="avatar-circle">{initials}</div> <div className="avatar-circle">{initials}</div>
<span className="d-none d-md-inline" style={{ fontSize: "0.85rem", fontWeight: 500 }}> <span className="d-none d-md-inline" style={{ fontSize: "0.85rem", fontWeight: 500 }}>
{user?.nombre || "Usuario"} {fullName}
</span> </span>
</div> </div>
); );
+6 -6
View File
@@ -5,7 +5,7 @@ import { useRouter } from "next/navigation";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
export default function LoginPage() { export default function LoginPage() {
const [rut, setRut] = useState(""); const [idUsuario, setIdUsuario] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [error, setError] = useState(""); const [error, setError] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -17,7 +17,7 @@ export default function LoginPage() {
setError(""); setError("");
setLoading(true); setLoading(true);
try { try {
await login(rut, password); await login(idUsuario, password);
router.push("/"); router.push("/");
} catch (err: any) { } catch (err: any) {
setError(err.message || "Credenciales inválidas"); setError(err.message || "Credenciales inválidas");
@@ -74,13 +74,13 @@ export default function LoginPage() {
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">RUT</label> <label className="form-label">Usuario</label>
<input <input
type="text" type="text"
className="form-control" className="form-control"
placeholder="12.345.678-9" placeholder="Tu ID de usuario"
value={rut} value={idUsuario}
onChange={(e) => setRut(e.target.value)} onChange={(e) => setIdUsuario(e.target.value)}
required required
autoFocus autoFocus
/> />
+4 -3
View File
@@ -14,7 +14,8 @@ export default function Sidebar() {
{ href: "/knowledge-base", icon: "bi-journal-bookmark", label: "Knowledge Base" }, { href: "/knowledge-base", icon: "bi-journal-bookmark", label: "Knowledge Base" },
]; ];
const initials = user?.nombre const fullName = user ? `${user.nombres} ${user.apPaterno}` : "Usuario";
const initials = user?.nombres
?.split(" ") ?.split(" ")
.map((n: string) => n[0]) .map((n: string) => n[0])
.join("") .join("")
@@ -49,8 +50,8 @@ export default function Sidebar() {
<div className="sidebar-user"> <div className="sidebar-user">
<div className="avatar">{initials}</div> <div className="avatar">{initials}</div>
<div className="sidebar-user-info"> <div className="sidebar-user-info">
<div className="name">{user?.nombre || "Usuario"}</div> <div className="name">{fullName}</div>
<div className="role">{user?.perfil || "Agente"}</div> <div className="role">{user?.tipo || "Usuario"}</div>
</div> </div>
<button <button
onClick={logout} onClick={logout}
+14 -14
View File
@@ -28,16 +28,15 @@ async function fetchApi<T>(
} }
// ==================== Auth ==================== // ==================== Auth ====================
// SAM uses RUT-based auth — adapt as needed export async function login(idUsuario: string, password: string) {
export async function login(rut: string, password: string) { return fetchApi<{ success: boolean; message?: string; token?: string; user?: HelpDeskUser }>(
return fetchApi<{ success: boolean; message?: string; token?: string; user?: User }>( "/api/helpdesk/login",
"/api/auth/login", { method: "POST", body: JSON.stringify({ idUsuario, password }) }
{ method: "POST", body: JSON.stringify({ rut, password }) }
); );
} }
export async function getMe() { export async function getMe() {
return fetchApi<User>("/api/auth/me"); return fetchApi<HelpDeskUser>("/api/helpdesk/me");
} }
// ==================== Tickets ==================== // ==================== Tickets ====================
@@ -152,15 +151,16 @@ export async function getSLAPolicies() {
} }
// ==================== Types ==================== // ==================== Types ====================
export interface User { export interface HelpDeskUser {
usuarioId: number; idUsuario: string;
nombre: string; nombres: string;
apPaterno: string; apPaterno: string;
apMaterno?: string; apMaterno?: string;
email: string; email: string;
perfil: string; tipo?: string;
sedeId?: number; activo: number;
telefono?: string; fono?: string;
idSede?: number;
} }
export interface Ticket { export interface Ticket {
@@ -196,7 +196,7 @@ export interface TicketListResult {
export interface Comment { export interface Comment {
id: number; id: number;
ticketId: number; ticketId: number;
userId: number; userId: string;
userName?: string; userName?: string;
content: string; content: string;
isInternal: boolean; isInternal: boolean;
@@ -216,7 +216,7 @@ export interface KBArticle {
content: string; content: string;
categoryId: number; categoryId: number;
categoryName?: string; categoryName?: string;
authorId: number; authorId: string;
authorName?: string; authorName?: string;
isPublished: boolean; isPublished: boolean;
viewCount: number; viewCount: number;
+6 -6
View File
@@ -1,11 +1,11 @@
"use client"; "use client";
import React, { createContext, useContext, useState, useEffect, useCallback } from "react"; import React, { createContext, useContext, useState, useEffect, useCallback } from "react";
import { getMe, login as apiLogin, type User } from "./api"; import { getMe, login as apiLogin, type HelpDeskUser } from "./api";
interface AuthContextType { interface AuthContextType {
user: User | null; user: HelpDeskUser | null;
login: (rut: string, password: string) => Promise<void>; login: (idUsuario: string, password: string) => Promise<void>;
logout: () => void; logout: () => void;
isAuthenticated: boolean; isAuthenticated: boolean;
isLoading: boolean; isLoading: boolean;
@@ -20,7 +20,7 @@ const AuthContext = createContext<AuthContextType>({
}); });
export function AuthProvider({ children }: { children: React.ReactNode }) { export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null); const [user, setUser] = useState<HelpDeskUser | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
useEffect(() => { useEffect(() => {
@@ -35,8 +35,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
} }
}, []); }, []);
const login = useCallback(async (rut: string, password: string) => { const login = useCallback(async (idUsuario: string, password: string) => {
const result = await apiLogin(rut, password); const result = await apiLogin(idUsuario, password);
if (!result.success || !result.token) { if (!result.success || !result.token) {
throw new Error(result.message || "Credenciales inválidas"); throw new Error(result.message || "Credenciales inválidas");
} }