Migrate app view to new-apps (#3161)

This commit is contained in:
Dawid 2023-02-28 14:20:17 +01:00 committed by GitHub
parent c5a25f9cb6
commit 9803202e75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 315 additions and 121 deletions

View file

@ -1 +0,0 @@
export * from "./AppPage";

View file

@ -12,10 +12,8 @@ import {
appInstallPath, appInstallPath,
AppInstallUrlQueryParams, AppInstallUrlQueryParams,
AppListUrlQueryParams, AppListUrlQueryParams,
appPath,
appsListPath, appsListPath,
} from "./urls"; } from "./urls";
import { AppView } from "./views/App";
import AppDetailsView from "./views/AppDetails"; import AppDetailsView from "./views/AppDetails";
import AppInstallView from "./views/AppInstall"; import AppInstallView from "./views/AppInstall";
import AppsListView from "./views/AppsList"; import AppsListView from "./views/AppsList";
@ -31,10 +29,6 @@ const AppDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
); );
}; };
const App: React.FC<RouteComponentProps<{ id: string }>> = ({ match }) => (
<AppView id={decodeURIComponent(match.params.id)} />
);
const AppInstall: React.FC<RouteComponentProps> = props => { const AppInstall: React.FC<RouteComponentProps> = props => {
const qs = parseQs(location.search.substr(1)); const qs = parseQs(location.search.substr(1));
const params: AppInstallUrlQueryParams = qs; const params: AppInstallUrlQueryParams = qs;
@ -58,7 +52,6 @@ const Component = () => {
<Route exact path={appsListPath} component={AppsList} /> <Route exact path={appsListPath} component={AppsList} />
<Route exact path={appInstallPath} component={AppInstall} /> <Route exact path={appInstallPath} component={AppInstall} />
<Route exact path={appDetailsPath(":id")} component={AppDetails} /> <Route exact path={appDetailsPath(":id")} component={AppDetails} />
<Route path={appPath(":id")} component={App} />
<WebhooksRoutes /> <WebhooksRoutes />
</Switch> </Switch>
</> </>

View file

@ -1 +0,0 @@
export * from "./AppView";

View file

@ -1,45 +0,0 @@
import { appMessages } from "@dashboard/apps/messages";
import NotFoundPage from "@dashboard/components/NotFoundPage";
import { useAppQuery } from "@dashboard/graphql";
import useNotifier from "@dashboard/hooks/useNotifier";
import React from "react";
import { useIntl } from "react-intl";
import { AppPage } from "../../components/AppPage";
import { appsListPath } from "../../urls";
interface AppSettingsProps {
id: string;
}
export const AppSettings: React.FC<AppSettingsProps> = ({ id }) => {
const { data, refetch } = useAppQuery({
displayLoader: true,
variables: { id },
});
const appExists = data?.app !== null;
const notify = useNotifier();
const intl = useIntl();
if (!appExists) {
return <NotFoundPage backHref={appsListPath} />;
}
return (
<AppPage
data={data?.app ?? null}
url={data?.app?.configurationUrl ?? ""}
refetch={refetch}
onError={() =>
notify({
status: "error",
text: intl.formatMessage(appMessages.failedToFetchAppSettings),
})
}
/>
);
};
export default AppSettings;

View file

@ -1,7 +1,7 @@
import { import {
extensionMountPoints, extensionMountPoints,
useExtensions, useExtensions,
} from "@dashboard/apps/useExtensions"; } from "@dashboard/new-apps/hooks/useExtensions";
import { Box, List, sprinkles, Text } from "@saleor/macaw-ui/next"; import { Box, List, sprinkles, Text } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";

View file

@ -1,8 +1,4 @@
import { appsListPath } from "@dashboard/apps/urls"; import { appsListPath } from "@dashboard/apps/urls";
import {
extensionMountPoints,
useExtensions,
} from "@dashboard/apps/useExtensions";
import { useUser } from "@dashboard/auth"; import { useUser } from "@dashboard/auth";
import { categoryListUrl } from "@dashboard/categories/urls"; import { categoryListUrl } from "@dashboard/categories/urls";
import { collectionListUrl } from "@dashboard/collections/urls"; import { collectionListUrl } from "@dashboard/collections/urls";
@ -15,6 +11,10 @@ import { giftCardListUrl } from "@dashboard/giftCards/urls";
import { PermissionEnum } from "@dashboard/graphql"; import { PermissionEnum } from "@dashboard/graphql";
import { commonMessages, sectionNames } from "@dashboard/intl"; import { commonMessages, sectionNames } from "@dashboard/intl";
import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver"; import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver";
import {
extensionMountPoints,
useExtensions,
} from "@dashboard/new-apps/hooks/useExtensions";
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls"; import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
import { pageListPath } from "@dashboard/pages/urls"; import { pageListPath } from "@dashboard/pages/urls";
import { productListUrl } from "@dashboard/products/urls"; import { productListUrl } from "@dashboard/products/urls";

View file

@ -1,6 +1,6 @@
import { getDashboardUrFromAppCompleteUrl } from "@dashboard/apps/urls"; import { getDashboardUrFromAppCompleteUrl } from "@dashboard/apps/urls";
import { Extension } from "@dashboard/apps/useExtensions";
import { AppExtensionMountEnum } from "@dashboard/graphql"; import { AppExtensionMountEnum } from "@dashboard/graphql";
import { Extension } from "@dashboard/new-apps/hooks/useExtensions";
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls"; import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
import { matchPath } from "react-router"; import { matchPath } from "react-router";

View file

@ -1,8 +1,3 @@
import {
extensionMountPoints,
mapToMenuItemsForCustomerDetails,
useExtensions,
} from "@dashboard/apps/useExtensions";
import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import { Backlink } from "@dashboard/components/Backlink"; import { Backlink } from "@dashboard/components/Backlink";
import CardMenu from "@dashboard/components/CardMenu/CardMenu"; import CardMenu from "@dashboard/components/CardMenu/CardMenu";
@ -26,6 +21,11 @@ import {
import { SubmitPromise } from "@dashboard/hooks/useForm"; import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import {
extensionMountPoints,
mapToMenuItemsForCustomerDetails,
useExtensions,
} from "@dashboard/new-apps/hooks/useExtensions";
import { orderListUrl } from "@dashboard/orders/urls"; import { orderListUrl } from "@dashboard/orders/urls";
import { mapEdgesToItems, mapMetadataItemToInput } from "@dashboard/utils/maps"; import { mapEdgesToItems, mapMetadataItemToInput } from "@dashboard/utils/maps";
import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger"; import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger";

View file

@ -1,9 +1,3 @@
import {
extensionMountPoints,
mapToMenuItems,
mapToMenuItemsForCustomerOverviewActions,
useExtensions,
} from "@dashboard/apps/useExtensions";
import { useUserPermissions } from "@dashboard/auth/hooks/useUserPermissions"; import { useUserPermissions } from "@dashboard/auth/hooks/useUserPermissions";
import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import ButtonWithSelect from "@dashboard/components/ButtonWithSelect"; import ButtonWithSelect from "@dashboard/components/ButtonWithSelect";
@ -16,6 +10,12 @@ import {
import { ListCustomersQuery } from "@dashboard/graphql"; import { ListCustomersQuery } from "@dashboard/graphql";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import {
extensionMountPoints,
mapToMenuItems,
mapToMenuItemsForCustomerOverviewActions,
useExtensions,
} from "@dashboard/new-apps/hooks/useExtensions";
import { import {
FilterPageProps, FilterPageProps,
ListActions, ListActions,

View file

@ -15,7 +15,6 @@ import TagManager from "react-gtm-module";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { BrowserRouter, Route, Switch } from "react-router-dom"; import { BrowserRouter, Route, Switch } from "react-router-dom";
import { ExternalAppProvider } from "./apps/components/ExternalAppContext";
import { useLocationState } from "./apps/hooks/useLocationState"; import { useLocationState } from "./apps/hooks/useLocationState";
import AttributeSection from "./attributes"; import AttributeSection from "./attributes";
import { attributeSection } from "./attributes/urls"; import { attributeSection } from "./attributes/urls";
@ -59,6 +58,7 @@ import { marketplaceUrl } from "./marketplace/urls";
import NavigationSection from "./navigation"; import NavigationSection from "./navigation";
import { navigationSection } from "./navigation/urls"; import { navigationSection } from "./navigation/urls";
import AppsSection from "./new-apps"; import AppsSection from "./new-apps";
import { ExternalAppProvider } from "./new-apps/components/ExternalAppContext";
import { AppSections } from "./new-apps/urls"; import { AppSections } from "./new-apps/urls";
import { NotFound } from "./NotFound"; import { NotFound } from "./NotFound";
import OrdersSection from "./orders"; import OrdersSection from "./orders";

View file

@ -1,4 +1,3 @@
import { AppFrame } from "@dashboard/apps/components/AppFrame";
import NotFoundPage from "@dashboard/components/NotFoundPage"; import NotFoundPage from "@dashboard/components/NotFoundPage";
import PreviewPill from "@dashboard/components/PreviewPill"; import PreviewPill from "@dashboard/components/PreviewPill";
import { WindowTitle } from "@dashboard/components/WindowTitle"; import { WindowTitle } from "@dashboard/components/WindowTitle";
@ -7,6 +6,7 @@ import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver"; import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver";
import { marketplaceUrl } from "@dashboard/marketplace/urls"; import { marketplaceUrl } from "@dashboard/marketplace/urls";
import { AppFrame } from "@dashboard/new-apps/components/AppFrame";
import { Container } from "@material-ui/core"; import { Container } from "@material-ui/core";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";

View file

@ -0,0 +1,36 @@
import {
Dialog,
DialogContent,
DialogProps,
DialogTitle,
IconButton,
Typography,
} from "@material-ui/core";
import CloseIcon from "@material-ui/icons/Close";
import React from "react";
import { useStyles } from "./styles";
interface AppDialogProps extends DialogProps {
onClose: () => void;
}
export const AppDialog: React.FC<AppDialogProps> = ({ children, ...props }) => {
const classes = useStyles();
return (
<Dialog aria-labelledby="extension app dialog" {...props}>
<DialogTitle disableTypography className={classes.header}>
<Typography variant="h6" component="h2">
{props.title}
</Typography>
<IconButton color="inherit" onClick={props.onClose} aria-label="close">
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent className={classes.content}>{children}</DialogContent>
</Dialog>
);
};
export default AppDialog;

View file

@ -0,0 +1 @@
export * from "./AppDialog";

View file

@ -0,0 +1,19 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
() => ({
header: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
},
content: {
margin: 0,
padding: 0,
overflow: "hidden",
width: 600,
height: 600,
},
}),
{ name: "AppDialog" },
);

View file

@ -1,10 +1,10 @@
import { useAppDashboardUpdates } from "@dashboard/apps/components/AppFrame/useAppDashboardUpdates"; import { useAllFlags } from "@dashboard/hooks/useFlags";
import { useAppDashboardUpdates } from "@dashboard/new-apps/components/AppFrame/useAppDashboardUpdates";
import { import {
AppDetailsUrlQueryParams, AppDetailsUrlQueryParams,
AppUrls,
prepareFeatureFlagsList, prepareFeatureFlagsList,
resolveAppIframeUrl, } from "@dashboard/new-apps/urls";
} from "@dashboard/apps/urls";
import { useAllFlags } from "@dashboard/hooks/useFlags";
import { CircularProgress } from "@material-ui/core"; import { CircularProgress } from "@material-ui/core";
import { useTheme } from "@saleor/macaw-ui"; import { useTheme } from "@saleor/macaw-ui";
import clsx from "clsx"; import clsx from "clsx";
@ -89,7 +89,7 @@ export const AppFrame: React.FC<Props> = ({
</div> </div>
<iframe <iframe
ref={frameRef} ref={frameRef}
src={resolveAppIframeUrl(appId, src, { src={AppUrls.resolveAppIframeUrl(appId, src, {
...params, ...params,
featureFlags: prepareFeatureFlagsList(flags), featureFlags: prepareFeatureFlagsList(flags),
theme: themeType, theme: themeType,

View file

@ -1,7 +1,7 @@
import { AppActionsHandler } from "@dashboard/apps/components/AppFrame/appActionsHandler";
import * as ExternalAppContext from "@dashboard/apps/components/ExternalAppContext/ExternalAppContext";
import * as dashboardConfig from "@dashboard/config"; import * as dashboardConfig from "@dashboard/config";
import { UseNotifierResult } from "@dashboard/hooks/useNotifier"; import { UseNotifierResult } from "@dashboard/hooks/useNotifier";
import { AppActionsHandler } from "@dashboard/new-apps/components/AppFrame/appActionsHandler";
import * as ExternalAppContext from "@dashboard/new-apps/components/ExternalAppContext/ExternalAppContext";
import { renderHook } from "@testing-library/react-hooks"; import { renderHook } from "@testing-library/react-hooks";
import * as ReactIntl from "react-intl"; import * as ReactIntl from "react-intl";
import { IntlShape } from "react-intl"; import { IntlShape } from "react-intl";

View file

@ -1,8 +1,8 @@
import { usePostToExtension } from "@dashboard/apps/components/AppFrame/usePostToExtension";
import { useExternalApp } from "@dashboard/apps/components/ExternalAppContext/ExternalAppContext";
import { getAppMountUri } from "@dashboard/config"; import { getAppMountUri } from "@dashboard/config";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import useNotifier from "@dashboard/hooks/useNotifier"; import useNotifier from "@dashboard/hooks/useNotifier";
import { usePostToExtension } from "@dashboard/new-apps/components/AppFrame/usePostToExtension";
import { useExternalApp } from "@dashboard/new-apps/components/ExternalAppContext/ExternalAppContext";
import { AppUrls } from "@dashboard/new-apps/urls"; import { AppUrls } from "@dashboard/new-apps/urls";
import { import {
DispatchResponseEvent, DispatchResponseEvent,

View file

@ -1,5 +1,5 @@
import { AppActionsHandler } from "@dashboard/apps/components/AppFrame/appActionsHandler"; import { AppActionsHandler } from "@dashboard/new-apps/components/AppFrame/appActionsHandler";
import { usePostToExtension } from "@dashboard/apps/components/AppFrame/usePostToExtension"; import { usePostToExtension } from "@dashboard/new-apps/components/AppFrame/usePostToExtension";
import { Actions, DispatchResponseEvent } from "@saleor/app-sdk/app-bridge"; import { Actions, DispatchResponseEvent } from "@saleor/app-sdk/app-bridge";
import React, { useState } from "react"; import React, { useState } from "react";

View file

@ -1,6 +1,6 @@
import { usePostToExtension } from "@dashboard/apps/components/AppFrame/usePostToExtension";
import { getAppDeepPathFromDashboardUrl } from "@dashboard/apps/urls";
import useLocale from "@dashboard/hooks/useLocale"; import useLocale from "@dashboard/hooks/useLocale";
import { usePostToExtension } from "@dashboard/new-apps/components/AppFrame/usePostToExtension";
import { AppUrls } from "@dashboard/new-apps/urls";
import { useTheme } from "@saleor/macaw-ui"; import { useTheme } from "@saleor/macaw-ui";
import { useEffect } from "react"; import { useEffect } from "react";
@ -51,7 +51,10 @@ export const useAppDashboardUpdates = (
postToExtension({ postToExtension({
type: "redirect", type: "redirect",
payload: { payload: {
path: getAppDeepPathFromDashboardUrl(location.pathname, appId), path: AppUrls.resolveAppDeepPathFromDashboardUrl(
location.pathname,
appId,
),
}, },
}); });
}, [appId, enabled, postToExtension]); }, [appId, enabled, postToExtension]);

View file

@ -35,3 +35,4 @@ export const AppPage: React.FC<AppPageProps> = ({
}; };
AppPage.displayName = "AppPage"; AppPage.displayName = "AppPage";
export default AppPage;

View file

@ -0,0 +1,2 @@
export * from "./AppPage";
export { default } from "./AppPage";

View file

@ -2,6 +2,7 @@ import {
AppInstallationFragment, AppInstallationFragment,
AppListItemFragment, AppListItemFragment,
AppManifestFragment, AppManifestFragment,
AppQuery,
AppTypeEnum, AppTypeEnum,
JobStatusEnum, JobStatusEnum,
PermissionEnum, PermissionEnum,
@ -88,6 +89,41 @@ export const appsInProgress: AppInstallationFragment[] = [
successAppInProgress, successAppInProgress,
]; ];
export const appDetails: NonNullable<AppQuery["app"]> = {
__typename: "App",
aboutApp: "Lorem ipsum",
accessToken: "token",
appUrl: "http://localhost:8888/app",
manifestUrl: "http://localhost:8888/api/manifest",
configurationUrl: "htpp://localhost:8888/configuration",
created: "2020-06-02T12:24:26.818138+00:00",
dataPrivacy: "Lorem ipsum",
dataPrivacyUrl: "http://localhost:8888/app-data-privacy",
homepageUrl: "http://localhost:8888/homepage",
id: "QXBwOjE4MQ==",
isActive: true,
metadata: [],
name: "app1",
permissions: [
{
__typename: "Permission",
code: PermissionEnum.MANAGE_ORDERS,
name: "Manage orders.",
},
{
__typename: "Permission",
code: PermissionEnum.MANAGE_USERS,
name: "Manage customers.",
},
],
privateMetadata: [],
supportUrl: "http://localhost:8888/support",
tokens: [],
type: AppTypeEnum.THIRDPARTY,
version: "1.0.0",
webhooks: [],
};
export const installApp: AppManifestFragment = { export const installApp: AppManifestFragment = {
__typename: "Manifest", __typename: "Manifest",
about: "Lorem ipsum", about: "Lorem ipsum",

View file

@ -8,9 +8,9 @@ import {
import { RelayToFlat } from "@dashboard/types"; import { RelayToFlat } from "@dashboard/types";
import { mapEdgesToItems } from "@dashboard/utils/maps"; import { mapEdgesToItems } from "@dashboard/utils/maps";
import { useExternalApp } from "./components/ExternalAppContext/"; import { useExternalApp } from "../components/ExternalAppContext";
import { AppData } from "./components/ExternalAppContext/context"; import { AppData } from "../components/ExternalAppContext/context";
import { AppDetailsUrlMountQueryParams } from "./urls"; import { AppDetailsUrlMountQueryParams } from "../urls";
export interface Extension { export interface Extension {
id: string; id: string;

View file

@ -2,7 +2,6 @@ import {
AppDetailsUrlQueryParams, AppDetailsUrlQueryParams,
AppInstallUrlQueryParams, AppInstallUrlQueryParams,
} from "@dashboard/apps/urls"; } from "@dashboard/apps/urls";
import { AppView } from "@dashboard/apps/views/App";
import AppDetailsView from "@dashboard/apps/views/AppDetails"; import AppDetailsView from "@dashboard/apps/views/AppDetails";
import AppInstallView from "@dashboard/apps/views/AppInstall"; import AppInstallView from "@dashboard/apps/views/AppInstall";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
@ -14,6 +13,7 @@ import { Route, RouteComponentProps, Switch } from "react-router-dom";
import { WindowTitle } from "../components/WindowTitle"; import { WindowTitle } from "../components/WindowTitle";
import { AppListUrlQueryParams, AppPaths } from "./urls"; import { AppListUrlQueryParams, AppPaths } from "./urls";
import AppListView from "./views/AppList"; import AppListView from "./views/AppList";
import AppView from "./views/AppView";
const AppDetails: React.FC<RouteComponentProps<{ id: string }>> = ({ const AppDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
match, match,

View file

@ -28,6 +28,14 @@ export const appsMessages = defineMessages({
}, },
}); });
export const appMessages = defineMessages({
failedToFetchAppSettings: {
id: "ac+Y98",
defaultMessage: "Failed to fetch app settings",
description: "app settings error",
},
});
export const appInstallationStatusMessages = defineMessages({ export const appInstallationStatusMessages = defineMessages({
pending: { pending: {
id: "F1VtFa", id: "F1VtFa",

View file

@ -1,4 +1,6 @@
import * as config from "@dashboard/config";
import { AppUrls } from "@dashboard/new-apps/urls"; import { AppUrls } from "@dashboard/new-apps/urls";
import { ThemeType } from "@saleor/app-sdk/app-bridge";
describe("AppUrls (apps/urls.ts)", () => { describe("AppUrls (apps/urls.ts)", () => {
describe("isAppDeepUrlChange", () => { describe("isAppDeepUrlChange", () => {
@ -17,4 +19,51 @@ describe("AppUrls (apps/urls.ts)", () => {
).toBe(false); ).toBe(false);
}); });
}); });
describe("resolveAppIframeUrl", () => {
afterAll(() => {
jest.clearAllMocks();
});
describe("For full URL provided in env", () => {
beforeEach(() => {
jest
.spyOn(config, "getApiUrl")
.mockImplementation(() => "https://shop.saleor.cloud/graphql/");
});
it.each<
[string, string, Record<string, string> & { theme: ThemeType }, string]
>([
[
"XyZ123",
"https://my-app.vercel.app",
{ param1: "param1", theme: "light" },
"https://my-app.vercel.app?domain=shop.saleor.cloud&saleorApiUrl=https%3A%2F%2Fshop.saleor.cloud%2Fgraphql%2F&id=XyZ123&param1=param1&theme=light",
],
[
"AbC987",
"https://my-app.vercel.app/configuration",
{ param1: "param1", param2: "param2", theme: "light" },
"https://my-app.vercel.app/configuration?domain=shop.saleor.cloud&saleorApiUrl=https%3A%2F%2Fshop.saleor.cloud%2Fgraphql%2F&id=AbC987&param1=param1&param2=param2&theme=light",
],
])(
"Generates valid URL from segments",
(id, appUrl, params, expectedUrl) => {
const result = AppUrls.resolveAppIframeUrl(id, appUrl, params);
expect(result).toEqual(expectedUrl);
},
);
});
/**
* Test this scenario for cloud deployments where origin is computed.
*
* Current jest is not set up for testing location/URL
*/
test.todo(
"Test if URL is valid when API_URL in env is absolute path like /graphql/",
);
});
}); });

View file

@ -1,12 +1,51 @@
import { AppDetailsUrlQueryParams, appPath } from "@dashboard/apps/urls"; import { getApiUrl } from "@dashboard/config";
import { FlagWithName } from "@dashboard/hooks/useFlags/types";
import { stringifyQs } from "@dashboard/utils/urls"; import { stringifyQs } from "@dashboard/utils/urls";
import { ThemeType } from "@saleor/app-sdk/app-bridge";
import urlJoin from "url-join"; import urlJoin from "url-join";
import { Dialog, SingleAction } from "../types"; import { Dialog, SingleAction } from "../types";
export const MANIFEST_ATTR = "manifestUrl";
export type AppListUrlDialog = "app-installation-remove"; export type AppListUrlDialog = "app-installation-remove";
export type AppListUrlQueryParams = Dialog<AppListUrlDialog> & SingleAction; export type AppListUrlQueryParams = Dialog<AppListUrlDialog> & SingleAction;
export type AppDetailsUrlDialog =
| "app-activate"
| "app-deactivate"
| "app-delete";
export interface AppDetailsUrlMountQueryParams {
productId?: string;
productIds?: string[];
orderId?: string;
customerId?: string;
customerIds?: string[];
}
interface FeatureFlagsQueryParams {
featureFlags?: Record<string, string>;
}
export interface AppDetailsCommonParams {
theme: ThemeType;
}
export type AppDetailsUrlQueryParams = Dialog<AppDetailsUrlDialog> &
SingleAction &
AppDetailsUrlMountQueryParams &
FeatureFlagsQueryParams;
export type AppInstallUrlQueryParams = Partial<{ [MANIFEST_ATTR]: string }>;
export const prepareFeatureFlagsList = (
flags: FlagWithName[],
): Record<string, string> =>
flags.reduce<Record<string, string>>((acc, flag) => {
if (flag.enabled) {
acc[flag.name] = `${flag.value || true}`;
}
return acc;
}, {});
export const AppSections = { export const AppSections = {
appsSection: "/apps/", appsSection: "/apps/",
}; };
@ -30,8 +69,60 @@ export const AppUrls = {
resolveAppInstallUrl: (manifestUrl: string) => resolveAppInstallUrl: (manifestUrl: string) =>
`${AppPaths.appInstallPath}?manifestUrl=${manifestUrl}`, `${AppPaths.appInstallPath}?manifestUrl=${manifestUrl}`,
isAppDeepUrlChange: (appId: string, from: string, to: string) => { isAppDeepUrlChange: (appId: string, from: string, to: string) => {
const appCompletePath = appPath(encodeURIComponent(appId)); const appCompletePath = AppPaths.resolveAppPath(encodeURIComponent(appId));
return to.startsWith(appCompletePath) && from.startsWith(appCompletePath); return to.startsWith(appCompletePath) && from.startsWith(appCompletePath);
}, },
resolveAppDeepPathFromDashboardUrl: (dashboardUrl: string, appId: string) => {
const deepSubPath = dashboardUrl.replace(
AppPaths.resolveAppPath(encodeURIComponent(appId)),
"",
);
return deepSubPath || "/";
},
resolveAppCompleteUrlFromDashboardUrl: (
dashboardUrl: string,
appUrl?: string,
appId?: string,
) => {
if (!appUrl || !appId) {
return appUrl;
}
const deepSubPath = dashboardUrl.replace(
AppPaths.resolveAppPath(encodeURIComponent(appId)),
"",
);
const appCompleteUrl = urlJoin(appUrl, deepSubPath);
return appCompleteUrl;
},
resolveAppIframeUrl: (
appId: string,
appUrl: string,
params: AppDetailsUrlQueryParams & AppDetailsCommonParams,
) => {
const apiUrl = new URL(getApiUrl(), window.location.origin).href;
/**
* Use host to preserve port, in case of multiple Saleors running on localhost
*/
const apiUrlHost = new URL(apiUrl).host;
const iframeContextQueryString = `?${stringifyQs(
{
/**
* @deprecated - domain will be removed in favor of saleorApiUrl.
* Current hostname (used as domain) can be extracted from full URL
*
* Difference will be:
* shop.saleor.cloud -> https://shop.saleor.cloud/graphql/
*/
domain: apiUrlHost,
saleorApiUrl: apiUrl,
id: appId,
...params,
},
"comma",
)}`;
return urlJoin(appUrl, window.location.search, iframeContextQueryString);
},
}; };

View file

@ -3,13 +3,12 @@ import NotFoundPage from "@dashboard/components/NotFoundPage";
import { useAppQuery } from "@dashboard/graphql"; import { useAppQuery } from "@dashboard/graphql";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import useNotifier from "@dashboard/hooks/useNotifier"; import useNotifier from "@dashboard/hooks/useNotifier";
import AppPage from "@dashboard/new-apps/components/AppPage";
import { AppPaths, AppUrls } from "@dashboard/new-apps/urls";
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { useLocation } from "react-router"; import { useLocation } from "react-router";
import { AppPage } from "../../components/AppPage";
import { appsListPath, getAppCompleteUrlFromDashboardUrl } from "../../urls";
interface AppProps { interface AppProps {
id: string; id: string;
} }
@ -37,10 +36,10 @@ export const AppView: React.FC<AppProps> = ({ id }) => {
); );
if (!appExists) { if (!appExists) {
return <NotFoundPage onBack={() => navigate(appsListPath)} />; return <NotFoundPage onBack={() => navigate(AppPaths.appListPath)} />;
} }
const appCompleteUrl = getAppCompleteUrlFromDashboardUrl( const appCompleteUrl = AppUrls.resolveAppCompleteUrlFromDashboardUrl(
location.pathname, location.pathname,
data?.app?.appUrl || "", data?.app?.appUrl || "",
id, id,
@ -59,3 +58,4 @@ export const AppView: React.FC<AppProps> = ({ id }) => {
/> />
); );
}; };
export default AppView;

View file

@ -0,0 +1,2 @@
export * from "./AppView";
export { default } from "./AppView";

View file

@ -1,8 +1,3 @@
import {
extensionMountPoints,
mapToMenuItemsForOrderDetails,
useExtensions,
} from "@dashboard/apps/useExtensions";
import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import CardMenu from "@dashboard/components/CardMenu"; import CardMenu from "@dashboard/components/CardMenu";
import { CardSpacer } from "@dashboard/components/CardSpacer"; import { CardSpacer } from "@dashboard/components/CardSpacer";
@ -18,6 +13,11 @@ import {
} from "@dashboard/graphql"; } from "@dashboard/graphql";
import { SubmitPromise } from "@dashboard/hooks/useForm"; import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import {
extensionMountPoints,
mapToMenuItemsForOrderDetails,
useExtensions,
} from "@dashboard/new-apps/hooks/useExtensions";
import { defaultGraphiQLQuery } from "@dashboard/orders/queries"; import { defaultGraphiQLQuery } from "@dashboard/orders/queries";
import { orderListUrl } from "@dashboard/orders/urls"; import { orderListUrl } from "@dashboard/orders/urls";
import { playgroundOpenHandler } from "@dashboard/utils/graphql"; import { playgroundOpenHandler } from "@dashboard/utils/graphql";

View file

@ -1,8 +1,3 @@
import {
extensionMountPoints,
mapToMenuItems,
useExtensions,
} from "@dashboard/apps/useExtensions";
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo"; import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect"; import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
@ -11,6 +6,11 @@ import FilterBar from "@dashboard/components/FilterBar";
import { ListPageLayout } from "@dashboard/components/Layouts"; import { ListPageLayout } from "@dashboard/components/Layouts";
import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql"; import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import {
extensionMountPoints,
mapToMenuItems,
useExtensions,
} from "@dashboard/new-apps/hooks/useExtensions";
import { OrderListUrlSortField } from "@dashboard/orders/urls"; import { OrderListUrlSortField } from "@dashboard/orders/urls";
import { import {
FilterPageProps, FilterPageProps,

View file

@ -1,9 +1,3 @@
import {
extensionMountPoints,
mapToMenuItems,
mapToMenuItemsForProductOverviewActions,
useExtensions,
} from "@dashboard/apps/useExtensions";
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo"; import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect"; import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
@ -22,6 +16,12 @@ import {
SearchAvailableInGridAttributesQuery, SearchAvailableInGridAttributesQuery,
} from "@dashboard/graphql"; } from "@dashboard/graphql";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import {
extensionMountPoints,
mapToMenuItems,
mapToMenuItemsForProductOverviewActions,
useExtensions,
} from "@dashboard/new-apps/hooks/useExtensions";
import { import {
ChannelProps, ChannelProps,
FetchMoreProps, FetchMoreProps,

View file

@ -1,8 +1,3 @@
import {
extensionMountPoints,
mapToMenuItemsForProductDetails,
useExtensions,
} from "@dashboard/apps/useExtensions";
import { import {
getReferenceAttributeEntityTypeFromAttribute, getReferenceAttributeEntityTypeFromAttribute,
mergeAttributeValues, mergeAttributeValues,
@ -40,6 +35,11 @@ import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import useStateFromProps from "@dashboard/hooks/useStateFromProps"; import useStateFromProps from "@dashboard/hooks/useStateFromProps";
import { maybe } from "@dashboard/misc"; import { maybe } from "@dashboard/misc";
import {
extensionMountPoints,
mapToMenuItemsForProductDetails,
useExtensions,
} from "@dashboard/new-apps/hooks/useExtensions";
import ProductExternalMediaDialog from "@dashboard/products/components/ProductExternalMediaDialog"; import ProductExternalMediaDialog from "@dashboard/products/components/ProductExternalMediaDialog";
import { defaultGraphiQLQuery } from "@dashboard/products/queries"; import { defaultGraphiQLQuery } from "@dashboard/products/queries";
import { productImageUrl, productListUrl } from "@dashboard/products/urls"; import { productImageUrl, productListUrl } from "@dashboard/products/urls";

View file

@ -1,6 +1,6 @@
import { ExternalAppProvider } from "@dashboard/apps/components/ExternalAppContext";
import { Locale, RawLocaleProvider } from "@dashboard/components/Locale"; import { Locale, RawLocaleProvider } from "@dashboard/components/Locale";
import { FlagsServiceProvider } from "@dashboard/hooks/useFlags/flagsService"; import { FlagsServiceProvider } from "@dashboard/hooks/useFlags/flagsService";
import { ExternalAppProvider } from "@dashboard/new-apps/components/ExternalAppContext";
import { paletteOverrides, themeOverrides } from "@dashboard/themeOverrides"; import { paletteOverrides, themeOverrides } from "@dashboard/themeOverrides";
import { ThemeProvider } from "@saleor/macaw-ui"; import { ThemeProvider } from "@saleor/macaw-ui";
import React from "react"; import React from "react";

View file

@ -1,7 +1,7 @@
import { ExternalAppProvider } from "@dashboard/apps/components/ExternalAppContext";
import { Provider as DateProvider } from "@dashboard/components/Date/DateContext"; import { Provider as DateProvider } from "@dashboard/components/Date/DateContext";
import { Locale, RawLocaleProvider } from "@dashboard/components/Locale"; import { Locale, RawLocaleProvider } from "@dashboard/components/Locale";
import { TimezoneProvider } from "@dashboard/components/Timezone"; import { TimezoneProvider } from "@dashboard/components/Timezone";
import { ExternalAppProvider } from "@dashboard/new-apps/components/ExternalAppContext";
import { ThemeProvider } from "@saleor/macaw-ui"; import { ThemeProvider } from "@saleor/macaw-ui";
import React from "react"; import React from "react";
import { IntlProvider } from "react-intl"; import { IntlProvider } from "react-intl";