feat(logout/Info): Aidiciona dados do usuário no menu. Adiciona a opção de realizar logout da aplicação
This commit is contained in:
parent
2945d8b1f1
commit
de4a2dafdb
13 changed files with 486 additions and 94 deletions
32
package-lock.json
generated
32
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "app",
|
||||
"version": "0.1.0",
|
||||
"version": "25.9.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "app",
|
||||
"version": "0.1.0",
|
||||
"version": "25.9.1",
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^10.0.0",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
|
|
@ -26,7 +26,9 @@
|
|||
"clsx": "^2.1.1",
|
||||
"cookies-next": "^6.1.0",
|
||||
"faker-js": "^1.0.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lucide-react": "^0.540.0",
|
||||
"next": "^15.5.3",
|
||||
"next-themes": "^0.4.6",
|
||||
|
|
@ -39,6 +41,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
|
|
@ -1949,6 +1952,13 @@
|
|||
"tailwindcss": "4.1.12"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/js-cookie": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
|
||||
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/jsonwebtoken": {
|
||||
"version": "9.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
|
||||
|
|
@ -2224,6 +2234,15 @@
|
|||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-cookie": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
|
|
@ -2267,6 +2286,15 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jwt-decode": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"name": "app",
|
||||
"version": "0.1.0",
|
||||
"version": "25.9.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
|
|
@ -27,7 +27,9 @@
|
|||
"clsx": "^2.1.1",
|
||||
"cookies-next": "^6.1.0",
|
||||
"faker-js": "^1.0.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lucide-react": "^0.540.0",
|
||||
"next": "^15.5.3",
|
||||
"next-themes": "^0.4.6",
|
||||
|
|
@ -40,6 +42,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
|
|
|
|||
27
public/images/logo.svg
Normal file
27
public/images/logo.svg
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Creator: CorelDRAW 2018 (64 Bit) -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="109.504mm" height="32.45mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
|
||||
viewBox="0 0 4674.85 1385.32"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
<![CDATA[
|
||||
.fil0 {fill:#FF6600}
|
||||
.fil1 {fill:white;fill-rule:nonzero}
|
||||
]]>
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Camada_x0020_1">
|
||||
<metadata id="CorelCorpID_0Corel-Layer"/>
|
||||
<g id="_2536796572048">
|
||||
<path class="fil0" d="M444.51 337.84c440.36,-70.7 476.63,243.79 383.3,374.93 -121.36,170.53 -504.61,132.14 -589.48,-37.21 -31.1,-62.05 -10.18,-188.76 23.33,-238.81 35.98,-53.73 97.41,-85.19 182.85,-98.91zm-444.51 192.86c0,156.21 12.26,277.5 171.92,375.2 214.59,131.32 589.63,134.32 795.72,-31.29 174.6,-140.3 173.88,-487.9 -30.87,-624.12 -52.36,-34.83 -93.57,-53.67 -162.11,-73.16 -236.65,-67.31 -582.01,-30.52 -720.03,178.21 -25.47,38.51 -54.62,111.65 -54.62,175.17z"/>
|
||||
<path class="fil0" d="M3818.84 393.91c0,148.34 145.5,201.77 263.73,234.14 93.01,25.46 365.44,57.71 365.44,126.96 0,66.65 -152.26,76.59 -224.32,76.59 -221.11,0 -348.08,-81.05 -399.39,-93.01l0 196.95 192.2 43.05c87.27,14.66 138.58,22.6 234.54,22.6 441.47,0 507.61,-275.63 337.69,-419.75 -140.93,-119.53 -545.59,-120.82 -545.59,-203.95 0,-95.45 359.81,-45.21 409.3,-31.8l154.22 53.68 0 -186.02 -176.42 -42.42c-214.47,-43.37 -611.42,-48.39 -611.42,222.96z"/>
|
||||
<path class="fil0" d="M2631.61 755.01c0,86.12 62.34,149.87 115.84,185.07 127.76,84.05 323.75,68.39 463.32,16.58 29.5,-10.95 61.1,-25 91.99,-39.32 39.53,-18.31 67.28,-41.35 94.8,-41.97l0 103.95 218.85 0 0 -809.72 -218.85 0 0 492.39c0,18.46 -54.62,47.33 -68.92,56.91 -26.89,18 -50.44,32.94 -78.73,47.11 -130.98,65.59 -399.45,116.59 -399.45,-114.96l0 -481.45 -218.85 0 0 585.41z"/>
|
||||
<path class="fil0" d="M1515.5 289.97c0,-46.34 -5.47,-72.07 -5.47,-120.37l-218.84 0 0 809.72 218.84 0 0 -475.99c0,-25.16 113.08,-106.37 136.96,-120.18 84.99,-49.19 220.78,-69.84 320.2,-46.99 23.36,5.37 43.45,15 62.61,19.46l0 -186.02c-126.58,-10.54 -178.34,-55.19 -359.3,29.15 -83.29,38.81 -108.59,66.67 -154.99,91.22z"/>
|
||||
<polygon class="fil0" points="2166.56,979.32 2385.41,979.32 2385.41,169.6 2166.56,169.6 "/>
|
||||
<path class="fil0" d="M2172.04 131.3l218.85 0 0 -131.3c-30.09,7.01 -146.3,65.43 -180.57,82.04 -33.64,16.3 -38.27,6.4 -38.27,49.26z"/>
|
||||
</g>
|
||||
<path class="fil1" d="M99.26 1380.26l0 -225.54 -81.33 0 0 -27.47 193.01 0 0 27.47 -81.33 0 0 225.54 -30.36 0zm433.68 0l0 -253.02 155.79 0 0 27.47 -125.43 0 0 84.58 115.3 0 0 27.47 -115.3 0 0 86.02 127.23 0 0 27.47 -157.59 0zm578.27 5.06c-30.12,0 -54.1,-8.73 -71.93,-26.2 -17.83,-17.47 -26.75,-42.95 -26.75,-76.45l0 -57.83c0,-33.5 8.91,-58.98 26.75,-76.45 17.83,-17.47 41.81,-26.2 71.93,-26.2 29.88,0 52.83,8.31 68.86,24.94 16.03,16.62 24.04,39.4 24.04,68.31l0 1.81 -30 0 0 -2.89c0,-19.04 -5.12,-34.64 -15.36,-46.81 -10.24,-12.17 -26.08,-18.25 -47.53,-18.25 -21.44,0 -38.19,6.57 -50.24,19.7 -12.05,13.13 -18.07,31.51 -18.07,55.12l0 59.28c0,23.62 6.02,41.99 18.07,55.12 12.05,13.13 28.8,19.7 50.24,19.7 21.45,0 37.29,-6.08 47.53,-18.25 10.24,-12.17 15.36,-27.77 15.36,-46.81l0 -5.78 30 0 0 4.7c0,28.92 -8.01,51.69 -24.04,68.31 -16.02,16.63 -38.98,24.94 -68.86,24.94zm423.92 -5.06l0 -253.02 58.92 0 88.2 233.86 4.7 0 0 -233.86 30 0 0 253.02 -58.92 0 -87.83 -234.22 -5.06 0 0 234.22 -30 0zm615.13 5.06c-30.12,0 -54.16,-8.73 -72.11,-26.2 -17.95,-17.47 -26.93,-42.95 -26.93,-76.45l0 -57.83c0,-33.5 8.98,-58.98 26.93,-76.45 17.95,-17.47 41.99,-26.2 72.11,-26.2 30.36,0 54.52,8.73 72.47,26.2 17.95,17.47 26.93,42.95 26.93,76.45l0 57.83c0,33.5 -8.97,58.98 -26.93,76.45 -17.96,17.47 -42.11,26.2 -72.47,26.2zm0 -27.11c21.93,0 38.92,-6.57 50.96,-19.7 12.05,-13.13 18.07,-31.39 18.07,-54.76l0 -60c0,-23.37 -6.02,-41.63 -18.07,-54.76 -12.05,-13.13 -29.04,-19.7 -50.96,-19.7 -21.69,0 -38.55,6.57 -50.61,19.7 -12.05,13.13 -18.07,31.39 -18.07,54.76l0 60c0,23.37 6.02,41.63 18.07,54.76 12.05,13.13 28.92,19.7 50.61,19.7zm433.32 22.05l0 -253.02 30.36 0 0 225.54 127.96 0 0 27.47 -158.32 0zm565.25 5.06c-30.12,0 -54.16,-8.73 -72.11,-26.2 -17.95,-17.47 -26.93,-42.95 -26.93,-76.45l0 -57.83c0,-33.5 8.98,-58.98 26.93,-76.45 17.95,-17.47 41.99,-26.2 72.11,-26.2 30.36,0 54.52,8.73 72.47,26.2 17.95,17.47 26.93,42.95 26.93,76.45l0 57.83c0,33.5 -8.97,58.98 -26.93,76.45 -17.96,17.47 -42.11,26.2 -72.47,26.2zm0 -27.11c21.93,0 38.92,-6.57 50.96,-19.7 12.05,-13.13 18.07,-31.39 18.07,-54.76l0 -60c0,-23.37 -6.02,-41.63 -18.07,-54.76 -12.05,-13.13 -29.04,-19.7 -50.96,-19.7 -21.69,0 -38.55,6.57 -50.61,19.7 -12.05,13.13 -18.07,31.39 -18.07,54.76l0 60c0,23.37 6.02,41.63 18.07,54.76 12.05,13.13 28.92,19.7 50.61,19.7zm521.51 27.11c-18.8,0 -35.36,-3.91 -49.7,-11.74 -14.34,-7.83 -25.54,-19.34 -33.61,-34.52 -8.07,-15.18 -12.11,-33.98 -12.11,-56.39l0 -57.83c0,-33.5 8.91,-58.98 26.75,-76.45 17.83,-17.47 41.81,-26.2 71.93,-26.2 29.88,0 52.65,8.19 68.31,24.58 15.66,16.38 23.5,38.32 23.5,65.79l0 1.81 -30 0 0 -2.53c0,-12.05 -2.17,-22.77 -6.51,-32.17 -4.34,-9.4 -11.08,-16.81 -20.24,-22.23 -9.16,-5.42 -20.85,-8.13 -35.06,-8.13 -21.44,0 -38.19,6.57 -50.24,19.7 -12.05,13.13 -18.07,31.51 -18.07,55.12l0 59.28c0,23.62 6.02,41.99 18.07,55.12 12.05,13.13 28.92,19.7 50.61,19.7 21.2,0 36.81,-6.02 46.81,-18.07 10,-12.05 15,-28.07 15,-48.07l0 -6.14 -76.63 0 0 -26.02 106.27 0 0 120.36 -27.47 0 0 -27.47 -5.06 0c-2.65,5.3 -6.38,10.48 -11.2,15.54 -4.82,5.06 -11.33,9.16 -19.52,12.29 -8.19,3.13 -18.8,4.7 -31.81,4.7zm433.68 -5.06l0 -253.02 30.36 0 0 253.02 -30.36 0zm351.64 0l78.07 -253.02 53.49 0 78.08 253.02 -31.45 0 -19.52 -63.98 -107.71 0 -19.52 63.98 -31.45 0zm58.55 -91.45l92.17 0 -43.37 -143.13 -5.06 0 -43.73 143.13z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
|
|
@ -1,11 +1,19 @@
|
|||
'use client'
|
||||
'use server'
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export default async function CookiesGet() {
|
||||
/**
|
||||
* Função para obter tokens do lado servidorde acordo com o nome informado
|
||||
*/
|
||||
export default async function CookiesGet(token: string) {
|
||||
|
||||
// Obtem a lista de Tokens
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get('access_token');
|
||||
return token?.value;
|
||||
|
||||
// Obtem um token em especifico
|
||||
const data = cookieStore.get(token);
|
||||
|
||||
// Retorna nulo ou o valor do token desejado
|
||||
return data?.value;
|
||||
|
||||
}
|
||||
15
src/actions/text/GetSigla.ts
Normal file
15
src/actions/text/GetSigla.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export default function GetSigla(data: string): string {
|
||||
|
||||
if (!data) return "";
|
||||
|
||||
// Remove espaços extras no início e no fim e divide em palavras
|
||||
const palavras = data.trim().split(/\s+/);
|
||||
|
||||
if (palavras.length === 1) {
|
||||
// Apenas uma palavra → retorna as duas primeiras letras
|
||||
return palavras[0].substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
// Duas ou mais palavras → retorna a primeira letra de cada
|
||||
return palavras.map(palavra => palavra.charAt(0).toUpperCase()).join('');
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
'use client'
|
||||
|
||||
import GUsuarioLogoutService from "../../_services/g_usuario/GUsuarioLogoutService";
|
||||
|
||||
export const useGUsuarioLogoutHook = () => {
|
||||
|
||||
const logoutUsuario = async () => {
|
||||
|
||||
await GUsuarioLogoutService('access_token')
|
||||
|
||||
}
|
||||
|
||||
return { logoutUsuario }
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
'use server'
|
||||
|
||||
import {
|
||||
cookies
|
||||
} from "next/headers";
|
||||
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function GUsuarioLogoutService(token: string) {
|
||||
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set(token, '', {
|
||||
expires: new Date(0),
|
||||
path: '/',
|
||||
});
|
||||
|
||||
redirect('/login');
|
||||
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ export default function RootLayout({
|
|||
<SidebarProvider>
|
||||
<AppSidebar />
|
||||
<SidebarInset>
|
||||
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
|
||||
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 border-b-1 mb-4">
|
||||
<div className="flex items-center gap-2 px-4">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
<Separator
|
||||
|
|
|
|||
|
|
@ -18,22 +18,21 @@ import {
|
|||
import { NavMain } from "@/components/nav-main"
|
||||
import { NavProjects } from "@/components/nav-projects"
|
||||
import { NavUser } from "@/components/nav-user"
|
||||
import { TeamSwitcher } from "@/components/team-switcher"
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarRail,
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
import useGUsuarioGetJWTHook from "@/hooks/auth/useGUsuarioGetJWTHook"
|
||||
|
||||
// This is sample data.
|
||||
const data = {
|
||||
user: {
|
||||
name: "shadcn",
|
||||
email: "m@example.com",
|
||||
avatar: "/avatars/shadcn.jpg",
|
||||
},
|
||||
teams: [
|
||||
{
|
||||
name: "Acme Inc",
|
||||
|
|
@ -178,19 +177,71 @@ const data = {
|
|||
}
|
||||
|
||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
|
||||
const { userAuthenticated } = useGUsuarioGetJWTHook();
|
||||
|
||||
return (
|
||||
|
||||
<Sidebar collapsible="icon" {...props}>
|
||||
|
||||
<SidebarHeader>
|
||||
<TeamSwitcher teams={data.teams} />
|
||||
|
||||
<SidebarMenu>
|
||||
|
||||
<SidebarMenuItem>
|
||||
|
||||
<SidebarMenuButton size="lg" asChild>
|
||||
|
||||
<a href="#">
|
||||
|
||||
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||
|
||||
<GalleryVerticalEnd className="size-4" />
|
||||
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-0.5 leading-none">
|
||||
|
||||
<span className="font-semibold">
|
||||
|
||||
Orius Tecnologia
|
||||
|
||||
</span>
|
||||
|
||||
<span className="">
|
||||
|
||||
25.9.1
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</a>
|
||||
|
||||
</SidebarMenuButton>
|
||||
|
||||
</SidebarMenuItem>
|
||||
|
||||
</SidebarMenu>
|
||||
|
||||
</SidebarHeader>
|
||||
|
||||
<SidebarContent>
|
||||
|
||||
<NavMain items={data.navMain} />
|
||||
<NavProjects projects={data.projects} />
|
||||
|
||||
</SidebarContent>
|
||||
|
||||
<SidebarFooter>
|
||||
<NavUser user={data.user} />
|
||||
|
||||
<NavUser user={userAuthenticated?.data} />
|
||||
|
||||
</SidebarFooter>
|
||||
|
||||
<SidebarRail />
|
||||
|
||||
</Sidebar>
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,41 +34,85 @@ export function NavMain({
|
|||
}[]
|
||||
}) {
|
||||
return (
|
||||
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
||||
|
||||
<SidebarGroupLabel>
|
||||
|
||||
Platform
|
||||
|
||||
</SidebarGroupLabel>
|
||||
|
||||
<SidebarMenu>
|
||||
|
||||
{items.map((item) => (
|
||||
|
||||
<Collapsible
|
||||
key={item.title}
|
||||
asChild
|
||||
defaultOpen={item.isActive}
|
||||
className="group/collapsible"
|
||||
>
|
||||
|
||||
<SidebarMenuItem>
|
||||
|
||||
<CollapsibleTrigger asChild>
|
||||
|
||||
<SidebarMenuButton tooltip={item.title}>
|
||||
|
||||
{item.icon && <item.icon />}
|
||||
<span>{item.title}</span>
|
||||
|
||||
<span>
|
||||
|
||||
{item.title}
|
||||
|
||||
</span>
|
||||
|
||||
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
|
||||
|
||||
</SidebarMenuButton>
|
||||
|
||||
</CollapsibleTrigger>
|
||||
|
||||
<CollapsibleContent>
|
||||
|
||||
<SidebarMenuSub>
|
||||
|
||||
{item.items?.map((subItem) => (
|
||||
|
||||
<SidebarMenuSubItem key={subItem.title}>
|
||||
|
||||
<SidebarMenuSubButton asChild>
|
||||
|
||||
<Link href={subItem.url}>
|
||||
<span>{subItem.title}</span>
|
||||
|
||||
<span>
|
||||
|
||||
{subItem.title}
|
||||
|
||||
</span>
|
||||
|
||||
</Link>
|
||||
|
||||
</SidebarMenuSubButton>
|
||||
|
||||
</SidebarMenuSubItem>
|
||||
|
||||
))}
|
||||
|
||||
</SidebarMenuSub>
|
||||
|
||||
</CollapsibleContent>
|
||||
|
||||
</SidebarMenuItem>
|
||||
|
||||
</Collapsible>
|
||||
|
||||
))}
|
||||
|
||||
</SidebarMenu>
|
||||
|
||||
</SidebarGroup>
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
"use client"
|
||||
|
||||
import {
|
||||
BadgeCheck,
|
||||
Bell,
|
||||
ChevronsUpDown,
|
||||
CreditCard,
|
||||
LogOut,
|
||||
Sparkles,
|
||||
} from "lucide-react"
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
|
|
@ -30,85 +26,192 @@ import {
|
|||
useSidebar,
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
import GUsuarioAuthenticatedInterface from "@/interfaces/GUsuarioAuthenticatedInterface"
|
||||
import ConfirmDialog from "@/app/_components/confirm_dialog/ConfirmDialog"
|
||||
import { useGUsuarioLogoutHook } from "@/app/(protected)/(administrativo)/_hooks/g_usuario/useGUsuarioLogoutHook"
|
||||
import { use, useCallback, useState } from "react"
|
||||
|
||||
export function NavUser({
|
||||
user,
|
||||
|
||||
user
|
||||
|
||||
}: {
|
||||
user: {
|
||||
name: string
|
||||
email: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
user: GUsuarioAuthenticatedInterface
|
||||
|
||||
}) {
|
||||
const { isMobile } = useSidebar()
|
||||
|
||||
// Hook para encerrar sessão
|
||||
const { logoutUsuario } = useGUsuarioLogoutHook();
|
||||
|
||||
// Controle de exibição do formulário de confirmação
|
||||
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
|
||||
|
||||
const { isMobile } = useSidebar();
|
||||
|
||||
// Manipulação de formulário de confirmação
|
||||
const handleConfirmOpen = useCallback(async () => {
|
||||
|
||||
setIsConfirmOpen(true);
|
||||
|
||||
}, []);
|
||||
|
||||
const handleLogoutConfirm = useCallback(async () => {
|
||||
|
||||
logoutUsuario();
|
||||
|
||||
}, []);
|
||||
|
||||
const handleLogoutCancel = useCallback(async () => {
|
||||
|
||||
setIsConfirmOpen(false);
|
||||
|
||||
}, [])
|
||||
|
||||
if (!user) {
|
||||
|
||||
return 'Carregando...';
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<Avatar className="h-8 w-8 rounded-lg">
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-medium">{user.name}</span>
|
||||
<span className="truncate text-xs">{user.email}</span>
|
||||
</div>
|
||||
<ChevronsUpDown className="ml-auto size-4" />
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuLabel className="p-0 font-normal">
|
||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
|
||||
<>
|
||||
<SidebarMenu>
|
||||
|
||||
<SidebarMenuItem>
|
||||
|
||||
<DropdownMenu>
|
||||
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground cursor-pointer"
|
||||
>
|
||||
|
||||
<Avatar className="h-8 w-8 rounded-lg">
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||
|
||||
<AvatarImage src={user.avatar} alt={user.nome} />
|
||||
|
||||
<AvatarFallback className="rounded-lg">
|
||||
|
||||
{user.sigla}
|
||||
|
||||
</AvatarFallback>
|
||||
|
||||
</Avatar>
|
||||
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-medium">{user.name}</span>
|
||||
<span className="truncate text-xs">{user.email}</span>
|
||||
|
||||
<span className="truncate font-medium">
|
||||
|
||||
{user.nome}
|
||||
|
||||
</span>
|
||||
|
||||
<span className="truncate text-xs">
|
||||
|
||||
{user.email}
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<Sparkles />
|
||||
Upgrade to Pro
|
||||
|
||||
<ChevronsUpDown className="ml-auto size-4" />
|
||||
|
||||
</SidebarMenuButton>
|
||||
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent
|
||||
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
|
||||
<DropdownMenuLabel className="p-0 font-normal">
|
||||
|
||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
|
||||
<Avatar className="h-8 w-8 rounded-lg">
|
||||
|
||||
<AvatarImage src={user.avatar} alt={user.nome} />
|
||||
|
||||
<AvatarFallback className="rounded-lg">
|
||||
|
||||
{user.sigla}
|
||||
|
||||
</AvatarFallback>
|
||||
|
||||
</Avatar>
|
||||
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
|
||||
<span className="truncate font-medium">
|
||||
|
||||
{user.nome}
|
||||
|
||||
</span>
|
||||
|
||||
<span className="truncate text-xs">
|
||||
|
||||
{user.email}
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuGroup>
|
||||
|
||||
<DropdownMenuItem className="cursor-pointer">
|
||||
|
||||
<Sparkles />
|
||||
|
||||
Configurações
|
||||
|
||||
</DropdownMenuItem>
|
||||
|
||||
</DropdownMenuGroup>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItem className="cursor-pointer" onClick={() => handleConfirmOpen()}>
|
||||
|
||||
<LogOut />
|
||||
|
||||
Log out
|
||||
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<BadgeCheck />
|
||||
Account
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<CreditCard />
|
||||
Billing
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Bell />
|
||||
Notifications
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<LogOut />
|
||||
Log out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
|
||||
</DropdownMenuContent>
|
||||
|
||||
</DropdownMenu>
|
||||
|
||||
</SidebarMenuItem>
|
||||
|
||||
</SidebarMenu>
|
||||
|
||||
{/* Modal de confirmação */}
|
||||
<ConfirmDialog
|
||||
isOpen={isConfirmOpen}
|
||||
title="Log-out"
|
||||
description="Atenção"
|
||||
message={`Deseja realmente encerrar a sessão ? Dados não salvos serão perdidos`}
|
||||
confirmText="Sim, sair"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={() => handleLogoutConfirm()}
|
||||
onCancel={() => handleLogoutCancel()}
|
||||
/>
|
||||
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
|||
67
src/hooks/auth/useGUsuarioGetJWTHook.ts
Normal file
67
src/hooks/auth/useGUsuarioGetJWTHook.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import CookiesGet from "../../actions/cookies/CookiesGet";
|
||||
import GetSigla from "@/actions/text/GetSigla";
|
||||
|
||||
interface JwtPayload {
|
||||
id: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
data?: string;
|
||||
}
|
||||
|
||||
export default function useGUsuarioGetJWTHook() {
|
||||
|
||||
const [userAuthenticated, setUserAuthenticated] = useState<JwtPayload | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
async function fetchToken() {
|
||||
|
||||
try {
|
||||
|
||||
// Executa a serve action para obtem o token, salvo em cookies
|
||||
const token = await CookiesGet("access_token");
|
||||
|
||||
// Verifica se o token esta preenchido
|
||||
if (!token) {
|
||||
|
||||
console.error("Não foi localizado dados dentro do token");
|
||||
|
||||
// Encerra a aplicação
|
||||
return;
|
||||
}
|
||||
|
||||
// Decodifica os dados do JWT
|
||||
const decoded = jwtDecode<JwtPayload>(token);
|
||||
|
||||
// Se existir campo data e for string, corrige aspas simples
|
||||
if (decoded.data && typeof decoded.data === "string") {
|
||||
|
||||
// Decodifica os dados enviado via json
|
||||
decoded.data = JSON.parse(decoded.data);
|
||||
|
||||
// Gera Sigla para o nome
|
||||
decoded.data.sigla = GetSigla(decoded.data.nome)
|
||||
|
||||
}
|
||||
|
||||
// Armazena os dados decodificados
|
||||
setUserAuthenticated(decoded);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
console.error("Erro ao buscar token", error);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Busca o TOken
|
||||
fetchToken();
|
||||
|
||||
}, []);
|
||||
|
||||
return { userAuthenticated };
|
||||
}
|
||||
13
src/interfaces/GUsuarioAuthenticatedInterface.ts
Normal file
13
src/interfaces/GUsuarioAuthenticatedInterface.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export default interface GUsuarioAuthenticatedInterface {
|
||||
usuario_id?: number,
|
||||
login?: string,
|
||||
situacao?: string,
|
||||
nome_completo?: string,
|
||||
funcao?: string,
|
||||
assina?: string,
|
||||
sigla?: string,
|
||||
email?: string,
|
||||
assina_certidao?: string,
|
||||
foto?: string,
|
||||
cpf?: string,
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue