@edifice.io/react 2.6.9-develop-orga.20261013105203 → 2.6.9-develop-b2school.20261013113840

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 (59) hide show
  1. package/dist/components/Layout/Layout.js +12 -4
  2. package/dist/components/Layout/components/Header.js +4 -4
  3. package/dist/components/Layout/components/NavLink.d.ts +3 -3
  4. package/dist/components/Layout/components/NavLink.js +5 -3
  5. package/dist/components/RadioTile/RadioTile.d.ts +35 -0
  6. package/dist/components/RadioTile/RadioTile.js +22 -0
  7. package/dist/components/RadioTile/index.d.ts +2 -0
  8. package/dist/components/index.d.ts +1 -0
  9. package/dist/homepage.js +10 -0
  10. package/dist/hooks/useZendeskGuide/useZendeskGuide.d.ts +2 -6
  11. package/dist/hooks/useZendeskGuide/useZendeskGuide.js +11 -8
  12. package/dist/icons.js +404 -402
  13. package/dist/index.js +138 -136
  14. package/dist/modules/HelpZone/HelpZone.js +6 -8
  15. package/dist/modules/HelpZone/HelpZoneContainer.js +2 -5
  16. package/dist/modules/homepage/components/Agenda/Agenda.d.ts +10 -0
  17. package/dist/modules/homepage/components/Agenda/Agenda.js +54 -0
  18. package/dist/modules/homepage/components/Agenda/AgendaContainer.d.ts +1 -0
  19. package/dist/modules/homepage/components/Agenda/AgendaContainer.js +14 -0
  20. package/dist/modules/homepage/components/Agenda/AgendaEventCard.d.ts +10 -0
  21. package/dist/modules/homepage/components/Agenda/AgendaEventCard.js +30 -0
  22. package/dist/modules/homepage/components/Agenda/hooks/useAgendaContainer.d.ts +14 -0
  23. package/dist/modules/homepage/components/Agenda/hooks/useAgendaContainer.js +27 -0
  24. package/dist/modules/homepage/components/Agenda/index.d.ts +3 -0
  25. package/dist/modules/homepage/components/Agenda/services/api/agendaService.d.ts +24 -0
  26. package/dist/modules/homepage/components/Agenda/services/api/agendaService.js +45 -0
  27. package/dist/modules/homepage/components/Agenda/services/api/index.d.ts +4 -0
  28. package/dist/modules/homepage/components/Agenda/services/api/index.js +5 -0
  29. package/dist/modules/homepage/components/Agenda/services/queries/agenda.d.ts +19 -0
  30. package/dist/modules/homepage/components/Agenda/services/queries/agenda.js +26 -0
  31. package/dist/modules/homepage/components/Header/Header.js +27 -11
  32. package/dist/modules/homepage/components/Header/MyAppsPopover.d.ts +5 -0
  33. package/dist/modules/homepage/components/Header/MyAppsPopover.js +28 -0
  34. package/dist/modules/homepage/components/Header/useSafeHoverPopover.js +32 -0
  35. package/dist/modules/homepage/components/HomeCard/HomeCardHeader.d.ts +5 -1
  36. package/dist/modules/homepage/components/HomeCard/HomeCardHeader.js +1 -1
  37. package/dist/modules/homepage/components/MessageFlashList/MessageFlash.js +1 -1
  38. package/dist/modules/homepage/components/Notifications/NotificationList.js +5 -5
  39. package/dist/modules/homepage/components/Notifications/NotificationSkeleton.js +2 -2
  40. package/dist/modules/homepage/components/Notifications/components/NotificationFilterMenu.js +1 -5
  41. package/dist/modules/homepage/components/Notifications/components/NotificationFilterModal.js +10 -12
  42. package/dist/modules/homepage/components/Notifications/components/NotificationOptionsMenu.js +3 -9
  43. package/dist/modules/homepage/components/Notifications/components/ReportNotificationModal.js +6 -12
  44. package/dist/modules/homepage/components/Notifications/components/notificationAdapter.js +12 -10
  45. package/dist/modules/homepage/components/Notifications/hooks/useNotificationList.js +9 -10
  46. package/dist/modules/homepage/components/UserSpace/UserSpace.d.ts +3 -1
  47. package/dist/modules/homepage/components/UserSpace/UserSpace.js +13 -7
  48. package/dist/modules/homepage/components/UserSpace/UserSpaceContainer.d.ts +4 -2
  49. package/dist/modules/homepage/components/UserSpace/UserSpaceContainer.js +3 -2
  50. package/dist/modules/homepage/components/WidgetsPersonalizationPanel/WidgetToggle.d.ts +16 -0
  51. package/dist/modules/homepage/components/WidgetsPersonalizationPanel/WidgetToggle.js +15 -0
  52. package/dist/modules/homepage/components/WidgetsPersonalizationPanel/WidgetsPersonalizationPanel.d.ts +21 -0
  53. package/dist/modules/homepage/components/WidgetsPersonalizationPanel/WidgetsPersonalizationPanel.js +47 -0
  54. package/dist/modules/homepage/components/WidgetsPersonalizationPanel/index.d.ts +2 -0
  55. package/dist/modules/homepage/components/index.d.ts +3 -1
  56. package/dist/modules/icons/components/IconAddWidget.d.ts +7 -0
  57. package/dist/modules/icons/components/IconAddWidget.js +12 -0
  58. package/dist/modules/icons/components/index.d.ts +1 -0
  59. package/package.json +6 -6
@@ -0,0 +1,45 @@
1
+ import { odeServices } from "@edifice.io/client";
2
+ const toCalendar = ({
3
+ _id,
4
+ title,
5
+ color,
6
+ isExternal
7
+ }) => ({
8
+ id: _id,
9
+ title,
10
+ color,
11
+ isExternal
12
+ }), toCalendarEvent = ({
13
+ _id,
14
+ title,
15
+ allday,
16
+ startMoment,
17
+ endMoment,
18
+ calendar
19
+ }) => ({
20
+ id: _id,
21
+ calendarId: calendar[0],
22
+ title,
23
+ startMoment,
24
+ endMoment,
25
+ allDay: allday
26
+ }), createAgendaService = (baseURL) => ({
27
+ /**
28
+ * Get the calendars the current user can see events from.
29
+ */
30
+ async getCalendars() {
31
+ return (await odeServices.http().get(`${baseURL}/calendar/calendars`)).map(toCalendar);
32
+ },
33
+ /**
34
+ * Get up to `nb` events for the given calendars, in no particular order.
35
+ */
36
+ async getEvents(calendarIds, nb) {
37
+ if (calendarIds.length === 0)
38
+ return [];
39
+ const query = new URLSearchParams();
40
+ return calendarIds.forEach((id) => query.append("calendarId", id)), query.set("nb", String(nb)), (await odeServices.http().get(`${baseURL}/calendar/events/widget?${query.toString()}`)).map(toCalendarEvent);
41
+ }
42
+ });
43
+ export {
44
+ createAgendaService
45
+ };
@@ -0,0 +1,4 @@
1
+ export declare const agendaService: {
2
+ getCalendars(): Promise<import('@edifice.io/client').Calendar[]>;
3
+ getEvents(calendarIds: string[], nb: number): Promise<import('@edifice.io/client').CalendarEvent[]>;
4
+ };
@@ -0,0 +1,5 @@
1
+ import { createAgendaService } from "./agendaService.js";
2
+ const agendaService = createAgendaService("");
3
+ export {
4
+ agendaService
5
+ };
@@ -0,0 +1,19 @@
1
+ import { Calendar, CalendarEvent } from '@edifice.io/client';
2
+ export declare const agendaQueryKeys: {
3
+ calendars: () => readonly ["agenda", "calendars"];
4
+ events: (calendarIds: string[], nb: number) => readonly ["agenda", "events", string[], number];
5
+ };
6
+ export declare const agendaQueryOptions: {
7
+ getCalendars(): import('@tanstack/react-query').OmitKeyof<import('@tanstack/react-query').UseQueryOptions<Calendar[], Error, Calendar[], import('@tanstack/react-query').QueryKey>, "queryFn"> & {
8
+ queryFn?: import('@tanstack/react-query').QueryFunction<Calendar[], import('@tanstack/react-query').QueryKey, never> | undefined;
9
+ } & {
10
+ queryKey: import('@tanstack/react-query').DataTag<import('@tanstack/react-query').QueryKey, Calendar[]>;
11
+ };
12
+ getEvents(calendarIds: string[], nb: number): import('@tanstack/react-query').OmitKeyof<import('@tanstack/react-query').UseQueryOptions<CalendarEvent[], Error, CalendarEvent[], import('@tanstack/react-query').QueryKey>, "queryFn"> & {
13
+ queryFn?: import('@tanstack/react-query').QueryFunction<CalendarEvent[], import('@tanstack/react-query').QueryKey, never> | undefined;
14
+ } & {
15
+ queryKey: import('@tanstack/react-query').DataTag<import('@tanstack/react-query').QueryKey, CalendarEvent[]>;
16
+ };
17
+ };
18
+ export declare const useCalendars: () => import('@tanstack/react-query').UseQueryResult<Calendar[], Error>;
19
+ export declare const useEvents: (calendarIds: string[], nb: number) => import('@tanstack/react-query').UseQueryResult<CalendarEvent[], Error>;
@@ -0,0 +1,26 @@
1
+ import { useQuery, queryOptions } from "@tanstack/react-query";
2
+ import { agendaService } from "../api/index.js";
3
+ const agendaQueryKeys = {
4
+ calendars: () => ["agenda", "calendars"],
5
+ events: (calendarIds, nb) => ["agenda", "events", calendarIds, nb]
6
+ }, agendaQueryOptions = {
7
+ getCalendars() {
8
+ return queryOptions({
9
+ queryKey: agendaQueryKeys.calendars(),
10
+ queryFn: async () => agendaService.getCalendars()
11
+ });
12
+ },
13
+ getEvents(calendarIds, nb) {
14
+ return queryOptions({
15
+ queryKey: agendaQueryKeys.events(calendarIds, nb),
16
+ queryFn: async () => agendaService.getEvents(calendarIds, nb),
17
+ enabled: calendarIds.length > 0
18
+ });
19
+ }
20
+ }, useCalendars = () => useQuery(agendaQueryOptions.getCalendars()), useEvents = (calendarIds, nb) => useQuery(agendaQueryOptions.getEvents(calendarIds, nb));
21
+ export {
22
+ agendaQueryKeys,
23
+ agendaQueryOptions,
24
+ useCalendars,
25
+ useEvents
26
+ };
@@ -2,6 +2,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import clsx from "clsx";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { useId } from "react";
5
+ import { MyAppsPopoverBody, MyAppsPopoverFooter } from "./MyAppsPopover.js";
6
+ import { useSafeHoverPopover } from "./useSafeHoverPopover.js";
5
7
  import { Navbar } from "../../../../components/Layout/components/Navbar.js";
6
8
  import { NavItem } from "../../../../components/Layout/components/NavItem.js";
7
9
  import { NavLink } from "../../../../components/Layout/components/NavLink.js";
@@ -18,13 +20,13 @@ import useConversation from "../../../../hooks/useConversation/useConversation.j
18
20
  import useUser from "../../../../hooks/useUser/useUser.js";
19
21
  import useHasWorkflow from "../../../../hooks/useHasWorkflow/useHasWorkflow.js";
20
22
  import { useEdificeTheme } from "../../../../providers/EdificeThemeProvider/EdificeThemeProvider.hook.js";
21
- import useHover from "../../../../hooks/useHover/useHover.js";
23
+ import useBreakpoint from "../../../../hooks/useBreakpoint/useBreakpoint.js";
22
24
  import LogoBeta from "../../../../components/LogoBeta/LogoBeta.js";
23
25
  import Badge from "../../../../components/Badge/Badge.js";
24
26
  import VisuallyHidden from "../../../../components/VisuallyHidden/VisuallyHidden.js";
25
27
  import ButtonBeta from "../../../../components/ButtonBeta/ButtonBeta.js";
26
28
  import Avatar from "../../../../components/Avatar/Avatar.js";
27
- import { Popover, PopoverBody } from "../../../../components/Popover/Popover.js";
29
+ import { Popover, PopoverBody, PopoverFooter } from "../../../../components/Popover/Popover.js";
28
30
  const Header = ({
29
31
  src = "",
30
32
  onNotificationsClick,
@@ -41,13 +43,21 @@ const Header = ({
41
43
  userAvatar,
42
44
  userName,
43
45
  communitiesWorkflow,
44
- conversationWorflow
46
+ conversationWorflow,
47
+ bookmarkedApps
45
48
  } = useHeader({
46
49
  user,
47
50
  avatar
48
51
  }), {
49
52
  theme
50
- } = useEdificeTheme(), hasMessages = messages > 0, hasNotificationToday = useHasNotificationToday(), [userRef, isUserHovered] = useHover(), popoverUserId = useId(), handleNotificationsClick = () => {
53
+ } = useEdificeTheme(), hasMessages = messages > 0, hasNotificationToday = useHasNotificationToday(), popoverUserId = useId(), popoverAppsId = useId(), {
54
+ lg: isDesktop
55
+ } = useBreakpoint(), myApps = useSafeHoverPopover({
56
+ hoverEnabled: isDesktop,
57
+ clickEnabled: !isDesktop
58
+ }), userMenu = useSafeHoverPopover(), handleMyAppsClick = (event) => {
59
+ isDesktop || event.preventDefault();
60
+ }, handleNotificationsClick = () => {
51
61
  onNotificationsClick == null || onNotificationsClick();
52
62
  };
53
63
  return /* @__PURE__ */ jsx("header", { className: classes, "data-product": dataProduct, children: /* @__PURE__ */ jsxs(Navbar, { className: "px-24", children: [
@@ -66,7 +76,15 @@ const Header = ({
66
76
  /* @__PURE__ */ jsx(SvgIconMessagesBeta, {}),
67
77
  /* @__PURE__ */ jsx(VisuallyHidden, { children: t("conversation") })
68
78
  ] }) }),
69
- /* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsx(NavLink, { link: "/welcome", translate: t("navbar.applications"), "data-testid": "header-my-apps-button", children: /* @__PURE__ */ jsx(SvgIconMyAppsBeta, {}) }) }),
79
+ /* @__PURE__ */ jsxs(NavItem, { className: "position-relative", children: [
80
+ /* @__PURE__ */ jsx(NavLink, { ref: myApps.setReference, link: "/welcome", translate: t("navbar.applications"), id: popoverAppsId, "aria-haspopup": "true", "aria-expanded": myApps.open, "data-testid": "header-my-apps-button", ...myApps.getReferenceProps({
81
+ onClick: handleMyAppsClick
82
+ }), children: /* @__PURE__ */ jsx(SvgIconMyAppsBeta, {}) }),
83
+ /* @__PURE__ */ jsxs(Popover, { ref: myApps.setFloating, className: "my-apps-popover", id: popoverAppsId, isVisible: myApps.open, ...myApps.getFloatingProps(), children: [
84
+ /* @__PURE__ */ jsx(PopoverBody, { children: /* @__PURE__ */ jsx(MyAppsPopoverBody, { bookmarkedApps }) }),
85
+ /* @__PURE__ */ jsx(PopoverFooter, { className: "d-flex justify-content-center", children: /* @__PURE__ */ jsx(MyAppsPopoverFooter, {}) })
86
+ ] })
87
+ ] }),
70
88
  /* @__PURE__ */ jsxs(NavItem, { className: "position-relative", children: [
71
89
  /* @__PURE__ */ jsx(ButtonBeta, { leftIcon: /* @__PURE__ */ jsx(SvgIconNotificationBeta, {}), variant: "ghost", onClick: handleNotificationsClick }),
72
90
  hasNotificationToday && /* @__PURE__ */ jsx("span", { style: {
@@ -81,13 +99,11 @@ const Header = ({
81
99
  pointerEvents: "none",
82
100
  zIndex: 1,
83
101
  display: "block"
84
- }, "aria-label": t("homepage.notifications.new-badge", {
85
- defaultValue: "Nouvelle notification"
86
- }) })
102
+ }, "aria-label": t("homepage.notifications.new-badge") })
87
103
  ] }),
88
- /* @__PURE__ */ jsxs(NavItem, { className: "position-relative", ref: userRef, id: popoverUserId, "aria-haspopup": "true", "aria-expanded": isUserHovered, "data-testid": "header-user-menu-button", children: [
89
- /* @__PURE__ */ jsx(NavLink, { link: "/userbook/mon-compte", translate: t("navbar.myaccount"), "data-testid": "header-user-profile-button", children: /* @__PURE__ */ jsx(Avatar, { alt: userName, size: "sm", src: userAvatar, variant: "circle", className: "bg-white", width: "32", height: "32" }) }),
90
- /* @__PURE__ */ jsx(Popover, { align: "end", className: "widget", id: popoverUserId, isVisible: isUserHovered, children: /* @__PURE__ */ jsxs(PopoverBody, { children: [
104
+ /* @__PURE__ */ jsxs(NavItem, { className: "position-relative", children: [
105
+ /* @__PURE__ */ jsx(NavLink, { ref: userMenu.setReference, link: "/userbook/mon-compte", translate: t("navbar.myaccount"), id: popoverUserId, "aria-haspopup": "true", "aria-expanded": userMenu.open, "data-testid": "header-user-profile-button", ...userMenu.getReferenceProps(), children: /* @__PURE__ */ jsx(Avatar, { alt: userName, size: "sm", src: userAvatar, variant: "circle", className: "bg-white", width: "32", height: "32" }) }),
106
+ /* @__PURE__ */ jsx(Popover, { ref: userMenu.setFloating, align: "end", className: "widget", id: popoverUserId, isVisible: userMenu.open, ...userMenu.getFloatingProps(), children: /* @__PURE__ */ jsxs(PopoverBody, { children: [
91
107
  /* @__PURE__ */ jsxs("a", { href: "/timeline/customize?callback=" + encodeURIComponent(`${window.location.pathname}${window.location.search}`), className: "nav-link customize d-flex align-items-center gap-8", "data-testid": "header-customize-button", children: [
92
108
  /* @__PURE__ */ jsx(SvgIconSetBackground, { className: "icon" }),
93
109
  /* @__PURE__ */ jsx("span", { id: "cutomize-label", className: "nav-text", children: t("navbar.customize") })
@@ -0,0 +1,5 @@
1
+ import { IWebApp } from '@edifice.io/client';
2
+ export declare const MyAppsPopoverFooter: () => import("react/jsx-runtime").JSX.Element;
3
+ export declare const MyAppsPopoverBody: ({ bookmarkedApps, }: {
4
+ bookmarkedApps: IWebApp[] | undefined;
5
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import illuEmptyFavorite from "@edifice.io/bootstrap/dist/images/homepage/illu-empty-favorite.svg";
4
+ import Flex from "../../../../components/Flex/Flex.js";
5
+ import AppIcon from "../../../../components/AppIcon/AppIcon.js";
6
+ const MAX_VISIBLE_APPS = 10, appToOpenOnBlank = ["Administration"], MyAppsPopoverFooter = () => {
7
+ const {
8
+ t
9
+ } = useTranslation();
10
+ return /* @__PURE__ */ jsx("a", { href: "/welcome", className: "btn-beta btn-beta-tertiary btn-beta--ghost btn-beta--small", "data-testid": "header-my-apps-popover-more", children: /* @__PURE__ */ jsx("span", { children: t("homepage.header.myapps.seeMore") }) });
11
+ }, MyAppsPopoverBody = ({
12
+ bookmarkedApps
13
+ }) => {
14
+ const {
15
+ t
16
+ } = useTranslation(), getAppName = (app) => app.prefix && app.prefix.length > 1 ? t(app.prefix.substring(1)) : t(app.displayName) || "";
17
+ return bookmarkedApps != null && bookmarkedApps.length ? /* @__PURE__ */ jsx("div", { className: "my-apps-popover-grid", "data-testid": "header-my-apps-popover", children: bookmarkedApps.slice(0, MAX_VISIBLE_APPS).map((app, index) => {
18
+ const opensInNewTab = appToOpenOnBlank.includes(app.name) || app.isExternal || app.target === "_blank", appName = getAppName(app);
19
+ return /* @__PURE__ */ jsx("a", { href: app.address, title: appName, "aria-label": appName, className: "my-apps-popover-item", "data-testid": "header-my-apps-popover-item", target: opensInNewTab ? "_blank" : void 0, rel: opensInNewTab ? "noopener noreferrer" : void 0, children: /* @__PURE__ */ jsx(AppIcon, { app, size: "32" }) }, index);
20
+ }) }) : /* @__PURE__ */ jsxs(Flex, { direction: "column", align: "center", gap: "8", children: [
21
+ /* @__PURE__ */ jsx("img", { src: illuEmptyFavorite, alt: "", width: 50, height: 50 }),
22
+ /* @__PURE__ */ jsx("span", { className: "my-apps-popover-empty-text text-center", children: t("homepage.header.myapps.empty") })
23
+ ] });
24
+ };
25
+ export {
26
+ MyAppsPopoverBody,
27
+ MyAppsPopoverFooter
28
+ };
@@ -0,0 +1,32 @@
1
+ import { useState } from "react";
2
+ import { useFloating, useHover, safePolygon, useClick, useDismiss, useFocus, useInteractions } from "@floating-ui/react";
3
+ function useSafeHoverPopover({
4
+ hoverEnabled = !0,
5
+ clickEnabled = !1
6
+ } = {}) {
7
+ const [open, setOpen] = useState(!1), {
8
+ refs,
9
+ context
10
+ } = useFloating({
11
+ open,
12
+ onOpenChange: setOpen
13
+ }), hover = useHover(context, {
14
+ enabled: hoverEnabled,
15
+ handleClose: safePolygon()
16
+ }), click = useClick(context, {
17
+ enabled: clickEnabled
18
+ }), dismiss = useDismiss(context), focus = useFocus(context), {
19
+ getReferenceProps,
20
+ getFloatingProps
21
+ } = useInteractions([hover, click, dismiss, focus]);
22
+ return {
23
+ open,
24
+ setReference: refs.setReference,
25
+ setFloating: refs.setFloating,
26
+ getReferenceProps,
27
+ getFloatingProps
28
+ };
29
+ }
30
+ export {
31
+ useSafeHoverPopover
32
+ };
@@ -3,7 +3,11 @@ import { ButtonBeta } from '../../../../components';
3
3
  export interface HomeCardHeaderProps extends Omit<ComponentPropsWithoutRef<'div'>, 'title'> {
4
4
  /** Title displayed on the left side of the header. */
5
5
  title: ReactNode;
6
- /** Label of the action button displayed on the right side of the header. */
6
+ /**
7
+ * Label of the action button displayed on the right side of the header.
8
+ * Can be omitted for an icon-only button — pass `aria-label` via
9
+ * `actionProps` in that case.
10
+ */
7
11
  actionLabel?: ReactNode;
8
12
  /** Callback invoked when the action button is clicked. */
9
13
  onActionClick?: MouseEventHandler<HTMLButtonElement>;
@@ -12,7 +12,7 @@ const HomeCardHeader = ({
12
12
  className,
13
13
  ...rest
14
14
  }) => {
15
- const hasAction = !!actionLabel && !!onActionClick;
15
+ const hasAction = !!onActionClick && (!!actionLabel || !!actionLeftIcon || !!actionRightIcon);
16
16
  return /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "between", gap: "8", wrap: "wrap", className: clsx("home-card-header", className), ...rest, children: [
17
17
  /* @__PURE__ */ jsx("h3", { className: "home-card-header-title", children: title }),
18
18
  hasAction && /* @__PURE__ */ jsx(ButtonBeta, { color: "tertiary", size: "sm", variant: "ghost", onClick: onActionClick, className: "home-card-header-action", leftIcon: actionLeftIcon, rightIcon: actionRightIcon, "data-testid": "home-card-header-action", ...actionProps, children: actionLabel })
@@ -47,7 +47,7 @@ const MessageFlash = ({
47
47
  }), title: t("close.message", {
48
48
  title: message.title
49
49
  }) }),
50
- /* @__PURE__ */ jsx("div", { className: "message-flash-icon", role: "img", "aria-label": message.color === "red" ? t("warning") : t("information"), children: message.color && message.color === "red" ? /* @__PURE__ */ jsx(SvgIconAlertTriangle, {}) : /* @__PURE__ */ jsx(SvgIconInfoCircle, {}) }),
50
+ /* @__PURE__ */ jsx("div", { className: "message-flash-icon", role: "img", "aria-label": message.color === "red" ? t("homepage.messageFlash.warning") : t("homepage.messageFlash.information"), children: message.color && message.color === "red" ? /* @__PURE__ */ jsx(SvgIconAlertTriangle, {}) : /* @__PURE__ */ jsx(SvgIconInfoCircle, {}) }),
51
51
  /* @__PURE__ */ jsxs(Flex, { direction: "column", className: "w-100 message-flash-body", children: [
52
52
  /* @__PURE__ */ jsx(Card.Title, { children: message.title }),
53
53
  /* @__PURE__ */ jsx("div", { ref: contentRef, className: classesContent, dangerouslySetInnerHTML: {
@@ -14,7 +14,7 @@ import Divider from "../../../../components/Divider/Divider.js";
14
14
  const NotificationList = ({
15
15
  notifications,
16
16
  notificationTypes,
17
- selectedTypes,
17
+ selectedTypes = [],
18
18
  onFilterChange,
19
19
  onCloseNotifications,
20
20
  onLoadNextPage,
@@ -25,19 +25,19 @@ const NotificationList = ({
25
25
  t
26
26
  } = useTranslation(), loadNextRef = useInfiniteScroll({
27
27
  callback: () => onLoadNextPage == null ? void 0 : onLoadNextPage()
28
- }), handleCloseClick = () => {
28
+ }), handleFilterChange = (types) => onFilterChange == null ? void 0 : onFilterChange(types), handleCloseClick = () => {
29
29
  onCloseNotifications && onCloseNotifications();
30
30
  };
31
31
  return /* @__PURE__ */ jsx("section", { role: "region", className: "notification-list", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
32
32
  /* @__PURE__ */ jsxs(Flex, { justify: "between", align: "center", wrap: "nowrap", className: "py-16 ps-24 pe-8", children: [
33
33
  /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "8", className: "notification-list-title", children: [
34
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) })
35
+ notificationTypes && notificationTypes.length > 0 && /* @__PURE__ */ jsx(NotificationFilterMenu, { notificationTypes, selectedTypes, onFilterChange: handleFilterChange })
36
36
  ] }),
37
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" })
38
38
  ] }),
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: [
40
- /* @__PURE__ */ jsx("ul", { children: notifications == null ? void 0 : notifications.map((notification, index) => /* @__PURE__ */ jsxs("li", { children: [
39
+ isLoading && !(notifications != null && notifications.length) ? /* @__PURE__ */ jsx(NotificationListSkeleton, {}) : !notifications || 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: [
40
+ /* @__PURE__ */ jsx("ul", { children: notifications.map((notification, index) => /* @__PURE__ */ jsxs("li", { children: [
41
41
  /* @__PURE__ */ jsx(NotificationItem, { notification }),
42
42
  /* @__PURE__ */ jsx(Divider, { className: "border-grey-300 my-0" })
43
43
  ] }, "notification-list-" + notification._id + "-" + index)) }),
@@ -3,9 +3,9 @@ import React from "react";
3
3
  import Flex from "../../../../components/Flex/Flex.js";
4
4
  import Avatar from "../../../../components/Avatar/Avatar.js";
5
5
  import TextSkeleton from "../../../../components/Skeleton/TextSkeleton.js";
6
- const NotificationSkeleton = /* @__PURE__ */ React.forwardRef((_props, ref) => /* @__PURE__ */ jsx(Flex, { direction: "column", className: "notification", gap: "8", ref, children: /* @__PURE__ */ jsxs(Flex, { direction: "row", gap: "8", children: [
6
+ const NotificationSkeleton = /* @__PURE__ */ React.forwardRef((_props, ref) => /* @__PURE__ */ jsx(Flex, { direction: "column", className: "notification-item", gap: "8", ref, children: /* @__PURE__ */ jsxs(Flex, { direction: "row", gap: "8", children: [
7
7
  /* @__PURE__ */ jsx("div", { className: "notification-item-picture", children: /* @__PURE__ */ jsx(Avatar, { alt: "Notification Picture", variant: "circle", className: "notification-item-avatar" }) }),
8
- /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "8", className: "notification-message", children: [
8
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "8", className: "notification-item-message", children: [
9
9
  /* @__PURE__ */ jsx(TextSkeleton, {}),
10
10
  /* @__PURE__ */ jsx(TextSkeleton, {}),
11
11
  /* @__PURE__ */ jsx("div", { className: "placeholder col-4", style: {
@@ -16,11 +16,7 @@ const NotificationFilterMenu = ({
16
16
  onFilterChange(types), setIsOpen(!1);
17
17
  };
18
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" }),
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"), title: t("homepage.notifications.filter-modal.trigger"), onClick: () => setIsOpen(!0), "data-testid": "notification-filter-button" }),
24
20
  isOpen && /* @__PURE__ */ jsx(NotificationFilterModal, { isOpen, allTypes: notificationTypes, appliedTypes: selectedTypes, onCancel: () => setIsOpen(!1), onApply: handleApply })
25
21
  ] });
26
22
  };
@@ -1,11 +1,12 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import clsx from "clsx";
3
- import { useState, useRef, useMemo, useEffect } from "react";
3
+ import { useState, useMemo } from "react";
4
4
  import { useTranslation } from "react-i18next";
5
5
  import SvgIconCheck from "../../../../icons/components/IconCheck.js";
6
6
  import { getAppCodeAndI18nKey } from "./notificationAdapter.js";
7
- import Modal from "../../../../../components/Modal/Modal.js";
7
+ import ModalBeta from "../../../../../components/ModalBeta/ModalBeta.js";
8
8
  import Flex from "../../../../../components/Flex/Flex.js";
9
+ import Checkbox from "../../../../../components/Checkbox/Checkbox.js";
9
10
  import AppIcon from "../../../../../components/AppIcon/AppIcon.js";
10
11
  import ButtonBeta from "../../../../../components/ButtonBeta/ButtonBeta.js";
11
12
  const NotificationFilterModal = ({
@@ -18,7 +19,7 @@ const NotificationFilterModal = ({
18
19
  const {
19
20
  t,
20
21
  i18n
21
- } = useTranslation(), [selected, setSelected] = useState(appliedTypes), refSelectAllCheckbox = useRef(null), allTypesSorted = useMemo(() => allTypes.map((type) => {
22
+ } = useTranslation(), [selected, setSelected] = useState(appliedTypes), allTypesSorted = useMemo(() => allTypes.map((type) => {
22
23
  const [appCode, appI18nKey] = getAppCodeAndI18nKey(type), label = t(appI18nKey, {
23
24
  defaultValue: appCode
24
25
  });
@@ -35,16 +36,13 @@ const NotificationFilterModal = ({
35
36
  }, toggleAll = () => {
36
37
  setSelected(selected.length === allTypes.length ? [] : allTypes);
37
38
  };
38
- return useEffect(() => {
39
- refSelectAllCheckbox.current && (refSelectAllCheckbox.current.indeterminate = selected.length > 0 && selected.length < allTypes.length);
40
- }, [selected.length, allTypes.length]), /* @__PURE__ */ jsxs(Modal, { id: "notification-filter-modal", size: "lg", scrollable: !0, isOpen, onModalClose: onCancel, children: [
41
- /* @__PURE__ */ jsx(Modal.Header, { onModalClose: onCancel, children: t("homepage.notifications.filter-modal.title") }),
42
- /* @__PURE__ */ jsx(Modal.Subtitle, { children: t("homepage.notifications.filter-modal.subtitle") }),
43
- /* @__PURE__ */ jsxs(Modal.Body, { children: [
39
+ return /* @__PURE__ */ jsxs(ModalBeta, { id: "notification-filter-modal", size: "l", isOpen, onModalClose: onCancel, children: [
40
+ /* @__PURE__ */ jsx(ModalBeta.Header, { subtitle: t("homepage.notifications.filter-modal.subtitle"), onModalClose: onCancel, children: t("homepage.notifications.filter-modal.title") }),
41
+ /* @__PURE__ */ jsxs(ModalBeta.Body, { children: [
44
42
  /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "8", className: "mb-24", children: [
45
43
  /* @__PURE__ */ jsxs("label", { className: "notification-filter-select-all notification-filter-chip", children: [
46
44
  /* @__PURE__ */ jsx("span", { children: t("homepage.notifications.filter-modal.select-all") }),
47
- /* @__PURE__ */ jsx("input", { type: "checkbox", ref: refSelectAllCheckbox, checked: selected.length === allTypes.length, className: "notification-filter-checkbox", onChange: toggleAll })
45
+ /* @__PURE__ */ jsx(Checkbox, { checked: selected.length === allTypes.length, indeterminate: selected.length > 0 && selected.length < allTypes.length, onChange: toggleAll })
48
46
  ] }),
49
47
  /* @__PURE__ */ jsx("span", { className: "notification-filter-count", children: t("homepage.notifications.filter-modal.count", {
50
48
  selected: selected.length,
@@ -62,11 +60,11 @@ const NotificationFilterModal = ({
62
60
  }), children: [
63
61
  /* @__PURE__ */ jsx(AppIcon, { app: appCode, size: "24", iconFit: "contain" }),
64
62
  /* @__PURE__ */ jsx("span", { children: label }),
65
- /* @__PURE__ */ jsx("input", { type: "checkbox", className: "notification-filter-checkbox", checked, onChange: () => toggleType(type) })
63
+ /* @__PURE__ */ jsx(Checkbox, { checked, onChange: () => toggleType(type) })
66
64
  ] }, type);
67
65
  }) })
68
66
  ] }),
69
- /* @__PURE__ */ jsxs(Modal.Footer, { children: [
67
+ /* @__PURE__ */ jsxs(ModalBeta.Footer, { children: [
70
68
  /* @__PURE__ */ jsx(ButtonBeta, { color: "tertiary", onClick: onCancel, type: "button", variant: "ghost", children: t("cancel") }),
71
69
  /* @__PURE__ */ jsx(ButtonBeta, { color: "default", onClick: () => onApply(selected), type: "button", variant: "filled", rightIcon: /* @__PURE__ */ jsx(SvgIconCheck, {}), children: t("homepage.notifications.filter-modal.confirm") })
72
70
  ] })
@@ -20,16 +20,10 @@ const NotificationOptionsMenu = ({
20
20
  } = useDeleteNotification(), [isReportModalOpen, setIsReportModalOpen] = useState(!1);
21
21
  return /* @__PURE__ */ jsxs(Fragment, { children: [
22
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" }),
23
+ /* @__PURE__ */ jsx(IconButton, { ...triggerProps, type: "button", "aria-label": t("homepage.notifications.options.label"), color: "tertiary", variant: "ghost", icon: /* @__PURE__ */ jsx(SvgIconOptions, {}), "data-testid": "notification-item-options-button" }),
26
24
  /* @__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
- }) })
25
+ /* @__PURE__ */ jsx(Dropdown.Item, { icon: /* @__PURE__ */ jsx(SvgIconAlertTriangle, {}), onClick: () => setIsReportModalOpen(!0), children: t("homepage.notifications.options.report") }),
26
+ /* @__PURE__ */ jsx(Dropdown.Item, { icon: /* @__PURE__ */ jsx(SvgIconDelete, {}), onClick: () => deleteNotification(notificationId), children: t("homepage.notifications.options.delete") })
33
27
  ] })
34
28
  ] }) }),
35
29
  isReportModalOpen && /* @__PURE__ */ jsx(ReportNotificationModal, { isOpen: isReportModalOpen, onCancel: () => setIsReportModalOpen(!1), onConfirm: () => {
@@ -1,7 +1,7 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useTranslation } from "react-i18next";
3
3
  import SvgIconAlertTriangle from "../../../../icons/components/IconAlertTriangle.js";
4
- import Modal from "../../../../../components/Modal/Modal.js";
4
+ import ModalBeta from "../../../../../components/ModalBeta/ModalBeta.js";
5
5
  import ButtonBeta from "../../../../../components/ButtonBeta/ButtonBeta.js";
6
6
  const ReportNotificationModal = ({
7
7
  isOpen,
@@ -11,18 +11,12 @@ const ReportNotificationModal = ({
11
11
  const {
12
12
  t
13
13
  } = useTranslation();
14
- return /* @__PURE__ */ jsxs(Modal, { id: "report-notification-modal", size: "sm", 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: [
14
+ return /* @__PURE__ */ jsxs(ModalBeta, { id: "report-notification-modal", size: "m", isOpen, onModalClose: onCancel, children: [
15
+ /* @__PURE__ */ jsx(ModalBeta.Header, { onModalClose: onCancel, children: t("homepage.notifications.report-modal.title") }),
16
+ /* @__PURE__ */ jsx(ModalBeta.Body, { children: /* @__PURE__ */ jsx("p", { children: t("homepage.notifications.report-modal.body") }) }),
17
+ /* @__PURE__ */ jsxs(ModalBeta.Footer, { children: [
22
18
  /* @__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
- }) })
19
+ /* @__PURE__ */ jsx(ButtonBeta, { color: "default", onClick: onConfirm, type: "button", variant: "filled", leftIcon: /* @__PURE__ */ jsx(SvgIconAlertTriangle, {}), children: t("homepage.notifications.report-modal.confirm") })
26
20
  ] })
27
21
  ] });
28
22
  };
@@ -8,34 +8,36 @@ const resolveNotificationResourceUri = (params, type, appCode, eventType) => {
8
8
  return value;
9
9
  }, parseNotificationMessage = (message) => message.replace(/<\/a>/gi, "</strong>").replace(/<a\b[^>]*>/gi, "<strong>"), getAppCodeAndI18nKey = (appCode) => {
10
10
  appCode = appCode.toLowerCase();
11
- let appI18nKey = appCode;
11
+ const appI18nKey = `homepage.notification.type.${appCode}`;
12
12
  switch (appCode) {
13
+ case "collaborativeeditor":
14
+ appCode = "pad";
15
+ break;
13
16
  case "collaborativewall":
14
17
  appCode = "collaborative-wall";
15
18
  break;
16
19
  case "formulaire":
17
- appI18nKey = "formulaire", appCode = "forms";
20
+ appCode = "forms";
18
21
  break;
19
22
  case "messagerie":
20
- appI18nKey = appCode = "conversation";
23
+ appCode = "conversation";
21
24
  break;
22
25
  case "news":
23
- appI18nKey = appCode = "actualites";
26
+ appCode = "actualites";
24
27
  break;
25
28
  case "homeworks":
26
29
  appCode = "cahier-de-texte";
27
30
  break;
28
31
  case "userbook_motto":
29
- appI18nKey = "userbook_motto", appCode = "userbook";
30
- break;
31
32
  case "userbook_mood":
32
- appI18nKey = "userbook_mood", appCode = "userbook";
33
- break;
34
33
  case "userbook_discovervisiblegroups":
35
- appI18nKey = "userbook_discovervisiblegroups", appCode = "userbook";
34
+ appCode = "userbook";
35
+ break;
36
+ case "viescolaire":
37
+ appCode = "vie-scolaire";
36
38
  break;
37
39
  default:
38
- appI18nKey = appCode = appCode.replace(/_/g, "-");
40
+ appCode = appCode.replace(/_/g, "-");
39
41
  }
40
42
  return [appCode, appI18nKey];
41
43
  }, notificationAdapter = (notification) => {
@@ -14,31 +14,30 @@ const useNotificationListContainer = () => {
14
14
  mutate: saveTimelinePreference
15
15
  } = useSaveTimelinePreference(), [selectedTypes, setSelectedTypesState] = useState();
16
16
  useEffect(() => {
17
- if (selectedTypes === void 0 && isFetchedTypes && isFetchedPreference) {
18
- const preferredTypes = preference == null ? void 0 : preference.type;
19
- setSelectedTypesState(preferredTypes && preferredTypes.length > 0 ? preferredTypes : notificationTypes ?? []);
20
- }
17
+ selectedTypes === void 0 && isFetchedTypes && isFetchedPreference && setSelectedTypesState((preference == null ? void 0 : preference.type) ?? notificationTypes ?? []);
21
18
  }, [selectedTypes, isFetchedTypes, isFetchedPreference, preference, notificationTypes]);
22
19
  const setSelectedTypes = (types) => {
23
20
  setSelectedTypesState(types), saveTimelinePreference({
24
21
  ...preference,
25
22
  type: types
26
23
  });
27
- }, {
28
- data: notifications,
24
+ }, hasNoTypeSelected = (selectedTypes == null ? void 0 : selectedTypes.length) === 0, {
25
+ data,
29
26
  hasNextPage,
30
27
  isLoading: isLoadingNotifications,
31
28
  error: errorNotifications,
32
29
  fetchNextPage
33
- } = useNotifications(selectedTypes ?? [], isFetchedTypes && !!selectedTypes);
30
+ } = useNotifications(selectedTypes ?? [], isFetchedTypes && !!selectedTypes && !hasNoTypeSelected);
34
31
  return {
35
- notifications,
32
+ notifications: hasNoTypeSelected ? [] : data,
36
33
  notificationTypes,
37
34
  selectedTypes,
38
35
  setSelectedTypes,
39
- hasNextPage,
36
+ hasNextPage: hasNoTypeSelected ? !1 : hasNextPage,
40
37
  loadNextPage: () => fetchNextPage(),
41
- isLoading: isLoadingTypes || isLoadingNotifications,
38
+ // The notification query stays disabled until the saved filter is
39
+ // read, so this wait must count as loading too.
40
+ isLoading: isLoadingTypes || selectedTypes === void 0 || isLoadingNotifications,
42
41
  error: errorTypes || errorNotifications
43
42
  };
44
43
  }, useHasNotificationToday = () => {
@@ -5,8 +5,10 @@ export type UserSpaceProps = {
5
5
  avatar: string;
6
6
  profile: UserProfile[number];
7
7
  children?: ReactNode;
8
+ /** Renders a trigger button in the top-right corner when provided. */
9
+ onCustomizeWidgetsClick?: () => void;
8
10
  };
9
- declare function UserSpace({ name, avatar, profile, children, }: UserSpaceProps): import("react/jsx-runtime").JSX.Element;
11
+ declare function UserSpace({ name, avatar, profile, children, onCustomizeWidgetsClick, }: UserSpaceProps): import("react/jsx-runtime").JSX.Element;
10
12
  declare namespace UserSpace {
11
13
  var displayName: string;
12
14
  }