@juv/codego-react-ui 3.0.8 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1996,6 +1996,61 @@ function MetricRow({ items, divided = true, className }) {
1996
1996
 
1997
1997
  // src/components/ui/data-grid.tsx
1998
1998
  import * as React24 from "react";
1999
+ import { createPortal as createPortal2 } from "react-dom";
2000
+
2001
+ // src/components/tools/decryptPayload.ts
2002
+ import CryptoJS from "crypto-js";
2003
+ function getLaravelSecretKey() {
2004
+ const viteKey = import.meta.env["VITE_LARAVEL_KEY"];
2005
+ const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
2006
+ const key = viteKey || legacyKey;
2007
+ if (!key) {
2008
+ throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env.");
2009
+ }
2010
+ return key;
2011
+ }
2012
+ function parseLaravelKey(secretKey) {
2013
+ if (secretKey.startsWith("base64:")) {
2014
+ return CryptoJS.enc.Base64.parse(secretKey.slice("base64:".length));
2015
+ }
2016
+ return CryptoJS.enc.Utf8.parse(secretKey);
2017
+ }
2018
+ function parseLaravelEncryptedPayload(payload) {
2019
+ let jsonStr = payload;
2020
+ try {
2021
+ jsonStr = atob(payload);
2022
+ } catch {
2023
+ }
2024
+ return JSON.parse(jsonStr);
2025
+ }
2026
+ function decryptLaravelPayload(payload) {
2027
+ const secretKey = getLaravelSecretKey();
2028
+ const parsed = parseLaravelEncryptedPayload(payload);
2029
+ if (parsed.tag) {
2030
+ throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
2031
+ }
2032
+ const key = parseLaravelKey(secretKey);
2033
+ const expectedMac = CryptoJS.HmacSHA256(parsed.iv + parsed.value, key).toString();
2034
+ if (expectedMac !== parsed.mac) {
2035
+ throw new Error("Invalid payload MAC (wrong key or tampered payload).");
2036
+ }
2037
+ const iv = CryptoJS.enc.Base64.parse(parsed.iv);
2038
+ const ciphertext = CryptoJS.enc.Base64.parse(parsed.value);
2039
+ const cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext });
2040
+ const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
2041
+ iv,
2042
+ mode: CryptoJS.mode.CBC,
2043
+ padding: CryptoJS.pad.Pkcs7
2044
+ });
2045
+ const plaintext = decrypted.toString(CryptoJS.enc.Utf8);
2046
+ if (!plaintext) {
2047
+ throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
2048
+ }
2049
+ console.log("Decrypted payload:", plaintext);
2050
+ return JSON.parse(plaintext);
2051
+ }
2052
+
2053
+ // src/components/ui/data-grid.tsx
1999
2054
  import axios from "axios";
2000
2055
  import { ChevronUp, ChevronDown as ChevronDown4, ChevronsUpDown, ChevronLeft as ChevronLeft5, ChevronRight as ChevronRight7, Search as Search4, Settings2, Check as Check5, Eye, Pencil, Trash, Loader2, X as X7 } from "lucide-react";
2001
2056
 
@@ -4729,7 +4784,7 @@ Input.displayName = "Input";
4729
4784
 
4730
4785
  // src/components/ui/data-grid.tsx
4731
4786
  import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
4732
- function useServerDataGrid({ url, params }) {
4787
+ function useServerDataGrid({ url, params, encrypt }) {
4733
4788
  const [data, setData] = React24.useState([]);
4734
4789
  const [columns, setColumns] = React24.useState([]);
4735
4790
  const [currentPage, setCurrentPage] = React24.useState(1);
@@ -4743,23 +4798,26 @@ function useServerDataGrid({ url, params }) {
4743
4798
  setError(null);
4744
4799
  axios.get(url, { params: { ...params, page: currentPage } }).then(({ data: res }) => {
4745
4800
  if (cancelled) return;
4746
- setData(res.data);
4747
- const rawTotal = res.total;
4748
- const rawPerPage = res.per_page;
4749
- const lastPage = Math.ceil(rawTotal / rawPerPage);
4750
- const pg = res.pagination ?? {
4751
- first_page_url: res.first_page_url ?? `${url}?page=1`,
4752
- last_page_url: res.last_page_url ?? `${url}?page=${lastPage}`,
4753
- next_page_url: res.next_page_url !== void 0 ? res.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
4754
- prev_page_url: res.prev_page_url !== void 0 ? res.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
4801
+ const payload = encrypt ? decryptLaravelPayload(res) : res;
4802
+ setData(payload.data);
4803
+ const rawTotal = payload.total;
4804
+ const rawPerPage = payload.per_page;
4805
+ const rawLastPage = payload.last_page;
4806
+ const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
4807
+ const pg = payload.pagination ?? {
4808
+ first_page_url: payload.first_page_url ?? `${url}?page=1`,
4809
+ last_page_url: payload.last_page_url ?? `${url}?page=${lastPage}`,
4810
+ last_page: lastPage,
4811
+ next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
4812
+ prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
4755
4813
  per_page: rawPerPage,
4756
4814
  total: rawTotal,
4757
- links: res.links ?? []
4815
+ links: payload.links ?? []
4758
4816
  };
4759
4817
  setPagination(pg);
4760
- if (res.data.length > 0) {
4818
+ if (payload.data.length > 0) {
4761
4819
  setColumns(
4762
- Object.keys(res.data[0]).map((key) => ({
4820
+ Object.keys(payload.data[0]).map((key) => ({
4763
4821
  key,
4764
4822
  header: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
4765
4823
  }))
@@ -4788,23 +4846,26 @@ function useServerDataGrid({ url, params }) {
4788
4846
  };
4789
4847
  }
4790
4848
  function DGModalShell({ title, onClose, children, footer }) {
4791
- return /* @__PURE__ */ jsx28(
4792
- "div",
4793
- {
4794
- className: "fixed inset-0 z-50 flex items-center justify-center p-4",
4795
- style: { background: "rgba(0,0,0,0.5)" },
4796
- onMouseDown: (e) => {
4797
- if (e.target === e.currentTarget) onClose();
4798
- },
4799
- children: /* @__PURE__ */ jsxs27("div", { className: "relative w-full max-w-lg rounded-2xl border border-border bg-card shadow-2xl flex flex-col max-h-[90vh]", children: [
4800
- /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border shrink-0", children: [
4801
- /* @__PURE__ */ jsx28("h2", { className: "text-base font-semibold", children: title }),
4802
- /* @__PURE__ */ jsx28("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx28(X7, { className: "h-4 w-4" }) })
4803
- ] }),
4804
- /* @__PURE__ */ jsx28("div", { className: "overflow-y-auto px-6 py-4 flex-1", children }),
4805
- footer && /* @__PURE__ */ jsx28("div", { className: "px-6 py-4 border-t border-border shrink-0 flex justify-end gap-2", children: footer })
4806
- ] })
4807
- }
4849
+ return createPortal2(
4850
+ /* @__PURE__ */ jsx28(
4851
+ "div",
4852
+ {
4853
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
4854
+ style: { background: "rgba(0,0,0,0.5)" },
4855
+ onMouseDown: (e) => {
4856
+ if (e.target === e.currentTarget) onClose();
4857
+ },
4858
+ children: /* @__PURE__ */ jsxs27("div", { className: "relative w-full max-w-lg rounded-2xl border border-border bg-card shadow-2xl flex flex-col max-h-[90vh]", children: [
4859
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border shrink-0", children: [
4860
+ /* @__PURE__ */ jsx28("h2", { className: "text-base font-semibold", children: title }),
4861
+ /* @__PURE__ */ jsx28("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx28(X7, { className: "h-4 w-4" }) })
4862
+ ] }),
4863
+ /* @__PURE__ */ jsx28("div", { className: "overflow-y-auto px-6 py-4 flex-1", children }),
4864
+ footer && /* @__PURE__ */ jsx28("div", { className: "px-6 py-4 border-t border-border shrink-0 flex justify-end gap-2", children: footer })
4865
+ ] })
4866
+ }
4867
+ ),
4868
+ document.body
4808
4869
  );
4809
4870
  }
4810
4871
  function DGFieldRenderer({ field, value, onChange }) {
@@ -5118,7 +5179,10 @@ function DataGrid({
5118
5179
  )
5119
5180
  ] })
5120
5181
  } : null;
5121
- const visibleCols = [
5182
+ const visibleCols = defaultActions?.position === "first" ? [
5183
+ ...actionsCol ? [actionsCol] : [],
5184
+ ...columns.filter((c) => !hiddenCols.includes(String(c.key)))
5185
+ ] : [
5122
5186
  ...columns.filter((c) => !hiddenCols.includes(String(c.key))),
5123
5187
  ...actionsCol ? [actionsCol] : []
5124
5188
  ];
@@ -5258,7 +5322,7 @@ function DataGrid({
5258
5322
  ),
5259
5323
  serverPagination && (() => {
5260
5324
  const { pagination, currentPage: cp, goToPage } = serverPagination;
5261
- const totalServerPages = Math.ceil(pagination.total / pagination.per_page);
5325
+ const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
5262
5326
  const pageLinks = Array.from({ length: totalServerPages }, (_, i) => i + 1);
5263
5327
  return /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
5264
5328
  /* @__PURE__ */ jsxs27("span", { className: "text-xs text-muted-foreground", children: [
@@ -9369,10 +9433,11 @@ function Stepper({
9369
9433
 
9370
9434
  // src/components/ui/table.tsx
9371
9435
  import * as React44 from "react";
9436
+ import { createPortal as createPortal4 } from "react-dom";
9372
9437
  import axios2 from "axios";
9373
9438
  import { ChevronLeft as ChevronLeft6, ChevronRight as ChevronRight9, Search as Search5, Trash2 as Trash23, ChevronsUpDown as ChevronsUpDown2, ChevronUp as ChevronUp2, ChevronDown as ChevronDown7, X as X13, Eye as Eye2, Pencil as Pencil2, Trash as Trash3, Loader2 as Loader22 } from "lucide-react";
9374
9439
  import { Fragment as Fragment15, jsx as jsx55, jsxs as jsxs48 } from "react/jsx-runtime";
9375
- function useServerTable({ url, params }) {
9440
+ function useServerTable({ url, params, encrypt }) {
9376
9441
  const [data, setData] = React44.useState([]);
9377
9442
  const [columns, setColumns] = React44.useState([]);
9378
9443
  const [currentPage, setCurrentPage] = React44.useState(1);
@@ -9388,23 +9453,26 @@ function useServerTable({ url, params }) {
9388
9453
  params: { ...params, page: currentPage }
9389
9454
  }).then(({ data: res }) => {
9390
9455
  if (cancelled) return;
9391
- setData(res.data);
9392
- const rawTotal = res.total;
9393
- const rawPerPage = res.per_page;
9394
- const lastPage = Math.ceil(rawTotal / rawPerPage);
9395
- const pg = res.pagination ?? {
9396
- first_page_url: res.first_page_url ?? `${url}?page=1`,
9397
- last_page_url: res.last_page_url ?? `${url}?page=${lastPage}`,
9398
- next_page_url: res.next_page_url !== void 0 ? res.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
9399
- prev_page_url: res.prev_page_url !== void 0 ? res.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
9456
+ const payload = encrypt ? decryptLaravelPayload(res) : res;
9457
+ setData(payload.data);
9458
+ const rawTotal = payload.total;
9459
+ const rawPerPage = payload.per_page;
9460
+ const rawLastPage = payload.last_page;
9461
+ const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
9462
+ const pg = payload.pagination ?? {
9463
+ first_page_url: payload.first_page_url ?? `${url}?page=1`,
9464
+ last_page_url: payload.last_page_url ?? `${url}?page=${lastPage}`,
9465
+ last_page: lastPage,
9466
+ next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
9467
+ prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
9400
9468
  per_page: rawPerPage,
9401
9469
  total: rawTotal,
9402
- links: res.links ?? []
9470
+ links: payload.links ?? []
9403
9471
  };
9404
9472
  setPagination(pg);
9405
- if (res.data.length > 0) {
9473
+ if (payload.data.length > 0) {
9406
9474
  setColumns(
9407
- Object.keys(res.data[0]).map((key) => ({
9475
+ Object.keys(payload.data[0]).map((key) => ({
9408
9476
  key,
9409
9477
  title: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
9410
9478
  }))
@@ -9433,23 +9501,26 @@ function useServerTable({ url, params }) {
9433
9501
  };
9434
9502
  }
9435
9503
  function ModalShell({ title, onClose, children, footer }) {
9436
- return /* @__PURE__ */ jsx55(
9437
- "div",
9438
- {
9439
- className: "fixed inset-0 z-50 flex items-center justify-center p-4",
9440
- style: { background: "rgba(0,0,0,0.5)" },
9441
- onMouseDown: (e) => {
9442
- if (e.target === e.currentTarget) onClose();
9443
- },
9444
- children: /* @__PURE__ */ jsxs48("div", { className: "relative w-full max-w-lg rounded-2xl border border-border bg-card shadow-2xl flex flex-col max-h-[90vh]", children: [
9445
- /* @__PURE__ */ jsxs48("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border shrink-0", children: [
9446
- /* @__PURE__ */ jsx55("h2", { className: "text-base font-semibold", children: title }),
9447
- /* @__PURE__ */ jsx55("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx55(X13, { className: "h-4 w-4" }) })
9448
- ] }),
9449
- /* @__PURE__ */ jsx55("div", { className: "overflow-y-auto px-6 py-4 flex-1", children }),
9450
- footer && /* @__PURE__ */ jsx55("div", { className: "px-6 py-4 border-t border-border shrink-0 flex justify-end gap-2", children: footer })
9451
- ] })
9452
- }
9504
+ return createPortal4(
9505
+ /* @__PURE__ */ jsx55(
9506
+ "div",
9507
+ {
9508
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
9509
+ style: { background: "rgba(0,0,0,0.5)" },
9510
+ onMouseDown: (e) => {
9511
+ if (e.target === e.currentTarget) onClose();
9512
+ },
9513
+ children: /* @__PURE__ */ jsxs48("div", { className: "relative w-full max-w-lg rounded-2xl border border-border bg-card shadow-2xl flex flex-col max-h-[90vh]", children: [
9514
+ /* @__PURE__ */ jsxs48("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border shrink-0", children: [
9515
+ /* @__PURE__ */ jsx55("h2", { className: "text-base font-semibold", children: title }),
9516
+ /* @__PURE__ */ jsx55("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx55(X13, { className: "h-4 w-4" }) })
9517
+ ] }),
9518
+ /* @__PURE__ */ jsx55("div", { className: "overflow-y-auto px-6 py-4 flex-1", children }),
9519
+ footer && /* @__PURE__ */ jsx55("div", { className: "px-6 py-4 border-t border-border shrink-0 flex justify-end gap-2", children: footer })
9520
+ ] })
9521
+ }
9522
+ ),
9523
+ document.body
9453
9524
  );
9454
9525
  }
9455
9526
  function FieldRenderer({ field, value, onChange }) {
@@ -9792,7 +9863,7 @@ function Table({
9792
9863
  )
9793
9864
  ] })
9794
9865
  };
9795
- return [...columns, actionsCol];
9866
+ return defaultActions.position === "first" ? [actionsCol, ...columns] : [...columns, actionsCol];
9796
9867
  }, [columns, defaultActions]);
9797
9868
  const handleSort = (key) => {
9798
9869
  if (sortKey !== key) {
@@ -9888,9 +9959,9 @@ function Table({
9888
9959
  }
9889
9960
  ),
9890
9961
  /* @__PURE__ */ jsxs48("span", { className: "text-xs text-muted-foreground", children: [
9891
- filteredData.length,
9962
+ serverPagination ? serverPagination.pagination.total : filteredData.length,
9892
9963
  " ",
9893
- filteredData.length === 1 ? "row" : "rows",
9964
+ (serverPagination ? serverPagination.pagination.total : filteredData.length) === 1 ? "row" : "rows",
9894
9965
  search && ` \xB7 filtered from ${tableData.length}`
9895
9966
  ] })
9896
9967
  ] })
@@ -10064,7 +10135,7 @@ function Table({
10064
10135
  ] }),
10065
10136
  serverPagination && (() => {
10066
10137
  const { pagination: pagination2, currentPage: cp, goToPage } = serverPagination;
10067
- const totalServerPages = Math.ceil(pagination2.total / pagination2.per_page);
10138
+ const totalServerPages = pagination2.last_page ?? Math.ceil(pagination2.total / pagination2.per_page);
10068
10139
  const pageLinks = Array.from({ length: totalServerPages }, (_, i) => i + 1);
10069
10140
  return /* @__PURE__ */ jsxs48("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
10070
10141
  /* @__PURE__ */ jsxs48("span", { className: "text-xs text-muted-foreground", children: [
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "registry": "https://registry.npmjs.org/",
5
5
  "access": "public"
6
6
  },
7
- "version": "3.0.8",
7
+ "version": "3.1.1",
8
8
  "description": "Reusable React UI components",
9
9
  "license": "MIT",
10
10
  "main": "dist/index.js",
@@ -31,8 +31,9 @@
31
31
  "@types/leaflet": "^1.9.21",
32
32
  "@types/leaflet.markercluster": "^1.5.6",
33
33
  "@vitejs/plugin-react": "^5.0.4",
34
- "axios": "^1.13.6",
34
+ "axios": ">=1",
35
35
  "clsx": "^2.1.1",
36
+ "crypto-js": "^4.2.0",
36
37
  "date-fns": "^4.1.0",
37
38
  "leaflet": "^1.9.4",
38
39
  "leaflet.markercluster": "^1.5.3",