@carlonicora/nextjs-jsonapi 1.0.4 → 1.0.5
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/{AbstractService-BKlpJA61.d.mts → AbstractService-B2n_JdiC.d.mts} +1 -1
- package/dist/{AbstractService-D9eSVKNa.d.ts → AbstractService-DtQTYovo.d.ts} +1 -1
- package/dist/{content.interface-Dg2lt_An.d.mts → AuthComponent-CPLvEerw.d.mts} +11 -15
- package/dist/{content.interface-BhyAiOFq.d.ts → AuthComponent-m6Qp4Hz6.d.ts} +11 -15
- package/dist/{BlockNoteEditor-UVO3VZZE.mjs → BlockNoteEditor-BLVXQPXV.mjs} +14 -18
- package/dist/{BlockNoteEditor-UVO3VZZE.mjs.map → BlockNoteEditor-BLVXQPXV.mjs.map} +1 -1
- package/dist/{BlockNoteEditor-VFWG6LXI.js → BlockNoteEditor-ZTDHULFT.js} +15 -19
- package/dist/BlockNoteEditor-ZTDHULFT.js.map +1 -0
- package/dist/JsonApiRequest-O7BGUMFO.mjs +23 -0
- package/dist/JsonApiRequest-VARLNKAF.js +23 -0
- package/dist/JsonApiRequest-VARLNKAF.js.map +1 -0
- package/dist/chunk-2LM6LCJW.mjs +1 -0
- package/dist/chunk-3APORDYP.mjs +7783 -0
- package/dist/chunk-3APORDYP.mjs.map +1 -0
- package/dist/{chunk-TMVHSY3Y.js → chunk-5ZEADNNP.js} +36 -17
- package/dist/chunk-5ZEADNNP.js.map +1 -0
- package/dist/{chunk-ECDTZBYO.mjs → chunk-74F6BBHH.mjs} +21 -2
- package/dist/chunk-74F6BBHH.mjs.map +1 -0
- package/dist/{chunk-GYWPEPOH.mjs → chunk-7C5RAEBO.mjs} +72 -68
- package/dist/chunk-7C5RAEBO.mjs.map +1 -0
- package/dist/chunk-A5DDIABK.js +1 -0
- package/dist/{chunk-TEGF6ZWG.js → chunk-AGAJMJ4T.js} +47 -9
- package/dist/chunk-AGAJMJ4T.js.map +1 -0
- package/dist/{chunk-CXQOWQSY.js → chunk-AYHKQWHH.js} +15 -2
- package/dist/chunk-AYHKQWHH.js.map +1 -0
- package/dist/{chunk-I2REI7OA.js → chunk-HMHGLXWC.js} +33 -15
- package/dist/chunk-HMHGLXWC.js.map +1 -0
- package/dist/chunk-IWFGEPAA.mjs +1 -0
- package/dist/chunk-JC3WJK65.js +1 -0
- package/dist/{chunk-L6EQEAXU.mjs → chunk-PYF2U6WG.mjs} +25 -7
- package/dist/chunk-PYF2U6WG.mjs.map +1 -0
- package/dist/{chunk-YDVTFM7X.mjs → chunk-RBIVEH2K.mjs} +42 -4
- package/dist/chunk-RBIVEH2K.mjs.map +1 -0
- package/dist/{chunk-V2JJPI7N.js → chunk-RZO2LOW4.js} +237 -233
- package/dist/chunk-RZO2LOW4.js.map +1 -0
- package/dist/{chunk-X4BIHJ2B.mjs → chunk-SM63SZCP.mjs} +15 -2
- package/dist/chunk-SM63SZCP.mjs.map +1 -0
- package/dist/chunk-WEC4YMOS.js +7783 -0
- package/dist/chunk-WEC4YMOS.js.map +1 -0
- package/dist/client/index.d.mts +21 -2
- package/dist/client/index.d.ts +21 -2
- package/dist/client/index.js +18 -245
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +28 -255
- package/dist/client/index.mjs.map +1 -1
- package/dist/components/index.d.mts +293 -8
- package/dist/components/index.d.ts +293 -8
- package/dist/components/index.js +78 -2323
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +172 -2417
- package/dist/components/index.mjs.map +1 -1
- package/dist/{config-hXufftVS.d.ts → config-BmnK65TD.d.mts} +1 -0
- package/dist/{config-hXufftVS.d.mts → config-BmnK65TD.d.ts} +1 -0
- package/dist/config-DQeAo9Kf.d.mts +49 -0
- package/dist/config-DQeAo9Kf.d.ts +49 -0
- package/dist/contexts/index.d.mts +109 -21
- package/dist/contexts/index.d.ts +109 -21
- package/dist/contexts/index.js +39 -7
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +40 -8
- package/dist/core/index.d.mts +3 -4
- package/dist/core/index.d.ts +3 -4
- package/dist/core/index.js +3 -7
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +4 -8
- package/dist/{d3.link.interface-QMdB22bC.d.mts → d3.link.interface-ClC4Irqp.d.mts} +2 -1
- package/dist/{d3.link.interface-QMdB22bC.d.ts → d3.link.interface-ClC4Irqp.d.ts} +2 -1
- package/dist/features/index.d.mts +17 -86
- package/dist/features/index.d.ts +17 -86
- package/dist/features/index.js +7 -16
- package/dist/features/index.js.map +1 -1
- package/dist/features/index.mjs +10 -19
- package/dist/hooks/index.d.mts +18 -43
- package/dist/hooks/index.d.ts +18 -43
- package/dist/hooks/index.js +20 -7
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +19 -6
- package/dist/index.d.mts +10 -6
- package/dist/index.d.ts +10 -6
- package/dist/index.js +13 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -19
- package/dist/interfaces/index.d.mts +2 -1
- package/dist/interfaces/index.d.ts +2 -1
- package/dist/notification.interface-BBgMUdLR.d.mts +14 -0
- package/dist/notification.interface-gyvT-Z2F.d.ts +14 -0
- package/dist/permissions/index.d.mts +2 -3
- package/dist/permissions/index.d.ts +2 -3
- package/dist/server/index.d.mts +38 -18
- package/dist/server/index.d.ts +38 -18
- package/dist/server/index.js +70 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +68 -0
- package/dist/server/index.mjs.map +1 -1
- package/dist/types-BUAlgqqh.d.ts +39 -0
- package/dist/{types-DluCaP1I.d.ts → types-Bl61ob-7.d.mts} +19 -2
- package/dist/{types-lQVA8d_P.d.mts → types-Bl61ob-7.d.ts} +19 -2
- package/dist/types-iVdVY7ba.d.mts +39 -0
- package/dist/useSocket-Cn7fB_B1.d.mts +25 -0
- package/dist/useSocket-DzMKRKCA.d.ts +25 -0
- package/dist/user.fields-CbdObSmS.d.mts +18 -0
- package/dist/user.fields-CbdObSmS.d.ts +18 -0
- package/dist/utils/index.d.mts +1 -2
- package/dist/utils/index.d.ts +1 -2
- package/package.json +5 -3
- package/src/client/index.ts +13 -0
- package/src/components/forms/index.ts +1 -0
- package/src/components/index.ts +5 -0
- package/src/components/tables/ContentListTable.tsx +1 -0
- package/src/contexts/CommonContext.tsx +52 -0
- package/src/contexts/SharedContext.tsx +2 -0
- package/src/contexts/SocketContext.tsx +65 -0
- package/src/contexts/index.ts +6 -1
- package/src/features/auth/components/containers/AuthContainer.tsx +32 -0
- package/src/features/auth/components/containers/index.ts +1 -0
- package/src/features/auth/components/details/LandingComponent.tsx +39 -0
- package/src/features/auth/components/details/index.ts +1 -0
- package/src/features/auth/components/forms/AcceptInvitation.tsx +136 -0
- package/src/features/auth/components/forms/ActivateAccount.tsx +75 -0
- package/src/features/auth/components/forms/Cookies.tsx +32 -0
- package/src/features/auth/components/forms/ForgotPassword.tsx +108 -0
- package/src/features/auth/components/forms/Login.tsx +118 -0
- package/src/features/auth/components/forms/Logout.tsx +19 -0
- package/src/features/auth/components/forms/RefreshUser.tsx +39 -0
- package/src/features/auth/components/forms/Register.tsx +150 -0
- package/src/features/auth/components/forms/ResetPassword.tsx +126 -0
- package/src/features/auth/components/forms/index.ts +9 -0
- package/src/features/auth/components/index.ts +3 -0
- package/src/features/auth/contexts/AuthContext.tsx +77 -0
- package/src/features/auth/contexts/index.ts +1 -0
- package/src/features/auth/enums/AuthComponent.ts +9 -0
- package/src/features/auth/enums/index.ts +1 -0
- package/src/features/auth/index.ts +2 -1
- package/src/features/auth/utils/AuthCookies.ts +134 -0
- package/src/features/auth/utils/index.ts +1 -0
- package/src/features/company/components/containers/AdminCompanyContainer.tsx +26 -0
- package/src/features/company/components/containers/CompanyContainer.tsx +17 -0
- package/src/features/company/components/containers/index.ts +2 -0
- package/src/features/company/components/details/CompanyDetails.tsx +26 -0
- package/src/features/company/components/details/index.ts +1 -0
- package/src/features/company/components/forms/CompanyConfigurationEditor.tsx +151 -0
- package/src/features/company/components/forms/CompanyConfigurationSecurityForm.tsx +97 -0
- package/src/features/company/components/forms/CompanyDeleter.tsx +121 -0
- package/src/features/company/components/forms/CompanyEditor.tsx +245 -0
- package/src/features/company/components/forms/CompanyLicense.tsx +213 -0
- package/src/features/company/components/forms/index.ts +5 -0
- package/src/features/company/components/index.ts +4 -0
- package/src/features/company/components/lists/CompaniesList.tsx +31 -0
- package/src/features/company/components/lists/index.ts +1 -0
- package/src/features/company/contexts/CompanyContext.tsx +99 -0
- package/src/features/company/contexts/index.ts +0 -0
- package/src/features/company/hooks/index.ts +1 -0
- package/src/features/company/hooks/useCompanyTableStructure.tsx +82 -0
- package/src/features/feature/components/forms/FormFeatures.tsx +141 -140
- package/src/features/feature/components/forms/index.ts +1 -0
- package/src/features/feature/components/index.ts +1 -1
- package/src/features/feature/index.ts +1 -2
- package/src/features/module/index.ts +1 -1
- package/src/features/notification/components/common/NotificationErrorBoundary.tsx +51 -0
- package/src/features/notification/components/common/index.ts +1 -0
- package/src/features/notification/components/containers/NotificationsListContainer.tsx +44 -0
- package/src/features/notification/components/containers/index.ts +1 -0
- package/src/features/notification/components/index.ts +5 -0
- package/src/features/notification/components/lists/NotificationsList.tsx +129 -0
- package/src/features/notification/components/lists/index.ts +1 -0
- package/src/features/notification/components/modals/NotificationModal.tsx +220 -0
- package/src/features/notification/components/modals/index.ts +1 -0
- package/src/features/notification/components/notifications/Notification.tsx +120 -0
- package/src/features/notification/components/notifications/PushNotificationProvider.tsx +9 -0
- package/src/features/notification/components/notifications/index.ts +2 -0
- package/src/features/notification/contexts/NotificationContext.tsx +187 -0
- package/src/features/notification/contexts/index.ts +1 -0
- package/src/features/notification/index.ts +1 -1
- package/src/features/push/index.ts +1 -1
- package/src/features/role/components/containers/RoleContainer.tsx +18 -0
- package/src/features/role/components/containers/index.ts +1 -0
- package/src/features/role/components/details/RoleDetails.tsx +21 -0
- package/src/features/role/components/details/index.ts +1 -0
- package/src/features/role/components/forms/FormRoles.tsx +82 -0
- package/src/features/role/components/forms/RemoveUserFromRole.tsx +108 -0
- package/src/features/role/components/forms/UserRoleAdd.tsx +128 -0
- package/src/features/role/components/forms/index.ts +3 -0
- package/src/features/role/components/index.ts +4 -0
- package/src/features/role/components/lists/RolesList.tsx +27 -0
- package/src/features/role/components/lists/UserRolesList.tsx +31 -0
- package/src/features/role/components/lists/index.ts +2 -0
- package/src/features/role/contexts/RoleContext.tsx +84 -0
- package/src/features/role/contexts/index.ts +1 -0
- package/src/features/role/hooks/index.ts +1 -0
- package/src/features/role/hooks/useRoleTableStructure.tsx +72 -0
- package/src/features/s3/index.ts +1 -1
- package/src/features/user/components/containers/UserContainer.tsx +23 -0
- package/src/features/user/components/containers/UserIndexContainer.tsx +12 -0
- package/src/features/user/components/containers/UsersListContainer.tsx +36 -0
- package/src/features/user/components/containers/index.ts +3 -0
- package/src/features/user/components/details/UserDetails.tsx +74 -0
- package/src/features/user/components/details/UserIndexDetails.tsx +28 -0
- package/src/features/user/components/details/index.ts +2 -0
- package/src/features/user/components/forms/RoleUserAdd.tsx +93 -0
- package/src/features/user/components/forms/UserAvatarEditor.tsx +78 -0
- package/src/features/user/components/forms/UserDeleter.tsx +49 -0
- package/src/features/user/components/forms/UserEditor.tsx +319 -0
- package/src/features/user/components/forms/UserMultiSelect.tsx +218 -0
- package/src/features/user/components/forms/UserReactivator.tsx +79 -0
- package/src/features/user/components/forms/UserResentInvitationEmail.tsx +88 -0
- package/src/features/user/components/forms/UserSelector.tsx +185 -0
- package/src/features/user/components/forms/index.ts +8 -0
- package/src/features/user/components/index.ts +3 -0
- package/src/features/user/components/lists/AdminUsersList.tsx +41 -0
- package/src/features/user/components/lists/CompanyUsersList.tsx +44 -0
- package/src/features/user/components/lists/RelevantUsersList.tsx +30 -0
- package/src/features/user/components/lists/RoleUsersList.tsx +31 -0
- package/src/features/user/components/lists/UserListInAdd.tsx +53 -0
- package/src/features/user/components/lists/UsersList.tsx +30 -0
- package/src/features/user/components/lists/UsersListByContentIds.tsx +30 -0
- package/src/features/user/components/lists/index.ts +7 -0
- package/src/features/user/components/widgets/UserAvatarList.tsx +31 -0
- package/src/features/user/components/widgets/UserSearchPopover.tsx +89 -0
- package/src/features/user/contexts/UserContext.tsx +106 -0
- package/src/features/user/contexts/index.ts +1 -0
- package/src/features/user/hooks/index.ts +2 -0
- package/src/features/user/hooks/useUserSearch.ts +53 -0
- package/src/features/user/hooks/useUserTableStructure.tsx +115 -0
- package/src/features/user/index.ts +0 -1
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useCustomD3Graph.tsx +2 -0
- package/src/hooks/useNotificationSync.ts +20 -0
- package/src/hooks/usePageTracker.ts +69 -0
- package/src/hooks/usePushNotifications.ts +82 -0
- package/src/hooks/useSocket.ts +201 -0
- package/src/hooks/useTableGenerator.ts +6 -2
- package/src/i18n/config.ts +1 -0
- package/src/index.ts +4 -0
- package/src/interfaces/d3.link.interface.ts +2 -1
- package/src/server/ServerSession.ts +103 -0
- package/src/server/index.ts +2 -1
- package/src/unified/JsonApiRequest.ts +23 -0
- package/dist/ApiRequestDataTypeInterface-CUKFDBx2.d.mts +0 -20
- package/dist/ApiRequestDataTypeInterface-CUKFDBx2.d.ts +0 -20
- package/dist/BlockNoteEditor-VFWG6LXI.js.map +0 -1
- package/dist/JsonApiRequest-S3ICLM7B.mjs +0 -20
- package/dist/JsonApiRequest-ZZLSP26T.js +0 -20
- package/dist/JsonApiRequest-ZZLSP26T.js.map +0 -1
- package/dist/chunk-366S2JCC.mjs +0 -31
- package/dist/chunk-366S2JCC.mjs.map +0 -1
- package/dist/chunk-5W6AKZE6.mjs +0 -131
- package/dist/chunk-5W6AKZE6.mjs.map +0 -1
- package/dist/chunk-A3J3AAYM.mjs +0 -97
- package/dist/chunk-A3J3AAYM.mjs.map +0 -1
- package/dist/chunk-AWONBQQP.js +0 -97
- package/dist/chunk-AWONBQQP.js.map +0 -1
- package/dist/chunk-CXQOWQSY.js.map +0 -1
- package/dist/chunk-DKKMWBP4.mjs +0 -1
- package/dist/chunk-DKKMWBP4.mjs.map +0 -1
- package/dist/chunk-DO2HLAZO.js +0 -48
- package/dist/chunk-DO2HLAZO.js.map +0 -1
- package/dist/chunk-DZXDB3K2.mjs +0 -17
- package/dist/chunk-DZXDB3K2.mjs.map +0 -1
- package/dist/chunk-ECDTZBYO.mjs.map +0 -1
- package/dist/chunk-FY4SXJGU.js +0 -806
- package/dist/chunk-FY4SXJGU.js.map +0 -1
- package/dist/chunk-GYWPEPOH.mjs.map +0 -1
- package/dist/chunk-H6FMOA6B.js +0 -1
- package/dist/chunk-H6FMOA6B.js.map +0 -1
- package/dist/chunk-I2REI7OA.js.map +0 -1
- package/dist/chunk-J4Q36PMP.js +0 -31
- package/dist/chunk-J4Q36PMP.js.map +0 -1
- package/dist/chunk-L6EQEAXU.mjs.map +0 -1
- package/dist/chunk-MFO27OHB.mjs +0 -48
- package/dist/chunk-MFO27OHB.mjs.map +0 -1
- package/dist/chunk-RAF7PNLG.js +0 -131
- package/dist/chunk-RAF7PNLG.js.map +0 -1
- package/dist/chunk-RUR22SVM.js +0 -17
- package/dist/chunk-RUR22SVM.js.map +0 -1
- package/dist/chunk-TEGF6ZWG.js.map +0 -1
- package/dist/chunk-TMVHSY3Y.js.map +0 -1
- package/dist/chunk-V2JJPI7N.js.map +0 -1
- package/dist/chunk-WWWMJZEF.mjs +0 -806
- package/dist/chunk-WWWMJZEF.mjs.map +0 -1
- package/dist/chunk-X4BIHJ2B.mjs.map +0 -1
- package/dist/chunk-YDVTFM7X.mjs.map +0 -1
- /package/dist/{JsonApiRequest-S3ICLM7B.mjs.map → JsonApiRequest-O7BGUMFO.mjs.map} +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useAtom } from "jotai";
|
|
4
|
+
import { usePathname } from "next/navigation";
|
|
5
|
+
import { useEffect } from "react";
|
|
6
|
+
import { RecentPage, recentPagesAtom } from "../atoms";
|
|
7
|
+
import { getTrackablePages } from "../unified/JsonApiRequest";
|
|
8
|
+
|
|
9
|
+
// Routes to exclude from tracking
|
|
10
|
+
const EXCLUDED_ROUTES = ["/", "/login", "/register", "/forgot-password", "/reset-password", "/activate"];
|
|
11
|
+
|
|
12
|
+
export function usePageTracker() {
|
|
13
|
+
const pathname = usePathname();
|
|
14
|
+
const [recentPages, setRecentPages] = useAtom(recentPagesAtom);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!pathname) return;
|
|
18
|
+
|
|
19
|
+
// Exclude certain routes
|
|
20
|
+
if (EXCLUDED_ROUTES.some((route) => pathname === route || pathname.endsWith(route))) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Extract page information from pathname (already locale-free from next-intl)
|
|
25
|
+
const pathParts = pathname.split("/").filter(Boolean);
|
|
26
|
+
|
|
27
|
+
if (pathParts.length === 0) return;
|
|
28
|
+
|
|
29
|
+
// Try to find the module based on the route
|
|
30
|
+
const moduleName = pathParts[0];
|
|
31
|
+
const entityId = pathParts.length > 1 ? pathParts[1] : null;
|
|
32
|
+
|
|
33
|
+
// Only track pages with entity IDs (detail pages)
|
|
34
|
+
if (!entityId) return;
|
|
35
|
+
|
|
36
|
+
// Find the module from configured trackable pages
|
|
37
|
+
const trackablePages = getTrackablePages();
|
|
38
|
+
const foundModule = trackablePages.find((mod) => mod.pageUrl === `/${moduleName}`);
|
|
39
|
+
|
|
40
|
+
if (!foundModule) return;
|
|
41
|
+
|
|
42
|
+
// Only use base path (module/id), ignoring any sub-paths
|
|
43
|
+
const baseUrl = `/${moduleName}/${entityId}`;
|
|
44
|
+
|
|
45
|
+
let pageTitle = foundModule.name;
|
|
46
|
+
if (typeof document !== "undefined") {
|
|
47
|
+
const titleParts = document.title.split("]");
|
|
48
|
+
if (titleParts[1]) {
|
|
49
|
+
const cleanTitle = titleParts[1].split("|")[0]?.trim();
|
|
50
|
+
pageTitle = cleanTitle || foundModule.name;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const newPage: RecentPage = {
|
|
55
|
+
url: baseUrl,
|
|
56
|
+
title: pageTitle,
|
|
57
|
+
moduleType: foundModule.name,
|
|
58
|
+
timestamp: Date.now(),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
setRecentPages((prev) => {
|
|
62
|
+
// Remove if already exists (to move to top)
|
|
63
|
+
const filtered = prev.filter((page) => page.url !== newPage.url);
|
|
64
|
+
|
|
65
|
+
// Add to beginning and limit to 10
|
|
66
|
+
return [newPage, ...filtered].slice(0, 10);
|
|
67
|
+
});
|
|
68
|
+
}, [pathname, setRecentPages]);
|
|
69
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { useCurrentUserContext } from "../contexts";
|
|
5
|
+
import { PushService, UserInterface } from "../features";
|
|
6
|
+
import { getRoleId } from "../roles";
|
|
7
|
+
import { getAppUrl } from "../unified/JsonApiRequest";
|
|
8
|
+
|
|
9
|
+
function urlBase64ToUint8Array(base64String: string): Uint8Array {
|
|
10
|
+
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
|
|
11
|
+
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
|
|
12
|
+
const rawData = window.atob(base64);
|
|
13
|
+
const outputArray = new Uint8Array(rawData.length);
|
|
14
|
+
for (let i = 0; i < rawData.length; i++) {
|
|
15
|
+
outputArray[i] = rawData.charCodeAt(i);
|
|
16
|
+
}
|
|
17
|
+
return outputArray;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function usePushNotifications(): void {
|
|
21
|
+
const { currentUser, hasRole } = useCurrentUserContext<UserInterface>();
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const register = async () => {
|
|
25
|
+
if ("serviceWorker" in navigator && "PushManager" in window) {
|
|
26
|
+
try {
|
|
27
|
+
// Check if we've already processed push notifications for this user in this session
|
|
28
|
+
const sessionKey = `push_registered_${currentUser?.id}`;
|
|
29
|
+
const lastRegisteredSubscription = sessionStorage.getItem(sessionKey);
|
|
30
|
+
|
|
31
|
+
const registration = await navigator.serviceWorker.register(`${getAppUrl()}/sw.js`);
|
|
32
|
+
|
|
33
|
+
// Check current permission status first
|
|
34
|
+
let permission = Notification.permission;
|
|
35
|
+
|
|
36
|
+
// Only request permission if it's not already determined
|
|
37
|
+
if (permission === "default") {
|
|
38
|
+
permission = await Notification.requestPermission();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (permission !== "granted") {
|
|
42
|
+
return; // User denied permission, this is not an error
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const vapidPublicKey = (process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY || "").trim();
|
|
46
|
+
const convertedKey = urlBase64ToUint8Array(vapidPublicKey);
|
|
47
|
+
|
|
48
|
+
await navigator.serviceWorker.ready;
|
|
49
|
+
|
|
50
|
+
let subscription = await registration.pushManager.getSubscription();
|
|
51
|
+
if (!subscription) {
|
|
52
|
+
const appServerKey = new Uint8Array(Array.from(convertedKey));
|
|
53
|
+
|
|
54
|
+
subscription = await registration.pushManager.subscribe({
|
|
55
|
+
userVisibleOnly: true,
|
|
56
|
+
applicationServerKey: appServerKey,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const plainSubscription = {
|
|
61
|
+
endpoint: subscription.endpoint,
|
|
62
|
+
keys: subscription.toJSON().keys,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Create a simple hash to detect subscription changes
|
|
66
|
+
const subscriptionHash = btoa(JSON.stringify(plainSubscription));
|
|
67
|
+
|
|
68
|
+
// Only call the API if subscription has changed or this is the first registration
|
|
69
|
+
if (lastRegisteredSubscription !== subscriptionHash) {
|
|
70
|
+
await PushService.register({ data: plainSubscription });
|
|
71
|
+
// Store the current subscription hash to avoid duplicate registrations
|
|
72
|
+
sessionStorage.setItem(sessionKey, subscriptionHash);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error("Error during service worker registration or push subscription:", error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (currentUser && !hasRole(getRoleId().Administrator)) register();
|
|
81
|
+
}, [currentUser]);
|
|
82
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import io from "socket.io-client";
|
|
5
|
+
import { Modules, rehydrate } from "../core";
|
|
6
|
+
import { NotificationInterface } from "../features";
|
|
7
|
+
|
|
8
|
+
type Socket = ReturnType<typeof io>;
|
|
9
|
+
|
|
10
|
+
interface UseSocketOptions {
|
|
11
|
+
token: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface SocketLike {
|
|
15
|
+
emit: (event: string, ...args: any[]) => void;
|
|
16
|
+
on: (event: string, callback: (...args: any[]) => void) => void;
|
|
17
|
+
off: (event: string, callback?: (...args: any[]) => void) => void;
|
|
18
|
+
connected: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface UseSocketReturn {
|
|
22
|
+
socket: SocketLike | null;
|
|
23
|
+
isConnected: boolean;
|
|
24
|
+
messages: any[];
|
|
25
|
+
socketNotifications: NotificationInterface[];
|
|
26
|
+
sendMessage: (event: string, data: any) => void;
|
|
27
|
+
removeMessage: (index: number) => void;
|
|
28
|
+
removeSocketNotification: (index: number) => void;
|
|
29
|
+
clearMessages: () => void;
|
|
30
|
+
clearSocketNotifications: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useSocket({ token }: UseSocketOptions): UseSocketReturn {
|
|
34
|
+
const errorCount = useRef(0);
|
|
35
|
+
const shouldConnect = useRef(true);
|
|
36
|
+
const hookInstanceId = useRef(Math.random().toString(36).substring(2, 11));
|
|
37
|
+
|
|
38
|
+
const [socket, setSocket] = useState<Socket | null>(null);
|
|
39
|
+
const [isConnected, setIsConnected] = useState<boolean>(false);
|
|
40
|
+
const [messages, setMessages] = useState<any[]>([]);
|
|
41
|
+
const [socketNotifications, setSocketNotifications] = useState<NotificationInterface[]>([]);
|
|
42
|
+
|
|
43
|
+
const socketRef = useRef<Socket | null>(null);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!token) return;
|
|
47
|
+
|
|
48
|
+
const globalSocketKey = `__socket_${process.env.NEXT_PUBLIC_API_URL?.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
49
|
+
|
|
50
|
+
if (typeof window !== "undefined") {
|
|
51
|
+
const allSocketKeys = Object.keys(window).filter((key) => key.startsWith("__socket_"));
|
|
52
|
+
|
|
53
|
+
const existingSocket = (window as any)[globalSocketKey];
|
|
54
|
+
if (existingSocket) {
|
|
55
|
+
if (existingSocket.connected) {
|
|
56
|
+
// CRITICAL: Clear existing listeners to prevent accumulation
|
|
57
|
+
existingSocket.removeAllListeners();
|
|
58
|
+
|
|
59
|
+
socketRef.current = existingSocket;
|
|
60
|
+
setSocket(existingSocket);
|
|
61
|
+
setIsConnected(existingSocket.connected);
|
|
62
|
+
} else {
|
|
63
|
+
existingSocket.disconnect();
|
|
64
|
+
delete (window as any)[globalSocketKey];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let currentSocket: any;
|
|
70
|
+
const isReusing = socketRef.current && socketRef.current.connected;
|
|
71
|
+
|
|
72
|
+
if (isReusing) {
|
|
73
|
+
currentSocket = socketRef.current;
|
|
74
|
+
} else {
|
|
75
|
+
// React StrictMode protection: Prevent double-mounting issues
|
|
76
|
+
if (!shouldConnect.current) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
shouldConnect.current = false;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
currentSocket = io(process.env.NEXT_PUBLIC_API_URL as string, {
|
|
83
|
+
auth: { token },
|
|
84
|
+
transports: ["websocket"],
|
|
85
|
+
timeout: 20000,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (typeof window !== "undefined") {
|
|
89
|
+
if (currentSocket.id && currentSocket.id !== "undefined") {
|
|
90
|
+
(window as any)[globalSocketKey] = currentSocket;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
socketRef.current = currentSocket;
|
|
95
|
+
setSocket(currentSocket);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return () => {}; // Return empty cleanup function on error
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const handleConnect = () => {
|
|
102
|
+
setIsConnected(true);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const handleDisconnect = () => {
|
|
106
|
+
setIsConnected(false);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const handleMessage = (data: any) => {
|
|
110
|
+
setMessages((prevMessages) => {
|
|
111
|
+
const newMessages = [...prevMessages, data];
|
|
112
|
+
return newMessages;
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleNotification = (data: any) => {
|
|
117
|
+
const notification = rehydrate(Modules.Notification, data) as NotificationInterface;
|
|
118
|
+
if (notification) {
|
|
119
|
+
setSocketNotifications((prev) => {
|
|
120
|
+
const newNotifications = [...prev, notification];
|
|
121
|
+
return newNotifications;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Attach event listeners
|
|
127
|
+
if (currentSocket) {
|
|
128
|
+
currentSocket.on("connect", handleConnect);
|
|
129
|
+
currentSocket.on("disconnect", handleDisconnect);
|
|
130
|
+
currentSocket.on("message", handleMessage);
|
|
131
|
+
currentSocket.on("notification", handleNotification);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return () => {
|
|
135
|
+
shouldConnect.current = true;
|
|
136
|
+
|
|
137
|
+
// In development, preserve socket in window for HMR but remove listeners
|
|
138
|
+
if (currentSocket) {
|
|
139
|
+
if (process.env.NODE_ENV === "development") {
|
|
140
|
+
currentSocket.off("connect", handleConnect);
|
|
141
|
+
currentSocket.off("disconnect", handleDisconnect);
|
|
142
|
+
currentSocket.off("message", handleMessage);
|
|
143
|
+
currentSocket.off("notification", handleNotification);
|
|
144
|
+
} else {
|
|
145
|
+
currentSocket.off("connect", handleConnect);
|
|
146
|
+
currentSocket.off("disconnect", handleDisconnect);
|
|
147
|
+
currentSocket.off("message", handleMessage);
|
|
148
|
+
currentSocket.off("notification", handleNotification);
|
|
149
|
+
|
|
150
|
+
currentSocket.disconnect();
|
|
151
|
+
|
|
152
|
+
if (typeof window !== "undefined") {
|
|
153
|
+
delete (window as any)[globalSocketKey];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}, [token]);
|
|
159
|
+
|
|
160
|
+
const sendMessage = (event: string, data: any) => {
|
|
161
|
+
if (socketRef.current && isConnected) {
|
|
162
|
+
socketRef.current.emit(event, data);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const removeMessage = (index: number) => {
|
|
167
|
+
setMessages((prevMessages) => {
|
|
168
|
+
const newMessages = [...prevMessages];
|
|
169
|
+
newMessages.splice(index, 1);
|
|
170
|
+
return newMessages;
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const removeSocketNotification = (index: number) => {
|
|
175
|
+
setSocketNotifications((prevNotifications) => {
|
|
176
|
+
const newNotifications = [...prevNotifications];
|
|
177
|
+
newNotifications.splice(index, 1);
|
|
178
|
+
return newNotifications;
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const clearMessages = () => {
|
|
183
|
+
setMessages([]);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const clearSocketNotifications = () => {
|
|
187
|
+
setSocketNotifications([]);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
socket,
|
|
192
|
+
isConnected,
|
|
193
|
+
messages,
|
|
194
|
+
socketNotifications,
|
|
195
|
+
sendMessage,
|
|
196
|
+
removeMessage,
|
|
197
|
+
removeSocketNotification,
|
|
198
|
+
clearMessages,
|
|
199
|
+
clearSocketNotifications,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -4,8 +4,12 @@ import { UseTableStructureHook, UseTableStructureHookParams, UseTableStructureHo
|
|
|
4
4
|
import { ModuleWithPermissions } from "../permissions";
|
|
5
5
|
import { tableGeneratorRegistry } from "./TableGeneratorRegistry";
|
|
6
6
|
|
|
7
|
-
export function registerTableGenerator<T, U>(
|
|
8
|
-
|
|
7
|
+
export function registerTableGenerator<T, U>(
|
|
8
|
+
type: string | ModuleWithPermissions,
|
|
9
|
+
hook: UseTableStructureHook<T, U>,
|
|
10
|
+
): void {
|
|
11
|
+
const name = typeof type === "string" ? type : type.name;
|
|
12
|
+
tableGeneratorRegistry.register(name, hook);
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
export function useTableGenerator<T, U>(
|
package/src/i18n/config.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -14,3 +14,7 @@ export type { I18nConfig } from "./i18n";
|
|
|
14
14
|
// Roles configuration
|
|
15
15
|
export { configureRoles, getRoleId, isRolesConfigured } from "./roles";
|
|
16
16
|
export type { RoleIdConfig } from "./roles";
|
|
17
|
+
|
|
18
|
+
// Auth configuration
|
|
19
|
+
export { configureAuth, getTokenHandler } from "./features/auth/config";
|
|
20
|
+
export type { TokenHandler, TokenParams } from "./features/auth/config";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { SimulationLinkDatum } from "d3";
|
|
1
2
|
import { D3Node } from "./d3.node.interface";
|
|
2
3
|
|
|
3
|
-
export interface D3Link extends
|
|
4
|
+
export interface D3Link extends SimulationLinkDatum<D3Node> {
|
|
4
5
|
source: string | D3Node;
|
|
5
6
|
target: string | D3Node;
|
|
6
7
|
relationshipType?: string;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { cookies } from "next/headers";
|
|
2
|
+
import { redirect } from "next/navigation";
|
|
3
|
+
import zlib from "zlib";
|
|
4
|
+
import { Action, checkPermissionsFromServer, ModuleWithPermissions } from "../permissions";
|
|
5
|
+
|
|
6
|
+
export class ServerSession {
|
|
7
|
+
static async isLogged() {
|
|
8
|
+
const cookieStore = await cookies();
|
|
9
|
+
const token = cookieStore.get("token");
|
|
10
|
+
|
|
11
|
+
if (!token || !token.value) return false;
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static async isLicenseActive(): Promise<boolean> {
|
|
16
|
+
const cookieStore = await cookies();
|
|
17
|
+
const licenseExpirationDate = cookieStore.get("licenseExpirationDate")?.value;
|
|
18
|
+
|
|
19
|
+
if (!licenseExpirationDate) return false;
|
|
20
|
+
|
|
21
|
+
const now = new Date();
|
|
22
|
+
return new Date(licenseExpirationDate) > now;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static async companyId() {
|
|
26
|
+
const cookieStore = await cookies();
|
|
27
|
+
return cookieStore.get("companyId")?.value ?? "";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static async userId() {
|
|
31
|
+
const cookieStore = await cookies();
|
|
32
|
+
return cookieStore.get("userId")?.value ?? "";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static async checkPermission<M extends ModuleWithPermissions>(params: { module: M; action: Action; data?: any }) {
|
|
36
|
+
if (!(await this.hasPermissionToModule(params))) redirect(`/401`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static async hasRole(roleId: string): Promise<boolean> {
|
|
40
|
+
const cookieStore = await cookies();
|
|
41
|
+
const roles = cookieStore.get("roles")?.value;
|
|
42
|
+
|
|
43
|
+
if (!roles || !roles.includes(roleId)) return false;
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static async hasFeature(featureId: string): Promise<boolean> {
|
|
49
|
+
const cookieStore = await cookies();
|
|
50
|
+
const features = cookieStore.get("features")?.value;
|
|
51
|
+
|
|
52
|
+
if (!features || !features.includes(featureId)) return false;
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static async hasPermissionToModule<M extends ModuleWithPermissions>(params: {
|
|
58
|
+
module: M;
|
|
59
|
+
action: Action;
|
|
60
|
+
data?: any;
|
|
61
|
+
}): Promise<boolean> {
|
|
62
|
+
const cookieStore = await cookies();
|
|
63
|
+
|
|
64
|
+
if (params.module.feature) {
|
|
65
|
+
const features = cookieStore.get("features")?.value;
|
|
66
|
+
|
|
67
|
+
if (features && !features.includes(params.module.feature)) return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const rawModules = cookieStore.get("modules")?.value;
|
|
71
|
+
if (!rawModules) return false;
|
|
72
|
+
|
|
73
|
+
const modules: {
|
|
74
|
+
id: string;
|
|
75
|
+
permissions: {
|
|
76
|
+
create: boolean | string;
|
|
77
|
+
read: boolean | string;
|
|
78
|
+
update: boolean | string;
|
|
79
|
+
delete: boolean | string;
|
|
80
|
+
};
|
|
81
|
+
}[] = JSON.parse(zlib.gunzipSync(Buffer.from(rawModules, "base64")).toString());
|
|
82
|
+
|
|
83
|
+
const selectedModule = modules.find((module) => module.id === params.module.moduleId);
|
|
84
|
+
|
|
85
|
+
return checkPermissionsFromServer({
|
|
86
|
+
module: params.module,
|
|
87
|
+
action: params.action,
|
|
88
|
+
data: params.data,
|
|
89
|
+
userId: await this.userId(),
|
|
90
|
+
selectedModule: selectedModule,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// if (!selectedModule) return false;
|
|
94
|
+
|
|
95
|
+
// if (!selectedModule.permissions[params.action]) return false;
|
|
96
|
+
// if (typeof selectedModule.permissions[params.action] === "boolean")
|
|
97
|
+
// return selectedModule.permissions[params.action] as boolean;
|
|
98
|
+
|
|
99
|
+
// if (!params.data) return false;
|
|
100
|
+
|
|
101
|
+
// return params.data[selectedModule.permissions[params.action] as string] === this.userId;
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/server/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ApiRequestDataTypeInterface } from "../core/interfaces/ApiRequestDataTy
|
|
|
3
3
|
import { ApiResponseInterface } from "../core/interfaces/ApiResponseInterface";
|
|
4
4
|
import { JsonApiDataFactory } from "../core/factories/JsonApiDataFactory";
|
|
5
5
|
import { translateResponse } from "../core/utils/translateResponse";
|
|
6
|
+
import { ModuleWithPermissions } from "../permissions/types";
|
|
6
7
|
|
|
7
8
|
// Type definitions for dynamically imported functions (avoiding typeof import to prevent bundling)
|
|
8
9
|
type DirectFetchFn = (params: {
|
|
@@ -39,6 +40,8 @@ let _getServerToken: GetTokenFn;
|
|
|
39
40
|
// Config storage for non-React contexts
|
|
40
41
|
let _staticConfig: {
|
|
41
42
|
apiUrl: string;
|
|
43
|
+
appUrl?: string;
|
|
44
|
+
trackablePages?: ModuleWithPermissions[];
|
|
42
45
|
bootstrapper?: () => void;
|
|
43
46
|
additionalHeaders?: Record<string, string>;
|
|
44
47
|
} | null = null;
|
|
@@ -49,6 +52,8 @@ let _staticConfig: {
|
|
|
49
52
|
*/
|
|
50
53
|
export function configureJsonApi(config: {
|
|
51
54
|
apiUrl: string;
|
|
55
|
+
appUrl?: string;
|
|
56
|
+
trackablePages?: ModuleWithPermissions[];
|
|
52
57
|
bootstrapper?: () => void;
|
|
53
58
|
additionalHeaders?: Record<string, string>;
|
|
54
59
|
}): void {
|
|
@@ -136,6 +141,24 @@ function getApiUrl(): string {
|
|
|
136
141
|
return envUrl;
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
export function getAppUrl(): string {
|
|
145
|
+
if (_staticConfig?.appUrl) {
|
|
146
|
+
return _staticConfig.appUrl;
|
|
147
|
+
}
|
|
148
|
+
// Fallback to environment variable
|
|
149
|
+
const envUrl = process.env.NEXT_PUBLIC_ADDRESS;
|
|
150
|
+
if (!envUrl) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
"App URL not configured. Use configureJsonApi({ appUrl }) or set NEXT_PUBLIC_ADDRESS environment variable.",
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return envUrl.trim().replace(/\/+$/, "");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function getTrackablePages(): ModuleWithPermissions[] {
|
|
159
|
+
return _staticConfig?.trackablePages ?? [];
|
|
160
|
+
}
|
|
161
|
+
|
|
139
162
|
function runBootstrapper(): void {
|
|
140
163
|
if (_staticConfig?.bootstrapper) {
|
|
141
164
|
_staticConfig.bootstrapper();
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
type GetterKeys<T> = {
|
|
2
|
-
[K in keyof T]: T[K] extends () => any ? never : K;
|
|
3
|
-
}[keyof T];
|
|
4
|
-
type FieldSelector<T> = {
|
|
5
|
-
type: string;
|
|
6
|
-
fields: ReadonlyArray<GetterKeys<T>>;
|
|
7
|
-
};
|
|
8
|
-
declare function createJsonApiInclusion<T>(dataType: string, fields: ReadonlyArray<GetterKeys<T>>): FieldSelector<T>;
|
|
9
|
-
|
|
10
|
-
type ApiRequestDataTypeInterface = {
|
|
11
|
-
name: string;
|
|
12
|
-
cache?: string;
|
|
13
|
-
inclusions?: Record<string, {
|
|
14
|
-
types?: string[];
|
|
15
|
-
fields?: FieldSelector<any>[];
|
|
16
|
-
}>;
|
|
17
|
-
model: new () => any;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export { type ApiRequestDataTypeInterface as A, type FieldSelector as F, type GetterKeys as G, createJsonApiInclusion as c };
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
type GetterKeys<T> = {
|
|
2
|
-
[K in keyof T]: T[K] extends () => any ? never : K;
|
|
3
|
-
}[keyof T];
|
|
4
|
-
type FieldSelector<T> = {
|
|
5
|
-
type: string;
|
|
6
|
-
fields: ReadonlyArray<GetterKeys<T>>;
|
|
7
|
-
};
|
|
8
|
-
declare function createJsonApiInclusion<T>(dataType: string, fields: ReadonlyArray<GetterKeys<T>>): FieldSelector<T>;
|
|
9
|
-
|
|
10
|
-
type ApiRequestDataTypeInterface = {
|
|
11
|
-
name: string;
|
|
12
|
-
cache?: string;
|
|
13
|
-
inclusions?: Record<string, {
|
|
14
|
-
types?: string[];
|
|
15
|
-
fields?: FieldSelector<any>[];
|
|
16
|
-
}>;
|
|
17
|
-
model: new () => any;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export { type ApiRequestDataTypeInterface as A, type FieldSelector as F, type GetterKeys as G, createJsonApiInclusion as c };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteEditor-VFWG6LXI.js","../src/components/editors/BlockNoteEditor.tsx","../src/components/editors/BlockNoteEditorFormattingToolbar.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteEditor-VFWG6LXI.js","sourcesContent":[null,"\"use client\";\n\nimport { BlockNoteSchema, defaultInlineContentSpecs, PartialBlock } from \"@blocknote/core\";\nimport { createReactInlineContentSpec, useCreateBlockNote } from \"@blocknote/react\";\nimport { BlockNoteView } from \"@blocknote/shadcn\";\nimport \"@blocknote/shadcn/style.css\";\nimport { CheckIcon, XIcon } from \"lucide-react\";\nimport { useTranslations } from \"next-intl\";\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useCurrentUserContext } from \"../../contexts\";\nimport { S3Interface, S3Service, UserInterface } from \"../../features\";\nimport { Button } from \"../../shadcnui\";\nimport { BlockNoteDiffUtil, BlockNoteWordDiffRendererUtil, cn } from \"../../utils\";\nimport { errorToast } from \"../errors\";\nimport { BlockNoteEditorFormattingToolbar } from \"./BlockNoteEditorFormattingToolbar\";\n\nexport type BlockNoteEditorProps = {\n id: string;\n type: string;\n initialContent?: PartialBlock[];\n onChange?: (content: any, isEmpty: boolean, hasUnresolvedDiff: boolean) => void;\n size?: \"sm\" | \"md\";\n className?: string;\n markdownContent?: string;\n diffContent?: PartialBlock[];\n placeholder?: string;\n bordered?: boolean;\n};\n\nconst createDiffActionsInlineContentSpec = (\n handleAcceptChange: (diffId: string) => void,\n handleRejectChange: (diffId: string) => void,\n) => {\n return createReactInlineContentSpec(\n {\n type: \"diffActions\",\n propSchema: {\n diffIds: {\n default: \"\",\n },\n },\n content: \"none\",\n },\n {\n render: (props) => {\n const diffIds = props.inlineContent.props.diffIds;\n\n return (\n <span className=\"diff-actions-container mx-2 inline-flex items-center gap-1 align-middle\">\n <Button\n title=\"Accept change\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n diffIds.split(\",\").forEach((id: string) => handleAcceptChange(id.trim()));\n }}\n >\n <CheckIcon className=\"h-3 w-3 text-green-600\" />\n </Button>\n <Button\n title=\"Reject change\"\n className=\"mx-2 p-0\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n diffIds.split(\",\").forEach((id: string) => handleRejectChange(id.trim()));\n }}\n >\n <XIcon className=\"h-3 w-3 text-red-600\" />\n </Button>\n </span>\n );\n },\n },\n );\n};\n\nexport default function BlockNoteEditor({\n id,\n type,\n initialContent,\n onChange,\n size,\n className,\n markdownContent,\n diffContent,\n placeholder,\n bordered,\n}: BlockNoteEditorProps): React.JSX.Element {\n const t = useTranslations();\n const { company } = useCurrentUserContext<UserInterface>();\n\n const [acceptedChanges, setAcceptedChanges] = useState<Set<string>>(new Set());\n const [rejectedChanges, setRejectedChanges] = useState<Set<string>>(new Set());\n\n const editorRef = useRef<HTMLDivElement>(null);\n\n const handleAcceptChange = useCallback((diffId: string) => {\n setAcceptedChanges((prev) => new Set([...prev, diffId]));\n setRejectedChanges((prev) => {\n const newSet = new Set(prev);\n newSet.delete(diffId);\n return newSet;\n });\n }, []);\n\n const handleRejectChange = useCallback((diffId: string) => {\n setRejectedChanges((prev) => new Set([...prev, diffId]));\n setAcceptedChanges((prev) => {\n const newSet = new Set(prev);\n newSet.delete(diffId);\n return newSet;\n });\n }, []);\n\n const DiffActionsInlineContent = useMemo(\n () => createDiffActionsInlineContentSpec(handleAcceptChange, handleRejectChange),\n [handleAcceptChange, handleRejectChange],\n );\n\n const schema = useMemo(\n () =>\n BlockNoteSchema.create({\n inlineContentSpecs: {\n ...defaultInlineContentSpecs,\n diffActions: DiffActionsInlineContent,\n },\n } as any),\n [DiffActionsInlineContent],\n );\n\n const uploadImage = useCallback(\n async (file: File): Promise<string> => {\n if (!company) {\n errorToast({\n title: t(`generic.errors.upload`),\n error: t(`generic.errors.upload_description`),\n });\n throw new Error(t(`generic.errors.upload`));\n }\n\n const fileType = file.type;\n const key = `companies/${company.id}/${type}/${id}/${file.name}`;\n\n const s3: S3Interface = await S3Service.getPreSignedUrl({\n key: key,\n contentType: fileType,\n isPublic: true,\n });\n\n await fetch(s3.url, {\n method: \"PUT\",\n headers: s3.headers,\n body: file,\n });\n\n const signedImage: S3Interface = await S3Service.getSignedUrl({\n key: key,\n isPublic: true,\n });\n\n return signedImage.url;\n },\n [company, id, t],\n );\n\n // Utility: Remove trailing empty blocks for read-only display\n const removeTrailingEmptyBlocks = useCallback(\n (blocks: PartialBlock[]): PartialBlock[] => {\n if (!blocks || blocks.length === 0) return blocks;\n\n // Only remove trailing empty blocks in read-only mode\n if (onChange !== undefined) return blocks;\n\n const result = [...blocks];\n\n // Remove trailing empty paragraph blocks, but keep at least one block\n while (result.length > 1) {\n const lastBlock = result[result.length - 1];\n\n // Check if it's an empty paragraph\n const isEmptyParagraph =\n lastBlock.type === \"paragraph\" &&\n (!lastBlock.content ||\n lastBlock.content.length === 0 ||\n (Array.isArray(lastBlock.content) && lastBlock.content.every((c: any) => !c.text || c.text.trim() === \"\")));\n\n if (isEmptyParagraph) {\n result.pop();\n } else {\n break;\n }\n }\n\n return result;\n },\n [onChange],\n );\n\n const processedContent = useMemo(() => {\n if (diffContent && initialContent) {\n try {\n const diffResult = BlockNoteDiffUtil.diff(initialContent, diffContent);\n const renderedDiff = BlockNoteWordDiffRendererUtil.renderWordDiffs(\n diffResult.blocks,\n handleAcceptChange,\n handleRejectChange,\n acceptedChanges,\n rejectedChanges,\n );\n return removeTrailingEmptyBlocks(renderedDiff);\n } catch (error) {\n return initialContent && Array.isArray(initialContent) && initialContent.length > 0\n ? removeTrailingEmptyBlocks(initialContent)\n : [];\n }\n }\n\n if (!initialContent) {\n return [];\n }\n\n if (!Array.isArray(initialContent)) {\n return [];\n }\n\n return initialContent.length > 0 ? removeTrailingEmptyBlocks(initialContent) : [];\n }, [\n initialContent,\n diffContent,\n handleAcceptChange,\n handleRejectChange,\n acceptedChanges,\n rejectedChanges,\n removeTrailingEmptyBlocks,\n ]);\n\n const validatedInitialContent = useMemo(() => {\n if (processedContent && Array.isArray(processedContent) && processedContent.length > 0) {\n const validatedContent = processedContent.filter((block) => {\n if (!block || typeof block !== \"object\") return false;\n if (!(block as any).type) return false;\n return true;\n });\n return validatedContent.length > 0 ? (validatedContent as PartialBlock[]) : undefined;\n }\n return undefined;\n }, [processedContent]);\n\n const editor = useCreateBlockNote(\n useMemo(\n () => ({\n placeholders: {\n emptyDocument: placeholder || t(`generic.blocknote.placeholder`),\n },\n schema,\n initialContent: validatedInitialContent,\n uploadFile: uploadImage,\n }),\n [placeholder, t, schema, validatedInitialContent, uploadImage],\n ),\n );\n\n const handleChange = useCallback(async () => {\n if (!onChange) return;\n const newBlocks = editor.document;\n\n const markdownFromBlocks = (await editor.blocksToMarkdownLossy(editor.document)).trim();\n\n function hasUnresolvedDiffsRecursive(block: any): boolean {\n if (!block || typeof block !== \"object\") return false;\n let diffId = undefined;\n if (block.props && block.props.diffId) diffId = block.props.diffId;\n if (!diffId && block.attrs && block.attrs.diffId) diffId = block.attrs.diffId;\n if (diffId && !acceptedChanges.has(diffId) && !rejectedChanges.has(diffId)) {\n return true;\n }\n\n if (block.content) {\n const contentArr = Array.isArray(block.content) ? block.content : [block.content];\n for (const inline of contentArr) {\n if (inline && typeof inline === \"object\") {\n if (inline.type === \"diffActions\" && inline.props && inline.props.diffIds) {\n const ids =\n typeof inline.props.diffIds === \"string\" ? inline.props.diffIds.split(\",\") : inline.props.diffIds;\n for (const id of ids) {\n const trimmed = (id || \"\").toString().trim();\n if (trimmed && !acceptedChanges.has(trimmed) && !rejectedChanges.has(trimmed)) {\n return true;\n }\n }\n }\n if (inline.props && inline.props.diffId) {\n const diffIdInline = inline.props.diffId;\n if (diffIdInline && !acceptedChanges.has(diffIdInline) && !rejectedChanges.has(diffIdInline)) {\n return true;\n }\n }\n if (inline.children && Array.isArray(inline.children)) {\n for (const child of inline.children) {\n if (hasUnresolvedDiffsRecursive(child)) return true;\n }\n }\n }\n }\n }\n if (Array.isArray(block.children)) {\n for (const child of block.children) {\n if (hasUnresolvedDiffsRecursive(child)) return true;\n }\n }\n return false;\n }\n\n let hasUnresolvedDiff = false;\n if (Array.isArray(newBlocks)) {\n hasUnresolvedDiff = newBlocks.some((block: any) => hasUnresolvedDiffsRecursive(block));\n }\n\n onChange(newBlocks, !markdownFromBlocks.length, hasUnresolvedDiff);\n }, [editor, onChange, id, acceptedChanges, rejectedChanges]);\n\n // Utility: deep equality for arrays of blocks\n const areBlocksEqual = (a: any[], b: any[]): boolean => {\n return JSON.stringify(a) === JSON.stringify(b);\n };\n\n // Only initialize from markdownContent once per value, and only if different\n const hasInitializedFromMarkdown = useRef<string | null>(null);\n useEffect(() => {\n const updateContent = async (markdown: string) => {\n const blocks = await editor.tryParseMarkdownToBlocks(markdown);\n if (!areBlocksEqual(blocks, editor.document)) {\n editor.replaceBlocks(editor.document, blocks);\n }\n };\n\n if (markdownContent && hasInitializedFromMarkdown.current !== markdownContent) {\n hasInitializedFromMarkdown.current = markdownContent;\n updateContent(markdownContent).then(() => handleChange());\n }\n }, [markdownContent, editor]);\n\n // Update editor content when diff content changes, but only if different\n // Prevent unnecessary replaceBlocks calls that reset scroll/cursor.\n const previousContentHashRef = useRef<string | null>(null);\n useEffect(() => {\n if (!processedContent || !editor) return;\n const hash = JSON.stringify(processedContent);\n if (previousContentHashRef.current === hash) return; // no changes\n const currentHash = JSON.stringify(editor.document);\n if (currentHash === hash) {\n previousContentHashRef.current = hash;\n return; // already in sync\n }\n editor.replaceBlocks(editor.document, processedContent as PartialBlock[]);\n previousContentHashRef.current = hash;\n }, [processedContent, editor]);\n\n // Handle audio received from whisper transcription\n const handleAudioReceived = useCallback(\n (message: string) => {\n try {\n // Ensure the editor has focus\n const editorElement = editorRef.current?.querySelector('[contenteditable=\"true\"]') as HTMLElement;\n if (editorElement && document.activeElement !== editorElement) {\n editorElement.focus();\n }\n\n // Insert the transcribed text at the current cursor position\n editor.insertInlineContent(message);\n } catch (error) {\n console.error(\"Error inserting transcribed text:\", error);\n // Fallback: try to insert at the end of the document\n try {\n const blocks = editor.document;\n if (blocks.length > 0) {\n const lastBlock = blocks[blocks.length - 1];\n editor.setTextCursorPosition(lastBlock.id, \"end\");\n editor.insertInlineContent(message);\n }\n } catch (fallbackError) {\n console.error(\"Fallback insertion also failed:\", fallbackError);\n }\n }\n },\n [editor],\n );\n\n return (\n <div ref={editorRef} className={cn(bordered ? \"rounded-md border\" : \"\", \"w-full\")}>\n <BlockNoteView\n editor={editor}\n onChange={handleChange}\n editable={onChange !== undefined}\n formattingToolbar={false}\n theme=\"light\"\n className={cn(`BlockNoteView ${onChange ? \"min-h-96 p-4\" : \"\"}`, className, size === \"sm\" && \"small\")}\n >\n <BlockNoteEditorFormattingToolbar />\n </BlockNoteView>\n </div>\n );\n}\n","\"use client\";\n\nimport {\n BasicTextStyleButton,\n BlockTypeSelect,\n CreateLinkButton,\n FileCaptionButton,\n FileReplaceButton,\n FormattingToolbar,\n FormattingToolbarController,\n TextAlignButton,\n} from \"@blocknote/react\";\n\nexport function BlockNoteEditorFormattingToolbar() {\n return (\n <FormattingToolbarController\n formattingToolbar={() => (\n <FormattingToolbar>\n <BlockTypeSelect key={\"blockTypeSelect\"} />\n\n <FileCaptionButton key={\"fileCaptionButton\"} />\n <FileReplaceButton key={\"replaceFileButton\"} />\n\n <BasicTextStyleButton basicTextStyle={\"bold\"} key={\"boldStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"italic\"} key={\"italicStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"underline\"} key={\"underlineStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"strike\"} key={\"strikeStyleButton\"} />\n\n <TextAlignButton textAlignment={\"left\"} key={\"textAlignLeftButton\"} />\n <TextAlignButton textAlignment={\"center\"} key={\"textAlignCenterButton\"} />\n <TextAlignButton textAlignment={\"right\"} key={\"textAlignRightButton\"} />\n\n <CreateLinkButton key={\"createLinkButton\"} />\n </FormattingToolbar>\n )}\n />\n );\n}\n"]}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
JsonApiDelete,
|
|
3
|
-
JsonApiGet,
|
|
4
|
-
JsonApiPatch,
|
|
5
|
-
JsonApiPost,
|
|
6
|
-
JsonApiPut,
|
|
7
|
-
configureJsonApi
|
|
8
|
-
} from "./chunk-ECDTZBYO.mjs";
|
|
9
|
-
import "./chunk-YDVTFM7X.mjs";
|
|
10
|
-
import "./chunk-MFO27OHB.mjs";
|
|
11
|
-
import "./chunk-PAWJFY3S.mjs";
|
|
12
|
-
export {
|
|
13
|
-
JsonApiDelete,
|
|
14
|
-
JsonApiGet,
|
|
15
|
-
JsonApiPatch,
|
|
16
|
-
JsonApiPost,
|
|
17
|
-
JsonApiPut,
|
|
18
|
-
configureJsonApi
|
|
19
|
-
};
|
|
20
|
-
//# sourceMappingURL=JsonApiRequest-S3ICLM7B.mjs.map
|