Add pages in project
This commit is contained in:
parent
fde68975a7
commit
ed8efb6ab4
24 changed files with 1572 additions and 166 deletions
1143
package-lock.json
generated
1143
package-lock.json
generated
File diff suppressed because it is too large
Load diff
19
package.json
19
package.json
|
|
@ -11,13 +11,24 @@
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.0.0",
|
"@reduxjs/toolkit": "^2.5.1",
|
||||||
"react-dom": "^19.0.0"
|
"@tailwindcss/postcss": "^4.0.6",
|
||||||
|
"@tanstack/react-query": "^5.66.3",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"lucide-react": "^0.475.0",
|
||||||
|
"postcss": "^8.5.2",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
|
"react-router-dom": "^7.1.5",
|
||||||
|
"tailwindcss": "^4.0.6",
|
||||||
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.19.0",
|
"@eslint/js": "^9.19.0",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^19.0.3",
|
"@types/react-dom": "^18.3.5",
|
||||||
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"eslint": "^9.19.0",
|
"eslint": "^9.19.0",
|
||||||
"eslint-config-prettier": "^10.0.1",
|
"eslint-config-prettier": "^10.0.1",
|
||||||
|
|
|
||||||
4
postcss.config.js
Normal file
4
postcss.config.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const plugins = {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
};
|
||||||
42
src/App.css
42
src/App.css
|
|
@ -1,42 +0,0 @@
|
||||||
#root {
|
|
||||||
max-width: 1280px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
height: 6em;
|
|
||||||
padding: 1.5em;
|
|
||||||
will-change: filter;
|
|
||||||
transition: filter 300ms;
|
|
||||||
}
|
|
||||||
.logo:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
|
||||||
}
|
|
||||||
.logo.react:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
a:nth-of-type(2) .logo {
|
|
||||||
animation: logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-the-docs {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
43
src/App.tsx
43
src/App.tsx
|
|
@ -1,12 +1,47 @@
|
||||||
import { useState } from 'react'
|
import * as React from 'react'
|
||||||
|
import {
|
||||||
|
BrowserRouter as Router,
|
||||||
|
Routes,
|
||||||
|
Route,
|
||||||
|
Navigate,
|
||||||
|
} from 'react-router-dom'
|
||||||
|
import Header from './components/Header'
|
||||||
|
import Sidebar from './components/Sidebar'
|
||||||
|
import ThemeToggle from './components/ThemeToggle'
|
||||||
|
import Dashboard from './pages/Dashboard'
|
||||||
|
import LoginPage from './pages/LoginPage'
|
||||||
|
import NotFound from './pages/NotFound'
|
||||||
|
import { ThemeProvider } from './context/ThemeContext'
|
||||||
|
import useAuthStore from './store/authStore' // Importando autenticação
|
||||||
|
|
||||||
function App() {
|
const App: React.FC = () => {
|
||||||
const [name, setName] = useState('')
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated) // Obtendo estado global de autenticação
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ThemeProvider>
|
||||||
|
<Router>
|
||||||
|
{!isAuthenticated ? (
|
||||||
|
<Routes>
|
||||||
|
<Route path="*" element={<Navigate to="/login" />} />
|
||||||
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
</Routes>
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
<p>Init panel branch</p>
|
<Header />
|
||||||
|
<div>
|
||||||
|
<Sidebar />
|
||||||
|
<div>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Navigate to="/dashboard" />} />
|
||||||
|
<Route path="/dashboard" element={<Dashboard />} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ThemeToggle />
|
||||||
</>
|
</>
|
||||||
|
)}
|
||||||
|
</Router>
|
||||||
|
</ThemeProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
11
src/components/Header.tsx
Normal file
11
src/components/Header.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
const Header: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<header className="bg-gray-900 text-white p-4 flex justify-between items-center">
|
||||||
|
<h1 className="text-xl font-bold">Painel de Controle</h1>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
31
src/components/Sidebar.tsx
Normal file
31
src/components/Sidebar.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
const Sidebar: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<aside className="bg-gray-800 text-white w-64 min-h-screen p-4">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li className="mb-2">
|
||||||
|
<Link
|
||||||
|
to="/dashboard"
|
||||||
|
className="block p-2 rounded hover:bg-gray-700"
|
||||||
|
>
|
||||||
|
Dashboard
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
to="/settings"
|
||||||
|
className="block p-2 rounded hover:bg-gray-700"
|
||||||
|
>
|
||||||
|
Configurações
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sidebar
|
||||||
17
src/components/ThemeToggle.tsx
Normal file
17
src/components/ThemeToggle.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
import { useTheme } from '../context/ThemeContext'
|
||||||
|
|
||||||
|
const ThemeToggle: React.FC = () => {
|
||||||
|
const { theme, toggleTheme } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={toggleTheme}
|
||||||
|
className="fixed bottom-4 right-4 bg-gray-700 text-white p-2 rounded"
|
||||||
|
>
|
||||||
|
{theme === 'light' ? '🌙 Modo Escuro' : '☀️ Modo Claro'}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ThemeToggle
|
||||||
46
src/context/ThemeContext.tsx
Normal file
46
src/context/ThemeContext.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
import { createContext, useContext, useState, ReactNode } from 'react'
|
||||||
|
|
||||||
|
interface ThemeContextType {
|
||||||
|
theme: string
|
||||||
|
toggleTheme: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
|
||||||
|
|
||||||
|
export const ThemeProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [theme, setTheme] = useState('light')
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={{ theme, toggleTheme }}>
|
||||||
|
<div className={theme}>{children}</div>
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTheme = () => {
|
||||||
|
const context = useContext(ThemeContext)
|
||||||
|
if (!context) throw new Error('useTheme must be used within a ThemeProvider')
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeToggle: React.FC = () => {
|
||||||
|
const { theme, toggleTheme } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={toggleTheme}
|
||||||
|
className="fixed bottom-4 right-4 bg-gray-700 text-white p-2 rounded"
|
||||||
|
>
|
||||||
|
{theme === 'light' ? '🌙 Modo Escuro' : '☀️ Modo Claro'}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ThemeToggle
|
||||||
1
src/global.d.ts
vendored
Normal file
1
src/global.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
declare module '*.css'
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
:root {
|
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
|
||||||
background-color: #242424;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #646cff;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #535bf2;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3.2em;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
padding: 0.6em 1.2em;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: inherit;
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: border-color 0.25s;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
border-color: #646cff;
|
|
||||||
}
|
|
||||||
button:focus,
|
|
||||||
button:focus-visible {
|
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
:root {
|
|
||||||
color: #213547;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #747bff;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
|
||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
|
import * as React from 'react'
|
||||||
|
import './styles/index.css'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|
|
||||||
103
src/pages/Dashboard.tsx
Normal file
103
src/pages/Dashboard.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Link, Navigate } from 'react-router-dom'
|
||||||
|
import { Menu, LogOut, Home, User, Settings } from 'lucide-react'
|
||||||
|
import { create } from 'zustand'
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
interface AuthState {
|
||||||
|
user: object | null
|
||||||
|
isAuthenticated: boolean
|
||||||
|
login: (token: string) => void
|
||||||
|
logout: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const useAuthStore = create<AuthState>((set) => ({
|
||||||
|
user: null,
|
||||||
|
isAuthenticated: false,
|
||||||
|
login: (token: string) => {
|
||||||
|
localStorage.setItem('token', token)
|
||||||
|
set({ isAuthenticated: true })
|
||||||
|
},
|
||||||
|
logout: () => {
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
set({ user: null, isAuthenticated: false })
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const Dashboard: React.FC = (): JSX.Element => {
|
||||||
|
const [sidebarOpen, setSidebarOpen] = useState<boolean>(false)
|
||||||
|
const { isAuthenticated, logout } = useAuthStore()
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
console.log(isAuthenticated)
|
||||||
|
return <Navigate to="/login" />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen bg-gray-100">
|
||||||
|
{/* Sidebar */}
|
||||||
|
<aside
|
||||||
|
className={`bg-gray-800 text-white w-64 space-y-6 py-7 px-2 absolute inset-y-0 left-0 transform ${sidebarOpen ? 'translate-x-0' : '-translate-x-full'} transition duration-200 ease-in-out md:relative md:translate-x-0`}
|
||||||
|
>
|
||||||
|
<h1 className="text-2xl font-bold text-center">Dashboard</h1>
|
||||||
|
<nav>
|
||||||
|
<Link
|
||||||
|
to="/home"
|
||||||
|
className="flex items-center space-x-2 px-4 py-2 hover:bg-gray-700 rounded"
|
||||||
|
>
|
||||||
|
<Home size={20} />
|
||||||
|
<span>Home</span>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/profile"
|
||||||
|
className="flex items-center space-x-2 px-4 py-2 hover:bg-gray-700 rounded"
|
||||||
|
>
|
||||||
|
<User size={20} />
|
||||||
|
<span>Perfil</span>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/settings"
|
||||||
|
className="flex items-center space-x-2 px-4 py-2 hover:bg-gray-700 rounded"
|
||||||
|
>
|
||||||
|
<Settings size={20} />
|
||||||
|
<span>Configurações</span>
|
||||||
|
</Link>
|
||||||
|
<button
|
||||||
|
className="flex items-center space-x-2 px-4 py-2 w-full text-left hover:bg-red-600 rounded mt-5"
|
||||||
|
onClick={logout}
|
||||||
|
>
|
||||||
|
<LogOut size={20} />
|
||||||
|
<span>Sair</span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div className="flex-1 flex flex-col">
|
||||||
|
{/* Header */}
|
||||||
|
<header className="bg-white shadow-md p-4 flex justify-between items-center">
|
||||||
|
<button
|
||||||
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||||
|
className="md:hidden"
|
||||||
|
>
|
||||||
|
<Menu size={24} />
|
||||||
|
</button>
|
||||||
|
<h2 className="text-xl font-semibold">Painel de Controle</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Conteúdo principal */}
|
||||||
|
<main className="p-6 flex-1 overflow-auto">
|
||||||
|
<h3 className="text-lg font-semibold mb-4">
|
||||||
|
Bem-vindo ao Dashboard!
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Aqui você pode gerenciar suas informações e visualizar os dados do
|
||||||
|
sistema.
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Dashboard
|
||||||
61
src/pages/LoginPage.tsx
Normal file
61
src/pages/LoginPage.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import useAuthStore from '../store/authStore'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { loginUser } from '../services/api'
|
||||||
|
|
||||||
|
const LoginPage: React.FC = () => {
|
||||||
|
const [username, setUsername] = useState<string>('')
|
||||||
|
const [password, setPassword] = useState<string>('')
|
||||||
|
const login = useAuthStore((state) => state.login)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
try {
|
||||||
|
console.log(username);
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
alert('Favor informar username e password')
|
||||||
|
return // Interrompe a execução da função
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await loginUser({ username, password })
|
||||||
|
login(data.token)
|
||||||
|
navigate('/dashboard')
|
||||||
|
|
||||||
|
|
||||||
|
alert('Favor informar username e password')
|
||||||
|
} catch (error) {
|
||||||
|
alert('Erro ao fazer login!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center h-screen bg-gray-100">
|
||||||
|
<div className="bg-white shadow-lg rounded-lg p-6 w-full max-w-sm flex flex-col gap-4">
|
||||||
|
<h1 className="text-2xl font-bold text-center">Login</h1>
|
||||||
|
<input
|
||||||
|
className="border border-gray-300 p-2 rounded-md w-full"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
placeholder="Usuário"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="border border-gray-300 p-2 rounded-md w-full"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
placeholder="Senha"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleLogin}
|
||||||
|
className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md w-full"
|
||||||
|
>
|
||||||
|
Entrar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoginPage
|
||||||
15
src/pages/NotFound.tsx
Normal file
15
src/pages/NotFound.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
const NotFound = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center h-screen text-center">
|
||||||
|
<h1 className="text-4xl font-bold text-red-600">404</h1>
|
||||||
|
<p className="text-lg">Página não encontrada</p>
|
||||||
|
<Link to="/" className="mt-4 text-blue-500">
|
||||||
|
Voltar para o início
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFound
|
||||||
11
src/services/api.ts
Normal file
11
src/services/api.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
export const loginUser = async (credentials: {
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
login: credentials.username,
|
||||||
|
token:
|
||||||
|
'asdasd7DGVTeBxO7bELfKr2KwVveTKXjmq9RxphemZDfbr0f5VpKQUheZ7CIFek24Vd4tzadads',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
23
src/store/authStore.ts
Normal file
23
src/store/authStore.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { create } from 'zustand'
|
||||||
|
|
||||||
|
interface AuthState {
|
||||||
|
user: any
|
||||||
|
isAuthenticated: boolean
|
||||||
|
login: (token: string) => void
|
||||||
|
logout: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const useAuthStore = create<AuthState>((set) => ({
|
||||||
|
user: null,
|
||||||
|
isAuthenticated: false,
|
||||||
|
login: (token) => {
|
||||||
|
localStorage.setItem('token', token)
|
||||||
|
set({ isAuthenticated: true })
|
||||||
|
},
|
||||||
|
logout: () => {
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
set({ user: null, isAuthenticated: false })
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default useAuthStore
|
||||||
25
src/store/permissionsSlice.ts
Normal file
25
src/store/permissionsSlice.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
// Definição do estado inicial
|
||||||
|
interface PermissionsState {
|
||||||
|
roles: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: PermissionsState = {
|
||||||
|
roles: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Criando o slice corretamente
|
||||||
|
const permissionsSlice = createSlice({
|
||||||
|
name: 'permissions',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setPermissions: (state, action: PayloadAction<string[]>) => {
|
||||||
|
state.roles = action.payload
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Exportando o reducer e a action corretamente
|
||||||
|
export const { setPermissions } = permissionsSlice.actions
|
||||||
|
export default permissionsSlice.reducer
|
||||||
11
src/styles/index.css
Normal file
11
src/styles/index.css
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
/* Estilos personalizados */
|
||||||
|
body {
|
||||||
|
@apply bg-gray-100 text-gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark body {
|
||||||
|
@apply bg-gray-900 text-white;
|
||||||
|
}
|
||||||
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
|
|
@ -1 +0,0 @@
|
||||||
/// <reference types="vite/client" />
|
|
||||||
8
tailwind.config.js
Normal file
8
tailwind.config.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ['./src/**/*.{js,ts,jsx,tsx,html}'],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
"target": "ES2020",
|
"target": "ES2022",
|
||||||
"useDefineForClassFields": true,
|
"lib": ["ES2023"],
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
|
@ -12,8 +11,13 @@
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx",
|
/* Não referencie noEmit se estiver em um projeto referenciado */
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
|
||||||
|
/* Necessário para referências */
|
||||||
|
"composite": true,
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|
@ -22,5 +26,5 @@
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["vite.config.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
{
|
{
|
||||||
"files": [],
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "./tsconfig.app.json" },
|
{ "path": "./tsconfig.app.json" },
|
||||||
{ "path": "./tsconfig.node.json" }
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,26 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
"jsx": "react",
|
||||||
"target": "ES2022",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
"lib": ["ES2023"],
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "node",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
|
||||||
|
/* Removendo "noEmit": true para evitar erro */
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
|
||||||
|
/* Necessário para referências */
|
||||||
|
"composite": true,
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|
@ -20,5 +29,5 @@
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
"include": ["vite.config.ts"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue