@oondemand/oon-core-front 0.2.0 → 0.3.0
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/README.md +2 -121
- package/dist/index.d.ts +18 -23
- package/dist/index.js +2016 -389
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { StrictMode } from "react";
|
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
|
|
5
5
|
// src/app/OonApp.tsx
|
|
6
|
-
import { useMemo as
|
|
6
|
+
import { useMemo as useMemo4 } from "react";
|
|
7
7
|
import { RouterProvider } from "react-router-dom";
|
|
8
8
|
|
|
9
9
|
// src/api/createRestClient.ts
|
|
@@ -56,7 +56,9 @@ function createRestClient(deps) {
|
|
|
56
56
|
});
|
|
57
57
|
instance.interceptors.request.use((config) => {
|
|
58
58
|
const token = getToken();
|
|
59
|
-
if (token
|
|
59
|
+
if (token && !config.headers.Authorization) {
|
|
60
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
61
|
+
}
|
|
60
62
|
if (appId) config.headers["x-oon-app"] = appId;
|
|
61
63
|
return config;
|
|
62
64
|
});
|
|
@@ -64,7 +66,9 @@ function createRestClient(deps) {
|
|
|
64
66
|
(res) => res,
|
|
65
67
|
(error) => {
|
|
66
68
|
const normalized = normalizeError(error);
|
|
67
|
-
|
|
69
|
+
const requestPath = String(error.config?.url || "").split("?")[0].replace(/\/$/, "");
|
|
70
|
+
const isLogin = requestPath.endsWith("/auth/autenticar");
|
|
71
|
+
if (normalized.status === 401 && !isLogin) onUnauthorized?.();
|
|
68
72
|
return Promise.reject(normalized);
|
|
69
73
|
}
|
|
70
74
|
);
|
|
@@ -144,26 +148,55 @@ function createOonQueryClient() {
|
|
|
144
148
|
import { createSystem, defaultConfig, defineConfig, mergeConfigs } from "@chakra-ui/react";
|
|
145
149
|
var brandColors = {
|
|
146
150
|
brand: {
|
|
147
|
-
25: { value: "#
|
|
148
|
-
50: { value: "#
|
|
149
|
-
75: { value: "#
|
|
150
|
-
100: { value: "#
|
|
151
|
-
200: { value: "#
|
|
151
|
+
25: { value: "#F8FCFF" },
|
|
152
|
+
50: { value: "#EEF8FD" },
|
|
153
|
+
75: { value: "#E2F3FB" },
|
|
154
|
+
100: { value: "#C7E8F7" },
|
|
155
|
+
200: { value: "#82C8E8" },
|
|
152
156
|
300: { value: "#3FA6E0" },
|
|
153
157
|
350: { value: "#0F8DD0" },
|
|
154
158
|
400: { value: "#0580B9" },
|
|
155
159
|
500: { value: "#0474AF" },
|
|
156
160
|
600: { value: "#036491" },
|
|
157
161
|
700: { value: "#014364" },
|
|
158
|
-
800: { value: "#
|
|
159
|
-
850: { value: "#
|
|
160
|
-
900: { value: "#
|
|
162
|
+
800: { value: "#073A52" },
|
|
163
|
+
850: { value: "#082F3F" },
|
|
164
|
+
900: { value: "#07262E" }
|
|
161
165
|
}
|
|
162
166
|
};
|
|
163
167
|
var oonThemeConfig = defineConfig({
|
|
168
|
+
globalCss: {
|
|
169
|
+
"html, body, #root": {
|
|
170
|
+
minHeight: "100%"
|
|
171
|
+
},
|
|
172
|
+
body: {
|
|
173
|
+
margin: 0,
|
|
174
|
+
bg: "#F8F9FA",
|
|
175
|
+
color: "#24323A",
|
|
176
|
+
fontFamily: "Poppins, Inter, system-ui, sans-serif",
|
|
177
|
+
fontSize: "14px"
|
|
178
|
+
},
|
|
179
|
+
"button, input, select, textarea": {
|
|
180
|
+
fontFamily: "inherit"
|
|
181
|
+
},
|
|
182
|
+
"::selection": {
|
|
183
|
+
bg: "#C7E8F7",
|
|
184
|
+
color: "#07262E"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
164
187
|
theme: {
|
|
165
188
|
tokens: {
|
|
166
|
-
colors: brandColors
|
|
189
|
+
colors: brandColors,
|
|
190
|
+
fonts: {
|
|
191
|
+
body: { value: "Poppins, Inter, system-ui, sans-serif" },
|
|
192
|
+
heading: { value: "Poppins, Inter, system-ui, sans-serif" }
|
|
193
|
+
},
|
|
194
|
+
radii: {
|
|
195
|
+
oon: { value: "10px" }
|
|
196
|
+
},
|
|
197
|
+
shadows: {
|
|
198
|
+
oon: { value: "0 8px 24px rgba(7, 38, 46, 0.08)" }
|
|
199
|
+
}
|
|
167
200
|
},
|
|
168
201
|
semanticTokens: {
|
|
169
202
|
colors: {
|
|
@@ -328,55 +361,212 @@ function useModelSchema(name) {
|
|
|
328
361
|
}
|
|
329
362
|
|
|
330
363
|
// src/security/AuthProvider.tsx
|
|
331
|
-
import {
|
|
364
|
+
import {
|
|
365
|
+
createContext as createContext3,
|
|
366
|
+
useCallback as useCallback2,
|
|
367
|
+
useContext as useContext3,
|
|
368
|
+
useEffect as useEffect2,
|
|
369
|
+
useState as useState2
|
|
370
|
+
} from "react";
|
|
371
|
+
|
|
372
|
+
// src/security/ActivationProvider.tsx
|
|
373
|
+
import {
|
|
374
|
+
createContext as createContext2,
|
|
375
|
+
useCallback,
|
|
376
|
+
useContext as useContext2,
|
|
377
|
+
useEffect,
|
|
378
|
+
useState
|
|
379
|
+
} from "react";
|
|
332
380
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
333
|
-
var
|
|
334
|
-
function
|
|
335
|
-
|
|
336
|
-
|
|
381
|
+
var ActivationContext = createContext2(null);
|
|
382
|
+
function mapStatus(data) {
|
|
383
|
+
if (data.status === "ativa") return "active";
|
|
384
|
+
if (data.status === "suspensa") return "suspended";
|
|
385
|
+
if (data.status === "revogada") return "revoked";
|
|
386
|
+
if (data.status === "configurando") return "configuring";
|
|
387
|
+
if (data.status === "erro" && !data.podeAtivar) return "error";
|
|
388
|
+
return "notActivated";
|
|
389
|
+
}
|
|
390
|
+
function ActivationProvider({
|
|
391
|
+
http,
|
|
392
|
+
children
|
|
393
|
+
}) {
|
|
337
394
|
const [status, setStatus] = useState("loading");
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
395
|
+
const [details, setDetails] = useState(null);
|
|
396
|
+
const refresh = useCallback(async () => {
|
|
397
|
+
setStatus("loading");
|
|
398
|
+
try {
|
|
399
|
+
const response = await http.get("/ativacao/status");
|
|
400
|
+
setDetails(response.data);
|
|
401
|
+
setStatus(mapStatus(response.data));
|
|
402
|
+
} catch {
|
|
403
|
+
setStatus("error");
|
|
343
404
|
}
|
|
405
|
+
}, [http]);
|
|
406
|
+
useEffect(() => {
|
|
407
|
+
void refresh();
|
|
408
|
+
}, [refresh]);
|
|
409
|
+
return /* @__PURE__ */ jsx2(ActivationContext.Provider, { value: { status, details, refresh }, children });
|
|
410
|
+
}
|
|
411
|
+
function useOptionalActivation() {
|
|
412
|
+
return useContext2(ActivationContext);
|
|
413
|
+
}
|
|
414
|
+
function useActivation() {
|
|
415
|
+
const context = useOptionalActivation();
|
|
416
|
+
if (!context) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
"useActivation deve ser usado dentro de <ActivationProvider>."
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
return context;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/security/AuthProvider.tsx
|
|
425
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
426
|
+
var AuthContext = createContext3(null);
|
|
427
|
+
function errorStatus(error) {
|
|
428
|
+
return error?.status;
|
|
429
|
+
}
|
|
430
|
+
function encodeBasicCredentials(email, password) {
|
|
431
|
+
const bytes = new TextEncoder().encode(`${email}:${password}`);
|
|
432
|
+
let binary = "";
|
|
433
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
434
|
+
return window.btoa(binary);
|
|
435
|
+
}
|
|
436
|
+
function AuthProvider({
|
|
437
|
+
http,
|
|
438
|
+
storage,
|
|
439
|
+
auth,
|
|
440
|
+
loginUrl,
|
|
441
|
+
children
|
|
442
|
+
}) {
|
|
443
|
+
const tokenParam = auth?.tokenParam ?? "code";
|
|
444
|
+
const devToken = auth?.devToken?.trim() || null;
|
|
445
|
+
const [user, setUser] = useState2(null);
|
|
446
|
+
const [status, setStatus] = useState2("loading");
|
|
447
|
+
const activation = useOptionalActivation();
|
|
448
|
+
const redirectToExternalLogin = useCallback2(() => {
|
|
449
|
+
if (loginUrl) window.location.href = loginUrl;
|
|
344
450
|
}, [loginUrl]);
|
|
345
|
-
const
|
|
451
|
+
const applyValidationError = useCallback2(
|
|
452
|
+
(error) => {
|
|
453
|
+
const currentStatus = errorStatus(error);
|
|
454
|
+
setUser(null);
|
|
455
|
+
if (currentStatus === 403) {
|
|
456
|
+
setStatus("forbidden");
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (currentStatus === 401) {
|
|
460
|
+
storage.clear();
|
|
461
|
+
setStatus("unauthenticated");
|
|
462
|
+
redirectToExternalLogin();
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
setStatus("error");
|
|
466
|
+
},
|
|
467
|
+
[storage, redirectToExternalLogin]
|
|
468
|
+
);
|
|
469
|
+
const validateSession = useCallback2(async () => {
|
|
470
|
+
const response = await http.get(
|
|
471
|
+
"/auth/validar-token"
|
|
472
|
+
);
|
|
473
|
+
setUser(response.data.usuario);
|
|
474
|
+
setStatus("authenticated");
|
|
475
|
+
}, [http]);
|
|
476
|
+
const retry = useCallback2(async () => {
|
|
477
|
+
setStatus("loading");
|
|
478
|
+
try {
|
|
479
|
+
await validateSession();
|
|
480
|
+
} catch (error) {
|
|
481
|
+
applyValidationError(error);
|
|
482
|
+
throw error;
|
|
483
|
+
}
|
|
484
|
+
}, [validateSession, applyValidationError]);
|
|
485
|
+
const login = useCallback2(
|
|
486
|
+
async (email, password) => {
|
|
487
|
+
setStatus("loading");
|
|
488
|
+
setUser(null);
|
|
489
|
+
try {
|
|
490
|
+
const response = await http.post(
|
|
491
|
+
"/auth/autenticar",
|
|
492
|
+
{},
|
|
493
|
+
{
|
|
494
|
+
headers: {
|
|
495
|
+
Authorization: `Basic ${encodeBasicCredentials(email, password)}`
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
if (!response.data.token) {
|
|
500
|
+
const error = {
|
|
501
|
+
code: "AUTH_TOKEN_MISSING",
|
|
502
|
+
message: "Token n\xE3o retornado pelo backend.",
|
|
503
|
+
status: 502
|
|
504
|
+
};
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
507
|
+
storage.set(response.data.token);
|
|
508
|
+
await validateSession();
|
|
509
|
+
} catch (error) {
|
|
510
|
+
applyValidationError(error);
|
|
511
|
+
throw error;
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
[http, storage, validateSession, applyValidationError]
|
|
515
|
+
);
|
|
516
|
+
const logout = useCallback2(() => {
|
|
346
517
|
storage.clear();
|
|
347
518
|
setUser(null);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
519
|
+
setStatus("unauthenticated");
|
|
520
|
+
redirectToExternalLogin();
|
|
521
|
+
}, [storage, redirectToExternalLogin]);
|
|
522
|
+
useEffect2(() => {
|
|
351
523
|
let active = true;
|
|
524
|
+
if (activation && !["active", "error"].includes(activation.status)) {
|
|
525
|
+
setUser(null);
|
|
526
|
+
setStatus("unauthenticated");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
352
529
|
const incoming = captureTokenFromUrl(tokenParam);
|
|
353
|
-
|
|
354
|
-
const token =
|
|
530
|
+
const stored = storage.get();
|
|
531
|
+
const token = incoming ?? devToken ?? stored;
|
|
532
|
+
if (token && token !== stored) storage.set(token);
|
|
355
533
|
if (!token) {
|
|
356
|
-
|
|
534
|
+
setUser(null);
|
|
535
|
+
setStatus("unauthenticated");
|
|
536
|
+
redirectToExternalLogin();
|
|
357
537
|
return;
|
|
358
538
|
}
|
|
359
|
-
|
|
539
|
+
setStatus("loading");
|
|
540
|
+
http.get("/auth/validar-token").then((response) => {
|
|
360
541
|
if (!active) return;
|
|
361
|
-
setUser(
|
|
542
|
+
setUser(response.data.usuario);
|
|
362
543
|
setStatus("authenticated");
|
|
363
|
-
}).catch(() => {
|
|
544
|
+
}).catch((error) => {
|
|
364
545
|
if (!active) return;
|
|
365
|
-
|
|
366
|
-
redirectToLogin();
|
|
546
|
+
applyValidationError(error);
|
|
367
547
|
});
|
|
368
548
|
return () => {
|
|
369
549
|
active = false;
|
|
370
550
|
};
|
|
371
|
-
}, [
|
|
551
|
+
}, [
|
|
552
|
+
http,
|
|
553
|
+
storage,
|
|
554
|
+
tokenParam,
|
|
555
|
+
devToken,
|
|
556
|
+
redirectToExternalLogin,
|
|
557
|
+
applyValidationError,
|
|
558
|
+
activation?.status
|
|
559
|
+
]);
|
|
372
560
|
const roles = user?.tipo ? [user.tipo] : [];
|
|
373
|
-
const hasPermission =
|
|
561
|
+
const hasPermission = useCallback2(
|
|
374
562
|
(permission) => {
|
|
375
563
|
if (!permission) return true;
|
|
376
564
|
const required = Array.isArray(permission) ? permission : [permission];
|
|
377
565
|
if (required.length === 0) return true;
|
|
378
566
|
if (roles.includes("admin") || roles.includes("master")) return true;
|
|
379
|
-
return required.some(
|
|
567
|
+
return required.some(
|
|
568
|
+
(value2) => roles.includes(value2) || roles.some((role) => value2.startsWith(`${role}.`))
|
|
569
|
+
);
|
|
380
570
|
},
|
|
381
571
|
[roles]
|
|
382
572
|
);
|
|
@@ -386,28 +576,34 @@ function AuthProvider({ http, storage, auth, loginUrl, children }) {
|
|
|
386
576
|
status,
|
|
387
577
|
isAuthenticated: status === "authenticated",
|
|
388
578
|
hasPermission,
|
|
579
|
+
login,
|
|
580
|
+
retry,
|
|
389
581
|
logout
|
|
390
582
|
};
|
|
391
|
-
return /* @__PURE__ */
|
|
583
|
+
return /* @__PURE__ */ jsx3(AuthContext.Provider, { value, children });
|
|
392
584
|
}
|
|
393
585
|
function useOonAuth() {
|
|
394
|
-
const
|
|
395
|
-
if (!
|
|
396
|
-
|
|
586
|
+
const context = useContext3(AuthContext);
|
|
587
|
+
if (!context) {
|
|
588
|
+
throw new Error(
|
|
589
|
+
"useOonAuth deve ser usado dentro de <AuthProvider> (oonCoreFront.start)."
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
return context;
|
|
397
593
|
}
|
|
398
594
|
|
|
399
595
|
// src/app/OonProviders.tsx
|
|
400
|
-
import { jsx as
|
|
596
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
401
597
|
function OonProviders({ config, http, storage, children }) {
|
|
402
598
|
const queryClient = useMemo2(() => createOonQueryClient(), []);
|
|
403
599
|
const system = useMemo2(() => createOonSystem(config.theme), [config.theme]);
|
|
404
600
|
const loginUrl = config.auth?.loginUrl ?? config.api.meusAppsUrl;
|
|
405
|
-
return /* @__PURE__ */
|
|
601
|
+
return /* @__PURE__ */ jsx4(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx4(ChakraProvider, { value: system, children: /* @__PURE__ */ jsx4(ApiProvider, { http, children: /* @__PURE__ */ jsx4(ActivationProvider, { http, children: /* @__PURE__ */ jsx4(AuthProvider, { http, storage, auth: config.auth, loginUrl, children }) }) }) }) });
|
|
406
602
|
}
|
|
407
603
|
|
|
408
604
|
// src/components/CoreCollection.tsx
|
|
409
|
-
import { useState as
|
|
410
|
-
import { Box as
|
|
605
|
+
import { useState as useState6 } from "react";
|
|
606
|
+
import { Box as Box5, Heading as Heading3, Stack as Stack3, Text as Text6 } from "@chakra-ui/react";
|
|
411
607
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
412
608
|
|
|
413
609
|
// src/components/internal/fieldUtils.ts
|
|
@@ -417,17 +613,39 @@ function isEditableField(meta) {
|
|
|
417
613
|
}
|
|
418
614
|
function fieldLabel(meta) {
|
|
419
615
|
if (meta.label) return meta.label;
|
|
420
|
-
return meta.name.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (
|
|
616
|
+
return meta.name.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (character) => character.toUpperCase());
|
|
421
617
|
}
|
|
422
618
|
function columnsFromMeta(fields) {
|
|
423
|
-
return fields.filter(isEditableField).slice(0, 8).map((
|
|
619
|
+
return fields.filter(isEditableField).slice(0, 8).map((field) => ({ field: field.name, label: fieldLabel(field), kind: field.kind, sortable: true }));
|
|
620
|
+
}
|
|
621
|
+
function formRequired(field) {
|
|
622
|
+
const raw = field;
|
|
623
|
+
const options = raw.options;
|
|
624
|
+
const validation = raw.validation;
|
|
625
|
+
const schema = raw.schema;
|
|
626
|
+
return Boolean(
|
|
627
|
+
field.required || options && !Array.isArray(options) && typeof options === "object" && options.required || validation && typeof validation === "object" && validation.required || schema && typeof schema === "object" && schema.required
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
function formOptions(field) {
|
|
631
|
+
const rawOptions = field.options;
|
|
632
|
+
if (Array.isArray(rawOptions)) return rawOptions.filter((value) => typeof value === "string");
|
|
633
|
+
if (!rawOptions || typeof rawOptions !== "object") return void 0;
|
|
634
|
+
const options = rawOptions;
|
|
635
|
+
const candidates = [options.values, options.enum, options.options];
|
|
636
|
+
for (const candidate of candidates) {
|
|
637
|
+
if (Array.isArray(candidate)) return candidate.filter((value) => typeof value === "string");
|
|
638
|
+
}
|
|
639
|
+
return void 0;
|
|
424
640
|
}
|
|
425
641
|
function formFieldsFromMeta(fields) {
|
|
426
|
-
return fields.filter(isEditableField).map((
|
|
427
|
-
field:
|
|
428
|
-
label: fieldLabel(
|
|
429
|
-
kind:
|
|
430
|
-
|
|
642
|
+
return fields.filter(isEditableField).map((field) => ({
|
|
643
|
+
field: field.name,
|
|
644
|
+
label: fieldLabel(field),
|
|
645
|
+
kind: field.kind,
|
|
646
|
+
required: formRequired(field),
|
|
647
|
+
options: formOptions(field),
|
|
648
|
+
ref: field.ref
|
|
431
649
|
}));
|
|
432
650
|
}
|
|
433
651
|
function formatCell(value, kind) {
|
|
@@ -446,8 +664,8 @@ function formatCell(value, kind) {
|
|
|
446
664
|
return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(Number(value));
|
|
447
665
|
case "ref":
|
|
448
666
|
if (value && typeof value === "object") {
|
|
449
|
-
const
|
|
450
|
-
return String(
|
|
667
|
+
const object = value;
|
|
668
|
+
return String(object.nome ?? object.label ?? object.descricao ?? object._id ?? "\u2014");
|
|
451
669
|
}
|
|
452
670
|
return String(value);
|
|
453
671
|
default:
|
|
@@ -456,30 +674,35 @@ function formatCell(value, kind) {
|
|
|
456
674
|
}
|
|
457
675
|
|
|
458
676
|
// src/components/internal/DataGrid.tsx
|
|
459
|
-
import { useState as
|
|
460
|
-
import { Box as Box2, Button, Flex as Flex2, Input, Spacer, Table, Text as Text3 } from "@chakra-ui/react";
|
|
677
|
+
import { useDeferredValue, useState as useState3 } from "react";
|
|
678
|
+
import { Badge, Box as Box2, Button as Button2, Flex as Flex2, Input, NativeSelect, Spacer, Table, Text as Text3 } from "@chakra-ui/react";
|
|
461
679
|
import { keepPreviousData, useQuery as useQuery2 } from "@tanstack/react-query";
|
|
462
680
|
|
|
463
681
|
// src/shell/LoadingScreen.tsx
|
|
464
|
-
import { Flex, Spinner, Text } from "@chakra-ui/react";
|
|
465
|
-
import { jsx as
|
|
466
|
-
function LoadingScreen({
|
|
682
|
+
import { Button, Flex, Spinner, Text } from "@chakra-ui/react";
|
|
683
|
+
import { jsx as jsx5, jsxs } from "react/jsx-runtime";
|
|
684
|
+
function LoadingScreen({
|
|
685
|
+
message,
|
|
686
|
+
actionLabel,
|
|
687
|
+
onAction
|
|
688
|
+
}) {
|
|
467
689
|
return /* @__PURE__ */ jsxs(Flex, { direction: "column", align: "center", justify: "center", minH: "60vh", gap: 4, children: [
|
|
468
|
-
/* @__PURE__ */
|
|
469
|
-
message ? /* @__PURE__ */
|
|
690
|
+
/* @__PURE__ */ jsx5(Spinner, { size: "lg" }),
|
|
691
|
+
message ? /* @__PURE__ */ jsx5(Text, { color: "gray.500", fontSize: "sm", textAlign: "center", children: message }) : null,
|
|
692
|
+
actionLabel && onAction ? /* @__PURE__ */ jsx5(Button, { size: "sm", variant: "outline", onClick: onAction, children: actionLabel }) : null
|
|
470
693
|
] });
|
|
471
694
|
}
|
|
472
695
|
|
|
473
696
|
// src/shell/ErrorState.tsx
|
|
474
697
|
import { Box, Heading, Text as Text2 } from "@chakra-ui/react";
|
|
475
|
-
import { jsx as
|
|
698
|
+
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
476
699
|
function ErrorState({
|
|
477
700
|
error,
|
|
478
701
|
title = "N\xE3o foi poss\xEDvel carregar"
|
|
479
702
|
}) {
|
|
480
703
|
return /* @__PURE__ */ jsxs2(Box, { borderWidth: "1px", borderColor: "red.200", bg: "red.50", borderRadius: "md", p: 4, children: [
|
|
481
|
-
/* @__PURE__ */
|
|
482
|
-
/* @__PURE__ */
|
|
704
|
+
/* @__PURE__ */ jsx6(Heading, { size: "sm", color: "red.700", children: title }),
|
|
705
|
+
/* @__PURE__ */ jsx6(Text2, { mt: 1, fontSize: "sm", color: "red.600", children: error?.message ?? "Erro inesperado." }),
|
|
483
706
|
error?.requestId ? /* @__PURE__ */ jsxs2(Text2, { mt: 2, fontSize: "xs", color: "red.400", children: [
|
|
484
707
|
"requestId: ",
|
|
485
708
|
error.requestId
|
|
@@ -488,8 +711,44 @@ function ErrorState({
|
|
|
488
711
|
}
|
|
489
712
|
|
|
490
713
|
// src/components/internal/DataGrid.tsx
|
|
491
|
-
import { jsx as
|
|
492
|
-
|
|
714
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
715
|
+
function SearchIcon() {
|
|
716
|
+
return /* @__PURE__ */ jsxs3("svg", { viewBox: "0 0 20 20", width: "17", height: "17", "aria-hidden": "true", children: [
|
|
717
|
+
/* @__PURE__ */ jsx7("circle", { cx: "8.5", cy: "8.5", r: "5.25", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
718
|
+
/* @__PURE__ */ jsx7("path", { d: "m12.4 12.4 4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
719
|
+
] });
|
|
720
|
+
}
|
|
721
|
+
function EditIcon() {
|
|
722
|
+
return /* @__PURE__ */ jsxs3("svg", { viewBox: "0 0 20 20", width: "15", height: "15", "aria-hidden": "true", children: [
|
|
723
|
+
/* @__PURE__ */ jsx7("path", { d: "M4 14.5V16h1.5L14.7 6.8l-1.5-1.5L4 14.5Z", fill: "none", stroke: "currentColor", strokeWidth: "1.45", strokeLinejoin: "round" }),
|
|
724
|
+
/* @__PURE__ */ jsx7("path", { d: "m12.5 6 1.5-1.5a1.05 1.05 0 0 1 1.5 0l.1.1a1.05 1.05 0 0 1 0 1.5L14.1 7.6", fill: "none", stroke: "currentColor", strokeWidth: "1.45", strokeLinecap: "round" })
|
|
725
|
+
] });
|
|
726
|
+
}
|
|
727
|
+
function TrashIcon() {
|
|
728
|
+
return /* @__PURE__ */ jsxs3("svg", { viewBox: "0 0 20 20", width: "15", height: "15", "aria-hidden": "true", children: [
|
|
729
|
+
/* @__PURE__ */ jsx7("path", { d: "M5.5 6.5h9l-.6 9H6.1l-.6-9Zm2-2h5l.7 2H6.8l.7-2Z", fill: "none", stroke: "currentColor", strokeWidth: "1.35", strokeLinejoin: "round" }),
|
|
730
|
+
/* @__PURE__ */ jsx7("path", { d: "M4.5 6.5h11", fill: "none", stroke: "currentColor", strokeWidth: "1.35", strokeLinecap: "round" })
|
|
731
|
+
] });
|
|
732
|
+
}
|
|
733
|
+
function ChevronIcon({ direction }) {
|
|
734
|
+
return /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 20 20", width: "17", height: "17", "aria-hidden": "true", style: { transform: direction === "right" ? "rotate(180deg)" : void 0 }, children: /* @__PURE__ */ jsx7("path", { d: "m12 5-5 5 5 5", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
735
|
+
}
|
|
736
|
+
function PlusIcon() {
|
|
737
|
+
return /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 20 20", width: "17", height: "17", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M10 4v12M4 10h12", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }) });
|
|
738
|
+
}
|
|
739
|
+
function renderCell(value, kind) {
|
|
740
|
+
const formatted = formatCell(value, kind);
|
|
741
|
+
if (kind === "enum") {
|
|
742
|
+
const normalized = String(value ?? "").toLowerCase();
|
|
743
|
+
const palette = /erro|cancel|inativo|recus|critica/.test(normalized) ? "red" : /pendente|aguardando|rascunho/.test(normalized) ? "orange" : /processando|conferencia|integrando|validando/.test(normalized) ? "blue" : /ativo|sucesso|conclu|aplicada|disponivel|aprov/.test(normalized) ? "green" : "gray";
|
|
744
|
+
return /* @__PURE__ */ jsx7(Badge, { colorPalette: palette, variant: "subtle", borderRadius: "full", px: "7px", py: "3px", fontSize: "9px", fontWeight: "600", textTransform: "capitalize", children: formatted });
|
|
745
|
+
}
|
|
746
|
+
if (kind === "boolean") {
|
|
747
|
+
const active = value === true || value === "true";
|
|
748
|
+
return /* @__PURE__ */ jsx7(Badge, { colorPalette: active ? "green" : "gray", variant: "subtle", borderRadius: "full", px: "7px", py: "3px", fontSize: "9px", fontWeight: "600", children: formatted });
|
|
749
|
+
}
|
|
750
|
+
return formatted;
|
|
751
|
+
}
|
|
493
752
|
function DataGrid({
|
|
494
753
|
resource,
|
|
495
754
|
resourceKey,
|
|
@@ -500,104 +759,544 @@ function DataGrid({
|
|
|
500
759
|
onDelete,
|
|
501
760
|
toolbarExtra
|
|
502
761
|
}) {
|
|
503
|
-
const [pageIndex, setPageIndex] =
|
|
504
|
-
const [
|
|
505
|
-
const [
|
|
762
|
+
const [pageIndex, setPageIndex] = useState3(0);
|
|
763
|
+
const [pageSize, setPageSize] = useState3(20);
|
|
764
|
+
const [searchTerm, setSearchTerm] = useState3("");
|
|
765
|
+
const deferredSearch = useDeferredValue(searchTerm);
|
|
766
|
+
const [sort, setSort] = useState3(void 0);
|
|
506
767
|
const query = useQuery2({
|
|
507
|
-
queryKey: ["oon", "list", resourceKey, { pageIndex, searchTerm, sort }],
|
|
508
|
-
queryFn: () => resource.list({ pageIndex, pageSize
|
|
768
|
+
queryKey: ["oon", "list", resourceKey, { pageIndex, pageSize, searchTerm: deferredSearch, sort }],
|
|
769
|
+
queryFn: () => resource.list({ pageIndex, pageSize, searchTerm: deferredSearch || void 0, sort }),
|
|
509
770
|
placeholderData: keepPreviousData
|
|
510
771
|
});
|
|
511
772
|
const toggleSort = (field) => {
|
|
512
773
|
setPageIndex(0);
|
|
513
|
-
setSort((
|
|
514
|
-
if (
|
|
515
|
-
if (
|
|
774
|
+
setSort((previous) => {
|
|
775
|
+
if (previous === `${field}.asc`) return `${field}.desc`;
|
|
776
|
+
if (previous === `${field}.desc`) return void 0;
|
|
516
777
|
return `${field}.asc`;
|
|
517
778
|
});
|
|
518
779
|
};
|
|
519
780
|
const pagination = query.data?.pagination;
|
|
520
781
|
const rows = query.data?.results ?? [];
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
782
|
+
const totalItems = pagination?.totalItems ?? rows.length;
|
|
783
|
+
const totalPages = Math.max(1, pagination?.totalPages ?? 1);
|
|
784
|
+
const currentPage = Math.min(pageIndex + 1, totalPages);
|
|
785
|
+
const actionCount = onEdit || onDelete ? 1 : 0;
|
|
786
|
+
return /* @__PURE__ */ jsxs3(Box2, { bg: "white", borderWidth: "1px", borderColor: "#E5E9ED", borderRadius: "10px", boxShadow: "0 1px 2px rgba(15,23,42,.06)", overflow: "hidden", children: [
|
|
787
|
+
/* @__PURE__ */ jsxs3(Flex2, { minH: "60px", px: "14px", py: "12px", align: "center", gap: 4, borderBottomWidth: "1px", borderColor: "#E5E9ED", children: [
|
|
788
|
+
search ? /* @__PURE__ */ jsxs3(Flex2, { minW: { base: "100%", md: "min(420px, 55vw)" }, maxW: "420px", h: "35px", px: "10px", align: "center", gap: 2, color: "#98A2B3", bg: "white", borderWidth: "1px", borderColor: "#D5DDE3", borderRadius: "6px", _focusWithin: { borderColor: "#0F8DD0", boxShadow: "0 0 0 3px rgba(15,141,208,.10)" }, children: [
|
|
789
|
+
/* @__PURE__ */ jsx7(SearchIcon, {}),
|
|
790
|
+
/* @__PURE__ */ jsx7(
|
|
791
|
+
Input,
|
|
792
|
+
{
|
|
793
|
+
value: searchTerm,
|
|
794
|
+
onChange: (event) => {
|
|
795
|
+
setSearchTerm(event.target.value);
|
|
796
|
+
setPageIndex(0);
|
|
797
|
+
},
|
|
798
|
+
placeholder: "Pesquisar registros...",
|
|
799
|
+
h: "100%",
|
|
800
|
+
p: 0,
|
|
801
|
+
color: "#475467",
|
|
802
|
+
bg: "transparent",
|
|
803
|
+
border: 0,
|
|
804
|
+
borderRadius: 0,
|
|
805
|
+
fontSize: "11px",
|
|
806
|
+
_placeholder: { color: "#A6B0BC" },
|
|
807
|
+
_focusVisible: { boxShadow: "none", outline: "none" }
|
|
533
808
|
}
|
|
534
|
-
|
|
535
|
-
) : null,
|
|
536
|
-
/* @__PURE__ */
|
|
809
|
+
)
|
|
810
|
+
] }) : null,
|
|
811
|
+
/* @__PURE__ */ jsx7(Spacer, {}),
|
|
812
|
+
/* @__PURE__ */ jsxs3(Flex2, { align: "center", gap: 3, color: "#98A2B3", fontSize: "10px", children: [
|
|
813
|
+
query.isFetching ? /* @__PURE__ */ jsx7(Text3, { children: "Atualizando..." }) : null,
|
|
814
|
+
/* @__PURE__ */ jsxs3(Text3, { as: "strong", color: "#667085", fontWeight: "600", children: [
|
|
815
|
+
totalItems,
|
|
816
|
+
" registro",
|
|
817
|
+
totalItems === 1 ? "" : "s"
|
|
818
|
+
] })
|
|
819
|
+
] }),
|
|
537
820
|
toolbarExtra,
|
|
538
|
-
onCreate ? /* @__PURE__ */
|
|
821
|
+
onCreate ? /* @__PURE__ */ jsxs3(Button2, { minH: "35px", px: "13px", colorPalette: "brand", borderRadius: "6px", fontSize: "11px", fontWeight: "600", onClick: onCreate, children: [
|
|
822
|
+
/* @__PURE__ */ jsx7(PlusIcon, {}),
|
|
823
|
+
" Novo registro"
|
|
824
|
+
] }) : null
|
|
539
825
|
] }),
|
|
540
|
-
query.isLoading ? /* @__PURE__ */
|
|
541
|
-
/* @__PURE__ */
|
|
542
|
-
columns.map((
|
|
826
|
+
query.isLoading ? /* @__PURE__ */ jsx7(LoadingScreen, {}) : query.isError ? /* @__PURE__ */ jsx7(ErrorState, { error: query.error }) : /* @__PURE__ */ jsx7(Box2, { overflowX: "auto", children: /* @__PURE__ */ jsxs3(Table.Root, { size: "sm", minW: "900px", borderCollapse: "collapse", children: [
|
|
827
|
+
/* @__PURE__ */ jsx7(Table.Header, { children: /* @__PURE__ */ jsxs3(Table.Row, { bg: "#F4F7F9", children: [
|
|
828
|
+
columns.map((column) => /* @__PURE__ */ jsxs3(
|
|
543
829
|
Table.ColumnHeader,
|
|
544
830
|
{
|
|
545
|
-
|
|
546
|
-
|
|
831
|
+
h: "39px",
|
|
832
|
+
px: "10px",
|
|
833
|
+
py: "8px",
|
|
834
|
+
color: "#667085",
|
|
835
|
+
borderBottomWidth: "1px",
|
|
836
|
+
borderColor: "#D5DDE3",
|
|
837
|
+
fontSize: "10px",
|
|
838
|
+
fontWeight: "600",
|
|
839
|
+
letterSpacing: ".015em",
|
|
840
|
+
textTransform: "none",
|
|
841
|
+
whiteSpace: "nowrap",
|
|
842
|
+
cursor: column.sortable ? "pointer" : void 0,
|
|
843
|
+
onClick: column.sortable ? () => toggleSort(column.field) : void 0,
|
|
547
844
|
children: [
|
|
548
|
-
|
|
549
|
-
sort?.startsWith(`${
|
|
845
|
+
column.label ?? column.field,
|
|
846
|
+
sort?.startsWith(`${column.field}.`) ? sort.endsWith(".asc") ? " \u25B2" : " \u25BC" : ""
|
|
550
847
|
]
|
|
551
848
|
},
|
|
552
|
-
|
|
849
|
+
column.field
|
|
553
850
|
)),
|
|
554
|
-
|
|
851
|
+
actionCount ? /* @__PURE__ */ jsx7(Table.ColumnHeader, { w: "84px", h: "39px", px: "10px", py: "8px", color: "#667085", borderBottomWidth: "1px", borderColor: "#D5DDE3", fontSize: "10px", fontWeight: "600", textAlign: "center", children: "A\xE7\xF5es" }) : null
|
|
555
852
|
] }) }),
|
|
556
|
-
/* @__PURE__ */
|
|
557
|
-
columns.map((
|
|
558
|
-
|
|
559
|
-
onEdit ? /* @__PURE__ */
|
|
560
|
-
onDelete ? /* @__PURE__ */
|
|
853
|
+
/* @__PURE__ */ jsx7(Table.Body, { children: rows.length === 0 ? /* @__PURE__ */ jsx7(Table.Row, { children: /* @__PURE__ */ jsx7(Table.Cell, { colSpan: columns.length + actionCount, children: /* @__PURE__ */ jsx7(Text3, { minH: "140px", display: "grid", placeItems: "center", color: "#98A2B3", fontSize: "12px", children: "Nenhum registro encontrado." }) }) }) : rows.map((row, index) => /* @__PURE__ */ jsxs3(Table.Row, { bg: index % 2 ? "#FBFCFD" : "white", _hover: { bg: "#F0F7FF" }, children: [
|
|
854
|
+
columns.map((column) => /* @__PURE__ */ jsx7(Table.Cell, { maxW: "250px", h: "42px", px: "10px", py: "8px", overflow: "hidden", color: "#52606D", borderBottomWidth: "1px", borderColor: "#EEF1F3", fontSize: "11px", textOverflow: "ellipsis", whiteSpace: "nowrap", children: renderCell(row[column.field], column.kind) }, column.field)),
|
|
855
|
+
actionCount ? /* @__PURE__ */ jsx7(Table.Cell, { h: "42px", px: "10px", py: "5px", borderBottomWidth: "1px", borderColor: "#EEF1F3", textAlign: "center", children: /* @__PURE__ */ jsxs3(Flex2, { justify: "center", gap: "5px", children: [
|
|
856
|
+
onEdit ? /* @__PURE__ */ jsx7(Button2, { type: "button", "aria-label": "Editar", w: "31px", minW: "31px", h: "31px", p: 0, color: "#667085", bg: "white", borderWidth: "1px", borderColor: "#E5E9ED", borderRadius: "6px", onClick: () => onEdit(row), _hover: { color: "#036491", bg: "#F0F7FF", borderColor: "#B8D9EB" }, children: /* @__PURE__ */ jsx7(EditIcon, {}) }) : null,
|
|
857
|
+
onDelete ? /* @__PURE__ */ jsx7(Button2, { type: "button", "aria-label": "Excluir", w: "31px", minW: "31px", h: "31px", p: 0, color: "#667085", bg: "white", borderWidth: "1px", borderColor: "#E5E9ED", borderRadius: "6px", onClick: () => onDelete(row), _hover: { color: "#B42318", bg: "#FFF5F5", borderColor: "#FECACA" }, children: /* @__PURE__ */ jsx7(TrashIcon, {}) }) : null
|
|
561
858
|
] }) }) : null
|
|
562
|
-
] }, row._id ??
|
|
859
|
+
] }, row._id ?? index)) })
|
|
563
860
|
] }) }),
|
|
564
|
-
|
|
565
|
-
/* @__PURE__ */ jsxs3(
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
861
|
+
/* @__PURE__ */ jsxs3(Flex2, { minH: "56px", px: "14px", py: "10px", align: "center", justify: "flex-end", gap: "18px", color: "#7B8794", borderTopWidth: "1px", borderColor: "#E5E9ED", fontSize: "10px", flexWrap: "wrap", children: [
|
|
862
|
+
/* @__PURE__ */ jsxs3(Flex2, { align: "center", gap: 2, children: [
|
|
863
|
+
/* @__PURE__ */ jsx7(Text3, { children: "Linhas por p\xE1gina" }),
|
|
864
|
+
/* @__PURE__ */ jsxs3(NativeSelect.Root, { size: "xs", w: "68px", children: [
|
|
865
|
+
/* @__PURE__ */ jsx7(
|
|
866
|
+
NativeSelect.Field,
|
|
867
|
+
{
|
|
868
|
+
value: pageSize,
|
|
869
|
+
h: "30px",
|
|
870
|
+
pl: "8px",
|
|
871
|
+
pr: "26px",
|
|
872
|
+
color: "#52606D",
|
|
873
|
+
bg: "white",
|
|
874
|
+
borderColor: "#D5DDE3",
|
|
875
|
+
borderRadius: "5px",
|
|
876
|
+
fontSize: "10px",
|
|
877
|
+
onChange: (event) => {
|
|
878
|
+
setPageSize(Number(event.currentTarget.value));
|
|
879
|
+
setPageIndex(0);
|
|
880
|
+
},
|
|
881
|
+
children: [10, 20, 50, 100].map((size) => /* @__PURE__ */ jsx7("option", { value: size, children: size }, size))
|
|
882
|
+
}
|
|
883
|
+
),
|
|
884
|
+
/* @__PURE__ */ jsx7(NativeSelect.Indicator, {})
|
|
885
|
+
] })
|
|
886
|
+
] }),
|
|
887
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
888
|
+
"P\xE1gina ",
|
|
889
|
+
currentPage,
|
|
569
890
|
" de ",
|
|
570
|
-
|
|
891
|
+
totalPages
|
|
571
892
|
] }),
|
|
572
|
-
/* @__PURE__ */
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
size: "xs",
|
|
578
|
-
variant: "outline",
|
|
579
|
-
disabled: pagination.currentPage >= pagination.totalPages,
|
|
580
|
-
onClick: () => setPageIndex((p) => p + 1),
|
|
581
|
-
children: "Pr\xF3xima"
|
|
582
|
-
}
|
|
583
|
-
)
|
|
584
|
-
] }) : null
|
|
893
|
+
/* @__PURE__ */ jsxs3(Flex2, { gap: "5px", children: [
|
|
894
|
+
/* @__PURE__ */ jsx7(Button2, { type: "button", "aria-label": "P\xE1gina anterior", w: "31px", minW: "31px", h: "31px", p: 0, color: "#667085", bg: "white", borderWidth: "1px", borderColor: "#E5E9ED", borderRadius: "6px", disabled: pageIndex <= 0, onClick: () => setPageIndex((current) => Math.max(0, current - 1)), _hover: { color: "#036491", bg: "#F0F7FF", borderColor: "#B8D9EB" }, children: /* @__PURE__ */ jsx7(ChevronIcon, { direction: "left" }) }),
|
|
895
|
+
/* @__PURE__ */ jsx7(Button2, { type: "button", "aria-label": "Pr\xF3xima p\xE1gina", w: "31px", minW: "31px", h: "31px", p: 0, color: "#667085", bg: "white", borderWidth: "1px", borderColor: "#E5E9ED", borderRadius: "6px", disabled: currentPage >= totalPages, onClick: () => setPageIndex((current) => Math.min(totalPages - 1, current + 1)), _hover: { color: "#036491", bg: "#F0F7FF", borderColor: "#B8D9EB" }, children: /* @__PURE__ */ jsx7(ChevronIcon, { direction: "right" }) })
|
|
896
|
+
] })
|
|
897
|
+
] })
|
|
585
898
|
] });
|
|
586
899
|
}
|
|
587
900
|
|
|
588
901
|
// src/components/internal/DynamicForm.tsx
|
|
589
|
-
import { useState as
|
|
590
|
-
import { Box as
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
902
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
903
|
+
import { Box as Box4, Button as Button4, Flex as Flex4, Heading as Heading2, Input as Input3, NativeSelect as NativeSelect2, SimpleGrid, Stack as Stack2, Text as Text5, Textarea } from "@chakra-ui/react";
|
|
904
|
+
|
|
905
|
+
// src/components/internal/ReferenceSelect.tsx
|
|
906
|
+
import { useEffect as useEffect3, useMemo as useMemo3, useRef, useState as useState4 } from "react";
|
|
907
|
+
import { Box as Box3, Button as Button3, Flex as Flex3, Input as Input2, Spinner as Spinner2, Stack, Text as Text4 } from "@chakra-ui/react";
|
|
908
|
+
import { useQuery as useQuery3 } from "@tanstack/react-query";
|
|
909
|
+
|
|
910
|
+
// src/components/internal/referenceUtils.ts
|
|
911
|
+
var COMMON_LABEL_FIELDS = [
|
|
912
|
+
"nome",
|
|
913
|
+
"razaoSocial",
|
|
914
|
+
"razao_social",
|
|
915
|
+
"descricao",
|
|
916
|
+
"numero",
|
|
917
|
+
"codigoIndividual",
|
|
918
|
+
"codigo_individual",
|
|
919
|
+
"codigo",
|
|
920
|
+
"label",
|
|
921
|
+
"email"
|
|
922
|
+
];
|
|
923
|
+
function primitiveText(value) {
|
|
924
|
+
if (value == null || value === "") return null;
|
|
925
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
928
|
+
function referenceOptionLabel(row, searchableFields = []) {
|
|
929
|
+
const ordered = [...COMMON_LABEL_FIELDS, ...searchableFields];
|
|
930
|
+
const seenFields = /* @__PURE__ */ new Set();
|
|
931
|
+
const seenValues = /* @__PURE__ */ new Set();
|
|
932
|
+
const values = [];
|
|
933
|
+
for (const field of ordered) {
|
|
934
|
+
if (seenFields.has(field)) continue;
|
|
935
|
+
seenFields.add(field);
|
|
936
|
+
const text = primitiveText(row[field]);
|
|
937
|
+
if (!text || seenValues.has(text)) continue;
|
|
938
|
+
seenValues.add(text);
|
|
939
|
+
values.push(text);
|
|
940
|
+
if (values.length === 2) break;
|
|
941
|
+
}
|
|
942
|
+
if (values.length) return values.join(" \u2014 ");
|
|
943
|
+
return primitiveText(row._id) ?? primitiveText(row.id) ?? "Registro sem identifica\xE7\xE3o";
|
|
944
|
+
}
|
|
945
|
+
function initialReferenceLabel(value) {
|
|
946
|
+
if (!value || typeof value !== "object") return "";
|
|
947
|
+
return referenceOptionLabel(value);
|
|
948
|
+
}
|
|
949
|
+
function referenceId(row) {
|
|
950
|
+
return String(row._id ?? row.id ?? "");
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/components/internal/ReferenceSelect.tsx
|
|
954
|
+
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
955
|
+
function SearchIcon2() {
|
|
956
|
+
return /* @__PURE__ */ jsxs4("svg", { viewBox: "0 0 20 20", width: "15", height: "15", "aria-hidden": "true", children: [
|
|
957
|
+
/* @__PURE__ */ jsx8("circle", { cx: "8.5", cy: "8.5", r: "5.25", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
958
|
+
/* @__PURE__ */ jsx8("path", { d: "m12.4 12.4 4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
959
|
+
] });
|
|
960
|
+
}
|
|
961
|
+
function ChevronIcon2({ open }) {
|
|
962
|
+
return /* @__PURE__ */ jsx8(
|
|
963
|
+
"svg",
|
|
964
|
+
{
|
|
965
|
+
viewBox: "0 0 20 20",
|
|
966
|
+
width: "16",
|
|
967
|
+
height: "16",
|
|
968
|
+
"aria-hidden": "true",
|
|
969
|
+
style: { display: "block", transition: "transform .15s ease", transform: open ? "rotate(180deg)" : "rotate(0deg)" },
|
|
970
|
+
children: /* @__PURE__ */ jsx8("path", { d: "M5.5 7.5 10 12l4.5-4.5", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
971
|
+
}
|
|
972
|
+
);
|
|
597
973
|
}
|
|
598
|
-
function
|
|
974
|
+
function ClearIcon() {
|
|
975
|
+
return /* @__PURE__ */ jsx8("svg", { viewBox: "0 0 20 20", width: "14", height: "14", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "m6 6 8 8M14 6l-8 8", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
|
|
976
|
+
}
|
|
977
|
+
function ReferenceSelect({
|
|
978
|
+
field,
|
|
979
|
+
inputId,
|
|
980
|
+
errorId,
|
|
981
|
+
label,
|
|
982
|
+
refModel,
|
|
983
|
+
value,
|
|
984
|
+
initialValue,
|
|
985
|
+
required,
|
|
986
|
+
invalid,
|
|
987
|
+
error,
|
|
988
|
+
onChange
|
|
989
|
+
}) {
|
|
990
|
+
const initialLabel = useMemo3(() => initialReferenceLabel(initialValue), [initialValue]);
|
|
991
|
+
const [selectedLabel, setSelectedLabel] = useState4(initialLabel);
|
|
992
|
+
const [search, setSearch] = useState4("");
|
|
993
|
+
const [debouncedSearch, setDebouncedSearch] = useState4("");
|
|
994
|
+
const [open, setOpen] = useState4(false);
|
|
995
|
+
const [activeIndex, setActiveIndex] = useState4(-1);
|
|
996
|
+
const rootRef = useRef(null);
|
|
997
|
+
const searchRef = useRef(null);
|
|
998
|
+
const optionRefs = useRef([]);
|
|
999
|
+
const schemaQuery = useModelSchema(refModel);
|
|
1000
|
+
const schema = schemaQuery.data;
|
|
1001
|
+
const basePath = schema?.basePath ?? `/${refModel.toLowerCase()}s`;
|
|
1002
|
+
const resource = useOonResource(basePath);
|
|
1003
|
+
useEffect3(() => {
|
|
1004
|
+
const timer = window.setTimeout(() => setDebouncedSearch(search.trim()), 250);
|
|
1005
|
+
return () => window.clearTimeout(timer);
|
|
1006
|
+
}, [search]);
|
|
1007
|
+
useEffect3(() => {
|
|
1008
|
+
if (!value) setSelectedLabel("");
|
|
1009
|
+
else if (initialLabel) setSelectedLabel(initialLabel);
|
|
1010
|
+
}, [initialLabel, value]);
|
|
1011
|
+
useEffect3(() => {
|
|
1012
|
+
if (!open) return;
|
|
1013
|
+
const onPointerDown = (event) => {
|
|
1014
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
1015
|
+
setOpen(false);
|
|
1016
|
+
setActiveIndex(-1);
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
1020
|
+
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
1021
|
+
}, [open]);
|
|
1022
|
+
useEffect3(() => {
|
|
1023
|
+
if (!open) return;
|
|
1024
|
+
window.setTimeout(() => searchRef.current?.focus(), 0);
|
|
1025
|
+
}, [open]);
|
|
1026
|
+
const listQuery = useQuery3({
|
|
1027
|
+
queryKey: ["oon", "reference-options", refModel, basePath, debouncedSearch],
|
|
1028
|
+
queryFn: () => resource.list({ pageIndex: 0, pageSize: 30, searchTerm: debouncedSearch || void 0 }),
|
|
1029
|
+
enabled: !!schema && open,
|
|
1030
|
+
staleTime: 3e4
|
|
1031
|
+
});
|
|
1032
|
+
const currentQuery = useQuery3({
|
|
1033
|
+
queryKey: ["oon", "reference-current", refModel, basePath, value],
|
|
1034
|
+
queryFn: () => resource.get(value),
|
|
1035
|
+
enabled: !!schema && !!value && !selectedLabel,
|
|
1036
|
+
staleTime: 5 * 6e4
|
|
1037
|
+
});
|
|
1038
|
+
useEffect3(() => {
|
|
1039
|
+
if (!currentQuery.data) return;
|
|
1040
|
+
setSelectedLabel(referenceOptionLabel(currentQuery.data, schema?.searchable));
|
|
1041
|
+
}, [currentQuery.data, schema?.searchable]);
|
|
1042
|
+
const items = listQuery.data?.results ?? [];
|
|
1043
|
+
const listId = `oon-reference-${field}`;
|
|
1044
|
+
const resolvedInputId = inputId ?? `oon-reference-input-${field}`;
|
|
1045
|
+
const activeOptionId = activeIndex >= 0 ? `${listId}-option-${activeIndex}` : void 0;
|
|
1046
|
+
const openList = (seed = "") => {
|
|
1047
|
+
if (seed) setSearch(seed);
|
|
1048
|
+
setOpen(true);
|
|
1049
|
+
setActiveIndex((current) => current >= 0 ? current : 0);
|
|
1050
|
+
};
|
|
1051
|
+
const closeList = () => {
|
|
1052
|
+
setOpen(false);
|
|
1053
|
+
setActiveIndex(-1);
|
|
1054
|
+
};
|
|
1055
|
+
const choose = (record) => {
|
|
1056
|
+
const id = referenceId(record);
|
|
1057
|
+
if (!id) return;
|
|
1058
|
+
onChange(id);
|
|
1059
|
+
setSelectedLabel(referenceOptionLabel(record, schema?.searchable));
|
|
1060
|
+
setSearch("");
|
|
1061
|
+
setDebouncedSearch("");
|
|
1062
|
+
closeList();
|
|
1063
|
+
};
|
|
1064
|
+
const clear = (event) => {
|
|
1065
|
+
event.preventDefault();
|
|
1066
|
+
event.stopPropagation();
|
|
1067
|
+
onChange("");
|
|
1068
|
+
setSelectedLabel("");
|
|
1069
|
+
setSearch("");
|
|
1070
|
+
setDebouncedSearch("");
|
|
1071
|
+
};
|
|
1072
|
+
const moveActive = (direction) => {
|
|
1073
|
+
if (!items.length) return;
|
|
1074
|
+
setActiveIndex((current) => {
|
|
1075
|
+
const start2 = current < 0 ? direction === 1 ? -1 : 0 : current;
|
|
1076
|
+
const next = (start2 + direction + items.length) % items.length;
|
|
1077
|
+
window.setTimeout(() => optionRefs.current[next]?.scrollIntoView({ block: "nearest" }), 0);
|
|
1078
|
+
return next;
|
|
1079
|
+
});
|
|
1080
|
+
};
|
|
1081
|
+
return /* @__PURE__ */ jsxs4(Box3, { ref: rootRef, position: "relative", children: [
|
|
1082
|
+
/* @__PURE__ */ jsxs4(
|
|
1083
|
+
Button3,
|
|
1084
|
+
{
|
|
1085
|
+
id: resolvedInputId,
|
|
1086
|
+
type: "button",
|
|
1087
|
+
variant: "outline",
|
|
1088
|
+
w: "100%",
|
|
1089
|
+
minH: "37px",
|
|
1090
|
+
h: "auto",
|
|
1091
|
+
px: "10px",
|
|
1092
|
+
py: "8px",
|
|
1093
|
+
justifyContent: "space-between",
|
|
1094
|
+
gap: 2,
|
|
1095
|
+
color: "#475467",
|
|
1096
|
+
bg: "white",
|
|
1097
|
+
borderColor: invalid ? "#DC2626" : "#D5DDE3",
|
|
1098
|
+
borderRadius: "6px",
|
|
1099
|
+
boxShadow: invalid ? "0 0 0 3px rgba(220,38,38,.08)" : void 0,
|
|
1100
|
+
fontSize: "11px",
|
|
1101
|
+
fontWeight: "400",
|
|
1102
|
+
textAlign: "left",
|
|
1103
|
+
"aria-haspopup": "listbox",
|
|
1104
|
+
"aria-expanded": open,
|
|
1105
|
+
"aria-controls": listId,
|
|
1106
|
+
"aria-activedescendant": open ? activeOptionId : void 0,
|
|
1107
|
+
"aria-required": required,
|
|
1108
|
+
"aria-invalid": invalid || void 0,
|
|
1109
|
+
"aria-describedby": error ? errorId : void 0,
|
|
1110
|
+
onClick: () => open ? closeList() : openList(),
|
|
1111
|
+
onKeyDown: (event) => {
|
|
1112
|
+
if (event.key === "ArrowDown") {
|
|
1113
|
+
event.preventDefault();
|
|
1114
|
+
if (!open) openList();
|
|
1115
|
+
else moveActive(1);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (event.key === "ArrowUp") {
|
|
1119
|
+
event.preventDefault();
|
|
1120
|
+
if (!open) openList();
|
|
1121
|
+
else moveActive(-1);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (event.key === "Enter" && open && activeIndex >= 0 && items[activeIndex]) {
|
|
1125
|
+
event.preventDefault();
|
|
1126
|
+
choose(items[activeIndex]);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
if (event.key === "Escape" && open) {
|
|
1130
|
+
event.preventDefault();
|
|
1131
|
+
event.stopPropagation();
|
|
1132
|
+
closeList();
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
if (!event.ctrlKey && !event.metaKey && !event.altKey && event.key.length === 1) {
|
|
1136
|
+
event.preventDefault();
|
|
1137
|
+
openList(event.key);
|
|
1138
|
+
}
|
|
1139
|
+
},
|
|
1140
|
+
_hover: { bg: "white", borderColor: "#B9C7D1" },
|
|
1141
|
+
_focusVisible: { borderColor: invalid ? "#DC2626" : "#0F8DD0", boxShadow: invalid ? "0 0 0 3px rgba(220,38,38,.08)" : "0 0 0 3px rgba(15,141,208,.10)" },
|
|
1142
|
+
children: [
|
|
1143
|
+
/* @__PURE__ */ jsx8(Text4, { as: "span", minW: 0, flex: "1", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: selectedLabel || value ? "#475467" : "#98A2B3", children: selectedLabel || (value ? String(value) : "Selecione ou digite para pesquisar...") }),
|
|
1144
|
+
/* @__PURE__ */ jsxs4(Flex3, { as: "span", align: "center", gap: 1, flex: "0 0 auto", color: "#98A2B3", children: [
|
|
1145
|
+
value ? /* @__PURE__ */ jsx8(
|
|
1146
|
+
Box3,
|
|
1147
|
+
{
|
|
1148
|
+
as: "span",
|
|
1149
|
+
role: "button",
|
|
1150
|
+
tabIndex: 0,
|
|
1151
|
+
"aria-label": `Limpar ${label}`,
|
|
1152
|
+
w: "20px",
|
|
1153
|
+
h: "20px",
|
|
1154
|
+
display: "grid",
|
|
1155
|
+
placeItems: "center",
|
|
1156
|
+
borderRadius: "4px",
|
|
1157
|
+
onClick: clear,
|
|
1158
|
+
onKeyDown: (event) => {
|
|
1159
|
+
if (event.key === "Enter" || event.key === " ") clear(event);
|
|
1160
|
+
},
|
|
1161
|
+
_hover: { color: "#B42318", bg: "#FFF1F1" },
|
|
1162
|
+
children: /* @__PURE__ */ jsx8(ClearIcon, {})
|
|
1163
|
+
}
|
|
1164
|
+
) : null,
|
|
1165
|
+
/* @__PURE__ */ jsx8(ChevronIcon2, { open })
|
|
1166
|
+
] })
|
|
1167
|
+
]
|
|
1168
|
+
}
|
|
1169
|
+
),
|
|
1170
|
+
open ? /* @__PURE__ */ jsxs4(
|
|
1171
|
+
Box3,
|
|
1172
|
+
{
|
|
1173
|
+
position: "absolute",
|
|
1174
|
+
zIndex: 120,
|
|
1175
|
+
top: "calc(100% + 5px)",
|
|
1176
|
+
left: 0,
|
|
1177
|
+
right: 0,
|
|
1178
|
+
overflow: "hidden",
|
|
1179
|
+
bg: "white",
|
|
1180
|
+
borderWidth: "1px",
|
|
1181
|
+
borderColor: "#D5DDE3",
|
|
1182
|
+
borderRadius: "8px",
|
|
1183
|
+
boxShadow: "0 10px 30px rgba(15,23,42,.10)",
|
|
1184
|
+
children: [
|
|
1185
|
+
/* @__PURE__ */ jsxs4(Flex3, { h: "38px", px: "10px", align: "center", gap: "7px", color: "#98A2B3", borderBottomWidth: "1px", borderColor: "#E5E9ED", children: [
|
|
1186
|
+
/* @__PURE__ */ jsx8(SearchIcon2, {}),
|
|
1187
|
+
/* @__PURE__ */ jsx8(
|
|
1188
|
+
Input2,
|
|
1189
|
+
{
|
|
1190
|
+
ref: searchRef,
|
|
1191
|
+
value: search,
|
|
1192
|
+
onChange: (event) => {
|
|
1193
|
+
setSearch(event.target.value);
|
|
1194
|
+
setActiveIndex(0);
|
|
1195
|
+
},
|
|
1196
|
+
placeholder: "Digite para filtrar...",
|
|
1197
|
+
h: "100%",
|
|
1198
|
+
p: 0,
|
|
1199
|
+
color: "#475467",
|
|
1200
|
+
bg: "transparent",
|
|
1201
|
+
border: 0,
|
|
1202
|
+
borderRadius: 0,
|
|
1203
|
+
fontSize: "11px",
|
|
1204
|
+
_focusVisible: { boxShadow: "none", outline: "none" },
|
|
1205
|
+
onKeyDown: (event) => {
|
|
1206
|
+
if (event.key === "ArrowDown") {
|
|
1207
|
+
event.preventDefault();
|
|
1208
|
+
moveActive(1);
|
|
1209
|
+
} else if (event.key === "ArrowUp") {
|
|
1210
|
+
event.preventDefault();
|
|
1211
|
+
moveActive(-1);
|
|
1212
|
+
} else if (event.key === "Enter" && activeIndex >= 0 && items[activeIndex]) {
|
|
1213
|
+
event.preventDefault();
|
|
1214
|
+
choose(items[activeIndex]);
|
|
1215
|
+
} else if (event.key === "Escape") {
|
|
1216
|
+
event.preventDefault();
|
|
1217
|
+
event.stopPropagation();
|
|
1218
|
+
closeList();
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
)
|
|
1223
|
+
] }),
|
|
1224
|
+
/* @__PURE__ */ jsx8(Stack, { id: listId, role: "listbox", gap: 0, maxH: "230px", p: "5px", overflowY: "auto", children: schemaQuery.isLoading || listQuery.isLoading ? /* @__PURE__ */ jsxs4(Flex3, { minH: "54px", align: "center", justify: "center", gap: 2, color: "#98A2B3", fontSize: "10px", children: [
|
|
1225
|
+
/* @__PURE__ */ jsx8(Spinner2, { size: "xs" }),
|
|
1226
|
+
" Carregando..."
|
|
1227
|
+
] }) : schemaQuery.isError || listQuery.isError ? /* @__PURE__ */ jsx8(Text4, { py: "18px", px: "10px", color: "#B42318", fontSize: "10px", textAlign: "center", children: "N\xE3o foi poss\xEDvel carregar as op\xE7\xF5es." }) : items.length === 0 ? /* @__PURE__ */ jsx8(Text4, { py: "18px", px: "10px", color: "#98A2B3", fontSize: "10px", textAlign: "center", children: "Nenhum registro encontrado." }) : items.map((record, index) => {
|
|
1228
|
+
const id = referenceId(record);
|
|
1229
|
+
if (!id) return null;
|
|
1230
|
+
const selected = String(id) === String(value);
|
|
1231
|
+
const active = index === activeIndex;
|
|
1232
|
+
const optionLabel = referenceOptionLabel(record, schema?.searchable);
|
|
1233
|
+
const status = record.status;
|
|
1234
|
+
return /* @__PURE__ */ jsxs4(
|
|
1235
|
+
Button3,
|
|
1236
|
+
{
|
|
1237
|
+
id: `${listId}-option-${index}`,
|
|
1238
|
+
ref: (element) => {
|
|
1239
|
+
optionRefs.current[index] = element;
|
|
1240
|
+
},
|
|
1241
|
+
type: "button",
|
|
1242
|
+
role: "option",
|
|
1243
|
+
"aria-selected": selected,
|
|
1244
|
+
w: "100%",
|
|
1245
|
+
minH: "38px",
|
|
1246
|
+
h: "auto",
|
|
1247
|
+
px: "9px",
|
|
1248
|
+
py: "7px",
|
|
1249
|
+
display: "flex",
|
|
1250
|
+
flexDirection: "column",
|
|
1251
|
+
alignItems: "flex-start",
|
|
1252
|
+
gap: 0,
|
|
1253
|
+
color: active || selected ? "#014364" : "#475467",
|
|
1254
|
+
bg: active || selected ? "#F0F7FF" : "transparent",
|
|
1255
|
+
border: 0,
|
|
1256
|
+
borderRadius: "5px",
|
|
1257
|
+
fontWeight: "400",
|
|
1258
|
+
textAlign: "left",
|
|
1259
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
1260
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1261
|
+
onClick: () => choose(record),
|
|
1262
|
+
_hover: { color: "#014364", bg: "#F0F7FF" },
|
|
1263
|
+
children: [
|
|
1264
|
+
/* @__PURE__ */ jsx8(Text4, { as: "strong", fontSize: "10px", fontWeight: "600", children: optionLabel }),
|
|
1265
|
+
status ? /* @__PURE__ */ jsx8(Text4, { as: "small", color: "#98A2B3", fontSize: "9px", textTransform: "capitalize", children: String(status).replace(/_/g, " ") }) : null
|
|
1266
|
+
]
|
|
1267
|
+
},
|
|
1268
|
+
id
|
|
1269
|
+
);
|
|
1270
|
+
}) })
|
|
1271
|
+
]
|
|
1272
|
+
}
|
|
1273
|
+
) : null
|
|
1274
|
+
] });
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
// src/components/internal/DynamicForm.tsx
|
|
1278
|
+
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1279
|
+
var idFor = (field) => `oon-form-field-${field.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
|
|
1280
|
+
var errorIdFor = (field) => `${idFor(field)}-error`;
|
|
1281
|
+
var normalize = (value) => String(value ?? "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
1282
|
+
function CloseIcon() {
|
|
1283
|
+
return /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 20 20", width: "18", height: "18", "aria-hidden": "true", children: /* @__PURE__ */ jsx9("path", { d: "m6 6 8 8M14 6l-8 8", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }) });
|
|
1284
|
+
}
|
|
1285
|
+
function CheckIcon() {
|
|
1286
|
+
return /* @__PURE__ */ jsxs5("svg", { viewBox: "0 0 20 20", width: "16", height: "16", "aria-hidden": "true", children: [
|
|
1287
|
+
/* @__PURE__ */ jsx9("circle", { cx: "10", cy: "10", r: "7", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1288
|
+
/* @__PURE__ */ jsx9("path", { d: "m6.8 10.1 2 2 4.3-4.4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1289
|
+
] });
|
|
1290
|
+
}
|
|
1291
|
+
function AlertIcon() {
|
|
1292
|
+
return /* @__PURE__ */ jsxs5("svg", { viewBox: "0 0 20 20", width: "17", height: "17", "aria-hidden": "true", children: [
|
|
1293
|
+
/* @__PURE__ */ jsx9("circle", { cx: "10", cy: "10", r: "7", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1294
|
+
/* @__PURE__ */ jsx9("path", { d: "M10 6.5v4.2M10 13.5v.1", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
|
|
1295
|
+
] });
|
|
1296
|
+
}
|
|
1297
|
+
function toValue(value, kind) {
|
|
599
1298
|
if (value == null) return "";
|
|
600
|
-
if (kind === "date"
|
|
1299
|
+
if (kind === "date") {
|
|
601
1300
|
try {
|
|
602
1301
|
return new Date(value).toISOString().slice(0, 10);
|
|
603
1302
|
} catch {
|
|
@@ -605,101 +1304,221 @@ function toInputValue(value, kind) {
|
|
|
605
1304
|
}
|
|
606
1305
|
}
|
|
607
1306
|
if (typeof value === "object") {
|
|
608
|
-
const
|
|
609
|
-
return String(
|
|
1307
|
+
const record = value;
|
|
1308
|
+
return String(record._id ?? record.id ?? "");
|
|
610
1309
|
}
|
|
611
1310
|
return String(value);
|
|
612
1311
|
}
|
|
613
|
-
function
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1312
|
+
function coerce(value, kind) {
|
|
1313
|
+
if (value === "") return void 0;
|
|
1314
|
+
if (["number", "currency", "currencyConverted"].includes(kind ?? "")) return Number(value);
|
|
1315
|
+
if (kind === "boolean") return value === "true";
|
|
1316
|
+
return value;
|
|
1317
|
+
}
|
|
1318
|
+
function validate(fields, values) {
|
|
1319
|
+
const errors = {};
|
|
1320
|
+
fields.forEach((field) => {
|
|
1321
|
+
if (field.required && !(values[field.field] ?? "").trim()) errors[field.field] = `${fieldLabel({ name: field.field, label: field.label })} \xE9 obrigat\xF3rio.`;
|
|
618
1322
|
});
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
1323
|
+
return errors;
|
|
1324
|
+
}
|
|
1325
|
+
function messageOf(error) {
|
|
1326
|
+
if (!error) return null;
|
|
1327
|
+
if (typeof error === "string") return error;
|
|
1328
|
+
if (error instanceof Error) return error.message;
|
|
1329
|
+
if (typeof error === "object") {
|
|
1330
|
+
const record = error;
|
|
1331
|
+
if (typeof record.message === "string") return record.message;
|
|
1332
|
+
if (record.error && typeof record.error === "object" && typeof record.error.message === "string") return record.error.message;
|
|
1333
|
+
}
|
|
1334
|
+
return "N\xE3o foi poss\xEDvel salvar o registro.";
|
|
1335
|
+
}
|
|
1336
|
+
function detailErrors(error, fields) {
|
|
1337
|
+
const result = {};
|
|
1338
|
+
const lookup = new Map(fields.flatMap((field) => [[normalize(field.field), field], [normalize(field.label), field]]));
|
|
1339
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1340
|
+
const visit = (value, hint) => {
|
|
1341
|
+
if (value == null) return;
|
|
1342
|
+
if (Array.isArray(value)) return value.forEach((item) => visit(item, hint));
|
|
1343
|
+
if (typeof value !== "object" || seen.has(value)) return;
|
|
1344
|
+
seen.add(value);
|
|
1345
|
+
const record = value;
|
|
1346
|
+
const candidate = String(record.field ?? record.path ?? record.property ?? record.name ?? hint ?? "");
|
|
1347
|
+
const field = lookup.get(normalize(candidate.split(/[.[\]]+/).filter(Boolean).pop() ?? candidate));
|
|
1348
|
+
const raw = record.message ?? record.msg ?? record.reason;
|
|
1349
|
+
if (field && typeof raw === "string") result[field.field] = /required|obrigat/i.test(raw) ? `${fieldLabel({ name: field.field, label: field.label })} \xE9 obrigat\xF3rio.` : raw;
|
|
1350
|
+
Object.entries(record).forEach(([key, nested]) => {
|
|
1351
|
+
const direct = lookup.get(normalize(key));
|
|
1352
|
+
if (direct && typeof nested === "string") result[direct.field] = nested;
|
|
1353
|
+
visit(nested, key);
|
|
1354
|
+
});
|
|
1355
|
+
};
|
|
1356
|
+
if (error && typeof error === "object") {
|
|
1357
|
+
const record = error;
|
|
1358
|
+
visit(record.details ?? record.error?.details ?? record);
|
|
1359
|
+
}
|
|
1360
|
+
return result;
|
|
1361
|
+
}
|
|
1362
|
+
function focusField(field) {
|
|
1363
|
+
window.setTimeout(() => {
|
|
1364
|
+
const element = document.getElementById(idFor(field));
|
|
1365
|
+
element?.scrollIntoView?.({ behavior: "smooth", block: "center" });
|
|
1366
|
+
element?.focus({ preventScroll: true });
|
|
1367
|
+
}, 0);
|
|
1368
|
+
}
|
|
1369
|
+
function isMultiline(field) {
|
|
1370
|
+
return field.multiline === true || /observa|descricao|mensagem|detalhe|necessidade/i.test(field.field);
|
|
1371
|
+
}
|
|
1372
|
+
function Label({ id, label, required }) {
|
|
1373
|
+
return /* @__PURE__ */ jsx9("label", { htmlFor: id, children: /* @__PURE__ */ jsxs5(Text5, { as: "span", display: "block", mb: "6px", color: "#475467", fontSize: "11px", fontWeight: "600", children: [
|
|
1374
|
+
label,
|
|
1375
|
+
required ? /* @__PURE__ */ jsx9(Text5, { as: "span", color: "#DC2626", children: " *" }) : null
|
|
1376
|
+
] }) });
|
|
1377
|
+
}
|
|
1378
|
+
function ErrorText({ id, text }) {
|
|
1379
|
+
return text ? /* @__PURE__ */ jsx9(Text5, { id, role: "alert", mt: "5px", color: "#DC2626", fontSize: "10px", fontWeight: "500", children: text }) : null;
|
|
1380
|
+
}
|
|
1381
|
+
var controlProps = (invalid) => ({
|
|
1382
|
+
minH: "37px",
|
|
1383
|
+
px: "10px",
|
|
1384
|
+
py: "8px",
|
|
1385
|
+
color: "#475467",
|
|
1386
|
+
bg: "white",
|
|
1387
|
+
borderColor: invalid ? "#DC2626" : "#D5DDE3",
|
|
1388
|
+
borderRadius: "6px",
|
|
1389
|
+
boxShadow: invalid ? "0 0 0 3px rgba(220,38,38,.08)" : void 0,
|
|
1390
|
+
fontSize: "11px"
|
|
1391
|
+
});
|
|
1392
|
+
function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submitting, error, onSubmit, onCancel, onFieldChange }) {
|
|
1393
|
+
const [values, setValues] = useState5(() => Object.fromEntries(fields.map((field) => [field.field, toValue(initialValues?.[field.field], field.kind)])));
|
|
1394
|
+
const [errors, setErrors] = useState5({});
|
|
1395
|
+
const [dismissed, setDismissed] = useState5(false);
|
|
1396
|
+
useEffect4(() => {
|
|
1397
|
+
const listener = (event) => {
|
|
1398
|
+
if (event.key !== "Escape" || event.defaultPrevented || submitting) return;
|
|
1399
|
+
if (event.target?.closest('[role="combobox"], [role="listbox"]')) return;
|
|
1400
|
+
onCancel();
|
|
1401
|
+
};
|
|
1402
|
+
document.addEventListener("keydown", listener);
|
|
1403
|
+
return () => document.removeEventListener("keydown", listener);
|
|
1404
|
+
}, [onCancel, submitting]);
|
|
1405
|
+
useEffect4(() => {
|
|
1406
|
+
setDismissed(false);
|
|
1407
|
+
if (!error) return;
|
|
1408
|
+
const detailed = detailErrors(error, fields);
|
|
1409
|
+
const fallback = /validacao|validation/i.test(normalize(messageOf(error))) ? validate(fields, values) : {};
|
|
1410
|
+
const next = Object.keys(detailed).length ? detailed : fallback;
|
|
1411
|
+
if (!Object.keys(next).length) return;
|
|
1412
|
+
setErrors((current) => ({ ...current, ...next }));
|
|
1413
|
+
const first = fields.find((field) => next[field.field]);
|
|
1414
|
+
if (first) focusField(first.field);
|
|
1415
|
+
}, [error]);
|
|
1416
|
+
const change = (field, value) => {
|
|
1417
|
+
setValues((current) => ({ ...current, [field]: value }));
|
|
1418
|
+
setErrors((current) => {
|
|
1419
|
+
if (!current[field]) return current;
|
|
1420
|
+
const next = { ...current };
|
|
1421
|
+
delete next[field];
|
|
1422
|
+
return next;
|
|
1423
|
+
});
|
|
1424
|
+
setDismissed(true);
|
|
1425
|
+
onFieldChange?.();
|
|
1426
|
+
};
|
|
1427
|
+
const submit = (event) => {
|
|
1428
|
+
event.preventDefault();
|
|
1429
|
+
const validation = validate(fields, values);
|
|
1430
|
+
if (Object.keys(validation).length) {
|
|
1431
|
+
setErrors(validation);
|
|
1432
|
+
setDismissed(true);
|
|
1433
|
+
const first = fields.find((field) => validation[field.field]);
|
|
1434
|
+
if (first) focusField(first.field);
|
|
1435
|
+
return;
|
|
626
1436
|
}
|
|
1437
|
+
const payload = {};
|
|
1438
|
+
fields.forEach((field) => {
|
|
1439
|
+
const value = coerce(values[field.field] ?? "", field.kind);
|
|
1440
|
+
if (value !== void 0) payload[field.field] = value;
|
|
1441
|
+
});
|
|
1442
|
+
setErrors({});
|
|
627
1443
|
onSubmit(payload);
|
|
628
1444
|
};
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
),
|
|
649
|
-
/* @__PURE__ */ jsx7(NativeSelect.Indicator, {})
|
|
650
|
-
] })
|
|
651
|
-
] }, f.field);
|
|
652
|
-
}
|
|
653
|
-
if (f.kind === "boolean") {
|
|
654
|
-
return /* @__PURE__ */ jsxs4(Box3, { children: [
|
|
655
|
-
/* @__PURE__ */ jsx7(Text4, { fontSize: "sm", mb: 1, fontWeight: "medium", children: label }),
|
|
656
|
-
/* @__PURE__ */ jsxs4(NativeSelect.Root, { size: "sm", children: [
|
|
657
|
-
/* @__PURE__ */ jsxs4(
|
|
658
|
-
NativeSelect.Field,
|
|
659
|
-
{
|
|
660
|
-
value: values[f.field] ?? "",
|
|
661
|
-
onChange: (e) => setField(f.field, e.currentTarget.value),
|
|
662
|
-
children: [
|
|
663
|
-
/* @__PURE__ */ jsx7("option", { value: "", children: "\u2014" }),
|
|
664
|
-
/* @__PURE__ */ jsx7("option", { value: "true", children: "Sim" }),
|
|
665
|
-
/* @__PURE__ */ jsx7("option", { value: "false", children: "N\xE3o" })
|
|
666
|
-
]
|
|
667
|
-
}
|
|
668
|
-
),
|
|
669
|
-
/* @__PURE__ */ jsx7(NativeSelect.Indicator, {})
|
|
670
|
-
] })
|
|
671
|
-
] }, f.field);
|
|
672
|
-
}
|
|
673
|
-
const inputType = f.kind === "date" ? "date" : f.kind === "number" || f.kind === "currency" || f.kind === "currencyConverted" ? "number" : "text";
|
|
674
|
-
return /* @__PURE__ */ jsxs4(Box3, { children: [
|
|
675
|
-
/* @__PURE__ */ jsxs4(Text4, { fontSize: "sm", mb: 1, fontWeight: "medium", children: [
|
|
676
|
-
label,
|
|
677
|
-
f.required ? " *" : ""
|
|
1445
|
+
const invalid = fields.filter((field) => errors[field.field]);
|
|
1446
|
+
const generic = dismissed ? null : messageOf(error);
|
|
1447
|
+
return /* @__PURE__ */ jsx9(Flex4, { position: "fixed", inset: 0, zIndex: 1e3, align: "center", justify: "center", p: { base: 2, md: 6 }, bg: "rgba(15,23,42,.48)", backdropFilter: "blur(2px)", onMouseDown: (event) => {
|
|
1448
|
+
if (event.currentTarget === event.target && !submitting) onCancel();
|
|
1449
|
+
}, children: /* @__PURE__ */ jsxs5(Box4, { role: "dialog", "aria-modal": "true", "aria-label": title, w: "min(860px, 96vw)", maxH: "min(880px, 94vh)", overflow: "hidden", bg: "white", borderWidth: "1px", borderColor: "#E5E9ED", borderRadius: "14px", boxShadow: "0 24px 70px rgba(15,23,42,.24)", children: [
|
|
1450
|
+
/* @__PURE__ */ jsxs5(Flex4, { minH: "76px", px: "20px", py: "16px", align: "center", justify: "space-between", borderBottomWidth: "1px", borderColor: "#E5E9ED", children: [
|
|
1451
|
+
/* @__PURE__ */ jsxs5(Box4, { children: [
|
|
1452
|
+
/* @__PURE__ */ jsx9(Text5, { color: "#0474AF", fontSize: "10px", fontWeight: "600", letterSpacing: ".07em", textTransform: "uppercase", children: eyebrow }),
|
|
1453
|
+
/* @__PURE__ */ jsx9(Heading2, { mt: "3px", color: "#344054", fontSize: "18px", fontWeight: "600", children: title })
|
|
1454
|
+
] }),
|
|
1455
|
+
/* @__PURE__ */ jsx9(Button4, { type: "button", "aria-label": "Fechar", w: "31px", minW: "31px", h: "31px", p: 0, color: "#667085", bg: "white", borderWidth: "1px", borderColor: "#E5E9ED", borderRadius: "6px", disabled: submitting, onClick: onCancel, _hover: { color: "#036491", bg: "#F0F7FF", borderColor: "#B8D9EB" }, children: /* @__PURE__ */ jsx9(CloseIcon, {}) })
|
|
1456
|
+
] }),
|
|
1457
|
+
/* @__PURE__ */ jsxs5("form", { noValidate: true, onSubmit: submit, children: [
|
|
1458
|
+
/* @__PURE__ */ jsx9(Box4, { maxH: "calc(94vh - 148px)", px: "20px", pt: "18px", pb: "24px", overflowY: "auto", children: /* @__PURE__ */ jsxs5(Stack2, { gap: 4, children: [
|
|
1459
|
+
invalid.length ? /* @__PURE__ */ jsxs5(Box4, { role: "alert", p: "11px 13px", color: "#9B1C1C", bg: "#FFF5F5", borderWidth: "1px", borderColor: "#FECACA", borderLeftWidth: "4px", borderLeftColor: "#DC2626", borderRadius: "8px", fontSize: "11px", children: [
|
|
1460
|
+
/* @__PURE__ */ jsxs5(Flex4, { align: "center", gap: "7px", children: [
|
|
1461
|
+
/* @__PURE__ */ jsx9(AlertIcon, {}),
|
|
1462
|
+
/* @__PURE__ */ jsx9(Text5, { as: "strong", children: "N\xE3o foi poss\xEDvel salvar. Corrija os campos abaixo:" })
|
|
678
1463
|
] }),
|
|
679
|
-
/* @__PURE__ */
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1464
|
+
/* @__PURE__ */ jsx9(Box4, { as: "ul", mt: "7px", mb: 0, pl: "20px", display: "grid", gap: "3px", children: invalid.map((field) => /* @__PURE__ */ jsx9("li", { children: /* @__PURE__ */ jsxs5(Button4, { type: "button", variant: "plain", minH: "auto", h: "auto", p: 0, color: "#B42318", fontSize: "10px", fontWeight: "400", textDecoration: "underline", onClick: () => focusField(field.field), children: [
|
|
1465
|
+
fieldLabel({ name: field.field, label: field.label }),
|
|
1466
|
+
": ",
|
|
1467
|
+
errors[field.field]
|
|
1468
|
+
] }) }, field.field)) })
|
|
1469
|
+
] }) : null,
|
|
1470
|
+
generic && !invalid.length ? /* @__PURE__ */ jsxs5(Flex4, { role: "alert", p: "10px 12px", align: "center", gap: 2, color: "#B42318", bg: "#FEF3F2", borderWidth: "1px", borderColor: "#FECDCA", borderRadius: "7px", fontSize: "11px", children: [
|
|
1471
|
+
/* @__PURE__ */ jsx9(AlertIcon, {}),
|
|
1472
|
+
generic
|
|
1473
|
+
] }) : null,
|
|
1474
|
+
/* @__PURE__ */ jsx9(SimpleGrid, { columns: { base: 1, lg: 2 }, gapX: "16px", gapY: "15px", children: fields.map((baseField) => {
|
|
1475
|
+
const field = baseField;
|
|
1476
|
+
const label = fieldLabel({ name: field.field, label: field.label });
|
|
1477
|
+
const message = errors[field.field];
|
|
1478
|
+
const id = idFor(field.field);
|
|
1479
|
+
const errorId = message ? errorIdFor(field.field) : void 0;
|
|
1480
|
+
const multiline = isMultiline(field);
|
|
1481
|
+
const span = field.span === 2 || multiline;
|
|
1482
|
+
const wrap = (control) => /* @__PURE__ */ jsxs5(Box4, { gridColumn: span ? "1 / -1" : void 0, children: [
|
|
1483
|
+
/* @__PURE__ */ jsx9(Label, { id, label, required: field.required }),
|
|
1484
|
+
control,
|
|
1485
|
+
/* @__PURE__ */ jsx9(ErrorText, { id: errorId, text: message })
|
|
1486
|
+
] }, field.field);
|
|
1487
|
+
if (field.kind === "ref" && field.ref) return wrap(/* @__PURE__ */ jsx9(ReferenceSelect, { field: field.field, inputId: id, errorId, label, refModel: field.ref, value: values[field.field] ?? "", initialValue: initialValues?.[field.field], required: field.required, invalid: !!message, error: message, onChange: (value) => change(field.field, value) }));
|
|
1488
|
+
if (field.kind === "enum" && field.options?.length) return wrap(/* @__PURE__ */ jsxs5(NativeSelect2.Root, { size: "sm", children: [
|
|
1489
|
+
/* @__PURE__ */ jsxs5(NativeSelect2.Field, { id, "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message), onChange: (event) => change(field.field, event.currentTarget.value), children: [
|
|
1490
|
+
/* @__PURE__ */ jsx9("option", { value: "", children: "\u2014 Selecione \u2014" }),
|
|
1491
|
+
field.options.map((option) => /* @__PURE__ */ jsx9("option", { value: option, children: option }, option))
|
|
1492
|
+
] }),
|
|
1493
|
+
/* @__PURE__ */ jsx9(NativeSelect2.Indicator, {})
|
|
1494
|
+
] }));
|
|
1495
|
+
if (field.kind === "boolean") return wrap(/* @__PURE__ */ jsxs5(NativeSelect2.Root, { size: "sm", children: [
|
|
1496
|
+
/* @__PURE__ */ jsxs5(NativeSelect2.Field, { id, "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message), onChange: (event) => change(field.field, event.currentTarget.value), children: [
|
|
1497
|
+
/* @__PURE__ */ jsx9("option", { value: "", children: "\u2014 Selecione \u2014" }),
|
|
1498
|
+
/* @__PURE__ */ jsx9("option", { value: "true", children: "Sim" }),
|
|
1499
|
+
/* @__PURE__ */ jsx9("option", { value: "false", children: "N\xE3o" })
|
|
1500
|
+
] }),
|
|
1501
|
+
/* @__PURE__ */ jsx9(NativeSelect2.Indicator, {})
|
|
1502
|
+
] }));
|
|
1503
|
+
if (multiline) return wrap(/* @__PURE__ */ jsx9(Textarea, { id, rows: field.rows ?? 4, resize: "vertical", "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message), lineHeight: "1.5", onChange: (event) => change(field.field, event.currentTarget.value) }));
|
|
1504
|
+
const type = field.kind === "date" ? "date" : ["number", "currency", "currencyConverted"].includes(field.kind ?? "") ? "number" : "text";
|
|
1505
|
+
return wrap(/* @__PURE__ */ jsx9(Input3, { id, type, step: type === "number" ? "any" : void 0, autoComplete: "off", "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message), onChange: (event) => change(field.field, event.currentTarget.value) }));
|
|
1506
|
+
}) })
|
|
1507
|
+
] }) }),
|
|
1508
|
+
/* @__PURE__ */ jsxs5(Flex4, { minH: "68px", px: "20px", py: "14px", justify: "flex-end", gap: "9px", borderTopWidth: "1px", borderColor: "#E5E9ED", children: [
|
|
1509
|
+
/* @__PURE__ */ jsx9(Button4, { type: "button", minH: "35px", px: "13px", color: "#475467", bg: "white", borderWidth: "1px", borderColor: "#D5DDE3", borderRadius: "6px", fontSize: "11px", fontWeight: "600", disabled: submitting, onClick: onCancel, children: "Cancelar" }),
|
|
1510
|
+
/* @__PURE__ */ jsxs5(Button4, { type: "submit", minH: "35px", px: "13px", colorPalette: "brand", borderRadius: "6px", fontSize: "11px", fontWeight: "600", loading: submitting, children: [
|
|
1511
|
+
/* @__PURE__ */ jsx9(CheckIcon, {}),
|
|
1512
|
+
" Salvar"
|
|
1513
|
+
] })
|
|
695
1514
|
] })
|
|
696
|
-
] })
|
|
697
|
-
] });
|
|
1515
|
+
] })
|
|
1516
|
+
] }) });
|
|
698
1517
|
}
|
|
699
1518
|
|
|
700
1519
|
// src/components/CoreCollection.tsx
|
|
701
|
-
import { jsx as
|
|
702
|
-
function CoreCollection({ model, label, mode = "dynamic", endpoint, columns, form, importExport }) {
|
|
1520
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1521
|
+
function CoreCollection({ model, label, section = "Cole\xE7\xE3o", mode = "dynamic", endpoint, columns, form, importExport }) {
|
|
703
1522
|
const needsSchema = mode !== "full" || !columns || !form || !endpoint;
|
|
704
1523
|
const schemaQuery = useModelSchema(needsSchema ? model : "");
|
|
705
1524
|
const schema = schemaQuery.data;
|
|
@@ -708,8 +1527,8 @@ function CoreCollection({ model, label, mode = "dynamic", endpoint, columns, for
|
|
|
708
1527
|
const resolvedForm = form ?? (schema ? formFieldsFromMeta(schema.fields) : []);
|
|
709
1528
|
const resource = useOonResource(basePath);
|
|
710
1529
|
const queryClient = useQueryClient();
|
|
711
|
-
const [formState, setFormState] =
|
|
712
|
-
const [formError, setFormError] =
|
|
1530
|
+
const [formState, setFormState] = useState6({ open: false });
|
|
1531
|
+
const [formError, setFormError] = useState6(null);
|
|
713
1532
|
const invalidate = () => queryClient.invalidateQueries({ queryKey: ["oon", "list", basePath] });
|
|
714
1533
|
const saveMutation = useMutation({
|
|
715
1534
|
mutationFn: (values) => {
|
|
@@ -722,54 +1541,68 @@ function CoreCollection({ model, label, mode = "dynamic", endpoint, columns, for
|
|
|
722
1541
|
setFormError(null);
|
|
723
1542
|
invalidate();
|
|
724
1543
|
},
|
|
725
|
-
onError: (
|
|
1544
|
+
onError: (error) => setFormError(error)
|
|
726
1545
|
});
|
|
727
1546
|
const deleteMutation = useMutation({
|
|
728
1547
|
mutationFn: (row) => resource.delete(row._id),
|
|
729
1548
|
onSuccess: invalidate
|
|
730
1549
|
});
|
|
731
|
-
if (needsSchema && schemaQuery.isLoading) return /* @__PURE__ */
|
|
732
|
-
if (needsSchema && schemaQuery.isError) return /* @__PURE__ */
|
|
1550
|
+
if (needsSchema && schemaQuery.isLoading) return /* @__PURE__ */ jsx10(LoadingScreen, {});
|
|
1551
|
+
if (needsSchema && schemaQuery.isError) return /* @__PURE__ */ jsx10(ErrorState, { error: schemaQuery.error });
|
|
733
1552
|
const title = label ?? schema?.name ?? model;
|
|
734
|
-
return /* @__PURE__ */
|
|
735
|
-
/* @__PURE__ */
|
|
736
|
-
|
|
1553
|
+
return /* @__PURE__ */ jsxs6(Stack3, { gap: 5, children: [
|
|
1554
|
+
/* @__PURE__ */ jsxs6(Box5, { children: [
|
|
1555
|
+
/* @__PURE__ */ jsx10(Text6, { fontSize: "11px", fontWeight: "700", color: "brand.500", textTransform: "uppercase", letterSpacing: "0.08em", mb: 1, children: section }),
|
|
1556
|
+
/* @__PURE__ */ jsx10(Heading3, { size: "lg", color: "#24323A", letterSpacing: "-0.02em", children: title }),
|
|
1557
|
+
/* @__PURE__ */ jsx10(Text6, { mt: 1, fontSize: "13px", color: "gray.500", children: "Consulte, filtre e mantenha os registros desta opera\xE7\xE3o." })
|
|
1558
|
+
] }),
|
|
1559
|
+
/* @__PURE__ */ jsx10(
|
|
1560
|
+
DataGrid,
|
|
1561
|
+
{
|
|
1562
|
+
resource,
|
|
1563
|
+
resourceKey: basePath,
|
|
1564
|
+
columns: resolvedColumns,
|
|
1565
|
+
onCreate: () => {
|
|
1566
|
+
setFormError(null);
|
|
1567
|
+
setFormState({ open: true });
|
|
1568
|
+
},
|
|
1569
|
+
onEdit: (row) => {
|
|
1570
|
+
setFormError(null);
|
|
1571
|
+
setFormState({ open: true, row });
|
|
1572
|
+
},
|
|
1573
|
+
onDelete: (row) => {
|
|
1574
|
+
if (window.confirm("Excluir este registro?")) deleteMutation.mutate(row);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
),
|
|
1578
|
+
formState.open ? /* @__PURE__ */ jsx10(
|
|
737
1579
|
DynamicForm,
|
|
738
1580
|
{
|
|
739
1581
|
title: formState.row ? `Editar ${title}` : `Novo ${title}`,
|
|
1582
|
+
eyebrow: section,
|
|
740
1583
|
fields: resolvedForm,
|
|
741
1584
|
initialValues: formState.row,
|
|
742
1585
|
submitting: saveMutation.isPending,
|
|
743
1586
|
error: formError,
|
|
744
|
-
onSubmit: (values) =>
|
|
1587
|
+
onSubmit: (values) => {
|
|
1588
|
+
setFormError(null);
|
|
1589
|
+
saveMutation.mutate(values);
|
|
1590
|
+
},
|
|
1591
|
+
onFieldChange: () => setFormError(null),
|
|
745
1592
|
onCancel: () => {
|
|
746
1593
|
setFormState({ open: false });
|
|
747
1594
|
setFormError(null);
|
|
748
1595
|
}
|
|
749
1596
|
}
|
|
750
|
-
) :
|
|
751
|
-
|
|
752
|
-
DataGrid,
|
|
753
|
-
{
|
|
754
|
-
resource,
|
|
755
|
-
resourceKey: basePath,
|
|
756
|
-
columns: resolvedColumns,
|
|
757
|
-
onCreate: () => setFormState({ open: true }),
|
|
758
|
-
onEdit: (row) => setFormState({ open: true, row }),
|
|
759
|
-
onDelete: (row) => {
|
|
760
|
-
if (window.confirm("Excluir este registro?")) deleteMutation.mutate(row);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
),
|
|
764
|
-
importExport ? null : null
|
|
765
|
-
] })
|
|
1597
|
+
) : null,
|
|
1598
|
+
importExport ? null : null
|
|
766
1599
|
] });
|
|
767
1600
|
}
|
|
768
1601
|
|
|
769
1602
|
// src/components/CoreDocument.tsx
|
|
770
|
-
import { jsx as
|
|
1603
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
771
1604
|
function CoreDocument({ model, label, endpoint, columns, form }) {
|
|
772
|
-
return /* @__PURE__ */
|
|
1605
|
+
return /* @__PURE__ */ jsx11(
|
|
773
1606
|
CoreCollection,
|
|
774
1607
|
{
|
|
775
1608
|
model,
|
|
@@ -783,25 +1616,48 @@ function CoreDocument({ model, label, endpoint, columns, form }) {
|
|
|
783
1616
|
}
|
|
784
1617
|
|
|
785
1618
|
// src/components/CorePipeline.tsx
|
|
786
|
-
import {
|
|
787
|
-
import {
|
|
788
|
-
import {
|
|
1619
|
+
import { useState as useState7 } from "react";
|
|
1620
|
+
import { Badge as Badge2, Box as Box6, Button as Button5, Flex as Flex5, Heading as Heading4, Stack as Stack4, Text as Text7 } from "@chakra-ui/react";
|
|
1621
|
+
import { useMutation as useMutation2, useQuery as useQuery4, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
|
|
1622
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1623
|
+
function ticketId(row) {
|
|
1624
|
+
const id = row._id ?? row.id;
|
|
1625
|
+
return id == null ? void 0 : String(id);
|
|
1626
|
+
}
|
|
789
1627
|
function CorePipeline({ model, label, endpoint, stageField = "status", stages, titleField }) {
|
|
790
1628
|
const schemaQuery = useModelSchema(model);
|
|
791
1629
|
const schema = schemaQuery.data;
|
|
792
1630
|
const basePath = endpoint ?? schema?.basePath ?? `/${model.toLowerCase()}s`;
|
|
793
1631
|
const resource = useOonResource(basePath);
|
|
794
|
-
const
|
|
1632
|
+
const queryClient = useQueryClient2();
|
|
1633
|
+
const [selectedTicket, setSelectedTicket] = useState7(null);
|
|
1634
|
+
const [formError, setFormError] = useState7(null);
|
|
1635
|
+
const listQuery = useQuery4({
|
|
795
1636
|
queryKey: ["oon", "pipeline", basePath],
|
|
796
1637
|
queryFn: () => resource.list({ pageSize: 200 }),
|
|
797
1638
|
enabled: !!schema
|
|
798
1639
|
});
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1640
|
+
const saveMutation = useMutation2({
|
|
1641
|
+
mutationFn: (values) => {
|
|
1642
|
+
const id = selectedTicket ? ticketId(selectedTicket) : void 0;
|
|
1643
|
+
if (!id) throw new Error("N\xE3o foi poss\xEDvel identificar o ticket selecionado.");
|
|
1644
|
+
return resource.update(id, values);
|
|
1645
|
+
},
|
|
1646
|
+
onSuccess: async () => {
|
|
1647
|
+
await queryClient.invalidateQueries({ queryKey: ["oon", "pipeline", basePath] });
|
|
1648
|
+
setSelectedTicket(null);
|
|
1649
|
+
setFormError(null);
|
|
1650
|
+
},
|
|
1651
|
+
onError: (error) => setFormError(error)
|
|
1652
|
+
});
|
|
1653
|
+
if (schemaQuery.isLoading || listQuery.isLoading) return /* @__PURE__ */ jsx12(LoadingScreen, {});
|
|
1654
|
+
if (schemaQuery.isError) return /* @__PURE__ */ jsx12(ErrorState, { error: schemaQuery.error });
|
|
1655
|
+
if (listQuery.isError) return /* @__PURE__ */ jsx12(ErrorState, { error: listQuery.error });
|
|
1656
|
+
const stageFieldMeta = schema?.fields.find((field) => field.name === stageField);
|
|
1657
|
+
const resolvedStages = stages ?? (stageFieldMeta?.options ?? []).map((option) => ({ id: option, label: option }));
|
|
1658
|
+
const title = titleField ?? schema?.fields.find((field) => field.kind === "string")?.name ?? schema?.searchable[0] ?? "_id";
|
|
1659
|
+
const detailFields = (schema?.fields ?? []).filter((field) => field.name !== stageField && field.name !== title && !["raw"].includes(field.kind)).slice(0, 3);
|
|
1660
|
+
const formFields = formFieldsFromMeta(schema?.fields ?? []);
|
|
805
1661
|
const rows = listQuery.data?.results ?? [];
|
|
806
1662
|
const byStage = /* @__PURE__ */ new Map();
|
|
807
1663
|
for (const stage of resolvedStages) byStage.set(stage.id, []);
|
|
@@ -810,25 +1666,101 @@ function CorePipeline({ model, label, endpoint, stageField = "status", stages, t
|
|
|
810
1666
|
if (!byStage.has(key)) byStage.set(key, []);
|
|
811
1667
|
byStage.get(key).push(row);
|
|
812
1668
|
}
|
|
813
|
-
return /* @__PURE__ */
|
|
814
|
-
/* @__PURE__ */
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1669
|
+
return /* @__PURE__ */ jsxs7(Stack4, { gap: 5, children: [
|
|
1670
|
+
/* @__PURE__ */ jsxs7(Box6, { children: [
|
|
1671
|
+
/* @__PURE__ */ jsx12(Text7, { fontSize: "11px", fontWeight: "700", color: "brand.500", textTransform: "uppercase", letterSpacing: "0.08em", mb: 1, children: "Esteira de processo" }),
|
|
1672
|
+
/* @__PURE__ */ jsx12(Heading4, { size: "lg", color: "#24323A", letterSpacing: "-0.02em", children: label ?? schema?.name ?? model }),
|
|
1673
|
+
/* @__PURE__ */ jsx12(Text7, { mt: 1, fontSize: "13px", color: "gray.500", children: "Acompanhe os tickets por etapa e visualize os principais dados de cada item." })
|
|
1674
|
+
] }),
|
|
1675
|
+
/* @__PURE__ */ jsx12(Flex5, { gap: 4, align: "stretch", overflowX: "auto", pb: 3, minH: "420px", children: Array.from(byStage.entries()).map(([stageId, items]) => {
|
|
1676
|
+
const stageLabel = resolvedStages.find((stage) => stage.id === stageId)?.label ?? (stageId || "Sem etapa");
|
|
1677
|
+
return /* @__PURE__ */ jsxs7(
|
|
1678
|
+
Box6,
|
|
1679
|
+
{
|
|
1680
|
+
minW: "300px",
|
|
1681
|
+
maxW: "340px",
|
|
1682
|
+
flex: "1 0 300px",
|
|
1683
|
+
bg: "#E8ECEF",
|
|
1684
|
+
borderRadius: "12px",
|
|
1685
|
+
p: 3,
|
|
1686
|
+
borderWidth: "1px",
|
|
1687
|
+
borderColor: "#DDE3E7",
|
|
1688
|
+
children: [
|
|
1689
|
+
/* @__PURE__ */ jsxs7(Flex5, { align: "center", justify: "space-between", mb: 3, px: 1, children: [
|
|
1690
|
+
/* @__PURE__ */ jsx12(Text7, { fontWeight: "700", fontSize: "12px", color: "#33434C", textTransform: "uppercase", letterSpacing: "0.03em", children: stageLabel }),
|
|
1691
|
+
/* @__PURE__ */ jsx12(Badge2, { colorPalette: "brand", variant: "solid", borderRadius: "full", minW: "24px", justifyContent: "center", children: items.length })
|
|
1692
|
+
] }),
|
|
1693
|
+
/* @__PURE__ */ jsx12(Stack4, { gap: 2, children: items.length === 0 ? /* @__PURE__ */ jsx12(Box6, { borderWidth: "1px", borderStyle: "dashed", borderColor: "#C8D0D5", borderRadius: "10px", p: 5, textAlign: "center", children: /* @__PURE__ */ jsx12(Text7, { fontSize: "12px", color: "gray.500", children: "Nenhum ticket nesta etapa." }) }) : items.map((row, index) => {
|
|
1694
|
+
const ticketTitle = formatCell(row[title], "string");
|
|
1695
|
+
return /* @__PURE__ */ jsxs7(
|
|
1696
|
+
Button5,
|
|
1697
|
+
{
|
|
1698
|
+
type: "button",
|
|
1699
|
+
variant: "plain",
|
|
1700
|
+
display: "block",
|
|
1701
|
+
w: "100%",
|
|
1702
|
+
h: "auto",
|
|
1703
|
+
minH: "auto",
|
|
1704
|
+
p: 3,
|
|
1705
|
+
bg: "white",
|
|
1706
|
+
borderRadius: "10px",
|
|
1707
|
+
borderWidth: "1px",
|
|
1708
|
+
borderColor: "#DDE3E7",
|
|
1709
|
+
boxShadow: "0 3px 10px rgba(7, 38, 46, 0.05)",
|
|
1710
|
+
textAlign: "left",
|
|
1711
|
+
whiteSpace: "normal",
|
|
1712
|
+
transition: "all 0.15s ease",
|
|
1713
|
+
"aria-label": `Abrir ticket ${ticketTitle}`,
|
|
1714
|
+
onClick: () => {
|
|
1715
|
+
setFormError(null);
|
|
1716
|
+
setSelectedTicket(row);
|
|
1717
|
+
},
|
|
1718
|
+
_hover: { borderColor: "brand.300", transform: "translateY(-1px)", boxShadow: "0 7px 18px rgba(7, 38, 46, 0.09)" },
|
|
1719
|
+
_focusVisible: { borderColor: "brand.500", boxShadow: "0 0 0 3px rgba(4, 116, 175, 0.16)" },
|
|
1720
|
+
children: [
|
|
1721
|
+
/* @__PURE__ */ jsx12(Text7, { fontSize: "13px", fontWeight: "700", color: "#24323A", mb: 2, lineClamp: 2, children: ticketTitle }),
|
|
1722
|
+
/* @__PURE__ */ jsx12(Stack4, { gap: 1, children: detailFields.map((field) => /* @__PURE__ */ jsxs7(Flex5, { gap: 2, align: "baseline", justify: "space-between", children: [
|
|
1723
|
+
/* @__PURE__ */ jsx12(Text7, { fontSize: "10px", color: "gray.500", textTransform: "uppercase", letterSpacing: "0.03em", children: fieldLabel(field) }),
|
|
1724
|
+
/* @__PURE__ */ jsx12(Text7, { fontSize: "11px", color: "#46545C", fontWeight: "500", textAlign: "right", lineClamp: 1, children: formatCell(row[field.name], field.kind) })
|
|
1725
|
+
] }, field.name)) })
|
|
1726
|
+
]
|
|
1727
|
+
},
|
|
1728
|
+
ticketId(row) ?? index
|
|
1729
|
+
);
|
|
1730
|
+
}) })
|
|
1731
|
+
]
|
|
1732
|
+
},
|
|
1733
|
+
stageId
|
|
1734
|
+
);
|
|
1735
|
+
}) }),
|
|
1736
|
+
selectedTicket ? /* @__PURE__ */ jsx12(
|
|
1737
|
+
DynamicForm,
|
|
1738
|
+
{
|
|
1739
|
+
title: `Editar ${schema?.singular ?? label ?? model}`,
|
|
1740
|
+
eyebrow: "Ticket",
|
|
1741
|
+
fields: formFields,
|
|
1742
|
+
initialValues: selectedTicket,
|
|
1743
|
+
submitting: saveMutation.isPending,
|
|
1744
|
+
error: formError,
|
|
1745
|
+
onSubmit: (values) => {
|
|
1746
|
+
setFormError(null);
|
|
1747
|
+
saveMutation.mutate(values);
|
|
1748
|
+
},
|
|
1749
|
+
onFieldChange: () => setFormError(null),
|
|
1750
|
+
onCancel: () => {
|
|
1751
|
+
if (saveMutation.isPending) return;
|
|
1752
|
+
setSelectedTicket(null);
|
|
1753
|
+
setFormError(null);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
) : null
|
|
825
1757
|
] });
|
|
826
1758
|
}
|
|
827
1759
|
|
|
828
1760
|
// src/components/CoreDashboard.tsx
|
|
829
|
-
import { Box as
|
|
1761
|
+
import { Box as Box7, Heading as Heading5, SimpleGrid as SimpleGrid2, Stack as Stack5, Text as Text8 } from "@chakra-ui/react";
|
|
830
1762
|
import { useQueries } from "@tanstack/react-query";
|
|
831
|
-
import { jsx as
|
|
1763
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
832
1764
|
function CoreDashboard({ label = "Dashboard", widgets = [] }) {
|
|
833
1765
|
const { resource } = useOonApi();
|
|
834
1766
|
const queries = useQueries({
|
|
@@ -845,17 +1777,17 @@ function CoreDashboard({ label = "Dashboard", widgets = [] }) {
|
|
|
845
1777
|
}))
|
|
846
1778
|
});
|
|
847
1779
|
let queryIdx = 0;
|
|
848
|
-
return /* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
/* @__PURE__ */
|
|
1780
|
+
return /* @__PURE__ */ jsxs8(Stack5, { gap: 6, children: [
|
|
1781
|
+
/* @__PURE__ */ jsx13(Heading5, { size: "lg", children: label }),
|
|
1782
|
+
/* @__PURE__ */ jsx13(SimpleGrid2, { columns: { base: 1, md: 2, lg: 3 }, gap: 4, children: widgets.map((w) => {
|
|
851
1783
|
if (w.kind === "custom" && w.render) {
|
|
852
|
-
return /* @__PURE__ */
|
|
1784
|
+
return /* @__PURE__ */ jsx13(Box7, { borderWidth: "1px", borderColor: "gray.200", borderRadius: "md", bg: "white", p: 5, children: w.render() }, w.id);
|
|
853
1785
|
}
|
|
854
1786
|
const q = queries[queryIdx++];
|
|
855
1787
|
const value = q?.isLoading ? "\u2026" : q?.isError ? "\u2014" : formatWidget(q?.data, w.kind, w.field);
|
|
856
|
-
return /* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
/* @__PURE__ */
|
|
1788
|
+
return /* @__PURE__ */ jsxs8(Box7, { borderWidth: "1px", borderColor: "gray.200", borderRadius: "md", bg: "white", p: 5, children: [
|
|
1789
|
+
/* @__PURE__ */ jsx13(Text8, { fontSize: "sm", color: "gray.500", children: w.label }),
|
|
1790
|
+
/* @__PURE__ */ jsx13(Heading5, { size: "xl", mt: 1, children: value })
|
|
859
1791
|
] }, w.id);
|
|
860
1792
|
}) })
|
|
861
1793
|
] });
|
|
@@ -869,7 +1801,7 @@ function formatWidget(value, kind, field) {
|
|
|
869
1801
|
}
|
|
870
1802
|
|
|
871
1803
|
// src/app/moduleRegistry.tsx
|
|
872
|
-
import { jsx as
|
|
1804
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
873
1805
|
function slug(value) {
|
|
874
1806
|
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
875
1807
|
}
|
|
@@ -892,7 +1824,7 @@ function renderView(view) {
|
|
|
892
1824
|
switch (view.type) {
|
|
893
1825
|
case "collection": {
|
|
894
1826
|
const v = view;
|
|
895
|
-
return /* @__PURE__ */
|
|
1827
|
+
return /* @__PURE__ */ jsx14(
|
|
896
1828
|
CoreCollection,
|
|
897
1829
|
{
|
|
898
1830
|
model: v.model,
|
|
@@ -906,11 +1838,11 @@ function renderView(view) {
|
|
|
906
1838
|
}
|
|
907
1839
|
case "document": {
|
|
908
1840
|
const v = view;
|
|
909
|
-
return /* @__PURE__ */
|
|
1841
|
+
return /* @__PURE__ */ jsx14(CoreDocument, { model: v.model, label: viewLabel(v), approval: v.approval, attachments: v.attachments });
|
|
910
1842
|
}
|
|
911
1843
|
case "pipeline": {
|
|
912
1844
|
const v = view;
|
|
913
|
-
return /* @__PURE__ */
|
|
1845
|
+
return /* @__PURE__ */ jsx14(
|
|
914
1846
|
CorePipeline,
|
|
915
1847
|
{
|
|
916
1848
|
model: v.model,
|
|
@@ -922,7 +1854,7 @@ function renderView(view) {
|
|
|
922
1854
|
}
|
|
923
1855
|
case "dashboard": {
|
|
924
1856
|
const v = view;
|
|
925
|
-
return /* @__PURE__ */
|
|
1857
|
+
return /* @__PURE__ */ jsx14(CoreDashboard, { label: viewLabel(v), widgets: v.widgets });
|
|
926
1858
|
}
|
|
927
1859
|
default:
|
|
928
1860
|
return null;
|
|
@@ -970,20 +1902,23 @@ function resolveRegistry(config) {
|
|
|
970
1902
|
}
|
|
971
1903
|
|
|
972
1904
|
// src/app/createOonRouter.tsx
|
|
973
|
-
import {
|
|
1905
|
+
import {
|
|
1906
|
+
Navigate as Navigate4,
|
|
1907
|
+
createBrowserRouter
|
|
1908
|
+
} from "react-router-dom";
|
|
974
1909
|
|
|
975
1910
|
// src/shell/AuthenticatedLayout.tsx
|
|
976
|
-
import { Box as
|
|
1911
|
+
import { Box as Box9, Button as Button6, Flex as Flex7, Spacer as Spacer2, Text as Text10 } from "@chakra-ui/react";
|
|
977
1912
|
import { Outlet } from "react-router-dom";
|
|
978
1913
|
|
|
979
1914
|
// src/shell/Sidebar.tsx
|
|
980
|
-
import { Box as
|
|
1915
|
+
import { Box as Box8, Flex as Flex6, Image, Stack as Stack6, Text as Text9 } from "@chakra-ui/react";
|
|
981
1916
|
import { NavLink } from "react-router-dom";
|
|
982
|
-
import { jsx as
|
|
1917
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
983
1918
|
function groupBySection(items) {
|
|
984
1919
|
const groups = /* @__PURE__ */ new Map();
|
|
985
1920
|
for (const item of items) {
|
|
986
|
-
const section = item.section ?? "";
|
|
1921
|
+
const section = item.section ?? "Geral";
|
|
987
1922
|
if (!groups.has(section)) groups.set(section, []);
|
|
988
1923
|
groups.get(section).push(item);
|
|
989
1924
|
}
|
|
@@ -992,85 +1927,762 @@ function groupBySection(items) {
|
|
|
992
1927
|
function Sidebar({ app, menu }) {
|
|
993
1928
|
const { hasPermission } = useOonAuth();
|
|
994
1929
|
const visible = menu.filter((item) => hasPermission(item.permissions));
|
|
995
|
-
return /* @__PURE__ */
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1930
|
+
return /* @__PURE__ */ jsxs9(
|
|
1931
|
+
Box8,
|
|
1932
|
+
{
|
|
1933
|
+
as: "nav",
|
|
1934
|
+
w: { base: "76px", lg: "248px" },
|
|
1935
|
+
minH: "100vh",
|
|
1936
|
+
borderRightWidth: "1px",
|
|
1937
|
+
borderColor: "#E8ECEF",
|
|
1938
|
+
px: { base: 2, lg: 3 },
|
|
1939
|
+
py: 4,
|
|
1940
|
+
bg: "white",
|
|
1941
|
+
position: "sticky",
|
|
1942
|
+
top: 0,
|
|
1943
|
+
alignSelf: "flex-start",
|
|
1944
|
+
overflowY: "auto",
|
|
1945
|
+
children: [
|
|
1946
|
+
/* @__PURE__ */ jsxs9(Flex6, { align: "center", gap: 3, mb: 6, px: { base: 1, lg: 2 }, minH: "42px", children: [
|
|
1947
|
+
app.logo ? /* @__PURE__ */ jsx15(Image, { src: app.logo, alt: app.name, maxH: "36px", maxW: { base: "44px", lg: "150px" }, objectFit: "contain" }) : /* @__PURE__ */ jsx15(
|
|
1948
|
+
Flex6,
|
|
1949
|
+
{
|
|
1950
|
+
w: "38px",
|
|
1951
|
+
h: "38px",
|
|
1952
|
+
flex: "0 0 auto",
|
|
1953
|
+
borderRadius: "12px",
|
|
1954
|
+
align: "center",
|
|
1955
|
+
justify: "center",
|
|
1956
|
+
bg: "brand.500",
|
|
1957
|
+
color: "white",
|
|
1958
|
+
fontWeight: "700",
|
|
1959
|
+
fontSize: "lg",
|
|
1960
|
+
children: "O"
|
|
1961
|
+
}
|
|
1962
|
+
),
|
|
1963
|
+
/* @__PURE__ */ jsxs9(Box8, { display: { base: "none", lg: "block" }, minW: 0, children: [
|
|
1964
|
+
/* @__PURE__ */ jsx15(Text9, { fontWeight: "700", fontSize: "sm", color: "#07262E", lineClamp: 1, children: app.name }),
|
|
1965
|
+
/* @__PURE__ */ jsx15(Text9, { fontSize: "10px", color: "gray.500", textTransform: "uppercase", letterSpacing: "0.08em", children: "Opera\xE7\xE3o digital" })
|
|
1966
|
+
] })
|
|
1967
|
+
] }),
|
|
1968
|
+
/* @__PURE__ */ jsx15(Stack6, { gap: 5, children: groupBySection(visible).map(([section, items]) => /* @__PURE__ */ jsxs9(Stack6, { gap: 1, children: [
|
|
1969
|
+
/* @__PURE__ */ jsx15(
|
|
1970
|
+
Text9,
|
|
1971
|
+
{
|
|
1972
|
+
display: { base: "none", lg: "block" },
|
|
1973
|
+
fontSize: "10px",
|
|
1974
|
+
fontWeight: "600",
|
|
1975
|
+
color: "gray.400",
|
|
1976
|
+
textTransform: "uppercase",
|
|
1977
|
+
letterSpacing: "0.08em",
|
|
1978
|
+
px: 3,
|
|
1979
|
+
mb: 1,
|
|
1980
|
+
children: section
|
|
1981
|
+
}
|
|
1982
|
+
),
|
|
1983
|
+
items.map((item) => /* @__PURE__ */ jsx15(NavLink, { to: item.href, end: item.href === "/", title: item.label, children: ({ isActive }) => /* @__PURE__ */ jsxs9(
|
|
1984
|
+
Flex6,
|
|
1985
|
+
{
|
|
1986
|
+
align: "center",
|
|
1987
|
+
justify: { base: "center", lg: "flex-start" },
|
|
1988
|
+
gap: 3,
|
|
1989
|
+
px: { base: 2, lg: 3 },
|
|
1990
|
+
py: "10px",
|
|
1991
|
+
borderRadius: "9px",
|
|
1992
|
+
fontSize: "13px",
|
|
1993
|
+
fontWeight: isActive ? "600" : "500",
|
|
1994
|
+
bg: isActive ? "brand.50" : "transparent",
|
|
1995
|
+
color: isActive ? "brand.700" : "#46545C",
|
|
1996
|
+
borderLeftWidth: "3px",
|
|
1997
|
+
borderLeftColor: isActive ? "brand.500" : "transparent",
|
|
1998
|
+
transition: "all 0.15s ease",
|
|
1999
|
+
_hover: { bg: isActive ? "brand.50" : "#F8F9FA", color: "brand.700" },
|
|
2000
|
+
children: [
|
|
2001
|
+
item.icon ? /* @__PURE__ */ jsx15(Box8, { flex: "0 0 auto", children: item.icon }) : /* @__PURE__ */ jsx15(Box8, { w: "7px", h: "7px", borderRadius: "full", bg: isActive ? "brand.500" : "gray.300" }),
|
|
2002
|
+
/* @__PURE__ */ jsx15(Text9, { display: { base: "none", lg: "block" }, lineClamp: 1, children: item.label })
|
|
2003
|
+
]
|
|
2004
|
+
}
|
|
2005
|
+
) }, item.href))
|
|
2006
|
+
] }, section)) })
|
|
2007
|
+
]
|
|
2008
|
+
}
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
// src/shell/AuthenticatedLayout.tsx
|
|
2013
|
+
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2014
|
+
function AuthenticatedLayout({ app, menu }) {
|
|
2015
|
+
const { user, logout } = useOonAuth();
|
|
2016
|
+
return /* @__PURE__ */ jsxs10(Flex7, { align: "stretch", minH: "100vh", bg: "#F8F9FA", children: [
|
|
2017
|
+
/* @__PURE__ */ jsx16(Sidebar, { app, menu }),
|
|
2018
|
+
/* @__PURE__ */ jsxs10(Flex7, { direction: "column", flex: "1", minW: 0, children: [
|
|
2019
|
+
/* @__PURE__ */ jsxs10(
|
|
2020
|
+
Flex7,
|
|
1004
2021
|
{
|
|
2022
|
+
as: "header",
|
|
1005
2023
|
align: "center",
|
|
1006
|
-
|
|
1007
|
-
px:
|
|
1008
|
-
py:
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
2024
|
+
minH: "64px",
|
|
2025
|
+
px: { base: 4, lg: 6 },
|
|
2026
|
+
py: 3,
|
|
2027
|
+
borderBottomWidth: "1px",
|
|
2028
|
+
borderColor: "#E8ECEF",
|
|
2029
|
+
bg: "rgba(255, 255, 255, 0.96)",
|
|
2030
|
+
backdropFilter: "blur(10px)",
|
|
2031
|
+
position: "sticky",
|
|
2032
|
+
top: 0,
|
|
2033
|
+
zIndex: 10,
|
|
1015
2034
|
children: [
|
|
1016
|
-
|
|
1017
|
-
|
|
2035
|
+
/* @__PURE__ */ jsxs10(Box9, { children: [
|
|
2036
|
+
/* @__PURE__ */ jsx16(Text10, { fontSize: "xs", fontWeight: "600", color: "brand.500", textTransform: "uppercase", letterSpacing: "0.08em", children: "Central Oon" }),
|
|
2037
|
+
/* @__PURE__ */ jsx16(Text10, { fontSize: "sm", fontWeight: "600", color: "#24323A", children: app.title ?? app.name })
|
|
2038
|
+
] }),
|
|
2039
|
+
/* @__PURE__ */ jsx16(Spacer2, {}),
|
|
2040
|
+
user ? /* @__PURE__ */ jsxs10(Flex7, { align: "center", gap: 3, children: [
|
|
2041
|
+
/* @__PURE__ */ jsxs10(Box9, { textAlign: "right", display: { base: "none", md: "block" }, children: [
|
|
2042
|
+
/* @__PURE__ */ jsx16(Text10, { fontSize: "sm", fontWeight: "600", color: "#24323A", children: user.nome }),
|
|
2043
|
+
user.email ? /* @__PURE__ */ jsx16(Text10, { fontSize: "xs", color: "gray.500", children: user.email }) : null
|
|
2044
|
+
] }),
|
|
2045
|
+
/* @__PURE__ */ jsx16(Button6, { size: "sm", variant: "outline", colorPalette: "brand", borderRadius: "8px", onClick: logout, children: "Sair" })
|
|
2046
|
+
] }) : null
|
|
1018
2047
|
]
|
|
1019
2048
|
}
|
|
1020
|
-
)
|
|
1021
|
-
|
|
2049
|
+
),
|
|
2050
|
+
/* @__PURE__ */ jsx16(Box9, { as: "main", flex: "1", p: { base: 4, lg: 6 }, minW: 0, children: /* @__PURE__ */ jsx16(Box9, { maxW: "1680px", mx: "auto", children: /* @__PURE__ */ jsx16(Outlet, {}) }) })
|
|
2051
|
+
] })
|
|
1022
2052
|
] });
|
|
1023
2053
|
}
|
|
1024
2054
|
|
|
1025
|
-
// src/
|
|
1026
|
-
import {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
2055
|
+
// src/security/AccessDeniedPage.tsx
|
|
2056
|
+
import { Box as Box10, Button as Button7, Flex as Flex8, Heading as Heading6, Text as Text11, VStack } from "@chakra-ui/react";
|
|
2057
|
+
import { useNavigate } from "react-router-dom";
|
|
2058
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2059
|
+
function AccessDeniedPage({ app }) {
|
|
2060
|
+
const { logout } = useOonAuth();
|
|
2061
|
+
const navigate = useNavigate();
|
|
2062
|
+
function useAnotherAccount() {
|
|
2063
|
+
logout();
|
|
2064
|
+
navigate("/login", { replace: true });
|
|
2065
|
+
}
|
|
2066
|
+
return /* @__PURE__ */ jsx17(Flex8, { minH: "100vh", align: "center", justify: "center", bg: "#F5F8FA", px: 4, children: /* @__PURE__ */ jsx17(
|
|
2067
|
+
Box10,
|
|
2068
|
+
{
|
|
2069
|
+
width: "100%",
|
|
2070
|
+
maxW: "560px",
|
|
2071
|
+
bg: "white",
|
|
2072
|
+
borderWidth: "1px",
|
|
2073
|
+
borderColor: "#E4EBEF",
|
|
2074
|
+
borderRadius: "18px",
|
|
2075
|
+
boxShadow: "0 24px 70px rgba(7, 38, 46, 0.10)",
|
|
2076
|
+
p: { base: 6, md: 10 },
|
|
2077
|
+
children: /* @__PURE__ */ jsxs11(VStack, { align: "stretch", gap: 6, children: [
|
|
2078
|
+
/* @__PURE__ */ jsx17(
|
|
2079
|
+
Box10,
|
|
2080
|
+
{
|
|
2081
|
+
width: "56px",
|
|
2082
|
+
height: "56px",
|
|
2083
|
+
display: "flex",
|
|
2084
|
+
alignItems: "center",
|
|
2085
|
+
justifyContent: "center",
|
|
2086
|
+
borderRadius: "16px",
|
|
2087
|
+
bg: "orange.50",
|
|
2088
|
+
color: "orange.700",
|
|
2089
|
+
fontSize: "2xl",
|
|
2090
|
+
fontWeight: "800",
|
|
2091
|
+
"aria-hidden": "true",
|
|
2092
|
+
children: "!"
|
|
2093
|
+
}
|
|
2094
|
+
),
|
|
2095
|
+
/* @__PURE__ */ jsxs11(Box10, { children: [
|
|
2096
|
+
/* @__PURE__ */ jsx17(
|
|
2097
|
+
Text11,
|
|
2098
|
+
{
|
|
2099
|
+
color: "#39BFED",
|
|
2100
|
+
fontSize: "xs",
|
|
2101
|
+
fontWeight: "700",
|
|
2102
|
+
letterSpacing: "0.12em",
|
|
2103
|
+
textTransform: "uppercase",
|
|
2104
|
+
children: "Acesso negado"
|
|
2105
|
+
}
|
|
2106
|
+
),
|
|
2107
|
+
/* @__PURE__ */ jsx17(Heading6, { mt: 2, size: "lg", color: "#07262E", children: "Voc\xEA n\xE3o possui permiss\xE3o para este aplicativo" }),
|
|
2108
|
+
/* @__PURE__ */ jsxs11(Text11, { mt: 3, color: "gray.600", children: [
|
|
2109
|
+
"Seu usu\xE1rio foi autenticado, mas n\xE3o existe uma permiss\xE3o ativa para acessar ",
|
|
2110
|
+
app.title ?? app.name,
|
|
2111
|
+
". Solicite o acesso ao administrador da sua organiza\xE7\xE3o."
|
|
2112
|
+
] })
|
|
2113
|
+
] }),
|
|
2114
|
+
/* @__PURE__ */ jsx17(
|
|
2115
|
+
Button7,
|
|
2116
|
+
{
|
|
2117
|
+
alignSelf: "flex-start",
|
|
2118
|
+
bg: "#07262E",
|
|
2119
|
+
color: "white",
|
|
2120
|
+
_hover: { bg: "#0B3843" },
|
|
2121
|
+
onClick: useAnotherAccount,
|
|
2122
|
+
children: "Entrar com outra conta"
|
|
2123
|
+
}
|
|
2124
|
+
)
|
|
2125
|
+
] })
|
|
2126
|
+
}
|
|
2127
|
+
) });
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// src/security/AppAccessGuard.tsx
|
|
2131
|
+
import { Navigate, useLocation } from "react-router-dom";
|
|
2132
|
+
import { Fragment, jsx as jsx18 } from "react/jsx-runtime";
|
|
2133
|
+
function loginRedirect(pathname, search) {
|
|
2134
|
+
const destination = `${pathname}${search}`;
|
|
2135
|
+
return `/login?redirect=${encodeURIComponent(destination)}`;
|
|
2136
|
+
}
|
|
2137
|
+
function AppAccessGuard({ children }) {
|
|
2138
|
+
const { status, retry } = useOonAuth();
|
|
2139
|
+
const location = useLocation();
|
|
2140
|
+
if (status === "loading") return /* @__PURE__ */ jsx18(LoadingScreen, {});
|
|
2141
|
+
if (status === "unauthenticated") {
|
|
2142
|
+
return /* @__PURE__ */ jsx18(
|
|
2143
|
+
Navigate,
|
|
2144
|
+
{
|
|
2145
|
+
to: loginRedirect(location.pathname, location.search),
|
|
2146
|
+
replace: true
|
|
2147
|
+
}
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
if (status === "forbidden") {
|
|
2151
|
+
return /* @__PURE__ */ jsx18(Navigate, { to: "/acesso-negado", replace: true });
|
|
2152
|
+
}
|
|
2153
|
+
if (status === "error") {
|
|
2154
|
+
return /* @__PURE__ */ jsx18(
|
|
2155
|
+
LoadingScreen,
|
|
2156
|
+
{
|
|
2157
|
+
message: "N\xE3o foi poss\xEDvel validar seu acesso. Tente novamente.",
|
|
2158
|
+
actionLabel: "Tentar novamente",
|
|
2159
|
+
onAction: () => {
|
|
2160
|
+
void retry().catch(() => void 0);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
return /* @__PURE__ */ jsx18(Fragment, { children });
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// src/security/LoginPage.tsx
|
|
2169
|
+
import { useState as useState8 } from "react";
|
|
2170
|
+
import {
|
|
2171
|
+
Box as Box11,
|
|
2172
|
+
Button as Button8,
|
|
2173
|
+
Flex as Flex9,
|
|
2174
|
+
Heading as Heading7,
|
|
2175
|
+
Input as Input4,
|
|
2176
|
+
Text as Text12,
|
|
2177
|
+
VStack as VStack2
|
|
2178
|
+
} from "@chakra-ui/react";
|
|
2179
|
+
import { Navigate as Navigate2, useLocation as useLocation2 } from "react-router-dom";
|
|
2180
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2181
|
+
function safeRedirect(search) {
|
|
2182
|
+
const value = new URLSearchParams(search).get("redirect") || "/";
|
|
2183
|
+
if (!value.startsWith("/") || value.startsWith("//")) return "/";
|
|
2184
|
+
if (value.startsWith("/login") || value.startsWith("/acesso-negado")) {
|
|
2185
|
+
return "/";
|
|
2186
|
+
}
|
|
2187
|
+
return value;
|
|
2188
|
+
}
|
|
2189
|
+
function LoginPage({ app }) {
|
|
2190
|
+
const { status, login } = useOonAuth();
|
|
2191
|
+
const location = useLocation2();
|
|
2192
|
+
const [email, setEmail] = useState8("");
|
|
2193
|
+
const [password, setPassword] = useState8("");
|
|
2194
|
+
const [submitting, setSubmitting] = useState8(false);
|
|
2195
|
+
const [message, setMessage] = useState8(null);
|
|
2196
|
+
const redirect = safeRedirect(location.search);
|
|
2197
|
+
if (status === "authenticated") {
|
|
2198
|
+
return /* @__PURE__ */ jsx19(Navigate2, { to: redirect, replace: true });
|
|
2199
|
+
}
|
|
2200
|
+
if (status === "forbidden") {
|
|
2201
|
+
return /* @__PURE__ */ jsx19(Navigate2, { to: "/acesso-negado", replace: true });
|
|
2202
|
+
}
|
|
2203
|
+
async function handleSubmit(event) {
|
|
2204
|
+
event.preventDefault();
|
|
2205
|
+
setSubmitting(true);
|
|
2206
|
+
setMessage(null);
|
|
2207
|
+
try {
|
|
2208
|
+
await login(email.trim(), password);
|
|
2209
|
+
} catch (error) {
|
|
2210
|
+
const authError = error;
|
|
2211
|
+
if (authError.status === 403) return;
|
|
2212
|
+
setMessage(
|
|
2213
|
+
authError.status === 401 ? "E-mail ou senha inv\xE1lidos." : authError.message || "N\xE3o foi poss\xEDvel entrar. Tente novamente."
|
|
2214
|
+
);
|
|
2215
|
+
} finally {
|
|
2216
|
+
setSubmitting(false);
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
return /* @__PURE__ */ jsx19(Flex9, { minH: "100vh", align: "center", justify: "center", bg: "#F5F8FA", px: 4, children: /* @__PURE__ */ jsx19(
|
|
2220
|
+
Box11,
|
|
2221
|
+
{
|
|
2222
|
+
width: "100%",
|
|
2223
|
+
maxW: "440px",
|
|
2224
|
+
bg: "white",
|
|
2225
|
+
borderWidth: "1px",
|
|
2226
|
+
borderColor: "#E4EBEF",
|
|
2227
|
+
borderRadius: "18px",
|
|
2228
|
+
boxShadow: "0 24px 70px rgba(7, 38, 46, 0.10)",
|
|
2229
|
+
p: { base: 6, md: 9 },
|
|
2230
|
+
children: /* @__PURE__ */ jsx19("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs12(VStack2, { align: "stretch", gap: 6, children: [
|
|
2231
|
+
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
2232
|
+
/* @__PURE__ */ jsx19(
|
|
2233
|
+
Text12,
|
|
2234
|
+
{
|
|
2235
|
+
color: "#39BFED",
|
|
2236
|
+
fontSize: "xs",
|
|
2237
|
+
fontWeight: "700",
|
|
2238
|
+
letterSpacing: "0.12em",
|
|
2239
|
+
textTransform: "uppercase",
|
|
2240
|
+
children: "Central Oon"
|
|
2241
|
+
}
|
|
2242
|
+
),
|
|
2243
|
+
/* @__PURE__ */ jsx19(Heading7, { mt: 2, size: "lg", color: "#07262E", children: app.title ?? app.name }),
|
|
2244
|
+
/* @__PURE__ */ jsx19(Text12, { mt: 2, color: "gray.600", fontSize: "sm", children: "Entre com seu usu\xE1rio autorizado para acessar este aplicativo." })
|
|
2245
|
+
] }),
|
|
2246
|
+
/* @__PURE__ */ jsxs12(VStack2, { align: "stretch", gap: 4, children: [
|
|
2247
|
+
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
2248
|
+
/* @__PURE__ */ jsx19(Text12, { mb: 2, fontSize: "sm", fontWeight: "600", color: "#24323A", children: "E-mail" }),
|
|
2249
|
+
/* @__PURE__ */ jsx19(
|
|
2250
|
+
Input4,
|
|
2251
|
+
{
|
|
2252
|
+
type: "email",
|
|
2253
|
+
value: email,
|
|
2254
|
+
onChange: (event) => setEmail(event.target.value),
|
|
2255
|
+
autoComplete: "email",
|
|
2256
|
+
placeholder: "voce@empresa.com.br",
|
|
2257
|
+
required: true
|
|
2258
|
+
}
|
|
2259
|
+
)
|
|
2260
|
+
] }),
|
|
2261
|
+
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
2262
|
+
/* @__PURE__ */ jsx19(Text12, { mb: 2, fontSize: "sm", fontWeight: "600", color: "#24323A", children: "Senha" }),
|
|
2263
|
+
/* @__PURE__ */ jsx19(
|
|
2264
|
+
Input4,
|
|
2265
|
+
{
|
|
2266
|
+
type: "password",
|
|
2267
|
+
value: password,
|
|
2268
|
+
onChange: (event) => setPassword(event.target.value),
|
|
2269
|
+
autoComplete: "current-password",
|
|
2270
|
+
placeholder: "Sua senha",
|
|
2271
|
+
required: true
|
|
2272
|
+
}
|
|
2273
|
+
)
|
|
2274
|
+
] })
|
|
2275
|
+
] }),
|
|
2276
|
+
message ? /* @__PURE__ */ jsx19(
|
|
2277
|
+
Box11,
|
|
2278
|
+
{
|
|
2279
|
+
role: "alert",
|
|
2280
|
+
borderRadius: "10px",
|
|
2281
|
+
bg: "red.50",
|
|
2282
|
+
color: "red.700",
|
|
2283
|
+
px: 4,
|
|
2284
|
+
py: 3,
|
|
2285
|
+
fontSize: "sm",
|
|
2286
|
+
children: message
|
|
2287
|
+
}
|
|
2288
|
+
) : null,
|
|
2289
|
+
/* @__PURE__ */ jsx19(
|
|
2290
|
+
Button8,
|
|
2291
|
+
{
|
|
2292
|
+
type: "submit",
|
|
2293
|
+
bg: "#07262E",
|
|
2294
|
+
color: "white",
|
|
2295
|
+
_hover: { bg: "#0B3843" },
|
|
2296
|
+
disabled: submitting || !email.trim() || !password,
|
|
2297
|
+
children: submitting ? "Validando acesso..." : "Entrar"
|
|
2298
|
+
}
|
|
2299
|
+
)
|
|
2300
|
+
] }) })
|
|
2301
|
+
}
|
|
2302
|
+
) });
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
// src/security/ActivationPage.tsx
|
|
2306
|
+
import { useEffect as useEffect5, useState as useState9 } from "react";
|
|
2307
|
+
import { Box as Box12, Button as Button9, Heading as Heading8, Input as Input5, Text as Text13, VStack as VStack3 } from "@chakra-ui/react";
|
|
2308
|
+
import { Fragment as Fragment2, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2309
|
+
function ActivationPage() {
|
|
2310
|
+
const { http } = useOonApi();
|
|
2311
|
+
const activation = useActivation();
|
|
2312
|
+
const [step, setStep] = useState9(1);
|
|
2313
|
+
const [config, setConfig] = useState9(null);
|
|
2314
|
+
const [code, setCode] = useState9("");
|
|
2315
|
+
const [preview, setPreview] = useState9(null);
|
|
2316
|
+
const [global, setGlobal] = useState9({});
|
|
2317
|
+
const [app, setApp] = useState9({});
|
|
2318
|
+
const [rootEmail, setRootEmail] = useState9("");
|
|
2319
|
+
const [rootPassword, setRootPassword] = useState9("");
|
|
2320
|
+
const [error, setError] = useState9(null);
|
|
2321
|
+
const [submitting, setSubmitting] = useState9(false);
|
|
2322
|
+
useEffect5(() => {
|
|
2323
|
+
http.get("/ativacao/configuracao").then((response) => {
|
|
2324
|
+
setConfig(response.data);
|
|
2325
|
+
setGlobal(response.data.defaults || {});
|
|
2326
|
+
}).catch(
|
|
2327
|
+
() => setError("N\xE3o foi poss\xEDvel carregar a configura\xE7\xE3o de ativa\xE7\xE3o.")
|
|
2328
|
+
);
|
|
2329
|
+
}, [http]);
|
|
2330
|
+
useEffect5(() => {
|
|
2331
|
+
if (activation.status === "active") {
|
|
2332
|
+
window.location.replace("/login");
|
|
2333
|
+
}
|
|
2334
|
+
}, [activation.status]);
|
|
2335
|
+
async function activateRoot() {
|
|
2336
|
+
setSubmitting(true);
|
|
2337
|
+
setError(null);
|
|
2338
|
+
try {
|
|
2339
|
+
await http.post("/ativacao/ativar-root", {
|
|
2340
|
+
email: rootEmail.trim(),
|
|
2341
|
+
senha: rootPassword
|
|
2342
|
+
});
|
|
2343
|
+
setRootPassword("");
|
|
2344
|
+
setStep(6);
|
|
2345
|
+
await activation.refresh();
|
|
2346
|
+
window.setTimeout(() => window.location.replace("/login"), 900);
|
|
2347
|
+
} catch (caught) {
|
|
2348
|
+
setRootPassword("");
|
|
2349
|
+
setError(
|
|
2350
|
+
caught.message || "N\xE3o foi poss\xEDvel concluir a ativa\xE7\xE3o inicial."
|
|
2351
|
+
);
|
|
2352
|
+
} finally {
|
|
2353
|
+
setSubmitting(false);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
async function validateCode() {
|
|
2357
|
+
setSubmitting(true);
|
|
2358
|
+
setError(null);
|
|
2359
|
+
try {
|
|
2360
|
+
const response = await http.post("/ativacao/validar-codigo", {
|
|
2361
|
+
codigoAtivacao: code
|
|
2362
|
+
});
|
|
2363
|
+
setPreview(response.data);
|
|
2364
|
+
setStep(2);
|
|
2365
|
+
} catch (caught) {
|
|
2366
|
+
setError(caught.message || "C\xF3digo inv\xE1lido.");
|
|
2367
|
+
} finally {
|
|
2368
|
+
setSubmitting(false);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
async function completeMemberActivation() {
|
|
2372
|
+
setSubmitting(true);
|
|
2373
|
+
setError(null);
|
|
2374
|
+
try {
|
|
2375
|
+
await http.post("/ativacao/concluir", {
|
|
2376
|
+
codigoAtivacao: code,
|
|
2377
|
+
nomeInstancia: global.nomeInstancia,
|
|
2378
|
+
parametrosGlobais: global,
|
|
2379
|
+
parametrosAplicacao: app
|
|
2380
|
+
});
|
|
2381
|
+
setStep(6);
|
|
2382
|
+
setCode("");
|
|
2383
|
+
await activation.refresh();
|
|
2384
|
+
window.setTimeout(() => window.location.replace("/login"), 1200);
|
|
2385
|
+
} catch (caught) {
|
|
2386
|
+
setError(
|
|
2387
|
+
caught.message || "N\xE3o foi poss\xEDvel concluir a ativa\xE7\xE3o."
|
|
2388
|
+
);
|
|
2389
|
+
} finally {
|
|
2390
|
+
setSubmitting(false);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
function renderField(field, values, setValues) {
|
|
2394
|
+
return /* @__PURE__ */ jsxs13(Box12, { w: "100%", children: [
|
|
2395
|
+
/* @__PURE__ */ jsxs13(Text13, { fontWeight: "medium", children: [
|
|
2396
|
+
field.label,
|
|
2397
|
+
field.required ? " *" : ""
|
|
1039
2398
|
] }),
|
|
1040
|
-
/* @__PURE__ */
|
|
1041
|
-
|
|
2399
|
+
/* @__PURE__ */ jsx20(
|
|
2400
|
+
Input5,
|
|
2401
|
+
{
|
|
2402
|
+
type: field.type === "password" || field.type === "secret" ? "password" : field.type === "email" ? "email" : field.type === "url" ? "url" : field.type === "number" || field.type === "integer" ? "number" : "text",
|
|
2403
|
+
value: String(values[field.name] ?? field.defaultValue ?? ""),
|
|
2404
|
+
readOnly: field.readOnly,
|
|
2405
|
+
placeholder: field.placeholder,
|
|
2406
|
+
minLength: field.minLength,
|
|
2407
|
+
autoComplete: field.autoComplete,
|
|
2408
|
+
onChange: (event) => setValues({ ...values, [field.name]: event.target.value })
|
|
2409
|
+
}
|
|
2410
|
+
),
|
|
2411
|
+
field.helpText ? /* @__PURE__ */ jsx20(Text13, { fontSize: "sm", color: "gray.500", children: field.helpText }) : null
|
|
2412
|
+
] }, field.name);
|
|
2413
|
+
}
|
|
2414
|
+
const rootMode = config?.mode === "root-bootstrap" || activation.details?.role === "root";
|
|
2415
|
+
if (activation.status === "loading" || !config) {
|
|
2416
|
+
return /* @__PURE__ */ jsx20(Box12, { minH: "100vh", bg: "gray.50", p: 8, children: /* @__PURE__ */ jsxs13(
|
|
2417
|
+
VStack3,
|
|
2418
|
+
{
|
|
2419
|
+
maxW: "760px",
|
|
2420
|
+
mx: "auto",
|
|
2421
|
+
bg: "white",
|
|
2422
|
+
p: 8,
|
|
2423
|
+
borderRadius: "lg",
|
|
2424
|
+
align: "stretch",
|
|
2425
|
+
gap: 5,
|
|
2426
|
+
children: [
|
|
2427
|
+
/* @__PURE__ */ jsx20(Heading8, { children: "Preparando ativa\xE7\xE3o..." }),
|
|
2428
|
+
error ? /* @__PURE__ */ jsx20(Text13, { color: "red.600", children: error }) : null
|
|
2429
|
+
]
|
|
2430
|
+
}
|
|
2431
|
+
) });
|
|
2432
|
+
}
|
|
2433
|
+
if (rootMode) {
|
|
2434
|
+
return /* @__PURE__ */ jsx20(Box12, { minH: "100vh", bg: "gray.50", p: 8, children: /* @__PURE__ */ jsxs13(
|
|
2435
|
+
VStack3,
|
|
2436
|
+
{
|
|
2437
|
+
maxW: "560px",
|
|
2438
|
+
mx: "auto",
|
|
2439
|
+
bg: "white",
|
|
2440
|
+
p: 8,
|
|
2441
|
+
borderRadius: "lg",
|
|
2442
|
+
align: "stretch",
|
|
2443
|
+
gap: 5,
|
|
2444
|
+
children: [
|
|
2445
|
+
/* @__PURE__ */ jsx20(Heading8, { children: "Ativa\xE7\xE3o inicial do Ecossistema Oon" }),
|
|
2446
|
+
/* @__PURE__ */ jsx20(Text13, { 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." }),
|
|
2447
|
+
/* @__PURE__ */ jsxs13(Text13, { fontSize: "sm", color: "gray.500", children: [
|
|
2448
|
+
"App: ",
|
|
2449
|
+
config.technical.appCode,
|
|
2450
|
+
" \xB7 Ambiente:",
|
|
2451
|
+
" ",
|
|
2452
|
+
config.technical.ambiente
|
|
2453
|
+
] }),
|
|
2454
|
+
error ? /* @__PURE__ */ jsx20(Text13, { color: "red.600", children: error }) : null,
|
|
2455
|
+
step !== 6 ? /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2456
|
+
/* @__PURE__ */ jsxs13(Box12, { w: "100%", children: [
|
|
2457
|
+
/* @__PURE__ */ jsx20(Text13, { fontWeight: "medium", children: "E-mail do administrador *" }),
|
|
2458
|
+
/* @__PURE__ */ jsx20(
|
|
2459
|
+
Input5,
|
|
2460
|
+
{
|
|
2461
|
+
type: "email",
|
|
2462
|
+
autoComplete: "email",
|
|
2463
|
+
value: rootEmail,
|
|
2464
|
+
onChange: (event) => setRootEmail(event.target.value),
|
|
2465
|
+
placeholder: "administrador@empresa.com.br"
|
|
2466
|
+
}
|
|
2467
|
+
)
|
|
2468
|
+
] }),
|
|
2469
|
+
/* @__PURE__ */ jsxs13(Box12, { w: "100%", children: [
|
|
2470
|
+
/* @__PURE__ */ jsx20(Text13, { fontWeight: "medium", children: "Senha *" }),
|
|
2471
|
+
/* @__PURE__ */ jsx20(
|
|
2472
|
+
Input5,
|
|
2473
|
+
{
|
|
2474
|
+
type: "password",
|
|
2475
|
+
minLength: 8,
|
|
2476
|
+
autoComplete: "new-password",
|
|
2477
|
+
value: rootPassword,
|
|
2478
|
+
onChange: (event) => setRootPassword(event.target.value),
|
|
2479
|
+
placeholder: "M\xEDnimo de 8 caracteres"
|
|
2480
|
+
}
|
|
2481
|
+
)
|
|
2482
|
+
] }),
|
|
2483
|
+
/* @__PURE__ */ jsx20(
|
|
2484
|
+
Button9,
|
|
2485
|
+
{
|
|
2486
|
+
colorPalette: "blue",
|
|
2487
|
+
onClick: activateRoot,
|
|
2488
|
+
loading: submitting,
|
|
2489
|
+
disabled: submitting || !rootEmail.trim() || rootPassword.length < 8 || !activation.details?.podeAtivar,
|
|
2490
|
+
children: "Criar ecossistema e ativar"
|
|
2491
|
+
}
|
|
2492
|
+
)
|
|
2493
|
+
] }) : /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2494
|
+
/* @__PURE__ */ jsx20(Heading8, { size: "md", children: "Ativa\xE7\xE3o conclu\xEDda" }),
|
|
2495
|
+
/* @__PURE__ */ jsx20(Text13, { children: "Usu\xE1rio, tenant, aplicativo, licen\xE7a e permiss\xF5es foram preparados. Redirecionando para o primeiro login..." })
|
|
2496
|
+
] })
|
|
2497
|
+
]
|
|
2498
|
+
}
|
|
2499
|
+
) });
|
|
2500
|
+
}
|
|
2501
|
+
return /* @__PURE__ */ jsx20(Box12, { minH: "100vh", bg: "gray.50", p: 8, children: /* @__PURE__ */ jsxs13(
|
|
2502
|
+
VStack3,
|
|
2503
|
+
{
|
|
2504
|
+
maxW: "760px",
|
|
2505
|
+
mx: "auto",
|
|
2506
|
+
bg: "white",
|
|
2507
|
+
p: 8,
|
|
2508
|
+
borderRadius: "lg",
|
|
2509
|
+
align: "stretch",
|
|
2510
|
+
gap: 5,
|
|
2511
|
+
children: [
|
|
2512
|
+
/* @__PURE__ */ jsx20(Heading8, { children: "Ativa\xE7\xE3o da Central Oon" }),
|
|
2513
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
2514
|
+
"App: ",
|
|
2515
|
+
config.technical.appCode,
|
|
2516
|
+
" \xB7 Ambiente:",
|
|
2517
|
+
" ",
|
|
2518
|
+
config.technical.ambiente,
|
|
2519
|
+
" \xB7 Central:",
|
|
2520
|
+
" ",
|
|
2521
|
+
config.technical.centralAtivacaoHost
|
|
2522
|
+
] }),
|
|
2523
|
+
error ? /* @__PURE__ */ jsx20(Text13, { color: "red.600", children: error }) : null,
|
|
2524
|
+
step === 1 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2525
|
+
/* @__PURE__ */ jsx20(Text13, { children: "Informe o c\xF3digo de ativa\xE7\xE3o para validar tenant, aplicativo e licen\xE7a antes de consumir o c\xF3digo." }),
|
|
2526
|
+
/* @__PURE__ */ jsx20(
|
|
2527
|
+
Input5,
|
|
2528
|
+
{
|
|
2529
|
+
"aria-label": "C\xF3digo de ativa\xE7\xE3o",
|
|
2530
|
+
value: code,
|
|
2531
|
+
onChange: (event) => setCode(event.target.value),
|
|
2532
|
+
placeholder: "OON-XXXX"
|
|
2533
|
+
}
|
|
2534
|
+
),
|
|
2535
|
+
/* @__PURE__ */ jsx20(
|
|
2536
|
+
Button9,
|
|
2537
|
+
{
|
|
2538
|
+
onClick: validateCode,
|
|
2539
|
+
loading: submitting,
|
|
2540
|
+
disabled: !code || submitting,
|
|
2541
|
+
children: "Validar c\xF3digo"
|
|
2542
|
+
}
|
|
2543
|
+
)
|
|
2544
|
+
] }),
|
|
2545
|
+
step === 2 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2546
|
+
/* @__PURE__ */ jsx20(Heading8, { size: "md", children: "Confirma\xE7\xE3o" }),
|
|
2547
|
+
/* @__PURE__ */ jsx20("pre", { children: JSON.stringify(preview, null, 2) }),
|
|
2548
|
+
/* @__PURE__ */ jsx20(Button9, { onClick: () => setStep(3), children: "Continuar" })
|
|
2549
|
+
] }),
|
|
2550
|
+
step === 3 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2551
|
+
config.globalFields.map(
|
|
2552
|
+
(field) => renderField(field, global, setGlobal)
|
|
2553
|
+
),
|
|
2554
|
+
/* @__PURE__ */ jsx20(Button9, { onClick: () => setStep(4), children: "Continuar" })
|
|
2555
|
+
] }),
|
|
2556
|
+
step === 4 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2557
|
+
config.appFields.length ? config.appFields.map((field) => renderField(field, app, setApp)) : /* @__PURE__ */ jsx20(Text13, { children: "Nenhum par\xE2metro adicional declarado." }),
|
|
2558
|
+
/* @__PURE__ */ jsx20(Button9, { onClick: () => setStep(5), children: "Revisar" })
|
|
2559
|
+
] }),
|
|
2560
|
+
step === 5 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2561
|
+
/* @__PURE__ */ jsx20(Text13, { children: "Ao confirmar, o c\xF3digo ser\xE1 consumido e a inst\xE2ncia ser\xE1 registrada no Ecossistema Oon." }),
|
|
2562
|
+
/* @__PURE__ */ jsx20(
|
|
2563
|
+
Button9,
|
|
2564
|
+
{
|
|
2565
|
+
colorPalette: "blue",
|
|
2566
|
+
onClick: completeMemberActivation,
|
|
2567
|
+
loading: submitting,
|
|
2568
|
+
disabled: submitting,
|
|
2569
|
+
children: "Concluir ativa\xE7\xE3o"
|
|
2570
|
+
}
|
|
2571
|
+
)
|
|
2572
|
+
] }),
|
|
2573
|
+
step === 6 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2574
|
+
/* @__PURE__ */ jsx20(Heading8, { size: "md", children: "Ativa\xE7\xE3o conclu\xEDda" }),
|
|
2575
|
+
/* @__PURE__ */ jsx20(Text13, { children: "Redirecionando para o login..." })
|
|
2576
|
+
] })
|
|
2577
|
+
]
|
|
2578
|
+
}
|
|
2579
|
+
) });
|
|
2580
|
+
}
|
|
2581
|
+
function InstanceBlockedPage({
|
|
2582
|
+
title,
|
|
2583
|
+
message
|
|
2584
|
+
}) {
|
|
2585
|
+
return /* @__PURE__ */ jsxs13(Box12, { p: 8, children: [
|
|
2586
|
+
/* @__PURE__ */ jsx20(Heading8, { children: title }),
|
|
2587
|
+
/* @__PURE__ */ jsx20(Text13, { children: message })
|
|
1042
2588
|
] });
|
|
1043
2589
|
}
|
|
1044
2590
|
|
|
1045
2591
|
// src/security/RouteGuard.tsx
|
|
1046
|
-
import { Fragment, jsx as
|
|
2592
|
+
import { Fragment as Fragment3, jsx as jsx21 } from "react/jsx-runtime";
|
|
1047
2593
|
function RouteGuard({
|
|
1048
2594
|
permission,
|
|
1049
2595
|
children
|
|
1050
2596
|
}) {
|
|
1051
2597
|
const { status, hasPermission } = useOonAuth();
|
|
1052
|
-
if (status === "loading") return /* @__PURE__ */
|
|
1053
|
-
if (status === "unauthenticated") return /* @__PURE__ */
|
|
2598
|
+
if (status === "loading") return /* @__PURE__ */ jsx21(LoadingScreen, {});
|
|
2599
|
+
if (status === "unauthenticated") return /* @__PURE__ */ jsx21(LoadingScreen, { message: "Redirecionando para o login..." });
|
|
1054
2600
|
if (!hasPermission(permission)) {
|
|
1055
|
-
return /* @__PURE__ */
|
|
2601
|
+
return /* @__PURE__ */ jsx21(LoadingScreen, { message: "Voc\xEA n\xE3o tem acesso a esta tela." });
|
|
1056
2602
|
}
|
|
1057
|
-
return /* @__PURE__ */
|
|
2603
|
+
return /* @__PURE__ */ jsx21(Fragment3, { children });
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
// src/security/ActivationGuard.tsx
|
|
2607
|
+
import { Navigate as Navigate3 } from "react-router-dom";
|
|
2608
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
2609
|
+
function ActivationGuard({ children }) {
|
|
2610
|
+
const { status } = useActivation();
|
|
2611
|
+
if (status === "loading") return null;
|
|
2612
|
+
if (status === "suspended") return /* @__PURE__ */ jsx22(Navigate3, { to: "/instancia-suspensa", replace: true });
|
|
2613
|
+
if (status === "revoked") return /* @__PURE__ */ jsx22(Navigate3, { to: "/instancia-revogada", replace: true });
|
|
2614
|
+
if (status !== "active" && window.location.pathname !== "/ativacao") return /* @__PURE__ */ jsx22(Navigate3, { to: "/ativacao", replace: true });
|
|
2615
|
+
return children;
|
|
1058
2616
|
}
|
|
1059
2617
|
|
|
1060
2618
|
// src/app/createOonRouter.tsx
|
|
1061
|
-
import { jsx as
|
|
2619
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
1062
2620
|
function createOonRouter({ app, menu, routes, security }) {
|
|
1063
2621
|
const guardEnabled = security?.enableRouteGuard !== false;
|
|
1064
|
-
const publicRoutes = routes.filter((
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
2622
|
+
const publicRoutes = routes.filter((route) => route.public).map((route) => ({ path: route.path, element: route.element }));
|
|
2623
|
+
const privateSource = routes.filter((route) => !route.public);
|
|
2624
|
+
const privateRoutes = privateSource.map((route) => {
|
|
2625
|
+
const element = guardEnabled ? /* @__PURE__ */ jsx23(RouteGuard, { permission: route.permissions, children: route.element }) : route.element;
|
|
2626
|
+
return route.path === "/" ? { index: true, element } : { path: route.path.replace(/^\//, ""), element };
|
|
1068
2627
|
});
|
|
2628
|
+
const hasIndexRoute = privateSource.some((route) => route.path === "/");
|
|
2629
|
+
const firstRoute = privateSource.find((route) => route.path !== "/");
|
|
2630
|
+
if (!hasIndexRoute && firstRoute) {
|
|
2631
|
+
privateRoutes.unshift({
|
|
2632
|
+
index: true,
|
|
2633
|
+
element: /* @__PURE__ */ jsx23(Navigate4, { to: firstRoute.path, replace: true })
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
const customPaths = new Set(publicRoutes.map((route) => route.path));
|
|
2637
|
+
const builtInPublicRoutes = [];
|
|
2638
|
+
if (!customPaths.has("/login")) {
|
|
2639
|
+
builtInPublicRoutes.push({
|
|
2640
|
+
path: "/login",
|
|
2641
|
+
element: /* @__PURE__ */ jsx23(ActivationGuard, { children: /* @__PURE__ */ jsx23(LoginPage, { app }) })
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
if (!customPaths.has("/ativacao")) {
|
|
2645
|
+
builtInPublicRoutes.push({
|
|
2646
|
+
path: "/ativacao",
|
|
2647
|
+
element: /* @__PURE__ */ jsx23(ActivationPage, {})
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2650
|
+
if (!customPaths.has("/instancia-suspensa")) {
|
|
2651
|
+
builtInPublicRoutes.push({
|
|
2652
|
+
path: "/instancia-suspensa",
|
|
2653
|
+
element: /* @__PURE__ */ jsx23(
|
|
2654
|
+
InstanceBlockedPage,
|
|
2655
|
+
{
|
|
2656
|
+
title: "Inst\xE2ncia suspensa",
|
|
2657
|
+
message: "Esta Central est\xE1 suspensa."
|
|
2658
|
+
}
|
|
2659
|
+
)
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
if (!customPaths.has("/instancia-revogada")) {
|
|
2663
|
+
builtInPublicRoutes.push({
|
|
2664
|
+
path: "/instancia-revogada",
|
|
2665
|
+
element: /* @__PURE__ */ jsx23(
|
|
2666
|
+
InstanceBlockedPage,
|
|
2667
|
+
{
|
|
2668
|
+
title: "Inst\xE2ncia revogada",
|
|
2669
|
+
message: "Esta Central foi revogada."
|
|
2670
|
+
}
|
|
2671
|
+
)
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
if (!customPaths.has("/acesso-negado")) {
|
|
2675
|
+
builtInPublicRoutes.push({
|
|
2676
|
+
path: "/acesso-negado",
|
|
2677
|
+
element: /* @__PURE__ */ jsx23(AccessDeniedPage, { app })
|
|
2678
|
+
});
|
|
2679
|
+
}
|
|
1069
2680
|
const tree = [
|
|
2681
|
+
...builtInPublicRoutes,
|
|
1070
2682
|
...publicRoutes,
|
|
1071
2683
|
{
|
|
1072
2684
|
path: "/",
|
|
1073
|
-
element: /* @__PURE__ */
|
|
2685
|
+
element: /* @__PURE__ */ jsx23(ActivationGuard, { children: /* @__PURE__ */ jsx23(AppAccessGuard, { children: /* @__PURE__ */ jsx23(AuthenticatedLayout, { app, menu }) }) }),
|
|
1074
2686
|
children: privateRoutes
|
|
1075
2687
|
}
|
|
1076
2688
|
];
|
|
@@ -1078,29 +2690,44 @@ function createOonRouter({ app, menu, routes, security }) {
|
|
|
1078
2690
|
}
|
|
1079
2691
|
|
|
1080
2692
|
// src/app/OonApp.tsx
|
|
1081
|
-
import { jsx as
|
|
2693
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
2694
|
+
function localLoginUrl() {
|
|
2695
|
+
const current = `${window.location.pathname}${window.location.search}`;
|
|
2696
|
+
return `/login?redirect=${encodeURIComponent(current)}`;
|
|
2697
|
+
}
|
|
1082
2698
|
function OonApp({ config }) {
|
|
1083
|
-
const { http, storage, router } =
|
|
2699
|
+
const { http, storage, router } = useMemo4(() => {
|
|
1084
2700
|
const storage2 = createTokenStorage(config.app.id);
|
|
1085
|
-
const
|
|
2701
|
+
const externalLoginUrl = config.auth?.loginUrl ?? config.api.meusAppsUrl;
|
|
1086
2702
|
const http2 = createRestClient({
|
|
1087
2703
|
api: config.api,
|
|
1088
2704
|
appId: config.app.id,
|
|
1089
2705
|
getToken: () => storage2.get(),
|
|
1090
2706
|
onUnauthorized: () => {
|
|
1091
2707
|
storage2.clear();
|
|
1092
|
-
if (
|
|
2708
|
+
if (externalLoginUrl) {
|
|
2709
|
+
window.location.href = externalLoginUrl;
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
if (window.location.pathname !== "/login") {
|
|
2713
|
+
window.location.href = localLoginUrl();
|
|
2714
|
+
}
|
|
1093
2715
|
}
|
|
1094
2716
|
});
|
|
1095
2717
|
const { routes, menu } = resolveRegistry(config);
|
|
1096
|
-
const router2 = createOonRouter({
|
|
2718
|
+
const router2 = createOonRouter({
|
|
2719
|
+
app: config.app,
|
|
2720
|
+
menu,
|
|
2721
|
+
routes,
|
|
2722
|
+
security: config.security
|
|
2723
|
+
});
|
|
1097
2724
|
return { http: http2, storage: storage2, router: router2 };
|
|
1098
2725
|
}, [config]);
|
|
1099
|
-
return /* @__PURE__ */
|
|
2726
|
+
return /* @__PURE__ */ jsx24(OonProviders, { config, http, storage, children: /* @__PURE__ */ jsx24(RouterProvider, { router }) });
|
|
1100
2727
|
}
|
|
1101
2728
|
|
|
1102
2729
|
// src/start.tsx
|
|
1103
|
-
import { jsx as
|
|
2730
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
1104
2731
|
function validateConfig(config) {
|
|
1105
2732
|
if (!config?.app?.id) throw new Error("oonCoreFront.start: config.app.id \xE9 obrigat\xF3rio.");
|
|
1106
2733
|
if (!config?.api?.baseUrl) throw new Error("oonCoreFront.start: config.api.baseUrl \xE9 obrigat\xF3rio.");
|
|
@@ -1134,7 +2761,7 @@ function start(config) {
|
|
|
1134
2761
|
const el = document.getElementById(rootId);
|
|
1135
2762
|
if (!el) throw new Error(`oonCoreFront.start: elemento #${rootId} n\xE3o encontrado no DOM.`);
|
|
1136
2763
|
createRoot(el).render(
|
|
1137
|
-
/* @__PURE__ */
|
|
2764
|
+
/* @__PURE__ */ jsx25(StrictMode, { children: /* @__PURE__ */ jsx25(OonApp, { config }) })
|
|
1138
2765
|
);
|
|
1139
2766
|
}
|
|
1140
2767
|
var oonCoreFront = { start };
|
|
@@ -1190,7 +2817,7 @@ function manifestToConfig(manifest, runtime) {
|
|
|
1190
2817
|
assistantBaseUrl: runtime.assistantBaseUrl,
|
|
1191
2818
|
versionPrefix: runtime.versionPrefix ?? ""
|
|
1192
2819
|
},
|
|
1193
|
-
auth: { mode: "bearer", tokenParam: "code" },
|
|
2820
|
+
auth: { mode: "bearer", tokenParam: "code", devToken: runtime.devToken },
|
|
1194
2821
|
security: {
|
|
1195
2822
|
enableRouteGuard: true,
|
|
1196
2823
|
enablePermissionGate: true,
|
|
@@ -1222,34 +2849,34 @@ function defineOonModule(mod) {
|
|
|
1222
2849
|
}
|
|
1223
2850
|
|
|
1224
2851
|
// src/components/CoreIntegration.tsx
|
|
1225
|
-
import { Badge, Box as
|
|
1226
|
-
import { useMutation as
|
|
1227
|
-
import { jsx as
|
|
2852
|
+
import { Badge as Badge3, Box as Box13, Button as Button10, Flex as Flex10, Heading as Heading9, Stack as Stack7, Text as Text14 } from "@chakra-ui/react";
|
|
2853
|
+
import { useMutation as useMutation3 } from "@tanstack/react-query";
|
|
2854
|
+
import { jsx as jsx26, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1228
2855
|
function CoreIntegration({ label = "Integra\xE7\xF5es" }) {
|
|
1229
2856
|
const { http } = useOonApi();
|
|
1230
2857
|
const metadata = useCoreMetadata();
|
|
1231
|
-
const reprocess =
|
|
2858
|
+
const reprocess = useMutation3({
|
|
1232
2859
|
mutationFn: () => http.post("/integrations/actions/process-active", {}, { headers: { "x-oon-origin": "integration" } })
|
|
1233
2860
|
});
|
|
1234
|
-
if (metadata.isLoading) return /* @__PURE__ */
|
|
1235
|
-
if (metadata.isError) return /* @__PURE__ */
|
|
2861
|
+
if (metadata.isLoading) return /* @__PURE__ */ jsx26(LoadingScreen, {});
|
|
2862
|
+
if (metadata.isError) return /* @__PURE__ */ jsx26(ErrorState, { error: metadata.error });
|
|
1236
2863
|
const integrations = metadata.data?.integrations ?? [];
|
|
1237
|
-
return /* @__PURE__ */
|
|
1238
|
-
/* @__PURE__ */
|
|
1239
|
-
/* @__PURE__ */
|
|
1240
|
-
/* @__PURE__ */
|
|
2864
|
+
return /* @__PURE__ */ jsxs14(Stack7, { gap: 6, children: [
|
|
2865
|
+
/* @__PURE__ */ jsxs14(Flex10, { align: "center", justify: "space-between", children: [
|
|
2866
|
+
/* @__PURE__ */ jsx26(Heading9, { size: "lg", children: label }),
|
|
2867
|
+
/* @__PURE__ */ jsx26(Button10, { size: "sm", variant: "outline", loading: reprocess.isPending, onClick: () => reprocess.mutate(), children: "Reprocessar ativos" })
|
|
1241
2868
|
] }),
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
|
|
2869
|
+
/* @__PURE__ */ jsx26(Stack7, { gap: 3, children: integrations.length === 0 ? /* @__PURE__ */ jsx26(Text14, { color: "gray.400", children: "Nenhuma integra\xE7\xE3o declarada." }) : integrations.map((it, idx) => /* @__PURE__ */ jsx26(
|
|
2870
|
+
Box13,
|
|
1244
2871
|
{
|
|
1245
2872
|
borderWidth: "1px",
|
|
1246
2873
|
borderColor: "gray.200",
|
|
1247
2874
|
borderRadius: "md",
|
|
1248
2875
|
bg: "white",
|
|
1249
2876
|
p: 4,
|
|
1250
|
-
children: /* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
2877
|
+
children: /* @__PURE__ */ jsxs14(Flex10, { align: "center", justify: "space-between", children: [
|
|
2878
|
+
/* @__PURE__ */ jsx26(Text14, { fontWeight: "medium", children: it.label ?? it.name ?? "Integra\xE7\xE3o" }),
|
|
2879
|
+
/* @__PURE__ */ jsx26(Badge3, { colorPalette: it.status === "erro" ? "red" : it.status === "ativo" ? "green" : "gray", children: it.status ?? "\u2014" })
|
|
1253
2880
|
] })
|
|
1254
2881
|
},
|
|
1255
2882
|
it.id ?? it.name ?? idx
|
|
@@ -1258,36 +2885,36 @@ function CoreIntegration({ label = "Integra\xE7\xF5es" }) {
|
|
|
1258
2885
|
}
|
|
1259
2886
|
|
|
1260
2887
|
// src/components/CoreCurrency.tsx
|
|
1261
|
-
import { Button as
|
|
1262
|
-
import { useMutation as
|
|
1263
|
-
import { jsx as
|
|
2888
|
+
import { Button as Button11, Flex as Flex11, Heading as Heading10, Stack as Stack8, Text as Text15 } from "@chakra-ui/react";
|
|
2889
|
+
import { useMutation as useMutation4, useQueryClient as useQueryClient3 } from "@tanstack/react-query";
|
|
2890
|
+
import { jsx as jsx27, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1264
2891
|
function CoreCurrency({ model = "Moeda", label = "Multi-moedas", endpoint = "/moedas", columns }) {
|
|
1265
2892
|
const { http } = useOonApi();
|
|
1266
|
-
const queryClient =
|
|
1267
|
-
const updateRates =
|
|
2893
|
+
const queryClient = useQueryClient3();
|
|
2894
|
+
const updateRates = useMutation4({
|
|
1268
2895
|
mutationFn: () => http.post(`${endpoint}/actions/update-rates`, {}, { headers: { "x-oon-origin": "integration" } }),
|
|
1269
2896
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["oon", "list", endpoint] })
|
|
1270
2897
|
});
|
|
1271
|
-
return /* @__PURE__ */
|
|
1272
|
-
/* @__PURE__ */
|
|
1273
|
-
/* @__PURE__ */
|
|
1274
|
-
/* @__PURE__ */
|
|
2898
|
+
return /* @__PURE__ */ jsxs15(Stack8, { gap: 6, children: [
|
|
2899
|
+
/* @__PURE__ */ jsxs15(Flex11, { align: "center", justify: "space-between", children: [
|
|
2900
|
+
/* @__PURE__ */ jsx27(Heading10, { size: "lg", children: label }),
|
|
2901
|
+
/* @__PURE__ */ jsx27(Button11, { size: "sm", colorPalette: "blue", loading: updateRates.isPending, onClick: () => updateRates.mutate(), children: "Atualizar cota\xE7\xF5es" })
|
|
1275
2902
|
] }),
|
|
1276
|
-
updateRates.isError ? /* @__PURE__ */
|
|
1277
|
-
/* @__PURE__ */
|
|
2903
|
+
updateRates.isError ? /* @__PURE__ */ jsx27(Text15, { fontSize: "sm", color: "red.600", children: updateRates.error?.message }) : null,
|
|
2904
|
+
/* @__PURE__ */ jsx27(CoreCollection, { model, label: "", mode: columns ? "full" : "dynamic", endpoint, columns })
|
|
1278
2905
|
] });
|
|
1279
2906
|
}
|
|
1280
2907
|
|
|
1281
2908
|
// src/components/CoreAssistant.tsx
|
|
1282
|
-
import { useState as
|
|
1283
|
-
import { Box as
|
|
1284
|
-
import { useMutation as
|
|
1285
|
-
import { jsx as
|
|
2909
|
+
import { useState as useState10 } from "react";
|
|
2910
|
+
import { Box as Box14, Button as Button12, Flex as Flex12, Heading as Heading11, Input as Input6, Stack as Stack9, Text as Text16 } from "@chakra-ui/react";
|
|
2911
|
+
import { useMutation as useMutation5 } from "@tanstack/react-query";
|
|
2912
|
+
import { jsx as jsx28, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1286
2913
|
function CoreAssistant({ assistantCode, label = "Assistente", entity, endpoint = "/assistant-runs" }) {
|
|
1287
2914
|
const { http } = useOonApi();
|
|
1288
|
-
const [messages, setMessages] =
|
|
1289
|
-
const [input, setInput] =
|
|
1290
|
-
const ask =
|
|
2915
|
+
const [messages, setMessages] = useState10([]);
|
|
2916
|
+
const [input, setInput] = useState10("");
|
|
2917
|
+
const ask = useMutation5({
|
|
1291
2918
|
mutationFn: async (message) => {
|
|
1292
2919
|
const { data } = await http.post(
|
|
1293
2920
|
endpoint,
|
|
@@ -1311,11 +2938,11 @@ function CoreAssistant({ assistantCode, label = "Assistente", entity, endpoint =
|
|
|
1311
2938
|
setInput("");
|
|
1312
2939
|
ask.mutate(text);
|
|
1313
2940
|
};
|
|
1314
|
-
return /* @__PURE__ */
|
|
1315
|
-
/* @__PURE__ */
|
|
1316
|
-
/* @__PURE__ */
|
|
1317
|
-
messages.length === 0 ? /* @__PURE__ */
|
|
1318
|
-
|
|
2941
|
+
return /* @__PURE__ */ jsxs16(Stack9, { gap: 4, maxW: "560px", children: [
|
|
2942
|
+
/* @__PURE__ */ jsx28(Heading11, { size: "md", children: label }),
|
|
2943
|
+
/* @__PURE__ */ jsx28(Box14, { borderWidth: "1px", borderColor: "gray.200", borderRadius: "md", bg: "white", p: 4, minH: "240px", children: /* @__PURE__ */ jsxs16(Stack9, { gap: 3, children: [
|
|
2944
|
+
messages.length === 0 ? /* @__PURE__ */ jsx28(Text16, { color: "gray.400", fontSize: "sm", children: "Fa\xE7a uma pergunta ao assistente." }) : messages.map((m, idx) => /* @__PURE__ */ jsx28(Flex12, { justify: m.role === "user" ? "flex-end" : "flex-start", children: /* @__PURE__ */ jsx28(
|
|
2945
|
+
Box14,
|
|
1319
2946
|
{
|
|
1320
2947
|
bg: m.role === "user" ? "brand.500" : "gray.100",
|
|
1321
2948
|
color: m.role === "user" ? "white" : "gray.800",
|
|
@@ -1327,12 +2954,12 @@ function CoreAssistant({ assistantCode, label = "Assistente", entity, endpoint =
|
|
|
1327
2954
|
children: m.content
|
|
1328
2955
|
}
|
|
1329
2956
|
) }, idx)),
|
|
1330
|
-
ask.isPending ? /* @__PURE__ */
|
|
1331
|
-
ask.isError ? /* @__PURE__ */
|
|
2957
|
+
ask.isPending ? /* @__PURE__ */ jsx28(Text16, { color: "gray.400", fontSize: "sm", children: "Pensando\u2026" }) : null,
|
|
2958
|
+
ask.isError ? /* @__PURE__ */ jsx28(Text16, { color: "red.600", fontSize: "sm", children: ask.error?.message }) : null
|
|
1332
2959
|
] }) }),
|
|
1333
|
-
/* @__PURE__ */
|
|
1334
|
-
/* @__PURE__ */
|
|
1335
|
-
|
|
2960
|
+
/* @__PURE__ */ jsxs16(Flex12, { gap: 2, children: [
|
|
2961
|
+
/* @__PURE__ */ jsx28(
|
|
2962
|
+
Input6,
|
|
1336
2963
|
{
|
|
1337
2964
|
size: "sm",
|
|
1338
2965
|
value: input,
|
|
@@ -1343,20 +2970,20 @@ function CoreAssistant({ assistantCode, label = "Assistente", entity, endpoint =
|
|
|
1343
2970
|
}
|
|
1344
2971
|
}
|
|
1345
2972
|
),
|
|
1346
|
-
/* @__PURE__ */
|
|
2973
|
+
/* @__PURE__ */ jsx28(Button12, { size: "sm", colorPalette: "blue", onClick: send, loading: ask.isPending, children: "Enviar" })
|
|
1347
2974
|
] })
|
|
1348
2975
|
] });
|
|
1349
2976
|
}
|
|
1350
2977
|
|
|
1351
2978
|
// src/security/PermissionGate.tsx
|
|
1352
|
-
import { Fragment as
|
|
2979
|
+
import { Fragment as Fragment4, jsx as jsx29 } from "react/jsx-runtime";
|
|
1353
2980
|
function PermissionGate({
|
|
1354
2981
|
permission,
|
|
1355
2982
|
fallback = null,
|
|
1356
2983
|
children
|
|
1357
2984
|
}) {
|
|
1358
2985
|
const { hasPermission } = useOonAuth();
|
|
1359
|
-
return /* @__PURE__ */
|
|
2986
|
+
return /* @__PURE__ */ jsx29(Fragment4, { children: hasPermission(permission) ? children : fallback });
|
|
1360
2987
|
}
|
|
1361
2988
|
export {
|
|
1362
2989
|
CoreAssistant,
|