@edifice.io/react 2.6.2-feat-INTEG-1924-port-react-nextcloud-filetree.20260824153355 → 2.6.3-develop-enabling.20260825190837

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 (97) hide show
  1. package/dist/components/Alert/Alert.js +1 -1
  2. package/dist/components/DatePicker/DatePicker.d.ts +5 -0
  3. package/dist/components/DatePicker/DatePicker.js +2 -0
  4. package/dist/components/Layout/Layout.js +5 -3
  5. package/dist/components/Layout/components/NavLink.d.ts +1 -1
  6. package/dist/components/Layout/components/NavLink.js +2 -0
  7. package/dist/components/Tree/utilities/tree.js +1 -21
  8. package/dist/hooks/index.d.ts +0 -1
  9. package/dist/hooks/useClickOutside/useClickOutside.d.ts +1 -1
  10. package/dist/hooks/useClickOutside/useClickOutside.js +1 -1
  11. package/dist/icons-apps.js +112 -100
  12. package/dist/icons.js +340 -338
  13. package/dist/index.js +146 -152
  14. package/dist/modules/editor/test-utils/createTestEditor.d.ts +10 -0
  15. package/dist/modules/homepage/components/Communities/Communities.d.ts +10 -0
  16. package/dist/modules/homepage/components/Communities/CommunitiesContainer.d.ts +9 -0
  17. package/dist/modules/homepage/components/Communities/CommunitiesSkeleton.d.ts +5 -0
  18. package/dist/modules/homepage/components/Communities/CommunityItem.d.ts +11 -0
  19. package/dist/modules/homepage/components/Communities/index.d.ts +8 -0
  20. package/dist/modules/homepage/components/Communities/useCommunities.d.ts +13 -0
  21. package/dist/modules/homepage/components/Favorites/Favorites.js +7 -4
  22. package/dist/modules/homepage/components/Header/Header.js +20 -2
  23. package/dist/modules/homepage/components/HomeCard/HomeCard.d.ts +5 -1
  24. package/dist/modules/homepage/components/HomeCard/HomeCard.js +10 -2
  25. package/dist/modules/homepage/components/LastInfos/LastInfosList.js +2 -2
  26. package/dist/modules/homepage/components/Notifications/NotificationItem.js +5 -4
  27. package/dist/modules/homepage/components/Notifications/NotificationList.d.ts +7 -1
  28. package/dist/modules/homepage/components/Notifications/NotificationList.js +10 -3
  29. package/dist/modules/homepage/components/Notifications/NotificationListContainer.js +4 -1
  30. package/dist/modules/homepage/components/Notifications/components/CommonNotificationItem.d.ts +2 -1
  31. package/dist/modules/homepage/components/Notifications/components/CommonNotificationItem.js +14 -10
  32. package/dist/modules/homepage/components/Notifications/components/NotificationFilterMenu.d.ts +16 -0
  33. package/dist/modules/homepage/components/Notifications/components/NotificationFilterMenu.js +29 -0
  34. package/dist/modules/homepage/components/Notifications/components/NotificationFilterModal.d.ts +18 -0
  35. package/dist/modules/homepage/components/Notifications/components/NotificationFilterModal.js +67 -0
  36. package/dist/modules/homepage/components/Notifications/components/NotificationItemResource.d.ts +2 -1
  37. package/dist/modules/homepage/components/Notifications/components/NotificationItemResource.js +3 -2
  38. package/dist/modules/homepage/components/Notifications/components/NotificationOptionsMenu.d.ts +12 -0
  39. package/dist/modules/homepage/components/Notifications/components/NotificationOptionsMenu.js +42 -0
  40. package/dist/modules/homepage/components/Notifications/components/ReportNotificationModal.d.ts +13 -0
  41. package/dist/modules/homepage/components/Notifications/components/ReportNotificationModal.js +31 -0
  42. package/dist/modules/homepage/components/Notifications/components/SystemNotificationItem.d.ts +3 -1
  43. package/dist/modules/homepage/components/Notifications/components/SystemNotificationItem.js +3 -2
  44. package/dist/modules/homepage/components/Notifications/components/UserNotificationItem.d.ts +3 -1
  45. package/dist/modules/homepage/components/Notifications/components/UserNotificationItem.js +3 -2
  46. package/dist/modules/homepage/components/Notifications/components/notificationAdapter.d.ts +22 -7
  47. package/dist/modules/homepage/components/Notifications/components/notificationAdapter.js +31 -13
  48. package/dist/modules/homepage/components/Notifications/hooks/useNotificationList.d.ts +11 -1
  49. package/dist/modules/homepage/components/Notifications/hooks/useNotificationList.js +34 -2
  50. package/dist/modules/homepage/components/Notifications/services/api/index.d.ts +2 -0
  51. package/dist/modules/homepage/components/Notifications/services/api/notificationService.d.ts +8 -0
  52. package/dist/modules/homepage/components/Notifications/services/api/notificationService.js +12 -0
  53. package/dist/modules/homepage/components/Notifications/services/queries/notification.d.ts +13 -1
  54. package/dist/modules/homepage/components/Notifications/services/queries/notification.js +41 -4
  55. package/dist/modules/homepage/components/SchoolSpace/SchoolSpace.js +20 -11
  56. package/dist/modules/homepage/components/UserSpace/UserSpace.js +5 -2
  57. package/dist/modules/homepage/components/UserSpace/hooks/useChildren.d.ts +14 -0
  58. package/dist/modules/homepage/components/UserSpace/hooks/useChildren.js +23 -0
  59. package/dist/modules/homepage/components/UserSpace/hooks/useProfileLinks.d.ts +8 -0
  60. package/dist/modules/homepage/components/UserSpace/hooks/useProfileLinks.js +65 -0
  61. package/dist/modules/icons/components/IconClass.d.ts +7 -0
  62. package/dist/modules/icons/components/IconClass.js +13 -0
  63. package/dist/modules/icons/components/apps/IconNextcloudDeck.d.ts +7 -0
  64. package/dist/modules/icons/components/apps/IconNextcloudDeck.js +12 -0
  65. package/dist/modules/icons/components/apps/IconNextcloudFiles.d.ts +7 -0
  66. package/dist/modules/icons/components/apps/IconNextcloudFiles.js +12 -0
  67. package/dist/modules/icons/components/apps/IconNextcloudForms.d.ts +7 -0
  68. package/dist/modules/icons/components/apps/IconNextcloudForms.js +12 -0
  69. package/dist/modules/icons/components/apps/IconNextcloudNotes.d.ts +7 -0
  70. package/dist/modules/icons/components/apps/IconNextcloudNotes.js +12 -0
  71. package/dist/modules/icons/components/apps/IconNextcloudTalk.d.ts +7 -0
  72. package/dist/modules/icons/components/apps/IconNextcloudTalk.js +12 -0
  73. package/dist/modules/icons/components/apps/IconPeertube.d.ts +7 -0
  74. package/dist/modules/icons/components/apps/IconPeertube.js +21 -0
  75. package/dist/modules/icons/components/apps/index.d.ts +6 -0
  76. package/dist/modules/icons/components/index.d.ts +1 -0
  77. package/dist/modules/multimedia/FileCard/index.d.ts +0 -2
  78. package/dist/modules/multimedia/MediaLibrary/MediaLibrary.d.ts +2 -2
  79. package/dist/modules/multimedia/MediaLibrary/MediaLibrary.js +7 -21
  80. package/dist/modules/multimedia/MediaLibrary/innertabs/index.d.ts +0 -1
  81. package/dist/modules/multimedia/MediaLibrary/innertabs/index.js +0 -2
  82. package/dist/modules/multimedia/index.d.ts +0 -1
  83. package/dist/modules/multimedia/testing/mediaHarness.d.ts +154 -0
  84. package/dist/multimedia.js +12 -16
  85. package/package.json +6 -6
  86. package/dist/hooks/useNextcloudSearch/index.d.ts +0 -1
  87. package/dist/hooks/useNextcloudSearch/useNextcloudSearch.d.ts +0 -12
  88. package/dist/hooks/useNextcloudSearch/useNextcloudSearch.js +0 -57
  89. package/dist/modules/multimedia/FileCard/NextcloudFileCard.d.ts +0 -25
  90. package/dist/modules/multimedia/FileCard/NextcloudFileCard.js +0 -102
  91. package/dist/modules/multimedia/LinkerCard/LinkerCard._.d.ts +0 -8
  92. package/dist/modules/multimedia/MediaLibrary/innertabs/Nextcloud.d.ts +0 -1
  93. package/dist/modules/multimedia/MediaLibrary/innertabs/Nextcloud.js +0 -35
  94. package/dist/modules/multimedia/Nextcloud/Nextcloud.d.ts +0 -24
  95. package/dist/modules/multimedia/Nextcloud/Nextcloud.js +0 -114
  96. package/dist/modules/multimedia/Nextcloud/index.d.ts +0 -2
  97. package/dist/modules/multimedia/UploadCard/UploadCard._.d.ts +0 -9
@@ -1,7 +1,8 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import illuEmptyNotification from "@edifice.io/bootstrap/dist/images/emptyscreen/illu-notifications.png";
2
+ import illuEmptyNotification from "@edifice.io/bootstrap/dist/images/homepage/illu-empty-notifications.png";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import SvgIconClose from "../../../icons/components/IconClose.js";
5
+ import NotificationFilterMenu from "./components/NotificationFilterMenu.js";
5
6
  import NotificationItem from "./NotificationItem.js";
6
7
  import NotificationListSkeleton from "./NotificationListSkeleton.js";
7
8
  import NotificationSkeleton from "./NotificationSkeleton.js";
@@ -12,6 +13,9 @@ import EmptyScreen from "../../../../components/EmptyScreen/EmptyScreen.js";
12
13
  import Divider from "../../../../components/Divider/Divider.js";
13
14
  const NotificationList = ({
14
15
  notifications,
16
+ notificationTypes,
17
+ selectedTypes,
18
+ onFilterChange,
15
19
  onCloseNotifications,
16
20
  onLoadNextPage,
17
21
  hasNextPage,
@@ -26,10 +30,13 @@ const NotificationList = ({
26
30
  };
27
31
  return /* @__PURE__ */ jsx("section", { role: "region", className: "notification-list", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
28
32
  /* @__PURE__ */ jsxs(Flex, { justify: "between", align: "center", wrap: "nowrap", className: "py-16 ps-24 pe-8", children: [
29
- /* @__PURE__ */ jsx("h4", { className: "text-truncate", children: t("homepage.notifications-list.title") }),
33
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "8", className: "notification-list-title", children: [
34
+ /* @__PURE__ */ jsx("h4", { className: "text-truncate", children: t("homepage.notifications-list.title") }),
35
+ notificationTypes && notificationTypes.length > 0 && /* @__PURE__ */ jsx(NotificationFilterMenu, { notificationTypes, selectedTypes: selectedTypes ?? notificationTypes, onFilterChange: (types) => onFilterChange == null ? void 0 : onFilterChange(types) })
36
+ ] }),
30
37
  onCloseNotifications && /* @__PURE__ */ jsx(ButtonBeta, { color: "tertiary", variant: "ghost", rightIcon: /* @__PURE__ */ jsx(SvgIconClose, {}), onClick: handleCloseClick, "aria-label": t("homepage.notifications-list.close"), title: t("homepage.notifications-list.close"), "data-testid": "notification-list-close-button" })
31
38
  ] }),
32
- (notifications == null ? void 0 : notifications.length) === 0 ? /* @__PURE__ */ jsx("div", { className: "mx-24", children: /* @__PURE__ */ jsx(EmptyScreen, { size: 120, imageSrc: illuEmptyNotification, imageAlt: t("homepage.notifications-list.empty.description"), text: t("homepage.notifications-list.empty.description") }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
39
+ (notifications == null ? void 0 : notifications.length) === 0 ? /* @__PURE__ */ jsx("div", { className: "mx-24", children: /* @__PURE__ */ jsx(EmptyScreen, { size: 135, imageSrc: illuEmptyNotification, imageAlt: t("homepage.notifications-list.empty.description"), text: t("homepage.notifications-list.empty.description") }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
33
40
  /* @__PURE__ */ jsx("ul", { children: notifications == null ? void 0 : notifications.map((notification, index) => /* @__PURE__ */ jsxs("li", { children: [
34
41
  /* @__PURE__ */ jsx(NotificationItem, { notification }),
35
42
  /* @__PURE__ */ jsx(Divider, { className: "border-grey-300 my-0" })
@@ -6,11 +6,14 @@ const NotificationListContainer = ({
6
6
  }) => {
7
7
  const {
8
8
  notifications,
9
+ notificationTypes,
10
+ selectedTypes,
11
+ setSelectedTypes,
9
12
  hasNextPage,
10
13
  loadNextPage,
11
14
  isLoading
12
15
  } = useNotificationListContainer();
13
- return /* @__PURE__ */ jsx(NotificationList, { notifications, onCloseNotifications, onLoadNextPage: () => {
16
+ return /* @__PURE__ */ jsx(NotificationList, { notifications, notificationTypes, selectedTypes, onFilterChange: setSelectedTypes, onCloseNotifications, onLoadNextPage: () => {
14
17
  hasNextPage && !isLoading && loadNextPage();
15
18
  }, hasNextPage, isLoading });
16
19
  };
@@ -5,6 +5,7 @@ export type NotificationItemProps = {
5
5
  date: Date;
6
6
  uri?: string;
7
7
  children?: ReactNode;
8
+ menu?: ReactNode;
8
9
  };
9
10
  /**
10
11
  * Base layout for a single notification row.
@@ -18,7 +19,7 @@ export type NotificationItemProps = {
18
19
  * `SystemNotification` instead of consuming this directly.
19
20
  */
20
21
  declare const CommonNotificationItem: {
21
- ({ picture, message, date, uri, children, }: NotificationItemProps): import("react/jsx-runtime").JSX.Element;
22
+ ({ picture, message, date, uri, children, menu, }: NotificationItemProps): import("react/jsx-runtime").JSX.Element;
22
23
  displayName: string;
23
24
  };
24
25
  export default CommonNotificationItem;
@@ -6,21 +6,25 @@ const CommonNotificationItem = ({
6
6
  message,
7
7
  date,
8
8
  uri,
9
- children
9
+ children,
10
+ menu
10
11
  }) => {
11
12
  const {
12
13
  formatTimeAgo,
13
14
  formatDate
14
15
  } = useDate();
15
- return /* @__PURE__ */ jsx(Flex, { direction: "column", className: "notification-item", gap: "8", children: /* @__PURE__ */ jsxs(Flex, { direction: "row", gap: "8", children: [
16
- /* @__PURE__ */ jsx("div", { className: "notification-item-picture", children: picture }),
17
- /* @__PURE__ */ jsx("a", { href: uri, "data-testid": "notification-item-content", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "8", children: [
18
- /* @__PURE__ */ jsx("div", { className: "notification-item-message", dangerouslySetInnerHTML: {
19
- __html: message
20
- } }),
21
- children,
22
- /* @__PURE__ */ jsx("p", { className: "notification-item-date", title: formatDate(date, "LLL"), children: formatTimeAgo(date) })
23
- ] }) })
16
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", className: "notification-item", gap: "8", children: /* @__PURE__ */ jsxs(Flex, { direction: "row", justify: "between", wrap: "nowrap", gap: "8", children: [
17
+ /* @__PURE__ */ jsxs(Flex, { direction: "row", align: "start", gap: "8", className: "notification-item-content-wrapper", children: [
18
+ /* @__PURE__ */ jsx("div", { className: "notification-item-picture", children: picture }),
19
+ /* @__PURE__ */ jsx("a", { href: uri, className: "notification-item-link", "data-testid": "notification-item-content", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "8", children: [
20
+ /* @__PURE__ */ jsx("div", { className: "notification-item-message", dangerouslySetInnerHTML: {
21
+ __html: message
22
+ } }),
23
+ children,
24
+ /* @__PURE__ */ jsx("p", { className: "notification-item-date", title: formatDate(date, "LLL"), children: formatTimeAgo(date) })
25
+ ] }) })
26
+ ] }),
27
+ menu && /* @__PURE__ */ jsx("div", { className: "notification-item-menu", children: menu })
24
28
  ] }) });
25
29
  };
26
30
  export {
@@ -0,0 +1,16 @@
1
+ export type NotificationFilterMenuProps = {
2
+ /** All notification types available */
3
+ notificationTypes: string[];
4
+ /** Notification types currently applied as filter */
5
+ selectedTypes: string[];
6
+ /** Callback when the user validates a new filter selection */
7
+ onFilterChange: (types: string[]) => void;
8
+ };
9
+ /**
10
+ * Trigger button opening the notification types filter modal.
11
+ */
12
+ declare const NotificationFilterMenu: {
13
+ ({ notificationTypes, selectedTypes, onFilterChange, }: NotificationFilterMenuProps): import("react/jsx-runtime").JSX.Element;
14
+ displayName: string;
15
+ };
16
+ export default NotificationFilterMenu;
@@ -0,0 +1,29 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useTranslation } from "react-i18next";
4
+ import SvgIconFilter from "../../../../icons/components/IconFilter.js";
5
+ import SvgIconRafterDown from "../../../../icons/components/IconRafterDown.js";
6
+ import NotificationFilterModal from "./NotificationFilterModal.js";
7
+ import ButtonBeta from "../../../../../components/ButtonBeta/ButtonBeta.js";
8
+ const NotificationFilterMenu = ({
9
+ notificationTypes,
10
+ selectedTypes,
11
+ onFilterChange
12
+ }) => {
13
+ const {
14
+ t
15
+ } = useTranslation(), [isOpen, setIsOpen] = useState(!1), handleApply = (types) => {
16
+ onFilterChange(types), setIsOpen(!1);
17
+ };
18
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
19
+ /* @__PURE__ */ jsx(ButtonBeta, { type: "button", color: "secondary", variant: "outline", size: "md", leftIcon: /* @__PURE__ */ jsx(SvgIconFilter, {}), rightIcon: /* @__PURE__ */ jsx(SvgIconRafterDown, {}), "aria-label": t("homepage.notifications.filter-modal.trigger", {
20
+ defaultValue: "Filtrer les notifications"
21
+ }), title: t("homepage.notifications.filter-modal.trigger", {
22
+ defaultValue: "Filtrer les notifications"
23
+ }), onClick: () => setIsOpen(!0), "data-testid": "notification-filter-button" }),
24
+ isOpen && /* @__PURE__ */ jsx(NotificationFilterModal, { isOpen, allTypes: notificationTypes, appliedTypes: selectedTypes, onCancel: () => setIsOpen(!1), onApply: handleApply })
25
+ ] });
26
+ };
27
+ export {
28
+ NotificationFilterMenu as default
29
+ };
@@ -0,0 +1,18 @@
1
+ export type NotificationFilterModalProps = {
2
+ isOpen: boolean;
3
+ /** All notification types available */
4
+ allTypes: string[];
5
+ /** Notification types currently applied as filter */
6
+ appliedTypes: string[];
7
+ onCancel: () => void;
8
+ onApply: (types: string[]) => void;
9
+ };
10
+ /**
11
+ * Modal letting the user choose which notification types to display in the
12
+ * homepage notifications list.
13
+ */
14
+ declare const NotificationFilterModal: {
15
+ ({ isOpen, allTypes, appliedTypes, onCancel, onApply, }: NotificationFilterModalProps): import("react/jsx-runtime").JSX.Element;
16
+ displayName: string;
17
+ };
18
+ export default NotificationFilterModal;
@@ -0,0 +1,67 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import { useState } from "react";
4
+ import { useTranslation } from "react-i18next";
5
+ import SvgIconCheck from "../../../../icons/components/IconCheck.js";
6
+ import { getAppCodeAndI18nKey } from "./notificationAdapter.js";
7
+ import Modal from "../../../../../components/Modal/Modal.js";
8
+ import Flex from "../../../../../components/Flex/Flex.js";
9
+ import Checkbox from "../../../../../components/Checkbox/Checkbox.js";
10
+ import AppIcon from "../../../../../components/AppIcon/AppIcon.js";
11
+ import ButtonBeta from "../../../../../components/ButtonBeta/ButtonBeta.js";
12
+ const NotificationFilterModal = ({
13
+ isOpen,
14
+ allTypes,
15
+ appliedTypes,
16
+ onCancel,
17
+ onApply
18
+ }) => {
19
+ const {
20
+ t
21
+ } = useTranslation(), [selected, setSelected] = useState(appliedTypes), toggleType = (type) => {
22
+ setSelected((current) => current.includes(type) ? current.filter((value) => value !== type) : [...current, type]);
23
+ }, toggleAll = () => {
24
+ setSelected(selected.length === allTypes.length ? [] : allTypes);
25
+ };
26
+ return /* @__PURE__ */ jsxs(Modal, { id: "notification-filter-modal", size: "lg", scrollable: !0, isOpen, onModalClose: onCancel, children: [
27
+ /* @__PURE__ */ jsx(Modal.Header, { onModalClose: onCancel, children: t("homepage.notifications.filter-modal.title", {
28
+ defaultValue: "Filtrer les notifications"
29
+ }) }),
30
+ /* @__PURE__ */ jsx(Modal.Subtitle, { children: t("homepage.notifications.filter-modal.subtitle", {
31
+ defaultValue: "Sélectionnez seulement les notifications importantes pour vous"
32
+ }) }),
33
+ /* @__PURE__ */ jsxs(Modal.Body, { children: [
34
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "8", className: "mb-16", children: [
35
+ /* @__PURE__ */ jsx("div", { className: "notification-filter-select-all", children: /* @__PURE__ */ jsx(Checkbox, { label: t("homepage.notifications.filter-modal.select-all", {
36
+ defaultValue: "Tout sélectionner"
37
+ }), checked: selected.length === allTypes.length, indeterminate: selected.length > 0 && selected.length < allTypes.length, onChange: toggleAll }) }),
38
+ /* @__PURE__ */ jsx("span", { className: "notification-filter-count", children: t("homepage.notifications.filter-modal.count", {
39
+ selected: selected.length,
40
+ total: allTypes.length,
41
+ defaultValue: "[[selected]] / [[total]] sélectionnées"
42
+ }) })
43
+ ] }),
44
+ /* @__PURE__ */ jsx(Flex, { wrap: "wrap", gap: "8", className: "notification-filter-options", children: allTypes.map((type) => {
45
+ const [appCode, appI18nKey] = getAppCodeAndI18nKey(type), checked = selected.includes(type);
46
+ return /* @__PURE__ */ jsxs("label", { className: clsx("notification-filter-chip", {
47
+ "is-checked": checked
48
+ }), children: [
49
+ /* @__PURE__ */ jsx(AppIcon, { app: appCode, size: "24", iconFit: "contain" }),
50
+ /* @__PURE__ */ jsx("span", { children: t(appI18nKey, {
51
+ defaultValue: appCode
52
+ }) }),
53
+ /* @__PURE__ */ jsx("input", { type: "checkbox", className: "form-check-input", checked, onChange: () => toggleType(type) })
54
+ ] }, type);
55
+ }) })
56
+ ] }),
57
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
58
+ /* @__PURE__ */ jsx(ButtonBeta, { color: "tertiary", onClick: onCancel, type: "button", variant: "ghost", children: t("cancel") }),
59
+ /* @__PURE__ */ jsx(ButtonBeta, { color: "default", onClick: () => onApply(selected), type: "button", variant: "filled", rightIcon: /* @__PURE__ */ jsx(SvgIconCheck, {}), children: t("homepage.notifications.filter-modal.confirm", {
60
+ defaultValue: "Enregistrer"
61
+ }) })
62
+ ] })
63
+ ] });
64
+ };
65
+ export {
66
+ NotificationFilterModal as default
67
+ };
@@ -1,5 +1,6 @@
1
1
  export type NotificationItemResourceProps = {
2
2
  appCode: string;
3
+ appI18nKey: string;
3
4
  };
4
5
  /**
5
6
  * Displays the originating application of a notification as a small icon + label.
@@ -9,7 +10,7 @@ export type NotificationItemResourceProps = {
9
10
  *
10
11
  */
11
12
  declare const NotificationItemResource: {
12
- ({ appCode, }: NotificationItemResourceProps): import("react/jsx-runtime").JSX.Element;
13
+ ({ appCode, appI18nKey, }: NotificationItemResourceProps): import("react/jsx-runtime").JSX.Element;
13
14
  displayName: string;
14
15
  };
15
16
  export default NotificationItemResource;
@@ -2,11 +2,12 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useTranslation } from "react-i18next";
3
3
  import AppIcon from "../../../../../components/AppIcon/AppIcon.js";
4
4
  const NotificationItemResource = ({
5
- appCode
5
+ appCode,
6
+ appI18nKey
6
7
  }) => {
7
8
  const {
8
9
  t
9
- } = useTranslation(), appLabel = t(`${appCode}`, {
10
+ } = useTranslation(), appLabel = t(`${appI18nKey}`, {
10
11
  defaultValue: appCode
11
12
  }), appCssClass = `app-${appCode}`;
12
13
  return /* @__PURE__ */ jsxs("div", { className: `notification-item-resource ${appCssClass} bg-app-light`, children: [
@@ -0,0 +1,12 @@
1
+ export type NotificationOptionsMenuProps = {
2
+ notificationId: string;
3
+ };
4
+ /**
5
+ * Options menu for a single notification row, allowing the user to report
6
+ * or delete it.
7
+ */
8
+ declare const NotificationOptionsMenu: {
9
+ ({ notificationId, }: NotificationOptionsMenuProps): import("react/jsx-runtime").JSX.Element;
10
+ displayName: string;
11
+ };
12
+ export default NotificationOptionsMenu;
@@ -0,0 +1,42 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useTranslation } from "react-i18next";
4
+ import SvgIconAlertTriangle from "../../../../icons/components/IconAlertTriangle.js";
5
+ import SvgIconDelete from "../../../../icons/components/IconDelete.js";
6
+ import SvgIconOptions from "../../../../icons/components/IconOptions.js";
7
+ import { useReportNotification, useDeleteNotification } from "../services/queries/notification.js";
8
+ import ReportNotificationModal from "./ReportNotificationModal.js";
9
+ import Dropdown from "../../../../../components/Dropdown/Dropdown.js";
10
+ import IconButton from "../../../../../components/Button/IconButton.js";
11
+ const NotificationOptionsMenu = ({
12
+ notificationId
13
+ }) => {
14
+ const {
15
+ t
16
+ } = useTranslation(), {
17
+ mutate: reportNotification
18
+ } = useReportNotification(), {
19
+ mutate: deleteNotification
20
+ } = useDeleteNotification(), [isReportModalOpen, setIsReportModalOpen] = useState(!1);
21
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
22
+ /* @__PURE__ */ jsx(Dropdown, { overflow: !1, children: (triggerProps) => /* @__PURE__ */ jsxs(Fragment, { children: [
23
+ /* @__PURE__ */ jsx(IconButton, { ...triggerProps, type: "button", "aria-label": t("homepage.notifications.options.label", {
24
+ defaultValue: "Options de la notification"
25
+ }), color: "tertiary", variant: "ghost", icon: /* @__PURE__ */ jsx(SvgIconOptions, {}), "data-testid": "notification-item-options-button" }),
26
+ /* @__PURE__ */ jsxs(Dropdown.Menu, { children: [
27
+ /* @__PURE__ */ jsx(Dropdown.Item, { icon: /* @__PURE__ */ jsx(SvgIconAlertTriangle, {}), onClick: () => setIsReportModalOpen(!0), children: t("homepage.notifications.options.report", {
28
+ defaultValue: "Signaler"
29
+ }) }),
30
+ /* @__PURE__ */ jsx(Dropdown.Item, { icon: /* @__PURE__ */ jsx(SvgIconDelete, {}), onClick: () => deleteNotification(notificationId), children: t("homepage.notifications.options.delete", {
31
+ defaultValue: "Supprimer"
32
+ }) })
33
+ ] })
34
+ ] }) }),
35
+ isReportModalOpen && /* @__PURE__ */ jsx(ReportNotificationModal, { isOpen: isReportModalOpen, onCancel: () => setIsReportModalOpen(!1), onConfirm: () => {
36
+ reportNotification(notificationId), setIsReportModalOpen(!1);
37
+ } })
38
+ ] });
39
+ };
40
+ export {
41
+ NotificationOptionsMenu as default
42
+ };
@@ -0,0 +1,13 @@
1
+ export type ReportNotificationModalProps = {
2
+ isOpen: boolean;
3
+ onCancel: () => void;
4
+ onConfirm: () => void;
5
+ };
6
+ /**
7
+ * Confirmation modal shown before reporting a notification as inappropriate.
8
+ */
9
+ declare const ReportNotificationModal: {
10
+ ({ isOpen, onCancel, onConfirm, }: ReportNotificationModalProps): import("react/jsx-runtime").JSX.Element;
11
+ displayName: string;
12
+ };
13
+ export default ReportNotificationModal;
@@ -0,0 +1,31 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import SvgIconAlertTriangle from "../../../../icons/components/IconAlertTriangle.js";
4
+ import Modal from "../../../../../components/Modal/Modal.js";
5
+ import ButtonBeta from "../../../../../components/ButtonBeta/ButtonBeta.js";
6
+ const ReportNotificationModal = ({
7
+ isOpen,
8
+ onCancel,
9
+ onConfirm
10
+ }) => {
11
+ const {
12
+ t
13
+ } = useTranslation();
14
+ return /* @__PURE__ */ jsxs(Modal, { id: "report-notification-modal", size: "lg", isOpen, onModalClose: onCancel, children: [
15
+ /* @__PURE__ */ jsx(Modal.Header, { onModalClose: onCancel, children: t("homepage.notifications.report-modal.title", {
16
+ defaultValue: "Signaler une notification"
17
+ }) }),
18
+ /* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsx("p", { children: t("homepage.notifications.report-modal.body", {
19
+ defaultValue: "Attention ! Vous souhaitez signaler une notification inappropriée ou qui ne respecte pas les conditions d'utilisation de ONE. L'heure et l'origine de ce signalement seront enregistrées et transmises à un administrateur pour traitement."
20
+ }) }) }),
21
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
22
+ /* @__PURE__ */ jsx(ButtonBeta, { color: "tertiary", onClick: onCancel, type: "button", variant: "ghost", children: t("cancel") }),
23
+ /* @__PURE__ */ jsx(ButtonBeta, { color: "default", onClick: onConfirm, type: "button", variant: "filled", leftIcon: /* @__PURE__ */ jsx(SvgIconAlertTriangle, {}), children: t("homepage.notifications.report-modal.confirm", {
24
+ defaultValue: "Signaler la notification"
25
+ }) })
26
+ ] })
27
+ ] });
28
+ };
29
+ export {
30
+ ReportNotificationModal as default
31
+ };
@@ -1,6 +1,8 @@
1
+ import { ReactNode } from 'react';
1
2
  import { SystemWebNotification } from './notificationAdapter';
2
3
  export type SystemNotificationItemProps = {
3
4
  notification: SystemWebNotification;
5
+ menu?: ReactNode;
4
6
  };
5
7
  /**
6
8
  * Renders a system notification (no sender) using an app icon as the picture.
@@ -9,7 +11,7 @@ export type SystemNotificationItemProps = {
9
11
  * rather than this component directly.
10
12
  */
11
13
  declare const SystemNotificationItem: {
12
- ({ notification, }: SystemNotificationItemProps): import("react/jsx-runtime").JSX.Element;
14
+ ({ notification, menu, }: SystemNotificationItemProps): import("react/jsx-runtime").JSX.Element;
13
15
  displayName: string;
14
16
  };
15
17
  export default SystemNotificationItem;
@@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next";
3
3
  import CommonNotificationItem from "./CommonNotificationItem.js";
4
4
  import AppIcon from "../../../../../components/AppIcon/AppIcon.js";
5
5
  const SystemNotificationItem = ({
6
- notification
6
+ notification,
7
+ menu
7
8
  }) => {
8
9
  const {
9
10
  params,
@@ -13,7 +14,7 @@ const SystemNotificationItem = ({
13
14
  } = notification, {
14
15
  t
15
16
  } = useTranslation(), appName = t(params.appCode);
16
- return /* @__PURE__ */ jsx(CommonNotificationItem, { uri, message, date, picture: /* @__PURE__ */ jsx("a", { href: uri, title: appName, "data-testid": "notification-item-app-icon", "aria-label": t("homepage.notifications.app-icon.placeholder"), children: /* @__PURE__ */ jsx(AppIcon, { app: params.appCode, size: "24", variant: "square", className: "notification-item-app-icon" }) }) });
17
+ return /* @__PURE__ */ jsx(CommonNotificationItem, { uri, message, date, menu, picture: /* @__PURE__ */ jsx("a", { href: uri, title: appName, "data-testid": "notification-item-app-icon", "aria-label": t("homepage.notifications.app-icon.placeholder"), children: /* @__PURE__ */ jsx(AppIcon, { app: params.appCode, size: "24", variant: "square", className: "notification-item-app-icon" }) }) });
17
18
  };
18
19
  export {
19
20
  SystemNotificationItem as default
@@ -1,6 +1,8 @@
1
+ import { ReactNode } from 'react';
1
2
  import { UserWebNotification } from './notificationAdapter';
2
3
  export type UserNotificationItemProps = {
3
4
  notification: UserWebNotification;
5
+ menu?: ReactNode;
4
6
  };
5
7
  /**
6
8
  * Renders a user-triggered notification using the sender's avatar as the picture.
@@ -9,7 +11,7 @@ export type UserNotificationItemProps = {
9
11
  * — prefer using `Notification` at the top level rather than this component directly.
10
12
  */
11
13
  declare const UserNotificationItem: {
12
- ({ notification }: UserNotificationItemProps): import("react/jsx-runtime").JSX.Element;
14
+ ({ notification, menu, }: UserNotificationItemProps): import("react/jsx-runtime").JSX.Element;
13
15
  displayName: string;
14
16
  };
15
17
  export default UserNotificationItem;
@@ -5,7 +5,8 @@ import NotificationItemResource from "./NotificationItemResource.js";
5
5
  import useDirectory from "../../../../../hooks/useDirectory/useDirectory.js";
6
6
  import Avatar from "../../../../../components/Avatar/Avatar.js";
7
7
  const UserNotificationItem = ({
8
- notification
8
+ notification,
9
+ menu
9
10
  }) => {
10
11
  const {
11
12
  getAvatarURL,
@@ -18,7 +19,7 @@ const UserNotificationItem = ({
18
19
  } = notification, {
19
20
  t
20
21
  } = useTranslation(), userName = t(params.username);
21
- return /* @__PURE__ */ jsx(CommonNotificationItem, { uri, message, date, picture: /* @__PURE__ */ jsx("a", { href: getUserbookURL(params.userId, "user"), title: userName, "data-testid": "notification-item-avatar", children: /* @__PURE__ */ jsx(Avatar, { "aria-label": t("homepage.notifications.avatar.placeholder"), src: getAvatarURL(params.userId, "user"), alt: userName, variant: "circle", className: "notification-item-avatar" }) }), children: /* @__PURE__ */ jsx(NotificationItemResource, { appCode: params.appCode }) });
22
+ return /* @__PURE__ */ jsx(CommonNotificationItem, { uri, message, date, menu, picture: /* @__PURE__ */ jsx("a", { href: getUserbookURL(params.userId, "user"), title: userName, "data-testid": "notification-item-avatar", children: /* @__PURE__ */ jsx(Avatar, { "aria-label": t("homepage.notifications.avatar.placeholder"), src: getAvatarURL(params.userId, "user"), alt: userName, variant: "circle", className: "notification-item-avatar" }) }), children: /* @__PURE__ */ jsx(NotificationItemResource, { appCode: params.appCode, appI18nKey: params.appI18nKey }) });
22
23
  };
23
24
  export {
24
25
  UserNotificationItem as default
@@ -1,15 +1,21 @@
1
1
  import { NotificationModel } from '@edifice.io/client';
2
- type WebNotificationBase = {
2
+ type NotificationType = 'system' | 'user';
3
+ interface WebNotificationParamsBase {
4
+ appCode: string;
5
+ appI18nKey: string;
6
+ }
7
+ interface WebNotificationBase {
8
+ type: NotificationType;
3
9
  id: string;
4
10
  uri?: string;
5
11
  message: string;
6
12
  date: Date;
7
- };
13
+ params: WebNotificationParamsBase;
14
+ }
8
15
  /** Notification triggered by another user (e.g. shared a resource). */
9
16
  export type UserWebNotification = WebNotificationBase & {
10
17
  type: 'user';
11
- params: {
12
- appCode: string;
18
+ params: WebNotificationParamsBase & {
13
19
  username: string;
14
20
  userId: string;
15
21
  };
@@ -17,11 +23,20 @@ export type UserWebNotification = WebNotificationBase & {
17
23
  /** Notification triggered by an application event (no sender). */
18
24
  export type SystemWebNotification = WebNotificationBase & {
19
25
  type: 'system';
20
- params: {
21
- appCode: string;
22
- };
23
26
  };
24
27
  /** Discriminated union — narrow on `type` to get the specific variant. */
25
28
  export type WebNotification = UserWebNotification | SystemWebNotification;
29
+ export declare const getAppCodeAndI18nKey: (appCode: string) => [string, string];
30
+ /**
31
+ * Transforms a raw `NotificationModel` (API shape) into a `WebNotification`
32
+ * ready for rendering.
33
+ *
34
+ * Responsibilities:
35
+ * - Determines the variant: **user** when a `sender` is present, **system** otherwise.
36
+ * - Resolves the target URI from the model's `params` (handles both user and system naming conventions).
37
+ * - Converts the MongoDB `$date` timestamp to a JS `Date`.
38
+ * - Normalises the `type` field into a kebab-case `appCode` (e.g. `"BLOG_POST"` → `"blog-post"`).
39
+ * - Strips anchor tags from the message, keeping only the inner text as `<strong>`.
40
+ */
26
41
  export declare const notificationAdapter: (notification: NotificationModel) => WebNotification;
27
42
  export {};
@@ -1,30 +1,45 @@
1
- const resolveNotificationResourceUri = (params, type) => {
1
+ const resolveNotificationResourceUri = (params, type, appCode, eventType) => {
2
+ if (appCode === "timeline" && eventType === "NOTIFY-REPORT")
3
+ return "/admin";
2
4
  if (type === "user")
3
5
  return params.resourceUri || params.uri;
4
6
  for (const [key, value] of Object.entries(params))
5
7
  if (typeof value == "string" && value.length > 0 && key.endsWith("Uri") && key !== "profilUri" && key !== "resourceFolderUri")
6
8
  return value;
7
- }, parseNotificationMessage = (message) => message.replace(/<\/a>/gi, "</strong>").replace(/<a\b[^>]*>/gi, "<strong>"), getAppCode = (appCode) => {
8
- switch (appCode = appCode.toLowerCase(), appCode) {
9
+ }, parseNotificationMessage = (message) => message.replace(/<\/a>/gi, "</strong>").replace(/<a\b[^>]*>/gi, "<strong>"), getAppCodeAndI18nKey = (appCode) => {
10
+ appCode = appCode.toLowerCase();
11
+ let appI18nKey = appCode;
12
+ switch (appCode) {
9
13
  case "collaborativewall":
10
- return "collaborative-wall";
14
+ appCode = "collaborative-wall";
15
+ break;
11
16
  case "formulaire":
12
- return "forms";
17
+ appI18nKey = "formulaire", appCode = "forms";
18
+ break;
13
19
  case "messagerie":
14
- return "conversation";
20
+ appI18nKey = appCode = "conversation";
21
+ break;
15
22
  case "news":
16
- return "actualites";
23
+ appI18nKey = appCode = "actualites";
24
+ break;
17
25
  case "homeworks":
18
- return "cahier-de-texte";
26
+ appCode = "cahier-de-texte";
27
+ break;
19
28
  case "userbook_motto":
29
+ appI18nKey = "userbook_motto", appCode = "userbook";
30
+ break;
20
31
  case "userbook_mood":
32
+ appI18nKey = "userbook_mood", appCode = "userbook";
33
+ break;
21
34
  case "userbook_discovervisiblegroups":
22
- return "userbook";
35
+ appI18nKey = "userbook_discovervisiblegroups", appCode = "userbook";
36
+ break;
23
37
  default:
24
- return appCode.replace(/_/g, "-");
38
+ appI18nKey = appCode = appCode.replace(/_/g, "-");
25
39
  }
40
+ return [appCode, appI18nKey];
26
41
  }, notificationAdapter = (notification) => {
27
- const type = notification.sender ? "user" : "system", uri = resolveNotificationResourceUri(notification.params, type), date = new Date(notification.date.$date), appCode = getAppCode(notification.type), message = parseNotificationMessage(notification.message), base = {
42
+ const type = notification.sender ? "user" : "system", [appCode, appI18nKey] = getAppCodeAndI18nKey(notification.type), uri = resolveNotificationResourceUri(notification.params, type, appCode, notification["event-type"] || notification.eventType), date = new Date(notification.date.$date), message = parseNotificationMessage(notification.message), base = {
28
43
  id: notification._id,
29
44
  uri,
30
45
  message,
@@ -38,16 +53,19 @@ const resolveNotificationResourceUri = (params, type) => {
38
53
  // cannot be undefined if sender is present (user type)
39
54
  userId: notification.sender,
40
55
  // cannot be undefined if sender is present (user type)
41
- appCode
56
+ appCode,
57
+ appI18nKey
42
58
  }
43
59
  } : {
44
60
  ...base,
45
61
  type: "system",
46
62
  params: {
47
- appCode
63
+ appCode,
64
+ appI18nKey
48
65
  }
49
66
  };
50
67
  };
51
68
  export {
69
+ getAppCodeAndI18nKey,
52
70
  notificationAdapter
53
71
  };
@@ -2,8 +2,12 @@ import { NotificationModel } from '@edifice.io/client';
2
2
  export interface UseNotificationListContainerReturn {
3
3
  /** Array of notifications */
4
4
  notifications: NotificationModel[] | undefined;
5
- /** Array of notification types */
5
+ /** Array of all notification types available */
6
6
  notificationTypes: string[] | undefined;
7
+ /** Array of notification types currently selected as filter */
8
+ selectedTypes: string[] | undefined;
9
+ /** Callback to change the selected notification types filter */
10
+ setSelectedTypes: (types: string[]) => void;
7
11
  /** Indicates if there are more notifications to load */
8
12
  hasNextPage: boolean | undefined;
9
13
  /** Callback to load the next page of notifications */
@@ -18,3 +22,9 @@ export interface UseNotificationListContainerReturn {
18
22
  * @returns Object containing notifications, loading state, and error state
19
23
  */
20
24
  export declare const useNotificationListContainer: () => UseNotificationListContainerReturn;
25
+ /**
26
+ * Indicates whether the user has at least one notification dated today,
27
+ * across all notification types. Used to display a "new notification"
28
+ * badge on the notification bell icon, independently of the list overlay.
29
+ */
30
+ export declare const useHasNotificationToday: () => boolean;