@juv/codego-react-ui 3.5.21 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/dist/index.cjs +89 -136
- package/dist/index.d.cts +39 -23
- package/dist/index.d.ts +39 -23
- package/dist/index.global.js +82 -135
- package/dist/index.js +84 -135
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -619,6 +619,14 @@ function AnnouncementsPage() {
|
|
|
619
619
|
}
|
|
620
620
|
```
|
|
621
621
|
|
|
622
|
+
> **Note:** `useServerBulletin` uses the internal `codego` api client which automatically attaches the Bearer token from `localStorage.getItem("token")`. For encrypted Laravel responses, pass `encrypt: true` and set `VITE_LARAVEL_KEY` in your `.env`:
|
|
623
|
+
>
|
|
624
|
+
> ```
|
|
625
|
+
> VITE_LARAVEL_KEY=base64:your_app_key_here
|
|
626
|
+
> ```
|
|
627
|
+
>
|
|
628
|
+
> Or pass the key directly: `key: "base64:..."`. The response can be a plain array `[...]` or a paginated object `{ data: [...], total, per_page, ... }` — both are handled automatically.
|
|
629
|
+
|
|
622
630
|
---
|
|
623
631
|
|
|
624
632
|
## LeafletMap Props
|
|
@@ -731,6 +739,53 @@ function AnnouncementsPage() {
|
|
|
731
739
|
|
|
732
740
|
---
|
|
733
741
|
|
|
742
|
+
## Laravel Response Decryption
|
|
743
|
+
|
|
744
|
+
Use `decryptResponse` to decrypt Laravel-encrypted API responses (AES-256-CBC).
|
|
745
|
+
|
|
746
|
+
### Setup
|
|
747
|
+
|
|
748
|
+
Add your Laravel `APP_KEY` to `.env`:
|
|
749
|
+
|
|
750
|
+
```env
|
|
751
|
+
VITE_LARAVEL_KEY=base64:your_laravel_app_key_here
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### Usage
|
|
755
|
+
|
|
756
|
+
```tsx
|
|
757
|
+
import { api, decryptResponse } from "@juv/codego-react-ui"
|
|
758
|
+
|
|
759
|
+
type Certificate = { id: number; name: string }
|
|
760
|
+
|
|
761
|
+
const fetchCertificates = async () => {
|
|
762
|
+
const data = await api.get<string>('/certificate')
|
|
763
|
+
const decoded = decryptResponse<Certificate[]>(data)
|
|
764
|
+
console.log(decoded)
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
> `api.get` should be typed as `string` since the raw response is an encrypted payload. `decryptResponse` reads `VITE_LARAVEL_KEY` automatically.
|
|
769
|
+
|
|
770
|
+
You can also pass the key explicitly:
|
|
771
|
+
|
|
772
|
+
```tsx
|
|
773
|
+
const decoded = decryptResponse<Certificate[]>(data, "base64:your_key_here")
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
### API
|
|
777
|
+
|
|
778
|
+
| Export | Description |
|
|
779
|
+
|---|---|
|
|
780
|
+
| `decryptResponse(response, key?)` | Decrypts a Laravel-encrypted string or `{ data: string }` object. |
|
|
781
|
+
| `decryptLaravelPayload(payload, key?)` | Low-level decryption of a raw encrypted payload string. |
|
|
782
|
+
| `getLaravelSecretKey()` | Reads the key from `VITE_LARAVEL_KEY`, `REACT_APP_LARAVEL_KEY`, or `window.__LARAVEL_KEY__`. |
|
|
783
|
+
| `parseLaravelKey(secretKey)` | Parses a `base64:...` or raw key string into a `CryptoJS.WordArray`. |
|
|
784
|
+
| `parseLaravelEncryptedPayload(payload)` | Parses a base64-encoded Laravel encrypted payload into `{ iv, value, mac }`. |
|
|
785
|
+
| `LaravelEncryptedPayload` | Type for the parsed payload object. |
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
734
789
|
## Run Locally
|
|
735
790
|
|
|
736
791
|
**Prerequisites:** Node.js
|
package/dist/index.cjs
CHANGED
|
@@ -127,7 +127,11 @@ __export(index_exports, {
|
|
|
127
127
|
Wizard: () => Wizard,
|
|
128
128
|
api: () => api,
|
|
129
129
|
createStore: () => createStore,
|
|
130
|
-
|
|
130
|
+
decryptLaravelPayload: () => decryptLaravelPayload,
|
|
131
|
+
decryptResponse: () => decryptResponse,
|
|
132
|
+
getLaravelSecretKey: () => getLaravelSecretKey,
|
|
133
|
+
parseLaravelEncryptedPayload: () => parseLaravelEncryptedPayload,
|
|
134
|
+
parseLaravelKey: () => parseLaravelKey,
|
|
131
135
|
useServerDataGrid: () => useServerDataGrid,
|
|
132
136
|
useServerTable: () => useServerTable,
|
|
133
137
|
useTheme: () => useTheme,
|
|
@@ -1559,66 +1563,6 @@ var React8 = __toESM(require("react"), 1);
|
|
|
1559
1563
|
var import_react_dom = require("react-dom");
|
|
1560
1564
|
var import_axios2 = __toESM(require("axios"), 1);
|
|
1561
1565
|
var import_lucide_react4 = require("lucide-react");
|
|
1562
|
-
|
|
1563
|
-
// src/components/tools/decryptPayload.ts
|
|
1564
|
-
var import_crypto_js = __toESM(require("crypto-js"), 1);
|
|
1565
|
-
function getLaravelSecretKey() {
|
|
1566
|
-
const viteKey = (() => {
|
|
1567
|
-
try {
|
|
1568
|
-
return new Function("return import.meta.env")();
|
|
1569
|
-
} catch {
|
|
1570
|
-
return void 0;
|
|
1571
|
-
}
|
|
1572
|
-
})()?.VITE_LARAVEL_KEY;
|
|
1573
|
-
const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
|
|
1574
|
-
const windowKey = globalThis?.__LARAVEL_KEY__;
|
|
1575
|
-
const key = viteKey || legacyKey || windowKey;
|
|
1576
|
-
if (!key) {
|
|
1577
|
-
throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env or inject window.__LARAVEL_KEY__ via Blade.");
|
|
1578
|
-
}
|
|
1579
|
-
return key;
|
|
1580
|
-
}
|
|
1581
|
-
function parseLaravelKey(secretKey) {
|
|
1582
|
-
if (secretKey.startsWith("base64:")) {
|
|
1583
|
-
return import_crypto_js.default.enc.Base64.parse(secretKey.slice("base64:".length));
|
|
1584
|
-
}
|
|
1585
|
-
return import_crypto_js.default.enc.Utf8.parse(secretKey);
|
|
1586
|
-
}
|
|
1587
|
-
function parseLaravelEncryptedPayload(payload) {
|
|
1588
|
-
let jsonStr = payload;
|
|
1589
|
-
try {
|
|
1590
|
-
jsonStr = atob(payload);
|
|
1591
|
-
} catch {
|
|
1592
|
-
}
|
|
1593
|
-
return JSON.parse(jsonStr);
|
|
1594
|
-
}
|
|
1595
|
-
function decryptLaravelPayload(payload, secretKey) {
|
|
1596
|
-
const resolvedKey = secretKey ?? getLaravelSecretKey();
|
|
1597
|
-
const parsed = parseLaravelEncryptedPayload(payload);
|
|
1598
|
-
if (parsed.tag) {
|
|
1599
|
-
throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
|
|
1600
|
-
}
|
|
1601
|
-
const key = parseLaravelKey(resolvedKey);
|
|
1602
|
-
const expectedMac = import_crypto_js.default.HmacSHA256(parsed.iv + parsed.value, key).toString(import_crypto_js.default.enc.Hex);
|
|
1603
|
-
if (expectedMac !== parsed.mac) {
|
|
1604
|
-
throw new Error("Invalid payload MAC (wrong key or tampered payload).");
|
|
1605
|
-
}
|
|
1606
|
-
const iv = import_crypto_js.default.enc.Base64.parse(parsed.iv);
|
|
1607
|
-
const ciphertext = import_crypto_js.default.enc.Base64.parse(parsed.value);
|
|
1608
|
-
const cipherParams = import_crypto_js.default.lib.CipherParams.create({ ciphertext });
|
|
1609
|
-
const decrypted = import_crypto_js.default.AES.decrypt(cipherParams, key, {
|
|
1610
|
-
iv,
|
|
1611
|
-
mode: import_crypto_js.default.mode.CBC,
|
|
1612
|
-
padding: import_crypto_js.default.pad.Pkcs7
|
|
1613
|
-
});
|
|
1614
|
-
const plaintext = decrypted.toString(import_crypto_js.default.enc.Utf8);
|
|
1615
|
-
if (!plaintext) {
|
|
1616
|
-
throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
|
|
1617
|
-
}
|
|
1618
|
-
return JSON.parse(plaintext);
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
// src/components/ui/bulletin-board.tsx
|
|
1622
1566
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1623
1567
|
var PRIORITY_CONFIG = {
|
|
1624
1568
|
urgent: { label: "Urgent", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.AlertCircle, { className: "h-3 w-3" }), badge: "error" },
|
|
@@ -1648,82 +1592,13 @@ function AuthorAvatar({ item }) {
|
|
|
1648
1592
|
}
|
|
1649
1593
|
return null;
|
|
1650
1594
|
}
|
|
1651
|
-
function
|
|
1652
|
-
url,
|
|
1653
|
-
params,
|
|
1654
|
-
encrypt,
|
|
1655
|
-
key,
|
|
1656
|
-
decryptPayloadLog,
|
|
1657
|
-
transform
|
|
1658
|
-
}) {
|
|
1659
|
-
const [items, setItems] = React8.useState([]);
|
|
1660
|
-
const [currentPage, setCurrentPage] = React8.useState(1);
|
|
1661
|
-
const [pagination, setPagination] = React8.useState(null);
|
|
1662
|
-
const [loading, setLoading] = React8.useState(false);
|
|
1663
|
-
const [error, setError] = React8.useState(null);
|
|
1664
|
-
const [tick, setTick] = React8.useState(0);
|
|
1665
|
-
React8.useEffect(() => {
|
|
1666
|
-
let cancelled = false;
|
|
1667
|
-
setLoading(true);
|
|
1668
|
-
setError(null);
|
|
1669
|
-
import_axios2.default.get(url, { params: { ...params, page: currentPage } }).then(({ data: res }) => {
|
|
1670
|
-
if (cancelled) return;
|
|
1671
|
-
let payload;
|
|
1672
|
-
try {
|
|
1673
|
-
payload = encrypt ? decryptLaravelPayload(res, key) : res;
|
|
1674
|
-
} catch (decryptErr) {
|
|
1675
|
-
console.error("[useServerBulletin] decryption failed:", decryptErr?.message ?? decryptErr);
|
|
1676
|
-
if (!cancelled) setError(decryptErr?.message ?? "Decryption failed");
|
|
1677
|
-
return;
|
|
1678
|
-
}
|
|
1679
|
-
if (encrypt && decryptPayloadLog) console.log("[useServerBulletin] decrypted:", payload);
|
|
1680
|
-
const rows = Array.isArray(payload) ? payload : payload.data ?? [];
|
|
1681
|
-
setItems(transform ? rows.map(transform) : rows);
|
|
1682
|
-
if (!Array.isArray(payload)) {
|
|
1683
|
-
const rawTotal = payload.total;
|
|
1684
|
-
const rawPerPage = payload.per_page;
|
|
1685
|
-
const rawLastPage = payload.last_page;
|
|
1686
|
-
const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
|
|
1687
|
-
const pg = payload.pagination ?? {
|
|
1688
|
-
first_page_url: payload.first_page_url ?? `${url}?page=1`,
|
|
1689
|
-
last_page_url: payload.last_page_url ?? `${url}?page=${lastPage}`,
|
|
1690
|
-
last_page: lastPage,
|
|
1691
|
-
next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
|
|
1692
|
-
prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
|
|
1693
|
-
per_page: rawPerPage,
|
|
1694
|
-
total: rawTotal,
|
|
1695
|
-
links: payload.links ?? []
|
|
1696
|
-
};
|
|
1697
|
-
setPagination(pg);
|
|
1698
|
-
}
|
|
1699
|
-
}).catch((err) => {
|
|
1700
|
-
if (cancelled) return;
|
|
1701
|
-
setError(err?.response?.data?.message ?? err.message ?? "Request failed");
|
|
1702
|
-
}).finally(() => {
|
|
1703
|
-
if (!cancelled) setLoading(false);
|
|
1704
|
-
});
|
|
1705
|
-
return () => {
|
|
1706
|
-
cancelled = true;
|
|
1707
|
-
};
|
|
1708
|
-
}, [url, currentPage, tick, JSON.stringify(params), encrypt, decryptPayloadLog]);
|
|
1709
|
-
return {
|
|
1710
|
-
items,
|
|
1711
|
-
currentPage,
|
|
1712
|
-
pagination,
|
|
1713
|
-
serverPagination: pagination ? { pagination, currentPage, goToPage: (p) => setCurrentPage(p) } : null,
|
|
1714
|
-
loading,
|
|
1715
|
-
error,
|
|
1716
|
-
goToPage: (p) => setCurrentPage(p),
|
|
1717
|
-
reload: () => setTick((t) => t + 1)
|
|
1718
|
-
};
|
|
1719
|
-
}
|
|
1720
|
-
function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions }) {
|
|
1595
|
+
function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions, headerAction, footerAction }) {
|
|
1721
1596
|
const priority = item.priority ? PRIORITY_CONFIG[item.priority] : null;
|
|
1722
1597
|
return (0, import_react_dom.createPortal)(
|
|
1723
1598
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1724
1599
|
"div",
|
|
1725
1600
|
{
|
|
1726
|
-
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
1601
|
+
className: "fixed inset-0 z-50 flex glass items-center justify-center p-4",
|
|
1727
1602
|
style: { background: "rgba(0,0,0,0.55)" },
|
|
1728
1603
|
onMouseDown: (e) => {
|
|
1729
1604
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -1736,7 +1611,8 @@ function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customAction
|
|
|
1736
1611
|
" Pinned"
|
|
1737
1612
|
] }),
|
|
1738
1613
|
priority && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { variant: priority.badge, size: "sm", icon: priority.icon ?? void 0, children: priority.label }),
|
|
1739
|
-
item.category && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { variant: "outline", size: "sm", children: item.category })
|
|
1614
|
+
item.category && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { variant: "outline", size: "sm", children: item.category }),
|
|
1615
|
+
headerAction
|
|
1740
1616
|
] }),
|
|
1741
1617
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
1742
1618
|
onEdit && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -1835,6 +1711,7 @@ function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customAction
|
|
|
1835
1711
|
] })
|
|
1836
1712
|
] }) }),
|
|
1837
1713
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1714
|
+
footerAction,
|
|
1838
1715
|
onEdit && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1839
1716
|
"button",
|
|
1840
1717
|
{
|
|
@@ -2229,6 +2106,9 @@ function BulletinBoard({
|
|
|
2229
2106
|
deleteBaseUrl,
|
|
2230
2107
|
deleteIdKey = "id",
|
|
2231
2108
|
serverPagination,
|
|
2109
|
+
footerAction,
|
|
2110
|
+
headerPreviewAction,
|
|
2111
|
+
footerPreviewAction,
|
|
2232
2112
|
className
|
|
2233
2113
|
}) {
|
|
2234
2114
|
const [previewItem, setPreviewItem] = React8.useState(null);
|
|
@@ -2313,6 +2193,7 @@ function BulletinBoard({
|
|
|
2313
2193
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.Pin, { className: "h-8 w-8 opacity-20" }),
|
|
2314
2194
|
emptyMessage
|
|
2315
2195
|
] }) : layout === "list" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-col gap-3", children: filtered.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BulletinCard, { item, variant, layout: "list", onClick: handleCardClick }, item.id)) }) : layout === "masonry" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: cn("gap-4", COLS_CLASS[columns]), style: { display: "grid", gridTemplateRows: "masonry" }, children: filtered.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BulletinCard, { item, variant, layout: "masonry", onClick: handleCardClick }, item.id)) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: cn("grid gap-4", COLS_CLASS[columns]), children: filtered.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BulletinCard, { item, variant, layout: "grid", onClick: handleCardClick }, item.id)) }),
|
|
2196
|
+
footerAction && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { children: footerAction }),
|
|
2316
2197
|
serverPagination && (() => {
|
|
2317
2198
|
const { pagination, currentPage: cp, goToPage } = serverPagination;
|
|
2318
2199
|
const totalPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
|
|
@@ -2389,7 +2270,9 @@ function BulletinBoard({
|
|
|
2389
2270
|
setDeleteItem(item);
|
|
2390
2271
|
} : onDelete ? (item) => {
|
|
2391
2272
|
onDelete(item);
|
|
2392
|
-
} : void 0
|
|
2273
|
+
} : void 0,
|
|
2274
|
+
footerAction: footerPreviewAction ? footerPreviewAction(previewItem) : void 0,
|
|
2275
|
+
headerAction: headerPreviewAction
|
|
2393
2276
|
}
|
|
2394
2277
|
),
|
|
2395
2278
|
editItem && editBaseUrl && editFields && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -3863,6 +3746,66 @@ function MetricRow({ items, divided = true, className }) {
|
|
|
3863
3746
|
// src/components/ui/data-grid.tsx
|
|
3864
3747
|
var React29 = __toESM(require("react"), 1);
|
|
3865
3748
|
var import_react_dom3 = require("react-dom");
|
|
3749
|
+
|
|
3750
|
+
// src/components/tools/decryptPayload.ts
|
|
3751
|
+
var import_crypto_js = __toESM(require("crypto-js"), 1);
|
|
3752
|
+
function getLaravelSecretKey() {
|
|
3753
|
+
const viteKey = (() => {
|
|
3754
|
+
try {
|
|
3755
|
+
return new Function("return import.meta.env")();
|
|
3756
|
+
} catch {
|
|
3757
|
+
return void 0;
|
|
3758
|
+
}
|
|
3759
|
+
})()?.VITE_LARAVEL_KEY;
|
|
3760
|
+
const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
|
|
3761
|
+
const windowKey = globalThis?.__LARAVEL_KEY__;
|
|
3762
|
+
const key = viteKey || legacyKey || windowKey;
|
|
3763
|
+
if (!key) {
|
|
3764
|
+
throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env or inject window.__LARAVEL_KEY__ via Blade.");
|
|
3765
|
+
}
|
|
3766
|
+
return key;
|
|
3767
|
+
}
|
|
3768
|
+
function parseLaravelKey(secretKey) {
|
|
3769
|
+
if (secretKey.startsWith("base64:")) {
|
|
3770
|
+
return import_crypto_js.default.enc.Base64.parse(secretKey.slice("base64:".length));
|
|
3771
|
+
}
|
|
3772
|
+
return import_crypto_js.default.enc.Utf8.parse(secretKey);
|
|
3773
|
+
}
|
|
3774
|
+
function parseLaravelEncryptedPayload(payload) {
|
|
3775
|
+
let jsonStr = payload;
|
|
3776
|
+
try {
|
|
3777
|
+
jsonStr = atob(payload);
|
|
3778
|
+
} catch {
|
|
3779
|
+
}
|
|
3780
|
+
return JSON.parse(jsonStr);
|
|
3781
|
+
}
|
|
3782
|
+
function decryptLaravelPayload(payload, secretKey) {
|
|
3783
|
+
const resolvedKey = secretKey ?? getLaravelSecretKey();
|
|
3784
|
+
const parsed = parseLaravelEncryptedPayload(payload);
|
|
3785
|
+
if (parsed.tag) {
|
|
3786
|
+
throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
|
|
3787
|
+
}
|
|
3788
|
+
const key = parseLaravelKey(resolvedKey);
|
|
3789
|
+
const expectedMac = import_crypto_js.default.HmacSHA256(parsed.iv + parsed.value, key).toString(import_crypto_js.default.enc.Hex);
|
|
3790
|
+
if (expectedMac !== parsed.mac) {
|
|
3791
|
+
throw new Error("Invalid payload MAC (wrong key or tampered payload).");
|
|
3792
|
+
}
|
|
3793
|
+
const iv = import_crypto_js.default.enc.Base64.parse(parsed.iv);
|
|
3794
|
+
const ciphertext = import_crypto_js.default.enc.Base64.parse(parsed.value);
|
|
3795
|
+
const cipherParams = import_crypto_js.default.lib.CipherParams.create({ ciphertext });
|
|
3796
|
+
const decrypted = import_crypto_js.default.AES.decrypt(cipherParams, key, {
|
|
3797
|
+
iv,
|
|
3798
|
+
mode: import_crypto_js.default.mode.CBC,
|
|
3799
|
+
padding: import_crypto_js.default.pad.Pkcs7
|
|
3800
|
+
});
|
|
3801
|
+
const plaintext = decrypted.toString(import_crypto_js.default.enc.Utf8);
|
|
3802
|
+
if (!plaintext) {
|
|
3803
|
+
throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
|
|
3804
|
+
}
|
|
3805
|
+
return JSON.parse(plaintext);
|
|
3806
|
+
}
|
|
3807
|
+
|
|
3808
|
+
// src/components/ui/data-grid.tsx
|
|
3866
3809
|
var import_axios4 = __toESM(require("axios"), 1);
|
|
3867
3810
|
var import_lucide_react18 = require("lucide-react");
|
|
3868
3811
|
|
|
@@ -6806,7 +6749,7 @@ function ModalShell({ title, onClose, children, footer, width = "lg" }) {
|
|
|
6806
6749
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6807
6750
|
"div",
|
|
6808
6751
|
{
|
|
6809
|
-
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
6752
|
+
className: "fixed inset-0 z-50 flex glass items-center justify-center p-4",
|
|
6810
6753
|
style: { background: "rgba(0,0,0,0.5)" },
|
|
6811
6754
|
onMouseDown: (e) => {
|
|
6812
6755
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -13817,6 +13760,12 @@ function createStore(initialValue, sessionName, expireInterval) {
|
|
|
13817
13760
|
};
|
|
13818
13761
|
return { store, getStore };
|
|
13819
13762
|
}
|
|
13763
|
+
|
|
13764
|
+
// src/core/decryption/decode.ts
|
|
13765
|
+
function decryptResponse(response, key) {
|
|
13766
|
+
const payload = typeof response === "string" ? response : response.data;
|
|
13767
|
+
return decryptLaravelPayload(payload, key);
|
|
13768
|
+
}
|
|
13820
13769
|
// Annotate the CommonJS export names for ESM import in node:
|
|
13821
13770
|
0 && (module.exports = {
|
|
13822
13771
|
Accordion,
|
|
@@ -13917,7 +13866,11 @@ function createStore(initialValue, sessionName, expireInterval) {
|
|
|
13917
13866
|
Wizard,
|
|
13918
13867
|
api,
|
|
13919
13868
|
createStore,
|
|
13920
|
-
|
|
13869
|
+
decryptLaravelPayload,
|
|
13870
|
+
decryptResponse,
|
|
13871
|
+
getLaravelSecretKey,
|
|
13872
|
+
parseLaravelEncryptedPayload,
|
|
13873
|
+
parseLaravelKey,
|
|
13921
13874
|
useServerDataGrid,
|
|
13922
13875
|
useServerTable,
|
|
13923
13876
|
useTheme,
|
package/dist/index.d.cts
CHANGED
|
@@ -3,6 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import React__default from 'react';
|
|
4
4
|
import { AxiosRequestConfig } from 'axios';
|
|
5
5
|
import { UseBoundStore, StoreApi } from 'zustand';
|
|
6
|
+
import CryptoJS from 'crypto-js';
|
|
6
7
|
|
|
7
8
|
type AuthView = "login" | "register" | "resetPassword";
|
|
8
9
|
interface AuthField {
|
|
@@ -810,26 +811,6 @@ interface BulletinServerPaginationProp {
|
|
|
810
811
|
currentPage: number;
|
|
811
812
|
goToPage: (page: number) => void;
|
|
812
813
|
}
|
|
813
|
-
interface UseServerBulletinOptions {
|
|
814
|
-
url: string;
|
|
815
|
-
params?: Record<string, string | number>;
|
|
816
|
-
encrypt?: boolean;
|
|
817
|
-
key?: string;
|
|
818
|
-
decryptPayloadLog?: boolean;
|
|
819
|
-
/** Map raw API row → BulletinItem */
|
|
820
|
-
transform?: (row: any) => BulletinItem;
|
|
821
|
-
}
|
|
822
|
-
interface UseServerBulletinReturn {
|
|
823
|
-
items: BulletinItem[];
|
|
824
|
-
currentPage: number;
|
|
825
|
-
pagination: ServerPagination | null;
|
|
826
|
-
serverPagination: BulletinServerPaginationProp | null;
|
|
827
|
-
loading: boolean;
|
|
828
|
-
error: string | null;
|
|
829
|
-
goToPage: (page: number) => void;
|
|
830
|
-
reload: () => void;
|
|
831
|
-
}
|
|
832
|
-
declare function useServerBulletin({ url, params, encrypt, key, decryptPayloadLog, transform, }: UseServerBulletinOptions): UseServerBulletinReturn;
|
|
833
814
|
interface BulletinPreviewProps {
|
|
834
815
|
item: BulletinItem;
|
|
835
816
|
onClose: () => void;
|
|
@@ -838,8 +819,12 @@ interface BulletinPreviewProps {
|
|
|
838
819
|
onView?: (item: BulletinItem) => void;
|
|
839
820
|
/** Custom actions to add to the preview header */
|
|
840
821
|
customActions?: BulletinAction[];
|
|
822
|
+
/** Extra React elements rendered in the preview modal header's left area */
|
|
823
|
+
headerAction?: React.ReactNode;
|
|
824
|
+
/** Extra React elements rendered in the preview modal footer's action area */
|
|
825
|
+
footerAction?: React.ReactNode;
|
|
841
826
|
}
|
|
842
|
-
declare function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions }: BulletinPreviewProps): React.ReactPortal;
|
|
827
|
+
declare function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions, headerAction, footerAction }: BulletinPreviewProps): React.ReactPortal;
|
|
843
828
|
interface BulletinEditField {
|
|
844
829
|
key: keyof BulletinItem | string;
|
|
845
830
|
label: string;
|
|
@@ -898,10 +883,16 @@ interface BulletinBoardProps {
|
|
|
898
883
|
serverPagination?: BulletinServerPaginationProp | null;
|
|
899
884
|
/** Fired when a post card is clicked (ignored when preview=true). */
|
|
900
885
|
onItemClick?: (item: BulletinItem) => void;
|
|
886
|
+
/** Extra React elements rendered below the board content (above pagination). */
|
|
887
|
+
footerAction?: React.ReactNode;
|
|
888
|
+
/** Extra React elements rendered in the preview modal header's left area. */
|
|
889
|
+
headerPreviewAction?: React.ReactNode;
|
|
890
|
+
/** Extra React elements rendered in the preview modal footer's action area. */
|
|
891
|
+
footerPreviewAction?: (item: BulletinItem) => React.ReactNode;
|
|
901
892
|
/** Additional CSS classes on the outer wrapper. */
|
|
902
893
|
className?: string;
|
|
903
894
|
}
|
|
904
|
-
declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, editBaseUrl, editMethod, editIdKey, editFields, deleteBaseUrl, deleteIdKey, serverPagination, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
|
|
895
|
+
declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, editBaseUrl, editMethod, editIdKey, editFields, deleteBaseUrl, deleteIdKey, serverPagination, footerAction, headerPreviewAction, footerPreviewAction, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
|
|
905
896
|
|
|
906
897
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
907
898
|
variant?: "primary" | "secondary" | "outline" | "ghost" | "link" | "danger" | "success" | "destructive";
|
|
@@ -2561,4 +2552,29 @@ type StoreWrapper<T extends object> = {
|
|
|
2561
2552
|
*/
|
|
2562
2553
|
declare function createStore<T extends object>(initialValue: T, sessionName: string, expireInterval?: number): StoreWrapper<T>;
|
|
2563
2554
|
|
|
2564
|
-
|
|
2555
|
+
type LaravelEncryptedPayload = {
|
|
2556
|
+
iv: string;
|
|
2557
|
+
value: string;
|
|
2558
|
+
mac: string;
|
|
2559
|
+
tag?: string;
|
|
2560
|
+
};
|
|
2561
|
+
declare function getLaravelSecretKey(): string;
|
|
2562
|
+
declare function parseLaravelKey(secretKey: string): CryptoJS.lib.WordArray;
|
|
2563
|
+
declare function parseLaravelEncryptedPayload(payload: string): LaravelEncryptedPayload;
|
|
2564
|
+
declare function decryptLaravelPayload<T>(payload: string, secretKey?: string): T;
|
|
2565
|
+
|
|
2566
|
+
/**
|
|
2567
|
+
* Decrypt a Laravel-encrypted API response.
|
|
2568
|
+
*
|
|
2569
|
+
* Accepts either:
|
|
2570
|
+
* - a raw encrypted string (the full response body)
|
|
2571
|
+
* - an object with an `data` key holding the encrypted string
|
|
2572
|
+
*
|
|
2573
|
+
* @param response Raw encrypted string or `{ data: string }` object
|
|
2574
|
+
* @param key Optional Laravel APP_KEY (base64:… or raw). Falls back to VITE_LARAVEL_KEY.
|
|
2575
|
+
*/
|
|
2576
|
+
declare function decryptResponse<T = unknown>(response: string | {
|
|
2577
|
+
data: string;
|
|
2578
|
+
}, key?: string): T;
|
|
2579
|
+
|
|
2580
|
+
export { Accordion, type AccordionItem, type AccordionProps, type AccordionVariant, type ActionField, type ActionFieldType, type AuthField, type AuthVariant, type AuthView, Authentication, type AuthenticationProps, AvatarStack, type AvatarStackProps, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BulletinAction, BulletinBoard, type BulletinBoardProps, type BulletinColumns, type BulletinEditField, type BulletinItem, type BulletinLayout, BulletinPreview, type BulletinPreviewProps, type BulletinPriority, type BulletinServerPaginationProp, type BulletinVariant, Button, type ButtonProps, COLOR_PALETTE, Calendar, CalendarDateRangePicker, type CalendarDateRangePickerProps, type CalendarDateRangeVariant, type CalendarEvent, type CalendarProps, type CalendarView, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChartDataPoint, ChartWidget, type ChartWidgetProps, Checkbox, type CheckboxProps, CircularProgress, type CircularProgressProps, type ClusterVariant, CodegoApiProvider, ColorPicker, type ColorPickerProps, type Column, Combobox, type ComboboxOption, type ComboboxProps, type CommandItem, CommandPalette, type CommandPaletteProps, ComposableWidget, type ComposableWidgetProps, type ConfirmVariant, ContextMenu, type ContextMenuItem, type ContextMenuProps, DataGrid, type DataGridColumn, type DataGridProps, DatePickerPopup, type DateRange, DateRangePicker, type DateRangePickerProps, type DefaultActionsConfig, DocsLayout, Drawer, type DrawerProps, type DrawerSide, Dropdown, DropdownItem, DropdownLabel, type DropdownProps, DropdownSeparator, EVENT_COLORS, type FileTypeValidation, FileUpload, type FileUploadProps, type FlexAlign, type FlexDirection, type FlexGap, FlexItem, type FlexItemProps, type FlexJustify, FlexLayout, type FlexLayoutProps, type FlexWrap, type FlyToOptions, type FormField, type FormFieldType, type GridAlign, type GridCols, type GridGap, GridItem, type GridItemProps, GridLayout, type GridLayoutProps, GroupNavigation, type GroupNavigationProps, type ImageEditorMode, type ImageEditorOptions, Input, type InputProps, KanbanBoard, type KanbanBoardProps, type KanbanCard, type KanbanColumn, Label, type LaravelEncryptedPayload, LeafletMap, type LeafletMapProps, LeftSidebar, type LeftSidebarProps, type MapLibreClusterVariant, MapLibreMap, type MapLibreMarker, type MapLibreProps, type MapLibreRoute, type MapLibreRouteType, type MapLibreStyle, type MapMarker, type MapRoute, type MarkerColor, type MetricItem, MetricRow, type MetricRowProps, Modal, ModalConfirmation, type ModalConfirmationProps, type ModalProps, ModalUnchange, type ModalUnchangeProps, ModalWithForms, type ModalWithFormsProps, type NavGroup, type NavItem, Navigation, type NavigationProps, NotificationBanner, type NotificationBannerProps, type NotificationItem, NotificationPanel, type NotificationPanelProps, type NotificationVariant, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Panel, type PanelProps, PanelSettings, type PanelSettingsProps, type PanelSettingsTab, PanelSidebarGroup, PanelSidebarItem, Popover, type PopoverPlacement, type PopoverProps, Progress, type ProgressProps, type ProgressSize, type ProgressVariant, type PropRow, PropsTable, RadioGroup, type RadioGroupProps, type RadioOption, type RadioSize, type RadioVariant, RangeSlider, type RangeSliderProps, Repeater, type RepeaterProps, type RequestConfig, ResizablePanels, type ResizablePanelsProps, RichTextEditor, type RichTextEditorProps, RightSidebar, type RightSidebarProps, type RouteType, ScrollArea, type ScrollAreaProps, Section, SectionBlock, type SectionProps, type SectionVariant, Select, type SelectOption, type SelectProps, type SemanticColor, type ServerDataGridProp, type ServerPagination, type ServerPaginationLink, type ServerPaginationProp, type ServerTableResponse, Skeleton, Slider, type SliderProps, type SortDir, StatCard, type StatCardProps, type StatTrend, StatsWidget, type StatsWidgetProps, type Step, type StepStatus, Stepper, type StepperProps, type TabItem, type TabSize, type TabVariant, Table, TableOfContents, type TableProps, TableWidget, type TableWidgetProps, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ThemeColors, ThemeProvider, type ThemeSettings, Timeline, type TimelineItem, type TimelineProps, type TimelineVariant, type ToastItem, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, type TocItem, TocProvider, ToggleSwitch, type ToggleSwitchProps, Tooltip, type TooltipProps, Topbar, type TopbarProps, type TreeNode, TreeView, type TreeViewProps, type TrendDir, type UseServerDataGridOptions, type UseServerDataGridReturn, type UseServerTableOptions, type UseServerTableReturn, Widget, type WidgetProps, Wizard, type WizardActionProps, type WizardLayout, type WizardProps, type WizardSize, type WizardStep, type WizardVariant, api, createStore, decryptLaravelPayload, decryptResponse, getLaravelSecretKey, parseLaravelEncryptedPayload, parseLaravelKey, useServerDataGrid, useServerTable, useTheme, useToast, useToc };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import React__default from 'react';
|
|
4
4
|
import { AxiosRequestConfig } from 'axios';
|
|
5
5
|
import { UseBoundStore, StoreApi } from 'zustand';
|
|
6
|
+
import CryptoJS from 'crypto-js';
|
|
6
7
|
|
|
7
8
|
type AuthView = "login" | "register" | "resetPassword";
|
|
8
9
|
interface AuthField {
|
|
@@ -810,26 +811,6 @@ interface BulletinServerPaginationProp {
|
|
|
810
811
|
currentPage: number;
|
|
811
812
|
goToPage: (page: number) => void;
|
|
812
813
|
}
|
|
813
|
-
interface UseServerBulletinOptions {
|
|
814
|
-
url: string;
|
|
815
|
-
params?: Record<string, string | number>;
|
|
816
|
-
encrypt?: boolean;
|
|
817
|
-
key?: string;
|
|
818
|
-
decryptPayloadLog?: boolean;
|
|
819
|
-
/** Map raw API row → BulletinItem */
|
|
820
|
-
transform?: (row: any) => BulletinItem;
|
|
821
|
-
}
|
|
822
|
-
interface UseServerBulletinReturn {
|
|
823
|
-
items: BulletinItem[];
|
|
824
|
-
currentPage: number;
|
|
825
|
-
pagination: ServerPagination | null;
|
|
826
|
-
serverPagination: BulletinServerPaginationProp | null;
|
|
827
|
-
loading: boolean;
|
|
828
|
-
error: string | null;
|
|
829
|
-
goToPage: (page: number) => void;
|
|
830
|
-
reload: () => void;
|
|
831
|
-
}
|
|
832
|
-
declare function useServerBulletin({ url, params, encrypt, key, decryptPayloadLog, transform, }: UseServerBulletinOptions): UseServerBulletinReturn;
|
|
833
814
|
interface BulletinPreviewProps {
|
|
834
815
|
item: BulletinItem;
|
|
835
816
|
onClose: () => void;
|
|
@@ -838,8 +819,12 @@ interface BulletinPreviewProps {
|
|
|
838
819
|
onView?: (item: BulletinItem) => void;
|
|
839
820
|
/** Custom actions to add to the preview header */
|
|
840
821
|
customActions?: BulletinAction[];
|
|
822
|
+
/** Extra React elements rendered in the preview modal header's left area */
|
|
823
|
+
headerAction?: React.ReactNode;
|
|
824
|
+
/** Extra React elements rendered in the preview modal footer's action area */
|
|
825
|
+
footerAction?: React.ReactNode;
|
|
841
826
|
}
|
|
842
|
-
declare function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions }: BulletinPreviewProps): React.ReactPortal;
|
|
827
|
+
declare function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions, headerAction, footerAction }: BulletinPreviewProps): React.ReactPortal;
|
|
843
828
|
interface BulletinEditField {
|
|
844
829
|
key: keyof BulletinItem | string;
|
|
845
830
|
label: string;
|
|
@@ -898,10 +883,16 @@ interface BulletinBoardProps {
|
|
|
898
883
|
serverPagination?: BulletinServerPaginationProp | null;
|
|
899
884
|
/** Fired when a post card is clicked (ignored when preview=true). */
|
|
900
885
|
onItemClick?: (item: BulletinItem) => void;
|
|
886
|
+
/** Extra React elements rendered below the board content (above pagination). */
|
|
887
|
+
footerAction?: React.ReactNode;
|
|
888
|
+
/** Extra React elements rendered in the preview modal header's left area. */
|
|
889
|
+
headerPreviewAction?: React.ReactNode;
|
|
890
|
+
/** Extra React elements rendered in the preview modal footer's action area. */
|
|
891
|
+
footerPreviewAction?: (item: BulletinItem) => React.ReactNode;
|
|
901
892
|
/** Additional CSS classes on the outer wrapper. */
|
|
902
893
|
className?: string;
|
|
903
894
|
}
|
|
904
|
-
declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, editBaseUrl, editMethod, editIdKey, editFields, deleteBaseUrl, deleteIdKey, serverPagination, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
|
|
895
|
+
declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, editBaseUrl, editMethod, editIdKey, editFields, deleteBaseUrl, deleteIdKey, serverPagination, footerAction, headerPreviewAction, footerPreviewAction, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
|
|
905
896
|
|
|
906
897
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
907
898
|
variant?: "primary" | "secondary" | "outline" | "ghost" | "link" | "danger" | "success" | "destructive";
|
|
@@ -2561,4 +2552,29 @@ type StoreWrapper<T extends object> = {
|
|
|
2561
2552
|
*/
|
|
2562
2553
|
declare function createStore<T extends object>(initialValue: T, sessionName: string, expireInterval?: number): StoreWrapper<T>;
|
|
2563
2554
|
|
|
2564
|
-
|
|
2555
|
+
type LaravelEncryptedPayload = {
|
|
2556
|
+
iv: string;
|
|
2557
|
+
value: string;
|
|
2558
|
+
mac: string;
|
|
2559
|
+
tag?: string;
|
|
2560
|
+
};
|
|
2561
|
+
declare function getLaravelSecretKey(): string;
|
|
2562
|
+
declare function parseLaravelKey(secretKey: string): CryptoJS.lib.WordArray;
|
|
2563
|
+
declare function parseLaravelEncryptedPayload(payload: string): LaravelEncryptedPayload;
|
|
2564
|
+
declare function decryptLaravelPayload<T>(payload: string, secretKey?: string): T;
|
|
2565
|
+
|
|
2566
|
+
/**
|
|
2567
|
+
* Decrypt a Laravel-encrypted API response.
|
|
2568
|
+
*
|
|
2569
|
+
* Accepts either:
|
|
2570
|
+
* - a raw encrypted string (the full response body)
|
|
2571
|
+
* - an object with an `data` key holding the encrypted string
|
|
2572
|
+
*
|
|
2573
|
+
* @param response Raw encrypted string or `{ data: string }` object
|
|
2574
|
+
* @param key Optional Laravel APP_KEY (base64:… or raw). Falls back to VITE_LARAVEL_KEY.
|
|
2575
|
+
*/
|
|
2576
|
+
declare function decryptResponse<T = unknown>(response: string | {
|
|
2577
|
+
data: string;
|
|
2578
|
+
}, key?: string): T;
|
|
2579
|
+
|
|
2580
|
+
export { Accordion, type AccordionItem, type AccordionProps, type AccordionVariant, type ActionField, type ActionFieldType, type AuthField, type AuthVariant, type AuthView, Authentication, type AuthenticationProps, AvatarStack, type AvatarStackProps, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BulletinAction, BulletinBoard, type BulletinBoardProps, type BulletinColumns, type BulletinEditField, type BulletinItem, type BulletinLayout, BulletinPreview, type BulletinPreviewProps, type BulletinPriority, type BulletinServerPaginationProp, type BulletinVariant, Button, type ButtonProps, COLOR_PALETTE, Calendar, CalendarDateRangePicker, type CalendarDateRangePickerProps, type CalendarDateRangeVariant, type CalendarEvent, type CalendarProps, type CalendarView, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChartDataPoint, ChartWidget, type ChartWidgetProps, Checkbox, type CheckboxProps, CircularProgress, type CircularProgressProps, type ClusterVariant, CodegoApiProvider, ColorPicker, type ColorPickerProps, type Column, Combobox, type ComboboxOption, type ComboboxProps, type CommandItem, CommandPalette, type CommandPaletteProps, ComposableWidget, type ComposableWidgetProps, type ConfirmVariant, ContextMenu, type ContextMenuItem, type ContextMenuProps, DataGrid, type DataGridColumn, type DataGridProps, DatePickerPopup, type DateRange, DateRangePicker, type DateRangePickerProps, type DefaultActionsConfig, DocsLayout, Drawer, type DrawerProps, type DrawerSide, Dropdown, DropdownItem, DropdownLabel, type DropdownProps, DropdownSeparator, EVENT_COLORS, type FileTypeValidation, FileUpload, type FileUploadProps, type FlexAlign, type FlexDirection, type FlexGap, FlexItem, type FlexItemProps, type FlexJustify, FlexLayout, type FlexLayoutProps, type FlexWrap, type FlyToOptions, type FormField, type FormFieldType, type GridAlign, type GridCols, type GridGap, GridItem, type GridItemProps, GridLayout, type GridLayoutProps, GroupNavigation, type GroupNavigationProps, type ImageEditorMode, type ImageEditorOptions, Input, type InputProps, KanbanBoard, type KanbanBoardProps, type KanbanCard, type KanbanColumn, Label, type LaravelEncryptedPayload, LeafletMap, type LeafletMapProps, LeftSidebar, type LeftSidebarProps, type MapLibreClusterVariant, MapLibreMap, type MapLibreMarker, type MapLibreProps, type MapLibreRoute, type MapLibreRouteType, type MapLibreStyle, type MapMarker, type MapRoute, type MarkerColor, type MetricItem, MetricRow, type MetricRowProps, Modal, ModalConfirmation, type ModalConfirmationProps, type ModalProps, ModalUnchange, type ModalUnchangeProps, ModalWithForms, type ModalWithFormsProps, type NavGroup, type NavItem, Navigation, type NavigationProps, NotificationBanner, type NotificationBannerProps, type NotificationItem, NotificationPanel, type NotificationPanelProps, type NotificationVariant, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Panel, type PanelProps, PanelSettings, type PanelSettingsProps, type PanelSettingsTab, PanelSidebarGroup, PanelSidebarItem, Popover, type PopoverPlacement, type PopoverProps, Progress, type ProgressProps, type ProgressSize, type ProgressVariant, type PropRow, PropsTable, RadioGroup, type RadioGroupProps, type RadioOption, type RadioSize, type RadioVariant, RangeSlider, type RangeSliderProps, Repeater, type RepeaterProps, type RequestConfig, ResizablePanels, type ResizablePanelsProps, RichTextEditor, type RichTextEditorProps, RightSidebar, type RightSidebarProps, type RouteType, ScrollArea, type ScrollAreaProps, Section, SectionBlock, type SectionProps, type SectionVariant, Select, type SelectOption, type SelectProps, type SemanticColor, type ServerDataGridProp, type ServerPagination, type ServerPaginationLink, type ServerPaginationProp, type ServerTableResponse, Skeleton, Slider, type SliderProps, type SortDir, StatCard, type StatCardProps, type StatTrend, StatsWidget, type StatsWidgetProps, type Step, type StepStatus, Stepper, type StepperProps, type TabItem, type TabSize, type TabVariant, Table, TableOfContents, type TableProps, TableWidget, type TableWidgetProps, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ThemeColors, ThemeProvider, type ThemeSettings, Timeline, type TimelineItem, type TimelineProps, type TimelineVariant, type ToastItem, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, type TocItem, TocProvider, ToggleSwitch, type ToggleSwitchProps, Tooltip, type TooltipProps, Topbar, type TopbarProps, type TreeNode, TreeView, type TreeViewProps, type TrendDir, type UseServerDataGridOptions, type UseServerDataGridReturn, type UseServerTableOptions, type UseServerTableReturn, Widget, type WidgetProps, Wizard, type WizardActionProps, type WizardLayout, type WizardProps, type WizardSize, type WizardStep, type WizardVariant, api, createStore, decryptLaravelPayload, decryptResponse, getLaravelSecretKey, parseLaravelEncryptedPayload, parseLaravelKey, useServerDataGrid, useServerTable, useTheme, useToast, useToc };
|
package/dist/index.global.js
CHANGED
|
@@ -53730,7 +53730,11 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
53730
53730
|
Wizard: () => Wizard,
|
|
53731
53731
|
api: () => api,
|
|
53732
53732
|
createStore: () => createStore2,
|
|
53733
|
-
|
|
53733
|
+
decryptLaravelPayload: () => decryptLaravelPayload,
|
|
53734
|
+
decryptResponse: () => decryptResponse,
|
|
53735
|
+
getLaravelSecretKey: () => getLaravelSecretKey,
|
|
53736
|
+
parseLaravelEncryptedPayload: () => parseLaravelEncryptedPayload,
|
|
53737
|
+
parseLaravelKey: () => parseLaravelKey,
|
|
53734
53738
|
useServerDataGrid: () => useServerDataGrid,
|
|
53735
53739
|
useServerTable: () => useServerTable,
|
|
53736
53740
|
useTheme: () => useTheme,
|
|
@@ -64733,66 +64737,6 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
64733
64737
|
// src/components/ui/bulletin-board.tsx
|
|
64734
64738
|
var React8 = __toESM(require_react(), 1);
|
|
64735
64739
|
var import_react_dom = __toESM(require_react_dom(), 1);
|
|
64736
|
-
|
|
64737
|
-
// src/components/tools/decryptPayload.ts
|
|
64738
|
-
var import_crypto_js = __toESM(require_crypto_js(), 1);
|
|
64739
|
-
function getLaravelSecretKey() {
|
|
64740
|
-
const viteKey = (() => {
|
|
64741
|
-
try {
|
|
64742
|
-
return new Function("return import.meta.env")();
|
|
64743
|
-
} catch {
|
|
64744
|
-
return void 0;
|
|
64745
|
-
}
|
|
64746
|
-
})()?.VITE_LARAVEL_KEY;
|
|
64747
|
-
const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
|
|
64748
|
-
const windowKey = globalThis?.__LARAVEL_KEY__;
|
|
64749
|
-
const key = viteKey || legacyKey || windowKey;
|
|
64750
|
-
if (!key) {
|
|
64751
|
-
throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env or inject window.__LARAVEL_KEY__ via Blade.");
|
|
64752
|
-
}
|
|
64753
|
-
return key;
|
|
64754
|
-
}
|
|
64755
|
-
function parseLaravelKey(secretKey) {
|
|
64756
|
-
if (secretKey.startsWith("base64:")) {
|
|
64757
|
-
return import_crypto_js.default.enc.Base64.parse(secretKey.slice("base64:".length));
|
|
64758
|
-
}
|
|
64759
|
-
return import_crypto_js.default.enc.Utf8.parse(secretKey);
|
|
64760
|
-
}
|
|
64761
|
-
function parseLaravelEncryptedPayload(payload) {
|
|
64762
|
-
let jsonStr = payload;
|
|
64763
|
-
try {
|
|
64764
|
-
jsonStr = atob(payload);
|
|
64765
|
-
} catch {
|
|
64766
|
-
}
|
|
64767
|
-
return JSON.parse(jsonStr);
|
|
64768
|
-
}
|
|
64769
|
-
function decryptLaravelPayload(payload, secretKey) {
|
|
64770
|
-
const resolvedKey = secretKey ?? getLaravelSecretKey();
|
|
64771
|
-
const parsed = parseLaravelEncryptedPayload(payload);
|
|
64772
|
-
if (parsed.tag) {
|
|
64773
|
-
throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
|
|
64774
|
-
}
|
|
64775
|
-
const key = parseLaravelKey(resolvedKey);
|
|
64776
|
-
const expectedMac = import_crypto_js.default.HmacSHA256(parsed.iv + parsed.value, key).toString(import_crypto_js.default.enc.Hex);
|
|
64777
|
-
if (expectedMac !== parsed.mac) {
|
|
64778
|
-
throw new Error("Invalid payload MAC (wrong key or tampered payload).");
|
|
64779
|
-
}
|
|
64780
|
-
const iv = import_crypto_js.default.enc.Base64.parse(parsed.iv);
|
|
64781
|
-
const ciphertext = import_crypto_js.default.enc.Base64.parse(parsed.value);
|
|
64782
|
-
const cipherParams = import_crypto_js.default.lib.CipherParams.create({ ciphertext });
|
|
64783
|
-
const decrypted = import_crypto_js.default.AES.decrypt(cipherParams, key, {
|
|
64784
|
-
iv,
|
|
64785
|
-
mode: import_crypto_js.default.mode.CBC,
|
|
64786
|
-
padding: import_crypto_js.default.pad.Pkcs7
|
|
64787
|
-
});
|
|
64788
|
-
const plaintext = decrypted.toString(import_crypto_js.default.enc.Utf8);
|
|
64789
|
-
if (!plaintext) {
|
|
64790
|
-
throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
|
|
64791
|
-
}
|
|
64792
|
-
return JSON.parse(plaintext);
|
|
64793
|
-
}
|
|
64794
|
-
|
|
64795
|
-
// src/components/ui/bulletin-board.tsx
|
|
64796
64740
|
var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
|
|
64797
64741
|
var PRIORITY_CONFIG = {
|
|
64798
64742
|
urgent: { label: "Urgent", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CircleAlert, { className: "h-3 w-3" }), badge: "error" },
|
|
@@ -64822,82 +64766,13 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
64822
64766
|
}
|
|
64823
64767
|
return null;
|
|
64824
64768
|
}
|
|
64825
|
-
function
|
|
64826
|
-
url: url2,
|
|
64827
|
-
params,
|
|
64828
|
-
encrypt,
|
|
64829
|
-
key,
|
|
64830
|
-
decryptPayloadLog,
|
|
64831
|
-
transform
|
|
64832
|
-
}) {
|
|
64833
|
-
const [items, setItems] = React8.useState([]);
|
|
64834
|
-
const [currentPage, setCurrentPage] = React8.useState(1);
|
|
64835
|
-
const [pagination, setPagination] = React8.useState(null);
|
|
64836
|
-
const [loading, setLoading] = React8.useState(false);
|
|
64837
|
-
const [error, setError] = React8.useState(null);
|
|
64838
|
-
const [tick, setTick] = React8.useState(0);
|
|
64839
|
-
React8.useEffect(() => {
|
|
64840
|
-
let cancelled = false;
|
|
64841
|
-
setLoading(true);
|
|
64842
|
-
setError(null);
|
|
64843
|
-
axios_default.get(url2, { params: { ...params, page: currentPage } }).then(({ data: res }) => {
|
|
64844
|
-
if (cancelled) return;
|
|
64845
|
-
let payload;
|
|
64846
|
-
try {
|
|
64847
|
-
payload = encrypt ? decryptLaravelPayload(res, key) : res;
|
|
64848
|
-
} catch (decryptErr) {
|
|
64849
|
-
console.error("[useServerBulletin] decryption failed:", decryptErr?.message ?? decryptErr);
|
|
64850
|
-
if (!cancelled) setError(decryptErr?.message ?? "Decryption failed");
|
|
64851
|
-
return;
|
|
64852
|
-
}
|
|
64853
|
-
if (encrypt && decryptPayloadLog) console.log("[useServerBulletin] decrypted:", payload);
|
|
64854
|
-
const rows = Array.isArray(payload) ? payload : payload.data ?? [];
|
|
64855
|
-
setItems(transform ? rows.map(transform) : rows);
|
|
64856
|
-
if (!Array.isArray(payload)) {
|
|
64857
|
-
const rawTotal = payload.total;
|
|
64858
|
-
const rawPerPage = payload.per_page;
|
|
64859
|
-
const rawLastPage = payload.last_page;
|
|
64860
|
-
const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
|
|
64861
|
-
const pg = payload.pagination ?? {
|
|
64862
|
-
first_page_url: payload.first_page_url ?? `${url2}?page=1`,
|
|
64863
|
-
last_page_url: payload.last_page_url ?? `${url2}?page=${lastPage}`,
|
|
64864
|
-
last_page: lastPage,
|
|
64865
|
-
next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url2}?page=${currentPage + 1}` : null,
|
|
64866
|
-
prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url2}?page=${currentPage - 1}` : null,
|
|
64867
|
-
per_page: rawPerPage,
|
|
64868
|
-
total: rawTotal,
|
|
64869
|
-
links: payload.links ?? []
|
|
64870
|
-
};
|
|
64871
|
-
setPagination(pg);
|
|
64872
|
-
}
|
|
64873
|
-
}).catch((err) => {
|
|
64874
|
-
if (cancelled) return;
|
|
64875
|
-
setError(err?.response?.data?.message ?? err.message ?? "Request failed");
|
|
64876
|
-
}).finally(() => {
|
|
64877
|
-
if (!cancelled) setLoading(false);
|
|
64878
|
-
});
|
|
64879
|
-
return () => {
|
|
64880
|
-
cancelled = true;
|
|
64881
|
-
};
|
|
64882
|
-
}, [url2, currentPage, tick, JSON.stringify(params), encrypt, decryptPayloadLog]);
|
|
64883
|
-
return {
|
|
64884
|
-
items,
|
|
64885
|
-
currentPage,
|
|
64886
|
-
pagination,
|
|
64887
|
-
serverPagination: pagination ? { pagination, currentPage, goToPage: (p) => setCurrentPage(p) } : null,
|
|
64888
|
-
loading,
|
|
64889
|
-
error,
|
|
64890
|
-
goToPage: (p) => setCurrentPage(p),
|
|
64891
|
-
reload: () => setTick((t) => t + 1)
|
|
64892
|
-
};
|
|
64893
|
-
}
|
|
64894
|
-
function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions }) {
|
|
64769
|
+
function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions, headerAction, footerAction }) {
|
|
64895
64770
|
const priority = item.priority ? PRIORITY_CONFIG[item.priority] : null;
|
|
64896
64771
|
return (0, import_react_dom.createPortal)(
|
|
64897
64772
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
64898
64773
|
"div",
|
|
64899
64774
|
{
|
|
64900
|
-
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
64775
|
+
className: "fixed inset-0 z-50 flex glass items-center justify-center p-4",
|
|
64901
64776
|
style: { background: "rgba(0,0,0,0.55)" },
|
|
64902
64777
|
onMouseDown: (e) => {
|
|
64903
64778
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -64910,7 +64785,8 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
64910
64785
|
" Pinned"
|
|
64911
64786
|
] }),
|
|
64912
64787
|
priority && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { variant: priority.badge, size: "sm", icon: priority.icon ?? void 0, children: priority.label }),
|
|
64913
|
-
item.category && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { variant: "outline", size: "sm", children: item.category })
|
|
64788
|
+
item.category && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { variant: "outline", size: "sm", children: item.category }),
|
|
64789
|
+
headerAction
|
|
64914
64790
|
] }),
|
|
64915
64791
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
64916
64792
|
onEdit && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -65009,6 +64885,7 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
65009
64885
|
] })
|
|
65010
64886
|
] }) }),
|
|
65011
64887
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
64888
|
+
footerAction,
|
|
65012
64889
|
onEdit && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
65013
64890
|
"button",
|
|
65014
64891
|
{
|
|
@@ -65403,6 +65280,9 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
65403
65280
|
deleteBaseUrl,
|
|
65404
65281
|
deleteIdKey = "id",
|
|
65405
65282
|
serverPagination,
|
|
65283
|
+
footerAction,
|
|
65284
|
+
headerPreviewAction,
|
|
65285
|
+
footerPreviewAction,
|
|
65406
65286
|
className
|
|
65407
65287
|
}) {
|
|
65408
65288
|
const [previewItem, setPreviewItem] = React8.useState(null);
|
|
@@ -65487,6 +65367,7 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
65487
65367
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Pin, { className: "h-8 w-8 opacity-20" }),
|
|
65488
65368
|
emptyMessage
|
|
65489
65369
|
] }) : layout === "list" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-col gap-3", children: filtered.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BulletinCard, { item, variant, layout: "list", onClick: handleCardClick }, item.id)) }) : layout === "masonry" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: cn("gap-4", COLS_CLASS[columns]), style: { display: "grid", gridTemplateRows: "masonry" }, children: filtered.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BulletinCard, { item, variant, layout: "masonry", onClick: handleCardClick }, item.id)) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: cn("grid gap-4", COLS_CLASS[columns]), children: filtered.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BulletinCard, { item, variant, layout: "grid", onClick: handleCardClick }, item.id)) }),
|
|
65370
|
+
footerAction && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { children: footerAction }),
|
|
65490
65371
|
serverPagination && (() => {
|
|
65491
65372
|
const { pagination, currentPage: cp, goToPage } = serverPagination;
|
|
65492
65373
|
const totalPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
|
|
@@ -65563,7 +65444,9 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
65563
65444
|
setDeleteItem(item);
|
|
65564
65445
|
} : onDelete ? (item) => {
|
|
65565
65446
|
onDelete(item);
|
|
65566
|
-
} : void 0
|
|
65447
|
+
} : void 0,
|
|
65448
|
+
footerAction: footerPreviewAction ? footerPreviewAction(previewItem) : void 0,
|
|
65449
|
+
headerAction: headerPreviewAction
|
|
65567
65450
|
}
|
|
65568
65451
|
),
|
|
65569
65452
|
editItem && editBaseUrl && editFields && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -67033,6 +66916,64 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
67033
66916
|
var React29 = __toESM(require_react(), 1);
|
|
67034
66917
|
var import_react_dom3 = __toESM(require_react_dom(), 1);
|
|
67035
66918
|
|
|
66919
|
+
// src/components/tools/decryptPayload.ts
|
|
66920
|
+
var import_crypto_js = __toESM(require_crypto_js(), 1);
|
|
66921
|
+
function getLaravelSecretKey() {
|
|
66922
|
+
const viteKey = (() => {
|
|
66923
|
+
try {
|
|
66924
|
+
return new Function("return import.meta.env")();
|
|
66925
|
+
} catch {
|
|
66926
|
+
return void 0;
|
|
66927
|
+
}
|
|
66928
|
+
})()?.VITE_LARAVEL_KEY;
|
|
66929
|
+
const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
|
|
66930
|
+
const windowKey = globalThis?.__LARAVEL_KEY__;
|
|
66931
|
+
const key = viteKey || legacyKey || windowKey;
|
|
66932
|
+
if (!key) {
|
|
66933
|
+
throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env or inject window.__LARAVEL_KEY__ via Blade.");
|
|
66934
|
+
}
|
|
66935
|
+
return key;
|
|
66936
|
+
}
|
|
66937
|
+
function parseLaravelKey(secretKey) {
|
|
66938
|
+
if (secretKey.startsWith("base64:")) {
|
|
66939
|
+
return import_crypto_js.default.enc.Base64.parse(secretKey.slice("base64:".length));
|
|
66940
|
+
}
|
|
66941
|
+
return import_crypto_js.default.enc.Utf8.parse(secretKey);
|
|
66942
|
+
}
|
|
66943
|
+
function parseLaravelEncryptedPayload(payload) {
|
|
66944
|
+
let jsonStr = payload;
|
|
66945
|
+
try {
|
|
66946
|
+
jsonStr = atob(payload);
|
|
66947
|
+
} catch {
|
|
66948
|
+
}
|
|
66949
|
+
return JSON.parse(jsonStr);
|
|
66950
|
+
}
|
|
66951
|
+
function decryptLaravelPayload(payload, secretKey) {
|
|
66952
|
+
const resolvedKey = secretKey ?? getLaravelSecretKey();
|
|
66953
|
+
const parsed = parseLaravelEncryptedPayload(payload);
|
|
66954
|
+
if (parsed.tag) {
|
|
66955
|
+
throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
|
|
66956
|
+
}
|
|
66957
|
+
const key = parseLaravelKey(resolvedKey);
|
|
66958
|
+
const expectedMac = import_crypto_js.default.HmacSHA256(parsed.iv + parsed.value, key).toString(import_crypto_js.default.enc.Hex);
|
|
66959
|
+
if (expectedMac !== parsed.mac) {
|
|
66960
|
+
throw new Error("Invalid payload MAC (wrong key or tampered payload).");
|
|
66961
|
+
}
|
|
66962
|
+
const iv = import_crypto_js.default.enc.Base64.parse(parsed.iv);
|
|
66963
|
+
const ciphertext = import_crypto_js.default.enc.Base64.parse(parsed.value);
|
|
66964
|
+
const cipherParams = import_crypto_js.default.lib.CipherParams.create({ ciphertext });
|
|
66965
|
+
const decrypted = import_crypto_js.default.AES.decrypt(cipherParams, key, {
|
|
66966
|
+
iv,
|
|
66967
|
+
mode: import_crypto_js.default.mode.CBC,
|
|
66968
|
+
padding: import_crypto_js.default.pad.Pkcs7
|
|
66969
|
+
});
|
|
66970
|
+
const plaintext = decrypted.toString(import_crypto_js.default.enc.Utf8);
|
|
66971
|
+
if (!plaintext) {
|
|
66972
|
+
throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
|
|
66973
|
+
}
|
|
66974
|
+
return JSON.parse(plaintext);
|
|
66975
|
+
}
|
|
66976
|
+
|
|
67036
66977
|
// src/components/ui/pagination.tsx
|
|
67037
66978
|
var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
|
|
67038
66979
|
function range(start, end) {
|
|
@@ -69964,7 +69905,7 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
69964
69905
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
69965
69906
|
"div",
|
|
69966
69907
|
{
|
|
69967
|
-
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
69908
|
+
className: "fixed inset-0 z-50 flex glass items-center justify-center p-4",
|
|
69968
69909
|
style: { background: "rgba(0,0,0,0.5)" },
|
|
69969
69910
|
onMouseDown: (e) => {
|
|
69970
69911
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -77626,6 +77567,12 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
|
|
|
77626
77567
|
};
|
|
77627
77568
|
return { store, getStore };
|
|
77628
77569
|
}
|
|
77570
|
+
|
|
77571
|
+
// src/core/decryption/decode.ts
|
|
77572
|
+
function decryptResponse(response, key) {
|
|
77573
|
+
const payload = typeof response === "string" ? response : response.data;
|
|
77574
|
+
return decryptLaravelPayload(payload, key);
|
|
77575
|
+
}
|
|
77629
77576
|
return __toCommonJS(index_exports);
|
|
77630
77577
|
})();
|
|
77631
77578
|
/*! Bundled license information:
|
package/dist/index.js
CHANGED
|
@@ -1438,66 +1438,6 @@ import * as React8 from "react";
|
|
|
1438
1438
|
import { createPortal as createPortal2 } from "react-dom";
|
|
1439
1439
|
import axios2 from "axios";
|
|
1440
1440
|
import { Search, Pin, Tag, MoreHorizontal as MoreHorizontal2, AlertCircle, AlertTriangle, Info, X, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, Pencil, Trash, Loader2 } from "lucide-react";
|
|
1441
|
-
|
|
1442
|
-
// src/components/tools/decryptPayload.ts
|
|
1443
|
-
import CryptoJS from "crypto-js";
|
|
1444
|
-
function getLaravelSecretKey() {
|
|
1445
|
-
const viteKey = (() => {
|
|
1446
|
-
try {
|
|
1447
|
-
return new Function("return import.meta.env")();
|
|
1448
|
-
} catch {
|
|
1449
|
-
return void 0;
|
|
1450
|
-
}
|
|
1451
|
-
})()?.VITE_LARAVEL_KEY;
|
|
1452
|
-
const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
|
|
1453
|
-
const windowKey = globalThis?.__LARAVEL_KEY__;
|
|
1454
|
-
const key = viteKey || legacyKey || windowKey;
|
|
1455
|
-
if (!key) {
|
|
1456
|
-
throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env or inject window.__LARAVEL_KEY__ via Blade.");
|
|
1457
|
-
}
|
|
1458
|
-
return key;
|
|
1459
|
-
}
|
|
1460
|
-
function parseLaravelKey(secretKey) {
|
|
1461
|
-
if (secretKey.startsWith("base64:")) {
|
|
1462
|
-
return CryptoJS.enc.Base64.parse(secretKey.slice("base64:".length));
|
|
1463
|
-
}
|
|
1464
|
-
return CryptoJS.enc.Utf8.parse(secretKey);
|
|
1465
|
-
}
|
|
1466
|
-
function parseLaravelEncryptedPayload(payload) {
|
|
1467
|
-
let jsonStr = payload;
|
|
1468
|
-
try {
|
|
1469
|
-
jsonStr = atob(payload);
|
|
1470
|
-
} catch {
|
|
1471
|
-
}
|
|
1472
|
-
return JSON.parse(jsonStr);
|
|
1473
|
-
}
|
|
1474
|
-
function decryptLaravelPayload(payload, secretKey) {
|
|
1475
|
-
const resolvedKey = secretKey ?? getLaravelSecretKey();
|
|
1476
|
-
const parsed = parseLaravelEncryptedPayload(payload);
|
|
1477
|
-
if (parsed.tag) {
|
|
1478
|
-
throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
|
|
1479
|
-
}
|
|
1480
|
-
const key = parseLaravelKey(resolvedKey);
|
|
1481
|
-
const expectedMac = CryptoJS.HmacSHA256(parsed.iv + parsed.value, key).toString(CryptoJS.enc.Hex);
|
|
1482
|
-
if (expectedMac !== parsed.mac) {
|
|
1483
|
-
throw new Error("Invalid payload MAC (wrong key or tampered payload).");
|
|
1484
|
-
}
|
|
1485
|
-
const iv = CryptoJS.enc.Base64.parse(parsed.iv);
|
|
1486
|
-
const ciphertext = CryptoJS.enc.Base64.parse(parsed.value);
|
|
1487
|
-
const cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext });
|
|
1488
|
-
const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
|
|
1489
|
-
iv,
|
|
1490
|
-
mode: CryptoJS.mode.CBC,
|
|
1491
|
-
padding: CryptoJS.pad.Pkcs7
|
|
1492
|
-
});
|
|
1493
|
-
const plaintext = decrypted.toString(CryptoJS.enc.Utf8);
|
|
1494
|
-
if (!plaintext) {
|
|
1495
|
-
throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
|
|
1496
|
-
}
|
|
1497
|
-
return JSON.parse(plaintext);
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
// src/components/ui/bulletin-board.tsx
|
|
1501
1441
|
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1502
1442
|
var PRIORITY_CONFIG = {
|
|
1503
1443
|
urgent: { label: "Urgent", icon: /* @__PURE__ */ jsx10(AlertCircle, { className: "h-3 w-3" }), badge: "error" },
|
|
@@ -1527,82 +1467,13 @@ function AuthorAvatar({ item }) {
|
|
|
1527
1467
|
}
|
|
1528
1468
|
return null;
|
|
1529
1469
|
}
|
|
1530
|
-
function
|
|
1531
|
-
url,
|
|
1532
|
-
params,
|
|
1533
|
-
encrypt,
|
|
1534
|
-
key,
|
|
1535
|
-
decryptPayloadLog,
|
|
1536
|
-
transform
|
|
1537
|
-
}) {
|
|
1538
|
-
const [items, setItems] = React8.useState([]);
|
|
1539
|
-
const [currentPage, setCurrentPage] = React8.useState(1);
|
|
1540
|
-
const [pagination, setPagination] = React8.useState(null);
|
|
1541
|
-
const [loading, setLoading] = React8.useState(false);
|
|
1542
|
-
const [error, setError] = React8.useState(null);
|
|
1543
|
-
const [tick, setTick] = React8.useState(0);
|
|
1544
|
-
React8.useEffect(() => {
|
|
1545
|
-
let cancelled = false;
|
|
1546
|
-
setLoading(true);
|
|
1547
|
-
setError(null);
|
|
1548
|
-
axios2.get(url, { params: { ...params, page: currentPage } }).then(({ data: res }) => {
|
|
1549
|
-
if (cancelled) return;
|
|
1550
|
-
let payload;
|
|
1551
|
-
try {
|
|
1552
|
-
payload = encrypt ? decryptLaravelPayload(res, key) : res;
|
|
1553
|
-
} catch (decryptErr) {
|
|
1554
|
-
console.error("[useServerBulletin] decryption failed:", decryptErr?.message ?? decryptErr);
|
|
1555
|
-
if (!cancelled) setError(decryptErr?.message ?? "Decryption failed");
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
if (encrypt && decryptPayloadLog) console.log("[useServerBulletin] decrypted:", payload);
|
|
1559
|
-
const rows = Array.isArray(payload) ? payload : payload.data ?? [];
|
|
1560
|
-
setItems(transform ? rows.map(transform) : rows);
|
|
1561
|
-
if (!Array.isArray(payload)) {
|
|
1562
|
-
const rawTotal = payload.total;
|
|
1563
|
-
const rawPerPage = payload.per_page;
|
|
1564
|
-
const rawLastPage = payload.last_page;
|
|
1565
|
-
const lastPage = rawLastPage ?? Math.ceil(rawTotal / rawPerPage);
|
|
1566
|
-
const pg = payload.pagination ?? {
|
|
1567
|
-
first_page_url: payload.first_page_url ?? `${url}?page=1`,
|
|
1568
|
-
last_page_url: payload.last_page_url ?? `${url}?page=${lastPage}`,
|
|
1569
|
-
last_page: lastPage,
|
|
1570
|
-
next_page_url: payload.next_page_url !== void 0 ? payload.next_page_url : currentPage < lastPage ? `${url}?page=${currentPage + 1}` : null,
|
|
1571
|
-
prev_page_url: payload.prev_page_url !== void 0 ? payload.prev_page_url : currentPage > 1 ? `${url}?page=${currentPage - 1}` : null,
|
|
1572
|
-
per_page: rawPerPage,
|
|
1573
|
-
total: rawTotal,
|
|
1574
|
-
links: payload.links ?? []
|
|
1575
|
-
};
|
|
1576
|
-
setPagination(pg);
|
|
1577
|
-
}
|
|
1578
|
-
}).catch((err) => {
|
|
1579
|
-
if (cancelled) return;
|
|
1580
|
-
setError(err?.response?.data?.message ?? err.message ?? "Request failed");
|
|
1581
|
-
}).finally(() => {
|
|
1582
|
-
if (!cancelled) setLoading(false);
|
|
1583
|
-
});
|
|
1584
|
-
return () => {
|
|
1585
|
-
cancelled = true;
|
|
1586
|
-
};
|
|
1587
|
-
}, [url, currentPage, tick, JSON.stringify(params), encrypt, decryptPayloadLog]);
|
|
1588
|
-
return {
|
|
1589
|
-
items,
|
|
1590
|
-
currentPage,
|
|
1591
|
-
pagination,
|
|
1592
|
-
serverPagination: pagination ? { pagination, currentPage, goToPage: (p) => setCurrentPage(p) } : null,
|
|
1593
|
-
loading,
|
|
1594
|
-
error,
|
|
1595
|
-
goToPage: (p) => setCurrentPage(p),
|
|
1596
|
-
reload: () => setTick((t) => t + 1)
|
|
1597
|
-
};
|
|
1598
|
-
}
|
|
1599
|
-
function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions }) {
|
|
1470
|
+
function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions, headerAction, footerAction }) {
|
|
1600
1471
|
const priority = item.priority ? PRIORITY_CONFIG[item.priority] : null;
|
|
1601
1472
|
return createPortal2(
|
|
1602
1473
|
/* @__PURE__ */ jsx10(
|
|
1603
1474
|
"div",
|
|
1604
1475
|
{
|
|
1605
|
-
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
1476
|
+
className: "fixed inset-0 z-50 flex glass items-center justify-center p-4",
|
|
1606
1477
|
style: { background: "rgba(0,0,0,0.55)" },
|
|
1607
1478
|
onMouseDown: (e) => {
|
|
1608
1479
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -1615,7 +1486,8 @@ function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customAction
|
|
|
1615
1486
|
" Pinned"
|
|
1616
1487
|
] }),
|
|
1617
1488
|
priority && /* @__PURE__ */ jsx10(Badge, { variant: priority.badge, size: "sm", icon: priority.icon ?? void 0, children: priority.label }),
|
|
1618
|
-
item.category && /* @__PURE__ */ jsx10(Badge, { variant: "outline", size: "sm", children: item.category })
|
|
1489
|
+
item.category && /* @__PURE__ */ jsx10(Badge, { variant: "outline", size: "sm", children: item.category }),
|
|
1490
|
+
headerAction
|
|
1619
1491
|
] }),
|
|
1620
1492
|
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
1621
1493
|
onEdit && /* @__PURE__ */ jsx10(
|
|
@@ -1714,6 +1586,7 @@ function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customAction
|
|
|
1714
1586
|
] })
|
|
1715
1587
|
] }) }),
|
|
1716
1588
|
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
|
|
1589
|
+
footerAction,
|
|
1717
1590
|
onEdit && /* @__PURE__ */ jsxs9(
|
|
1718
1591
|
"button",
|
|
1719
1592
|
{
|
|
@@ -2108,6 +1981,9 @@ function BulletinBoard({
|
|
|
2108
1981
|
deleteBaseUrl,
|
|
2109
1982
|
deleteIdKey = "id",
|
|
2110
1983
|
serverPagination,
|
|
1984
|
+
footerAction,
|
|
1985
|
+
headerPreviewAction,
|
|
1986
|
+
footerPreviewAction,
|
|
2111
1987
|
className
|
|
2112
1988
|
}) {
|
|
2113
1989
|
const [previewItem, setPreviewItem] = React8.useState(null);
|
|
@@ -2192,6 +2068,7 @@ function BulletinBoard({
|
|
|
2192
2068
|
/* @__PURE__ */ jsx10(Pin, { className: "h-8 w-8 opacity-20" }),
|
|
2193
2069
|
emptyMessage
|
|
2194
2070
|
] }) : layout === "list" ? /* @__PURE__ */ jsx10("div", { className: "flex flex-col gap-3", children: filtered.map((item) => /* @__PURE__ */ jsx10(BulletinCard, { item, variant, layout: "list", onClick: handleCardClick }, item.id)) }) : layout === "masonry" ? /* @__PURE__ */ jsx10("div", { className: cn("gap-4", COLS_CLASS[columns]), style: { display: "grid", gridTemplateRows: "masonry" }, children: filtered.map((item) => /* @__PURE__ */ jsx10(BulletinCard, { item, variant, layout: "masonry", onClick: handleCardClick }, item.id)) }) : /* @__PURE__ */ jsx10("div", { className: cn("grid gap-4", COLS_CLASS[columns]), children: filtered.map((item) => /* @__PURE__ */ jsx10(BulletinCard, { item, variant, layout: "grid", onClick: handleCardClick }, item.id)) }),
|
|
2071
|
+
footerAction && /* @__PURE__ */ jsx10("div", { children: footerAction }),
|
|
2195
2072
|
serverPagination && (() => {
|
|
2196
2073
|
const { pagination, currentPage: cp, goToPage } = serverPagination;
|
|
2197
2074
|
const totalPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
|
|
@@ -2268,7 +2145,9 @@ function BulletinBoard({
|
|
|
2268
2145
|
setDeleteItem(item);
|
|
2269
2146
|
} : onDelete ? (item) => {
|
|
2270
2147
|
onDelete(item);
|
|
2271
|
-
} : void 0
|
|
2148
|
+
} : void 0,
|
|
2149
|
+
footerAction: footerPreviewAction ? footerPreviewAction(previewItem) : void 0,
|
|
2150
|
+
headerAction: headerPreviewAction
|
|
2272
2151
|
}
|
|
2273
2152
|
),
|
|
2274
2153
|
editItem && editBaseUrl && editFields && /* @__PURE__ */ jsx10(
|
|
@@ -3742,6 +3621,66 @@ function MetricRow({ items, divided = true, className }) {
|
|
|
3742
3621
|
// src/components/ui/data-grid.tsx
|
|
3743
3622
|
import * as React29 from "react";
|
|
3744
3623
|
import { createPortal as createPortal4 } from "react-dom";
|
|
3624
|
+
|
|
3625
|
+
// src/components/tools/decryptPayload.ts
|
|
3626
|
+
import CryptoJS from "crypto-js";
|
|
3627
|
+
function getLaravelSecretKey() {
|
|
3628
|
+
const viteKey = (() => {
|
|
3629
|
+
try {
|
|
3630
|
+
return new Function("return import.meta.env")();
|
|
3631
|
+
} catch {
|
|
3632
|
+
return void 0;
|
|
3633
|
+
}
|
|
3634
|
+
})()?.VITE_LARAVEL_KEY;
|
|
3635
|
+
const legacyKey = globalThis?.process?.env?.REACT_APP_LARAVEL_KEY;
|
|
3636
|
+
const windowKey = globalThis?.__LARAVEL_KEY__;
|
|
3637
|
+
const key = viteKey || legacyKey || windowKey;
|
|
3638
|
+
if (!key) {
|
|
3639
|
+
throw new Error("Missing Laravel decryption key. Set VITE_LARAVEL_KEY in your .env or inject window.__LARAVEL_KEY__ via Blade.");
|
|
3640
|
+
}
|
|
3641
|
+
return key;
|
|
3642
|
+
}
|
|
3643
|
+
function parseLaravelKey(secretKey) {
|
|
3644
|
+
if (secretKey.startsWith("base64:")) {
|
|
3645
|
+
return CryptoJS.enc.Base64.parse(secretKey.slice("base64:".length));
|
|
3646
|
+
}
|
|
3647
|
+
return CryptoJS.enc.Utf8.parse(secretKey);
|
|
3648
|
+
}
|
|
3649
|
+
function parseLaravelEncryptedPayload(payload) {
|
|
3650
|
+
let jsonStr = payload;
|
|
3651
|
+
try {
|
|
3652
|
+
jsonStr = atob(payload);
|
|
3653
|
+
} catch {
|
|
3654
|
+
}
|
|
3655
|
+
return JSON.parse(jsonStr);
|
|
3656
|
+
}
|
|
3657
|
+
function decryptLaravelPayload(payload, secretKey) {
|
|
3658
|
+
const resolvedKey = secretKey ?? getLaravelSecretKey();
|
|
3659
|
+
const parsed = parseLaravelEncryptedPayload(payload);
|
|
3660
|
+
if (parsed.tag) {
|
|
3661
|
+
throw new Error("Unsupported Laravel cipher (AEAD tag present). Expected AES-*-CBC payload.");
|
|
3662
|
+
}
|
|
3663
|
+
const key = parseLaravelKey(resolvedKey);
|
|
3664
|
+
const expectedMac = CryptoJS.HmacSHA256(parsed.iv + parsed.value, key).toString(CryptoJS.enc.Hex);
|
|
3665
|
+
if (expectedMac !== parsed.mac) {
|
|
3666
|
+
throw new Error("Invalid payload MAC (wrong key or tampered payload).");
|
|
3667
|
+
}
|
|
3668
|
+
const iv = CryptoJS.enc.Base64.parse(parsed.iv);
|
|
3669
|
+
const ciphertext = CryptoJS.enc.Base64.parse(parsed.value);
|
|
3670
|
+
const cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext });
|
|
3671
|
+
const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
|
|
3672
|
+
iv,
|
|
3673
|
+
mode: CryptoJS.mode.CBC,
|
|
3674
|
+
padding: CryptoJS.pad.Pkcs7
|
|
3675
|
+
});
|
|
3676
|
+
const plaintext = decrypted.toString(CryptoJS.enc.Utf8);
|
|
3677
|
+
if (!plaintext) {
|
|
3678
|
+
throw new Error("Decryption produced empty plaintext (wrong key/cipher).");
|
|
3679
|
+
}
|
|
3680
|
+
return JSON.parse(plaintext);
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3683
|
+
// src/components/ui/data-grid.tsx
|
|
3745
3684
|
import axios4 from "axios";
|
|
3746
3685
|
import { ChevronUp as ChevronUp2, ChevronDown as ChevronDown5, ChevronsUpDown as ChevronsUpDown2, ChevronLeft as ChevronLeft7, ChevronRight as ChevronRight9, Search as Search6, Settings2, Check as Check5, Eye as Eye3, Pencil as Pencil3, Trash as Trash4, Loader2 as Loader23, X as X10 } from "lucide-react";
|
|
3747
3686
|
|
|
@@ -6685,7 +6624,7 @@ function ModalShell({ title, onClose, children, footer, width = "lg" }) {
|
|
|
6685
6624
|
/* @__PURE__ */ jsx32(
|
|
6686
6625
|
"div",
|
|
6687
6626
|
{
|
|
6688
|
-
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
6627
|
+
className: "fixed inset-0 z-50 flex glass items-center justify-center p-4",
|
|
6689
6628
|
style: { background: "rgba(0,0,0,0.5)" },
|
|
6690
6629
|
onMouseDown: (e) => {
|
|
6691
6630
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -13696,6 +13635,12 @@ function createStore(initialValue, sessionName, expireInterval) {
|
|
|
13696
13635
|
};
|
|
13697
13636
|
return { store, getStore };
|
|
13698
13637
|
}
|
|
13638
|
+
|
|
13639
|
+
// src/core/decryption/decode.ts
|
|
13640
|
+
function decryptResponse(response, key) {
|
|
13641
|
+
const payload = typeof response === "string" ? response : response.data;
|
|
13642
|
+
return decryptLaravelPayload(payload, key);
|
|
13643
|
+
}
|
|
13699
13644
|
export {
|
|
13700
13645
|
Accordion,
|
|
13701
13646
|
Authentication,
|
|
@@ -13795,7 +13740,11 @@ export {
|
|
|
13795
13740
|
Wizard,
|
|
13796
13741
|
api,
|
|
13797
13742
|
createStore,
|
|
13798
|
-
|
|
13743
|
+
decryptLaravelPayload,
|
|
13744
|
+
decryptResponse,
|
|
13745
|
+
getLaravelSecretKey,
|
|
13746
|
+
parseLaravelEncryptedPayload,
|
|
13747
|
+
parseLaravelKey,
|
|
13799
13748
|
useServerDataGrid,
|
|
13800
13749
|
useServerTable,
|
|
13801
13750
|
useTheme,
|