@oondemand/oon-core-front 0.5.6 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-32GGDIWB.js +71 -0
- package/dist/chunk-32GGDIWB.js.map +1 -0
- package/dist/chunk-ALZCHGGI.js +19 -0
- package/dist/chunk-ALZCHGGI.js.map +1 -0
- package/dist/chunk-KD7UQF5E.js +103 -0
- package/dist/chunk-KD7UQF5E.js.map +1 -0
- package/dist/chunk-QXKS2WEG.js +2111 -0
- package/dist/chunk-QXKS2WEG.js.map +1 -0
- package/dist/chunk-R7ZBLDC2.js +5429 -0
- package/dist/chunk-R7ZBLDC2.js.map +1 -0
- package/dist/chunk-V6TY7KAL.js +8 -0
- package/dist/chunk-V6TY7KAL.js.map +1 -0
- package/dist/chunk-VC6NTMZS.js +187 -0
- package/dist/chunk-VC6NTMZS.js.map +1 -0
- package/dist/chunk-VJ7YNFF7.js +694 -0
- package/dist/chunk-VJ7YNFF7.js.map +1 -0
- package/dist/hooks-lXMaiLqz.d.ts +89 -0
- package/dist/hooks.d.ts +8 -0
- package/dist/hooks.js +24 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +26 -1187
- package/dist/index.js +63 -10202
- package/dist/index.js.map +1 -1
- package/dist/routing.d.ts +12 -0
- package/dist/routing.js +17 -0
- package/dist/routing.js.map +1 -0
- package/dist/testing.d.ts +8 -0
- package/dist/testing.js +22 -0
- package/dist/testing.js.map +1 -0
- package/dist/theme-Clhoswb3.d.ts +28 -0
- package/dist/theme.d.ts +12 -0
- package/dist/theme.js +14 -0
- package/dist/theme.js.map +1 -0
- package/dist/types-BEVt-nU8.d.ts +666 -0
- package/dist/ui-1Um8gXIZ.d.ts +337 -0
- package/dist/ui.d.ts +5 -0
- package/dist/ui.js +86 -0
- package/dist/ui.js.map +1 -0
- package/package.json +21 -1
|
@@ -0,0 +1,2111 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CoreCollection,
|
|
3
|
+
CoreDashboard,
|
|
4
|
+
CoreDocument,
|
|
5
|
+
CorePage,
|
|
6
|
+
CorePipeline,
|
|
7
|
+
CoreTransactionalEmail,
|
|
8
|
+
CoreUsersAccess,
|
|
9
|
+
MissingRenderer,
|
|
10
|
+
resolveComponent
|
|
11
|
+
} from "./chunk-R7ZBLDC2.js";
|
|
12
|
+
import {
|
|
13
|
+
OON_RESERVED_ROUTES,
|
|
14
|
+
defineOonNavigation,
|
|
15
|
+
defineOonRoutes,
|
|
16
|
+
routeElement
|
|
17
|
+
} from "./chunk-32GGDIWB.js";
|
|
18
|
+
import {
|
|
19
|
+
LoadingScreen
|
|
20
|
+
} from "./chunk-ALZCHGGI.js";
|
|
21
|
+
import {
|
|
22
|
+
ActivationProvider,
|
|
23
|
+
ApiProvider,
|
|
24
|
+
AuthProvider,
|
|
25
|
+
TenantProvider,
|
|
26
|
+
createRestClient,
|
|
27
|
+
createTokenStorage,
|
|
28
|
+
useActivation,
|
|
29
|
+
useCoreMetadata,
|
|
30
|
+
useOonApi,
|
|
31
|
+
useOonAuth
|
|
32
|
+
} from "./chunk-VJ7YNFF7.js";
|
|
33
|
+
import {
|
|
34
|
+
OonThemeProvider
|
|
35
|
+
} from "./chunk-VC6NTMZS.js";
|
|
36
|
+
|
|
37
|
+
// src/security/tenantStorage.ts
|
|
38
|
+
function normalize(value2) {
|
|
39
|
+
const normalized = String(value2 ?? "").trim();
|
|
40
|
+
return normalized || null;
|
|
41
|
+
}
|
|
42
|
+
function tenantFromUrl(key) {
|
|
43
|
+
if (typeof window === "undefined") return null;
|
|
44
|
+
const url = new URL(window.location.href);
|
|
45
|
+
const tenantId = normalize(url.searchParams.get("tenant"));
|
|
46
|
+
if (!tenantId) return null;
|
|
47
|
+
window.sessionStorage.setItem(key, tenantId);
|
|
48
|
+
url.searchParams.delete("tenant");
|
|
49
|
+
window.history.replaceState(window.history.state, "", `${url.pathname}${url.search}${url.hash}`);
|
|
50
|
+
return tenantId;
|
|
51
|
+
}
|
|
52
|
+
function createTenantStorage(appId, options = {}) {
|
|
53
|
+
const key = `oon:${appId}:tenant`;
|
|
54
|
+
const enabled = options.enabled !== false;
|
|
55
|
+
const clearStoredTenant = () => {
|
|
56
|
+
if (typeof window === "undefined") return;
|
|
57
|
+
window.sessionStorage.removeItem(key);
|
|
58
|
+
window.localStorage.removeItem(key);
|
|
59
|
+
};
|
|
60
|
+
if (!enabled) {
|
|
61
|
+
return {
|
|
62
|
+
get() {
|
|
63
|
+
clearStoredTenant();
|
|
64
|
+
return null;
|
|
65
|
+
},
|
|
66
|
+
set() {
|
|
67
|
+
clearStoredTenant();
|
|
68
|
+
},
|
|
69
|
+
clear: clearStoredTenant
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
get() {
|
|
74
|
+
if (typeof window === "undefined") return null;
|
|
75
|
+
const incoming = tenantFromUrl(key);
|
|
76
|
+
if (incoming) return incoming;
|
|
77
|
+
window.localStorage.removeItem(key);
|
|
78
|
+
return normalize(window.sessionStorage.getItem(key));
|
|
79
|
+
},
|
|
80
|
+
set(tenantId) {
|
|
81
|
+
if (typeof window === "undefined") return;
|
|
82
|
+
const value2 = normalize(tenantId);
|
|
83
|
+
if (!value2) {
|
|
84
|
+
window.sessionStorage.removeItem(key);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
window.sessionStorage.setItem(key, value2);
|
|
88
|
+
},
|
|
89
|
+
clear() {
|
|
90
|
+
if (typeof window !== "undefined") window.sessionStorage.removeItem(key);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/start.tsx
|
|
96
|
+
import { StrictMode } from "react";
|
|
97
|
+
import { createRoot } from "react-dom/client";
|
|
98
|
+
|
|
99
|
+
// src/app/OonApp.tsx
|
|
100
|
+
import { useMemo as useMemo2 } from "react";
|
|
101
|
+
import { RouterProvider } from "react-router-dom";
|
|
102
|
+
|
|
103
|
+
// src/security/AcceptInviteCentralPage.tsx
|
|
104
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
105
|
+
import { Box as Box2, Text as Text2 } from "@chakra-ui/react";
|
|
106
|
+
|
|
107
|
+
// src/security/AcceptInvitePage.tsx
|
|
108
|
+
import { useEffect, useState } from "react";
|
|
109
|
+
import { Box, Button, Flex, Heading, Input, Text, VStack } from "@chakra-ui/react";
|
|
110
|
+
import { Link, useLocation } from "react-router-dom";
|
|
111
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
112
|
+
function AcceptInvitePage() {
|
|
113
|
+
const { http } = useOonApi();
|
|
114
|
+
const location = useLocation();
|
|
115
|
+
const token = new URLSearchParams(location.search).get("token") || "";
|
|
116
|
+
const [invitation, setInvitation] = useState(null);
|
|
117
|
+
const [password, setPassword] = useState("");
|
|
118
|
+
const [confirm, setConfirm] = useState("");
|
|
119
|
+
const [message, setMessage] = useState(null);
|
|
120
|
+
const [done, setDone] = useState(false);
|
|
121
|
+
const [loading, setLoading] = useState(true);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
let active = true;
|
|
124
|
+
if (!token) {
|
|
125
|
+
setMessage("Link de convite inv\xE1lido.");
|
|
126
|
+
setLoading(false);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
http.get(`/core/invitations/${encodeURIComponent(token)}`).then((response) => {
|
|
130
|
+
if (active) setInvitation(response.data.invitation);
|
|
131
|
+
}).catch(() => {
|
|
132
|
+
if (active) setMessage("Este convite \xE9 inv\xE1lido, expirou ou j\xE1 foi utilizado.");
|
|
133
|
+
}).finally(() => {
|
|
134
|
+
if (active) setLoading(false);
|
|
135
|
+
});
|
|
136
|
+
return () => {
|
|
137
|
+
active = false;
|
|
138
|
+
};
|
|
139
|
+
}, [http, token]);
|
|
140
|
+
async function submit(event) {
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
setMessage(null);
|
|
143
|
+
if (password.length < 8) {
|
|
144
|
+
setMessage("A senha deve possuir pelo menos 8 caracteres.");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (password !== confirm) {
|
|
148
|
+
setMessage("As senhas informadas n\xE3o conferem.");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
await http.post(`/core/invitations/${encodeURIComponent(token)}/accept`, { password });
|
|
153
|
+
setDone(true);
|
|
154
|
+
setMessage("Convite aceito e senha definida. Voc\xEA j\xE1 pode entrar na Central.");
|
|
155
|
+
} catch (error) {
|
|
156
|
+
setMessage(error?.message || "N\xE3o foi poss\xEDvel concluir o convite.");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return /* @__PURE__ */ jsx(Flex, { minH: "100vh", align: "center", justify: "center", bg: "#F5F8FA", px: 4, children: /* @__PURE__ */ jsx(Box, { width: "100%", maxW: "480px", bg: "white", borderWidth: "1px", borderRadius: "18px", p: { base: 6, md: 9 }, children: /* @__PURE__ */ jsxs(VStack, { align: "stretch", gap: 5, children: [
|
|
160
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
161
|
+
/* @__PURE__ */ jsx(Text, { color: "#39BFED", fontSize: "xs", fontWeight: "700", textTransform: "uppercase", children: "Central Oon" }),
|
|
162
|
+
/* @__PURE__ */ jsx(Heading, { mt: 2, size: "lg", children: "Aceitar convite" })
|
|
163
|
+
] }),
|
|
164
|
+
loading ? /* @__PURE__ */ jsx(Text, { children: "Validando convite\u2026" }) : null,
|
|
165
|
+
invitation && !done ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
166
|
+
/* @__PURE__ */ jsxs(Box, { bg: "gray.50", borderRadius: "md", p: 4, children: [
|
|
167
|
+
/* @__PURE__ */ jsx(Text, { fontWeight: "700", children: invitation.name || invitation.email }),
|
|
168
|
+
/* @__PURE__ */ jsx(Text, { fontSize: "sm", children: invitation.email }),
|
|
169
|
+
/* @__PURE__ */ jsxs(Text, { fontSize: "sm", color: "gray.600", children: [
|
|
170
|
+
"Perfil: ",
|
|
171
|
+
invitation.roleName || invitation.roleCode
|
|
172
|
+
] })
|
|
173
|
+
] }),
|
|
174
|
+
/* @__PURE__ */ jsx("form", { onSubmit: (event) => void submit(event), children: /* @__PURE__ */ jsxs(VStack, { align: "stretch", gap: 4, children: [
|
|
175
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
176
|
+
/* @__PURE__ */ jsx(Text, { mb: 1, fontSize: "sm", fontWeight: "600", children: "Nova senha" }),
|
|
177
|
+
/* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "new-password", value: password, onChange: (event) => setPassword(event.target.value), required: true })
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
180
|
+
/* @__PURE__ */ jsx(Text, { mb: 1, fontSize: "sm", fontWeight: "600", children: "Confirmar senha" }),
|
|
181
|
+
/* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "new-password", value: confirm, onChange: (event) => setConfirm(event.target.value), required: true })
|
|
182
|
+
] }),
|
|
183
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", children: "Concluir primeiro acesso" })
|
|
184
|
+
] }) })
|
|
185
|
+
] }) : null,
|
|
186
|
+
message ? /* @__PURE__ */ jsx(Box, { role: "status", bg: done ? "green.50" : "red.50", borderRadius: "md", p: 3, children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", children: message }) }) : null,
|
|
187
|
+
done ? /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsx(Link, { to: "/login", children: "Ir para o login" }) }) : null
|
|
188
|
+
] }) }) });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/security/AcceptInviteCentralPage.tsx
|
|
192
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
193
|
+
function AcceptInviteCentralPage() {
|
|
194
|
+
const { http } = useOonApi();
|
|
195
|
+
const [centralName, setCentralName] = useState2("Central Oon");
|
|
196
|
+
useEffect2(() => {
|
|
197
|
+
let active = true;
|
|
198
|
+
http.get("/core/rbac").then((response) => {
|
|
199
|
+
if (active && response.data.central?.name) setCentralName(response.data.central.name);
|
|
200
|
+
}).catch(() => void 0);
|
|
201
|
+
return () => {
|
|
202
|
+
active = false;
|
|
203
|
+
};
|
|
204
|
+
}, [http]);
|
|
205
|
+
return /* @__PURE__ */ jsxs2(Box2, { bg: "#F5F8FA", minH: "100vh", pt: 4, children: [
|
|
206
|
+
/* @__PURE__ */ jsx2(Box2, { mx: "auto", maxW: "480px", px: 4, textAlign: "center", children: /* @__PURE__ */ jsxs2(Text2, { fontSize: "sm", fontWeight: "700", children: [
|
|
207
|
+
"Convite para: ",
|
|
208
|
+
centralName
|
|
209
|
+
] }) }),
|
|
210
|
+
/* @__PURE__ */ jsx2(AcceptInvitePage, {})
|
|
211
|
+
] });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/security/OnboardingPage.tsx
|
|
215
|
+
import { useEffect as useEffect3, useRef, useState as useState3 } from "react";
|
|
216
|
+
import { Box as Box3, Button as Button2, Flex as Flex2, Heading as Heading2, Input as Input2, Text as Text3, VStack as VStack2 } from "@chakra-ui/react";
|
|
217
|
+
import { Link as Link2, useLocation as useLocation2 } from "react-router-dom";
|
|
218
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
219
|
+
var TERMS_VERSION = "2026-08-18";
|
|
220
|
+
var PRIVACY_VERSION = "2026-08-18";
|
|
221
|
+
async function post(baseUrl, path, body) {
|
|
222
|
+
const response = await fetch(baseUrl.replace(/\/$/, "") + "/onboarding/" + path, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: { "content-type": "application/json", "x-correlation-id": crypto.randomUUID() },
|
|
225
|
+
body: JSON.stringify(body)
|
|
226
|
+
});
|
|
227
|
+
const payload = await response.json().catch(() => ({}));
|
|
228
|
+
if (!response.ok) throw new Error(payload.message || "N\xE3o foi poss\xEDvel concluir a solicita\xE7\xE3o.");
|
|
229
|
+
return payload;
|
|
230
|
+
}
|
|
231
|
+
function OnboardingPage({ baseUrl, mode }) {
|
|
232
|
+
const location = useLocation2();
|
|
233
|
+
const [form, setForm] = useState3({ nome: "", email: "", password: "", organizationName: "", organizationDocument: "" });
|
|
234
|
+
const [accepted, setAccepted] = useState3(false);
|
|
235
|
+
const [message, setMessage] = useState3("");
|
|
236
|
+
const [busy, setBusy] = useState3(false);
|
|
237
|
+
const confirmationToken = useRef(null);
|
|
238
|
+
useEffect3(() => {
|
|
239
|
+
if (mode !== "confirm") return;
|
|
240
|
+
const token = new URLSearchParams(location.search).get("token");
|
|
241
|
+
if (!token) return setMessage("Link de confirma\xE7\xE3o inv\xE1lido.");
|
|
242
|
+
if (confirmationToken.current === token) return;
|
|
243
|
+
confirmationToken.current = token;
|
|
244
|
+
setBusy(true);
|
|
245
|
+
post(baseUrl, "confirm-email", { token }).then(() => setMessage("E-mail confirmado. Seu acesso ao Meus Apps e Workspace est\xE1 pronto.")).catch((error) => setMessage(error.message)).finally(() => setBusy(false));
|
|
246
|
+
}, [baseUrl, location.search, mode]);
|
|
247
|
+
async function submit(event) {
|
|
248
|
+
event.preventDefault();
|
|
249
|
+
setBusy(true);
|
|
250
|
+
setMessage("");
|
|
251
|
+
try {
|
|
252
|
+
const result = await post(baseUrl, "register", {
|
|
253
|
+
...form,
|
|
254
|
+
termsAccepted: accepted,
|
|
255
|
+
termsVersion: TERMS_VERSION,
|
|
256
|
+
privacyVersion: PRIVACY_VERSION
|
|
257
|
+
});
|
|
258
|
+
setMessage(result.message);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
setMessage(error.message);
|
|
261
|
+
} finally {
|
|
262
|
+
setBusy(false);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return /* @__PURE__ */ jsx3(Flex2, { minH: "100vh", align: "center", justify: "center", bg: "#F5F8FA", px: 4, children: /* @__PURE__ */ jsx3(Box3, { width: "100%", maxW: "520px", bg: "white", borderWidth: "1px", borderRadius: "18px", p: { base: 6, md: 9 }, children: /* @__PURE__ */ jsxs3(VStack2, { align: "stretch", gap: 5, children: [
|
|
266
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
267
|
+
/* @__PURE__ */ jsx3(Text3, { color: "#39BFED", fontSize: "xs", fontWeight: "700", textTransform: "uppercase", children: "Plataforma Oon" }),
|
|
268
|
+
/* @__PURE__ */ jsx3(Heading2, { mt: 2, size: "lg", color: "#07262E", children: mode === "register" ? "Crie seu acesso" : "Confirma\xE7\xE3o de e-mail" }),
|
|
269
|
+
/* @__PURE__ */ jsx3(Text3, { mt: 2, color: "gray.600", fontSize: "sm", children: mode === "register" ? "Comece com Meus Apps e Workspace em modo de onboarding." : "Validando seu link seguro de uso \xFAnico." })
|
|
270
|
+
] }),
|
|
271
|
+
mode === "register" ? /* @__PURE__ */ jsx3("form", { onSubmit: submit, children: /* @__PURE__ */ jsxs3(VStack2, { align: "stretch", gap: 3, children: [
|
|
272
|
+
/* @__PURE__ */ jsx3(Input2, { placeholder: "Seu nome", value: form.nome, onChange: (e) => setForm({ ...form, nome: e.target.value }), required: true }),
|
|
273
|
+
/* @__PURE__ */ jsx3(Input2, { type: "email", placeholder: "voce@empresa.com.br", value: form.email, onChange: (e) => setForm({ ...form, email: e.target.value }), required: true }),
|
|
274
|
+
/* @__PURE__ */ jsx3(Input2, { type: "password", minLength: 8, placeholder: "Senha com 8 ou mais caracteres", value: form.password, onChange: (e) => setForm({ ...form, password: e.target.value }), required: true }),
|
|
275
|
+
/* @__PURE__ */ jsx3(Input2, { placeholder: "Nome da organiza\xE7\xE3o", value: form.organizationName, onChange: (e) => setForm({ ...form, organizationName: e.target.value }), required: true }),
|
|
276
|
+
/* @__PURE__ */ jsx3(Input2, { placeholder: "CNPJ/Documento (opcional)", value: form.organizationDocument, onChange: (e) => setForm({ ...form, organizationDocument: e.target.value }) }),
|
|
277
|
+
/* @__PURE__ */ jsxs3("label", { children: [
|
|
278
|
+
/* @__PURE__ */ jsx3("input", { type: "checkbox", checked: accepted, onChange: (e) => setAccepted(e.target.checked) }),
|
|
279
|
+
" ",
|
|
280
|
+
/* @__PURE__ */ jsx3(Text3, { as: "span", fontSize: "sm", children: "Aceito os termos e a pol\xEDtica de privacidade vigentes." })
|
|
281
|
+
] }),
|
|
282
|
+
/* @__PURE__ */ jsx3(Button2, { type: "submit", bg: "#07262E", color: "white", disabled: busy || !accepted, children: busy ? "Enviando..." : "Criar acesso" })
|
|
283
|
+
] }) }) : null,
|
|
284
|
+
message ? /* @__PURE__ */ jsx3(Box3, { role: "status", bg: "blue.50", color: "blue.800", borderRadius: "10px", p: 4, children: message }) : null,
|
|
285
|
+
/* @__PURE__ */ jsx3(Text3, { fontSize: "sm", children: /* @__PURE__ */ jsx3(Link2, { to: "/login", children: "Voltar para o login" }) })
|
|
286
|
+
] }) }) });
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/app/OonProviders.tsx
|
|
290
|
+
import { useMemo } from "react";
|
|
291
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
292
|
+
|
|
293
|
+
// src/config/queryClient.ts
|
|
294
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
295
|
+
var QUERY_STALE_TIME_MS = 5 * 1e3;
|
|
296
|
+
var QUERY_REFETCH_INTERVAL_MS = 10 * 1e3;
|
|
297
|
+
function createOonQueryClient() {
|
|
298
|
+
return new QueryClient({
|
|
299
|
+
defaultOptions: {
|
|
300
|
+
queries: {
|
|
301
|
+
retry: 1,
|
|
302
|
+
staleTime: QUERY_STALE_TIME_MS,
|
|
303
|
+
refetchInterval: QUERY_REFETCH_INTERVAL_MS,
|
|
304
|
+
refetchIntervalInBackground: false,
|
|
305
|
+
refetchOnMount: "always",
|
|
306
|
+
refetchOnReconnect: "always",
|
|
307
|
+
refetchOnWindowFocus: "always"
|
|
308
|
+
},
|
|
309
|
+
mutations: {
|
|
310
|
+
retry: 0
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/shell/LocalRuntimeBanner.tsx
|
|
317
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
318
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
319
|
+
function LocalRuntimeBanner({ http, children }) {
|
|
320
|
+
const auth = useOonAuth();
|
|
321
|
+
const activation = useActivation();
|
|
322
|
+
const [roles, setRoles] = useState4([]);
|
|
323
|
+
const roleCode = String(auth.user?.roleCode || "");
|
|
324
|
+
useEffect4(() => {
|
|
325
|
+
if (!auth.isAuthenticated) return;
|
|
326
|
+
void http.get("/auth/local/context").then((response) => setRoles(response.data.roles || [])).catch(() => setRoles([]));
|
|
327
|
+
}, [auth.isAuthenticated, http]);
|
|
328
|
+
async function switchRole(nextRole) {
|
|
329
|
+
await http.post("/auth/local/role", { roleCode: nextRole });
|
|
330
|
+
await auth.retry();
|
|
331
|
+
}
|
|
332
|
+
const expiration = activation.details?.expiresAt ? new Intl.DateTimeFormat("pt-BR", { dateStyle: "short", timeStyle: "short" }).format(new Date(activation.details.expiresAt)) : null;
|
|
333
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
334
|
+
/* @__PURE__ */ jsxs4(
|
|
335
|
+
"div",
|
|
336
|
+
{
|
|
337
|
+
role: "status",
|
|
338
|
+
style: {
|
|
339
|
+
alignItems: "center",
|
|
340
|
+
background: "#172554",
|
|
341
|
+
color: "white",
|
|
342
|
+
display: "flex",
|
|
343
|
+
flexWrap: "wrap",
|
|
344
|
+
font: "600 13px system-ui",
|
|
345
|
+
gap: 12,
|
|
346
|
+
justifyContent: "center",
|
|
347
|
+
padding: "8px 16px"
|
|
348
|
+
},
|
|
349
|
+
children: [
|
|
350
|
+
/* @__PURE__ */ jsx4("span", { children: "Desenvolvimento local \xB7 desconectado do Ecossistema" }),
|
|
351
|
+
expiration ? /* @__PURE__ */ jsxs4("span", { children: [
|
|
352
|
+
"Expira em ",
|
|
353
|
+
expiration
|
|
354
|
+
] }) : null,
|
|
355
|
+
auth.isAuthenticated && roles.length > 1 ? /* @__PURE__ */ jsxs4("label", { children: [
|
|
356
|
+
"Perfil local:",
|
|
357
|
+
" ",
|
|
358
|
+
/* @__PURE__ */ jsx4(
|
|
359
|
+
"select",
|
|
360
|
+
{
|
|
361
|
+
"aria-label": "Perfil local",
|
|
362
|
+
value: roleCode,
|
|
363
|
+
onChange: (event) => void switchRole(event.target.value),
|
|
364
|
+
style: { color: "#111827" },
|
|
365
|
+
children: roles.map((role) => /* @__PURE__ */ jsx4("option", { value: role.code, children: role.name }, role.code))
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
] }) : null
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
),
|
|
372
|
+
children
|
|
373
|
+
] });
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/app/OonProviders.tsx
|
|
377
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
378
|
+
function OonProviders({
|
|
379
|
+
config,
|
|
380
|
+
http,
|
|
381
|
+
storage,
|
|
382
|
+
tenantStorage,
|
|
383
|
+
children
|
|
384
|
+
}) {
|
|
385
|
+
const queryClient = useMemo(() => createOonQueryClient(), []);
|
|
386
|
+
const loginUrl = config.auth?.loginUrl ?? config.api.meusAppsUrl;
|
|
387
|
+
const authenticatedContent = /* @__PURE__ */ jsx5(
|
|
388
|
+
AuthProvider,
|
|
389
|
+
{
|
|
390
|
+
http,
|
|
391
|
+
storage,
|
|
392
|
+
auth: config.auth,
|
|
393
|
+
loginUrl,
|
|
394
|
+
runtimeMode: config.runtimeMode,
|
|
395
|
+
children: config.runtimeMode === "local" ? /* @__PURE__ */ jsx5(LocalRuntimeBanner, { http, children }) : children
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
return /* @__PURE__ */ jsx5(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx5(OonThemeProvider, { config, children: /* @__PURE__ */ jsx5(ApiProvider, { http, children: /* @__PURE__ */ jsx5(ActivationProvider, { http, children: config.app.tenancyModel === "none" ? authenticatedContent : /* @__PURE__ */ jsx5(TenantProvider, { http, storage: tenantStorage, children: authenticatedContent }) }) }) }) });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/app/moduleRegistry.tsx
|
|
402
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
403
|
+
function slug(value2) {
|
|
404
|
+
return value2.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
405
|
+
}
|
|
406
|
+
function requiredPermissions(source) {
|
|
407
|
+
const values = [...source.permissions ?? []].filter(Boolean);
|
|
408
|
+
return values.length ? values : void 0;
|
|
409
|
+
}
|
|
410
|
+
function declaredAppCapabilities(config) {
|
|
411
|
+
const capabilities = config.app.capabilities;
|
|
412
|
+
return Array.isArray(capabilities) ? capabilities : void 0;
|
|
413
|
+
}
|
|
414
|
+
function supportsCapabilities(config, source) {
|
|
415
|
+
const required = (source.capabilities ?? []).filter(Boolean);
|
|
416
|
+
if (!required.length) return true;
|
|
417
|
+
const declared = declaredAppCapabilities(config);
|
|
418
|
+
if (!declared) return true;
|
|
419
|
+
return required.every((capability) => declared.includes(capability));
|
|
420
|
+
}
|
|
421
|
+
function viewId(view) {
|
|
422
|
+
if (view.id) return view.id;
|
|
423
|
+
if (view.type === "dashboard") return "dashboard";
|
|
424
|
+
return slug(view.model);
|
|
425
|
+
}
|
|
426
|
+
function viewPath(view) {
|
|
427
|
+
if (view.path) return view.path;
|
|
428
|
+
if (view.type === "dashboard") return "/";
|
|
429
|
+
return `/${viewId(view)}`;
|
|
430
|
+
}
|
|
431
|
+
function viewLabel(view) {
|
|
432
|
+
if (view.label) return view.label;
|
|
433
|
+
if (view.type === "dashboard") return "Dashboard";
|
|
434
|
+
return view.model;
|
|
435
|
+
}
|
|
436
|
+
function renderView(view, registry) {
|
|
437
|
+
switch (view.type) {
|
|
438
|
+
case "collection": {
|
|
439
|
+
const v = view;
|
|
440
|
+
return /* @__PURE__ */ jsx6(CoreCollection, { model: v.model, mode: v.mode, columns: v.columns, form: v.form, label: viewLabel(v), section: v.section, importExport: v.importExport, list: v.list, relations: v.relations, detailModal: v.detailModal, cellRenderers: registry?.cellRenderers, registry });
|
|
441
|
+
}
|
|
442
|
+
case "document": {
|
|
443
|
+
const v = view;
|
|
444
|
+
return /* @__PURE__ */ jsx6(CoreDocument, { model: v.model, label: viewLabel(v), endpoint: v.endpoint, approval: v.approval, attachments: v.attachments, display: v.display, categoryField: v.categoryField, titleField: v.titleField, descriptionField: v.descriptionField, contentField: v.contentField, statusField: v.statusField, updatedAtField: v.updatedAtField });
|
|
445
|
+
}
|
|
446
|
+
case "pipeline": {
|
|
447
|
+
const v = view;
|
|
448
|
+
return /* @__PURE__ */ jsx6(CorePipeline, { model: v.model, label: viewLabel(v), stageField: v.stageField, stages: v.stages, titleField: v.titleField, form: v.form, relations: v.relations, ticketModal: v.ticketModal, filters: v.filters, create: v.create, viewModes: v.viewModes, defaultView: v.defaultView, list: v.list, defaultActions: v.defaultActions, ticketActions: v.ticketActions, registry });
|
|
449
|
+
}
|
|
450
|
+
case "dashboard": {
|
|
451
|
+
const v = view;
|
|
452
|
+
return /* @__PURE__ */ jsx6(CoreDashboard, { label: viewLabel(v), widgets: v.widgets });
|
|
453
|
+
}
|
|
454
|
+
default:
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function viewToRoute(view, registry) {
|
|
459
|
+
return { path: viewPath(view), element: renderView(view, registry), permissions: requiredPermissions(view), capabilities: "capabilities" in view ? view.capabilities : void 0 };
|
|
460
|
+
}
|
|
461
|
+
function pageToRoute(page, registry) {
|
|
462
|
+
return { path: page.path, element: /* @__PURE__ */ jsx6(CorePage, { page, registry }), public: page.public === true, permissions: requiredPermissions(page), capabilities: page.capabilities };
|
|
463
|
+
}
|
|
464
|
+
function pageToMenu(page) {
|
|
465
|
+
if (!page.label) return null;
|
|
466
|
+
return { label: page.label, href: page.path, icon: page.icon, section: page.section, permissions: requiredPermissions(page), capabilities: page.capabilities, hidden: page.hidden, order: page.order };
|
|
467
|
+
}
|
|
468
|
+
function viewToMenu(view) {
|
|
469
|
+
const extended = view;
|
|
470
|
+
return { label: viewLabel(view), href: viewPath(view), icon: view.icon, section: view.section, permissions: requiredPermissions(view), capabilities: "capabilities" in view ? view.capabilities : void 0, hidden: extended.hidden, order: extended.order };
|
|
471
|
+
}
|
|
472
|
+
function resolveRegistry(config) {
|
|
473
|
+
const routes = [];
|
|
474
|
+
const menu = [];
|
|
475
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
476
|
+
const componentRegistry = config.registry ?? config.ui?.registry;
|
|
477
|
+
const pushRoute = (route) => {
|
|
478
|
+
if (!supportsCapabilities(config, route) || seenPaths.has(route.path)) return;
|
|
479
|
+
seenPaths.add(route.path);
|
|
480
|
+
routes.push({ ...route, permissions: requiredPermissions(route) });
|
|
481
|
+
};
|
|
482
|
+
const pushMenu = (item) => {
|
|
483
|
+
if (!supportsCapabilities(config, item)) return;
|
|
484
|
+
menu.push({ ...item, permissions: requiredPermissions(item) });
|
|
485
|
+
};
|
|
486
|
+
for (const mod of config.modules ?? []) {
|
|
487
|
+
for (const route of mod.routes ?? []) pushRoute(route);
|
|
488
|
+
for (const item of mod.menu ?? []) pushMenu(item);
|
|
489
|
+
for (const view of mod.views ?? []) {
|
|
490
|
+
pushRoute(viewToRoute(view, componentRegistry));
|
|
491
|
+
pushMenu(viewToMenu(view));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (["root-central", "member-central"].includes(String(config.app.kind || ""))) {
|
|
495
|
+
pushRoute({ path: "/usuarios-acessos", element: /* @__PURE__ */ jsx6(CoreUsersAccess, {}), permissions: ["users.read"] });
|
|
496
|
+
pushMenu({ label: "Usu\xE1rios e Acessos", href: "/usuarios-acessos", section: "Configura\xE7\xF5es", order: 900, permissions: ["users.read"] });
|
|
497
|
+
}
|
|
498
|
+
if (declaredAppCapabilities(config)?.includes("core.transactional-email")) {
|
|
499
|
+
pushRoute({ path: "/configuracoes/email-transacional", element: /* @__PURE__ */ jsx6(CoreTransactionalEmail, {}), permissions: ["transactional-email.config.read"], capabilities: ["core.transactional-email"] });
|
|
500
|
+
pushMenu({ label: "E-mail transacional", href: "/configuracoes/email-transacional", section: "Configura\xE7\xF5es", order: 910, permissions: ["transactional-email.config.read"], capabilities: ["core.transactional-email"] });
|
|
501
|
+
}
|
|
502
|
+
const navMode = config.ui?.navigation?.mode ?? "auto";
|
|
503
|
+
const includeAuto = navMode === "auto" || navMode === "mixed";
|
|
504
|
+
const includeManual = navMode === "manual" || navMode === "mixed";
|
|
505
|
+
for (const view of config.ui?.views ?? []) {
|
|
506
|
+
pushRoute(viewToRoute(view, componentRegistry));
|
|
507
|
+
if (includeAuto) pushMenu(viewToMenu(view));
|
|
508
|
+
}
|
|
509
|
+
for (const page of config.ui?.pages ?? []) {
|
|
510
|
+
pushRoute(pageToRoute(page, componentRegistry));
|
|
511
|
+
if (includeAuto) {
|
|
512
|
+
const item = pageToMenu(page);
|
|
513
|
+
if (item) pushMenu(item);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (includeManual) for (const item of config.ui?.navigation?.items ?? []) pushMenu(item);
|
|
517
|
+
menu.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
518
|
+
return { routes, menu };
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/app/createOonRouter.tsx
|
|
522
|
+
import {
|
|
523
|
+
Navigate as Navigate5,
|
|
524
|
+
createBrowserRouter
|
|
525
|
+
} from "react-router-dom";
|
|
526
|
+
|
|
527
|
+
// src/shell/DefaultShell.tsx
|
|
528
|
+
import { Box as Box5, Button as Button3, Flex as Flex4, Spacer, Text as Text5 } from "@chakra-ui/react";
|
|
529
|
+
import { Outlet, useNavigate } from "react-router-dom";
|
|
530
|
+
|
|
531
|
+
// src/shell/Sidebar.tsx
|
|
532
|
+
import { Box as Box4, Flex as Flex3, Image, Spinner, Stack, Text as Text4 } from "@chakra-ui/react";
|
|
533
|
+
import { useState as useState5 } from "react";
|
|
534
|
+
import { NavLink } from "react-router-dom";
|
|
535
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
536
|
+
function groupBySection(items) {
|
|
537
|
+
const groups = /* @__PURE__ */ new Map();
|
|
538
|
+
for (const item of items) {
|
|
539
|
+
const section = item.section ?? "Geral";
|
|
540
|
+
if (!groups.has(section)) groups.set(section, []);
|
|
541
|
+
groups.get(section).push(item);
|
|
542
|
+
}
|
|
543
|
+
return Array.from(groups.entries());
|
|
544
|
+
}
|
|
545
|
+
function launchAppCode(action) {
|
|
546
|
+
const match = /^launch-app:([a-z][a-z0-9-]{2,62})$/.exec(String(action || ""));
|
|
547
|
+
return match?.[1] || "";
|
|
548
|
+
}
|
|
549
|
+
function errorMessage(error) {
|
|
550
|
+
const value2 = error;
|
|
551
|
+
return value2?.response?.data?.error?.message || value2?.response?.data?.message || value2?.message || "N\xE3o foi poss\xEDvel abrir o App.";
|
|
552
|
+
}
|
|
553
|
+
function PlatformAppMenuItem({ item, appCode }) {
|
|
554
|
+
const { http } = useOonApi();
|
|
555
|
+
const [opening, setOpening] = useState5(false);
|
|
556
|
+
const [launchError, setLaunchError] = useState5("");
|
|
557
|
+
async function launch() {
|
|
558
|
+
if (opening) return;
|
|
559
|
+
const popup = window.open("about:blank", "_blank");
|
|
560
|
+
if (!popup) {
|
|
561
|
+
setLaunchError("Permita pop-ups para abrir o App em uma nova aba.");
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
popup.opener = null;
|
|
565
|
+
setOpening(true);
|
|
566
|
+
setLaunchError("");
|
|
567
|
+
try {
|
|
568
|
+
const home = (await http.get("/portal/home")).data;
|
|
569
|
+
const target = home.platformApps.find((candidate) => candidate.app.codigo === appCode);
|
|
570
|
+
if (!target || target.action !== "open") {
|
|
571
|
+
throw new Error(`${target?.app.nome || item.label} ainda n\xE3o est\xE1 dispon\xEDvel.`);
|
|
572
|
+
}
|
|
573
|
+
const response = await http.post("/portal/launch", {
|
|
574
|
+
appCode,
|
|
575
|
+
environment: target.deployment.environment
|
|
576
|
+
});
|
|
577
|
+
if (!response.data.openUrl) {
|
|
578
|
+
throw new Error("A autoriza\xE7\xE3o de abertura n\xE3o retornou uma URL.");
|
|
579
|
+
}
|
|
580
|
+
popup.location.replace(response.data.openUrl);
|
|
581
|
+
} catch (error) {
|
|
582
|
+
popup.close();
|
|
583
|
+
setLaunchError(errorMessage(error));
|
|
584
|
+
} finally {
|
|
585
|
+
setOpening(false);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return /* @__PURE__ */ jsxs5(Box4, { width: "100%", children: [
|
|
589
|
+
/* @__PURE__ */ jsxs5(
|
|
590
|
+
Flex3,
|
|
591
|
+
{
|
|
592
|
+
as: "button",
|
|
593
|
+
width: "100%",
|
|
594
|
+
align: "center",
|
|
595
|
+
justify: { base: "center", lg: "flex-start" },
|
|
596
|
+
gap: 3,
|
|
597
|
+
px: { base: 2, lg: 3 },
|
|
598
|
+
py: "10px",
|
|
599
|
+
borderRadius: "9px",
|
|
600
|
+
fontSize: "13px",
|
|
601
|
+
fontWeight: "500",
|
|
602
|
+
color: "#46545C",
|
|
603
|
+
cursor: opening ? "wait" : "pointer",
|
|
604
|
+
"aria-disabled": opening,
|
|
605
|
+
pointerEvents: opening ? "none" : "auto",
|
|
606
|
+
title: item.label,
|
|
607
|
+
"aria-label": `Abrir ${item.label} em uma nova aba`,
|
|
608
|
+
onClick: () => void launch(),
|
|
609
|
+
_hover: { bg: "#F8F9FA", color: "brand.700" },
|
|
610
|
+
children: [
|
|
611
|
+
opening ? /* @__PURE__ */ jsx7(Spinner, { size: "xs" }) : /* @__PURE__ */ jsx7(Box4, { w: "7px", h: "7px", borderRadius: "full", bg: "gray.300" }),
|
|
612
|
+
/* @__PURE__ */ jsx7(Text4, { display: { base: "none", lg: "block" }, lineClamp: 1, children: item.label })
|
|
613
|
+
]
|
|
614
|
+
}
|
|
615
|
+
),
|
|
616
|
+
launchError ? /* @__PURE__ */ jsx7(Text4, { role: "alert", px: 3, py: 1, fontSize: "10px", color: "red.600", children: launchError }) : null
|
|
617
|
+
] });
|
|
618
|
+
}
|
|
619
|
+
function Sidebar({ app, menu, registry }) {
|
|
620
|
+
const { hasPermission } = useOonAuth();
|
|
621
|
+
const visible = menu.filter((item) => !item.hidden && hasPermission(item.permissions));
|
|
622
|
+
return /* @__PURE__ */ jsxs5(
|
|
623
|
+
Box4,
|
|
624
|
+
{
|
|
625
|
+
as: "nav",
|
|
626
|
+
w: { base: "76px", lg: "248px" },
|
|
627
|
+
minH: "100vh",
|
|
628
|
+
borderRightWidth: "1px",
|
|
629
|
+
borderColor: "#E8ECEF",
|
|
630
|
+
px: { base: 2, lg: 3 },
|
|
631
|
+
py: 4,
|
|
632
|
+
bg: "white",
|
|
633
|
+
position: "sticky",
|
|
634
|
+
top: 0,
|
|
635
|
+
alignSelf: "flex-start",
|
|
636
|
+
overflowY: "auto",
|
|
637
|
+
children: [
|
|
638
|
+
/* @__PURE__ */ jsxs5(Flex3, { align: "center", gap: 3, mb: 6, px: { base: 1, lg: 2 }, minH: "42px", children: [
|
|
639
|
+
app.logo ? /* @__PURE__ */ jsx7(Image, { src: app.logo, alt: app.name, maxH: "36px", maxW: { base: "44px", lg: "150px" }, objectFit: "contain" }) : /* @__PURE__ */ jsx7(
|
|
640
|
+
Flex3,
|
|
641
|
+
{
|
|
642
|
+
w: "38px",
|
|
643
|
+
h: "38px",
|
|
644
|
+
flex: "0 0 auto",
|
|
645
|
+
borderRadius: "12px",
|
|
646
|
+
align: "center",
|
|
647
|
+
justify: "center",
|
|
648
|
+
bg: "brand.500",
|
|
649
|
+
color: "white",
|
|
650
|
+
fontWeight: "700",
|
|
651
|
+
fontSize: "lg",
|
|
652
|
+
children: "O"
|
|
653
|
+
}
|
|
654
|
+
),
|
|
655
|
+
/* @__PURE__ */ jsxs5(Box4, { display: { base: "none", lg: "block" }, minW: 0, children: [
|
|
656
|
+
/* @__PURE__ */ jsx7(Text4, { fontWeight: "700", fontSize: "sm", color: "#07262E", lineClamp: 1, children: app.name }),
|
|
657
|
+
/* @__PURE__ */ jsx7(Text4, { fontSize: "10px", color: "gray.500", textTransform: "uppercase", letterSpacing: "0.08em", children: "Opera\xE7\xE3o digital" })
|
|
658
|
+
] })
|
|
659
|
+
] }),
|
|
660
|
+
/* @__PURE__ */ jsx7(Stack, { gap: 5, children: groupBySection(visible).map(([section, items]) => /* @__PURE__ */ jsxs5(Stack, { gap: 1, children: [
|
|
661
|
+
/* @__PURE__ */ jsx7(
|
|
662
|
+
Text4,
|
|
663
|
+
{
|
|
664
|
+
display: { base: "none", lg: "block" },
|
|
665
|
+
fontSize: "10px",
|
|
666
|
+
fontWeight: "600",
|
|
667
|
+
color: "gray.400",
|
|
668
|
+
textTransform: "uppercase",
|
|
669
|
+
letterSpacing: "0.08em",
|
|
670
|
+
px: 3,
|
|
671
|
+
mb: 1,
|
|
672
|
+
children: section
|
|
673
|
+
}
|
|
674
|
+
),
|
|
675
|
+
items.map((item) => {
|
|
676
|
+
const CustomMenuItem = resolveComponent(registry, item.renderer, "menuRenderers");
|
|
677
|
+
if (CustomMenuItem) return /* @__PURE__ */ jsx7(CustomMenuItem, { item }, item.href);
|
|
678
|
+
const appCode = launchAppCode(item.action);
|
|
679
|
+
if (appCode) return /* @__PURE__ */ jsx7(PlatformAppMenuItem, { item, appCode }, item.href);
|
|
680
|
+
return /* @__PURE__ */ jsx7(NavLink, { to: item.href, end: item.href === "/", title: item.label, children: ({ isActive }) => /* @__PURE__ */ jsxs5(
|
|
681
|
+
Flex3,
|
|
682
|
+
{
|
|
683
|
+
align: "center",
|
|
684
|
+
justify: { base: "center", lg: "flex-start" },
|
|
685
|
+
gap: 3,
|
|
686
|
+
px: { base: 2, lg: 3 },
|
|
687
|
+
py: "10px",
|
|
688
|
+
borderRadius: "9px",
|
|
689
|
+
fontSize: "13px",
|
|
690
|
+
fontWeight: isActive ? "600" : "500",
|
|
691
|
+
bg: isActive ? "brand.50" : "transparent",
|
|
692
|
+
color: isActive ? "brand.700" : "#46545C",
|
|
693
|
+
borderLeftWidth: "3px",
|
|
694
|
+
borderLeftColor: isActive ? "brand.500" : "transparent",
|
|
695
|
+
transition: "all 0.15s ease",
|
|
696
|
+
_hover: { bg: isActive ? "brand.50" : "#F8F9FA", color: "brand.700" },
|
|
697
|
+
children: [
|
|
698
|
+
item.icon ? /* @__PURE__ */ jsx7(Box4, { flex: "0 0 auto", children: item.icon }) : /* @__PURE__ */ jsx7(Box4, { w: "7px", h: "7px", borderRadius: "full", bg: isActive ? "brand.500" : "gray.300" }),
|
|
699
|
+
/* @__PURE__ */ jsx7(Text4, { display: { base: "none", lg: "block" }, lineClamp: 1, children: item.label })
|
|
700
|
+
]
|
|
701
|
+
}
|
|
702
|
+
) }, item.href);
|
|
703
|
+
})
|
|
704
|
+
] }, section)) })
|
|
705
|
+
]
|
|
706
|
+
}
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// package.json
|
|
711
|
+
var package_default = {
|
|
712
|
+
name: "@oondemand/oon-core-front",
|
|
713
|
+
version: "0.6.1",
|
|
714
|
+
repository: {
|
|
715
|
+
type: "git",
|
|
716
|
+
url: "git+https://github.com/oondemand/oon-platform.git",
|
|
717
|
+
directory: "packages/oonCore-front"
|
|
718
|
+
},
|
|
719
|
+
description: "Core de frontend para Centrais Oon: shell, providers, roteamento por metadata, SDK REST do /core/*, RBAC e componentes Core* (Cole\xE7\xE3o, Documento, Esteira, Multi-moedas, Assistente, Dashboard).",
|
|
720
|
+
type: "module",
|
|
721
|
+
main: "dist/index.js",
|
|
722
|
+
module: "dist/index.js",
|
|
723
|
+
types: "dist/index.d.ts",
|
|
724
|
+
exports: {
|
|
725
|
+
".": {
|
|
726
|
+
types: "./dist/index.d.ts",
|
|
727
|
+
import: "./dist/index.js"
|
|
728
|
+
},
|
|
729
|
+
"./ui": {
|
|
730
|
+
types: "./dist/ui.d.ts",
|
|
731
|
+
import: "./dist/ui.js"
|
|
732
|
+
},
|
|
733
|
+
"./routing": {
|
|
734
|
+
types: "./dist/routing.d.ts",
|
|
735
|
+
import: "./dist/routing.js"
|
|
736
|
+
},
|
|
737
|
+
"./theme": {
|
|
738
|
+
types: "./dist/theme.d.ts",
|
|
739
|
+
import: "./dist/theme.js"
|
|
740
|
+
},
|
|
741
|
+
"./hooks": {
|
|
742
|
+
types: "./dist/hooks.d.ts",
|
|
743
|
+
import: "./dist/hooks.js"
|
|
744
|
+
},
|
|
745
|
+
"./testing": {
|
|
746
|
+
types: "./dist/testing.d.ts",
|
|
747
|
+
import: "./dist/testing.js"
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
bin: {
|
|
751
|
+
"oonCore-front": "bin/cli.js"
|
|
752
|
+
},
|
|
753
|
+
files: [
|
|
754
|
+
"bin",
|
|
755
|
+
"dist",
|
|
756
|
+
"README.md"
|
|
757
|
+
],
|
|
758
|
+
scripts: {
|
|
759
|
+
build: "tsup",
|
|
760
|
+
prepublishOnly: "npm run build",
|
|
761
|
+
typecheck: "tsc --noEmit",
|
|
762
|
+
test: "vitest run"
|
|
763
|
+
},
|
|
764
|
+
engines: {
|
|
765
|
+
node: ">=18"
|
|
766
|
+
},
|
|
767
|
+
keywords: [
|
|
768
|
+
"oondemand",
|
|
769
|
+
"ooncore",
|
|
770
|
+
"central",
|
|
771
|
+
"frontend",
|
|
772
|
+
"metadata-driven",
|
|
773
|
+
"react",
|
|
774
|
+
"vite"
|
|
775
|
+
],
|
|
776
|
+
license: "SEE LICENSE IN LICENSE",
|
|
777
|
+
publishConfig: {
|
|
778
|
+
access: "public"
|
|
779
|
+
},
|
|
780
|
+
dependencies: {
|
|
781
|
+
axios: "^1.7.7"
|
|
782
|
+
},
|
|
783
|
+
peerDependencies: {
|
|
784
|
+
"@chakra-ui/react": "^3.0.0",
|
|
785
|
+
"@tanstack/react-query": "^5.0.0",
|
|
786
|
+
"@tanstack/react-table": "^8.0.0",
|
|
787
|
+
react: "^18.0.0",
|
|
788
|
+
"react-dom": "^18.0.0",
|
|
789
|
+
"react-router-dom": "^6.0.0"
|
|
790
|
+
},
|
|
791
|
+
devDependencies: {
|
|
792
|
+
"@testing-library/dom": "^10.4.1",
|
|
793
|
+
"@testing-library/react": "^16.3.2",
|
|
794
|
+
"@types/react": "^18.3.18",
|
|
795
|
+
"@types/react-dom": "^18.3.5",
|
|
796
|
+
"happy-dom": "^20.10.2",
|
|
797
|
+
tsup: "^8.5.1",
|
|
798
|
+
typescript: "^5.5.0",
|
|
799
|
+
vitest: "^2.0.0"
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// src/runtime/versionInfo.ts
|
|
804
|
+
function value(...candidates) {
|
|
805
|
+
return candidates.find((candidate) => typeof candidate === "string" && candidate.trim())?.trim();
|
|
806
|
+
}
|
|
807
|
+
function browserBuildEnvironment() {
|
|
808
|
+
return import.meta.env ?? {};
|
|
809
|
+
}
|
|
810
|
+
function resolveRuntimeVersionInfo(app, metadata, environment = browserBuildEnvironment()) {
|
|
811
|
+
const extended = metadata;
|
|
812
|
+
const runtime = extended?.runtime;
|
|
813
|
+
const deployment = runtime?.deployment;
|
|
814
|
+
return {
|
|
815
|
+
centralVersion: value(
|
|
816
|
+
environment.VITE_OON_CENTRAL_VERSION,
|
|
817
|
+
app.version,
|
|
818
|
+
runtime?.central?.version
|
|
819
|
+
),
|
|
820
|
+
coreFrontVersion: package_default.version,
|
|
821
|
+
coreBackVersion: value(runtime?.core?.backendVersion, metadata?.service?.version),
|
|
822
|
+
updatedAt: value(
|
|
823
|
+
environment.VITE_OON_BUILD_TIME,
|
|
824
|
+
deployment?.builtAt,
|
|
825
|
+
deployment?.startedAt
|
|
826
|
+
),
|
|
827
|
+
startedAt: deployment?.startedAt,
|
|
828
|
+
commit: value(environment.VITE_OON_BUILD_COMMIT, deployment?.commit),
|
|
829
|
+
releaseId: value(environment.VITE_OON_RELEASE_ID, deployment?.releaseId),
|
|
830
|
+
environment: value(environment.VITE_OON_ENVIRONMENT, deployment?.environment)
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function formatRuntimeDate(valueToFormat) {
|
|
834
|
+
if (!valueToFormat) return void 0;
|
|
835
|
+
const date = new Date(valueToFormat);
|
|
836
|
+
if (Number.isNaN(date.getTime())) return valueToFormat;
|
|
837
|
+
return new Intl.DateTimeFormat("pt-BR", {
|
|
838
|
+
day: "2-digit",
|
|
839
|
+
month: "2-digit",
|
|
840
|
+
year: "numeric",
|
|
841
|
+
hour: "2-digit",
|
|
842
|
+
minute: "2-digit",
|
|
843
|
+
hour12: false,
|
|
844
|
+
timeZone: "UTC"
|
|
845
|
+
}).format(date);
|
|
846
|
+
}
|
|
847
|
+
function shortCommit(commit) {
|
|
848
|
+
if (!commit) return void 0;
|
|
849
|
+
return commit.length > 8 ? commit.slice(0, 8) : commit;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// src/shell/DefaultShell.tsx
|
|
853
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
854
|
+
function SettingsIcon() {
|
|
855
|
+
return /* @__PURE__ */ jsxs6(
|
|
856
|
+
"svg",
|
|
857
|
+
{
|
|
858
|
+
"aria-hidden": "true",
|
|
859
|
+
width: "18",
|
|
860
|
+
height: "18",
|
|
861
|
+
viewBox: "0 0 24 24",
|
|
862
|
+
fill: "none",
|
|
863
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ jsx8(
|
|
866
|
+
"path",
|
|
867
|
+
{
|
|
868
|
+
d: "M12 15.25a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5Z",
|
|
869
|
+
stroke: "currentColor",
|
|
870
|
+
strokeWidth: "1.8"
|
|
871
|
+
}
|
|
872
|
+
),
|
|
873
|
+
/* @__PURE__ */ jsx8(
|
|
874
|
+
"path",
|
|
875
|
+
{
|
|
876
|
+
d: "M19.43 12.98c.04-.32.07-.65.07-.98s-.03-.66-.08-.98l2.12-1.66-2-3.46-2.5 1a7.64 7.64 0 0 0-1.7-.98L15 3.25h-4l-.35 2.67c-.61.25-1.18.58-1.7.98l-2.5-1-2 3.46 2.12 1.66c-.04.32-.07.65-.07.98s.03.66.08.98l-2.12 1.66 2 3.46 2.5-1c.52.4 1.09.73 1.7.98l.34 2.67h4l.35-2.67a7.64 7.64 0 0 0 1.7-.98l2.5 1 2-3.46-2.12-1.66Z",
|
|
877
|
+
stroke: "currentColor",
|
|
878
|
+
strokeWidth: "1.8",
|
|
879
|
+
strokeLinecap: "round",
|
|
880
|
+
strokeLinejoin: "round"
|
|
881
|
+
}
|
|
882
|
+
)
|
|
883
|
+
]
|
|
884
|
+
}
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
function resolveSettingsHref(menu) {
|
|
888
|
+
const explicit = menu.find((item) => item.action === "settings");
|
|
889
|
+
if (explicit) return explicit.href;
|
|
890
|
+
const settingsPage = menu.find((item) => item.href === "/configuracoes");
|
|
891
|
+
if (settingsPage) return settingsPage.href;
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
function CoreTopbar({ app, menu }) {
|
|
895
|
+
const { user, logout, hasPermission } = useOonAuth();
|
|
896
|
+
const navigate = useNavigate();
|
|
897
|
+
const metadataQuery = useCoreMetadata();
|
|
898
|
+
const runtime = resolveRuntimeVersionInfo(app, metadataQuery.data);
|
|
899
|
+
const centralVersion = runtime.centralVersion ?? "n\xE3o informada";
|
|
900
|
+
const backendVersion = metadataQuery.isLoading ? "carregando" : runtime.coreBackVersion ?? "indispon\xEDvel";
|
|
901
|
+
const updatedAt = formatRuntimeDate(runtime.updatedAt);
|
|
902
|
+
const commit = shortCommit(runtime.commit);
|
|
903
|
+
const publicationParts = [
|
|
904
|
+
updatedAt ? `Atualizado ${updatedAt}` : void 0,
|
|
905
|
+
commit ? `commit ${commit}` : void 0,
|
|
906
|
+
runtime.releaseId ? `release ${runtime.releaseId}` : void 0
|
|
907
|
+
].filter(Boolean);
|
|
908
|
+
const details = [
|
|
909
|
+
`Central ${centralVersion}`,
|
|
910
|
+
`OonCore front ${runtime.coreFrontVersion}`,
|
|
911
|
+
`OonCore back ${backendVersion}`,
|
|
912
|
+
runtime.environment ? `Ambiente ${runtime.environment}` : void 0,
|
|
913
|
+
runtime.commit ? `Commit ${runtime.commit}` : void 0,
|
|
914
|
+
runtime.releaseId ? `Release ${runtime.releaseId}` : void 0,
|
|
915
|
+
runtime.startedAt ? `Backend iniciado ${formatRuntimeDate(runtime.startedAt)}` : void 0
|
|
916
|
+
].filter(Boolean).join(" \xB7 ");
|
|
917
|
+
const settingsHref = resolveSettingsHref(
|
|
918
|
+
menu.filter((item) => hasPermission(item.permissions))
|
|
919
|
+
);
|
|
920
|
+
const tenant = user?.tenant;
|
|
921
|
+
const tenantLabel = String(tenant?.nome || tenant?.slug || "").trim();
|
|
922
|
+
return /* @__PURE__ */ jsxs6(
|
|
923
|
+
Flex4,
|
|
924
|
+
{
|
|
925
|
+
as: "header",
|
|
926
|
+
align: "center",
|
|
927
|
+
minH: "64px",
|
|
928
|
+
px: { base: 4, lg: 6 },
|
|
929
|
+
py: 3,
|
|
930
|
+
borderBottomWidth: "1px",
|
|
931
|
+
borderColor: "border.default",
|
|
932
|
+
bg: "surface.panel",
|
|
933
|
+
backdropFilter: "blur(10px)",
|
|
934
|
+
position: "sticky",
|
|
935
|
+
top: 0,
|
|
936
|
+
zIndex: 10,
|
|
937
|
+
children: [
|
|
938
|
+
/* @__PURE__ */ jsxs6(Box5, { minW: 0, title: details, children: [
|
|
939
|
+
/* @__PURE__ */ jsx8(
|
|
940
|
+
Text5,
|
|
941
|
+
{
|
|
942
|
+
fontSize: "xs",
|
|
943
|
+
fontWeight: "600",
|
|
944
|
+
color: "brand.500",
|
|
945
|
+
textTransform: "uppercase",
|
|
946
|
+
letterSpacing: "0.08em",
|
|
947
|
+
children: "Central Oon"
|
|
948
|
+
}
|
|
949
|
+
),
|
|
950
|
+
/* @__PURE__ */ jsx8(Text5, { fontSize: "sm", fontWeight: "600", color: "text.primary", children: app.title ?? app.name }),
|
|
951
|
+
tenantLabel ? /* @__PURE__ */ jsxs6(Text5, { fontSize: "xs", fontWeight: "600", color: "brand.fg", lineHeight: "1.4", children: [
|
|
952
|
+
"Tenant: ",
|
|
953
|
+
tenantLabel
|
|
954
|
+
] }) : null,
|
|
955
|
+
/* @__PURE__ */ jsxs6(Text5, { fontSize: "10px", color: "text.muted", lineHeight: "1.35", children: [
|
|
956
|
+
"Central ",
|
|
957
|
+
centralVersion,
|
|
958
|
+
" \xB7 Front ",
|
|
959
|
+
runtime.coreFrontVersion,
|
|
960
|
+
" \xB7 Back ",
|
|
961
|
+
backendVersion
|
|
962
|
+
] }),
|
|
963
|
+
publicationParts.length ? /* @__PURE__ */ jsx8(Text5, { fontSize: "10px", color: "text.muted", lineHeight: "1.35", children: publicationParts.join(" \xB7 ") }) : null
|
|
964
|
+
] }),
|
|
965
|
+
/* @__PURE__ */ jsx8(Spacer, {}),
|
|
966
|
+
user ? /* @__PURE__ */ jsxs6(Flex4, { align: "center", gap: 3, children: [
|
|
967
|
+
/* @__PURE__ */ jsxs6(Box5, { textAlign: "right", display: { base: "none", md: "block" }, children: [
|
|
968
|
+
/* @__PURE__ */ jsx8(Text5, { fontSize: "sm", fontWeight: "600", color: "text.primary", children: user.nome }),
|
|
969
|
+
user.email ? /* @__PURE__ */ jsx8(Text5, { fontSize: "xs", color: "text.muted", children: user.email }) : null
|
|
970
|
+
] }),
|
|
971
|
+
settingsHref ? /* @__PURE__ */ jsx8(
|
|
972
|
+
Button3,
|
|
973
|
+
{
|
|
974
|
+
size: "sm",
|
|
975
|
+
variant: "outline",
|
|
976
|
+
colorPalette: "brand",
|
|
977
|
+
borderRadius: "8px",
|
|
978
|
+
minW: "36px",
|
|
979
|
+
px: 2,
|
|
980
|
+
"aria-label": "Abrir configura\xE7\xF5es",
|
|
981
|
+
title: "Configura\xE7\xF5es",
|
|
982
|
+
onClick: () => navigate(settingsHref),
|
|
983
|
+
children: /* @__PURE__ */ jsx8(SettingsIcon, {})
|
|
984
|
+
}
|
|
985
|
+
) : null,
|
|
986
|
+
/* @__PURE__ */ jsx8(
|
|
987
|
+
Button3,
|
|
988
|
+
{
|
|
989
|
+
size: "sm",
|
|
990
|
+
variant: "outline",
|
|
991
|
+
colorPalette: "brand",
|
|
992
|
+
borderRadius: "8px",
|
|
993
|
+
onClick: logout,
|
|
994
|
+
children: "Sair"
|
|
995
|
+
}
|
|
996
|
+
)
|
|
997
|
+
] }) : null
|
|
998
|
+
]
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
function Slot({
|
|
1003
|
+
name,
|
|
1004
|
+
slot,
|
|
1005
|
+
registry,
|
|
1006
|
+
app,
|
|
1007
|
+
menu
|
|
1008
|
+
}) {
|
|
1009
|
+
if (slot === "none") return null;
|
|
1010
|
+
if (slot?.startsWith("custom:")) {
|
|
1011
|
+
const Component = resolveComponent(registry, slot, "layoutSlots");
|
|
1012
|
+
return Component ? /* @__PURE__ */ jsx8(Component, { app, menu }) : /* @__PURE__ */ jsx8(MissingRenderer, { renderer: slot });
|
|
1013
|
+
}
|
|
1014
|
+
if (name === "sidebar") return /* @__PURE__ */ jsx8(Sidebar, { app, menu, registry });
|
|
1015
|
+
if (name === "topbar") return /* @__PURE__ */ jsx8(CoreTopbar, { app, menu });
|
|
1016
|
+
if (name === "footer" && slot === "core") {
|
|
1017
|
+
return /* @__PURE__ */ jsx8(Box5, { as: "footer", px: 6, py: 4, color: "gray.500", fontSize: "xs", borderTopWidth: "1px", children: "OonCore" });
|
|
1018
|
+
}
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
function DefaultShell({
|
|
1022
|
+
app,
|
|
1023
|
+
menu,
|
|
1024
|
+
layout,
|
|
1025
|
+
registry
|
|
1026
|
+
}) {
|
|
1027
|
+
const portal = layout?.shell === "portal" || layout?.shell === "content-only";
|
|
1028
|
+
const mainPadding = portal ? { base: 4, lg: 10 } : { base: 4, lg: 6 };
|
|
1029
|
+
return /* @__PURE__ */ jsxs6(Flex4, { align: "stretch", minH: "100vh", bg: portal ? "surface.panel" : "surface.canvas", children: [
|
|
1030
|
+
/* @__PURE__ */ jsx8(
|
|
1031
|
+
Slot,
|
|
1032
|
+
{
|
|
1033
|
+
name: "sidebar",
|
|
1034
|
+
slot: layout?.sidebar ?? "core",
|
|
1035
|
+
registry,
|
|
1036
|
+
app,
|
|
1037
|
+
menu
|
|
1038
|
+
}
|
|
1039
|
+
),
|
|
1040
|
+
/* @__PURE__ */ jsxs6(Flex4, { direction: "column", flex: "1", minW: 0, children: [
|
|
1041
|
+
/* @__PURE__ */ jsx8(
|
|
1042
|
+
Slot,
|
|
1043
|
+
{
|
|
1044
|
+
name: "topbar",
|
|
1045
|
+
slot: layout?.topbar ?? "core",
|
|
1046
|
+
registry,
|
|
1047
|
+
app,
|
|
1048
|
+
menu
|
|
1049
|
+
}
|
|
1050
|
+
),
|
|
1051
|
+
/* @__PURE__ */ jsx8(Slot, { name: "header", slot: layout?.header, registry, app, menu }),
|
|
1052
|
+
/* @__PURE__ */ jsx8(Box5, { as: "main", flex: "1", p: mainPadding, minW: 0, children: /* @__PURE__ */ jsx8(Box5, { maxW: portal ? "1180px" : "1680px", mx: "auto", children: /* @__PURE__ */ jsx8(Outlet, {}) }) }),
|
|
1053
|
+
/* @__PURE__ */ jsx8(
|
|
1054
|
+
Slot,
|
|
1055
|
+
{
|
|
1056
|
+
name: "footer",
|
|
1057
|
+
slot: layout?.footer ?? "none",
|
|
1058
|
+
registry,
|
|
1059
|
+
app,
|
|
1060
|
+
menu
|
|
1061
|
+
}
|
|
1062
|
+
)
|
|
1063
|
+
] })
|
|
1064
|
+
] });
|
|
1065
|
+
}
|
|
1066
|
+
function PortalShell(props) {
|
|
1067
|
+
return /* @__PURE__ */ jsx8(DefaultShell, { ...props, layout: { ...props.layout, shell: "portal" } });
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// src/shell/AuthenticatedLayout.tsx
|
|
1071
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1072
|
+
function AuthenticatedLayout({ app, menu, layout, registry }) {
|
|
1073
|
+
if (layout?.shell?.startsWith("custom:")) {
|
|
1074
|
+
const CustomShell = resolveComponent(registry, layout.shell, "layoutSlots");
|
|
1075
|
+
return CustomShell ? /* @__PURE__ */ jsx9(CustomShell, { app, menu, layout }) : /* @__PURE__ */ jsx9(MissingRenderer, { renderer: layout.shell });
|
|
1076
|
+
}
|
|
1077
|
+
if (layout?.shell === "portal") return /* @__PURE__ */ jsx9(PortalShell, { app, menu, layout, registry });
|
|
1078
|
+
return /* @__PURE__ */ jsx9(DefaultShell, { app, menu, layout, registry });
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/security/AccessDeniedPage.tsx
|
|
1082
|
+
import { Box as Box6, Button as Button4, Flex as Flex5, Heading as Heading3, Text as Text6, VStack as VStack3 } from "@chakra-ui/react";
|
|
1083
|
+
import { useNavigate as useNavigate2 } from "react-router-dom";
|
|
1084
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1085
|
+
function AccessDeniedPage({ app }) {
|
|
1086
|
+
const { logout } = useOonAuth();
|
|
1087
|
+
const navigate = useNavigate2();
|
|
1088
|
+
function useAnotherAccount() {
|
|
1089
|
+
logout();
|
|
1090
|
+
navigate("/login", { replace: true });
|
|
1091
|
+
}
|
|
1092
|
+
return /* @__PURE__ */ jsx10(Flex5, { minH: "100vh", align: "center", justify: "center", bg: "#F5F8FA", px: 4, children: /* @__PURE__ */ jsx10(
|
|
1093
|
+
Box6,
|
|
1094
|
+
{
|
|
1095
|
+
width: "100%",
|
|
1096
|
+
maxW: "560px",
|
|
1097
|
+
bg: "white",
|
|
1098
|
+
borderWidth: "1px",
|
|
1099
|
+
borderColor: "#E4EBEF",
|
|
1100
|
+
borderRadius: "18px",
|
|
1101
|
+
boxShadow: "0 24px 70px rgba(7, 38, 46, 0.10)",
|
|
1102
|
+
p: { base: 6, md: 10 },
|
|
1103
|
+
children: /* @__PURE__ */ jsxs7(VStack3, { align: "stretch", gap: 6, children: [
|
|
1104
|
+
/* @__PURE__ */ jsx10(
|
|
1105
|
+
Box6,
|
|
1106
|
+
{
|
|
1107
|
+
width: "56px",
|
|
1108
|
+
height: "56px",
|
|
1109
|
+
display: "flex",
|
|
1110
|
+
alignItems: "center",
|
|
1111
|
+
justifyContent: "center",
|
|
1112
|
+
borderRadius: "16px",
|
|
1113
|
+
bg: "orange.50",
|
|
1114
|
+
color: "orange.700",
|
|
1115
|
+
fontSize: "2xl",
|
|
1116
|
+
fontWeight: "800",
|
|
1117
|
+
"aria-hidden": "true",
|
|
1118
|
+
children: "!"
|
|
1119
|
+
}
|
|
1120
|
+
),
|
|
1121
|
+
/* @__PURE__ */ jsxs7(Box6, { children: [
|
|
1122
|
+
/* @__PURE__ */ jsx10(
|
|
1123
|
+
Text6,
|
|
1124
|
+
{
|
|
1125
|
+
color: "#39BFED",
|
|
1126
|
+
fontSize: "xs",
|
|
1127
|
+
fontWeight: "700",
|
|
1128
|
+
letterSpacing: "0.12em",
|
|
1129
|
+
textTransform: "uppercase",
|
|
1130
|
+
children: "Acesso negado"
|
|
1131
|
+
}
|
|
1132
|
+
),
|
|
1133
|
+
/* @__PURE__ */ jsx10(Heading3, { mt: 2, size: "lg", color: "#07262E", children: "Voc\xEA n\xE3o possui permiss\xE3o para este aplicativo" }),
|
|
1134
|
+
/* @__PURE__ */ jsxs7(Text6, { mt: 3, color: "gray.600", children: [
|
|
1135
|
+
"Seu usu\xE1rio foi autenticado, mas n\xE3o existe uma permiss\xE3o ativa para acessar ",
|
|
1136
|
+
app.title ?? app.name,
|
|
1137
|
+
". Solicite o acesso ao administrador da sua organiza\xE7\xE3o."
|
|
1138
|
+
] })
|
|
1139
|
+
] }),
|
|
1140
|
+
/* @__PURE__ */ jsx10(
|
|
1141
|
+
Button4,
|
|
1142
|
+
{
|
|
1143
|
+
alignSelf: "flex-start",
|
|
1144
|
+
bg: "#07262E",
|
|
1145
|
+
color: "white",
|
|
1146
|
+
_hover: { bg: "#0B3843" },
|
|
1147
|
+
onClick: useAnotherAccount,
|
|
1148
|
+
children: "Entrar com outra conta"
|
|
1149
|
+
}
|
|
1150
|
+
)
|
|
1151
|
+
] })
|
|
1152
|
+
}
|
|
1153
|
+
) });
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// src/security/AppAccessGuard.tsx
|
|
1157
|
+
import { Navigate, useLocation as useLocation3 } from "react-router-dom";
|
|
1158
|
+
import { Fragment as Fragment3, jsx as jsx11 } from "react/jsx-runtime";
|
|
1159
|
+
function loginRedirect(pathname, search) {
|
|
1160
|
+
const destination = `${pathname}${search}`;
|
|
1161
|
+
return `/login?redirect=${encodeURIComponent(destination)}`;
|
|
1162
|
+
}
|
|
1163
|
+
function AppAccessGuard({ children }) {
|
|
1164
|
+
const { status, retry } = useOonAuth();
|
|
1165
|
+
const location = useLocation3();
|
|
1166
|
+
if (status === "loading") return /* @__PURE__ */ jsx11(LoadingScreen, {});
|
|
1167
|
+
if (status === "unauthenticated") {
|
|
1168
|
+
return /* @__PURE__ */ jsx11(Navigate, { to: loginRedirect(location.pathname, location.search), replace: true });
|
|
1169
|
+
}
|
|
1170
|
+
if (status === "first-access") return /* @__PURE__ */ jsx11(Navigate, { to: "/first-access", replace: true });
|
|
1171
|
+
if (status === "forbidden") return /* @__PURE__ */ jsx11(Navigate, { to: "/acesso-negado", replace: true });
|
|
1172
|
+
if (status === "error") {
|
|
1173
|
+
return /* @__PURE__ */ jsx11(
|
|
1174
|
+
LoadingScreen,
|
|
1175
|
+
{
|
|
1176
|
+
message: "N\xE3o foi poss\xEDvel validar seu acesso. Tente novamente.",
|
|
1177
|
+
actionLabel: "Tentar novamente",
|
|
1178
|
+
onAction: () => {
|
|
1179
|
+
void retry().catch(() => void 0);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
return /* @__PURE__ */ jsx11(Fragment3, { children });
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// src/security/LoginPage.tsx
|
|
1188
|
+
import { useState as useState6 } from "react";
|
|
1189
|
+
import { Box as Box7, Button as Button5, Flex as Flex6, Heading as Heading4, Input as Input3, SimpleGrid, Text as Text7, VStack as VStack4 } from "@chakra-ui/react";
|
|
1190
|
+
import { Link as Link3, Navigate as Navigate2, useLocation as useLocation4 } from "react-router-dom";
|
|
1191
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1192
|
+
function tenantOptions(error) {
|
|
1193
|
+
const details = error.details;
|
|
1194
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) return [];
|
|
1195
|
+
const tenants = details.tenants;
|
|
1196
|
+
if (!Array.isArray(tenants)) return [];
|
|
1197
|
+
return tenants.filter((tenant) => Boolean(tenant) && typeof tenant === "object").map((tenant) => ({
|
|
1198
|
+
id: String(tenant.id || ""),
|
|
1199
|
+
nome: String(tenant.nome || tenant.slug || tenant.id || ""),
|
|
1200
|
+
slug: tenant.slug ? String(tenant.slug) : void 0,
|
|
1201
|
+
tipo: tenant.tipo ? String(tenant.tipo) : void 0,
|
|
1202
|
+
status: tenant.status ? String(tenant.status) : void 0
|
|
1203
|
+
})).filter((tenant) => tenant.id && tenant.nome);
|
|
1204
|
+
}
|
|
1205
|
+
function safeRedirect(search) {
|
|
1206
|
+
const value2 = new URLSearchParams(search).get("redirect") || "/";
|
|
1207
|
+
if (!value2.startsWith("/") || value2.startsWith("//")) return "/";
|
|
1208
|
+
if (value2.startsWith("/login") || value2.startsWith("/acesso-negado")) return "/";
|
|
1209
|
+
return value2;
|
|
1210
|
+
}
|
|
1211
|
+
function tenantUrl(path, tenantId) {
|
|
1212
|
+
const target = new URL(path, window.location.origin);
|
|
1213
|
+
target.searchParams.set("tenant", tenantId);
|
|
1214
|
+
return target.toString();
|
|
1215
|
+
}
|
|
1216
|
+
function LoginPage({ app, onboardingEnabled = false }) {
|
|
1217
|
+
const { status, login } = useOonAuth();
|
|
1218
|
+
const location = useLocation4();
|
|
1219
|
+
const [email, setEmail] = useState6("");
|
|
1220
|
+
const [password, setPassword] = useState6("");
|
|
1221
|
+
const [submitting, setSubmitting] = useState6(false);
|
|
1222
|
+
const [message, setMessage] = useState6(null);
|
|
1223
|
+
const [tenants, setTenants] = useState6([]);
|
|
1224
|
+
const [selectorMode, setSelectorMode] = useState6(false);
|
|
1225
|
+
const [openingTenant, setOpeningTenant] = useState6(null);
|
|
1226
|
+
const redirect = safeRedirect(location.search);
|
|
1227
|
+
if (status === "authenticated" && !selectorMode) return /* @__PURE__ */ jsx12(Navigate2, { to: redirect, replace: true });
|
|
1228
|
+
if (status === "forbidden") return /* @__PURE__ */ jsx12(Navigate2, { to: "/acesso-negado", replace: true });
|
|
1229
|
+
async function handleSubmit(event) {
|
|
1230
|
+
event.preventDefault();
|
|
1231
|
+
if (tenants.length > 1) return;
|
|
1232
|
+
setSubmitting(true);
|
|
1233
|
+
setMessage(null);
|
|
1234
|
+
try {
|
|
1235
|
+
await login(email.trim(), password);
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
const authError = error;
|
|
1238
|
+
if (authError.code === "TENANT_SELECTION_REQUIRED") {
|
|
1239
|
+
const options = tenantOptions(authError);
|
|
1240
|
+
if (options.length > 1) {
|
|
1241
|
+
setTenants(options);
|
|
1242
|
+
setMessage("Escolha uma organiza\xE7\xE3o. O tenant ser\xE1 aberto em uma nova aba.");
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
if (authError.status === 403) return;
|
|
1247
|
+
setMessage(authError.status === 401 ? "E-mail ou senha inv\xE1lidos." : authError.message || "N\xE3o foi poss\xEDvel entrar. Tente novamente.");
|
|
1248
|
+
} finally {
|
|
1249
|
+
setSubmitting(false);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
async function openTenant(tenant) {
|
|
1253
|
+
if (openingTenant) return;
|
|
1254
|
+
const popup = window.open("about:blank", "_blank");
|
|
1255
|
+
if (!popup) {
|
|
1256
|
+
setMessage("Permita pop-ups para abrir o tenant em uma nova aba.");
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
popup.opener = null;
|
|
1260
|
+
setOpeningTenant(tenant.id);
|
|
1261
|
+
setSelectorMode(true);
|
|
1262
|
+
setMessage(null);
|
|
1263
|
+
try {
|
|
1264
|
+
if (status !== "authenticated") await login(email.trim(), password, tenant.id);
|
|
1265
|
+
popup.location.replace(tenantUrl(redirect, tenant.id));
|
|
1266
|
+
setPassword("");
|
|
1267
|
+
setMessage(`${tenant.nome} foi aberto em uma nova aba. Escolha outro tenant quando precisar.`);
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
popup.close();
|
|
1270
|
+
const authError = error;
|
|
1271
|
+
setMessage(authError.message || "N\xE3o foi poss\xEDvel abrir o tenant.");
|
|
1272
|
+
} finally {
|
|
1273
|
+
setOpeningTenant(null);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return /* @__PURE__ */ jsx12(Flex6, { minH: "100vh", align: "center", justify: "center", bg: "#F5F8FA", px: 4, children: /* @__PURE__ */ jsx12(Box7, { width: "100%", maxW: tenants.length > 1 ? "760px" : "440px", bg: "white", borderWidth: "1px", borderColor: "#E4EBEF", borderRadius: "18px", boxShadow: "0 24px 70px rgba(7, 38, 46, 0.10)", p: { base: 6, md: 9 }, children: /* @__PURE__ */ jsx12("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs8(VStack4, { align: "stretch", gap: 6, children: [
|
|
1277
|
+
/* @__PURE__ */ jsxs8(Box7, { children: [
|
|
1278
|
+
/* @__PURE__ */ jsx12(Text7, { color: "#39BFED", fontSize: "xs", fontWeight: "700", letterSpacing: "0.12em", textTransform: "uppercase", children: "Central Oon" }),
|
|
1279
|
+
/* @__PURE__ */ jsx12(Heading4, { mt: 2, size: "lg", color: "#07262E", children: app.title ?? app.name }),
|
|
1280
|
+
/* @__PURE__ */ jsx12(Text7, { mt: 2, color: "gray.600", fontSize: "sm", children: tenants.length > 1 ? "Abra uma ou mais organiza\xE7\xF5es para trabalhar em contextos separados." : "Entre com seu usu\xE1rio autorizado para acessar este aplicativo." })
|
|
1281
|
+
] }),
|
|
1282
|
+
tenants.length <= 1 ? /* @__PURE__ */ jsxs8(VStack4, { align: "stretch", gap: 4, children: [
|
|
1283
|
+
/* @__PURE__ */ jsxs8(Box7, { children: [
|
|
1284
|
+
/* @__PURE__ */ jsx12(Text7, { mb: 2, fontSize: "sm", fontWeight: "600", color: "#24323A", children: "E-mail" }),
|
|
1285
|
+
/* @__PURE__ */ jsx12(Input3, { type: "email", value: email, onChange: (event) => {
|
|
1286
|
+
setEmail(event.target.value);
|
|
1287
|
+
setTenants([]);
|
|
1288
|
+
setSelectorMode(false);
|
|
1289
|
+
}, autoComplete: "email", placeholder: "voce@empresa.com.br", required: true })
|
|
1290
|
+
] }),
|
|
1291
|
+
/* @__PURE__ */ jsxs8(Box7, { children: [
|
|
1292
|
+
/* @__PURE__ */ jsx12(Text7, { mb: 2, fontSize: "sm", fontWeight: "600", color: "#24323A", children: "Senha" }),
|
|
1293
|
+
/* @__PURE__ */ jsx12(Input3, { type: "password", value: password, onChange: (event) => setPassword(event.target.value), autoComplete: "current-password", placeholder: "Sua senha", required: true })
|
|
1294
|
+
] })
|
|
1295
|
+
] }) : /* @__PURE__ */ jsxs8(Box7, { children: [
|
|
1296
|
+
/* @__PURE__ */ jsx12(Text7, { mb: 3, fontSize: "sm", fontWeight: "600", color: "#24323A", children: "Escolha o tenant para acessar" }),
|
|
1297
|
+
/* @__PURE__ */ jsx12(SimpleGrid, { columns: { base: 1, md: 2 }, gap: 3, children: tenants.map((tenant) => /* @__PURE__ */ jsx12(Button5, { type: "button", variant: "outline", height: "auto", minH: "104px", justifyContent: "flex-start", whiteSpace: "normal", textAlign: "left", borderWidth: "2px", borderColor: "#D9E2E7", bg: "white", color: "#24323A", disabled: Boolean(openingTenant), "aria-label": `Abrir ${tenant.nome} em nova aba`, onClick: () => void openTenant(tenant), _hover: { borderColor: "#39BFED", bg: "#F6FCFE" }, children: /* @__PURE__ */ jsxs8(VStack4, { align: "start", gap: 1, py: 3, children: [
|
|
1298
|
+
/* @__PURE__ */ jsx12(Text7, { fontWeight: "700", children: tenant.nome }),
|
|
1299
|
+
/* @__PURE__ */ jsx12(Text7, { fontSize: "xs", color: "gray.500", children: [tenant.slug, tenant.tipo, tenant.status].filter(Boolean).join(" \xB7 ") }),
|
|
1300
|
+
/* @__PURE__ */ jsx12(Text7, { fontSize: "xs", color: "#168EB9", children: openingTenant === tenant.id ? "Abrindo..." : "Abrir em nova aba" })
|
|
1301
|
+
] }) }, tenant.id)) })
|
|
1302
|
+
] }),
|
|
1303
|
+
message ? /* @__PURE__ */ jsx12(Box7, { role: "status", borderRadius: "10px", bg: "blue.50", color: "blue.800", px: 4, py: 3, fontSize: "sm", children: message }) : null,
|
|
1304
|
+
tenants.length <= 1 ? /* @__PURE__ */ jsx12(Button5, { type: "submit", bg: "#07262E", color: "white", _hover: { bg: "#0B3843" }, disabled: submitting || !email.trim() || !password, children: submitting ? "Validando acesso..." : "Entrar" }) : null,
|
|
1305
|
+
app.kind === "portal-cockpit" && onboardingEnabled && tenants.length <= 1 ? /* @__PURE__ */ jsxs8(Text7, { textAlign: "center", fontSize: "sm", color: "gray.600", children: [
|
|
1306
|
+
"Ainda n\xE3o possui acesso? ",
|
|
1307
|
+
/* @__PURE__ */ jsx12(Link3, { to: "/cadastro", children: "Criar conta" })
|
|
1308
|
+
] }) : null
|
|
1309
|
+
] }) }) }) });
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// src/security/FirstAccessPage.tsx
|
|
1313
|
+
import { useState as useState7 } from "react";
|
|
1314
|
+
import { Box as Box8, Button as Button6, Flex as Flex7, Heading as Heading5, Input as Input4, Text as Text8, VStack as VStack5 } from "@chakra-ui/react";
|
|
1315
|
+
import { Navigate as Navigate3 } from "react-router-dom";
|
|
1316
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1317
|
+
function FirstAccessPage() {
|
|
1318
|
+
const { status, completeFirstAccess } = useOonAuth();
|
|
1319
|
+
const [password, setPassword] = useState7("");
|
|
1320
|
+
const [confirm, setConfirm] = useState7("");
|
|
1321
|
+
const [submitting, setSubmitting] = useState7(false);
|
|
1322
|
+
const [message, setMessage] = useState7(null);
|
|
1323
|
+
if (status === "authenticated") return /* @__PURE__ */ jsx13(Navigate3, { to: "/", replace: true });
|
|
1324
|
+
if (status === "unauthenticated") return /* @__PURE__ */ jsx13(Navigate3, { to: "/login", replace: true });
|
|
1325
|
+
if (status === "forbidden") return /* @__PURE__ */ jsx13(Navigate3, { to: "/acesso-negado", replace: true });
|
|
1326
|
+
async function submit(event) {
|
|
1327
|
+
event.preventDefault();
|
|
1328
|
+
setMessage(null);
|
|
1329
|
+
if (password.length < 8) {
|
|
1330
|
+
setMessage("A senha deve possuir pelo menos 8 caracteres.");
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
if (password !== confirm) {
|
|
1334
|
+
setMessage("As senhas informadas n\xE3o conferem.");
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
setSubmitting(true);
|
|
1338
|
+
try {
|
|
1339
|
+
await completeFirstAccess(password);
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
setMessage(error?.message || "N\xE3o foi poss\xEDvel definir a nova senha.");
|
|
1342
|
+
} finally {
|
|
1343
|
+
setSubmitting(false);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
return /* @__PURE__ */ jsx13(Flex7, { minH: "100vh", align: "center", justify: "center", bg: "#F5F8FA", px: 4, children: /* @__PURE__ */ jsx13(Box8, { width: "100%", maxW: "460px", bg: "white", borderWidth: "1px", borderRadius: "18px", p: { base: 6, md: 9 }, children: /* @__PURE__ */ jsx13("form", { onSubmit: (event) => void submit(event), children: /* @__PURE__ */ jsxs9(VStack5, { align: "stretch", gap: 5, children: [
|
|
1347
|
+
/* @__PURE__ */ jsxs9(Box8, { children: [
|
|
1348
|
+
/* @__PURE__ */ jsx13(Text8, { color: "#39BFED", fontSize: "xs", fontWeight: "700", textTransform: "uppercase", children: "Primeiro acesso" }),
|
|
1349
|
+
/* @__PURE__ */ jsx13(Heading5, { mt: 2, size: "lg", children: "Defina sua nova senha" }),
|
|
1350
|
+
/* @__PURE__ */ jsx13(Text8, { mt: 2, fontSize: "sm", color: "gray.600", children: "O acesso \xE0 Central s\xF3 ser\xE1 liberado depois desta etapa." })
|
|
1351
|
+
] }),
|
|
1352
|
+
/* @__PURE__ */ jsxs9(Box8, { children: [
|
|
1353
|
+
/* @__PURE__ */ jsx13(Text8, { mb: 1, fontSize: "sm", fontWeight: "600", children: "Nova senha" }),
|
|
1354
|
+
/* @__PURE__ */ jsx13(Input4, { type: "password", autoComplete: "new-password", value: password, onChange: (event) => setPassword(event.target.value), required: true })
|
|
1355
|
+
] }),
|
|
1356
|
+
/* @__PURE__ */ jsxs9(Box8, { children: [
|
|
1357
|
+
/* @__PURE__ */ jsx13(Text8, { mb: 1, fontSize: "sm", fontWeight: "600", children: "Confirmar senha" }),
|
|
1358
|
+
/* @__PURE__ */ jsx13(Input4, { type: "password", autoComplete: "new-password", value: confirm, onChange: (event) => setConfirm(event.target.value), required: true })
|
|
1359
|
+
] }),
|
|
1360
|
+
message ? /* @__PURE__ */ jsx13(Box8, { role: "alert", bg: "red.50", borderRadius: "md", p: 3, children: /* @__PURE__ */ jsx13(Text8, { fontSize: "sm", children: message }) }) : null,
|
|
1361
|
+
/* @__PURE__ */ jsx13(Button6, { type: "submit", disabled: submitting, children: submitting ? "Salvando\u2026" : "Definir nova senha" })
|
|
1362
|
+
] }) }) }) });
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// src/security/ActivationPage.tsx
|
|
1366
|
+
import { useEffect as useEffect5, useState as useState8 } from "react";
|
|
1367
|
+
import { Box as Box9, Button as Button7, Heading as Heading6, Input as Input5, Text as Text9, VStack as VStack6 } from "@chakra-ui/react";
|
|
1368
|
+
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1369
|
+
function encodeBasicCredentials(email, password) {
|
|
1370
|
+
const bytes = new TextEncoder().encode(`${email}:${password}`);
|
|
1371
|
+
let binary = "";
|
|
1372
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
1373
|
+
return window.btoa(binary);
|
|
1374
|
+
}
|
|
1375
|
+
function ActivationPage() {
|
|
1376
|
+
const { http } = useOonApi();
|
|
1377
|
+
const activation = useActivation();
|
|
1378
|
+
const [step, setStep] = useState8(1);
|
|
1379
|
+
const [config, setConfig] = useState8(null);
|
|
1380
|
+
const [code, setCode] = useState8("");
|
|
1381
|
+
const [preview, setPreview] = useState8(null);
|
|
1382
|
+
const [global, setGlobal] = useState8({});
|
|
1383
|
+
const [app, setApp] = useState8({});
|
|
1384
|
+
const [rootEmail, setRootEmail] = useState8("");
|
|
1385
|
+
const [rootPassword, setRootPassword] = useState8("");
|
|
1386
|
+
const [devName, setDevName] = useState8("");
|
|
1387
|
+
const [devEmail, setDevEmail] = useState8("");
|
|
1388
|
+
const [devPassword, setDevPassword] = useState8("");
|
|
1389
|
+
const [showCodeFlow, setShowCodeFlow] = useState8(false);
|
|
1390
|
+
const [devResult, setDevResult] = useState8(null);
|
|
1391
|
+
const [error, setError] = useState8(null);
|
|
1392
|
+
const [submitting, setSubmitting] = useState8(false);
|
|
1393
|
+
useEffect5(() => {
|
|
1394
|
+
http.get("/ativacao/configuracao").then((response) => {
|
|
1395
|
+
setConfig(response.data);
|
|
1396
|
+
setGlobal(response.data.defaults || {});
|
|
1397
|
+
}).catch(
|
|
1398
|
+
() => setError("N\xE3o foi poss\xEDvel carregar a configura\xE7\xE3o de ativa\xE7\xE3o.")
|
|
1399
|
+
);
|
|
1400
|
+
}, [http]);
|
|
1401
|
+
useEffect5(() => {
|
|
1402
|
+
if (activation.status === "active") {
|
|
1403
|
+
window.location.replace("/login");
|
|
1404
|
+
}
|
|
1405
|
+
}, [activation.status]);
|
|
1406
|
+
async function activateRoot() {
|
|
1407
|
+
setSubmitting(true);
|
|
1408
|
+
setError(null);
|
|
1409
|
+
try {
|
|
1410
|
+
await http.post("/ativacao/ativar-root", {
|
|
1411
|
+
email: rootEmail.trim(),
|
|
1412
|
+
senha: rootPassword
|
|
1413
|
+
});
|
|
1414
|
+
setRootPassword("");
|
|
1415
|
+
setStep(6);
|
|
1416
|
+
await activation.refresh();
|
|
1417
|
+
window.setTimeout(() => window.location.replace("/login"), 900);
|
|
1418
|
+
} catch (caught) {
|
|
1419
|
+
setRootPassword("");
|
|
1420
|
+
setError(
|
|
1421
|
+
caught.message || "N\xE3o foi poss\xEDvel concluir a ativa\xE7\xE3o inicial."
|
|
1422
|
+
);
|
|
1423
|
+
} finally {
|
|
1424
|
+
setSubmitting(false);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
async function activateDevLocal() {
|
|
1428
|
+
setSubmitting(true);
|
|
1429
|
+
setError(null);
|
|
1430
|
+
try {
|
|
1431
|
+
const response = await http.post(
|
|
1432
|
+
"/ativacao/ativar-dev-local",
|
|
1433
|
+
{
|
|
1434
|
+
nomeUsuario: devName.trim() || void 0,
|
|
1435
|
+
nomeCentral: global.nomeInstancia || config?.defaults?.nomeInstancia,
|
|
1436
|
+
appName: config?.defaults?.nomeInstancia || config?.technical?.appCode,
|
|
1437
|
+
urlPublica: global.urlPublica
|
|
1438
|
+
},
|
|
1439
|
+
{
|
|
1440
|
+
headers: {
|
|
1441
|
+
Authorization: `Basic ${encodeBasicCredentials(
|
|
1442
|
+
devEmail.trim().toLowerCase(),
|
|
1443
|
+
devPassword
|
|
1444
|
+
)}`
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
);
|
|
1448
|
+
setDevPassword("");
|
|
1449
|
+
setDevResult(response.data);
|
|
1450
|
+
setStep(6);
|
|
1451
|
+
const token = response.data.token;
|
|
1452
|
+
window.setTimeout(() => {
|
|
1453
|
+
window.location.replace(token ? `/?code=${encodeURIComponent(token)}` : "/login");
|
|
1454
|
+
}, 900);
|
|
1455
|
+
} catch (caught) {
|
|
1456
|
+
setDevPassword("");
|
|
1457
|
+
setError(
|
|
1458
|
+
caught.message || "N\xE3o foi poss\xEDvel ativar a Central em desenvolvimento."
|
|
1459
|
+
);
|
|
1460
|
+
} finally {
|
|
1461
|
+
setSubmitting(false);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
async function validateCode() {
|
|
1465
|
+
setSubmitting(true);
|
|
1466
|
+
setError(null);
|
|
1467
|
+
try {
|
|
1468
|
+
const response = await http.post("/ativacao/validar-codigo", {
|
|
1469
|
+
codigoAtivacao: code
|
|
1470
|
+
});
|
|
1471
|
+
setPreview(response.data);
|
|
1472
|
+
setStep(2);
|
|
1473
|
+
} catch (caught) {
|
|
1474
|
+
setError(caught.message || "C\xF3digo inv\xE1lido.");
|
|
1475
|
+
} finally {
|
|
1476
|
+
setSubmitting(false);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
async function completeMemberActivation() {
|
|
1480
|
+
setSubmitting(true);
|
|
1481
|
+
setError(null);
|
|
1482
|
+
try {
|
|
1483
|
+
await http.post("/ativacao/concluir", {
|
|
1484
|
+
codigoAtivacao: code,
|
|
1485
|
+
nomeInstancia: global.nomeInstancia,
|
|
1486
|
+
parametrosGlobais: global,
|
|
1487
|
+
parametrosAplicacao: app
|
|
1488
|
+
});
|
|
1489
|
+
setStep(6);
|
|
1490
|
+
setCode("");
|
|
1491
|
+
await activation.refresh();
|
|
1492
|
+
window.setTimeout(() => window.location.replace("/login"), 1200);
|
|
1493
|
+
} catch (caught) {
|
|
1494
|
+
setError(
|
|
1495
|
+
caught.message || "N\xE3o foi poss\xEDvel concluir a ativa\xE7\xE3o."
|
|
1496
|
+
);
|
|
1497
|
+
} finally {
|
|
1498
|
+
setSubmitting(false);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
function renderField(field, values, setValues) {
|
|
1502
|
+
return /* @__PURE__ */ jsxs10(Box9, { w: "100%", children: [
|
|
1503
|
+
/* @__PURE__ */ jsxs10(Text9, { fontWeight: "medium", children: [
|
|
1504
|
+
field.label,
|
|
1505
|
+
field.required ? " *" : ""
|
|
1506
|
+
] }),
|
|
1507
|
+
/* @__PURE__ */ jsx14(
|
|
1508
|
+
Input5,
|
|
1509
|
+
{
|
|
1510
|
+
type: field.type === "password" || field.type === "secret" ? "password" : field.type === "email" ? "email" : field.type === "url" ? "url" : field.type === "number" || field.type === "integer" ? "number" : "text",
|
|
1511
|
+
value: String(values[field.name] ?? field.defaultValue ?? ""),
|
|
1512
|
+
readOnly: field.readOnly,
|
|
1513
|
+
placeholder: field.placeholder,
|
|
1514
|
+
minLength: field.minLength,
|
|
1515
|
+
autoComplete: field.autoComplete,
|
|
1516
|
+
onChange: (event) => setValues({ ...values, [field.name]: event.target.value })
|
|
1517
|
+
}
|
|
1518
|
+
),
|
|
1519
|
+
field.helpText ? /* @__PURE__ */ jsx14(Text9, { fontSize: "sm", color: "gray.500", children: field.helpText }) : null
|
|
1520
|
+
] }, field.name);
|
|
1521
|
+
}
|
|
1522
|
+
function renderDevLocalActivation() {
|
|
1523
|
+
return /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1524
|
+
/* @__PURE__ */ jsx14(Text9, { color: "gray.700", children: "Entre com seu e-mail e senha para ativar esta Central em ambiente de desenvolvimento. Se o e-mail ainda n\xE3o existir na Central de Ativa\xE7\xF5es, o usu\xE1rio ser\xE1 criado automaticamente com perfil de desenvolvedor." }),
|
|
1525
|
+
/* @__PURE__ */ jsxs10(Box9, { w: "100%", children: [
|
|
1526
|
+
/* @__PURE__ */ jsx14(Text9, { fontWeight: "medium", children: "Nome do usu\xE1rio" }),
|
|
1527
|
+
/* @__PURE__ */ jsx14(
|
|
1528
|
+
Input5,
|
|
1529
|
+
{
|
|
1530
|
+
value: devName,
|
|
1531
|
+
onChange: (event) => setDevName(event.target.value),
|
|
1532
|
+
placeholder: "Seu nome",
|
|
1533
|
+
autoComplete: "name"
|
|
1534
|
+
}
|
|
1535
|
+
)
|
|
1536
|
+
] }),
|
|
1537
|
+
/* @__PURE__ */ jsxs10(Box9, { w: "100%", children: [
|
|
1538
|
+
/* @__PURE__ */ jsx14(Text9, { fontWeight: "medium", children: "E-mail *" }),
|
|
1539
|
+
/* @__PURE__ */ jsx14(
|
|
1540
|
+
Input5,
|
|
1541
|
+
{
|
|
1542
|
+
type: "email",
|
|
1543
|
+
value: devEmail,
|
|
1544
|
+
onChange: (event) => setDevEmail(event.target.value),
|
|
1545
|
+
placeholder: "dev@empresa.com.br",
|
|
1546
|
+
autoComplete: "email"
|
|
1547
|
+
}
|
|
1548
|
+
)
|
|
1549
|
+
] }),
|
|
1550
|
+
/* @__PURE__ */ jsxs10(Box9, { w: "100%", children: [
|
|
1551
|
+
/* @__PURE__ */ jsx14(Text9, { fontWeight: "medium", children: "Senha *" }),
|
|
1552
|
+
/* @__PURE__ */ jsx14(
|
|
1553
|
+
Input5,
|
|
1554
|
+
{
|
|
1555
|
+
type: "password",
|
|
1556
|
+
value: devPassword,
|
|
1557
|
+
onChange: (event) => setDevPassword(event.target.value),
|
|
1558
|
+
placeholder: "M\xEDnimo de 8 caracteres",
|
|
1559
|
+
minLength: 8,
|
|
1560
|
+
autoComplete: "current-password"
|
|
1561
|
+
}
|
|
1562
|
+
)
|
|
1563
|
+
] }),
|
|
1564
|
+
/* @__PURE__ */ jsx14(
|
|
1565
|
+
Button7,
|
|
1566
|
+
{
|
|
1567
|
+
colorPalette: "blue",
|
|
1568
|
+
onClick: activateDevLocal,
|
|
1569
|
+
loading: submitting,
|
|
1570
|
+
disabled: submitting || !devEmail.trim() || devPassword.length < 8 || !activation.details?.podeAtivar,
|
|
1571
|
+
children: "Ativar ambiente de desenvolvimento"
|
|
1572
|
+
}
|
|
1573
|
+
),
|
|
1574
|
+
/* @__PURE__ */ jsx14(
|
|
1575
|
+
Button7,
|
|
1576
|
+
{
|
|
1577
|
+
variant: "outline",
|
|
1578
|
+
onClick: () => setShowCodeFlow(true),
|
|
1579
|
+
disabled: submitting,
|
|
1580
|
+
children: "Usar c\xF3digo de ativa\xE7\xE3o manual"
|
|
1581
|
+
}
|
|
1582
|
+
)
|
|
1583
|
+
] });
|
|
1584
|
+
}
|
|
1585
|
+
const rootMode = config?.mode === "root-bootstrap" || activation.details?.role === "root";
|
|
1586
|
+
const isDevelopmentMember = !rootMode && config?.technical?.ambiente === "desenvolvimento";
|
|
1587
|
+
if (activation.status === "loading" || !config) {
|
|
1588
|
+
return /* @__PURE__ */ jsx14(Box9, { minH: "100vh", bg: "gray.50", p: 8, children: /* @__PURE__ */ jsxs10(
|
|
1589
|
+
VStack6,
|
|
1590
|
+
{
|
|
1591
|
+
maxW: "760px",
|
|
1592
|
+
mx: "auto",
|
|
1593
|
+
bg: "white",
|
|
1594
|
+
p: 8,
|
|
1595
|
+
borderRadius: "lg",
|
|
1596
|
+
align: "stretch",
|
|
1597
|
+
gap: 5,
|
|
1598
|
+
children: [
|
|
1599
|
+
/* @__PURE__ */ jsx14(Heading6, { children: "Preparando ativa\xE7\xE3o..." }),
|
|
1600
|
+
error ? /* @__PURE__ */ jsx14(Text9, { color: "red.600", children: error }) : null
|
|
1601
|
+
]
|
|
1602
|
+
}
|
|
1603
|
+
) });
|
|
1604
|
+
}
|
|
1605
|
+
if (rootMode) {
|
|
1606
|
+
return /* @__PURE__ */ jsx14(Box9, { minH: "100vh", bg: "gray.50", p: 8, children: /* @__PURE__ */ jsxs10(
|
|
1607
|
+
VStack6,
|
|
1608
|
+
{
|
|
1609
|
+
maxW: "560px",
|
|
1610
|
+
mx: "auto",
|
|
1611
|
+
bg: "white",
|
|
1612
|
+
p: 8,
|
|
1613
|
+
borderRadius: "lg",
|
|
1614
|
+
align: "stretch",
|
|
1615
|
+
gap: 5,
|
|
1616
|
+
children: [
|
|
1617
|
+
/* @__PURE__ */ jsx14(Heading6, { children: "Ativa\xE7\xE3o inicial do Ecossistema Oon" }),
|
|
1618
|
+
/* @__PURE__ */ jsx14(Text9, { color: "gray.600", children: "Esta \xE9 a aplica\xE7\xE3o root do ecossistema. Informe somente o e-mail e a senha do primeiro administrador. O Core criar\xE1 a estrutura m\xEDnima necess\xE1ria para liberar o primeiro login." }),
|
|
1619
|
+
/* @__PURE__ */ jsxs10(Text9, { fontSize: "sm", color: "gray.500", children: [
|
|
1620
|
+
"App: ",
|
|
1621
|
+
config.technical.appCode,
|
|
1622
|
+
" \xB7 Ambiente:",
|
|
1623
|
+
" ",
|
|
1624
|
+
config.technical.ambiente
|
|
1625
|
+
] }),
|
|
1626
|
+
error ? /* @__PURE__ */ jsx14(Text9, { color: "red.600", children: error }) : null,
|
|
1627
|
+
step !== 6 ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1628
|
+
/* @__PURE__ */ jsxs10(Box9, { w: "100%", children: [
|
|
1629
|
+
/* @__PURE__ */ jsx14(Text9, { fontWeight: "medium", children: "E-mail do administrador *" }),
|
|
1630
|
+
/* @__PURE__ */ jsx14(
|
|
1631
|
+
Input5,
|
|
1632
|
+
{
|
|
1633
|
+
type: "email",
|
|
1634
|
+
autoComplete: "email",
|
|
1635
|
+
value: rootEmail,
|
|
1636
|
+
onChange: (event) => setRootEmail(event.target.value),
|
|
1637
|
+
placeholder: "administrador@empresa.com.br"
|
|
1638
|
+
}
|
|
1639
|
+
)
|
|
1640
|
+
] }),
|
|
1641
|
+
/* @__PURE__ */ jsxs10(Box9, { w: "100%", children: [
|
|
1642
|
+
/* @__PURE__ */ jsx14(Text9, { fontWeight: "medium", children: "Senha *" }),
|
|
1643
|
+
/* @__PURE__ */ jsx14(
|
|
1644
|
+
Input5,
|
|
1645
|
+
{
|
|
1646
|
+
type: "password",
|
|
1647
|
+
minLength: 8,
|
|
1648
|
+
autoComplete: "new-password",
|
|
1649
|
+
value: rootPassword,
|
|
1650
|
+
onChange: (event) => setRootPassword(event.target.value),
|
|
1651
|
+
placeholder: "M\xEDnimo de 8 caracteres"
|
|
1652
|
+
}
|
|
1653
|
+
)
|
|
1654
|
+
] }),
|
|
1655
|
+
/* @__PURE__ */ jsx14(
|
|
1656
|
+
Button7,
|
|
1657
|
+
{
|
|
1658
|
+
colorPalette: "blue",
|
|
1659
|
+
onClick: activateRoot,
|
|
1660
|
+
loading: submitting,
|
|
1661
|
+
disabled: submitting || !rootEmail.trim() || rootPassword.length < 8 || !activation.details?.podeAtivar,
|
|
1662
|
+
children: "Criar ecossistema e ativar"
|
|
1663
|
+
}
|
|
1664
|
+
)
|
|
1665
|
+
] }) : /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1666
|
+
/* @__PURE__ */ jsx14(Heading6, { size: "md", children: "Ativa\xE7\xE3o conclu\xEDda" }),
|
|
1667
|
+
/* @__PURE__ */ jsx14(Text9, { children: "Usu\xE1rio, acesso do sistema e aplicativo root foram preparados. Redirecionando para o primeiro login..." })
|
|
1668
|
+
] })
|
|
1669
|
+
]
|
|
1670
|
+
}
|
|
1671
|
+
) });
|
|
1672
|
+
}
|
|
1673
|
+
return /* @__PURE__ */ jsx14(Box9, { minH: "100vh", bg: "gray.50", p: 8, children: /* @__PURE__ */ jsxs10(
|
|
1674
|
+
VStack6,
|
|
1675
|
+
{
|
|
1676
|
+
maxW: "760px",
|
|
1677
|
+
mx: "auto",
|
|
1678
|
+
bg: "white",
|
|
1679
|
+
p: 8,
|
|
1680
|
+
borderRadius: "lg",
|
|
1681
|
+
align: "stretch",
|
|
1682
|
+
gap: 5,
|
|
1683
|
+
children: [
|
|
1684
|
+
/* @__PURE__ */ jsx14(Heading6, { children: "Ativa\xE7\xE3o da Central Oon" }),
|
|
1685
|
+
/* @__PURE__ */ jsxs10(Text9, { children: [
|
|
1686
|
+
"App: ",
|
|
1687
|
+
config.technical.appCode,
|
|
1688
|
+
" \xB7 Ambiente:",
|
|
1689
|
+
" ",
|
|
1690
|
+
config.technical.ambiente,
|
|
1691
|
+
" \xB7 Central:",
|
|
1692
|
+
" ",
|
|
1693
|
+
config.technical.centralAtivacaoUrl || config.technical.centralAtivacaoHost
|
|
1694
|
+
] }),
|
|
1695
|
+
error ? /* @__PURE__ */ jsx14(Text9, { color: "red.600", children: error }) : null,
|
|
1696
|
+
step === 1 && isDevelopmentMember && !showCodeFlow ? renderDevLocalActivation() : null,
|
|
1697
|
+
step === 1 && (!isDevelopmentMember || showCodeFlow) && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1698
|
+
/* @__PURE__ */ jsx14(Text9, { children: "Informe o c\xF3digo de ativa\xE7\xE3o para validar Tenant, App, Entitlement e Deployment. A licen\xE7a ser\xE1 validada somente para Apps comerciais." }),
|
|
1699
|
+
/* @__PURE__ */ jsx14(
|
|
1700
|
+
Input5,
|
|
1701
|
+
{
|
|
1702
|
+
"aria-label": "C\xF3digo de ativa\xE7\xE3o",
|
|
1703
|
+
value: code,
|
|
1704
|
+
onChange: (event) => setCode(event.target.value),
|
|
1705
|
+
placeholder: "OON-XXXX"
|
|
1706
|
+
}
|
|
1707
|
+
),
|
|
1708
|
+
/* @__PURE__ */ jsx14(
|
|
1709
|
+
Button7,
|
|
1710
|
+
{
|
|
1711
|
+
onClick: validateCode,
|
|
1712
|
+
loading: submitting,
|
|
1713
|
+
disabled: !code || submitting,
|
|
1714
|
+
children: "Validar c\xF3digo"
|
|
1715
|
+
}
|
|
1716
|
+
),
|
|
1717
|
+
isDevelopmentMember ? /* @__PURE__ */ jsx14(
|
|
1718
|
+
Button7,
|
|
1719
|
+
{
|
|
1720
|
+
variant: "outline",
|
|
1721
|
+
onClick: () => setShowCodeFlow(false),
|
|
1722
|
+
disabled: submitting,
|
|
1723
|
+
children: "Voltar para ativa\xE7\xE3o com usu\xE1rio"
|
|
1724
|
+
}
|
|
1725
|
+
) : null
|
|
1726
|
+
] }),
|
|
1727
|
+
step === 2 && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1728
|
+
/* @__PURE__ */ jsx14(Heading6, { size: "md", children: "Confirma\xE7\xE3o" }),
|
|
1729
|
+
/* @__PURE__ */ jsx14("pre", { children: JSON.stringify(preview, null, 2) }),
|
|
1730
|
+
/* @__PURE__ */ jsx14(Button7, { onClick: () => setStep(3), children: "Continuar" })
|
|
1731
|
+
] }),
|
|
1732
|
+
step === 3 && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1733
|
+
config.globalFields.map(
|
|
1734
|
+
(field) => renderField(field, global, setGlobal)
|
|
1735
|
+
),
|
|
1736
|
+
/* @__PURE__ */ jsx14(Button7, { onClick: () => setStep(4), children: "Continuar" })
|
|
1737
|
+
] }),
|
|
1738
|
+
step === 4 && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1739
|
+
config.appFields.length ? config.appFields.map((field) => renderField(field, app, setApp)) : /* @__PURE__ */ jsx14(Text9, { children: "Nenhum par\xE2metro adicional declarado." }),
|
|
1740
|
+
/* @__PURE__ */ jsx14(Button7, { onClick: () => setStep(5), children: "Revisar" })
|
|
1741
|
+
] }),
|
|
1742
|
+
step === 5 && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1743
|
+
/* @__PURE__ */ jsx14(Text9, { children: "Ao confirmar, o c\xF3digo ser\xE1 consumido e este runtime ser\xE1 vinculado ao Deployment do Ecossistema Oon." }),
|
|
1744
|
+
/* @__PURE__ */ jsx14(
|
|
1745
|
+
Button7,
|
|
1746
|
+
{
|
|
1747
|
+
colorPalette: "blue",
|
|
1748
|
+
onClick: completeMemberActivation,
|
|
1749
|
+
loading: submitting,
|
|
1750
|
+
disabled: submitting,
|
|
1751
|
+
children: "Concluir ativa\xE7\xE3o"
|
|
1752
|
+
}
|
|
1753
|
+
)
|
|
1754
|
+
] }),
|
|
1755
|
+
step === 6 && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1756
|
+
/* @__PURE__ */ jsx14(Heading6, { size: "md", children: "Ativa\xE7\xE3o conclu\xEDda" }),
|
|
1757
|
+
devResult?.appCode ? /* @__PURE__ */ jsxs10(Text9, { children: [
|
|
1758
|
+
"C\xF3digo do app: ",
|
|
1759
|
+
devResult.appCode,
|
|
1760
|
+
". Redirecionando para a Central em desenvolvimento..."
|
|
1761
|
+
] }) : /* @__PURE__ */ jsx14(Text9, { children: "Redirecionando para o login..." }),
|
|
1762
|
+
devResult?.instrucoes?.env ? /* @__PURE__ */ jsx14(Text9, { fontSize: "sm", color: "gray.500", children: devResult.instrucoes.env }) : null
|
|
1763
|
+
] })
|
|
1764
|
+
]
|
|
1765
|
+
}
|
|
1766
|
+
) });
|
|
1767
|
+
}
|
|
1768
|
+
function InstanceBlockedPage({
|
|
1769
|
+
title,
|
|
1770
|
+
message
|
|
1771
|
+
}) {
|
|
1772
|
+
return /* @__PURE__ */ jsxs10(Box9, { p: 8, children: [
|
|
1773
|
+
/* @__PURE__ */ jsx14(Heading6, { children: title }),
|
|
1774
|
+
/* @__PURE__ */ jsx14(Text9, { children: message })
|
|
1775
|
+
] });
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// src/security/RouteGuard.tsx
|
|
1779
|
+
import { Fragment as Fragment5, jsx as jsx15 } from "react/jsx-runtime";
|
|
1780
|
+
function RouteGuard({
|
|
1781
|
+
permission,
|
|
1782
|
+
children
|
|
1783
|
+
}) {
|
|
1784
|
+
const { status, hasPermission } = useOonAuth();
|
|
1785
|
+
if (status === "loading") return /* @__PURE__ */ jsx15(LoadingScreen, {});
|
|
1786
|
+
if (status === "unauthenticated") return /* @__PURE__ */ jsx15(LoadingScreen, { message: "Redirecionando para o login..." });
|
|
1787
|
+
if (!hasPermission(permission)) {
|
|
1788
|
+
return /* @__PURE__ */ jsx15(LoadingScreen, { message: "Voc\xEA n\xE3o tem acesso a esta tela." });
|
|
1789
|
+
}
|
|
1790
|
+
return /* @__PURE__ */ jsx15(Fragment5, { children });
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
// src/security/ActivationGuard.tsx
|
|
1794
|
+
import { Navigate as Navigate4 } from "react-router-dom";
|
|
1795
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
1796
|
+
function ActivationGuard({ children }) {
|
|
1797
|
+
const { status } = useActivation();
|
|
1798
|
+
if (status === "loading") return null;
|
|
1799
|
+
if (status === "suspended") return /* @__PURE__ */ jsx16(Navigate4, { to: "/instancia-suspensa", replace: true });
|
|
1800
|
+
if (status === "revoked") return /* @__PURE__ */ jsx16(Navigate4, { to: "/instancia-revogada", replace: true });
|
|
1801
|
+
if (status !== "active" && window.location.pathname !== "/ativacao") return /* @__PURE__ */ jsx16(Navigate4, { to: "/ativacao", replace: true });
|
|
1802
|
+
return children;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// src/app/createOonRouter.tsx
|
|
1806
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
1807
|
+
function createOonRouter({ app, menu, routes, corePublicRoutes = [], onboardingEnabled, security, layout, registry, systemPages }) {
|
|
1808
|
+
const guardEnabled = security?.enableRouteGuard !== false;
|
|
1809
|
+
const publicRoutes = routes.filter((route) => route.public).map((route) => ({ path: route.path, element: routeElement(route) }));
|
|
1810
|
+
const privateSource = routes.filter((route) => !route.public);
|
|
1811
|
+
const privateRoutes = privateSource.map((route) => {
|
|
1812
|
+
const content = routeElement(route);
|
|
1813
|
+
const element = guardEnabled ? /* @__PURE__ */ jsx17(RouteGuard, { permission: route.permissions, children: content }) : content;
|
|
1814
|
+
return route.path === "/" ? { index: true, element } : { path: route.path.replace(/^\//, ""), element };
|
|
1815
|
+
});
|
|
1816
|
+
const hasIndexRoute = privateSource.some((route) => route.path === "/");
|
|
1817
|
+
const firstRoute = privateSource.find((route) => route.path !== "/");
|
|
1818
|
+
if (!hasIndexRoute && firstRoute) {
|
|
1819
|
+
privateRoutes.unshift({ index: true, element: /* @__PURE__ */ jsx17(Navigate5, { to: firstRoute.path, replace: true }) });
|
|
1820
|
+
}
|
|
1821
|
+
const customPaths = new Set(publicRoutes.map((route) => route.path));
|
|
1822
|
+
const reservedCollision = [...customPaths].find((path) => OON_RESERVED_ROUTES.includes(String(path)));
|
|
1823
|
+
if (reservedCollision) throw new Error(`Rota reservada pelo OonCore: ${reservedCollision}.`);
|
|
1824
|
+
const coreRouteObjects = corePublicRoutes.map((route) => ({
|
|
1825
|
+
path: route.path,
|
|
1826
|
+
element: routeElement(route)
|
|
1827
|
+
}));
|
|
1828
|
+
const occupiedPaths = /* @__PURE__ */ new Set([
|
|
1829
|
+
...customPaths,
|
|
1830
|
+
...corePublicRoutes.map((route) => route.path)
|
|
1831
|
+
]);
|
|
1832
|
+
const builtInPublicRoutes = [...coreRouteObjects];
|
|
1833
|
+
if (!occupiedPaths.has("/login")) {
|
|
1834
|
+
const Login = systemPages?.login;
|
|
1835
|
+
builtInPublicRoutes.push({
|
|
1836
|
+
path: "/login",
|
|
1837
|
+
element: /* @__PURE__ */ jsx17(ActivationGuard, { children: Login ? /* @__PURE__ */ jsx17(Login, {}) : /* @__PURE__ */ jsx17(LoginPage, { app, onboardingEnabled }) })
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
if (!occupiedPaths.has("/accept-invite")) {
|
|
1841
|
+
builtInPublicRoutes.push({
|
|
1842
|
+
path: "/accept-invite",
|
|
1843
|
+
element: /* @__PURE__ */ jsx17(ActivationGuard, { children: /* @__PURE__ */ jsx17(AcceptInvitePage, {}) })
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
if (!occupiedPaths.has("/first-access")) {
|
|
1847
|
+
builtInPublicRoutes.push({
|
|
1848
|
+
path: "/first-access",
|
|
1849
|
+
element: /* @__PURE__ */ jsx17(ActivationGuard, { children: /* @__PURE__ */ jsx17(FirstAccessPage, {}) })
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
if (!occupiedPaths.has("/ativacao")) builtInPublicRoutes.push({ path: "/ativacao", element: /* @__PURE__ */ jsx17(ActivationPage, {}) });
|
|
1853
|
+
if (!occupiedPaths.has("/instancia-suspensa")) {
|
|
1854
|
+
builtInPublicRoutes.push({
|
|
1855
|
+
path: "/instancia-suspensa",
|
|
1856
|
+
element: /* @__PURE__ */ jsx17(InstanceBlockedPage, { title: "Inst\xE2ncia suspensa", message: "Esta Central est\xE1 suspensa." })
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
if (!occupiedPaths.has("/instancia-revogada")) {
|
|
1860
|
+
builtInPublicRoutes.push({
|
|
1861
|
+
path: "/instancia-revogada",
|
|
1862
|
+
element: /* @__PURE__ */ jsx17(InstanceBlockedPage, { title: "Inst\xE2ncia revogada", message: "Esta Central foi revogada." })
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
if (!occupiedPaths.has("/acesso-negado")) {
|
|
1866
|
+
builtInPublicRoutes.push({ path: "/acesso-negado", element: /* @__PURE__ */ jsx17(AccessDeniedPage, { app }) });
|
|
1867
|
+
}
|
|
1868
|
+
const tree = [
|
|
1869
|
+
...builtInPublicRoutes,
|
|
1870
|
+
...publicRoutes,
|
|
1871
|
+
{
|
|
1872
|
+
path: "/",
|
|
1873
|
+
element: /* @__PURE__ */ jsx17(ActivationGuard, { children: /* @__PURE__ */ jsx17(AppAccessGuard, { children: /* @__PURE__ */ jsx17(AuthenticatedLayout, { app, menu, layout, registry }) }) }),
|
|
1874
|
+
children: privateRoutes
|
|
1875
|
+
}
|
|
1876
|
+
];
|
|
1877
|
+
return createBrowserRouter(tree);
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// src/app/OonApp.tsx
|
|
1881
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1882
|
+
function localLoginUrl() {
|
|
1883
|
+
const current = `${window.location.pathname}${window.location.search}`;
|
|
1884
|
+
return `/login?redirect=${encodeURIComponent(current)}`;
|
|
1885
|
+
}
|
|
1886
|
+
function OonApp({ config }) {
|
|
1887
|
+
const { http, storage, tenantStorage, router } = useMemo2(() => {
|
|
1888
|
+
const storage2 = createTokenStorage(config.app.id);
|
|
1889
|
+
const tenantStorage2 = createTenantStorage(config.app.id, {
|
|
1890
|
+
enabled: config.app.tenancyModel !== "none"
|
|
1891
|
+
});
|
|
1892
|
+
const externalLoginUrl = config.auth?.loginUrl ?? config.api.meusAppsUrl;
|
|
1893
|
+
const http2 = createRestClient({
|
|
1894
|
+
api: config.api,
|
|
1895
|
+
appId: config.app.id,
|
|
1896
|
+
runtimeMode: config.runtimeMode,
|
|
1897
|
+
getToken: () => storage2.get(),
|
|
1898
|
+
getTenantId: () => tenantStorage2.get(),
|
|
1899
|
+
onUnauthorized: () => {
|
|
1900
|
+
storage2.clear();
|
|
1901
|
+
if (config.runtimeMode === "local") return;
|
|
1902
|
+
if (externalLoginUrl) {
|
|
1903
|
+
window.location.href = externalLoginUrl;
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
if (window.location.pathname !== "/login") {
|
|
1907
|
+
window.location.href = localLoginUrl();
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
});
|
|
1911
|
+
const resolved = resolveRegistry(config);
|
|
1912
|
+
const routes = [...resolved.routes];
|
|
1913
|
+
const corePublicRoutes = [{
|
|
1914
|
+
path: "/accept-invite",
|
|
1915
|
+
public: true,
|
|
1916
|
+
element: /* @__PURE__ */ jsx18(AcceptInviteCentralPage, {})
|
|
1917
|
+
}];
|
|
1918
|
+
if (config.api.onboardingBaseUrl) {
|
|
1919
|
+
corePublicRoutes.push({ path: "/cadastro", public: true, element: /* @__PURE__ */ jsx18(OnboardingPage, { baseUrl: config.api.onboardingBaseUrl, mode: "register" }) });
|
|
1920
|
+
corePublicRoutes.push({ path: "/confirmar-email", public: true, element: /* @__PURE__ */ jsx18(OnboardingPage, { baseUrl: config.api.onboardingBaseUrl, mode: "confirm" }) });
|
|
1921
|
+
}
|
|
1922
|
+
const router2 = createOonRouter({
|
|
1923
|
+
app: config.app,
|
|
1924
|
+
menu: resolved.menu,
|
|
1925
|
+
routes,
|
|
1926
|
+
corePublicRoutes,
|
|
1927
|
+
onboardingEnabled: Boolean(config.api.onboardingBaseUrl),
|
|
1928
|
+
security: config.security,
|
|
1929
|
+
layout: config.layout ?? config.ui?.layout,
|
|
1930
|
+
registry: config.registry ?? config.ui?.registry,
|
|
1931
|
+
systemPages: config.systemPages
|
|
1932
|
+
});
|
|
1933
|
+
return { http: http2, storage: storage2, tenantStorage: tenantStorage2, router: router2 };
|
|
1934
|
+
}, [config]);
|
|
1935
|
+
return /* @__PURE__ */ jsx18(
|
|
1936
|
+
OonProviders,
|
|
1937
|
+
{
|
|
1938
|
+
config,
|
|
1939
|
+
http,
|
|
1940
|
+
storage,
|
|
1941
|
+
tenantStorage,
|
|
1942
|
+
children: /* @__PURE__ */ jsx18(RouterProvider, { router })
|
|
1943
|
+
}
|
|
1944
|
+
);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
// src/components/internal/pipelineBoardLayout.ts
|
|
1948
|
+
var PIPELINE_BOARD_STYLE_ID = "oon-core-pipeline-board-natural-height";
|
|
1949
|
+
var PIPELINE_BOARD_LAYOUT_CSS = `
|
|
1950
|
+
:where(*) > [data-oon-pipeline-board="true"] {
|
|
1951
|
+
height: auto !important;
|
|
1952
|
+
min-height: 420px;
|
|
1953
|
+
overflow-y: visible !important;
|
|
1954
|
+
align-items: stretch !important;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
:where(*) > [data-oon-pipeline-board="true"] > * {
|
|
1958
|
+
align-self: stretch;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
:where(*):has(> [data-oon-pipeline-board="true"]) {
|
|
1962
|
+
display: grid;
|
|
1963
|
+
grid-template-areas: "oon-pipeline-board";
|
|
1964
|
+
align-items: start;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
:where(*):has(> [data-oon-pipeline-board="true"]) > [data-oon-pipeline-board="true"] {
|
|
1968
|
+
grid-area: oon-pipeline-board;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
:where(*):has(> [data-oon-pipeline-board="true"]) > button[aria-label="Deslizar quadro para a esquerda"],
|
|
1972
|
+
:where(*):has(> [data-oon-pipeline-board="true"]) > button[aria-label="Deslizar quadro para a direita"] {
|
|
1973
|
+
grid-area: oon-pipeline-board;
|
|
1974
|
+
position: sticky !important;
|
|
1975
|
+
top: calc(50vh - 17px) !important;
|
|
1976
|
+
right: auto !important;
|
|
1977
|
+
left: auto !important;
|
|
1978
|
+
align-self: start;
|
|
1979
|
+
transform: none !important;
|
|
1980
|
+
z-index: 3;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
:where(*):has(> [data-oon-pipeline-board="true"]) > button[aria-label="Deslizar quadro para a esquerda"] {
|
|
1984
|
+
justify-self: start;
|
|
1985
|
+
margin-left: 8px;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
:where(*):has(> [data-oon-pipeline-board="true"]) > button[aria-label="Deslizar quadro para a direita"] {
|
|
1989
|
+
justify-self: end;
|
|
1990
|
+
margin-right: 8px;
|
|
1991
|
+
}
|
|
1992
|
+
`;
|
|
1993
|
+
function installPipelineBoardLayoutStyles(targetDocument = document) {
|
|
1994
|
+
if (targetDocument.getElementById(PIPELINE_BOARD_STYLE_ID)) return;
|
|
1995
|
+
const style = targetDocument.createElement("style");
|
|
1996
|
+
style.id = PIPELINE_BOARD_STYLE_ID;
|
|
1997
|
+
style.textContent = PIPELINE_BOARD_LAYOUT_CSS;
|
|
1998
|
+
targetDocument.head.appendChild(style);
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// src/start.tsx
|
|
2002
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
2003
|
+
function validateConfig(config) {
|
|
2004
|
+
if (!config?.app?.id) throw new Error("oonCoreFront.start: config.app.id \xE9 obrigat\xF3rio.");
|
|
2005
|
+
if (!config?.api?.baseUrl) throw new Error("oonCoreFront.start: config.api.baseUrl \xE9 obrigat\xF3rio.");
|
|
2006
|
+
}
|
|
2007
|
+
function applyDocumentMeta(config) {
|
|
2008
|
+
if (config.app.title) document.title = config.app.title;
|
|
2009
|
+
if (config.app.favicon) {
|
|
2010
|
+
let link = document.querySelector('link[rel="icon"]');
|
|
2011
|
+
if (!link) {
|
|
2012
|
+
link = document.createElement("link");
|
|
2013
|
+
link.rel = "icon";
|
|
2014
|
+
document.head.appendChild(link);
|
|
2015
|
+
}
|
|
2016
|
+
link.href = config.app.favicon;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
function hardenConsole(config) {
|
|
2020
|
+
if (config.security?.disableConsoleInProduction && import.meta.env?.PROD) {
|
|
2021
|
+
const noop = () => {
|
|
2022
|
+
};
|
|
2023
|
+
console.log = noop;
|
|
2024
|
+
console.debug = noop;
|
|
2025
|
+
console.info = noop;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
function start(config) {
|
|
2029
|
+
validateConfig(config);
|
|
2030
|
+
applyDocumentMeta(config);
|
|
2031
|
+
installPipelineBoardLayoutStyles();
|
|
2032
|
+
hardenConsole(config);
|
|
2033
|
+
const rootId = config.rootElementId ?? "root";
|
|
2034
|
+
const el = document.getElementById(rootId);
|
|
2035
|
+
if (!el) throw new Error(`oonCoreFront.start: elemento #${rootId} n\xE3o encontrado no DOM.`);
|
|
2036
|
+
createRoot(el).render(
|
|
2037
|
+
/* @__PURE__ */ jsx19(StrictMode, { children: /* @__PURE__ */ jsx19(OonApp, { config }) })
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
// src/app/codeFirst.ts
|
|
2042
|
+
function navigationItems(navigation) {
|
|
2043
|
+
if (!navigation) return [];
|
|
2044
|
+
return Array.isArray(navigation) ? navigation : navigation.items ?? [];
|
|
2045
|
+
}
|
|
2046
|
+
function defineOonApp(definition) {
|
|
2047
|
+
if (!definition?.app?.id) throw new Error("defineOonApp: app.id \xE9 obrigat\xF3rio.");
|
|
2048
|
+
if (!definition?.app?.name) throw new Error("defineOonApp: app.name \xE9 obrigat\xF3rio.");
|
|
2049
|
+
if (!definition?.api?.baseUrl) throw new Error("defineOonApp: api.baseUrl \xE9 obrigat\xF3rio.");
|
|
2050
|
+
if (definition.security?.enableRouteGuard === false || definition.security?.enablePermissionGate === false) {
|
|
2051
|
+
throw new Error("defineOonApp: guards obrigat\xF3rios de rota e permiss\xE3o n\xE3o podem ser desabilitados.");
|
|
2052
|
+
}
|
|
2053
|
+
defineOonRoutes(definition.routes ?? []);
|
|
2054
|
+
defineOonNavigation(navigationItems(definition.navigation));
|
|
2055
|
+
return definition;
|
|
2056
|
+
}
|
|
2057
|
+
function appDefinitionToConfig(definition) {
|
|
2058
|
+
const app = defineOonApp(definition);
|
|
2059
|
+
const localModule = {
|
|
2060
|
+
id: "app.local",
|
|
2061
|
+
label: app.app.name,
|
|
2062
|
+
routes: [...app.routes ?? []],
|
|
2063
|
+
menu: navigationItems(app.navigation)
|
|
2064
|
+
};
|
|
2065
|
+
const modules = localModule.routes?.length || localModule.menu?.length ? [localModule, ...app.modules ?? []] : [...app.modules ?? []];
|
|
2066
|
+
const uiNavigation = Array.isArray(app.navigation) ? { mode: "manual", items: app.navigation } : app.navigation;
|
|
2067
|
+
return {
|
|
2068
|
+
app: app.app,
|
|
2069
|
+
api: app.api,
|
|
2070
|
+
runtimeMode: app.runtimeMode,
|
|
2071
|
+
auth: app.auth,
|
|
2072
|
+
theme: app.theme,
|
|
2073
|
+
themes: app.themes,
|
|
2074
|
+
defaultThemeId: app.defaultThemeId,
|
|
2075
|
+
modules,
|
|
2076
|
+
ui: app.ui || app.pages?.length ? {
|
|
2077
|
+
...app.ui,
|
|
2078
|
+
schemaVersion: 2,
|
|
2079
|
+
views: app.ui?.views ?? [],
|
|
2080
|
+
navigation: app.ui?.navigation ?? uiNavigation,
|
|
2081
|
+
pages: [...app.ui?.pages ?? [], ...app.pages ?? []]
|
|
2082
|
+
} : void 0,
|
|
2083
|
+
layout: app.shell ?? app.ui?.layout,
|
|
2084
|
+
registry: {
|
|
2085
|
+
...app.components,
|
|
2086
|
+
layoutSlots: { ...app.components?.layoutSlots, ...app.layouts }
|
|
2087
|
+
},
|
|
2088
|
+
security: {
|
|
2089
|
+
enableRouteGuard: true,
|
|
2090
|
+
enablePermissionGate: true,
|
|
2091
|
+
redactAssistantContext: true,
|
|
2092
|
+
disableConsoleInProduction: true,
|
|
2093
|
+
...app.security
|
|
2094
|
+
},
|
|
2095
|
+
systemPages: app.systemPages,
|
|
2096
|
+
rootElementId: app.rootElementId
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
function startOonApp(definition) {
|
|
2100
|
+
const config = appDefinitionToConfig(definition);
|
|
2101
|
+
start(config);
|
|
2102
|
+
return config;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
export {
|
|
2106
|
+
createTenantStorage,
|
|
2107
|
+
defineOonApp,
|
|
2108
|
+
appDefinitionToConfig,
|
|
2109
|
+
startOonApp
|
|
2110
|
+
};
|
|
2111
|
+
//# sourceMappingURL=chunk-QXKS2WEG.js.map
|