@bluemarble/bm-components 2.4.2 → 2.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1179,6 +1179,114 @@ var require_react_is2 = __commonJS({
1179
1179
  }
1180
1180
  });
1181
1181
 
1182
+ // packages/nookies/index.ts
1183
+ import * as cookie from "cookie";
1184
+ import * as setCookieParser from "set-cookie-parser";
1185
+
1186
+ // packages/nookies/util.ts
1187
+ function isBrowser() {
1188
+ return typeof window !== "undefined";
1189
+ }
1190
+ function createCookie(name, value, options) {
1191
+ let sameSite = options.sameSite;
1192
+ if (sameSite === true) {
1193
+ sameSite = "strict";
1194
+ }
1195
+ if (sameSite === void 0 || sameSite === false) {
1196
+ sameSite = "lax";
1197
+ }
1198
+ const cookieToSet = { ...options, sameSite };
1199
+ delete cookieToSet.encode;
1200
+ return {
1201
+ name,
1202
+ value,
1203
+ ...cookieToSet
1204
+ };
1205
+ }
1206
+ function hasSameProperties(a, b) {
1207
+ const aProps = Object.getOwnPropertyNames(a);
1208
+ const bProps = Object.getOwnPropertyNames(b);
1209
+ if (aProps.length !== bProps.length) {
1210
+ return false;
1211
+ }
1212
+ for (let i = 0; i < aProps.length; i++) {
1213
+ const propName = aProps[i];
1214
+ if (a[propName] !== b[propName]) {
1215
+ return false;
1216
+ }
1217
+ }
1218
+ return true;
1219
+ }
1220
+ function areCookiesEqual(a, b) {
1221
+ let sameSiteSame = a.sameSite === b.sameSite;
1222
+ if (typeof a.sameSite === "string" && typeof b.sameSite === "string") {
1223
+ sameSiteSame = a.sameSite.toLowerCase() === b.sameSite.toLowerCase();
1224
+ }
1225
+ return hasSameProperties(
1226
+ { ...a, sameSite: void 0 },
1227
+ { ...b, sameSite: void 0 }
1228
+ ) && sameSiteSame;
1229
+ }
1230
+
1231
+ // packages/nookies/index.ts
1232
+ function parseCookies(ctx, options) {
1233
+ if (ctx?.req?.headers?.cookie) {
1234
+ return cookie.parse(ctx.req.headers.cookie, options);
1235
+ }
1236
+ if (isBrowser()) {
1237
+ return cookie.parse(document.cookie, options);
1238
+ }
1239
+ return {};
1240
+ }
1241
+ function setCookie(ctx, name, value, options = {}) {
1242
+ if (ctx?.res?.getHeader && ctx.res.setHeader) {
1243
+ if (ctx?.res?.finished) {
1244
+ console.warn(`Not setting "${name}" cookie. Response has finished.`);
1245
+ console.warn(`You should set cookie before res.send()`);
1246
+ return {};
1247
+ }
1248
+ let cookies = ctx.res.getHeader("Set-Cookie") || [];
1249
+ if (typeof cookies === "string") cookies = [cookies];
1250
+ if (typeof cookies === "number") cookies = [];
1251
+ const parsedCookies = setCookieParser.parse(cookies, {
1252
+ decodeValues: false
1253
+ });
1254
+ const newCookie = createCookie(name, value, options);
1255
+ let cookiesToSet = [];
1256
+ parsedCookies.forEach((parsedCookie) => {
1257
+ if (!areCookiesEqual(parsedCookie, newCookie)) {
1258
+ const serializedCookie = cookie.serialize(
1259
+ parsedCookie.name,
1260
+ parsedCookie.value,
1261
+ {
1262
+ // we prevent reencoding by default, but you might override it
1263
+ encode: (val) => val,
1264
+ ...parsedCookie
1265
+ }
1266
+ );
1267
+ cookiesToSet.push(serializedCookie);
1268
+ }
1269
+ });
1270
+ cookiesToSet.push(cookie.serialize(name, value, options));
1271
+ ctx.res.setHeader("Set-Cookie", cookiesToSet);
1272
+ }
1273
+ if (isBrowser()) {
1274
+ if (options && options.httpOnly) {
1275
+ throw new Error("Can not set a httpOnly cookie in the browser.");
1276
+ }
1277
+ document.cookie = cookie.serialize(name, value, options);
1278
+ }
1279
+ return {};
1280
+ }
1281
+ function destroyCookie(ctx, name, options) {
1282
+ return setCookie(ctx, name, "", { ...options || {}, maxAge: -1 });
1283
+ }
1284
+ var nookies = {
1285
+ set: setCookie,
1286
+ get: parseCookies,
1287
+ destroy: destroyCookie
1288
+ };
1289
+
1182
1290
  // src/components/Grid/Grid.tsx
1183
1291
  import React7, {
1184
1292
  useEffect,
@@ -4480,1222 +4588,1110 @@ var BaseDialog = {
4480
4588
  Body: BaseDialogBody
4481
4589
  };
4482
4590
 
4483
- // src/errors/HttpError.ts
4484
- var HttpError = class extends Error {
4485
- constructor(status, message) {
4486
- super(message);
4487
- this.message = message;
4488
- this.stack = `HttpError: ${message}`;
4489
- this.status = status;
4490
- }
4591
+ // src/contexts/FormHelperProvider.tsx
4592
+ import React30 from "react";
4593
+ import { createContext as createContext3 } from "react";
4594
+ var FormHelperContext = createContext3({});
4595
+ var FormHelperProvider = ({ formatErrorMessage, api, children }) => {
4596
+ return /* @__PURE__ */ React30.createElement(FormHelperContext.Provider, { value: { formatErrorMessage, api } }, children);
4491
4597
  };
4492
4598
 
4493
- // src/errors/DomainError.ts
4494
- var DomainError = class extends Error {
4495
- constructor(message) {
4496
- super(message);
4497
- this.message = message;
4498
- this.stack = `DomainError: ${message}`;
4499
- }
4500
- };
4599
+ // src/contexts/AlertContext.tsx
4600
+ import React32, { useCallback as useCallback2 } from "react";
4601
+ import { createContext as createContext4, useState as useState6 } from "react";
4501
4602
 
4502
- // src/helpers/apiHelper/index.ts
4503
- var VALID_METHODS = [
4504
- "GET",
4505
- "HEAD",
4506
- "POST",
4507
- "PUT",
4508
- "DELETE",
4509
- "CONNECT",
4510
- "OPTIONS",
4511
- "TRACE",
4512
- "PATCH"
4513
- ];
4514
- var _ApiHelper = class _ApiHelper {
4515
- async onFinally(_req, _res) {
4516
- }
4517
- async onError(_req, _res, _error) {
4518
- }
4519
- constructor(props) {
4520
- this.public = props?.public ?? false;
4521
- this.middlewares = (props?.middlewares || []).reverse();
4522
- this.onFinally = props?.onFinally || (async () => {
4523
- });
4524
- this.onError = props?.onError || (async () => {
4525
- });
4526
- }
4527
- createMethods(methods) {
4528
- return async (req, res) => {
4529
- const currentMethod = methods[req.method] || methods.ALL;
4530
- const options = { public: this.public };
4531
- if (req.method === "OPTIONS") return res.status(200).end();
4532
- try {
4533
- if (!VALID_METHODS.includes(req.method))
4534
- throw new HttpError(405, "M\xE9todo inv\xE1lido");
4535
- if (!currentMethod) throw new HttpError(500, "M\xE9todo n\xE3o encontrado");
4536
- const methodWithMiddlewares = this.middlewares.reduce(
4537
- (acc, fn) => fn(acc, options),
4538
- currentMethod
4539
- );
4540
- return await methodWithMiddlewares(req, res, options);
4541
- } catch (error) {
4542
- if (error instanceof DomainError) return res.status(400).json(error.message);
4543
- if (error instanceof HttpError) return res.status(error.status).json(error.message);
4544
- this.onError(req, res, error);
4545
- throw error;
4546
- } finally {
4547
- await this.onFinally(req, res);
4548
- }
4549
- };
4550
- }
4551
- buildFactory(factory) {
4552
- const options = {
4553
- public: this.public
4554
- };
4555
- return async (req, res) => {
4556
- const methods = factory(req, res);
4557
- const handler = methods[req.method];
4558
- if (!handler) throw new HttpError(405, "M\xE9todo inv\xE1lido");
4559
- const methodWithMiddlewares = this.middlewares.reduce((acc, fn) => {
4560
- return fn(acc, options);
4561
- }, handler);
4562
- return await methodWithMiddlewares(req, res, options);
4563
- };
4564
- }
4565
- static build(factory, options) {
4566
- const helper = new _ApiHelper({
4567
- ...options
4568
- }).buildFactory(factory);
4569
- return helper;
4570
- }
4571
- static parse(body, parser) {
4572
- try {
4573
- const object = parser.parse(body);
4574
- return object;
4575
- } catch (error) {
4576
- throw new HttpError(400, {
4577
- code: "invalid.body",
4578
- error: "Dados inv\xE1lidos",
4579
- details: error
4580
- });
4581
- }
4582
- }
4583
- /** @deprecated Use {@Link ApiHelper.build} instead. */
4584
- static create({ onFinally }) {
4585
- return new _ApiHelper({
4586
- onFinally
4587
- });
4588
- }
4603
+ // src/components/Toast/index.tsx
4604
+ import React31 from "react";
4605
+ import { Alert, IconButton as IconButton5, Snackbar } from "@mui/material";
4606
+ import { MdClose as MdClose3 } from "react-icons/md";
4607
+ var Toast = ({ open, onClose, severity, message }) => {
4608
+ return /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement(
4609
+ Snackbar,
4610
+ {
4611
+ open,
4612
+ autoHideDuration: 6e3,
4613
+ onClose,
4614
+ anchorOrigin: { vertical: "top", horizontal: "right" },
4615
+ sx: { zIndex: 99999999 }
4616
+ },
4617
+ /* @__PURE__ */ React31.createElement(
4618
+ Alert,
4619
+ {
4620
+ severity,
4621
+ elevation: 2,
4622
+ action: /* @__PURE__ */ React31.createElement(
4623
+ IconButton5,
4624
+ {
4625
+ "aria-label": "close",
4626
+ color: "inherit",
4627
+ size: "small",
4628
+ onClick: onClose
4629
+ },
4630
+ /* @__PURE__ */ React31.createElement(MdClose3, { fontSize: "inherit" })
4631
+ )
4632
+ },
4633
+ message
4634
+ )
4635
+ ));
4589
4636
  };
4590
- /** @deprecated Use {@link ApiHelper.parser} instead. */
4591
- _ApiHelper.parserErrorWrapper = _ApiHelper.parse;
4592
- var ApiHelper = _ApiHelper;
4593
-
4594
- // src/helpers/authHelper.ts
4595
- import { serialize as serialize2 } from "cookie";
4596
- import jwt from "jsonwebtoken";
4597
-
4598
- // packages/nookies/index.ts
4599
- import * as cookie from "cookie";
4600
- import * as setCookieParser from "set-cookie-parser";
4601
4637
 
4602
- // packages/nookies/util.ts
4603
- function isBrowser() {
4604
- return typeof window !== "undefined";
4638
+ // src/contexts/AlertContext.tsx
4639
+ var AlertContext = createContext4({});
4640
+ var AlertProvider = ({ children }) => {
4641
+ const [severity, setSeverity] = useState6("info");
4642
+ const [message, setMessage] = useState6("");
4643
+ const [isVisible, setIsVisible] = useState6(false);
4644
+ const createAlert = useCallback2(
4645
+ (newMessage, severity2) => {
4646
+ setMessage(newMessage);
4647
+ setSeverity(severity2);
4648
+ setIsVisible(true);
4649
+ },
4650
+ []
4651
+ );
4652
+ const onCloseToast = useCallback2(() => {
4653
+ setIsVisible(false);
4654
+ }, []);
4655
+ return /* @__PURE__ */ React32.createElement(AlertContext.Provider, { value: { createAlert } }, children, /* @__PURE__ */ React32.createElement(
4656
+ Toast,
4657
+ {
4658
+ open: isVisible,
4659
+ onClose: onCloseToast,
4660
+ severity,
4661
+ message
4662
+ }
4663
+ ));
4664
+ };
4665
+
4666
+ // src/contexts/AuthContext.tsx
4667
+ import React33, { useCallback as useCallback8 } from "react";
4668
+ import {
4669
+ createContext as createContext5,
4670
+ useEffect as useEffect8,
4671
+ useState as useState11
4672
+ } from "react";
4673
+
4674
+ // src/hooks/useGrid.ts
4675
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useState as useState8 } from "react";
4676
+
4677
+ // src/hooks/useFilter.ts
4678
+ import { useCallback as useCallback3, useState as useState7 } from "react";
4679
+
4680
+ // src/components/utils/getObjectValue.ts
4681
+ function getObjectValue(obj) {
4682
+ return (prop) => {
4683
+ try {
4684
+ return prop.split(".").reduce((o, k) => o[k], obj);
4685
+ } catch (_) {
4686
+ return void 0;
4687
+ }
4688
+ };
4605
4689
  }
4606
- function createCookie(name, value, options) {
4607
- let sameSite = options.sameSite;
4608
- if (sameSite === true) {
4609
- sameSite = "strict";
4610
- }
4611
- if (sameSite === void 0 || sameSite === false) {
4612
- sameSite = "lax";
4690
+
4691
+ // src/hooks/useFilter.ts
4692
+ function useFilter(props = { defaultFilters: [] }) {
4693
+ const [selectedFilters, setSelectedFilters] = useState7(() => {
4694
+ const { defaultFilters } = props;
4695
+ return defaultFilters || [];
4696
+ });
4697
+ const filterBy = useCallback3((newFilter) => {
4698
+ const propToCompare = newFilter?.id ? "id" : "prop";
4699
+ function removeRepeatedFilters(filter) {
4700
+ return filter[propToCompare] !== newFilter[propToCompare];
4701
+ }
4702
+ setSelectedFilters((filters) => [
4703
+ ...filters.filter(removeRepeatedFilters),
4704
+ newFilter
4705
+ ]);
4706
+ }, []);
4707
+ const removeFilter = useCallback3(
4708
+ (prop, isId) => {
4709
+ const propToCompare = isId ? "id" : "prop";
4710
+ setSelectedFilters(
4711
+ selectedFilters.filter((filter) => filter[propToCompare] !== prop)
4712
+ );
4713
+ },
4714
+ [selectedFilters]
4715
+ );
4716
+ function clearAllFilters() {
4717
+ setSelectedFilters([]);
4613
4718
  }
4614
- const cookieToSet = { ...options, sameSite };
4615
- delete cookieToSet.encode;
4616
4719
  return {
4617
- name,
4618
- value,
4619
- ...cookieToSet
4720
+ filters: selectedFilters,
4721
+ filterBy,
4722
+ removeFilter,
4723
+ createFilter,
4724
+ clearAllFilters
4620
4725
  };
4621
4726
  }
4622
- function hasSameProperties(a, b) {
4623
- const aProps = Object.getOwnPropertyNames(a);
4624
- const bProps = Object.getOwnPropertyNames(b);
4625
- if (aProps.length !== bProps.length) {
4626
- return false;
4627
- }
4628
- for (let i = 0; i < aProps.length; i++) {
4629
- const propName = aProps[i];
4630
- if (a[propName] !== b[propName]) {
4631
- return false;
4632
- }
4633
- }
4634
- return true;
4727
+ function isDate(date) {
4728
+ if (date instanceof Date) return true;
4729
+ else if (String(date).endsWith("Z")) return true;
4730
+ return false;
4635
4731
  }
4636
- function areCookiesEqual(a, b) {
4637
- let sameSiteSame = a.sameSite === b.sameSite;
4638
- if (typeof a.sameSite === "string" && typeof b.sameSite === "string") {
4639
- sameSiteSame = a.sameSite.toLowerCase() === b.sameSite.toLowerCase();
4732
+ function compareFilter(item, filter) {
4733
+ const itemValue = getObjectValue(item)(filter.prop);
4734
+ switch (filter.compareType) {
4735
+ case "equal":
4736
+ return itemValue === filter.value;
4737
+ case "notEqual":
4738
+ return itemValue !== filter.value;
4739
+ case "in":
4740
+ return filter.value.includes(itemValue);
4741
+ case "notIn":
4742
+ return !filter.value.includes(itemValue);
4743
+ case "valueIn":
4744
+ return itemValue.includes(filter.value);
4745
+ case "valueNotIn":
4746
+ return !itemValue.includes(filter.value);
4747
+ case "gte":
4748
+ return isDate(itemValue) ? new Date(String(itemValue)) >= filter.value : itemValue >= filter.value;
4749
+ case "gt":
4750
+ return isDate(itemValue) ? new Date(String(itemValue)) > filter.value : itemValue > filter.value;
4751
+ case "lte":
4752
+ return isDate(itemValue) ? new Date(String(itemValue)) <= filter.value : itemValue <= filter.value;
4753
+ case "lt":
4754
+ return isDate(itemValue) ? new Date(String(itemValue)) < filter.value : itemValue < filter.value;
4640
4755
  }
4641
- return hasSameProperties(
4642
- { ...a, sameSite: void 0 },
4643
- { ...b, sameSite: void 0 }
4644
- ) && sameSiteSame;
4645
4756
  }
4646
-
4647
- // packages/nookies/index.ts
4648
- function parseCookies(ctx, options) {
4649
- if (ctx?.req?.headers?.cookie) {
4650
- return cookie.parse(ctx.req.headers.cookie, options);
4651
- }
4652
- if (isBrowser()) {
4653
- return cookie.parse(document.cookie, options);
4757
+ function createFilter(filters) {
4758
+ function apply(item) {
4759
+ const satisfiedFilters = filters.reduce((acc, filter) => {
4760
+ if (compareFilter(item, filter)) acc += 1;
4761
+ return acc;
4762
+ }, 0);
4763
+ return satisfiedFilters === filters.length;
4654
4764
  }
4655
- return {};
4765
+ return {
4766
+ apply
4767
+ };
4656
4768
  }
4657
- function setCookie(ctx, name, value, options = {}) {
4658
- if (ctx?.res?.getHeader && ctx.res.setHeader) {
4659
- if (ctx?.res?.finished) {
4660
- console.warn(`Not setting "${name}" cookie. Response has finished.`);
4661
- console.warn(`You should set cookie before res.send()`);
4662
- return {};
4663
- }
4664
- let cookies = ctx.res.getHeader("Set-Cookie") || [];
4665
- if (typeof cookies === "string") cookies = [cookies];
4666
- if (typeof cookies === "number") cookies = [];
4667
- const parsedCookies = setCookieParser.parse(cookies, {
4668
- decodeValues: false
4669
- });
4670
- const newCookie = createCookie(name, value, options);
4671
- let cookiesToSet = [];
4672
- parsedCookies.forEach((parsedCookie) => {
4673
- if (!areCookiesEqual(parsedCookie, newCookie)) {
4674
- const serializedCookie = cookie.serialize(
4675
- parsedCookie.name,
4676
- parsedCookie.value,
4677
- {
4678
- // we prevent reencoding by default, but you might override it
4679
- encode: (val) => val,
4680
- ...parsedCookie
4681
- }
4682
- );
4683
- cookiesToSet.push(serializedCookie);
4769
+
4770
+ // src/helpers/sortHelper.ts
4771
+ function SortHelper(...fields) {
4772
+ return (a, b) => {
4773
+ for (const field of fields) {
4774
+ let direction = 1;
4775
+ let key = field;
4776
+ if (field.startsWith("-")) {
4777
+ direction = -1;
4778
+ key = field.slice(1);
4684
4779
  }
4685
- });
4686
- cookiesToSet.push(cookie.serialize(name, value, options));
4687
- ctx.res.setHeader("Set-Cookie", cookiesToSet);
4688
- }
4689
- if (isBrowser()) {
4690
- if (options && options.httpOnly) {
4691
- throw new Error("Can not set a httpOnly cookie in the browser.");
4780
+ const aVal = a[key];
4781
+ const bVal = b[key];
4782
+ if (aVal > bVal) return direction;
4783
+ if (aVal < bVal) return -direction;
4692
4784
  }
4693
- document.cookie = cookie.serialize(name, value, options);
4694
- }
4695
- return {};
4696
- }
4697
- function destroyCookie(ctx, name, options) {
4698
- return setCookie(ctx, name, "", { ...options || {}, maxAge: -1 });
4785
+ return 0;
4786
+ };
4699
4787
  }
4700
- var nookies = {
4701
- set: setCookie,
4702
- get: parseCookies,
4703
- destroy: destroyCookie
4704
- };
4705
4788
 
4706
- // src/helpers/authHelper.ts
4707
- import { randomUUID } from "crypto";
4708
- function decodeSessionToken({
4709
- req,
4710
- res,
4711
- sessionTokenName,
4712
- validate
4789
+ // src/hooks/useGrid.ts
4790
+ function useGrid({
4791
+ columns,
4792
+ filters,
4793
+ search,
4794
+ rowsPerPageOptions = [30, 60, 100],
4795
+ defaultData: externalDefaultData,
4796
+ defaultCurrentPage,
4797
+ defaultSortedBy
4713
4798
  }) {
4714
- const token = req.headers.authorization?.split(" ")[1] || req.cookies[sessionTokenName];
4715
- if (!token) {
4716
- res.status(401).json({ error: "Token inv\xE1lido", code: "token.invalid" });
4717
- return true;
4718
- }
4719
- const jwtDecode = (token2) => {
4720
- if (validate) {
4721
- return jwt.verify(token2, process.env.JWT_SECRET);
4722
- }
4723
- return jwt.decode(token2);
4724
- };
4725
- try {
4726
- const decoded = jwtDecode(token);
4727
- req.user = decoded.sub;
4728
- } catch (_) {
4729
- res.status(401).json({ error: "Token inv\xE1lido", code: "token.expired" });
4730
- return true;
4731
- }
4732
- }
4733
- var AuthHelper = class {
4734
- constructor({
4735
- cookies,
4736
- oauth,
4737
- tokenExpTimeInSeconds,
4738
- onLogin,
4739
- onValidateRefreshToken,
4740
- onInvalidateRefreshToken,
4741
- onCreateRefreshToken,
4742
- onGetUserData
4743
- }) {
4744
- this.generateJwtAndRefreshToken = async (userId, payload = {}) => {
4745
- const token = jwt.sign(payload, process.env.JWT_SECRET, {
4746
- subject: String(userId),
4747
- expiresIn: this.tokenExpTimeInSeconds || 60 * 15
4748
- // 15 minutos
4749
- });
4750
- const uniqueToken = randomUUID();
4751
- await this.onCreateRefreshToken(userId, uniqueToken);
4752
- return {
4753
- token,
4754
- refreshToken: uniqueToken
4755
- };
4756
- };
4757
- this.invalidateCookies = (res) => {
4758
- return res.setHeader("Set-Cookie", [
4759
- serialize2(this.cookies.sessionToken, "", {
4760
- maxAge: -1,
4761
- path: "/"
4762
- }),
4763
- serialize2(this.cookies.refreshToken, "", {
4764
- maxAge: -1,
4765
- path: "/"
4766
- })
4767
- ]);
4768
- };
4769
- this.cookies = cookies;
4770
- this.oauth = oauth;
4771
- this.tokenExpTimeInSeconds = tokenExpTimeInSeconds;
4772
- this.onLogin = onLogin;
4773
- this.onValidateRefreshToken = onValidateRefreshToken;
4774
- this.onInvalidateRefreshToken = onInvalidateRefreshToken;
4775
- this.onCreateRefreshToken = onCreateRefreshToken;
4776
- this.onGetUserData = onGetUserData;
4777
- }
4778
- async handler(req, res) {
4779
- if (!req.url) return res.status(400).json({ error: "url not sent" });
4780
- if (req.url.endsWith("/login")) {
4781
- const loginResult = await this.onLogin(req.body);
4782
- if (loginResult.status === "success") {
4783
- const { refreshToken, token } = await this.generateJwtAndRefreshToken(
4784
- loginResult.userId,
4785
- {}
4786
- );
4787
- setCookie({ res }, this.cookies.sessionToken, token, {
4788
- secure: true,
4789
- maxAge: 60 * 60 * 24 * 30,
4790
- // 30 days
4791
- path: "/",
4792
- sameSite: true
4793
- });
4794
- setCookie({ res }, this.cookies.refreshToken, refreshToken, {
4795
- secure: true,
4796
- maxAge: 60 * 60 * 24 * 30,
4797
- // 30 days
4798
- path: "/",
4799
- sameSite: true,
4800
- httpOnly: true
4801
- });
4802
- return res.json({ token, refreshToken });
4803
- }
4804
- throw new HttpError(400, loginResult.response);
4805
- }
4806
- if (req.url.endsWith("/logout")) {
4807
- this.invalidateCookies(res).end();
4808
- }
4809
- if (req.url.endsWith("/refresh")) {
4810
- const error = decodeSessionToken({
4811
- req,
4812
- res,
4813
- sessionTokenName: this.cookies.sessionToken,
4814
- validate: false
4815
- });
4816
- if (error) return;
4817
- const userId = String(req.user);
4818
- const refreshToken = parseCookies({ req })[this.cookies.refreshToken];
4819
- if (!refreshToken) {
4820
- this.invalidateCookies(res);
4821
- return res.status(400).json({
4822
- error: "Refresh Token inv\xE1lido"
4823
- });
4824
- }
4825
- const isValidRefreshToken = await this.onValidateRefreshToken(
4826
- userId,
4827
- refreshToken
4828
- );
4829
- if (!isValidRefreshToken) {
4830
- this.invalidateCookies(res);
4831
- return res.status(400).json({
4832
- error: "Refresh Token inv\xE1lido"
4833
- });
4834
- }
4835
- await this.onInvalidateRefreshToken(userId, refreshToken);
4836
- const { token, refreshToken: newRefreshToken } = await this.generateJwtAndRefreshToken(userId, {});
4837
- setCookie({ res }, this.cookies.sessionToken, token, {
4838
- secure: true,
4839
- maxAge: 60 * 60 * 24 * 30,
4840
- // 30 days
4841
- path: "/",
4842
- sameSite: true
4843
- });
4844
- setCookie({ res }, this.cookies.refreshToken, newRefreshToken, {
4845
- secure: true,
4846
- maxAge: 60 * 60 * 24 * 30,
4847
- // 30 days
4848
- path: "/",
4849
- sameSite: true,
4850
- httpOnly: true
4851
- });
4852
- return res.json({
4853
- token,
4854
- refreshToken: newRefreshToken
4855
- });
4856
- }
4857
- if (req.url.endsWith("/me")) {
4858
- const error = decodeSessionToken({
4859
- req,
4860
- res,
4861
- sessionTokenName: this.cookies.sessionToken,
4862
- validate: true
4863
- });
4864
- if (error) return;
4865
- if (!req.user)
4866
- return res.status(400).json({ error: "Usu\xE1rio n\xE3o encontrado" });
4867
- const userData = await this.onGetUserData(req.user);
4868
- if (!userData)
4869
- return res.status(400).json({ error: "Usu\xE1rio n\xE3o encontrado" });
4870
- return res.json(userData);
4871
- }
4872
- if (req.url.endsWith("/oauth-url") && this.oauth) {
4873
- const params = {
4874
- client_id: this.oauth.client_id,
4875
- redirect_uri: this.oauth.redirect_uri,
4876
- scope: this.oauth.scope,
4877
- response_type: "code",
4878
- response_mode: "query"
4879
- };
4880
- const url = `https://login.microsoftonline.com/${this.oauth.tenant_id}/oauth2/v2.0/authorize?${new URLSearchParams(params)}`;
4881
- return res.json({
4882
- url
4883
- });
4884
- }
4885
- return res.status(404).json({ error: "Route not found" });
4886
- }
4887
- async oauthSignInCallback(code) {
4888
- if (!this.oauth) throw new Error("OAUTH variables is not defined");
4889
- const body = {
4890
- client_id: this.oauth.client_id,
4891
- scope: this.oauth.scope,
4892
- code,
4893
- session_state: this.oauth.client_id,
4894
- redirect_uri: this.oauth.redirect_uri,
4895
- grant_type: "authorization_code",
4896
- client_secret: this.oauth.client_secret
4897
- };
4898
- const response = await fetch(
4899
- `https://login.microsoftonline.com/${this.oauth.tenant_id}/oauth2/v2.0/token`,
4900
- {
4901
- method: "POST",
4902
- body: new URLSearchParams(body),
4903
- headers: { "Content-Type": "application/x-www-form-urlencoded" }
4904
- }
4905
- );
4906
- const data = await response.json();
4907
- const decodedToken = jwt.decode(data.access_token);
4908
- const email = decodedToken.upn;
4909
- const fullName = `${decodedToken?.given_name} ${decodedToken?.family_name}`;
4910
- return { decodedToken, email, fullName };
4911
- }
4912
- createOauthCallbackGetServerSideProps({
4913
- onSuccessDestination,
4914
- onFailedDestination
4915
- }) {
4916
- return async (ctx) => {
4917
- if (!this.oauth) throw new Error("Oauth env variables are not defined");
4918
- const code = ctx.query.code;
4919
- if (!code)
4920
- return {
4921
- redirect: {
4922
- permanent: false,
4923
- destination: onFailedDestination || "/"
4924
- }
4925
- };
4926
- try {
4927
- const { fullName, email } = await this.oauthSignInCallback(code);
4928
- const userExists = await this.onGetUserData(email);
4929
- if (!userExists && !this.oauth.onCreateUser)
4930
- throw new Error("User does not exists");
4931
- if (!userExists && this.oauth.onCreateUser) {
4932
- await this.oauth.onCreateUser({ fullname: fullName, email });
4799
+ const [defaultData, setDefaultData] = useState8(externalDefaultData || []);
4800
+ const [sortedBy, setSortedBy] = useState8(
4801
+ defaultSortedBy || []
4802
+ );
4803
+ const [currentPage, setCurrentPage] = useState8(defaultCurrentPage || 0);
4804
+ const [rowsPerPage, setRowsPerPage] = useState8(rowsPerPageOptions[0]);
4805
+ const toggleSortedDirection = useCallback4(
4806
+ (direction) => {
4807
+ if (direction === "asc") return "desc";
4808
+ return "asc";
4809
+ },
4810
+ []
4811
+ );
4812
+ const clearSort = useCallback4(() => {
4813
+ setSortedBy([]);
4814
+ }, []);
4815
+ const appendSort = useCallback4((prop, direction) => {
4816
+ setSortedBy((prev) => [...prev, { prop, direction }]);
4817
+ }, []);
4818
+ const setSort = useCallback4((prop, direction) => {
4819
+ setSortedBy([{ prop, direction }]);
4820
+ }, []);
4821
+ const onSortBy = useCallback4(
4822
+ (prop) => {
4823
+ if (!prop) return;
4824
+ const currentSorted = sortedBy.find((p) => p.prop === prop);
4825
+ if (currentSorted) {
4826
+ if (currentSorted.direction === "asc") {
4827
+ setSortedBy((prev) => prev.filter((p) => p.prop !== prop));
4828
+ } else {
4829
+ setSortedBy((prev) => {
4830
+ const newArr = prev.map((p) => {
4831
+ if (p.prop !== prop) return p;
4832
+ return {
4833
+ prop: p.prop,
4834
+ direction: toggleSortedDirection(p.direction)
4835
+ };
4836
+ });
4837
+ return [...newArr].slice(0);
4838
+ });
4933
4839
  }
4934
- const { token, refreshToken } = await this.generateJwtAndRefreshToken(
4935
- email,
4936
- {}
4937
- );
4938
- setCookie(ctx, this.cookies.sessionToken, token, {
4939
- secure: true,
4940
- maxAge: 60 * 60 * 24 * 30,
4941
- // 30 days
4942
- path: "/"
4943
- });
4944
- setCookie(ctx, this.cookies.refreshToken, refreshToken, {
4945
- secure: true,
4946
- maxAge: 60 * 60 * 24 * 30,
4947
- // 30 days
4948
- path: "/",
4949
- httpOnly: true
4950
- });
4951
- return {
4952
- redirect: {
4953
- destination: onSuccessDestination,
4954
- permanent: false
4955
- }
4956
- };
4957
- } catch (error) {
4958
- return {
4959
- props: {
4960
- error: JSON.stringify(error)
4961
- }
4962
- };
4840
+ } else {
4841
+ setSortedBy((prev) => [...prev, { prop, direction: "desc" }]);
4963
4842
  }
4964
- };
4965
- }
4966
- };
4967
-
4968
- // src/hooks/useGrid.ts
4969
- import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo3, useState as useState7 } from "react";
4970
-
4971
- // src/hooks/useFilter.ts
4972
- import { useCallback as useCallback2, useState as useState6 } from "react";
4973
-
4974
- // src/components/utils/getObjectValue.ts
4975
- function getObjectValue(obj) {
4976
- return (prop) => {
4977
- try {
4978
- return prop.split(".").reduce((o, k) => o[k], obj);
4979
- } catch (_) {
4980
- return void 0;
4981
- }
4982
- };
4983
- }
4984
-
4985
- // src/hooks/useFilter.ts
4986
- function useFilter(props = { defaultFilters: [] }) {
4987
- const [selectedFilters, setSelectedFilters] = useState6(() => {
4988
- const { defaultFilters } = props;
4989
- return defaultFilters || [];
4990
- });
4991
- const filterBy = useCallback2((newFilter) => {
4992
- const propToCompare = newFilter?.id ? "id" : "prop";
4993
- function removeRepeatedFilters(filter) {
4994
- return filter[propToCompare] !== newFilter[propToCompare];
4995
- }
4996
- setSelectedFilters((filters) => [
4997
- ...filters.filter(removeRepeatedFilters),
4998
- newFilter
4999
- ]);
4843
+ },
4844
+ [toggleSortedDirection, sortedBy]
4845
+ );
4846
+ const set = useCallback4((data) => {
4847
+ setDefaultData(data);
5000
4848
  }, []);
5001
- const removeFilter = useCallback2(
5002
- (prop, isId) => {
5003
- const propToCompare = isId ? "id" : "prop";
5004
- setSelectedFilters(
5005
- selectedFilters.filter((filter) => filter[propToCompare] !== prop)
5006
- );
4849
+ const sortData = useCallback4(
4850
+ (data) => {
4851
+ if (sortedBy.length > 0) {
4852
+ const symbolDir = {
4853
+ asc: "",
4854
+ desc: "-"
4855
+ };
4856
+ const formattedKeys = sortedBy.map(
4857
+ ({ prop, direction }) => `${symbolDir[direction]}${prop}`
4858
+ );
4859
+ return data.sort(SortHelper(...formattedKeys));
4860
+ } else return data;
5007
4861
  },
5008
- [selectedFilters]
4862
+ [sortedBy]
5009
4863
  );
5010
- function clearAllFilters() {
5011
- setSelectedFilters([]);
5012
- }
5013
- return {
5014
- filters: selectedFilters,
5015
- filterBy,
5016
- removeFilter,
5017
- createFilter,
5018
- clearAllFilters
4864
+ const onPageChange = (pageNumber) => {
4865
+ if (pageNumber < 0) return;
4866
+ if (pageNumber > totalNumberOfPages) return;
4867
+ setCurrentPage(pageNumber);
5019
4868
  };
5020
- }
5021
- function isDate(date) {
5022
- if (date instanceof Date) return true;
5023
- else if (String(date).endsWith("Z")) return true;
5024
- return false;
5025
- }
5026
- function compareFilter(item, filter) {
5027
- const itemValue = getObjectValue(item)(filter.prop);
5028
- switch (filter.compareType) {
5029
- case "equal":
5030
- return itemValue === filter.value;
5031
- case "notEqual":
5032
- return itemValue !== filter.value;
5033
- case "in":
5034
- return filter.value.includes(itemValue);
5035
- case "notIn":
5036
- return !filter.value.includes(itemValue);
5037
- case "valueIn":
5038
- return itemValue.includes(filter.value);
5039
- case "valueNotIn":
5040
- return !itemValue.includes(filter.value);
5041
- case "gte":
5042
- return isDate(itemValue) ? new Date(String(itemValue)) >= filter.value : itemValue >= filter.value;
5043
- case "gt":
5044
- return isDate(itemValue) ? new Date(String(itemValue)) > filter.value : itemValue > filter.value;
5045
- case "lte":
5046
- return isDate(itemValue) ? new Date(String(itemValue)) <= filter.value : itemValue <= filter.value;
5047
- case "lt":
5048
- return isDate(itemValue) ? new Date(String(itemValue)) < filter.value : itemValue < filter.value;
5049
- }
5050
- }
5051
- function createFilter(filters) {
5052
- function apply(item) {
5053
- const satisfiedFilters = filters.reduce((acc, filter) => {
5054
- if (compareFilter(item, filter)) acc += 1;
5055
- return acc;
5056
- }, 0);
5057
- return satisfiedFilters === filters.length;
5058
- }
4869
+ const onChangeRowsPerPage = useCallback4(
4870
+ (rows) => {
4871
+ let totalNumberOfPages2 = Math.round(filteredData.length / rows) - 1;
4872
+ totalNumberOfPages2 = totalNumberOfPages2 <= 0 ? 0 : 1;
4873
+ if (currentPage > totalNumberOfPages2) setCurrentPage(totalNumberOfPages2);
4874
+ setRowsPerPage(rows);
4875
+ },
4876
+ [currentPage]
4877
+ );
4878
+ const orderedData = useMemo3(() => {
4879
+ if (sortedBy.length === 0) return defaultData;
4880
+ const newData = defaultData.slice(0);
4881
+ const sortedData = sortData(newData);
4882
+ return sortedData;
4883
+ }, [defaultData, sortData, sortedBy]);
4884
+ const filteredData = useMemo3(() => {
4885
+ let newData = orderedData.slice(0);
4886
+ if (search && search.value !== "") {
4887
+ const searchBy = createSearch(search);
4888
+ newData = newData.filter(searchBy);
4889
+ }
4890
+ if (!filters) return newData;
4891
+ const newFilter = createFilter(filters);
4892
+ return newData.filter(newFilter.apply);
4893
+ }, [orderedData, search, filters]);
4894
+ const paginatedData = useMemo3(() => {
4895
+ const startPage = currentPage * rowsPerPage;
4896
+ const endPage = startPage + rowsPerPage;
4897
+ return filteredData.slice(startPage, endPage);
4898
+ }, [currentPage, rowsPerPage, filteredData]);
4899
+ const totalNumberOfPages = Math.ceil(filteredData.length / rowsPerPage) - 1;
4900
+ useEffect4(() => {
4901
+ if (externalDefaultData) setDefaultData(externalDefaultData);
4902
+ }, [externalDefaultData]);
5059
4903
  return {
5060
- apply
4904
+ data: paginatedData,
4905
+ orderedData,
4906
+ filteredData,
4907
+ defaultData,
4908
+ sortedBy,
4909
+ columns,
4910
+ currentPage,
4911
+ totalNumberOfPages: totalNumberOfPages < 0 ? 0 : totalNumberOfPages,
4912
+ rowsPerPageOptions,
4913
+ rowsPerPage,
4914
+ set,
4915
+ onSortBy,
4916
+ onPageChange,
4917
+ setRowsPerPage: onChangeRowsPerPage,
4918
+ appendSort,
4919
+ setSort,
4920
+ clearSort
5061
4921
  };
5062
4922
  }
5063
-
5064
- // src/helpers/sortHelper.ts
5065
- function SortHelper(...fields) {
5066
- return (a, b) => {
5067
- for (const field of fields) {
5068
- let direction = 1;
5069
- let key = field;
5070
- if (field.startsWith("-")) {
5071
- direction = -1;
5072
- key = field.slice(1);
5073
- }
5074
- const aVal = a[key];
5075
- const bVal = b[key];
5076
- if (aVal > bVal) return direction;
5077
- if (aVal < bVal) return -direction;
4923
+ var isNumberOrString = (value) => ["number", "string"].includes(typeof value);
4924
+ var concatenateKey = (key, prev) => {
4925
+ if (!prev) return key;
4926
+ return `${prev}.${key}`;
4927
+ };
4928
+ function searchKeysForValue(row, accKey, compare) {
4929
+ if (typeof row === "undefined") return false;
4930
+ const rowKeys = Object.keys(row);
4931
+ let match = false;
4932
+ for (const key of rowKeys) {
4933
+ if (match) break;
4934
+ const objValue = row[key];
4935
+ if (!objValue) continue;
4936
+ const concatenatedKey = concatenateKey(key, accKey);
4937
+ if (Array.isArray(objValue)) {
4938
+ match = objValue.some(
4939
+ (obj) => isNumberOrString(obj) ? compare(concatenatedKey, obj) : searchKeysForValue(obj, concatenatedKey, compare)
4940
+ );
4941
+ continue;
5078
4942
  }
5079
- return 0;
4943
+ if (typeof objValue === "object") {
4944
+ match = searchKeysForValue(objValue, concatenatedKey, compare);
4945
+ continue;
4946
+ }
4947
+ match = compare(concatenatedKey, row[key]);
4948
+ }
4949
+ return match;
4950
+ }
4951
+ var normalize = (str) => str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
4952
+ function createSearch(options) {
4953
+ const searchValue = options.caseSensitive ? options.value : String(options.value).toLowerCase();
4954
+ function getValue2(value) {
4955
+ if (options.caseSensitive) return String(value);
4956
+ return String(value).toLowerCase();
4957
+ }
4958
+ function compare(key, objValue) {
4959
+ if (options.ignoredKeys) {
4960
+ const isIgnoredKey = options.ignoredKeys.includes(key);
4961
+ if (isIgnoredKey) return false;
4962
+ }
4963
+ const value = getValue2(objValue);
4964
+ if (options.exact) return value === searchValue;
4965
+ if (options.ignoreAccentMark) return normalize(value).includes(normalize(searchValue));
4966
+ return value.includes(searchValue);
4967
+ }
4968
+ return (row) => {
4969
+ const match = searchKeysForValue(row, "", compare);
4970
+ return match;
5080
4971
  };
5081
4972
  }
5082
4973
 
5083
- // src/hooks/useGrid.ts
5084
- function useGrid({
4974
+ // src/hooks/useAsyncGrid.ts
4975
+ import { useCallback as useCallback5, useEffect as useEffect5, useState as useState9 } from "react";
4976
+ function useAsyncGrid({
5085
4977
  columns,
5086
- filters,
4978
+ filters = [],
5087
4979
  search,
5088
4980
  rowsPerPageOptions = [30, 60, 100],
4981
+ onRequest,
4982
+ axiosInstance,
4983
+ url,
5089
4984
  defaultData: externalDefaultData,
5090
4985
  defaultCurrentPage,
5091
4986
  defaultSortedBy
5092
4987
  }) {
5093
- const [defaultData, setDefaultData] = useState7(externalDefaultData || []);
5094
- const [sortedBy, setSortedBy] = useState7(
4988
+ const [defaultData, setDefaultData] = useState9(externalDefaultData || []);
4989
+ const [sortedBy, setSortedBy] = useState9(
5095
4990
  defaultSortedBy || []
5096
4991
  );
5097
- const [currentPage, setCurrentPage] = useState7(defaultCurrentPage || 0);
5098
- const [rowsPerPage, setRowsPerPage] = useState7(rowsPerPageOptions[0]);
5099
- const toggleSortedDirection = useCallback3(
5100
- (direction) => {
5101
- if (direction === "asc") return "desc";
5102
- return "asc";
4992
+ const [totalNumberOfItems, setTotalNumberOfItems] = useState9(0);
4993
+ const [isLoading, setIsLoading] = useState9(false);
4994
+ const [currentPage, setCurrentPage] = useState9(defaultCurrentPage || 0);
4995
+ const [rowsPerPage, setRowsPerPage] = useState9(rowsPerPageOptions[0]);
4996
+ const totalNumberOfPages = Math.ceil(totalNumberOfItems / rowsPerPage) - 1;
4997
+ const toggleSortedDirection = useCallback5(
4998
+ (direction) => {
4999
+ if (direction === "asc") return "desc";
5000
+ return "asc";
5001
+ },
5002
+ []
5003
+ );
5004
+ const setSort = useCallback5((prop, direction) => {
5005
+ setSortedBy((prev) => [...prev, { prop, direction }]);
5006
+ }, []);
5007
+ const onSortBy = useCallback5(
5008
+ async (prop) => {
5009
+ if (!prop) return;
5010
+ let finalArr = [];
5011
+ const currentSorted = sortedBy.find((p) => p.prop === prop);
5012
+ if (currentSorted) {
5013
+ if (currentSorted.direction === "asc") {
5014
+ finalArr = sortedBy.filter((p) => p.prop !== prop);
5015
+ } else {
5016
+ finalArr = sortedBy.map((p) => {
5017
+ if (p.prop !== prop) return p;
5018
+ return {
5019
+ prop: p.prop,
5020
+ direction: toggleSortedDirection(p.direction)
5021
+ };
5022
+ });
5023
+ }
5024
+ } else {
5025
+ finalArr = [...sortedBy, { prop, direction: "desc" }];
5026
+ }
5027
+ await updateGridContent({
5028
+ page: currentPage,
5029
+ sortedBy: finalArr,
5030
+ rowsPerPage
5031
+ });
5032
+ },
5033
+ [sortedBy, toggleSortedDirection, rowsPerPage, currentPage]
5034
+ );
5035
+ const set = useCallback5((data) => {
5036
+ setDefaultData(data);
5037
+ }, []);
5038
+ const baseRequest = useCallback5(
5039
+ async ({
5040
+ page,
5041
+ search: search2,
5042
+ filters: filters2,
5043
+ sortedBy: sortedBy2,
5044
+ rowsPerPage: rowsPerPage2
5045
+ }) => {
5046
+ if (!axiosInstance) throw new Error("Axios instance not provided");
5047
+ try {
5048
+ const params = new URLSearchParams({
5049
+ page: String(page),
5050
+ rowsPerPage: String(rowsPerPage2),
5051
+ searchText: search2?.value || "",
5052
+ sort: sortedBy2.map(({ prop, direction }) => `${prop}:${direction}`).join(","),
5053
+ filters: filters2.map(
5054
+ (filter) => `${filter.prop}:${filter.compareType}:${filter.value}`
5055
+ ).join(",")
5056
+ });
5057
+ const pathWithParams = `${url}?${params.toString()}`;
5058
+ const { data } = await axiosInstance.get(pathWithParams);
5059
+ setTotalNumberOfItems(data.totalNumberOfItems);
5060
+ return data.rows;
5061
+ } catch (_) {
5062
+ return [];
5063
+ }
5064
+ },
5065
+ [axiosInstance, url]
5066
+ );
5067
+ const updateGridContent = useCallback5(
5068
+ async ({
5069
+ page,
5070
+ sortedBy: sortedBy2,
5071
+ rowsPerPage: rowsPerPage2
5072
+ }) => {
5073
+ setIsLoading(true);
5074
+ try {
5075
+ const props = {
5076
+ page,
5077
+ rowsPerPage: rowsPerPage2,
5078
+ sortedBy: sortedBy2,
5079
+ search,
5080
+ filters
5081
+ };
5082
+ const result = !onRequest ? await baseRequest(props) : await onRequest(props);
5083
+ setSortedBy(sortedBy2);
5084
+ setRowsPerPage(rowsPerPage2);
5085
+ set(result);
5086
+ setCurrentPage(page);
5087
+ } finally {
5088
+ setIsLoading(false);
5089
+ }
5090
+ },
5091
+ [set, search, filters, onRequest, baseRequest]
5092
+ );
5093
+ const onPageChange = useCallback5(
5094
+ (pageNumber) => {
5095
+ if (pageNumber < 0) return;
5096
+ if (pageNumber > totalNumberOfPages) return;
5097
+ updateGridContent({ page: pageNumber, sortedBy, rowsPerPage });
5098
+ },
5099
+ [updateGridContent, totalNumberOfPages, sortedBy, rowsPerPage]
5100
+ );
5101
+ const onChangeRowsPerPage = useCallback5(
5102
+ (rows) => {
5103
+ let totalNumberOfPages2 = Math.round(totalNumberOfItems / rows) - 1;
5104
+ totalNumberOfPages2 = totalNumberOfPages2 <= 0 ? 0 : 1;
5105
+ if (currentPage > totalNumberOfPages2)
5106
+ updateGridContent({
5107
+ page: totalNumberOfPages2,
5108
+ sortedBy,
5109
+ rowsPerPage: rows
5110
+ });
5111
+ updateGridContent({
5112
+ page: currentPage,
5113
+ sortedBy,
5114
+ rowsPerPage: rows
5115
+ });
5116
+ },
5117
+ [updateGridContent, totalNumberOfItems, sortedBy, currentPage]
5118
+ );
5119
+ const displayData = defaultData;
5120
+ useEffect5(() => {
5121
+ updateGridContent({ page: 0, sortedBy: [], rowsPerPage });
5122
+ }, [updateGridContent, rowsPerPage]);
5123
+ return {
5124
+ data: displayData,
5125
+ set,
5126
+ onSortBy,
5127
+ sortedBy,
5128
+ defaultData,
5129
+ columns,
5130
+ currentPage,
5131
+ totalNumberOfPages: totalNumberOfPages < 0 ? 0 : totalNumberOfPages,
5132
+ onPageChange,
5133
+ setRowsPerPage: onChangeRowsPerPage,
5134
+ rowsPerPageOptions,
5135
+ rowsPerPage,
5136
+ setSort,
5137
+ isLoading,
5138
+ setIsLoading
5139
+ };
5140
+ }
5141
+
5142
+ // src/hooks/useEvent.ts
5143
+ import { useEffect as useEffect6 } from "react";
5144
+ function useEvent(event, handler, passive = false) {
5145
+ useEffect6(() => {
5146
+ window.addEventListener(event, handler, passive);
5147
+ return function cleanup() {
5148
+ window.removeEventListener(event, handler);
5149
+ };
5150
+ });
5151
+ }
5152
+
5153
+ // src/hooks/useLoading.ts
5154
+ import { useCallback as useCallback6, useState as useState10 } from "react";
5155
+ function useLoading() {
5156
+ const [state, setState] = useState10([]);
5157
+ const isLoading = useCallback6((prop) => state.includes(prop), [state]);
5158
+ const setLoading = useCallback6((prop, remove) => {
5159
+ if (remove)
5160
+ setState((prevState) => prevState.filter((state2) => state2 !== prop));
5161
+ else setState((prevState) => [...prevState, prop]);
5162
+ }, []);
5163
+ return { isLoading, setLoading };
5164
+ }
5165
+
5166
+ // src/hooks/useAlert.ts
5167
+ import { useContext as useContext3 } from "react";
5168
+ var useAlert = () => {
5169
+ return useContext3(AlertContext);
5170
+ };
5171
+
5172
+ // src/hooks/useFormHelper.ts
5173
+ import { useCallback as useCallback7, useContext as useContext4, useEffect as useEffect7, useRef as useRef4 } from "react";
5174
+ function useFormHelper() {
5175
+ const alertProps = useAlert();
5176
+ const loadingProps = useLoading();
5177
+ const { api, formatErrorMessage } = useContext4(FormHelperContext);
5178
+ const { createAlert } = alertProps;
5179
+ const { setLoading } = loadingProps;
5180
+ const sourceRef = useRef4(new AbortController());
5181
+ const onSubmitWrapper = useCallback7(
5182
+ (fn, { name }) => {
5183
+ return async (fields, methods) => {
5184
+ const LOADING_NAME = name;
5185
+ setLoading(LOADING_NAME);
5186
+ try {
5187
+ await fn(fields, methods);
5188
+ } catch (error) {
5189
+ errorHandler(error, methods.setErrors);
5190
+ } finally {
5191
+ setLoading(LOADING_NAME, true);
5192
+ }
5193
+ };
5194
+ },
5195
+ [setLoading]
5196
+ );
5197
+ const onRequestWrapper = useCallback7(
5198
+ (fn, { name }) => {
5199
+ return async (...params) => {
5200
+ const LOADING_NAME = name;
5201
+ setLoading(LOADING_NAME);
5202
+ api.interceptors.request.use(
5203
+ (config) => {
5204
+ if (!config.signal && sourceRef.current && config.method === "get") {
5205
+ config.signal = sourceRef.current.signal;
5206
+ }
5207
+ return config;
5208
+ },
5209
+ (error) => {
5210
+ return Promise.reject(error);
5211
+ }
5212
+ );
5213
+ try {
5214
+ const response = await fn(...params);
5215
+ return response;
5216
+ } catch (error) {
5217
+ errorHandler(error);
5218
+ } finally {
5219
+ setLoading(LOADING_NAME, true);
5220
+ }
5221
+ };
5103
5222
  },
5104
- []
5223
+ [setLoading, api]
5105
5224
  );
5106
- const clearSort = useCallback3(() => {
5107
- setSortedBy([]);
5108
- }, []);
5109
- const appendSort = useCallback3((prop, direction) => {
5110
- setSortedBy((prev) => [...prev, { prop, direction }]);
5111
- }, []);
5112
- const setSort = useCallback3((prop, direction) => {
5113
- setSortedBy([{ prop, direction }]);
5114
- }, []);
5115
- const onSortBy = useCallback3(
5116
- (prop) => {
5117
- if (!prop) return;
5118
- const currentSorted = sortedBy.find((p) => p.prop === prop);
5119
- if (currentSorted) {
5120
- if (currentSorted.direction === "asc") {
5121
- setSortedBy((prev) => prev.filter((p) => p.prop !== prop));
5122
- } else {
5123
- setSortedBy((prev) => {
5124
- const newArr = prev.map((p) => {
5125
- if (p.prop !== prop) return p;
5126
- return {
5127
- prop: p.prop,
5128
- direction: toggleSortedDirection(p.direction)
5129
- };
5130
- });
5131
- return [...newArr].slice(0);
5132
- });
5225
+ const errorHandler = useCallback7(
5226
+ (error, callback) => {
5227
+ if (error?.message === "cancel.navigation") return;
5228
+ if (callback) {
5229
+ if (error.response.data.code === "invalid.body") {
5230
+ const errors = error.response.data.details.issues;
5231
+ const currentErrors = errors.reduce((acc, item) => {
5232
+ acc[item.path.join(".")] = item.message;
5233
+ return acc;
5234
+ }, {});
5235
+ callback(currentErrors);
5133
5236
  }
5134
- } else {
5135
- setSortedBy((prev) => [...prev, { prop, direction: "desc" }]);
5136
5237
  }
5238
+ createAlert(formatErrorMessage(error), "error");
5137
5239
  },
5138
- [toggleSortedDirection, sortedBy]
5240
+ [formatErrorMessage, createAlert]
5139
5241
  );
5140
- const set = useCallback3((data) => {
5141
- setDefaultData(data);
5242
+ useEffect7(() => {
5243
+ return () => {
5244
+ sourceRef.current.abort();
5245
+ sourceRef.current = new AbortController();
5246
+ };
5142
5247
  }, []);
5143
- const sortData = useCallback3(
5144
- (data) => {
5145
- if (sortedBy.length > 0) {
5146
- const symbolDir = {
5147
- asc: "",
5148
- desc: "-"
5149
- };
5150
- const formattedKeys = sortedBy.map(
5151
- ({ prop, direction }) => `${symbolDir[direction]}${prop}`
5152
- );
5153
- return data.sort(SortHelper(...formattedKeys));
5154
- } else return data;
5155
- },
5156
- [sortedBy]
5157
- );
5158
- const onPageChange = (pageNumber) => {
5159
- if (pageNumber < 0) return;
5160
- if (pageNumber > totalNumberOfPages) return;
5161
- setCurrentPage(pageNumber);
5248
+ return {
5249
+ ...alertProps,
5250
+ ...loadingProps,
5251
+ onSubmitWrapper,
5252
+ onRequestWrapper
5162
5253
  };
5163
- const onChangeRowsPerPage = useCallback3(
5164
- (rows) => {
5165
- let totalNumberOfPages2 = Math.round(filteredData.length / rows) - 1;
5166
- totalNumberOfPages2 = totalNumberOfPages2 <= 0 ? 0 : 1;
5167
- if (currentPage > totalNumberOfPages2) setCurrentPage(totalNumberOfPages2);
5168
- setRowsPerPage(rows);
5254
+ }
5255
+
5256
+ // src/contexts/AuthContext.tsx
5257
+ function createAuthContext() {
5258
+ return createContext5({});
5259
+ }
5260
+ function CreateAuthProvider({
5261
+ api,
5262
+ children,
5263
+ sessionTokenName,
5264
+ Provider
5265
+ }) {
5266
+ const [user, setUser] = useState11();
5267
+ const [status, setStatus] = useState11("unauthenticated");
5268
+ const { createAlert } = useAlert();
5269
+ const signIn = useCallback8(
5270
+ async ({ email, password }) => {
5271
+ setStatus("loading");
5272
+ try {
5273
+ const response = await api.post("/auth/login", {
5274
+ email,
5275
+ password
5276
+ });
5277
+ const { token } = response.data;
5278
+ api.defaults.headers.common.Authorization = `Bearer ${token}`;
5279
+ const { data } = await api.get("/auth/me");
5280
+ setUser(data);
5281
+ setStatus("autenticated");
5282
+ return true;
5283
+ } catch (error) {
5284
+ createAlert(error?.response?.data?.error, "error");
5285
+ setStatus("unauthenticated");
5286
+ throw error;
5287
+ }
5169
5288
  },
5170
- [currentPage]
5289
+ [createAlert, api]
5171
5290
  );
5172
- const orderedData = useMemo3(() => {
5173
- if (sortedBy.length === 0) return defaultData;
5174
- const newData = defaultData.slice(0);
5175
- const sortedData = sortData(newData);
5176
- return sortedData;
5177
- }, [defaultData, sortData, sortedBy]);
5178
- const filteredData = useMemo3(() => {
5179
- let newData = orderedData.slice(0);
5180
- if (search && search.value !== "") {
5181
- const searchBy = createSearch(search);
5182
- newData = newData.filter(searchBy);
5291
+ const ClientSignOut = useCallback8(async () => {
5292
+ await api.get("/auth/logout");
5293
+ setUser(void 0);
5294
+ }, [api]);
5295
+ useEffect8(() => {
5296
+ const token = parseCookies()[sessionTokenName];
5297
+ if (token) {
5298
+ setStatus("loading");
5299
+ api.get("/auth/me").then((response) => {
5300
+ setStatus("autenticated");
5301
+ setUser(response.data);
5302
+ }).catch(() => {
5303
+ setStatus("unauthenticated");
5304
+ });
5183
5305
  }
5184
- if (!filters) return newData;
5185
- const newFilter = createFilter(filters);
5186
- return newData.filter(newFilter.apply);
5187
- }, [orderedData, search, filters]);
5188
- const paginatedData = useMemo3(() => {
5189
- const startPage = currentPage * rowsPerPage;
5190
- const endPage = startPage + rowsPerPage;
5191
- return filteredData.slice(startPage, endPage);
5192
- }, [currentPage, rowsPerPage, filteredData]);
5193
- const totalNumberOfPages = Math.ceil(filteredData.length / rowsPerPage) - 1;
5194
- useEffect4(() => {
5195
- if (externalDefaultData) setDefaultData(externalDefaultData);
5196
- }, [externalDefaultData]);
5197
- return {
5198
- data: paginatedData,
5199
- orderedData,
5200
- filteredData,
5201
- defaultData,
5202
- sortedBy,
5203
- columns,
5204
- currentPage,
5205
- totalNumberOfPages: totalNumberOfPages < 0 ? 0 : totalNumberOfPages,
5206
- rowsPerPageOptions,
5207
- rowsPerPage,
5208
- set,
5209
- onSortBy,
5210
- onPageChange,
5211
- setRowsPerPage: onChangeRowsPerPage,
5212
- appendSort,
5213
- setSort,
5214
- clearSort
5215
- };
5306
+ }, [api, sessionTokenName]);
5307
+ return /* @__PURE__ */ React33.createElement(
5308
+ Provider,
5309
+ {
5310
+ value: {
5311
+ user,
5312
+ signOut: ClientSignOut,
5313
+ signIn,
5314
+ status
5315
+ }
5316
+ },
5317
+ children
5318
+ );
5216
5319
  }
5217
- var isNumberOrString = (value) => ["number", "string"].includes(typeof value);
5218
- var concatenateKey = (key, prev) => {
5219
- if (!prev) return key;
5220
- return `${prev}.${key}`;
5320
+
5321
+ // src/errors/HttpError.ts
5322
+ var HttpError = class extends Error {
5323
+ constructor(status, message) {
5324
+ super(message);
5325
+ this.message = message;
5326
+ this.stack = `HttpError: ${message}`;
5327
+ this.status = status;
5328
+ }
5329
+ };
5330
+
5331
+ // src/errors/DomainError.ts
5332
+ var DomainError = class extends Error {
5333
+ constructor(message) {
5334
+ super(message);
5335
+ this.message = message;
5336
+ this.stack = `DomainError: ${message}`;
5337
+ }
5221
5338
  };
5222
- function searchKeysForValue(row, accKey, compare) {
5223
- if (typeof row === "undefined") return false;
5224
- const rowKeys = Object.keys(row);
5225
- let match = false;
5226
- for (const key of rowKeys) {
5227
- if (match) break;
5228
- const objValue = row[key];
5229
- if (!objValue) continue;
5230
- const concatenatedKey = concatenateKey(key, accKey);
5231
- if (Array.isArray(objValue)) {
5232
- match = objValue.some(
5233
- (obj) => isNumberOrString(obj) ? compare(concatenatedKey, obj) : searchKeysForValue(obj, concatenatedKey, compare)
5234
- );
5235
- continue;
5236
- }
5237
- if (typeof objValue === "object") {
5238
- match = searchKeysForValue(objValue, concatenatedKey, compare);
5239
- continue;
5240
- }
5241
- match = compare(concatenatedKey, row[key]);
5339
+
5340
+ // src/helpers/apiHelper/index.ts
5341
+ var VALID_METHODS = [
5342
+ "GET",
5343
+ "HEAD",
5344
+ "POST",
5345
+ "PUT",
5346
+ "DELETE",
5347
+ "CONNECT",
5348
+ "OPTIONS",
5349
+ "TRACE",
5350
+ "PATCH"
5351
+ ];
5352
+ var _ApiHelper = class _ApiHelper {
5353
+ async onFinally(_req, _res) {
5242
5354
  }
5243
- return match;
5244
- }
5245
- var normalize = (str) => str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
5246
- function createSearch(options) {
5247
- const searchValue = options.caseSensitive ? options.value : String(options.value).toLowerCase();
5248
- function getValue2(value) {
5249
- if (options.caseSensitive) return String(value);
5250
- return String(value).toLowerCase();
5355
+ async onError(_req, _res, _error) {
5251
5356
  }
5252
- function compare(key, objValue) {
5253
- if (options.ignoredKeys) {
5254
- const isIgnoredKey = options.ignoredKeys.includes(key);
5255
- if (isIgnoredKey) return false;
5357
+ constructor(props) {
5358
+ this.public = props?.public ?? false;
5359
+ this.middlewares = (props?.middlewares || []).reverse();
5360
+ this.onFinally = props?.onFinally || (async () => {
5361
+ });
5362
+ this.onError = props?.onError || (async () => {
5363
+ });
5364
+ }
5365
+ createMethods(methods) {
5366
+ return async (req, res) => {
5367
+ const currentMethod = methods[req.method] || methods.ALL;
5368
+ const options = { public: this.public };
5369
+ if (req.method === "OPTIONS") return res.status(200).end();
5370
+ try {
5371
+ if (!VALID_METHODS.includes(req.method))
5372
+ throw new HttpError(405, "M\xE9todo inv\xE1lido");
5373
+ if (!currentMethod) throw new HttpError(500, "M\xE9todo n\xE3o encontrado");
5374
+ const methodWithMiddlewares = this.middlewares.reduce(
5375
+ (acc, fn) => fn(acc, options),
5376
+ currentMethod
5377
+ );
5378
+ return await methodWithMiddlewares(req, res, options);
5379
+ } catch (error) {
5380
+ if (error instanceof DomainError) return res.status(400).json(error.message);
5381
+ if (error instanceof HttpError) return res.status(error.status).json(error.message);
5382
+ this.onError(req, res, error);
5383
+ throw error;
5384
+ } finally {
5385
+ await this.onFinally(req, res);
5386
+ }
5387
+ };
5388
+ }
5389
+ buildFactory(factory) {
5390
+ const options = {
5391
+ public: this.public
5392
+ };
5393
+ return async (req, res) => {
5394
+ const methods = factory(req, res);
5395
+ const handler = methods[req.method];
5396
+ if (!handler) throw new HttpError(405, "M\xE9todo inv\xE1lido");
5397
+ const methodWithMiddlewares = this.middlewares.reduce((acc, fn) => {
5398
+ return fn(acc, options);
5399
+ }, handler);
5400
+ return await methodWithMiddlewares(req, res, options);
5401
+ };
5402
+ }
5403
+ static build(factory, options) {
5404
+ const helper = new _ApiHelper({
5405
+ ...options
5406
+ }).buildFactory(factory);
5407
+ return helper;
5408
+ }
5409
+ static parse(body, parser) {
5410
+ try {
5411
+ const object = parser.parse(body);
5412
+ return object;
5413
+ } catch (error) {
5414
+ throw new HttpError(400, {
5415
+ code: "invalid.body",
5416
+ error: "Dados inv\xE1lidos",
5417
+ details: error
5418
+ });
5256
5419
  }
5257
- const value = getValue2(objValue);
5258
- if (options.exact) return value === searchValue;
5259
- if (options.ignoreAccentMark) return normalize(value).includes(normalize(searchValue));
5260
- return value.includes(searchValue);
5261
5420
  }
5262
- return (row) => {
5263
- const match = searchKeysForValue(row, "", compare);
5264
- return match;
5265
- };
5266
- }
5421
+ /** @deprecated Use {@Link ApiHelper.build} instead. */
5422
+ static create({ onFinally }) {
5423
+ return new _ApiHelper({
5424
+ onFinally
5425
+ });
5426
+ }
5427
+ };
5428
+ /** @deprecated Use {@link ApiHelper.parser} instead. */
5429
+ _ApiHelper.parserErrorWrapper = _ApiHelper.parse;
5430
+ var ApiHelper = _ApiHelper;
5267
5431
 
5268
- // src/hooks/useAsyncGrid.ts
5269
- import { useCallback as useCallback4, useEffect as useEffect5, useState as useState8 } from "react";
5270
- function useAsyncGrid({
5271
- columns,
5272
- filters = [],
5273
- search,
5274
- rowsPerPageOptions = [30, 60, 100],
5275
- onRequest,
5276
- axiosInstance,
5277
- url,
5278
- defaultData: externalDefaultData,
5279
- defaultCurrentPage,
5280
- defaultSortedBy
5432
+ // src/helpers/authHelper.ts
5433
+ import { serialize as serialize2 } from "cookie";
5434
+ import jwt from "jsonwebtoken";
5435
+ import { randomUUID } from "crypto";
5436
+ function decodeSessionToken({
5437
+ req,
5438
+ res,
5439
+ sessionTokenName,
5440
+ validate
5281
5441
  }) {
5282
- const [defaultData, setDefaultData] = useState8(externalDefaultData || []);
5283
- const [sortedBy, setSortedBy] = useState8(
5284
- defaultSortedBy || []
5285
- );
5286
- const [totalNumberOfItems, setTotalNumberOfItems] = useState8(0);
5287
- const [isLoading, setIsLoading] = useState8(false);
5288
- const [currentPage, setCurrentPage] = useState8(defaultCurrentPage || 0);
5289
- const [rowsPerPage, setRowsPerPage] = useState8(rowsPerPageOptions[0]);
5290
- const totalNumberOfPages = Math.ceil(totalNumberOfItems / rowsPerPage) - 1;
5291
- const toggleSortedDirection = useCallback4(
5292
- (direction) => {
5293
- if (direction === "asc") return "desc";
5294
- return "asc";
5295
- },
5296
- []
5297
- );
5298
- const setSort = useCallback4((prop, direction) => {
5299
- setSortedBy((prev) => [...prev, { prop, direction }]);
5300
- }, []);
5301
- const onSortBy = useCallback4(
5302
- async (prop) => {
5303
- if (!prop) return;
5304
- let finalArr = [];
5305
- const currentSorted = sortedBy.find((p) => p.prop === prop);
5306
- if (currentSorted) {
5307
- if (currentSorted.direction === "asc") {
5308
- finalArr = sortedBy.filter((p) => p.prop !== prop);
5309
- } else {
5310
- finalArr = sortedBy.map((p) => {
5311
- if (p.prop !== prop) return p;
5312
- return {
5313
- prop: p.prop,
5314
- direction: toggleSortedDirection(p.direction)
5315
- };
5316
- });
5317
- }
5318
- } else {
5319
- finalArr = [...sortedBy, { prop, direction: "desc" }];
5442
+ const token = req.headers.authorization?.split(" ")[1] || req.cookies[sessionTokenName];
5443
+ if (!token) {
5444
+ res.status(401).json({ error: "Token inv\xE1lido", code: "token.invalid" });
5445
+ return true;
5446
+ }
5447
+ const jwtDecode = (token2) => {
5448
+ if (validate) {
5449
+ return jwt.verify(token2, process.env.JWT_SECRET);
5450
+ }
5451
+ return jwt.decode(token2);
5452
+ };
5453
+ try {
5454
+ const decoded = jwtDecode(token);
5455
+ req.user = decoded.sub;
5456
+ } catch (_) {
5457
+ res.status(401).json({ error: "Token inv\xE1lido", code: "token.expired" });
5458
+ return true;
5459
+ }
5460
+ }
5461
+ var AuthHelper = class {
5462
+ constructor({
5463
+ cookies,
5464
+ oauth,
5465
+ tokenExpTimeInSeconds,
5466
+ onLogin,
5467
+ onValidateRefreshToken,
5468
+ onInvalidateRefreshToken,
5469
+ onCreateRefreshToken,
5470
+ onGetUserData
5471
+ }) {
5472
+ this.generateJwtAndRefreshToken = async (userId, payload = {}) => {
5473
+ const token = jwt.sign(payload, process.env.JWT_SECRET, {
5474
+ subject: String(userId),
5475
+ expiresIn: this.tokenExpTimeInSeconds || 60 * 15
5476
+ // 15 minutos
5477
+ });
5478
+ const uniqueToken = randomUUID();
5479
+ await this.onCreateRefreshToken(userId, uniqueToken);
5480
+ return {
5481
+ token,
5482
+ refreshToken: uniqueToken
5483
+ };
5484
+ };
5485
+ this.invalidateCookies = (res) => {
5486
+ return res.setHeader("Set-Cookie", [
5487
+ serialize2(this.cookies.sessionToken, "", {
5488
+ maxAge: -1,
5489
+ path: "/"
5490
+ }),
5491
+ serialize2(this.cookies.refreshToken, "", {
5492
+ maxAge: -1,
5493
+ path: "/"
5494
+ })
5495
+ ]);
5496
+ };
5497
+ this.cookies = cookies;
5498
+ this.oauth = oauth;
5499
+ this.tokenExpTimeInSeconds = tokenExpTimeInSeconds;
5500
+ this.onLogin = onLogin;
5501
+ this.onValidateRefreshToken = onValidateRefreshToken;
5502
+ this.onInvalidateRefreshToken = onInvalidateRefreshToken;
5503
+ this.onCreateRefreshToken = onCreateRefreshToken;
5504
+ this.onGetUserData = onGetUserData;
5505
+ }
5506
+ async handler(req, res) {
5507
+ if (!req.url) return res.status(400).json({ error: "url not sent" });
5508
+ if (req.url.endsWith("/login")) {
5509
+ const loginResult = await this.onLogin(req.body);
5510
+ if (loginResult.status === "success") {
5511
+ const { refreshToken, token } = await this.generateJwtAndRefreshToken(
5512
+ loginResult.userId,
5513
+ {}
5514
+ );
5515
+ setCookie({ res }, this.cookies.sessionToken, token, {
5516
+ secure: true,
5517
+ maxAge: 60 * 60 * 24 * 30,
5518
+ // 30 days
5519
+ path: "/",
5520
+ sameSite: true
5521
+ });
5522
+ setCookie({ res }, this.cookies.refreshToken, refreshToken, {
5523
+ secure: true,
5524
+ maxAge: 60 * 60 * 24 * 30,
5525
+ // 30 days
5526
+ path: "/",
5527
+ sameSite: true,
5528
+ httpOnly: true
5529
+ });
5530
+ return res.json({ token, refreshToken });
5320
5531
  }
5321
- await updateGridContent({
5322
- page: currentPage,
5323
- sortedBy: finalArr,
5324
- rowsPerPage
5532
+ throw new HttpError(400, loginResult.response);
5533
+ }
5534
+ if (req.url.endsWith("/logout")) {
5535
+ this.invalidateCookies(res).end();
5536
+ }
5537
+ if (req.url.endsWith("/refresh")) {
5538
+ const error = decodeSessionToken({
5539
+ req,
5540
+ res,
5541
+ sessionTokenName: this.cookies.sessionToken,
5542
+ validate: false
5325
5543
  });
5326
- },
5327
- [sortedBy, toggleSortedDirection, rowsPerPage, currentPage]
5328
- );
5329
- const set = useCallback4((data) => {
5330
- setDefaultData(data);
5331
- }, []);
5332
- const baseRequest = useCallback4(
5333
- async ({
5334
- page,
5335
- search: search2,
5336
- filters: filters2,
5337
- sortedBy: sortedBy2,
5338
- rowsPerPage: rowsPerPage2
5339
- }) => {
5340
- if (!axiosInstance) throw new Error("Axios instance not provided");
5341
- try {
5342
- const params = new URLSearchParams({
5343
- page: String(page),
5344
- rowsPerPage: String(rowsPerPage2),
5345
- searchText: search2?.value || "",
5346
- sort: sortedBy2.map(({ prop, direction }) => `${prop}:${direction}`).join(","),
5347
- filters: filters2.map(
5348
- (filter) => `${filter.prop}:${filter.compareType}:${filter.value}`
5349
- ).join(",")
5544
+ if (error) return;
5545
+ const userId = String(req.user);
5546
+ const refreshToken = parseCookies({ req })[this.cookies.refreshToken];
5547
+ if (!refreshToken) {
5548
+ this.invalidateCookies(res);
5549
+ return res.status(400).json({
5550
+ error: "Refresh Token inv\xE1lido"
5350
5551
  });
5351
- const pathWithParams = `${url}?${params.toString()}`;
5352
- const { data } = await axiosInstance.get(pathWithParams);
5353
- setTotalNumberOfItems(data.totalNumberOfItems);
5354
- return data.rows;
5355
- } catch (_) {
5356
- return [];
5357
- }
5358
- },
5359
- [axiosInstance, url]
5360
- );
5361
- const updateGridContent = useCallback4(
5362
- async ({
5363
- page,
5364
- sortedBy: sortedBy2,
5365
- rowsPerPage: rowsPerPage2
5366
- }) => {
5367
- setIsLoading(true);
5368
- try {
5369
- const props = {
5370
- page,
5371
- rowsPerPage: rowsPerPage2,
5372
- sortedBy: sortedBy2,
5373
- search,
5374
- filters
5375
- };
5376
- const result = !onRequest ? await baseRequest(props) : await onRequest(props);
5377
- setSortedBy(sortedBy2);
5378
- setRowsPerPage(rowsPerPage2);
5379
- set(result);
5380
- setCurrentPage(page);
5381
- } finally {
5382
- setIsLoading(false);
5383
5552
  }
5384
- },
5385
- [set, search, filters, onRequest, baseRequest]
5386
- );
5387
- const onPageChange = useCallback4(
5388
- (pageNumber) => {
5389
- if (pageNumber < 0) return;
5390
- if (pageNumber > totalNumberOfPages) return;
5391
- updateGridContent({ page: pageNumber, sortedBy, rowsPerPage });
5392
- },
5393
- [updateGridContent, totalNumberOfPages, sortedBy, rowsPerPage]
5394
- );
5395
- const onChangeRowsPerPage = useCallback4(
5396
- (rows) => {
5397
- let totalNumberOfPages2 = Math.round(totalNumberOfItems / rows) - 1;
5398
- totalNumberOfPages2 = totalNumberOfPages2 <= 0 ? 0 : 1;
5399
- if (currentPage > totalNumberOfPages2)
5400
- updateGridContent({
5401
- page: totalNumberOfPages2,
5402
- sortedBy,
5403
- rowsPerPage: rows
5553
+ const isValidRefreshToken = await this.onValidateRefreshToken(
5554
+ userId,
5555
+ refreshToken
5556
+ );
5557
+ if (!isValidRefreshToken) {
5558
+ this.invalidateCookies(res);
5559
+ return res.status(400).json({
5560
+ error: "Refresh Token inv\xE1lido"
5404
5561
  });
5405
- updateGridContent({
5406
- page: currentPage,
5407
- sortedBy,
5408
- rowsPerPage: rows
5562
+ }
5563
+ await this.onInvalidateRefreshToken(userId, refreshToken);
5564
+ const { token, refreshToken: newRefreshToken } = await this.generateJwtAndRefreshToken(userId, {});
5565
+ setCookie({ res }, this.cookies.sessionToken, token, {
5566
+ secure: true,
5567
+ maxAge: 60 * 60 * 24 * 30,
5568
+ // 30 days
5569
+ path: "/",
5570
+ sameSite: true
5409
5571
  });
5410
- },
5411
- [updateGridContent, totalNumberOfItems, sortedBy, currentPage]
5412
- );
5413
- const displayData = defaultData;
5414
- useEffect5(() => {
5415
- updateGridContent({ page: 0, sortedBy: [], rowsPerPage });
5416
- }, [updateGridContent, rowsPerPage]);
5417
- return {
5418
- data: displayData,
5419
- set,
5420
- onSortBy,
5421
- sortedBy,
5422
- defaultData,
5423
- columns,
5424
- currentPage,
5425
- totalNumberOfPages: totalNumberOfPages < 0 ? 0 : totalNumberOfPages,
5426
- onPageChange,
5427
- setRowsPerPage: onChangeRowsPerPage,
5428
- rowsPerPageOptions,
5429
- rowsPerPage,
5430
- setSort,
5431
- isLoading,
5432
- setIsLoading
5433
- };
5434
- }
5435
-
5436
- // src/hooks/useEvent.ts
5437
- import { useEffect as useEffect6 } from "react";
5438
- function useEvent(event, handler, passive = false) {
5439
- useEffect6(() => {
5440
- window.addEventListener(event, handler, passive);
5441
- return function cleanup() {
5442
- window.removeEventListener(event, handler);
5443
- };
5444
- });
5445
- }
5446
-
5447
- // src/hooks/useLoading.ts
5448
- import { useCallback as useCallback5, useState as useState9 } from "react";
5449
- function useLoading() {
5450
- const [state, setState] = useState9([]);
5451
- const isLoading = useCallback5((prop) => state.includes(prop), [state]);
5452
- const setLoading = useCallback5((prop, remove) => {
5453
- if (remove)
5454
- setState((prevState) => prevState.filter((state2) => state2 !== prop));
5455
- else setState((prevState) => [...prevState, prop]);
5456
- }, []);
5457
- return { isLoading, setLoading };
5458
- }
5459
-
5460
- // src/hooks/useAlert.ts
5461
- import { useContext as useContext3 } from "react";
5462
-
5463
- // src/contexts/AlertContext.tsx
5464
- import React31, { useCallback as useCallback6 } from "react";
5465
- import { createContext as createContext3, useState as useState10 } from "react";
5466
-
5467
- // src/components/Toast/index.tsx
5468
- import React30 from "react";
5469
- import { Alert, IconButton as IconButton5, Snackbar } from "@mui/material";
5470
- import { MdClose as MdClose3 } from "react-icons/md";
5471
- var Toast = ({ open, onClose, severity, message }) => {
5472
- return /* @__PURE__ */ React30.createElement(React30.Fragment, null, /* @__PURE__ */ React30.createElement(
5473
- Snackbar,
5474
- {
5475
- open,
5476
- autoHideDuration: 6e3,
5477
- onClose,
5478
- anchorOrigin: { vertical: "top", horizontal: "right" },
5479
- sx: { zIndex: 99999999 }
5480
- },
5481
- /* @__PURE__ */ React30.createElement(
5482
- Alert,
5483
- {
5484
- severity,
5485
- elevation: 2,
5486
- action: /* @__PURE__ */ React30.createElement(
5487
- IconButton5,
5488
- {
5489
- "aria-label": "close",
5490
- color: "inherit",
5491
- size: "small",
5492
- onClick: onClose
5493
- },
5494
- /* @__PURE__ */ React30.createElement(MdClose3, { fontSize: "inherit" })
5495
- )
5496
- },
5497
- message
5498
- )
5499
- ));
5500
- };
5501
-
5502
- // src/contexts/AlertContext.tsx
5503
- var AlertContext = createContext3({});
5504
- var AlertProvider = ({ children }) => {
5505
- const [severity, setSeverity] = useState10("info");
5506
- const [message, setMessage] = useState10("");
5507
- const [isVisible, setIsVisible] = useState10(false);
5508
- const createAlert = useCallback6(
5509
- (newMessage, severity2) => {
5510
- setMessage(newMessage);
5511
- setSeverity(severity2);
5512
- setIsVisible(true);
5513
- },
5514
- []
5515
- );
5516
- const onCloseToast = useCallback6(() => {
5517
- setIsVisible(false);
5518
- }, []);
5519
- return /* @__PURE__ */ React31.createElement(AlertContext.Provider, { value: { createAlert } }, children, /* @__PURE__ */ React31.createElement(
5520
- Toast,
5521
- {
5522
- open: isVisible,
5523
- onClose: onCloseToast,
5524
- severity,
5525
- message
5572
+ setCookie({ res }, this.cookies.refreshToken, newRefreshToken, {
5573
+ secure: true,
5574
+ maxAge: 60 * 60 * 24 * 30,
5575
+ // 30 days
5576
+ path: "/",
5577
+ sameSite: true,
5578
+ httpOnly: true
5579
+ });
5580
+ return res.json({
5581
+ token,
5582
+ refreshToken: newRefreshToken
5583
+ });
5584
+ }
5585
+ if (req.url.endsWith("/me")) {
5586
+ const error = decodeSessionToken({
5587
+ req,
5588
+ res,
5589
+ sessionTokenName: this.cookies.sessionToken,
5590
+ validate: true
5591
+ });
5592
+ if (error) return;
5593
+ if (!req.user)
5594
+ return res.status(400).json({ error: "Usu\xE1rio n\xE3o encontrado" });
5595
+ const userData = await this.onGetUserData(req.user);
5596
+ if (!userData)
5597
+ return res.status(400).json({ error: "Usu\xE1rio n\xE3o encontrado" });
5598
+ return res.json(userData);
5526
5599
  }
5527
- ));
5528
- };
5529
-
5530
- // src/hooks/useAlert.ts
5531
- var useAlert = () => {
5532
- return useContext3(AlertContext);
5533
- };
5534
-
5535
- // src/hooks/useFormHelper.ts
5536
- import { useCallback as useCallback7, useContext as useContext4, useEffect as useEffect7, useRef as useRef4 } from "react";
5537
-
5538
- // src/contexts/FormHelperProvider.tsx
5539
- import React32 from "react";
5540
- import { createContext as createContext4 } from "react";
5541
- var FormHelperContext = createContext4({});
5542
- var FormHelperProvider = ({ formatErrorMessage, api, children }) => {
5543
- return /* @__PURE__ */ React32.createElement(FormHelperContext.Provider, { value: { formatErrorMessage, api } }, children);
5544
- };
5545
-
5546
- // src/hooks/useFormHelper.ts
5547
- function useFormHelper() {
5548
- const alertProps = useAlert();
5549
- const loadingProps = useLoading();
5550
- const { api, formatErrorMessage } = useContext4(FormHelperContext);
5551
- const { createAlert } = alertProps;
5552
- const { setLoading } = loadingProps;
5553
- const sourceRef = useRef4(new AbortController());
5554
- const onSubmitWrapper = useCallback7(
5555
- (fn, { name }) => {
5556
- return async (fields, methods) => {
5557
- const LOADING_NAME = name;
5558
- setLoading(LOADING_NAME);
5559
- try {
5560
- await fn(fields, methods);
5561
- } catch (error) {
5562
- errorHandler(error, methods.setErrors);
5563
- } finally {
5564
- setLoading(LOADING_NAME, true);
5565
- }
5566
- };
5567
- },
5568
- [setLoading]
5569
- );
5570
- const onRequestWrapper = useCallback7(
5571
- (fn, { name }) => {
5572
- return async (...params) => {
5573
- const LOADING_NAME = name;
5574
- setLoading(LOADING_NAME);
5575
- api.interceptors.request.use(
5576
- (config) => {
5577
- if (!config.signal && sourceRef.current && config.method === "get") {
5578
- config.signal = sourceRef.current.signal;
5579
- }
5580
- return config;
5581
- },
5582
- (error) => {
5583
- return Promise.reject(error);
5584
- }
5585
- );
5586
- try {
5587
- const response = await fn(...params);
5588
- return response;
5589
- } catch (error) {
5590
- errorHandler(error);
5591
- } finally {
5592
- setLoading(LOADING_NAME, true);
5593
- }
5600
+ if (req.url.endsWith("/oauth-url") && this.oauth) {
5601
+ const params = {
5602
+ client_id: this.oauth.client_id,
5603
+ redirect_uri: this.oauth.redirect_uri,
5604
+ scope: this.oauth.scope,
5605
+ response_type: "code",
5606
+ response_mode: "query"
5594
5607
  };
5595
- },
5596
- [setLoading, api]
5597
- );
5598
- const errorHandler = useCallback7(
5599
- (error, callback) => {
5600
- if (error?.message === "cancel.navigation") return;
5601
- if (callback) {
5602
- if (error.response.data.code === "invalid.body") {
5603
- const errors = error.response.data.details.issues;
5604
- const currentErrors = errors.reduce((acc, item) => {
5605
- acc[item.path.join(".")] = item.message;
5606
- return acc;
5607
- }, {});
5608
- callback(currentErrors);
5609
- }
5610
- }
5611
- createAlert(formatErrorMessage(error), "error");
5612
- },
5613
- [formatErrorMessage, createAlert]
5614
- );
5615
- useEffect7(() => {
5616
- return () => {
5617
- sourceRef.current.abort();
5618
- sourceRef.current = new AbortController();
5608
+ const url = `https://login.microsoftonline.com/${this.oauth.tenant_id}/oauth2/v2.0/authorize?${new URLSearchParams(params)}`;
5609
+ return res.json({
5610
+ url
5611
+ });
5612
+ }
5613
+ return res.status(404).json({ error: "Route not found" });
5614
+ }
5615
+ async oauthSignInCallback(code) {
5616
+ if (!this.oauth) throw new Error("OAUTH variables is not defined");
5617
+ const body = {
5618
+ client_id: this.oauth.client_id,
5619
+ scope: this.oauth.scope,
5620
+ code,
5621
+ session_state: this.oauth.client_id,
5622
+ redirect_uri: this.oauth.redirect_uri,
5623
+ grant_type: "authorization_code",
5624
+ client_secret: this.oauth.client_secret
5619
5625
  };
5620
- }, []);
5621
- return {
5622
- ...alertProps,
5623
- ...loadingProps,
5624
- onSubmitWrapper,
5625
- onRequestWrapper
5626
- };
5627
- }
5628
-
5629
- // src/contexts/AuthContext.tsx
5630
- import React33, { useCallback as useCallback8 } from "react";
5631
- import {
5632
- createContext as createContext5,
5633
- useEffect as useEffect8,
5634
- useState as useState11
5635
- } from "react";
5636
- function createAuthContext() {
5637
- return createContext5({});
5638
- }
5639
- function CreateAuthProvider({
5640
- api,
5641
- children,
5642
- sessionTokenName,
5643
- Provider
5644
- }) {
5645
- const [user, setUser] = useState11();
5646
- const [status, setStatus] = useState11("unauthenticated");
5647
- const { createAlert } = useAlert();
5648
- const signIn = useCallback8(
5649
- async ({ email, password }) => {
5650
- setStatus("loading");
5626
+ const response = await fetch(
5627
+ `https://login.microsoftonline.com/${this.oauth.tenant_id}/oauth2/v2.0/token`,
5628
+ {
5629
+ method: "POST",
5630
+ body: new URLSearchParams(body),
5631
+ headers: { "Content-Type": "application/x-www-form-urlencoded" }
5632
+ }
5633
+ );
5634
+ const data = await response.json();
5635
+ const decodedToken = jwt.decode(data.access_token);
5636
+ const email = decodedToken.upn;
5637
+ const fullName = `${decodedToken?.given_name} ${decodedToken?.family_name}`;
5638
+ return { decodedToken, email, fullName };
5639
+ }
5640
+ createOauthCallbackGetServerSideProps({
5641
+ onSuccessDestination,
5642
+ onFailedDestination
5643
+ }) {
5644
+ return async (ctx) => {
5645
+ if (!this.oauth) throw new Error("Oauth env variables are not defined");
5646
+ const code = ctx.query.code;
5647
+ if (!code)
5648
+ return {
5649
+ redirect: {
5650
+ permanent: false,
5651
+ destination: onFailedDestination || "/"
5652
+ }
5653
+ };
5651
5654
  try {
5652
- const response = await api.post("/auth/login", {
5655
+ const { fullName, email } = await this.oauthSignInCallback(code);
5656
+ const userExists = await this.onGetUserData(email);
5657
+ if (!userExists && !this.oauth.onCreateUser)
5658
+ throw new Error("User does not exists");
5659
+ if (!userExists && this.oauth.onCreateUser) {
5660
+ await this.oauth.onCreateUser({ fullname: fullName, email });
5661
+ }
5662
+ const { token, refreshToken } = await this.generateJwtAndRefreshToken(
5653
5663
  email,
5654
- password
5664
+ {}
5665
+ );
5666
+ setCookie(ctx, this.cookies.sessionToken, token, {
5667
+ secure: true,
5668
+ maxAge: 60 * 60 * 24 * 30,
5669
+ // 30 days
5670
+ path: "/"
5655
5671
  });
5656
- const { token } = response.data;
5657
- api.defaults.headers.common.Authorization = `Bearer ${token}`;
5658
- const { data } = await api.get("/auth/me");
5659
- setUser(data);
5660
- setStatus("autenticated");
5661
- return true;
5672
+ setCookie(ctx, this.cookies.refreshToken, refreshToken, {
5673
+ secure: true,
5674
+ maxAge: 60 * 60 * 24 * 30,
5675
+ // 30 days
5676
+ path: "/",
5677
+ httpOnly: true
5678
+ });
5679
+ return {
5680
+ redirect: {
5681
+ destination: onSuccessDestination,
5682
+ permanent: false
5683
+ }
5684
+ };
5662
5685
  } catch (error) {
5663
- createAlert(error?.response?.data?.error, "error");
5664
- setStatus("unauthenticated");
5665
- throw error;
5666
- }
5667
- },
5668
- [createAlert, api]
5669
- );
5670
- const ClientSignOut = useCallback8(async () => {
5671
- await api.get("/auth/logout");
5672
- setUser(void 0);
5673
- }, [api]);
5674
- useEffect8(() => {
5675
- const token = parseCookies()[sessionTokenName];
5676
- if (token) {
5677
- setStatus("loading");
5678
- api.get("/auth/me").then((response) => {
5679
- setStatus("autenticated");
5680
- setUser(response.data);
5681
- }).catch(() => {
5682
- setStatus("unauthenticated");
5683
- });
5684
- }
5685
- }, [api, sessionTokenName]);
5686
- return /* @__PURE__ */ React33.createElement(
5687
- Provider,
5688
- {
5689
- value: {
5690
- user,
5691
- signOut: ClientSignOut,
5692
- signIn,
5693
- status
5686
+ return {
5687
+ props: {
5688
+ error: JSON.stringify(error)
5689
+ }
5690
+ };
5694
5691
  }
5695
- },
5696
- children
5697
- );
5698
- }
5692
+ };
5693
+ }
5694
+ };
5699
5695
  export {
5700
5696
  AlertContext,
5701
5697
  AlertProvider,