@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.cjs CHANGED
@@ -2128,6 +2128,61 @@ function MetricRow({ items, divided = true, className }) {
2128
2128
  // src/components/ui/data-grid.tsx
2129
2129
  var React24 = __toESM(require("react"), 1);
2130
2130
  var import_react_dom = require("react-dom");
2131
+
2132
+ // src/components/tools/decryptPayload.ts
2133
+ var import_crypto_js = __toESM(require("crypto-js"), 1);
2134
+ var import_meta = {};
2135
+ function getLaravelSecretKey() {
2136
+ const viteKey = import_meta.env["VITE_LARAVEL_KEY"];
2137
+ const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
2138
+ const key = viteKey || legacyKey;
2139
+ if (!key) {
2140
+ throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env.");
2141
+ }
2142
+ return key;
2143
+ }
2144
+ function parseLaravelKey(secretKey) {
2145
+ if (secretKey.startsWith("base64:")) {
2146
+ return import_crypto_js.default.enc.Base64.parse(secretKey.slice("base64:".length));
2147
+ }
2148
+ return import_crypto_js.default.enc.Utf8.parse(secretKey);
2149
+ }
2150
+ function parseLaravelEncryptedPayload(payload) {
2151
+ let jsonStr = payload;
2152
+ try {
2153
+ jsonStr = atob(payload);
2154
+ } catch {
2155
+ }
2156
+ return JSON.parse(jsonStr);
2157
+ }
2158
+ function decryptLaravelPayload(payload) {
2159
+ const secretKey = getLaravelSecretKey();
2160
+ const parsed = parseLaravelEncryptedPayload(payload);
2161
+ if (parsed.tag) {
2162
+ throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
2163
+ }
2164
+ const key = parseLaravelKey(secretKey);
2165
+ const expectedMac = import_crypto_js.default.HmacSHA256(parsed.iv + parsed.value, key).toString();
2166
+ if (expectedMac !== parsed.mac) {
2167
+ throw new Error("Invalid payload MAC (wrong key or tampered payload).");
2168
+ }
2169
+ const iv = import_crypto_js.default.enc.Base64.parse(parsed.iv);
2170
+ const ciphertext = import_crypto_js.default.enc.Base64.parse(parsed.value);
2171
+ const cipherParams = import_crypto_js.default.lib.CipherParams.create({ ciphertext });
2172
+ const decrypted = import_crypto_js.default.AES.decrypt(cipherParams, key, {
2173
+ iv,
2174
+ mode: import_crypto_js.default.mode.CBC,
2175
+ padding: import_crypto_js.default.pad.Pkcs7
2176
+ });
2177
+ const plaintext = decrypted.toString(import_crypto_js.default.enc.Utf8);
2178
+ if (!plaintext) {
2179
+ throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
2180
+ }
2181
+ console.log("Decrypted payload:", plaintext);
2182
+ return JSON.parse(plaintext);
2183
+ }
2184
+
2185
+ // src/components/ui/data-grid.tsx
2131
2186
  var import_axios = __toESM(require("axios"), 1);
2132
2187
  var import_lucide_react15 = require("lucide-react");
2133
2188
 
@@ -4844,7 +4899,7 @@ Input.displayName = "Input";
4844
4899
 
4845
4900
  // src/components/ui/data-grid.tsx
4846
4901
  var import_jsx_runtime28 = require("react/jsx-runtime");
4847
- function useServerDataGrid({ url, params }) {
4902
+ function useServerDataGrid({ url, params, encrypt }) {
4848
4903
  const [data, setData] = React24.useState([]);
4849
4904
  const [columns, setColumns] = React24.useState([]);
4850
4905
  const [currentPage, setCurrentPage] = React24.useState(1);
@@ -4858,25 +4913,26 @@ function useServerDataGrid({ url, params }) {
4858
4913
  setError(null);
4859
4914
  import_axios.default.get(url, { params: { ...params, page: currentPage } }).then(({ data: res }) => {
4860
4915
  if (cancelled) return;
4861
- setData(res.data);
4862
- const rawTotal = res.total;
4863
- const rawPerPage = res.per_page;
4864
- const rawLastPage = res.last_page;
4916
+ const payload = encrypt ? decryptLaravelPayload(res) : res;
4917
+ setData(payload.data);
4918
+ const rawTotal = payload.total;
4919
+ const rawPerPage = payload.per_page;
4920
+ const rawLastPage = payload.last_page;
4865
4921
  const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
4866
- const pg = res.pagination ?? {
4867
- first_page_url: res.first_page_url ?? `${url}?page=1`,
4868
- last_page_url: res.last_page_url ?? `${url}?page=${lastPage}`,
4922
+ const pg = payload.pagination ?? {
4923
+ first_page_url: payload.first_page_url ?? `${url}?page=1`,
4924
+ last_page_url: payload.last_page_url ?? `${url}?page=${lastPage}`,
4869
4925
  last_page: lastPage,
4870
- next_page_url: res.next_page_url !== void 0 ? res.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
4871
- prev_page_url: res.prev_page_url !== void 0 ? res.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
4926
+ next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
4927
+ prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
4872
4928
  per_page: rawPerPage,
4873
4929
  total: rawTotal,
4874
- links: res.links ?? []
4930
+ links: payload.links ?? []
4875
4931
  };
4876
4932
  setPagination(pg);
4877
- if (res.data.length > 0) {
4933
+ if (payload.data.length > 0) {
4878
4934
  setColumns(
4879
- Object.keys(res.data[0]).map((key) => ({
4935
+ Object.keys(payload.data[0]).map((key) => ({
4880
4936
  key,
4881
4937
  header: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
4882
4938
  }))
@@ -5137,9 +5193,9 @@ function DataGrid({
5137
5193
  const [viewItem, setViewItem] = React24.useState(null);
5138
5194
  const [editItem, setEditItem] = React24.useState(null);
5139
5195
  const [deleteItem, setDeleteItem] = React24.useState(null);
5140
- const [tableData, setTableData] = React24.useState(data);
5196
+ const [tableData, setTableData] = React24.useState(data ?? []);
5141
5197
  React24.useEffect(() => {
5142
- setTableData(data);
5198
+ setTableData(data ?? []);
5143
5199
  }, [data]);
5144
5200
  const actionIdKey = defaultActions?.idKey ?? (typeof rowKey === "string" ? rowKey : "id");
5145
5201
  const autoFields = React24.useMemo(() => {
@@ -5184,7 +5240,7 @@ function DataGrid({
5184
5240
  document.addEventListener("mousedown", handler);
5185
5241
  return () => document.removeEventListener("mousedown", handler);
5186
5242
  }, []);
5187
- let processed = data.filter(
5243
+ let processed = (data ?? []).filter(
5188
5244
  (row) => Object.entries(filters).every(([k, v]) => {
5189
5245
  if (!v) return true;
5190
5246
  const cell = String(row[k] ?? "").toLowerCase();
@@ -5382,7 +5438,16 @@ function DataGrid({
5382
5438
  serverPagination && (() => {
5383
5439
  const { pagination, currentPage: cp, goToPage } = serverPagination;
5384
5440
  const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
5385
- const pageLinks = Array.from({ length: totalServerPages }, (_, i) => i + 1);
5441
+ const pills = [];
5442
+ if (totalServerPages <= 7) {
5443
+ for (let i = 1; i <= totalServerPages; i++) pills.push(i);
5444
+ } else if (cp <= 4) {
5445
+ pills.push(1, 2, 3, 4, 5, -1, totalServerPages);
5446
+ } else if (cp >= totalServerPages - 3) {
5447
+ pills.push(1, -1, totalServerPages - 4, totalServerPages - 3, totalServerPages - 2, totalServerPages - 1, totalServerPages);
5448
+ } else {
5449
+ pills.push(1, -1, cp - 1, cp, cp + 1, -2, totalServerPages);
5450
+ }
5386
5451
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
5387
5452
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
5388
5453
  pagination.total,
@@ -5401,18 +5466,20 @@ function DataGrid({
5401
5466
  children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react15.ChevronLeft, { className: "h-4 w-4" })
5402
5467
  }
5403
5468
  ),
5404
- pageLinks.map((p) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
5405
- "button",
5406
- {
5407
- onClick: () => goToPage(p),
5408
- className: cn(
5409
- "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
5410
- p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
5411
- ),
5412
- children: p
5413
- },
5414
- p
5415
- )),
5469
+ pills.map(
5470
+ (p, i) => p < 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" }, p - i) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
5471
+ "button",
5472
+ {
5473
+ onClick: () => goToPage(p),
5474
+ className: cn(
5475
+ "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
5476
+ p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
5477
+ ),
5478
+ children: p
5479
+ },
5480
+ p
5481
+ )
5482
+ ),
5416
5483
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
5417
5484
  "button",
5418
5485
  {
@@ -9496,7 +9563,7 @@ var import_react_dom2 = require("react-dom");
9496
9563
  var import_axios2 = __toESM(require("axios"), 1);
9497
9564
  var import_lucide_react28 = require("lucide-react");
9498
9565
  var import_jsx_runtime55 = require("react/jsx-runtime");
9499
- function useServerTable({ url, params }) {
9566
+ function useServerTable({ url, params, encrypt }) {
9500
9567
  const [data, setData] = React44.useState([]);
9501
9568
  const [columns, setColumns] = React44.useState([]);
9502
9569
  const [currentPage, setCurrentPage] = React44.useState(1);
@@ -9512,25 +9579,26 @@ function useServerTable({ url, params }) {
9512
9579
  params: { ...params, page: currentPage }
9513
9580
  }).then(({ data: res }) => {
9514
9581
  if (cancelled) return;
9515
- setData(res.data);
9516
- const rawTotal = res.total;
9517
- const rawPerPage = res.per_page;
9518
- const rawLastPage = res.last_page;
9582
+ const payload = encrypt ? decryptLaravelPayload(res) : res;
9583
+ setData(payload.data);
9584
+ const rawTotal = payload.total;
9585
+ const rawPerPage = payload.per_page;
9586
+ const rawLastPage = payload.last_page;
9519
9587
  const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
9520
- const pg = res.pagination ?? {
9521
- first_page_url: res.first_page_url ?? `${url}?page=1`,
9522
- last_page_url: res.last_page_url ?? `${url}?page=${lastPage}`,
9588
+ const pg = payload.pagination ?? {
9589
+ first_page_url: payload.first_page_url ?? `${url}?page=1`,
9590
+ last_page_url: payload.last_page_url ?? `${url}?page=${lastPage}`,
9523
9591
  last_page: lastPage,
9524
- next_page_url: res.next_page_url !== void 0 ? res.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
9525
- prev_page_url: res.prev_page_url !== void 0 ? res.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
9592
+ next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
9593
+ prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
9526
9594
  per_page: rawPerPage,
9527
9595
  total: rawTotal,
9528
- links: res.links ?? []
9596
+ links: payload.links ?? []
9529
9597
  };
9530
9598
  setPagination(pg);
9531
- if (res.data.length > 0) {
9599
+ if (payload.data.length > 0) {
9532
9600
  setColumns(
9533
- Object.keys(res.data[0]).map((key) => ({
9601
+ Object.keys(payload.data[0]).map((key) => ({
9534
9602
  key,
9535
9603
  title: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
9536
9604
  }))
@@ -9869,9 +9937,9 @@ function Table({
9869
9937
  const [viewItem, setViewItem] = React44.useState(null);
9870
9938
  const [editItem, setEditItem] = React44.useState(null);
9871
9939
  const [deleteItem, setDeleteItem] = React44.useState(null);
9872
- const [tableData, setTableData] = React44.useState(data);
9940
+ const [tableData, setTableData] = React44.useState(data ?? []);
9873
9941
  React44.useEffect(() => {
9874
- setTableData(data);
9942
+ setTableData(data ?? []);
9875
9943
  }, [data]);
9876
9944
  const actionIdKey = defaultActions?.idKey ?? idKey;
9877
9945
  const autoFields = React44.useMemo(() => {
@@ -10194,7 +10262,16 @@ function Table({
10194
10262
  serverPagination && (() => {
10195
10263
  const { pagination: pagination2, currentPage: cp, goToPage } = serverPagination;
10196
10264
  const totalServerPages = pagination2.last_page ?? Math.ceil(pagination2.total / pagination2.per_page);
10197
- const pageLinks = Array.from({ length: totalServerPages }, (_, i) => i + 1);
10265
+ const pills = [];
10266
+ if (totalServerPages <= 7) {
10267
+ for (let i = 1; i <= totalServerPages; i++) pills.push(i);
10268
+ } else if (cp <= 4) {
10269
+ pills.push(1, 2, 3, 4, 5, -1, totalServerPages);
10270
+ } else if (cp >= totalServerPages - 3) {
10271
+ pills.push(1, -1, totalServerPages - 4, totalServerPages - 3, totalServerPages - 2, totalServerPages - 1, totalServerPages);
10272
+ } else {
10273
+ pills.push(1, -1, cp - 1, cp, cp + 1, -2, totalServerPages);
10274
+ }
10198
10275
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
10199
10276
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
10200
10277
  pagination2.total,
@@ -10213,18 +10290,20 @@ function Table({
10213
10290
  children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_lucide_react28.ChevronLeft, { className: "h-4 w-4" })
10214
10291
  }
10215
10292
  ),
10216
- pageLinks.map((p) => /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
10217
- "button",
10218
- {
10219
- onClick: () => goToPage(p),
10220
- className: cn(
10221
- "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
10222
- p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
10223
- ),
10224
- children: p
10225
- },
10226
- p
10227
- )),
10293
+ pills.map(
10294
+ (p, i) => p < 0 ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" }, p - i) : /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
10295
+ "button",
10296
+ {
10297
+ onClick: () => goToPage(p),
10298
+ className: cn(
10299
+ "flex h-8 w-8 items-center justify-center rounded-lg border text-xs font-medium transition-colors",
10300
+ p === cp ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
10301
+ ),
10302
+ children: p
10303
+ },
10304
+ p
10305
+ )
10306
+ ),
10228
10307
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
10229
10308
  "button",
10230
10309
  {
package/dist/index.d.cts CHANGED
@@ -361,6 +361,8 @@ interface UseServerTableOptions {
361
361
  url: string;
362
362
  /** Extra query params merged on every request */
363
363
  params?: Record<string, string | number>;
364
+ /** If true, the response is expected to be a Laravel-encrypted payload and will be decrypted using VITE_LARAVEL_KEY */
365
+ encrypt?: boolean;
364
366
  }
365
367
  interface UseServerTableReturn<T> {
366
368
  data: T[];
@@ -378,7 +380,7 @@ interface ServerPaginationProp {
378
380
  currentPage: number;
379
381
  goToPage: (page: number) => void;
380
382
  }
381
- declare function useServerTable<T extends Record<string, any>>({ url, params }: UseServerTableOptions): UseServerTableReturn<T>;
383
+ declare function useServerTable<T extends Record<string, any>>({ url, params, encrypt }: UseServerTableOptions): UseServerTableReturn<T>;
382
384
  type ActionFieldType = "input" | "password" | "textarea" | "checkbox" | "toggle" | "select" | "radio" | "slider" | "tag-input" | "otp" | "combobox" | "color-picker" | "date-range" | "rich-text" | "file-upload" | "repeater";
383
385
  interface ActionField {
384
386
  key: string;
@@ -465,6 +467,8 @@ interface ServerDataGridProp {
465
467
  interface UseServerDataGridOptions {
466
468
  url: string;
467
469
  params?: Record<string, string | number>;
470
+ /** If true, the response is expected to be a Laravel-encrypted payload and will be decrypted using VITE_LARAVEL_KEY */
471
+ encrypt?: boolean;
468
472
  }
469
473
  interface UseServerDataGridReturn<T> {
470
474
  data: T[];
@@ -477,7 +481,7 @@ interface UseServerDataGridReturn<T> {
477
481
  goToPage: (page: number) => void;
478
482
  reload: () => void;
479
483
  }
480
- declare function useServerDataGrid<T extends Record<string, any>>({ url, params }: UseServerDataGridOptions): UseServerDataGridReturn<T>;
484
+ declare function useServerDataGrid<T extends Record<string, any>>({ url, params, encrypt }: UseServerDataGridOptions): UseServerDataGridReturn<T>;
481
485
  type SortDir = "asc" | "desc" | null;
482
486
  interface DataGridColumn<T> {
483
487
  key: keyof T | string;
package/dist/index.d.ts CHANGED
@@ -361,6 +361,8 @@ interface UseServerTableOptions {
361
361
  url: string;
362
362
  /** Extra query params merged on every request */
363
363
  params?: Record<string, string | number>;
364
+ /** If true, the response is expected to be a Laravel-encrypted payload and will be decrypted using VITE_LARAVEL_KEY */
365
+ encrypt?: boolean;
364
366
  }
365
367
  interface UseServerTableReturn<T> {
366
368
  data: T[];
@@ -378,7 +380,7 @@ interface ServerPaginationProp {
378
380
  currentPage: number;
379
381
  goToPage: (page: number) => void;
380
382
  }
381
- declare function useServerTable<T extends Record<string, any>>({ url, params }: UseServerTableOptions): UseServerTableReturn<T>;
383
+ declare function useServerTable<T extends Record<string, any>>({ url, params, encrypt }: UseServerTableOptions): UseServerTableReturn<T>;
382
384
  type ActionFieldType = "input" | "password" | "textarea" | "checkbox" | "toggle" | "select" | "radio" | "slider" | "tag-input" | "otp" | "combobox" | "color-picker" | "date-range" | "rich-text" | "file-upload" | "repeater";
383
385
  interface ActionField {
384
386
  key: string;
@@ -465,6 +467,8 @@ interface ServerDataGridProp {
465
467
  interface UseServerDataGridOptions {
466
468
  url: string;
467
469
  params?: Record<string, string | number>;
470
+ /** If true, the response is expected to be a Laravel-encrypted payload and will be decrypted using VITE_LARAVEL_KEY */
471
+ encrypt?: boolean;
468
472
  }
469
473
  interface UseServerDataGridReturn<T> {
470
474
  data: T[];
@@ -477,7 +481,7 @@ interface UseServerDataGridReturn<T> {
477
481
  goToPage: (page: number) => void;
478
482
  reload: () => void;
479
483
  }
480
- declare function useServerDataGrid<T extends Record<string, any>>({ url, params }: UseServerDataGridOptions): UseServerDataGridReturn<T>;
484
+ declare function useServerDataGrid<T extends Record<string, any>>({ url, params, encrypt }: UseServerDataGridOptions): UseServerDataGridReturn<T>;
481
485
  type SortDir = "asc" | "desc" | null;
482
486
  interface DataGridColumn<T> {
483
487
  key: keyof T | string;