@carlonicora/nextjs-jsonapi 1.29.4 → 1.29.6

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.
Files changed (56) hide show
  1. package/dist/{BlockNoteEditor-G532N3N7.mjs → BlockNoteEditor-4NQ5LSD6.mjs} +4 -4
  2. package/dist/{BlockNoteEditor-XWHUG6VH.js → BlockNoteEditor-AZNYW5Y7.js} +14 -14
  3. package/dist/{BlockNoteEditor-XWHUG6VH.js.map → BlockNoteEditor-AZNYW5Y7.js.map} +1 -1
  4. package/dist/billing/index.js +335 -335
  5. package/dist/billing/index.mjs +3 -3
  6. package/dist/{chunk-UYBCHXXL.js → chunk-5KQXRLK3.js} +25 -2
  7. package/dist/chunk-5KQXRLK3.js.map +1 -0
  8. package/dist/{chunk-GR4QPP36.mjs → chunk-C62JXXUX.mjs} +1 -1
  9. package/dist/{chunk-ALGMJ4JS.js → chunk-CWRYS2F3.js} +539 -530
  10. package/dist/chunk-CWRYS2F3.js.map +1 -0
  11. package/dist/{chunk-EW6QPMN3.js → chunk-OR5NPUWF.js} +1 -1
  12. package/dist/{chunk-EW6QPMN3.js.map → chunk-OR5NPUWF.js.map} +1 -1
  13. package/dist/{chunk-NYNLJEPF.mjs → chunk-WQ3KF6BG.mjs} +24 -1
  14. package/dist/chunk-WQ3KF6BG.mjs.map +1 -0
  15. package/dist/{chunk-CWY6AL4V.mjs → chunk-ZQEFAWFP.mjs} +91 -82
  16. package/dist/chunk-ZQEFAWFP.mjs.map +1 -0
  17. package/dist/client/index.js +4 -4
  18. package/dist/client/index.mjs +3 -3
  19. package/dist/components/index.js +4 -4
  20. package/dist/components/index.mjs +3 -3
  21. package/dist/contexts/index.js +4 -4
  22. package/dist/contexts/index.mjs +3 -3
  23. package/dist/core/index.d.mts +25 -2
  24. package/dist/core/index.d.ts +25 -2
  25. package/dist/core/index.js +10 -2
  26. package/dist/core/index.js.map +1 -1
  27. package/dist/core/index.mjs +9 -1
  28. package/dist/index.d.mts +2 -1
  29. package/dist/index.d.ts +2 -1
  30. package/dist/index.js +11 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +10 -2
  33. package/dist/server/index.js +3 -3
  34. package/dist/server/index.mjs +1 -1
  35. package/package.json +1 -1
  36. package/src/components/errors/errorToast.ts +2 -4
  37. package/src/components/forms/CommonAssociationForm.tsx +3 -3
  38. package/src/components/forms/FileUploader.tsx +4 -4
  39. package/src/features/auth/components/forms/AcceptInvitation.tsx +2 -2
  40. package/src/features/auth/components/forms/ActivateAccount.tsx +2 -2
  41. package/src/features/auth/components/forms/ResetPassword.tsx +2 -2
  42. package/src/features/company/components/forms/CompanyConfigurationEditor.tsx +2 -2
  43. package/src/features/notification/components/modals/NotificationModal.tsx +2 -2
  44. package/src/features/notification/contexts/NotificationContext.tsx +27 -1
  45. package/src/features/role/components/forms/UserRoleAdd.tsx +2 -2
  46. package/src/features/user/components/forms/UserResentInvitationEmail.tsx +2 -2
  47. package/src/index.ts +3 -0
  48. package/src/shadcnui/ui/sonner.tsx +13 -23
  49. package/src/utils/index.ts +3 -0
  50. package/src/utils/toast.ts +39 -0
  51. package/dist/chunk-ALGMJ4JS.js.map +0 -1
  52. package/dist/chunk-CWY6AL4V.mjs.map +0 -1
  53. package/dist/chunk-NYNLJEPF.mjs.map +0 -1
  54. package/dist/chunk-UYBCHXXL.js.map +0 -1
  55. /package/dist/{BlockNoteEditor-G532N3N7.mjs.map → BlockNoteEditor-4NQ5LSD6.mjs.map} +0 -0
  56. /package/dist/{chunk-GR4QPP36.mjs.map → chunk-C62JXXUX.mjs.map} +0 -0
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  getStripePublishableKey,
10
10
  getTrackablePages,
11
11
  isRolesConfigured
12
- } from "./chunk-GR4QPP36.mjs";
12
+ } from "./chunk-C62JXXUX.mjs";
13
13
  import {
14
14
  AVAILABLE_OAUTH_SCOPES,
15
15
  AbstractApiData,
@@ -98,6 +98,7 @@ import {
98
98
  composeRefs,
99
99
  configureAuth,
100
100
  createJsonApiInclusion,
101
+ dismissToast,
101
102
  entityObjectSchema,
102
103
  exists,
103
104
  formatDate,
@@ -117,10 +118,13 @@ import {
117
118
  rehydrateList,
118
119
  setClientGlobalErrorHandler,
119
120
  setGlobalErrorHandler,
121
+ showCustomToast,
122
+ showError,
123
+ showToast,
120
124
  useComposedRefs,
121
125
  useIsMobile,
122
126
  userObjectSchema
123
- } from "./chunk-NYNLJEPF.mjs";
127
+ } from "./chunk-WQ3KF6BG.mjs";
124
128
  import "./chunk-AUXK7QSA.mjs";
125
129
  import "./chunk-C7C7VY4F.mjs";
126
130
  import {
@@ -230,6 +234,7 @@ export {
230
234
  configureLogin,
231
235
  configureRoles,
232
236
  createJsonApiInclusion,
237
+ dismissToast,
233
238
  entityObjectSchema,
234
239
  exists,
235
240
  formatDate,
@@ -259,6 +264,9 @@ export {
259
264
  setBootstrapper,
260
265
  setClientGlobalErrorHandler,
261
266
  setGlobalErrorHandler,
267
+ showCustomToast,
268
+ showError,
269
+ showToast,
262
270
  translateData,
263
271
  translateResponse,
264
272
  tryBootstrap,
@@ -15,7 +15,7 @@ var _chunk3ZPK4QOBjs = require('../chunk-3ZPK4QOB.js');
15
15
 
16
16
 
17
17
 
18
- var _chunkUYBCHXXLjs = require('../chunk-UYBCHXXL.js');
18
+ var _chunk5KQXRLK3js = require('../chunk-5KQXRLK3.js');
19
19
  require('../chunk-LXKSUWAV.js');
20
20
  require('../chunk-IBS6NI7D.js');
21
21
 
@@ -86,7 +86,7 @@ var ServerSession = class {
86
86
  if (!rawModules) return false;
87
87
  const modules = JSON.parse(_pako2.default.ungzip(Buffer.from(rawModules, "base64"), { to: "string" }));
88
88
  const selectedModule = modules.find((module) => module.id === params.module.moduleId);
89
- return _chunkUYBCHXXLjs.checkPermissionsFromServer.call(void 0, {
89
+ return _chunk5KQXRLK3js.checkPermissionsFromServer.call(void 0, {
90
90
  module: params.module,
91
91
  action: params.action,
92
92
  data: params.data,
@@ -296,5 +296,5 @@ _chunk7QVYU63Ejs.__name.call(void 0, ServerJsonApiDelete, "ServerJsonApiDelete")
296
296
 
297
297
 
298
298
 
299
- exports.ServerAuthService = _chunkUYBCHXXLjs.AuthService; exports.ServerCompanyService = _chunkUYBCHXXLjs.CompanyService; exports.ServerContentService = _chunkUYBCHXXLjs.ContentService; exports.ServerFeatureService = _chunkUYBCHXXLjs.FeatureService; exports.ServerJsonApiDelete = ServerJsonApiDelete; exports.ServerJsonApiGet = ServerJsonApiGet; exports.ServerJsonApiPatch = ServerJsonApiPatch; exports.ServerJsonApiPost = ServerJsonApiPost; exports.ServerJsonApiPut = ServerJsonApiPut; exports.ServerNotificationService = _chunkUYBCHXXLjs.NotificationService; exports.ServerPushService = _chunkUYBCHXXLjs.PushService; exports.ServerRoleService = _chunkUYBCHXXLjs.RoleService; exports.ServerS3Service = _chunkUYBCHXXLjs.S3Service; exports.ServerSession = ServerSession; exports.ServerUserService = _chunkUYBCHXXLjs.UserService; exports.configureServerJsonApi = configureServerJsonApi; exports.getServerApiUrl = getServerApiUrl; exports.getServerAppUrl = getServerAppUrl; exports.getServerToken = _chunkYUO55Q5Ajs.getServerToken; exports.getServerTrackablePages = getServerTrackablePages; exports.invalidateCacheTag = invalidateCacheTag; exports.invalidateCacheTags = invalidateCacheTags; exports.serverRequest = _chunk3ZPK4QOBjs.serverRequest;
299
+ exports.ServerAuthService = _chunk5KQXRLK3js.AuthService; exports.ServerCompanyService = _chunk5KQXRLK3js.CompanyService; exports.ServerContentService = _chunk5KQXRLK3js.ContentService; exports.ServerFeatureService = _chunk5KQXRLK3js.FeatureService; exports.ServerJsonApiDelete = ServerJsonApiDelete; exports.ServerJsonApiGet = ServerJsonApiGet; exports.ServerJsonApiPatch = ServerJsonApiPatch; exports.ServerJsonApiPost = ServerJsonApiPost; exports.ServerJsonApiPut = ServerJsonApiPut; exports.ServerNotificationService = _chunk5KQXRLK3js.NotificationService; exports.ServerPushService = _chunk5KQXRLK3js.PushService; exports.ServerRoleService = _chunk5KQXRLK3js.RoleService; exports.ServerS3Service = _chunk5KQXRLK3js.S3Service; exports.ServerSession = ServerSession; exports.ServerUserService = _chunk5KQXRLK3js.UserService; exports.configureServerJsonApi = configureServerJsonApi; exports.getServerApiUrl = getServerApiUrl; exports.getServerAppUrl = getServerAppUrl; exports.getServerToken = _chunkYUO55Q5Ajs.getServerToken; exports.getServerTrackablePages = getServerTrackablePages; exports.invalidateCacheTag = invalidateCacheTag; exports.invalidateCacheTags = invalidateCacheTags; exports.serverRequest = _chunk3ZPK4QOBjs.serverRequest;
300
300
  //# sourceMappingURL=index.js.map
@@ -15,7 +15,7 @@ import {
15
15
  S3Service,
16
16
  UserService,
17
17
  checkPermissionsFromServer
18
- } from "../chunk-NYNLJEPF.mjs";
18
+ } from "../chunk-WQ3KF6BG.mjs";
19
19
  import "../chunk-AUXK7QSA.mjs";
20
20
  import "../chunk-C7C7VY4F.mjs";
21
21
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlonicora/nextjs-jsonapi",
3
- "version": "1.29.4",
3
+ "version": "1.29.6",
4
4
  "description": "Next.js JSON:API client with server/client support and caching",
5
5
  "author": "Carlo Nicora",
6
6
  "license": "GPL-3.0-or-later",
@@ -1,9 +1,7 @@
1
- // import { toast } from "@/hooks/use-toast";
2
-
3
- import { toast } from "sonner";
1
+ import { showError } from "../../utils/toast";
4
2
 
5
3
  export function errorToast(params: { title?: string; error: any }) {
6
- toast.error(params?.title ?? "Error", {
4
+ showError(params?.title ?? "Error", {
7
5
  description: params.error instanceof Error ? params.error.message : String(params.error),
8
6
  });
9
7
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useTranslations } from "next-intl";
4
4
  import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
5
- import { toast } from "sonner";
5
+ import { showToast } from "../../utils/toast";
6
6
  import { DataListRetriever, useDebounce } from "../../hooks";
7
7
  import {
8
8
  Button,
@@ -128,7 +128,7 @@ export const triggerAssociationToast = (params: {
128
128
  level?: string;
129
129
  }) => {
130
130
  if (params.level) {
131
- toast.message(
131
+ showToast(
132
132
  params.t(`common.association.label`, {
133
133
  source: params.source,
134
134
  destination: params.destination,
@@ -144,7 +144,7 @@ export const triggerAssociationToast = (params: {
144
144
  },
145
145
  );
146
146
  } else {
147
- toast.message(
147
+ showToast(
148
148
  params.t(`common.association.label`, {
149
149
  source: params.source,
150
150
  destination: params.destination,
@@ -14,7 +14,7 @@ import {
14
14
  useState,
15
15
  } from "react";
16
16
  import { DropzoneOptions, DropzoneState, FileRejection, useDropzone } from "react-dropzone";
17
- import { toast } from "sonner";
17
+ import { showError } from "../../utils/toast";
18
18
  import { buttonVariants, Input } from "../../shadcnui";
19
19
  import { cn } from "../../utils";
20
20
 
@@ -124,7 +124,7 @@ export const FileUploader = forwardRef<HTMLDivElement, FileUploaderProps & React
124
124
  const files = acceptedFiles;
125
125
 
126
126
  if (!files) {
127
- toast.error(t("common.errors.file"), {
127
+ showError(t("common.errors.file"), {
128
128
  description: t("common.errors.file_large"),
129
129
  });
130
130
  return;
@@ -147,13 +147,13 @@ export const FileUploader = forwardRef<HTMLDivElement, FileUploaderProps & React
147
147
  if (rejectedFiles.length > 0) {
148
148
  for (let i = 0; i < rejectedFiles.length; i++) {
149
149
  if (rejectedFiles[i].errors[0]?.code === "file-too-large") {
150
- toast.error(t("common.errors.file"), {
150
+ showError(t("common.errors.file"), {
151
151
  description: t(`common.errors.file_max`, { size: maxSize / 1024 / 1024 }),
152
152
  });
153
153
  break;
154
154
  }
155
155
  if (rejectedFiles[i].errors[0]?.message) {
156
- toast.error(t(`common.errors.file`), {
156
+ showError(t(`common.errors.file`), {
157
157
  description: rejectedFiles[i].errors[0].message,
158
158
  });
159
159
  break;
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
5
5
  import Image from "next/image";
6
6
  import { useEffect, useState } from "react";
7
7
  import { SubmitHandler, useForm } from "react-hook-form";
8
- import { toast } from "sonner";
8
+ import { showToast } from "../../../../utils/toast";
9
9
  import { z } from "zod";
10
10
  import { errorToast, FormPassword } from "../../../../components";
11
11
  import { Button, CardContent, CardDescription, CardHeader, CardTitle, Form } from "../../../../shadcnui";
@@ -74,7 +74,7 @@ export function AcceptInvitation() {
74
74
  await AuthService.acceptInvitation(payload);
75
75
  setShowConfirmation(true);
76
76
 
77
- toast.success(t("auth.account_activated"), {
77
+ showToast(t("auth.account_activated"), {
78
78
  description: t("auth.account_activated_description"),
79
79
  });
80
80
 
@@ -3,7 +3,7 @@
3
3
  import { useTranslations } from "next-intl";
4
4
  import Image from "next/image";
5
5
  import { useEffect, useState } from "react";
6
- import { toast } from "sonner";
6
+ import { showToast } from "../../../../utils/toast";
7
7
  import { errorToast } from "../../../../components";
8
8
  import { CardContent, CardDescription, CardHeader, CardTitle } from "../../../../shadcnui";
9
9
  import { useAuthContext } from "../../contexts";
@@ -28,7 +28,7 @@ export function ActivateAccount() {
28
28
 
29
29
  setParams(undefined);
30
30
 
31
- toast.success(t("auth.account_activated"), {
31
+ showToast(t("auth.account_activated"), {
32
32
  description: t("auth.account_activated_description"),
33
33
  });
34
34
 
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
5
5
  import Image from "next/image";
6
6
  import { useEffect, useState } from "react";
7
7
  import { SubmitHandler, useForm } from "react-hook-form";
8
- import { toast } from "sonner";
8
+ import { showToast } from "../../../../utils/toast";
9
9
  import { z } from "zod";
10
10
  import { errorToast, FormPassword } from "../../../../components";
11
11
  import { Button, CardContent, CardDescription, CardHeader, CardTitle, Form } from "../../../../shadcnui";
@@ -74,7 +74,7 @@ export function ResetPassword() {
74
74
  await AuthService.resetPassword(payload);
75
75
  setShowConfirmation(true);
76
76
 
77
- toast.success(t(`auth.reset_success`), {
77
+ showToast(t(`auth.reset_success`), {
78
78
  description: t(`auth.reset_success_description`),
79
79
  });
80
80
 
@@ -5,7 +5,7 @@ import { Settings2Icon } from "lucide-react";
5
5
  import { useTranslations } from "next-intl";
6
6
  import { useEffect, useMemo, useState } from "react";
7
7
  import { SubmitHandler, useForm } from "react-hook-form";
8
- import { toast } from "sonner";
8
+ import { showToast } from "../../../../utils/toast";
9
9
  import z from "zod";
10
10
  import { CommonEditorButtons, errorToast } from "../../../../components";
11
11
  import { getRoleId } from "../../../../roles";
@@ -89,7 +89,7 @@ function CompanyConfigurationEditorInternal({ company }: CompanyConfigurationEdi
89
89
  setUser(fullUser);
90
90
  }
91
91
 
92
- toast.message("Configurations Updated", {
92
+ showToast("Configurations Updated", {
93
93
  description: `The system configurations have been updated successfully.`,
94
94
  });
95
95
  close();
@@ -1,7 +1,7 @@
1
1
  import { BellIcon } from "lucide-react";
2
2
  import { useTranslations } from "next-intl";
3
3
  import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
- import { toast } from "sonner";
4
+ import { showToast } from "../../../../utils/toast";
5
5
  import { useSocketContext } from "../../../../contexts";
6
6
  import { usePageUrlGenerator } from "../../../../hooks";
7
7
  import {
@@ -116,7 +116,7 @@ function NotificationModalContent({ isOpen, setIsOpen }: NotificationModalProps)
116
116
  addNotification(notification);
117
117
  const toastNotification = generateToastNotification(notification, t, generateUrl);
118
118
 
119
- toast.message(toastNotification.title, {
119
+ showToast(toastNotification.title, {
120
120
  description: toastNotification.description,
121
121
  action: toastNotification.action,
122
122
  });
@@ -1,12 +1,15 @@
1
1
  "use client";
2
2
 
3
3
  import { useTranslations } from "next-intl";
4
- import React, { createContext, ReactElement, useCallback, useContext, useState } from "react";
4
+ import React, { createContext, ReactElement, useCallback, useContext, useEffect, useState } from "react";
5
5
  import { SharedProvider } from "../../../contexts";
6
6
  import { Modules } from "../../../core";
7
7
  import { useI18nRouter } from "../../../i18n";
8
8
  import { BreadcrumbItemData } from "../../../interfaces";
9
9
  import { NotificationMenuItem, NotificationToast } from "../components/notifications/Notification";
10
+ import { getRoleId, isRolesConfigured } from "../../../roles";
11
+ import { RoleInterface } from "../../role";
12
+ import { useCurrentUserContext } from "../../user/contexts/CurrentUserContext";
10
13
  import { NotificationInterface } from "../data";
11
14
  import { NotificationService } from "../data/notification.service";
12
15
 
@@ -50,6 +53,10 @@ export const NotificationContextProvider = ({ children }: NotificationContextPro
50
53
  const [isLoading, setIsLoading] = useState(false);
51
54
  const [error, setError] = useState<string | null>(null);
52
55
  const [lastLoaded, setLastLoaded] = useState(0);
56
+ const [hasInitiallyLoaded, setHasInitiallyLoaded] = useState(false);
57
+
58
+ // Access current user for initial load check
59
+ const { currentUser } = useCurrentUserContext();
53
60
 
54
61
  // Calculate shouldRefresh (5 minute cache)
55
62
  const shouldRefresh = Date.now() - lastLoaded > 5 * 60 * 1000;
@@ -88,6 +95,25 @@ export const NotificationContextProvider = ({ children }: NotificationContextPro
88
95
  }
89
96
  }, []);
90
97
 
98
+ // Initial load - runs once per session when user is available and not admin
99
+ useEffect(() => {
100
+ if (hasInitiallyLoaded || !currentUser) return;
101
+
102
+ // Skip for admin users
103
+ if (isRolesConfigured()) {
104
+ const isAdmin = currentUser.roles?.some(
105
+ (role: RoleInterface) => role.id === getRoleId().Administrator,
106
+ );
107
+ if (isAdmin) {
108
+ setHasInitiallyLoaded(true);
109
+ return;
110
+ }
111
+ }
112
+
113
+ loadNotifications();
114
+ setHasInitiallyLoaded(true);
115
+ }, [currentUser, hasInitiallyLoaded, loadNotifications]);
116
+
91
117
  const markNotificationsAsRead = useCallback(async (ids: string[]) => {
92
118
  setIsLoading(true);
93
119
  setError(null);
@@ -3,7 +3,7 @@
3
3
  import { PlusCircle } from "lucide-react";
4
4
  import { useTranslations } from "next-intl";
5
5
  import { useCallback, useEffect, useRef, useState } from "react";
6
- import { toast } from "sonner";
6
+ import { showToast } from "../../../../utils/toast";
7
7
  import { useDebounce } from "../../../../hooks";
8
8
  import {
9
9
  Button,
@@ -39,7 +39,7 @@ export function UserRoleAdd({ user, refresh }: UserRoleAddProps) {
39
39
  });
40
40
  setRoles(roles.filter((u) => u.id !== role.id));
41
41
 
42
- toast.message(
42
+ showToast(
43
43
  t(`common.association.label`, {
44
44
  source: t(`entities.roles`, { count: 1 }),
45
45
  destination: t(`entities.users`, { count: 1 }),
@@ -3,7 +3,7 @@
3
3
  import { MailIcon } from "lucide-react";
4
4
  import { useTranslations } from "next-intl";
5
5
  import { useState } from "react";
6
- import { toast } from "sonner";
6
+ import { showToast } from "../../../../utils/toast";
7
7
  import { errorToast } from "../../../../components";
8
8
  import { Modules } from "../../../../core";
9
9
  import { Action } from "../../../../permissions";
@@ -36,7 +36,7 @@ function UserResentInvitationEmailInternal({ user }: UserResentInvitationEmailPr
36
36
  await UserService.sendInvitation({ userId: user.id, companyId: user.company!.id });
37
37
 
38
38
  setOpen(false);
39
- toast.message(t(`user.resend_activation.email_sent`), {
39
+ showToast(t(`user.resend_activation.email_sent`), {
40
40
  description: t(`user.resend_activation.email_sent_description`, { email: user.email }),
41
41
  });
42
42
  } catch (error) {
package/src/index.ts CHANGED
@@ -26,3 +26,6 @@ export type { RoleIdConfig } from "./roles";
26
26
  // Auth configuration
27
27
  export { configureAuth, getTokenHandler } from "./features/auth/config";
28
28
  export type { TokenHandler, TokenParams } from "./features/auth/config";
29
+
30
+ // Toast utilities
31
+ export { showToast, showError, dismissToast, showCustomToast, type ToastOptions } from "./utils/toast";
@@ -1,32 +1,22 @@
1
- "use client"
1
+ "use client";
2
2
 
3
- import { useTheme } from "next-themes"
4
- import { Toaster as Sonner, type ToasterProps } from "sonner"
5
- import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"
3
+ import { useTheme } from "next-themes";
4
+ import { Toaster as Sonner, type ToasterProps } from "sonner";
5
+ import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react";
6
6
 
7
7
  const Toaster = ({ ...props }: ToasterProps) => {
8
- const { theme = "system" } = useTheme()
8
+ const { theme = "system" } = useTheme();
9
9
 
10
10
  return (
11
11
  <Sonner
12
12
  theme={theme as ToasterProps["theme"]}
13
13
  className="toaster group"
14
14
  icons={{
15
- success: (
16
- <CircleCheckIcon className="size-4" />
17
- ),
18
- info: (
19
- <InfoIcon className="size-4" />
20
- ),
21
- warning: (
22
- <TriangleAlertIcon className="size-4" />
23
- ),
24
- error: (
25
- <OctagonXIcon className="size-4" />
26
- ),
27
- loading: (
28
- <Loader2Icon className="size-4 animate-spin" />
29
- ),
15
+ success: <CircleCheckIcon className="size-4" />,
16
+ info: <InfoIcon className="size-4" />,
17
+ warning: <TriangleAlertIcon className="size-4" />,
18
+ error: <OctagonXIcon className="size-4" />,
19
+ loading: <Loader2Icon className="size-4 animate-spin" />,
30
20
  }}
31
21
  style={
32
22
  {
@@ -43,7 +33,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
43
33
  }}
44
34
  {...props}
45
35
  />
46
- )
47
- }
36
+ );
37
+ };
48
38
 
49
- export { Toaster }
39
+ export { Toaster };
@@ -14,3 +14,6 @@ export { entityObjectSchema, userObjectSchema, type EntityObject, type UserObjec
14
14
  export * from "./blocknote-diff.util";
15
15
  export * from "./blocknote-word-diff-renderer.util";
16
16
  export * from "./icons";
17
+
18
+ // Toast utilities
19
+ export { showToast, showError, dismissToast, showCustomToast, type ToastOptions } from "./toast";
@@ -0,0 +1,39 @@
1
+ import { toast as sonnerToast, type ExternalToast } from "sonner";
2
+ import type { ReactElement, JSXElementConstructor, ReactNode } from "react";
3
+
4
+ export type ToastOptions = {
5
+ description?: ReactNode;
6
+ duration?: number;
7
+ id?: string;
8
+ onDismiss?: () => void;
9
+ action?: ExternalToast["action"];
10
+ dismissible?: boolean;
11
+ };
12
+
13
+ /** Standard toast - neutral/white */
14
+ export function showToast(message: string, options?: ToastOptions) {
15
+ return sonnerToast(message, options as ExternalToast);
16
+ }
17
+
18
+ /** Error toast - destructive red */
19
+ export function showError(message: string, options?: ToastOptions) {
20
+ return sonnerToast.error(message, options as ExternalToast);
21
+ }
22
+
23
+ /** Dismiss toast(s) */
24
+ export function dismissToast(toastId?: string | number) {
25
+ return sonnerToast.dismiss(toastId);
26
+ }
27
+
28
+ /** Custom toast - ONLY for complex UI like upload progress */
29
+ export function showCustomToast(
30
+ render: (id: string | number) => ReactElement<unknown, string | JSXElementConstructor<unknown>>,
31
+ options?: {
32
+ id?: string | number;
33
+ duration?: number;
34
+ dismissible?: boolean;
35
+ onDismiss?: () => void;
36
+ },
37
+ ) {
38
+ return sonnerToast.custom(render, options);
39
+ }