Migrate app view to new-apps (#3161)
This commit is contained in:
parent
c5a25f9cb6
commit
9803202e75
44 changed files with 315 additions and 121 deletions
|
@ -1 +0,0 @@
|
|||
export * from "./AppPage";
|
|
@ -12,10 +12,8 @@ import {
|
|||
appInstallPath,
|
||||
AppInstallUrlQueryParams,
|
||||
AppListUrlQueryParams,
|
||||
appPath,
|
||||
appsListPath,
|
||||
} from "./urls";
|
||||
import { AppView } from "./views/App";
|
||||
import AppDetailsView from "./views/AppDetails";
|
||||
import AppInstallView from "./views/AppInstall";
|
||||
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 qs = parseQs(location.search.substr(1));
|
||||
const params: AppInstallUrlQueryParams = qs;
|
||||
|
@ -58,7 +52,6 @@ const Component = () => {
|
|||
<Route exact path={appsListPath} component={AppsList} />
|
||||
<Route exact path={appInstallPath} component={AppInstall} />
|
||||
<Route exact path={appDetailsPath(":id")} component={AppDetails} />
|
||||
<Route path={appPath(":id")} component={App} />
|
||||
<WebhooksRoutes />
|
||||
</Switch>
|
||||
</>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from "./AppView";
|
|
@ -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;
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import { Box, List, sprinkles, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import { appsListPath } from "@dashboard/apps/urls";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { useUser } from "@dashboard/auth";
|
||||
import { categoryListUrl } from "@dashboard/categories/urls";
|
||||
import { collectionListUrl } from "@dashboard/collections/urls";
|
||||
|
@ -15,6 +11,10 @@ import { giftCardListUrl } from "@dashboard/giftCards/urls";
|
|||
import { PermissionEnum } from "@dashboard/graphql";
|
||||
import { commonMessages, sectionNames } from "@dashboard/intl";
|
||||
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 { pageListPath } from "@dashboard/pages/urls";
|
||||
import { productListUrl } from "@dashboard/products/urls";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getDashboardUrFromAppCompleteUrl } from "@dashboard/apps/urls";
|
||||
import { Extension } from "@dashboard/apps/useExtensions";
|
||||
import { AppExtensionMountEnum } from "@dashboard/graphql";
|
||||
import { Extension } from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
|
||||
import { matchPath } from "react-router";
|
||||
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItemsForCustomerDetails,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import CardMenu from "@dashboard/components/CardMenu/CardMenu";
|
||||
|
@ -26,6 +21,11 @@ import {
|
|||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItemsForCustomerDetails,
|
||||
useExtensions,
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import { orderListUrl } from "@dashboard/orders/urls";
|
||||
import { mapEdgesToItems, mapMetadataItemToInput } from "@dashboard/utils/maps";
|
||||
import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger";
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItems,
|
||||
mapToMenuItemsForCustomerOverviewActions,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { useUserPermissions } from "@dashboard/auth/hooks/useUserPermissions";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import ButtonWithSelect from "@dashboard/components/ButtonWithSelect";
|
||||
|
@ -16,6 +10,12 @@ import {
|
|||
import { ListCustomersQuery } from "@dashboard/graphql";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItems,
|
||||
mapToMenuItemsForCustomerOverviewActions,
|
||||
useExtensions,
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import {
|
||||
FilterPageProps,
|
||||
ListActions,
|
||||
|
|
|
@ -15,7 +15,6 @@ import TagManager from "react-gtm-module";
|
|||
import { useIntl } from "react-intl";
|
||||
import { BrowserRouter, Route, Switch } from "react-router-dom";
|
||||
|
||||
import { ExternalAppProvider } from "./apps/components/ExternalAppContext";
|
||||
import { useLocationState } from "./apps/hooks/useLocationState";
|
||||
import AttributeSection from "./attributes";
|
||||
import { attributeSection } from "./attributes/urls";
|
||||
|
@ -59,6 +58,7 @@ import { marketplaceUrl } from "./marketplace/urls";
|
|||
import NavigationSection from "./navigation";
|
||||
import { navigationSection } from "./navigation/urls";
|
||||
import AppsSection from "./new-apps";
|
||||
import { ExternalAppProvider } from "./new-apps/components/ExternalAppContext";
|
||||
import { AppSections } from "./new-apps/urls";
|
||||
import { NotFound } from "./NotFound";
|
||||
import OrdersSection from "./orders";
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { AppFrame } from "@dashboard/apps/components/AppFrame";
|
||||
import NotFoundPage from "@dashboard/components/NotFoundPage";
|
||||
import PreviewPill from "@dashboard/components/PreviewPill";
|
||||
import { WindowTitle } from "@dashboard/components/WindowTitle";
|
||||
|
@ -7,6 +6,7 @@ import useNavigator from "@dashboard/hooks/useNavigator";
|
|||
import { sectionNames } from "@dashboard/intl";
|
||||
import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver";
|
||||
import { marketplaceUrl } from "@dashboard/marketplace/urls";
|
||||
import { AppFrame } from "@dashboard/new-apps/components/AppFrame";
|
||||
import { Container } from "@material-ui/core";
|
||||
import React, { useMemo } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
|
36
src/new-apps/components/AppDialog/AppDialog.tsx
Normal file
36
src/new-apps/components/AppDialog/AppDialog.tsx
Normal 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;
|
1
src/new-apps/components/AppDialog/index.ts
Normal file
1
src/new-apps/components/AppDialog/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./AppDialog";
|
19
src/new-apps/components/AppDialog/styles.ts
Normal file
19
src/new-apps/components/AppDialog/styles.ts
Normal 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" },
|
||||
);
|
|
@ -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 {
|
||||
AppDetailsUrlQueryParams,
|
||||
AppUrls,
|
||||
prepareFeatureFlagsList,
|
||||
resolveAppIframeUrl,
|
||||
} from "@dashboard/apps/urls";
|
||||
import { useAllFlags } from "@dashboard/hooks/useFlags";
|
||||
} from "@dashboard/new-apps/urls";
|
||||
import { CircularProgress } from "@material-ui/core";
|
||||
import { useTheme } from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
|
@ -89,7 +89,7 @@ export const AppFrame: React.FC<Props> = ({
|
|||
</div>
|
||||
<iframe
|
||||
ref={frameRef}
|
||||
src={resolveAppIframeUrl(appId, src, {
|
||||
src={AppUrls.resolveAppIframeUrl(appId, src, {
|
||||
...params,
|
||||
featureFlags: prepareFeatureFlagsList(flags),
|
||||
theme: themeType,
|
|
@ -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 { 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 * as ReactIntl from "react-intl";
|
||||
import { IntlShape } from "react-intl";
|
|
@ -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 useNavigator from "@dashboard/hooks/useNavigator";
|
||||
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 {
|
||||
DispatchResponseEvent,
|
|
@ -1,5 +1,5 @@
|
|||
import { AppActionsHandler } from "@dashboard/apps/components/AppFrame/appActionsHandler";
|
||||
import { usePostToExtension } from "@dashboard/apps/components/AppFrame/usePostToExtension";
|
||||
import { AppActionsHandler } from "@dashboard/new-apps/components/AppFrame/appActionsHandler";
|
||||
import { usePostToExtension } from "@dashboard/new-apps/components/AppFrame/usePostToExtension";
|
||||
import { Actions, DispatchResponseEvent } from "@saleor/app-sdk/app-bridge";
|
||||
import React, { useState } from "react";
|
||||
|
|
@ -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 { usePostToExtension } from "@dashboard/new-apps/components/AppFrame/usePostToExtension";
|
||||
import { AppUrls } from "@dashboard/new-apps/urls";
|
||||
import { useTheme } from "@saleor/macaw-ui";
|
||||
import { useEffect } from "react";
|
||||
|
||||
|
@ -51,7 +51,10 @@ export const useAppDashboardUpdates = (
|
|||
postToExtension({
|
||||
type: "redirect",
|
||||
payload: {
|
||||
path: getAppDeepPathFromDashboardUrl(location.pathname, appId),
|
||||
path: AppUrls.resolveAppDeepPathFromDashboardUrl(
|
||||
location.pathname,
|
||||
appId,
|
||||
),
|
||||
},
|
||||
});
|
||||
}, [appId, enabled, postToExtension]);
|
|
@ -35,3 +35,4 @@ export const AppPage: React.FC<AppPageProps> = ({
|
|||
};
|
||||
|
||||
AppPage.displayName = "AppPage";
|
||||
export default AppPage;
|
2
src/new-apps/components/AppPage/index.ts
Normal file
2
src/new-apps/components/AppPage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from "./AppPage";
|
||||
export { default } from "./AppPage";
|
|
@ -2,6 +2,7 @@ import {
|
|||
AppInstallationFragment,
|
||||
AppListItemFragment,
|
||||
AppManifestFragment,
|
||||
AppQuery,
|
||||
AppTypeEnum,
|
||||
JobStatusEnum,
|
||||
PermissionEnum,
|
||||
|
@ -88,6 +89,41 @@ export const appsInProgress: AppInstallationFragment[] = [
|
|||
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 = {
|
||||
__typename: "Manifest",
|
||||
about: "Lorem ipsum",
|
||||
|
|
|
@ -8,9 +8,9 @@ import {
|
|||
import { RelayToFlat } from "@dashboard/types";
|
||||
import { mapEdgesToItems } from "@dashboard/utils/maps";
|
||||
|
||||
import { useExternalApp } from "./components/ExternalAppContext/";
|
||||
import { AppData } from "./components/ExternalAppContext/context";
|
||||
import { AppDetailsUrlMountQueryParams } from "./urls";
|
||||
import { useExternalApp } from "../components/ExternalAppContext";
|
||||
import { AppData } from "../components/ExternalAppContext/context";
|
||||
import { AppDetailsUrlMountQueryParams } from "../urls";
|
||||
|
||||
export interface Extension {
|
||||
id: string;
|
|
@ -2,7 +2,6 @@ import {
|
|||
AppDetailsUrlQueryParams,
|
||||
AppInstallUrlQueryParams,
|
||||
} from "@dashboard/apps/urls";
|
||||
import { AppView } from "@dashboard/apps/views/App";
|
||||
import AppDetailsView from "@dashboard/apps/views/AppDetails";
|
||||
import AppInstallView from "@dashboard/apps/views/AppInstall";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
|
@ -14,6 +13,7 @@ import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
|||
import { WindowTitle } from "../components/WindowTitle";
|
||||
import { AppListUrlQueryParams, AppPaths } from "./urls";
|
||||
import AppListView from "./views/AppList";
|
||||
import AppView from "./views/AppView";
|
||||
|
||||
const AppDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||
match,
|
||||
|
|
|
@ -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({
|
||||
pending: {
|
||||
id: "F1VtFa",
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import * as config from "@dashboard/config";
|
||||
import { AppUrls } from "@dashboard/new-apps/urls";
|
||||
import { ThemeType } from "@saleor/app-sdk/app-bridge";
|
||||
|
||||
describe("AppUrls (apps/urls.ts)", () => {
|
||||
describe("isAppDeepUrlChange", () => {
|
||||
|
@ -17,4 +19,51 @@ describe("AppUrls (apps/urls.ts)", () => {
|
|||
).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¶m1=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¶m1=param1¶m2=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/",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 { ThemeType } from "@saleor/app-sdk/app-bridge";
|
||||
import urlJoin from "url-join";
|
||||
|
||||
import { Dialog, SingleAction } from "../types";
|
||||
|
||||
export const MANIFEST_ATTR = "manifestUrl";
|
||||
|
||||
export type AppListUrlDialog = "app-installation-remove";
|
||||
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 = {
|
||||
appsSection: "/apps/",
|
||||
};
|
||||
|
@ -30,8 +69,60 @@ export const AppUrls = {
|
|||
resolveAppInstallUrl: (manifestUrl: string) =>
|
||||
`${AppPaths.appInstallPath}?manifestUrl=${manifestUrl}`,
|
||||
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);
|
||||
},
|
||||
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);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,13 +3,12 @@ import NotFoundPage from "@dashboard/components/NotFoundPage";
|
|||
import { useAppQuery } from "@dashboard/graphql";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
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 { useIntl } from "react-intl";
|
||||
import { useLocation } from "react-router";
|
||||
|
||||
import { AppPage } from "../../components/AppPage";
|
||||
import { appsListPath, getAppCompleteUrlFromDashboardUrl } from "../../urls";
|
||||
|
||||
interface AppProps {
|
||||
id: string;
|
||||
}
|
||||
|
@ -37,10 +36,10 @@ export const AppView: React.FC<AppProps> = ({ id }) => {
|
|||
);
|
||||
|
||||
if (!appExists) {
|
||||
return <NotFoundPage onBack={() => navigate(appsListPath)} />;
|
||||
return <NotFoundPage onBack={() => navigate(AppPaths.appListPath)} />;
|
||||
}
|
||||
|
||||
const appCompleteUrl = getAppCompleteUrlFromDashboardUrl(
|
||||
const appCompleteUrl = AppUrls.resolveAppCompleteUrlFromDashboardUrl(
|
||||
location.pathname,
|
||||
data?.app?.appUrl || "",
|
||||
id,
|
||||
|
@ -59,3 +58,4 @@ export const AppView: React.FC<AppProps> = ({ id }) => {
|
|||
/>
|
||||
);
|
||||
};
|
||||
export default AppView;
|
2
src/new-apps/views/AppView/index.ts
Normal file
2
src/new-apps/views/AppView/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from "./AppView";
|
||||
export { default } from "./AppView";
|
|
@ -1,8 +1,3 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItemsForOrderDetails,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardMenu from "@dashboard/components/CardMenu";
|
||||
import { CardSpacer } from "@dashboard/components/CardSpacer";
|
||||
|
@ -18,6 +13,11 @@ import {
|
|||
} from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItemsForOrderDetails,
|
||||
useExtensions,
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import { defaultGraphiQLQuery } from "@dashboard/orders/queries";
|
||||
import { orderListUrl } from "@dashboard/orders/urls";
|
||||
import { playgroundOpenHandler } from "@dashboard/utils/graphql";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItems,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
|
||||
|
@ -11,6 +6,11 @@ import FilterBar from "@dashboard/components/FilterBar";
|
|||
import { ListPageLayout } from "@dashboard/components/Layouts";
|
||||
import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItems,
|
||||
useExtensions,
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import { OrderListUrlSortField } from "@dashboard/orders/urls";
|
||||
import {
|
||||
FilterPageProps,
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItems,
|
||||
mapToMenuItemsForProductOverviewActions,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
|
||||
|
@ -22,6 +16,12 @@ import {
|
|||
SearchAvailableInGridAttributesQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItems,
|
||||
mapToMenuItemsForProductOverviewActions,
|
||||
useExtensions,
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import {
|
||||
ChannelProps,
|
||||
FetchMoreProps,
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItemsForProductDetails,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import {
|
||||
getReferenceAttributeEntityTypeFromAttribute,
|
||||
mergeAttributeValues,
|
||||
|
@ -40,6 +35,11 @@ import { SubmitPromise } from "@dashboard/hooks/useForm";
|
|||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import useStateFromProps from "@dashboard/hooks/useStateFromProps";
|
||||
import { maybe } from "@dashboard/misc";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
mapToMenuItemsForProductDetails,
|
||||
useExtensions,
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import ProductExternalMediaDialog from "@dashboard/products/components/ProductExternalMediaDialog";
|
||||
import { defaultGraphiQLQuery } from "@dashboard/products/queries";
|
||||
import { productImageUrl, productListUrl } from "@dashboard/products/urls";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ExternalAppProvider } from "@dashboard/apps/components/ExternalAppContext";
|
||||
import { Locale, RawLocaleProvider } from "@dashboard/components/Locale";
|
||||
import { FlagsServiceProvider } from "@dashboard/hooks/useFlags/flagsService";
|
||||
import { ExternalAppProvider } from "@dashboard/new-apps/components/ExternalAppContext";
|
||||
import { paletteOverrides, themeOverrides } from "@dashboard/themeOverrides";
|
||||
import { ThemeProvider } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ExternalAppProvider } from "@dashboard/apps/components/ExternalAppContext";
|
||||
import { Provider as DateProvider } from "@dashboard/components/Date/DateContext";
|
||||
import { Locale, RawLocaleProvider } from "@dashboard/components/Locale";
|
||||
import { TimezoneProvider } from "@dashboard/components/Timezone";
|
||||
import { ExternalAppProvider } from "@dashboard/new-apps/components/ExternalAppContext";
|
||||
import { ThemeProvider } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { IntlProvider } from "react-intl";
|
||||
|
|
Loading…
Reference in a new issue