integracao-core

This commit is contained in:
2025-11-14 17:17:30 -03:00
parent 2b2886a75c
commit f0b86641b6
18 changed files with 517 additions and 17 deletions
+18 -2
View File
@@ -1,3 +1,19 @@
VITE_ESTABELECIMENTO_ID=27
VITE_USER_EMAIL=your-email@example.com
# API Configuration
# Base URL da API do HGTX
VITE_API_BASE_URL=https://prod-hgtx-intelligence-n8n.hgtx.com.br
VITE_BASE_URL_HGTX_CORE_API=https://core-api.hgtx.com.br/api/
VITE_BASE_URL_HGTX_CORE=https://core.hgtx.com.br/
# API Key para autenticação
# IMPORTANTE: Mantenha esta chave segura e não compartilhe publicamente
VITE_API_KEY=zp01m-pmn5q-0l2eg-aj5vy
# User Configuration
# Configure com os dados do usuário/estabelecimento
VITE_USER_EMAIL=suporte@hightechx.com
VITE_ESTABELECIMENTO_ID=27
# Instruções:
# 1. Copie este arquivo para .env
# 2. Substitua os valores acima com as configurações reais
# 3. NUNCA commite o arquivo .env no git
+19
View File
@@ -0,0 +1,19 @@
# API Configuration
# Base URL da API do HGTX
VITE_API_BASE_URL=https://prod-hgtx-intelligence-n8n.hgtx.com.br
VITE_BASE_URL_HGTX_CORE_API=https://core-api.hgtx.com.br/api/
VITE_BASE_URL_HGTX_CORE=https://core.hgtx.com.br/
# API Key para autenticação
# IMPORTANTE: Mantenha esta chave segura e não compartilhe publicamente
VITE_API_KEY=zp01m-pmn5q-0l2eg-aj5vy
# User Configuration
# Configure com os dados do usuário/estabelecimento
VITE_USER_EMAIL=suporte@hightechx.com
VITE_ESTABELECIMENTO_ID=1
# Instruções:
# 1. Copie este arquivo para .env
# 2. Substitua os valores acima com as configurações reais
# 3. NUNCA commite o arquivo .env no git
+9
View File
@@ -44,6 +44,7 @@
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.6.0",
"input-otp": "^1.4.2",
"jwt-decode": "^4.0.0",
"lucide-react": "^0.462.0",
"next-themes": "^0.3.0",
"react": "^18.3.1",
@@ -4828,6 +4829,14 @@
"dev": true,
"license": "MIT"
},
"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==",
"engines": {
"node": ">=18"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+1
View File
@@ -47,6 +47,7 @@
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.6.0",
"input-otp": "^1.4.2",
"jwt-decode": "^4.0.0",
"lucide-react": "^0.462.0",
"next-themes": "^0.3.0",
"react": "^18.3.1",
+19 -5
View File
@@ -10,6 +10,8 @@ import AgentDetails from "./pages/AgentDetails";
import TestPrompt from "./pages/TestPrompt";
import History from "./pages/History";
import NotFound from "./pages/NotFound";
import Redirect from "./pages/Redirect";
import React from "react";
const queryClient = new QueryClient();
@@ -21,14 +23,26 @@ const App = () => (
<Sonner />
<BrowserRouter>
<Routes>
<Route element={<AppLayout />}>
<Route path="/" element={<Agents />} />
<Route path="/agents/:id" element={<AgentDetails />} />
<Route path="/test" element={<TestPrompt />} />
<Route path="/history" element={<History />} />
<Route
path="/redir/:gsikey"
element={<Redirect />}
/>
{/* Layout principal para /prompt-labs */}
<Route path="/prompt-labs" element={<AppLayout />}>
{/* Página inicial: /prompt-labs */}
<Route index element={<Agents />} />
{/* Rotas internas */}
<Route path="agents/:id" element={<AgentDetails />} />
<Route path="test" element={<TestPrompt />} />
<Route path="history" element={<History />} />
{/* Catch-all exclusivo para o contexto de /prompt-labs */}
<Route path="*" element={<NotFound />} />
</Route>
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
<Route path="/404" element={<NotFound />} />
</Routes>
</BrowserRouter>
</TooltipProvider>
+286
View File
@@ -0,0 +1,286 @@
import { jwtDecode } from 'jwt-decode';
import { RedirectService } from './pages/RedirectService';
export class GlobalFunctions {
public static getTranferAreaObj(): TransferAreaResponse {
let objRetorno: TransferAreaResponse = {
aplicativoID: 0,
aplicativoNome: "",
areaTransferenciaID: 0,
dataRegistro: "",
estabelecimentoID: 0,
usuarioID: 0,
usuarioEmail: "",
usuarioNome: "",
aplicativoShareCode: "",
nivelAcesso: 0
};
try {
const objTransferStr = sessionStorage.getItem("TRANSFER_AREA");
if (
objTransferStr != null &&
objTransferStr != undefined &&
objTransferStr.trim() != ""
) {
const objTransfer = <TransferAreaResponse>JSON.parse(objTransferStr);
objRetorno = objTransfer;
}
} catch (error) {}
return objRetorno;
}
public static getRedirectComponent(): string {
let objRetorno = "";
try {
const objTransfer = GlobalFunctions.getTranferAreaObj();
if (objTransfer != null) {
// let objRedir = objTransfer.find(x => x.name == 'REDIRECT');
// objRetorno = objRedir?.value ?? '';
objRetorno = objTransfer.urlDestino ?? "";
if (objRetorno.startsWith("/")) objRetorno = objRetorno.substring(1);
} else {
objRetorno = "";
}
} catch (error) {
//console.log(error);
objRetorno = "";
}
return objRetorno;
}
public static getTransferProperty(propertyName: TransferAreaProperties): any {
let objRetorno: any = "";
try {
const objTransfer = this.getTranferAreaObj();
if (objTransfer != null) {
//let obj: any = objTransfer[propertyName];
const element = objTransfer[propertyName];
objRetorno = element;
} else {
objRetorno = null;
}
} catch (error) {
objRetorno = null;
}
return objRetorno;
}
public static isUsuarioLogado(): boolean {
let jsonUsuario = sessionStorage.getItem('usuarioLogado');
if (jsonUsuario) {
let tokenDecode: any = GlobalFunctions.getUsuarioLogado();
let d: Date = new Date();
let dateAtual: number = Date.UTC(d.getFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes() + 5, d.getUTCSeconds()) / 1000;
if (dateAtual > tokenDecode.exp) return false;
return true;
}
return false;
}
public static tokenExpired(tokenDecode: any): boolean {
let d: Date = new Date();
let dateAtual: number = Date.UTC(d.getFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes() + 5, d.getUTCSeconds()) / 1000;
if (dateAtual > tokenDecode.exp) return true;
return false;
}
public static handleNavigateBack(): void {
try {
// Verifica se existe histórico de navegação
if (window.history && window.history.length > 1) {
window.history.back();
return;
}
// Tenta fechar a janela
if (!window.closed) {
try {
const windowRef = window.open('', '_self');
if (windowRef) {
windowRef.close();
// Pequeno timeout para verificar se a janela foi fechada
setTimeout(() => {
// Se ainda estiver aberta, redireciona
if (!windowRef.closed) {
window.location.href = "https://core.hgtx.com.br/aplicativos/";
}
}, 100);
return;
}
} catch (closeError) {
console.warn("Não foi possível fechar a janela:", closeError);
}
}
// Se não conseguiu fechar, redireciona
window.location.href = "https://core.hgtx.com.br/aplicativos/";
} catch (error) {
// Em caso de qualquer erro, garante o redirecionamento
console.error("Erro ao tentar navegar:", error);
window.location.href = "https://core.hgtx.com.br/aplicativos/";
}
}
public static getUsuarioLogado(): UsuarioLogadoData {
const jsonUsuario = sessionStorage.getItem("usuarioLogado") ?? "";
try {
const objJwt = JSON.parse(jsonUsuario);
//return jwt_decode(objJwt.token);
return jwtDecode(objJwt.token);
} catch (error) {}
return {
EID: "0",
email: "",
exp: 0,
iat: 0,
jti: "",
name: "",
nbf: 0,
UID: "0",
};
}
public static getUsuarioLogadoToken(): string {
let retorno = "";
const jsonUsuario = sessionStorage.getItem("usuarioLogado") ?? "";
try {
const objJwt = JSON.parse(jsonUsuario);
retorno = objJwt.token;
} catch (error) {}
return retorno;
}
public static getUsuarioLogadoRefreshToken(): string {
let retorno = "";
const jsonUsuario = sessionStorage.getItem("usuarioLogado") ?? "";
try {
const objJwt = JSON.parse(jsonUsuario);
retorno = objJwt.refreshToken;
} catch (error) {}
return retorno;
}
public static getPerfilUsuario(): PerfilUsuario | null {
let retorno: PerfilUsuario | null = null;
const jsonUsuario = sessionStorage.getItem("perfilUsuario") ?? "";
try {
const objJwt = <PerfilUsuario>JSON.parse(jsonUsuario);
retorno = objJwt;
} catch (error) {
retorno = null;
}
return retorno;
}
public static getHighUserProfile(): number {
let retorno = 6;
const objPerfil = GlobalFunctions.getPerfilUsuario();
if (objPerfil) {
const tiposPerfil: number[] = objPerfil.results.map((v) => v.tipoPerfil);
retorno = Math.min(...tiposPerfil);
}
return retorno;
}
public static async getToken(): Promise<string> {
let token: string = GlobalFunctions.getUsuarioLogadoToken();
const tokenDecode: any = GlobalFunctions.getUsuarioLogado();
let currentDate = new Date();
// JWT exp is in seconds
if ((tokenDecode.exp * 1000) < currentDate.getTime()) {
//console.log("Token expired.");
let gsikey: number = parseInt(sessionStorage.getItem('gsikey') ?? '0');
RedirectService.loadTransferArea(gsikey).then((response) => {
if(response.data.errorCode == 0){
sessionStorage.setItem('TRANSFER_AREA',JSON.stringify(response.data.areTrs));
sessionStorage.setItem('usuarioLogado',JSON.stringify({
token: response.data.jwt.token,
refreshToken: response.data.jwt.refreshToken
}));
token = response.data.jwt.token;
}
// else if(error !== 1401){
// navigate('/',{replace: true});
// }
});
}
// else {
// //console.log("Valid token");
// result = true;
// }
return token;
}
}
export interface UsuarioLogadoData {
UID: string;
EID: string;
name: string;
email: string;
jti: string;
nbf: number;
exp: number;
iat: number;
}
export interface TransferAreaResponse {
estabelecimentoID: number;
nivelAcesso: number;
aplicativoID: number;
usuarioID: number;
areaTransferenciaID: number;
dataRegistro: string;
urlBase?: string;
urlDestino?: string;
aplicativoNome: string;
usuarioNome: string;
usuarioEmail: string;
aplicativoShareCode: string;
}
export enum TransferAreaProperties {
EstabelecimentoCodigo = "estabelecimentoID",
UsuarioCodigo = "usuarioID",
UsuarioNome = "usuarioNome",
UsuarioEmail = "usuarioEmail",
APP_COD = "aplicativoID",
APP_TITULO = "aplicativoNome",
areaTransferenciaID = "areaTransferenciaID",
urlBase = "urlBase",
urlDestino = "urlDestino",
aplicativoShareCode = "aplicativoShareCode",
nivelAcesso = "nivelAcesso",
}
type PerfilUsuario = {
errorCode: number;
errorMessage: string;
results: PerfilUsuarioResult[];
};
export type PerfilUsuarioResult = {
tipoPerfil: number;
itens: PerfilUsuarioItem[];
};
type PerfilUsuarioItem = {
id: number;
descricao: string;
identificador: string;
};
+35
View File
@@ -0,0 +1,35 @@
import { Loader2 } from "lucide-react";
import { useEffect, useState } from "react";
interface FullScreenLoaderProps {
show: boolean;
text?: string;
}
export default function FullScreenLoader({ show, text = "Carregando..." }: FullScreenLoaderProps) {
const [visible, setVisible] = useState(show);
useEffect(() => {
if (show) setVisible(true);
else {
// adiciona um pequeno atraso para suavizar o fade-out
const timeout = setTimeout(() => setVisible(false), 200);
return () => clearTimeout(timeout);
}
}, [show]);
if (!visible && !show) return null;
return (
<div
className={`fixed inset-0 z-[9999] flex items-center justify-center bg-background/80 backdrop-blur-sm transition-opacity duration-200 ${
show ? "opacity-100" : "opacity-0 pointer-events-none"
}`}
>
<div className="flex flex-col items-center gap-3 p-6 rounded-2xl bg-card shadow-lg border border-border transition-all duration-200">
<Loader2 className="h-10 w-10 animate-spin text-primary" />
<p className="text-card-foreground text-sm font-medium">{text}</p>
</div>
</div>
);
}
+1 -1
View File
@@ -49,7 +49,7 @@ export function CreateAgentDialog({ open, onOpenChange }: CreateAgentDialogProps
onOpenChange(false);
// Navegar para a tela de detalhes do novo agente
navigate(`/agents/${data.agent_id}`);
navigate(`/prompt-labs/agents/${data.agent_id}`);
// Reset form
setName("");
+16 -1
View File
@@ -1,8 +1,23 @@
import React from "react";
import { AppSidebar } from "./AppSidebar";
import { MobileNav } from "./MobileNav";
import { Outlet } from "react-router-dom";
import { Outlet, useLocation } from "react-router-dom";
import { GlobalFunctions } from "@/GlobalFunctions";
export function AppLayout() {
const { pathname } = useLocation();
// ignora layout se for redirecionamento
if (pathname.startsWith("/redir")) {
return <></>;
}
React.useEffect(() => {
console.log('Aqui');
if(!GlobalFunctions.isUsuarioLogado())window.location.replace(import.meta.env.VITE_BASE_URL_HGTX_CORE);
},[]);
return (
<div className="flex h-screen w-full bg-background overflow-hidden">
<MobileNav />
+4 -2
View File
@@ -10,10 +10,11 @@ import {
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { ThemeToggle } from "./ThemeToggle";
import { GlobalFunctions } from "@/GlobalFunctions";
const menuItems = [
{ title: "Agentes", url: "/", icon: Bot },
{ title: "Testar Prompt", url: "/test", icon: TestTube },
{ title: "Agentes", url: "/prompt-labs", icon: Bot },
{ title: "Testar Prompt", url: "/prompt-labs/test", icon: TestTube },
];
export function AppSidebar() {
@@ -134,6 +135,7 @@ export function AppSidebar() {
</div>
<Button
variant="ghost"
onClick={GlobalFunctions.handleNavigateBack}
className="w-full justify-start gap-2 text-sidebar-foreground"
>
<ArrowLeft className="w-4 h-4" />
+2 -1
View File
@@ -13,8 +13,9 @@ import type {
ToggleAgentStatusResponse,
ListAgentsResponse,
} from "../types/agent";
import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions";
const estabelecimentoId = import.meta.env.VITE_ESTABELECIMENTO_ID;
const estabelecimentoId = GlobalFunctions.getTransferProperty(TransferAreaProperties.EstabelecimentoCodigo) ?? import.meta.env.VITE_ESTABELECIMENTO_ID;
const userEmail = import.meta.env.VITE_USER_EMAIL;
/**
+2 -1
View File
@@ -5,8 +5,9 @@ import type {
TestAgentRequest,
TestAgentResponse,
} from "../types/chat";
import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions";
const estabelecimentoId = import.meta.env.VITE_ESTABELECIMENTO_ID;
const estabelecimentoId = GlobalFunctions.getTransferProperty(TransferAreaProperties.EstabelecimentoCodigo) ?? import.meta.env.VITE_ESTABELECIMENTO_ID;
const userEmail = import.meta.env.VITE_USER_EMAIL;
/**
+1 -1
View File
@@ -236,7 +236,7 @@ export default function AgentDetails() {
<Button
variant="ghost"
className="mb-4 gap-2"
onClick={() => navigate("/")}
onClick={() => navigate("/prompt-labs")}
>
<ArrowLeft className="h-4 w-4" />
Voltar para Agentes
+1 -1
View File
@@ -147,7 +147,7 @@ export default function Agents() {
<Card
key={agent.agente_id}
className="group relative overflow-hidden border-2 transition-all duration-300 hover:shadow-lg hover:scale-[1.02] bg-gradient-to-br from-card to-card/50 cursor-pointer"
onClick={() => navigate(`/agents/${agent.agente_id}`)}
onClick={() => navigate(`/prompt-labs/agents/${agent.agente_id}`)}
>
{/* Glow effect on hover */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-primary/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
+9 -2
View File
@@ -1,5 +1,6 @@
import { useLocation } from "react-router-dom";
import { useEffect } from "react";
import { GlobalFunctions } from "@/GlobalFunctions";
const NotFound = () => {
const location = useLocation();
@@ -13,9 +14,15 @@ const NotFound = () => {
<div className="text-center">
<h1 className="mb-4 text-4xl font-bold">404</h1>
<p className="mb-4 text-xl text-gray-600">Oops! Page not found</p>
<a href="/" className="text-blue-500 underline hover:text-blue-700">
{/* <a href="/" className="text-blue-500 underline hover:text-blue-700">
Return to Home
</a>
</a> */}
<button
className="text-blue-500 underline hover:text-blue-700"
onClick={GlobalFunctions.handleNavigateBack}
>
Return to Home
</button>
</div>
</div>
);
+50
View File
@@ -0,0 +1,50 @@
import { GlobalFunctions } from "@/GlobalFunctions";
import { RedirectService } from "./RedirectService";
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
import FullScreenLoader from "@/components/FullScreenLoader";
export default function Redirect() {
const navigate = useNavigate();
let { gsikey } = useParams();
React.useEffect(() => {
let error: number = 0;
sessionStorage.setItem('gsikey', gsikey ?? '0');
RedirectService.loadTransferArea(parseInt(gsikey ?? '0')).then((response) => {
console.log(response);
error = response.data.errorCode;
//msg = response.data.errorMessage;
if(response.data.errorCode == 0){
sessionStorage.setItem('TRANSFER_AREA',JSON.stringify(response.data.areTrs));
sessionStorage.setItem('usuarioLogado',JSON.stringify({
token: response.data.jwt.token,
refreshToken: response.data.jwt.refreshToken
}));
}
// else if(error !== 1401){
// navigate('/',{replace: true});
// }
})
.catch((_err) => {
navigate('/404',{replace: true});
})
.finally(() => {
if(error == 0 || error === 1401){
//navigate(`/${GlobalFunctions.getRedirectComponent()}`,{replace: true});
window.location.replace(`/${GlobalFunctions.getRedirectComponent()}`);
}
// else if(error === 1401){
// navigate({pathname: '/',search:`?p=${window.btoa(msg)}`},{replace: true});
// }
});
}, []);
return (
<FullScreenLoader show={true} text="Carregando...Aguarde..." />
);
}
+32
View File
@@ -0,0 +1,32 @@
import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions";
import axios from "axios";
const endPoint: string = 'v1/redirect/';
export class RedirectService{
public static loadTransferArea(gsikey: number): Promise<any>{
return axios.get(`${import.meta.env.VITE_BASE_URL_HGTX_CORE_API}${endPoint}get_component/${gsikey}/S`);
}
public static async validaAcessoApp(urlDestino: string): Promise<any>{
//let token = await GlobalFunctions.getToken();
let usuLogado = GlobalFunctions.getUsuarioLogado();
let usuCod: string = GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioCodigo);
let estCod: string = usuLogado.EID;
let urlDestinoPost: string = urlDestino;//urlDestino.trim().length < 1 ? GlobalFunctions.getTransferProperty(TransferAreaProperties.urlDestino) : urlDestino;
if(urlDestinoPost.trim().length < 1){
try {
let pathSplit = window.location.pathname.split('/');
urlDestinoPost = `/${pathSplit[pathSplit.length-2]}/${pathSplit[pathSplit.length-1]}`;
} catch (error) {
}
}
//const heads = { headers: {'Content-Type':'application/json; charset=utf-8','Authorization': `Bearer ${token}`} };
const heads = { headers: {'Content-Type':'application/json; charset=utf-8'} };
return axios.post(`${import.meta.env.VITE_BASE_URL_HGTX_CORE_API}v1/aplicativos/validar_acesso_app?usuCod=${usuCod}&estCod=${estCod}`,{
"urlDestino": urlDestinoPost
},heads);
}
}
+12
View File
@@ -1 +1,13 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string
readonly VITE_API_KEY: string
readonly VITE_USER_EMAIL: string
readonly VITE_ESTABELECIMENTO_ID: string
readonly VITE_BASE_URL_HGTX_CORE_API: string
readonly VITE_BASE_URL_HGTX_CORE: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}