diff --git a/frontend/admin-panel/src/App.tsx b/frontend/admin-panel/src/App.tsx index 3d7ded3..ceb59a0 100644 --- a/frontend/admin-panel/src/App.tsx +++ b/frontend/admin-panel/src/App.tsx @@ -1,35 +1,20 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import Login from './pages/Login'; +import Dashboard from './pages/Dashboard'; +import ProtectedLayout from './layouts/ProtectedLayout'; function App() { - const [count, setCount] = useState(0) - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) + + + } /> + + }> + } /> + + + + ); } -export default App +export default App; diff --git a/frontend/admin-panel/src/layouts/ProtectedLayout.tsx b/frontend/admin-panel/src/layouts/ProtectedLayout.tsx new file mode 100644 index 0000000..a87d2b9 --- /dev/null +++ b/frontend/admin-panel/src/layouts/ProtectedLayout.tsx @@ -0,0 +1,43 @@ +import { Navigate, Outlet } from 'react-router-dom'; +import { useAuthStore } from '../store/authStore'; +import { LogOut } from 'lucide-react'; + +export default function ProtectedLayout() { + const { isAuthenticated, logout } = useAuthStore(); + + if (!isAuthenticated) { + return ; + } + + return ( +
+ {/* Sidebar minimalista por ahora */} + + + {/* Main Content */} +
+ +
+
+ ); +} diff --git a/frontend/admin-panel/src/pages/Dashboard.tsx b/frontend/admin-panel/src/pages/Dashboard.tsx new file mode 100644 index 0000000..67ec6b3 --- /dev/null +++ b/frontend/admin-panel/src/pages/Dashboard.tsx @@ -0,0 +1,8 @@ +export default function Dashboard() { + return ( +
+

Bienvenido al Panel de Administración

+

Seleccione una opción del menú para comenzar.

+
+ ); +} diff --git a/frontend/admin-panel/src/pages/Login.tsx b/frontend/admin-panel/src/pages/Login.tsx new file mode 100644 index 0000000..f467765 --- /dev/null +++ b/frontend/admin-panel/src/pages/Login.tsx @@ -0,0 +1,75 @@ +import { useState } from 'react'; +import { useAuthStore } from '../store/authStore'; +import { authService } from '../services/authService'; +import { useNavigate } from 'react-router-dom'; +import { LayoutDashboard, Lock } from 'lucide-react'; + +export default function Login() { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const setToken = useAuthStore((state) => state.setToken); + const navigate = useNavigate(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const token = await authService.login(username, password); + setToken(token); + navigate('/'); + } catch (err) { + setError('Credenciales inválidas'); + } + }; + + return ( +
+
+
+
+ +
+
+

SIG-CM Admin

+ + {error && ( +
+ {error} +
+ )} + +
+
+ + setUsername(e.target.value)} + className="w-full bg-gray-900 border border-gray-700 rounded p-2 focus:ring-2 focus:ring-blue-500 focus:outline-none" + placeholder="Ingrese su usuario" + /> +
+
+ +
+ setPassword(e.target.value)} + className="w-full bg-gray-900 border border-gray-700 rounded p-2 focus:ring-2 focus:ring-blue-500 focus:outline-none pl-10" + placeholder="••••••••" + /> + +
+
+ +
+
+
+ ); +} diff --git a/frontend/admin-panel/src/services/api.ts b/frontend/admin-panel/src/services/api.ts new file mode 100644 index 0000000..670cdda --- /dev/null +++ b/frontend/admin-panel/src/services/api.ts @@ -0,0 +1,15 @@ +import axios from 'axios'; + +const api = axios.create({ + baseURL: 'https://localhost:7034/api', // Puerto HTTPS obtenido de launchSettings.json +}); + +api.interceptors.request.use((config) => { + const token = localStorage.getItem('token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +export default api; diff --git a/frontend/admin-panel/src/services/authService.ts b/frontend/admin-panel/src/services/authService.ts new file mode 100644 index 0000000..c34ed10 --- /dev/null +++ b/frontend/admin-panel/src/services/authService.ts @@ -0,0 +1,8 @@ +import api from './api'; + +export const authService = { + login: async (username: string, password: string): Promise => { + const response = await api.post('/auth/login', { username, password }); + return response.data.token; + } +}; diff --git a/frontend/admin-panel/src/store/authStore.ts b/frontend/admin-panel/src/store/authStore.ts new file mode 100644 index 0000000..cec4fcd --- /dev/null +++ b/frontend/admin-panel/src/store/authStore.ts @@ -0,0 +1,21 @@ +import { create } from 'zustand'; + +interface AuthState { + token: string | null; + isAuthenticated: boolean; + setToken: (token: string) => void; + logout: () => void; +} + +export const useAuthStore = create((set) => ({ + token: localStorage.getItem('token'), + isAuthenticated: !!localStorage.getItem('token'), + setToken: (token: string) => { + localStorage.setItem('token', token); + set({ token, isAuthenticated: true }); + }, + logout: () => { + localStorage.removeItem('token'); + set({ token: null, isAuthenticated: false }); + }, +}));