diff --git a/i18n/en.pot b/i18n/en.pot
index 6e6cc3a..0b7c635 100644
--- a/i18n/en.pot
+++ b/i18n/en.pot
@@ -449,6 +449,9 @@ msgstr ""
msgid "New action"
msgstr ""
+msgid "You do not have access to this page."
+msgstr ""
+
msgid "First load can take a couple of minutes, please wait..."
msgstr ""
diff --git a/i18n/es.po b/i18n/es.po
index 65c75ff..e7ed136 100644
--- a/i18n/es.po
+++ b/i18n/es.po
@@ -448,6 +448,9 @@ msgstr ""
msgid "New action"
msgstr ""
+msgid "You do not have access to this page."
+msgstr ""
+
msgid "First load can take a couple of minutes, please wait..."
msgstr ""
diff --git a/package.json b/package.json
index 5ef80a7..f835d7a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "home-page",
"description": "Home Page App",
- "version": "1.7.0",
+ "version": "1.7.1",
"license": "GPL-3.0",
"author": "EyeSeeTea team",
"homepage": ".",
diff --git a/public/index.html b/public/index.html
index e87e0bb..7486350 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,15 +3,16 @@
-
diff --git a/src/domain/entities/LandingNode.ts b/src/domain/entities/LandingNode.ts
index ce53c3e..6515adc 100644
--- a/src/domain/entities/LandingNode.ts
+++ b/src/domain/entities/LandingNode.ts
@@ -132,4 +132,8 @@ export function getPrimaryRedirectUrl(
return redirectUrl;
}
+export function flattenLandingNodes(nodes: LandingNode[]): LandingNode[] {
+ return nodes.flatMap(node => [node, ...flattenLandingNodes(node.children)]);
+}
+
type Url = string;
diff --git a/src/webapp/components/additional-components/AdditionalComponents.tsx b/src/webapp/components/additional-components/AdditionalComponents.tsx
index 806fe1e..a390f50 100644
--- a/src/webapp/components/additional-components/AdditionalComponents.tsx
+++ b/src/webapp/components/additional-components/AdditionalComponents.tsx
@@ -1,7 +1,6 @@
-import { useConfig } from "../../pages/settings/useConfig";
-import i18n from "@eyeseetea/d2-ui-components/locales";
import React from "react";
-import { LandingNode, updateLandingNodes } from "../../../domain/entities/LandingNode";
+import { useConfig } from "../../pages/settings/useConfig";
+import { LandingNode } from "../../../domain/entities/LandingNode";
import { useAppContext } from "../../contexts/app-context";
import { BigCard } from "../card-board/BigCard";
import { Cardboard } from "../card-board/Cardboard";
@@ -9,6 +8,7 @@ import { LandingParagraph } from "../landing-layout";
import { useAnalytics } from "../../hooks/useAnalytics";
import { Action, getPageActions } from "../../../domain/entities/Action";
import { useSnackbar } from "@eyeseetea/d2-ui-components";
+import i18n from "@eyeseetea/d2-ui-components/locales";
export const AdditionalComponents: React.FC<{
isRoot: boolean;
@@ -16,22 +16,11 @@ export const AdditionalComponents: React.FC<{
openPage(page: LandingNode): void;
}> = React.memo(props => {
const { isRoot, currentPage, openPage } = props;
- const { actions, translate, launchAppBaseUrl, landings } = useAppContext();
- const { showAllActions, landingPagePermissions, user } = useConfig();
+ const { actions, translate, launchAppBaseUrl, getLandingNodeById } = useAppContext();
+ const { showAllActions, user } = useConfig();
const analytics = useAnalytics();
const snackbar = useSnackbar();
- const userLandings = React.useMemo(() => {
- return landings && landingPagePermissions && user
- ? updateLandingNodes(landings, landingPagePermissions, user)
- : undefined;
- }, [landingPagePermissions, landings, user]);
-
- const getLandingNodeById = React.useCallback(
- (id: string) => userLandings?.find(landing => landing.id === id),
- [userLandings]
- );
-
const actionHandleClick = React.useCallback(
(action: Action) => {
switch (action.type) {
diff --git a/src/webapp/components/item-category/ItemCategory.tsx b/src/webapp/components/item-category/ItemCategory.tsx
index db670a2..ead7462 100644
--- a/src/webapp/components/item-category/ItemCategory.tsx
+++ b/src/webapp/components/item-category/ItemCategory.tsx
@@ -46,7 +46,7 @@ export const ItemCategory: React.FC<{
{showAdditionalComponents && (
- )}{" "}
+ )}
);
diff --git a/src/webapp/components/item-root/ItemRoot.tsx b/src/webapp/components/item-root/ItemRoot.tsx
index 71e9522..144dfd0 100644
--- a/src/webapp/components/item-root/ItemRoot.tsx
+++ b/src/webapp/components/item-root/ItemRoot.tsx
@@ -32,12 +32,7 @@ export const ItemRoot: React.FC<{
{currentPage.pageRendering === "single" ? (
currentPage.children.map(node => (
- - openPage(node)}
- currentPage={node}
- />
+
))
) : (
diff --git a/src/webapp/contexts/app-context.tsx b/src/webapp/contexts/app-context.tsx
index a79f8e9..d6746fe 100644
--- a/src/webapp/contexts/app-context.tsx
+++ b/src/webapp/contexts/app-context.tsx
@@ -9,10 +9,12 @@ import { cacheImages } from "../utils/image-cache";
import { Instance } from "../../data/entities/Instance";
import { Typography } from "@material-ui/core";
import i18n from "../../locales";
+import { Maybe } from "../../types/utils";
const AppContext = React.createContext(null);
export const AppContextProvider: React.FC = ({ children, baseUrl, locale }) => {
+ const [compositionRoot, setCompositionRoot] = React.useState();
const [actions, setActions] = useState([]);
const [landings, setLandings] = useState();
const [hasSettingsAccess, setHasSettingsAccess] = useState(false);
@@ -22,7 +24,7 @@ export const AppContextProvider: React.FC = ({ children
const [launchAppBaseUrl, setLaunchAppBaseUrl] = useState("");
const translate = buildTranslate(locale);
- const [compositionRoot, setCompositionRoot] = React.useState();
+ const getLandingNodeById = useCallback((id: string) => landings?.find(landing => landing.id === id), [landings]);
React.useEffect(() => {
getCompositionRoot(new Instance({ url: baseUrl })).then(compositionRoot => {
@@ -66,6 +68,7 @@ export const AppContextProvider: React.FC = ({ children
hasSettingsAccess,
isAdmin,
launchAppBaseUrl,
+ getLandingNodeById,
}}
>
{children}
@@ -112,4 +115,5 @@ export interface AppContextState {
hasSettingsAccess: boolean;
isAdmin: boolean;
launchAppBaseUrl: string;
+ getLandingNodeById: (id: string) => Maybe;
}
diff --git a/src/webapp/pages/home/HomePage.tsx b/src/webapp/pages/home/HomePage.tsx
index 6d150b1..575e69c 100644
--- a/src/webapp/pages/home/HomePage.tsx
+++ b/src/webapp/pages/home/HomePage.tsx
@@ -1,12 +1,12 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import CircularProgress from "material-ui/CircularProgress";
import styled from "styled-components";
+import { useSnackbar } from "@eyeseetea/d2-ui-components";
import {
LandingNode,
+ flattenLandingNodes,
getPrimaryRedirectUrl as getPrimaryActionUrl,
- updateLandingNodes,
} from "../../../domain/entities/LandingNode";
-import i18n from "../../../locales";
import { LandingLayout, LandingContent } from "../../components/landing-layout";
import { useAppContext } from "../../contexts/app-context";
import { useNavigate } from "react-router-dom";
@@ -18,29 +18,24 @@ import { goTo } from "../../utils/routes";
import { defaultIcon, defaultTitle } from "../../router/Router";
import { useAnalytics } from "../../hooks/useAnalytics";
import { Maybe } from "../../../types/utils";
+import i18n from "../../../locales";
export const HomePage: React.FC = React.memo(() => {
- const { hasSettingsAccess, landings, reload, isLoading, launchAppBaseUrl, translate, compositionRoot } =
- useAppContext();
- const { defaultApplication, landingPagePermissions, user } = useConfig();
-
- const userLandings = useMemo(() => {
- return landings && landingPagePermissions && user
- ? updateLandingNodes(landings, landingPagePermissions, user)
- : undefined;
- }, [landingPagePermissions, landings, user]);
+ const { hasSettingsAccess, reload, isLoading, launchAppBaseUrl, translate, compositionRoot } = useAppContext();
+ const { defaultApplication, userLandings } = useConfig();
const initLandings = useMemo(() => userLandings?.filter(landing => landing.executeOnInit), [userLandings]);
const navigate = useNavigate();
const analytics = useAnalytics();
+ const snackbar = useSnackbar();
const [history, updateHistory] = useState([]);
const [isLoadingLong, setLoadingLong] = useState(false);
const [pageType, setPageType] = useState<"userLandings" | "singleLanding">(
userLandings && userLandings?.length > 1 ? "userLandings" : "singleLanding"
);
- const favicon = useRef(document.head.querySelector('link[rel="icon"]'));
+ const favicon = useRef(document.head.querySelector('link[rel="shortcut icon"]'));
const currentPage = useMemo(() => {
return history[0] ?? initLandings?.[0];
@@ -59,10 +54,15 @@ export const HomePage: React.FC = React.memo(() => {
const openPage = useCallback(
(page: LandingNode) => {
- compositionRoot.analytics.sendPageView({ title: page.name.referenceValue, location: undefined });
- updateHistory(history => [page, ...history]);
+ const nodes = userLandings && flattenLandingNodes(userLandings);
+ if (nodes?.some(landing => landing.id === page.id)) {
+ compositionRoot.analytics.sendPageView({ title: page.name.referenceValue, location: undefined });
+ updateHistory(history => [page, ...history]);
+ } else {
+ snackbar.error(i18n.t("You do not have access to this page."));
+ }
},
- [compositionRoot.analytics]
+ [compositionRoot.analytics, userLandings, snackbar]
);
const goBack = useCallback(() => {
diff --git a/src/webapp/pages/settings/useConfig.ts b/src/webapp/pages/settings/useConfig.ts
index 4413cf8..4f92c77 100644
--- a/src/webapp/pages/settings/useConfig.ts
+++ b/src/webapp/pages/settings/useConfig.ts
@@ -1,12 +1,13 @@
-import { useState, useEffect, useCallback } from "react";
+import { useState, useEffect, useCallback, useMemo } from "react";
import { LandingPagePermission, Permission } from "../../../domain/entities/Permission";
import { SharedUpdate } from "../../components/permissions-dialog/PermissionsDialog";
import { useAppContext } from "../../contexts/app-context";
import { User } from "../../../domain/entities/User";
import { Maybe } from "../../../types/utils";
+import { LandingNode, updateLandingNodes } from "../../../domain/entities/LandingNode";
export function useConfig(): useConfigPloc {
- const { compositionRoot } = useAppContext();
+ const { compositionRoot, landings } = useAppContext();
const [showAllActions, setShowAllActions] = useState(false);
const [defaultApplication, setDefaultApplication] = useState("");
const [googleAnalyticsCode, setGoogleAnalyticsCode] = useState>();
@@ -14,6 +15,11 @@ export function useConfig(): useConfigPloc {
const [landingPagePermissions, setLandingPagePermissions] = useState();
const [user, setUser] = useState();
+ const userLandings = useMemo(() => {
+ if (!(landings && landingPagePermissions && user)) return undefined;
+ return updateLandingNodes(landings, landingPagePermissions, user);
+ }, [landingPagePermissions, landings, user]);
+
useEffect(() => {
compositionRoot.config.getShowAllActions().then(setShowAllActions);
compositionRoot.config.getDefaultApplication().then(setDefaultApplication);
@@ -89,6 +95,7 @@ export function useConfig(): useConfigPloc {
landingPagePermissions,
updateLandingPagePermissions,
googleAnalyticsCode,
+ userLandings,
};
}
@@ -104,4 +111,5 @@ interface useConfigPloc {
landingPagePermissions?: LandingPagePermission[];
updateLandingPagePermissions: (sharedUpdate: SharedUpdate, id: string) => Promise;
googleAnalyticsCode: Maybe;
+ userLandings: Maybe;
}