@juv/codego-react-ui 3.1.0 → 3.1.2

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
@@ -1997,6 +1997,60 @@ function MetricRow({ items, divided = true, className }) {
1997
1997
  // src/components/ui/data-grid.tsx
1998
1998
  import * as React24 from "react";
1999
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
2000
2054
  import axios from "axios";
2001
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";
2002
2056
 
@@ -4730,7 +4784,7 @@ Input.displayName = "Input";
4730
4784
 
4731
4785
  // src/components/ui/data-grid.tsx
4732
4786
  import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
4733
- function useServerDataGrid({ url, params }) {
4787
+ function useServerDataGrid({ url, params, encrypt }) {
4734
4788
  const [data, setData] = React24.useState([]);
4735
4789
  const [columns, setColumns] = React24.useState([]);
4736
4790
  const [currentPage, setCurrentPage] = React24.useState(1);
@@ -4744,25 +4798,26 @@ function useServerDataGrid({ url, params }) {
4744
4798
  setError(null);
4745
4799
  axios.get(url, { params: { ...params, page: currentPage } }).then(({ data: res }) => {
4746
4800
  if (cancelled) return;
4747
- setData(res.data);
4748
- const rawTotal = res.total;
4749
- const rawPerPage = res.per_page;
4750
- const rawLastPage = res.last_page;
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;
4751
4806
  const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
4752
- const pg = res.pagination ?? {
4753
- first_page_url: res.first_page_url ?? `${url}?page=1`,
4754
- last_page_url: res.last_page_url ?? `${url}?page=${lastPage}`,
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}`,
4755
4810
  last_page: lastPage,
4756
- next_page_url: res.next_page_url !== void 0 ? res.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
4757
- prev_page_url: res.prev_page_url !== void 0 ? res.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
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,
4758
4813
  per_page: rawPerPage,
4759
4814
  total: rawTotal,
4760
- links: res.links ?? []
4815
+ links: payload.links ?? []
4761
4816
  };
4762
4817
  setPagination(pg);
4763
- if (res.data.length > 0) {
4818
+ if (payload.data.length > 0) {
4764
4819
  setColumns(
4765
- Object.keys(res.data[0]).map((key) => ({
4820
+ Object.keys(payload.data[0]).map((key) => ({
4766
4821
  key,
4767
4822
  header: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
4768
4823
  }))
@@ -5023,9 +5078,9 @@ function DataGrid({
5023
5078
  const [viewItem, setViewItem] = React24.useState(null);
5024
5079
  const [editItem, setEditItem] = React24.useState(null);
5025
5080
  const [deleteItem, setDeleteItem] = React24.useState(null);
5026
- const [tableData, setTableData] = React24.useState(data);
5081
+ const [tableData, setTableData] = React24.useState(data ?? []);
5027
5082
  React24.useEffect(() => {
5028
- setTableData(data);
5083
+ setTableData(data ?? []);
5029
5084
  }, [data]);
5030
5085
  const actionIdKey = defaultActions?.idKey ?? (typeof rowKey === "string" ? rowKey : "id");
5031
5086
  const autoFields = React24.useMemo(() => {
@@ -5070,7 +5125,7 @@ function DataGrid({
5070
5125
  document.addEventListener("mousedown", handler);
5071
5126
  return () => document.removeEventListener("mousedown", handler);
5072
5127
  }, []);
5073
- let processed = data.filter(
5128
+ let processed = (data ?? []).filter(
5074
5129
  (row) => Object.entries(filters).every(([k, v]) => {
5075
5130
  if (!v) return true;
5076
5131
  const cell = String(row[k] ?? "").toLowerCase();
@@ -5268,7 +5323,16 @@ function DataGrid({
5268
5323
  serverPagination && (() => {
5269
5324
  const { pagination, currentPage: cp, goToPage } = serverPagination;
5270
5325
  const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
5271
- const pageLinks = Array.from({ length: totalServerPages }, (_, i) => i + 1);
5326
+ const pills = [];
5327
+ if (totalServerPages <= 7) {
5328
+ for (let i = 1; i <= totalServerPages; i++) pills.push(i);
5329
+ } else if (cp <= 4) {
5330
+ pills.push(1, 2, 3, 4, 5, -1, totalServerPages);
5331
+ } else if (cp >= totalServerPages - 3) {
5332
+ pills.push(1, -1, totalServerPages - 4, totalServerPages - 3, totalServerPages - 2, totalServerPages - 1, totalServerPages);
5333
+ } else {
5334
+ pills.push(1, -1, cp - 1, cp, cp + 1, -2, totalServerPages);
5335
+ }
5272
5336
  return /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
5273
5337
  /* @__PURE__ */ jsxs27("span", { className: "text-xs text-muted-foreground", children: [
5274
5338
  pagination.total,
@@ -5287,18 +5351,20 @@ function DataGrid({
5287
5351
  children: /* @__PURE__ */ jsx28(ChevronLeft5, { className: "h-4 w-4" })
5288
5352
  }
5289
5353
  ),
5290
- pageLinks.map((p) => /* @__PURE__ */ jsx28(
5291
- "button",
5292
- {
5293
- onClick: () => goToPage(p),
5294
- className: cn(
5295
- "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
5296
- p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
5297
- ),
5298
- children: p
5299
- },
5300
- p
5301
- )),
5354
+ pills.map(
5355
+ (p, i) => p < 0 ? /* @__PURE__ */ jsx28("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" }, p - i) : /* @__PURE__ */ jsx28(
5356
+ "button",
5357
+ {
5358
+ onClick: () => goToPage(p),
5359
+ className: cn(
5360
+ "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
5361
+ p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
5362
+ ),
5363
+ children: p
5364
+ },
5365
+ p
5366
+ )
5367
+ ),
5302
5368
  /* @__PURE__ */ jsx28(
5303
5369
  "button",
5304
5370
  {
@@ -9382,7 +9448,7 @@ import { createPortal as createPortal4 } from "react-dom";
9382
9448
  import axios2 from "axios";
9383
9449
  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";
9384
9450
  import { Fragment as Fragment15, jsx as jsx55, jsxs as jsxs48 } from "react/jsx-runtime";
9385
- function useServerTable({ url, params }) {
9451
+ function useServerTable({ url, params, encrypt }) {
9386
9452
  const [data, setData] = React44.useState([]);
9387
9453
  const [columns, setColumns] = React44.useState([]);
9388
9454
  const [currentPage, setCurrentPage] = React44.useState(1);
@@ -9398,25 +9464,26 @@ function useServerTable({ url, params }) {
9398
9464
  params: { ...params, page: currentPage }
9399
9465
  }).then(({ data: res }) => {
9400
9466
  if (cancelled) return;
9401
- setData(res.data);
9402
- const rawTotal = res.total;
9403
- const rawPerPage = res.per_page;
9404
- const rawLastPage = res.last_page;
9467
+ const payload = encrypt ? decryptLaravelPayload(res) : res;
9468
+ setData(payload.data);
9469
+ const rawTotal = payload.total;
9470
+ const rawPerPage = payload.per_page;
9471
+ const rawLastPage = payload.last_page;
9405
9472
  const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
9406
- const pg = res.pagination ?? {
9407
- first_page_url: res.first_page_url ?? `${url}?page=1`,
9408
- last_page_url: res.last_page_url ?? `${url}?page=${lastPage}`,
9473
+ const pg = payload.pagination ?? {
9474
+ first_page_url: payload.first_page_url ?? `${url}?page=1`,
9475
+ last_page_url: payload.last_page_url ?? `${url}?page=${lastPage}`,
9409
9476
  last_page: lastPage,
9410
- next_page_url: res.next_page_url !== void 0 ? res.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
9411
- prev_page_url: res.prev_page_url !== void 0 ? res.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
9477
+ next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
9478
+ prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
9412
9479
  per_page: rawPerPage,
9413
9480
  total: rawTotal,
9414
- links: res.links ?? []
9481
+ links: payload.links ?? []
9415
9482
  };
9416
9483
  setPagination(pg);
9417
- if (res.data.length > 0) {
9484
+ if (payload.data.length > 0) {
9418
9485
  setColumns(
9419
- Object.keys(res.data[0]).map((key) => ({
9486
+ Object.keys(payload.data[0]).map((key) => ({
9420
9487
  key,
9421
9488
  title: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
9422
9489
  }))
@@ -9755,9 +9822,9 @@ function Table({
9755
9822
  const [viewItem, setViewItem] = React44.useState(null);
9756
9823
  const [editItem, setEditItem] = React44.useState(null);
9757
9824
  const [deleteItem, setDeleteItem] = React44.useState(null);
9758
- const [tableData, setTableData] = React44.useState(data);
9825
+ const [tableData, setTableData] = React44.useState(data ?? []);
9759
9826
  React44.useEffect(() => {
9760
- setTableData(data);
9827
+ setTableData(data ?? []);
9761
9828
  }, [data]);
9762
9829
  const actionIdKey = defaultActions?.idKey ?? idKey;
9763
9830
  const autoFields = React44.useMemo(() => {
@@ -10080,7 +10147,16 @@ function Table({
10080
10147
  serverPagination && (() => {
10081
10148
  const { pagination: pagination2, currentPage: cp, goToPage } = serverPagination;
10082
10149
  const totalServerPages = pagination2.last_page ?? Math.ceil(pagination2.total / pagination2.per_page);
10083
- const pageLinks = Array.from({ length: totalServerPages }, (_, i) => i + 1);
10150
+ const pills = [];
10151
+ if (totalServerPages <= 7) {
10152
+ for (let i = 1; i <= totalServerPages; i++) pills.push(i);
10153
+ } else if (cp <= 4) {
10154
+ pills.push(1, 2, 3, 4, 5, -1, totalServerPages);
10155
+ } else if (cp >= totalServerPages - 3) {
10156
+ pills.push(1, -1, totalServerPages - 4, totalServerPages - 3, totalServerPages - 2, totalServerPages - 1, totalServerPages);
10157
+ } else {
10158
+ pills.push(1, -1, cp - 1, cp, cp + 1, -2, totalServerPages);
10159
+ }
10084
10160
  return /* @__PURE__ */ jsxs48("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
10085
10161
  /* @__PURE__ */ jsxs48("span", { className: "text-xs text-muted-foreground", children: [
10086
10162
  pagination2.total,
@@ -10099,18 +10175,20 @@ function Table({
10099
10175
  children: /* @__PURE__ */ jsx55(ChevronLeft6, { className: "h-4 w-4" })
10100
10176
  }
10101
10177
  ),
10102
- pageLinks.map((p) => /* @__PURE__ */ jsx55(
10103
- "button",
10104
- {
10105
- onClick: () => goToPage(p),
10106
- className: cn(
10107
- "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
10108
- p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
10109
- ),
10110
- children: p
10111
- },
10112
- p
10113
- )),
10178
+ pills.map(
10179
+ (p, i) => p < 0 ? /* @__PURE__ */ jsx55("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" }, p - i) : /* @__PURE__ */ jsx55(
10180
+ "button",
10181
+ {
10182
+ onClick: () => goToPage(p),
10183
+ className: cn(
10184
+ "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
10185
+ p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
10186
+ ),
10187
+ children: p
10188
+ },
10189
+ p
10190
+ )
10191
+ ),
10114
10192
  /* @__PURE__ */ jsx55(
10115
10193
  "button",
10116
10194
  {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "registry": "https://registry.npmjs.org/",
5
5
  "access": "public"
6
6
  },
7
- "version": "3.1.0",
7
+ "version": "3.1.2",
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",